public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ibus] autotool: Removed upstreamed pathces
@ 2026-05-31  2:06 Takao Fujiwara
  0 siblings, 0 replies; only message in thread
From: Takao Fujiwara @ 2026-05-31  2:06 UTC (permalink / raw)
  To: git-commits

A new commit has been pushed.

Repo   : rpms/ibus
Branch : autotool
Commit : 176dd52b5225a72f73cafaac9bd4cf9e1c3ecf52
Author : Takao Fujiwara <tfujiwar@redhat.com>
Date   : 2017-05-15T16:44:47+09:00
Stats  : +0/-13004 in 2 file(s)
URL    : https://src.fedoraproject.org/rpms/ibus/c/176dd52b5225a72f73cafaac9bd4cf9e1c3ecf52?branch=autotool

Log:
Removed upstreamed pathces

---
diff --git a/emoji-test.txt b/emoji-test.txt
deleted file mode 100644
index 533739f..0000000
--- a/emoji-test.txt
+++ /dev/null
@@ -1,3033 +0,0 @@
-# emoji-test.txt
-# Date: 2016-11-16, 18:29:53 GMT
-# © 2016 Unicode®, Inc.
-# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
-#
-# Emoji Keyboard/Display Test Data for UTR #51
-# Version: 4.0
-#
-# For documentation and usage, see http://www.unicode.org/reports/tr51
-#
-# This file provides data for testing which emoji forms should be in keyboards and which should also be displayed/processed.
-# Format
-#   Code points; status # emoji name
-#     Status
-#       keyboard — see “Emoji Implementation Notes” in UTR#51
-#       process — see “Emoji Implementation Notes” in UTR#51
-# Notes:
-#   • This currently omits the 12 keycap bases, the 5 modifier characters, and 26 singleton Regional Indicator characters
-#   • The file is in CLDR order, not codepoint order. This is recommended (but not required!) for keyboard palettes.
-#   • The groups and subgroups are purely illustrative. See the Emoji Order chart for more information.
-
-# group: Smileys & People
-
-# subgroup: face-positive
-1F600                                      ; fully-qualified     # 😀 grinning face
-1F601                                      ; fully-qualified     # 😁 grinning face with smiling eyes
-1F602                                      ; fully-qualified     # 😂 face with tears of joy
-1F923                                      ; fully-qualified     # 🤣 rolling on the floor laughing
-1F603                                      ; fully-qualified     # 😃 smiling face with open mouth
-1F604                                      ; fully-qualified     # 😄 smiling face with open mouth & smiling eyes
-1F605                                      ; fully-qualified     # 😅 smiling face with open mouth & cold sweat
-1F606                                      ; fully-qualified     # 😆 smiling face with open mouth & closed eyes
-1F609                                      ; fully-qualified     # 😉 winking face
-1F60A                                      ; fully-qualified     # 😊 smiling face with smiling eyes
-1F60B                                      ; fully-qualified     # 😋 face savouring delicious food
-1F60E                                      ; fully-qualified     # 😎 smiling face with sunglasses
-1F60D                                      ; fully-qualified     # 😍 smiling face with heart-eyes
-1F618                                      ; fully-qualified     # 😘 face blowing a kiss
-1F617                                      ; fully-qualified     # 😗 kissing face
-1F619                                      ; fully-qualified     # 😙 kissing face with smiling eyes
-1F61A                                      ; fully-qualified     # 😚 kissing face with closed eyes
-263A FE0F                                  ; fully-qualified     # ☺️ smiling face
-1F642                                      ; fully-qualified     # 🙂 slightly smiling face
-1F917                                      ; fully-qualified     # 🤗 hugging face
-
-# subgroup: face-neutral
-1F914                                      ; fully-qualified     # 🤔 thinking face
-1F610                                      ; fully-qualified     # 😐 neutral face
-1F611                                      ; fully-qualified     # 😑 expressionless face
-1F636                                      ; fully-qualified     # 😶 face without mouth
-1F644                                      ; fully-qualified     # 🙄 face with rolling eyes
-1F60F                                      ; fully-qualified     # 😏 smirking face
-1F623                                      ; fully-qualified     # 😣 persevering face
-1F625                                      ; fully-qualified     # 😥 disappointed but relieved face
-1F62E                                      ; fully-qualified     # 😮 face with open mouth
-1F910                                      ; fully-qualified     # 🤐 zipper-mouth face
-1F62F                                      ; fully-qualified     # 😯 hushed face
-1F62A                                      ; fully-qualified     # 😪 sleepy face
-1F62B                                      ; fully-qualified     # 😫 tired face
-1F634                                      ; fully-qualified     # 😴 sleeping face
-1F60C                                      ; fully-qualified     # 😌 relieved face
-1F913                                      ; fully-qualified     # 🤓 nerd face
-1F61B                                      ; fully-qualified     # 😛 face with stuck-out tongue
-1F61C                                      ; fully-qualified     # 😜 face with stuck-out tongue & winking eye
-1F61D                                      ; fully-qualified     # 😝 face with stuck-out tongue & closed eyes
-1F924                                      ; fully-qualified     # 🤤 drooling face
-1F612                                      ; fully-qualified     # 😒 unamused face
-1F613                                      ; fully-qualified     # 😓 face with cold sweat
-1F614                                      ; fully-qualified     # 😔 pensive face
-1F615                                      ; fully-qualified     # 😕 confused face
-1F643                                      ; fully-qualified     # 🙃 upside-down face
-1F911                                      ; fully-qualified     # 🤑 money-mouth face
-1F632                                      ; fully-qualified     # 😲 astonished face
-
-# subgroup: face-negative
-2639 FE0F                                  ; fully-qualified     # ☹️ frowning face
-1F641                                      ; fully-qualified     # 🙁 slightly frowning face
-1F616                                      ; fully-qualified     # 😖 confounded face
-1F61E                                      ; fully-qualified     # 😞 disappointed face
-1F61F                                      ; fully-qualified     # 😟 worried face
-1F624                                      ; fully-qualified     # 😤 face with steam from nose
-1F622                                      ; fully-qualified     # 😢 crying face
-1F62D                                      ; fully-qualified     # 😭 loudly crying face
-1F626                                      ; fully-qualified     # 😦 frowning face with open mouth
-1F627                                      ; fully-qualified     # 😧 anguished face
-1F628                                      ; fully-qualified     # 😨 fearful face
-1F629                                      ; fully-qualified     # 😩 weary face
-1F62C                                      ; fully-qualified     # 😬 grimacing face
-1F630                                      ; fully-qualified     # 😰 face with open mouth & cold sweat
-1F631                                      ; fully-qualified     # 😱 face screaming in fear
-1F633                                      ; fully-qualified     # 😳 flushed face
-1F635                                      ; fully-qualified     # 😵 dizzy face
-1F621                                      ; fully-qualified     # 😡 pouting face
-1F620                                      ; fully-qualified     # 😠 angry face
-
-# subgroup: face-role
-1F607                                      ; fully-qualified     # 😇 smiling face with halo
-1F920                                      ; fully-qualified     # 🤠 cowboy hat face
-1F921                                      ; fully-qualified     # 🤡 clown face
-1F925                                      ; fully-qualified     # 🤥 lying face
-
-# subgroup: face-sick
-1F637                                      ; fully-qualified     # 😷 face with medical mask
-1F912                                      ; fully-qualified     # 🤒 face with thermometer
-1F915                                      ; fully-qualified     # 🤕 face with head-bandage
-1F922                                      ; fully-qualified     # 🤢 nauseated face
-1F927                                      ; fully-qualified     # 🤧 sneezing face
-
-# subgroup: creature-face
-1F608                                      ; fully-qualified     # 😈 smiling face with horns
-1F47F                                      ; fully-qualified     # 👿 angry face with horns
-1F479                                      ; fully-qualified     # 👹 ogre
-1F47A                                      ; fully-qualified     # 👺 goblin
-1F480                                      ; fully-qualified     # 💀 skull
-2620 FE0F                                  ; fully-qualified     # ☠️ skull and crossbones
-1F47B                                      ; fully-qualified     # 👻 ghost
-1F47D                                      ; fully-qualified     # 👽 alien
-1F47E                                      ; fully-qualified     # 👾 alien monster
-1F916                                      ; fully-qualified     # 🤖 robot face
-1F4A9                                      ; fully-qualified     # 💩 pile of poo
-
-# subgroup: cat-face
-1F63A                                      ; fully-qualified     # 😺 smiling cat face with open mouth
-1F638                                      ; fully-qualified     # 😸 grinning cat face with smiling eyes
-1F639                                      ; fully-qualified     # 😹 cat face with tears of joy
-1F63B                                      ; fully-qualified     # 😻 smiling cat face with heart-eyes
-1F63C                                      ; fully-qualified     # 😼 cat face with wry smile
-1F63D                                      ; fully-qualified     # 😽 kissing cat face with closed eyes
-1F640                                      ; fully-qualified     # 🙀 weary cat face
-1F63F                                      ; fully-qualified     # 😿 crying cat face
-1F63E                                      ; fully-qualified     # 😾 pouting cat face
-
-# subgroup: monkey-face
-1F648                                      ; fully-qualified     # 🙈 see-no-evil monkey
-1F649                                      ; fully-qualified     # 🙉 hear-no-evil monkey
-1F64A                                      ; fully-qualified     # 🙊 speak-no-evil monkey
-
-# subgroup: person
-1F466                                      ; fully-qualified     # 👦 boy
-1F466 1F3FB                                ; fully-qualified     # 👦🏻 boy: light skin tone
-1F466 1F3FC                                ; fully-qualified     # 👦🏼 boy: medium-light skin tone
-1F466 1F3FD                                ; fully-qualified     # 👦🏽 boy: medium skin tone
-1F466 1F3FE                                ; fully-qualified     # 👦🏾 boy: medium-dark skin tone
-1F466 1F3FF                                ; fully-qualified     # 👦🏿 boy: dark skin tone
-1F467                                      ; fully-qualified     # 👧 girl
-1F467 1F3FB                                ; fully-qualified     # 👧🏻 girl: light skin tone
-1F467 1F3FC                                ; fully-qualified     # 👧🏼 girl: medium-light skin tone
-1F467 1F3FD                                ; fully-qualified     # 👧🏽 girl: medium skin tone
-1F467 1F3FE                                ; fully-qualified     # 👧🏾 girl: medium-dark skin tone
-1F467 1F3FF                                ; fully-qualified     # 👧🏿 girl: dark skin tone
-1F468                                      ; fully-qualified     # 👨 man
-1F468 1F3FB                                ; fully-qualified     # 👨🏻 man: light skin tone
-1F468 1F3FC                                ; fully-qualified     # 👨🏼 man: medium-light skin tone
-1F468 1F3FD                                ; fully-qualified     # 👨🏽 man: medium skin tone
-1F468 1F3FE                                ; fully-qualified     # 👨🏾 man: medium-dark skin tone
-1F468 1F3FF                                ; fully-qualified     # 👨🏿 man: dark skin tone
-1F469                                      ; fully-qualified     # 👩 woman
-1F469 1F3FB                                ; fully-qualified     # 👩🏻 woman: light skin tone
-1F469 1F3FC                                ; fully-qualified     # 👩🏼 woman: medium-light skin tone
-1F469 1F3FD                                ; fully-qualified     # 👩🏽 woman: medium skin tone
-1F469 1F3FE                                ; fully-qualified     # 👩🏾 woman: medium-dark skin tone
-1F469 1F3FF                                ; fully-qualified     # 👩🏿 woman: dark skin tone
-1F474                                      ; fully-qualified     # 👴 old man
-1F474 1F3FB                                ; fully-qualified     # 👴🏻 old man: light skin tone
-1F474 1F3FC                                ; fully-qualified     # 👴🏼 old man: medium-light skin tone
-1F474 1F3FD                                ; fully-qualified     # 👴🏽 old man: medium skin tone
-1F474 1F3FE                                ; fully-qualified     # 👴🏾 old man: medium-dark skin tone
-1F474 1F3FF                                ; fully-qualified     # 👴🏿 old man: dark skin tone
-1F475                                      ; fully-qualified     # 👵 old woman
-1F475 1F3FB                                ; fully-qualified     # 👵🏻 old woman: light skin tone
-1F475 1F3FC                                ; fully-qualified     # 👵🏼 old woman: medium-light skin tone
-1F475 1F3FD                                ; fully-qualified     # 👵🏽 old woman: medium skin tone
-1F475 1F3FE                                ; fully-qualified     # 👵🏾 old woman: medium-dark skin tone
-1F475 1F3FF                                ; fully-qualified     # 👵🏿 old woman: dark skin tone
-1F476                                      ; fully-qualified     # 👶 baby
-1F476 1F3FB                                ; fully-qualified     # 👶🏻 baby: light skin tone
-1F476 1F3FC                                ; fully-qualified     # 👶🏼 baby: medium-light skin tone
-1F476 1F3FD                                ; fully-qualified     # 👶🏽 baby: medium skin tone
-1F476 1F3FE                                ; fully-qualified     # 👶🏾 baby: medium-dark skin tone
-1F476 1F3FF                                ; fully-qualified     # 👶🏿 baby: dark skin tone
-1F47C                                      ; fully-qualified     # 👼 baby angel
-1F47C 1F3FB                                ; fully-qualified     # 👼🏻 baby angel: light skin tone
-1F47C 1F3FC                                ; fully-qualified     # 👼🏼 baby angel: medium-light skin tone
-1F47C 1F3FD                                ; fully-qualified     # 👼🏽 baby angel: medium skin tone
-1F47C 1F3FE                                ; fully-qualified     # 👼🏾 baby angel: medium-dark skin tone
-1F47C 1F3FF                                ; fully-qualified     # 👼🏿 baby angel: dark skin tone
-
-# subgroup: person-role
-1F468 200D 2695 FE0F                       ; fully-qualified     # 👨‍⚕️ man health worker
-1F468 200D 2695                            ; non-fully-qualified # 👨‍⚕ man health worker
-1F468 1F3FB 200D 2695 FE0F                 ; fully-qualified     # 👨🏻‍⚕️ man health worker: light skin tone
-1F468 1F3FB 200D 2695                      ; non-fully-qualified # 👨🏻‍⚕ man health worker: light skin tone
-1F468 1F3FC 200D 2695 FE0F                 ; fully-qualified     # 👨🏼‍⚕️ man health worker: medium-light skin tone
-1F468 1F3FC 200D 2695                      ; non-fully-qualified # 👨🏼‍⚕ man health worker: medium-light skin tone
-1F468 1F3FD 200D 2695 FE0F                 ; fully-qualified     # 👨🏽‍⚕️ man health worker: medium skin tone
-1F468 1F3FD 200D 2695                      ; non-fully-qualified # 👨🏽‍⚕ man health worker: medium skin tone
-1F468 1F3FE 200D 2695 FE0F                 ; fully-qualified     # 👨🏾‍⚕️ man health worker: medium-dark skin tone
-1F468 1F3FE 200D 2695                      ; non-fully-qualified # 👨🏾‍⚕ man health worker: medium-dark skin tone
-1F468 1F3FF 200D 2695 FE0F                 ; fully-qualified     # 👨🏿‍⚕️ man health worker: dark skin tone
-1F468 1F3FF 200D 2695                      ; non-fully-qualified # 👨🏿‍⚕ man health worker: dark skin tone
-1F469 200D 2695 FE0F                       ; fully-qualified     # 👩‍⚕️ woman health worker
-1F469 200D 2695                            ; non-fully-qualified # 👩‍⚕ woman health worker
-1F469 1F3FB 200D 2695 FE0F                 ; fully-qualified     # 👩🏻‍⚕️ woman health worker: light skin tone
-1F469 1F3FB 200D 2695                      ; non-fully-qualified # 👩🏻‍⚕ woman health worker: light skin tone
-1F469 1F3FC 200D 2695 FE0F                 ; fully-qualified     # 👩🏼‍⚕️ woman health worker: medium-light skin tone
-1F469 1F3FC 200D 2695                      ; non-fully-qualified # 👩🏼‍⚕ woman health worker: medium-light skin tone
-1F469 1F3FD 200D 2695 FE0F                 ; fully-qualified     # 👩🏽‍⚕️ woman health worker: medium skin tone
-1F469 1F3FD 200D 2695                      ; non-fully-qualified # 👩🏽‍⚕ woman health worker: medium skin tone
-1F469 1F3FE 200D 2695 FE0F                 ; fully-qualified     # 👩🏾‍⚕️ woman health worker: medium-dark skin tone
-1F469 1F3FE 200D 2695                      ; non-fully-qualified # 👩🏾‍⚕ woman health worker: medium-dark skin tone
-1F469 1F3FF 200D 2695 FE0F                 ; fully-qualified     # 👩🏿‍⚕️ woman health worker: dark skin tone
-1F469 1F3FF 200D 2695                      ; non-fully-qualified # 👩🏿‍⚕ woman health worker: dark skin tone
-1F468 200D 1F393                           ; fully-qualified     # 👨‍🎓 man student
-1F468 1F3FB 200D 1F393                     ; fully-qualified     # 👨🏻‍🎓 man student: light skin tone
-1F468 1F3FC 200D 1F393                     ; fully-qualified     # 👨🏼‍🎓 man student: medium-light skin tone
-1F468 1F3FD 200D 1F393                     ; fully-qualified     # 👨🏽‍🎓 man student: medium skin tone
-1F468 1F3FE 200D 1F393                     ; fully-qualified     # 👨🏾‍🎓 man student: medium-dark skin tone
-1F468 1F3FF 200D 1F393                     ; fully-qualified     # 👨🏿‍🎓 man student: dark skin tone
-1F469 200D 1F393                           ; fully-qualified     # 👩‍🎓 woman student
-1F469 1F3FB 200D 1F393                     ; fully-qualified     # 👩🏻‍🎓 woman student: light skin tone
-1F469 1F3FC 200D 1F393                     ; fully-qualified     # 👩🏼‍🎓 woman student: medium-light skin tone
-1F469 1F3FD 200D 1F393                     ; fully-qualified     # 👩🏽‍🎓 woman student: medium skin tone
-1F469 1F3FE 200D 1F393                     ; fully-qualified     # 👩🏾‍🎓 woman student: medium-dark skin tone
-1F469 1F3FF 200D 1F393                     ; fully-qualified     # 👩🏿‍🎓 woman student: dark skin tone
-1F468 200D 1F3EB                           ; fully-qualified     # 👨‍🏫 man teacher
-1F468 1F3FB 200D 1F3EB                     ; fully-qualified     # 👨🏻‍🏫 man teacher: light skin tone
-1F468 1F3FC 200D 1F3EB                     ; fully-qualified     # 👨🏼‍🏫 man teacher: medium-light skin tone
-1F468 1F3FD 200D 1F3EB                     ; fully-qualified     # 👨🏽‍🏫 man teacher: medium skin tone
-1F468 1F3FE 200D 1F3EB                     ; fully-qualified     # 👨🏾‍🏫 man teacher: medium-dark skin tone
-1F468 1F3FF 200D 1F3EB                     ; fully-qualified     # 👨🏿‍🏫 man teacher: dark skin tone
-1F469 200D 1F3EB                           ; fully-qualified     # 👩‍🏫 woman teacher
-1F469 1F3FB 200D 1F3EB                     ; fully-qualified     # 👩🏻‍🏫 woman teacher: light skin tone
-1F469 1F3FC 200D 1F3EB                     ; fully-qualified     # 👩🏼‍🏫 woman teacher: medium-light skin tone
-1F469 1F3FD 200D 1F3EB                     ; fully-qualified     # 👩🏽‍🏫 woman teacher: medium skin tone
-1F469 1F3FE 200D 1F3EB                     ; fully-qualified     # 👩🏾‍🏫 woman teacher: medium-dark skin tone
-1F469 1F3FF 200D 1F3EB                     ; fully-qualified     # 👩🏿‍🏫 woman teacher: dark skin tone
-1F468 200D 2696 FE0F                       ; fully-qualified     # 👨‍⚖️ man judge
-1F468 200D 2696                            ; non-fully-qualified # 👨‍⚖ man judge
-1F468 1F3FB 200D 2696 FE0F                 ; fully-qualified     # 👨🏻‍⚖️ man judge: light skin tone
-1F468 1F3FB 200D 2696                      ; non-fully-qualified # 👨🏻‍⚖ man judge: light skin tone
-1F468 1F3FC 200D 2696 FE0F                 ; fully-qualified     # 👨🏼‍⚖️ man judge: medium-light skin tone
-1F468 1F3FC 200D 2696                      ; non-fully-qualified # 👨🏼‍⚖ man judge: medium-light skin tone
-1F468 1F3FD 200D 2696 FE0F                 ; fully-qualified     # 👨🏽‍⚖️ man judge: medium skin tone
-1F468 1F3FD 200D 2696                      ; non-fully-qualified # 👨🏽‍⚖ man judge: medium skin tone
-1F468 1F3FE 200D 2696 FE0F                 ; fully-qualified     # 👨🏾‍⚖️ man judge: medium-dark skin tone
-1F468 1F3FE 200D 2696                      ; non-fully-qualified # 👨🏾‍⚖ man judge: medium-dark skin tone
-1F468 1F3FF 200D 2696 FE0F                 ; fully-qualified     # 👨🏿‍⚖️ man judge: dark skin tone
-1F468 1F3FF 200D 2696                      ; non-fully-qualified # 👨🏿‍⚖ man judge: dark skin tone
-1F469 200D 2696 FE0F                       ; fully-qualified     # 👩‍⚖️ woman judge
-1F469 200D 2696                            ; non-fully-qualified # 👩‍⚖ woman judge
-1F469 1F3FB 200D 2696 FE0F                 ; fully-qualified     # 👩🏻‍⚖️ woman judge: light skin tone
-1F469 1F3FB 200D 2696                      ; non-fully-qualified # 👩🏻‍⚖ woman judge: light skin tone
-1F469 1F3FC 200D 2696 FE0F                 ; fully-qualified     # 👩🏼‍⚖️ woman judge: medium-light skin tone
-1F469 1F3FC 200D 2696                      ; non-fully-qualified # 👩🏼‍⚖ woman judge: medium-light skin tone
-1F469 1F3FD 200D 2696 FE0F                 ; fully-qualified     # 👩🏽‍⚖️ woman judge: medium skin tone
-1F469 1F3FD 200D 2696                      ; non-fully-qualified # 👩🏽‍⚖ woman judge: medium skin tone
-1F469 1F3FE 200D 2696 FE0F                 ; fully-qualified     # 👩🏾‍⚖️ woman judge: medium-dark skin tone
-1F469 1F3FE 200D 2696                      ; non-fully-qualified # 👩🏾‍⚖ woman judge: medium-dark skin tone
-1F469 1F3FF 200D 2696 FE0F                 ; fully-qualified     # 👩🏿‍⚖️ woman judge: dark skin tone
-1F469 1F3FF 200D 2696                      ; non-fully-qualified # 👩🏿‍⚖ woman judge: dark skin tone
-1F468 200D 1F33E                           ; fully-qualified     # 👨‍🌾 man farmer
-1F468 1F3FB 200D 1F33E                     ; fully-qualified     # 👨🏻‍🌾 man farmer: light skin tone
-1F468 1F3FC 200D 1F33E                     ; fully-qualified     # 👨🏼‍🌾 man farmer: medium-light skin tone
-1F468 1F3FD 200D 1F33E                     ; fully-qualified     # 👨🏽‍🌾 man farmer: medium skin tone
-1F468 1F3FE 200D 1F33E                     ; fully-qualified     # 👨🏾‍🌾 man farmer: medium-dark skin tone
-1F468 1F3FF 200D 1F33E                     ; fully-qualified     # 👨🏿‍🌾 man farmer: dark skin tone
-1F469 200D 1F33E                           ; fully-qualified     # 👩‍🌾 woman farmer
-1F469 1F3FB 200D 1F33E                     ; fully-qualified     # 👩🏻‍🌾 woman farmer: light skin tone
-1F469 1F3FC 200D 1F33E                     ; fully-qualified     # 👩🏼‍🌾 woman farmer: medium-light skin tone
-1F469 1F3FD 200D 1F33E                     ; fully-qualified     # 👩🏽‍🌾 woman farmer: medium skin tone
-1F469 1F3FE 200D 1F33E                     ; fully-qualified     # 👩🏾‍🌾 woman farmer: medium-dark skin tone
-1F469 1F3FF 200D 1F33E                     ; fully-qualified     # 👩🏿‍🌾 woman farmer: dark skin tone
-1F468 200D 1F373                           ; fully-qualified     # 👨‍🍳 man cook
-1F468 1F3FB 200D 1F373                     ; fully-qualified     # 👨🏻‍🍳 man cook: light skin tone
-1F468 1F3FC 200D 1F373                     ; fully-qualified     # 👨🏼‍🍳 man cook: medium-light skin tone
-1F468 1F3FD 200D 1F373                     ; fully-qualified     # 👨🏽‍🍳 man cook: medium skin tone
-1F468 1F3FE 200D 1F373                     ; fully-qualified     # 👨🏾‍🍳 man cook: medium-dark skin tone
-1F468 1F3FF 200D 1F373                     ; fully-qualified     # 👨🏿‍🍳 man cook: dark skin tone
-1F469 200D 1F373                           ; fully-qualified     # 👩‍🍳 woman cook
-1F469 1F3FB 200D 1F373                     ; fully-qualified     # 👩🏻‍🍳 woman cook: light skin tone
-1F469 1F3FC 200D 1F373                     ; fully-qualified     # 👩🏼‍🍳 woman cook: medium-light skin tone
-1F469 1F3FD 200D 1F373                     ; fully-qualified     # 👩🏽‍🍳 woman cook: medium skin tone
-1F469 1F3FE 200D 1F373                     ; fully-qualified     # 👩🏾‍🍳 woman cook: medium-dark skin tone
-1F469 1F3FF 200D 1F373                     ; fully-qualified     # 👩🏿‍🍳 woman cook: dark skin tone
-1F468 200D 1F527                           ; fully-qualified     # 👨‍🔧 man mechanic
-1F468 1F3FB 200D 1F527                     ; fully-qualified     # 👨🏻‍🔧 man mechanic: light skin tone
-1F468 1F3FC 200D 1F527                     ; fully-qualified     # 👨🏼‍🔧 man mechanic: medium-light skin tone
-1F468 1F3FD 200D 1F527                     ; fully-qualified     # 👨🏽‍🔧 man mechanic: medium skin tone
-1F468 1F3FE 200D 1F527                     ; fully-qualified     # 👨🏾‍🔧 man mechanic: medium-dark skin tone
-1F468 1F3FF 200D 1F527                     ; fully-qualified     # 👨🏿‍🔧 man mechanic: dark skin tone
-1F469 200D 1F527                           ; fully-qualified     # 👩‍🔧 woman mechanic
-1F469 1F3FB 200D 1F527                     ; fully-qualified     # 👩🏻‍🔧 woman mechanic: light skin tone
-1F469 1F3FC 200D 1F527                     ; fully-qualified     # 👩🏼‍🔧 woman mechanic: medium-light skin tone
-1F469 1F3FD 200D 1F527                     ; fully-qualified     # 👩🏽‍🔧 woman mechanic: medium skin tone
-1F469 1F3FE 200D 1F527                     ; fully-qualified     # 👩🏾‍🔧 woman mechanic: medium-dark skin tone
-1F469 1F3FF 200D 1F527                     ; fully-qualified     # 👩🏿‍🔧 woman mechanic: dark skin tone
-1F468 200D 1F3ED                           ; fully-qualified     # 👨‍🏭 man factory worker
-1F468 1F3FB 200D 1F3ED                     ; fully-qualified     # 👨🏻‍🏭 man factory worker: light skin tone
-1F468 1F3FC 200D 1F3ED                     ; fully-qualified     # 👨🏼‍🏭 man factory worker: medium-light skin tone
-1F468 1F3FD 200D 1F3ED                     ; fully-qualified     # 👨🏽‍🏭 man factory worker: medium skin tone
-1F468 1F3FE 200D 1F3ED                     ; fully-qualified     # 👨🏾‍🏭 man factory worker: medium-dark skin tone
-1F468 1F3FF 200D 1F3ED                     ; fully-qualified     # 👨🏿‍🏭 man factory worker: dark skin tone
-1F469 200D 1F3ED                           ; fully-qualified     # 👩‍🏭 woman factory worker
-1F469 1F3FB 200D 1F3ED                     ; fully-qualified     # 👩🏻‍🏭 woman factory worker: light skin tone
-1F469 1F3FC 200D 1F3ED                     ; fully-qualified     # 👩🏼‍🏭 woman factory worker: medium-light skin tone
-1F469 1F3FD 200D 1F3ED                     ; fully-qualified     # 👩🏽‍🏭 woman factory worker: medium skin tone
-1F469 1F3FE 200D 1F3ED                     ; fully-qualified     # 👩🏾‍🏭 woman factory worker: medium-dark skin tone
-1F469 1F3FF 200D 1F3ED                     ; fully-qualified     # 👩🏿‍🏭 woman factory worker: dark skin tone
-1F468 200D 1F4BC                           ; fully-qualified     # 👨‍💼 man office worker
-1F468 1F3FB 200D 1F4BC                     ; fully-qualified     # 👨🏻‍💼 man office worker: light skin tone
-1F468 1F3FC 200D 1F4BC                     ; fully-qualified     # 👨🏼‍💼 man office worker: medium-light skin tone
-1F468 1F3FD 200D 1F4BC                     ; fully-qualified     # 👨🏽‍💼 man office worker: medium skin tone
-1F468 1F3FE 200D 1F4BC                     ; fully-qualified     # 👨🏾‍💼 man office worker: medium-dark skin tone
-1F468 1F3FF 200D 1F4BC                     ; fully-qualified     # 👨🏿‍💼 man office worker: dark skin tone
-1F469 200D 1F4BC                           ; fully-qualified     # 👩‍💼 woman office worker
-1F469 1F3FB 200D 1F4BC                     ; fully-qualified     # 👩🏻‍💼 woman office worker: light skin tone
-1F469 1F3FC 200D 1F4BC                     ; fully-qualified     # 👩🏼‍💼 woman office worker: medium-light skin tone
-1F469 1F3FD 200D 1F4BC                     ; fully-qualified     # 👩🏽‍💼 woman office worker: medium skin tone
-1F469 1F3FE 200D 1F4BC                     ; fully-qualified     # 👩🏾‍💼 woman office worker: medium-dark skin tone
-1F469 1F3FF 200D 1F4BC                     ; fully-qualified     # 👩🏿‍💼 woman office worker: dark skin tone
-1F468 200D 1F52C                           ; fully-qualified     # 👨‍🔬 man scientist
-1F468 1F3FB 200D 1F52C                     ; fully-qualified     # 👨🏻‍🔬 man scientist: light skin tone
-1F468 1F3FC 200D 1F52C                     ; fully-qualified     # 👨🏼‍🔬 man scientist: medium-light skin tone
-1F468 1F3FD 200D 1F52C                     ; fully-qualified     # 👨🏽‍🔬 man scientist: medium skin tone
-1F468 1F3FE 200D 1F52C                     ; fully-qualified     # 👨🏾‍🔬 man scientist: medium-dark skin tone
-1F468 1F3FF 200D 1F52C                     ; fully-qualified     # 👨🏿‍🔬 man scientist: dark skin tone
-1F469 200D 1F52C                           ; fully-qualified     # 👩‍🔬 woman scientist
-1F469 1F3FB 200D 1F52C                     ; fully-qualified     # 👩🏻‍🔬 woman scientist: light skin tone
-1F469 1F3FC 200D 1F52C                     ; fully-qualified     # 👩🏼‍🔬 woman scientist: medium-light skin tone
-1F469 1F3FD 200D 1F52C                     ; fully-qualified     # 👩🏽‍🔬 woman scientist: medium skin tone
-1F469 1F3FE 200D 1F52C                     ; fully-qualified     # 👩🏾‍🔬 woman scientist: medium-dark skin tone
-1F469 1F3FF 200D 1F52C                     ; fully-qualified     # 👩🏿‍🔬 woman scientist: dark skin tone
-1F468 200D 1F4BB                           ; fully-qualified     # 👨‍💻 man technologist
-1F468 1F3FB 200D 1F4BB                     ; fully-qualified     # 👨🏻‍💻 man technologist: light skin tone
-1F468 1F3FC 200D 1F4BB                     ; fully-qualified     # 👨🏼‍💻 man technologist: medium-light skin tone
-1F468 1F3FD 200D 1F4BB                     ; fully-qualified     # 👨🏽‍💻 man technologist: medium skin tone
-1F468 1F3FE 200D 1F4BB                     ; fully-qualified     # 👨🏾‍💻 man technologist: medium-dark skin tone
-1F468 1F3FF 200D 1F4BB                     ; fully-qualified     # 👨🏿‍💻 man technologist: dark skin tone
-1F469 200D 1F4BB                           ; fully-qualified     # 👩‍💻 woman technologist
-1F469 1F3FB 200D 1F4BB                     ; fully-qualified     # 👩🏻‍💻 woman technologist: light skin tone
-1F469 1F3FC 200D 1F4BB                     ; fully-qualified     # 👩🏼‍💻 woman technologist: medium-light skin tone
-1F469 1F3FD 200D 1F4BB                     ; fully-qualified     # 👩🏽‍💻 woman technologist: medium skin tone
-1F469 1F3FE 200D 1F4BB                     ; fully-qualified     # 👩🏾‍💻 woman technologist: medium-dark skin tone
-1F469 1F3FF 200D 1F4BB                     ; fully-qualified     # 👩🏿‍💻 woman technologist: dark skin tone
-1F468 200D 1F3A4                           ; fully-qualified     # 👨‍🎤 man singer
-1F468 1F3FB 200D 1F3A4                     ; fully-qualified     # 👨🏻‍🎤 man singer: light skin tone
-1F468 1F3FC 200D 1F3A4                     ; fully-qualified     # 👨🏼‍🎤 man singer: medium-light skin tone
-1F468 1F3FD 200D 1F3A4                     ; fully-qualified     # 👨🏽‍🎤 man singer: medium skin tone
-1F468 1F3FE 200D 1F3A4                     ; fully-qualified     # 👨🏾‍🎤 man singer: medium-dark skin tone
-1F468 1F3FF 200D 1F3A4                     ; fully-qualified     # 👨🏿‍🎤 man singer: dark skin tone
-1F469 200D 1F3A4                           ; fully-qualified     # 👩‍🎤 woman singer
-1F469 1F3FB 200D 1F3A4                     ; fully-qualified     # 👩🏻‍🎤 woman singer: light skin tone
-1F469 1F3FC 200D 1F3A4                     ; fully-qualified     # 👩🏼‍🎤 woman singer: medium-light skin tone
-1F469 1F3FD 200D 1F3A4                     ; fully-qualified     # 👩🏽‍🎤 woman singer: medium skin tone
-1F469 1F3FE 200D 1F3A4                     ; fully-qualified     # 👩🏾‍🎤 woman singer: medium-dark skin tone
-1F469 1F3FF 200D 1F3A4                     ; fully-qualified     # 👩🏿‍🎤 woman singer: dark skin tone
-1F468 200D 1F3A8                           ; fully-qualified     # 👨‍🎨 man artist
-1F468 1F3FB 200D 1F3A8                     ; fully-qualified     # 👨🏻‍🎨 man artist: light skin tone
-1F468 1F3FC 200D 1F3A8                     ; fully-qualified     # 👨🏼‍🎨 man artist: medium-light skin tone
-1F468 1F3FD 200D 1F3A8                     ; fully-qualified     # 👨🏽‍🎨 man artist: medium skin tone
-1F468 1F3FE 200D 1F3A8                     ; fully-qualified     # 👨🏾‍🎨 man artist: medium-dark skin tone
-1F468 1F3FF 200D 1F3A8                     ; fully-qualified     # 👨🏿‍🎨 man artist: dark skin tone
-1F469 200D 1F3A8                           ; fully-qualified     # 👩‍🎨 woman artist
-1F469 1F3FB 200D 1F3A8                     ; fully-qualified     # 👩🏻‍🎨 woman artist: light skin tone
-1F469 1F3FC 200D 1F3A8                     ; fully-qualified     # 👩🏼‍🎨 woman artist: medium-light skin tone
-1F469 1F3FD 200D 1F3A8                     ; fully-qualified     # 👩🏽‍🎨 woman artist: medium skin tone
-1F469 1F3FE 200D 1F3A8                     ; fully-qualified     # 👩🏾‍🎨 woman artist: medium-dark skin tone
-1F469 1F3FF 200D 1F3A8                     ; fully-qualified     # 👩🏿‍🎨 woman artist: dark skin tone
-1F468 200D 2708 FE0F                       ; fully-qualified     # 👨‍✈️ man pilot
-1F468 200D 2708                            ; non-fully-qualified # 👨‍✈ man pilot
-1F468 1F3FB 200D 2708 FE0F                 ; fully-qualified     # 👨🏻‍✈️ man pilot: light skin tone
-1F468 1F3FB 200D 2708                      ; non-fully-qualified # 👨🏻‍✈ man pilot: light skin tone
-1F468 1F3FC 200D 2708 FE0F                 ; fully-qualified     # 👨🏼‍✈️ man pilot: medium-light skin tone
-1F468 1F3FC 200D 2708                      ; non-fully-qualified # 👨🏼‍✈ man pilot: medium-light skin tone
-1F468 1F3FD 200D 2708 FE0F                 ; fully-qualified     # 👨🏽‍✈️ man pilot: medium skin tone
-1F468 1F3FD 200D 2708                      ; non-fully-qualified # 👨🏽‍✈ man pilot: medium skin tone
-1F468 1F3FE 200D 2708 FE0F                 ; fully-qualified     # 👨🏾‍✈️ man pilot: medium-dark skin tone
-1F468 1F3FE 200D 2708                      ; non-fully-qualified # 👨🏾‍✈ man pilot: medium-dark skin tone
-1F468 1F3FF 200D 2708 FE0F                 ; fully-qualified     # 👨🏿‍✈️ man pilot: dark skin tone
-1F468 1F3FF 200D 2708                      ; non-fully-qualified # 👨🏿‍✈ man pilot: dark skin tone
-1F469 200D 2708 FE0F                       ; fully-qualified     # 👩‍✈️ woman pilot
-1F469 200D 2708                            ; non-fully-qualified # 👩‍✈ woman pilot
-1F469 1F3FB 200D 2708 FE0F                 ; fully-qualified     # 👩🏻‍✈️ woman pilot: light skin tone
-1F469 1F3FB 200D 2708                      ; non-fully-qualified # 👩🏻‍✈ woman pilot: light skin tone
-1F469 1F3FC 200D 2708 FE0F                 ; fully-qualified     # 👩🏼‍✈️ woman pilot: medium-light skin tone
-1F469 1F3FC 200D 2708                      ; non-fully-qualified # 👩🏼‍✈ woman pilot: medium-light skin tone
-1F469 1F3FD 200D 2708 FE0F                 ; fully-qualified     # 👩🏽‍✈️ woman pilot: medium skin tone
-1F469 1F3FD 200D 2708                      ; non-fully-qualified # 👩🏽‍✈ woman pilot: medium skin tone
-1F469 1F3FE 200D 2708 FE0F                 ; fully-qualified     # 👩🏾‍✈️ woman pilot: medium-dark skin tone
-1F469 1F3FE 200D 2708                      ; non-fully-qualified # 👩🏾‍✈ woman pilot: medium-dark skin tone
-1F469 1F3FF 200D 2708 FE0F                 ; fully-qualified     # 👩🏿‍✈️ woman pilot: dark skin tone
-1F469 1F3FF 200D 2708                      ; non-fully-qualified # 👩🏿‍✈ woman pilot: dark skin tone
-1F468 200D 1F680                           ; fully-qualified     # 👨‍🚀 man astronaut
-1F468 1F3FB 200D 1F680                     ; fully-qualified     # 👨🏻‍🚀 man astronaut: light skin tone
-1F468 1F3FC 200D 1F680                     ; fully-qualified     # 👨🏼‍🚀 man astronaut: medium-light skin tone
-1F468 1F3FD 200D 1F680                     ; fully-qualified     # 👨🏽‍🚀 man astronaut: medium skin tone
-1F468 1F3FE 200D 1F680                     ; fully-qualified     # 👨🏾‍🚀 man astronaut: medium-dark skin tone
-1F468 1F3FF 200D 1F680                     ; fully-qualified     # 👨🏿‍🚀 man astronaut: dark skin tone
-1F469 200D 1F680                           ; fully-qualified     # 👩‍🚀 woman astronaut
-1F469 1F3FB 200D 1F680                     ; fully-qualified     # 👩🏻‍🚀 woman astronaut: light skin tone
-1F469 1F3FC 200D 1F680                     ; fully-qualified     # 👩🏼‍🚀 woman astronaut: medium-light skin tone
-1F469 1F3FD 200D 1F680                     ; fully-qualified     # 👩🏽‍🚀 woman astronaut: medium skin tone
-1F469 1F3FE 200D 1F680                     ; fully-qualified     # 👩🏾‍🚀 woman astronaut: medium-dark skin tone
-1F469 1F3FF 200D 1F680                     ; fully-qualified     # 👩🏿‍🚀 woman astronaut: dark skin tone
-1F468 200D 1F692                           ; fully-qualified     # 👨‍🚒 man firefighter
-1F468 1F3FB 200D 1F692                     ; fully-qualified     # 👨🏻‍🚒 man firefighter: light skin tone
-1F468 1F3FC 200D 1F692                     ; fully-qualified     # 👨🏼‍🚒 man firefighter: medium-light skin tone
-1F468 1F3FD 200D 1F692                     ; fully-qualified     # 👨🏽‍🚒 man firefighter: medium skin tone
-1F468 1F3FE 200D 1F692                     ; fully-qualified     # 👨🏾‍🚒 man firefighter: medium-dark skin tone
-1F468 1F3FF 200D 1F692                     ; fully-qualified     # 👨🏿‍🚒 man firefighter: dark skin tone
-1F469 200D 1F692                           ; fully-qualified     # 👩‍🚒 woman firefighter
-1F469 1F3FB 200D 1F692                     ; fully-qualified     # 👩🏻‍🚒 woman firefighter: light skin tone
-1F469 1F3FC 200D 1F692                     ; fully-qualified     # 👩🏼‍🚒 woman firefighter: medium-light skin tone
-1F469 1F3FD 200D 1F692                     ; fully-qualified     # 👩🏽‍🚒 woman firefighter: medium skin tone
-1F469 1F3FE 200D 1F692                     ; fully-qualified     # 👩🏾‍🚒 woman firefighter: medium-dark skin tone
-1F469 1F3FF 200D 1F692                     ; fully-qualified     # 👩🏿‍🚒 woman firefighter: dark skin tone
-1F46E                                      ; fully-qualified     # 👮 police officer
-1F46E 1F3FB                                ; fully-qualified     # 👮🏻 police officer: light skin tone
-1F46E 1F3FC                                ; fully-qualified     # 👮🏼 police officer: medium-light skin tone
-1F46E 1F3FD                                ; fully-qualified     # 👮🏽 police officer: medium skin tone
-1F46E 1F3FE                                ; fully-qualified     # 👮🏾 police officer: medium-dark skin tone
-1F46E 1F3FF                                ; fully-qualified     # 👮🏿 police officer: dark skin tone
-1F46E 200D 2642 FE0F                       ; fully-qualified     # 👮‍♂️ man police officer
-1F46E 200D 2642                            ; non-fully-qualified # 👮‍♂ man police officer
-1F46E 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 👮🏻‍♂️ man police officer: light skin tone
-1F46E 1F3FB 200D 2642                      ; non-fully-qualified # 👮🏻‍♂ man police officer: light skin tone
-1F46E 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 👮🏼‍♂️ man police officer: medium-light skin tone
-1F46E 1F3FC 200D 2642                      ; non-fully-qualified # 👮🏼‍♂ man police officer: medium-light skin tone
-1F46E 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 👮🏽‍♂️ man police officer: medium skin tone
-1F46E 1F3FD 200D 2642                      ; non-fully-qualified # 👮🏽‍♂ man police officer: medium skin tone
-1F46E 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 👮🏾‍♂️ man police officer: medium-dark skin tone
-1F46E 1F3FE 200D 2642                      ; non-fully-qualified # 👮🏾‍♂ man police officer: medium-dark skin tone
-1F46E 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 👮🏿‍♂️ man police officer: dark skin tone
-1F46E 1F3FF 200D 2642                      ; non-fully-qualified # 👮🏿‍♂ man police officer: dark skin tone
-1F46E 200D 2640 FE0F                       ; fully-qualified     # 👮‍♀️ woman police officer
-1F46E 200D 2640                            ; non-fully-qualified # 👮‍♀ woman police officer
-1F46E 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 👮🏻‍♀️ woman police officer: light skin tone
-1F46E 1F3FB 200D 2640                      ; non-fully-qualified # 👮🏻‍♀ woman police officer: light skin tone
-1F46E 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 👮🏼‍♀️ woman police officer: medium-light skin tone
-1F46E 1F3FC 200D 2640                      ; non-fully-qualified # 👮🏼‍♀ woman police officer: medium-light skin tone
-1F46E 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 👮🏽‍♀️ woman police officer: medium skin tone
-1F46E 1F3FD 200D 2640                      ; non-fully-qualified # 👮🏽‍♀ woman police officer: medium skin tone
-1F46E 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 👮🏾‍♀️ woman police officer: medium-dark skin tone
-1F46E 1F3FE 200D 2640                      ; non-fully-qualified # 👮🏾‍♀ woman police officer: medium-dark skin tone
-1F46E 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 👮🏿‍♀️ woman police officer: dark skin tone
-1F46E 1F3FF 200D 2640                      ; non-fully-qualified # 👮🏿‍♀ woman police officer: dark skin tone
-1F575 FE0F                                 ; fully-qualified     # 🕵️ detective
-1F575 1F3FB                                ; fully-qualified     # 🕵🏻 detective: light skin tone
-1F575 1F3FC                                ; fully-qualified     # 🕵🏼 detective: medium-light skin tone
-1F575 1F3FD                                ; fully-qualified     # 🕵🏽 detective: medium skin tone
-1F575 1F3FE                                ; fully-qualified     # 🕵🏾 detective: medium-dark skin tone
-1F575 1F3FF                                ; fully-qualified     # 🕵🏿 detective: dark skin tone
-1F575 FE0F 200D 2642 FE0F                  ; fully-qualified     # 🕵️‍♂️ man detective
-1F575 200D 2642                            ; non-fully-qualified # 🕵‍♂ man detective
-1F575 FE0F 200D 2642                       ; non-fully-qualified # 🕵️‍♂ man detective
-1F575 200D 2642 FE0F                       ; non-fully-qualified # 🕵‍♂️ man detective
-1F575 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🕵🏻‍♂️ man detective: light skin tone
-1F575 1F3FB 200D 2642                      ; non-fully-qualified # 🕵🏻‍♂ man detective: light skin tone
-1F575 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🕵🏼‍♂️ man detective: medium-light skin tone
-1F575 1F3FC 200D 2642                      ; non-fully-qualified # 🕵🏼‍♂ man detective: medium-light skin tone
-1F575 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🕵🏽‍♂️ man detective: medium skin tone
-1F575 1F3FD 200D 2642                      ; non-fully-qualified # 🕵🏽‍♂ man detective: medium skin tone
-1F575 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🕵🏾‍♂️ man detective: medium-dark skin tone
-1F575 1F3FE 200D 2642                      ; non-fully-qualified # 🕵🏾‍♂ man detective: medium-dark skin tone
-1F575 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🕵🏿‍♂️ man detective: dark skin tone
-1F575 1F3FF 200D 2642                      ; non-fully-qualified # 🕵🏿‍♂ man detective: dark skin tone
-1F575 FE0F 200D 2640 FE0F                  ; fully-qualified     # 🕵️‍♀️ woman detective
-1F575 200D 2640                            ; non-fully-qualified # 🕵‍♀ woman detective
-1F575 FE0F 200D 2640                       ; non-fully-qualified # 🕵️‍♀ woman detective
-1F575 200D 2640 FE0F                       ; non-fully-qualified # 🕵‍♀️ woman detective
-1F575 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🕵🏻‍♀️ woman detective: light skin tone
-1F575 1F3FB 200D 2640                      ; non-fully-qualified # 🕵🏻‍♀ woman detective: light skin tone
-1F575 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🕵🏼‍♀️ woman detective: medium-light skin tone
-1F575 1F3FC 200D 2640                      ; non-fully-qualified # 🕵🏼‍♀ woman detective: medium-light skin tone
-1F575 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🕵🏽‍♀️ woman detective: medium skin tone
-1F575 1F3FD 200D 2640                      ; non-fully-qualified # 🕵🏽‍♀ woman detective: medium skin tone
-1F575 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🕵🏾‍♀️ woman detective: medium-dark skin tone
-1F575 1F3FE 200D 2640                      ; non-fully-qualified # 🕵🏾‍♀ woman detective: medium-dark skin tone
-1F575 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🕵🏿‍♀️ woman detective: dark skin tone
-1F575 1F3FF 200D 2640                      ; non-fully-qualified # 🕵🏿‍♀ woman detective: dark skin tone
-1F482                                      ; fully-qualified     # 💂 guard
-1F482 1F3FB                                ; fully-qualified     # 💂🏻 guard: light skin tone
-1F482 1F3FC                                ; fully-qualified     # 💂🏼 guard: medium-light skin tone
-1F482 1F3FD                                ; fully-qualified     # 💂🏽 guard: medium skin tone
-1F482 1F3FE                                ; fully-qualified     # 💂🏾 guard: medium-dark skin tone
-1F482 1F3FF                                ; fully-qualified     # 💂🏿 guard: dark skin tone
-1F482 200D 2642 FE0F                       ; fully-qualified     # 💂‍♂️ man guard
-1F482 200D 2642                            ; non-fully-qualified # 💂‍♂ man guard
-1F482 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 💂🏻‍♂️ man guard: light skin tone
-1F482 1F3FB 200D 2642                      ; non-fully-qualified # 💂🏻‍♂ man guard: light skin tone
-1F482 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 💂🏼‍♂️ man guard: medium-light skin tone
-1F482 1F3FC 200D 2642                      ; non-fully-qualified # 💂🏼‍♂ man guard: medium-light skin tone
-1F482 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 💂🏽‍♂️ man guard: medium skin tone
-1F482 1F3FD 200D 2642                      ; non-fully-qualified # 💂🏽‍♂ man guard: medium skin tone
-1F482 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 💂🏾‍♂️ man guard: medium-dark skin tone
-1F482 1F3FE 200D 2642                      ; non-fully-qualified # 💂🏾‍♂ man guard: medium-dark skin tone
-1F482 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 💂🏿‍♂️ man guard: dark skin tone
-1F482 1F3FF 200D 2642                      ; non-fully-qualified # 💂🏿‍♂ man guard: dark skin tone
-1F482 200D 2640 FE0F                       ; fully-qualified     # 💂‍♀️ woman guard
-1F482 200D 2640                            ; non-fully-qualified # 💂‍♀ woman guard
-1F482 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 💂🏻‍♀️ woman guard: light skin tone
-1F482 1F3FB 200D 2640                      ; non-fully-qualified # 💂🏻‍♀ woman guard: light skin tone
-1F482 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 💂🏼‍♀️ woman guard: medium-light skin tone
-1F482 1F3FC 200D 2640                      ; non-fully-qualified # 💂🏼‍♀ woman guard: medium-light skin tone
-1F482 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 💂🏽‍♀️ woman guard: medium skin tone
-1F482 1F3FD 200D 2640                      ; non-fully-qualified # 💂🏽‍♀ woman guard: medium skin tone
-1F482 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 💂🏾‍♀️ woman guard: medium-dark skin tone
-1F482 1F3FE 200D 2640                      ; non-fully-qualified # 💂🏾‍♀ woman guard: medium-dark skin tone
-1F482 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 💂🏿‍♀️ woman guard: dark skin tone
-1F482 1F3FF 200D 2640                      ; non-fully-qualified # 💂🏿‍♀ woman guard: dark skin tone
-1F477                                      ; fully-qualified     # 👷 construction worker
-1F477 1F3FB                                ; fully-qualified     # 👷🏻 construction worker: light skin tone
-1F477 1F3FC                                ; fully-qualified     # 👷🏼 construction worker: medium-light skin tone
-1F477 1F3FD                                ; fully-qualified     # 👷🏽 construction worker: medium skin tone
-1F477 1F3FE                                ; fully-qualified     # 👷🏾 construction worker: medium-dark skin tone
-1F477 1F3FF                                ; fully-qualified     # 👷🏿 construction worker: dark skin tone
-1F477 200D 2642 FE0F                       ; fully-qualified     # 👷‍♂️ man construction worker
-1F477 200D 2642                            ; non-fully-qualified # 👷‍♂ man construction worker
-1F477 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 👷🏻‍♂️ man construction worker: light skin tone
-1F477 1F3FB 200D 2642                      ; non-fully-qualified # 👷🏻‍♂ man construction worker: light skin tone
-1F477 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 👷🏼‍♂️ man construction worker: medium-light skin tone
-1F477 1F3FC 200D 2642                      ; non-fully-qualified # 👷🏼‍♂ man construction worker: medium-light skin tone
-1F477 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 👷🏽‍♂️ man construction worker: medium skin tone
-1F477 1F3FD 200D 2642                      ; non-fully-qualified # 👷🏽‍♂ man construction worker: medium skin tone
-1F477 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 👷🏾‍♂️ man construction worker: medium-dark skin tone
-1F477 1F3FE 200D 2642                      ; non-fully-qualified # 👷🏾‍♂ man construction worker: medium-dark skin tone
-1F477 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 👷🏿‍♂️ man construction worker: dark skin tone
-1F477 1F3FF 200D 2642                      ; non-fully-qualified # 👷🏿‍♂ man construction worker: dark skin tone
-1F477 200D 2640 FE0F                       ; fully-qualified     # 👷‍♀️ woman construction worker
-1F477 200D 2640                            ; non-fully-qualified # 👷‍♀ woman construction worker
-1F477 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 👷🏻‍♀️ woman construction worker: light skin tone
-1F477 1F3FB 200D 2640                      ; non-fully-qualified # 👷🏻‍♀ woman construction worker: light skin tone
-1F477 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 👷🏼‍♀️ woman construction worker: medium-light skin tone
-1F477 1F3FC 200D 2640                      ; non-fully-qualified # 👷🏼‍♀ woman construction worker: medium-light skin tone
-1F477 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 👷🏽‍♀️ woman construction worker: medium skin tone
-1F477 1F3FD 200D 2640                      ; non-fully-qualified # 👷🏽‍♀ woman construction worker: medium skin tone
-1F477 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 👷🏾‍♀️ woman construction worker: medium-dark skin tone
-1F477 1F3FE 200D 2640                      ; non-fully-qualified # 👷🏾‍♀ woman construction worker: medium-dark skin tone
-1F477 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 👷🏿‍♀️ woman construction worker: dark skin tone
-1F477 1F3FF 200D 2640                      ; non-fully-qualified # 👷🏿‍♀ woman construction worker: dark skin tone
-1F473                                      ; fully-qualified     # 👳 person wearing turban
-1F473 1F3FB                                ; fully-qualified     # 👳🏻 person wearing turban: light skin tone
-1F473 1F3FC                                ; fully-qualified     # 👳🏼 person wearing turban: medium-light skin tone
-1F473 1F3FD                                ; fully-qualified     # 👳🏽 person wearing turban: medium skin tone
-1F473 1F3FE                                ; fully-qualified     # 👳🏾 person wearing turban: medium-dark skin tone
-1F473 1F3FF                                ; fully-qualified     # 👳🏿 person wearing turban: dark skin tone
-1F473 200D 2642 FE0F                       ; fully-qualified     # 👳‍♂️ man wearing turban
-1F473 200D 2642                            ; non-fully-qualified # 👳‍♂ man wearing turban
-1F473 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 👳🏻‍♂️ man wearing turban: light skin tone
-1F473 1F3FB 200D 2642                      ; non-fully-qualified # 👳🏻‍♂ man wearing turban: light skin tone
-1F473 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 👳🏼‍♂️ man wearing turban: medium-light skin tone
-1F473 1F3FC 200D 2642                      ; non-fully-qualified # 👳🏼‍♂ man wearing turban: medium-light skin tone
-1F473 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 👳🏽‍♂️ man wearing turban: medium skin tone
-1F473 1F3FD 200D 2642                      ; non-fully-qualified # 👳🏽‍♂ man wearing turban: medium skin tone
-1F473 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 👳🏾‍♂️ man wearing turban: medium-dark skin tone
-1F473 1F3FE 200D 2642                      ; non-fully-qualified # 👳🏾‍♂ man wearing turban: medium-dark skin tone
-1F473 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 👳🏿‍♂️ man wearing turban: dark skin tone
-1F473 1F3FF 200D 2642                      ; non-fully-qualified # 👳🏿‍♂ man wearing turban: dark skin tone
-1F473 200D 2640 FE0F                       ; fully-qualified     # 👳‍♀️ woman wearing turban
-1F473 200D 2640                            ; non-fully-qualified # 👳‍♀ woman wearing turban
-1F473 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 👳🏻‍♀️ woman wearing turban: light skin tone
-1F473 1F3FB 200D 2640                      ; non-fully-qualified # 👳🏻‍♀ woman wearing turban: light skin tone
-1F473 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 👳🏼‍♀️ woman wearing turban: medium-light skin tone
-1F473 1F3FC 200D 2640                      ; non-fully-qualified # 👳🏼‍♀ woman wearing turban: medium-light skin tone
-1F473 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 👳🏽‍♀️ woman wearing turban: medium skin tone
-1F473 1F3FD 200D 2640                      ; non-fully-qualified # 👳🏽‍♀ woman wearing turban: medium skin tone
-1F473 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 👳🏾‍♀️ woman wearing turban: medium-dark skin tone
-1F473 1F3FE 200D 2640                      ; non-fully-qualified # 👳🏾‍♀ woman wearing turban: medium-dark skin tone
-1F473 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 👳🏿‍♀️ woman wearing turban: dark skin tone
-1F473 1F3FF 200D 2640                      ; non-fully-qualified # 👳🏿‍♀ woman wearing turban: dark skin tone
-1F471                                      ; fully-qualified     # 👱 blond-haired person
-1F471 1F3FB                                ; fully-qualified     # 👱🏻 blond-haired person: light skin tone
-1F471 1F3FC                                ; fully-qualified     # 👱🏼 blond-haired person: medium-light skin tone
-1F471 1F3FD                                ; fully-qualified     # 👱🏽 blond-haired person: medium skin tone
-1F471 1F3FE                                ; fully-qualified     # 👱🏾 blond-haired person: medium-dark skin tone
-1F471 1F3FF                                ; fully-qualified     # 👱🏿 blond-haired person: dark skin tone
-1F471 200D 2642 FE0F                       ; fully-qualified     # 👱‍♂️ blond-haired man
-1F471 200D 2642                            ; non-fully-qualified # 👱‍♂ blond-haired man
-1F471 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 👱🏻‍♂️ blond-haired man: light skin tone
-1F471 1F3FB 200D 2642                      ; non-fully-qualified # 👱🏻‍♂ blond-haired man: light skin tone
-1F471 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 👱🏼‍♂️ blond-haired man: medium-light skin tone
-1F471 1F3FC 200D 2642                      ; non-fully-qualified # 👱🏼‍♂ blond-haired man: medium-light skin tone
-1F471 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 👱🏽‍♂️ blond-haired man: medium skin tone
-1F471 1F3FD 200D 2642                      ; non-fully-qualified # 👱🏽‍♂ blond-haired man: medium skin tone
-1F471 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 👱🏾‍♂️ blond-haired man: medium-dark skin tone
-1F471 1F3FE 200D 2642                      ; non-fully-qualified # 👱🏾‍♂ blond-haired man: medium-dark skin tone
-1F471 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 👱🏿‍♂️ blond-haired man: dark skin tone
-1F471 1F3FF 200D 2642                      ; non-fully-qualified # 👱🏿‍♂ blond-haired man: dark skin tone
-1F471 200D 2640 FE0F                       ; fully-qualified     # 👱‍♀️ blond-haired woman
-1F471 200D 2640                            ; non-fully-qualified # 👱‍♀ blond-haired woman
-1F471 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 👱🏻‍♀️ blond-haired woman: light skin tone
-1F471 1F3FB 200D 2640                      ; non-fully-qualified # 👱🏻‍♀ blond-haired woman: light skin tone
-1F471 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 👱🏼‍♀️ blond-haired woman: medium-light skin tone
-1F471 1F3FC 200D 2640                      ; non-fully-qualified # 👱🏼‍♀ blond-haired woman: medium-light skin tone
-1F471 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 👱🏽‍♀️ blond-haired woman: medium skin tone
-1F471 1F3FD 200D 2640                      ; non-fully-qualified # 👱🏽‍♀ blond-haired woman: medium skin tone
-1F471 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 👱🏾‍♀️ blond-haired woman: medium-dark skin tone
-1F471 1F3FE 200D 2640                      ; non-fully-qualified # 👱🏾‍♀ blond-haired woman: medium-dark skin tone
-1F471 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 👱🏿‍♀️ blond-haired woman: dark skin tone
-1F471 1F3FF 200D 2640                      ; non-fully-qualified # 👱🏿‍♀ blond-haired woman: dark skin tone
-1F385                                      ; fully-qualified     # 🎅 Santa Claus
-1F385 1F3FB                                ; fully-qualified     # 🎅🏻 Santa Claus: light skin tone
-1F385 1F3FC                                ; fully-qualified     # 🎅🏼 Santa Claus: medium-light skin tone
-1F385 1F3FD                                ; fully-qualified     # 🎅🏽 Santa Claus: medium skin tone
-1F385 1F3FE                                ; fully-qualified     # 🎅🏾 Santa Claus: medium-dark skin tone
-1F385 1F3FF                                ; fully-qualified     # 🎅🏿 Santa Claus: dark skin tone
-1F936                                      ; fully-qualified     # 🤶 Mrs. Claus
-1F936 1F3FB                                ; fully-qualified     # 🤶🏻 Mrs. Claus: light skin tone
-1F936 1F3FC                                ; fully-qualified     # 🤶🏼 Mrs. Claus: medium-light skin tone
-1F936 1F3FD                                ; fully-qualified     # 🤶🏽 Mrs. Claus: medium skin tone
-1F936 1F3FE                                ; fully-qualified     # 🤶🏾 Mrs. Claus: medium-dark skin tone
-1F936 1F3FF                                ; fully-qualified     # 🤶🏿 Mrs. Claus: dark skin tone
-1F478                                      ; fully-qualified     # 👸 princess
-1F478 1F3FB                                ; fully-qualified     # 👸🏻 princess: light skin tone
-1F478 1F3FC                                ; fully-qualified     # 👸🏼 princess: medium-light skin tone
-1F478 1F3FD                                ; fully-qualified     # 👸🏽 princess: medium skin tone
-1F478 1F3FE                                ; fully-qualified     # 👸🏾 princess: medium-dark skin tone
-1F478 1F3FF                                ; fully-qualified     # 👸🏿 princess: dark skin tone
-1F934                                      ; fully-qualified     # 🤴 prince
-1F934 1F3FB                                ; fully-qualified     # 🤴🏻 prince: light skin tone
-1F934 1F3FC                                ; fully-qualified     # 🤴🏼 prince: medium-light skin tone
-1F934 1F3FD                                ; fully-qualified     # 🤴🏽 prince: medium skin tone
-1F934 1F3FE                                ; fully-qualified     # 🤴🏾 prince: medium-dark skin tone
-1F934 1F3FF                                ; fully-qualified     # 🤴🏿 prince: dark skin tone
-1F470                                      ; fully-qualified     # 👰 bride with veil
-1F470 1F3FB                                ; fully-qualified     # 👰🏻 bride with veil: light skin tone
-1F470 1F3FC                                ; fully-qualified     # 👰🏼 bride with veil: medium-light skin tone
-1F470 1F3FD                                ; fully-qualified     # 👰🏽 bride with veil: medium skin tone
-1F470 1F3FE                                ; fully-qualified     # 👰🏾 bride with veil: medium-dark skin tone
-1F470 1F3FF                                ; fully-qualified     # 👰🏿 bride with veil: dark skin tone
-1F935                                      ; fully-qualified     # 🤵 man in tuxedo
-1F935 1F3FB                                ; fully-qualified     # 🤵🏻 man in tuxedo: light skin tone
-1F935 1F3FC                                ; fully-qualified     # 🤵🏼 man in tuxedo: medium-light skin tone
-1F935 1F3FD                                ; fully-qualified     # 🤵🏽 man in tuxedo: medium skin tone
-1F935 1F3FE                                ; fully-qualified     # 🤵🏾 man in tuxedo: medium-dark skin tone
-1F935 1F3FF                                ; fully-qualified     # 🤵🏿 man in tuxedo: dark skin tone
-1F930                                      ; fully-qualified     # 🤰 pregnant woman
-1F930 1F3FB                                ; fully-qualified     # 🤰🏻 pregnant woman: light skin tone
-1F930 1F3FC                                ; fully-qualified     # 🤰🏼 pregnant woman: medium-light skin tone
-1F930 1F3FD                                ; fully-qualified     # 🤰🏽 pregnant woman: medium skin tone
-1F930 1F3FE                                ; fully-qualified     # 🤰🏾 pregnant woman: medium-dark skin tone
-1F930 1F3FF                                ; fully-qualified     # 🤰🏿 pregnant woman: dark skin tone
-1F472                                      ; fully-qualified     # 👲 man with Chinese cap
-1F472 1F3FB                                ; fully-qualified     # 👲🏻 man with Chinese cap: light skin tone
-1F472 1F3FC                                ; fully-qualified     # 👲🏼 man with Chinese cap: medium-light skin tone
-1F472 1F3FD                                ; fully-qualified     # 👲🏽 man with Chinese cap: medium skin tone
-1F472 1F3FE                                ; fully-qualified     # 👲🏾 man with Chinese cap: medium-dark skin tone
-1F472 1F3FF                                ; fully-qualified     # 👲🏿 man with Chinese cap: dark skin tone
-
-# subgroup: person-gesture
-1F64D                                      ; fully-qualified     # 🙍 person frowning
-1F64D 1F3FB                                ; fully-qualified     # 🙍🏻 person frowning: light skin tone
-1F64D 1F3FC                                ; fully-qualified     # 🙍🏼 person frowning: medium-light skin tone
-1F64D 1F3FD                                ; fully-qualified     # 🙍🏽 person frowning: medium skin tone
-1F64D 1F3FE                                ; fully-qualified     # 🙍🏾 person frowning: medium-dark skin tone
-1F64D 1F3FF                                ; fully-qualified     # 🙍🏿 person frowning: dark skin tone
-1F64D 200D 2642 FE0F                       ; fully-qualified     # 🙍‍♂️ man frowning
-1F64D 200D 2642                            ; non-fully-qualified # 🙍‍♂ man frowning
-1F64D 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🙍🏻‍♂️ man frowning: light skin tone
-1F64D 1F3FB 200D 2642                      ; non-fully-qualified # 🙍🏻‍♂ man frowning: light skin tone
-1F64D 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🙍🏼‍♂️ man frowning: medium-light skin tone
-1F64D 1F3FC 200D 2642                      ; non-fully-qualified # 🙍🏼‍♂ man frowning: medium-light skin tone
-1F64D 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🙍🏽‍♂️ man frowning: medium skin tone
-1F64D 1F3FD 200D 2642                      ; non-fully-qualified # 🙍🏽‍♂ man frowning: medium skin tone
-1F64D 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🙍🏾‍♂️ man frowning: medium-dark skin tone
-1F64D 1F3FE 200D 2642                      ; non-fully-qualified # 🙍🏾‍♂ man frowning: medium-dark skin tone
-1F64D 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🙍🏿‍♂️ man frowning: dark skin tone
-1F64D 1F3FF 200D 2642                      ; non-fully-qualified # 🙍🏿‍♂ man frowning: dark skin tone
-1F64D 200D 2640 FE0F                       ; fully-qualified     # 🙍‍♀️ woman frowning
-1F64D 200D 2640                            ; non-fully-qualified # 🙍‍♀ woman frowning
-1F64D 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🙍🏻‍♀️ woman frowning: light skin tone
-1F64D 1F3FB 200D 2640                      ; non-fully-qualified # 🙍🏻‍♀ woman frowning: light skin tone
-1F64D 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🙍🏼‍♀️ woman frowning: medium-light skin tone
-1F64D 1F3FC 200D 2640                      ; non-fully-qualified # 🙍🏼‍♀ woman frowning: medium-light skin tone
-1F64D 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🙍🏽‍♀️ woman frowning: medium skin tone
-1F64D 1F3FD 200D 2640                      ; non-fully-qualified # 🙍🏽‍♀ woman frowning: medium skin tone
-1F64D 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🙍🏾‍♀️ woman frowning: medium-dark skin tone
-1F64D 1F3FE 200D 2640                      ; non-fully-qualified # 🙍🏾‍♀ woman frowning: medium-dark skin tone
-1F64D 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🙍🏿‍♀️ woman frowning: dark skin tone
-1F64D 1F3FF 200D 2640                      ; non-fully-qualified # 🙍🏿‍♀ woman frowning: dark skin tone
-1F64E                                      ; fully-qualified     # 🙎 person pouting
-1F64E 1F3FB                                ; fully-qualified     # 🙎🏻 person pouting: light skin tone
-1F64E 1F3FC                                ; fully-qualified     # 🙎🏼 person pouting: medium-light skin tone
-1F64E 1F3FD                                ; fully-qualified     # 🙎🏽 person pouting: medium skin tone
-1F64E 1F3FE                                ; fully-qualified     # 🙎🏾 person pouting: medium-dark skin tone
-1F64E 1F3FF                                ; fully-qualified     # 🙎🏿 person pouting: dark skin tone
-1F64E 200D 2642 FE0F                       ; fully-qualified     # 🙎‍♂️ man pouting
-1F64E 200D 2642                            ; non-fully-qualified # 🙎‍♂ man pouting
-1F64E 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🙎🏻‍♂️ man pouting: light skin tone
-1F64E 1F3FB 200D 2642                      ; non-fully-qualified # 🙎🏻‍♂ man pouting: light skin tone
-1F64E 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🙎🏼‍♂️ man pouting: medium-light skin tone
-1F64E 1F3FC 200D 2642                      ; non-fully-qualified # 🙎🏼‍♂ man pouting: medium-light skin tone
-1F64E 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🙎🏽‍♂️ man pouting: medium skin tone
-1F64E 1F3FD 200D 2642                      ; non-fully-qualified # 🙎🏽‍♂ man pouting: medium skin tone
-1F64E 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🙎🏾‍♂️ man pouting: medium-dark skin tone
-1F64E 1F3FE 200D 2642                      ; non-fully-qualified # 🙎🏾‍♂ man pouting: medium-dark skin tone
-1F64E 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🙎🏿‍♂️ man pouting: dark skin tone
-1F64E 1F3FF 200D 2642                      ; non-fully-qualified # 🙎🏿‍♂ man pouting: dark skin tone
-1F64E 200D 2640 FE0F                       ; fully-qualified     # 🙎‍♀️ woman pouting
-1F64E 200D 2640                            ; non-fully-qualified # 🙎‍♀ woman pouting
-1F64E 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🙎🏻‍♀️ woman pouting: light skin tone
-1F64E 1F3FB 200D 2640                      ; non-fully-qualified # 🙎🏻‍♀ woman pouting: light skin tone
-1F64E 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🙎🏼‍♀️ woman pouting: medium-light skin tone
-1F64E 1F3FC 200D 2640                      ; non-fully-qualified # 🙎🏼‍♀ woman pouting: medium-light skin tone
-1F64E 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🙎🏽‍♀️ woman pouting: medium skin tone
-1F64E 1F3FD 200D 2640                      ; non-fully-qualified # 🙎🏽‍♀ woman pouting: medium skin tone
-1F64E 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🙎🏾‍♀️ woman pouting: medium-dark skin tone
-1F64E 1F3FE 200D 2640                      ; non-fully-qualified # 🙎🏾‍♀ woman pouting: medium-dark skin tone
-1F64E 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🙎🏿‍♀️ woman pouting: dark skin tone
-1F64E 1F3FF 200D 2640                      ; non-fully-qualified # 🙎🏿‍♀ woman pouting: dark skin tone
-1F645                                      ; fully-qualified     # 🙅 person gesturing NO
-1F645 1F3FB                                ; fully-qualified     # 🙅🏻 person gesturing NO: light skin tone
-1F645 1F3FC                                ; fully-qualified     # 🙅🏼 person gesturing NO: medium-light skin tone
-1F645 1F3FD                                ; fully-qualified     # 🙅🏽 person gesturing NO: medium skin tone
-1F645 1F3FE                                ; fully-qualified     # 🙅🏾 person gesturing NO: medium-dark skin tone
-1F645 1F3FF                                ; fully-qualified     # 🙅🏿 person gesturing NO: dark skin tone
-1F645 200D 2642 FE0F                       ; fully-qualified     # 🙅‍♂️ man gesturing NO
-1F645 200D 2642                            ; non-fully-qualified # 🙅‍♂ man gesturing NO
-1F645 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🙅🏻‍♂️ man gesturing NO: light skin tone
-1F645 1F3FB 200D 2642                      ; non-fully-qualified # 🙅🏻‍♂ man gesturing NO: light skin tone
-1F645 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🙅🏼‍♂️ man gesturing NO: medium-light skin tone
-1F645 1F3FC 200D 2642                      ; non-fully-qualified # 🙅🏼‍♂ man gesturing NO: medium-light skin tone
-1F645 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🙅🏽‍♂️ man gesturing NO: medium skin tone
-1F645 1F3FD 200D 2642                      ; non-fully-qualified # 🙅🏽‍♂ man gesturing NO: medium skin tone
-1F645 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🙅🏾‍♂️ man gesturing NO: medium-dark skin tone
-1F645 1F3FE 200D 2642                      ; non-fully-qualified # 🙅🏾‍♂ man gesturing NO: medium-dark skin tone
-1F645 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🙅🏿‍♂️ man gesturing NO: dark skin tone
-1F645 1F3FF 200D 2642                      ; non-fully-qualified # 🙅🏿‍♂ man gesturing NO: dark skin tone
-1F645 200D 2640 FE0F                       ; fully-qualified     # 🙅‍♀️ woman gesturing NO
-1F645 200D 2640                            ; non-fully-qualified # 🙅‍♀ woman gesturing NO
-1F645 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🙅🏻‍♀️ woman gesturing NO: light skin tone
-1F645 1F3FB 200D 2640                      ; non-fully-qualified # 🙅🏻‍♀ woman gesturing NO: light skin tone
-1F645 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🙅🏼‍♀️ woman gesturing NO: medium-light skin tone
-1F645 1F3FC 200D 2640                      ; non-fully-qualified # 🙅🏼‍♀ woman gesturing NO: medium-light skin tone
-1F645 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🙅🏽‍♀️ woman gesturing NO: medium skin tone
-1F645 1F3FD 200D 2640                      ; non-fully-qualified # 🙅🏽‍♀ woman gesturing NO: medium skin tone
-1F645 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🙅🏾‍♀️ woman gesturing NO: medium-dark skin tone
-1F645 1F3FE 200D 2640                      ; non-fully-qualified # 🙅🏾‍♀ woman gesturing NO: medium-dark skin tone
-1F645 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🙅🏿‍♀️ woman gesturing NO: dark skin tone
-1F645 1F3FF 200D 2640                      ; non-fully-qualified # 🙅🏿‍♀ woman gesturing NO: dark skin tone
-1F646                                      ; fully-qualified     # 🙆 person gesturing OK
-1F646 1F3FB                                ; fully-qualified     # 🙆🏻 person gesturing OK: light skin tone
-1F646 1F3FC                                ; fully-qualified     # 🙆🏼 person gesturing OK: medium-light skin tone
-1F646 1F3FD                                ; fully-qualified     # 🙆🏽 person gesturing OK: medium skin tone
-1F646 1F3FE                                ; fully-qualified     # 🙆🏾 person gesturing OK: medium-dark skin tone
-1F646 1F3FF                                ; fully-qualified     # 🙆🏿 person gesturing OK: dark skin tone
-1F646 200D 2642 FE0F                       ; fully-qualified     # 🙆‍♂️ man gesturing OK
-1F646 200D 2642                            ; non-fully-qualified # 🙆‍♂ man gesturing OK
-1F646 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🙆🏻‍♂️ man gesturing OK: light skin tone
-1F646 1F3FB 200D 2642                      ; non-fully-qualified # 🙆🏻‍♂ man gesturing OK: light skin tone
-1F646 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🙆🏼‍♂️ man gesturing OK: medium-light skin tone
-1F646 1F3FC 200D 2642                      ; non-fully-qualified # 🙆🏼‍♂ man gesturing OK: medium-light skin tone
-1F646 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🙆🏽‍♂️ man gesturing OK: medium skin tone
-1F646 1F3FD 200D 2642                      ; non-fully-qualified # 🙆🏽‍♂ man gesturing OK: medium skin tone
-1F646 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🙆🏾‍♂️ man gesturing OK: medium-dark skin tone
-1F646 1F3FE 200D 2642                      ; non-fully-qualified # 🙆🏾‍♂ man gesturing OK: medium-dark skin tone
-1F646 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🙆🏿‍♂️ man gesturing OK: dark skin tone
-1F646 1F3FF 200D 2642                      ; non-fully-qualified # 🙆🏿‍♂ man gesturing OK: dark skin tone
-1F646 200D 2640 FE0F                       ; fully-qualified     # 🙆‍♀️ woman gesturing OK
-1F646 200D 2640                            ; non-fully-qualified # 🙆‍♀ woman gesturing OK
-1F646 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🙆🏻‍♀️ woman gesturing OK: light skin tone
-1F646 1F3FB 200D 2640                      ; non-fully-qualified # 🙆🏻‍♀ woman gesturing OK: light skin tone
-1F646 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🙆🏼‍♀️ woman gesturing OK: medium-light skin tone
-1F646 1F3FC 200D 2640                      ; non-fully-qualified # 🙆🏼‍♀ woman gesturing OK: medium-light skin tone
-1F646 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🙆🏽‍♀️ woman gesturing OK: medium skin tone
-1F646 1F3FD 200D 2640                      ; non-fully-qualified # 🙆🏽‍♀ woman gesturing OK: medium skin tone
-1F646 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🙆🏾‍♀️ woman gesturing OK: medium-dark skin tone
-1F646 1F3FE 200D 2640                      ; non-fully-qualified # 🙆🏾‍♀ woman gesturing OK: medium-dark skin tone
-1F646 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🙆🏿‍♀️ woman gesturing OK: dark skin tone
-1F646 1F3FF 200D 2640                      ; non-fully-qualified # 🙆🏿‍♀ woman gesturing OK: dark skin tone
-1F481                                      ; fully-qualified     # 💁 person tipping hand
-1F481 1F3FB                                ; fully-qualified     # 💁🏻 person tipping hand: light skin tone
-1F481 1F3FC                                ; fully-qualified     # 💁🏼 person tipping hand: medium-light skin tone
-1F481 1F3FD                                ; fully-qualified     # 💁🏽 person tipping hand: medium skin tone
-1F481 1F3FE                                ; fully-qualified     # 💁🏾 person tipping hand: medium-dark skin tone
-1F481 1F3FF                                ; fully-qualified     # 💁🏿 person tipping hand: dark skin tone
-1F481 200D 2642 FE0F                       ; fully-qualified     # 💁‍♂️ man tipping hand
-1F481 200D 2642                            ; non-fully-qualified # 💁‍♂ man tipping hand
-1F481 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 💁🏻‍♂️ man tipping hand: light skin tone
-1F481 1F3FB 200D 2642                      ; non-fully-qualified # 💁🏻‍♂ man tipping hand: light skin tone
-1F481 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 💁🏼‍♂️ man tipping hand: medium-light skin tone
-1F481 1F3FC 200D 2642                      ; non-fully-qualified # 💁🏼‍♂ man tipping hand: medium-light skin tone
-1F481 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 💁🏽‍♂️ man tipping hand: medium skin tone
-1F481 1F3FD 200D 2642                      ; non-fully-qualified # 💁🏽‍♂ man tipping hand: medium skin tone
-1F481 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 💁🏾‍♂️ man tipping hand: medium-dark skin tone
-1F481 1F3FE 200D 2642                      ; non-fully-qualified # 💁🏾‍♂ man tipping hand: medium-dark skin tone
-1F481 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 💁🏿‍♂️ man tipping hand: dark skin tone
-1F481 1F3FF 200D 2642                      ; non-fully-qualified # 💁🏿‍♂ man tipping hand: dark skin tone
-1F481 200D 2640 FE0F                       ; fully-qualified     # 💁‍♀️ woman tipping hand
-1F481 200D 2640                            ; non-fully-qualified # 💁‍♀ woman tipping hand
-1F481 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 💁🏻‍♀️ woman tipping hand: light skin tone
-1F481 1F3FB 200D 2640                      ; non-fully-qualified # 💁🏻‍♀ woman tipping hand: light skin tone
-1F481 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 💁🏼‍♀️ woman tipping hand: medium-light skin tone
-1F481 1F3FC 200D 2640                      ; non-fully-qualified # 💁🏼‍♀ woman tipping hand: medium-light skin tone
-1F481 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 💁🏽‍♀️ woman tipping hand: medium skin tone
-1F481 1F3FD 200D 2640                      ; non-fully-qualified # 💁🏽‍♀ woman tipping hand: medium skin tone
-1F481 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 💁🏾‍♀️ woman tipping hand: medium-dark skin tone
-1F481 1F3FE 200D 2640                      ; non-fully-qualified # 💁🏾‍♀ woman tipping hand: medium-dark skin tone
-1F481 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 💁🏿‍♀️ woman tipping hand: dark skin tone
-1F481 1F3FF 200D 2640                      ; non-fully-qualified # 💁🏿‍♀ woman tipping hand: dark skin tone
-1F64B                                      ; fully-qualified     # 🙋 person raising hand
-1F64B 1F3FB                                ; fully-qualified     # 🙋🏻 person raising hand: light skin tone
-1F64B 1F3FC                                ; fully-qualified     # 🙋🏼 person raising hand: medium-light skin tone
-1F64B 1F3FD                                ; fully-qualified     # 🙋🏽 person raising hand: medium skin tone
-1F64B 1F3FE                                ; fully-qualified     # 🙋🏾 person raising hand: medium-dark skin tone
-1F64B 1F3FF                                ; fully-qualified     # 🙋🏿 person raising hand: dark skin tone
-1F64B 200D 2642 FE0F                       ; fully-qualified     # 🙋‍♂️ man raising hand
-1F64B 200D 2642                            ; non-fully-qualified # 🙋‍♂ man raising hand
-1F64B 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🙋🏻‍♂️ man raising hand: light skin tone
-1F64B 1F3FB 200D 2642                      ; non-fully-qualified # 🙋🏻‍♂ man raising hand: light skin tone
-1F64B 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🙋🏼‍♂️ man raising hand: medium-light skin tone
-1F64B 1F3FC 200D 2642                      ; non-fully-qualified # 🙋🏼‍♂ man raising hand: medium-light skin tone
-1F64B 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🙋🏽‍♂️ man raising hand: medium skin tone
-1F64B 1F3FD 200D 2642                      ; non-fully-qualified # 🙋🏽‍♂ man raising hand: medium skin tone
-1F64B 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🙋🏾‍♂️ man raising hand: medium-dark skin tone
-1F64B 1F3FE 200D 2642                      ; non-fully-qualified # 🙋🏾‍♂ man raising hand: medium-dark skin tone
-1F64B 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🙋🏿‍♂️ man raising hand: dark skin tone
-1F64B 1F3FF 200D 2642                      ; non-fully-qualified # 🙋🏿‍♂ man raising hand: dark skin tone
-1F64B 200D 2640 FE0F                       ; fully-qualified     # 🙋‍♀️ woman raising hand
-1F64B 200D 2640                            ; non-fully-qualified # 🙋‍♀ woman raising hand
-1F64B 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🙋🏻‍♀️ woman raising hand: light skin tone
-1F64B 1F3FB 200D 2640                      ; non-fully-qualified # 🙋🏻‍♀ woman raising hand: light skin tone
-1F64B 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🙋🏼‍♀️ woman raising hand: medium-light skin tone
-1F64B 1F3FC 200D 2640                      ; non-fully-qualified # 🙋🏼‍♀ woman raising hand: medium-light skin tone
-1F64B 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🙋🏽‍♀️ woman raising hand: medium skin tone
-1F64B 1F3FD 200D 2640                      ; non-fully-qualified # 🙋🏽‍♀ woman raising hand: medium skin tone
-1F64B 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🙋🏾‍♀️ woman raising hand: medium-dark skin tone
-1F64B 1F3FE 200D 2640                      ; non-fully-qualified # 🙋🏾‍♀ woman raising hand: medium-dark skin tone
-1F64B 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🙋🏿‍♀️ woman raising hand: dark skin tone
-1F64B 1F3FF 200D 2640                      ; non-fully-qualified # 🙋🏿‍♀ woman raising hand: dark skin tone
-1F647                                      ; fully-qualified     # 🙇 person bowing
-1F647 1F3FB                                ; fully-qualified     # 🙇🏻 person bowing: light skin tone
-1F647 1F3FC                                ; fully-qualified     # 🙇🏼 person bowing: medium-light skin tone
-1F647 1F3FD                                ; fully-qualified     # 🙇🏽 person bowing: medium skin tone
-1F647 1F3FE                                ; fully-qualified     # 🙇🏾 person bowing: medium-dark skin tone
-1F647 1F3FF                                ; fully-qualified     # 🙇🏿 person bowing: dark skin tone
-1F647 200D 2642 FE0F                       ; fully-qualified     # 🙇‍♂️ man bowing
-1F647 200D 2642                            ; non-fully-qualified # 🙇‍♂ man bowing
-1F647 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🙇🏻‍♂️ man bowing: light skin tone
-1F647 1F3FB 200D 2642                      ; non-fully-qualified # 🙇🏻‍♂ man bowing: light skin tone
-1F647 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🙇🏼‍♂️ man bowing: medium-light skin tone
-1F647 1F3FC 200D 2642                      ; non-fully-qualified # 🙇🏼‍♂ man bowing: medium-light skin tone
-1F647 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🙇🏽‍♂️ man bowing: medium skin tone
-1F647 1F3FD 200D 2642                      ; non-fully-qualified # 🙇🏽‍♂ man bowing: medium skin tone
-1F647 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🙇🏾‍♂️ man bowing: medium-dark skin tone
-1F647 1F3FE 200D 2642                      ; non-fully-qualified # 🙇🏾‍♂ man bowing: medium-dark skin tone
-1F647 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🙇🏿‍♂️ man bowing: dark skin tone
-1F647 1F3FF 200D 2642                      ; non-fully-qualified # 🙇🏿‍♂ man bowing: dark skin tone
-1F647 200D 2640 FE0F                       ; fully-qualified     # 🙇‍♀️ woman bowing
-1F647 200D 2640                            ; non-fully-qualified # 🙇‍♀ woman bowing
-1F647 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🙇🏻‍♀️ woman bowing: light skin tone
-1F647 1F3FB 200D 2640                      ; non-fully-qualified # 🙇🏻‍♀ woman bowing: light skin tone
-1F647 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🙇🏼‍♀️ woman bowing: medium-light skin tone
-1F647 1F3FC 200D 2640                      ; non-fully-qualified # 🙇🏼‍♀ woman bowing: medium-light skin tone
-1F647 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🙇🏽‍♀️ woman bowing: medium skin tone
-1F647 1F3FD 200D 2640                      ; non-fully-qualified # 🙇🏽‍♀ woman bowing: medium skin tone
-1F647 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🙇🏾‍♀️ woman bowing: medium-dark skin tone
-1F647 1F3FE 200D 2640                      ; non-fully-qualified # 🙇🏾‍♀ woman bowing: medium-dark skin tone
-1F647 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🙇🏿‍♀️ woman bowing: dark skin tone
-1F647 1F3FF 200D 2640                      ; non-fully-qualified # 🙇🏿‍♀ woman bowing: dark skin tone
-1F926                                      ; fully-qualified     # 🤦 person facepalming
-1F926 1F3FB                                ; fully-qualified     # 🤦🏻 person facepalming: light skin tone
-1F926 1F3FC                                ; fully-qualified     # 🤦🏼 person facepalming: medium-light skin tone
-1F926 1F3FD                                ; fully-qualified     # 🤦🏽 person facepalming: medium skin tone
-1F926 1F3FE                                ; fully-qualified     # 🤦🏾 person facepalming: medium-dark skin tone
-1F926 1F3FF                                ; fully-qualified     # 🤦🏿 person facepalming: dark skin tone
-1F926 200D 2642 FE0F                       ; fully-qualified     # 🤦‍♂️ man facepalming
-1F926 200D 2642                            ; non-fully-qualified # 🤦‍♂ man facepalming
-1F926 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🤦🏻‍♂️ man facepalming: light skin tone
-1F926 1F3FB 200D 2642                      ; non-fully-qualified # 🤦🏻‍♂ man facepalming: light skin tone
-1F926 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🤦🏼‍♂️ man facepalming: medium-light skin tone
-1F926 1F3FC 200D 2642                      ; non-fully-qualified # 🤦🏼‍♂ man facepalming: medium-light skin tone
-1F926 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🤦🏽‍♂️ man facepalming: medium skin tone
-1F926 1F3FD 200D 2642                      ; non-fully-qualified # 🤦🏽‍♂ man facepalming: medium skin tone
-1F926 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🤦🏾‍♂️ man facepalming: medium-dark skin tone
-1F926 1F3FE 200D 2642                      ; non-fully-qualified # 🤦🏾‍♂ man facepalming: medium-dark skin tone
-1F926 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🤦🏿‍♂️ man facepalming: dark skin tone
-1F926 1F3FF 200D 2642                      ; non-fully-qualified # 🤦🏿‍♂ man facepalming: dark skin tone
-1F926 200D 2640 FE0F                       ; fully-qualified     # 🤦‍♀️ woman facepalming
-1F926 200D 2640                            ; non-fully-qualified # 🤦‍♀ woman facepalming
-1F926 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🤦🏻‍♀️ woman facepalming: light skin tone
-1F926 1F3FB 200D 2640                      ; non-fully-qualified # 🤦🏻‍♀ woman facepalming: light skin tone
-1F926 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🤦🏼‍♀️ woman facepalming: medium-light skin tone
-1F926 1F3FC 200D 2640                      ; non-fully-qualified # 🤦🏼‍♀ woman facepalming: medium-light skin tone
-1F926 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🤦🏽‍♀️ woman facepalming: medium skin tone
-1F926 1F3FD 200D 2640                      ; non-fully-qualified # 🤦🏽‍♀ woman facepalming: medium skin tone
-1F926 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🤦🏾‍♀️ woman facepalming: medium-dark skin tone
-1F926 1F3FE 200D 2640                      ; non-fully-qualified # 🤦🏾‍♀ woman facepalming: medium-dark skin tone
-1F926 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🤦🏿‍♀️ woman facepalming: dark skin tone
-1F926 1F3FF 200D 2640                      ; non-fully-qualified # 🤦🏿‍♀ woman facepalming: dark skin tone
-1F937                                      ; fully-qualified     # 🤷 person shrugging
-1F937 1F3FB                                ; fully-qualified     # 🤷🏻 person shrugging: light skin tone
-1F937 1F3FC                                ; fully-qualified     # 🤷🏼 person shrugging: medium-light skin tone
-1F937 1F3FD                                ; fully-qualified     # 🤷🏽 person shrugging: medium skin tone
-1F937 1F3FE                                ; fully-qualified     # 🤷🏾 person shrugging: medium-dark skin tone
-1F937 1F3FF                                ; fully-qualified     # 🤷🏿 person shrugging: dark skin tone
-1F937 200D 2642 FE0F                       ; fully-qualified     # 🤷‍♂️ man shrugging
-1F937 200D 2642                            ; non-fully-qualified # 🤷‍♂ man shrugging
-1F937 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🤷🏻‍♂️ man shrugging: light skin tone
-1F937 1F3FB 200D 2642                      ; non-fully-qualified # 🤷🏻‍♂ man shrugging: light skin tone
-1F937 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🤷🏼‍♂️ man shrugging: medium-light skin tone
-1F937 1F3FC 200D 2642                      ; non-fully-qualified # 🤷🏼‍♂ man shrugging: medium-light skin tone
-1F937 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🤷🏽‍♂️ man shrugging: medium skin tone
-1F937 1F3FD 200D 2642                      ; non-fully-qualified # 🤷🏽‍♂ man shrugging: medium skin tone
-1F937 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🤷🏾‍♂️ man shrugging: medium-dark skin tone
-1F937 1F3FE 200D 2642                      ; non-fully-qualified # 🤷🏾‍♂ man shrugging: medium-dark skin tone
-1F937 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🤷🏿‍♂️ man shrugging: dark skin tone
-1F937 1F3FF 200D 2642                      ; non-fully-qualified # 🤷🏿‍♂ man shrugging: dark skin tone
-1F937 200D 2640 FE0F                       ; fully-qualified     # 🤷‍♀️ woman shrugging
-1F937 200D 2640                            ; non-fully-qualified # 🤷‍♀ woman shrugging
-1F937 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🤷🏻‍♀️ woman shrugging: light skin tone
-1F937 1F3FB 200D 2640                      ; non-fully-qualified # 🤷🏻‍♀ woman shrugging: light skin tone
-1F937 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🤷🏼‍♀️ woman shrugging: medium-light skin tone
-1F937 1F3FC 200D 2640                      ; non-fully-qualified # 🤷🏼‍♀ woman shrugging: medium-light skin tone
-1F937 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🤷🏽‍♀️ woman shrugging: medium skin tone
-1F937 1F3FD 200D 2640                      ; non-fully-qualified # 🤷🏽‍♀ woman shrugging: medium skin tone
-1F937 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🤷🏾‍♀️ woman shrugging: medium-dark skin tone
-1F937 1F3FE 200D 2640                      ; non-fully-qualified # 🤷🏾‍♀ woman shrugging: medium-dark skin tone
-1F937 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🤷🏿‍♀️ woman shrugging: dark skin tone
-1F937 1F3FF 200D 2640                      ; non-fully-qualified # 🤷🏿‍♀ woman shrugging: dark skin tone
-
-# subgroup: person-activity
-1F486                                      ; fully-qualified     # 💆 person getting massage
-1F486 1F3FB                                ; fully-qualified     # 💆🏻 person getting massage: light skin tone
-1F486 1F3FC                                ; fully-qualified     # 💆🏼 person getting massage: medium-light skin tone
-1F486 1F3FD                                ; fully-qualified     # 💆🏽 person getting massage: medium skin tone
-1F486 1F3FE                                ; fully-qualified     # 💆🏾 person getting massage: medium-dark skin tone
-1F486 1F3FF                                ; fully-qualified     # 💆🏿 person getting massage: dark skin tone
-1F486 200D 2642 FE0F                       ; fully-qualified     # 💆‍♂️ man getting massage
-1F486 200D 2642                            ; non-fully-qualified # 💆‍♂ man getting massage
-1F486 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 💆🏻‍♂️ man getting massage: light skin tone
-1F486 1F3FB 200D 2642                      ; non-fully-qualified # 💆🏻‍♂ man getting massage: light skin tone
-1F486 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 💆🏼‍♂️ man getting massage: medium-light skin tone
-1F486 1F3FC 200D 2642                      ; non-fully-qualified # 💆🏼‍♂ man getting massage: medium-light skin tone
-1F486 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 💆🏽‍♂️ man getting massage: medium skin tone
-1F486 1F3FD 200D 2642                      ; non-fully-qualified # 💆🏽‍♂ man getting massage: medium skin tone
-1F486 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 💆🏾‍♂️ man getting massage: medium-dark skin tone
-1F486 1F3FE 200D 2642                      ; non-fully-qualified # 💆🏾‍♂ man getting massage: medium-dark skin tone
-1F486 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 💆🏿‍♂️ man getting massage: dark skin tone
-1F486 1F3FF 200D 2642                      ; non-fully-qualified # 💆🏿‍♂ man getting massage: dark skin tone
-1F486 200D 2640 FE0F                       ; fully-qualified     # 💆‍♀️ woman getting massage
-1F486 200D 2640                            ; non-fully-qualified # 💆‍♀ woman getting massage
-1F486 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 💆🏻‍♀️ woman getting massage: light skin tone
-1F486 1F3FB 200D 2640                      ; non-fully-qualified # 💆🏻‍♀ woman getting massage: light skin tone
-1F486 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 💆🏼‍♀️ woman getting massage: medium-light skin tone
-1F486 1F3FC 200D 2640                      ; non-fully-qualified # 💆🏼‍♀ woman getting massage: medium-light skin tone
-1F486 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 💆🏽‍♀️ woman getting massage: medium skin tone
-1F486 1F3FD 200D 2640                      ; non-fully-qualified # 💆🏽‍♀ woman getting massage: medium skin tone
-1F486 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 💆🏾‍♀️ woman getting massage: medium-dark skin tone
-1F486 1F3FE 200D 2640                      ; non-fully-qualified # 💆🏾‍♀ woman getting massage: medium-dark skin tone
-1F486 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 💆🏿‍♀️ woman getting massage: dark skin tone
-1F486 1F3FF 200D 2640                      ; non-fully-qualified # 💆🏿‍♀ woman getting massage: dark skin tone
-1F487                                      ; fully-qualified     # 💇 person getting haircut
-1F487 1F3FB                                ; fully-qualified     # 💇🏻 person getting haircut: light skin tone
-1F487 1F3FC                                ; fully-qualified     # 💇🏼 person getting haircut: medium-light skin tone
-1F487 1F3FD                                ; fully-qualified     # 💇🏽 person getting haircut: medium skin tone
-1F487 1F3FE                                ; fully-qualified     # 💇🏾 person getting haircut: medium-dark skin tone
-1F487 1F3FF                                ; fully-qualified     # 💇🏿 person getting haircut: dark skin tone
-1F487 200D 2642 FE0F                       ; fully-qualified     # 💇‍♂️ man getting haircut
-1F487 200D 2642                            ; non-fully-qualified # 💇‍♂ man getting haircut
-1F487 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 💇🏻‍♂️ man getting haircut: light skin tone
-1F487 1F3FB 200D 2642                      ; non-fully-qualified # 💇🏻‍♂ man getting haircut: light skin tone
-1F487 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 💇🏼‍♂️ man getting haircut: medium-light skin tone
-1F487 1F3FC 200D 2642                      ; non-fully-qualified # 💇🏼‍♂ man getting haircut: medium-light skin tone
-1F487 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 💇🏽‍♂️ man getting haircut: medium skin tone
-1F487 1F3FD 200D 2642                      ; non-fully-qualified # 💇🏽‍♂ man getting haircut: medium skin tone
-1F487 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 💇🏾‍♂️ man getting haircut: medium-dark skin tone
-1F487 1F3FE 200D 2642                      ; non-fully-qualified # 💇🏾‍♂ man getting haircut: medium-dark skin tone
-1F487 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 💇🏿‍♂️ man getting haircut: dark skin tone
-1F487 1F3FF 200D 2642                      ; non-fully-qualified # 💇🏿‍♂ man getting haircut: dark skin tone
-1F487 200D 2640 FE0F                       ; fully-qualified     # 💇‍♀️ woman getting haircut
-1F487 200D 2640                            ; non-fully-qualified # 💇‍♀ woman getting haircut
-1F487 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 💇🏻‍♀️ woman getting haircut: light skin tone
-1F487 1F3FB 200D 2640                      ; non-fully-qualified # 💇🏻‍♀ woman getting haircut: light skin tone
-1F487 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 💇🏼‍♀️ woman getting haircut: medium-light skin tone
-1F487 1F3FC 200D 2640                      ; non-fully-qualified # 💇🏼‍♀ woman getting haircut: medium-light skin tone
-1F487 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 💇🏽‍♀️ woman getting haircut: medium skin tone
-1F487 1F3FD 200D 2640                      ; non-fully-qualified # 💇🏽‍♀ woman getting haircut: medium skin tone
-1F487 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 💇🏾‍♀️ woman getting haircut: medium-dark skin tone
-1F487 1F3FE 200D 2640                      ; non-fully-qualified # 💇🏾‍♀ woman getting haircut: medium-dark skin tone
-1F487 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 💇🏿‍♀️ woman getting haircut: dark skin tone
-1F487 1F3FF 200D 2640                      ; non-fully-qualified # 💇🏿‍♀ woman getting haircut: dark skin tone
-1F6B6                                      ; fully-qualified     # 🚶 person walking
-1F6B6 1F3FB                                ; fully-qualified     # 🚶🏻 person walking: light skin tone
-1F6B6 1F3FC                                ; fully-qualified     # 🚶🏼 person walking: medium-light skin tone
-1F6B6 1F3FD                                ; fully-qualified     # 🚶🏽 person walking: medium skin tone
-1F6B6 1F3FE                                ; fully-qualified     # 🚶🏾 person walking: medium-dark skin tone
-1F6B6 1F3FF                                ; fully-qualified     # 🚶🏿 person walking: dark skin tone
-1F6B6 200D 2642 FE0F                       ; fully-qualified     # 🚶‍♂️ man walking
-1F6B6 200D 2642                            ; non-fully-qualified # 🚶‍♂ man walking
-1F6B6 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🚶🏻‍♂️ man walking: light skin tone
-1F6B6 1F3FB 200D 2642                      ; non-fully-qualified # 🚶🏻‍♂ man walking: light skin tone
-1F6B6 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🚶🏼‍♂️ man walking: medium-light skin tone
-1F6B6 1F3FC 200D 2642                      ; non-fully-qualified # 🚶🏼‍♂ man walking: medium-light skin tone
-1F6B6 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🚶🏽‍♂️ man walking: medium skin tone
-1F6B6 1F3FD 200D 2642                      ; non-fully-qualified # 🚶🏽‍♂ man walking: medium skin tone
-1F6B6 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🚶🏾‍♂️ man walking: medium-dark skin tone
-1F6B6 1F3FE 200D 2642                      ; non-fully-qualified # 🚶🏾‍♂ man walking: medium-dark skin tone
-1F6B6 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🚶🏿‍♂️ man walking: dark skin tone
-1F6B6 1F3FF 200D 2642                      ; non-fully-qualified # 🚶🏿‍♂ man walking: dark skin tone
-1F6B6 200D 2640 FE0F                       ; fully-qualified     # 🚶‍♀️ woman walking
-1F6B6 200D 2640                            ; non-fully-qualified # 🚶‍♀ woman walking
-1F6B6 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🚶🏻‍♀️ woman walking: light skin tone
-1F6B6 1F3FB 200D 2640                      ; non-fully-qualified # 🚶🏻‍♀ woman walking: light skin tone
-1F6B6 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🚶🏼‍♀️ woman walking: medium-light skin tone
-1F6B6 1F3FC 200D 2640                      ; non-fully-qualified # 🚶🏼‍♀ woman walking: medium-light skin tone
-1F6B6 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🚶🏽‍♀️ woman walking: medium skin tone
-1F6B6 1F3FD 200D 2640                      ; non-fully-qualified # 🚶🏽‍♀ woman walking: medium skin tone
-1F6B6 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🚶🏾‍♀️ woman walking: medium-dark skin tone
-1F6B6 1F3FE 200D 2640                      ; non-fully-qualified # 🚶🏾‍♀ woman walking: medium-dark skin tone
-1F6B6 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🚶🏿‍♀️ woman walking: dark skin tone
-1F6B6 1F3FF 200D 2640                      ; non-fully-qualified # 🚶🏿‍♀ woman walking: dark skin tone
-1F3C3                                      ; fully-qualified     # 🏃 person running
-1F3C3 1F3FB                                ; fully-qualified     # 🏃🏻 person running: light skin tone
-1F3C3 1F3FC                                ; fully-qualified     # 🏃🏼 person running: medium-light skin tone
-1F3C3 1F3FD                                ; fully-qualified     # 🏃🏽 person running: medium skin tone
-1F3C3 1F3FE                                ; fully-qualified     # 🏃🏾 person running: medium-dark skin tone
-1F3C3 1F3FF                                ; fully-qualified     # 🏃🏿 person running: dark skin tone
-1F3C3 200D 2642 FE0F                       ; fully-qualified     # 🏃‍♂️ man running
-1F3C3 200D 2642                            ; non-fully-qualified # 🏃‍♂ man running
-1F3C3 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🏃🏻‍♂️ man running: light skin tone
-1F3C3 1F3FB 200D 2642                      ; non-fully-qualified # 🏃🏻‍♂ man running: light skin tone
-1F3C3 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🏃🏼‍♂️ man running: medium-light skin tone
-1F3C3 1F3FC 200D 2642                      ; non-fully-qualified # 🏃🏼‍♂ man running: medium-light skin tone
-1F3C3 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🏃🏽‍♂️ man running: medium skin tone
-1F3C3 1F3FD 200D 2642                      ; non-fully-qualified # 🏃🏽‍♂ man running: medium skin tone
-1F3C3 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🏃🏾‍♂️ man running: medium-dark skin tone
-1F3C3 1F3FE 200D 2642                      ; non-fully-qualified # 🏃🏾‍♂ man running: medium-dark skin tone
-1F3C3 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🏃🏿‍♂️ man running: dark skin tone
-1F3C3 1F3FF 200D 2642                      ; non-fully-qualified # 🏃🏿‍♂ man running: dark skin tone
-1F3C3 200D 2640 FE0F                       ; fully-qualified     # 🏃‍♀️ woman running
-1F3C3 200D 2640                            ; non-fully-qualified # 🏃‍♀ woman running
-1F3C3 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🏃🏻‍♀️ woman running: light skin tone
-1F3C3 1F3FB 200D 2640                      ; non-fully-qualified # 🏃🏻‍♀ woman running: light skin tone
-1F3C3 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🏃🏼‍♀️ woman running: medium-light skin tone
-1F3C3 1F3FC 200D 2640                      ; non-fully-qualified # 🏃🏼‍♀ woman running: medium-light skin tone
-1F3C3 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🏃🏽‍♀️ woman running: medium skin tone
-1F3C3 1F3FD 200D 2640                      ; non-fully-qualified # 🏃🏽‍♀ woman running: medium skin tone
-1F3C3 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🏃🏾‍♀️ woman running: medium-dark skin tone
-1F3C3 1F3FE 200D 2640                      ; non-fully-qualified # 🏃🏾‍♀ woman running: medium-dark skin tone
-1F3C3 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🏃🏿‍♀️ woman running: dark skin tone
-1F3C3 1F3FF 200D 2640                      ; non-fully-qualified # 🏃🏿‍♀ woman running: dark skin tone
-1F483                                      ; fully-qualified     # 💃 woman dancing
-1F483 1F3FB                                ; fully-qualified     # 💃🏻 woman dancing: light skin tone
-1F483 1F3FC                                ; fully-qualified     # 💃🏼 woman dancing: medium-light skin tone
-1F483 1F3FD                                ; fully-qualified     # 💃🏽 woman dancing: medium skin tone
-1F483 1F3FE                                ; fully-qualified     # 💃🏾 woman dancing: medium-dark skin tone
-1F483 1F3FF                                ; fully-qualified     # 💃🏿 woman dancing: dark skin tone
-1F57A                                      ; fully-qualified     # 🕺 man dancing
-1F57A 1F3FB                                ; fully-qualified     # 🕺🏻 man dancing: light skin tone
-1F57A 1F3FC                                ; fully-qualified     # 🕺🏼 man dancing: medium-light skin tone
-1F57A 1F3FD                                ; fully-qualified     # 🕺🏽 man dancing: medium skin tone
-1F57A 1F3FE                                ; fully-qualified     # 🕺🏾 man dancing: medium-dark skin tone
-1F57A 1F3FF                                ; fully-qualified     # 🕺🏿 man dancing: dark skin tone
-1F46F                                      ; fully-qualified     # 👯 people with bunny ears partying
-1F46F 200D 2642 FE0F                       ; fully-qualified     # 👯‍♂️ men with bunny ears partying
-1F46F 200D 2642                            ; non-fully-qualified # 👯‍♂ men with bunny ears partying
-1F46F 200D 2640 FE0F                       ; fully-qualified     # 👯‍♀️ women with bunny ears partying
-1F46F 200D 2640                            ; non-fully-qualified # 👯‍♀ women with bunny ears partying
-1F574 FE0F                                 ; fully-qualified     # 🕴️ man in business suit levitating
-1F574 1F3FB                                ; fully-qualified     # 🕴🏻 man in business suit levitating: light skin tone
-1F574 1F3FC                                ; fully-qualified     # 🕴🏼 man in business suit levitating: medium-light skin tone
-1F574 1F3FD                                ; fully-qualified     # 🕴🏽 man in business suit levitating: medium skin tone
-1F574 1F3FE                                ; fully-qualified     # 🕴🏾 man in business suit levitating: medium-dark skin tone
-1F574 1F3FF                                ; fully-qualified     # 🕴🏿 man in business suit levitating: dark skin tone
-1F5E3 FE0F                                 ; fully-qualified     # 🗣️ speaking head
-1F464                                      ; fully-qualified     # 👤 bust in silhouette
-1F465                                      ; fully-qualified     # 👥 busts in silhouette
-
-# subgroup: person-sport
-1F93A                                      ; fully-qualified     # 🤺 person fencing
-1F3C7                                      ; fully-qualified     # 🏇 horse racing
-1F3C7 1F3FB                                ; fully-qualified     # 🏇🏻 horse racing: light skin tone
-1F3C7 1F3FC                                ; fully-qualified     # 🏇🏼 horse racing: medium-light skin tone
-1F3C7 1F3FD                                ; fully-qualified     # 🏇🏽 horse racing: medium skin tone
-1F3C7 1F3FE                                ; fully-qualified     # 🏇🏾 horse racing: medium-dark skin tone
-1F3C7 1F3FF                                ; fully-qualified     # 🏇🏿 horse racing: dark skin tone
-26F7 FE0F                                  ; fully-qualified     # ⛷️ skier
-1F3C2                                      ; fully-qualified     # 🏂 snowboarder
-1F3C2 1F3FB                                ; fully-qualified     # 🏂🏻 snowboarder: light skin tone
-1F3C2 1F3FC                                ; fully-qualified     # 🏂🏼 snowboarder: medium-light skin tone
-1F3C2 1F3FD                                ; fully-qualified     # 🏂🏽 snowboarder: medium skin tone
-1F3C2 1F3FE                                ; fully-qualified     # 🏂🏾 snowboarder: medium-dark skin tone
-1F3C2 1F3FF                                ; fully-qualified     # 🏂🏿 snowboarder: dark skin tone
-1F3CC FE0F                                 ; fully-qualified     # 🏌️ person golfing
-1F3CC 1F3FB                                ; fully-qualified     # 🏌🏻 person golfing: light skin tone
-1F3CC 1F3FC                                ; fully-qualified     # 🏌🏼 person golfing: medium-light skin tone
-1F3CC 1F3FD                                ; fully-qualified     # 🏌🏽 person golfing: medium skin tone
-1F3CC 1F3FE                                ; fully-qualified     # 🏌🏾 person golfing: medium-dark skin tone
-1F3CC 1F3FF                                ; fully-qualified     # 🏌🏿 person golfing: dark skin tone
-1F3CC FE0F 200D 2642 FE0F                  ; fully-qualified     # 🏌️‍♂️ man golfing
-1F3CC 200D 2642                            ; non-fully-qualified # 🏌‍♂ man golfing
-1F3CC FE0F 200D 2642                       ; non-fully-qualified # 🏌️‍♂ man golfing
-1F3CC 200D 2642 FE0F                       ; non-fully-qualified # 🏌‍♂️ man golfing
-1F3CC 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🏌🏻‍♂️ man golfing: light skin tone
-1F3CC 1F3FB 200D 2642                      ; non-fully-qualified # 🏌🏻‍♂ man golfing: light skin tone
-1F3CC 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🏌🏼‍♂️ man golfing: medium-light skin tone
-1F3CC 1F3FC 200D 2642                      ; non-fully-qualified # 🏌🏼‍♂ man golfing: medium-light skin tone
-1F3CC 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🏌🏽‍♂️ man golfing: medium skin tone
-1F3CC 1F3FD 200D 2642                      ; non-fully-qualified # 🏌🏽‍♂ man golfing: medium skin tone
-1F3CC 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🏌🏾‍♂️ man golfing: medium-dark skin tone
-1F3CC 1F3FE 200D 2642                      ; non-fully-qualified # 🏌🏾‍♂ man golfing: medium-dark skin tone
-1F3CC 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🏌🏿‍♂️ man golfing: dark skin tone
-1F3CC 1F3FF 200D 2642                      ; non-fully-qualified # 🏌🏿‍♂ man golfing: dark skin tone
-1F3CC FE0F 200D 2640 FE0F                  ; fully-qualified     # 🏌️‍♀️ woman golfing
-1F3CC 200D 2640                            ; non-fully-qualified # 🏌‍♀ woman golfing
-1F3CC FE0F 200D 2640                       ; non-fully-qualified # 🏌️‍♀ woman golfing
-1F3CC 200D 2640 FE0F                       ; non-fully-qualified # 🏌‍♀️ woman golfing
-1F3CC 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🏌🏻‍♀️ woman golfing: light skin tone
-1F3CC 1F3FB 200D 2640                      ; non-fully-qualified # 🏌🏻‍♀ woman golfing: light skin tone
-1F3CC 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🏌🏼‍♀️ woman golfing: medium-light skin tone
-1F3CC 1F3FC 200D 2640                      ; non-fully-qualified # 🏌🏼‍♀ woman golfing: medium-light skin tone
-1F3CC 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🏌🏽‍♀️ woman golfing: medium skin tone
-1F3CC 1F3FD 200D 2640                      ; non-fully-qualified # 🏌🏽‍♀ woman golfing: medium skin tone
-1F3CC 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🏌🏾‍♀️ woman golfing: medium-dark skin tone
-1F3CC 1F3FE 200D 2640                      ; non-fully-qualified # 🏌🏾‍♀ woman golfing: medium-dark skin tone
-1F3CC 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🏌🏿‍♀️ woman golfing: dark skin tone
-1F3CC 1F3FF 200D 2640                      ; non-fully-qualified # 🏌🏿‍♀ woman golfing: dark skin tone
-1F3C4                                      ; fully-qualified     # 🏄 person surfing
-1F3C4 1F3FB                                ; fully-qualified     # 🏄🏻 person surfing: light skin tone
-1F3C4 1F3FC                                ; fully-qualified     # 🏄🏼 person surfing: medium-light skin tone
-1F3C4 1F3FD                                ; fully-qualified     # 🏄🏽 person surfing: medium skin tone
-1F3C4 1F3FE                                ; fully-qualified     # 🏄🏾 person surfing: medium-dark skin tone
-1F3C4 1F3FF                                ; fully-qualified     # 🏄🏿 person surfing: dark skin tone
-1F3C4 200D 2642 FE0F                       ; fully-qualified     # 🏄‍♂️ man surfing
-1F3C4 200D 2642                            ; non-fully-qualified # 🏄‍♂ man surfing
-1F3C4 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🏄🏻‍♂️ man surfing: light skin tone
-1F3C4 1F3FB 200D 2642                      ; non-fully-qualified # 🏄🏻‍♂ man surfing: light skin tone
-1F3C4 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🏄🏼‍♂️ man surfing: medium-light skin tone
-1F3C4 1F3FC 200D 2642                      ; non-fully-qualified # 🏄🏼‍♂ man surfing: medium-light skin tone
-1F3C4 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🏄🏽‍♂️ man surfing: medium skin tone
-1F3C4 1F3FD 200D 2642                      ; non-fully-qualified # 🏄🏽‍♂ man surfing: medium skin tone
-1F3C4 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🏄🏾‍♂️ man surfing: medium-dark skin tone
-1F3C4 1F3FE 200D 2642                      ; non-fully-qualified # 🏄🏾‍♂ man surfing: medium-dark skin tone
-1F3C4 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🏄🏿‍♂️ man surfing: dark skin tone
-1F3C4 1F3FF 200D 2642                      ; non-fully-qualified # 🏄🏿‍♂ man surfing: dark skin tone
-1F3C4 200D 2640 FE0F                       ; fully-qualified     # 🏄‍♀️ woman surfing
-1F3C4 200D 2640                            ; non-fully-qualified # 🏄‍♀ woman surfing
-1F3C4 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🏄🏻‍♀️ woman surfing: light skin tone
-1F3C4 1F3FB 200D 2640                      ; non-fully-qualified # 🏄🏻‍♀ woman surfing: light skin tone
-1F3C4 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🏄🏼‍♀️ woman surfing: medium-light skin tone
-1F3C4 1F3FC 200D 2640                      ; non-fully-qualified # 🏄🏼‍♀ woman surfing: medium-light skin tone
-1F3C4 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🏄🏽‍♀️ woman surfing: medium skin tone
-1F3C4 1F3FD 200D 2640                      ; non-fully-qualified # 🏄🏽‍♀ woman surfing: medium skin tone
-1F3C4 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🏄🏾‍♀️ woman surfing: medium-dark skin tone
-1F3C4 1F3FE 200D 2640                      ; non-fully-qualified # 🏄🏾‍♀ woman surfing: medium-dark skin tone
-1F3C4 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🏄🏿‍♀️ woman surfing: dark skin tone
-1F3C4 1F3FF 200D 2640                      ; non-fully-qualified # 🏄🏿‍♀ woman surfing: dark skin tone
-1F6A3                                      ; fully-qualified     # 🚣 person rowing boat
-1F6A3 1F3FB                                ; fully-qualified     # 🚣🏻 person rowing boat: light skin tone
-1F6A3 1F3FC                                ; fully-qualified     # 🚣🏼 person rowing boat: medium-light skin tone
-1F6A3 1F3FD                                ; fully-qualified     # 🚣🏽 person rowing boat: medium skin tone
-1F6A3 1F3FE                                ; fully-qualified     # 🚣🏾 person rowing boat: medium-dark skin tone
-1F6A3 1F3FF                                ; fully-qualified     # 🚣🏿 person rowing boat: dark skin tone
-1F6A3 200D 2642 FE0F                       ; fully-qualified     # 🚣‍♂️ man rowing boat
-1F6A3 200D 2642                            ; non-fully-qualified # 🚣‍♂ man rowing boat
-1F6A3 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🚣🏻‍♂️ man rowing boat: light skin tone
-1F6A3 1F3FB 200D 2642                      ; non-fully-qualified # 🚣🏻‍♂ man rowing boat: light skin tone
-1F6A3 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🚣🏼‍♂️ man rowing boat: medium-light skin tone
-1F6A3 1F3FC 200D 2642                      ; non-fully-qualified # 🚣🏼‍♂ man rowing boat: medium-light skin tone
-1F6A3 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🚣🏽‍♂️ man rowing boat: medium skin tone
-1F6A3 1F3FD 200D 2642                      ; non-fully-qualified # 🚣🏽‍♂ man rowing boat: medium skin tone
-1F6A3 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🚣🏾‍♂️ man rowing boat: medium-dark skin tone
-1F6A3 1F3FE 200D 2642                      ; non-fully-qualified # 🚣🏾‍♂ man rowing boat: medium-dark skin tone
-1F6A3 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🚣🏿‍♂️ man rowing boat: dark skin tone
-1F6A3 1F3FF 200D 2642                      ; non-fully-qualified # 🚣🏿‍♂ man rowing boat: dark skin tone
-1F6A3 200D 2640 FE0F                       ; fully-qualified     # 🚣‍♀️ woman rowing boat
-1F6A3 200D 2640                            ; non-fully-qualified # 🚣‍♀ woman rowing boat
-1F6A3 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🚣🏻‍♀️ woman rowing boat: light skin tone
-1F6A3 1F3FB 200D 2640                      ; non-fully-qualified # 🚣🏻‍♀ woman rowing boat: light skin tone
-1F6A3 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🚣🏼‍♀️ woman rowing boat: medium-light skin tone
-1F6A3 1F3FC 200D 2640                      ; non-fully-qualified # 🚣🏼‍♀ woman rowing boat: medium-light skin tone
-1F6A3 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🚣🏽‍♀️ woman rowing boat: medium skin tone
-1F6A3 1F3FD 200D 2640                      ; non-fully-qualified # 🚣🏽‍♀ woman rowing boat: medium skin tone
-1F6A3 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🚣🏾‍♀️ woman rowing boat: medium-dark skin tone
-1F6A3 1F3FE 200D 2640                      ; non-fully-qualified # 🚣🏾‍♀ woman rowing boat: medium-dark skin tone
-1F6A3 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🚣🏿‍♀️ woman rowing boat: dark skin tone
-1F6A3 1F3FF 200D 2640                      ; non-fully-qualified # 🚣🏿‍♀ woman rowing boat: dark skin tone
-1F3CA                                      ; fully-qualified     # 🏊 person swimming
-1F3CA 1F3FB                                ; fully-qualified     # 🏊🏻 person swimming: light skin tone
-1F3CA 1F3FC                                ; fully-qualified     # 🏊🏼 person swimming: medium-light skin tone
-1F3CA 1F3FD                                ; fully-qualified     # 🏊🏽 person swimming: medium skin tone
-1F3CA 1F3FE                                ; fully-qualified     # 🏊🏾 person swimming: medium-dark skin tone
-1F3CA 1F3FF                                ; fully-qualified     # 🏊🏿 person swimming: dark skin tone
-1F3CA 200D 2642 FE0F                       ; fully-qualified     # 🏊‍♂️ man swimming
-1F3CA 200D 2642                            ; non-fully-qualified # 🏊‍♂ man swimming
-1F3CA 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🏊🏻‍♂️ man swimming: light skin tone
-1F3CA 1F3FB 200D 2642                      ; non-fully-qualified # 🏊🏻‍♂ man swimming: light skin tone
-1F3CA 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🏊🏼‍♂️ man swimming: medium-light skin tone
-1F3CA 1F3FC 200D 2642                      ; non-fully-qualified # 🏊🏼‍♂ man swimming: medium-light skin tone
-1F3CA 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🏊🏽‍♂️ man swimming: medium skin tone
-1F3CA 1F3FD 200D 2642                      ; non-fully-qualified # 🏊🏽‍♂ man swimming: medium skin tone
-1F3CA 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🏊🏾‍♂️ man swimming: medium-dark skin tone
-1F3CA 1F3FE 200D 2642                      ; non-fully-qualified # 🏊🏾‍♂ man swimming: medium-dark skin tone
-1F3CA 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🏊🏿‍♂️ man swimming: dark skin tone
-1F3CA 1F3FF 200D 2642                      ; non-fully-qualified # 🏊🏿‍♂ man swimming: dark skin tone
-1F3CA 200D 2640 FE0F                       ; fully-qualified     # 🏊‍♀️ woman swimming
-1F3CA 200D 2640                            ; non-fully-qualified # 🏊‍♀ woman swimming
-1F3CA 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🏊🏻‍♀️ woman swimming: light skin tone
-1F3CA 1F3FB 200D 2640                      ; non-fully-qualified # 🏊🏻‍♀ woman swimming: light skin tone
-1F3CA 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🏊🏼‍♀️ woman swimming: medium-light skin tone
-1F3CA 1F3FC 200D 2640                      ; non-fully-qualified # 🏊🏼‍♀ woman swimming: medium-light skin tone
-1F3CA 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🏊🏽‍♀️ woman swimming: medium skin tone
-1F3CA 1F3FD 200D 2640                      ; non-fully-qualified # 🏊🏽‍♀ woman swimming: medium skin tone
-1F3CA 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🏊🏾‍♀️ woman swimming: medium-dark skin tone
-1F3CA 1F3FE 200D 2640                      ; non-fully-qualified # 🏊🏾‍♀ woman swimming: medium-dark skin tone
-1F3CA 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🏊🏿‍♀️ woman swimming: dark skin tone
-1F3CA 1F3FF 200D 2640                      ; non-fully-qualified # 🏊🏿‍♀ woman swimming: dark skin tone
-26F9 FE0F                                  ; fully-qualified     # ⛹️ person bouncing ball
-26F9 1F3FB                                 ; fully-qualified     # ⛹🏻 person bouncing ball: light skin tone
-26F9 1F3FC                                 ; fully-qualified     # ⛹🏼 person bouncing ball: medium-light skin tone
-26F9 1F3FD                                 ; fully-qualified     # ⛹🏽 person bouncing ball: medium skin tone
-26F9 1F3FE                                 ; fully-qualified     # ⛹🏾 person bouncing ball: medium-dark skin tone
-26F9 1F3FF                                 ; fully-qualified     # ⛹🏿 person bouncing ball: dark skin tone
-26F9 FE0F 200D 2642 FE0F                   ; fully-qualified     # ⛹️‍♂️ man bouncing ball
-26F9 200D 2642                             ; non-fully-qualified # ⛹‍♂ man bouncing ball
-26F9 FE0F 200D 2642                        ; non-fully-qualified # ⛹️‍♂ man bouncing ball
-26F9 200D 2642 FE0F                        ; non-fully-qualified # ⛹‍♂️ man bouncing ball
-26F9 1F3FB 200D 2642 FE0F                  ; fully-qualified     # ⛹🏻‍♂️ man bouncing ball: light skin tone
-26F9 1F3FB 200D 2642                       ; non-fully-qualified # ⛹🏻‍♂ man bouncing ball: light skin tone
-26F9 1F3FC 200D 2642 FE0F                  ; fully-qualified     # ⛹🏼‍♂️ man bouncing ball: medium-light skin tone
-26F9 1F3FC 200D 2642                       ; non-fully-qualified # ⛹🏼‍♂ man bouncing ball: medium-light skin tone
-26F9 1F3FD 200D 2642 FE0F                  ; fully-qualified     # ⛹🏽‍♂️ man bouncing ball: medium skin tone
-26F9 1F3FD 200D 2642                       ; non-fully-qualified # ⛹🏽‍♂ man bouncing ball: medium skin tone
-26F9 1F3FE 200D 2642 FE0F                  ; fully-qualified     # ⛹🏾‍♂️ man bouncing ball: medium-dark skin tone
-26F9 1F3FE 200D 2642                       ; non-fully-qualified # ⛹🏾‍♂ man bouncing ball: medium-dark skin tone
-26F9 1F3FF 200D 2642 FE0F                  ; fully-qualified     # ⛹🏿‍♂️ man bouncing ball: dark skin tone
-26F9 1F3FF 200D 2642                       ; non-fully-qualified # ⛹🏿‍♂ man bouncing ball: dark skin tone
-26F9 FE0F 200D 2640 FE0F                   ; fully-qualified     # ⛹️‍♀️ woman bouncing ball
-26F9 200D 2640                             ; non-fully-qualified # ⛹‍♀ woman bouncing ball
-26F9 FE0F 200D 2640                        ; non-fully-qualified # ⛹️‍♀ woman bouncing ball
-26F9 200D 2640 FE0F                        ; non-fully-qualified # ⛹‍♀️ woman bouncing ball
-26F9 1F3FB 200D 2640 FE0F                  ; fully-qualified     # ⛹🏻‍♀️ woman bouncing ball: light skin tone
-26F9 1F3FB 200D 2640                       ; non-fully-qualified # ⛹🏻‍♀ woman bouncing ball: light skin tone
-26F9 1F3FC 200D 2640 FE0F                  ; fully-qualified     # ⛹🏼‍♀️ woman bouncing ball: medium-light skin tone
-26F9 1F3FC 200D 2640                       ; non-fully-qualified # ⛹🏼‍♀ woman bouncing ball: medium-light skin tone
-26F9 1F3FD 200D 2640 FE0F                  ; fully-qualified     # ⛹🏽‍♀️ woman bouncing ball: medium skin tone
-26F9 1F3FD 200D 2640                       ; non-fully-qualified # ⛹🏽‍♀ woman bouncing ball: medium skin tone
-26F9 1F3FE 200D 2640 FE0F                  ; fully-qualified     # ⛹🏾‍♀️ woman bouncing ball: medium-dark skin tone
-26F9 1F3FE 200D 2640                       ; non-fully-qualified # ⛹🏾‍♀ woman bouncing ball: medium-dark skin tone
-26F9 1F3FF 200D 2640 FE0F                  ; fully-qualified     # ⛹🏿‍♀️ woman bouncing ball: dark skin tone
-26F9 1F3FF 200D 2640                       ; non-fully-qualified # ⛹🏿‍♀ woman bouncing ball: dark skin tone
-1F3CB FE0F                                 ; fully-qualified     # 🏋️ person lifting weights
-1F3CB 1F3FB                                ; fully-qualified     # 🏋🏻 person lifting weights: light skin tone
-1F3CB 1F3FC                                ; fully-qualified     # 🏋🏼 person lifting weights: medium-light skin tone
-1F3CB 1F3FD                                ; fully-qualified     # 🏋🏽 person lifting weights: medium skin tone
-1F3CB 1F3FE                                ; fully-qualified     # 🏋🏾 person lifting weights: medium-dark skin tone
-1F3CB 1F3FF                                ; fully-qualified     # 🏋🏿 person lifting weights: dark skin tone
-1F3CB FE0F 200D 2642 FE0F                  ; fully-qualified     # 🏋️‍♂️ man lifting weights
-1F3CB 200D 2642                            ; non-fully-qualified # 🏋‍♂ man lifting weights
-1F3CB FE0F 200D 2642                       ; non-fully-qualified # 🏋️‍♂ man lifting weights
-1F3CB 200D 2642 FE0F                       ; non-fully-qualified # 🏋‍♂️ man lifting weights
-1F3CB 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🏋🏻‍♂️ man lifting weights: light skin tone
-1F3CB 1F3FB 200D 2642                      ; non-fully-qualified # 🏋🏻‍♂ man lifting weights: light skin tone
-1F3CB 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🏋🏼‍♂️ man lifting weights: medium-light skin tone
-1F3CB 1F3FC 200D 2642                      ; non-fully-qualified # 🏋🏼‍♂ man lifting weights: medium-light skin tone
-1F3CB 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🏋🏽‍♂️ man lifting weights: medium skin tone
-1F3CB 1F3FD 200D 2642                      ; non-fully-qualified # 🏋🏽‍♂ man lifting weights: medium skin tone
-1F3CB 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🏋🏾‍♂️ man lifting weights: medium-dark skin tone
-1F3CB 1F3FE 200D 2642                      ; non-fully-qualified # 🏋🏾‍♂ man lifting weights: medium-dark skin tone
-1F3CB 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🏋🏿‍♂️ man lifting weights: dark skin tone
-1F3CB 1F3FF 200D 2642                      ; non-fully-qualified # 🏋🏿‍♂ man lifting weights: dark skin tone
-1F3CB FE0F 200D 2640 FE0F                  ; fully-qualified     # 🏋️‍♀️ woman lifting weights
-1F3CB 200D 2640                            ; non-fully-qualified # 🏋‍♀ woman lifting weights
-1F3CB FE0F 200D 2640                       ; non-fully-qualified # 🏋️‍♀ woman lifting weights
-1F3CB 200D 2640 FE0F                       ; non-fully-qualified # 🏋‍♀️ woman lifting weights
-1F3CB 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🏋🏻‍♀️ woman lifting weights: light skin tone
-1F3CB 1F3FB 200D 2640                      ; non-fully-qualified # 🏋🏻‍♀ woman lifting weights: light skin tone
-1F3CB 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🏋🏼‍♀️ woman lifting weights: medium-light skin tone
-1F3CB 1F3FC 200D 2640                      ; non-fully-qualified # 🏋🏼‍♀ woman lifting weights: medium-light skin tone
-1F3CB 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🏋🏽‍♀️ woman lifting weights: medium skin tone
-1F3CB 1F3FD 200D 2640                      ; non-fully-qualified # 🏋🏽‍♀ woman lifting weights: medium skin tone
-1F3CB 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🏋🏾‍♀️ woman lifting weights: medium-dark skin tone
-1F3CB 1F3FE 200D 2640                      ; non-fully-qualified # 🏋🏾‍♀ woman lifting weights: medium-dark skin tone
-1F3CB 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🏋🏿‍♀️ woman lifting weights: dark skin tone
-1F3CB 1F3FF 200D 2640                      ; non-fully-qualified # 🏋🏿‍♀ woman lifting weights: dark skin tone
-1F6B4                                      ; fully-qualified     # 🚴 person biking
-1F6B4 1F3FB                                ; fully-qualified     # 🚴🏻 person biking: light skin tone
-1F6B4 1F3FC                                ; fully-qualified     # 🚴🏼 person biking: medium-light skin tone
-1F6B4 1F3FD                                ; fully-qualified     # 🚴🏽 person biking: medium skin tone
-1F6B4 1F3FE                                ; fully-qualified     # 🚴🏾 person biking: medium-dark skin tone
-1F6B4 1F3FF                                ; fully-qualified     # 🚴🏿 person biking: dark skin tone
-1F6B4 200D 2642 FE0F                       ; fully-qualified     # 🚴‍♂️ man biking
-1F6B4 200D 2642                            ; non-fully-qualified # 🚴‍♂ man biking
-1F6B4 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🚴🏻‍♂️ man biking: light skin tone
-1F6B4 1F3FB 200D 2642                      ; non-fully-qualified # 🚴🏻‍♂ man biking: light skin tone
-1F6B4 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🚴🏼‍♂️ man biking: medium-light skin tone
-1F6B4 1F3FC 200D 2642                      ; non-fully-qualified # 🚴🏼‍♂ man biking: medium-light skin tone
-1F6B4 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🚴🏽‍♂️ man biking: medium skin tone
-1F6B4 1F3FD 200D 2642                      ; non-fully-qualified # 🚴🏽‍♂ man biking: medium skin tone
-1F6B4 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🚴🏾‍♂️ man biking: medium-dark skin tone
-1F6B4 1F3FE 200D 2642                      ; non-fully-qualified # 🚴🏾‍♂ man biking: medium-dark skin tone
-1F6B4 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🚴🏿‍♂️ man biking: dark skin tone
-1F6B4 1F3FF 200D 2642                      ; non-fully-qualified # 🚴🏿‍♂ man biking: dark skin tone
-1F6B4 200D 2640 FE0F                       ; fully-qualified     # 🚴‍♀️ woman biking
-1F6B4 200D 2640                            ; non-fully-qualified # 🚴‍♀ woman biking
-1F6B4 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🚴🏻‍♀️ woman biking: light skin tone
-1F6B4 1F3FB 200D 2640                      ; non-fully-qualified # 🚴🏻‍♀ woman biking: light skin tone
-1F6B4 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🚴🏼‍♀️ woman biking: medium-light skin tone
-1F6B4 1F3FC 200D 2640                      ; non-fully-qualified # 🚴🏼‍♀ woman biking: medium-light skin tone
-1F6B4 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🚴🏽‍♀️ woman biking: medium skin tone
-1F6B4 1F3FD 200D 2640                      ; non-fully-qualified # 🚴🏽‍♀ woman biking: medium skin tone
-1F6B4 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🚴🏾‍♀️ woman biking: medium-dark skin tone
-1F6B4 1F3FE 200D 2640                      ; non-fully-qualified # 🚴🏾‍♀ woman biking: medium-dark skin tone
-1F6B4 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🚴🏿‍♀️ woman biking: dark skin tone
-1F6B4 1F3FF 200D 2640                      ; non-fully-qualified # 🚴🏿‍♀ woman biking: dark skin tone
-1F6B5                                      ; fully-qualified     # 🚵 person mountain biking
-1F6B5 1F3FB                                ; fully-qualified     # 🚵🏻 person mountain biking: light skin tone
-1F6B5 1F3FC                                ; fully-qualified     # 🚵🏼 person mountain biking: medium-light skin tone
-1F6B5 1F3FD                                ; fully-qualified     # 🚵🏽 person mountain biking: medium skin tone
-1F6B5 1F3FE                                ; fully-qualified     # 🚵🏾 person mountain biking: medium-dark skin tone
-1F6B5 1F3FF                                ; fully-qualified     # 🚵🏿 person mountain biking: dark skin tone
-1F6B5 200D 2642 FE0F                       ; fully-qualified     # 🚵‍♂️ man mountain biking
-1F6B5 200D 2642                            ; non-fully-qualified # 🚵‍♂ man mountain biking
-1F6B5 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🚵🏻‍♂️ man mountain biking: light skin tone
-1F6B5 1F3FB 200D 2642                      ; non-fully-qualified # 🚵🏻‍♂ man mountain biking: light skin tone
-1F6B5 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🚵🏼‍♂️ man mountain biking: medium-light skin tone
-1F6B5 1F3FC 200D 2642                      ; non-fully-qualified # 🚵🏼‍♂ man mountain biking: medium-light skin tone
-1F6B5 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🚵🏽‍♂️ man mountain biking: medium skin tone
-1F6B5 1F3FD 200D 2642                      ; non-fully-qualified # 🚵🏽‍♂ man mountain biking: medium skin tone
-1F6B5 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🚵🏾‍♂️ man mountain biking: medium-dark skin tone
-1F6B5 1F3FE 200D 2642                      ; non-fully-qualified # 🚵🏾‍♂ man mountain biking: medium-dark skin tone
-1F6B5 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🚵🏿‍♂️ man mountain biking: dark skin tone
-1F6B5 1F3FF 200D 2642                      ; non-fully-qualified # 🚵🏿‍♂ man mountain biking: dark skin tone
-1F6B5 200D 2640 FE0F                       ; fully-qualified     # 🚵‍♀️ woman mountain biking
-1F6B5 200D 2640                            ; non-fully-qualified # 🚵‍♀ woman mountain biking
-1F6B5 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🚵🏻‍♀️ woman mountain biking: light skin tone
-1F6B5 1F3FB 200D 2640                      ; non-fully-qualified # 🚵🏻‍♀ woman mountain biking: light skin tone
-1F6B5 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🚵🏼‍♀️ woman mountain biking: medium-light skin tone
-1F6B5 1F3FC 200D 2640                      ; non-fully-qualified # 🚵🏼‍♀ woman mountain biking: medium-light skin tone
-1F6B5 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🚵🏽‍♀️ woman mountain biking: medium skin tone
-1F6B5 1F3FD 200D 2640                      ; non-fully-qualified # 🚵🏽‍♀ woman mountain biking: medium skin tone
-1F6B5 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🚵🏾‍♀️ woman mountain biking: medium-dark skin tone
-1F6B5 1F3FE 200D 2640                      ; non-fully-qualified # 🚵🏾‍♀ woman mountain biking: medium-dark skin tone
-1F6B5 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🚵🏿‍♀️ woman mountain biking: dark skin tone
-1F6B5 1F3FF 200D 2640                      ; non-fully-qualified # 🚵🏿‍♀ woman mountain biking: dark skin tone
-1F3CE FE0F                                 ; fully-qualified     # 🏎️ racing car
-1F3CD FE0F                                 ; fully-qualified     # 🏍️ motorcycle
-1F938                                      ; fully-qualified     # 🤸 person cartwheeling
-1F938 1F3FB                                ; fully-qualified     # 🤸🏻 person cartwheeling: light skin tone
-1F938 1F3FC                                ; fully-qualified     # 🤸🏼 person cartwheeling: medium-light skin tone
-1F938 1F3FD                                ; fully-qualified     # 🤸🏽 person cartwheeling: medium skin tone
-1F938 1F3FE                                ; fully-qualified     # 🤸🏾 person cartwheeling: medium-dark skin tone
-1F938 1F3FF                                ; fully-qualified     # 🤸🏿 person cartwheeling: dark skin tone
-1F938 200D 2642 FE0F                       ; fully-qualified     # 🤸‍♂️ man cartwheeling
-1F938 200D 2642                            ; non-fully-qualified # 🤸‍♂ man cartwheeling
-1F938 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🤸🏻‍♂️ man cartwheeling: light skin tone
-1F938 1F3FB 200D 2642                      ; non-fully-qualified # 🤸🏻‍♂ man cartwheeling: light skin tone
-1F938 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🤸🏼‍♂️ man cartwheeling: medium-light skin tone
-1F938 1F3FC 200D 2642                      ; non-fully-qualified # 🤸🏼‍♂ man cartwheeling: medium-light skin tone
-1F938 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🤸🏽‍♂️ man cartwheeling: medium skin tone
-1F938 1F3FD 200D 2642                      ; non-fully-qualified # 🤸🏽‍♂ man cartwheeling: medium skin tone
-1F938 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🤸🏾‍♂️ man cartwheeling: medium-dark skin tone
-1F938 1F3FE 200D 2642                      ; non-fully-qualified # 🤸🏾‍♂ man cartwheeling: medium-dark skin tone
-1F938 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🤸🏿‍♂️ man cartwheeling: dark skin tone
-1F938 1F3FF 200D 2642                      ; non-fully-qualified # 🤸🏿‍♂ man cartwheeling: dark skin tone
-1F938 200D 2640 FE0F                       ; fully-qualified     # 🤸‍♀️ woman cartwheeling
-1F938 200D 2640                            ; non-fully-qualified # 🤸‍♀ woman cartwheeling
-1F938 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🤸🏻‍♀️ woman cartwheeling: light skin tone
-1F938 1F3FB 200D 2640                      ; non-fully-qualified # 🤸🏻‍♀ woman cartwheeling: light skin tone
-1F938 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🤸🏼‍♀️ woman cartwheeling: medium-light skin tone
-1F938 1F3FC 200D 2640                      ; non-fully-qualified # 🤸🏼‍♀ woman cartwheeling: medium-light skin tone
-1F938 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🤸🏽‍♀️ woman cartwheeling: medium skin tone
-1F938 1F3FD 200D 2640                      ; non-fully-qualified # 🤸🏽‍♀ woman cartwheeling: medium skin tone
-1F938 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🤸🏾‍♀️ woman cartwheeling: medium-dark skin tone
-1F938 1F3FE 200D 2640                      ; non-fully-qualified # 🤸🏾‍♀ woman cartwheeling: medium-dark skin tone
-1F938 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🤸🏿‍♀️ woman cartwheeling: dark skin tone
-1F938 1F3FF 200D 2640                      ; non-fully-qualified # 🤸🏿‍♀ woman cartwheeling: dark skin tone
-1F93C                                      ; fully-qualified     # 🤼 people wrestling
-1F93C 200D 2642 FE0F                       ; fully-qualified     # 🤼‍♂️ men wrestling
-1F93C 200D 2642                            ; non-fully-qualified # 🤼‍♂ men wrestling
-1F93C 200D 2640 FE0F                       ; fully-qualified     # 🤼‍♀️ women wrestling
-1F93C 200D 2640                            ; non-fully-qualified # 🤼‍♀ women wrestling
-1F93D                                      ; fully-qualified     # 🤽 person playing water polo
-1F93D 1F3FB                                ; fully-qualified     # 🤽🏻 person playing water polo: light skin tone
-1F93D 1F3FC                                ; fully-qualified     # 🤽🏼 person playing water polo: medium-light skin tone
-1F93D 1F3FD                                ; fully-qualified     # 🤽🏽 person playing water polo: medium skin tone
-1F93D 1F3FE                                ; fully-qualified     # 🤽🏾 person playing water polo: medium-dark skin tone
-1F93D 1F3FF                                ; fully-qualified     # 🤽🏿 person playing water polo: dark skin tone
-1F93D 200D 2642 FE0F                       ; fully-qualified     # 🤽‍♂️ man playing water polo
-1F93D 200D 2642                            ; non-fully-qualified # 🤽‍♂ man playing water polo
-1F93D 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🤽🏻‍♂️ man playing water polo: light skin tone
-1F93D 1F3FB 200D 2642                      ; non-fully-qualified # 🤽🏻‍♂ man playing water polo: light skin tone
-1F93D 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🤽🏼‍♂️ man playing water polo: medium-light skin tone
-1F93D 1F3FC 200D 2642                      ; non-fully-qualified # 🤽🏼‍♂ man playing water polo: medium-light skin tone
-1F93D 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🤽🏽‍♂️ man playing water polo: medium skin tone
-1F93D 1F3FD 200D 2642                      ; non-fully-qualified # 🤽🏽‍♂ man playing water polo: medium skin tone
-1F93D 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🤽🏾‍♂️ man playing water polo: medium-dark skin tone
-1F93D 1F3FE 200D 2642                      ; non-fully-qualified # 🤽🏾‍♂ man playing water polo: medium-dark skin tone
-1F93D 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🤽🏿‍♂️ man playing water polo: dark skin tone
-1F93D 1F3FF 200D 2642                      ; non-fully-qualified # 🤽🏿‍♂ man playing water polo: dark skin tone
-1F93D 200D 2640 FE0F                       ; fully-qualified     # 🤽‍♀️ woman playing water polo
-1F93D 200D 2640                            ; non-fully-qualified # 🤽‍♀ woman playing water polo
-1F93D 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🤽🏻‍♀️ woman playing water polo: light skin tone
-1F93D 1F3FB 200D 2640                      ; non-fully-qualified # 🤽🏻‍♀ woman playing water polo: light skin tone
-1F93D 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🤽🏼‍♀️ woman playing water polo: medium-light skin tone
-1F93D 1F3FC 200D 2640                      ; non-fully-qualified # 🤽🏼‍♀ woman playing water polo: medium-light skin tone
-1F93D 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🤽🏽‍♀️ woman playing water polo: medium skin tone
-1F93D 1F3FD 200D 2640                      ; non-fully-qualified # 🤽🏽‍♀ woman playing water polo: medium skin tone
-1F93D 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🤽🏾‍♀️ woman playing water polo: medium-dark skin tone
-1F93D 1F3FE 200D 2640                      ; non-fully-qualified # 🤽🏾‍♀ woman playing water polo: medium-dark skin tone
-1F93D 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🤽🏿‍♀️ woman playing water polo: dark skin tone
-1F93D 1F3FF 200D 2640                      ; non-fully-qualified # 🤽🏿‍♀ woman playing water polo: dark skin tone
-1F93E                                      ; fully-qualified     # 🤾 person playing handball
-1F93E 1F3FB                                ; fully-qualified     # 🤾🏻 person playing handball: light skin tone
-1F93E 1F3FC                                ; fully-qualified     # 🤾🏼 person playing handball: medium-light skin tone
-1F93E 1F3FD                                ; fully-qualified     # 🤾🏽 person playing handball: medium skin tone
-1F93E 1F3FE                                ; fully-qualified     # 🤾🏾 person playing handball: medium-dark skin tone
-1F93E 1F3FF                                ; fully-qualified     # 🤾🏿 person playing handball: dark skin tone
-1F93E 200D 2642 FE0F                       ; fully-qualified     # 🤾‍♂️ man playing handball
-1F93E 200D 2642                            ; non-fully-qualified # 🤾‍♂ man playing handball
-1F93E 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🤾🏻‍♂️ man playing handball: light skin tone
-1F93E 1F3FB 200D 2642                      ; non-fully-qualified # 🤾🏻‍♂ man playing handball: light skin tone
-1F93E 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🤾🏼‍♂️ man playing handball: medium-light skin tone
-1F93E 1F3FC 200D 2642                      ; non-fully-qualified # 🤾🏼‍♂ man playing handball: medium-light skin tone
-1F93E 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🤾🏽‍♂️ man playing handball: medium skin tone
-1F93E 1F3FD 200D 2642                      ; non-fully-qualified # 🤾🏽‍♂ man playing handball: medium skin tone
-1F93E 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🤾🏾‍♂️ man playing handball: medium-dark skin tone
-1F93E 1F3FE 200D 2642                      ; non-fully-qualified # 🤾🏾‍♂ man playing handball: medium-dark skin tone
-1F93E 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🤾🏿‍♂️ man playing handball: dark skin tone
-1F93E 1F3FF 200D 2642                      ; non-fully-qualified # 🤾🏿‍♂ man playing handball: dark skin tone
-1F93E 200D 2640 FE0F                       ; fully-qualified     # 🤾‍♀️ woman playing handball
-1F93E 200D 2640                            ; non-fully-qualified # 🤾‍♀ woman playing handball
-1F93E 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🤾🏻‍♀️ woman playing handball: light skin tone
-1F93E 1F3FB 200D 2640                      ; non-fully-qualified # 🤾🏻‍♀ woman playing handball: light skin tone
-1F93E 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🤾🏼‍♀️ woman playing handball: medium-light skin tone
-1F93E 1F3FC 200D 2640                      ; non-fully-qualified # 🤾🏼‍♀ woman playing handball: medium-light skin tone
-1F93E 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🤾🏽‍♀️ woman playing handball: medium skin tone
-1F93E 1F3FD 200D 2640                      ; non-fully-qualified # 🤾🏽‍♀ woman playing handball: medium skin tone
-1F93E 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🤾🏾‍♀️ woman playing handball: medium-dark skin tone
-1F93E 1F3FE 200D 2640                      ; non-fully-qualified # 🤾🏾‍♀ woman playing handball: medium-dark skin tone
-1F93E 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🤾🏿‍♀️ woman playing handball: dark skin tone
-1F93E 1F3FF 200D 2640                      ; non-fully-qualified # 🤾🏿‍♀ woman playing handball: dark skin tone
-1F939                                      ; fully-qualified     # 🤹 person juggling
-1F939 1F3FB                                ; fully-qualified     # 🤹🏻 person juggling: light skin tone
-1F939 1F3FC                                ; fully-qualified     # 🤹🏼 person juggling: medium-light skin tone
-1F939 1F3FD                                ; fully-qualified     # 🤹🏽 person juggling: medium skin tone
-1F939 1F3FE                                ; fully-qualified     # 🤹🏾 person juggling: medium-dark skin tone
-1F939 1F3FF                                ; fully-qualified     # 🤹🏿 person juggling: dark skin tone
-1F939 200D 2642 FE0F                       ; fully-qualified     # 🤹‍♂️ man juggling
-1F939 200D 2642                            ; non-fully-qualified # 🤹‍♂ man juggling
-1F939 1F3FB 200D 2642 FE0F                 ; fully-qualified     # 🤹🏻‍♂️ man juggling: light skin tone
-1F939 1F3FB 200D 2642                      ; non-fully-qualified # 🤹🏻‍♂ man juggling: light skin tone
-1F939 1F3FC 200D 2642 FE0F                 ; fully-qualified     # 🤹🏼‍♂️ man juggling: medium-light skin tone
-1F939 1F3FC 200D 2642                      ; non-fully-qualified # 🤹🏼‍♂ man juggling: medium-light skin tone
-1F939 1F3FD 200D 2642 FE0F                 ; fully-qualified     # 🤹🏽‍♂️ man juggling: medium skin tone
-1F939 1F3FD 200D 2642                      ; non-fully-qualified # 🤹🏽‍♂ man juggling: medium skin tone
-1F939 1F3FE 200D 2642 FE0F                 ; fully-qualified     # 🤹🏾‍♂️ man juggling: medium-dark skin tone
-1F939 1F3FE 200D 2642                      ; non-fully-qualified # 🤹🏾‍♂ man juggling: medium-dark skin tone
-1F939 1F3FF 200D 2642 FE0F                 ; fully-qualified     # 🤹🏿‍♂️ man juggling: dark skin tone
-1F939 1F3FF 200D 2642                      ; non-fully-qualified # 🤹🏿‍♂ man juggling: dark skin tone
-1F939 200D 2640 FE0F                       ; fully-qualified     # 🤹‍♀️ woman juggling
-1F939 200D 2640                            ; non-fully-qualified # 🤹‍♀ woman juggling
-1F939 1F3FB 200D 2640 FE0F                 ; fully-qualified     # 🤹🏻‍♀️ woman juggling: light skin tone
-1F939 1F3FB 200D 2640                      ; non-fully-qualified # 🤹🏻‍♀ woman juggling: light skin tone
-1F939 1F3FC 200D 2640 FE0F                 ; fully-qualified     # 🤹🏼‍♀️ woman juggling: medium-light skin tone
-1F939 1F3FC 200D 2640                      ; non-fully-qualified # 🤹🏼‍♀ woman juggling: medium-light skin tone
-1F939 1F3FD 200D 2640 FE0F                 ; fully-qualified     # 🤹🏽‍♀️ woman juggling: medium skin tone
-1F939 1F3FD 200D 2640                      ; non-fully-qualified # 🤹🏽‍♀ woman juggling: medium skin tone
-1F939 1F3FE 200D 2640 FE0F                 ; fully-qualified     # 🤹🏾‍♀️ woman juggling: medium-dark skin tone
-1F939 1F3FE 200D 2640                      ; non-fully-qualified # 🤹🏾‍♀ woman juggling: medium-dark skin tone
-1F939 1F3FF 200D 2640 FE0F                 ; fully-qualified     # 🤹🏿‍♀️ woman juggling: dark skin tone
-1F939 1F3FF 200D 2640                      ; non-fully-qualified # 🤹🏿‍♀ woman juggling: dark skin tone
-
-# subgroup: family
-1F46B                                      ; fully-qualified     # 👫 man and woman holding hands
-1F46C                                      ; fully-qualified     # 👬 two men holding hands
-1F46D                                      ; fully-qualified     # 👭 two women holding hands
-1F48F                                      ; fully-qualified     # 💏 kiss
-1F469 200D 2764 FE0F 200D 1F48B 200D 1F468 ; fully-qualified     # 👩‍❤️‍💋‍👨 kiss: woman, man
-1F469 200D 2764 200D 1F48B 200D 1F468      ; non-fully-qualified # 👩‍❤‍💋‍👨 kiss: woman, man
-1F468 200D 2764 FE0F 200D 1F48B 200D 1F468 ; fully-qualified     # 👨‍❤️‍💋‍👨 kiss: man, man
-1F468 200D 2764 200D 1F48B 200D 1F468      ; non-fully-qualified # 👨‍❤‍💋‍👨 kiss: man, man
-1F469 200D 2764 FE0F 200D 1F48B 200D 1F469 ; fully-qualified     # 👩‍❤️‍💋‍👩 kiss: woman, woman
-1F469 200D 2764 200D 1F48B 200D 1F469      ; non-fully-qualified # 👩‍❤‍💋‍👩 kiss: woman, woman
-1F491                                      ; fully-qualified     # 💑 couple with heart
-1F469 200D 2764 FE0F 200D 1F468            ; fully-qualified     # 👩‍❤️‍👨 couple with heart: woman, man
-1F469 200D 2764 200D 1F468                 ; non-fully-qualified # 👩‍❤‍👨 couple with heart: woman, man
-1F468 200D 2764 FE0F 200D 1F468            ; fully-qualified     # 👨‍❤️‍👨 couple with heart: man, man
-1F468 200D 2764 200D 1F468                 ; non-fully-qualified # 👨‍❤‍👨 couple with heart: man, man
-1F469 200D 2764 FE0F 200D 1F469            ; fully-qualified     # 👩‍❤️‍👩 couple with heart: woman, woman
-1F469 200D 2764 200D 1F469                 ; non-fully-qualified # 👩‍❤‍👩 couple with heart: woman, woman
-1F46A                                      ; fully-qualified     # 👪 family
-1F468 200D 1F469 200D 1F466                ; fully-qualified     # 👨‍👩‍👦 family: man, woman, boy
-1F468 200D 1F469 200D 1F467                ; fully-qualified     # 👨‍👩‍👧 family: man, woman, girl
-1F468 200D 1F469 200D 1F467 200D 1F466     ; fully-qualified     # 👨‍👩‍👧‍👦 family: man, woman, girl, boy
-1F468 200D 1F469 200D 1F466 200D 1F466     ; fully-qualified     # 👨‍👩‍👦‍👦 family: man, woman, boy, boy
-1F468 200D 1F469 200D 1F467 200D 1F467     ; fully-qualified     # 👨‍👩‍👧‍👧 family: man, woman, girl, girl
-1F468 200D 1F468 200D 1F466                ; fully-qualified     # 👨‍👨‍👦 family: man, man, boy
-1F468 200D 1F468 200D 1F467                ; fully-qualified     # 👨‍👨‍👧 family: man, man, girl
-1F468 200D 1F468 200D 1F467 200D 1F466     ; fully-qualified     # 👨‍👨‍👧‍👦 family: man, man, girl, boy
-1F468 200D 1F468 200D 1F466 200D 1F466     ; fully-qualified     # 👨‍👨‍👦‍👦 family: man, man, boy, boy
-1F468 200D 1F468 200D 1F467 200D 1F467     ; fully-qualified     # 👨‍👨‍👧‍👧 family: man, man, girl, girl
-1F469 200D 1F469 200D 1F466                ; fully-qualified     # 👩‍👩‍👦 family: woman, woman, boy
-1F469 200D 1F469 200D 1F467                ; fully-qualified     # 👩‍👩‍👧 family: woman, woman, girl
-1F469 200D 1F469 200D 1F467 200D 1F466     ; fully-qualified     # 👩‍👩‍👧‍👦 family: woman, woman, girl, boy
-1F469 200D 1F469 200D 1F466 200D 1F466     ; fully-qualified     # 👩‍👩‍👦‍👦 family: woman, woman, boy, boy
-1F469 200D 1F469 200D 1F467 200D 1F467     ; fully-qualified     # 👩‍👩‍👧‍👧 family: woman, woman, girl, girl
-1F468 200D 1F466                           ; fully-qualified     # 👨‍👦 family: man, boy
-1F468 200D 1F466 200D 1F466                ; fully-qualified     # 👨‍👦‍👦 family: man, boy, boy
-1F468 200D 1F467                           ; fully-qualified     # 👨‍👧 family: man, girl
-1F468 200D 1F467 200D 1F466                ; fully-qualified     # 👨‍👧‍👦 family: man, girl, boy
-1F468 200D 1F467 200D 1F467                ; fully-qualified     # 👨‍👧‍👧 family: man, girl, girl
-1F469 200D 1F466                           ; fully-qualified     # 👩‍👦 family: woman, boy
-1F469 200D 1F466 200D 1F466                ; fully-qualified     # 👩‍👦‍👦 family: woman, boy, boy
-1F469 200D 1F467                           ; fully-qualified     # 👩‍👧 family: woman, girl
-1F469 200D 1F467 200D 1F466                ; fully-qualified     # 👩‍👧‍👦 family: woman, girl, boy
-1F469 200D 1F467 200D 1F467                ; fully-qualified     # 👩‍👧‍👧 family: woman, girl, girl
-
-# subgroup: body
-1F4AA                                      ; fully-qualified     # 💪 flexed biceps
-1F4AA 1F3FB                                ; fully-qualified     # 💪🏻 flexed biceps: light skin tone
-1F4AA 1F3FC                                ; fully-qualified     # 💪🏼 flexed biceps: medium-light skin tone
-1F4AA 1F3FD                                ; fully-qualified     # 💪🏽 flexed biceps: medium skin tone
-1F4AA 1F3FE                                ; fully-qualified     # 💪🏾 flexed biceps: medium-dark skin tone
-1F4AA 1F3FF                                ; fully-qualified     # 💪🏿 flexed biceps: dark skin tone
-1F933                                      ; fully-qualified     # 🤳 selfie
-1F933 1F3FB                                ; fully-qualified     # 🤳🏻 selfie: light skin tone
-1F933 1F3FC                                ; fully-qualified     # 🤳🏼 selfie: medium-light skin tone
-1F933 1F3FD                                ; fully-qualified     # 🤳🏽 selfie: medium skin tone
-1F933 1F3FE                                ; fully-qualified     # 🤳🏾 selfie: medium-dark skin tone
-1F933 1F3FF                                ; fully-qualified     # 🤳🏿 selfie: dark skin tone
-1F448                                      ; fully-qualified     # 👈 backhand index pointing left
-1F448 1F3FB                                ; fully-qualified     # 👈🏻 backhand index pointing left: light skin tone
-1F448 1F3FC                                ; fully-qualified     # 👈🏼 backhand index pointing left: medium-light skin tone
-1F448 1F3FD                                ; fully-qualified     # 👈🏽 backhand index pointing left: medium skin tone
-1F448 1F3FE                                ; fully-qualified     # 👈🏾 backhand index pointing left: medium-dark skin tone
-1F448 1F3FF                                ; fully-qualified     # 👈🏿 backhand index pointing left: dark skin tone
-1F449                                      ; fully-qualified     # 👉 backhand index pointing right
-1F449 1F3FB                                ; fully-qualified     # 👉🏻 backhand index pointing right: light skin tone
-1F449 1F3FC                                ; fully-qualified     # 👉🏼 backhand index pointing right: medium-light skin tone
-1F449 1F3FD                                ; fully-qualified     # 👉🏽 backhand index pointing right: medium skin tone
-1F449 1F3FE                                ; fully-qualified     # 👉🏾 backhand index pointing right: medium-dark skin tone
-1F449 1F3FF                                ; fully-qualified     # 👉🏿 backhand index pointing right: dark skin tone
-261D FE0F                                  ; fully-qualified     # ☝️ index pointing up
-261D 1F3FB                                 ; fully-qualified     # ☝🏻 index pointing up: light skin tone
-261D 1F3FC                                 ; fully-qualified     # ☝🏼 index pointing up: medium-light skin tone
-261D 1F3FD                                 ; fully-qualified     # ☝🏽 index pointing up: medium skin tone
-261D 1F3FE                                 ; fully-qualified     # ☝🏾 index pointing up: medium-dark skin tone
-261D 1F3FF                                 ; fully-qualified     # ☝🏿 index pointing up: dark skin tone
-1F446                                      ; fully-qualified     # 👆 backhand index pointing up
-1F446 1F3FB                                ; fully-qualified     # 👆🏻 backhand index pointing up: light skin tone
-1F446 1F3FC                                ; fully-qualified     # 👆🏼 backhand index pointing up: medium-light skin tone
-1F446 1F3FD                                ; fully-qualified     # 👆🏽 backhand index pointing up: medium skin tone
-1F446 1F3FE                                ; fully-qualified     # 👆🏾 backhand index pointing up: medium-dark skin tone
-1F446 1F3FF                                ; fully-qualified     # 👆🏿 backhand index pointing up: dark skin tone
-1F595                                      ; fully-qualified     # 🖕 middle finger
-1F595 1F3FB                                ; fully-qualified     # 🖕🏻 middle finger: light skin tone
-1F595 1F3FC                                ; fully-qualified     # 🖕🏼 middle finger: medium-light skin tone
-1F595 1F3FD                                ; fully-qualified     # 🖕🏽 middle finger: medium skin tone
-1F595 1F3FE                                ; fully-qualified     # 🖕🏾 middle finger: medium-dark skin tone
-1F595 1F3FF                                ; fully-qualified     # 🖕🏿 middle finger: dark skin tone
-1F447                                      ; fully-qualified     # 👇 backhand index pointing down
-1F447 1F3FB                                ; fully-qualified     # 👇🏻 backhand index pointing down: light skin tone
-1F447 1F3FC                                ; fully-qualified     # 👇🏼 backhand index pointing down: medium-light skin tone
-1F447 1F3FD                                ; fully-qualified     # 👇🏽 backhand index pointing down: medium skin tone
-1F447 1F3FE                                ; fully-qualified     # 👇🏾 backhand index pointing down: medium-dark skin tone
-1F447 1F3FF                                ; fully-qualified     # 👇🏿 backhand index pointing down: dark skin tone
-270C FE0F                                  ; fully-qualified     # ✌️ victory hand
-270C 1F3FB                                 ; fully-qualified     # ✌🏻 victory hand: light skin tone
-270C 1F3FC                                 ; fully-qualified     # ✌🏼 victory hand: medium-light skin tone
-270C 1F3FD                                 ; fully-qualified     # ✌🏽 victory hand: medium skin tone
-270C 1F3FE                                 ; fully-qualified     # ✌🏾 victory hand: medium-dark skin tone
-270C 1F3FF                                 ; fully-qualified     # ✌🏿 victory hand: dark skin tone
-1F91E                                      ; fully-qualified     # 🤞 crossed fingers
-1F91E 1F3FB                                ; fully-qualified     # 🤞🏻 crossed fingers: light skin tone
-1F91E 1F3FC                                ; fully-qualified     # 🤞🏼 crossed fingers: medium-light skin tone
-1F91E 1F3FD                                ; fully-qualified     # 🤞🏽 crossed fingers: medium skin tone
-1F91E 1F3FE                                ; fully-qualified     # 🤞🏾 crossed fingers: medium-dark skin tone
-1F91E 1F3FF                                ; fully-qualified     # 🤞🏿 crossed fingers: dark skin tone
-1F596                                      ; fully-qualified     # 🖖 vulcan salute
-1F596 1F3FB                                ; fully-qualified     # 🖖🏻 vulcan salute: light skin tone
-1F596 1F3FC                                ; fully-qualified     # 🖖🏼 vulcan salute: medium-light skin tone
-1F596 1F3FD                                ; fully-qualified     # 🖖🏽 vulcan salute: medium skin tone
-1F596 1F3FE                                ; fully-qualified     # 🖖🏾 vulcan salute: medium-dark skin tone
-1F596 1F3FF                                ; fully-qualified     # 🖖🏿 vulcan salute: dark skin tone
-1F918                                      ; fully-qualified     # 🤘 sign of the horns
-1F918 1F3FB                                ; fully-qualified     # 🤘🏻 sign of the horns: light skin tone
-1F918 1F3FC                                ; fully-qualified     # 🤘🏼 sign of the horns: medium-light skin tone
-1F918 1F3FD                                ; fully-qualified     # 🤘🏽 sign of the horns: medium skin tone
-1F918 1F3FE                                ; fully-qualified     # 🤘🏾 sign of the horns: medium-dark skin tone
-1F918 1F3FF                                ; fully-qualified     # 🤘🏿 sign of the horns: dark skin tone
-1F919                                      ; fully-qualified     # 🤙 call me hand
-1F919 1F3FB                                ; fully-qualified     # 🤙🏻 call me hand: light skin tone
-1F919 1F3FC                                ; fully-qualified     # 🤙🏼 call me hand: medium-light skin tone
-1F919 1F3FD                                ; fully-qualified     # 🤙🏽 call me hand: medium skin tone
-1F919 1F3FE                                ; fully-qualified     # 🤙🏾 call me hand: medium-dark skin tone
-1F919 1F3FF                                ; fully-qualified     # 🤙🏿 call me hand: dark skin tone
-1F590 FE0F                                 ; fully-qualified     # 🖐️ raised hand with fingers splayed
-1F590 1F3FB                                ; fully-qualified     # 🖐🏻 raised hand with fingers splayed: light skin tone
-1F590 1F3FC                                ; fully-qualified     # 🖐🏼 raised hand with fingers splayed: medium-light skin tone
-1F590 1F3FD                                ; fully-qualified     # 🖐🏽 raised hand with fingers splayed: medium skin tone
-1F590 1F3FE                                ; fully-qualified     # 🖐🏾 raised hand with fingers splayed: medium-dark skin tone
-1F590 1F3FF                                ; fully-qualified     # 🖐🏿 raised hand with fingers splayed: dark skin tone
-270B                                       ; fully-qualified     # ✋ raised hand
-270B 1F3FB                                 ; fully-qualified     # ✋🏻 raised hand: light skin tone
-270B 1F3FC                                 ; fully-qualified     # ✋🏼 raised hand: medium-light skin tone
-270B 1F3FD                                 ; fully-qualified     # ✋🏽 raised hand: medium skin tone
-270B 1F3FE                                 ; fully-qualified     # ✋🏾 raised hand: medium-dark skin tone
-270B 1F3FF                                 ; fully-qualified     # ✋🏿 raised hand: dark skin tone
-1F44C                                      ; fully-qualified     # 👌 OK hand
-1F44C 1F3FB                                ; fully-qualified     # 👌🏻 OK hand: light skin tone
-1F44C 1F3FC                                ; fully-qualified     # 👌🏼 OK hand: medium-light skin tone
-1F44C 1F3FD                                ; fully-qualified     # 👌🏽 OK hand: medium skin tone
-1F44C 1F3FE                                ; fully-qualified     # 👌🏾 OK hand: medium-dark skin tone
-1F44C 1F3FF                                ; fully-qualified     # 👌🏿 OK hand: dark skin tone
-1F44D                                      ; fully-qualified     # 👍 thumbs up
-1F44D 1F3FB                                ; fully-qualified     # 👍🏻 thumbs up: light skin tone
-1F44D 1F3FC                                ; fully-qualified     # 👍🏼 thumbs up: medium-light skin tone
-1F44D 1F3FD                                ; fully-qualified     # 👍🏽 thumbs up: medium skin tone
-1F44D 1F3FE                                ; fully-qualified     # 👍🏾 thumbs up: medium-dark skin tone
-1F44D 1F3FF                                ; fully-qualified     # 👍🏿 thumbs up: dark skin tone
-1F44E                                      ; fully-qualified     # 👎 thumbs down
-1F44E 1F3FB                                ; fully-qualified     # 👎🏻 thumbs down: light skin tone
-1F44E 1F3FC                                ; fully-qualified     # 👎🏼 thumbs down: medium-light skin tone
-1F44E 1F3FD                                ; fully-qualified     # 👎🏽 thumbs down: medium skin tone
-1F44E 1F3FE                                ; fully-qualified     # 👎🏾 thumbs down: medium-dark skin tone
-1F44E 1F3FF                                ; fully-qualified     # 👎🏿 thumbs down: dark skin tone
-270A                                       ; fully-qualified     # ✊ raised fist
-270A 1F3FB                                 ; fully-qualified     # ✊🏻 raised fist: light skin tone
-270A 1F3FC                                 ; fully-qualified     # ✊🏼 raised fist: medium-light skin tone
-270A 1F3FD                                 ; fully-qualified     # ✊🏽 raised fist: medium skin tone
-270A 1F3FE                                 ; fully-qualified     # ✊🏾 raised fist: medium-dark skin tone
-270A 1F3FF                                 ; fully-qualified     # ✊🏿 raised fist: dark skin tone
-1F44A                                      ; fully-qualified     # 👊 oncoming fist
-1F44A 1F3FB                                ; fully-qualified     # 👊🏻 oncoming fist: light skin tone
-1F44A 1F3FC                                ; fully-qualified     # 👊🏼 oncoming fist: medium-light skin tone
-1F44A 1F3FD                                ; fully-qualified     # 👊🏽 oncoming fist: medium skin tone
-1F44A 1F3FE                                ; fully-qualified     # 👊🏾 oncoming fist: medium-dark skin tone
-1F44A 1F3FF                                ; fully-qualified     # 👊🏿 oncoming fist: dark skin tone
-1F91B                                      ; fully-qualified     # 🤛 left-facing fist
-1F91B 1F3FB                                ; fully-qualified     # 🤛🏻 left-facing fist: light skin tone
-1F91B 1F3FC                                ; fully-qualified     # 🤛🏼 left-facing fist: medium-light skin tone
-1F91B 1F3FD                                ; fully-qualified     # 🤛🏽 left-facing fist: medium skin tone
-1F91B 1F3FE                                ; fully-qualified     # 🤛🏾 left-facing fist: medium-dark skin tone
-1F91B 1F3FF                                ; fully-qualified     # 🤛🏿 left-facing fist: dark skin tone
-1F91C                                      ; fully-qualified     # 🤜 right-facing fist
-1F91C 1F3FB                                ; fully-qualified     # 🤜🏻 right-facing fist: light skin tone
-1F91C 1F3FC                                ; fully-qualified     # 🤜🏼 right-facing fist: medium-light skin tone
-1F91C 1F3FD                                ; fully-qualified     # 🤜🏽 right-facing fist: medium skin tone
-1F91C 1F3FE                                ; fully-qualified     # 🤜🏾 right-facing fist: medium-dark skin tone
-1F91C 1F3FF                                ; fully-qualified     # 🤜🏿 right-facing fist: dark skin tone
-1F91A                                      ; fully-qualified     # 🤚 raised back of hand
-1F91A 1F3FB                                ; fully-qualified     # 🤚🏻 raised back of hand: light skin tone
-1F91A 1F3FC                                ; fully-qualified     # 🤚🏼 raised back of hand: medium-light skin tone
-1F91A 1F3FD                                ; fully-qualified     # 🤚🏽 raised back of hand: medium skin tone
-1F91A 1F3FE                                ; fully-qualified     # 🤚🏾 raised back of hand: medium-dark skin tone
-1F91A 1F3FF                                ; fully-qualified     # 🤚🏿 raised back of hand: dark skin tone
-1F44B                                      ; fully-qualified     # 👋 waving hand
-1F44B 1F3FB                                ; fully-qualified     # 👋🏻 waving hand: light skin tone
-1F44B 1F3FC                                ; fully-qualified     # 👋🏼 waving hand: medium-light skin tone
-1F44B 1F3FD                                ; fully-qualified     # 👋🏽 waving hand: medium skin tone
-1F44B 1F3FE                                ; fully-qualified     # 👋🏾 waving hand: medium-dark skin tone
-1F44B 1F3FF                                ; fully-qualified     # 👋🏿 waving hand: dark skin tone
-1F44F                                      ; fully-qualified     # 👏 clapping hands
-1F44F 1F3FB                                ; fully-qualified     # 👏🏻 clapping hands: light skin tone
-1F44F 1F3FC                                ; fully-qualified     # 👏🏼 clapping hands: medium-light skin tone
-1F44F 1F3FD                                ; fully-qualified     # 👏🏽 clapping hands: medium skin tone
-1F44F 1F3FE                                ; fully-qualified     # 👏🏾 clapping hands: medium-dark skin tone
-1F44F 1F3FF                                ; fully-qualified     # 👏🏿 clapping hands: dark skin tone
-270D FE0F                                  ; fully-qualified     # ✍️ writing hand
-270D 1F3FB                                 ; fully-qualified     # ✍🏻 writing hand: light skin tone
-270D 1F3FC                                 ; fully-qualified     # ✍🏼 writing hand: medium-light skin tone
-270D 1F3FD                                 ; fully-qualified     # ✍🏽 writing hand: medium skin tone
-270D 1F3FE                                 ; fully-qualified     # ✍🏾 writing hand: medium-dark skin tone
-270D 1F3FF                                 ; fully-qualified     # ✍🏿 writing hand: dark skin tone
-1F450                                      ; fully-qualified     # 👐 open hands
-1F450 1F3FB                                ; fully-qualified     # 👐🏻 open hands: light skin tone
-1F450 1F3FC                                ; fully-qualified     # 👐🏼 open hands: medium-light skin tone
-1F450 1F3FD                                ; fully-qualified     # 👐🏽 open hands: medium skin tone
-1F450 1F3FE                                ; fully-qualified     # 👐🏾 open hands: medium-dark skin tone
-1F450 1F3FF                                ; fully-qualified     # 👐🏿 open hands: dark skin tone
-1F64C                                      ; fully-qualified     # 🙌 raising hands
-1F64C 1F3FB                                ; fully-qualified     # 🙌🏻 raising hands: light skin tone
-1F64C 1F3FC                                ; fully-qualified     # 🙌🏼 raising hands: medium-light skin tone
-1F64C 1F3FD                                ; fully-qualified     # 🙌🏽 raising hands: medium skin tone
-1F64C 1F3FE                                ; fully-qualified     # 🙌🏾 raising hands: medium-dark skin tone
-1F64C 1F3FF                                ; fully-qualified     # 🙌🏿 raising hands: dark skin tone
-1F64F                                      ; fully-qualified     # 🙏 folded hands
-1F64F 1F3FB                                ; fully-qualified     # 🙏🏻 folded hands: light skin tone
-1F64F 1F3FC                                ; fully-qualified     # 🙏🏼 folded hands: medium-light skin tone
-1F64F 1F3FD                                ; fully-qualified     # 🙏🏽 folded hands: medium skin tone
-1F64F 1F3FE                                ; fully-qualified     # 🙏🏾 folded hands: medium-dark skin tone
-1F64F 1F3FF                                ; fully-qualified     # 🙏🏿 folded hands: dark skin tone
-1F91D                                      ; fully-qualified     # 🤝 handshake
-1F485                                      ; fully-qualified     # 💅 nail polish
-1F485 1F3FB                                ; fully-qualified     # 💅🏻 nail polish: light skin tone
-1F485 1F3FC                                ; fully-qualified     # 💅🏼 nail polish: medium-light skin tone
-1F485 1F3FD                                ; fully-qualified     # 💅🏽 nail polish: medium skin tone
-1F485 1F3FE                                ; fully-qualified     # 💅🏾 nail polish: medium-dark skin tone
-1F485 1F3FF                                ; fully-qualified     # 💅🏿 nail polish: dark skin tone
-1F442                                      ; fully-qualified     # 👂 ear
-1F442 1F3FB                                ; fully-qualified     # 👂🏻 ear: light skin tone
-1F442 1F3FC                                ; fully-qualified     # 👂🏼 ear: medium-light skin tone
-1F442 1F3FD                                ; fully-qualified     # 👂🏽 ear: medium skin tone
-1F442 1F3FE                                ; fully-qualified     # 👂🏾 ear: medium-dark skin tone
-1F442 1F3FF                                ; fully-qualified     # 👂🏿 ear: dark skin tone
-1F443                                      ; fully-qualified     # 👃 nose
-1F443 1F3FB                                ; fully-qualified     # 👃🏻 nose: light skin tone
-1F443 1F3FC                                ; fully-qualified     # 👃🏼 nose: medium-light skin tone
-1F443 1F3FD                                ; fully-qualified     # 👃🏽 nose: medium skin tone
-1F443 1F3FE                                ; fully-qualified     # 👃🏾 nose: medium-dark skin tone
-1F443 1F3FF                                ; fully-qualified     # 👃🏿 nose: dark skin tone
-1F463                                      ; fully-qualified     # 👣 footprints
-1F440                                      ; fully-qualified     # 👀 eyes
-1F441 FE0F                                 ; fully-qualified     # 👁️ eye
-1F441 FE0F 200D 1F5E8 FE0F                 ; fully-qualified     # 👁️‍🗨️ eye in speech bubble
-1F441 200D 1F5E8                           ; non-fully-qualified # 👁‍🗨 eye in speech bubble
-1F441 FE0F 200D 1F5E8                      ; non-fully-qualified # 👁️‍🗨 eye in speech bubble
-1F441 200D 1F5E8 FE0F                      ; non-fully-qualified # 👁‍🗨️ eye in speech bubble
-1F445                                      ; fully-qualified     # 👅 tongue
-1F444                                      ; fully-qualified     # 👄 mouth
-
-# subgroup: emotion
-1F48B                                      ; fully-qualified     # 💋 kiss mark
-1F498                                      ; fully-qualified     # 💘 heart with arrow
-2764 FE0F                                  ; fully-qualified     # ❤️ red heart
-1F493                                      ; fully-qualified     # 💓 beating heart
-1F494                                      ; fully-qualified     # 💔 broken heart
-1F495                                      ; fully-qualified     # 💕 two hearts
-1F496                                      ; fully-qualified     # 💖 sparkling heart
-1F497                                      ; fully-qualified     # 💗 growing heart
-1F499                                      ; fully-qualified     # 💙 blue heart
-1F49A                                      ; fully-qualified     # 💚 green heart
-1F49B                                      ; fully-qualified     # 💛 yellow heart
-1F49C                                      ; fully-qualified     # 💜 purple heart
-1F5A4                                      ; fully-qualified     # 🖤 black heart
-1F49D                                      ; fully-qualified     # 💝 heart with ribbon
-1F49E                                      ; fully-qualified     # 💞 revolving hearts
-1F49F                                      ; fully-qualified     # 💟 heart decoration
-2763 FE0F                                  ; fully-qualified     # ❣️ heavy heart exclamation
-1F48C                                      ; fully-qualified     # 💌 love letter
-1F4A4                                      ; fully-qualified     # 💤 zzz
-1F4A2                                      ; fully-qualified     # 💢 anger symbol
-1F4A3                                      ; fully-qualified     # 💣 bomb
-1F4A5                                      ; fully-qualified     # 💥 collision
-1F4A6                                      ; fully-qualified     # 💦 sweat droplets
-1F4A8                                      ; fully-qualified     # 💨 dashing away
-1F4AB                                      ; fully-qualified     # 💫 dizzy
-1F4AC                                      ; fully-qualified     # 💬 speech balloon
-1F5E8 FE0F                                 ; fully-qualified     # 🗨️ left speech bubble
-1F5EF FE0F                                 ; fully-qualified     # 🗯️ right anger bubble
-1F4AD                                      ; fully-qualified     # 💭 thought balloon
-1F573 FE0F                                 ; fully-qualified     # 🕳️ hole
-
-# subgroup: clothing
-1F453                                      ; fully-qualified     # 👓 glasses
-1F576 FE0F                                 ; fully-qualified     # 🕶️ sunglasses
-1F454                                      ; fully-qualified     # 👔 necktie
-1F455                                      ; fully-qualified     # 👕 t-shirt
-1F456                                      ; fully-qualified     # 👖 jeans
-1F457                                      ; fully-qualified     # 👗 dress
-1F458                                      ; fully-qualified     # 👘 kimono
-1F459                                      ; fully-qualified     # 👙 bikini
-1F45A                                      ; fully-qualified     # 👚 woman’s clothes
-1F45B                                      ; fully-qualified     # 👛 purse
-1F45C                                      ; fully-qualified     # 👜 handbag
-1F45D                                      ; fully-qualified     # 👝 clutch bag
-1F6CD FE0F                                 ; fully-qualified     # 🛍️ shopping bags
-1F392                                      ; fully-qualified     # 🎒 school backpack
-1F45E                                      ; fully-qualified     # 👞 man’s shoe
-1F45F                                      ; fully-qualified     # 👟 running shoe
-1F460                                      ; fully-qualified     # 👠 high-heeled shoe
-1F461                                      ; fully-qualified     # 👡 woman’s sandal
-1F462                                      ; fully-qualified     # 👢 woman’s boot
-1F451                                      ; fully-qualified     # 👑 crown
-1F452                                      ; fully-qualified     # 👒 woman’s hat
-1F3A9                                      ; fully-qualified     # 🎩 top hat
-1F393                                      ; fully-qualified     # 🎓 graduation cap
-26D1 FE0F                                  ; fully-qualified     # ⛑️ rescue worker’s helmet
-1F4FF                                      ; fully-qualified     # 📿 prayer beads
-1F484                                      ; fully-qualified     # 💄 lipstick
-1F48D                                      ; fully-qualified     # 💍 ring
-1F48E                                      ; fully-qualified     # 💎 gem stone
-
-# Smileys & People subtotal:		1281
-# Smileys & People subtotal:		391	w/o modifiers
-
-# group: Animals & Nature
-
-# subgroup: animal-mammal
-1F435                                      ; fully-qualified     # 🐵 monkey face
-1F412                                      ; fully-qualified     # 🐒 monkey
-1F98D                                      ; fully-qualified     # 🦍 gorilla
-1F436                                      ; fully-qualified     # 🐶 dog face
-1F415                                      ; fully-qualified     # 🐕 dog
-1F429                                      ; fully-qualified     # 🐩 poodle
-1F43A                                      ; fully-qualified     # 🐺 wolf face
-1F98A                                      ; fully-qualified     # 🦊 fox face
-1F431                                      ; fully-qualified     # 🐱 cat face
-1F408                                      ; fully-qualified     # 🐈 cat
-1F981                                      ; fully-qualified     # 🦁 lion face
-1F42F                                      ; fully-qualified     # 🐯 tiger face
-1F405                                      ; fully-qualified     # 🐅 tiger
-1F406                                      ; fully-qualified     # 🐆 leopard
-1F434                                      ; fully-qualified     # 🐴 horse face
-1F40E                                      ; fully-qualified     # 🐎 horse
-1F98C                                      ; fully-qualified     # 🦌 deer
-1F984                                      ; fully-qualified     # 🦄 unicorn face
-1F42E                                      ; fully-qualified     # 🐮 cow face
-1F402                                      ; fully-qualified     # 🐂 ox
-1F403                                      ; fully-qualified     # 🐃 water buffalo
-1F404                                      ; fully-qualified     # 🐄 cow
-1F437                                      ; fully-qualified     # 🐷 pig face
-1F416                                      ; fully-qualified     # 🐖 pig
-1F417                                      ; fully-qualified     # 🐗 boar
-1F43D                                      ; fully-qualified     # 🐽 pig nose
-1F40F                                      ; fully-qualified     # 🐏 ram
-1F411                                      ; fully-qualified     # 🐑 sheep
-1F410                                      ; fully-qualified     # 🐐 goat
-1F42A                                      ; fully-qualified     # 🐪 camel
-1F42B                                      ; fully-qualified     # 🐫 two-hump camel
-1F418                                      ; fully-qualified     # 🐘 elephant
-1F98F                                      ; fully-qualified     # 🦏 rhinoceros
-1F42D                                      ; fully-qualified     # 🐭 mouse face
-1F401                                      ; fully-qualified     # 🐁 mouse
-1F400                                      ; fully-qualified     # 🐀 rat
-1F439                                      ; fully-qualified     # 🐹 hamster face
-1F430                                      ; fully-qualified     # 🐰 rabbit face
-1F407                                      ; fully-qualified     # 🐇 rabbit
-1F43F FE0F                                 ; fully-qualified     # 🐿️ chipmunk
-1F987                                      ; fully-qualified     # 🦇 bat
-1F43B                                      ; fully-qualified     # 🐻 bear face
-1F428                                      ; fully-qualified     # 🐨 koala
-1F43C                                      ; fully-qualified     # 🐼 panda face
-1F43E                                      ; fully-qualified     # 🐾 paw prints
-
-# subgroup: animal-bird
-1F983                                      ; fully-qualified     # 🦃 turkey
-1F414                                      ; fully-qualified     # 🐔 chicken
-1F413                                      ; fully-qualified     # 🐓 rooster
-1F423                                      ; fully-qualified     # 🐣 hatching chick
-1F424                                      ; fully-qualified     # 🐤 baby chick
-1F425                                      ; fully-qualified     # 🐥 front-facing baby chick
-1F426                                      ; fully-qualified     # 🐦 bird
-1F427                                      ; fully-qualified     # 🐧 penguin
-1F54A FE0F                                 ; fully-qualified     # 🕊️ dove
-1F985                                      ; fully-qualified     # 🦅 eagle
-1F986                                      ; fully-qualified     # 🦆 duck
-1F989                                      ; fully-qualified     # 🦉 owl
-
-# subgroup: animal-amphibian
-1F438                                      ; fully-qualified     # 🐸 frog face
-
-# subgroup: animal-reptile
-1F40A                                      ; fully-qualified     # 🐊 crocodile
-1F422                                      ; fully-qualified     # 🐢 turtle
-1F98E                                      ; fully-qualified     # 🦎 lizard
-1F40D                                      ; fully-qualified     # 🐍 snake
-1F432                                      ; fully-qualified     # 🐲 dragon face
-1F409                                      ; fully-qualified     # 🐉 dragon
-
-# subgroup: animal-marine
-1F433                                      ; fully-qualified     # 🐳 spouting whale
-1F40B                                      ; fully-qualified     # 🐋 whale
-1F42C                                      ; fully-qualified     # 🐬 dolphin
-1F41F                                      ; fully-qualified     # 🐟 fish
-1F420                                      ; fully-qualified     # 🐠 tropical fish
-1F421                                      ; fully-qualified     # 🐡 blowfish
-1F988                                      ; fully-qualified     # 🦈 shark
-1F419                                      ; fully-qualified     # 🐙 octopus
-1F41A                                      ; fully-qualified     # 🐚 spiral shell
-1F980                                      ; fully-qualified     # 🦀 crab
-1F990                                      ; fully-qualified     # 🦐 shrimp
-1F991                                      ; fully-qualified     # 🦑 squid
-
-# subgroup: animal-bug
-1F98B                                      ; fully-qualified     # 🦋 butterfly
-1F40C                                      ; fully-qualified     # 🐌 snail
-1F41B                                      ; fully-qualified     # 🐛 bug
-1F41C                                      ; fully-qualified     # 🐜 ant
-1F41D                                      ; fully-qualified     # 🐝 honeybee
-1F41E                                      ; fully-qualified     # 🐞 lady beetle
-1F577 FE0F                                 ; fully-qualified     # 🕷️ spider
-1F578 FE0F                                 ; fully-qualified     # 🕸️ spider web
-1F982                                      ; fully-qualified     # 🦂 scorpion
-
-# subgroup: plant-flower
-1F490                                      ; fully-qualified     # 💐 bouquet
-1F338                                      ; fully-qualified     # 🌸 cherry blossom
-1F4AE                                      ; fully-qualified     # 💮 white flower
-1F3F5 FE0F                                 ; fully-qualified     # 🏵️ rosette
-1F339                                      ; fully-qualified     # 🌹 rose
-1F940                                      ; fully-qualified     # 🥀 wilted flower
-1F33A                                      ; fully-qualified     # 🌺 hibiscus
-1F33B                                      ; fully-qualified     # 🌻 sunflower
-1F33C                                      ; fully-qualified     # 🌼 blossom
-1F337                                      ; fully-qualified     # 🌷 tulip
-
-# subgroup: plant-other
-1F331                                      ; fully-qualified     # 🌱 seedling
-1F332                                      ; fully-qualified     # 🌲 evergreen tree
-1F333                                      ; fully-qualified     # 🌳 deciduous tree
-1F334                                      ; fully-qualified     # 🌴 palm tree
-1F335                                      ; fully-qualified     # 🌵 cactus
-1F33E                                      ; fully-qualified     # 🌾 sheaf of rice
-1F33F                                      ; fully-qualified     # 🌿 herb
-2618 FE0F                                  ; fully-qualified     # ☘️ shamrock
-1F340                                      ; fully-qualified     # 🍀 four leaf clover
-1F341                                      ; fully-qualified     # 🍁 maple leaf
-1F342                                      ; fully-qualified     # 🍂 fallen leaf
-1F343                                      ; fully-qualified     # 🍃 leaf fluttering in wind
-
-# Animals & Nature subtotal:		107
-# Animals & Nature subtotal:		107	w/o modifiers
-
-# group: Food & Drink
-
-# subgroup: food-fruit
-1F347                                      ; fully-qualified     # 🍇 grapes
-1F348                                      ; fully-qualified     # 🍈 melon
-1F349                                      ; fully-qualified     # 🍉 watermelon
-1F34A                                      ; fully-qualified     # 🍊 tangerine
-1F34B                                      ; fully-qualified     # 🍋 lemon
-1F34C                                      ; fully-qualified     # 🍌 banana
-1F34D                                      ; fully-qualified     # 🍍 pineapple
-1F34E                                      ; fully-qualified     # 🍎 red apple
-1F34F                                      ; fully-qualified     # 🍏 green apple
-1F350                                      ; fully-qualified     # 🍐 pear
-1F351                                      ; fully-qualified     # 🍑 peach
-1F352                                      ; fully-qualified     # 🍒 cherries
-1F353                                      ; fully-qualified     # 🍓 strawberry
-1F95D                                      ; fully-qualified     # 🥝 kiwi fruit
-1F345                                      ; fully-qualified     # 🍅 tomato
-
-# subgroup: food-vegetable
-1F951                                      ; fully-qualified     # 🥑 avocado
-1F346                                      ; fully-qualified     # 🍆 eggplant
-1F954                                      ; fully-qualified     # 🥔 potato
-1F955                                      ; fully-qualified     # 🥕 carrot
-1F33D                                      ; fully-qualified     # 🌽 ear of corn
-1F336 FE0F                                 ; fully-qualified     # 🌶️ hot pepper
-1F952                                      ; fully-qualified     # 🥒 cucumber
-1F344                                      ; fully-qualified     # 🍄 mushroom
-1F95C                                      ; fully-qualified     # 🥜 peanuts
-1F330                                      ; fully-qualified     # 🌰 chestnut
-
-# subgroup: food-prepared
-1F35E                                      ; fully-qualified     # 🍞 bread
-1F950                                      ; fully-qualified     # 🥐 croissant
-1F956                                      ; fully-qualified     # 🥖 baguette bread
-1F95E                                      ; fully-qualified     # 🥞 pancakes
-1F9C0                                      ; fully-qualified     # 🧀 cheese wedge
-1F356                                      ; fully-qualified     # 🍖 meat on bone
-1F357                                      ; fully-qualified     # 🍗 poultry leg
-1F953                                      ; fully-qualified     # 🥓 bacon
-1F354                                      ; fully-qualified     # 🍔 hamburger
-1F35F                                      ; fully-qualified     # 🍟 french fries
-1F355                                      ; fully-qualified     # 🍕 pizza
-1F32D                                      ; fully-qualified     # 🌭 hot dog
-1F32E                                      ; fully-qualified     # 🌮 taco
-1F32F                                      ; fully-qualified     # 🌯 burrito
-1F959                                      ; fully-qualified     # 🥙 stuffed flatbread
-1F95A                                      ; fully-qualified     # 🥚 egg
-1F373                                      ; fully-qualified     # 🍳 cooking
-1F958                                      ; fully-qualified     # 🥘 shallow pan of food
-1F372                                      ; fully-qualified     # 🍲 pot of food
-1F957                                      ; fully-qualified     # 🥗 green salad
-1F37F                                      ; fully-qualified     # 🍿 popcorn
-
-# subgroup: food-asian
-1F371                                      ; fully-qualified     # 🍱 bento box
-1F358                                      ; fully-qualified     # 🍘 rice cracker
-1F359                                      ; fully-qualified     # 🍙 rice ball
-1F35A                                      ; fully-qualified     # 🍚 cooked rice
-1F35B                                      ; fully-qualified     # 🍛 curry rice
-1F35C                                      ; fully-qualified     # 🍜 steaming bowl
-1F35D                                      ; fully-qualified     # 🍝 spaghetti
-1F360                                      ; fully-qualified     # 🍠 roasted sweet potato
-1F362                                      ; fully-qualified     # 🍢 oden
-1F363                                      ; fully-qualified     # 🍣 sushi
-1F364                                      ; fully-qualified     # 🍤 fried shrimp
-1F365                                      ; fully-qualified     # 🍥 fish cake with swirl
-1F361                                      ; fully-qualified     # 🍡 dango
-
-# subgroup: food-sweet
-1F366                                      ; fully-qualified     # 🍦 soft ice cream
-1F367                                      ; fully-qualified     # 🍧 shaved ice
-1F368                                      ; fully-qualified     # 🍨 ice cream
-1F369                                      ; fully-qualified     # 🍩 doughnut
-1F36A                                      ; fully-qualified     # 🍪 cookie
-1F382                                      ; fully-qualified     # 🎂 birthday cake
-1F370                                      ; fully-qualified     # 🍰 shortcake
-1F36B                                      ; fully-qualified     # 🍫 chocolate bar
-1F36C                                      ; fully-qualified     # 🍬 candy
-1F36D                                      ; fully-qualified     # 🍭 lollipop
-1F36E                                      ; fully-qualified     # 🍮 custard
-1F36F                                      ; fully-qualified     # 🍯 honey pot
-
-# subgroup: drink
-1F37C                                      ; fully-qualified     # 🍼 baby bottle
-1F95B                                      ; fully-qualified     # 🥛 glass of milk
-2615                                       ; fully-qualified     # ☕ hot beverage
-1F375                                      ; fully-qualified     # 🍵 teacup without handle
-1F376                                      ; fully-qualified     # 🍶 sake
-1F37E                                      ; fully-qualified     # 🍾 bottle with popping cork
-1F377                                      ; fully-qualified     # 🍷 wine glass
-1F378                                      ; fully-qualified     # 🍸 cocktail glass
-1F379                                      ; fully-qualified     # 🍹 tropical drink
-1F37A                                      ; fully-qualified     # 🍺 beer mug
-1F37B                                      ; fully-qualified     # 🍻 clinking beer mugs
-1F942                                      ; fully-qualified     # 🥂 clinking glasses
-1F943                                      ; fully-qualified     # 🥃 tumbler glass
-
-# subgroup: dishware
-1F37D FE0F                                 ; fully-qualified     # 🍽️ fork and knife with plate
-1F374                                      ; fully-qualified     # 🍴 fork and knife
-1F944                                      ; fully-qualified     # 🥄 spoon
-1F52A                                      ; fully-qualified     # 🔪 kitchen knife
-1F3FA                                      ; fully-qualified     # 🏺 amphora
-
-# Food & Drink subtotal:		89
-# Food & Drink subtotal:		89	w/o modifiers
-
-# group: Travel & Places
-
-# subgroup: place-map
-1F30D                                      ; fully-qualified     # 🌍 globe showing Europe-Africa
-1F30E                                      ; fully-qualified     # 🌎 globe showing Americas
-1F30F                                      ; fully-qualified     # 🌏 globe showing Asia-Australia
-1F310                                      ; fully-qualified     # 🌐 globe with meridians
-1F5FA FE0F                                 ; fully-qualified     # 🗺️ world map
-1F5FE                                      ; fully-qualified     # 🗾 map of Japan
-
-# subgroup: place-geographic
-1F3D4 FE0F                                 ; fully-qualified     # 🏔️ snow-capped mountain
-26F0 FE0F                                  ; fully-qualified     # ⛰️ mountain
-1F30B                                      ; fully-qualified     # 🌋 volcano
-1F5FB                                      ; fully-qualified     # 🗻 mount fuji
-1F3D5 FE0F                                 ; fully-qualified     # 🏕️ camping
-1F3D6 FE0F                                 ; fully-qualified     # 🏖️ beach with umbrella
-1F3DC FE0F                                 ; fully-qualified     # 🏜️ desert
-1F3DD FE0F                                 ; fully-qualified     # 🏝️ desert island
-1F3DE FE0F                                 ; fully-qualified     # 🏞️ national park
-
-# subgroup: place-building
-1F3DF FE0F                                 ; fully-qualified     # 🏟️ stadium
-1F3DB FE0F                                 ; fully-qualified     # 🏛️ classical building
-1F3D7 FE0F                                 ; fully-qualified     # 🏗️ building construction
-1F3D8 FE0F                                 ; fully-qualified     # 🏘️ house
-1F3D9 FE0F                                 ; fully-qualified     # 🏙️ cityscape
-1F3DA FE0F                                 ; fully-qualified     # 🏚️ derelict house
-1F3E0                                      ; fully-qualified     # 🏠 house
-1F3E1                                      ; fully-qualified     # 🏡 house with garden
-1F3E2                                      ; fully-qualified     # 🏢 office building
-1F3E3                                      ; fully-qualified     # 🏣 Japanese post office
-1F3E4                                      ; fully-qualified     # 🏤 post office
-1F3E5                                      ; fully-qualified     # 🏥 hospital
-1F3E6                                      ; fully-qualified     # 🏦 bank
-1F3E8                                      ; fully-qualified     # 🏨 hotel
-1F3E9                                      ; fully-qualified     # 🏩 love hotel
-1F3EA                                      ; fully-qualified     # 🏪 convenience store
-1F3EB                                      ; fully-qualified     # 🏫 school
-1F3EC                                      ; fully-qualified     # 🏬 department store
-1F3ED                                      ; fully-qualified     # 🏭 factory
-1F3EF                                      ; fully-qualified     # 🏯 Japanese castle
-1F3F0                                      ; fully-qualified     # 🏰 castle
-1F492                                      ; fully-qualified     # 💒 wedding
-1F5FC                                      ; fully-qualified     # 🗼 Tokyo tower
-1F5FD                                      ; fully-qualified     # 🗽 Statue of Liberty
-
-# subgroup: place-religious
-26EA                                       ; fully-qualified     # ⛪ church
-1F54C                                      ; fully-qualified     # 🕌 mosque
-1F54D                                      ; fully-qualified     # 🕍 synagogue
-26E9 FE0F                                  ; fully-qualified     # ⛩️ shinto shrine
-1F54B                                      ; fully-qualified     # 🕋 kaaba
-
-# subgroup: place-other
-26F2                                       ; fully-qualified     # ⛲ fountain
-26FA                                       ; fully-qualified     # ⛺ tent
-1F301                                      ; fully-qualified     # 🌁 foggy
-1F303                                      ; fully-qualified     # 🌃 night with stars
-1F304                                      ; fully-qualified     # 🌄 sunrise over mountains
-1F305                                      ; fully-qualified     # 🌅 sunrise
-1F306                                      ; fully-qualified     # 🌆 cityscape at dusk
-1F307                                      ; fully-qualified     # 🌇 sunset
-1F309                                      ; fully-qualified     # 🌉 bridge at night
-2668 FE0F                                  ; fully-qualified     # ♨️ hot springs
-1F30C                                      ; fully-qualified     # 🌌 milky way
-1F3A0                                      ; fully-qualified     # 🎠 carousel horse
-1F3A1                                      ; fully-qualified     # 🎡 ferris wheel
-1F3A2                                      ; fully-qualified     # 🎢 roller coaster
-1F488                                      ; fully-qualified     # 💈 barber pole
-1F3AA                                      ; fully-qualified     # 🎪 circus tent
-1F3AD                                      ; fully-qualified     # 🎭 performing arts
-1F5BC FE0F                                 ; fully-qualified     # 🖼️ framed picture
-1F3A8                                      ; fully-qualified     # 🎨 artist palette
-1F3B0                                      ; fully-qualified     # 🎰 slot machine
-
-# subgroup: transport-ground
-1F682                                      ; fully-qualified     # 🚂 locomotive
-1F683                                      ; fully-qualified     # 🚃 railway car
-1F684                                      ; fully-qualified     # 🚄 high-speed train
-1F685                                      ; fully-qualified     # 🚅 high-speed train with bullet nose
-1F686                                      ; fully-qualified     # 🚆 train
-1F687                                      ; fully-qualified     # 🚇 metro
-1F688                                      ; fully-qualified     # 🚈 light rail
-1F689                                      ; fully-qualified     # 🚉 station
-1F68A                                      ; fully-qualified     # 🚊 tram
-1F69D                                      ; fully-qualified     # 🚝 monorail
-1F69E                                      ; fully-qualified     # 🚞 mountain railway
-1F68B                                      ; fully-qualified     # 🚋 tram car
-1F68C                                      ; fully-qualified     # 🚌 bus
-1F68D                                      ; fully-qualified     # 🚍 oncoming bus
-1F68E                                      ; fully-qualified     # 🚎 trolleybus
-1F690                                      ; fully-qualified     # 🚐 minibus
-1F691                                      ; fully-qualified     # 🚑 ambulance
-1F692                                      ; fully-qualified     # 🚒 fire engine
-1F693                                      ; fully-qualified     # 🚓 police car
-1F694                                      ; fully-qualified     # 🚔 oncoming police car
-1F695                                      ; fully-qualified     # 🚕 taxi
-1F696                                      ; fully-qualified     # 🚖 oncoming taxi
-1F697                                      ; fully-qualified     # 🚗 automobile
-1F698                                      ; fully-qualified     # 🚘 oncoming automobile
-1F699                                      ; fully-qualified     # 🚙 sport utility vehicle
-1F69A                                      ; fully-qualified     # 🚚 delivery truck
-1F69B                                      ; fully-qualified     # 🚛 articulated lorry
-1F69C                                      ; fully-qualified     # 🚜 tractor
-1F6B2                                      ; fully-qualified     # 🚲 bicycle
-1F6F4                                      ; fully-qualified     # 🛴 kick scooter
-1F6F5                                      ; fully-qualified     # 🛵 motor scooter
-1F68F                                      ; fully-qualified     # 🚏 bus stop
-1F6E3 FE0F                                 ; fully-qualified     # 🛣️ motorway
-1F6E4 FE0F                                 ; fully-qualified     # 🛤️ railway track
-26FD                                       ; fully-qualified     # ⛽ fuel pump
-1F6A8                                      ; fully-qualified     # 🚨 police car light
-1F6A5                                      ; fully-qualified     # 🚥 horizontal traffic light
-1F6A6                                      ; fully-qualified     # 🚦 vertical traffic light
-1F6A7                                      ; fully-qualified     # 🚧 construction
-1F6D1                                      ; fully-qualified     # 🛑 stop sign
-
-# subgroup: transport-water
-2693                                       ; fully-qualified     # ⚓ anchor
-26F5                                       ; fully-qualified     # ⛵ sailboat
-1F6F6                                      ; fully-qualified     # 🛶 canoe
-1F6A4                                      ; fully-qualified     # 🚤 speedboat
-1F6F3 FE0F                                 ; fully-qualified     # 🛳️ passenger ship
-26F4 FE0F                                  ; fully-qualified     # ⛴️ ferry
-1F6E5 FE0F                                 ; fully-qualified     # 🛥️ motor boat
-1F6A2                                      ; fully-qualified     # 🚢 ship
-
-# subgroup: transport-air
-2708 FE0F                                  ; fully-qualified     # ✈️ airplane
-1F6E9 FE0F                                 ; fully-qualified     # 🛩️ small airplane
-1F6EB                                      ; fully-qualified     # 🛫 airplane departure
-1F6EC                                      ; fully-qualified     # 🛬 airplane arrival
-1F4BA                                      ; fully-qualified     # 💺 seat
-1F681                                      ; fully-qualified     # 🚁 helicopter
-1F69F                                      ; fully-qualified     # 🚟 suspension railway
-1F6A0                                      ; fully-qualified     # 🚠 mountain cableway
-1F6A1                                      ; fully-qualified     # 🚡 aerial tramway
-1F680                                      ; fully-qualified     # 🚀 rocket
-1F6F0 FE0F                                 ; fully-qualified     # 🛰️ satellite
-
-# subgroup: hotel
-1F6CE FE0F                                 ; fully-qualified     # 🛎️ bellhop bell
-1F6AA                                      ; fully-qualified     # 🚪 door
-1F6CC                                      ; fully-qualified     # 🛌 person in bed
-1F6CC 1F3FB                                ; fully-qualified     # 🛌🏻 person in bed: light skin tone
-1F6CC 1F3FC                                ; fully-qualified     # 🛌🏼 person in bed: medium-light skin tone
-1F6CC 1F3FD                                ; fully-qualified     # 🛌🏽 person in bed: medium skin tone
-1F6CC 1F3FE                                ; fully-qualified     # 🛌🏾 person in bed: medium-dark skin tone
-1F6CC 1F3FF                                ; fully-qualified     # 🛌🏿 person in bed: dark skin tone
-1F6CF FE0F                                 ; fully-qualified     # 🛏️ bed
-1F6CB FE0F                                 ; fully-qualified     # 🛋️ couch and lamp
-1F6BD                                      ; fully-qualified     # 🚽 toilet
-1F6BF                                      ; fully-qualified     # 🚿 shower
-1F6C0                                      ; fully-qualified     # 🛀 person taking bath
-1F6C0 1F3FB                                ; fully-qualified     # 🛀🏻 person taking bath: light skin tone
-1F6C0 1F3FC                                ; fully-qualified     # 🛀🏼 person taking bath: medium-light skin tone
-1F6C0 1F3FD                                ; fully-qualified     # 🛀🏽 person taking bath: medium skin tone
-1F6C0 1F3FE                                ; fully-qualified     # 🛀🏾 person taking bath: medium-dark skin tone
-1F6C0 1F3FF                                ; fully-qualified     # 🛀🏿 person taking bath: dark skin tone
-1F6C1                                      ; fully-qualified     # 🛁 bathtub
-
-# subgroup: time
-231B                                       ; fully-qualified     # ⌛ hourglass
-23F3                                       ; fully-qualified     # ⏳ hourglass with flowing sand
-231A                                       ; fully-qualified     # ⌚ watch
-23F0                                       ; fully-qualified     # ⏰ alarm clock
-23F1 FE0F                                  ; fully-qualified     # ⏱️ stopwatch
-23F2 FE0F                                  ; fully-qualified     # ⏲️ timer clock
-1F570 FE0F                                 ; fully-qualified     # 🕰️ mantelpiece clock
-1F55B                                      ; fully-qualified     # 🕛 twelve o’clock
-1F567                                      ; fully-qualified     # 🕧 twelve-thirty
-1F550                                      ; fully-qualified     # 🕐 one o’clock
-1F55C                                      ; fully-qualified     # 🕜 one-thirty
-1F551                                      ; fully-qualified     # 🕑 two o’clock
-1F55D                                      ; fully-qualified     # 🕝 two-thirty
-1F552                                      ; fully-qualified     # 🕒 three o’clock
-1F55E                                      ; fully-qualified     # 🕞 three-thirty
-1F553                                      ; fully-qualified     # 🕓 four o’clock
-1F55F                                      ; fully-qualified     # 🕟 four-thirty
-1F554                                      ; fully-qualified     # 🕔 five o’clock
-1F560                                      ; fully-qualified     # 🕠 five-thirty
-1F555                                      ; fully-qualified     # 🕕 six o’clock
-1F561                                      ; fully-qualified     # 🕡 six-thirty
-1F556                                      ; fully-qualified     # 🕖 seven o’clock
-1F562                                      ; fully-qualified     # 🕢 seven-thirty
-1F557                                      ; fully-qualified     # 🕗 eight o’clock
-1F563                                      ; fully-qualified     # 🕣 eight-thirty
-1F558                                      ; fully-qualified     # 🕘 nine o’clock
-1F564                                      ; fully-qualified     # 🕤 nine-thirty
-1F559                                      ; fully-qualified     # 🕙 ten o’clock
-1F565                                      ; fully-qualified     # 🕥 ten-thirty
-1F55A                                      ; fully-qualified     # 🕚 eleven o’clock
-1F566                                      ; fully-qualified     # 🕦 eleven-thirty
-
-# subgroup: sky & weather
-1F311                                      ; fully-qualified     # 🌑 new moon
-1F312                                      ; fully-qualified     # 🌒 waxing crescent moon
-1F313                                      ; fully-qualified     # 🌓 first quarter moon
-1F314                                      ; fully-qualified     # 🌔 waxing gibbous moon
-1F315                                      ; fully-qualified     # 🌕 full moon
-1F316                                      ; fully-qualified     # 🌖 waning gibbous moon
-1F317                                      ; fully-qualified     # 🌗 last quarter moon
-1F318                                      ; fully-qualified     # 🌘 waning crescent moon
-1F319                                      ; fully-qualified     # 🌙 crescent moon
-1F31A                                      ; fully-qualified     # 🌚 new moon face
-1F31B                                      ; fully-qualified     # 🌛 first quarter moon with face
-1F31C                                      ; fully-qualified     # 🌜 last quarter moon with face
-1F321 FE0F                                 ; fully-qualified     # 🌡️ thermometer
-2600 FE0F                                  ; fully-qualified     # ☀️ sun
-1F31D                                      ; fully-qualified     # 🌝 full moon with face
-1F31E                                      ; fully-qualified     # 🌞 sun with face
-2B50                                       ; fully-qualified     # ⭐ white medium star
-1F31F                                      ; fully-qualified     # 🌟 glowing star
-1F320                                      ; fully-qualified     # 🌠 shooting star
-2601 FE0F                                  ; fully-qualified     # ☁️ cloud
-26C5                                       ; fully-qualified     # ⛅ sun behind cloud
-26C8 FE0F                                  ; fully-qualified     # ⛈️ cloud with lightning and rain
-1F324 FE0F                                 ; fully-qualified     # 🌤️ sun behind small cloud
-1F325 FE0F                                 ; fully-qualified     # 🌥️ sun behind large cloud
-1F326 FE0F                                 ; fully-qualified     # 🌦️ sun behind rain cloud
-1F327 FE0F                                 ; fully-qualified     # 🌧️ cloud with rain
-1F328 FE0F                                 ; fully-qualified     # 🌨️ cloud with snow
-1F329 FE0F                                 ; fully-qualified     # 🌩️ cloud with lightning
-1F32A FE0F                                 ; fully-qualified     # 🌪️ tornado
-1F32B FE0F                                 ; fully-qualified     # 🌫️ fog
-1F32C FE0F                                 ; fully-qualified     # 🌬️ wind face
-1F300                                      ; fully-qualified     # 🌀 cyclone
-1F308                                      ; fully-qualified     # 🌈 rainbow
-1F302                                      ; fully-qualified     # 🌂 closed umbrella
-2602 FE0F                                  ; fully-qualified     # ☂️ umbrella
-2614                                       ; fully-qualified     # ☔ umbrella with rain drops
-26F1 FE0F                                  ; fully-qualified     # ⛱️ umbrella on ground
-26A1                                       ; fully-qualified     # ⚡ high voltage
-2744 FE0F                                  ; fully-qualified     # ❄️ snowflake
-2603 FE0F                                  ; fully-qualified     # ☃️ snowman
-26C4                                       ; fully-qualified     # ⛄ snowman without snow
-2604 FE0F                                  ; fully-qualified     # ☄️ comet
-1F525                                      ; fully-qualified     # 🔥 fire
-1F4A7                                      ; fully-qualified     # 💧 droplet
-1F30A                                      ; fully-qualified     # 🌊 water wave
-
-# Travel & Places subtotal:		218
-# Travel & Places subtotal:		208	w/o modifiers
-
-# group: Activities
-
-# subgroup: event
-1F383                                      ; fully-qualified     # 🎃 jack-o-lantern
-1F384                                      ; fully-qualified     # 🎄 Christmas tree
-1F386                                      ; fully-qualified     # 🎆 fireworks
-1F387                                      ; fully-qualified     # 🎇 sparkler
-2728                                       ; fully-qualified     # ✨ sparkles
-1F388                                      ; fully-qualified     # 🎈 balloon
-1F389                                      ; fully-qualified     # 🎉 party popper
-1F38A                                      ; fully-qualified     # 🎊 confetti ball
-1F38B                                      ; fully-qualified     # 🎋 tanabata tree
-1F38D                                      ; fully-qualified     # 🎍 pine decoration
-1F38E                                      ; fully-qualified     # 🎎 Japanese dolls
-1F38F                                      ; fully-qualified     # 🎏 carp streamer
-1F390                                      ; fully-qualified     # 🎐 wind chime
-1F391                                      ; fully-qualified     # 🎑 moon viewing ceremony
-1F380                                      ; fully-qualified     # 🎀 ribbon
-1F381                                      ; fully-qualified     # 🎁 wrapped gift
-1F397 FE0F                                 ; fully-qualified     # 🎗️ reminder ribbon
-1F39F FE0F                                 ; fully-qualified     # 🎟️ admission tickets
-1F3AB                                      ; fully-qualified     # 🎫 ticket
-
-# subgroup: award-medal
-1F396 FE0F                                 ; fully-qualified     # 🎖️ military medal
-1F3C6                                      ; fully-qualified     # 🏆 trophy
-1F3C5                                      ; fully-qualified     # 🏅 sports medal
-1F947                                      ; fully-qualified     # 🥇 1st place medal
-1F948                                      ; fully-qualified     # 🥈 2nd place medal
-1F949                                      ; fully-qualified     # 🥉 3rd place medal
-
-# subgroup: sport
-26BD                                       ; fully-qualified     # ⚽ soccer ball
-26BE                                       ; fully-qualified     # ⚾ baseball
-1F3C0                                      ; fully-qualified     # 🏀 basketball
-1F3D0                                      ; fully-qualified     # 🏐 volleyball
-1F3C8                                      ; fully-qualified     # 🏈 american football
-1F3C9                                      ; fully-qualified     # 🏉 rugby football
-1F3BE                                      ; fully-qualified     # 🎾 tennis
-1F3B1                                      ; fully-qualified     # 🎱 pool 8 ball
-1F3B3                                      ; fully-qualified     # 🎳 bowling
-1F3CF                                      ; fully-qualified     # 🏏 cricket
-1F3D1                                      ; fully-qualified     # 🏑 field hockey
-1F3D2                                      ; fully-qualified     # 🏒 ice hockey
-1F3D3                                      ; fully-qualified     # 🏓 ping pong
-1F3F8                                      ; fully-qualified     # 🏸 badminton
-1F94A                                      ; fully-qualified     # 🥊 boxing glove
-1F94B                                      ; fully-qualified     # 🥋 martial arts uniform
-1F945                                      ; fully-qualified     # 🥅 goal net
-1F3AF                                      ; fully-qualified     # 🎯 direct hit
-26F3                                       ; fully-qualified     # ⛳ flag in hole
-26F8 FE0F                                  ; fully-qualified     # ⛸️ ice skate
-1F3A3                                      ; fully-qualified     # 🎣 fishing pole
-1F3BD                                      ; fully-qualified     # 🎽 running shirt
-1F3BF                                      ; fully-qualified     # 🎿 skis
-
-# subgroup: game
-1F3AE                                      ; fully-qualified     # 🎮 video game
-1F579 FE0F                                 ; fully-qualified     # 🕹️ joystick
-1F3B2                                      ; fully-qualified     # 🎲 game die
-2660 FE0F                                  ; fully-qualified     # ♠️ spade suit
-2665 FE0F                                  ; fully-qualified     # ♥️ heart suit
-2666 FE0F                                  ; fully-qualified     # ♦️ diamond suit
-2663 FE0F                                  ; fully-qualified     # ♣️ club suit
-1F0CF                                      ; fully-qualified     # 🃏 joker
-1F004                                      ; fully-qualified     # 🀄 mahjong red dragon
-1F3B4                                      ; fully-qualified     # 🎴 flower playing cards
-
-# Activities subtotal:		58
-# Activities subtotal:		58	w/o modifiers
-
-# group: Objects
-
-# subgroup: sound
-1F507                                      ; fully-qualified     # 🔇 muted speaker
-1F508                                      ; fully-qualified     # 🔈 speaker low volume
-1F509                                      ; fully-qualified     # 🔉 speaker medium volume
-1F50A                                      ; fully-qualified     # 🔊 speaker high volume
-1F4E2                                      ; fully-qualified     # 📢 loudspeaker
-1F4E3                                      ; fully-qualified     # 📣 megaphone
-1F4EF                                      ; fully-qualified     # 📯 postal horn
-1F514                                      ; fully-qualified     # 🔔 bell
-1F515                                      ; fully-qualified     # 🔕 bell with slash
-
-# subgroup: music
-1F3BC                                      ; fully-qualified     # 🎼 musical score
-1F3B5                                      ; fully-qualified     # 🎵 musical note
-1F3B6                                      ; fully-qualified     # 🎶 musical notes
-1F399 FE0F                                 ; fully-qualified     # 🎙️ studio microphone
-1F39A FE0F                                 ; fully-qualified     # 🎚️ level slider
-1F39B FE0F                                 ; fully-qualified     # 🎛️ control knobs
-1F3A4                                      ; fully-qualified     # 🎤 microphone
-1F3A7                                      ; fully-qualified     # 🎧 headphone
-1F4FB                                      ; fully-qualified     # 📻 radio
-
-# subgroup: musical-instrument
-1F3B7                                      ; fully-qualified     # 🎷 saxophone
-1F3B8                                      ; fully-qualified     # 🎸 guitar
-1F3B9                                      ; fully-qualified     # 🎹 musical keyboard
-1F3BA                                      ; fully-qualified     # 🎺 trumpet
-1F3BB                                      ; fully-qualified     # 🎻 violin
-1F941                                      ; fully-qualified     # 🥁 drum
-
-# subgroup: phone
-1F4F1                                      ; fully-qualified     # 📱 mobile phone
-1F4F2                                      ; fully-qualified     # 📲 mobile phone with arrow
-260E FE0F                                  ; fully-qualified     # ☎️ telephone
-1F4DE                                      ; fully-qualified     # 📞 telephone receiver
-1F4DF                                      ; fully-qualified     # 📟 pager
-1F4E0                                      ; fully-qualified     # 📠 fax machine
-
-# subgroup: computer
-1F50B                                      ; fully-qualified     # 🔋 battery
-1F50C                                      ; fully-qualified     # 🔌 electric plug
-1F4BB                                      ; fully-qualified     # 💻 laptop computer
-1F5A5 FE0F                                 ; fully-qualified     # 🖥️ desktop computer
-1F5A8 FE0F                                 ; fully-qualified     # 🖨️ printer
-2328 FE0F                                  ; fully-qualified     # ⌨️ keyboard
-1F5B1 FE0F                                 ; fully-qualified     # 🖱️ computer mouse
-1F5B2 FE0F                                 ; fully-qualified     # 🖲️ trackball
-1F4BD                                      ; fully-qualified     # 💽 computer disk
-1F4BE                                      ; fully-qualified     # 💾 floppy disk
-1F4BF                                      ; fully-qualified     # 💿 optical disk
-1F4C0                                      ; fully-qualified     # 📀 dvd
-
-# subgroup: light & video
-1F3A5                                      ; fully-qualified     # 🎥 movie camera
-1F39E FE0F                                 ; fully-qualified     # 🎞️ film frames
-1F4FD FE0F                                 ; fully-qualified     # 📽️ film projector
-1F3AC                                      ; fully-qualified     # 🎬 clapper board
-1F4FA                                      ; fully-qualified     # 📺 television
-1F4F7                                      ; fully-qualified     # 📷 camera
-1F4F8                                      ; fully-qualified     # 📸 camera with flash
-1F4F9                                      ; fully-qualified     # 📹 video camera
-1F4FC                                      ; fully-qualified     # 📼 videocassette
-1F50D                                      ; fully-qualified     # 🔍 left-pointing magnifying glass
-1F50E                                      ; fully-qualified     # 🔎 right-pointing magnifying glass
-1F52C                                      ; fully-qualified     # 🔬 microscope
-1F52D                                      ; fully-qualified     # 🔭 telescope
-1F4E1                                      ; fully-qualified     # 📡 satellite antenna
-1F56F FE0F                                 ; fully-qualified     # 🕯️ candle
-1F4A1                                      ; fully-qualified     # 💡 light bulb
-1F526                                      ; fully-qualified     # 🔦 flashlight
-1F3EE                                      ; fully-qualified     # 🏮 red paper lantern
-
-# subgroup: book-paper
-1F4D4                                      ; fully-qualified     # 📔 notebook with decorative cover
-1F4D5                                      ; fully-qualified     # 📕 closed book
-1F4D6                                      ; fully-qualified     # 📖 open book
-1F4D7                                      ; fully-qualified     # 📗 green book
-1F4D8                                      ; fully-qualified     # 📘 blue book
-1F4D9                                      ; fully-qualified     # 📙 orange book
-1F4DA                                      ; fully-qualified     # 📚 books
-1F4D3                                      ; fully-qualified     # 📓 notebook
-1F4D2                                      ; fully-qualified     # 📒 ledger
-1F4C3                                      ; fully-qualified     # 📃 page with curl
-1F4DC                                      ; fully-qualified     # 📜 scroll
-1F4C4                                      ; fully-qualified     # 📄 page facing up
-1F4F0                                      ; fully-qualified     # 📰 newspaper
-1F5DE FE0F                                 ; fully-qualified     # 🗞️ rolled-up newspaper
-1F4D1                                      ; fully-qualified     # 📑 bookmark tabs
-1F516                                      ; fully-qualified     # 🔖 bookmark
-1F3F7 FE0F                                 ; fully-qualified     # 🏷️ label
-
-# subgroup: money
-1F4B0                                      ; fully-qualified     # 💰 money bag
-1F4B4                                      ; fully-qualified     # 💴 yen banknote
-1F4B5                                      ; fully-qualified     # 💵 dollar banknote
-1F4B6                                      ; fully-qualified     # 💶 euro banknote
-1F4B7                                      ; fully-qualified     # 💷 pound banknote
-1F4B8                                      ; fully-qualified     # 💸 money with wings
-1F4B3                                      ; fully-qualified     # 💳 credit card
-1F4B9                                      ; fully-qualified     # 💹 chart increasing with yen
-1F4B1                                      ; fully-qualified     # 💱 currency exchange
-1F4B2                                      ; fully-qualified     # 💲 heavy dollar sign
-
-# subgroup: mail
-2709 FE0F                                  ; fully-qualified     # ✉️ envelope
-1F4E7                                      ; fully-qualified     # 📧 e-mail
-1F4E8                                      ; fully-qualified     # 📨 incoming envelope
-1F4E9                                      ; fully-qualified     # 📩 envelope with arrow
-1F4E4                                      ; fully-qualified     # 📤 outbox tray
-1F4E5                                      ; fully-qualified     # 📥 inbox tray
-1F4E6                                      ; fully-qualified     # 📦 package
-1F4EB                                      ; fully-qualified     # 📫 closed mailbox with raised flag
-1F4EA                                      ; fully-qualified     # 📪 closed mailbox with lowered flag
-1F4EC                                      ; fully-qualified     # 📬 open mailbox with raised flag
-1F4ED                                      ; fully-qualified     # 📭 open mailbox with lowered flag
-1F4EE                                      ; fully-qualified     # 📮 postbox
-1F5F3 FE0F                                 ; fully-qualified     # 🗳️ ballot box with ballot
-
-# subgroup: writing
-270F FE0F                                  ; fully-qualified     # ✏️ pencil
-2712 FE0F                                  ; fully-qualified     # ✒️ black nib
-1F58B FE0F                                 ; fully-qualified     # 🖋️ fountain pen
-1F58A FE0F                                 ; fully-qualified     # 🖊️ pen
-1F58C FE0F                                 ; fully-qualified     # 🖌️ paintbrush
-1F58D FE0F                                 ; fully-qualified     # 🖍️ crayon
-1F4DD                                      ; fully-qualified     # 📝 memo
-
-# subgroup: office
-1F4BC                                      ; fully-qualified     # 💼 briefcase
-1F4C1                                      ; fully-qualified     # 📁 file folder
-1F4C2                                      ; fully-qualified     # 📂 open file folder
-1F5C2 FE0F                                 ; fully-qualified     # 🗂️ card index dividers
-1F4C5                                      ; fully-qualified     # 📅 calendar
-1F4C6                                      ; fully-qualified     # 📆 tear-off calendar
-1F5D2 FE0F                                 ; fully-qualified     # 🗒️ spiral notepad
-1F5D3 FE0F                                 ; fully-qualified     # 🗓️ spiral calendar
-1F4C7                                      ; fully-qualified     # 📇 card index
-1F4C8                                      ; fully-qualified     # 📈 chart increasing
-1F4C9                                      ; fully-qualified     # 📉 chart decreasing
-1F4CA                                      ; fully-qualified     # 📊 bar chart
-1F4CB                                      ; fully-qualified     # 📋 clipboard
-1F4CC                                      ; fully-qualified     # 📌 pushpin
-1F4CD                                      ; fully-qualified     # 📍 round pushpin
-1F4CE                                      ; fully-qualified     # 📎 paperclip
-1F587 FE0F                                 ; fully-qualified     # 🖇️ linked paperclips
-1F4CF                                      ; fully-qualified     # 📏 straight ruler
-1F4D0                                      ; fully-qualified     # 📐 triangular ruler
-2702 FE0F                                  ; fully-qualified     # ✂️ scissors
-1F5C3 FE0F                                 ; fully-qualified     # 🗃️ card file box
-1F5C4 FE0F                                 ; fully-qualified     # 🗄️ file cabinet
-1F5D1 FE0F                                 ; fully-qualified     # 🗑️ wastebasket
-
-# subgroup: lock
-1F512                                      ; fully-qualified     # 🔒 locked
-1F513                                      ; fully-qualified     # 🔓 unlocked
-1F50F                                      ; fully-qualified     # 🔏 locked with pen
-1F510                                      ; fully-qualified     # 🔐 locked with key
-1F511                                      ; fully-qualified     # 🔑 key
-1F5DD FE0F                                 ; fully-qualified     # 🗝️ old key
-
-# subgroup: tool
-1F528                                      ; fully-qualified     # 🔨 hammer
-26CF FE0F                                  ; fully-qualified     # ⛏️ pick
-2692 FE0F                                  ; fully-qualified     # ⚒️ hammer and pick
-1F6E0 FE0F                                 ; fully-qualified     # 🛠️ hammer and wrench
-1F5E1 FE0F                                 ; fully-qualified     # 🗡️ dagger
-2694 FE0F                                  ; fully-qualified     # ⚔️ crossed swords
-1F52B                                      ; fully-qualified     # 🔫 pistol
-1F3F9                                      ; fully-qualified     # 🏹 bow and arrow
-1F6E1 FE0F                                 ; fully-qualified     # 🛡️ shield
-1F527                                      ; fully-qualified     # 🔧 wrench
-1F529                                      ; fully-qualified     # 🔩 nut and bolt
-2699 FE0F                                  ; fully-qualified     # ⚙️ gear
-1F5DC FE0F                                 ; fully-qualified     # 🗜️ clamp
-2697 FE0F                                  ; fully-qualified     # ⚗️ alembic
-2696 FE0F                                  ; fully-qualified     # ⚖️ balance scale
-1F517                                      ; fully-qualified     # 🔗 link
-26D3 FE0F                                  ; fully-qualified     # ⛓️ chains
-
-# subgroup: medical
-1F489                                      ; fully-qualified     # 💉 syringe
-1F48A                                      ; fully-qualified     # 💊 pill
-
-# subgroup: other-object
-1F6AC                                      ; fully-qualified     # 🚬 cigarette
-26B0 FE0F                                  ; fully-qualified     # ⚰️ coffin
-26B1 FE0F                                  ; fully-qualified     # ⚱️ funeral urn
-1F5FF                                      ; fully-qualified     # 🗿 moai
-1F6E2 FE0F                                 ; fully-qualified     # 🛢️ oil drum
-1F52E                                      ; fully-qualified     # 🔮 crystal ball
-1F6D2                                      ; fully-qualified     # 🛒 shopping cart
-
-# Objects subtotal:		162
-# Objects subtotal:		162	w/o modifiers
-
-# group: Symbols
-
-# subgroup: transport-sign
-1F3E7                                      ; fully-qualified     # 🏧 ATM sign
-1F6AE                                      ; fully-qualified     # 🚮 litter in bin sign
-1F6B0                                      ; fully-qualified     # 🚰 potable water
-267F                                       ; fully-qualified     # ♿ wheelchair symbol
-1F6B9                                      ; fully-qualified     # 🚹 men’s room
-1F6BA                                      ; fully-qualified     # 🚺 women’s room
-1F6BB                                      ; fully-qualified     # 🚻 restroom
-1F6BC                                      ; fully-qualified     # 🚼 baby symbol
-1F6BE                                      ; fully-qualified     # 🚾 water closet
-1F6C2                                      ; fully-qualified     # 🛂 passport control
-1F6C3                                      ; fully-qualified     # 🛃 customs
-1F6C4                                      ; fully-qualified     # 🛄 baggage claim
-1F6C5                                      ; fully-qualified     # 🛅 left luggage
-
-# subgroup: warning
-26A0 FE0F                                  ; fully-qualified     # ⚠️ warning
-1F6B8                                      ; fully-qualified     # 🚸 children crossing
-26D4                                       ; fully-qualified     # ⛔ no entry
-1F6AB                                      ; fully-qualified     # 🚫 prohibited
-1F6B3                                      ; fully-qualified     # 🚳 no bicycles
-1F6AD                                      ; fully-qualified     # 🚭 no smoking
-1F6AF                                      ; fully-qualified     # 🚯 no littering
-1F6B1                                      ; fully-qualified     # 🚱 non-potable water
-1F6B7                                      ; fully-qualified     # 🚷 no pedestrians
-1F4F5                                      ; fully-qualified     # 📵 no mobile phones
-1F51E                                      ; fully-qualified     # 🔞 no one under eighteen
-2622 FE0F                                  ; fully-qualified     # ☢️ radioactive
-2623 FE0F                                  ; fully-qualified     # ☣️ biohazard
-
-# subgroup: arrow
-2B06 FE0F                                  ; fully-qualified     # ⬆️ up arrow
-2197 FE0F                                  ; fully-qualified     # ↗️ up-right arrow
-27A1 FE0F                                  ; fully-qualified     # ➡️ right arrow
-2198 FE0F                                  ; fully-qualified     # ↘️ down-right arrow
-2B07 FE0F                                  ; fully-qualified     # ⬇️ down arrow
-2199 FE0F                                  ; fully-qualified     # ↙️ down-left arrow
-2B05 FE0F                                  ; fully-qualified     # ⬅️ left arrow
-2196 FE0F                                  ; fully-qualified     # ↖️ up-left arrow
-2195 FE0F                                  ; fully-qualified     # ↕️ up-down arrow
-2194 FE0F                                  ; fully-qualified     # ↔️ left-right arrow
-21A9 FE0F                                  ; fully-qualified     # ↩️ right arrow curving left
-21AA FE0F                                  ; fully-qualified     # ↪️ left arrow curving right
-2934 FE0F                                  ; fully-qualified     # ⤴️ right arrow curving up
-2935 FE0F                                  ; fully-qualified     # ⤵️ right arrow curving down
-1F503                                      ; fully-qualified     # 🔃 clockwise vertical arrows
-1F504                                      ; fully-qualified     # 🔄 anticlockwise arrows button
-1F519                                      ; fully-qualified     # 🔙 BACK arrow
-1F51A                                      ; fully-qualified     # 🔚 END arrow
-1F51B                                      ; fully-qualified     # 🔛 ON! arrow
-1F51C                                      ; fully-qualified     # 🔜 SOON arrow
-1F51D                                      ; fully-qualified     # 🔝 TOP arrow
-
-# subgroup: religion
-1F6D0                                      ; fully-qualified     # 🛐 place of worship
-269B FE0F                                  ; fully-qualified     # ⚛️ atom symbol
-1F549 FE0F                                 ; fully-qualified     # 🕉️ om
-2721 FE0F                                  ; fully-qualified     # ✡️ star of David
-2638 FE0F                                  ; fully-qualified     # ☸️ wheel of dharma
-262F FE0F                                  ; fully-qualified     # ☯️ yin yang
-271D FE0F                                  ; fully-qualified     # ✝️ latin cross
-2626 FE0F                                  ; fully-qualified     # ☦️ orthodox cross
-262A FE0F                                  ; fully-qualified     # ☪️ star and crescent
-262E FE0F                                  ; fully-qualified     # ☮️ peace symbol
-1F54E                                      ; fully-qualified     # 🕎 menorah
-1F52F                                      ; fully-qualified     # 🔯 dotted six-pointed star
-
-# subgroup: zodiac
-2648                                       ; fully-qualified     # ♈ Aries
-2649                                       ; fully-qualified     # ♉ Taurus
-264A                                       ; fully-qualified     # ♊ Gemini
-264B                                       ; fully-qualified     # ♋ Cancer
-264C                                       ; fully-qualified     # ♌ Leo
-264D                                       ; fully-qualified     # ♍ Virgo
-264E                                       ; fully-qualified     # ♎ Libra
-264F                                       ; fully-qualified     # ♏ Scorpius
-2650                                       ; fully-qualified     # ♐ Sagittarius
-2651                                       ; fully-qualified     # ♑ Capricorn
-2652                                       ; fully-qualified     # ♒ Aquarius
-2653                                       ; fully-qualified     # ♓ Pisces
-26CE                                       ; fully-qualified     # ⛎ Ophiuchus
-
-# subgroup: av-symbol
-1F500                                      ; fully-qualified     # 🔀 shuffle tracks button
-1F501                                      ; fully-qualified     # 🔁 repeat button
-1F502                                      ; fully-qualified     # 🔂 repeat single button
-25B6 FE0F                                  ; fully-qualified     # ▶️ play button
-23E9                                       ; fully-qualified     # ⏩ fast-forward button
-23ED FE0F                                  ; fully-qualified     # ⏭️ next track button
-23EF FE0F                                  ; fully-qualified     # ⏯️ play or pause button
-25C0 FE0F                                  ; fully-qualified     # ◀️ reverse button
-23EA                                       ; fully-qualified     # ⏪ fast reverse button
-23EE FE0F                                  ; fully-qualified     # ⏮️ last track button
-1F53C                                      ; fully-qualified     # 🔼 up button
-23EB                                       ; fully-qualified     # ⏫ fast up button
-1F53D                                      ; fully-qualified     # 🔽 down button
-23EC                                       ; fully-qualified     # ⏬ fast down button
-23F8 FE0F                                  ; fully-qualified     # ⏸️ pause button
-23F9 FE0F                                  ; fully-qualified     # ⏹️ stop button
-23FA FE0F                                  ; fully-qualified     # ⏺️ record button
-23CF FE0F                                  ; fully-qualified     # ⏏️ eject button
-1F3A6                                      ; fully-qualified     # 🎦 cinema
-1F505                                      ; fully-qualified     # 🔅 dim button
-1F506                                      ; fully-qualified     # 🔆 bright button
-1F4F6                                      ; fully-qualified     # 📶 antenna bars
-1F4F3                                      ; fully-qualified     # 📳 vibration mode
-1F4F4                                      ; fully-qualified     # 📴 mobile phone off
-
-# subgroup: other-symbol
-267B FE0F                                  ; fully-qualified     # ♻️ recycling symbol
-1F4DB                                      ; fully-qualified     # 📛 name badge
-269C FE0F                                  ; fully-qualified     # ⚜️ fleur-de-lis
-1F530                                      ; fully-qualified     # 🔰 Japanese symbol for beginner
-1F531                                      ; fully-qualified     # 🔱 trident emblem
-2B55                                       ; fully-qualified     # ⭕ heavy large circle
-2705                                       ; fully-qualified     # ✅ white heavy check mark
-2611 FE0F                                  ; fully-qualified     # ☑️ ballot box with check
-2714 FE0F                                  ; fully-qualified     # ✔️ heavy check mark
-2716 FE0F                                  ; fully-qualified     # ✖️ heavy multiplication x
-274C                                       ; fully-qualified     # ❌ cross mark
-274E                                       ; fully-qualified     # ❎ cross mark button
-2795                                       ; fully-qualified     # ➕ heavy plus sign
-2640 FE0F                                  ; fully-qualified     # ♀️ female sign
-2642 FE0F                                  ; fully-qualified     # ♂️ male sign
-2695 FE0F                                  ; fully-qualified     # ⚕️ medical symbol
-2796                                       ; fully-qualified     # ➖ heavy minus sign
-2797                                       ; fully-qualified     # ➗ heavy division sign
-27B0                                       ; fully-qualified     # ➰ curly loop
-27BF                                       ; fully-qualified     # ➿ double curly loop
-303D FE0F                                  ; fully-qualified     # 〽️ part alternation mark
-2733 FE0F                                  ; fully-qualified     # ✳️ eight-spoked asterisk
-2734 FE0F                                  ; fully-qualified     # ✴️ eight-pointed star
-2747 FE0F                                  ; fully-qualified     # ❇️ sparkle
-203C FE0F                                  ; fully-qualified     # ‼️ double exclamation mark
-2049 FE0F                                  ; fully-qualified     # ⁉️ exclamation question mark
-2753                                       ; fully-qualified     # ❓ question mark
-2754                                       ; fully-qualified     # ❔ white question mark
-2755                                       ; fully-qualified     # ❕ white exclamation mark
-2757                                       ; fully-qualified     # ❗ exclamation mark
-3030 FE0F                                  ; fully-qualified     # 〰️ wavy dash
-00A9 FE0F                                  ; fully-qualified     # ©️ copyright
-00AE FE0F                                  ; fully-qualified     # ®️ registered
-2122 FE0F                                  ; fully-qualified     # ™️ trade mark
-
-# subgroup: keycap
-0023 FE0F 20E3                             ; fully-qualified     # #️⃣ keycap: #
-002A FE0F 20E3                             ; fully-qualified     # *️⃣ keycap: *
-0030 FE0F 20E3                             ; fully-qualified     # 0️⃣ keycap: 0
-0031 FE0F 20E3                             ; fully-qualified     # 1️⃣ keycap: 1
-0032 FE0F 20E3                             ; fully-qualified     # 2️⃣ keycap: 2
-0033 FE0F 20E3                             ; fully-qualified     # 3️⃣ keycap: 3
-0034 FE0F 20E3                             ; fully-qualified     # 4️⃣ keycap: 4
-0035 FE0F 20E3                             ; fully-qualified     # 5️⃣ keycap: 5
-0036 FE0F 20E3                             ; fully-qualified     # 6️⃣ keycap: 6
-0037 FE0F 20E3                             ; fully-qualified     # 7️⃣ keycap: 7
-0038 FE0F 20E3                             ; fully-qualified     # 8️⃣ keycap: 8
-0039 FE0F 20E3                             ; fully-qualified     # 9️⃣ keycap: 9
-1F51F                                      ; fully-qualified     # 🔟 keycap 10
-
-# subgroup: alphanum
-1F4AF                                      ; fully-qualified     # 💯 hundred points
-1F520                                      ; fully-qualified     # 🔠 input latin uppercase
-1F521                                      ; fully-qualified     # 🔡 input latin lowercase
-1F522                                      ; fully-qualified     # 🔢 input numbers
-1F523                                      ; fully-qualified     # 🔣 input symbols
-1F524                                      ; fully-qualified     # 🔤 input latin letters
-1F170 FE0F                                 ; fully-qualified     # 🅰️ A button (blood type)
-1F18E                                      ; fully-qualified     # 🆎 AB button (blood type)
-1F171 FE0F                                 ; fully-qualified     # 🅱️ B button (blood type)
-1F191                                      ; fully-qualified     # 🆑 CL button
-1F192                                      ; fully-qualified     # 🆒 COOL button
-1F193                                      ; fully-qualified     # 🆓 FREE button
-2139 FE0F                                  ; fully-qualified     # ℹ️ information
-1F194                                      ; fully-qualified     # 🆔 ID button
-24C2 FE0F                                  ; fully-qualified     # Ⓜ️ circled M
-1F195                                      ; fully-qualified     # 🆕 NEW button
-1F196                                      ; fully-qualified     # 🆖 NG button
-1F17E FE0F                                 ; fully-qualified     # 🅾️ O button (blood type)
-1F197                                      ; fully-qualified     # 🆗 OK button
-1F17F FE0F                                 ; fully-qualified     # 🅿️ P button
-1F198                                      ; fully-qualified     # 🆘 SOS button
-1F199                                      ; fully-qualified     # 🆙 UP! button
-1F19A                                      ; fully-qualified     # 🆚 VS button
-1F201                                      ; fully-qualified     # 🈁 Japanese “here” button
-1F202 FE0F                                 ; fully-qualified     # 🈂️ Japanese “service charge” button
-1F237 FE0F                                 ; fully-qualified     # 🈷️ Japanese “monthly amount” button
-1F236                                      ; fully-qualified     # 🈶 Japanese “not free of charge” button
-1F22F                                      ; fully-qualified     # 🈯 Japanese “reserved” button
-1F250                                      ; fully-qualified     # 🉐 Japanese “bargain” button
-1F239                                      ; fully-qualified     # 🈹 Japanese “discount” button
-1F21A                                      ; fully-qualified     # 🈚 Japanese “free of charge” button
-1F232                                      ; fully-qualified     # 🈲 Japanese “prohibited” button
-1F251                                      ; fully-qualified     # 🉑 Japanese “acceptable” button
-1F238                                      ; fully-qualified     # 🈸 Japanese “application” button
-1F234                                      ; fully-qualified     # 🈴 Japanese “passing grade” button
-1F233                                      ; fully-qualified     # 🈳 Japanese “vacancy” button
-3297 FE0F                                  ; fully-qualified     # ㊗️ Japanese “congratulations” button
-3299 FE0F                                  ; fully-qualified     # ㊙️ Japanese “secret” button
-1F23A                                      ; fully-qualified     # 🈺 Japanese “open for business” button
-1F235                                      ; fully-qualified     # 🈵 Japanese “no vacancy” button
-
-# subgroup: geometric
-25AA FE0F                                  ; fully-qualified     # ▪️ black small square
-25AB FE0F                                  ; fully-qualified     # ▫️ white small square
-25FB FE0F                                  ; fully-qualified     # ◻️ white medium square
-25FC FE0F                                  ; fully-qualified     # ◼️ black medium square
-25FD                                       ; fully-qualified     # ◽ white medium-small square
-25FE                                       ; fully-qualified     # ◾ black medium-small square
-2B1B                                       ; fully-qualified     # ⬛ black large square
-2B1C                                       ; fully-qualified     # ⬜ white large square
-1F536                                      ; fully-qualified     # 🔶 large orange diamond
-1F537                                      ; fully-qualified     # 🔷 large blue diamond
-1F538                                      ; fully-qualified     # 🔸 small orange diamond
-1F539                                      ; fully-qualified     # 🔹 small blue diamond
-1F53A                                      ; fully-qualified     # 🔺 red triangle pointed up
-1F53B                                      ; fully-qualified     # 🔻 red triangle pointed down
-1F4A0                                      ; fully-qualified     # 💠 diamond with a dot
-1F518                                      ; fully-qualified     # 🔘 radio button
-1F532                                      ; fully-qualified     # 🔲 black square button
-1F533                                      ; fully-qualified     # 🔳 white square button
-26AA                                       ; fully-qualified     # ⚪ white circle
-26AB                                       ; fully-qualified     # ⚫ black circle
-1F534                                      ; fully-qualified     # 🔴 red circle
-1F535                                      ; fully-qualified     # 🔵 blue circle
-
-# Symbols subtotal:		205
-# Symbols subtotal:		205	w/o modifiers
-
-# group: Flags
-
-# subgroup: flag
-1F3C1                                      ; fully-qualified     # 🏁 chequered flag
-1F6A9                                      ; fully-qualified     # 🚩 triangular flag
-1F38C                                      ; fully-qualified     # 🎌 crossed flags
-1F3F4                                      ; fully-qualified     # 🏴 black flag
-1F3F3 FE0F                                 ; fully-qualified     # 🏳️ white flag
-1F3F3 FE0F 200D 1F308                      ; fully-qualified     # 🏳️‍🌈 rainbow flag
-1F3F3 200D 1F308                           ; non-fully-qualified # 🏳‍🌈 rainbow flag
-
-# subgroup: country-flag
-1F1E6 1F1E8                                ; fully-qualified     # 🇦🇨 Ascension Island
-1F1E6 1F1E9                                ; fully-qualified     # 🇦🇩 Andorra
-1F1E6 1F1EA                                ; fully-qualified     # 🇦🇪 United Arab Emirates
-1F1E6 1F1EB                                ; fully-qualified     # 🇦🇫 Afghanistan
-1F1E6 1F1EC                                ; fully-qualified     # 🇦🇬 Antigua & Barbuda
-1F1E6 1F1EE                                ; fully-qualified     # 🇦🇮 Anguilla
-1F1E6 1F1F1                                ; fully-qualified     # 🇦🇱 Albania
-1F1E6 1F1F2                                ; fully-qualified     # 🇦🇲 Armenia
-1F1E6 1F1F4                                ; fully-qualified     # 🇦🇴 Angola
-1F1E6 1F1F6                                ; fully-qualified     # 🇦🇶 Antarctica
-1F1E6 1F1F7                                ; fully-qualified     # 🇦🇷 Argentina
-1F1E6 1F1F8                                ; fully-qualified     # 🇦🇸 American Samoa
-1F1E6 1F1F9                                ; fully-qualified     # 🇦🇹 Austria
-1F1E6 1F1FA                                ; fully-qualified     # 🇦🇺 Australia
-1F1E6 1F1FC                                ; fully-qualified     # 🇦🇼 Aruba
-1F1E6 1F1FD                                ; fully-qualified     # 🇦🇽 Åland Islands
-1F1E6 1F1FF                                ; fully-qualified     # 🇦🇿 Azerbaijan
-1F1E7 1F1E6                                ; fully-qualified     # 🇧🇦 Bosnia & Herzegovina
-1F1E7 1F1E7                                ; fully-qualified     # 🇧🇧 Barbados
-1F1E7 1F1E9                                ; fully-qualified     # 🇧🇩 Bangladesh
-1F1E7 1F1EA                                ; fully-qualified     # 🇧🇪 Belgium
-1F1E7 1F1EB                                ; fully-qualified     # 🇧🇫 Burkina Faso
-1F1E7 1F1EC                                ; fully-qualified     # 🇧🇬 Bulgaria
-1F1E7 1F1ED                                ; fully-qualified     # 🇧🇭 Bahrain
-1F1E7 1F1EE                                ; fully-qualified     # 🇧🇮 Burundi
-1F1E7 1F1EF                                ; fully-qualified     # 🇧🇯 Benin
-1F1E7 1F1F1                                ; fully-qualified     # 🇧🇱 St. Barthélemy
-1F1E7 1F1F2                                ; fully-qualified     # 🇧🇲 Bermuda
-1F1E7 1F1F3                                ; fully-qualified     # 🇧🇳 Brunei
-1F1E7 1F1F4                                ; fully-qualified     # 🇧🇴 Bolivia
-1F1E7 1F1F6                                ; fully-qualified     # 🇧🇶 Caribbean Netherlands
-1F1E7 1F1F7                                ; fully-qualified     # 🇧🇷 Brazil
-1F1E7 1F1F8                                ; fully-qualified     # 🇧🇸 Bahamas
-1F1E7 1F1F9                                ; fully-qualified     # 🇧🇹 Bhutan
-1F1E7 1F1FB                                ; fully-qualified     # 🇧🇻 Bouvet Island
-1F1E7 1F1FC                                ; fully-qualified     # 🇧🇼 Botswana
-1F1E7 1F1FE                                ; fully-qualified     # 🇧🇾 Belarus
-1F1E7 1F1FF                                ; fully-qualified     # 🇧🇿 Belize
-1F1E8 1F1E6                                ; fully-qualified     # 🇨🇦 Canada
-1F1E8 1F1E8                                ; fully-qualified     # 🇨🇨 Cocos (Keeling) Islands
-1F1E8 1F1E9                                ; fully-qualified     # 🇨🇩 Congo - Kinshasa
-1F1E8 1F1EB                                ; fully-qualified     # 🇨🇫 Central African Republic
-1F1E8 1F1EC                                ; fully-qualified     # 🇨🇬 Congo - Brazzaville
-1F1E8 1F1ED                                ; fully-qualified     # 🇨🇭 Switzerland
-1F1E8 1F1EE                                ; fully-qualified     # 🇨🇮 Côte d’Ivoire
-1F1E8 1F1F0                                ; fully-qualified     # 🇨🇰 Cook Islands
-1F1E8 1F1F1                                ; fully-qualified     # 🇨🇱 Chile
-1F1E8 1F1F2                                ; fully-qualified     # 🇨🇲 Cameroon
-1F1E8 1F1F3                                ; fully-qualified     # 🇨🇳 China
-1F1E8 1F1F4                                ; fully-qualified     # 🇨🇴 Colombia
-1F1E8 1F1F5                                ; fully-qualified     # 🇨🇵 Clipperton Island
-1F1E8 1F1F7                                ; fully-qualified     # 🇨🇷 Costa Rica
-1F1E8 1F1FA                                ; fully-qualified     # 🇨🇺 Cuba
-1F1E8 1F1FB                                ; fully-qualified     # 🇨🇻 Cape Verde
-1F1E8 1F1FC                                ; fully-qualified     # 🇨🇼 Curaçao
-1F1E8 1F1FD                                ; fully-qualified     # 🇨🇽 Christmas Island
-1F1E8 1F1FE                                ; fully-qualified     # 🇨🇾 Cyprus
-1F1E8 1F1FF                                ; fully-qualified     # 🇨🇿 Czech Republic
-1F1E9 1F1EA                                ; fully-qualified     # 🇩🇪 Germany
-1F1E9 1F1EC                                ; fully-qualified     # 🇩🇬 Diego Garcia
-1F1E9 1F1EF                                ; fully-qualified     # 🇩🇯 Djibouti
-1F1E9 1F1F0                                ; fully-qualified     # 🇩🇰 Denmark
-1F1E9 1F1F2                                ; fully-qualified     # 🇩🇲 Dominica
-1F1E9 1F1F4                                ; fully-qualified     # 🇩🇴 Dominican Republic
-1F1E9 1F1FF                                ; fully-qualified     # 🇩🇿 Algeria
-1F1EA 1F1E6                                ; fully-qualified     # 🇪🇦 Ceuta & Melilla
-1F1EA 1F1E8                                ; fully-qualified     # 🇪🇨 Ecuador
-1F1EA 1F1EA                                ; fully-qualified     # 🇪🇪 Estonia
-1F1EA 1F1EC                                ; fully-qualified     # 🇪🇬 Egypt
-1F1EA 1F1ED                                ; fully-qualified     # 🇪🇭 Western Sahara
-1F1EA 1F1F7                                ; fully-qualified     # 🇪🇷 Eritrea
-1F1EA 1F1F8                                ; fully-qualified     # 🇪🇸 Spain
-1F1EA 1F1F9                                ; fully-qualified     # 🇪🇹 Ethiopia
-1F1EA 1F1FA                                ; fully-qualified     # 🇪🇺 European Union
-1F1EB 1F1EE                                ; fully-qualified     # 🇫🇮 Finland
-1F1EB 1F1EF                                ; fully-qualified     # 🇫🇯 Fiji
-1F1EB 1F1F0                                ; fully-qualified     # 🇫🇰 Falkland Islands
-1F1EB 1F1F2                                ; fully-qualified     # 🇫🇲 Micronesia
-1F1EB 1F1F4                                ; fully-qualified     # 🇫🇴 Faroe Islands
-1F1EB 1F1F7                                ; fully-qualified     # 🇫🇷 France
-1F1EC 1F1E6                                ; fully-qualified     # 🇬🇦 Gabon
-1F1EC 1F1E7                                ; fully-qualified     # 🇬🇧 United Kingdom
-1F1EC 1F1E9                                ; fully-qualified     # 🇬🇩 Grenada
-1F1EC 1F1EA                                ; fully-qualified     # 🇬🇪 Georgia
-1F1EC 1F1EB                                ; fully-qualified     # 🇬🇫 French Guiana
-1F1EC 1F1EC                                ; fully-qualified     # 🇬🇬 Guernsey
-1F1EC 1F1ED                                ; fully-qualified     # 🇬🇭 Ghana
-1F1EC 1F1EE                                ; fully-qualified     # 🇬🇮 Gibraltar
-1F1EC 1F1F1                                ; fully-qualified     # 🇬🇱 Greenland
-1F1EC 1F1F2                                ; fully-qualified     # 🇬🇲 Gambia
-1F1EC 1F1F3                                ; fully-qualified     # 🇬🇳 Guinea
-1F1EC 1F1F5                                ; fully-qualified     # 🇬🇵 Guadeloupe
-1F1EC 1F1F6                                ; fully-qualified     # 🇬🇶 Equatorial Guinea
-1F1EC 1F1F7                                ; fully-qualified     # 🇬🇷 Greece
-1F1EC 1F1F8                                ; fully-qualified     # 🇬🇸 South Georgia & South Sandwich Islands
-1F1EC 1F1F9                                ; fully-qualified     # 🇬🇹 Guatemala
-1F1EC 1F1FA                                ; fully-qualified     # 🇬🇺 Guam
-1F1EC 1F1FC                                ; fully-qualified     # 🇬🇼 Guinea-Bissau
-1F1EC 1F1FE                                ; fully-qualified     # 🇬🇾 Guyana
-1F1ED 1F1F0                                ; fully-qualified     # 🇭🇰 Hong Kong SAR China
-1F1ED 1F1F2                                ; fully-qualified     # 🇭🇲 Heard & McDonald Islands
-1F1ED 1F1F3                                ; fully-qualified     # 🇭🇳 Honduras
-1F1ED 1F1F7                                ; fully-qualified     # 🇭🇷 Croatia
-1F1ED 1F1F9                                ; fully-qualified     # 🇭🇹 Haiti
-1F1ED 1F1FA                                ; fully-qualified     # 🇭🇺 Hungary
-1F1EE 1F1E8                                ; fully-qualified     # 🇮🇨 Canary Islands
-1F1EE 1F1E9                                ; fully-qualified     # 🇮🇩 Indonesia
-1F1EE 1F1EA                                ; fully-qualified     # 🇮🇪 Ireland
-1F1EE 1F1F1                                ; fully-qualified     # 🇮🇱 Israel
-1F1EE 1F1F2                                ; fully-qualified     # 🇮🇲 Isle of Man
-1F1EE 1F1F3                                ; fully-qualified     # 🇮🇳 India
-1F1EE 1F1F4                                ; fully-qualified     # 🇮🇴 British Indian Ocean Territory
-1F1EE 1F1F6                                ; fully-qualified     # 🇮🇶 Iraq
-1F1EE 1F1F7                                ; fully-qualified     # 🇮🇷 Iran
-1F1EE 1F1F8                                ; fully-qualified     # 🇮🇸 Iceland
-1F1EE 1F1F9                                ; fully-qualified     # 🇮🇹 Italy
-1F1EF 1F1EA                                ; fully-qualified     # 🇯🇪 Jersey
-1F1EF 1F1F2                                ; fully-qualified     # 🇯🇲 Jamaica
-1F1EF 1F1F4                                ; fully-qualified     # 🇯🇴 Jordan
-1F1EF 1F1F5                                ; fully-qualified     # 🇯🇵 Japan
-1F1F0 1F1EA                                ; fully-qualified     # 🇰🇪 Kenya
-1F1F0 1F1EC                                ; fully-qualified     # 🇰🇬 Kyrgyzstan
-1F1F0 1F1ED                                ; fully-qualified     # 🇰🇭 Cambodia
-1F1F0 1F1EE                                ; fully-qualified     # 🇰🇮 Kiribati
-1F1F0 1F1F2                                ; fully-qualified     # 🇰🇲 Comoros
-1F1F0 1F1F3                                ; fully-qualified     # 🇰🇳 St. Kitts & Nevis
-1F1F0 1F1F5                                ; fully-qualified     # 🇰🇵 North Korea
-1F1F0 1F1F7                                ; fully-qualified     # 🇰🇷 South Korea
-1F1F0 1F1FC                                ; fully-qualified     # 🇰🇼 Kuwait
-1F1F0 1F1FE                                ; fully-qualified     # 🇰🇾 Cayman Islands
-1F1F0 1F1FF                                ; fully-qualified     # 🇰🇿 Kazakhstan
-1F1F1 1F1E6                                ; fully-qualified     # 🇱🇦 Laos
-1F1F1 1F1E7                                ; fully-qualified     # 🇱🇧 Lebanon
-1F1F1 1F1E8                                ; fully-qualified     # 🇱🇨 St. Lucia
-1F1F1 1F1EE                                ; fully-qualified     # 🇱🇮 Liechtenstein
-1F1F1 1F1F0                                ; fully-qualified     # 🇱🇰 Sri Lanka
-1F1F1 1F1F7                                ; fully-qualified     # 🇱🇷 Liberia
-1F1F1 1F1F8                                ; fully-qualified     # 🇱🇸 Lesotho
-1F1F1 1F1F9                                ; fully-qualified     # 🇱🇹 Lithuania
-1F1F1 1F1FA                                ; fully-qualified     # 🇱🇺 Luxembourg
-1F1F1 1F1FB                                ; fully-qualified     # 🇱🇻 Latvia
-1F1F1 1F1FE                                ; fully-qualified     # 🇱🇾 Libya
-1F1F2 1F1E6                                ; fully-qualified     # 🇲🇦 Morocco
-1F1F2 1F1E8                                ; fully-qualified     # 🇲🇨 Monaco
-1F1F2 1F1E9                                ; fully-qualified     # 🇲🇩 Moldova
-1F1F2 1F1EA                                ; fully-qualified     # 🇲🇪 Montenegro
-1F1F2 1F1EB                                ; fully-qualified     # 🇲🇫 St. Martin
-1F1F2 1F1EC                                ; fully-qualified     # 🇲🇬 Madagascar
-1F1F2 1F1ED                                ; fully-qualified     # 🇲🇭 Marshall Islands
-1F1F2 1F1F0                                ; fully-qualified     # 🇲🇰 Macedonia
-1F1F2 1F1F1                                ; fully-qualified     # 🇲🇱 Mali
-1F1F2 1F1F2                                ; fully-qualified     # 🇲🇲 Myanmar (Burma)
-1F1F2 1F1F3                                ; fully-qualified     # 🇲🇳 Mongolia
-1F1F2 1F1F4                                ; fully-qualified     # 🇲🇴 Macau SAR China
-1F1F2 1F1F5                                ; fully-qualified     # 🇲🇵 Northern Mariana Islands
-1F1F2 1F1F6                                ; fully-qualified     # 🇲🇶 Martinique
-1F1F2 1F1F7                                ; fully-qualified     # 🇲🇷 Mauritania
-1F1F2 1F1F8                                ; fully-qualified     # 🇲🇸 Montserrat
-1F1F2 1F1F9                                ; fully-qualified     # 🇲🇹 Malta
-1F1F2 1F1FA                                ; fully-qualified     # 🇲🇺 Mauritius
-1F1F2 1F1FB                                ; fully-qualified     # 🇲🇻 Maldives
-1F1F2 1F1FC                                ; fully-qualified     # 🇲🇼 Malawi
-1F1F2 1F1FD                                ; fully-qualified     # 🇲🇽 Mexico
-1F1F2 1F1FE                                ; fully-qualified     # 🇲🇾 Malaysia
-1F1F2 1F1FF                                ; fully-qualified     # 🇲🇿 Mozambique
-1F1F3 1F1E6                                ; fully-qualified     # 🇳🇦 Namibia
-1F1F3 1F1E8                                ; fully-qualified     # 🇳🇨 New Caledonia
-1F1F3 1F1EA                                ; fully-qualified     # 🇳🇪 Niger
-1F1F3 1F1EB                                ; fully-qualified     # 🇳🇫 Norfolk Island
-1F1F3 1F1EC                                ; fully-qualified     # 🇳🇬 Nigeria
-1F1F3 1F1EE                                ; fully-qualified     # 🇳🇮 Nicaragua
-1F1F3 1F1F1                                ; fully-qualified     # 🇳🇱 Netherlands
-1F1F3 1F1F4                                ; fully-qualified     # 🇳🇴 Norway
-1F1F3 1F1F5                                ; fully-qualified     # 🇳🇵 Nepal
-1F1F3 1F1F7                                ; fully-qualified     # 🇳🇷 Nauru
-1F1F3 1F1FA                                ; fully-qualified     # 🇳🇺 Niue
-1F1F3 1F1FF                                ; fully-qualified     # 🇳🇿 New Zealand
-1F1F4 1F1F2                                ; fully-qualified     # 🇴🇲 Oman
-1F1F5 1F1E6                                ; fully-qualified     # 🇵🇦 Panama
-1F1F5 1F1EA                                ; fully-qualified     # 🇵🇪 Peru
-1F1F5 1F1EB                                ; fully-qualified     # 🇵🇫 French Polynesia
-1F1F5 1F1EC                                ; fully-qualified     # 🇵🇬 Papua New Guinea
-1F1F5 1F1ED                                ; fully-qualified     # 🇵🇭 Philippines
-1F1F5 1F1F0                                ; fully-qualified     # 🇵🇰 Pakistan
-1F1F5 1F1F1                                ; fully-qualified     # 🇵🇱 Poland
-1F1F5 1F1F2                                ; fully-qualified     # 🇵🇲 St. Pierre & Miquelon
-1F1F5 1F1F3                                ; fully-qualified     # 🇵🇳 Pitcairn Islands
-1F1F5 1F1F7                                ; fully-qualified     # 🇵🇷 Puerto Rico
-1F1F5 1F1F8                                ; fully-qualified     # 🇵🇸 Palestinian Territories
-1F1F5 1F1F9                                ; fully-qualified     # 🇵🇹 Portugal
-1F1F5 1F1FC                                ; fully-qualified     # 🇵🇼 Palau
-1F1F5 1F1FE                                ; fully-qualified     # 🇵🇾 Paraguay
-1F1F6 1F1E6                                ; fully-qualified     # 🇶🇦 Qatar
-1F1F7 1F1EA                                ; fully-qualified     # 🇷🇪 Réunion
-1F1F7 1F1F4                                ; fully-qualified     # 🇷🇴 Romania
-1F1F7 1F1F8                                ; fully-qualified     # 🇷🇸 Serbia
-1F1F7 1F1FA                                ; fully-qualified     # 🇷🇺 Russia
-1F1F7 1F1FC                                ; fully-qualified     # 🇷🇼 Rwanda
-1F1F8 1F1E6                                ; fully-qualified     # 🇸🇦 Saudi Arabia
-1F1F8 1F1E7                                ; fully-qualified     # 🇸🇧 Solomon Islands
-1F1F8 1F1E8                                ; fully-qualified     # 🇸🇨 Seychelles
-1F1F8 1F1E9                                ; fully-qualified     # 🇸🇩 Sudan
-1F1F8 1F1EA                                ; fully-qualified     # 🇸🇪 Sweden
-1F1F8 1F1EC                                ; fully-qualified     # 🇸🇬 Singapore
-1F1F8 1F1ED                                ; fully-qualified     # 🇸🇭 St. Helena
-1F1F8 1F1EE                                ; fully-qualified     # 🇸🇮 Slovenia
-1F1F8 1F1EF                                ; fully-qualified     # 🇸🇯 Svalbard & Jan Mayen
-1F1F8 1F1F0                                ; fully-qualified     # 🇸🇰 Slovakia
-1F1F8 1F1F1                                ; fully-qualified     # 🇸🇱 Sierra Leone
-1F1F8 1F1F2                                ; fully-qualified     # 🇸🇲 San Marino
-1F1F8 1F1F3                                ; fully-qualified     # 🇸🇳 Senegal
-1F1F8 1F1F4                                ; fully-qualified     # 🇸🇴 Somalia
-1F1F8 1F1F7                                ; fully-qualified     # 🇸🇷 Suriname
-1F1F8 1F1F8                                ; fully-qualified     # 🇸🇸 South Sudan
-1F1F8 1F1F9                                ; fully-qualified     # 🇸🇹 São Tomé & Príncipe
-1F1F8 1F1FB                                ; fully-qualified     # 🇸🇻 El Salvador
-1F1F8 1F1FD                                ; fully-qualified     # 🇸🇽 Sint Maarten
-1F1F8 1F1FE                                ; fully-qualified     # 🇸🇾 Syria
-1F1F8 1F1FF                                ; fully-qualified     # 🇸🇿 Swaziland
-1F1F9 1F1E6                                ; fully-qualified     # 🇹🇦 Tristan da Cunha
-1F1F9 1F1E8                                ; fully-qualified     # 🇹🇨 Turks & Caicos Islands
-1F1F9 1F1E9                                ; fully-qualified     # 🇹🇩 Chad
-1F1F9 1F1EB                                ; fully-qualified     # 🇹🇫 French Southern Territories
-1F1F9 1F1EC                                ; fully-qualified     # 🇹🇬 Togo
-1F1F9 1F1ED                                ; fully-qualified     # 🇹🇭 Thailand
-1F1F9 1F1EF                                ; fully-qualified     # 🇹🇯 Tajikistan
-1F1F9 1F1F0                                ; fully-qualified     # 🇹🇰 Tokelau
-1F1F9 1F1F1                                ; fully-qualified     # 🇹🇱 Timor-Leste
-1F1F9 1F1F2                                ; fully-qualified     # 🇹🇲 Turkmenistan
-1F1F9 1F1F3                                ; fully-qualified     # 🇹🇳 Tunisia
-1F1F9 1F1F4                                ; fully-qualified     # 🇹🇴 Tonga
-1F1F9 1F1F7                                ; fully-qualified     # 🇹🇷 Turkey
-1F1F9 1F1F9                                ; fully-qualified     # 🇹🇹 Trinidad & Tobago
-1F1F9 1F1FB                                ; fully-qualified     # 🇹🇻 Tuvalu
-1F1F9 1F1FC                                ; fully-qualified     # 🇹🇼 Taiwan
-1F1F9 1F1FF                                ; fully-qualified     # 🇹🇿 Tanzania
-1F1FA 1F1E6                                ; fully-qualified     # 🇺🇦 Ukraine
-1F1FA 1F1EC                                ; fully-qualified     # 🇺🇬 Uganda
-1F1FA 1F1F2                                ; fully-qualified     # 🇺🇲 U.S. Outlying Islands
-1F1FA 1F1F3                                ; fully-qualified     # 🇺🇳 United Nations
-1F1FA 1F1F8                                ; fully-qualified     # 🇺🇸 United States
-1F1FA 1F1FE                                ; fully-qualified     # 🇺🇾 Uruguay
-1F1FA 1F1FF                                ; fully-qualified     # 🇺🇿 Uzbekistan
-1F1FB 1F1E6                                ; fully-qualified     # 🇻🇦 Vatican City
-1F1FB 1F1E8                                ; fully-qualified     # 🇻🇨 St. Vincent & Grenadines
-1F1FB 1F1EA                                ; fully-qualified     # 🇻🇪 Venezuela
-1F1FB 1F1EC                                ; fully-qualified     # 🇻🇬 British Virgin Islands
-1F1FB 1F1EE                                ; fully-qualified     # 🇻🇮 U.S. Virgin Islands
-1F1FB 1F1F3                                ; fully-qualified     # 🇻🇳 Vietnam
-1F1FB 1F1FA                                ; fully-qualified     # 🇻🇺 Vanuatu
-1F1FC 1F1EB                                ; fully-qualified     # 🇼🇫 Wallis & Futuna
-1F1FC 1F1F8                                ; fully-qualified     # 🇼🇸 Samoa
-1F1FD 1F1F0                                ; fully-qualified     # 🇽🇰 Kosovo
-1F1FE 1F1EA                                ; fully-qualified     # 🇾🇪 Yemen
-1F1FE 1F1F9                                ; fully-qualified     # 🇾🇹 Mayotte
-1F1FF 1F1E6                                ; fully-qualified     # 🇿🇦 South Africa
-1F1FF 1F1F2                                ; fully-qualified     # 🇿🇲 Zambia
-1F1FF 1F1FC                                ; fully-qualified     # 🇿🇼 Zimbabwe
-
-# Flags subtotal:		264
-# Flags subtotal:		264	w/o modifiers
-
-#EOF

diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
deleted file mode 100644
index 6f1dcb0..0000000
--- a/ibus-HEAD.patch
+++ /dev/null
@@ -1,9971 +0,0 @@
-From 7e477d5e0ffe19b6c52558c5b37fdd9cb82c097a Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Thu, 9 Mar 2017 11:31:21 +0900
-Subject: [PATCH] tools: Fix `ibus emoji` SEGV when language is changed.
-
-BUG=rhbz#1430290
-
-Review URL: https://codereview.appspot.com/317410043
----
- tools/main.vala | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/tools/main.vala b/tools/main.vala
-index 73c6f57..fd9fd0e 100644
---- a/tools/main.vala
-+++ b/tools/main.vala
-@@ -353,6 +353,9 @@ int emoji_dialog(string[] argv) {
-     } else {
-         GLib.MainLoop loop = new GLib.MainLoop();
-         emojier.loaded_emoji_dict.connect(() => {
-+            // The signal is called when the language is changed.
-+            if (emojier.is_running())
-+                return;
-             run_dialog(emojier);
-             loop.quit();
-         });
--- 
-2.7.4
-
-From 9dbea347050ae2ad79d1b53f2ad62a7a2cafadc6 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Thu, 9 Mar 2017 12:45:20 +0900
-Subject: [PATCH] ui/gtk3: Get emoji colors from the theme
-
-Get selected and normal text color from the theme.
-Implement to activate an emoji with mouse motion.
-Create back button on emoji language chooser dialog.
-Set row_homogeneous on emoji table.
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/319470043
----
- ui/gtk3/Makefile.am        |   3 +
- ui/gtk3/candidatearea.vala | 186 +++++++++++++++++++++++++------------------
- ui/gtk3/emojier.vala       | 192 ++++++++++++++++++++++++++++++++-------------
- 3 files changed, 251 insertions(+), 130 deletions(-)
-
-diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
-index d5ddc42..4e7fd1b 100644
---- a/ui/gtk3/Makefile.am
-+++ b/ui/gtk3/Makefile.am
-@@ -172,8 +172,11 @@ libibus_emoji_dialog_1_0_la_LDFLAGS =   \
-     -version-info @LT_VERSION_INFO@     \
-     $(NULL)
- libibus_emoji_dialog_1_0_la_SOURCES =   \
-+    candidatearea.c                     \
-     emojier.c                           \
-     iconwidget.c                        \
-+    pango.c                             \
-+    separator.c                         \
-     $(NULL)
- 
- -include $(INTROSPECTION_MAKEFILE)
-diff --git a/ui/gtk3/candidatearea.vala b/ui/gtk3/candidatearea.vala
-index a095e76..e162a96 100644
---- a/ui/gtk3/candidatearea.vala
-+++ b/ui/gtk3/candidatearea.vala
-@@ -21,6 +21,108 @@
-  * USA
-  */
- 
-+class ThemedRGBA {
-+    public Gdk.RGBA *normal_fg { get; set; }
-+    public Gdk.RGBA *normal_bg { get; set; }
-+    public Gdk.RGBA *selected_fg { get; set; }
-+    public Gdk.RGBA *selected_bg { get; set; }
-+
-+    private Gtk.StyleContext m_style_context;
-+
-+    public ThemedRGBA(Gtk.Widget widget) {
-+        this.normal_fg = null;
-+        this.normal_bg = null;
-+        this.selected_fg = null;
-+        this.selected_bg = null;
-+
-+        /* Use the color of Gtk.TextView instead of Gtk.Label
-+         * because the selected label "color" is not configured
-+         * in "Adwaita" theme and the selected label "background-color"
-+         * is not configured in "Maia" theme.
-+         * https://github.com/ibus/ibus/issues/1871
-+         */
-+        Gtk.WidgetPath widget_path = new Gtk.WidgetPath();
-+        widget_path.append_type(typeof(Gtk.TextView));
-+        m_style_context = new Gtk.StyleContext();
-+        m_style_context.set_path(widget_path);
-+        m_style_context.add_class(Gtk.STYLE_CLASS_VIEW);
-+
-+        /* "-gtk-secondary-caret-color" value is different
-+         * if the parent widget is set in "Menta" theme.
-+         */
-+        m_style_context.set_parent(widget.get_style_context());
-+
-+        get_rgba();
-+
-+        m_style_context.changed.connect(() => { get_rgba(); });
-+    }
-+
-+    ~ThemedRGBA() {
-+        reset_rgba();
-+    }
-+
-+    private void reset_rgba() {
-+        if (this.normal_fg != null) {
-+            this.normal_fg.free();
-+            this.normal_fg = null;
-+        }
-+        if (this.normal_bg != null) {
-+            this.normal_bg.free();
-+            this.normal_bg = null;
-+        }
-+        if (this.selected_fg != null) {
-+            this.selected_fg.free();
-+            this.selected_fg = null;
-+        }
-+        if (this.selected_bg != null) {
-+            this.selected_bg.free();
-+            this.selected_bg = null;
-+        }
-+    }
-+
-+    private void get_rgba() {
-+        reset_rgba();
-+        Gdk.RGBA *normal_fg = null;
-+        Gdk.RGBA *normal_bg = null;
-+        Gdk.RGBA *selected_fg = null;
-+        Gdk.RGBA *selected_bg = null;
-+        m_style_context.get(Gtk.StateFlags.NORMAL,
-+                            "color",
-+                            out normal_fg);
-+        m_style_context.get(Gtk.StateFlags.SELECTED,
-+                            "color",
-+                            out selected_fg);
-+
-+        string bg_prop = "background-color";
-+        m_style_context.get(Gtk.StateFlags.NORMAL,
-+                            bg_prop,
-+                            out normal_bg);
-+        m_style_context.get(Gtk.StateFlags.SELECTED,
-+                            bg_prop,
-+                            out selected_bg);
-+        if (normal_bg.red   == selected_bg.red &&
-+            normal_bg.green == selected_bg.green &&
-+            normal_bg.blue  == selected_bg.blue &&
-+            normal_bg.alpha == selected_bg.alpha) {
-+            normal_bg.free();
-+            normal_bg = null;
-+            normal_bg.free();
-+            normal_bg = null;
-+            bg_prop = "-gtk-secondary-caret-color";
-+            m_style_context.get(Gtk.StateFlags.NORMAL,
-+                                bg_prop,
-+                                out normal_bg);
-+            m_style_context.get(Gtk.StateFlags.SELECTED,
-+                                bg_prop,
-+                                out selected_bg);
-+        }
-+        this.normal_fg   = normal_fg;
-+        this.normal_bg   = normal_bg;
-+        this.selected_fg = selected_fg;
-+        this.selected_bg = selected_bg;
-+    }
-+}
-+
- class CandidateArea : Gtk.Box {
-     private bool m_vertical;
-     private Gtk.Label[] m_labels;
-@@ -30,9 +132,7 @@ class CandidateArea : Gtk.Box {
-     private IBus.Text[] m_ibus_candidates;
-     private uint m_focus_candidate;
-     private bool m_show_cursor;
--    Gtk.StyleContext m_style_context;
--    private Gdk.RGBA *m_selected_fg_color = null;
--    private Gdk.RGBA *m_selected_bg_color = null;
-+    private ThemedRGBA m_rgba;
- 
-     private const string LABELS[] = {
-         "1.", "2.", "3.", "4.", "5.", "6.", "7.", "8.",
-@@ -58,38 +158,7 @@ class CandidateArea : Gtk.Box {
-     public CandidateArea(bool vertical) {
-         GLib.Object();
-         set_vertical(vertical, true);
--
--        /* Use the color of Gtk.TextView instead of Gtk.Label
--         * because the selected label "color" is not configured
--         * in "Adwaita" theme and the selected label "background-color"
--         * is not configured in "Maia" theme.
--         * https://github.com/ibus/ibus/issues/1871
--         */
--        Gtk.WidgetPath widget_path = new Gtk.WidgetPath();
--        widget_path.append_type(typeof(Gtk.TextView));
--        m_style_context = new Gtk.StyleContext();
--        m_style_context.set_path(widget_path);
--        m_style_context.add_class(Gtk.STYLE_CLASS_VIEW);
--
--        /* "-gtk-secondary-caret-color" value is different
--         * if the parent widget is set in "Menta" theme.
--         */
--        m_style_context.set_parent(get_style_context());
--
--        get_selected_color();
--
--        m_style_context.changed.connect(() => { get_selected_color(); });
--    }
--
--    ~CandidateArea() {
--        if (m_selected_bg_color != null) {
--            m_selected_bg_color.free();
--            m_selected_bg_color = null;
--        }
--        if (m_selected_bg_color != null) {
--            m_selected_bg_color.free();
--            m_selected_bg_color = null;
--        }
-+        m_rgba = new ThemedRGBA(this);
-     }
- 
-     public bool candidate_scrolled(Gdk.EventScroll event) {
-@@ -150,17 +219,17 @@ class CandidateArea : Gtk.Box {
-                 Pango.AttrList attrs = get_pango_attr_list_from_ibus_text(candidates[i]);
-                 if (i == focus_candidate && show_cursor) {
-                     Pango.Attribute pango_attr = Pango.attr_foreground_new(
--                            (uint16)(m_selected_fg_color.red * uint16.MAX),
--                            (uint16)(m_selected_fg_color.green * uint16.MAX),
--                            (uint16)(m_selected_fg_color.blue * uint16.MAX));
-+                            (uint16)(m_rgba.selected_fg.red * uint16.MAX),
-+                            (uint16)(m_rgba.selected_fg.green * uint16.MAX),
-+                            (uint16)(m_rgba.selected_fg.blue * uint16.MAX));
-                     pango_attr.start_index = 0;
-                     pango_attr.end_index = candidates[i].get_text().length;
-                     attrs.insert((owned)pango_attr);
- 
-                     pango_attr = Pango.attr_background_new(
--                            (uint16)(m_selected_bg_color.red * uint16.MAX),
--                            (uint16)(m_selected_bg_color.green * uint16.MAX),
--                            (uint16)(m_selected_bg_color.blue * uint16.MAX));
-+                           (uint16)(m_rgba.selected_bg.red * uint16.MAX),
-+                           (uint16)(m_rgba.selected_bg.green * uint16.MAX),
-+                           (uint16)(m_rgba.selected_bg.blue * uint16.MAX));
-                     pango_attr.start_index = 0;
-                     pango_attr.end_index = candidates[i].get_text().length;
-                     attrs.insert((owned)pango_attr);
-@@ -181,41 +250,6 @@ class CandidateArea : Gtk.Box {
-         }
-     }
- 
--    private void get_selected_color() {
--        if (m_selected_fg_color != null) {
--            m_selected_fg_color.free();
--            m_selected_fg_color = null;
--        }
--        m_style_context.get(Gtk.StateFlags.SELECTED,
--                            "color",
--                            out m_selected_fg_color);
--
--        string bg_prop = "background-color";
--        Gdk.RGBA *normal_color = null;
--        if (m_selected_bg_color != null) {
--            m_selected_bg_color.free();
--            m_selected_bg_color = null;
--        }
--        m_style_context.get(Gtk.StateFlags.NORMAL,
--                            bg_prop,
--                            out normal_color);
--        m_style_context.get(Gtk.StateFlags.SELECTED,
--                            bg_prop,
--                            out m_selected_bg_color);
--        if (normal_color.red   == m_selected_bg_color.red &&
--            normal_color.green == m_selected_bg_color.green &&
--            normal_color.blue  == m_selected_bg_color.blue &&
--            normal_color.alpha == m_selected_bg_color.alpha) {
--            m_selected_bg_color.free();
--            m_selected_bg_color = null;
--            bg_prop = "-gtk-secondary-caret-color";
--            m_style_context.get(Gtk.StateFlags.SELECTED,
--                                bg_prop,
--                                out m_selected_bg_color);
--        }
--        normal_color.free();
--    }
--
-     private void recreate_ui() {
-         foreach (Gtk.Widget w in get_children()) {
-             w.destroy();
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 5496c4e..bc1eff4 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -72,12 +72,31 @@ class IBusEmojier : Gtk.Window {
-     private class EGrid : Gtk.Grid {
-         public EGrid() {
-             GLib.Object(
-+                row_homogeneous : false,
-                 vexpand : true,
-                 halign : Gtk.Align.FILL,
-                 valign : Gtk.Align.FILL
-             );
-         }
-     }
-+    private class EWhiteLabel : Gtk.Label {
-+        public EWhiteLabel(string text) {
-+            GLib.Object(
-+                name : "IBusEmojierWhiteLabel"
-+            );
-+            if (text != "")
-+                set_label(text);
-+        }
-+    }
-+    private class ESelectedLabel : Gtk.Label {
-+        public ESelectedLabel(string text) {
-+            GLib.Object(
-+                name : "IBusEmojierSelectedLabel"
-+            );
-+            if (text != "")
-+                set_label(text);
-+        }
-+    }
-     private class EPaddedLabel : Gtk.Box {
-         public EPaddedLabel(string          text,
-                             Gtk.Align       align,
-@@ -161,6 +180,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
-     private const uint EMOJI_GRID_PAGE = 10;
-+    private ThemedRGBA m_rgba;
-     private Gtk.Box m_vbox;
-     private ETitleLabel m_title;
-     private EEntry m_entry;
-@@ -174,7 +194,8 @@ class IBusEmojier : Gtk.Window {
-     private GLib.MainLoop? m_loop;
-     private string? m_result;
-     private GLib.SList<string> m_lang_list;
--    private string m_current_lang = "en";
-+    private string m_current_lang_id = "en";
-+    private string m_current_language = "English";
-     private string? m_unicode_point = null;
-     private bool m_candidate_panel_is_visible;
-     private GLib.HashTable<string, GLib.SList>?
-@@ -189,11 +210,8 @@ class IBusEmojier : Gtk.Window {
-     private Gtk.Label[] m_candidates;
-     private string m_emoji_font = "Monospace 16";
-     private string[] m_favorites = {};
--    // TODO: Get the selected color from CandidateArea
--    private Gdk.RGBA m_selected_fg_color = Gdk.RGBA(){
--            red = 1.0, green = 1.0, blue = 1.0, alpha = 1.0 };
--    private Gdk.RGBA m_selected_bg_color = Gdk.RGBA(){
--            red = 0.300, green = 0.565, blue = 0.851, alpha = 1.0 };
-+    private bool m_enter_notify_enable = true;
-+    private uint m_entry_notify_show_id;
- 
-     public signal void candidate_clicked(uint index, uint button, uint state);
-     public signal void loaded_emoji_dict();
-@@ -220,7 +238,33 @@ class IBusEmojier : Gtk.Window {
-             warning("Could not open display.");
-             return;
-         }
--        string data = "grid { background-color: #ffffff; }";
-+        m_rgba = new ThemedRGBA(this);
-+        uint bg_red = (uint)(m_rgba.normal_bg.red * 255);
-+        uint bg_green = (uint)(m_rgba.normal_bg.green * 255);
-+        uint bg_blue = (uint)(m_rgba.normal_bg.blue * 255);
-+        double bg_alpha = m_rgba.normal_bg.alpha;
-+        string data =
-+                "#IBusEmojierWhiteLabel { background-color: " +
-+                        "rgba(%u, %u, %u, %lf); ".printf(
-+                        bg_red, bg_green, bg_blue, bg_alpha) +
-+                "border-width: 4px; border-radius: 3px; } ";
-+
-+        uint fg_red = (uint)(m_rgba.selected_fg.red * 255);
-+        uint fg_green = (uint)(m_rgba.selected_fg.green * 255);
-+        uint fg_blue = (uint)(m_rgba.selected_fg.blue * 255);
-+        double fg_alpha = m_rgba.selected_fg.alpha;
-+        bg_red = (uint)(m_rgba.selected_bg.red * 255);
-+        bg_green = (uint)(m_rgba.selected_bg.green * 255);
-+        bg_blue = (uint)(m_rgba.selected_bg.blue * 255);
-+        bg_alpha = m_rgba.selected_bg.alpha;
-+        data += "#IBusEmojierSelectedLabel { color: " +
-+                        "rgba(%u, %u, %u, %lf); ".printf(
-+                        fg_red, fg_green, fg_blue, fg_alpha) +
-+                "background-color: " +
-+                        "rgba(%u, %u, %u, %lf); ".printf(
-+                        bg_red, bg_green, bg_blue, bg_alpha) +
-+                "border-width: 4px; border-radius: 3px; }";
-+
-         Gtk.CssProvider css_provider = new Gtk.CssProvider();
-         try {
-             css_provider.load_from_data(data, -1);
-@@ -317,6 +361,8 @@ class IBusEmojier : Gtk.Window {
-         lang_list.sort((a, b) => {
-             string a_lang = IBus.get_language_name(a);
-             string b_lang = IBus.get_language_name(b);
-+            a_lang = "%s (%s)".printf(a_lang, a);
-+            b_lang = "%s (%s)".printf(b_lang, b);
-             return GLib.strcmp(a_lang, b_lang);
-         });
-         return lang_list;
-@@ -325,8 +371,8 @@ class IBusEmojier : Gtk.Window {
-     private void reload_emoji_dict() {
-         init_emoji_dict();
-         make_emoji_dict("en");
--        if (m_current_lang != "en")
--            make_emoji_dict(m_current_lang);
-+        if (m_current_lang_id != "en")
-+            make_emoji_dict(m_current_lang_id);
-         loaded_emoji_dict();
-     }
- 
-@@ -458,22 +504,50 @@ class IBusEmojier : Gtk.Window {
-         }
-     }
- 
-+    private void activated_language(EBoxRow row) {
-+        m_category_active_index = 0;
-+        if (m_current_lang_id != row.id) {
-+            m_current_lang_id = row.id;
-+            m_current_language = row.text;
-+            reload_emoji_dict();
-+        }
-+        m_current_category_type = CategoryType.EMOJI;
-+        show_category_list();
-+    }
-+
-     private void show_category_list() {
-         remove_all_children();
-         m_scrolled_window = new EScrolledWindow();
-         set_fixed_size();
--        string language = IBus.get_language_name(m_current_lang);
--        EPaddedLabel label = new EPaddedLabel(language, Gtk.Align.CENTER);
-+        EPaddedLabel label;
-+        if (m_current_category_type == CategoryType.EMOJI) {
-+            label = new EPaddedLabel(m_current_language, Gtk.Align.CENTER);
-+        } else if (m_current_category_type == CategoryType.LANG) {
-+            label = new EPaddedLabel(m_current_language,
-+                                     Gtk.Align.CENTER,
-+                                     TravelDirection.BACKWARD);
-+        } else {
-+            label = new EPaddedLabel("", Gtk.Align.CENTER);
-+        }
-         Gtk.Button button = new Gtk.Button();
-         button.add(label);
-         m_vbox.add(button);
-         button.show_all();
--        button.button_press_event.connect((e) => {
--            m_category_active_index = 0;
--            m_current_category_type = CategoryType.LANG;
--            show_category_list();
--            return true;
--        });
-+        if (m_current_category_type == CategoryType.EMOJI) {
-+            button.button_press_event.connect((e) => {
-+                m_category_active_index = 0;
-+                m_current_category_type = CategoryType.LANG;
-+                show_category_list();
-+                return true;
-+            });
-+        } else if (m_current_category_type == CategoryType.LANG) {
-+            button.button_press_event.connect((e) => {
-+                m_category_active_index = 0;
-+                m_current_category_type = CategoryType.EMOJI;
-+                show_category_list();
-+                return true;
-+            });
-+        }
- 
-         m_vbox.add(m_scrolled_window);
-         Gtk.Viewport viewport = new Gtk.Viewport(null, null);
-@@ -523,21 +597,19 @@ class IBusEmojier : Gtk.Window {
-             }
-         } else if (m_current_category_type == CategoryType.LANG) {
-             m_list_box.row_activated.connect((box, gtkrow) => {
--                m_category_active_index = 0;
--                EBoxRow row = gtkrow as EBoxRow;
--                if (m_current_lang != row.id) {
--                    m_current_lang = row.id;
--                    reload_emoji_dict();
--                }
--                m_current_category_type = CategoryType.EMOJI;
--                show_category_list();
-+                activated_language(gtkrow as EBoxRow);
-             });
-             uint n = 1;
-+            string prev_language = null;
-             foreach (unowned string id in m_lang_list) {
--                string selected_language = IBus.get_language_name(id);
--                EBoxRow row = new EBoxRow("", id);
-+                string language = IBus.get_language_name(id);
-+                if (prev_language == language)
-+                    language = "%s (%s)".printf(language, id);
-+                else
-+                    prev_language = language;
-+                EBoxRow row = new EBoxRow(language, id);
-                 EPaddedLabel widget =
--                        new EPaddedLabel(selected_language, Gtk.Align.CENTER);
-+                        new EPaddedLabel(language, Gtk.Align.CENTER);
-                 row.add(widget);
-                 m_list_box.add(row);
-                 if (n++ == m_category_active_index)
-@@ -573,27 +645,6 @@ class IBusEmojier : Gtk.Window {
-         show_candidate_panel();
-     }
- 
--    private void label_set_active_color(Gtk.Label label) {
--        unowned string text = label.get_text();
--        Pango.AttrList attrs = new Pango.AttrList();
--        Pango.Attribute pango_attr = Pango.attr_foreground_new(
--                (uint16)(m_selected_fg_color.red * uint16.MAX),
--                (uint16)(m_selected_fg_color.green * uint16.MAX),
--                (uint16)(m_selected_fg_color.blue * uint16.MAX));
--        pango_attr.start_index = 0;
--        pango_attr.end_index = text.char_count();
--        attrs.insert((owned)pango_attr);
--
--        pango_attr = Pango.attr_background_new(
--                (uint16)(m_selected_bg_color.red * uint16.MAX),
--                (uint16)(m_selected_bg_color.green * uint16.MAX),
--                (uint16)(m_selected_bg_color.blue * uint16.MAX));
--        pango_attr.start_index = 0;
--        pango_attr.end_index = text.char_count();
--        attrs.insert((owned)pango_attr);
--        label.set_attributes(attrs);
--    }
--
-     private void show_arrow_buttons() {
-         Gtk.Button next_button = new Gtk.Button();
-         next_button.clicked.connect(() => {
-@@ -709,10 +760,17 @@ class IBusEmojier : Gtk.Window {
-             });
-         }
-         EGrid grid = new EGrid();
-+        grid.set_row_spacing(5);
-+        grid.set_column_spacing(5);
-+        grid.set_border_width(2);
-         int n = 0;
-         for (uint i = page_start_pos; i < page_end_pos; i++) {
-             IBus.Text candidate = m_lookup_table.get_candidate(i);
--            Gtk.Label label = new Gtk.Label(candidate.text);
-+            Gtk.Label label;
-+            if (i == cursor)
-+                label = new ESelectedLabel(candidate.text) as Gtk.Label;
-+            else
-+                label = new EWhiteLabel(candidate.text) as Gtk.Label;
-             string emoji_font = m_emoji_font;
-             if (candidate.text.char_count() > 2) {
-                 Pango.FontDescription font_desc =
-@@ -726,9 +784,6 @@ class IBusEmojier : Gtk.Window {
-             label.set_markup(markup);
-             label.set_halign(Gtk.Align.FILL);
-             label.set_valign(Gtk.Align.FILL);
--            if (i == cursor) {
--                label_set_active_color(label);
--            }
-             Gtk.EventBox candidate_ebox = new Gtk.EventBox();
-             candidate_ebox.add(label);
-             // Make a copy of i to workaround a bug in vala.
-@@ -738,6 +793,23 @@ class IBusEmojier : Gtk.Window {
-                 candidate_clicked(index, e.button, e.state);
-                 return true;
-             });
-+            // m_enter_notify_enable is added because
-+            // enter_notify_event conflicts with keyboard operations.
-+            if (m_enter_notify_enable) {
-+                candidate_ebox.enter_notify_event.connect((e) => {
-+                    m_lookup_table.set_cursor_pos(index);
-+                    if (m_entry_notify_show_id > 0) {
-+                        GLib.Source.remove(m_entry_notify_show_id);
-+                    }
-+                    // If timeout is not added, memory leak happens and
-+                    // button_press_event signal does not work above.
-+                    m_entry_notify_show_id = GLib.Timeout.add(100, () => {
-+                        show_candidate_panel();
-+                        return false;
-+                    });
-+                    return true;
-+                });
-+            }
-             grid.attach(candidate_ebox,
-                         n % (int)EMOJI_GRID_PAGE, n / (int)EMOJI_GRID_PAGE,
-                         1, 1);
-@@ -797,6 +869,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
-     private void hide_candidate_panel() {
-+        m_enter_notify_enable = true;
-         m_candidate_panel_is_visible = false;
-         if (m_loop.is_running())
-             show_category_list();
-@@ -841,6 +914,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
-     private void candidate_panel_cursor_down() {
-+        m_enter_notify_enable = false;
-         uint ncandidates = m_lookup_table.get_number_of_candidates();
-         uint cursor = m_lookup_table.get_cursor_pos();
-         if ((cursor + EMOJI_GRID_PAGE) < ncandidates) {
-@@ -854,6 +928,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
-     private void candidate_panel_cursor_up() {
-+        m_enter_notify_enable = false;
-         int ncandidates = (int)m_lookup_table.get_number_of_candidates();
-         int cursor = (int)m_lookup_table.get_cursor_pos();
-         int highest_pos =
-@@ -891,6 +966,7 @@ class IBusEmojier : Gtk.Window {
-         m_input_context_path = input_context_path;
-         m_candidate_panel_is_visible = false;
-         m_result = null;
-+        m_enter_notify_enable = true;
- 
-         /* Let gtk recalculate the window size. */
-         resize(1, 1);
-@@ -1011,7 +1087,10 @@ class IBusEmojier : Gtk.Window {
-             } else if (m_category_active_index > 0) {
-                 Gtk.ListBoxRow gtkrow = m_list_box.get_selected_row();
-                 EBoxRow row = gtkrow as EBoxRow;
--                show_emoji_for_category(row);
-+                if (m_current_category_type == CategoryType.EMOJI)
-+                    show_emoji_for_category(row);
-+                else if (m_current_category_type == CategoryType.LANG)
-+                    activated_language(row);
-             }
-             return true;
-         case Gdk.Key.BackSpace:
-@@ -1026,6 +1105,7 @@ class IBusEmojier : Gtk.Window {
-                 break;
-             }
-             if (m_candidate_panel_is_visible) {
-+                m_enter_notify_enable = false;
-                 m_lookup_table.cursor_down();
-                 show_candidate_panel();
-             }
-@@ -1035,6 +1115,7 @@ class IBusEmojier : Gtk.Window {
-             return true;
-         case Gdk.Key.Right:
-             if (m_candidate_panel_is_visible) {
-+                m_enter_notify_enable = false;
-                 m_lookup_table.cursor_down();
-                 show_candidate_panel();
-                 return true;
-@@ -1042,6 +1123,7 @@ class IBusEmojier : Gtk.Window {
-             break;
-         case Gdk.Key.Left:
-             if (m_candidate_panel_is_visible) {
-+                m_enter_notify_enable = false;
-                 m_lookup_table.cursor_up();
-                 show_candidate_panel();
-                 return true;
-@@ -1061,6 +1143,7 @@ class IBusEmojier : Gtk.Window {
-             return true;
-         case Gdk.Key.Page_Down:
-             if (m_candidate_panel_is_visible) {
-+                m_enter_notify_enable = false;
-                 m_lookup_table.page_down();
-                 show_candidate_panel();
-                 return true;
-@@ -1068,6 +1151,7 @@ class IBusEmojier : Gtk.Window {
-             break;
-         case Gdk.Key.Page_Up:
-             if (m_candidate_panel_is_visible) {
-+                m_enter_notify_enable = false;
-                 m_lookup_table.page_up();
-                 show_candidate_panel();
-                 return true;
--- 
-2.7.4
-
-From 31ed31e5303c3946f53b035a63d76f5c1e3cd84a Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 13 Mar 2017 11:43:09 +0900
-Subject: [PATCH] src: Fix ibus_emoji_dict_load()
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/320350043
----
- configure.ac       | 3 ++-
- src/emoji-parser.c | 6 ++++--
- src/ibusemoji.c    | 6 +++---
- 3 files changed, 9 insertions(+), 6 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
-index 027c027..369485c 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -620,7 +620,8 @@ 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")]),
-+        [Set emoji.json. (default: "/usr/lib/node_modules/emojione/emoji.json")
-+         You can get emoji.json with "npm install -g emojione".]),
-     EMOJI_JSON_FILE=$with_emoji_json_file,
-     EMOJI_JSON_FILE="/usr/lib/node_modules/emojione/emoji.json"
- )
-diff --git a/src/emoji-parser.c b/src/emoji-parser.c
-index c5af42c..5965309 100644
---- a/src/emoji-parser.c
-+++ b/src/emoji-parser.c
-@@ -1,7 +1,7 @@
- /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-- * Copyright (C) 2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2016-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-  * Copyright (C) 2016 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-@@ -20,9 +20,11 @@
-  * USA
-  */
- 
--/* Convert ../data/annotations/en.xml and
-+/* Convert /usr/share/unicode/cldr/common/annotations/*.xml and
-  * /usr/lib/node_modules/emojione/emoji.json
-  * to the dictionary file which look up the Emoji from the annotation.
-+ * Get *.xml from https://github.com/fujiwarat/cldr-emoji-annotation
-+ * or http://www.unicode.org/repos/cldr/trunk/common/annotations .
-  * 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".
-diff --git a/src/ibusemoji.c b/src/ibusemoji.c
-index 8d88704..996d136 100644
---- a/src/ibusemoji.c
-+++ b/src/ibusemoji.c
-@@ -431,10 +431,10 @@ ibus_emoji_dict_load (const gchar *path)
-     GHashTable *dict = g_hash_table_new_full (g_str_hash,
-                                               g_str_equal,
-                                               g_free,
--                                              free_dict_words);
-+                                              g_object_unref);
- 
-     for (l = list; l; l = l->next) {
--        IBusEmojiData *data = list->data;
-+        IBusEmojiData *data = l->data;
-         if (!IBUS_IS_EMOJI_DATA (data)) {
-             g_warning ("Your dict format is no longer supported.\n"
-                        "Need to create the dictionaries again.");
-@@ -442,7 +442,7 @@ ibus_emoji_dict_load (const gchar *path)
-         }
-         g_hash_table_insert (dict,
-                              g_strdup (ibus_emoji_data_get_emoji (data)),
--                             data);
-+                             g_object_ref_sink (data));
-     }
- 
-     g_slist_free (list);
--- 
-2.9.3
-
-From c580845167b54ccab76d8b72bdc797ef8d64d554 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 13 Mar 2017 11:58:06 +0900
-Subject: [PATCH] ui/gtk3: Fix emoji text entry cusor position
-
-Focus on emoji text entry by default
-Remove internal text buffer and use Gtk.Entry buffer instead.
-Implement cusor left, right, home, end on emoji annotation preedit.
-
-Review URL: https://codereview.appspot.com/313710043
----
- ui/gtk3/emojier.vala | 104 +++++++++++++++++++++++++++++++++------------------
- 1 file changed, 68 insertions(+), 36 deletions(-)
-
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index bc1eff4..8cecea8 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -75,7 +75,10 @@ class IBusEmojier : Gtk.Window {
-                 row_homogeneous : false,
-                 vexpand : true,
-                 halign : Gtk.Align.FILL,
--                valign : Gtk.Align.FILL
-+                valign : Gtk.Align.FILL,
-+                row_spacing : 5,
-+                column_spacing : 5,
-+                border_width : 2
-             );
-         }
-     }
-@@ -190,7 +193,6 @@ class IBusEmojier : Gtk.Window {
-     private CategoryType m_current_category_type = CategoryType.EMOJI;
-     private bool m_is_running = false;
-     private string m_input_context_path = "";
--    private GLib.StringBuilder m_buffer_string;
-     private GLib.MainLoop? m_loop;
-     private string? m_result;
-     private GLib.SList<string> m_lang_list;
-@@ -288,11 +290,9 @@ class IBusEmojier : Gtk.Window {
-         m_entry.set_placeholder_text(_("Type annotation or choose emoji"));
-         m_vbox.add(m_entry);
-         m_entry.changed.connect(() => {
--            m_buffer_string.assign(m_entry.get_text());
--            update_cadidate_window();
-+            update_candidate_window();
-         });
-         m_entry.icon_release.connect((icon_pos, event) => {
--            m_buffer_string.erase();
-             hide_candidate_panel();
-         });
- 
-@@ -303,7 +303,6 @@ class IBusEmojier : Gtk.Window {
-         Atk.Object obj = m_entry.get_accessible();
-         obj.set_role (Atk.Role.STATUSBAR);
- 
--        m_buffer_string = new StringBuilder();
-         grab_focus();
- 
-         // The constructor of IBus.LookupTable does not support more than
-@@ -680,11 +679,16 @@ class IBusEmojier : Gtk.Window {
-         buttons_hbox.show_all();
-     }
- 
--    private bool check_unicode_point(bool check_xdigit_only) {
--        m_unicode_point = null;
-+    private bool check_unicode_point(string? annotation=null) {
-+        bool check_xdigit_only = true;
-+        if (annotation == null) {
-+            annotation = m_entry.get_text();
-+            m_unicode_point = null;
-+            check_xdigit_only = false;
-+        }
-         GLib.StringBuilder buff = new GLib.StringBuilder();
--        for (int i = 0; i < m_buffer_string.str.char_count(); i++) {
--            unichar ch = m_buffer_string.str.get_char(i);
-+        for (int i = 0; i < annotation.char_count(); i++) {
-+            unichar ch = annotation.get_char(i);
-             if (ch == 0)
-                 return false;
-             if (!ch.isxdigit())
-@@ -704,7 +708,7 @@ class IBusEmojier : Gtk.Window {
-         return true;
-     }
- 
--    public void update_cadidate_window() {
-+    public void update_candidate_window() {
-         string annotation = m_entry.get_text();
-         if (annotation.length == 0) {
-             hide_candidate_panel();
-@@ -716,7 +720,7 @@ class IBusEmojier : Gtk.Window {
-             return;
-         }
-         // Call check_unicode_point() to get m_unicode_point
--        check_unicode_point(false);
-+        check_unicode_point();
-         unowned GLib.SList<string>? emojis =
-             m_annotation_to_emojis_dict.lookup(annotation);
-         if (emojis == null && m_unicode_point == null) {
-@@ -725,7 +729,7 @@ class IBusEmojier : Gtk.Window {
-         }
-         m_lookup_table.clear();
-         // Call check_unicode_point() to update m_lookup_table
--        check_unicode_point(false);
-+        check_unicode_point();
-         foreach (unowned string emoji in emojis) {
-             IBus.Text text = new IBus.Text.from_string(emoji);
-             m_lookup_table.append_candidate(text);
-@@ -760,9 +764,6 @@ class IBusEmojier : Gtk.Window {
-             });
-         }
-         EGrid grid = new EGrid();
--        grid.set_row_spacing(5);
--        grid.set_column_spacing(5);
--        grid.set_border_width(2);
-         int n = 0;
-         for (uint i = page_start_pos; i < page_end_pos; i++) {
-             IBus.Text candidate = m_lookup_table.get_candidate(i);
-@@ -878,14 +879,11 @@ class IBusEmojier : Gtk.Window {
-     private bool if_in_range_of_lookup(uint keyval) {
-         if (!m_candidate_panel_is_visible)
-             return false;
--        string backup_annotation = m_buffer_string.str.dup();
-+        StringBuilder buffer_string = new StringBuilder(m_entry.get_text());
-         unichar ch = IBus.keyval_to_unicode (keyval);
--        m_buffer_string.append_unichar(ch);
--        if (check_unicode_point(true)) {
--            m_buffer_string.assign(backup_annotation);
-+        buffer_string.append_unichar(ch);
-+        if (check_unicode_point(buffer_string.str))
-             return false;
--        }
--        m_buffer_string.assign(backup_annotation);
-         if (keyval < Gdk.Key.@0 || keyval > Gdk.Key.@9)
-             return false;
-         if (keyval == Gdk.Key.@0)
-@@ -958,6 +956,18 @@ class IBusEmojier : Gtk.Window {
-         show_category_list();
-     }
- 
-+    private void entry_enter_keyval(uint keyval) {
-+        unichar ch = IBus.keyval_to_unicode(keyval);
-+        if (!ch.isgraph())
-+            return;
-+        string str = ch.to_string();
-+
-+        // what gtk_entry_commit_cb() do
-+        int pos = m_entry.get_position();
-+        m_entry.insert_text(str, -1, ref pos);
-+        m_entry.set_position(pos);
-+    }
-+
-     public string run(Gdk.Event event,
-                       string    input_context_path) {
-         assert (m_loop == null);
-@@ -972,7 +982,7 @@ class IBusEmojier : Gtk.Window {
-         resize(1, 1);
- 
-         m_entry.set_text("");
--        m_buffer_string.erase();
-+        m_entry.grab_focus();
- 
-         Gdk.Device device = event.get_device();
-         if (device == null) {
-@@ -1070,12 +1080,12 @@ class IBusEmojier : Gtk.Window {
-                 m_current_category_type = CategoryType.EMOJI;
-                 show_candidate_panel();
-                 return true;
--            } else if (m_buffer_string.str.length == 0) {
-+            } else if (m_entry.get_text().length == 0) {
-                 m_loop.quit();
-                 hide_candidate_panel();
-                 return true;
-             }
--            m_buffer_string.erase();
-+            m_entry.delete_text(0, -1);
-             break;
-         case Gdk.Key.Return:
-             if (m_candidate_panel_is_visible) {
-@@ -1094,14 +1104,14 @@ class IBusEmojier : Gtk.Window {
-             }
-             return true;
-         case Gdk.Key.BackSpace:
--            if (m_buffer_string.len > 0)
--                m_buffer_string.erase(m_buffer_string.len - 1);
-+            if (m_entry.get_text().len() > 0) {
-+                GLib.Signal.emit_by_name(m_entry, "backspace");
-+            }
-             break;
-         case Gdk.Key.space:
-         case Gdk.Key.KP_Space:
-             if ((modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) {
--                unichar ch = IBus.keyval_to_unicode (keyval);
--                m_buffer_string.append_unichar(ch);
-+                entry_enter_keyval(keyval);
-                 break;
-             }
-             if (m_candidate_panel_is_visible) {
-@@ -1120,6 +1130,12 @@ class IBusEmojier : Gtk.Window {
-                 show_candidate_panel();
-                 return true;
-             }
-+            if (m_entry.get_text().len() > 0) {
-+                GLib.Signal.emit_by_name(m_entry, "move-cursor",
-+                                         Gtk.MovementStep.VISUAL_POSITIONS,
-+                                         1, false);
-+                return true;
-+            }
-             break;
-         case Gdk.Key.Left:
-             if (m_candidate_panel_is_visible) {
-@@ -1128,6 +1144,12 @@ class IBusEmojier : Gtk.Window {
-                 show_candidate_panel();
-                 return true;
-             }
-+            if (m_entry.get_text().len() > 0) {
-+                GLib.Signal.emit_by_name(m_entry, "move-cursor",
-+                                         Gtk.MovementStep.VISUAL_POSITIONS,
-+                                         -1, false);
-+                return true;
-+            }
-             break;
-         case Gdk.Key.Down:
-             if (m_candidate_panel_is_visible)
-@@ -1157,17 +1179,27 @@ class IBusEmojier : Gtk.Window {
-                 return true;
-             }
-             break;
--        default:
--            unichar ch = IBus.keyval_to_unicode(keyval);
--            if (!ch.isgraph())
-+        case Gdk.Key.Home:
-+            if (m_entry.get_text().len() > 0) {
-+                GLib.Signal.emit_by_name(m_entry, "move-cursor",
-+                                         Gtk.MovementStep.DISPLAY_LINE_ENDS,
-+                                         -1, false);
-+                return true;
-+            }
-+            break;
-+        case Gdk.Key.End:
-+            if (m_entry.get_text().len() > 0) {
-+                GLib.Signal.emit_by_name(m_entry, "move-cursor",
-+                                         Gtk.MovementStep.DISPLAY_LINE_ENDS,
-+                                         1, false);
-                 return true;
--            m_buffer_string.append_unichar(ch);
-+            }
-+            break;
-+        default:
-+            entry_enter_keyval(keyval);
-             break;
-         }
- 
--        string annotation = m_buffer_string.str;
--        m_entry.set_text(annotation);
--
-         return true;
-     }
- 
--- 
-2.9.3
-
-From fbe3de1789c73a8a175a451b2a6b967f5d3c6514 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 13 Mar 2017 12:08:21 +0900
-Subject: [PATCH] src: Update emoji-parser to treat tts as emoji
- description
-
-unicode annotation xml files have a 'tts' attribute and seem
-it is used as the emoji descriptions instead of emoji annotations.
-This can show the translated descriptions.
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/319480043
----
- src/emoji-parser.c   | 26 +++++++++++++++++++-------
- src/ibusemoji.c      | 16 +++++++++++++---
- src/ibusemoji.h      | 11 +++++++++++
- ui/gtk3/emojier.vala | 17 ++++++++++++++---
- 4 files changed, 57 insertions(+), 13 deletions(-)
-
-diff --git a/src/emoji-parser.c b/src/emoji-parser.c
-index 5965309..8ff04f1 100644
---- a/src/emoji-parser.c
-+++ b/src/emoji-parser.c
-@@ -43,6 +43,7 @@ struct _EmojiData {
-     GSList     *annotations;
-     gboolean    is_annotation;
-     gchar      *description;
-+    gboolean    is_tts;
-     gchar      *category;
-     GSList     *list;
- };
-@@ -97,6 +98,8 @@ update_emoji_list (EmojiData *data)
-                                        (GCopyFunc) g_strdup,
-                                        NULL));
-         }
-+        if (data->description)
-+            ibus_emoji_data_set_description (emoji, data->description);
-     } else {
-         IBusEmojiData *emoji =
-                 ibus_emoji_data_new ("emoji",
-@@ -149,13 +152,12 @@ unicode_annotations_start_element_cb (GMarkupParseContext *context,
-                 data->emoji = g_strdup (value);
-             }
-         }
--        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));
-+        /* tts seems 'text to speach' and it would be a description
-+         * instead of annotation.
-+         */
-+        else if (g_strcmp0 (attribute, "type") == 0) {
-+            if (g_strcmp0 (value, "tts") == 0) {
-+                data->is_tts = TRUE;
-             }
-         }
-     }
-@@ -177,6 +179,7 @@ unicode_annotations_end_element_cb (GMarkupParseContext *context,
- 
-     update_emoji_list (data);
-     data->is_annotation = FALSE;
-+    data->is_tts = FALSE;
- }
- 
- void
-@@ -194,6 +197,15 @@ unicode_annotations_text_cb (GMarkupParseContext *context,
-     g_assert (data != NULL);
-     if (!data->is_annotation)
-         return;
-+    if (data->is_tts) {
-+        if (data->description) {
-+            g_warning ("Duplicated 'tts' is found: %s: %s",
-+                       data->description, text);
-+            g_clear_pointer (&data->description, g_free);
-+        }
-+        data->description = g_strdup (text);
-+        return;
-+    }
-     annotations = g_strsplit (text, " | ", -1);
-     for (i = 0; (annotation = annotations[i]) != NULL; i++) {
-         GSList *duplicated = g_slist_find_custom (data->annotations,
-diff --git a/src/ibusemoji.c b/src/ibusemoji.c
-index 996d136..c61cd70 100644
---- a/src/ibusemoji.c
-+++ b/src/ibusemoji.c
-@@ -29,7 +29,7 @@
- #include "ibusinternal.h"
- 
- #define IBUS_EMOJI_DATA_MAGIC "IBusEmojiData"
--#define IBUS_EMOJI_DATA_VERSION (1)
-+#define IBUS_EMOJI_DATA_VERSION (2)
- 
- enum {
-     PROP_0 = 0,
-@@ -128,7 +128,7 @@ ibus_emoji_data_class_init (IBusEmojiDataClass *class)
-                         "emoji description",
-                         "The emoji description",
-                         "",
--                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-+                        G_PARAM_READWRITE));
- 
-     /**
-      * IBusEmojiData:category:
-@@ -352,6 +352,16 @@ ibus_emoji_data_get_description (IBusEmojiData *emoji)
-     return emoji->priv->description;
- }
- 
-+void
-+ibus_emoji_data_set_description (IBusEmojiData *emoji,
-+                                 const gchar   *description)
-+{
-+    g_return_if_fail (IBUS_IS_EMOJI_DATA (emoji));
-+
-+    g_free (emoji->priv->description);
-+    emoji->priv->description = g_strdup (description);
-+}
-+
- const gchar *
- ibus_emoji_data_get_category (IBusEmojiData *emoji)
- {
-@@ -541,7 +551,7 @@ ibus_emoji_data_load (const gchar *path)
-         goto out_load_cache;
-     }
- 
--    if (version != IBUS_EMOJI_DATA_VERSION) {
-+    if (version > IBUS_EMOJI_DATA_VERSION) {
-         g_warning ("cache version is different: %u != %u",
-                    version, IBUS_EMOJI_DATA_VERSION);
-         goto out_load_cache;
-diff --git a/src/ibusemoji.h b/src/ibusemoji.h
-index 3fd09a9..eb24fdd 100644
---- a/src/ibusemoji.h
-+++ b/src/ibusemoji.h
-@@ -134,6 +134,17 @@ void            ibus_emoji_data_set_annotations (IBusEmojiData *emoji,
- const gchar *   ibus_emoji_data_get_description (IBusEmojiData *emoji);
- 
- /**
-+ * ibus_emoji_data_set_description:
-+ * @emoji : An #IBusEmojiData
-+ * @description: An emoji description
-+ *
-+ * Sets the description in #IBusEmojiData.
-+ */
-+void            ibus_emoji_data_set_description (IBusEmojiData *emoji,
-+                                                 const gchar   *description);
-+
-+
-+/**
-  * ibus_emoji_data_get_category:
-  * @emoji : An #IBusEmojiData
-  *
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 8cecea8..5e126e9 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -370,8 +370,14 @@ class IBusEmojier : Gtk.Window {
-     private void reload_emoji_dict() {
-         init_emoji_dict();
-         make_emoji_dict("en");
--        if (m_current_lang_id != "en")
-+        if (m_current_lang_id != "en") {
-+            var lang_ids = m_current_lang_id.split("_");
-+            if (lang_ids.length > 1) {
-+                string sub_id = lang_ids[0];
-+                make_emoji_dict(sub_id);
-+            }
-             make_emoji_dict(m_current_lang_id);
-+        }
-         loaded_emoji_dict();
-     }
- 
-@@ -393,8 +399,8 @@ class IBusEmojier : Gtk.Window {
-         if (emoji_list == null)
-             return;
-         foreach (IBus.EmojiData data in emoji_list) {
--            update_annotation_to_emojis_dict(data);
-             update_emoji_to_data_dict(data, lang);
-+            update_annotation_to_emojis_dict(data);
-             update_category_to_emojis_dict(data, lang);
-         }
-         GLib.List<unowned string> annotations =
-@@ -434,11 +440,16 @@ class IBusEmojier : Gtk.Window {
-             unowned IBus.EmojiData? en_data =
-                     m_emoji_to_data_dict.lookup(emoji);
-             if (en_data == null) {
--                warning("No IBusEmojiData for English: %s".printf(emoji));
-                 m_emoji_to_data_dict.insert(emoji, data);
-                 return;
-             }
-+            string trans_description = data.get_description();
-+            en_data.set_description(trans_description);
-             unowned GLib.SList<string> annotations = data.get_annotations();
-+            var words = trans_description.split(" ");
-+            // If the description has less than 3 words, add it to annotations
-+            if (words.length < 3)
-+                annotations.append(trans_description);
-             unowned GLib.SList<string> en_annotations
-                 = en_data.get_annotations();
-             foreach (string annotation in en_annotations) {
--- 
-2.9.3
-
-From ab6c38c192cdf22356cbf254b98fb5b3d9d9a680 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 15 Mar 2017 11:48:24 +0900
-Subject: [PATCH] client/x11: Add XSetIOErrorHandler() for GNOME3 desktop
-
-When log into GNOME3 desktop immediately after the system is booted,
-ibus-daemon is sometimes alive but ibus-x11 is dead after log out
-the session. Because gdk_x_io_error() is called as the callback of
-XSetIOErrorHandler() in gtk/gdk/x11/gdkmain-x11.c in ibus-x11.
-Now I assume the callback is called in logout.
-
-BUG=https://github.com/ibus/ibus/issues/1907
-
-Review URL: https://codereview.appspot.com/319490043
----
- client/x11/main.c | 16 ++++++++++++++++
- 1 file changed, 16 insertions(+)
-
-diff --git a/client/x11/main.c b/client/x11/main.c
-index a717a2c..159f430 100644
---- a/client/x11/main.c
-+++ b/client/x11/main.c
-@@ -2,6 +2,7 @@
- /* vim:set et sts=4: */
- /* ibus
-  * Copyright (C) 2007-2015 Peng Huang <shawn.p.huang@gmail.com>
-+ * Copyright (C) 2015-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-  * Copyright (C) 2007-2015 Red Hat, Inc.
-  *
-  * main.c:
-@@ -1131,6 +1132,20 @@ _xerror_handler (Display *dpy, XErrorEvent *e)
-     return 1;
- }
- 
-+/* When log into GNOME3 desktop immediately after the system is booted,
-+ * ibus-daemon is sometimes alive but ibus-x11 is dead after log out
-+ * the session. Because gdk_x_io_error() is called as the callback of
-+ * XSetIOErrorHandler() in gtk/gdk/x11/gdkmain-x11.c in ibus-x11.
-+ * Now I assume the callback is called in logout.
-+ */
-+static int
-+_xerror_io_handler (Display *dpy)
-+{
-+    if (_kill_daemon)
-+        _atexit_cb ();
-+    return 0;
-+}
-+
- int
- main (int argc, char **argv)
- {
-@@ -1146,6 +1161,7 @@ main (int argc, char **argv)
- 
-     gtk_init (&argc, &argv);
-     XSetErrorHandler (_xerror_handler);
-+    XSetIOErrorHandler (_xerror_io_handler);
- 
-     while (1) {
-         static struct option long_options [] = {
--- 
-2.9.3
-
-From 58f6140f427815adc947a5bb5c7dea4f3e315ae8 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 15 Mar 2017 11:52:39 +0900
-Subject: [PATCH] ui/gtk3: Implement shortcut keys on emoji dialog
-
-- Implement Ctrl-f, Ctrl-b, Ctrl-n, Ctrl-p, Ctrl-h, Ctrh-e for
-  cursor movements; forward, back, next, previous, head, end
-  on emoji grid.
-- Implement Ctrl-a and Shift+arrow for text selection on emoji annotation.
-- Implement Ctrl-u to delete text on emoji annotation.
-- Implement to delete a selected text on emoji annotation.
-- Change to show page indices to candidate indices on emoji.
-- Sorted emoji categories.
-- Added timeout of m_enter_notify_enable = false to bring back mouse.
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/315700043
----
- ui/gtk3/emojier.vala | 311 +++++++++++++++++++++++++++++++++++----------------
- 1 file changed, 215 insertions(+), 96 deletions(-)
-
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 5e126e9..7da96c7 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -214,6 +214,7 @@ class IBusEmojier : Gtk.Window {
-     private string[] m_favorites = {};
-     private bool m_enter_notify_enable = true;
-     private uint m_entry_notify_show_id;
-+    private uint m_entry_notify_disable_id;
- 
-     public signal void candidate_clicked(uint index, uint button, uint state);
-     public signal void loaded_emoji_dict();
-@@ -525,6 +526,18 @@ class IBusEmojier : Gtk.Window {
-         show_category_list();
-     }
- 
-+    private string get_title_string(string orig) {
-+        StringBuilder buff = new StringBuilder();
-+        for (int i = 0; i < orig.char_count(); i++) {
-+            unichar ch = orig.get_char(i);
-+            if (i == 0)
-+                buff.append_unichar(ch.toupper());
-+            else
-+                buff.append_unichar(ch);
-+        }
-+        return buff.str;
-+    }
-+
-     private void show_category_list() {
-         remove_all_children();
-         m_scrolled_window = new EScrolledWindow();
-@@ -586,19 +599,14 @@ class IBusEmojier : Gtk.Window {
-             }
-             GLib.List<unowned string> categories =
-                     m_category_to_emojis_dict.get_keys();
-+            categories.sort((a, b) => {
-+                return GLib.strcmp(_(a), _(b));
-+            });
-             foreach (unowned string category in categories) {
-                 EBoxRow row = new EBoxRow(category);
-                 string locale_category = _(category);
--                StringBuilder capital_category = new StringBuilder();
--                for (int i = 0; i < locale_category.char_count(); i++) {
--                    unichar ch = locale_category.get_char(i);
--                    if (i == 0)
--                        capital_category.append_unichar(ch.toupper());
--                    else
--                        capital_category.append_unichar(ch);
--                }
-                 EPaddedLabel widget =
--                        new EPaddedLabel(capital_category.str,
-+                        new EPaddedLabel(get_title_string(locale_category),
-                                          Gtk.Align.CENTER);
-                 row.add(widget);
-                 m_list_box.add(row);
-@@ -650,7 +658,7 @@ class IBusEmojier : Gtk.Window {
-                 IBus.Text text = new IBus.Text.from_string(emoji);
-                 m_lookup_table.append_candidate(text);
-             }
--            m_backward = row.text;
-+            m_backward = get_title_string(row.text);
-         }
-         show_candidate_panel();
-     }
-@@ -759,9 +767,7 @@ class IBusEmojier : Gtk.Window {
-         uint page_end_pos = uint.min(page_start_pos + page_size, ncandidates);
-         if (m_backward != null) {
-             string backward_desc =
--                    "%s (%u / %u)".printf(m_backward,
--                                          cursor / page_size + 1,
--                                          ncandidates / page_size + 1);
-+                    "%s (%u / %u)".printf(m_backward, cursor, ncandidates - 1);
-             EPaddedLabel label = new EPaddedLabel(backward_desc,
-                                                   Gtk.Align.CENTER,
-                                                   TravelDirection.BACKWARD);
-@@ -805,23 +811,23 @@ class IBusEmojier : Gtk.Window {
-                 candidate_clicked(index, e.button, e.state);
-                 return true;
-             });
--            // m_enter_notify_enable is added because
--            // enter_notify_event conflicts with keyboard operations.
--            if (m_enter_notify_enable) {
--                candidate_ebox.enter_notify_event.connect((e) => {
--                    m_lookup_table.set_cursor_pos(index);
--                    if (m_entry_notify_show_id > 0) {
-+            candidate_ebox.enter_notify_event.connect((e) => {
-+                // m_enter_notify_enable is added because
-+                // enter_notify_event conflicts with keyboard operations.
-+                if (!m_enter_notify_enable)
-+                    return true;
-+                m_lookup_table.set_cursor_pos(index);
-+                if (m_entry_notify_show_id > 0) {
-                         GLib.Source.remove(m_entry_notify_show_id);
--                    }
--                    // If timeout is not added, memory leak happens and
--                    // button_press_event signal does not work above.
--                    m_entry_notify_show_id = GLib.Timeout.add(100, () => {
-+                }
-+                // If timeout is not added, memory leak happens and
-+                // button_press_event signal does not work above.
-+                m_entry_notify_show_id = GLib.Timeout.add(100, () => {
-                         show_candidate_panel();
-                         return false;
--                    });
--                    return true;
-                 });
--            }
-+                return true;
-+            });
-             grid.attach(candidate_ebox,
-                         n % (int)EMOJI_GRID_PAGE, n / (int)EMOJI_GRID_PAGE,
-                         1, 1);
-@@ -844,16 +850,23 @@ class IBusEmojier : Gtk.Window {
-                 widget.show_all();
-                 return;
-             }
--            unowned IBus.EmojiData data =
-+            unowned IBus.EmojiData? data =
-                     m_emoji_to_data_dict.lookup(candidate.text);
--            unowned string description = data.get_description();
--            if (description != "") {
-+            if (data == null) {
-+                // TODO: Provide a description for the favorite emojis.
-                 EPaddedLabel widget = new EPaddedLabel(
--                        _("Description: %s").printf(description),
-+                        _("Description: %s").printf(_("None")),
-                         Gtk.Align.START);
-                 m_vbox.add(widget);
-                 widget.show_all();
-+                return;
-             }
-+            unowned string description = data.get_description();
-+            EPaddedLabel desc_widget = new EPaddedLabel(
-+                    _("Description: %s").printf(description),
-+                    Gtk.Align.START);
-+            m_vbox.add(desc_widget);
-+            desc_widget.show_all();
-             unowned GLib.SList<unowned string>? annotations =
-                     data.get_annotations();
-             GLib.StringBuilder buff = new GLib.StringBuilder();
-@@ -922,8 +935,21 @@ class IBusEmojier : Gtk.Window {
-         m_result = text.text;
-     }
- 
--    private void candidate_panel_cursor_down() {
-+    private void enter_notify_disable_with_timer() {
-+        // Enable keyboard operation and disable mouse operation.
-         m_enter_notify_enable = false;
-+        if (m_entry_notify_disable_id > 0) {
-+            GLib.Source.remove(m_entry_notify_disable_id);
-+        }
-+        // Bring back the mouse operation after a timeout.
-+        m_entry_notify_show_id = GLib.Timeout.add(100, () => {
-+            m_enter_notify_enable = true;
-+            return false;
-+        });
-+    }
-+
-+    private void candidate_panel_cursor_down() {
-+        enter_notify_disable_with_timer();
-         uint ncandidates = m_lookup_table.get_number_of_candidates();
-         uint cursor = m_lookup_table.get_cursor_pos();
-         if ((cursor + EMOJI_GRID_PAGE) < ncandidates) {
-@@ -937,11 +963,11 @@ class IBusEmojier : Gtk.Window {
-     }
- 
-     private void candidate_panel_cursor_up() {
--        m_enter_notify_enable = false;
-+        enter_notify_disable_with_timer();
-         int ncandidates = (int)m_lookup_table.get_number_of_candidates();
-         int cursor = (int)m_lookup_table.get_cursor_pos();
-         int highest_pos =
--            (ncandidates / (int)EMOJI_GRID_PAGE * (int)EMOJI_GRID_PAGE)
-+            ((ncandidates - 1)/ (int)EMOJI_GRID_PAGE * (int)EMOJI_GRID_PAGE)
-             + (cursor % (int)EMOJI_GRID_PAGE);
-         if ((cursor - (int)EMOJI_GRID_PAGE) >= 0) {
-             m_lookup_table.set_cursor_pos(cursor - (int)EMOJI_GRID_PAGE);
-@@ -967,13 +993,119 @@ class IBusEmojier : Gtk.Window {
-         show_category_list();
-     }
- 
-+    private bool key_press_cursor_horizontal(uint keyval,
-+                                             uint modifiers) {
-+        assert (keyval == Gdk.Key.Left || keyval == Gdk.Key.Right);
-+
-+        if (m_candidate_panel_is_visible) {
-+            enter_notify_disable_with_timer();
-+            if (keyval == Gdk.Key.Left)
-+                m_lookup_table.cursor_up();
-+            else if (keyval == Gdk.Key.Right)
-+                m_lookup_table.cursor_down();
-+            show_candidate_panel();
-+        } else if (m_entry.get_text().len() > 0) {
-+            int step = 0;
-+            if (keyval == Gdk.Key.Left)
-+                step = -1;
-+            else if (keyval == Gdk.Key.Right)
-+                step = 1;
-+            GLib.Signal.emit_by_name(
-+                    m_entry, "move-cursor",
-+                    Gtk.MovementStep.VISUAL_POSITIONS,
-+                    step,
-+                    (modifiers & Gdk.ModifierType.SHIFT_MASK) != 0
-+                            ? true : false);
-+        } else {
-+            // For Gdk.Key.f and Gdk.Key.b
-+            if (keyval == Gdk.Key.Left)
-+                keyval = Gdk.Key.Up;
-+            else if (keyval == Gdk.Key.Right)
-+                keyval = Gdk.Key.Down;
-+            category_list_cursor_move(keyval);
-+        }
-+        return true;
-+    }
-+
-+    private bool key_press_cursor_vertical(uint keyval) {
-+        assert (keyval == Gdk.Key.Down || keyval == Gdk.Key.Up);
-+
-+        if (m_candidate_panel_is_visible) {
-+            if (keyval == Gdk.Key.Down)
-+                candidate_panel_cursor_down();
-+            else if (keyval == Gdk.Key.Up)
-+                candidate_panel_cursor_up();
-+        } else {
-+            category_list_cursor_move(keyval);
-+        }
-+        return true;
-+    }
-+
-+    private bool key_press_cursor_home_end(uint keyval,
-+                                           uint modifiers) {
-+        assert (keyval == Gdk.Key.Home || keyval == Gdk.Key.End);
-+
-+        if (m_candidate_panel_is_visible) {
-+            enter_notify_disable_with_timer();
-+            if (keyval == Gdk.Key.Home) {
-+                m_lookup_table.set_cursor_pos(0);
-+            } else if (keyval == Gdk.Key.End) {
-+                uint ncandidates = m_lookup_table.get_number_of_candidates();
-+                m_lookup_table.set_cursor_pos(ncandidates - 1);
-+            }
-+            show_candidate_panel();
-+            return true;
-+        }
-+        if (m_entry.get_text().len() > 0) {
-+            int step = 0;
-+            if (keyval == Gdk.Key.Home)
-+                step = -1;
-+            else if (keyval == Gdk.Key.End)
-+                step = 1;
-+            GLib.Signal.emit_by_name(
-+                    m_entry, "move-cursor",
-+                    Gtk.MovementStep.DISPLAY_LINE_ENDS,
-+                    step,
-+                    (modifiers & Gdk.ModifierType.SHIFT_MASK) != 0
-+                            ? true : false);
-+            return true;
-+        }
-+        return false;
-+    }
-+
-+    private bool key_press_cursor_escape() {
-+        if (m_candidate_panel_is_visible) {
-+            hide_candidate_panel();
-+            return true;
-+        } else if (m_current_category_type == CategoryType.LANG) {
-+            m_current_category_type = CategoryType.EMOJI;
-+            show_candidate_panel();
-+            return true;
-+        } else if (m_entry.get_text().length == 0) {
-+            m_loop.quit();
-+            hide_candidate_panel();
-+            return true;
-+        }
-+        m_entry.delete_text(0, -1);
-+        return true;
-+    }
-+
-     private void entry_enter_keyval(uint keyval) {
-         unichar ch = IBus.keyval_to_unicode(keyval);
--        if (!ch.isgraph())
-+        if (ch.iscntrl())
-             return;
-         string str = ch.to_string();
- 
-         // what gtk_entry_commit_cb() do
-+        if (m_entry.get_selection_bounds(null, null)) {
-+            m_entry.delete_selection();
-+        } else {
-+            if (m_entry.get_overwrite_mode()) {
-+               uint text_length = m_entry.get_buffer().get_length();
-+               if (m_entry.cursor_position < text_length)
-+                   m_entry.delete_from_cursor(Gtk.DeleteType.CHARS, 1);
-+            }
-+        }
-         int pos = m_entry.get_position();
-         m_entry.insert_text(str, -1, ref pos);
-         m_entry.set_position(pos);
-@@ -1084,19 +1216,8 @@ class IBusEmojier : Gtk.Window {
-         }
-         switch (keyval) {
-         case Gdk.Key.Escape:
--            if (m_candidate_panel_is_visible) {
--                hide_candidate_panel();
--                return true;
--            } else if (m_current_category_type == CategoryType.LANG) {
--                m_current_category_type = CategoryType.EMOJI;
--                show_candidate_panel();
-+            if (key_press_cursor_escape())
-                 return true;
--            } else if (m_entry.get_text().length == 0) {
--                m_loop.quit();
--                hide_candidate_panel();
--                return true;
--            }
--            m_entry.delete_text(0, -1);
-             break;
-         case Gdk.Key.Return:
-             if (m_candidate_panel_is_visible) {
-@@ -1126,7 +1247,7 @@ class IBusEmojier : Gtk.Window {
-                 break;
-             }
-             if (m_candidate_panel_is_visible) {
--                m_enter_notify_enable = false;
-+                enter_notify_disable_with_timer();
-                 m_lookup_table.cursor_down();
-                 show_candidate_panel();
-             }
-@@ -1135,48 +1256,20 @@ class IBusEmojier : Gtk.Window {
-             }
-             return true;
-         case Gdk.Key.Right:
--            if (m_candidate_panel_is_visible) {
--                m_enter_notify_enable = false;
--                m_lookup_table.cursor_down();
--                show_candidate_panel();
--                return true;
--            }
--            if (m_entry.get_text().len() > 0) {
--                GLib.Signal.emit_by_name(m_entry, "move-cursor",
--                                         Gtk.MovementStep.VISUAL_POSITIONS,
--                                         1, false);
--                return true;
--            }
--            break;
-+            key_press_cursor_horizontal(keyval, modifiers);
-+            return true;
-         case Gdk.Key.Left:
--            if (m_candidate_panel_is_visible) {
--                m_enter_notify_enable = false;
--                m_lookup_table.cursor_up();
--                show_candidate_panel();
--                return true;
--            }
--            if (m_entry.get_text().len() > 0) {
--                GLib.Signal.emit_by_name(m_entry, "move-cursor",
--                                         Gtk.MovementStep.VISUAL_POSITIONS,
--                                         -1, false);
--                return true;
--            }
--            break;
-+            key_press_cursor_horizontal(keyval, modifiers);
-+            return true;
-         case Gdk.Key.Down:
--            if (m_candidate_panel_is_visible)
--                candidate_panel_cursor_down();
--            else
--                category_list_cursor_move(Gdk.Key.Down);
-+            key_press_cursor_vertical(keyval);
-             return true;
-         case Gdk.Key.Up:
--            if (m_candidate_panel_is_visible)
--                candidate_panel_cursor_up();
--            else
--                category_list_cursor_move(Gdk.Key.Up);
-+            key_press_cursor_vertical(keyval);
-             return true;
-         case Gdk.Key.Page_Down:
-             if (m_candidate_panel_is_visible) {
--                m_enter_notify_enable = false;
-+                enter_notify_disable_with_timer();
-                 m_lookup_table.page_down();
-                 show_candidate_panel();
-                 return true;
-@@ -1184,33 +1277,59 @@ class IBusEmojier : Gtk.Window {
-             break;
-         case Gdk.Key.Page_Up:
-             if (m_candidate_panel_is_visible) {
--                m_enter_notify_enable = false;
-+                enter_notify_disable_with_timer();
-                 m_lookup_table.page_up();
-                 show_candidate_panel();
-                 return true;
-             }
-             break;
-         case Gdk.Key.Home:
--            if (m_entry.get_text().len() > 0) {
--                GLib.Signal.emit_by_name(m_entry, "move-cursor",
--                                         Gtk.MovementStep.DISPLAY_LINE_ENDS,
--                                         -1, false);
-+            if (key_press_cursor_home_end(keyval, modifiers))
-                 return true;
--            }
-             break;
-         case Gdk.Key.End:
--            if (m_entry.get_text().len() > 0) {
--                GLib.Signal.emit_by_name(m_entry, "move-cursor",
--                                         Gtk.MovementStep.DISPLAY_LINE_ENDS,
--                                         1, false);
-+            if (key_press_cursor_home_end(keyval, modifiers))
-                 return true;
--            }
--            break;
--        default:
--            entry_enter_keyval(keyval);
-             break;
-         }
- 
-+        if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) {
-+            switch (keyval) {
-+            case Gdk.Key.f:
-+                key_press_cursor_horizontal(Gdk.Key.Right, modifiers);
-+                return true;
-+            case Gdk.Key.b:
-+                key_press_cursor_horizontal(Gdk.Key.Left, modifiers);
-+                return true;
-+            case Gdk.Key.n:
-+                key_press_cursor_vertical(Gdk.Key.Down);
-+                return true;
-+            case Gdk.Key.p:
-+                key_press_cursor_vertical(Gdk.Key.Up);
-+                return true;
-+            case Gdk.Key.h:
-+                if (key_press_cursor_home_end(Gdk.Key.Home, modifiers))
-+                    return true;
-+                break;
-+            case Gdk.Key.e:
-+                if (key_press_cursor_home_end(Gdk.Key.End, modifiers))
-+                    return true;
-+                break;
-+            case Gdk.Key.u:
-+                if (key_press_cursor_escape())
-+                    return true;
-+                break;
-+            case Gdk.Key.a:
-+                if (m_entry.get_text().len() > 0) {
-+                    m_entry.select_region(0, -1);
-+                    return true;
-+                }
-+                break;
-+            }
-+            return false;
-+        }
-+
-+        entry_enter_keyval(keyval);
-         return true;
-     }
- 
--- 
-2.9.3
-
-From 50e344afaffc29e626dbc27747a1aeee6cccafdf Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Fri, 17 Mar 2017 12:08:50 +0900
-Subject: [PATCH] ui/gtk3: Enable strcasecmp to match emoji annotation
-
-Users can type capital annotations.
-Also shows emoji annotations in the status bar if the
-typing unicode point matches a emoji character.
-
-Review URL: https://codereview.appspot.com/314640043
----
- ui/gtk3/emojier.vala | 97 +++++++++++++++++++++++++++++++++++-----------------
- 1 file changed, 65 insertions(+), 32 deletions(-)
-
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 7da96c7..b1dc50c 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -432,10 +432,45 @@ class IBusEmojier : Gtk.Window {
-         }
-     }
- 
-+    private string utf8_down(string str) {
-+        GLib.StringBuilder buff = new GLib.StringBuilder();
-+        int length = str.char_count();
-+        for (int i = 0; i < length; i++) {
-+            buff.append_unichar(str.get_char(0).tolower());
-+            str = str.next_char();
-+        }
-+        return buff.str;
-+    }
-+
-+    private string utf8_title(string str) {
-+        StringBuilder buff = new StringBuilder();
-+        int length = str.char_count();
-+        for (int i = 0; i < length; i++) {
-+            unichar ch = str.get_char(0);
-+            if (i == 0)
-+                buff.append_unichar(ch.toupper());
-+            else
-+                buff.append_unichar(ch);
-+            str = str.next_char();
-+        }
-+        return buff.str;
-+    }
-+
-     private void update_emoji_to_data_dict(IBus.EmojiData data,
-                                            string         lang) {
-         string emoji = data.get_emoji();
-         if (lang == "en") {
-+            string description = utf8_down(data.get_description());
-+            unowned GLib.SList<string> annotations = data.get_annotations();
-+            var words = description.split(" ");
-+            // If the description has less than 3 words, add it to annotations
-+            if (words.length < 3 &&
-+                annotations.find_custom(
-+                        description,
-+                        (GLib.CompareFunc<string>)GLib.strcmp) == null) {
-+                annotations.append(description);
-+                data.set_annotations(annotations.copy_deep(GLib.strdup));
-+            }
-             m_emoji_to_data_dict.replace(emoji, data);
-         } else {
-             unowned IBus.EmojiData? en_data =
-@@ -446,16 +481,24 @@ class IBusEmojier : Gtk.Window {
-             }
-             string trans_description = data.get_description();
-             en_data.set_description(trans_description);
-+            trans_description = utf8_down(trans_description);
-             unowned GLib.SList<string> annotations = data.get_annotations();
-             var words = trans_description.split(" ");
-             // If the description has less than 3 words, add it to annotations
--            if (words.length < 3)
-+            if (words.length < 3 &&
-+                annotations.find_custom(
-+                        trans_description,
-+                        (GLib.CompareFunc<string>)GLib.strcmp) == null) {
-                 annotations.append(trans_description);
-+            }
-             unowned GLib.SList<string> en_annotations
-                 = en_data.get_annotations();
-             foreach (string annotation in en_annotations) {
--                if (annotations.find_custom(annotation, GLib.strcmp) == null)
-+                if (annotations.find_custom(
-+                            annotation,
-+                            (GLib.CompareFunc<string>)GLib.strcmp) == null) {
-                     annotations.append(annotation.dup());
-+                }
-             }
-             en_data.set_annotations(annotations.copy_deep(GLib.strdup));
-         }
-@@ -526,18 +569,6 @@ class IBusEmojier : Gtk.Window {
-         show_category_list();
-     }
- 
--    private string get_title_string(string orig) {
--        StringBuilder buff = new StringBuilder();
--        for (int i = 0; i < orig.char_count(); i++) {
--            unichar ch = orig.get_char(i);
--            if (i == 0)
--                buff.append_unichar(ch.toupper());
--            else
--                buff.append_unichar(ch);
--        }
--        return buff.str;
--    }
--
-     private void show_category_list() {
-         remove_all_children();
-         m_scrolled_window = new EScrolledWindow();
-@@ -606,7 +637,7 @@ class IBusEmojier : Gtk.Window {
-                 EBoxRow row = new EBoxRow(category);
-                 string locale_category = _(category);
-                 EPaddedLabel widget =
--                        new EPaddedLabel(get_title_string(locale_category),
-+                        new EPaddedLabel(utf8_title(locale_category),
-                                          Gtk.Align.CENTER);
-                 row.add(widget);
-                 m_list_box.add(row);
-@@ -658,7 +689,7 @@ class IBusEmojier : Gtk.Window {
-                 IBus.Text text = new IBus.Text.from_string(emoji);
-                 m_lookup_table.append_candidate(text);
-             }
--            m_backward = get_title_string(row.text);
-+            m_backward = utf8_title(row.text);
-         }
-         show_candidate_panel();
-     }
-@@ -734,6 +765,7 @@ class IBusEmojier : Gtk.Window {
-             m_backward = null;
-             return;
-         }
-+        annotation = utf8_down(annotation);
-         if (annotation.length > m_emoji_max_seq_len) {
-             hide_candidate_panel();
-             return;
-@@ -841,6 +873,8 @@ class IBusEmojier : Gtk.Window {
-             m_vbox.add(grid);
-             grid.show_all();
-             IBus.Text candidate = m_lookup_table.get_candidate(cursor);
-+            unowned IBus.EmojiData? data =
-+                    m_emoji_to_data_dict.lookup(candidate.text);
-             if (cursor == 0 && candidate.text == m_unicode_point) {
-                 EPaddedLabel widget = new EPaddedLabel(
-                         _("Description: Unicode point U+%04X").printf(
-@@ -848,25 +882,25 @@ class IBusEmojier : Gtk.Window {
-                         Gtk.Align.START);
-                 m_vbox.add(widget);
-                 widget.show_all();
--                return;
--            }
--            unowned IBus.EmojiData? data =
--                    m_emoji_to_data_dict.lookup(candidate.text);
--            if (data == null) {
--                // TODO: Provide a description for the favorite emojis.
-+                if (data == null)
-+                    return;
-+            } else if (data == null) {
-+                // TODO: Provide a custom description and annotation for
-+                // the favorite emojis.
-                 EPaddedLabel widget = new EPaddedLabel(
-                         _("Description: %s").printf(_("None")),
-                         Gtk.Align.START);
-                 m_vbox.add(widget);
-                 widget.show_all();
-                 return;
-+            } else {
-+                unowned string description = data.get_description();
-+                EPaddedLabel widget = new EPaddedLabel(
-+                        _("Description: %s").printf(description),
-+                        Gtk.Align.START);
-+                m_vbox.add(widget);
-+                widget.show_all();
-             }
--            unowned string description = data.get_description();
--            EPaddedLabel desc_widget = new EPaddedLabel(
--                    _("Description: %s").printf(description),
--                    Gtk.Align.START);
--            m_vbox.add(desc_widget);
--            desc_widget.show_all();
-             unowned GLib.SList<unowned string>? annotations =
-                     data.get_annotations();
-             GLib.StringBuilder buff = new GLib.StringBuilder();
-@@ -1243,10 +1277,9 @@ class IBusEmojier : Gtk.Window {
-         case Gdk.Key.space:
-         case Gdk.Key.KP_Space:
-             if ((modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) {
--                entry_enter_keyval(keyval);
--                break;
--            }
--            if (m_candidate_panel_is_visible) {
-+                if (m_entry.get_text().len() > 0)
-+                    entry_enter_keyval(keyval);
-+            } else if (m_candidate_panel_is_visible) {
-                 enter_notify_disable_with_timer();
-                 m_lookup_table.cursor_down();
-                 show_candidate_panel();
--- 
-2.9.3
-
-From bd7e0ba297f72ae1e2989743f2426c44df29f3ec Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 21 Mar 2017 12:56:23 +0900
-Subject: [PATCH] Make more readable error messages if emoji xml files are
- missed
-
-Also Fix CONFIG_CLEAN_FILES for autoreconf
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/320750043
----
- configure.ac        | 26 +++++++++++++++++---------
- src/Makefile.am     |  5 ++++-
- src/emoji-parser.c  |  2 +-
- ui/gtk3/Makefile.am |  4 +++-
- 4 files changed, 25 insertions(+), 12 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
-index 369485c..0a5f2d5 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -611,17 +611,11 @@ AC_ARG_ENABLE(emoji-dict,
-     [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(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")
--         You can get emoji.json with "npm install -g emojione".]),
-+        ]),
-     EMOJI_JSON_FILE=$with_emoji_json_file,
-     EMOJI_JSON_FILE="/usr/lib/node_modules/emojione/emoji.json"
- )
-@@ -630,13 +624,27 @@ AC_SUBST(EMOJI_JSON_FILE)
- AC_ARG_WITH(emoji-annotation-dir,
-     AS_HELP_STRING([--with-emoji-annotation-dir[=DIR]],
-         [Set the directory of CLDR annotation files.
--         (default: "/usr/share/unicode/cldr/common/annotations")
--         You can get https://github.com/fujiwarat/cldr-emoji-annotation]),
-+         (default: "/usr/share/unicode/cldr/common/annotations")]),
-     EMOJI_ANNOTATION_DIR=$with_emoji_annotation_dir,
-     EMOJI_ANNOTATION_DIR="/usr/share/unicode/cldr/common/annotations"
- )
- AC_SUBST(EMOJI_ANNOTATION_DIR)
- 
-+if test x"$enable_emoji_dict" = x"yes"; then
-+    if test ! -f $EMOJI_JSON_FILE ; then
-+        AC_MSG_ERROR(Not found $EMOJI_JSON_FILE. You can get emoji.json \
-+with "npm install -g emojione".)
-+    fi
-+    if test ! -f $EMOJI_ANNOTATION_DIR/en.xml ; then
-+        AC_MSG_ERROR(Not found $EMOJI_ANNOTATION_DIR/en.xml. You can get \
-+https://github.com/fujiwarat/cldr-emoji-annotation)
-+    fi
-+    PKG_CHECK_MODULES(JSON_GLIB1, [
-+        json-glib-1.0
-+    ])
-+    enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)"
-+fi
-+
- # Check iso-codes.
- PKG_CHECK_MODULES(ISOCODES, [
-     iso-codes
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 0d403e8..7053e3e 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -243,7 +243,10 @@ LANG_FILES = $(basename $(notdir $(wildcard $(EMOJI_ANNOTATION_DIR)/*.xml)))
- noinst_PROGRAMS = emoji-parser
- 
- dicts/emoji-en.dict: emoji-parser
--	$(AM_V_at)for f in $(LANG_FILES) ; do \
-+	$(AM_V_at)if test x"$(LANG_FILES)" = x ; then \
-+	    echo "WARNING: Not found $(EMOJI_ANNOTATION_DIR)/en.xml" 1>&2; \
-+	fi; \
-+	for f in $(LANG_FILES) ; do \
- 	    if test x"$$f" = xen ; then \
- 	        $(builddir)/emoji-parser \
- 	            --xml $(EMOJI_ANNOTATION_DIR)/$$f.xml \
-diff --git a/src/emoji-parser.c b/src/emoji-parser.c
-index 8ff04f1..f9e3470 100644
---- a/src/emoji-parser.c
-+++ b/src/emoji-parser.c
-@@ -20,7 +20,7 @@
-  * USA
-  */
- 
--/* Convert /usr/share/unicode/cldr/common/annotations/*.xml and
-+/* Convert /usr/share/unicode/cldr/common/annotations/\*.xml and
-  * /usr/lib/node_modules/emojione/emoji.json
-  * to the dictionary file which look up the Emoji from the annotation.
-  * Get *.xml from https://github.com/fujiwarat/cldr-emoji-annotation
-diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
-index 4e7fd1b..b055f67 100644
---- a/ui/gtk3/Makefile.am
-+++ b/ui/gtk3/Makefile.am
-@@ -81,6 +81,8 @@ AM_VALAFLAGS = \
- 	--target-glib="$(VALA_TARGET_GLIB_VERSION)" \
- 	$(NULL)
- 
-+CONFIG_CLEAN_FILES =
-+
- if ENABLE_LIBNOTIFY
- AM_CFLAGS += \
- 	@LIBNOTIFY_CFLAGS@ \
-@@ -239,7 +241,7 @@ vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
- 
- MAINTAINERCLEANFILES = $(VAPIGEN_VAPIS)
- # for make distclean
--CONFIG_CLEAN_FILES = $(VAPIGEN_VAPIS)
-+CONFIG_CLEAN_FILES += $(VAPIGEN_VAPIS)
- EXTRA_DIST += $(VAPIGEN_VAPIS)
- 
- endif
--- 
-2.9.3
-
-From 0efb1c503d5901bbddcdb6fa73007b364ba4368d Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 27 Mar 2017 15:12:42 +0900
-Subject: [PATCH] Move language setting from IBusEmojier to ibus-setup
-
-The language setting of emoji annotations now can be saved
-with ibus-setup.
-Implement `ibus emoji [--font|--lang|--help]`
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/323720043
----
- data/ibus.schemas.in      |  46 +++---
- setup/Makefile.am         |  26 ++--
- setup/emojilang.py        | 348 ++++++++++++++++++++++++++++++++++++++++++++++
- setup/main.py             |  41 ++++--
- setup/setup.ui            |  97 ++++++++-----
- tools/main.vala           |  38 ++++-
- ui/gtk3/emojier.vala      | 295 +++++++++++++++------------------------
- ui/gtk3/ibusemojidialog.h |  11 ++
- ui/gtk3/panel.vala        |  39 ++++--
- 9 files changed, 671 insertions(+), 270 deletions(-)
- create mode 100644 setup/emojilang.py
-
-diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
-index 218d223..c0bbd6f 100644
---- a/data/ibus.schemas.in
-+++ b/data/ibus.schemas.in
-@@ -106,18 +106,6 @@
-       </locale>
-     </schema>
-     <schema>
--      <key>/schemas/desktop/ibus/general/hotkey/emoji</key>
--      <applyto>/desktop/ibus/general/hotkey/emoji</applyto>
--      <owner>ibus</owner>
--      <type>list</type>
--      <list_type>string</list_type>
--      <default>[&lt;Control&gt;&lt;Shift&gt;e]</default>
--      <locale name="C">
--        <short>Emoji shortcut keys for gtk_accelerator_parse</short>
--          <long>The shortcut keys for turning emoji typing on or off</long>
--      </locale>
--    </schema>
--    <schema>
-       <key>/schemas/desktop/ibus/general/hotkey/enable_unconditional</key>
-       <applyto>/desktop/ibus/general/hotkey/enable_unconditional</applyto>
-       <owner>ibus</owner>
-@@ -366,8 +354,20 @@
-       </locale>
-     </schema>
-     <schema>
--      <key>/schemas/desktop/ibus/panel/emoji_font</key>
--      <applyto>/desktop/ibus/panel/emoji_font</applyto>
-+      <key>/schemas/desktop/ibus/panel/emoji/hotkey</key>
-+      <applyto>/desktop/ibus/panel/emoji/hotkey</applyto>
-+      <owner>ibus</owner>
-+      <type>list</type>
-+      <list_type>string</list_type>
-+      <default>[&lt;Control&gt;&lt;Shift&gt;e]</default>
-+      <locale name="C">
-+        <short>Emoji shortcut keys for gtk_accelerator_parse</short>
-+          <long>The shortcut keys for turning emoji typing on or off</long>
-+      </locale>
-+    </schema>
-+    <schema>
-+      <key>/schemas/desktop/ibus/panel/emoji/font</key>
-+      <applyto>/desktop/ibus/panel/emoji/font</applyto>
-       <owner>ibus</owner>
-       <type>string</type>
-       <default>Monospace 16</default>
-@@ -377,8 +377,22 @@
-       </locale>
-     </schema>
-     <schema>
--      <key>/schemas/desktop/ibus/panel/emoji_favorites</key>
--      <applyto>/desktop/ibus/panel/emoji_favorites</applyto>
-+      <key>/schemas/desktop/ibus/panel/emoji/lang</key>
-+      <applyto>/desktop/ibus/panel/emoji/lang</applyto>
-+      <owner>ibus</owner>
-+      <type>string</type>
-+      <default>en</default>
-+      <locale name="C">
-+        <short>Default language for emoji dictionary</short>
-+	    <long>Choose a default language of emoji dictionaries on
-+	          the emoji dialog. The value $lang is applied to
-+                  /usr/share/unicode/cldr/common/annotations/$lang.xml
-+            </long>
-+      </locale>
-+    </schema>
-+    <schema>
-+      <key>/schemas/desktop/ibus/panel/emoji/favorites</key>
-+      <applyto>/desktop/ibus/panel/emoji/favorites</applyto>
-       <owner>ibus</owner>
-       <type>list</type>
-       <default>[]</default>
-diff --git a/setup/Makefile.am b/setup/Makefile.am
-index 2d822d2..b7dd755 100644
---- a/setup/Makefile.am
-+++ b/setup/Makefile.am
-@@ -3,7 +3,8 @@
- # ibus - The Input Bus
- #
- # Copyright (c) 2007-2014 Peng Huang <shawn.p.huang@gmail.com>
--# Copyright (c) 2007-2014 Red Hat, Inc.
-+# Copyright (c) 2015-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+# Copyright (c) 2007-2017 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,19 +22,20 @@
- # USA
- 
- ibussetup_PYTHON = \
--	main.py \
--	i18n.py \
--	icon.py \
--	enginecombobox.py \
--	enginedialog.py \
--	enginetreeview.py \
--	engineabout.py \
--	keyboardshortcut.py \
--	$(NULL)
-+    emojilang.py \
-+    enginecombobox.py \
-+    enginedialog.py \
-+    enginetreeview.py \
-+    engineabout.py \
-+    i18n.py \
-+    icon.py \
-+    keyboardshortcut.py \
-+    main.py \
-+    $(NULL)
- 
- ibussetup_DATA = \
--	setup.ui \
--	$(NULL)
-+    setup.ui \
-+    $(NULL)
- 
- bin_SCRIPTS = ibus-setup
- ibussetupdir = $(pkgdatadir)/setup
-diff --git a/setup/emojilang.py b/setup/emojilang.py
-new file mode 100644
-index 0000000..8250589
---- /dev/null
-+++ b/setup/emojilang.py
-@@ -0,0 +1,348 @@
-+# vim:set et sts=4 sw=4:
-+# -*- coding: utf-8 -*-
-+#
-+# ibus - The Input Bus
-+#
-+# Copyright (c) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+# Copyright (c) 2017 Red Hat, Inc.
-+#
-+# This program is free software; you can redistribute it and/or
-+# modify it under the terms of the GNU General Public License as
-+# published by the Free Software Foundation; either version 2 of the
-+# License, or (at your option) any later version.
-+#
-+# This program 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
-+# General Public License for more details.
-+#
-+# You should have received a copy of the GNU General Public License
-+# along with this program; if not, see <http://www.gnu.org/licenses/>.
-+
-+# for python2
-+from __future__ import print_function
-+
-+__all__ = (
-+    "EmojiLangButton",
-+);
-+
-+from gi.repository import Gtk
-+from gi.repository import GLib
-+from gi.repository import GObject
-+from gi.repository import IBus
-+
-+import functools
-+import gettext
-+import i18n
-+import locale
-+import os
-+
-+from icon import load_icon
-+from i18n import _, N_
-+
-+ROW_TRAVEL_DIRECTION_NONE,      \
-+ROW_TRAVEL_DIRECTION_FORWARD,   \
-+ROW_TRAVEL_DIRECTION_BACKWARD = list(range(3))
-+
-+class LanguageString:
-+    def __init__(self, id, trans = ""):
-+        self.id = id
-+        self.trans = trans
-+
-+class EmojiLangChooser(Gtk.Dialog):
-+    __gtype_name__ = 'EmojiLangChooser'
-+    __initial_languages = [ IBus.get_language_name('en_US'),
-+                            IBus.get_language_name('en_GB'),
-+                            IBus.get_language_name('de_DE'),
-+                            IBus.get_language_name('fr_FR'),
-+                            IBus.get_language_name('es_ES'),
-+                            IBus.get_language_name('zh_CN'),
-+                            IBus.get_language_name('ja_JP'),
-+                            IBus.get_language_name('ru_RU'),
-+                            IBus.get_language_name('ar_EG') ]
-+
-+
-+    def __init__(self, id = None, transient_for = None):
-+        super(EmojiLangChooser, self).__init__(
-+                title = _("Select a language"),
-+                transient_for = transient_for,
-+                resizable = True)
-+        buttons = (_("_Cancel"), Gtk.ResponseType.CANCEL,
-+                   _("_OK"), Gtk.ResponseType.APPLY)
-+        self.add_buttons(*buttons)
-+
-+        if id == None:
-+            id = 'en'
-+        self.__id = id
-+        self.__engines_for_lang = {}
-+        self.__untrans_for_lang = {}
-+        self.__langs = {}
-+        self.__lang_list = []
-+
-+        self.__scrolled = Gtk.ScrolledWindow(
-+                hscrollbar_policy = Gtk.PolicyType.NEVER,
-+                vscrollbar_policy = Gtk.PolicyType.NEVER,
-+                shadow_type = Gtk.ShadowType.IN,
-+                margin_left = 6,
-+                margin_right = 6,
-+                margin_top = 6,
-+                margin_bottom = 6)
-+        self.vbox.add(self.__scrolled)
-+        viewport = Gtk.Viewport()
-+        self.__scrolled.add(viewport)
-+        self.__list = Gtk.ListBox(vexpand = True,
-+                                  halign = Gtk.Align.FILL,
-+                                  valign = Gtk.Align.FILL)
-+        viewport.add(self.__list)
-+
-+        self.__adjustment = self.__scrolled.get_vadjustment()
-+        self.__list.set_adjustment(self.__adjustment)
-+        self.__list.set_filter_func(self.__list_filter, None)
-+        self.__list.connect('row-activated', self.__row_activated)
-+
-+        self.__showing_extra = False
-+        self.__more_row = self.__more_row_new()
-+        self.__load_lang_list()
-+        self.__show_lang_rows()
-+        self.show_all()
-+
-+
-+    def __load_lang_list(self):
-+        dictdir = os.path.dirname(__file__) + '/../dicts'
-+        for filename in os.listdir(dictdir):
-+            suffix = '.dict'
-+            if not filename.endswith(suffix):
-+                continue
-+            lang_id = filename[0:len(filename) - len(suffix)]
-+            prefix = 'emoji-'
-+            if not lang_id.startswith(prefix):
-+                continue
-+            lang_id = lang_id[len(prefix):]
-+            lang = LanguageString(lang_id, IBus.get_language_name(lang_id))
-+            self.__lang_list.append(lang)
-+        if len(self.__lang_list) == 0:
-+            print("Not found dicts in %s" % dictdir, file=sys.stderr)
-+            lang = LanguageString('en', IBus.get_language_name('en'))
-+            self.__lang_list.append(lang)
-+            return
-+
-+        def cmp_lang(a, b):
-+            label_a = a.trans + a.id
-+            label_b = b.trans + b.id
-+            return (label_a > label_b) - (label_a < label_b)
-+
-+        self.__lang_list.sort(key = functools.cmp_to_key(cmp_lang))
-+
-+        loc = locale.getlocale()[0]
-+        # None on C locale
-+        if loc == None or loc == 'C':
-+            loc = 'en_US'
-+        index = 0
-+        for lang in self.__lang_list:
-+            # move current language to the first place
-+            if lang.trans == IBus.get_language_name(loc):
-+                self.__lang_list.remove(lang)
-+                self.__lang_list.insert(index, lang)
-+                index += 1
-+
-+        for lang in self.__lang_list:
-+            # move English to the second place
-+            if lang.trans == IBus.get_language_name('en'):
-+                self.__lang_list.remove(lang)
-+                self.__lang_list.insert(index, lang)
-+                index += 1
-+
-+
-+    def __list_filter(self, row, data):
-+        if row.id == self.__id:
-+            self.__list.select_row(row)
-+        if row == self.__more_row:
-+            return not self.__showing_extra
-+        if not self.__showing_extra and row.is_extra:
-+            return False
-+        return True
-+
-+
-+    def __row_activated(self, box, row):
-+        if row == self.__more_row:
-+            self.__show_more()
-+            return
-+        self.__id = row.id
-+
-+
-+    def __padded_label_new(self, text, icon, alignment, direction):
-+        hbox = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL)
-+
-+        if direction == ROW_TRAVEL_DIRECTION_BACKWARD:
-+            rtl = (Gtk.Widget.get_default_direction() == \
-+                   Gtk.TextDirection.RTL)
-+            if rtl:
-+                arrow = Gtk.Image.new_from_icon_name(
-+                    'go-previous-rtl-symbolic', Gtk.IconSize.MENU)
-+            else:
-+                arrow = Gtk.Image.new_from_icon_name(
-+                    'go-previous-symbolic', Gtk.IconSize.MENU)
-+            hbox.pack_start(arrow, False, True, 0)
-+
-+        if icon != None:
-+            pixbuf = load_icon(icon, Gtk.IconSize.LARGE_TOOLBAR)
-+            image = Gtk.Image(pixbuf = pixbuf)
-+            hbox.pack_start(image, False, True, 0)
-+
-+        label = Gtk.Label(label = text)
-+        label.set_halign(alignment)
-+        label.set_valign(Gtk.Align.CENTER)
-+        label.set_margin_left(20)
-+        label.set_margin_right(20)
-+        label.set_margin_top(6)
-+        label.set_margin_bottom(6)
-+        hbox.pack_start(label, True, True, 0)
-+        return hbox
-+
-+
-+    def __list_box_row_new(self, lang):
-+        row = Gtk.ListBoxRow()
-+        row.trans = lang.trans
-+        row.id = lang.id
-+        row.is_extra = False
-+        return row
-+
-+
-+    def __lang_row_new(self, lang, prev_lang):
-+        row = self.__list_box_row_new(lang)
-+        label = lang.trans
-+        if lang.id == self.__id:
-+            row.is_extra = False
-+        elif prev_lang != None and label == prev_lang.trans:
-+            label = "%s (%s)" % (lang.trans, lang.id)
-+            row.is_extra = True
-+        elif not self.__showing_extra and \
-+           lang.trans not in self.__initial_languages:
-+            row.is_extra = True
-+        widget = self.__padded_label_new(label,
-+                                         None,
-+                                         Gtk.Align.CENTER,
-+                                         ROW_TRAVEL_DIRECTION_NONE)
-+        row.add(widget)
-+        return row
-+
-+
-+    def __more_row_new(self):
-+        row = Gtk.ListBoxRow()
-+        row.id = None
-+        hbox = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL)
-+        row.add(hbox)
-+        row.set_tooltip_text(_("More…"))
-+        arrow = Gtk.Image.new_from_icon_name('view-more-symbolic',
-+                                             Gtk.IconSize.MENU)
-+        arrow.set_margin_left(20)
-+        arrow.set_margin_right(20)
-+        arrow.set_margin_top(6)
-+        arrow.set_margin_bottom(6)
-+        arrow.set_halign(Gtk.Align.CENTER)
-+        arrow.set_valign(Gtk.Align.CENTER)
-+        hbox.pack_start(arrow, True, True, 0)
-+        return row
-+
-+
-+    def __set_fixed_size(self):
-+        if self.__scrolled.get_policy()[0] == Gtk.PolicyType.AUTOMATIC:
-+            return
-+        (width, height) = self.get_size()
-+        self.set_size_request(width, height)
-+        self.__scrolled.set_policy(Gtk.PolicyType.AUTOMATIC,
-+                                   Gtk.PolicyType.AUTOMATIC)
-+
-+
-+    def __remove_all_children(self):
-+        for l in self.__list.get_children():
-+            self.__list.remove(l)
-+
-+
-+    def __show_lang_rows(self):
-+        self.__remove_all_children()
-+        prev_lang = None
-+        for lang in self.__lang_list:
-+            row = self.__lang_row_new(lang, prev_lang)
-+            self.__list.add(row)
-+            prev_lang = lang
-+        self.__list.add(self.__more_row)
-+        self.__list.show_all()
-+        self.__adjustment.set_value(self.__adjustment.get_lower())
-+        self.__list.invalidate_filter()
-+        self.__list.set_selection_mode(Gtk.SelectionMode.SINGLE)
-+
-+
-+    def __show_more(self):
-+        self.__set_fixed_size()
-+        self.__showing_extra = True
-+        self.__list.invalidate_filter()
-+
-+
-+    def get_selected_lang(self):
-+        return self.__id
-+
-+
-+class EmojiLangButton(Gtk.Button):
-+    __gtype_name__ = 'EmojiLangButton'
-+    __gproperties__ = {
-+        'lang' : (
-+            str,
-+            'lang',
-+            'lang for emojo-*.dict',
-+            'en',
-+            GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE)
-+    }
-+
-+
-+    def __init__(self):
-+        super(EmojiLangButton, self).__init__()
-+        self.__lang = ''
-+
-+
-+    def do_get_property(self, prop):
-+        if prop.name == 'lang':
-+            return self.__lang
-+        else:
-+            raise AttributeError('unknown property %s' % prop.name)
-+
-+
-+    def do_set_property(self, prop, value):
-+        if prop.name == 'lang':
-+            self.set_lang(value)
-+        else:
-+            raise AttributeError('unknown property %s' % prop.name)
-+
-+
-+    def do_clicked(self):
-+        dialog = EmojiLangChooser(id = self.__lang,
-+                                  transient_for = self.get_toplevel())
-+        id = dialog.run()
-+        if id != Gtk.ResponseType.APPLY:
-+            dialog.destroy()
-+            return
-+        self.set_lang(dialog.get_selected_lang())
-+        dialog.destroy()
-+
-+
-+    def set_lang(self, lang):
-+        self.__lang = lang
-+        self.notify("lang")
-+        self.set_label(IBus.get_language_name(lang))
-+
-+
-+    def get_lang(self, lang):
-+        return self.__lang
-+
-+
-+GObject.type_register(EmojiLangButton)
-+
-+
-+if __name__ == "__main__":
-+        dialog = EmojiLangChooser()
-+        id = dialog.run()
-+        if id != Gtk.ResponseType.APPLY:
-+            dialog.destroy()
-+            import sys
-+            sys.exit(0)
-+        print("Selected language:", dialog.get_selected_lang())
-diff --git a/setup/main.py b/setup/main.py
-index 09b0ebd..7839cea 100644
---- a/setup/main.py
-+++ b/setup/main.py
-@@ -51,6 +51,7 @@ from os import path
- import i18n
- import keyboardshortcut
- import locale
-+from emojilang import EmojiLangButton
- from enginecombobox import EngineComboBox
- from enginedialog import EngineDialog
- from enginetreeview import EngineTreeView
-@@ -92,6 +93,8 @@ class Setup(object):
-                 schema = "org.freedesktop.ibus.general.hotkey");
-         self.__settings_panel = Gio.Settings(
-                 schema = "org.freedesktop.ibus.panel");
-+        self.__settings_emoji = Gio.Settings(
-+                schema = "org.freedesktop.ibus.panel.emoji");
- 
-         # IBus.Bus() calls ibus_bus_new().
-         # Gtk.Builder().add_from_file() also calls ibus_bus_new_async()
-@@ -122,7 +125,10 @@ class Setup(object):
-         self.__init_hotkey(name, label)
- 
-     def __init_hotkey(self, name, label, comment=None):
--        shortcuts = self.__settings_hotkey.get_strv(name)
-+        if name == 'emoji':
-+            shortcuts = self.__settings_emoji.get_strv('hotkey')
-+        else:
-+            shortcuts = self.__settings_hotkey.get_strv(name)
-         button = self.__builder.get_object("button_%s" % label)
-         entry = self.__builder.get_object("entry_%s" % label)
-         entry.set_text("; ".join(shortcuts))
-@@ -130,8 +136,12 @@ class Setup(object):
-         if comment != None:
-             tooltip += "\n" + comment
-         entry.set_tooltip_text(tooltip)
--        button.connect("clicked", self.__shortcut_button_clicked_cb,
--                name, "general/hotkey", label, entry)
-+        if name == 'emoji':
-+            button.connect("clicked", self.__shortcut_button_clicked_cb,
-+                    'hotkey', 'panel/' + name, label, entry)
-+        else:
-+            button.connect("clicked", self.__shortcut_button_clicked_cb,
-+                    name, "general/hotkey", label, entry)
- 
-     def __init_panel(self):
-         # lookup table orientation
-@@ -169,21 +179,27 @@ class Setup(object):
- 
-         self.__fontbutton_custom_font = self.__builder.get_object(
-                 "fontbutton_custom_font")
--        self.__fontbutton_emoji_font = self.__builder.get_object(
--                "fontbutton_emoji_font")
--        self.__fontbutton_emoji_font.set_preview_text("🙂🍎🚃💓📧⚽🐳");
-         self.__settings_panel.bind('custom-font',
-                                     self.__fontbutton_custom_font,
-                                    'font-name',
-                                    Gio.SettingsBindFlags.DEFAULT)
--        self.__settings_panel.bind('emoji-font',
--                                    self.__fontbutton_emoji_font,
--                                   'font-name',
--                                   Gio.SettingsBindFlags.DEFAULT)
-         self.__settings_panel.bind('use-custom-font',
-                                     self.__fontbutton_custom_font,
-                                    'sensitive',
-                                    Gio.SettingsBindFlags.GET)
-+        self.__fontbutton_emoji_font = self.__builder.get_object(
-+                'fontbutton_emoji_font')
-+        self.__fontbutton_emoji_font.set_preview_text('🙂🍎🚃💓📧⚽🐳');
-+        self.__settings_emoji.bind('font',
-+                                    self.__fontbutton_emoji_font,
-+                                   'font-name',
-+                                   Gio.SettingsBindFlags.DEFAULT)
-+        self.__button_emoji_lang = self.__builder.get_object(
-+                'button_emoji_lang')
-+        self.__settings_emoji.bind('lang',
-+                                    self.__button_emoji_lang,
-+                                   'lang',
-+                                   Gio.SettingsBindFlags.DEFAULT)
- 
-         # show icon on system tray
-         self.__checkbutton_show_icon_on_systray = self.__builder.get_object(
-@@ -458,7 +474,10 @@ class Setup(object):
-         dialog.destroy()
-         if id != Gtk.ResponseType.OK:
-             return
--        self.__settings_hotkey.set_strv(name, shortcuts)
-+        if section == 'panel/emoji':
-+            self.__settings_emoji.set_strv(name, shortcuts)
-+        else:
-+            self.__settings_hotkey.set_strv(name, shortcuts)
-         text = "; ".join(shortcuts)
-         entry.set_text(text)
-         tooltip = "\n".join(shortcuts)
-diff --git a/setup/setup.ui b/setup/setup.ui
-index d5ee392..4ef3423 100644
---- a/setup/setup.ui
-+++ b/setup/setup.ui
-@@ -661,38 +661,11 @@
-                           </packing>
-                         </child>
-                         <child>
--                          <object class="GtkBox" id="fontbutton_box">
--                            <property name="orientation">vertical</property>
-+                          <object class="GtkFontButton" id="fontbutton_custom_font">
-                             <property name="visible">True</property>
--                            <property name="can_focus">False</property>
--                            <child>
--                              <object class="GtkFontButton" id="fontbutton_custom_font">
--                              <property name="use_action_appearance">False</property>
--                              <property name="visible">True</property>
--                              <property name="can_focus">True</property>
--                              <property name="receives_default">True</property>
--                              <property name="use_action_appearance">False</property>
--                              </object>
--                              <packing>
--                                <property name="expand">False</property>
--                                <property name="fill">False</property>
--                                <property name="position">0</property>
--                              </packing>
--                            </child>
--                            <child>
--                              <object class="GtkFontButton" id="fontbutton_emoji_font">
--                              <property name="use_action_appearance">False</property>
--                              <property name="visible">True</property>
--                              <property name="can_focus">True</property>
--                              <property name="receives_default">True</property>
--                              <property name="use_action_appearance">False</property>
--                              </object>
--                              <packing>
--                                <property name="expand">False</property>
--                                <property name="fill">False</property>
--                                <property name="position">1</property>
--                              </packing>
--                            </child>
-+                            <property name="can_focus">True</property>
-+                            <property name="receives_default">True</property>
-+                            <property name="use_action_appearance">False</property>
-                           </object>
-                           <packing>
-                             <property name="left_attach">1</property>
-@@ -702,6 +675,68 @@
-                             <property name="y_options">GTK_FILL</property>
-                           </packing>
-                         </child>
-+                        <child>
-+                          <object class="GtkLabel" id="label_emoji_font">
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">False</property>
-+                            <property name="tooltip_text" translatable="yes">Set a font of emoji candidates on the emoji dialog</property>
-+                            <property name="halign">start</property>
-+                            <property name="label" translatable="yes">Emoji font:</property>
-+                            <property name="justify">right</property>
-+                          </object>
-+                          <packing>
-+                            <property name="top_attach">7</property>
-+                            <property name="bottom_attach">8</property>
-+                            <property name="x_options">GTK_FILL</property>
-+                            <property name="y_options">GTK_FILL</property>
-+                          </packing>
-+                        </child>
-+                        <child>
-+                          <object class="GtkFontButton" id="fontbutton_emoji_font">
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">True</property>
-+                            <property name="receives_default">True</property>
-+                            <property name="use_action_appearance">False</property>
-+                          </object>
-+                          <packing>
-+                            <property name="left_attach">1</property>
-+                            <property name="right_attach">2</property>
-+                            <property name="top_attach">7</property>
-+                            <property name="bottom_attach">8</property>
-+                            <property name="y_options">GTK_FILL</property>
-+                          </packing>
-+                        </child>
-+                        <child>
-+                          <object class="GtkLabel" id="label_emoji_lang">
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">False</property>
-+                            <property name="tooltip_text" translatable="yes">Set a language of emoji annotations on the emoji dialog</property>
-+                            <property name="halign">start</property>
-+                            <property name="label" translatable="yes">Emoji annotation language:</property>
-+                            <property name="justify">right</property>
-+                          </object>
-+                          <packing>
-+                            <property name="top_attach">8</property>
-+                            <property name="bottom_attach">9</property>
-+                            <property name="x_options">GTK_FILL</property>
-+                            <property name="y_options">GTK_FILL</property>
-+                          </packing>
-+                        </child>
-+                        <child>
-+                          <object class="EmojiLangButton" id="button_emoji_lang">
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">True</property>
-+                            <property name="receives_default">True</property>
-+                            <property name="use_action_appearance">False</property>
-+                          </object>
-+                          <packing>
-+                            <property name="left_attach">1</property>
-+                            <property name="right_attach">2</property>
-+                            <property name="top_attach">8</property>
-+                            <property name="bottom_attach">9</property>
-+                            <property name="y_options">GTK_FILL</property>
-+                          </packing>
-+                        </child>
-                       </object>
-                     </child>
-                     <child type="label">
-diff --git a/tools/main.vala b/tools/main.vala
-index fd9fd0e..2bf1dc7 100644
---- a/tools/main.vala
-+++ b/tools/main.vala
-@@ -31,6 +31,8 @@ bool name_only = false;
- /* system() exists as a public API. */
- bool is_system = false;
- string cache_file = null;
-+string emoji_font = null;
-+string annotation_lang = null;
- 
- class EngineList {
-     public IBus.EngineDesc[] data = {};
-@@ -342,12 +344,40 @@ private void run_dialog(IBus.Emojier emojier) {
- }
- 
- int emoji_dialog(string[] argv) {
-+    const OptionEntry[] options = {
-+        { "font", 0, 0, OptionArg.STRING, out emoji_font,
-+          N_("FONT for emoji chracters on emoji dialog."), "FONT" },
-+        { "lang", 0, 0, OptionArg.STRING, out annotation_lang,
-+          N_("LANG for annotations on emoji dialog. E.g. \"en\""), "LANG" },
-+        { null }
-+    };
-+
-+    var option = new OptionContext();
-+    option.add_main_entries(options, Config.GETTEXT_PACKAGE);
-+
-+    try {
-+        option.parse(ref argv);
-+    } catch (OptionError e) {
-+        stderr.printf("%s\n", e.message);
-+        return Posix.EXIT_FAILURE;
-+    }
-+
-     Gtk.init(ref argv);
--    GLib.Settings settings_panel =
--            new GLib.Settings("org.freedesktop.ibus.panel");
--    string emoji_font = settings_panel.get_string("emoji-font");
-+    if (emoji_font == null) {
-+        GLib.Settings settings_emoji =
-+                new GLib.Settings("org.freedesktop.ibus.panel.emoji");
-+        emoji_font = settings_emoji.get_string("font");
-+    }
-+    if (annotation_lang == null) {
-+        GLib.Settings settings_emoji =
-+                new GLib.Settings("org.freedesktop.ibus.panel.emoji");
-+        annotation_lang = settings_emoji.get_string("lang");
-+    }
-     IBus.Emojier emojier = new IBus.Emojier();
--    emojier.set_emoji_font(emoji_font);
-+    if (emoji_font != null && emoji_font != "")
-+        emojier.set_emoji_font(emoji_font);
-+    if (annotation_lang != null && annotation_lang != "")
-+        emojier.set_annotation_lang(annotation_lang);
-     if (emojier.has_loaded_emoji_dict()) {
-         run_dialog(emojier);
-     } else {
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index b1dc50c..20c1378 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -42,14 +42,11 @@ class IBusEmojier : Gtk.Window {
-         }
-     }
-     private class EBoxRow : Gtk.ListBoxRow {
--        public EBoxRow(string text,
--                       string id="") {
-+        public EBoxRow(string text) {
-             this.text = text;
--            this.id = id;
-         }
- 
-         public string text { get; set; }
--        public string id { get; set; }
-     }
-     private class EScrolledWindow : Gtk.ScrolledWindow {
-         public EScrolledWindow(Gtk.Adjustment? hadjustment=null,
-@@ -132,6 +129,7 @@ class IBusEmojier : Gtk.Window {
-         }
-     }
-     private class ETitleLabel : Gtk.Box {
-+        private Gtk.Label m_label;
-         private Gtk.Button m_close_button;
-         private ulong m_close_handler;
- 
-@@ -142,14 +140,14 @@ class IBusEmojier : Gtk.Window {
-                 orientation : Gtk.Orientation.HORIZONTAL,
-                 spacing : 0
-             );
--            Gtk.Label label = new Gtk.Label(text);
--            label.set_halign(align);
--            label.set_valign(align);
--            label.set_margin_start(20);
--            label.set_margin_end(20);
--            label.set_margin_top(6);
--            label.set_margin_bottom(6);
--            pack_start(label, true, true, 0);
-+            m_label = new Gtk.Label(text);
-+            m_label.set_halign(align);
-+            m_label.set_valign(align);
-+            m_label.set_margin_start(20);
-+            m_label.set_margin_end(20);
-+            m_label.set_margin_top(6);
-+            m_label.set_margin_bottom(6);
-+            pack_start(m_label, true, true, 0);
-             IconWidget icon = new IconWidget("window-close", Gtk.IconSize.MENU);
-             m_close_button = new Gtk.Button();
-             m_close_button.add(icon);
-@@ -170,6 +168,9 @@ class IBusEmojier : Gtk.Window {
-                 m_close_handler = 0;
-             }
-         }
-+        public void set_label(string str) {
-+            m_label.set_label(str);
-+        }
-     }
- 
-     private enum TravelDirection {
-@@ -177,11 +178,6 @@ class IBusEmojier : Gtk.Window {
-         BACKWARD,
-     }
- 
--    private enum CategoryType {
--        EMOJI,
--        LANG,
--    }
--
-     private const uint EMOJI_GRID_PAGE = 10;
-     private ThemedRGBA m_rgba;
-     private Gtk.Box m_vbox;
-@@ -190,14 +186,11 @@ class IBusEmojier : Gtk.Window {
-     private string? m_backward;
-     private EScrolledWindow? m_scrolled_window = null;
-     private EListBox m_list_box;
--    private CategoryType m_current_category_type = CategoryType.EMOJI;
-     private bool m_is_running = false;
-     private string m_input_context_path = "";
-     private GLib.MainLoop? m_loop;
-     private string? m_result;
--    private GLib.SList<string> m_lang_list;
-     private string m_current_lang_id = "en";
--    private string m_current_language = "English";
-     private string? m_unicode_point = null;
-     private bool m_candidate_panel_is_visible;
-     private GLib.HashTable<string, GLib.SList>?
-@@ -215,6 +208,7 @@ class IBusEmojier : Gtk.Window {
-     private bool m_enter_notify_enable = true;
-     private uint m_entry_notify_show_id;
-     private uint m_entry_notify_disable_id;
-+    private uint m_reload_emoji_dict_id;
- 
-     public signal void candidate_clicked(uint index, uint button, uint state);
-     public signal void loaded_emoji_dict();
-@@ -323,50 +317,13 @@ class IBusEmojier : Gtk.Window {
-             hide_candidate_panel();
-         });
- 
--        GLib.Idle.add(() => {
--            m_lang_list = read_lang_list();
-+        m_reload_emoji_dict_id = GLib.Idle.add(() => {
-             reload_emoji_dict();
-+            m_reload_emoji_dict_id = 0;
-             return false;
-         });
-     }
- 
--    private GLib.SList<string> read_lang_list() {
--        GLib.SList<string> lang_list = new GLib.SList<string>();
--        const string dict_path = Config.PKGDATADIR + "/dicts";
--        GLib.Dir dir = null;
--        try {
--            dir = GLib.Dir.open(dict_path);
--        } catch (GLib.FileError e) {
--            warning("Error loading %s: %s", dict_path, e.message);
--            return lang_list;
--        }
--        string name;
--        while ((name = dir.read_name()) != null) {
--            const string dict_suffix = ".dict";
--            const string dict_prefix = "emoji-";
--            if (name.has_suffix(dict_suffix)) {
--                name = name[0:name.length - dict_suffix.length];
--                if (name.has_prefix(dict_prefix)) {
--                    name = name[dict_prefix.length:name.length];
--                    lang_list.append(name);
--                } else {
--                    warning("Need %s prefix in the filename: %s/%s%s",
--                            dict_prefix, dict_path, name, dict_suffix);
--                }
--            } else {
--                warning("Need %s extention in the filename: %s/%s",
--                        dict_suffix, dict_path, name);
--            }
--        }
--        lang_list.sort((a, b) => {
--            string a_lang = IBus.get_language_name(a);
--            string b_lang = IBus.get_language_name(b);
--            a_lang = "%s (%s)".printf(a_lang, a);
--            b_lang = "%s (%s)".printf(b_lang, b);
--            return GLib.strcmp(a_lang, b_lang);
--        });
--        return lang_list;
--    }
- 
-     private void reload_emoji_dict() {
-         init_emoji_dict();
-@@ -382,6 +339,7 @@ class IBusEmojier : Gtk.Window {
-         loaded_emoji_dict();
-     }
- 
-+
-     private void init_emoji_dict() {
-         m_annotation_to_emojis_dict =
-                 new GLib.HashTable<string, GLib.SList>(GLib.str_hash,
-@@ -394,6 +352,7 @@ class IBusEmojier : Gtk.Window {
-                                                        GLib.str_equal);
-     }
- 
-+
-     private void make_emoji_dict(string lang) {
-         GLib.SList<IBus.EmojiData> emoji_list = IBus.EmojiData.load(
-                     Config.PKGDATADIR + "/dicts/emoji-" + lang + ".dict");
-@@ -412,6 +371,7 @@ class IBusEmojier : Gtk.Window {
-         }
-     }
- 
-+
-     private void update_annotation_to_emojis_dict(IBus.EmojiData data) {
-         string emoji = data.get_emoji();
-         unowned GLib.SList<string> annotations = data.get_annotations();
-@@ -432,6 +392,7 @@ class IBusEmojier : Gtk.Window {
-         }
-     }
- 
-+
-     private string utf8_down(string str) {
-         GLib.StringBuilder buff = new GLib.StringBuilder();
-         int length = str.char_count();
-@@ -442,6 +403,7 @@ class IBusEmojier : Gtk.Window {
-         return buff.str;
-     }
- 
-+
-     private string utf8_title(string str) {
-         StringBuilder buff = new StringBuilder();
-         int length = str.char_count();
-@@ -456,6 +418,7 @@ class IBusEmojier : Gtk.Window {
-         return buff.str;
-     }
- 
-+
-     private void update_emoji_to_data_dict(IBus.EmojiData data,
-                                            string         lang) {
-         string emoji = data.get_emoji();
-@@ -464,10 +427,11 @@ class IBusEmojier : Gtk.Window {
-             unowned GLib.SList<string> annotations = data.get_annotations();
-             var words = description.split(" ");
-             // If the description has less than 3 words, add it to annotations
-+            // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
-             if (words.length < 3 &&
-                 annotations.find_custom(
-                         description,
--                        (GLib.CompareFunc<string>)GLib.strcmp) == null) {
-+                        GLib.strcmp) == null) {
-                 annotations.append(description);
-                 data.set_annotations(annotations.copy_deep(GLib.strdup));
-             }
-@@ -485,18 +449,20 @@ class IBusEmojier : Gtk.Window {
-             unowned GLib.SList<string> annotations = data.get_annotations();
-             var words = trans_description.split(" ");
-             // If the description has less than 3 words, add it to annotations
-+            // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
-             if (words.length < 3 &&
-                 annotations.find_custom(
-                         trans_description,
--                        (GLib.CompareFunc<string>)GLib.strcmp) == null) {
-+                        GLib.strcmp) == null) {
-                 annotations.append(trans_description);
-             }
-             unowned GLib.SList<string> en_annotations
-                 = en_data.get_annotations();
-             foreach (string annotation in en_annotations) {
-+                // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
-                 if (annotations.find_custom(
-                             annotation,
--                            (GLib.CompareFunc<string>)GLib.strcmp) == null) {
-+                            GLib.strcmp) == null) {
-                     annotations.append(annotation.dup());
-                 }
-             }
-@@ -504,6 +470,7 @@ class IBusEmojier : Gtk.Window {
-         }
-     }
- 
-+
-     private void update_category_to_emojis_dict(IBus.EmojiData data,
-                                                 string         lang) {
-         string emoji = data.get_emoji();
-@@ -525,29 +492,12 @@ class IBusEmojier : Gtk.Window {
-         }
-     }
- 
-+
-     private void set_fixed_size() {
--        if (!m_candidate_panel_is_visible &&
--            m_current_category_type == CategoryType.LANG) {
--            Gtk.PolicyType vpolicy;
--            m_scrolled_window.get_policy(null, out vpolicy);
--            if (vpolicy == Gtk.PolicyType.AUTOMATIC)
--                return;
--            int width, height;
--            get_size(out width, out height);
--            set_size_request(width, height);
--            if (m_scrolled_window != null) {
--                m_scrolled_window.set_policy(Gtk.PolicyType.NEVER,
--                                             Gtk.PolicyType.AUTOMATIC);
--            }
--        } else {
--            resize(20, 1);
--            if (m_scrolled_window != null) {
--                m_scrolled_window.set_policy(Gtk.PolicyType.NEVER,
--                                             Gtk.PolicyType.NEVER);
--            }
--        }
-+        resize(20, 1);
-     }
- 
-+
-     private void remove_all_children() {
-         foreach (Gtk.Widget w in m_vbox.get_children()) {
-             if (w.name == "IBusEmojierEntry" ||
-@@ -558,51 +508,16 @@ class IBusEmojier : Gtk.Window {
-         }
-     }
- 
--    private void activated_language(EBoxRow row) {
--        m_category_active_index = 0;
--        if (m_current_lang_id != row.id) {
--            m_current_lang_id = row.id;
--            m_current_language = row.text;
--            reload_emoji_dict();
--        }
--        m_current_category_type = CategoryType.EMOJI;
--        show_category_list();
--    }
- 
-     private void show_category_list() {
-         remove_all_children();
-         m_scrolled_window = new EScrolledWindow();
-         set_fixed_size();
--        EPaddedLabel label;
--        if (m_current_category_type == CategoryType.EMOJI) {
--            label = new EPaddedLabel(m_current_language, Gtk.Align.CENTER);
--        } else if (m_current_category_type == CategoryType.LANG) {
--            label = new EPaddedLabel(m_current_language,
--                                     Gtk.Align.CENTER,
--                                     TravelDirection.BACKWARD);
--        } else {
--            label = new EPaddedLabel("", Gtk.Align.CENTER);
--        }
--        Gtk.Button button = new Gtk.Button();
--        button.add(label);
--        m_vbox.add(button);
--        button.show_all();
--        if (m_current_category_type == CategoryType.EMOJI) {
--            button.button_press_event.connect((e) => {
--                m_category_active_index = 0;
--                m_current_category_type = CategoryType.LANG;
--                show_category_list();
--                return true;
--            });
--        } else if (m_current_category_type == CategoryType.LANG) {
--            button.button_press_event.connect((e) => {
--                m_category_active_index = 0;
--                m_current_category_type = CategoryType.EMOJI;
--                show_category_list();
--                return true;
--            });
--        }
- 
-+        string language = "%s (%s)".printf(
-+            _("Emoji Dialog"),
-+            IBus.get_language_name(m_current_lang_id));
-+        m_title.set_label(language);
-         m_vbox.add(m_scrolled_window);
-         Gtk.Viewport viewport = new Gtk.Viewport(null, null);
-         m_scrolled_window.add(viewport);
-@@ -611,59 +526,38 @@ class IBusEmojier : Gtk.Window {
-         viewport.add(m_list_box);
-         Gtk.Adjustment adjustment = m_scrolled_window.get_vadjustment();
-         m_list_box.set_adjustment(adjustment);
--        if (m_current_category_type == CategoryType.EMOJI) {
--            m_list_box.row_activated.connect((box, gtkrow) => {
--                m_category_active_index = 0;
--                EBoxRow row = gtkrow as EBoxRow;
--                show_emoji_for_category(row);
--            });
-+        m_list_box.row_activated.connect((box, gtkrow) => {
-+            m_category_active_index = 0;
-+            EBoxRow row = gtkrow as EBoxRow;
-+            show_emoji_for_category(row);
-+        });
- 
--            uint n = 1;
--            if (m_favorites.length > 0) {
--                EBoxRow row = new EBoxRow("@favorites");
--                EPaddedLabel widget =
--                        new EPaddedLabel(_("Favorites"), Gtk.Align.CENTER);
--                row.add(widget);
--                m_list_box.add(row);
--                if (n++ == m_category_active_index)
--                    m_list_box.select_row(row);
--            }
--            GLib.List<unowned string> categories =
--                    m_category_to_emojis_dict.get_keys();
--            categories.sort((a, b) => {
--                return GLib.strcmp(_(a), _(b));
--            });
--            foreach (unowned string category in categories) {
--                EBoxRow row = new EBoxRow(category);
--                string locale_category = _(category);
--                EPaddedLabel widget =
--                        new EPaddedLabel(utf8_title(locale_category),
--                                         Gtk.Align.CENTER);
--                row.add(widget);
--                m_list_box.add(row);
--                if (n++ == m_category_active_index)
--                    m_list_box.select_row(row);
--            }
--        } else if (m_current_category_type == CategoryType.LANG) {
--            m_list_box.row_activated.connect((box, gtkrow) => {
--                activated_language(gtkrow as EBoxRow);
--            });
--            uint n = 1;
--            string prev_language = null;
--            foreach (unowned string id in m_lang_list) {
--                string language = IBus.get_language_name(id);
--                if (prev_language == language)
--                    language = "%s (%s)".printf(language, id);
--                else
--                    prev_language = language;
--                EBoxRow row = new EBoxRow(language, id);
--                EPaddedLabel widget =
--                        new EPaddedLabel(language, Gtk.Align.CENTER);
--                row.add(widget);
--                m_list_box.add(row);
--                if (n++ == m_category_active_index)
--                    m_list_box.select_row(row);
--            }
-+        uint n = 1;
-+        if (m_favorites.length > 0) {
-+            EBoxRow row = new EBoxRow("@favorites");
-+            EPaddedLabel widget =
-+                    new EPaddedLabel(_("Favorites"), Gtk.Align.CENTER);
-+            row.add(widget);
-+            m_list_box.add(row);
-+            if (n++ == m_category_active_index)
-+                m_list_box.select_row(row);
-+        }
-+        GLib.List<unowned string> categories =
-+                m_category_to_emojis_dict.get_keys();
-+        // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
-+        categories.sort((a, b) => {
-+            return GLib.strcmp(_(a), _(b));
-+        });
-+        foreach (unowned string category in categories) {
-+            EBoxRow row = new EBoxRow(category);
-+            string locale_category = _(category);
-+            EPaddedLabel widget =
-+                    new EPaddedLabel(utf8_title(locale_category),
-+                                     Gtk.Align.CENTER);
-+            row.add(widget);
-+            m_list_box.add(row);
-+            if (n++ == m_category_active_index)
-+                m_list_box.select_row(row);
-         }
- 
-         m_scrolled_window.show_all();
-@@ -673,6 +567,7 @@ class IBusEmojier : Gtk.Window {
-         m_list_box.set_selection_mode(Gtk.SelectionMode.SINGLE);
-     }
- 
-+
-     private void show_emoji_for_category(EBoxRow row) {
-         if (row.text == "@favorites") {
-             m_lookup_table.clear();
-@@ -694,6 +589,7 @@ class IBusEmojier : Gtk.Window {
-         show_candidate_panel();
-     }
- 
-+
-     private void show_arrow_buttons() {
-         Gtk.Button next_button = new Gtk.Button();
-         next_button.clicked.connect(() => {
-@@ -729,6 +625,7 @@ class IBusEmojier : Gtk.Window {
-         buttons_hbox.show_all();
-     }
- 
-+
-     private bool check_unicode_point(string? annotation=null) {
-         bool check_xdigit_only = true;
-         if (annotation == null) {
-@@ -758,6 +655,7 @@ class IBusEmojier : Gtk.Window {
-         return true;
-     }
- 
-+
-     public void update_candidate_window() {
-         string annotation = m_entry.get_text();
-         if (annotation.length == 0) {
-@@ -788,6 +686,7 @@ class IBusEmojier : Gtk.Window {
-         show_candidate_panel();
-     }
- 
-+
-     private void show_candidate_panel() {
-         remove_all_children();
-         set_fixed_size();
-@@ -848,6 +747,8 @@ class IBusEmojier : Gtk.Window {
-                 // enter_notify_event conflicts with keyboard operations.
-                 if (!m_enter_notify_enable)
-                     return true;
-+                if (m_lookup_table.get_cursor_pos() == index)
-+                    return true;
-                 m_lookup_table.set_cursor_pos(index);
-                 if (m_entry_notify_show_id > 0) {
-                         GLib.Source.remove(m_entry_notify_show_id);
-@@ -927,6 +828,7 @@ class IBusEmojier : Gtk.Window {
-         }
-     }
- 
-+
-     private void hide_candidate_panel() {
-         m_enter_notify_enable = true;
-         m_candidate_panel_is_visible = false;
-@@ -934,6 +836,7 @@ class IBusEmojier : Gtk.Window {
-             show_category_list();
-     }
- 
-+
-     private bool if_in_range_of_lookup(uint keyval) {
-         if (!m_candidate_panel_is_visible)
-             return false;
-@@ -957,6 +860,7 @@ class IBusEmojier : Gtk.Window {
-         return true;
-     }
- 
-+
-     private void set_number_on_lookup(uint keyval) {
-         if (keyval == Gdk.Key.@0)
-             keyval = Gdk.Key.@9 + 1;
-@@ -969,6 +873,7 @@ class IBusEmojier : Gtk.Window {
-         m_result = text.text;
-     }
- 
-+
-     private void enter_notify_disable_with_timer() {
-         // Enable keyboard operation and disable mouse operation.
-         m_enter_notify_enable = false;
-@@ -982,6 +887,7 @@ class IBusEmojier : Gtk.Window {
-         });
-     }
- 
-+
-     private void candidate_panel_cursor_down() {
-         enter_notify_disable_with_timer();
-         uint ncandidates = m_lookup_table.get_number_of_candidates();
-@@ -996,6 +902,7 @@ class IBusEmojier : Gtk.Window {
-         show_candidate_panel();
-     }
- 
-+
-     private void candidate_panel_cursor_up() {
-         enter_notify_disable_with_timer();
-         int ncandidates = (int)m_lookup_table.get_number_of_candidates();
-@@ -1013,6 +920,7 @@ class IBusEmojier : Gtk.Window {
-         show_candidate_panel();
-     }
- 
-+
-     private void category_list_cursor_move(uint keyval) {
-         GLib.List<weak Gtk.Widget> list = m_list_box.get_children();
-         if (keyval == Gdk.Key.Down) {
-@@ -1027,6 +935,7 @@ class IBusEmojier : Gtk.Window {
-         show_category_list();
-     }
- 
-+
-     private bool key_press_cursor_horizontal(uint keyval,
-                                              uint modifiers) {
-         assert (keyval == Gdk.Key.Left || keyval == Gdk.Key.Right);
-@@ -1061,6 +970,7 @@ class IBusEmojier : Gtk.Window {
-         return true;
-     }
- 
-+
-     private bool key_press_cursor_vertical(uint keyval) {
-         assert (keyval == Gdk.Key.Down || keyval == Gdk.Key.Up);
- 
-@@ -1075,6 +985,7 @@ class IBusEmojier : Gtk.Window {
-         return true;
-     }
- 
-+
-     private bool key_press_cursor_home_end(uint keyval,
-                                            uint modifiers) {
-         assert (keyval == Gdk.Key.Home || keyval == Gdk.Key.End);
-@@ -1107,14 +1018,11 @@ class IBusEmojier : Gtk.Window {
-         return false;
-     }
- 
-+
-     private bool key_press_cursor_escape() {
-         if (m_candidate_panel_is_visible) {
-             hide_candidate_panel();
-             return true;
--        } else if (m_current_category_type == CategoryType.LANG) {
--            m_current_category_type = CategoryType.EMOJI;
--            show_candidate_panel();
--            return true;
-         } else if (m_entry.get_text().length == 0) {
-             m_loop.quit();
-             hide_candidate_panel();
-@@ -1124,6 +1032,7 @@ class IBusEmojier : Gtk.Window {
-         return true;
-     }
- 
-+
-     private void entry_enter_keyval(uint keyval) {
-         unichar ch = IBus.keyval_to_unicode(keyval);
-         if (ch.iscntrl())
-@@ -1145,6 +1054,7 @@ class IBusEmojier : Gtk.Window {
-         m_entry.set_position(pos);
-     }
- 
-+
-     public string run(Gdk.Event event,
-                       string    input_context_path) {
-         assert (m_loop == null);
-@@ -1178,7 +1088,6 @@ class IBusEmojier : Gtk.Window {
-             keyboard = device.get_associated_device();
-         }
- 
--        m_current_category_type = CategoryType.EMOJI;
-         show_category_list();
-         m_entry.set_activates_default(true);
-         show_all();
-@@ -1229,12 +1138,14 @@ class IBusEmojier : Gtk.Window {
-         return m_result;
-     }
- 
-+
-     /* override virtual functions */
-     public override void show() {
-         base.show();
-         set_focus_visible(true);
-     }
- 
-+
-     public override bool key_press_event(Gdk.EventKey event) {
-         uint keyval = event.keyval;
-         uint modifiers = event.state;
-@@ -1263,10 +1174,7 @@ class IBusEmojier : Gtk.Window {
-             } else if (m_category_active_index > 0) {
-                 Gtk.ListBoxRow gtkrow = m_list_box.get_selected_row();
-                 EBoxRow row = gtkrow as EBoxRow;
--                if (m_current_category_type == CategoryType.EMOJI)
--                    show_emoji_for_category(row);
--                else if (m_current_category_type == CategoryType.LANG)
--                    activated_language(row);
-+                show_emoji_for_category(row);
-             }
-             return true;
-         case Gdk.Key.BackSpace:
-@@ -1366,27 +1274,33 @@ class IBusEmojier : Gtk.Window {
-         return true;
-     }
- 
-+
-     public bool is_running() {
-         return m_is_running;
-     }
- 
-+
-     public string get_input_context_path() {
-         return m_input_context_path;
-     }
- 
-+
-     public string get_selected_string() {
-         return m_result;
-     }
- 
-+
-     public void reset() {
-         m_input_context_path = "";
-         m_result = null;
-     }
- 
-+
-     public void set_emoji_font(string emoji_font) {
-         m_emoji_font = emoji_font;
-     }
- 
-+
-     public void set_favorites(string[]? unowned_favorites) {
-         m_favorites = {};
-         foreach (string favorite in unowned_favorites) {
-@@ -1394,6 +1308,7 @@ class IBusEmojier : Gtk.Window {
-         }
-     }
- 
-+
-     public bool has_loaded_emoji_dict() {
-         if (m_emoji_to_data_dict == null)
-             return false;
-@@ -1402,4 +1317,20 @@ class IBusEmojier : Gtk.Window {
-             return false;
-         return true;
-     }
-+
-+
-+    public void set_annotation_lang(string lang) {
-+        if (m_current_lang_id == lang)
-+            return;
-+        if (m_reload_emoji_dict_id > 0) {
-+            GLib.Source.remove(m_reload_emoji_dict_id);
-+            m_reload_emoji_dict_id = 0;
-+        }
-+        m_current_lang_id = lang;
-+        m_reload_emoji_dict_id = GLib.Idle.add(() => {
-+            reload_emoji_dict();
-+            m_reload_emoji_dict_id = 0;
-+            return false;
-+        });
-+    }
- }
-diff --git a/ui/gtk3/ibusemojidialog.h b/ui/gtk3/ibusemojidialog.h
-index c36060c..0f84a48 100644
---- a/ui/gtk3/ibusemojidialog.h
-+++ b/ui/gtk3/ibusemojidialog.h
-@@ -149,5 +149,16 @@ void          ibus_emojier_set_favorites          (IBusEmojier* self,
-  */
- gboolean      ibus_emojier_has_loaded_emoji_dict  (IBusEmojier* self);
- 
-+/**
-+ * ibus_emojier_set_annotation_lang:
-+ * @self: An #IBusEmojier
-+ * @lang: A langauge id for emoji annotations.
-+ *
-+ * Set a language id for emoji annotations. #IBusEmojier will load
-+ * $PKGDATADIR/dicts/emoji-@lang.dict. The default is "en".
-+ */
-+void          ibus_emojier_set_annotation_lang    (IBusEmojier* self,
-+                                                   const gchar* lang);
-+
- G_END_DECLS
- #endif
-diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
-index 0982134..7350dcc 100644
---- a/ui/gtk3/panel.vala
-+++ b/ui/gtk3/panel.vala
-@@ -54,6 +54,7 @@ class Panel : IBus.PanelService {
-     private GLib.Settings m_settings_general = null;
-     private GLib.Settings m_settings_hotkey = null;
-     private GLib.Settings m_settings_panel = null;
-+    private GLib.Settings m_settings_emoji = null;
-     private IconType m_icon_type = IconType.STATUS_ICON;
-     private Indicator m_indicator;
- #if INDICATOR
-@@ -161,6 +162,7 @@ class Panel : IBus.PanelService {
-         m_settings_hotkey =
-                 new GLib.Settings("org.freedesktop.ibus.general.hotkey");
-         m_settings_panel = new GLib.Settings("org.freedesktop.ibus.panel");
-+        m_settings_emoji = new GLib.Settings("org.freedesktop.ibus.panel.emoji");
- 
-         m_settings_general.changed["preload-engines"].connect((key) => {
-                 update_engines(m_settings_general.get_strv(key),
-@@ -193,19 +195,10 @@ class Panel : IBus.PanelService {
-                 bind_switch_shortcut();
-         });
- 
--        m_settings_hotkey.changed["emoji"].connect((key) => {
--                unbind_switch_shortcut(KeyEventFuncType.EMOJI_TYPING);
--                bind_emoji_shortcut();
--        });
--
-         m_settings_panel.changed["custom-font"].connect((key) => {
-                 set_custom_font();
-         });
- 
--        m_settings_panel.changed["emoji-font"].connect((key) => {
--                set_custom_font();
--        });
--
-         m_settings_panel.changed["use-custom-font"].connect((key) => {
-                 set_custom_font();
-         });
-@@ -239,9 +232,22 @@ class Panel : IBus.PanelService {
-                 set_property_icon_delay_time();
-         });
- 
--        m_settings_panel.changed["emoji-favorites"].connect((key) => {
-+        m_settings_emoji.changed["hotkey"].connect((key) => {
-+                unbind_switch_shortcut(KeyEventFuncType.EMOJI_TYPING);
-+                bind_emoji_shortcut();
-+        });
-+
-+        m_settings_emoji.changed["font"].connect((key) => {
-+                set_custom_font();
-+        });
-+
-+        m_settings_emoji.changed["favorites"].connect((key) => {
-                 set_emoji_favorites();
-         });
-+
-+        m_settings_emoji.changed["lang"].connect((key) => {
-+                set_emoji_lang();
-+        });
-     }
- 
- #if INDICATOR
-@@ -398,7 +404,7 @@ class Panel : IBus.PanelService {
- 
-     private void bind_emoji_shortcut() {
- #if EMOJI_DICT
--        string[] accelerators = m_settings_hotkey.get_strv("emoji");
-+        string[] accelerators = m_settings_emoji.get_strv("hotkey");
- 
-         var keybinding_manager = KeybindingManager.get_instance();
- 
-@@ -584,9 +590,9 @@ class Panel : IBus.PanelService {
-             return;
-         }
- 
--        string emoji_font = m_settings_panel.get_string("emoji-font");
-+        string emoji_font = m_settings_emoji.get_string("font");
-         if (emoji_font == null) {
--            warning("No config panel:emoji-font.");
-+            warning("No config emoji:font.");
-             return;
-         }
-         m_emojier.set_emoji_font(emoji_font);
-@@ -760,7 +766,11 @@ class Panel : IBus.PanelService {
-     }
- 
-     private void set_emoji_favorites() {
--        m_emojier.set_favorites(m_settings_panel.get_strv("emoji-favorites"));
-+        m_emojier.set_favorites(m_settings_emoji.get_strv("favorites"));
-+    }
-+
-+    private void set_emoji_lang() {
-+        m_emojier.set_annotation_lang(m_settings_emoji.get_string("lang"));
-     }
- 
-     private int compare_versions(string version1, string version2) {
-@@ -877,6 +887,7 @@ class Panel : IBus.PanelService {
-         set_xkb_icon_rgba();
-         set_property_icon_delay_time();
-         set_emoji_favorites();
-+        set_emoji_lang();
-     }
- 
-     private void engine_contexts_insert(IBus.EngineDesc engine) {
--- 
-2.9.3
-
-From 164300758c70fd3a590788e038de31b6c53d458a Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 28 Mar 2017 12:15:26 +0900
-Subject: [PATCH] data: Fix a typo
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/321800043
----
- data/ibus.schemas.in | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
-index c0bbd6f..096dd71 100644
---- a/data/ibus.schemas.in
-+++ b/data/ibus.schemas.in
-@@ -386,7 +386,7 @@
-         <short>Default language for emoji dictionary</short>
- 	    <long>Choose a default language of emoji dictionaries on
- 	          the emoji dialog. The value $lang is applied to
--                  /usr/share/unicode/cldr/common/annotations/$lang.xml
-+                  /usr/share/ibus/dicts/emoji-$lang.dict
-             </long>
-       </locale>
-     </schema>
--- 
-2.9.3
-
-From c3168d4701eb4e89094249abaa4f0f83ab24149b Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 29 Mar 2017 13:01:28 +0900
-Subject: [PATCH] Fix IBusEmojiDialog_1_0_gir_LIBS for --as-needed LDFLAGS
-
-The order gets omitted libibus-1.0.la
-
-BUG=https://github.com/ibus/ibus/issues/1917
-
-Review URL: https://codereview.appspot.com/324720043
----
- ui/gtk3/Makefile.am | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
-index b055f67..7122ff3 100644
---- a/ui/gtk3/Makefile.am
-+++ b/ui/gtk3/Makefile.am
-@@ -205,7 +205,7 @@ IBusEmojiDialog_1_0_gir_SCANNERFLAGS = \
-     $(NULL)
- IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile
- IBusEmojiDialog_1_0_gir_INCLUDES = Gtk-3.0 GLib-2.0 GObject-2.0 Gio-2.0
--IBusEmojiDialog_1_0_gir_LIBS = $(libibus) $(libibus_emoji_dialog)
-+IBusEmojiDialog_1_0_gir_LIBS = $(libibus_emoji_dialog) $(libibus)
- IBusEmojiDialog_1_0_gir_FILES =                      \
-     $(addprefix $(srcdir)/,$(introspection_sources)) \
-     $(NULL)
--- 
-2.9.3
-
-From 8b6f9fa531aa9d9b5d6c0184b2294d071f920d7f Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Thu, 30 Mar 2017 12:08:39 +0900
-Subject: [PATCH] ui/gtk3: Enable to type multiple code points on Emojier
-
-- Can type multiple Unicode characters with digits and Shift-space
-  keys.  E.g. "1f468 1f468 1f466"
-- Always show Unicode points of the selected emoji
-- Removed a function to commit an emoji by typing a digit char since
-  it conflicts with digit annotations, code points but it had enabled
-  with single digits only so not so useful.
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/321820043
----
- ui/gtk3/emojier.vala | 207 ++++++++++++++++++++++++---------------------------
- 1 file changed, 98 insertions(+), 109 deletions(-)
-
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 20c1378..8a2726c 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -97,12 +97,27 @@ class IBusEmojier : Gtk.Window {
-                 set_label(text);
-         }
-     }
--    private class EPaddedLabel : Gtk.Box {
-+    private class EPaddedLabel : Gtk.Label {
-         public EPaddedLabel(string          text,
--                            Gtk.Align       align,
--                            TravelDirection direction=TravelDirection.NONE) {
-+                            Gtk.Align       align) {
-             GLib.Object(
-                 name : "IBusEmojierPaddedLabel",
-+                halign : align,
-+                valign : Gtk.Align.CENTER,
-+                margin_start : 20,
-+                margin_end : 20,
-+                margin_top : 6,
-+                margin_bottom : 6
-+            );
-+            set_text(text);
-+        }
-+    }
-+    private class EPaddedLabelBox : Gtk.Box {
-+        public EPaddedLabelBox(string          text,
-+                               Gtk.Align       align,
-+                               TravelDirection direction=TravelDirection.NONE) {
-+            GLib.Object(
-+                name : "IBusEmojierPaddedLabelBox",
-                 orientation : Gtk.Orientation.HORIZONTAL,
-                 spacing : 0
-             );
-@@ -118,36 +133,29 @@ class IBusEmojier : Gtk.Window {
-                 }
-                 pack_start(icon, false, true, 0);
-             }
--            Gtk.Label label = new Gtk.Label(text);
--            label.set_halign(align);
--            label.set_valign(Gtk.Align.CENTER);
--            label.set_margin_start(20);
--            label.set_margin_end(20);
--            label.set_margin_top(6);
--            label.set_margin_bottom(6);
-+            EPaddedLabel label = new EPaddedLabel(text, align);
-             pack_start(label, true, true, 0);
-         }
-     }
--    private class ETitleLabel : Gtk.Box {
--        private Gtk.Label m_label;
-+    private class ETitleLabelBox : Gtk.Box {
-+        EPaddedLabel m_lang_label;
-         private Gtk.Button m_close_button;
-         private ulong m_close_handler;
- 
--        public ETitleLabel(string    text,
--                           Gtk.Align align) {
-+        public ETitleLabelBox(string    text,
-+                              Gtk.Align align) {
-             GLib.Object(
--                name : "IBusEmojierTitleLabel",
-+                name : "IBusEmojierTitleLabelBox",
-                 orientation : Gtk.Orientation.HORIZONTAL,
-                 spacing : 0
-             );
--            m_label = new Gtk.Label(text);
--            m_label.set_halign(align);
--            m_label.set_valign(align);
--            m_label.set_margin_start(20);
--            m_label.set_margin_end(20);
--            m_label.set_margin_top(6);
--            m_label.set_margin_bottom(6);
--            pack_start(m_label, true, true, 0);
-+            EPaddedLabel label = new EPaddedLabel(text, align);
-+            pack_start(label, true, true, 0);
-+            Gtk.Separator separator =
-+                    new Gtk.Separator (Gtk.Orientation.VERTICAL);
-+            pack_start(separator, false, true, 0);
-+            m_lang_label = new EPaddedLabel("", align);
-+            pack_start(m_lang_label, false, true, 0);
-             IconWidget icon = new IconWidget("window-close", Gtk.IconSize.MENU);
-             m_close_button = new Gtk.Button();
-             m_close_button.add(icon);
-@@ -168,8 +176,8 @@ class IBusEmojier : Gtk.Window {
-                 m_close_handler = 0;
-             }
-         }
--        public void set_label(string str) {
--            m_label.set_label(str);
-+        public void set_lang_label(string str) {
-+            m_lang_label.set_label(str);
-         }
-     }
- 
-@@ -181,7 +189,7 @@ class IBusEmojier : Gtk.Window {
-     private const uint EMOJI_GRID_PAGE = 10;
-     private ThemedRGBA m_rgba;
-     private Gtk.Box m_vbox;
--    private ETitleLabel m_title;
-+    private ETitleLabelBox m_title;
-     private EEntry m_entry;
-     private string? m_backward;
-     private EScrolledWindow? m_scrolled_window = null;
-@@ -278,8 +286,8 @@ class IBusEmojier : Gtk.Window {
-         m_vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
-         add(m_vbox);
- 
--        m_title = new ETitleLabel(_("Emoji Dialog"),
--                                  Gtk.Align.CENTER);
-+        m_title = new ETitleLabelBox(_("Emoji Dialog"),
-+                                     Gtk.Align.CENTER);
-         m_vbox.add(m_title);
-         m_entry = new EEntry();
-         m_entry.set_placeholder_text(_("Type annotation or choose emoji"));
-@@ -419,6 +427,21 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
-+    private string utf8_code_point(string str) {
-+        StringBuilder buff = new StringBuilder();
-+        int length = str.char_count();
-+        for (int i = 0; i < length; i++) {
-+            unichar ch = str.get_char(0);
-+            if (i == 0)
-+                buff.append("U+%04X".printf(ch));
-+            else
-+                buff.append(" %04X".printf(ch));
-+            str = str.next_char();
-+        }
-+        return buff.str;
-+    }
-+
-+
-     private void update_emoji_to_data_dict(IBus.EmojiData data,
-                                            string         lang) {
-         string emoji = data.get_emoji();
-@@ -501,7 +524,7 @@ class IBusEmojier : Gtk.Window {
-     private void remove_all_children() {
-         foreach (Gtk.Widget w in m_vbox.get_children()) {
-             if (w.name == "IBusEmojierEntry" ||
--                w.name == "IBusEmojierTitleLabel") {
-+                w.name == "IBusEmojierTitleLabelBox") {
-                 continue;
-             }
-             w.destroy();
-@@ -514,10 +537,9 @@ class IBusEmojier : Gtk.Window {
-         m_scrolled_window = new EScrolledWindow();
-         set_fixed_size();
- 
--        string language = "%s (%s)".printf(
--            _("Emoji Dialog"),
--            IBus.get_language_name(m_current_lang_id));
--        m_title.set_label(language);
-+        string language =
-+            IBus.get_language_name(m_current_lang_id);
-+        m_title.set_lang_label(language);
-         m_vbox.add(m_scrolled_window);
-         Gtk.Viewport viewport = new Gtk.Viewport(null, null);
-         m_scrolled_window.add(viewport);
-@@ -535,8 +557,8 @@ class IBusEmojier : Gtk.Window {
-         uint n = 1;
-         if (m_favorites.length > 0) {
-             EBoxRow row = new EBoxRow("@favorites");
--            EPaddedLabel widget =
--                    new EPaddedLabel(_("Favorites"), Gtk.Align.CENTER);
-+            EPaddedLabelBox widget =
-+                    new EPaddedLabelBox(_("Favorites"), Gtk.Align.CENTER);
-             row.add(widget);
-             m_list_box.add(row);
-             if (n++ == m_category_active_index)
-@@ -551,9 +573,9 @@ class IBusEmojier : Gtk.Window {
-         foreach (unowned string category in categories) {
-             EBoxRow row = new EBoxRow(category);
-             string locale_category = _(category);
--            EPaddedLabel widget =
--                    new EPaddedLabel(utf8_title(locale_category),
--                                     Gtk.Align.CENTER);
-+            EPaddedLabelBox widget =
-+                    new EPaddedLabelBox(utf8_title(locale_category),
-+                                        Gtk.Align.CENTER);
-             row.add(widget);
-             m_list_box.add(row);
-             if (n++ == m_category_active_index)
-@@ -626,18 +648,23 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private bool check_unicode_point(string? annotation=null) {
--        bool check_xdigit_only = true;
--        if (annotation == null) {
--            annotation = m_entry.get_text();
--            m_unicode_point = null;
--            check_xdigit_only = false;
--        }
-+    private bool check_unicode_point() {
-+        string annotation = m_entry.get_text();
-+        m_unicode_point = null;
-         GLib.StringBuilder buff = new GLib.StringBuilder();
-+        GLib.StringBuilder retval = new GLib.StringBuilder();
-         for (int i = 0; i < annotation.char_count(); i++) {
-             unichar ch = annotation.get_char(i);
-             if (ch == 0)
-                 return false;
-+            if (ch.isspace()) {
-+                unichar code = (unichar)buff.str.to_ulong(null, 16);
-+                buff.erase();
-+                if (!code.validate())
-+                    return false;
-+                retval.append(code.to_string());
-+                continue;
-+            }
-             if (!ch.isxdigit())
-                 return false;
-             buff.append_unichar(ch);
-@@ -645,9 +672,8 @@ class IBusEmojier : Gtk.Window {
-         unichar code = (unichar)buff.str.to_ulong(null, 16);
-         if (!code.validate())
-             return false;
--        if (check_xdigit_only)
--            return true;
--        m_unicode_point = code.to_string();
-+        retval.append(code.to_string());
-+        m_unicode_point = retval.str;
-         if (m_unicode_point == null)
-             return true;
-         IBus.Text text = new IBus.Text.from_string(m_unicode_point);
-@@ -699,9 +725,10 @@ class IBusEmojier : Gtk.Window {
-         if (m_backward != null) {
-             string backward_desc =
-                     "%s (%u / %u)".printf(m_backward, cursor, ncandidates - 1);
--            EPaddedLabel label = new EPaddedLabel(backward_desc,
--                                                  Gtk.Align.CENTER,
--                                                  TravelDirection.BACKWARD);
-+            EPaddedLabelBox label =
-+                    new EPaddedLabelBox(backward_desc,
-+                                        Gtk.Align.CENTER,
-+                                        TravelDirection.BACKWARD);
-             Gtk.Button button = new Gtk.Button();
-             button.add(label);
-             m_vbox.add(button);
-@@ -776,27 +803,24 @@ class IBusEmojier : Gtk.Window {
-             IBus.Text candidate = m_lookup_table.get_candidate(cursor);
-             unowned IBus.EmojiData? data =
-                     m_emoji_to_data_dict.lookup(candidate.text);
--            if (cursor == 0 && candidate.text == m_unicode_point) {
--                EPaddedLabel widget = new EPaddedLabel(
--                        _("Description: Unicode point U+%04X").printf(
--                                m_unicode_point.get_char(0)),
--                        Gtk.Align.START);
--                m_vbox.add(widget);
--                widget.show_all();
--                if (data == null)
--                    return;
--            } else if (data == null) {
-+            if (data == null) {
-                 // TODO: Provide a custom description and annotation for
-                 // the favorite emojis.
--                EPaddedLabel widget = new EPaddedLabel(
-+                EPaddedLabelBox widget = new EPaddedLabelBox(
-                         _("Description: %s").printf(_("None")),
-                         Gtk.Align.START);
-                 m_vbox.add(widget);
-                 widget.show_all();
-+                EPaddedLabelBox widget_code = new EPaddedLabelBox(
-+                        _("Code point: %s").printf(
-+                                utf8_code_point(candidate.text)),
-+                        Gtk.Align.START);
-+                m_vbox.add(widget_code);
-+                widget_code.show_all();
-                 return;
-             } else {
-                 unowned string description = data.get_description();
--                EPaddedLabel widget = new EPaddedLabel(
-+                EPaddedLabelBox widget = new EPaddedLabelBox(
-                         _("Description: %s").printf(description),
-                         Gtk.Align.START);
-                 m_vbox.add(widget);
-@@ -812,19 +836,26 @@ class IBusEmojier : Gtk.Window {
-                 else
-                     buff.append_printf(" | %s", annotation);
-                 if (buff.str.char_count() > 30) {
--                    EPaddedLabel widget = new EPaddedLabel(buff.str,
--                                                           Gtk.Align.START);
-+                    EPaddedLabelBox widget =
-+                            new EPaddedLabelBox(buff.str,
-+                                                Gtk.Align.START);
-                     m_vbox.add(widget);
-                     widget.show_all();
-                     buff.erase();
-                 }
-             }
-             if (buff.str != "") {
--                EPaddedLabel widget = new EPaddedLabel(buff.str,
--                                                       Gtk.Align.START);
-+                EPaddedLabelBox widget = new EPaddedLabelBox(buff.str,
-+                                                             Gtk.Align.START);
-                 m_vbox.add(widget);
-                 widget.show_all();
-             }
-+            EPaddedLabelBox widget_code = new EPaddedLabelBox(
-+                    _("Code point: %s").printf(
-+                            utf8_code_point(candidate.text)),
-+                    Gtk.Align.START);
-+            m_vbox.add(widget_code);
-+            widget_code.show_all();
-         }
-     }
- 
-@@ -837,43 +868,6 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private bool if_in_range_of_lookup(uint keyval) {
--        if (!m_candidate_panel_is_visible)
--            return false;
--        StringBuilder buffer_string = new StringBuilder(m_entry.get_text());
--        unichar ch = IBus.keyval_to_unicode (keyval);
--        buffer_string.append_unichar(ch);
--        if (check_unicode_point(buffer_string.str))
--            return false;
--        if (keyval < Gdk.Key.@0 || keyval > Gdk.Key.@9)
--            return false;
--        if (keyval == Gdk.Key.@0)
--            keyval = Gdk.Key.@9 + 1;
--        uint index = keyval - Gdk.Key.@1 + 1;
--        uint candidates = m_lookup_table.get_number_of_candidates();
--        uint cursor_pos = m_lookup_table.get_cursor_pos();
--        uint page_size = m_lookup_table.get_page_size();
--        if (index > uint.min(candidates - (cursor_pos / page_size) * page_size,
--                             page_size)) {
--            return false;
--        }
--        return true;
--    }
--
--
--    private void set_number_on_lookup(uint keyval) {
--        if (keyval == Gdk.Key.@0)
--            keyval = Gdk.Key.@9 + 1;
--        uint index = keyval - Gdk.Key.@1;
--        uint cursor_pos = m_lookup_table.get_cursor_pos();
--        uint cursor_in_page= m_lookup_table.get_cursor_in_page();
--        uint real_index = cursor_pos - cursor_in_page + index;
--        m_lookup_table.set_cursor_pos(real_index);
--        IBus.Text text = m_lookup_table.get_candidate(real_index);
--        m_result = text.text;
--    }
--
--
-     private void enter_notify_disable_with_timer() {
-         // Enable keyboard operation and disable mouse operation.
-         m_enter_notify_enable = false;
-@@ -1154,11 +1148,6 @@ class IBusEmojier : Gtk.Window {
-          * key_release_event() so that this can know if the event
-          * was handled by IME.
-          */
--        if (if_in_range_of_lookup(keyval)) {
--            set_number_on_lookup(keyval);
--            m_loop.quit();
--            return true;
--        }
-         switch (keyval) {
-         case Gdk.Key.Escape:
-             if (key_press_cursor_escape())
--- 
-2.9.3
-
-From cb0a36c254dc7a96b2a984e715f96cc2ec32e2d5 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 3 Apr 2017 12:24:27 +0900
-Subject: [PATCH] src: Enable unicode_alt in EmojiOne json file
-
-EmojiOne json file has unicode_alt property which includes
-emoji modifer characters.
-
-Review URL: https://codereview.appspot.com/316420043
----
- src/emoji-parser.c   | 20 +++++++++++++++++---
- src/ibusemoji.c      | 48 ++++++++++++++++++++++++++++++++++++++++++++----
- src/ibusemoji.h      | 14 ++++++++++++++
- ui/gtk3/emojier.vala |  9 ++++++---
- 4 files changed, 81 insertions(+), 10 deletions(-)
-
-diff --git a/src/emoji-parser.c b/src/emoji-parser.c
-index f9e3470..e5dce3f 100644
---- a/src/emoji-parser.c
-+++ b/src/emoji-parser.c
-@@ -40,6 +40,7 @@
- typedef struct _EmojiData EmojiData;
- struct _EmojiData {
-     gchar      *emoji;
-+    gchar      *emoji_alternates;
-     GSList     *annotations;
-     gboolean    is_annotation;
-     gchar      *description;
-@@ -54,6 +55,7 @@ reset_emoji_element (EmojiData *data)
-     g_assert (data != NULL);
- 
-     g_clear_pointer (&data->emoji, g_free);
-+    g_clear_pointer (&data->emoji_alternates, g_free);
-     g_slist_free_full (data->annotations, g_free);
-     data->annotations = NULL;
-     g_clear_pointer (&data->description, g_free);
-@@ -112,6 +114,10 @@ update_emoji_list (EmojiData *data)
-                                      "category",
-                                      data->category ? data->category
-                                              : g_strdup (""),
-+                                     "emoji-alternates",
-+                                     data->emoji_alternates
-+                                             ? data->emoji_alternates
-+                                             : g_strdup (""),
-                                      NULL);
-         data->list = g_slist_append (data->list, emoji);
-     }
-@@ -271,7 +277,8 @@ failed_to_parse_unicode_annotations:
- 
- static gboolean
- parse_emojione_unicode (JsonNode  *node,
--                        EmojiData *data)
-+                        EmojiData *data,
-+                        gboolean   is_alternates)
- {
-     const gchar *str, *unicode;
-     gchar *endptr = NULL;
-@@ -305,7 +312,10 @@ parse_emojione_unicode (JsonNode  *node,
-         endptr = NULL;
-     }
- 
--    data->emoji = g_string_free (emoji, FALSE);
-+    if (is_alternates)
-+        data->emoji_alternates = g_string_free (emoji, FALSE);
-+    else
-+        data->emoji = g_string_free (emoji, FALSE);
- 
-     return TRUE;
- }
-@@ -480,7 +490,11 @@ parse_emojione_emoji_data (JsonNode    *node,
-                            EmojiData   *data)
- {
-     if (g_strcmp0 (member, "unicode") == 0)
--        return parse_emojione_unicode (node, data);
-+        return parse_emojione_unicode (node, data, FALSE);
-+    else if (g_strcmp0 (member, "unicode_alt") == 0)
-+        return parse_emojione_unicode (node, data, TRUE);
-+    else if (g_strcmp0 (member, "unicode_alternates") == 0)
-+        return parse_emojione_unicode (node, data, TRUE);
-     else if (g_strcmp0 (member, "shortname") == 0)
-         return parse_emojione_shortname (node, data);
-     else if (g_strcmp0 (member, "name") == 0)
-diff --git a/src/ibusemoji.c b/src/ibusemoji.c
-index c61cd70..4be092a 100644
---- a/src/ibusemoji.c
-+++ b/src/ibusemoji.c
-@@ -29,14 +29,15 @@
- #include "ibusinternal.h"
- 
- #define IBUS_EMOJI_DATA_MAGIC "IBusEmojiData"
--#define IBUS_EMOJI_DATA_VERSION (2)
-+#define IBUS_EMOJI_DATA_VERSION (3)
- 
- enum {
-     PROP_0 = 0,
-     PROP_EMOJI,
-     PROP_ANNOTATIONS,
-     PROP_DESCRIPTION,
--    PROP_CATEGORY
-+    PROP_CATEGORY,
-+    PROP_EMOJI_ALTERNATES
- };
- 
- struct _IBusEmojiDataPrivate {
-@@ -44,6 +45,7 @@ struct _IBusEmojiDataPrivate {
-     GSList     *annotations;
-     gchar      *description;
-     gchar      *category;
-+    gchar      *emoji_alternates;
- };
- 
- #define IBUS_EMOJI_DATA_GET_PRIVATE(o)  \
-@@ -142,6 +144,19 @@ ibus_emoji_data_class_init (IBusEmojiDataClass *class)
-                         "The emoji category",
-                         "",
-                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusEmojiData:emoji_alternates:
-+     *
-+     * The emoji alternate characters
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_EMOJI_ALTERNATES,
-+                    g_param_spec_string ("emoji-alternates",
-+                        "emoji alternate charasters",
-+                        "The emoji alternate characters UTF-8",
-+                        "",
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
- }
- 
- static void
-@@ -180,19 +195,24 @@ ibus_emoji_data_set_property (IBusEmojiData *emoji,
-         emoji->priv->emoji = g_value_dup_string (value);
-         break;
-     case PROP_ANNOTATIONS:
--        g_assert (emoji->priv->annotations == NULL);
-+        if (emoji->priv->annotations)
-+            g_slist_free_full (emoji->priv->annotations, g_free);
-         emoji->priv->annotations =
-                 g_slist_copy_deep (g_value_get_pointer (value),
-                                    (GCopyFunc) g_strdup, NULL);
-         break;
-     case PROP_DESCRIPTION:
--        g_assert (emoji->priv->description == NULL);
-+        g_free (emoji->priv->description);
-         emoji->priv->description = g_value_dup_string (value);
-         break;
-     case PROP_CATEGORY:
-         g_assert (emoji->priv->category == NULL);
-         emoji->priv->category = g_value_dup_string (value);
-         break;
-+    case PROP_EMOJI_ALTERNATES:
-+        g_assert (emoji->priv->emoji_alternates == NULL);
-+        emoji->priv->emoji_alternates = g_value_dup_string (value);
-+        break;
-     default:
-         G_OBJECT_WARN_INVALID_PROPERTY_ID (emoji, prop_id, pspec);
-     }
-@@ -220,6 +240,9 @@ ibus_emoji_data_get_property (IBusEmojiData *emoji,
-     case PROP_CATEGORY:
-         g_value_set_string (value, ibus_emoji_data_get_category (emoji));
-         break;
-+    case PROP_EMOJI_ALTERNATES:
-+        g_value_set_string (value, ibus_emoji_data_get_emoji_alternates(emoji));
-+        break;
-     default:
-         G_OBJECT_WARN_INVALID_PROPERTY_ID (emoji, prop_id, pspec);
-     }
-@@ -247,6 +270,9 @@ ibus_emoji_data_serialize (IBusEmojiData   *emoji,
-     }
-     g_variant_builder_add (builder, "s", NOTNULL (emoji->priv->description));
-     g_variant_builder_add (builder, "s", NOTNULL (emoji->priv->category));
-+    g_variant_builder_add (builder, "s",
-+                           NOTNULL (emoji->priv->emoji_alternates));
-+#undef NOTNULL
-     return TRUE;
- }
- 
-@@ -277,6 +303,10 @@ ibus_emoji_data_deserialize (IBusEmojiData *emoji,
-                                      &emoji->priv->description);
-     ibus_g_variant_get_child_string (variant, retval++,
-                                      &emoji->priv->category);
-+    if (g_variant_n_children (variant) < retval + 1)
-+        return retval;
-+    ibus_g_variant_get_child_string (variant, retval++,
-+                                     &emoji->priv->emoji_alternates);
-     return retval;
- }
- 
-@@ -295,6 +325,7 @@ ibus_emoji_data_copy (IBusEmojiData       *dest,
-                                                       NULL);
-     dest->priv->description      = g_strdup (src->priv->description);
-     dest->priv->category         = g_strdup (src->priv->category);
-+    dest->priv->emoji_alternates = g_strdup (src->priv->emoji_alternates);
-     return TRUE;
- }
- 
-@@ -314,6 +345,7 @@ ibus_emoji_data_new (const gchar *first_property_name, ...)
-     g_assert (emoji->priv->emoji != NULL);
-     g_assert (emoji->priv->description != NULL);
-     g_assert (emoji->priv->category != NULL);
-+    g_assert (emoji->priv->emoji_alternates != NULL);
-     return emoji;
- }
- 
-@@ -370,6 +402,14 @@ ibus_emoji_data_get_category (IBusEmojiData *emoji)
-     return emoji->priv->category;
- }
- 
-+const gchar *
-+ibus_emoji_data_get_emoji_alternates (IBusEmojiData *emoji)
-+{
-+    g_return_val_if_fail (IBUS_IS_EMOJI_DATA (emoji), NULL);
-+
-+    return emoji->priv->emoji_alternates;
-+}
-+
- 
- static void
- variant_foreach_add_emoji (IBusEmojiData   *emoji,
-diff --git a/src/ibusemoji.h b/src/ibusemoji.h
-index eb24fdd..233cadd 100644
---- a/src/ibusemoji.h
-+++ b/src/ibusemoji.h
-@@ -156,6 +156,20 @@ const gchar *   ibus_emoji_data_get_category    (IBusEmojiData *emoji);
- 
- 
- /**
-+ * ibus_emoji_data_get_emoji_alternates:
-+ * @emoji : An #IBusEmojiData
-+ *
-+ * Gets the emoji alternate characters in #IBusEmojiData. It should not be
-+ * freed. The alternates are defined in "unicode_alt" in EmojiOne json.
-+ *
-+ * Returns: emoji alternates property in #IBusEmojiData
-+ *
-+ */
-+const gchar *   ibus_emoji_data_get_emoji_alternates
-+                                                (IBusEmojiData *emoji);
-+
-+
-+/**
-  * ibus_emoji_dict_save:
-  * @path: A path of the saved dictionary file.
-  * @dict: (element-type utf8 gpointer) (transfer none): An Emoji dictionary
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 8a2726c..7b6107f 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -381,7 +381,8 @@ class IBusEmojier : Gtk.Window {
- 
- 
-     private void update_annotation_to_emojis_dict(IBus.EmojiData data) {
--        string emoji = data.get_emoji();
-+        string emoji = (data.get_emoji_alternates() != "") ?
-+                data.get_emoji_alternates() : data.get_emoji();
-         unowned GLib.SList<string> annotations = data.get_annotations();
-         foreach (string annotation in annotations) {
-             bool has_emoji = false;
-@@ -444,7 +445,8 @@ class IBusEmojier : Gtk.Window {
- 
-     private void update_emoji_to_data_dict(IBus.EmojiData data,
-                                            string         lang) {
--        string emoji = data.get_emoji();
-+        string emoji = (data.get_emoji_alternates() != "") ?
-+                data.get_emoji_alternates() : data.get_emoji();
-         if (lang == "en") {
-             string description = utf8_down(data.get_description());
-             unowned GLib.SList<string> annotations = data.get_annotations();
-@@ -496,7 +498,8 @@ class IBusEmojier : Gtk.Window {
- 
-     private void update_category_to_emojis_dict(IBus.EmojiData data,
-                                                 string         lang) {
--        string emoji = data.get_emoji();
-+        string emoji = (data.get_emoji_alternates() != "") ?
-+                data.get_emoji_alternates() : data.get_emoji();
-         string category = data.get_category();
-         if (lang == "en" && category != "") {
-             bool has_emoji = false;
--- 
-2.9.3
-
-From 648f58a361ea1407f229ed1682486240cc2e5026 Mon Sep 17 00:00:00 2001
-From: Peng Wu <alexepico@gmail.com>
-Date: Mon, 10 Apr 2017 14:54:08 +0900
-Subject: [PATCH] Make ibus emojier dialog as an unique application
-
-Because wayland doesn't support clipboard persistence, just hide the dialog; will show the emojier dialog again, when run it again.
-
-In next patches, ibus emoji command will just spawn the process of ibus-ui-emojier.
-
-BUG=
-R=takao.fujiwara1@gmail.com
-
-Review URL: https://codereview.appspot.com/320450043
-
-Patch from Peng Wu <alexepico@gmail.com>.
----
- po/POTFILES.in      |  1 +
- po/POTFILES.skip    |  1 +
- ui/gtk3/Makefile.am | 24 ++++++++++++++++++++++++
- 3 files changed, 26 insertions(+)
-
-diff --git a/po/POTFILES.in b/po/POTFILES.in
-index 469f90b..65a3526 100644
---- a/po/POTFILES.in
-+++ b/po/POTFILES.in
-@@ -65,3 +65,4 @@ ui/gtk3/property.vala
- ui/gtk3/propertypanel.vala
- ui/gtk3/separator.vala
- ui/gtk3/switcher.vala
-+ui/gtk3/emojierapp.vala
-diff --git a/po/POTFILES.skip b/po/POTFILES.skip
-index a818c48..891d2cc 100644
---- a/po/POTFILES.skip
-+++ b/po/POTFILES.skip
-@@ -21,3 +21,4 @@ ui/gtk3/property.c
- ui/gtk3/propertypanel.c
- ui/gtk3/separator.c
- ui/gtk3/switcher.c
-+ui/gtk3/emojierapp.c
-diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
-index 7122ff3..96ed3e6 100644
---- a/ui/gtk3/Makefile.am
-+++ b/ui/gtk3/Makefile.am
-@@ -108,6 +108,10 @@ endif
- 
- libexec_PROGRAMS = ibus-ui-gtk3
- 
-+if ENABLE_EMOJI_DICT
-+libexec_PROGRAMS += ibus-ui-emojier
-+endif
-+
- ibus_ui_gtk3_SOURCES = \
- 	application.vala \
- 	candidatearea.vala \
-@@ -132,6 +136,26 @@ ibus_ui_gtk3_LDADD = \
- 	$(AM_LDADD) \
- 	$(NULL)
- 
-+if ENABLE_EMOJI_DICT
-+AM_VALAFLAGS += \
-+        --define=EMOJI_DICT \
-+        --vapidir=$(top_builddir)/ui/gtk3 \
-+        --vapidir=$(top_srcdir)/ui/gtk3 \
-+        --pkg=ibus-emoji-dialog-1.0 \
-+        --pkg=gtk+-3.0 \
-+        $(NULL)
-+
-+
-+ibus_ui_emojier_SOURCES = \
-+	emojierapp.vala \
-+	$(NULL)
-+
-+ibus_ui_emojier_LDADD = \
-+	$(AM_LDADD) \
-+	$(libibus_emoji_dialog) \
-+	$(NULL)
-+endif
-+
- gen-%.xml.c: %.xml
- 	echo "Building $@ from $<"
- 	echo "const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<)))) = " > $@
--- 
-2.9.3
-
-From 2f8982a0a4b25f98c969c09b81724e44efc41c27 Mon Sep 17 00:00:00 2001
-From: Peng Wu <alexepico@gmail.com>
-Date: Mon, 10 Apr 2017 15:57:48 +0900
-Subject: [PATCH] Make ibus emojier dialog as an unique application
-
-Forgot to commit ui/gtk3/emojierapp.vala from the previous commit.
-
-BUG=
-R=takao.fujiwara1@gmail.com
-
-Review URL: https://codereview.appspot.com/319650043
-
-Patch from Peng Wu <alexepico@gmail.com>.
----
- ui/gtk3/emojierapp.vala | 149 ++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 149 insertions(+)
- create mode 100644 ui/gtk3/emojierapp.vala
-
-diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
-new file mode 100644
-index 0000000..4287bff
---- /dev/null
-+++ b/ui/gtk3/emojierapp.vala
-@@ -0,0 +1,149 @@
-+/* vim:set et sts=4 sw=4:
-+ *
-+ * ibus - The Input Bus
-+ *
-+ * Copyright (c) 2017 Peng Wu <alexepico@gmail.com>
-+ *
-+ * 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
-+ */
-+
-+string emoji_font = null;
-+string annotation_lang = null;
-+
-+public class EmojiApplication : Application {
-+    private IBus.Emojier emojier = new IBus.Emojier();
-+
-+    private EmojiApplication() {
-+        Object(application_id: "org.freedesktop.ibus.panel.emojier",
-+                flags: ApplicationFlags.HANDLES_COMMAND_LINE);
-+        set_inactivity_timeout(100000);
-+    }
-+
-+    private void show_dialog(ApplicationCommandLine command_line) {
-+        Gdk.Event event = new Gdk.Event(Gdk.EventType.KEY_PRESS);
-+        var display = Gdk.Display.get_default();
-+        var device_manager = display.get_device_manager();
-+        var device = device_manager.list_devices(Gdk.DeviceType.MASTER).data;
-+        event.set_device(device);
-+        string emoji = emojier.run(event, "");
-+        if (emoji == null) {
-+            emojier.reset();
-+            command_line.print("%s\n", _("Canceled to choose an emoji."));
-+            return;
-+        }
-+        Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
-+        clipboard.set_text(emoji, -1);
-+        clipboard.store();
-+        emojier.reset();
-+        command_line.print("%s\n", _("Copied an emoji to your clipboard."));
-+    }
-+
-+    public void activate_dialog(ApplicationCommandLine command_line) {
-+        this.hold ();
-+
-+        // show dialog
-+        if (emojier.has_loaded_emoji_dict()) {
-+            show_dialog(command_line);
-+        } else {
-+            emojier.loaded_emoji_dict.connect(() => {
-+                    // The signal is called when the language is changed.
-+                    if (emojier.is_running())
-+                    return;
-+
-+                    show_dialog(command_line);
-+                    });
-+        }
-+
-+        this.release ();
-+    }
-+
-+    private int _command_line (ApplicationCommandLine command_line) {
-+        const OptionEntry[] options = {
-+            { "font", 0, 0, OptionArg.STRING, out emoji_font,
-+                N_("FONT for emoji chracters on emoji dialog."),
-+                "FONT" },
-+            { "lang", 0, 0, OptionArg.STRING, out annotation_lang,
-+                N_("LANG for annotations on emoji dialog. E.g. \"en\""),
-+                "LANG" },
-+            { null }
-+        };
-+
-+        var option = new OptionContext();
-+        option.add_main_entries(options, Config.GETTEXT_PACKAGE);
-+
-+        // We have to make an extra copy of the array,
-+        // since .parse assumes that it can remove strings
-+        // from the array without freeing them.
-+        string[] args = command_line.get_arguments();
-+        string*[] _args = new string[args.length];
-+        for (int i = 0; i < args.length; i++) {
-+            _args[i] = args[i];
-+        }
-+
-+        try {
-+            unowned string[] tmp = _args;
-+            option.parse(ref tmp);
-+        } catch (OptionError e) {
-+            stderr.printf("%s\n", e.message);
-+            return Posix.EXIT_FAILURE;
-+        }
-+
-+        if (emoji_font == null) {
-+            GLib.Settings settings_emoji =
-+                new GLib.Settings("org.freedesktop.ibus.panel.emoji");
-+            emoji_font = settings_emoji.get_string("font");
-+        }
-+
-+        if (annotation_lang == null) {
-+            GLib.Settings settings_emoji =
-+                new GLib.Settings("org.freedesktop.ibus.panel.emoji");
-+            annotation_lang = settings_emoji.get_string("lang");
-+        }
-+
-+        if (emoji_font != null && emoji_font != "")
-+            emojier.set_emoji_font(emoji_font);
-+        if (annotation_lang != null && annotation_lang != "")
-+            emojier.set_annotation_lang(annotation_lang);
-+
-+        activate_dialog(command_line);
-+
-+        return Posix.EXIT_SUCCESS;
-+    }
-+
-+    public override int command_line (ApplicationCommandLine command_line) {
-+        // keep the application running until we are done with this commandline
-+        this.hold();
-+        int result = _command_line(command_line);
-+        this.release();
-+        return result;
-+    }
-+
-+    public static int main (string[] args) {
-+        GLib.Intl.bindtextdomain(Config.GETTEXT_PACKAGE,
-+                Config.GLIB_LOCALE_DIR);
-+        GLib.Intl.bind_textdomain_codeset(Config.GETTEXT_PACKAGE, "UTF-8");
-+        GLib.Intl.textdomain(Config.GETTEXT_PACKAGE);
-+
-+        IBus.init();
-+
-+        Gtk.init(ref args);
-+
-+        EmojiApplication app = new EmojiApplication();
-+        int status = app.run(args);
-+        return status;
-+    }
-+
-+}
--- 
-2.9.3
-
-From da33672bc738889dcd4ebbec51cb3aa67567f70c Mon Sep 17 00:00:00 2001
-From: Peng Wu <alexepico@gmail.com>
-Date: Thu, 13 Apr 2017 12:40:22 +0900
-Subject: [PATCH] tools: spawn the process of ibus-ui-emojier
-
-BUG=
-R=Shawn.P.Huang@gmail.com, takao.fujiwara1@gmail.com
-
-Review URL: https://codereview.appspot.com/324770043
-
-Patch from Peng Wu <alexepico@gmail.com>.
----
- bindings/vala/config.vapi |  1 +
- tools/Makefile.am         |  1 +
- tools/main.vala           | 72 +++++++++--------------------------------------
- 3 files changed, 15 insertions(+), 59 deletions(-)
-
-diff --git a/bindings/vala/config.vapi b/bindings/vala/config.vapi
-index f2195da..e3c43df 100644
---- a/bindings/vala/config.vapi
-+++ b/bindings/vala/config.vapi
-@@ -7,6 +7,7 @@ namespace Config
-     public const string BINDIR;
-     public const string DATADIR;
-     public const string PKGDATADIR;
-+    public const string LIBEXECDIR;
-     public const string GETTEXT_PACKAGE;
-     public const string GLIB_LOCALE_DIR;
- }
-diff --git a/tools/Makefile.am b/tools/Makefile.am
-index bd655af..9d542bd 100644
---- a/tools/Makefile.am
-+++ b/tools/Makefile.am
-@@ -39,6 +39,7 @@ AM_CFLAGS = \
- 	@GIO2_CFLAGS@ \
- 	@GTHREAD2_CFLAGS@ \
- 	-DG_LOG_DOMAIN=\"IBUS\" \
-+	-DLIBEXECDIR=\"$(libexecdir)\" \
- 	-DIBUS_DISABLE_DEPRECATED \
- 	-Wno-unused-variable \
- 	-Wno-unused-but-set-variable \
-diff --git a/tools/main.vala b/tools/main.vala
-index 2bf1dc7..9aca4b0 100644
---- a/tools/main.vala
-+++ b/tools/main.vala
-@@ -324,73 +324,27 @@ int reset_config(string[] argv) {
- }
- 
- #if EMOJI_DICT
--private void run_dialog(IBus.Emojier emojier) {
--    Gdk.Event event = new Gdk.Event(Gdk.EventType.KEY_PRESS);
--    var display = Gdk.Display.get_default();
--    var device_manager = display.get_device_manager();
--    var device = device_manager.list_devices(Gdk.DeviceType.MASTER).data;
--    event.set_device(device);
--    string emoji = emojier.run(event, "");
--    if (emoji == null) {
--        emojier.reset();
--        print("%s\n", _("Canceled to choose an emoji."));
--        return;
--    }
--    Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
--    clipboard.set_text(emoji, -1);
--    clipboard.store();
--    emojier.reset();
--    print("%s\n", _("Copied an emoji to your clipboard."));
--}
--
- int emoji_dialog(string[] argv) {
--    const OptionEntry[] options = {
--        { "font", 0, 0, OptionArg.STRING, out emoji_font,
--          N_("FONT for emoji chracters on emoji dialog."), "FONT" },
--        { "lang", 0, 0, OptionArg.STRING, out annotation_lang,
--          N_("LANG for annotations on emoji dialog. E.g. \"en\""), "LANG" },
--        { null }
--    };
-+    string cmd = Config.LIBEXECDIR + "/ibus-ui-emojier";
- 
--    var option = new OptionContext();
--    option.add_main_entries(options, Config.GETTEXT_PACKAGE);
-+    var file = File.new_for_path(cmd);
-+    if (!file.query_exists())
-+        cmd = "../ui/gtk3/ibus-ui-emojier";
-+
-+    argv[0] = cmd;
-+
-+    string[] env = Environ.get();
- 
-     try {
--        option.parse(ref argv);
--    } catch (OptionError e) {
-+        // Non-blocking
-+        Process.spawn_async(null, argv, env,
-+                            SpawnFlags.SEARCH_PATH,
-+                            null, null);
-+    } catch (SpawnError e) {
-         stderr.printf("%s\n", e.message);
-         return Posix.EXIT_FAILURE;
-     }
- 
--    Gtk.init(ref argv);
--    if (emoji_font == null) {
--        GLib.Settings settings_emoji =
--                new GLib.Settings("org.freedesktop.ibus.panel.emoji");
--        emoji_font = settings_emoji.get_string("font");
--    }
--    if (annotation_lang == null) {
--        GLib.Settings settings_emoji =
--                new GLib.Settings("org.freedesktop.ibus.panel.emoji");
--        annotation_lang = settings_emoji.get_string("lang");
--    }
--    IBus.Emojier emojier = new IBus.Emojier();
--    if (emoji_font != null && emoji_font != "")
--        emojier.set_emoji_font(emoji_font);
--    if (annotation_lang != null && annotation_lang != "")
--        emojier.set_annotation_lang(annotation_lang);
--    if (emojier.has_loaded_emoji_dict()) {
--        run_dialog(emojier);
--    } else {
--        GLib.MainLoop loop = new GLib.MainLoop();
--        emojier.loaded_emoji_dict.connect(() => {
--            // The signal is called when the language is changed.
--            if (emojier.is_running())
--                return;
--            run_dialog(emojier);
--            loop.quit();
--        });
--        loop.run();
--    }
-     return Posix.EXIT_SUCCESS;
- }
- #endif
--- 
-2.9.3
-
-From 290f786b82158e7c087b81946727606cd3424e94 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 17 Apr 2017 16:00:26 +0900
-Subject: [PATCH] ui/gtk3: Fix build failures
-
-ibus-ui-emoijer needs to be built at last in ui/gtk3
-Also fixed po/POTFILES.in for `make dist`
-Now I moved libibusemojidialog.so and IBusEmojiDialog-1.0.[gir|typelib]
-and ibus-emoji-dialog-1.0.vapi to noinst because now
-ibus-ui-emojier is available and they are private libraries.
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/312610043
----
- po/POTFILES.in          |  1 +
- tools/Makefile.am       | 19 ++-----------
- ui/gtk3/Makefile.am     | 72 ++++++++++++++++++++++---------------------------
- ui/gtk3/emojierapp.vala | 33 +++++++++++++----------
- 4 files changed, 54 insertions(+), 71 deletions(-)
-
-diff --git a/po/POTFILES.in b/po/POTFILES.in
-index 65a3526..25be4f4 100644
---- a/po/POTFILES.in
-+++ b/po/POTFILES.in
-@@ -31,6 +31,7 @@ ibus/object.py
- ibus/panel.py
- ibus/property.py
- ibus/utility.py
-+setup/emojilang.py
- setup/engineabout.py
- setup/enginecombobox.py
- setup/enginedialog.py
-diff --git a/tools/Makefile.am b/tools/Makefile.am
-index 9d542bd..5c18d3d 100644
---- a/tools/Makefile.am
-+++ b/tools/Makefile.am
-@@ -101,24 +101,9 @@ CLEANFILES = \
- 
- if ENABLE_EMOJI_DICT
- if ENABLE_UI
--AM_CPPFLAGS += \
--	-I$(top_srcdir)/ui/gtk3 \
--	-I$(top_builddir)/ui/gtk3 \
--	$(NULL)
--AM_CFLAGS += \
--	@GTK3_CFLAGS@ \
--	$(NULL)
--AM_LDADD += \
--	@GTK3_LIBS@ \
--	$(libibus_emoji_dialog) \
--	$(NULL)
- AM_VALAFLAGS += \
--	--define=EMOJI_DICT \
--	--vapidir=$(top_builddir)/ui/gtk3 \
--	--vapidir=$(top_srcdir)/ui/gtk3 \
--	--pkg=ibus-emoji-dialog-1.0 \
--	--pkg=gtk+-3.0 \
--	$(NULL)
-+    --define=EMOJI_DICT \
-+    $(NULL)
- endif
- endif
- 
-diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
-index 96ed3e6..6f0fb62 100644
---- a/ui/gtk3/Makefile.am
-+++ b/ui/gtk3/Makefile.am
-@@ -81,7 +81,9 @@ AM_VALAFLAGS = \
- 	--target-glib="$(VALA_TARGET_GLIB_VERSION)" \
- 	$(NULL)
- 
-+MAINTAINERCLEANFILES =
- CONFIG_CLEAN_FILES =
-+noinst_DATA =
- 
- if ENABLE_LIBNOTIFY
- AM_CFLAGS += \
-@@ -108,10 +110,6 @@ endif
- 
- libexec_PROGRAMS = ibus-ui-gtk3
- 
--if ENABLE_EMOJI_DICT
--libexec_PROGRAMS += ibus-ui-emojier
--endif
--
- ibus_ui_gtk3_SOURCES = \
- 	application.vala \
- 	candidatearea.vala \
-@@ -136,26 +134,6 @@ ibus_ui_gtk3_LDADD = \
- 	$(AM_LDADD) \
- 	$(NULL)
- 
--if ENABLE_EMOJI_DICT
--AM_VALAFLAGS += \
--        --define=EMOJI_DICT \
--        --vapidir=$(top_builddir)/ui/gtk3 \
--        --vapidir=$(top_srcdir)/ui/gtk3 \
--        --pkg=ibus-emoji-dialog-1.0 \
--        --pkg=gtk+-3.0 \
--        $(NULL)
--
--
--ibus_ui_emojier_SOURCES = \
--	emojierapp.vala \
--	$(NULL)
--
--ibus_ui_emojier_LDADD = \
--	$(AM_LDADD) \
--	$(libibus_emoji_dialog) \
--	$(NULL)
--endif
--
- gen-%.xml.c: %.xml
- 	echo "Building $@ from $<"
- 	echo "const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<)))) = " > $@
-@@ -185,11 +163,17 @@ EXTRA_DIST =                            \
-     $(NULL)
- 
- if ENABLE_EMOJI_DICT
--AM_VALAFLAGS += --define=EMOJI_DICT
-+AM_VALAFLAGS += \
-+    --define=EMOJI_DICT \
-+    --vapidir=$(top_builddir)/ui/gtk3 \
-+    --vapidir=$(top_srcdir)/ui/gtk3 \
-+    --pkg=ibus-emoji-dialog-1.0 \
-+    --pkg=gtk+-3.0 \
-+    $(NULL)
- 
- libibus_emoji_dialog = libibus-emoji-dialog-1.0.la
- 
--lib_LTLIBRARIES = $(libibus_emoji_dialog)
-+noinst_LTLIBRARIES = $(libibus_emoji_dialog)
- 
- libibus_emoji_dialog_1_0_la_CFLAGS = $(AM_CFLAGS)
- libibus_emoji_dialog_1_0_la_LDFLAGS =   \
-@@ -198,11 +182,22 @@ libibus_emoji_dialog_1_0_la_LDFLAGS =   \
-     -version-info @LT_VERSION_INFO@     \
-     $(NULL)
- libibus_emoji_dialog_1_0_la_SOURCES =   \
--    candidatearea.c                     \
--    emojier.c                           \
--    iconwidget.c                        \
--    pango.c                             \
--    separator.c                         \
-+    candidatearea.vala                  \
-+    emojier.vala                        \
-+    iconwidget.vala                     \
-+    pango.vala                          \
-+    separator.vala                      \
-+    $(NULL)
-+
-+libexec_PROGRAMS += ibus-ui-emojier
-+
-+ibus_ui_emojier_SOURCES =                       \
-+    $(libibus_emoji_dialog_1_0_la_SOURCES)      \
-+    emojierapp.vala                             \
-+    $(NULL)
-+
-+ibus_ui_emojier_LDADD =                         \
-+    $(AM_LDADD)                                 \
-     $(NULL)
- 
- -include $(INTROSPECTION_MAKEFILE)
-@@ -214,10 +209,6 @@ INTROSPECTION_COMPILER_ARGS =      \
-     $(NULL)
- 
- if HAVE_INTROSPECTION
--ibusincludedir = $(includedir)/ibus-@IBUS_API_VERSION@
--ibusinclude_HEADERS =                  \
--    $(emoji_headers)                   \
--    $(NULL)
- introspection_sources =                \
-     $(emoji_headers)                   \
-     $(NULL)
-@@ -242,12 +233,13 @@ IBusEmojiDialog_1_0_gir_CFLAGS =       \
- INTROSPECTION_GIRS = IBusEmojiDialog-1.0.gir
- 
- girdir = $(datadir)/gir-1.0
--dist_gir_DATA = $(INTROSPECTION_GIRS)
-+noinst_DATA += $(INTROSPECTION_GIRS)
-+CLEANFILES += $(INTROSPECTION_GIRS)
- 
- typelibsdir = $(libdir)/girepository-1.0
--typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
-+noinst_DATA += $(INTROSPECTION_GIRS:.gir=.typelib)
-+CLEANFILES += $(INTROSPECTION_GIRS:.gir=.typelib)
- 
--CLEANFILES += $(dist_gir_DATA) $(typelibs_DATA)
- 
- if ENABLE_VAPIGEN
- -include $(VAPIGEN_MAKEFILE)
-@@ -261,9 +253,9 @@ ibus_emoji_dialog_1_0_vapi_METADATADIRS = $(srcdir)
- ibus_emoji_dialog_1_0_vapi_FILES = $(INTROSPECTION_GIRS)
- 
- vapidir = $(datadir)/vala/vapi
--vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
-+noinst_DATA += $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
- 
--MAINTAINERCLEANFILES = $(VAPIGEN_VAPIS)
-+MAINTAINERCLEANFILES += $(VAPIGEN_VAPIS)
- # for make distclean
- CONFIG_CLEAN_FILES += $(VAPIGEN_VAPIS)
- EXTRA_DIST += $(VAPIGEN_VAPIS)
-diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
-index 4287bff..6349940 100644
---- a/ui/gtk3/emojierapp.vala
-+++ b/ui/gtk3/emojierapp.vala
-@@ -24,52 +24,55 @@ string emoji_font = null;
- string annotation_lang = null;
- 
- public class EmojiApplication : Application {
--    private IBus.Emojier emojier = new IBus.Emojier();
-+    private IBusEmojier m_emojier = new IBusEmojier();
- 
-     private EmojiApplication() {
-         Object(application_id: "org.freedesktop.ibus.panel.emojier",
--                flags: ApplicationFlags.HANDLES_COMMAND_LINE);
-+               flags: ApplicationFlags.HANDLES_COMMAND_LINE);
-         set_inactivity_timeout(100000);
-     }
- 
-+
-     private void show_dialog(ApplicationCommandLine command_line) {
-         Gdk.Event event = new Gdk.Event(Gdk.EventType.KEY_PRESS);
-         var display = Gdk.Display.get_default();
-         var device_manager = display.get_device_manager();
-         var device = device_manager.list_devices(Gdk.DeviceType.MASTER).data;
-         event.set_device(device);
--        string emoji = emojier.run(event, "");
-+        string emoji = m_emojier.run(event, "");
-         if (emoji == null) {
--            emojier.reset();
-+            m_emojier.reset();
-             command_line.print("%s\n", _("Canceled to choose an emoji."));
-             return;
-         }
-         Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
-         clipboard.set_text(emoji, -1);
-         clipboard.store();
--        emojier.reset();
-+        m_emojier.reset();
-         command_line.print("%s\n", _("Copied an emoji to your clipboard."));
-     }
- 
-+
-     public void activate_dialog(ApplicationCommandLine command_line) {
-         this.hold ();
- 
-         // show dialog
--        if (emojier.has_loaded_emoji_dict()) {
-+        if (m_emojier.has_loaded_emoji_dict()) {
-             show_dialog(command_line);
-         } else {
--            emojier.loaded_emoji_dict.connect(() => {
--                    // The signal is called when the language is changed.
--                    if (emojier.is_running())
-+            m_emojier.loaded_emoji_dict.connect(() => {
-+                // The signal is called when the language is changed.
-+                if (m_emojier.is_running())
-                     return;
- 
--                    show_dialog(command_line);
--                    });
-+                show_dialog(command_line);
-+            });
-         }
- 
-         this.release ();
-     }
- 
-+
-     private int _command_line (ApplicationCommandLine command_line) {
-         const OptionEntry[] options = {
-             { "font", 0, 0, OptionArg.STRING, out emoji_font,
-@@ -114,15 +117,16 @@ public class EmojiApplication : Application {
-         }
- 
-         if (emoji_font != null && emoji_font != "")
--            emojier.set_emoji_font(emoji_font);
-+            m_emojier.set_emoji_font(emoji_font);
-         if (annotation_lang != null && annotation_lang != "")
--            emojier.set_annotation_lang(annotation_lang);
-+            m_emojier.set_annotation_lang(annotation_lang);
- 
-         activate_dialog(command_line);
- 
-         return Posix.EXIT_SUCCESS;
-     }
- 
-+
-     public override int command_line (ApplicationCommandLine command_line) {
-         // keep the application running until we are done with this commandline
-         this.hold();
-@@ -131,9 +135,10 @@ public class EmojiApplication : Application {
-         return result;
-     }
- 
-+
-     public static int main (string[] args) {
-         GLib.Intl.bindtextdomain(Config.GETTEXT_PACKAGE,
--                Config.GLIB_LOCALE_DIR);
-+                                 Config.GLIB_LOCALE_DIR);
-         GLib.Intl.bind_textdomain_codeset(Config.GETTEXT_PACKAGE, "UTF-8");
-         GLib.Intl.textdomain(Config.GETTEXT_PACKAGE);
- 
--- 
-2.9.3
-
-From 4b8d7baf627fb3fc9e4dde74ba33a867091f95b7 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 18 Apr 2017 11:44:52 +0900
-Subject: [PATCH] ui/gtk3: Mark LANG and FONT as translatable strings
-
-BUG=https://github.com/ibus/ibus/issues/1920
-
-Review URL: https://codereview.appspot.com/322880043
----
- ui/gtk3/emojierapp.vala | 14 ++++++++++----
- 1 file changed, 10 insertions(+), 4 deletions(-)
-
-diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
-index 6349940..eac8d6a 100644
---- a/ui/gtk3/emojierapp.vala
-+++ b/ui/gtk3/emojierapp.vala
-@@ -76,11 +76,17 @@ public class EmojiApplication : Application {
-     private int _command_line (ApplicationCommandLine command_line) {
-         const OptionEntry[] options = {
-             { "font", 0, 0, OptionArg.STRING, out emoji_font,
--                N_("FONT for emoji chracters on emoji dialog."),
--                "FONT" },
-+                /* TRANSLATORS: "FONT" should be capital and translatable.
-+                 * It's used for an argument command --font=FONT
-+                 */
-+                N_("\"FONT\" for emoji chracters on emoji dialog"),
-+                N_("FONT") },
-             { "lang", 0, 0, OptionArg.STRING, out annotation_lang,
--                N_("LANG for annotations on emoji dialog. E.g. \"en\""),
--                "LANG" },
-+                /* TRANSLATORS: "LANG" should be capital and translatable.
-+                 * It's used for an argument command --lang=LANG
-+                 */
-+                N_("\"LANG\" for annotations on emoji dialog. E.g. \"en\""),
-+                N_("LANG") },
-             { null }
-         };
- 
--- 
-2.9.3
-
-From c6cdf21c7364cbb1e848e44cab0bff270e432e82 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 19 Apr 2017 12:16:37 +0900
-Subject: [PATCH] ui/gtk3: Change modal dialog to focused dialog
-
-There are several problems with the current emoji modal dialog.
-If keyboard is grabbed on the popup window, the focus out/in events
-cannot be detected so the dialog cannot be closed by the focus changes.
-
-If mouse operation is supported on the popup window, need a custom
-GtkHeaderBar with extended handle.vala but the behavior is unclear. [1]
-
-Also current popup window has several extensions for the keyboard grab.
-
-If the closed button is needed on the popup window since the focus events
-cannot be detected, I think there is no merit to use the popup window.
-Now IBusEmojier simply uses the focused window.
-
-[1] https://mail.gnome.org/archives/gtk-app-devel-list/2017-April/msg00017.html
-
-R=Shawn.P.Huang@gmail.com, alexepico@gmail.com
-
-Review URL: https://codereview.appspot.com/316510043
----
- ui/gtk3/emojier.vala      | 243 +++++++++++++++++++---------------------------
- ui/gtk3/emojierapp.vala   |  55 ++++-------
- ui/gtk3/ibusemojidialog.h |  54 +++++------
- ui/gtk3/panel.vala        |  73 ++++++++++----
- 4 files changed, 194 insertions(+), 231 deletions(-)
-
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 7b6107f..5a9bf8f 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -137,47 +137,27 @@ class IBusEmojier : Gtk.Window {
-             pack_start(label, true, true, 0);
-         }
-     }
--    private class ETitleLabelBox : Gtk.Box {
--        EPaddedLabel m_lang_label;
--        private Gtk.Button m_close_button;
--        private ulong m_close_handler;
-+    private class ETitleLabelBox : Gtk.HeaderBar {
-+        private Gtk.Label m_lang_label;
- 
--        public ETitleLabelBox(string    text,
--                              Gtk.Align align) {
-+        public ETitleLabelBox(string title) {
-             GLib.Object(
-                 name : "IBusEmojierTitleLabelBox",
--                orientation : Gtk.Orientation.HORIZONTAL,
--                spacing : 0
-+                show_close_button: true,
-+                decoration_layout: ":close",
-+                title: title
-             );
--            EPaddedLabel label = new EPaddedLabel(text, align);
--            pack_start(label, true, true, 0);
--            Gtk.Separator separator =
--                    new Gtk.Separator (Gtk.Orientation.VERTICAL);
--            pack_start(separator, false, true, 0);
--            m_lang_label = new EPaddedLabel("", align);
--            pack_start(m_lang_label, false, true, 0);
--            IconWidget icon = new IconWidget("window-close", Gtk.IconSize.MENU);
--            m_close_button = new Gtk.Button();
--            m_close_button.add(icon);
--            pack_end(m_close_button, false, true, 0);
--        }
--        public void set_loop(GLib.MainLoop? loop) {
--            if (m_close_handler > 0)
--                GLib.SignalHandler.disconnect(m_close_button, m_close_handler);
--            m_close_handler = m_close_button.button_press_event.connect((e) => {
--                if (loop != null && loop.is_running())
--                    loop.quit();
--                return true;
--            });
--        }
--        public void unset_loop() {
--            if (m_close_handler > 0) {
--                GLib.SignalHandler.disconnect(m_close_button, m_close_handler);
--                m_close_handler = 0;
--            }
-+            var vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
-+            set_custom_title(vbox);
-+            var label = new Gtk.Label(title);
-+            label.get_style_context().add_class("title");
-+            vbox.pack_start(label, true, false, 0);
-+            m_lang_label = new Gtk.Label(null);
-+            m_lang_label.get_style_context().add_class("subtitle");
-+            vbox.pack_start(m_lang_label, true, false, 0);
-         }
-         public void set_lang_label(string str) {
--            m_lang_label.set_label(str);
-+            m_lang_label.set_text(str);
-         }
-     }
- 
-@@ -186,7 +166,22 @@ class IBusEmojier : Gtk.Window {
-         BACKWARD,
-     }
- 
--    private const uint EMOJI_GRID_PAGE = 10;
-+    private static const uint EMOJI_GRID_PAGE = 10;
-+
-+    // Set the actual default values in the constructor
-+    // because these fields are used for class_init() and static functions,
-+    // e.g. set_emoji_font(), can be called before class_init() is called.
-+    private static string m_current_lang_id;
-+    private static string m_emoji_font;
-+    private static string[] m_favorites;
-+    private static int m_emoji_max_seq_len;
-+    private static GLib.HashTable<string, GLib.SList>?
-+            m_annotation_to_emojis_dict;
-+    private static GLib.HashTable<string, IBus.EmojiData>?
-+            m_emoji_to_data_dict;
-+    private static GLib.HashTable<string, GLib.SList>?
-+            m_category_to_emojis_dict;
-+
-     private ThemedRGBA m_rgba;
-     private Gtk.Box m_vbox;
-     private ETitleLabelBox m_title;
-@@ -198,43 +193,38 @@ class IBusEmojier : Gtk.Window {
-     private string m_input_context_path = "";
-     private GLib.MainLoop? m_loop;
-     private string? m_result;
--    private string m_current_lang_id = "en";
-     private string? m_unicode_point = null;
-     private bool m_candidate_panel_is_visible;
--    private GLib.HashTable<string, GLib.SList>?
--            m_annotation_to_emojis_dict = null;
--    private GLib.HashTable<string, IBus.EmojiData>?
--            m_emoji_to_data_dict = null;
--    private GLib.HashTable<string, GLib.SList>?
--            m_category_to_emojis_dict = null;
-     int m_category_active_index;
--    private int m_emoji_max_seq_len = 0;
-     private IBus.LookupTable m_lookup_table;
-     private Gtk.Label[] m_candidates;
--    private string m_emoji_font = "Monospace 16";
--    private string[] m_favorites = {};
-     private bool m_enter_notify_enable = true;
-     private uint m_entry_notify_show_id;
-     private uint m_entry_notify_disable_id;
--    private uint m_reload_emoji_dict_id;
- 
-     public signal void candidate_clicked(uint index, uint button, uint state);
--    public signal void loaded_emoji_dict();
- 
-     public IBusEmojier() {
-         GLib.Object(
--            type : Gtk.WindowType.POPUP,
-+            type : Gtk.WindowType.TOPLEVEL,
-             events : Gdk.EventMask.KEY_PRESS_MASK |
-                      Gdk.EventMask.KEY_RELEASE_MASK |
--                     Gdk.EventMask.BUTTON_PRESS_MASK,
-+                     Gdk.EventMask.BUTTON_PRESS_MASK |
-+                     Gdk.EventMask.BUTTON_RELEASE_MASK,
-             window_position : Gtk.WindowPosition.CENTER,
-+            icon_name: "ibus-setup",
-             accept_focus : true,
--            decorated : false,
--            modal : true,
-             resizable : true,
-             focus_visible : true
-         );
- 
-+        if (m_current_lang_id == null)
-+            m_current_lang_id = "en";
-+        if (m_emoji_font == null)
-+            m_emoji_font = "Monospace 16";
-+        if (m_favorites == null)
-+            m_favorites = {};
-+
-         Gdk.Display display = Gdk.Display.get_default();
-         Gdk.Screen screen = (display != null) ?
-                 display.get_default_screen() : null;
-@@ -283,12 +273,11 @@ class IBusEmojier : Gtk.Window {
-                 css_provider,
-                 Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
- 
-+        m_title = new ETitleLabelBox(_("Emoji Chooser"));
-+        set_titlebar(m_title);
-         m_vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
-         add(m_vbox);
- 
--        m_title = new ETitleLabelBox(_("Emoji Dialog"),
--                                     Gtk.Align.CENTER);
--        m_vbox.add(m_title);
-         m_entry = new EEntry();
-         m_entry.set_placeholder_text(_("Type annotation or choose emoji"));
-         m_vbox.add(m_entry);
-@@ -306,8 +295,6 @@ class IBusEmojier : Gtk.Window {
-         Atk.Object obj = m_entry.get_accessible();
-         obj.set_role (Atk.Role.STATUSBAR);
- 
--        grab_focus();
--
-         // The constructor of IBus.LookupTable does not support more than
-         // 16 pages.
-         m_lookup_table = new IBus.LookupTable(1, 0, true, true);
-@@ -325,15 +312,13 @@ class IBusEmojier : Gtk.Window {
-             hide_candidate_panel();
-         });
- 
--        m_reload_emoji_dict_id = GLib.Idle.add(() => {
-+        if (m_annotation_to_emojis_dict == null) {
-             reload_emoji_dict();
--            m_reload_emoji_dict_id = 0;
--            return false;
--        });
-+        }
-     }
- 
- 
--    private void reload_emoji_dict() {
-+    private static void reload_emoji_dict() {
-         init_emoji_dict();
-         make_emoji_dict("en");
-         if (m_current_lang_id != "en") {
-@@ -344,11 +329,10 @@ class IBusEmojier : Gtk.Window {
-             }
-             make_emoji_dict(m_current_lang_id);
-         }
--        loaded_emoji_dict();
-     }
- 
- 
--    private void init_emoji_dict() {
-+    private static void init_emoji_dict() {
-         m_annotation_to_emojis_dict =
-                 new GLib.HashTable<string, GLib.SList>(GLib.str_hash,
-                                                        GLib.str_equal);
-@@ -361,7 +345,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private void make_emoji_dict(string lang) {
-+    private static void make_emoji_dict(string lang) {
-         GLib.SList<IBus.EmojiData> emoji_list = IBus.EmojiData.load(
-                     Config.PKGDATADIR + "/dicts/emoji-" + lang + ".dict");
-         if (emoji_list == null)
-@@ -380,7 +364,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private void update_annotation_to_emojis_dict(IBus.EmojiData data) {
-+    private static void update_annotation_to_emojis_dict(IBus.EmojiData data) {
-         string emoji = (data.get_emoji_alternates() != "") ?
-                 data.get_emoji_alternates() : data.get_emoji();
-         unowned GLib.SList<string> annotations = data.get_annotations();
-@@ -402,7 +386,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private string utf8_down(string str) {
-+    private static string utf8_down(string str) {
-         GLib.StringBuilder buff = new GLib.StringBuilder();
-         int length = str.char_count();
-         for (int i = 0; i < length; i++) {
-@@ -413,7 +397,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private string utf8_title(string str) {
-+    private static string utf8_title(string str) {
-         StringBuilder buff = new StringBuilder();
-         int length = str.char_count();
-         for (int i = 0; i < length; i++) {
-@@ -428,7 +412,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private string utf8_code_point(string str) {
-+    private static string utf8_code_point(string str) {
-         StringBuilder buff = new StringBuilder();
-         int length = str.char_count();
-         for (int i = 0; i < length; i++) {
-@@ -443,8 +427,8 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private void update_emoji_to_data_dict(IBus.EmojiData data,
--                                           string         lang) {
-+    private static void update_emoji_to_data_dict(IBus.EmojiData data,
-+                                                  string         lang) {
-         string emoji = (data.get_emoji_alternates() != "") ?
-                 data.get_emoji_alternates() : data.get_emoji();
-         if (lang == "en") {
-@@ -496,8 +480,8 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private void update_category_to_emojis_dict(IBus.EmojiData data,
--                                                string         lang) {
-+    private static void update_category_to_emojis_dict(IBus.EmojiData data,
-+                                                       string         lang) {
-         string emoji = (data.get_emoji_alternates() != "") ?
-                 data.get_emoji_alternates() : data.get_emoji();
-         string category = data.get_category();
-@@ -937,7 +921,8 @@ class IBusEmojier : Gtk.Window {
-                                              uint modifiers) {
-         assert (keyval == Gdk.Key.Left || keyval == Gdk.Key.Right);
- 
--        if (m_candidate_panel_is_visible) {
-+        uint ncandidates = m_lookup_table.get_number_of_candidates();
-+        if (m_candidate_panel_is_visible && ncandidates > 1) {
-             enter_notify_disable_with_timer();
-             if (keyval == Gdk.Key.Left)
-                 m_lookup_table.cursor_up();
-@@ -971,7 +956,8 @@ class IBusEmojier : Gtk.Window {
-     private bool key_press_cursor_vertical(uint keyval) {
-         assert (keyval == Gdk.Key.Down || keyval == Gdk.Key.Up);
- 
--        if (m_candidate_panel_is_visible) {
-+        uint ncandidates = m_lookup_table.get_number_of_candidates();
-+        if (m_candidate_panel_is_visible && ncandidates > 1) {
-             if (keyval == Gdk.Key.Down)
-                 candidate_panel_cursor_down();
-             else if (keyval == Gdk.Key.Up)
-@@ -987,12 +973,12 @@ class IBusEmojier : Gtk.Window {
-                                            uint modifiers) {
-         assert (keyval == Gdk.Key.Home || keyval == Gdk.Key.End);
- 
--        if (m_candidate_panel_is_visible) {
-+        uint ncandidates = m_lookup_table.get_number_of_candidates();
-+        if (m_candidate_panel_is_visible && ncandidates > 1) {
-             enter_notify_disable_with_timer();
-             if (keyval == Gdk.Key.Home) {
-                 m_lookup_table.set_cursor_pos(0);
-             } else if (keyval == Gdk.Key.End) {
--                uint ncandidates = m_lookup_table.get_number_of_candidates();
-                 m_lookup_table.set_cursor_pos(ncandidates - 1);
-             }
-             show_candidate_panel();
-@@ -1052,8 +1038,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    public string run(Gdk.Event event,
--                      string    input_context_path) {
-+    public string run(string input_context_path) {
-         assert (m_loop == null);
- 
-         m_is_running = true;
-@@ -1066,65 +1051,22 @@ class IBusEmojier : Gtk.Window {
-         resize(1, 1);
- 
-         m_entry.set_text("");
--        m_entry.grab_focus();
--
--        Gdk.Device device = event.get_device();
--        if (device == null) {
--            var display = get_display();
--            var device_manager = display.get_device_manager();
--            device = device_manager.list_devices(Gdk.DeviceType.MASTER).data;
--        }
--
--        Gdk.Device keyboard;
--        Gdk.Device pointer;
--        if (device.get_source() == Gdk.InputSource.KEYBOARD) {
--            keyboard = device;
--            pointer = device.get_associated_device();
--        } else {
--            pointer = device;
--            keyboard = device.get_associated_device();
--        }
-+        //m_entry.grab_focus();
- 
-         show_category_list();
-         m_entry.set_activates_default(true);
-         show_all();
- 
--        Gdk.GrabStatus status;
--        // Grab all keyboard events
--        status = keyboard.grab(get_window(),
--                               Gdk.GrabOwnership.NONE,
--                               true,
--                               Gdk.EventMask.KEY_PRESS_MASK |
--                               Gdk.EventMask.KEY_RELEASE_MASK,
--                               null,
--                               Gdk.CURRENT_TIME);
--        if (status != Gdk.GrabStatus.SUCCESS)
--            warning("Grab keyboard failed! status = %d", status);
--        // Grab all pointer events
--        status = pointer.grab(get_window(),
--                              Gdk.GrabOwnership.NONE,
--                              true,
--                              Gdk.EventMask.BUTTON_PRESS_MASK |
--                              Gdk.EventMask.BUTTON_RELEASE_MASK,
--                              null,
--                              Gdk.CURRENT_TIME);
--        if (status != Gdk.GrabStatus.SUCCESS)
--            warning("Grab pointer failed! status = %d", status);
--
-         m_loop = new GLib.MainLoop();
--        m_title.set_loop(m_loop);
-         m_loop.run();
--        m_title.unset_loop();
-         m_loop = null;
- 
--        keyboard.ungrab(Gdk.CURRENT_TIME);
--        pointer.ungrab(Gdk.CURRENT_TIME);
--
-         // Need focus-out on Gtk.Entry to send the emoji to applications.
-         Gdk.Event fevent = new Gdk.Event(Gdk.EventType.FOCUS_CHANGE);
-         fevent.focus_change.in = 0;
-         fevent.focus_change.window  = get_window();
-         m_entry.send_focus_change(fevent);
-+        fevent.focus_change.window  = null;
- 
-         hide();
-         // Make sure the switcher is hidden before returning from this function.
-@@ -1288,20 +1230,23 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    public void set_emoji_font(string emoji_font) {
--        m_emoji_font = emoji_font;
--    }
--
--
--    public void set_favorites(string[]? unowned_favorites) {
--        m_favorites = {};
--        foreach (string favorite in unowned_favorites) {
--            m_favorites += favorite;
--        }
-+    public void present_centralize() {
-+        present();
-+        m_entry.set_activates_default(true);
-+        Gtk.Allocation allocation;
-+        get_allocation(out allocation);
-+        Gdk.Screen screen = Gdk.Screen.get_default();
-+        int monitor_num = screen.get_monitor_at_window(get_window());
-+        Gdk.Rectangle monitor_area;
-+        screen.get_monitor_geometry(monitor_num, out monitor_area);
-+        int x = (monitor_area.x + monitor_area.width - allocation.width)/2;
-+        int y = (monitor_area.y + monitor_area.height
-+                 - allocation.height)/2;
-+        move(x, y);
-     }
- 
- 
--    public bool has_loaded_emoji_dict() {
-+    public static bool has_loaded_emoji_dict() {
-         if (m_emoji_to_data_dict == null)
-             return false;
-         GLib.List keys = m_emoji_to_data_dict.get_keys();
-@@ -1311,18 +1256,26 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    public void set_annotation_lang(string lang) {
-+    public static void set_annotation_lang(string? lang) {
-+        if (lang == null || lang == "")
-+            lang = "en";
-         if (m_current_lang_id == lang)
-             return;
--        if (m_reload_emoji_dict_id > 0) {
--            GLib.Source.remove(m_reload_emoji_dict_id);
--            m_reload_emoji_dict_id = 0;
--        }
-         m_current_lang_id = lang;
--        m_reload_emoji_dict_id = GLib.Idle.add(() => {
--            reload_emoji_dict();
--            m_reload_emoji_dict_id = 0;
--            return false;
--        });
-+        reload_emoji_dict();
-+    }
-+
-+
-+    public static void set_emoji_font(string? emoji_font) {
-+        return_if_fail(emoji_font != null && emoji_font != "");
-+        m_emoji_font = emoji_font;
-+    }
-+
-+
-+    public static void set_favorites(string[]? unowned_favorites) {
-+        m_favorites = {};
-+        foreach (string favorite in unowned_favorites) {
-+            m_favorites += favorite;
-+        }
-     }
- }
-diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
-index eac8d6a..4564a25 100644
---- a/ui/gtk3/emojierapp.vala
-+++ b/ui/gtk3/emojierapp.vala
-@@ -24,7 +24,10 @@ string emoji_font = null;
- string annotation_lang = null;
- 
- public class EmojiApplication : Application {
--    private IBusEmojier m_emojier = new IBusEmojier();
-+    private IBusEmojier? m_emojier;
-+    GLib.Settings m_settings_emoji =
-+            new GLib.Settings("org.freedesktop.ibus.panel.emoji");
-+
- 
-     private EmojiApplication() {
-         Object(application_id: "org.freedesktop.ibus.panel.emojier",
-@@ -34,41 +37,24 @@ public class EmojiApplication : Application {
- 
- 
-     private void show_dialog(ApplicationCommandLine command_line) {
--        Gdk.Event event = new Gdk.Event(Gdk.EventType.KEY_PRESS);
--        var display = Gdk.Display.get_default();
--        var device_manager = display.get_device_manager();
--        var device = device_manager.list_devices(Gdk.DeviceType.MASTER).data;
--        event.set_device(device);
--        string emoji = m_emojier.run(event, "");
-+        m_emojier = new IBusEmojier();
-+        string emoji = m_emojier.run("");
-         if (emoji == null) {
--            m_emojier.reset();
-+            m_emojier = null;
-             command_line.print("%s\n", _("Canceled to choose an emoji."));
-             return;
-         }
-         Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
-         clipboard.set_text(emoji, -1);
-         clipboard.store();
--        m_emojier.reset();
-+        m_emojier = null;
-         command_line.print("%s\n", _("Copied an emoji to your clipboard."));
-     }
- 
- 
-     public void activate_dialog(ApplicationCommandLine command_line) {
-         this.hold ();
--
--        // show dialog
--        if (m_emojier.has_loaded_emoji_dict()) {
--            show_dialog(command_line);
--        } else {
--            m_emojier.loaded_emoji_dict.connect(() => {
--                // The signal is called when the language is changed.
--                if (m_emojier.is_running())
--                    return;
--
--                show_dialog(command_line);
--            });
--        }
--
-+        show_dialog(command_line);
-         this.release ();
-     }
- 
-@@ -110,22 +96,18 @@ public class EmojiApplication : Application {
-             return Posix.EXIT_FAILURE;
-         }
- 
--        if (emoji_font == null) {
--            GLib.Settings settings_emoji =
--                new GLib.Settings("org.freedesktop.ibus.panel.emoji");
--            emoji_font = settings_emoji.get_string("font");
-+        if (m_emojier != null && m_emojier.is_running()) {
-+            m_emojier.present_centralize();
-+            return Posix.EXIT_SUCCESS;
-         }
- 
--        if (annotation_lang == null) {
--            GLib.Settings settings_emoji =
--                new GLib.Settings("org.freedesktop.ibus.panel.emoji");
--            annotation_lang = settings_emoji.get_string("lang");
--        }
-+        if (emoji_font == null)
-+            emoji_font = m_settings_emoji.get_string("font");
-+        if (annotation_lang == null)
-+            annotation_lang = m_settings_emoji.get_string("lang");
- 
--        if (emoji_font != null && emoji_font != "")
--            m_emojier.set_emoji_font(emoji_font);
--        if (annotation_lang != null && annotation_lang != "")
--            m_emojier.set_annotation_lang(annotation_lang);
-+        IBusEmojier.set_annotation_lang(annotation_lang);
-+        IBusEmojier.set_emoji_font(emoji_font);
- 
-         activate_dialog(command_line);
- 
-@@ -156,5 +138,4 @@ public class EmojiApplication : Application {
-         int status = app.run(args);
-         return status;
-     }
--
- }
-diff --git a/ui/gtk3/ibusemojidialog.h b/ui/gtk3/ibusemojidialog.h
-index 0f84a48..1499a3c 100644
---- a/ui/gtk3/ibusemojidialog.h
-+++ b/ui/gtk3/ibusemojidialog.h
-@@ -69,7 +69,6 @@ IBusEmojier * ibus_emojier_new                    (void);
- /**
-  * ibus_emojier_run:
-  * @self: An #IBusEmojier
-- * @event: An #GdkEvent
-  * @input_context_path: An input context path of #IBusInputContext
-  *                      of the focused application.
-  *
-@@ -78,7 +77,6 @@ IBusEmojier * ibus_emojier_new                    (void);
-  * Returns: A selected emoji character.
-  */
- gchar *       ibus_emojier_run                    (IBusEmojier* self,
--                                                   GdkEvent*    event,
-                                                    const gchar*
-                                                            input_context_path);
- 
-@@ -116,49 +114,49 @@ gchar *       ibus_emojier_get_selected_string    (IBusEmojier* self);
- void          ibus_emojier_reset                  (IBusEmojier* self);
- 
- /**
-- * ibus_emojier_set_emoji_font:
-+ * ibus_emojier_present_centralize:
-  * @self: An #IBusEmojier
-- * @emoji_font: font name for emoji characters
-  *
-- * Set emoji font on the emoji dialog
-+ * Move the window to the toplevel on the screen and centralize it.
-  */
--void          ibus_emojier_set_emoji_font         (IBusEmojier* self,
--                                                   const gchar* emoji_font);
--
--#if 0
--/* TODO: set customized annotations */
--/**
-- * ibus_emojier_set_favorites:
-- * @self: An #IBusEmojier
-- * @favorites: (array length=favorites_length): A custom emoji list.
-- * @favorites_length: A length of @favorites
-- *
-- * Set emoji font on the emoji dialog
-- */
--void          ibus_emojier_set_favorites          (IBusEmojier* self,
--                                                   gchar**      favorites,
--                                                   int
--                                                             favorites_length);
--#endif
-+void          ibus_emojier_present_centralize     (IBusEmojier* self);
- 
- /**
-  * ibus_emojier_has_loaded_emoji_dict:
-- * @self: An #IBusEmojier
-  *
-  * Returns: %TRUE if the emoji dict is loaded, otherwise %FALSE.
-  */
--gboolean      ibus_emojier_has_loaded_emoji_dict  (IBusEmojier* self);
-+gboolean      ibus_emojier_has_loaded_emoji_dict  (void);
- 
- /**
-  * ibus_emojier_set_annotation_lang:
-- * @self: An #IBusEmojier
-  * @lang: A langauge id for emoji annotations.
-  *
-  * Set a language id for emoji annotations. #IBusEmojier will load
-  * $PKGDATADIR/dicts/emoji-@lang.dict. The default is "en".
-  */
--void          ibus_emojier_set_annotation_lang    (IBusEmojier* self,
--                                                   const gchar* lang);
-+void          ibus_emojier_set_annotation_lang    (const gchar* lang);
- 
-+/**
-+ * ibus_emojier_set_emoji_font:
-+ * @emoji_font: font name for emoji characters
-+ *
-+ * Set emoji font on the emoji dialog
-+ */
-+void          ibus_emojier_set_emoji_font         (const gchar* emoji_font);
-+
-+#if 0
-+/* TODO: set customized annotations */
-+/**
-+ * ibus_emojier_set_favorites:
-+ * @favorites: (array length=favorites_length): A custom emoji list.
-+ * @favorites_length: A length of @favorites
-+ *
-+ * Set emoji font on the emoji dialog
-+ */
-+void          ibus_emojier_set_favorites          (gchar**      favorites,
-+                                                   int
-+                                                             favorites_length);
-+#endif
- G_END_DECLS
- #endif
-diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
-index 7350dcc..2375734 100644
---- a/ui/gtk3/panel.vala
-+++ b/ui/gtk3/panel.vala
-@@ -73,7 +73,8 @@ class Panel : IBus.PanelService {
-     private CandidatePanel m_candidate_panel;
-     private Switcher m_switcher;
-     private uint m_switcher_focus_set_engine_id;
--    private IBusEmojier m_emojier;
-+    private IBusEmojier? m_emojier;
-+    private uint m_emojier_set_emoji_lang_id;
-     private uint m_emojier_focus_commit_text_id;
-     private PropertyManager m_property_manager;
-     private PropertyPanel m_property_panel;
-@@ -137,7 +138,6 @@ class Panel : IBus.PanelService {
-             m_switcher.set_popup_delay_time((uint) m_switcher_delay_time);
-         }
- 
--        m_emojier = new IBusEmojier();
-         bind_emoji_shortcut();
- 
-         m_property_manager = new PropertyManager();
-@@ -595,7 +595,7 @@ class Panel : IBus.PanelService {
-             warning("No config emoji:font.");
-             return;
-         }
--        m_emojier.set_emoji_font(emoji_font);
-+        IBusEmojier.set_emoji_font(emoji_font);
- 
-         bool use_custom_font = m_settings_panel.get_boolean("use-custom-font");
- 
-@@ -766,11 +766,20 @@ class Panel : IBus.PanelService {
-     }
- 
-     private void set_emoji_favorites() {
--        m_emojier.set_favorites(m_settings_emoji.get_strv("favorites"));
-+        IBusEmojier.set_favorites(m_settings_emoji.get_strv("favorites"));
-     }
- 
-     private void set_emoji_lang() {
--        m_emojier.set_annotation_lang(m_settings_emoji.get_string("lang"));
-+        if (m_emojier_set_emoji_lang_id > 0) {
-+            GLib.Source.remove(m_emojier_set_emoji_lang_id);
-+            m_emojier_set_emoji_lang_id = 0;
-+        }
-+        m_emojier_set_emoji_lang_id = GLib.Idle.add(() => {
-+            IBusEmojier.set_annotation_lang(
-+                    m_settings_emoji.get_string("lang"));
-+            m_emojier_set_emoji_lang_id = 0;
-+            return false;
-+        });
-     }
- 
-     private int compare_versions(string version1, string version2) {
-@@ -984,15 +993,24 @@ class Panel : IBus.PanelService {
-         }
-     }
- 
--    private void handle_emoji_typing(Gdk.Event event) {
--        if (m_emojier.is_running())
--            return;
--        string emoji = m_emojier.run(event, m_real_current_context_path);
--        if (emoji == null)
-+    private void show_emojier() {
-+        m_emojier = new IBusEmojier();
-+        string emoji = m_emojier.run(m_real_current_context_path);
-+        if (emoji == null) {
-+            m_emojier = null;
-             return;
-+        }
-         this.emojier_focus_commit();
-     }
- 
-+    private void handle_emoji_typing(Gdk.Event event) {
-+        if (m_emojier != null && m_emojier.is_running()) {
-+            m_emojier.present_centralize();
-+            return;
-+        }
-+        show_emojier();
-+    }
-+
-     private void run_preload_engines(IBus.EngineDesc[] engines, int index) {
-         string[] names = {};
- 
-@@ -1361,29 +1379,34 @@ class Panel : IBus.PanelService {
-         if (selected_engine == null &&
-             prev_context_path != "" &&
-             m_switcher.is_running()) {
--            if (m_switcher_focus_set_engine_id > 0) {
-+            var context = GLib.MainContext.default();
-+            if (m_switcher_focus_set_engine_id > 0 &&
-+                context.find_source_by_id(m_switcher_focus_set_engine_id)
-+                        != null) {
-                 GLib.Source.remove(m_switcher_focus_set_engine_id);
-             }
-             m_switcher_focus_set_engine_id = GLib.Timeout.add(100, () => {
-                 // focus_in is comming before switcher returns
-                 switcher_focus_set_engine_real();
--                if (m_switcher_focus_set_engine_id > 0) {
--                    GLib.Source.remove(m_switcher_focus_set_engine_id);
--                    m_switcher_focus_set_engine_id = -1;
--                }
-+                m_switcher_focus_set_engine_id = -1;
-                 return false;
-             });
-         } else {
-             if (switcher_focus_set_engine_real()) {
--                if (m_switcher_focus_set_engine_id > 0) {
-+                var context = GLib.MainContext.default();
-+                if (m_switcher_focus_set_engine_id > 0 &&
-+                    context.find_source_by_id(m_switcher_focus_set_engine_id)
-+                            != null) {
-                     GLib.Source.remove(m_switcher_focus_set_engine_id);
--                    m_switcher_focus_set_engine_id = -1;
-                 }
-+                m_switcher_focus_set_engine_id = -1;
-             }
-         }
-     }
- 
-     private bool emojier_focus_commit_real() {
-+        if (m_emojier == null)
-+            return true;
-         string selected_string = m_emojier.get_selected_string();
-         string prev_context_path = m_emojier.get_input_context_path();
-         if (selected_string != null &&
-@@ -1391,7 +1414,7 @@ class Panel : IBus.PanelService {
-             prev_context_path == m_current_context_path) {
-             IBus.Text text = new IBus.Text.from_string(selected_string);
-             commit_text(text);
--            m_emojier.reset();
-+            m_emojier = null;
-             return true;
-         }
- 
-@@ -1399,12 +1422,17 @@ class Panel : IBus.PanelService {
-     }
- 
-     private void emojier_focus_commit() {
-+        if (m_emojier == null)
-+            return;
-         string selected_string = m_emojier.get_selected_string();
-         string prev_context_path = m_emojier.get_input_context_path();
-         if (selected_string == null &&
-             prev_context_path != "" &&
-             m_emojier.is_running()) {
--            if (m_emojier_focus_commit_text_id > 0) {
-+            var context = GLib.MainContext.default();
-+            if (m_emojier_focus_commit_text_id > 0 &&
-+                context.find_source_by_id(m_emojier_focus_commit_text_id)
-+                        != null) {
-                 GLib.Source.remove(m_emojier_focus_commit_text_id);
-             }
-             m_emojier_focus_commit_text_id = GLib.Timeout.add(100, () => {
-@@ -1415,10 +1443,13 @@ class Panel : IBus.PanelService {
-             });
-         } else {
-             if (emojier_focus_commit_real()) {
--                if (m_emojier_focus_commit_text_id > 0) {
-+                var context = GLib.MainContext.default();
-+                if (m_emojier_focus_commit_text_id > 0 &&
-+                    context.find_source_by_id(m_emojier_focus_commit_text_id)
-+                            != null) {
-                     GLib.Source.remove(m_emojier_focus_commit_text_id);
--                    m_emojier_focus_commit_text_id = -1;
-                 }
-+                m_emojier_focus_commit_text_id = -1;
-             }
-         }
-     }
--- 
-2.9.3
-
-From aba81ace1487bea57e80406413ae31cbb37ac7d2 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Thu, 20 Apr 2017 12:10:47 +0900
-Subject: [PATCH] ui/gtk3: Fix build failures
-
-Add-on fix to 290f786
-if /usr/share/vala*/vapi/ibus-emoji-dialog*.vapi does not exist.
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/323780043
----
- ui/gtk3/Makefile.am | 8 +-------
- 1 file changed, 1 insertion(+), 7 deletions(-)
-
-diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
-index 6f0fb62..d2ae0d6 100644
---- a/ui/gtk3/Makefile.am
-+++ b/ui/gtk3/Makefile.am
-@@ -163,13 +163,7 @@ EXTRA_DIST =                            \
-     $(NULL)
- 
- if ENABLE_EMOJI_DICT
--AM_VALAFLAGS += \
--    --define=EMOJI_DICT \
--    --vapidir=$(top_builddir)/ui/gtk3 \
--    --vapidir=$(top_srcdir)/ui/gtk3 \
--    --pkg=ibus-emoji-dialog-1.0 \
--    --pkg=gtk+-3.0 \
--    $(NULL)
-+AM_VALAFLAGS += --define=EMOJI_DICT
- 
- libibus_emoji_dialog = libibus-emoji-dialog-1.0.la
- 
--- 
-2.9.3
-
-From 61fa8eabd812a75b3cadd883ad4a4e92cf6877d3 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 25 Apr 2017 12:10:00 +0900
-Subject: [PATCH] ui/gtk3: Get emoji font size from gsettings
-
-If Emojier CLI does not specify the font size likes
---font 'Noto Emoji Color Regular', get the default size from GSettings.
-
-R=Shawn.P.Huang@gmail.com, alexepico@gmail.com
-
-Review URL: https://codereview.appspot.com/317540043
----
- setup/setup.ui          |  2 +-
- ui/gtk3/emojier.vala    | 41 ++++++++++++++++++++++++++---------------
- ui/gtk3/emojierapp.vala | 14 ++++++++++----
- ui/gtk3/panel.vala      |  2 +-
- 4 files changed, 38 insertions(+), 21 deletions(-)
-
-diff --git a/setup/setup.ui b/setup/setup.ui
-index 4ef3423..54ef916 100644
---- a/setup/setup.ui
-+++ b/setup/setup.ui
-@@ -137,7 +137,7 @@
-                             <property name="can_focus">False</property>
-                             <property name="tooltip_text" translatable="yes">The shortcut keys for showing emoji dialog</property>
-                             <property name="halign">start</property>
--                            <property name="label" translatable="yes">Emoji dialog:</property>
-+                            <property name="label" translatable="yes">Emoji choice:</property>
-                           </object>
-                           <packing>
-                             <property name="top_attach">5</property>
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 5a9bf8f..76dca6c 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -166,13 +166,14 @@ class IBusEmojier : Gtk.Window {
-         BACKWARD,
-     }
- 
--    private static const uint EMOJI_GRID_PAGE = 10;
-+    private const uint EMOJI_GRID_PAGE = 10;
- 
-     // Set the actual default values in the constructor
-     // because these fields are used for class_init() and static functions,
-     // e.g. set_emoji_font(), can be called before class_init() is called.
-     private static string m_current_lang_id;
--    private static string m_emoji_font;
-+    private static string m_emoji_font_family;
-+    private static int m_emoji_font_size;
-     private static string[] m_favorites;
-     private static int m_emoji_max_seq_len;
-     private static GLib.HashTable<string, GLib.SList>?
-@@ -220,8 +221,10 @@ class IBusEmojier : Gtk.Window {
- 
-         if (m_current_lang_id == null)
-             m_current_lang_id = "en";
--        if (m_emoji_font == null)
--            m_emoji_font = "Monospace 16";
-+        if (m_emoji_font_family == null)
-+            m_emoji_font_family = "Monospace";
-+        if (m_emoji_font_size == 0)
-+            m_emoji_font_size = 16;
-         if (m_favorites == null)
-             m_favorites = {};
- 
-@@ -242,6 +245,8 @@ class IBusEmojier : Gtk.Window {
-                 "#IBusEmojierWhiteLabel { background-color: " +
-                         "rgba(%u, %u, %u, %lf); ".printf(
-                         bg_red, bg_green, bg_blue, bg_alpha) +
-+                "font-family: %s; font-size: %dpt; ".printf(
-+                        m_emoji_font_family, m_emoji_font_size) +
-                 "border-width: 4px; border-radius: 3px; } ";
- 
-         uint fg_red = (uint)(m_rgba.selected_fg.red * 255);
-@@ -255,6 +260,8 @@ class IBusEmojier : Gtk.Window {
-         data += "#IBusEmojierSelectedLabel { color: " +
-                         "rgba(%u, %u, %u, %lf); ".printf(
-                         fg_red, fg_green, fg_blue, fg_alpha) +
-+                "font-family: %s; font-size: %dpt; ".printf(
-+                        m_emoji_font_family, m_emoji_font_size) +
-                 "background-color: " +
-                         "rgba(%u, %u, %u, %lf); ".printf(
-                         bg_red, bg_green, bg_blue, bg_alpha) +
-@@ -273,7 +280,7 @@ class IBusEmojier : Gtk.Window {
-                 css_provider,
-                 Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
- 
--        m_title = new ETitleLabelBox(_("Emoji Chooser"));
-+        m_title = new ETitleLabelBox(_("Emoji Choice"));
-         set_titlebar(m_title);
-         m_vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
-         add(m_vbox);
-@@ -734,17 +741,14 @@ class IBusEmojier : Gtk.Window {
-                 label = new ESelectedLabel(candidate.text) as Gtk.Label;
-             else
-                 label = new EWhiteLabel(candidate.text) as Gtk.Label;
--            string emoji_font = m_emoji_font;
-             if (candidate.text.char_count() > 2) {
--                Pango.FontDescription font_desc =
--                        Pango.FontDescription.from_string(emoji_font);
--                string font_family = font_desc.get_family();
--                int font_size = font_desc.get_size() / Pango.SCALE;
--                emoji_font = "%s %d".printf(font_family, font_size -2);
-+                string font_family = m_emoji_font_family;
-+                int font_size = m_emoji_font_size - 2;
-+                string emoji_font = "%s %d".printf(font_family, font_size);
-+                string markup = "<span font=\"%s\">%s</span>".
-+                        printf(emoji_font, candidate.get_text());
-+                label.set_markup(markup);
-             }
--            string markup = "<span font=\"%s\">%s</span>".
--                    printf(emoji_font, candidate.get_text());
--            label.set_markup(markup);
-             label.set_halign(Gtk.Align.FILL);
-             label.set_valign(Gtk.Align.FILL);
-             Gtk.EventBox candidate_ebox = new Gtk.EventBox();
-@@ -1268,7 +1272,14 @@ class IBusEmojier : Gtk.Window {
- 
-     public static void set_emoji_font(string? emoji_font) {
-         return_if_fail(emoji_font != null && emoji_font != "");
--        m_emoji_font = emoji_font;
-+        Pango.FontDescription font_desc =
-+                Pango.FontDescription.from_string(emoji_font);
-+        string font_family = font_desc.get_family();
-+        if (font_family != null)
-+            m_emoji_font_family = font_family;
-+        int font_size = font_desc.get_size() / Pango.SCALE;
-+        if (font_size != 0)
-+            m_emoji_font_size = font_size;
-     }
- 
- 
-diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
-index 4564a25..cbdd8ba 100644
---- a/ui/gtk3/emojierapp.vala
-+++ b/ui/gtk3/emojierapp.vala
-@@ -60,6 +60,9 @@ public class EmojiApplication : Application {
- 
- 
-     private int _command_line (ApplicationCommandLine command_line) {
-+        // Set default font size
-+        IBusEmojier.set_emoji_font(m_settings_emoji.get_string("font"));
-+
-         const OptionEntry[] options = {
-             { "font", 0, 0, OptionArg.STRING, out emoji_font,
-                 /* TRANSLATORS: "FONT" should be capital and translatable.
-@@ -88,6 +91,10 @@ public class EmojiApplication : Application {
-             _args[i] = args[i];
-         }
- 
-+        // Need to initialize for the second instance.
-+        emoji_font = null;
-+        annotation_lang = null;
-+
-         try {
-             unowned string[] tmp = _args;
-             option.parse(ref tmp);
-@@ -101,13 +108,12 @@ public class EmojiApplication : Application {
-             return Posix.EXIT_SUCCESS;
-         }
- 
--        if (emoji_font == null)
--            emoji_font = m_settings_emoji.get_string("font");
-         if (annotation_lang == null)
-             annotation_lang = m_settings_emoji.get_string("lang");
--
-         IBusEmojier.set_annotation_lang(annotation_lang);
--        IBusEmojier.set_emoji_font(emoji_font);
-+
-+        if (emoji_font != null)
-+            IBusEmojier.set_emoji_font(emoji_font);
- 
-         activate_dialog(command_line);
- 
-diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
-index 2375734..745489b 100644
---- a/ui/gtk3/panel.vala
-+++ b/ui/gtk3/panel.vala
-@@ -1239,7 +1239,7 @@ class Panel : IBus.PanelService {
-             m_sys_menu.append(item);
- 
- #if EMOJI_DICT
--            item = new Gtk.MenuItem.with_label(_("Emoji Dialog"));
-+            item = new Gtk.MenuItem.with_label(_("Emoji Choice"));
-             item.activate.connect((i) => {
-                 Gdk.Event event = new Gdk.Event(Gdk.EventType.KEY_PRESS);
-                 handle_emoji_typing(event);
--- 
-2.9.3
-
-From 2f73064d03481c9f34d278dd19d1a4427d6ecb27 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Fri, 28 Apr 2017 13:10:03 +0900
-Subject: [PATCH] Change to use Unicode Emoji files instead of EmojiOne
- files
-
-EmojiOne Json file have changed the file format since 3.0 [1]
-Now IBus migrated to use Unicode Emoji files since the EmojiOne file
-had been used for the emoji categories but it can be done with
-Unicode emoji-test.txt [2]
-Use Unicode Emoji 4.0 until 5.0 will be released officially.
-Unicode Emoji 5.0 is available but fonts does not support it yet.
-
-[1] https://github.com/Ranks/emojione/releases/tag/v3.0.0
-[2] http://www.unicode.org/Public/emoji/4.0/
-
-Review URL: https://codereview.appspot.com/321860043
----
- bindings/vala/Makefile.am     |   3 +-
- configure.ac                  |  27 +-
- data/annotations/Makefile.am  |   3 +-
- data/annotations/en_ascii.xml |  56 ++++
- po/POTFILES.in                |   3 +-
- po/POTFILES.skip              |   2 +-
- src/Makefile.am               |  17 +-
- src/emoji-parser.c            | 632 ++++++++++++++++++++++++++++++++++++++----
- src/ibusemoji.c               |  47 +---
- src/ibusemoji.h               |  14 -
- src/ibusemojigen.h            |  39 +++
- ui/gtk3/Makefile.am           |   5 +-
- ui/gtk3/emojier.vala          | 106 +++++--
- 13 files changed, 780 insertions(+), 174 deletions(-)
- create mode 100644 data/annotations/en_ascii.xml
- create mode 100644 src/ibusemojigen.h
-
-diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am
-index f3eb0d4..4e34afc 100644
---- a/bindings/vala/Makefile.am
-+++ b/bindings/vala/Makefile.am
-@@ -43,8 +43,7 @@ vapidir = $(datadir)/vala/vapi
- vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
- 
- MAINTAINERCLEANFILES = $(VAPIGEN_VAPIS)
--# for make distclean
--CONFIG_CLEAN_FILES = $(VAPIGEN_VAPIS)
-+DISTCLEANFILES = $(VAPIGEN_VAPIS)
- 
- EXTRA_DIST = \
- 	$(VAPIGEN_VAPIS) \
-diff --git a/configure.ac b/configure.ac
-index 0a5f2d5..ea568a0 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -612,14 +612,14 @@ AC_ARG_ENABLE(emoji-dict,
- )
- AM_CONDITIONAL([ENABLE_EMOJI_DICT], [test x"$enable_emoji_dict" = x"yes"])
- 
--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_ARG_WITH(unicode-emoji-dir,
-+    AS_HELP_STRING([--with-unicode-emoji-dir[=DIR]],
-+        [Set the directory of Unicode Emoji.
-+         (default: "/usr/share/unicode/emoji")]),
-+    UNICODE_EMOJI_DIR=$with_unicode_emoji_dir,
-+    UNICODE_EMOJI_DIR="/usr/share/unicode/emoji"
- )
--AC_SUBST(EMOJI_JSON_FILE)
-+AC_SUBST(UNICODE_EMOJI_DIR)
- 
- AC_ARG_WITH(emoji-annotation-dir,
-     AS_HELP_STRING([--with-emoji-annotation-dir[=DIR]],
-@@ -631,17 +631,14 @@ AC_ARG_WITH(emoji-annotation-dir,
- AC_SUBST(EMOJI_ANNOTATION_DIR)
- 
- if test x"$enable_emoji_dict" = x"yes"; then
--    if test ! -f $EMOJI_JSON_FILE ; then
--        AC_MSG_ERROR(Not found $EMOJI_JSON_FILE. You can get emoji.json \
--with "npm install -g emojione".)
-+    if test ! -f $UNICODE_EMOJI_DIR/emoji-test.txt ; then
-+        AC_MSG_ERROR(Not found $UNICODE_EMOJI_DIR/emoji-test.txt. You can get \
-+the emoji files from http://www.unicode.org/Public/emoji/4.0/)
-     fi
-     if test ! -f $EMOJI_ANNOTATION_DIR/en.xml ; then
-         AC_MSG_ERROR(Not found $EMOJI_ANNOTATION_DIR/en.xml. You can get \
- https://github.com/fujiwarat/cldr-emoji-annotation)
-     fi
--    PKG_CHECK_MODULES(JSON_GLIB1, [
--        json-glib-1.0
--    ])
-     enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)"
- fi
- 
-@@ -730,8 +727,8 @@ Build options:
-   Enable surrounding-text       $enable_surrounding_text
-   Enable libnotify              $enable_libnotify
-   Enable Emoji dict             $enable_emoji_dict
--  emoji.json path               $EMOJI_JSON_FILE
--  CLDR annotation dir           $EMOJI_ANNOTATION_DIR
-+  Uicode Emoji directory        $UNICODE_EMOJI_DIR
-+  CLDR annotation directory     $EMOJI_ANNOTATION_DIR
-   Run test cases                $enable_tests
- ])
- 
-diff --git a/data/annotations/Makefile.am b/data/annotations/Makefile.am
-index d87b933..0b7779b 100644
---- a/data/annotations/Makefile.am
-+++ b/data/annotations/Makefile.am
-@@ -2,7 +2,7 @@
- #
- # ibus - The Input Bus
- #
--# Copyright (c) 2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+# Copyright (c) 2016-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
- # Copyright (c) 2016 Red Hat, Inc.
- #
- # This library is free software; you can redistribute it and/or
-@@ -22,6 +22,7 @@
- 
- EXTRA_DIST = \
- 	en.xml \
-+	en_ascii.xml \
- 	$(NULL)
- 
- -include $(top_srcdir)/git.mk
-diff --git a/data/annotations/en_ascii.xml b/data/annotations/en_ascii.xml
-new file mode 100644
-index 0000000..f1bdcbb
---- /dev/null
-+++ b/data/annotations/en_ascii.xml
-@@ -0,0 +1,56 @@
-+<?xml version='1.0' encoding='UTF-8' ?>
-+<!DOCTYPE ldml SYSTEM '../../common/dtd/ldml.dtd'>
-+<!-- vim:set et sts=4: -->
-+<!--
-+    Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+    Copyright (C) 2017 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
-+ -->
-+<ldml>
-+    <identity>
-+        <version number="$Revision: 1 $"/>
-+        <language type='en'/>
-+    </identity>
-+    <annotations>
-+        <annotation cp="😂">:') | :'-)</annotation>
-+        <annotation cp="😃">:D | :-D | =D</annotation>
-+        <annotation cp="😅">':) | ':-) | '=) | ':D | ':-D | '=D</annotation>
-+        <annotation cp="😆">&gt;:) | &gt;;) | &gt;:-) | &gt;=)</annotation>
-+        <annotation cp="😉">;) | ;-) | *-) | *) | ;-] | ;] | ;D | ;^)</annotation>
-+        <annotation cp="😎">B-) | B) | 8) | 8-) | B-D | 8-D</annotation>
-+        <annotation cp="😘">:* | :-* | =* | :^*</annotation>
-+        <annotation cp="🙂">:) | :-) | =] | =) | :]</annotation>
-+        <annotation cp="😑">-_- | -__- | -___-</annotation>
-+        <annotation cp="😶">:-X | :X | :-# | :# | =X | =x | :x | :-x | =#</annotation>
-+        <annotation cp="😣">&gt;.&lt;</annotation>
-+        <annotation cp="😮">:-O | :O | :-o | :o | O_O | &gt;:O</annotation>
-+        <annotation cp="😛">:P | :-P | =P | :-p | :p | =p | :-Þ | :Þ | :þ | :-þ | :-b | :b | d:</annotation>
-+        <annotation cp="😜">&gt;:P | X-P | x-p</annotation>
-+        <annotation cp="😓">':( | ':-( | '=(</annotation>
-+        <annotation cp="😕">&gt;:\ | &gt;:/ | :-/ | :-. | :/ | :\ | =/ | =\ | :L | =L</annotation>
-+        <annotation cp="😞">&gt;:[ | :-( | :( | :-[ | :[ | =(</annotation>
-+        <annotation cp="😢">:'( | :'-( | ;( | ;-(</annotation>
-+        <annotation cp="😨">D:</annotation>
-+        <annotation cp="😳">:$ | =$</annotation>
-+        <annotation cp="😵">#-) | #) | %-) | %) | X) | X-)</annotation>
-+        <annotation cp="😠">&gt;:( | &gt;:-( | :@</annotation>
-+        <annotation cp="😇">O:-) | 0:-3 | 0:3 | 0:-) | 0:) | 0;^) | O:) | O;-) | O=) | 0;-) | O:-3 | O:3</annotation>
-+        <annotation cp="🙆">*\0/* | \0/ | *\O/* | \O/</annotation>
-+        <annotation cp="❤">&lt;3</annotation>
-+        <annotation cp="💔">&lt;/3</annotation>
-+    </annotations>
-+</ldml>
-diff --git a/po/POTFILES.in b/po/POTFILES.in
-index 25be4f4..00f7c7f 100644
---- a/po/POTFILES.in
-+++ b/po/POTFILES.in
-@@ -41,6 +41,7 @@ setup/keyboardshortcut.py
- setup/main.py
- src/ibusbus.c
- src/ibusconfig.c
-+src/ibusemojigen.h
- src/ibusengine.c
- src/ibusfactory.c
- src/ibushotkey.c
-@@ -57,6 +58,7 @@ ui/gtk3/application.vala
- ui/gtk3/candidatearea.vala
- ui/gtk3/candidatepanel.vala
- ui/gtk3/emojier.vala
-+ui/gtk3/emojierapp.vala
- ui/gtk3/handle.vala
- ui/gtk3/iconwidget.vala
- ui/gtk3/keybindingmanager.vala
-@@ -66,4 +68,3 @@ ui/gtk3/property.vala
- ui/gtk3/propertypanel.vala
- ui/gtk3/separator.vala
- ui/gtk3/switcher.vala
--ui/gtk3/emojierapp.vala
-diff --git a/po/POTFILES.skip b/po/POTFILES.skip
-index 891d2cc..7190221 100644
---- a/po/POTFILES.skip
-+++ b/po/POTFILES.skip
-@@ -12,6 +12,7 @@ ui/gtk3/application.c
- ui/gtk3/candidatearea.c
- ui/gtk3/candidatepanel.c
- ui/gtk3/emojier.c
-+ui/gtk3/emojierapp.c
- ui/gtk3/handle.c
- ui/gtk3/iconwidget.c
- ui/gtk3/keybindingmanager.c
-@@ -21,4 +22,3 @@ ui/gtk3/property.c
- ui/gtk3/propertypanel.c
- ui/gtk3/separator.c
- ui/gtk3/switcher.c
--ui/gtk3/emojierapp.c
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 7053e3e..27cd168 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -166,6 +166,7 @@ ibusinclude_HEADERS =       \
- ibus_private_headers =          \
-     gtkimcontextsimpleseqs.h    \
-     ibuscomposetable.h          \
-+    ibusemojigen.h              \
-     ibusenginesimpleprivate.h   \
-     ibusinternal.h              \
-     keyname-table.h             \
-@@ -247,17 +248,26 @@ dicts/emoji-en.dict: emoji-parser
- 	    echo "WARNING: Not found $(EMOJI_ANNOTATION_DIR)/en.xml" 1>&2; \
- 	fi; \
- 	for f in $(LANG_FILES) ; do \
-+	    if test -f \
-+	    "$(EMOJI_ANNOTATION_DIR)/../annotationsDerived/$$f.xml" ; then \
-+	        xml_derived_option="--xml-derived $(EMOJI_ANNOTATION_DIR)/../annotationsDerived/$$f.xml"; \
-+	        plus_comment="derived"; \
-+	    fi; \
- 	    if test x"$$f" = xen ; then \
- 	        $(builddir)/emoji-parser \
-+	            --unicode-emoji-dir $(UNICODE_EMOJI_DIR) \
- 	            --xml $(EMOJI_ANNOTATION_DIR)/$$f.xml \
--	            --json $(EMOJI_JSON_FILE) \
-+	            $$xml_derived_option \
-+	            --xml-ascii $(top_srcdir)/data/annotations/en_ascii.xml \
-+	            --out-category ibusemojigen.h \
- 	            --out $@; \
- 	    else \
- 	        $(builddir)/emoji-parser \
- 	            --xml $(EMOJI_ANNOTATION_DIR)/$$f.xml \
-+	            $$xml_derived_option \
- 	            --out dicts/emoji-$$f.dict; \
- 	    fi; \
--	    echo "Generated dicts/emoji-$$f.dict"; \
-+	    echo "Generated $$plus_comment dicts/emoji-$$f.dict"; \
- 	done
- 
- install-data-hook: $(dict_DATA)
-@@ -301,11 +311,9 @@ emoji_parser_SOURCES =          \
-     $(NULL)
- emoji_parser_CFLAGS =           \
-     $(GLIB2_CFLAGS)             \
--    $(JSON_GLIB1_CFLAGS)        \
-     $(NULL)
- emoji_parser_LDADD =            \
-     $(GLIB2_LIBS)               \
--    $(JSON_GLIB1_LIBS)          \
-     $(libibus)                  \
-     $(NULL)
- 
-@@ -329,6 +337,7 @@ CLEANFILES +=                   \
-     $(NULL)
- 
- DISTCLEANFILES =                \
-+    ibusemojigen.h              \
-     ibusversion.h               \
-     $(NULL)
- 
-diff --git a/src/emoji-parser.c b/src/emoji-parser.c
-index e5dce3f..5e6155b 100644
---- a/src/emoji-parser.c
-+++ b/src/emoji-parser.c
-@@ -21,34 +21,52 @@
-  */
- 
- /* Convert /usr/share/unicode/cldr/common/annotations/\*.xml and
-- * /usr/lib/node_modules/emojione/emoji.json
-+ * /usr/share/unicode/emoji/emoji-test.txt
-  * to the dictionary file which look up the Emoji from the annotation.
-  * Get *.xml from https://github.com/fujiwarat/cldr-emoji-annotation
-  * or http://www.unicode.org/repos/cldr/trunk/common/annotations .
-- * 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".
-+ * Get emoji-test.txt from http://unicode.org/Public/emoji/4.0/ .
-+ * en.xml is used for the Unicode annotations and emoji-test.txt is used
-+ * for the category, e.g. "Smileys & People".
-+ * ASCII emoji annotations are saved in ../data/annotations/en_ascii.xml
-  */
- 
- #include <glib.h>
-+
-+#ifdef HAVE_JSON_GLIB1
- #include <json-glib/json-glib.h>
-+#endif
- 
- #include <string.h>
- 
- #include "ibusemoji.h"
- 
-+/* This file has 21 lines about the license at the top of the file. */
-+#define LICENSE_LINES 21
-+
-+typedef enum {
-+  EMOJI_STRICT,
-+  EMOJI_VARIANT,
-+  EMOJI_NOVARIANT
-+} EmojiDataSearchType;
-+
- typedef struct _EmojiData EmojiData;
- struct _EmojiData {
--    gchar      *emoji;
--    gchar      *emoji_alternates;
--    GSList     *annotations;
--    gboolean    is_annotation;
--    gchar      *description;
--    gboolean    is_tts;
--    gchar      *category;
--    GSList     *list;
-+    gchar              *emoji;
-+    gchar              *emoji_alternates;
-+    GSList             *annotations;
-+    gboolean            is_annotation;
-+    gchar              *description;
-+    gboolean            is_tts;
-+    gchar              *category;
-+    gchar              *subcategory;
-+    gboolean            is_derived;
-+    GSList             *list;
-+    EmojiDataSearchType search_type;
- };
- 
-+static gchar *unicode_emoji_version;
-+
- static void
- reset_emoji_element (EmojiData *data)
- {
-@@ -59,68 +77,175 @@ reset_emoji_element (EmojiData *data)
-     g_slist_free_full (data->annotations, g_free);
-     data->annotations = NULL;
-     g_clear_pointer (&data->description, g_free);
--    g_clear_pointer (&data->category, g_free);
-+}
-+
-+gint
-+strcmp_novariant (const gchar *a,
-+                  const gchar *b,
-+                  gunichar     a_variant,
-+                  gunichar     b_variant)
-+{
-+    gint retval;
-+    gchar *p = NULL;
-+    GString *buff = NULL;;
-+    gchar *substr = NULL;
-+
-+    if (a_variant > 0) {
-+        if ((p = g_utf8_strchr (a, -1, a_variant)) != NULL) {
-+            buff = g_string_new (NULL);
-+            if (a != p) {
-+                substr = g_strndup (a, p - a);
-+                g_string_append (buff, substr);
-+                g_free (substr);
-+            }
-+            p = g_utf8_next_char (p);
-+            if (*p != '\0')
-+                g_string_append (buff, p);
-+            retval = g_strcmp0 (buff->str, b);
-+            g_string_free (buff, TRUE);
-+            return retval;
-+        } else {
-+            return -1;
-+        }
-+    } else if (b_variant > 0) {
-+        if ((p = g_utf8_strchr (b, -1, b_variant)) != NULL) {
-+            buff = g_string_new (NULL);
-+            if (b != p) {
-+                substr = g_strndup (b, p - b);
-+                g_string_append (buff, substr);
-+                g_free (substr);
-+            }
-+            p = g_utf8_next_char (p);
-+            if (*p != '\0')
-+                g_string_append (buff, p);
-+            retval = g_strcmp0 (a, buff->str);
-+            g_string_free (buff, TRUE);
-+            return retval;
-+        } else {
-+            return -1;
-+        }
-+    }
-+    return g_strcmp0 (a, b);
- }
- 
- gint
- find_emoji_data_list (IBusEmojiData *a,
--                      const gchar   *b)
-+                      EmojiData     *b)
- {
--   g_return_val_if_fail (IBUS_IS_EMOJI_DATA (a), 0);
--   return g_strcmp0 (ibus_emoji_data_get_emoji (a), b);
-+    const gchar *a_str;
-+
-+    g_return_val_if_fail (IBUS_IS_EMOJI_DATA (a), 0);
-+
-+    a_str = ibus_emoji_data_get_emoji (a);
-+    switch (b->search_type) {
-+    case EMOJI_VARIANT:
-+        if (strcmp_novariant (a_str, b->emoji, 0xfe0e, 0) == 0)
-+            return 0;
-+        else if (strcmp_novariant (a_str, b->emoji, 0xfe0f, 0) == 0)
-+            return 0;
-+        else
-+            return g_strcmp0 (a_str, b->emoji);
-+        break;
-+    case EMOJI_NOVARIANT:
-+        if (strcmp_novariant (a_str, b->emoji, 0, 0xfe0e) == 0)
-+            return 0;
-+        else if (strcmp_novariant (a_str, b->emoji, 0, 0xfe0f) == 0)
-+            return 0;
-+        else
-+            return g_strcmp0 (a_str, b->emoji);
-+        break;
-+    default:;
-+    }
-+    return g_strcmp0 (a_str, b->emoji);
- }
- 
- static void
--update_emoji_list (EmojiData *data)
-+emoji_data_update_object (EmojiData     *data,
-+                          IBusEmojiData *emoji)
- {
--    GSList *list = g_slist_find_custom (
--            data->list,
--            data->emoji,
--            (GCompareFunc) find_emoji_data_list);
--    if (list) {
--        IBusEmojiData *emoji = list->data;
--        GSList *src_annotations = data->annotations;
--        GSList *dest_annotations = ibus_emoji_data_get_annotations (emoji);
--        GSList *l;
--        gboolean updated_annotations = FALSE;
--        for (l = src_annotations; l; l = l->next) {
--            GSList *duplicated = g_slist_find_custom (dest_annotations,
--                                                      l->data,
--                                                      (GCompareFunc) g_strcmp0);
--            if (duplicated == NULL) {
--                dest_annotations = g_slist_append (dest_annotations,
--                                                   g_strdup (l->data));
--                updated_annotations = TRUE;
--            }
-+    GSList *src_annotations = data->annotations;
-+    GSList *dest_annotations = ibus_emoji_data_get_annotations (emoji);
-+    GSList *l;
-+    gboolean updated_annotations = FALSE;
-+    for (l = src_annotations; l; l = l->next) {
-+        GSList *duplicated = g_slist_find_custom (dest_annotations,
-+                                                  l->data,
-+                                                  (GCompareFunc) g_strcmp0);
-+        if (duplicated == NULL) {
-+            dest_annotations = g_slist_append (dest_annotations,
-+                                               g_strdup (l->data));
-+            updated_annotations = TRUE;
-         }
--        if (updated_annotations) {
--            ibus_emoji_data_set_annotations (
-+    }
-+    if (updated_annotations) {
-+        ibus_emoji_data_set_annotations (
-                     emoji,
-                     g_slist_copy_deep (dest_annotations,
-                                        (GCopyFunc) g_strdup,
-                                        NULL));
-+    }
-+    if (data->description)
-+        ibus_emoji_data_set_description (emoji, data->description);
-+}
-+
-+static void
-+emoji_data_new_object (EmojiData *data)
-+{
-+    IBusEmojiData *emoji =
-+            ibus_emoji_data_new ("emoji",
-+                                 data->emoji,
-+                                 "annotations",
-+                                 data->annotations,
-+                                 "description",
-+                                 data->description ? data->description
-+                                         : g_strdup (""),
-+                                 "category",
-+                                 data->category ? data->category
-+                                         : g_strdup (""),
-+                                 NULL);
-+    data->list = g_slist_append (data->list, emoji);
-+}
-+
-+static void
-+update_emoji_list (EmojiData *data,
-+                   gboolean   base_update)
-+{
-+    GSList *list;
-+    data->search_type = EMOJI_STRICT;
-+    list = g_slist_find_custom (
-+            data->list,
-+            data,
-+            (GCompareFunc) find_emoji_data_list);
-+    if (list) {
-+        emoji_data_update_object (data, list->data);
-+        return;
-+    } else if (base_update) {
-+        emoji_data_new_object (data);
-+        return;
-+    }
-+    if (g_utf8_strchr (data->emoji, -1, 0xfe0e) == NULL &&
-+        g_utf8_strchr (data->emoji, -1, 0xfe0f) == NULL) {
-+        data->search_type = EMOJI_VARIANT;
-+        list = g_slist_find_custom (
-+                data->list,
-+                data,
-+                (GCompareFunc) find_emoji_data_list);
-+        if (list) {
-+            emoji_data_update_object (data, list->data);
-+            return;
-         }
--        if (data->description)
--            ibus_emoji_data_set_description (emoji, data->description);
-     } else {
--        IBusEmojiData *emoji =
--                ibus_emoji_data_new ("emoji",
--                                     data->emoji,
--                                     "annotations",
--                                     data->annotations,
--                                     "description",
--                                     data->description ? data->description
--                                             : g_strdup (""),
--                                     "category",
--                                     data->category ? data->category
--                                             : g_strdup (""),
--                                     "emoji-alternates",
--                                     data->emoji_alternates
--                                             ? data->emoji_alternates
--                                             : g_strdup (""),
--                                     NULL);
--        data->list = g_slist_append (data->list, emoji);
-+        data->search_type = EMOJI_NOVARIANT;
-+        list = g_slist_find_custom (
-+                data->list,
-+                data,
-+                (GCompareFunc) find_emoji_data_list);
-+        if (list) {
-+            emoji_data_update_object (data, list->data);
-+            return;
-+        }
-     }
-+    emoji_data_new_object (data);
- }
- 
- static void
-@@ -183,7 +308,7 @@ unicode_annotations_end_element_cb (GMarkupParseContext *context,
-     if (!data->is_annotation)
-         return;
- 
--    update_emoji_list (data);
-+    update_emoji_list (data, FALSE);
-     data->is_annotation = FALSE;
-     data->is_tts = FALSE;
- }
-@@ -227,7 +352,8 @@ unicode_annotations_text_cb (GMarkupParseContext *context,
- 
- static gboolean
- unicode_annotations_parse_xml_file (const gchar  *filename,
--                                    GSList      **list)
-+                                    GSList      **list,
-+                                    gboolean      is_derived)
- {
-     gchar *content = NULL;
-     gsize length = 0;
-@@ -251,6 +377,7 @@ unicode_annotations_parse_xml_file (const gchar  *filename,
-     }
- 
-     data.list = *list;
-+    data.is_derived = is_derived;
- 
-     context = g_markup_parse_context_new (&parser, 0, &data, NULL);
-     if (!g_markup_parse_context_parse (context, content, length, &error)) {
-@@ -276,6 +403,233 @@ failed_to_parse_unicode_annotations:
- }
- 
- static gboolean
-+unicode_emoji_test_parse_unicode (const gchar *line,
-+                                  EmojiData   *data)
-+{
-+    GString *emoji = NULL;
-+    gchar *endptr = NULL;
-+    guint32 uch;
-+    static gchar outbuf[8] = { 0, };
-+
-+    g_return_val_if_fail (line != NULL, FALSE);
-+
-+    emoji = g_string_new (NULL);
-+    while (line && *line) {
-+        uch = g_ascii_strtoull (line, &endptr, 16);
-+        outbuf[g_unichar_to_utf8 (uch, outbuf)] = '\0';
-+        g_string_append (emoji, outbuf);
-+        if (*endptr == '\0') {
-+            break;
-+        }
-+        line = endptr + 1;
-+        while (*line == ' ')
-+            line++;
-+        endptr = NULL;
-+    }
-+
-+    data->emoji = g_string_free (emoji, FALSE);
-+    return TRUE;
-+}
-+
-+static gboolean
-+unicode_emoji_test_parse_description (const gchar *line,
-+                                      EmojiData   *data)
-+{
-+    g_return_val_if_fail (line != NULL, FALSE);
-+
-+    /* skip spaces */
-+    while (*line == ' ')
-+        line++;
-+    /* skip emoji */
-+    while (*line != ' ')
-+        line++;
-+    /* skip spaces */
-+    while (*line == ' ')
-+        line++;
-+    if (*line == '\0')
-+        return FALSE;
-+    data->description = g_strdup (line);
-+    return TRUE;
-+}
-+
-+#define EMOJI_VERSION_TAG "# Version: "
-+#define EMOJI_GROUP_TAG "# group: "
-+#define EMOJI_SUBGROUP_TAG "# subgroup: "
-+#define EMOJI_NON_FULLY_QUALIFIED_TAG "non-fully-qualified"
-+
-+static gboolean
-+unicode_emoji_test_parse_line (const gchar *line,
-+                               EmojiData   *data)
-+{
-+    int tag_length;
-+    gchar **segments = NULL;
-+
-+    g_return_val_if_fail (line != NULL, FALSE);
-+
-+    tag_length = strlen (EMOJI_VERSION_TAG);
-+    if (strlen (line) > tag_length &&
-+        g_ascii_strncasecmp (line, EMOJI_VERSION_TAG, tag_length) == 0) {
-+        unicode_emoji_version = g_strdup (line + tag_length);
-+        return TRUE;
-+    }
-+    tag_length = strlen (EMOJI_GROUP_TAG);
-+    if (strlen (line) > tag_length &&
-+        g_ascii_strncasecmp (line, EMOJI_GROUP_TAG, tag_length) == 0) {
-+        g_free (data->category);
-+        g_clear_pointer (&data->subcategory, g_free);
-+        data->category = g_strdup (line + tag_length);
-+        return TRUE;
-+    }
-+    tag_length = strlen (EMOJI_SUBGROUP_TAG);
-+    if (strlen (line) > tag_length &&
-+        g_ascii_strncasecmp (line, EMOJI_SUBGROUP_TAG, tag_length) == 0) {
-+        g_free (data->subcategory);
-+        data->subcategory = g_strdup (line + tag_length);
-+        return TRUE;
-+    }
-+    if (*line == '#')
-+        return TRUE;
-+    segments = g_strsplit (line, "; ", 2);
-+    if (segments[1] == NULL) {
-+        g_warning ("No qualified line\n");
-+        goto failed_to_parse_unicode_emoji_test_line;
-+        return FALSE;
-+    }
-+    tag_length = strlen (EMOJI_NON_FULLY_QUALIFIED_TAG);
-+    /* Ignore the non-fully-qualified emoji */
-+    if (g_ascii_strncasecmp (segments[1], EMOJI_NON_FULLY_QUALIFIED_TAG,
-+                             tag_length) == 0) {
-+        g_strfreev (segments);
-+        return TRUE;
-+    }
-+    unicode_emoji_test_parse_unicode (segments[0], data);
-+    g_strfreev (segments);
-+    segments = g_strsplit (line, "# ", 2);
-+    if (segments[1] == NULL) {
-+        g_warning ("No description line\n");
-+        goto failed_to_parse_unicode_emoji_test_line;
-+        return FALSE;
-+    }
-+    unicode_emoji_test_parse_description (segments[1], data);
-+    g_strfreev (segments);
-+    if (data->annotations == NULL) {
-+        if (data->subcategory) {
-+            int i;
-+            gchar *amp;
-+            segments = g_strsplit(data->subcategory, "-", -1);
-+            for (i = 0; segments[i]; i++) {
-+                if ((amp = strchr (segments[i], '&')) != NULL) {
-+                    if (amp - segments[i] <= 1) {
-+                        g_warning ("Wrong ampersand");
-+                        goto failed_to_parse_unicode_emoji_test_line;
-+                    }
-+                    data->annotations = g_slist_append (
-+                            data->annotations,
-+                            g_strndup (segments[i], amp - segments[i] - 1));
-+                    data->annotations = g_slist_append (
-+                            data->annotations,
-+                            g_strdup (amp + 1));
-+                    continue;
-+                }
-+                data->annotations = g_slist_append (data->annotations,
-+                                                    g_strdup (segments[i]));
-+            }
-+            g_strfreev (segments);
-+        } else {
-+            g_warning ("No subcategory line\n");
-+            goto failed_to_parse_unicode_emoji_test_line;
-+        }
-+    }
-+    update_emoji_list (data, TRUE);
-+    reset_emoji_element (data);
-+    return TRUE;
-+
-+failed_to_parse_unicode_emoji_test_line:
-+    if (segments)
-+        g_strfreev (segments);
-+    reset_emoji_element (data);
-+    return FALSE;
-+}
-+
-+#undef EMOJI_VERSION_TAG
-+#undef EMOJI_GROUP_TAG
-+#undef EMOJI_SUBGROUP_TAG
-+#undef EMOJI_NON_FULLY_QUALIFIED_TAG
-+
-+static gboolean
-+unicode_emoji_test_parse_file (const gchar *filename,
-+                               GSList      **list)
-+{
-+    gchar *content = NULL;
-+    gsize length = 0;
-+    GError *error = NULL;
-+    gchar *head, *end, *line;
-+    int n = 1;
-+    EmojiData data = { 0, };
-+
-+    g_return_val_if_fail (filename != NULL, FALSE);
-+    g_return_val_if_fail (list != NULL, FALSE);
-+
-+    if (!g_file_get_contents (filename, &content, &length, &error)) {
-+        g_warning ("Failed to load %s: %s", filename, error->message);
-+        goto failed_to_parse_unicode_emoji_test;
-+    }
-+    head = end = content;
-+    while (*end == '\n' && end - content < length) {
-+        end++;
-+        n++;
-+    }
-+    head = end;
-+    data.list = *list;
-+    while (end - content < length) {
-+        while (*end != '\n' && end - content < length)
-+            end++;
-+        if (end - content >= length)
-+            break;
-+        line = g_strndup (head, end - head);
-+        if (!unicode_emoji_test_parse_line (line, &data))
-+            g_warning ("parse error #%d in %s version %s: %s",
-+                       n, filename,
-+                       unicode_emoji_version ? unicode_emoji_version : "(null)",
-+                       line);
-+        while (*end == '\n' && end - content < length) {
-+            end++;
-+            n++;
-+        }
-+        g_free (line);
-+        head = end;
-+    }
-+    g_free (content);
-+    g_free (unicode_emoji_version);
-+    *list = data.list;
-+    return TRUE;
-+
-+failed_to_parse_unicode_emoji_test:
-+    if (error)
-+        g_error_free (error);
-+    g_clear_pointer (&content, g_free);
-+    return FALSE;
-+}
-+
-+static gboolean 
-+unicode_emoji_parse_dir (const gchar *dirname,
-+                         GSList      **list)
-+{
-+    gchar *filename = NULL;
-+    g_return_val_if_fail (dirname != NULL, FALSE);
-+    g_return_val_if_fail (list != NULL, FALSE);
-+
-+    filename = g_build_path ("/", dirname, "emoji-test.txt", NULL);
-+    if (!unicode_emoji_test_parse_file (filename, list)) {
-+        g_free (filename);
-+        return FALSE;
-+    }
-+    g_free (filename);
-+    return TRUE;
-+}
-+
-+#ifdef HAVE_JSON_GLIB1
-+static gboolean
- parse_emojione_unicode (JsonNode  *node,
-                         EmojiData *data,
-                         gboolean   is_alternates)
-@@ -426,6 +780,31 @@ parse_emojione_category (JsonNode  *node,
-     return TRUE;
- }
- 
-+#ifdef EMOJIONE_ALIASES_ASCII_PRINT
-+static gchar *
-+text_to_entity (const gchar *text)
-+{
-+    gchar *p;
-+    GString *buff = g_string_new (NULL);
-+    for (p = text; *p; p++) {
-+        switch (*p) {
-+        case '<':
-+            g_string_append (buff, "&lt;");
-+            break;
-+        case '>':
-+            g_string_append (buff, "&gt;");
-+            break;
-+        case '&':
-+            g_string_append (buff, "&amp;");
-+            break;
-+        default:
-+            g_string_append_c (buff, *p);
-+        }
-+    }
-+    g_string_free (buff, FALSE);
-+}
-+#endif
-+
- static gboolean
- parse_emojione_aliases_ascii (JsonNode  *node,
-                               EmojiData *data)
-@@ -441,11 +820,23 @@ parse_emojione_aliases_ascii (JsonNode  *node,
-     aliases_ascii = json_node_get_array (node);
-     length = json_array_get_length (aliases_ascii);
-     for (i = 0; i < length; i++) {
-+#ifdef EMOJIONE_ALIASES_ASCII_PRINT
-+        if (i == 0)
-+            printf ("        <annotation cp=\"%s\">", data->emoji);
-+#endif
-         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) {
-+#ifdef EMOJIONE_ALIASES_ASCII_PRINT
-+            gchar *entity = text_to_entity (alias);
-+            if (i != length - 1)
-+                printf ("%s | ", entity);
-+            else
-+                printf ("%s</annotation>\n", entity);
-+            g_free (entity);
-+#endif
-             data->annotations = g_slist_prepend (data->annotations,
-                                                  g_strdup (alias));
-         }
-@@ -535,7 +926,7 @@ parse_emojione_element (JsonNode  *node,
-     }
-     g_list_free (members);
- 
--    update_emoji_list (data);
-+    update_emoji_list (data, TRUE);
- 
-     return TRUE;
- }
-@@ -591,27 +982,136 @@ fail_to_json_file:
-     g_object_unref (parser);
-     return FALSE;
- }
-+#endif /* HAVE_JSON_GLIB1 */
-+
-+static void
-+emoji_data_list_unify_categories (IBusEmojiData  *data,
-+                                  GSList        **list)
-+{
-+    g_return_if_fail (IBUS_IS_EMOJI_DATA (data));
-+    g_return_if_fail (list != NULL);
-+
-+    const gchar *category = ibus_emoji_data_get_category (data);
-+    if (*category == '\0')
-+        return;
-+    if (g_slist_find_custom (*list, category, (GCompareFunc)g_strcmp0) == NULL)
-+        *list = g_slist_append (*list, g_strdup (category));
-+}
-+
-+static void
-+category_list_dump (const gchar *category,
-+                    GString     *buff)
-+{
-+    g_return_if_fail (buff != NULL);
-+
-+    const gchar *line = g_strdup_printf ("    N_(\"%s\"),\n", category);
-+    g_string_append (buff, line);
-+}
-+
-+static void
-+category_file_save (const gchar *filename,
-+                    GSList      *list)
-+{
-+    gchar *content = NULL;
-+    gsize length = 0;
-+    GError *error = NULL;
-+    gchar *p;
-+    GString *buff = NULL;
-+    int i;
-+    GSList *list_categories = NULL;
-+
-+    g_return_if_fail (filename != NULL);
-+    g_return_if_fail (list != NULL);
-+
-+    g_slist_foreach (list, (GFunc)emoji_data_list_unify_categories, &list_categories);
-+    if (list_categories == NULL) {
-+        g_warning ("Not found categories in IBusEmojiData list");
-+        return;
-+    }
-+
-+    if (!g_file_get_contents (__FILE__, &content, &length, &error)) {
-+        g_warning ("Failed to load %s: %s", __FILE__, error->message);
-+        g_clear_pointer (&error, g_error_free);
-+    }
-+    buff = g_string_new (NULL);
-+    p = content;
-+    for (i = 0; i < LICENSE_LINES; i++, p++) {
-+        if ((p = strchr (p, '\n')) == NULL)
-+            break;
-+    }
-+    if (p != NULL) {
-+        g_string_append (buff, g_strndup (content, p - content));
-+        g_string_append_c (buff, '\n');
-+    }
-+    g_clear_pointer (&content, g_free);
-+
-+    g_string_append (buff, g_strdup ("\n"));
-+    g_string_append (buff, g_strdup_printf ("/* This file is generated by %s. */", __FILE__));
-+    g_string_append (buff, g_strdup ("\n"));
-+    g_string_append (buff, g_strdup ("include <glib/gi18n.h>\n"));
-+    g_string_append (buff, g_strdup ("\n"));
-+    g_string_append (buff, g_strdup ("#ifndef __IBUS_EMOJI_GEN_H_\n"));
-+    g_string_append (buff, g_strdup ("#define __IBUS_EMOJI_GEN_H_\n"));
-+    g_string_append (buff, g_strdup ("const static char *unicode_emoji_categories[] = {\n"));
-+    list_categories = g_slist_sort (list_categories, (GCompareFunc)g_strcmp0);
-+    g_slist_foreach (list_categories, (GFunc)category_list_dump, buff);
-+    g_slist_free (list_categories);
-+    g_string_append (buff, g_strdup ("};\n"));
-+    g_string_append (buff, g_strdup ("#endif\n"));
-+
-+    if (!g_file_set_contents (filename, buff->str, -1, &error)) {
-+        g_warning ("Failed to save emoji category file %s: %s", filename, error->message);
-+        g_error_free (error);
-+    }
-+
-+    g_string_free (buff, TRUE);
-+}
- 
- int
- main (int argc, char *argv[])
- {
-     gchar *prgname;
-+#ifdef HAVE_JSON_GLIB1
-     gchar *json_file = NULL;
-+#endif
-+    gchar *emoji_dir = NULL;
-     gchar *xml_file = NULL;
-+    gchar *xml_derived_file = NULL;
-+    gchar *xml_ascii_file = NULL;
-     gchar *output = NULL;
-+    gchar *output_category = NULL;
-     GOptionEntry     entries[] = {
-+#ifdef HAVE_JSON_GLIB1
-         { "json", 'j', 0, G_OPTION_ARG_STRING, &json_file,
-           "Parse Emoji One JSON file",
-           "JSON"
-         },
-+#endif
-+        { "unicode-emoji-dir", 'd', 0, G_OPTION_ARG_STRING, &emoji_dir,
-+          "Parse Emoji files in DIRECTORY which includes emoji-test.txt " \
-+          "emoji-sequences.txt emoji-zwj-sequences.txt in unicode.org",
-+          "DIRECTORY"
-+        },
-         { "out", 'o', 0, G_OPTION_ARG_STRING, &output,
-           "Save the emoji data as FILE",
-           "FILE"
-         },
-+        { "out-category", 'C', 0, G_OPTION_ARG_STRING, &output_category,
-+          "Save the translatable categories as FILE",
-+          "FILE"
-+        },
-         { "xml", 'x', 0, G_OPTION_ARG_STRING, &xml_file,
-           "Parse Unocode.org ANNOTATIONS file",
-           "ANNOTATIONS"
-         },
-+        { "xml-derived", 'X', 0, G_OPTION_ARG_STRING, &xml_derived_file,
-+          "Parse Unocode.org derived ANNOTATIONS file",
-+          "ANNOTATIONS"
-+        },
-+        { "xml-ascii", 'A', 0, G_OPTION_ARG_STRING, &xml_ascii_file,
-+          "Parse ASCII ANNOTATIONS file",
-+          "ANNOTATIONS"
-+        },
-         { NULL }
-     };
-     GOptionContext *context;
-@@ -638,12 +1138,22 @@ main (int argc, char *argv[])
-     }
-     g_option_context_free (context);
- 
-+#ifdef HAVE_JSON_GLIB1
-     if (json_file)
-         emojione_parse_json_file (json_file, &list);
-+#endif
-+    if (emoji_dir)
-+        unicode_emoji_parse_dir (emoji_dir, &list);
-     if (xml_file)
--        unicode_annotations_parse_xml_file (xml_file, &list);
-+        unicode_annotations_parse_xml_file (xml_file, &list, FALSE);
-+    if (xml_derived_file)
-+        unicode_annotations_parse_xml_file (xml_derived_file, &list, TRUE);
-+    if (xml_ascii_file)
-+        unicode_annotations_parse_xml_file (xml_ascii_file, &list, FALSE);
-     if (list != NULL && output)
-         ibus_emoji_data_save (output, list);
-+    if (list != NULL && output_category)
-+        category_file_save (output_category, list);
-     if (list)
-         g_slist_free (list);
- 
-diff --git a/src/ibusemoji.c b/src/ibusemoji.c
-index 4be092a..d2e16c5 100644
---- a/src/ibusemoji.c
-+++ b/src/ibusemoji.c
-@@ -29,7 +29,7 @@
- #include "ibusinternal.h"
- 
- #define IBUS_EMOJI_DATA_MAGIC "IBusEmojiData"
--#define IBUS_EMOJI_DATA_VERSION (3)
-+#define IBUS_EMOJI_DATA_VERSION (4)
- 
- enum {
-     PROP_0 = 0,
-@@ -37,7 +37,6 @@ enum {
-     PROP_ANNOTATIONS,
-     PROP_DESCRIPTION,
-     PROP_CATEGORY,
--    PROP_EMOJI_ALTERNATES
- };
- 
- struct _IBusEmojiDataPrivate {
-@@ -45,7 +44,6 @@ struct _IBusEmojiDataPrivate {
-     GSList     *annotations;
-     gchar      *description;
-     gchar      *category;
--    gchar      *emoji_alternates;
- };
- 
- #define IBUS_EMOJI_DATA_GET_PRIVATE(o)  \
-@@ -108,7 +106,7 @@ ibus_emoji_data_class_init (IBusEmojiDataClass *class)
-                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
- 
-     /**
--     * IBusEmojiData:annotations:
-+     * IBusEmojiData:annotations: (transfer container) (element-type utf8):
-      *
-      * The emoji annotations
-      */
-@@ -117,7 +115,7 @@ ibus_emoji_data_class_init (IBusEmojiDataClass *class)
-                     g_param_spec_pointer ("annotations",
-                         "emoji annotations",
-                         "The emoji annotation list",
--                        G_PARAM_READWRITE));
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
- 
-     /**
-      * IBusEmojiData:description:
-@@ -130,7 +128,7 @@ ibus_emoji_data_class_init (IBusEmojiDataClass *class)
-                         "emoji description",
-                         "The emoji description",
-                         "",
--                        G_PARAM_READWRITE));
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
- 
-     /**
-      * IBusEmojiData:category:
-@@ -144,19 +142,6 @@ ibus_emoji_data_class_init (IBusEmojiDataClass *class)
-                         "The emoji category",
-                         "",
-                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
--
--    /**
--     * IBusEmojiData:emoji_alternates:
--     *
--     * The emoji alternate characters
--     */
--    g_object_class_install_property (gobject_class,
--                    PROP_EMOJI_ALTERNATES,
--                    g_param_spec_string ("emoji-alternates",
--                        "emoji alternate charasters",
--                        "The emoji alternate characters UTF-8",
--                        "",
--                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
- }
- 
- static void
-@@ -209,10 +194,6 @@ ibus_emoji_data_set_property (IBusEmojiData *emoji,
-         g_assert (emoji->priv->category == NULL);
-         emoji->priv->category = g_value_dup_string (value);
-         break;
--    case PROP_EMOJI_ALTERNATES:
--        g_assert (emoji->priv->emoji_alternates == NULL);
--        emoji->priv->emoji_alternates = g_value_dup_string (value);
--        break;
-     default:
-         G_OBJECT_WARN_INVALID_PROPERTY_ID (emoji, prop_id, pspec);
-     }
-@@ -240,9 +221,6 @@ ibus_emoji_data_get_property (IBusEmojiData *emoji,
-     case PROP_CATEGORY:
-         g_value_set_string (value, ibus_emoji_data_get_category (emoji));
-         break;
--    case PROP_EMOJI_ALTERNATES:
--        g_value_set_string (value, ibus_emoji_data_get_emoji_alternates(emoji));
--        break;
-     default:
-         G_OBJECT_WARN_INVALID_PROPERTY_ID (emoji, prop_id, pspec);
-     }
-@@ -270,8 +248,6 @@ ibus_emoji_data_serialize (IBusEmojiData   *emoji,
-     }
-     g_variant_builder_add (builder, "s", NOTNULL (emoji->priv->description));
-     g_variant_builder_add (builder, "s", NOTNULL (emoji->priv->category));
--    g_variant_builder_add (builder, "s",
--                           NOTNULL (emoji->priv->emoji_alternates));
- #undef NOTNULL
-     return TRUE;
- }
-@@ -303,10 +279,6 @@ ibus_emoji_data_deserialize (IBusEmojiData *emoji,
-                                      &emoji->priv->description);
-     ibus_g_variant_get_child_string (variant, retval++,
-                                      &emoji->priv->category);
--    if (g_variant_n_children (variant) < retval + 1)
--        return retval;
--    ibus_g_variant_get_child_string (variant, retval++,
--                                     &emoji->priv->emoji_alternates);
-     return retval;
- }
- 
-@@ -325,7 +297,6 @@ ibus_emoji_data_copy (IBusEmojiData       *dest,
-                                                       NULL);
-     dest->priv->description      = g_strdup (src->priv->description);
-     dest->priv->category         = g_strdup (src->priv->category);
--    dest->priv->emoji_alternates = g_strdup (src->priv->emoji_alternates);
-     return TRUE;
- }
- 
-@@ -345,7 +316,6 @@ ibus_emoji_data_new (const gchar *first_property_name, ...)
-     g_assert (emoji->priv->emoji != NULL);
-     g_assert (emoji->priv->description != NULL);
-     g_assert (emoji->priv->category != NULL);
--    g_assert (emoji->priv->emoji_alternates != NULL);
-     return emoji;
- }
- 
-@@ -402,15 +372,6 @@ ibus_emoji_data_get_category (IBusEmojiData *emoji)
-     return emoji->priv->category;
- }
- 
--const gchar *
--ibus_emoji_data_get_emoji_alternates (IBusEmojiData *emoji)
--{
--    g_return_val_if_fail (IBUS_IS_EMOJI_DATA (emoji), NULL);
--
--    return emoji->priv->emoji_alternates;
--}
--
--
- static void
- variant_foreach_add_emoji (IBusEmojiData   *emoji,
-                            GVariantBuilder *builder)
-diff --git a/src/ibusemoji.h b/src/ibusemoji.h
-index 233cadd..eb24fdd 100644
---- a/src/ibusemoji.h
-+++ b/src/ibusemoji.h
-@@ -156,20 +156,6 @@ const gchar *   ibus_emoji_data_get_category    (IBusEmojiData *emoji);
- 
- 
- /**
-- * ibus_emoji_data_get_emoji_alternates:
-- * @emoji : An #IBusEmojiData
-- *
-- * Gets the emoji alternate characters in #IBusEmojiData. It should not be
-- * freed. The alternates are defined in "unicode_alt" in EmojiOne json.
-- *
-- * Returns: emoji alternates property in #IBusEmojiData
-- *
-- */
--const gchar *   ibus_emoji_data_get_emoji_alternates
--                                                (IBusEmojiData *emoji);
--
--
--/**
-  * ibus_emoji_dict_save:
-  * @path: A path of the saved dictionary file.
-  * @dict: (element-type utf8 gpointer) (transfer none): An Emoji dictionary
-diff --git a/src/ibusemojigen.h b/src/ibusemojigen.h
-new file mode 100644
-index 0000000..74a6015
---- /dev/null
-+++ b/src/ibusemojigen.h
-@@ -0,0 +1,39 @@
-+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-+/* vim:set et sts=4: */
-+/* ibus - The Input Bus
-+ * Copyright (C) 2016-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2016 Red Hat, Inc.
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2.1 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-+ * USA
-+ */
-+
-+
-+/* This file is generated by emoji-parser.c. */
-+include <glib/gi18n.h>
-+
-+#ifndef __IBUS_EMOJI_GEN_H_
-+#define __IBUS_EMOJI_GEN_H_
-+const static char *unicode_emoji_categories[] = {
-+    N_("Activities"),
-+    N_("Animals & Nature"),
-+    N_("Flags"),
-+    N_("Food & Drink"),
-+    N_("Objects"),
-+    N_("Smileys & People"),
-+    N_("Symbols"),
-+    N_("Travel & Places"),
-+};
-+#endif
-diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
-index d2ae0d6..c557474 100644
---- a/ui/gtk3/Makefile.am
-+++ b/ui/gtk3/Makefile.am
-@@ -82,7 +82,7 @@ AM_VALAFLAGS = \
- 	$(NULL)
- 
- MAINTAINERCLEANFILES =
--CONFIG_CLEAN_FILES =
-+DISTCLEANFILES =
- noinst_DATA =
- 
- if ENABLE_LIBNOTIFY
-@@ -250,8 +250,7 @@ vapidir = $(datadir)/vala/vapi
- noinst_DATA += $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
- 
- MAINTAINERCLEANFILES += $(VAPIGEN_VAPIS)
--# for make distclean
--CONFIG_CLEAN_FILES += $(VAPIGEN_VAPIS)
-+DISTCLEANFILES += $(VAPIGEN_VAPIS)
- EXTRA_DIST += $(VAPIGEN_VAPIS)
- 
- endif
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 76dca6c..8f55bd4 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -167,6 +167,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
-     private const uint EMOJI_GRID_PAGE = 10;
-+    private const string EMOJI_CATEGORY_OTHERS = N_("Others");
- 
-     // Set the actual default values in the constructor
-     // because these fields are used for class_init() and static functions,
-@@ -372,8 +373,7 @@ class IBusEmojier : Gtk.Window {
- 
- 
-     private static void update_annotation_to_emojis_dict(IBus.EmojiData data) {
--        string emoji = (data.get_emoji_alternates() != "") ?
--                data.get_emoji_alternates() : data.get_emoji();
-+        string emoji = data.get_emoji();
-         unowned GLib.SList<string> annotations = data.get_annotations();
-         foreach (string annotation in annotations) {
-             bool has_emoji = false;
-@@ -434,23 +434,70 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
-+    private static string utf8_entity(string str) {
-+        GLib.StringBuilder buff = new GLib.StringBuilder();
-+        int length = str.char_count();
-+        for (int i = 0; i < length; i++) {
-+            unichar ch = str.get_char(0);
-+            switch(ch) {
-+            case '<':
-+                buff.append("&lt;");
-+                break;
-+            case '>':
-+                buff.append("&gt;");
-+                break;
-+            case '&':
-+                buff.append("&amp;");
-+                break;
-+            default:
-+                buff.append_unichar(ch);
-+                break;
-+            }
-+            str = str.next_char();
-+        }
-+        return buff.str;
-+    }
-+
-+
-+    private static void
-+    update_annotations_with_description (IBus.EmojiData data,
-+                                         string         description) {
-+        unowned GLib.SList<string> annotations = data.get_annotations();
-+        bool update_annotations = false;
-+        string former = null;
-+        string later = null;
-+        int index = description.index_of(": ");
-+        if (index > 0) {
-+            former = description.substring(0, index);
-+            if (annotations.find_custom(former, GLib.strcmp) == null) {
-+                annotations.append(former);
-+                update_annotations = true;
-+            }
-+            later = description.substring(index + 2);
-+        } else {
-+            later = description.dup();
-+        }
-+        var words = later.split(" ");
-+        // If the description has less than 3 words, add it to annotations
-+        // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
-+        if (words.length < 3 &&
-+            annotations.find_custom(
-+                    later,
-+                    GLib.strcmp) == null) {
-+            annotations.append(later);
-+            update_annotations = true;
-+        }
-+        if (update_annotations)
-+            data.set_annotations(annotations.copy_deep(GLib.strdup));
-+    }
-+
-+
-     private static void update_emoji_to_data_dict(IBus.EmojiData data,
-                                                   string         lang) {
--        string emoji = (data.get_emoji_alternates() != "") ?
--                data.get_emoji_alternates() : data.get_emoji();
-+        string emoji = data.get_emoji();
-         if (lang == "en") {
-             string description = utf8_down(data.get_description());
--            unowned GLib.SList<string> annotations = data.get_annotations();
--            var words = description.split(" ");
--            // If the description has less than 3 words, add it to annotations
--            // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
--            if (words.length < 3 &&
--                annotations.find_custom(
--                        description,
--                        GLib.strcmp) == null) {
--                annotations.append(description);
--                data.set_annotations(annotations.copy_deep(GLib.strdup));
--            }
-+            update_annotations_with_description (data, description);
-             m_emoji_to_data_dict.replace(emoji, data);
-         } else {
-             unowned IBus.EmojiData? en_data =
-@@ -462,16 +509,8 @@ class IBusEmojier : Gtk.Window {
-             string trans_description = data.get_description();
-             en_data.set_description(trans_description);
-             trans_description = utf8_down(trans_description);
-+            update_annotations_with_description (data, trans_description);
-             unowned GLib.SList<string> annotations = data.get_annotations();
--            var words = trans_description.split(" ");
--            // If the description has less than 3 words, add it to annotations
--            // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
--            if (words.length < 3 &&
--                annotations.find_custom(
--                        trans_description,
--                        GLib.strcmp) == null) {
--                annotations.append(trans_description);
--            }
-             unowned GLib.SList<string> en_annotations
-                 = en_data.get_annotations();
-             foreach (string annotation in en_annotations) {
-@@ -489,10 +528,11 @@ class IBusEmojier : Gtk.Window {
- 
-     private static void update_category_to_emojis_dict(IBus.EmojiData data,
-                                                        string         lang) {
--        string emoji = (data.get_emoji_alternates() != "") ?
--                data.get_emoji_alternates() : data.get_emoji();
-+        string emoji = data.get_emoji();
-         string category = data.get_category();
--        if (lang == "en" && category != "") {
-+        if (category == "")
-+            category = EMOJI_CATEGORY_OTHERS;
-+        if (lang == "en") {
-             bool has_emoji = false;
-             unowned GLib.SList<string> hits =
-                     m_category_to_emojis_dict.lookup(category);
-@@ -562,9 +602,17 @@ class IBusEmojier : Gtk.Window {
-                 m_category_to_emojis_dict.get_keys();
-         // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
-         categories.sort((a, b) => {
-+            if (a == EMOJI_CATEGORY_OTHERS && b != EMOJI_CATEGORY_OTHERS)
-+                return 1;
-+            else if (a != EMOJI_CATEGORY_OTHERS && b == EMOJI_CATEGORY_OTHERS)
-+                return -1;
-             return GLib.strcmp(_(a), _(b));
-         });
-         foreach (unowned string category in categories) {
-+            // "Others" category includes next unicode chars and fonts do not support
-+            // the base and varints yet.
-+            if (category == EMOJI_CATEGORY_OTHERS)
-+               continue;
-             EBoxRow row = new EBoxRow(category);
-             string locale_category = _(category);
-             EPaddedLabelBox widget =
-@@ -718,7 +766,7 @@ class IBusEmojier : Gtk.Window {
-         uint page_end_pos = uint.min(page_start_pos + page_size, ncandidates);
-         if (m_backward != null) {
-             string backward_desc =
--                    "%s (%u / %u)".printf(m_backward, cursor, ncandidates - 1);
-+                    "%s (%u / %u)".printf(_(m_backward), cursor, ncandidates - 1);
-             EPaddedLabelBox label =
-                     new EPaddedLabelBox(backward_desc,
-                                         Gtk.Align.CENTER,
-@@ -746,7 +794,7 @@ class IBusEmojier : Gtk.Window {
-                 int font_size = m_emoji_font_size - 2;
-                 string emoji_font = "%s %d".printf(font_family, font_size);
-                 string markup = "<span font=\"%s\">%s</span>".
--                        printf(emoji_font, candidate.get_text());
-+                        printf(emoji_font, utf8_entity(candidate.get_text()));
-                 label.set_markup(markup);
-             }
-             label.set_halign(Gtk.Align.FILL);
--- 
-2.9.3
-
-From 0836c3ffd6764969dfaa9953f52a24c1cbf2c983 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Sat, 6 May 2017 00:22:50 +0900
-Subject: [PATCH] Support to match emoji annotations with partial string
-
-Now ibus-setup has a new tab of "Emoji" and it provides
-the options to choose a condtion and length to match
-emoji annotations partically.
-
-R=alexepico@gmail.com, penghuang@google.com
-
-Review URL: https://codereview.appspot.com/319700043
----
- data/ibus.schemas.in    |  39 ++++
- setup/main.py           | 121 ++++++++++--
- setup/setup.ui          | 491 ++++++++++++++++++++++++++++++------------------
- ui/gtk3/emojier.vala    |  64 ++++++-
- ui/gtk3/emojierapp.vala |  52 ++++-
- ui/gtk3/panel.vala      |  22 +++
- 6 files changed, 582 insertions(+), 207 deletions(-)
-
-diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
-index 096dd71..0647004 100644
---- a/data/ibus.schemas.in
-+++ b/data/ibus.schemas.in
-@@ -404,6 +404,45 @@
-       </locale>
-     </schema>
-     <schema>
-+      <key>/schemas/desktop/ibus/panel/emoji/has-partial-match</key>
-+      <applyto>/desktop/ibus/panel/emoji/has-partial-match</applyto>
-+      <owner>ibus</owner>
-+      <type>bool</type>
-+      <default>false</default>
-+      <locale name="C">
-+        <short>Whether emoji annotaions can be match partially or not</short>
-+	    <long>Whether emoji annotations can be matched with a partial
-+                  string instead of the exact match or not.</long>
-+      </locale>
-+    </schema>
-+    <schema>
-+      <key>/schemas/desktop/ibus/panel/emoji/partial-match-length</key>
-+      <applyto>/desktop/ibus/panel/emoji/partial-match-length</applyto>
-+      <owner>ibus</owner>
-+      <type>int</type>
-+      <default>3</default>
-+      <locale name="C">
-+        <short>Match emoji annotaions with the specified length</short>
-+	    <long>Match emoji annotations partially with more than
-+                  the specified number of characters instead of
-+                  the exact match.</long>
-+      </locale>
-+    </schema>
-+    <schema>
-+      <key>/schemas/desktop/ibus/panel/emoji/partial-match-condition</key>
-+      <applyto>/desktop/ibus/panel/emoji/partial-match-condition</applyto>
-+      <owner>ibus</owner>
-+      <type>int</type>
-+      <default>0</default>
-+      <locale name="C">
-+        <short>Choose a condition to match emoji annotations partially</short>
-+	    <long>Choose one of the following conditions to match emoji
-+	          annotations partially:
-+                  0 == Prefix match, 1 == Suffix match, 2 == Containing match
-+                  </long>
-+      </locale>
-+    </schema>
-+    <schema>
-       <key>/schemas/desktop/ibus/general/embed_preedit_text</key>
-       <applyto>/desktop/ibus/general/embed_preedit_text</applyto>
-       <owner>ibus</owner>
-diff --git a/setup/main.py b/setup/main.py
-index 7839cea..f0eee99 100644
---- a/setup/main.py
-+++ b/setup/main.py
-@@ -187,19 +187,6 @@ class Setup(object):
-                                     self.__fontbutton_custom_font,
-                                    'sensitive',
-                                    Gio.SettingsBindFlags.GET)
--        self.__fontbutton_emoji_font = self.__builder.get_object(
--                'fontbutton_emoji_font')
--        self.__fontbutton_emoji_font.set_preview_text('🙂🍎🚃💓📧⚽🐳');
--        self.__settings_emoji.bind('font',
--                                    self.__fontbutton_emoji_font,
--                                   'font-name',
--                                   Gio.SettingsBindFlags.DEFAULT)
--        self.__button_emoji_lang = self.__builder.get_object(
--                'button_emoji_lang')
--        self.__settings_emoji.bind('lang',
--                                    self.__button_emoji_lang,
--                                   'lang',
--                                   Gio.SettingsBindFlags.DEFAULT)
- 
-         # show icon on system tray
-         self.__checkbutton_show_icon_on_systray = self.__builder.get_object(
-@@ -287,6 +274,113 @@ class Setup(object):
-         self.__treeview.connect("notify::active-engine", self.__treeview_notify_cb)
-         self.__treeview.connect("notify::engines", self.__treeview_notify_cb)
- 
-+    def __init_emoji(self):
-+        self.__fontbutton_emoji_font = self.__builder.get_object(
-+                'fontbutton_emoji_font')
-+        self.__fontbutton_emoji_font.set_preview_text('🙂🍎🚃💓📧⚽🐳');
-+        self.__settings_emoji.bind('font',
-+                                    self.__fontbutton_emoji_font,
-+                                   'font-name',
-+                                   Gio.SettingsBindFlags.DEFAULT)
-+        self.__button_emoji_lang = self.__builder.get_object(
-+                'button_emoji_lang')
-+        self.__settings_emoji.bind('lang',
-+                                    self.__button_emoji_lang,
-+                                   'lang',
-+                                   Gio.SettingsBindFlags.DEFAULT)
-+        self.__checkbutton_emoji_partial_match = self.__builder.get_object(
-+                'checkbutton_emoji_partial_match')
-+        checkbutton_label = self.__checkbutton_emoji_partial_match.get_child()
-+        if type(checkbutton_label) == Gtk.Label:
-+            checkbutton_label.set_property('wrap', True)
-+            checkbutton_label.set_property('max-width-chars', 74)
-+        self.__spinbutton_emoji_partial_match = self.__builder.get_object(
-+                'spinbutton_emoji_partial_match')
-+        self.__settings_emoji.bind('has-partial-match',
-+                                   self.__checkbutton_emoji_partial_match,
-+                                   'active',
-+                                   Gio.SettingsBindFlags.DEFAULT)
-+        self.__settings_emoji.bind('has-partial-match',
-+                                   self.__spinbutton_emoji_partial_match,
-+                                   'sensitive',
-+                                   Gio.SettingsBindFlags.GET)
-+
-+        def adjustment_value_changed_cb(obj):
-+            key = 'partial-match-length'
-+            value = int(adjustment.get_value())
-+            if value == self.__settings_emoji.get_int(key):
-+                return
-+            self.__settings_emoji.set_int(key, value)
-+        def settings_emoji_partial_match_length_cb(settings, key):
-+            value = self.__settings_emoji.get_int(key)
-+            old_value = int(self.__spinbutton_emoji_partial_match.get_value())
-+            if value == old_value:
-+                return
-+            self.__spinbutton_emoji_partial_match.set_value(value)
-+        settings_emoji_partial_match_length_cb(None, 'partial-match-length')
-+        adjustment = self.__spinbutton_emoji_partial_match.get_adjustment()
-+        adjustment.connect('value-changed', adjustment_value_changed_cb)
-+        self.__settings_emoji.connect('changed::partial-match-length',
-+                                      settings_emoji_partial_match_length_cb)
-+
-+        self.__hbox_emoji_partial_match = self.__builder.get_object(
-+                'hbox_emoji_partial_match')
-+        self.__settings_emoji.bind('has-partial-match',
-+                                   self.__hbox_emoji_partial_match,
-+                                   'sensitive',
-+                                   Gio.SettingsBindFlags.GET)
-+        self.__radiobutton_emoji_prefix_match = self.__builder.get_object(
-+                'radiobutton_emoji_prefix_match')
-+        self.__radiobutton_emoji_suffix_match = self.__builder.get_object(
-+                'radiobutton_emoji_suffix_match')
-+        self.__radiobutton_emoji_contain_match = self.__builder.get_object(
-+                'radiobutton_emoji_contain_match')
-+
-+        def radiobuton_emoji_partial_match_cb(obj):
-+            key = 'partial-match-condition'
-+            condition = 0
-+            if not obj.get_active():
-+                return
-+            if obj == self.__radiobutton_emoji_prefix_match:
-+                condition = 0
-+            elif obj == self.__radiobutton_emoji_suffix_match:
-+                condition = 1
-+            elif obj == self.__radiobutton_emoji_contain_match:
-+                condition = 2
-+            else:
-+                print('Wrong emoji partial match object')
-+                return
-+            self.__settings_emoji.set_int(key, condition)
-+        def settings_emoji_partial_match_condition_cb(settings, key):
-+            value = self.__settings_emoji.get_int(key)
-+            obj = None
-+            if value == 0:
-+                obj = self.__radiobutton_emoji_prefix_match
-+            elif value == 1:
-+                obj = self.__radiobutton_emoji_suffix_match
-+            elif value == 2:
-+                obj = self.__radiobutton_emoji_contain_match
-+            else:
-+                print('Wrong emoji partial match condition')
-+                return
-+            if obj.get_active():
-+                return
-+            obj.set_active(True)
-+
-+        settings_emoji_partial_match_condition_cb(None,
-+                                                  'partial-match-condition')
-+        self.__radiobutton_emoji_prefix_match.connect(
-+                'toggled',
-+                radiobuton_emoji_partial_match_cb)
-+        self.__radiobutton_emoji_suffix_match.connect(
-+                'toggled',
-+                radiobuton_emoji_partial_match_cb)
-+        self.__radiobutton_emoji_contain_match.connect(
-+                'toggled',
-+                radiobuton_emoji_partial_match_cb)
-+        self.__settings_emoji.connect('changed::partial-match-condition',
-+                                      settings_emoji_partial_match_condition_cb)
-+
-     def __init_ui(self):
-         # add icon search path
-         self.__window = self.__builder.get_object("window_preferences")
-@@ -306,6 +400,7 @@ class Setup(object):
-         self.__init_hotkeys()
-         self.__init_panel()
-         self.__init_general()
-+        self.__init_emoji()
- 
-     def __gdk_window_set_cb(self, object, pspec):
-         window = object.get_window()
-diff --git a/setup/setup.ui b/setup/setup.ui
-index 54ef916..322f514 100644
---- a/setup/setup.ui
-+++ b/setup/setup.ui
-@@ -55,6 +55,14 @@
-       </row>
-     </data>
-   </object>
-+  <object class="GtkAdjustment" id="adjustment_emoji_partial_match">
-+    <property name="value">3.0</property>
-+    <property name="lower">1.0</property>
-+    <property name="upper">255.0</property>
-+    <property name="step-increment">1.0</property>
-+    <property name="page-increment">10.0</property>
-+    <property name="page-size">10.0</property>
-+  </object>
-   <object class="GtkWindow" id="window_preferences">
-     <property name="can_focus">False</property>
-     <property name="border_width">12</property>
-@@ -109,8 +117,6 @@
-                             <property name="label" translatable="yes">Next input method:</property>
-                           </object>
-                           <packing>
--                            <property name="top_attach">3</property>
--                            <property name="bottom_attach">4</property>
-                             <property name="x_options">GTK_FILL</property>
-                             <property name="y_options">GTK_FILL</property>
-                           </packing>
-@@ -125,69 +131,13 @@
-                             <property name="label" translatable="yes">Previous input method:</property>
-                           </object>
-                           <packing>
--                            <property name="top_attach">4</property>
--                            <property name="bottom_attach">5</property>
--                            <property name="x_options">GTK_FILL</property>
--                            <property name="y_options">GTK_FILL</property>
--                          </packing>
--                        </child>
--                        <child>
--                          <object class="GtkLabel" id="emoji_label">
--                            <property name="visible">True</property>
--                            <property name="can_focus">False</property>
--                            <property name="tooltip_text" translatable="yes">The shortcut keys for showing emoji dialog</property>
--                            <property name="halign">start</property>
--                            <property name="label" translatable="yes">Emoji choice:</property>
--                          </object>
--                          <packing>
--                            <property name="top_attach">5</property>
--                            <property name="bottom_attach">6</property>
-+                            <property name="top_attach">1</property>
-+                            <property name="bottom_attach">2</property>
-                             <property name="x_options">GTK_FILL</property>
-                             <property name="y_options">GTK_FILL</property>
-                           </packing>
-                         </child>
-                         <child>
--                          <object class="GtkBox" id="hbox4">
--                            <property name="orientation">horizontal</property>
--                            <property name="no_show_all">True</property>
--                            <property name="can_focus">False</property>
--                            <property name="spacing">6</property>
--                            <child>
--                              <object class="GtkEntry" id="entry_trigger">
--                                <property name="visible">True</property>
--                                <property name="can_focus">True</property>
--                                <property name="editable">False</property>
--                              </object>
--                              <packing>
--                                <property name="expand">True</property>
--                                <property name="fill">True</property>
--                                <property name="position">0</property>
--                              </packing>
--                            </child>
--                            <child>
--                              <object class="GtkButton" id="button_trigger">
--                                <property name="label" translatable="yes">...</property>
--                                <property name="use_action_appearance">False</property>
--                                <property name="visible">True</property>
--                                <property name="can_focus">True</property>
--                                <property name="receives_default">False</property>
--                                <property name="use_action_appearance">False</property>
--                                <property name="use_underline">True</property>
--                              </object>
--                              <packing>
--                                <property name="expand">False</property>
--                                <property name="fill">True</property>
--                                <property name="position">1</property>
--                              </packing>
--                            </child>
--                          </object>
--                          <packing>
--                            <property name="left_attach">1</property>
--                            <property name="right_attach">2</property>
--                            <property name="y_options">GTK_FILL</property>
--                          </packing>
--                        </child>
--                        <child>
-                           <object class="GtkBox" id="hbox5">
-                             <property name="orientation">horizontal</property>
-                             <property name="visible">True</property>
-@@ -225,8 +175,6 @@
-                           <packing>
-                             <property name="left_attach">1</property>
-                             <property name="right_attach">2</property>
--                            <property name="top_attach">3</property>
--                            <property name="bottom_attach">4</property>
-                             <property name="y_options">GTK_FILL</property>
-                           </packing>
-                         </child>
-@@ -270,51 +218,8 @@
-                           <packing>
-                             <property name="left_attach">1</property>
-                             <property name="right_attach">2</property>
--                            <property name="top_attach">4</property>
--                            <property name="bottom_attach">5</property>
--                            <property name="y_options">GTK_FILL</property>
--                          </packing>
--                        </child>
--                        <child>
--                          <object class="GtkBox" id="emoji_hbox">
--                            <property name="orientation">horizontal</property>
--                            <property name="visible">True</property>
--                            <property name="can_focus">False</property>
--                            <property name="spacing">6</property>
--                            <child>
--                              <object class="GtkEntry" id="entry_emoji_dialog">
--                                <property name="visible">True</property>
--                                <property name="can_focus">True</property>
--                                <property name="editable">False</property>
--                              </object>
--                              <packing>
--                                <property name="expand">True</property>
--                                <property name="fill">True</property>
--                                <property name="position">0</property>
--                              </packing>
--                            </child>
--                            <child>
--                              <object class="GtkButton" id="button_emoji_dialog">
--                                <property name="label" translatable="yes">...</property>
--                                <property name="use_action_appearance">False</property>
--                                <property name="visible">True</property>
--                                <property name="can_focus">True</property>
--                                <property name="receives_default">False</property>
--                                <property name="use_action_appearance">False</property>
--                                <property name="use_underline">True</property>
--                              </object>
--                              <packing>
--                                <property name="expand">False</property>
--                                <property name="fill">True</property>
--                                <property name="position">1</property>
--                              </packing>
--                            </child>
--                          </object>
--                          <packing>
--                            <property name="left_attach">1</property>
--                            <property name="right_attach">2</property>
--                            <property name="top_attach">5</property>
--                            <property name="bottom_attach">6</property>
-+                            <property name="top_attach">1</property>
-+                            <property name="bottom_attach">2</property>
-                             <property name="y_options">GTK_FILL</property>
-                           </packing>
-                         </child>
-@@ -327,6 +232,8 @@
-                             <property name="label" translatable="yes">Enable or disable:</property>
-                           </object>
-                           <packing>
-+                            <property name="top_attach">2</property>
-+                            <property name="bottom_attach">3</property>
-                             <property name="x_options">GTK_FILL</property>
-                             <property name="y_options">GTK_FILL</property>
-                           </packing>
-@@ -339,8 +246,8 @@
-                             <property name="label" translatable="yes">Enable:</property>
-                           </object>
-                           <packing>
--                            <property name="top_attach">1</property>
--                            <property name="bottom_attach">2</property>
-+                            <property name="top_attach">3</property>
-+                            <property name="bottom_attach">4</property>
-                             <property name="x_options">GTK_FILL</property>
-                             <property name="y_options">GTK_FILL</property>
-                           </packing>
-@@ -383,8 +290,8 @@
-                           <packing>
-                             <property name="left_attach">1</property>
-                             <property name="right_attach">2</property>
--                            <property name="top_attach">1</property>
--                            <property name="bottom_attach">2</property>
-+                            <property name="top_attach">3</property>
-+                            <property name="bottom_attach">4</property>
-                             <property name="y_options">GTK_FILL</property>
-                           </packing>
-                         </child>
-@@ -396,8 +303,8 @@
-                             <property name="label" translatable="yes">Disable:</property>
-                           </object>
-                           <packing>
--                            <property name="top_attach">2</property>
--                            <property name="bottom_attach">3</property>
-+                            <property name="top_attach">4</property>
-+                            <property name="bottom_attach">5</property>
-                             <property name="x_options">GTK_FILL</property>
-                             <property name="y_options">GTK_FILL</property>
-                           </packing>
-@@ -440,8 +347,8 @@
-                           <packing>
-                             <property name="left_attach">1</property>
-                             <property name="right_attach">2</property>
--                            <property name="top_attach">2</property>
--                            <property name="bottom_attach">3</property>
-+                            <property name="top_attach">4</property>
-+                            <property name="bottom_attach">5</property>
-                             <property name="y_options">GTK_FILL</property>
-                           </packing>
-                         </child>
-@@ -675,68 +582,6 @@
-                             <property name="y_options">GTK_FILL</property>
-                           </packing>
-                         </child>
--                        <child>
--                          <object class="GtkLabel" id="label_emoji_font">
--                            <property name="visible">True</property>
--                            <property name="can_focus">False</property>
--                            <property name="tooltip_text" translatable="yes">Set a font of emoji candidates on the emoji dialog</property>
--                            <property name="halign">start</property>
--                            <property name="label" translatable="yes">Emoji font:</property>
--                            <property name="justify">right</property>
--                          </object>
--                          <packing>
--                            <property name="top_attach">7</property>
--                            <property name="bottom_attach">8</property>
--                            <property name="x_options">GTK_FILL</property>
--                            <property name="y_options">GTK_FILL</property>
--                          </packing>
--                        </child>
--                        <child>
--                          <object class="GtkFontButton" id="fontbutton_emoji_font">
--                            <property name="visible">True</property>
--                            <property name="can_focus">True</property>
--                            <property name="receives_default">True</property>
--                            <property name="use_action_appearance">False</property>
--                          </object>
--                          <packing>
--                            <property name="left_attach">1</property>
--                            <property name="right_attach">2</property>
--                            <property name="top_attach">7</property>
--                            <property name="bottom_attach">8</property>
--                            <property name="y_options">GTK_FILL</property>
--                          </packing>
--                        </child>
--                        <child>
--                          <object class="GtkLabel" id="label_emoji_lang">
--                            <property name="visible">True</property>
--                            <property name="can_focus">False</property>
--                            <property name="tooltip_text" translatable="yes">Set a language of emoji annotations on the emoji dialog</property>
--                            <property name="halign">start</property>
--                            <property name="label" translatable="yes">Emoji annotation language:</property>
--                            <property name="justify">right</property>
--                          </object>
--                          <packing>
--                            <property name="top_attach">8</property>
--                            <property name="bottom_attach">9</property>
--                            <property name="x_options">GTK_FILL</property>
--                            <property name="y_options">GTK_FILL</property>
--                          </packing>
--                        </child>
--                        <child>
--                          <object class="EmojiLangButton" id="button_emoji_lang">
--                            <property name="visible">True</property>
--                            <property name="can_focus">True</property>
--                            <property name="receives_default">True</property>
--                            <property name="use_action_appearance">False</property>
--                          </object>
--                          <packing>
--                            <property name="left_attach">1</property>
--                            <property name="right_attach">2</property>
--                            <property name="top_attach">8</property>
--                            <property name="bottom_attach">9</property>
--                            <property name="y_options">GTK_FILL</property>
--                          </packing>
--                        </child>
-                       </object>
-                     </child>
-                     <child type="label">
-@@ -1027,6 +872,292 @@
-               </packing>
-             </child>
-             <child>
-+              <object class="GtkBox" id="vbox_emoji1">
-+                <property name="orientation">vertical</property>
-+                <property name="visible">True</property>
-+                <property name="can_focus">False</property>
-+                <property name="spacing">18</property>
-+                <property name="margin_top">12</property>
-+                <property name="margin_bottom">12</property>
-+                <property name="margin_left">12</property>
-+                <property name="margin_right">12</property>
-+                <child>
-+                  <object class="GtkFrame" id="frame_emoji1">
-+                    <property name="visible">True</property>
-+                    <property name="can_focus">False</property>
-+                    <property name="label_xalign">0</property>
-+                    <property name="shadow_type">none</property>
-+                    <child>
-+                      <object class="GtkTable" id="table_emoji1">
-+                        <property name="visible">True</property>
-+                        <property name="can_focus">False</property>
-+                        <property name="n_rows">5</property>
-+                        <property name="n_columns">2</property>
-+                        <property name="column_spacing">12</property>
-+                        <property name="row_spacing">6</property>
-+                        <property name="margin_top">6</property>
-+                        <property name="margin_left">12</property>
-+                        <child>
-+                          <object class="GtkLabel" id="label_emoji1">
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">False</property>
-+                            <property name="tooltip_text" translatable="yes">The shortcut keys for showing emoji dialog</property>
-+                            <property name="halign">start</property>
-+                            <property name="label" translatable="yes">Emoji choice:</property>
-+                          </object>
-+                          <packing>
-+                            <property name="x_options">GTK_FILL</property>
-+                            <property name="y_options">GTK_FILL</property>
-+                          </packing>
-+                        </child>
-+                        <child>
-+                          <object class="GtkBox" id="hbox_emoji1">
-+                            <property name="orientation">horizontal</property>
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">False</property>
-+                            <property name="spacing">6</property>
-+                            <child>
-+                              <object class="GtkEntry" id="entry_emoji_dialog">
-+                                <property name="visible">True</property>
-+                                <property name="can_focus">True</property>
-+                                <property name="editable">False</property>
-+                              </object>
-+                              <packing>
-+                                <property name="expand">True</property>
-+                                <property name="fill">True</property>
-+                                <property name="position">0</property>
-+                              </packing>
-+                            </child>
-+                            <child>
-+                              <object class="GtkButton" id="button_emoji_dialog">
-+                                <property name="label" translatable="yes">...</property>
-+                                <property name="use_action_appearance">False</property>
-+                                <property name="visible">True</property>
-+                                <property name="can_focus">True</property>
-+                                <property name="receives_default">False</property>
-+                                <property name="use_action_appearance">False</property>
-+                                <property name="use_underline">True</property>
-+                              </object>
-+                              <packing>
-+                                <property name="expand">False</property>
-+                                <property name="fill">True</property>
-+                                <property name="position">1</property>
-+                              </packing>
-+                            </child>
-+                          </object>
-+                          <packing>
-+                            <property name="left_attach">1</property>
-+                            <property name="right_attach">2</property>
-+                            <property name="y_options">GTK_FILL</property>
-+                          </packing>
-+                        </child>
-+                      </object>
-+                    </child>
-+                    <child type="label">
-+                      <object class="GtkLabel" id="label_emoji2">
-+                        <property name="visible">True</property>
-+                        <property name="can_focus">False</property>
-+                        <property name="label" translatable="yes">&lt;b&gt;Keyboard Shortcuts&lt;/b&gt;</property>
-+                        <property name="use_markup">True</property>
-+                      </object>
-+                    </child>
-+                  </object>
-+                  <packing>
-+                    <property name="expand">False</property>
-+                    <property name="fill">True</property>
-+                    <property name="position">0</property>
-+                  </packing>
-+                </child>
-+                <child>
-+                  <object class="GtkFrame" id="frame_emoji2">
-+                    <property name="visible">True</property>
-+                    <property name="can_focus">False</property>
-+                    <property name="label_xalign">0</property>
-+                    <property name="shadow_type">none</property>
-+                    <child>
-+                      <object class="GtkGrid" id="table_emoji2">
-+                        <property name="visible">True</property>
-+                        <property name="can_focus">False</property>
-+                        <property name="column_spacing">12</property>
-+                        <property name="row_spacing">6</property>
-+                        <property name="margin_top">6</property>
-+                        <property name="margin_left">12</property>
-+                        <property name="row_homogeneous">false</property>
-+                        <child>
-+                          <object class="GtkLabel" id="label_emoji_font">
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">False</property>
-+                            <property name="tooltip_text" translatable="yes">Set a font of emoji candidates on the emoji dialog</property>
-+                            <property name="halign">start</property>
-+                            <property name="label" translatable="yes">Emoji font:</property>
-+                            <property name="justify">right</property>
-+                          </object>
-+                          <packing>
-+                            <property name="left_attach">0</property>
-+                          </packing>
-+                        </child>
-+                        <child>
-+                          <object class="GtkFontButton" id="fontbutton_emoji_font">
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">True</property>
-+                            <property name="receives_default">True</property>
-+                            <property name="use_action_appearance">False</property>
-+                          </object>
-+                          <packing>
-+                            <property name="left_attach">1</property>
-+                            <!-- property name="right_attach">2</property -->
-+                          </packing>
-+                        </child>
-+                        <child>
-+                          <object class="GtkLabel" id="label_emoji_lang">
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">False</property>
-+                            <property name="tooltip_text" translatable="yes">Set a language of emoji annotations on the emoji dialog</property>
-+                            <property name="halign">start</property>
-+                            <property name="label" translatable="yes">Emoji annotation language:</property>
-+                            <property name="justify">right</property>
-+                          </object>
-+                          <packing>
-+                            <property name="left_attach">0</property>
-+                            <property name="top_attach">1</property>
-+                          </packing>
-+                        </child>
-+                        <child>
-+                          <object class="EmojiLangButton" id="button_emoji_lang">
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">True</property>
-+                            <property name="receives_default">True</property>
-+                            <property name="use_action_appearance">False</property>
-+                          </object>
-+                          <packing>
-+                            <property name="left_attach">1</property>
-+                            <property name="top_attach">1</property>
-+                          </packing>
-+                        </child>
-+                        <child>
-+                          <object class="GtkCheckButton" id="checkbutton_emoji_partial_match">
-+                            <property name="label" translatable="yes">Match emoji annotations partially with the following condition and more than the number of characters:</property>
-+                            <property name="use_action_appearance">False</property>
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">True</property>
-+                            <property name="receives_default">False</property>
-+                            <property name="tooltip_text" translatable="yes">If emoji annotations can be matched with a partial string instead of the exact match</property>
-+                            <property name="use_action_appearance">False</property>
-+                            <property name="valign">start</property>
-+                            <property name="draw_indicator">True</property>
-+                          </object>
-+                          <packing>
-+                            <property name="left_attach">0</property>
-+                            <property name="width">2</property>
-+                            <property name="top_attach">2</property>
-+                          </packing>
-+                        </child>
-+                        <child>
-+                          <object class="GtkSpinButton" id="spinbutton_emoji_partial_match">
-+                            <property name="adjustment">adjustment_emoji_partial_match</property>
-+                            <property name="value">3.0</property>
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">True</property>
-+                            <property name="receives_default">False</property>
-+                            <property name="halign">start</property>
-+                          </object>
-+                          <packing>
-+                            <property name="left_attach">0</property>
-+                            <property name="width">2</property>
-+                            <property name="top_attach">3</property>
-+                          </packing>
-+                        </child>
-+                        <child>
-+                          <object class="GtkBox" id="hbox_emoji_partial_match">
-+                            <property name="orientation">horizontal</property>
-+                            <property name="visible">True</property>
-+                            <property name="can_focus">False</property>
-+                            <property name="spacing">6</property>
-+                            <child>
-+                              <object class="GtkRadioButton" id="radiobutton_emoji_prefix_match">
-+                                <property name="label" translatable="yes">Prefix match</property>
-+                                <property name="visible">True</property>
-+                                <property name="can_focus">True</property>
-+                                <property name="receives_default">False</property>
-+                                <property name="halign">start</property>
-+                              </object>
-+                              <packing>
-+                                <property name="expand">False</property>
-+                                <property name="fill">True</property>
-+                                <property name="position">0</property>
-+                              </packing>
-+                            </child>
-+                            <child>
-+                              <object class="GtkRadioButton" id="radiobutton_emoji_suffix_match">
-+                                <property name="label" translatable="yes">Suffix match</property>
-+                                <property name="group">radiobutton_emoji_prefix_match</property>
-+                                <property name="visible">True</property>
-+                                <property name="can_focus">True</property>
-+                                <property name="receives_default">False</property>
-+                                <property name="halign">start</property>
-+                              </object>
-+                              <packing>
-+                                <property name="expand">False</property>
-+                                <property name="fill">True</property>
-+                                <property name="position">1</property>
-+                              </packing>
-+                            </child>
-+                            <child>
-+                              <object class="GtkRadioButton" id="radiobutton_emoji_contain_match">
-+                                <property name="label" translatable="yes">Containing match</property>
-+                                <property name="group">radiobutton_emoji_prefix_match</property>
-+                                <property name="visible">True</property>
-+                                <property name="can_focus">True</property>
-+                                <property name="receives_default">False</property>
-+                                <property name="halign">start</property>
-+                              </object>
-+                              <packing>
-+                                <property name="expand">False</property>
-+                                <property name="fill">True</property>
-+                                <property name="position">2</property>
-+                              </packing>
-+                            </child>
-+                          </object>
-+                          <packing>
-+                            <property name="left_attach">0</property>
-+                            <property name="width">2</property>
-+                            <property name="top_attach">4</property>
-+                          </packing>
-+                        </child>
-+                      </object>
-+                    </child>
-+                    <child type="label">
-+                      <object class="GtkLabel" id="label_emoji3">
-+                        <property name="visible">True</property>
-+                        <property name="can_focus">False</property>
-+                        <property name="label" translatable="yes">&lt;b&gt;Font and Style&lt;/b&gt;</property>
-+                        <property name="use_markup">True</property>
-+                      </object>
-+                    </child>
-+                  </object>
-+                  <packing>
-+                    <property name="expand">False</property>
-+                    <property name="fill">True</property>
-+                    <property name="position">1</property>
-+                  </packing>
-+                </child>
-+              </object>
-+              <packing>
-+                <property name="position">2</property>
-+              </packing>
-+            </child>
-+            <child type="tab">
-+              <object class="GtkLabel" id="emoji_tab">
-+                <property name="visible">True</property>
-+                <property name="can_focus">False</property>
-+                <property name="label" translatable="yes">Emoji</property>
-+              </object>
-+              <packing>
-+                <property name="position">2</property>
-+                <property name="tab_fill">False</property>
-+              </packing>
-+            </child>
-+            <child>
-               <object class="GtkBox" id="vbox7">
-                 <property name="orientation">vertical</property>
-                 <property name="visible">True</property>
-@@ -1134,7 +1265,7 @@
-                 </child>
-               </object>
-               <packing>
--                <property name="position">2</property>
-+                <property name="position">3</property>
-               </packing>
-             </child>
-             <child type="tab">
-@@ -1214,7 +1345,7 @@ Homepage: https://github.com/ibus/ibus/wiki
-                 </child>
-               </object>
-               <packing>
--                <property name="position">3</property>
-+                <property name="position">4</property>
-               </packing>
-             </child>
-             <child type="tab">
-@@ -1224,7 +1355,7 @@ Homepage: https://github.com/ibus/ibus/wiki
-                 <property name="label" translatable="yes">About</property>
-               </object>
-               <packing>
--                <property name="position">3</property>
-+                <property name="position">4</property>
-                 <property name="tab_fill">False</property>
-               </packing>
-             </child>
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 8f55bd4..76d5fed 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -177,6 +177,9 @@ class IBusEmojier : Gtk.Window {
-     private static int m_emoji_font_size;
-     private static string[] m_favorites;
-     private static int m_emoji_max_seq_len;
-+    private static bool m_has_partial_match;
-+    private static uint m_partial_match_length;
-+    private static uint m_partial_match_condition;
-     private static GLib.HashTable<string, GLib.SList>?
-             m_annotation_to_emojis_dict;
-     private static GLib.HashTable<string, IBus.EmojiData>?
-@@ -738,16 +741,53 @@ class IBusEmojier : Gtk.Window {
-         }
-         // Call check_unicode_point() to get m_unicode_point
-         check_unicode_point();
--        unowned GLib.SList<string>? emojis =
--            m_annotation_to_emojis_dict.lookup(annotation);
--        if (emojis == null && m_unicode_point == null) {
-+        GLib.SList<string>? total_emojis = null;
-+        unowned GLib.SList<string>? sub_emojis = null;
-+        int length = annotation.length;
-+        if (m_has_partial_match && length >= m_partial_match_length) {
-+            foreach (unowned string key in
-+                     m_annotation_to_emojis_dict.get_keys()) {
-+                if (key.length < length)
-+                    continue;
-+                bool matched = false;
-+                switch(m_partial_match_condition) {
-+                case 0:
-+                    if (key.has_prefix(annotation))
-+                        matched = true;
-+                    break;
-+                case 1:
-+                    if (key.has_suffix(annotation))
-+                        matched = true;
-+                    break;
-+                case 2:
-+                    if (key.str(annotation) != null)
-+                        matched = true;
-+                    break;
-+                default:
-+                    break;
-+                }
-+                if (!matched)
-+                    continue;
-+                sub_emojis = m_annotation_to_emojis_dict.lookup(key);
-+                foreach (unowned string emoji in sub_emojis) {
-+                    if (total_emojis.find_custom(emoji, GLib.strcmp) == null) {
-+                        total_emojis.append(emoji);
-+                    }
-+                }
-+            }
-+        } else {
-+            sub_emojis = m_annotation_to_emojis_dict.lookup(annotation);
-+            foreach (unowned string emoji in sub_emojis)
-+                total_emojis.append(emoji);
-+        }
-+        if (total_emojis == null && m_unicode_point == null) {
-             hide_candidate_panel();
-             return;
-         }
-         m_lookup_table.clear();
-         // Call check_unicode_point() to update m_lookup_table
-         check_unicode_point();
--        foreach (unowned string emoji in emojis) {
-+        foreach (unowned string emoji in total_emojis) {
-             IBus.Text text = new IBus.Text.from_string(emoji);
-             m_lookup_table.append_candidate(text);
-         }
-@@ -1331,6 +1371,22 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
-+    public static void set_partial_match(bool has_partial_match) {
-+        m_has_partial_match = has_partial_match;
-+    }
-+
-+    public static void set_partial_match_length(int length) {
-+        if (length < 1)
-+            return;
-+        m_partial_match_length = length;
-+    }
-+
-+    public static void set_partial_match_condition(int condition) {
-+        if (condition < 0)
-+            return;
-+        m_partial_match_condition = condition;
-+    }
-+
-     public static void set_favorites(string[]? unowned_favorites) {
-         m_favorites = {};
-         foreach (string favorite in unowned_favorites) {
-diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
-index cbdd8ba..dbdbc27 100644
---- a/ui/gtk3/emojierapp.vala
-+++ b/ui/gtk3/emojierapp.vala
-@@ -22,6 +22,9 @@
- 
- string emoji_font = null;
- string annotation_lang = null;
-+bool partial_match = false;
-+int partial_match_length = -1;
-+int partial_match_condition = -1;
- 
- public class EmojiApplication : Application {
-     private IBusEmojier? m_emojier;
-@@ -65,17 +68,28 @@ public class EmojiApplication : Application {
- 
-         const OptionEntry[] options = {
-             { "font", 0, 0, OptionArg.STRING, out emoji_font,
--                /* TRANSLATORS: "FONT" should be capital and translatable.
--                 * It's used for an argument command --font=FONT
--                 */
--                N_("\"FONT\" for emoji chracters on emoji dialog"),
--                N_("FONT") },
-+              /* TRANSLATORS: "FONT" should be capital and translatable.
-+               * It's used for an argument command --font=FONT
-+               */
-+              N_("\"FONT\" for emoji chracters on emoji dialog"),
-+              N_("FONT") },
-             { "lang", 0, 0, OptionArg.STRING, out annotation_lang,
--                /* TRANSLATORS: "LANG" should be capital and translatable.
--                 * It's used for an argument command --lang=LANG
--                 */
--                N_("\"LANG\" for annotations on emoji dialog. E.g. \"en\""),
--                N_("LANG") },
-+              /* TRANSLATORS: "LANG" should be capital and translatable.
-+               * It's used for an argument command --lang=LANG
-+               */
-+              N_("\"LANG\" for annotations on emoji dialog. E.g. \"en\""),
-+              N_("LANG") },
-+            { "partial-match", 0, 0, OptionArg.NONE, out partial_match,
-+              N_("Emoji annotaions can be match partially"),
-+              null },
-+            { "partial-match-length", 0, 0, OptionArg.INT,
-+              out partial_match_length,
-+              N_("Match with the length of the specified integer"),
-+              null },
-+            { "partial-match-condition", 0, 0, OptionArg.INT,
-+              out partial_match_condition,
-+              N_("Match with the condition of the specified integer"),
-+              null },
-             { null }
-         };
- 
-@@ -111,6 +125,24 @@ public class EmojiApplication : Application {
-         if (annotation_lang == null)
-             annotation_lang = m_settings_emoji.get_string("lang");
-         IBusEmojier.set_annotation_lang(annotation_lang);
-+        IBusEmojier.set_partial_match(partial_match);
-+        if (partial_match_length > 0) {
-+            IBusEmojier.set_partial_match_length(partial_match_length);
-+        } else {
-+            IBusEmojier.set_partial_match_length(
-+                    m_settings_emoji.get_int("partial-match-length"));
-+        }
-+        if (partial_match_condition > 2) {
-+            warning("Need condition between 0 and 2.");
-+            IBusEmojier.set_partial_match_condition(
-+                    m_settings_emoji.get_int("partial-match-condition"));
-+        }
-+        else if (partial_match_condition >= 0) {
-+            IBusEmojier.set_partial_match_condition(partial_match_condition);
-+        } else {
-+            IBusEmojier.set_partial_match_condition(
-+                    m_settings_emoji.get_int("partial-match-condition"));
-+        }
- 
-         if (emoji_font != null)
-             IBusEmojier.set_emoji_font(emoji_font);
-diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
-index 745489b..5f0009e 100644
---- a/ui/gtk3/panel.vala
-+++ b/ui/gtk3/panel.vala
-@@ -248,6 +248,18 @@ class Panel : IBus.PanelService {
-         m_settings_emoji.changed["lang"].connect((key) => {
-                 set_emoji_lang();
-         });
-+
-+        m_settings_emoji.changed["has-partial-match"].connect((key) => {
-+                set_emoji_partial_match();
-+        });
-+
-+        m_settings_emoji.changed["partial-match-length"].connect((key) => {
-+                set_emoji_partial_match();
-+        });
-+
-+        m_settings_emoji.changed["partial-match-condition"].connect((key) => {
-+                set_emoji_partial_match();
-+        });
-     }
- 
- #if INDICATOR
-@@ -782,6 +794,15 @@ class Panel : IBus.PanelService {
-         });
-     }
- 
-+    private void set_emoji_partial_match() {
-+        IBusEmojier.set_partial_match(
-+                m_settings_emoji.get_boolean("has-partial-match"));
-+        IBusEmojier.set_partial_match_length(
-+                m_settings_emoji.get_int("partial-match-length"));
-+        IBusEmojier.set_partial_match_condition(
-+                m_settings_emoji.get_int("partial-match-condition"));
-+    }
-+
-     private int compare_versions(string version1, string version2) {
-         string[] version1_list = version1.split(".");
-         string[] version2_list = version2.split(".");
-@@ -897,6 +918,7 @@ class Panel : IBus.PanelService {
-         set_property_icon_delay_time();
-         set_emoji_favorites();
-         set_emoji_lang();
-+        set_emoji_partial_match();
-     }
- 
-     private void engine_contexts_insert(IBus.EngineDesc engine) {
--- 
-2.9.3
-
-From edcb4a0259d2833904e7d1aac7c74e1aa49cea5b Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 8 May 2017 12:25:54 +0900
-Subject: [PATCH] ui/gtk3: Hide emoji variants by default
-
-The emoji variants, i.e. skin colors and additional items, would not
-be used in most cases and it is nice to hide it by default.
-Now added GtkMenu and changed GtkWindow to GtkApplicationWindow
-for the menu.
-
-R=penghuang@google.com
-
-Review URL: https://codereview.appspot.com/322970043
----
- ui/gtk3/emojier.vala | 299 ++++++++++++++++++++++++++++++++++++---------------
- 1 file changed, 211 insertions(+), 88 deletions(-)
-
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 76d5fed..db7520a 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -20,7 +20,7 @@
-  * USA
-  */
- 
--class IBusEmojier : Gtk.Window {
-+class IBusEmojier : Gtk.ApplicationWindow {
-     private class EEntry : Gtk.SearchEntry {
-         public EEntry() {
-             GLib.Object(
-@@ -97,6 +97,15 @@ class IBusEmojier : Gtk.Window {
-                 set_label(text);
-         }
-     }
-+    private class EGoldLabel : Gtk.Label {
-+        public EGoldLabel(string text) {
-+            GLib.Object(
-+                name : "IBusEmojierGoldLabel"
-+            );
-+            if (text != "")
-+                set_label(text);
-+        }
-+    }
-     private class EPaddedLabel : Gtk.Label {
-         public EPaddedLabel(string          text,
-                             Gtk.Align       align) {
-@@ -150,11 +159,21 @@ class IBusEmojier : Gtk.Window {
-             var vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
-             set_custom_title(vbox);
-             var label = new Gtk.Label(title);
--            label.get_style_context().add_class("title");
-+            label.get_style_context().add_class(Gtk.STYLE_CLASS_TITLE);
-             vbox.pack_start(label, true, false, 0);
-             m_lang_label = new Gtk.Label(null);
--            m_lang_label.get_style_context().add_class("subtitle");
-+            m_lang_label.get_style_context().add_class(
-+                    Gtk.STYLE_CLASS_SUBTITLE);
-             vbox.pack_start(m_lang_label, true, false, 0);
-+
-+            var menu = new GLib.Menu();
-+            menu.append(_("Show emoji variants"), "win.variant");
-+            var menu_button = new Gtk.MenuButton();
-+            menu_button.set_direction(Gtk.ArrowType.NONE);
-+            menu_button.set_valign(Gtk.Align.CENTER);
-+            menu_button.set_menu_model(menu);
-+            menu_button.set_tooltip_text(_("Menu"));
-+            pack_end(menu_button);
-         }
-         public void set_lang_label(string str) {
-             m_lang_label.set_text(str);
-@@ -167,7 +186,13 @@ class IBusEmojier : Gtk.Window {
-     }
- 
-     private const uint EMOJI_GRID_PAGE = 10;
-+    private const string EMOJI_CATEGORY_FAVORITES = N_("Favorites");
-     private const string EMOJI_CATEGORY_OTHERS = N_("Others");
-+    private const unichar[] EMOJI_VARIANT_LIST = {
-+            0x1f3fb, 0x1f3fc, 0x1f3fd, 0x1f3fe, 0x1f3ff, 0x200d };
-+    private const GLib.ActionEntry[] m_action_entries = {
-+        { "variant", check_action_variant_cb, null, "false", null }
-+    };
- 
-     // Set the actual default values in the constructor
-     // because these fields are used for class_init() and static functions,
-@@ -180,18 +205,22 @@ class IBusEmojier : Gtk.Window {
-     private static bool m_has_partial_match;
-     private static uint m_partial_match_length;
-     private static uint m_partial_match_condition;
--    private static GLib.HashTable<string, GLib.SList>?
-+    private static bool m_show_emoji_variant = false;
-+    private static GLib.HashTable<string, GLib.SList<string>>?
-             m_annotation_to_emojis_dict;
-     private static GLib.HashTable<string, IBus.EmojiData>?
-             m_emoji_to_data_dict;
--    private static GLib.HashTable<string, GLib.SList>?
-+    private static GLib.HashTable<string, GLib.SList<string>>?
-             m_category_to_emojis_dict;
-+    private static GLib.HashTable<string, GLib.SList<string>>?
-+            m_emoji_to_emoji_variants_dict;
- 
-     private ThemedRGBA m_rgba;
-     private Gtk.Box m_vbox;
-     private ETitleLabelBox m_title;
-     private EEntry m_entry;
-     private string? m_backward;
-+    private int m_backward_index = -1;
-     private EScrolledWindow? m_scrolled_window = null;
-     private EListBox m_list_box;
-     private bool m_is_running = false;
-@@ -200,7 +229,7 @@ class IBusEmojier : Gtk.Window {
-     private string? m_result;
-     private string? m_unicode_point = null;
-     private bool m_candidate_panel_is_visible;
--    int m_category_active_index;
-+    private int m_category_active_index;
-     private IBus.LookupTable m_lookup_table;
-     private Gtk.Label[] m_candidates;
-     private bool m_enter_notify_enable = true;
-@@ -223,6 +252,7 @@ class IBusEmojier : Gtk.Window {
-             focus_visible : true
-         );
- 
-+        add_action_entries(m_action_entries, this);
-         if (m_current_lang_id == null)
-             m_current_lang_id = "en";
-         if (m_emoji_font_family == null)
-@@ -270,6 +300,13 @@ class IBusEmojier : Gtk.Window {
-                         "rgba(%u, %u, %u, %lf); ".printf(
-                         bg_red, bg_green, bg_blue, bg_alpha) +
-                 "border-width: 4px; border-radius: 3px; }";
-+        data += "#IBusEmojierGoldLabel { color: " +
-+                        "rgba(%u, %u, %u, %lf); ".printf(
-+                        fg_red, fg_green, fg_blue, fg_alpha) +
-+                "font-family: %s; font-size: %dpt; ".printf(
-+                        m_emoji_font_family, m_emoji_font_size) +
-+                "background-color: #b09c5f; " +
-+                "border-width: 4px; border-radius: 3px; }";
- 
-         Gtk.CssProvider css_provider = new Gtk.CssProvider();
-         try {
-@@ -317,10 +354,7 @@ class IBusEmojier : Gtk.Window {
-         });
- 
-         candidate_clicked.connect((i, b, s) => {
--            IBus.Text candidate = m_lookup_table.get_candidate(i);
--            m_result = candidate.text;
--            m_loop.quit();
--            hide_candidate_panel();
-+            candidate_panel_select_index(i);
-         });
- 
-         if (m_annotation_to_emojis_dict == null) {
-@@ -345,14 +379,17 @@ class IBusEmojier : Gtk.Window {
- 
-     private static void init_emoji_dict() {
-         m_annotation_to_emojis_dict =
--                new GLib.HashTable<string, GLib.SList>(GLib.str_hash,
--                                                       GLib.str_equal);
-+                new GLib.HashTable<string, GLib.SList<string>>(GLib.str_hash,
-+                                                               GLib.str_equal);
-         m_emoji_to_data_dict =
-                 new GLib.HashTable<string, IBus.EmojiData>(GLib.str_hash,
-                                                            GLib.str_equal);
-         m_category_to_emojis_dict =
--                new GLib.HashTable<string, GLib.SList>(GLib.str_hash,
--                                                       GLib.str_equal);
-+                new GLib.HashTable<string, GLib.SList<string>>(GLib.str_hash,
-+                                                               GLib.str_equal);
-+        m_emoji_to_emoji_variants_dict =
-+                new GLib.HashTable<string, GLib.SList<string>>(GLib.str_hash,
-+                                                               GLib.str_equal);
-     }
- 
- 
-@@ -390,14 +427,16 @@ class IBusEmojier : Gtk.Window {
-             }
-             if (!has_emoji) {
-                 hits.append(emoji);
--                m_annotation_to_emojis_dict.replace(annotation, hits.copy());
-+                m_annotation_to_emojis_dict.replace(
-+                        annotation,
-+                        hits.copy_deep(GLib.strdup));
-             }
-         }
-     }
- 
- 
-     private static string utf8_down(string str) {
--        GLib.StringBuilder buff = new GLib.StringBuilder();
-+        var buff = new GLib.StringBuilder();
-         int length = str.char_count();
-         for (int i = 0; i < length; i++) {
-             buff.append_unichar(str.get_char(0).tolower());
-@@ -407,23 +446,8 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private static string utf8_title(string str) {
--        StringBuilder buff = new StringBuilder();
--        int length = str.char_count();
--        for (int i = 0; i < length; i++) {
--            unichar ch = str.get_char(0);
--            if (i == 0)
--                buff.append_unichar(ch.toupper());
--            else
--                buff.append_unichar(ch);
--            str = str.next_char();
--        }
--        return buff.str;
--    }
--
--
-     private static string utf8_code_point(string str) {
--        StringBuilder buff = new StringBuilder();
-+        var buff = new GLib.StringBuilder();
-         int length = str.char_count();
-         for (int i = 0; i < length; i++) {
-             unichar ch = str.get_char(0);
-@@ -438,7 +462,7 @@ class IBusEmojier : Gtk.Window {
- 
- 
-     private static string utf8_entity(string str) {
--        GLib.StringBuilder buff = new GLib.StringBuilder();
-+        var buff = new GLib.StringBuilder();
-         int length = str.char_count();
-         for (int i = 0; i < length; i++) {
-             unichar ch = str.get_char(0);
-@@ -503,8 +527,19 @@ class IBusEmojier : Gtk.Window {
-             update_annotations_with_description (data, description);
-             m_emoji_to_data_dict.replace(emoji, data);
-         } else {
--            unowned IBus.EmojiData? en_data =
--                    m_emoji_to_data_dict.lookup(emoji);
-+            unowned IBus.EmojiData? en_data = null;
-+            // If emoji presentation (+= 0xfe0f) is already saved in dict,
-+            // update it instead of no presentation.
-+            // emoji-test.txt has all emoji presentations but $lang.xml has
-+            // some no emoji presentations.
-+            if (emoji.chr(-1, 0xfe0f) == null) {
-+                var buff = new GLib.StringBuilder();
-+                buff.append(emoji);
-+                buff.append_unichar(0xfe0f);
-+                en_data = m_emoji_to_data_dict.lookup(buff.str);
-+            }
-+            if (en_data == null)
-+                en_data = m_emoji_to_data_dict.lookup(emoji);
-             if (en_data == null) {
-                 m_emoji_to_data_dict.insert(emoji, data);
-                 return;
-@@ -536,6 +571,36 @@ class IBusEmojier : Gtk.Window {
-         if (category == "")
-             category = EMOJI_CATEGORY_OTHERS;
-         if (lang == "en") {
-+            bool has_variant = false;
-+            foreach (unichar ch in EMOJI_VARIANT_LIST) {
-+                if (emoji.chr(-1, ch) != null) {
-+                    has_variant = true;
-+                    break;
-+                }
-+            }
-+            // If emoji includes variants (skin colors and items),
-+            // it's escaped in m_emoji_to_emoji_variants_dict and
-+            // not shown by default.
-+            if (has_variant) {
-+                unichar base_ch = emoji.get_char();
-+                string base_emoji  = base_ch.to_string();
-+                var buff = new GLib.StringBuilder();
-+                buff.append_unichar(base_ch);
-+                buff.append_unichar(0xfe0f);
-+                if (m_emoji_to_data_dict.lookup(buff.str) != null)
-+                    base_emoji = buff.str;
-+                unowned GLib.SList<string>? variants =
-+                    m_emoji_to_emoji_variants_dict.lookup(base_emoji);
-+                if (variants.find_custom(emoji, GLib.strcmp) == null) {
-+                    if (variants == null)
-+                        variants.append(base_emoji);
-+                    variants.append(emoji);
-+                    m_emoji_to_emoji_variants_dict.replace(
-+                            base_emoji,
-+                            variants.copy_deep(GLib.strdup));
-+                }
-+                return;
-+            }
-             bool has_emoji = false;
-             unowned GLib.SList<string> hits =
-                     m_category_to_emojis_dict.lookup(category);
-@@ -547,7 +612,8 @@ class IBusEmojier : Gtk.Window {
-             }
-             if (!has_emoji) {
-                 hits.append(emoji);
--                m_category_to_emojis_dict.replace(category, hits.copy());
-+                m_category_to_emojis_dict.replace(category,
-+                                                  hits.copy_deep(GLib.strdup));
-             }
-         }
-     }
-@@ -588,14 +654,15 @@ class IBusEmojier : Gtk.Window {
-         m_list_box.row_activated.connect((box, gtkrow) => {
-             m_category_active_index = 0;
-             EBoxRow row = gtkrow as EBoxRow;
--            show_emoji_for_category(row);
-+            show_emoji_for_category(row.text);
-         });
- 
-         uint n = 1;
-         if (m_favorites.length > 0) {
--            EBoxRow row = new EBoxRow("@favorites");
-+            EBoxRow row = new EBoxRow(EMOJI_CATEGORY_FAVORITES);
-             EPaddedLabelBox widget =
--                    new EPaddedLabelBox(_("Favorites"), Gtk.Align.CENTER);
-+                    new EPaddedLabelBox(_(EMOJI_CATEGORY_FAVORITES),
-+                                        Gtk.Align.CENTER);
-             row.add(widget);
-             m_list_box.add(row);
-             if (n++ == m_category_active_index)
-@@ -617,10 +684,8 @@ class IBusEmojier : Gtk.Window {
-             if (category == EMOJI_CATEGORY_OTHERS)
-                continue;
-             EBoxRow row = new EBoxRow(category);
--            string locale_category = _(category);
-             EPaddedLabelBox widget =
--                    new EPaddedLabelBox(utf8_title(locale_category),
--                                        Gtk.Align.CENTER);
-+                    new EPaddedLabelBox(_(category), Gtk.Align.CENTER);
-             row.add(widget);
-             m_list_box.add(row);
-             if (n++ == m_category_active_index)
-@@ -635,23 +700,40 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private void show_emoji_for_category(EBoxRow row) {
--        if (row.text == "@favorites") {
-+    private void show_emoji_for_category(string category) {
-+        if (category == EMOJI_CATEGORY_FAVORITES) {
-             m_lookup_table.clear();
-             foreach (unowned string favorate in m_favorites) {
-                 IBus.Text text = new IBus.Text.from_string(favorate);
-                 m_lookup_table.append_candidate(text);
-             }
--            m_backward = _("Favorites");
-+            m_backward = category;
-         } else {
-             unowned GLib.SList<unowned string> emojis =
--                    m_category_to_emojis_dict.lookup(row.text);
-+                    m_category_to_emojis_dict.lookup(category);
-             m_lookup_table.clear();
-             foreach (unowned string emoji in emojis) {
-                 IBus.Text text = new IBus.Text.from_string(emoji);
-                 m_lookup_table.append_candidate(text);
-             }
--            m_backward = utf8_title(row.text);
-+            m_backward = category;
-+        }
-+        // Restore the cursor position before the special table of
-+        // emoji variants is shown.
-+        if (m_backward_index >= 0) {
-+            m_lookup_table.set_cursor_pos((uint)m_backward_index);
-+            m_backward_index = -1;
-+        }
-+        show_candidate_panel();
-+    }
-+
-+
-+    private void show_emoji_variants(GLib.SList<string>? emojis) {
-+        m_backward_index = (int)m_lookup_table.get_cursor_pos();
-+        m_lookup_table.clear();
-+        foreach (unowned string emoji in emojis) {
-+            IBus.Text text = new IBus.Text.from_string(emoji);
-+            m_lookup_table.append_candidate(text);
-         }
-         show_candidate_panel();
-     }
-@@ -696,8 +778,8 @@ class IBusEmojier : Gtk.Window {
-     private bool check_unicode_point() {
-         string annotation = m_entry.get_text();
-         m_unicode_point = null;
--        GLib.StringBuilder buff = new GLib.StringBuilder();
--        GLib.StringBuilder retval = new GLib.StringBuilder();
-+        var buff = new GLib.StringBuilder();
-+        var retval = new GLib.StringBuilder();
-         for (int i = 0; i < annotation.char_count(); i++) {
-             unichar ch = annotation.get_char(i);
-             if (ch == 0)
-@@ -727,7 +809,7 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    public void update_candidate_window() {
-+    private void update_candidate_window() {
-         string annotation = m_entry.get_text();
-         if (annotation.length == 0) {
-             hide_candidate_panel();
-@@ -795,6 +877,22 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
-+    private void show_code_point_description(string text) {
-+        EPaddedLabelBox widget_code = new EPaddedLabelBox(
-+                    _("Code point: %s").printf(utf8_code_point(text)),
-+                    Gtk.Align.START);
-+        m_vbox.add(widget_code);
-+        widget_code.show_all();
-+        if (m_emoji_to_emoji_variants_dict.lookup(text) != null) {
-+            EPaddedLabelBox widget_has_variant = new EPaddedLabelBox(
-+                    _("Has emoji variants"),
-+                    Gtk.Align.START);
-+            m_vbox.add(widget_has_variant);
-+            widget_has_variant.show_all();
-+        }
-+    }
-+
-+
-     private void show_candidate_panel() {
-         remove_all_children();
-         set_fixed_size();
-@@ -823,18 +921,27 @@ class IBusEmojier : Gtk.Window {
-         EGrid grid = new EGrid();
-         int n = 0;
-         for (uint i = page_start_pos; i < page_end_pos; i++) {
--            IBus.Text candidate = m_lookup_table.get_candidate(i);
-+            string text = m_lookup_table.get_candidate(i).text;
-+            bool has_variant =
-+                    (m_emoji_to_emoji_variants_dict.lookup(text) != null);
-             Gtk.Label label;
--            if (i == cursor)
--                label = new ESelectedLabel(candidate.text) as Gtk.Label;
--            else
--                label = new EWhiteLabel(candidate.text) as Gtk.Label;
--            if (candidate.text.char_count() > 2) {
-+            // If 'i' is the cursor position, use the selected color.
-+            // If the emoji has emoji variants, use the gold color.
-+            // Otherwise the white color.
-+            if (i == cursor) {
-+                label = new ESelectedLabel(text) as Gtk.Label;
-+            } else if (m_show_emoji_variant && has_variant &&
-+                       m_backward_index < 0) {
-+                label = new EGoldLabel(text) as Gtk.Label;
-+            } else {
-+                label = new EWhiteLabel(text) as Gtk.Label;
-+            }
-+            if (text.char_count() > 2) {
-                 string font_family = m_emoji_font_family;
-                 int font_size = m_emoji_font_size - 2;
-                 string emoji_font = "%s %d".printf(font_family, font_size);
-                 string markup = "<span font=\"%s\">%s</span>".
--                        printf(emoji_font, utf8_entity(candidate.get_text()));
-+                        printf(emoji_font, utf8_entity(text));
-                 label.set_markup(markup);
-             }
-             label.set_halign(Gtk.Align.FILL);
-@@ -879,9 +986,8 @@ class IBusEmojier : Gtk.Window {
-             show_arrow_buttons();
-             m_vbox.add(grid);
-             grid.show_all();
--            IBus.Text candidate = m_lookup_table.get_candidate(cursor);
--            unowned IBus.EmojiData? data =
--                    m_emoji_to_data_dict.lookup(candidate.text);
-+            string text = m_lookup_table.get_candidate(cursor).text;
-+            unowned IBus.EmojiData? data = m_emoji_to_data_dict.lookup(text);
-             if (data == null) {
-                 // TODO: Provide a custom description and annotation for
-                 // the favorite emojis.
-@@ -890,12 +996,7 @@ class IBusEmojier : Gtk.Window {
-                         Gtk.Align.START);
-                 m_vbox.add(widget);
-                 widget.show_all();
--                EPaddedLabelBox widget_code = new EPaddedLabelBox(
--                        _("Code point: %s").printf(
--                                utf8_code_point(candidate.text)),
--                        Gtk.Align.START);
--                m_vbox.add(widget_code);
--                widget_code.show_all();
-+                show_code_point_description(text);
-                 return;
-             } else {
-                 unowned string description = data.get_description();
-@@ -907,7 +1008,7 @@ class IBusEmojier : Gtk.Window {
-             }
-             unowned GLib.SList<unowned string>? annotations =
-                     data.get_annotations();
--            GLib.StringBuilder buff = new GLib.StringBuilder();
-+            var buff = new GLib.StringBuilder();
-             int i = 0;
-             foreach (unowned string annotation in annotations) {
-                 if (i++ == 0)
-@@ -929,12 +1030,7 @@ class IBusEmojier : Gtk.Window {
-                 m_vbox.add(widget);
-                 widget.show_all();
-             }
--            EPaddedLabelBox widget_code = new EPaddedLabelBox(
--                    _("Code point: %s").printf(
--                            utf8_code_point(candidate.text)),
--                    Gtk.Align.START);
--            m_vbox.add(widget_code);
--            widget_code.show_all();
-+            show_code_point_description(text);
-         }
-     }
- 
-@@ -961,6 +1057,20 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
-+    private void candidate_panel_select_index(uint index) {
-+        string text = m_lookup_table.get_candidate(index).text;
-+        unowned GLib.SList<string>? emojis =
-+                m_emoji_to_emoji_variants_dict.lookup(text);
-+        if (m_show_emoji_variant && emojis != null &&
-+            m_backward_index < 0) {
-+            show_emoji_variants(emojis);
-+        } else {
-+            m_result = text;
-+            m_loop.quit();
-+            hide_candidate_panel();
-+        }
-+    }
-+
-     private void candidate_panel_cursor_down() {
-         enter_notify_disable_with_timer();
-         uint ncandidates = m_lookup_table.get_number_of_candidates();
-@@ -1094,8 +1204,11 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
--    private bool key_press_cursor_escape() {
--        if (m_candidate_panel_is_visible) {
-+    private bool key_press_escape() {
-+        if (m_backward_index >= 0 && m_backward != null) {
-+            show_emoji_for_category(m_backward);
-+            return true;
-+        } else if (m_candidate_panel_is_visible) {
-             hide_candidate_panel();
-             return true;
-         } else if (m_entry.get_text().length == 0) {
-@@ -1108,6 +1221,19 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
-+    private bool key_press_enter() {
-+        if (m_candidate_panel_is_visible) {
-+            uint index = m_lookup_table.get_cursor_pos();
-+            candidate_panel_select_index(index);
-+        } else if (m_category_active_index > 0) {
-+            Gtk.ListBoxRow gtkrow = m_list_box.get_selected_row();
-+            EBoxRow row = gtkrow as EBoxRow;
-+            show_emoji_for_category(row.text);
-+        }
-+        return true;
-+    }
-+
-+
-     private void entry_enter_keyval(uint keyval) {
-         unichar ch = IBus.keyval_to_unicode(keyval);
-         if (ch.iscntrl())
-@@ -1130,6 +1256,13 @@ class IBusEmojier : Gtk.Window {
-     }
- 
- 
-+    private void check_action_variant_cb(GLib.SimpleAction action,
-+                                         GLib.Variant?     parameter) {
-+        m_show_emoji_variant = !action.get_state().get_boolean();
-+        action.set_state(new GLib.Variant.boolean(m_show_emoji_variant));
-+    }
-+
-+
-     public string run(string input_context_path) {
-         assert (m_loop == null);
- 
-@@ -1187,21 +1320,11 @@ class IBusEmojier : Gtk.Window {
-          */
-         switch (keyval) {
-         case Gdk.Key.Escape:
--            if (key_press_cursor_escape())
-+            if (key_press_escape())
-                 return true;
-             break;
-         case Gdk.Key.Return:
--            if (m_candidate_panel_is_visible) {
--                uint index = m_lookup_table.get_cursor_pos();
--                IBus.Text text = m_lookup_table.get_candidate(index);
--                m_result = text.text;
--                m_loop.quit();
--                hide_candidate_panel();
--            } else if (m_category_active_index > 0) {
--                Gtk.ListBoxRow gtkrow = m_list_box.get_selected_row();
--                EBoxRow row = gtkrow as EBoxRow;
--                show_emoji_for_category(row);
--            }
-+            key_press_enter();
-             return true;
-         case Gdk.Key.BackSpace:
-             if (m_entry.get_text().len() > 0) {
-@@ -1283,7 +1406,7 @@ class IBusEmojier : Gtk.Window {
-                     return true;
-                 break;
-             case Gdk.Key.u:
--                if (key_press_cursor_escape())
-+                if (key_press_escape())
-                     return true;
-                 break;
-             case Gdk.Key.a:
--- 
-2.9.3
-
-From 5f653d3102ada9139a2bc7504d6beaee1bc9984b Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 9 May 2017 13:54:01 +0900
-Subject: [PATCH] ui/gtk3: Add ibus-emoji man mapge
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/323810043
----
- setup/ibus-setup.1.in   | 21 +++++++++++--
- tools/ibus.1.in         | 20 ++++++++++--
- ui/gtk3/Makefile.am     | 22 ++++++++++++++
- ui/gtk3/ibus-emoji.1.in | 81 +++++++++++++++++++++++++++++++++++++++++++++++++
- 4 files changed, 140 insertions(+), 4 deletions(-)
- create mode 100644 ui/gtk3/ibus-emoji.1.in
-
-diff --git a/setup/ibus-setup.1.in b/setup/ibus-setup.1.in
-index dab032e..f9b5f71 100644
---- a/setup/ibus-setup.1.in
-+++ b/setup/ibus-setup.1.in
-@@ -1,10 +1,10 @@
- .\" This file is distributed under the same license as the ibus
- .\" package.
- .\" Copyright (C) LI Daobing <lidaobing@gmail.com>, 2008.
--.\" Copyright (C) Takao Fujiwara <takao.fujiwara1@gmail.com>, 2013-2015.
-+.\" Copyright (C) Takao Fujiwara <takao.fujiwara1@gmail.com>, 2013-2017.
- .\" Copyright (c) Peng Huang <shawn.p.huang@gmail.com>, 2013.
- .\"
--.TH "IBUS-SETUP" 1 "November 2008" "@VERSION@" "User Commands"
-+.TH "IBUS-SETUP" 1 "November 2017" "@VERSION@" "User Commands"
- .SH NAME
- .B ibus-setup
- \- configuration program for ibus
-@@ -26,6 +26,23 @@ is the configuration program for IBus.
- .PP
- Homepage: https://github.com/ibus/ibus/wiki
- 
-+.SH "ADVANCED"
-+.TP
-+\fBUse system keyboard layout\fR
-+If this option is enabled, use your system keymap instead and IBus
-+does not change it with any input method engines. The system keymap is
-+decided by your desktop session. IBus calls
-+.B setxkbmap (1)
-+command internally to set the selected keymap and override the
-+system keymap if this options is disabled.
-+You can check the current keymap with
-+.B setxkbmap -query
-+command.
-+.TP
-+\fBShare the same input method among all applications\fR
-+If this option is disabled, You can choose an input metod engine
-+by input context.
-+
- .SH BUGS
- If you find a bug, please report it at http://code.google.com/p/ibus/issues/list
- 
-diff --git a/tools/ibus.1.in b/tools/ibus.1.in
-index a420567..d34a77a 100644
---- a/tools/ibus.1.in
-+++ b/tools/ibus.1.in
-@@ -1,9 +1,9 @@
- .\" This file is distributed under the same license as the ibus
- .\" package.
--.\" Copyright (C) Takao Fujiwara <takao.fujiwara1@gmail.com>, 2013.
-+.\" Copyright (C) Takao Fujiwara <takao.fujiwara1@gmail.com>, 2013-2017.
- .\" Copyright (c) Peng Huang <shawn.p.huang@gmail.com>, 2013.
- .\"
--.TH "IBUS" 1 "May 2013" "@VERSION@" "User Commands"
-+.TH "IBUS" 1 "May 2017" "@VERSION@" "User Commands"
- .SH NAME
- .B ibus
- \- command line utility for ibus
-@@ -95,6 +95,22 @@ configuration file.
- \fBwatch\fR
- Under construction.
- .TP
-+\fBemoji\fR [\fB\-\-font=FONT|\-\-lang=LANG|\-\-help|\-\-partial\-match\fR]
-+Launch IBus Emojier (
-+.B ibus\-emoji (1)
-+) and save the selected emoji to your clipboard. Can choose an emoji font with
-+.B \-\-font
-+option. Can choose a language of emoji annotations with
-+.B \-\-lang
-+option. If LANG="en", IBus Emojier loads /usr/share/ibus/dicts/emoji-en.dict .
-+.B \-\-partial\-match
-+option enables to match annotations with a partial string. These settings
-+are avaiable with
-+.B ibus\-setup (1)
-+utility.
- 
- .SH BUGS
- If you find a bug, please report it at https://github.com/ibus/ibus/issues
-+
-+.SH "SEE ALSO"
-+.BR ibus-emoji (1)
-diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
-index c557474..270ad1c 100644
---- a/ui/gtk3/Makefile.am
-+++ b/ui/gtk3/Makefile.am
-@@ -148,6 +148,8 @@ emoji_headers =         \
-     ibusemojidialog.h   \
-     $(NULL)
- 
-+man_one_in_files = ibus-emoji.1.in
-+
- # References:
- # libappindicator/src/notification-item.xml
- # libappindicator/src/notification-watcher.xml
-@@ -155,6 +157,7 @@ emoji_headers =         \
- # kdelibs/kdeui/knotifications/src/org.kde.StatusNotifierWatcher.xml
- EXTRA_DIST =                            \
-     $(emoji_headers)                    \
-+    $(man_one_in_files)                 \
-     IBusEmojiDialog-1.0.metadata        \
-     gtkpanel.xml.in                     \
-     ibus-emoji-dialog-1.0.deps          \
-@@ -253,8 +256,27 @@ MAINTAINERCLEANFILES += $(VAPIGEN_VAPIS)
- DISTCLEANFILES += $(VAPIGEN_VAPIS)
- EXTRA_DIST += $(VAPIGEN_VAPIS)
- 
-+# end of ENABLE_VAPIGEN
- endif
-+# end of HAVE_INTROSPECTION
- endif
-+
-+man_one_files = $(man_one_in_files:.1.in=.1)
-+man_one_DATA =$(man_one_files:.1=.1.gz)
-+man_onedir = $(mandir)/man1
-+%.1: %.1.in
-+	$(AM_V_GEN) sed \
-+	    -e 's|@VERSION[@]|$(VERSION)|g' $< > $@.tmp && \
-+	    mv $@.tmp $@
-+%.1.gz: %.1
-+	$(AM_V_GEN) gzip -c $< > $@.tmp && mv $@.tmp $@
-+
-+CLEANFILES += \
-+    $(man_one_DATA) \
-+    $(man_one_files) \
-+    $(NULL)
-+
-+# end of ENABLE_EMOJI_DICT
- endif
- 
- -include $(top_srcdir)/git.mk
-diff --git a/ui/gtk3/ibus-emoji.1.in b/ui/gtk3/ibus-emoji.1.in
-new file mode 100644
-index 0000000..3523271
---- /dev/null
-+++ b/ui/gtk3/ibus-emoji.1.in
-@@ -0,0 +1,81 @@
-+.\" This file is distributed under the same license as the ibus
-+.\" package.
-+.\" Copyright (C) Takao Fujiwara <takao.fujiwara1@gmail.com>, 2017.
-+.\"
-+.TH "IBUS EMOJI" 1 "May 2017" "@VERSION@" "User Commands"
-+.SH NAME
-+.B ibus emoji utility
-+\- Call the IBus emoji utility by
-+.B IBus Emojier
-+
-+.SH "SYNOPSIS"
-+.B /usr/libexec/ibus\-ui\-emojier
-+[\fIOPTION\fR]...
-+
-+.SH "DESCRIPTION"
-+
-+.PP
-+IBus Emojier provides a GUI to select an emoji by typing an emoji annotaion
-+or choosing a character with mouse click and it's designed to work as
-+an extended IBus lookup window using Space, Enter, and Arrow keys.
-+The text entry accepts an emoji annotation or Unicode points.
-+If IBus panel is launched, IBus panel calls IBus Emojier with a
-+shortcut key or the right click menu and output the selected emoji
-+on the previous focused text on an application.
-+If IBus panel is not available likes GNOME desktop, You can still use
-+.B ibus emoji
-+command to launch IBus Emojier and it saves the selected emoji in
-+your clipboard and you can paste the emoji by a paste key likes
-+Control\-v.
-+
-+.PP
-+Refer
-+.B emoji
-+section in
-+.B ibus (1)
-+for the command line options.
-+
-+.PP
-+Homepage: https://github.com/ibus/ibus/wiki
-+
-+.SH "SETTINGS"
-+.TP
-+\fBANNOTATION LANGUAGE\fR
-+You can choose a language of emoji annotations with
-+.B ibus\-setup (1).
-+.TP
-+\fBFONT\fR
-+You can choose an emoji font with
-+.B ibus\-setup (1).
-+E.g. "Noto Color Emoji", "Android Emoji" font.
-+
-+.SH "KEYBOARD OPERATIONS"
-+.TP
-+\fBControl-Shift-e\fR
-+Launch IBus Emojier. The shortcut key can be customized by
-+.B ibus\-setup (1).
-+.TP
-+\fBLeft or right arrow\fR
-+Move the selected category or emoji if an annotation is not typed.
-+Otherwise move the cursor in typed annotation.
-+.TP
-+\fBDown or up arrow\fR
-+Move the selected category or emoji.
-+.TP
-+\fBEnter\fR
-+Commit the selected emoji.
-+.TP
-+\fBEscape\fR
-+Go back to the category list from emoji list, erase a typed annotation,
-+and close IBus Emojier.
-+.TP
-+\fBSpace\fR
-+Move the selected category or emoji. If Shift\-Space is typed,
-+a space chararacter can be inserted between words in an annotation or
-+Unicode points. E.g. 1f466 Shift\-Space 1f3fb
-+.TP
-+\fBPageDown or PageUp\fR
-+Move to the next or previous page in emoji list.
-+
-+.SH BUGS
-+If you find a bug, please report it at https://github.com/ibus/ibus/issues
--- 
-2.9.3
-
-From 1159093dea3fdb8ceb62157f06b3560f8fc28378 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 9 May 2017 13:58:02 +0900
-Subject: [PATCH] Update favorites category automatically by selecting
- emoji
-
-When you commit an emoji with Emojier, Favorite category
-will be shown on GUI.
-Now a custom annotation also can be assigned to a favorite emoji
-by gsetting command.
-
-R=penghuang@google.com
-
-Review URL: https://codereview.appspot.com/312660043
----
- data/ibus.schemas.in      | 13 ++++++++++
- src/ibusemoji.h           |  2 +-
- ui/gtk3/emojier.vala      | 63 +++++++++++++++++++++++++++++++++++++++++++++--
- ui/gtk3/emojierapp.vala   | 18 ++++++++++++++
- ui/gtk3/ibusemojidialog.h | 20 ++++++++++-----
- ui/gtk3/panel.vala        | 21 +++++++++++++++-
- 6 files changed, 127 insertions(+), 10 deletions(-)
-
-diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
-index 0647004..09b6b2c 100644
---- a/data/ibus.schemas.in
-+++ b/data/ibus.schemas.in
-@@ -404,6 +404,19 @@
-       </locale>
-     </schema>
-     <schema>
-+      <key>/schemas/desktop/ibus/panel/emoji/favorite-annotations</key>
-+      <applyto>/desktop/ibus/panel/emoji/favorite-annotations</applyto>
-+      <owner>ibus</owner>
-+      <type>list</type>
-+      <default>[]</default>
-+      <list_type>string</list_type>
-+      <locale name="C">
-+        <short>favorite emoji annotation list on emoji dialog</short>
-+	    <long>You can assign an annotation for a favorite emoji
-+                  in this list.</long>
-+      </locale>
-+    </schema>
-+    <schema>
-       <key>/schemas/desktop/ibus/panel/emoji/has-partial-match</key>
-       <applyto>/desktop/ibus/panel/emoji/has-partial-match</applyto>
-       <owner>ibus</owner>
-diff --git a/src/ibusemoji.h b/src/ibusemoji.h
-index eb24fdd..4edee72 100644
---- a/src/ibusemoji.h
-+++ b/src/ibusemoji.h
-@@ -188,7 +188,7 @@ GHashTable *    ibus_emoji_dict_load            (const gchar    *path);
-  * @dict: (element-type utf8 IBusEmojiData) (transfer full): An Emoji dictionary
-  * @emoji: an emoji character
-  *
-- * Returns: An #IBusEmojiData of @emoji.
-+ * Returns: (transfer none): An #IBusEmojiData of @emoji.
-  * This API was prepared for the old dict foramat with Gir and Vala
-  * but no longer needed.
-  * Use ibus_emoji_data_load() instead.
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index db7520a..4169550 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -201,6 +201,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     private static string m_emoji_font_family;
-     private static int m_emoji_font_size;
-     private static string[] m_favorites;
-+    private static string[] m_favorite_annotations;
-     private static int m_emoji_max_seq_len;
-     private static bool m_has_partial_match;
-     private static uint m_partial_match_length;
-@@ -261,6 +262,8 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             m_emoji_font_size = 16;
-         if (m_favorites == null)
-             m_favorites = {};
-+        if (m_favorite_annotations == null)
-+            m_favorite_annotations = {};
- 
-         Gdk.Display display = Gdk.Display.get_default();
-         Gdk.Screen screen = (display != null) ?
-@@ -374,6 +377,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             }
-             make_emoji_dict(m_current_lang_id);
-         }
-+        update_favorite_emoji_dict();
-     }
- 
- 
-@@ -1263,6 +1267,49 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     }
- 
- 
-+    public static void update_favorite_emoji_dict() {
-+        if (m_emoji_to_data_dict == null ||
-+            m_annotation_to_emojis_dict == null)
-+            return;
-+
-+        for(int i = 0; i < m_favorites.length; i++) {
-+            var favorite = m_favorites[i];
-+
-+            string? annotation = "";
-+            if (i < m_favorite_annotations.length) {
-+                annotation = m_favorite_annotations[i];
-+            }
-+            if (annotation == "")
-+                continue;
-+            unowned IBus.EmojiData? data =
-+                    m_emoji_to_data_dict.lookup(favorite);
-+            if (data == null) {
-+                GLib.SList<string> new_annotations = new GLib.SList<string>();
-+                new_annotations.append(annotation);
-+                IBus.EmojiData new_data = GLib.Object.new(
-+                            typeof(IBus.EmojiData),
-+                            "emoji", favorite.dup(),
-+                            "annotations", new_annotations,
-+                            "description", annotation.dup()
-+                    ) as IBus.EmojiData;
-+                m_emoji_to_data_dict.insert(favorite, new_data);
-+            } else {
-+                unowned GLib.SList<string> annotations = data.get_annotations();
-+                if (annotations.find_custom(annotation, GLib.strcmp) == null) {
-+                    annotations.append(annotation);
-+                    data.set_annotations(annotations.copy());
-+                }
-+            }
-+            unowned GLib.SList<string> emojis =
-+                    m_annotation_to_emojis_dict.lookup(annotation);
-+            if (emojis.find_custom(favorite, GLib.strcmp) == null) {
-+                emojis.append(favorite);
-+                m_annotation_to_emojis_dict.replace(annotation, emojis.copy());
-+            }
-+        }
-+    }
-+
-+
-     public string run(string input_context_path) {
-         assert (m_loop == null);
- 
-@@ -1510,10 +1557,22 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         m_partial_match_condition = condition;
-     }
- 
--    public static void set_favorites(string[]? unowned_favorites) {
-+    public static void set_favorites(string[]? unowned_favorites,
-+                                     string[]? unowned_favorite_annotations) {
-         m_favorites = {};
--        foreach (string favorite in unowned_favorites) {
-+        m_favorite_annotations = {};
-+        for(int i = 0; i < unowned_favorites.length; i++) {
-+            string? favorite = unowned_favorites[i];
-+            // Avoid gsetting value error by manual setting
-+            GLib.return_if_fail(favorite != null);
-+            GLib.return_if_fail(favorite != "");
-             m_favorites += favorite;
-         }
-+        for(int i = 0; i < unowned_favorite_annotations.length; i++) {
-+            string? favorite_annotation = unowned_favorite_annotations[i];
-+            GLib.return_if_fail(favorite_annotation != null);
-+            m_favorite_annotations += favorite_annotation;
-+        }
-+        update_favorite_emoji_dict();
-     }
- }
-diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
-index dbdbc27..24816bb 100644
---- a/ui/gtk3/emojierapp.vala
-+++ b/ui/gtk3/emojierapp.vala
-@@ -50,6 +50,20 @@ public class EmojiApplication : Application {
-         Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
-         clipboard.set_text(emoji, -1);
-         clipboard.store();
-+
-+        var emojier_favorites = m_settings_emoji.get_strv("favorites");
-+        bool has_favorite = false;
-+        foreach (unowned string favorite in emojier_favorites) {
-+            if (favorite == emoji) {
-+                has_favorite = true;
-+                break;
-+            }
-+        }
-+        if (!has_favorite) {
-+            emojier_favorites += emoji;
-+            m_settings_emoji.set_strv("favorites", emojier_favorites);
-+        }
-+
-         m_emojier = null;
-         command_line.print("%s\n", _("Copied an emoji to your clipboard."));
-     }
-@@ -147,6 +161,10 @@ public class EmojiApplication : Application {
-         if (emoji_font != null)
-             IBusEmojier.set_emoji_font(emoji_font);
- 
-+        IBusEmojier.set_favorites(
-+                m_settings_emoji.get_strv("favorites"),
-+                m_settings_emoji.get_strv("favorite-annotations"));
-+
-         activate_dialog(command_line);
- 
-         return Posix.EXIT_SUCCESS;
-diff --git a/ui/gtk3/ibusemojidialog.h b/ui/gtk3/ibusemojidialog.h
-index 1499a3c..fb0f5a6 100644
---- a/ui/gtk3/ibusemojidialog.h
-+++ b/ui/gtk3/ibusemojidialog.h
-@@ -145,18 +145,26 @@ void          ibus_emojier_set_annotation_lang    (const gchar* lang);
-  */
- void          ibus_emojier_set_emoji_font         (const gchar* emoji_font);
- 
--#if 0
--/* TODO: set customized annotations */
- /**
-  * ibus_emojier_set_favorites:
-  * @favorites: (array length=favorites_length): A custom emoji list.
-  * @favorites_length: A length of @favorites
-- *
-- * Set emoji font on the emoji dialog
-+ * @favorite_annotations: (array length=favorite_annotations_length):
-+ *                        (nullable):
-+ *                        A custom annotation listfor @favorites.
-+ * @favorite_annotations_length: A length of @favorite_annotations
-+ *
-+ * Set custom emojis on the emoji dialog. @favorite_annotations
-+ * can be null. If you don't assign an annotation for a specific emoji,
-+ * you can pass the annotation as "", e.g.
-+ * favorite_annotations = { "", "", "my annotation" };
-  */
- void          ibus_emojier_set_favorites          (gchar**      favorites,
-                                                    int
--                                                             favorites_length);
--#endif
-+                                                   favorites_length,
-+                                                   gchar**
-+                                                   favorite_annotations,
-+                                                   int
-+                                                   favorite_annotations_length);
- G_END_DECLS
- #endif
-diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
-index 5f0009e..e4936df 100644
---- a/ui/gtk3/panel.vala
-+++ b/ui/gtk3/panel.vala
-@@ -76,6 +76,7 @@ class Panel : IBus.PanelService {
-     private IBusEmojier? m_emojier;
-     private uint m_emojier_set_emoji_lang_id;
-     private uint m_emojier_focus_commit_text_id;
-+    private string[] m_emojier_favorites = {};
-     private PropertyManager m_property_manager;
-     private PropertyPanel m_property_panel;
-     private GLib.Pid m_setup_pid = 0;
-@@ -245,6 +246,10 @@ class Panel : IBus.PanelService {
-                 set_emoji_favorites();
-         });
- 
-+        m_settings_emoji.changed["favorite-annotations"].connect((key) => {
-+                set_emoji_favorites();
-+        });
-+
-         m_settings_emoji.changed["lang"].connect((key) => {
-                 set_emoji_lang();
-         });
-@@ -778,7 +783,10 @@ class Panel : IBus.PanelService {
-     }
- 
-     private void set_emoji_favorites() {
--        IBusEmojier.set_favorites(m_settings_emoji.get_strv("favorites"));
-+        m_emojier_favorites = m_settings_emoji.get_strv("favorites");
-+        IBusEmojier.set_favorites(
-+                m_emojier_favorites,
-+                m_settings_emoji.get_strv("favorite-annotations"));
-     }
- 
-     private void set_emoji_lang() {
-@@ -1437,6 +1445,17 @@ class Panel : IBus.PanelService {
-             IBus.Text text = new IBus.Text.from_string(selected_string);
-             commit_text(text);
-             m_emojier = null;
-+            bool has_favorite = false;
-+            foreach (unowned string favorite in m_emojier_favorites) {
-+                if (favorite == selected_string) {
-+                    has_favorite = true;
-+                    break;
-+                }
-+            }
-+            if (!has_favorite) {
-+                m_emojier_favorites += selected_string;
-+                m_settings_emoji.set_strv("favorites", m_emojier_favorites);
-+            }
-             return true;
-         }
- 
--- 
-2.9.3
-
-From a897dab2957568f842b78d54dc40dd294fb6191f Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 9 May 2017 14:47:08 +0900
-Subject: [PATCH] ui/gtk3: Rename ibus-emoji.1.in to ibus-emoji.7.in
-
----
- tools/ibus.1.in                              |  2 +-
- ui/gtk3/Makefile.am                          | 18 +++++++++---------
- ui/gtk3/{ibus-emoji.1.in => ibus-emoji.7.in} |  0
- 3 files changed, 10 insertions(+), 10 deletions(-)
- rename ui/gtk3/{ibus-emoji.1.in => ibus-emoji.7.in} (100%)
-
-diff --git a/tools/ibus.1.in b/tools/ibus.1.in
-index d34a77a..9275abb 100644
---- a/tools/ibus.1.in
-+++ b/tools/ibus.1.in
-@@ -113,4 +113,4 @@ utility.
- If you find a bug, please report it at https://github.com/ibus/ibus/issues
- 
- .SH "SEE ALSO"
--.BR ibus-emoji (1)
-+.BR ibus-emoji (7)
-diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
-index 270ad1c..c79641a 100644
---- a/ui/gtk3/Makefile.am
-+++ b/ui/gtk3/Makefile.am
-@@ -148,7 +148,7 @@ emoji_headers =         \
-     ibusemojidialog.h   \
-     $(NULL)
- 
--man_one_in_files = ibus-emoji.1.in
-+man_seven_in_files = ibus-emoji.7.in
- 
- # References:
- # libappindicator/src/notification-item.xml
-@@ -157,7 +157,7 @@ man_one_in_files = ibus-emoji.1.in
- # kdelibs/kdeui/knotifications/src/org.kde.StatusNotifierWatcher.xml
- EXTRA_DIST =                            \
-     $(emoji_headers)                    \
--    $(man_one_in_files)                 \
-+    $(man_seven_in_files)               \
-     IBusEmojiDialog-1.0.metadata        \
-     gtkpanel.xml.in                     \
-     ibus-emoji-dialog-1.0.deps          \
-@@ -261,19 +261,19 @@ endif
- # end of HAVE_INTROSPECTION
- endif
- 
--man_one_files = $(man_one_in_files:.1.in=.1)
--man_one_DATA =$(man_one_files:.1=.1.gz)
--man_onedir = $(mandir)/man1
--%.1: %.1.in
-+man_seven_files = $(man_seven_in_files:.7.in=.7)
-+man_seven_DATA =$(man_seven_files:.7=.7.gz)
-+man_sevendir = $(mandir)/man7
-+%.7: %.7.in
- 	$(AM_V_GEN) sed \
- 	    -e 's|@VERSION[@]|$(VERSION)|g' $< > $@.tmp && \
- 	    mv $@.tmp $@
--%.1.gz: %.1
-+%.7.gz: %.7
- 	$(AM_V_GEN) gzip -c $< > $@.tmp && mv $@.tmp $@
- 
- CLEANFILES += \
--    $(man_one_DATA) \
--    $(man_one_files) \
-+    $(man_seven_DATA) \
-+    $(man_seven_files) \
-     $(NULL)
- 
- # end of ENABLE_EMOJI_DICT
-diff --git a/ui/gtk3/ibus-emoji.1.in b/ui/gtk3/ibus-emoji.7.in
-similarity index 100%
-rename from ui/gtk3/ibus-emoji.1.in
-rename to ui/gtk3/ibus-emoji.7.in
--- 
-2.9.3
-

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-05-31  2:06 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-31  2:06 [rpms/ibus] autotool: Removed upstreamed pathces Takao Fujiwara

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox