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>[<Control><Shift>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>[<Control><Shift>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="😆">>:) | >;) | >:-) | >=)</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="😣">>.<</annotation>
-+ <annotation cp="😮">:-O | :O | :-o | :o | O_O | >:O</annotation>
-+ <annotation cp="😛">:P | :-P | =P | :-p | :p | =p | :-Þ | :Þ | :þ | :-þ | :-b | :b | d:</annotation>
-+ <annotation cp="😜">>:P | X-P | x-p</annotation>
-+ <annotation cp="😓">':( | ':-( | '=(</annotation>
-+ <annotation cp="😕">>:\ | >:/ | :-/ | :-. | :/ | :\ | =/ | =\ | :L | =L</annotation>
-+ <annotation cp="😞">>:[ | :-( | :( | :-[ | :[ | =(</annotation>
-+ <annotation cp="😢">:'( | :'-( | ;( | ;-(</annotation>
-+ <annotation cp="😨">D:</annotation>
-+ <annotation cp="😳">:$ | =$</annotation>
-+ <annotation cp="😵">#-) | #) | %-) | %) | X) | X-)</annotation>
-+ <annotation cp="😠">>:( | >:-( | :@</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="❤"><3</annotation>
-+ <annotation cp="💔"></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, "<");
-+ break;
-+ case '>':
-+ g_string_append (buff, ">");
-+ break;
-+ case '&':
-+ g_string_append (buff, "&");
-+ 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("<");
-+ break;
-+ case '>':
-+ buff.append(">");
-+ break;
-+ case '&':
-+ buff.append("&");
-+ 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"><b>Keyboard Shortcuts</b></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"><b>Font and Style</b></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