public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ibus] autotool: Moved language setting on IBusEmojier to ibus-setup.
@ 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 : 424038a8b92f724fba6e28b4f204f7b0ae00bef6
Author : Takao Fujiwara <tfujiwar@redhat.com>
Date : 2017-03-27T20:36:32+09:00
Stats : +1907/-1 in 2 file(s)
URL : https://src.fedoraproject.org/rpms/ibus/c/424038a8b92f724fba6e28b4f204f7b0ae00bef6?branch=autotool
Log:
Moved language setting on IBusEmojier to ibus-setup.
Enabled strcasecmp to match emoji annotations.
Added a build error message if emoji xml files are not found.
---
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index 56a760c..95db7c3 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -1768,3 +1768,1903 @@ index 5e126e9..7da96c7 100644
--
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
+
diff --git a/ibus.spec b/ibus.spec
index ca0f66b..013f96a 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -28,7 +28,7 @@
Name: ibus
Version: 1.5.15
-Release: 4%{?dist}
+Release: 5%{?dist}
Summary: Intelligent Input Bus for Linux OS
License: LGPLv2+
Group: System Environment/Libraries
@@ -235,6 +235,7 @@ The ibus-devel-docs package contains developer documentation for IBus
%build
#autoreconf -f -i -v
+autoreconf -f -i -v
#make -C ui/gtk3 maintainer-clean-generic
#make -C tools maintainer-clean-generic
%configure \
@@ -426,6 +427,11 @@ gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || :
%{_datadir}/gtk-doc/html/*
%changelog
+* Mon Mar 27 2017 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.15-5
+- Moved language setting on IBusEmojier to ibus-setup.
+- Enabled strcasecmp to match emoji annotations.
+- Added a build error message if emoji xml files are not found.
+
* Wed Mar 15 2017 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.15-4
- Implemented Ctrl-[f|b|n|p|h|e|a|u] for cursor operations on emoji dialog
- Added XSetIOErrorHandler() for GNOME3 desktop
^ 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: Moved language setting on IBusEmojier to ibus-setup Takao Fujiwara
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox