From fdb28fa6f0f44ed5de958574fa4248ab60744be2679d7341214a91fdb7176528 Mon Sep 17 00:00:00 2001 From: Fuminobu Takeyama Date: Fri, 13 Sep 2013 03:49:26 +0000 Subject: [PATCH] Accepting request 198743 from home:ftake:ibus-fedora improve distribution integration by adding patches used in Fedora, Debian, and Ubuntu. OBS-URL: https://build.opensuse.org/request/show/198743 OBS-URL: https://build.opensuse.org/package/show/M17N/ibus?expand=0&rev=72 --- ...bd-and-load-preload-engines-69171c9c.patch | 1960 +++++++++++++++++ ibus.changes | 9 + ibus.spec | 10 + ...il-users-customize-the-list-95fd937e.patch | 425 ++++ 4 files changed, 2404 insertions(+) create mode 100644 add-libgnomekbd-and-load-preload-engines-69171c9c.patch create mode 100644 reload-preload-engines-until-users-customize-the-list-95fd937e.patch diff --git a/add-libgnomekbd-and-load-preload-engines-69171c9c.patch b/add-libgnomekbd-and-load-preload-engines-69171c9c.patch new file mode 100644 index 0000000..a9ec55e --- /dev/null +++ b/add-libgnomekbd-and-load-preload-engines-69171c9c.patch @@ -0,0 +1,1960 @@ +From 69171c9c9446a60da1979c88478055c9df35136f Mon Sep 17 00:00:00 2001 +From: fujiwarat +Date: Wed, 5 Jun 2013 11:48:54 +0900 +Subject: [PATCH] Add libgnomekbd and load preload engines. + +This patch was generated by ftake@geeko.jp from a git repository maintained by +a upstream developer (fujiwarat). +https://github.com/fujiwarat/ibus/commits/gtk3-vala + +It allows to configure ibus automatically according to the current locale +at the first login. This feature depends on another patch +reload-preload-engines-until-users-customize-the-list-95fd937e.patch +Note that this patch set is used for Fedora, Debian, and Ubuntu since the upstream +version is not sufficient with regard to distribution integration. + +--- + bindings/vala/Gkbd-3.0.metadata | 1 + + bindings/vala/Makefile.am | 19 +- + bindings/vala/Xkl-1.0.metadata | 3 + + bindings/vala/gkbd.deps | 3 + + configure.ac | 40 ++++ + data/ibus.schemas.in | 59 +++++ + ibus-1.0.pc.in | 1 + + ibus.spec.in | 12 ++ + src/Makefile.am | 3 + + src/ibus.h | 1 + + src/ibusxkbxml.c | 466 ++++++++++++++++++++++++++++++++++++++++ + src/ibusxkbxml.h | 187 ++++++++++++++++ + ui/gtk3/Makefile.am | 36 ++++ + ui/gtk3/gkbdlayout.vala.false | 63 ++++++ + ui/gtk3/gkbdlayout.vala.true | 108 ++++++++++ + ui/gtk3/panel.vala | 216 ++++++++++++++++++- + ui/gtk3/xkblayout.vala | 431 +++++++++++++++++++++++++++++++++++++ + 17 files changed, 1645 insertions(+), 4 deletions(-) + create mode 100644 bindings/vala/Gkbd-3.0.metadata + create mode 100644 bindings/vala/Xkl-1.0.metadata + create mode 100644 bindings/vala/gkbd.deps + create mode 100644 src/ibusxkbxml.c + create mode 100644 src/ibusxkbxml.h + create mode 100644 ui/gtk3/gkbdlayout.vala.false + create mode 100644 ui/gtk3/gkbdlayout.vala.true + create mode 100644 ui/gtk3/xkblayout.vala + +diff --git a/bindings/vala/Gkbd-3.0.metadata b/bindings/vala/Gkbd-3.0.metadata +new file mode 100644 +index 0000000..661e6fd +--- /dev/null ++++ b/bindings/vala/Gkbd-3.0.metadata +@@ -0,0 +1 @@ ++Configuration cheader_filename="libgnomekbd/gkbd-configuration.h" +diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am +index be45e41..44b3e61 100644 +--- a/bindings/vala/Makefile.am ++++ b/bindings/vala/Makefile.am +@@ -28,8 +28,6 @@ vapi_deps = \ + $(top_builddir)/src/IBus-1.0.gir \ + $(NULL) + +-ibus-1.0.vapi: $(vapi_deps) +- + VAPIGEN_VAPIS = ibus-1.0.vapi + + ibus_1_0_vapi_DEPS = gio-2.0 +@@ -39,18 +37,33 @@ ibus_1_0_vapi_FILES = \ + $(srcdir)/IBus-1.0-custom.vala \ + $(NULL) + ++if ENABLE_LIBGNOMEKBD ++ibus-1.0.vapi: $(vapi_deps) gkbd.vapi ++ ++VAPIGEN_VAPIS += gkbd.vapi ++ ++gkbd_vapi_DEPS = gtk+-3.0 glib-2.0 gmodule-2.0 ++gkbd_vapi_METADATADIRS = $(srcdir) ++gkbd_vapi_FILES = /usr/share/gir-1.0/Gkbd-3.0.gir ++else ++ibus-1.0.vapi: $(vapi_deps) ++endif ++ + vapidir = $(datadir)/vala/vapi +-vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps) ++vapi_DATA = ibus-1.0.vapi ibus-1.0.deps + + MAINTAINERCLEANFILES = $(VAPIGEN_VAPIS) + + EXTRA_DIST = \ + $(VAPIGEN_VAPIS) \ ++ Gkbd-3.0.metadata \ ++ gkbd.deps \ + IBus-1.0.metadata \ + IBus-1.0-custom.vala \ + ibus-1.0.deps \ + config.vapi \ + xi.vapi \ ++ Xkl-1.0.metadata \ + $(NULL) + + -include $(top_srcdir)/git.mk +diff --git a/bindings/vala/Xkl-1.0.metadata b/bindings/vala/Xkl-1.0.metadata +new file mode 100644 +index 0000000..4961d0c +--- /dev/null ++++ b/bindings/vala/Xkl-1.0.metadata +@@ -0,0 +1,3 @@ ++Xkl cheader_filename="libxklavier/xklavier.h" ++Engine ++ .filter_events.evt ref type="X.Event" +diff --git a/bindings/vala/gkbd.deps b/bindings/vala/gkbd.deps +new file mode 100644 +index 0000000..172632c +--- /dev/null ++++ b/bindings/vala/gkbd.deps +@@ -0,0 +1,3 @@ ++gtk+-3.0 ++glib-2.0 ++gmodule-2.0 +diff --git a/configure.ac b/configure.ac +index 781bbf5..f576f46 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -237,6 +237,45 @@ else + enable_xim="no (disabled, use --enable-xim to enable)" + fi + ++# Option for XKB command. ++PKG_CHECK_MODULES(XKB, ++ [xkbfile],, ++ [XKB_LIBS="-lxkbfile"] ++) ++ ++# --enable-libgnomekbd option. ++AC_ARG_ENABLE(libgnomekbd, ++ AS_HELP_STRING([--enable-libgnomekbd], ++ [Use libgnomekbd to handle the keymaps]), ++ [enable_libgnomekbd=$enableval], ++ [enable_libgnomekbd=no] ++) ++AM_CONDITIONAL([ENABLE_LIBGNOMEKBD], [test x"$enable_libgnomekbd" = x"yes"]) ++if test x"$enable_libgnomekbd" = x"yes"; then ++ # check for libgnomekbd ++ PKG_CHECK_MODULES(LIBGNOMEKBDUI, [ ++ libgnomekbdui ++ ]) ++ PKG_CHECK_MODULES(ATK, [ ++ atk ++ ]) ++ HAVE_IBUS_GKBD=true ++else ++ enable_libgnomekbd="no (disabled, use --enable-libgnomekbd to enable)" ++ HAVE_IBUS_GKBD=false ++fi ++AC_SUBST(HAVE_IBUS_GKBD) ++ ++# Define XKB rules file ++AC_ARG_WITH(xkb-rules-xml, ++ AS_HELP_STRING([--with-xkb-rules-xml[=$DIR/evdev.xml]], ++ [Set evdev.xml file path (default: /usr/share/X11/xkb/rules/evdev.xml)]), ++ XKB_RULES_XML_FILE=$with_xkb_rules_xml, ++ XKB_RULES_XML_FILE="/usr/share/X11/xkb/rules/evdev.xml" ++) ++AC_DEFINE_UNQUOTED(XKB_RULES_XML_FILE, "$XKB_RULES_XML_FILE", ++ [Define file path of evdev.xml]) ++ + # GObject introspection + GOBJECT_INTROSPECTION_CHECK([0.6.8]) + +@@ -590,6 +629,7 @@ Build options: + Panel icon "$IBUS_ICON_KEYBOARD" + Enable surrounding-text $enable_surrounding_text + Enable libnotify $enable_libnotify ++ Build libgnomebkd $enable_libgnomekbd + Run test cases $enable_tests + ]) + +diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in +index 9cfe83b..d0b3db8 100644 +--- a/data/ibus.schemas.in ++++ b/data/ibus.schemas.in +@@ -56,6 +56,52 @@ + + + ++ /schemas/desktop/ibus/general/use_xmodmap ++ /desktop/ibus/general/use_xmodmap ++ ibus ++ bool ++ true ++ ++ Use xmodmap ++ Run xmodmap if .xmodmap/.Xmodmap exists. ++ ++ ++ ++ /schemas/desktop/ibus/general/xkb_latin_layouts ++ /desktop/ibus/general/xkb_latin_layouts ++ ibus ++ list ++ string ++ [ara,bg,cz,dev,gr,gur,in,jp(kana),mal,mkd,ru,ua] ++ ++ Latin layout which have no ASCII ++ us layout is appended to the latin layouts. variant is not needed. ++ ++ ++ ++ /schemas/desktop/ibus/general/load_xkb_layouts ++ /desktop/ibus/general/load_xkb_layouts ++ ibus ++ list ++ string ++ [us,us(chr),us(dvorak),ad,al,am,ara,az,ba,bd,be,bg,br,bt,by, ++de,dk,ca,ch,cn(tib),cz,ee,epo,es,et,fi,fo,fr, ++gb,ge,ge(dsb),ge(ru),ge(os),gh,gh(akan),gh(ewe),gh(fula),gh(ga),gh(hausa), ++gn,gr,hu,hr,ie,ie(CloGaelach),il, ++in, ++in(tel),in(bolnagri),iq,iq(ku),ir,ir(ku),is,it,jp, ++kg,kh,kz,la,latam,lk,lk(tam_unicode),lt,lv,ma,ma(tifinagh),mal,mao, ++me,mk,mm,mt,mv,ng,ng(hausa),ng,ng(igbo),ng(yoruba),nl,no,no(smi),np, ++pk,pl,pl(csb),pt,ro,rs,ru,ru(cv),ru(kom),ru(sah),ru(tt),ru(xal), ++se,si,sk,sy,sy(ku),th,tj,tr,ua,uz,vn ++] ++ ++ XKB layout list which is shown on ibus-setup ++ XKB layout list which is shown on ibus-setup. ++ The format is "layout" or "layout(variant)". ++ ++ ++ + /schemas/desktop/ibus/general/hotkey/trigger + /desktop/ibus/general/hotkey/trigger + ibus +@@ -80,6 +126,19 @@ + + + ++ /schemas/desktop/ibus/general/hotkey/triggers-no-modifiers ++ /desktop/ibus/general/hotkey/triggers-no-modifiers ++ ibus ++ list ++ string ++ [] ++ ++ Trigger shortcut keys without modifier keys ++ Trigger shortcut keys without modifier keys. ++ The list is used by ibus-gjs. ++ ++ ++ + /schemas/desktop/ibus/general/hotkey/enable_unconditional + /desktop/ibus/general/hotkey/enable_unconditional + ibus +diff --git a/ibus-1.0.pc.in b/ibus-1.0.pc.in +index 9f593ab..c93a0ed 100644 +--- a/ibus-1.0.pc.in ++++ b/ibus-1.0.pc.in +@@ -4,6 +4,7 @@ libdir=@libdir@ + includedir=@includedir@ + datadir=@datadir@ + pkgdatadir=@datadir@/ibus ++have_ibus_gkbd=@HAVE_IBUS_GKBD@ + + Name: IBus + Description: IBus Library +diff --git a/ibus.spec.in b/ibus.spec.in +index 7989949..5391538 100644 +--- a/ibus.spec.in ++++ b/ibus.spec.in +@@ -5,6 +5,7 @@ + + # Build flags + %define build_python_library 0 ++%define build_libgnomekbd 0 + + %define glib_ver %([ -a %{_libdir}/pkgconfig/glib-2.0.pc ] && pkg-config --modversion glib-2.0 | cut -d. -f 1,2 || echo -n "999") + %define gconf2_version 2.12.0 +@@ -40,6 +41,10 @@ BuildRequires: dconf-devel + BuildRequires: pygobject2-devel + BuildRequires: intltool + BuildRequires: iso-codes-devel ++%if %{build_libgnomekbd} ++BuildRequires: libxkbfile-devel ++BuildRequires: libgnomekbd-devel ++%endif + + Requires: %{name}-libs = %{version}-%{release} + Requires: %{name}-gtk2 = %{version}-%{release} +@@ -53,6 +58,9 @@ Requires: dbus-python >= %{dbus_python_version} + Requires: im-chooser >= %{im_chooser_version} + Requires: notify-python + Requires: librsvg2 ++%if %{build_libgnomekbd} ++Requires: libgnomekbd ++%endif + + Requires(post): desktop-file-utils + Requires(postun): desktop-file-utils +@@ -153,6 +161,10 @@ OPTIONS="$OPTIONS --enable-python-library" + OPTIONS="$OPTIONS --disable-python-library" + %endif + ++%if %{build_libgnomekbd} ++OPTIONS="$OPTIONS --enable-libgnomekbd" ++%endif ++ + %configure $OPTIONS + + # make -C po update-gmo +diff --git a/src/Makefile.am b/src/Makefile.am +index 7ee5df8..8fa954e 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -197,6 +197,9 @@ typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + CLEANFILES += $(dist_gir_DATA) $(typelibs_DATA) + endif + ++ibus_sources += ibusxkbxml.c ++ibus_headers += ibusxkbxml.h ++ + # gen enum types + ibusenumtypes.h: $(ibus_headers) ibusenumtypes.h.template + $(AM_V_GEN) ( top_builddir=`cd $(top_builddir) && pwd`; \ +diff --git a/src/ibus.h b/src/ibus.h +index ef811a4..f82a162 100644 +--- a/src/ibus.h ++++ b/src/ibus.h +@@ -47,6 +47,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/ibusxkbxml.c b/src/ibusxkbxml.c +new file mode 100644 +index 0000000..f815e5d +--- /dev/null ++++ b/src/ibusxkbxml.c +@@ -0,0 +1,466 @@ ++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ ++/* vim:set et sts=4: */ ++/* bus - The Input Bus ++ * Copyright (C) 2013 Takao Fujiwara ++ * Copyright (C) 2013 Peng Huang ++ * Copyright (C) 2013 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 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., 59 Temple Place - Suite 330, ++ * Boston, MA 02111-1307, USA. ++ */ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++ ++#include "ibus.h" ++#include "ibusxkbxml.h" ++ ++#ifndef XKB_RULES_XML_FILE ++#define XKB_RULES_XML_FILE "/usr/share/X11/xkb/rules/evdev.xml" ++#endif ++ ++#define IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE(o) \ ++ (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryPrivate)) ++ ++typedef struct _IBusXKBConfigRegistryPrivate IBusXKBConfigRegistryPrivate; ++ ++struct _IBusXKBConfigRegistryPrivate { ++ GHashTable *layout_list; ++ GHashTable *layout_lang; ++ GHashTable *layout_desc; ++ GHashTable *variant_desc; ++}; ++ ++ ++/* functions prototype */ ++static void ibus_xkb_config_registry_destroy ++ (IBusXKBConfigRegistry *xkb_config); ++ ++G_DEFINE_TYPE (IBusXKBConfigRegistry, ibus_xkb_config_registry, IBUS_TYPE_OBJECT) ++ ++static void ++parse_xkb_xml_languagelist_node (IBusXKBConfigRegistryPrivate *priv, ++ XMLNode *parent_node, ++ const gchar *layout_name) ++{ ++ XMLNode *node = parent_node; ++ XMLNode *sub_node; ++ GList *p; ++ GList *lang_list = NULL; ++ ++ g_assert (node != NULL); ++ g_assert (layout_name != NULL); ++ for (p = node->sub_nodes; p; p = p->next) { ++ sub_node = (XMLNode *) p->data; ++ if (g_strcmp0 (sub_node->name, "iso639Id") == 0) { ++ lang_list = g_list_append (lang_list, ++ (gpointer) g_strdup (sub_node->text)); ++ continue; ++ } ++ } ++ if (lang_list == NULL) { ++ /* some nodes have no lang */ ++ return; ++ } ++ if (g_hash_table_lookup (priv->layout_lang, layout_name) != NULL) { ++ g_warning ("duplicated name %s exists", layout_name); ++ return; ++ } ++ g_hash_table_insert (priv->layout_lang, ++ (gpointer) g_strdup (layout_name), ++ (gpointer) lang_list); ++} ++ ++static const gchar * ++parse_xkb_xml_configitem_node (IBusXKBConfigRegistryPrivate *priv, ++ XMLNode *parent_node) ++{ ++ XMLNode *node = parent_node; ++ XMLNode *sub_node; ++ GList *p; ++ gchar *name = NULL; ++ gchar *description = NULL; ++ ++ g_assert (node != NULL); ++ for (p = node->sub_nodes; p; p = p->next) { ++ sub_node = (XMLNode *) p->data; ++ if (g_strcmp0 (sub_node->name, "name") == 0) { ++ name = sub_node->text; ++ continue; ++ } ++ if (g_strcmp0 (sub_node->name, "description") == 0) { ++ description = sub_node->text; ++ continue; ++ } ++ if (g_strcmp0 (sub_node->name, "languageList") == 0) { ++ if (name == NULL) { ++ g_warning ("layout name is NULL in node %s", node->name); ++ continue; ++ } ++ parse_xkb_xml_languagelist_node (priv, sub_node, name); ++ continue; ++ } ++ } ++ if (name == NULL) { ++ g_warning ("No name in layout node"); ++ return NULL; ++ } ++ if (g_hash_table_lookup (priv->layout_desc, name) != NULL) { ++ g_warning ("duplicated name %s exists", name); ++ return name; ++ } ++ g_hash_table_insert (priv->layout_desc, ++ (gpointer) g_strdup (name), ++ (gpointer) g_strdup (description)); ++ ++ return name; ++} ++ ++static const gchar * ++parse_xkb_xml_variant_configitem_node (IBusXKBConfigRegistryPrivate *priv, ++ XMLNode *parent_node, ++ const gchar *layout_name) ++{ ++ XMLNode *node = parent_node; ++ XMLNode *sub_node; ++ GList *p; ++ gchar *name = NULL; ++ gchar *description = NULL; ++ gchar *variant_lang_name = NULL; ++ ++ g_assert (node != NULL); ++ g_assert (layout_name != NULL); ++ for (p = node->sub_nodes; p; p = p->next) { ++ sub_node = (XMLNode *) p->data; ++ if (g_strcmp0 (sub_node->name, "name") == 0) { ++ name = sub_node->text; ++ continue; ++ } ++ if (g_strcmp0 (sub_node->name, "description") == 0) { ++ description = sub_node->text; ++ continue; ++ } ++ if (g_strcmp0 (sub_node->name, "languageList") == 0) { ++ if (name == NULL) { ++ g_warning ("layout name is NULL in node %s", node->name); ++ continue; ++ } ++ variant_lang_name = g_strdup_printf ("%s(%s)", layout_name, name); ++ parse_xkb_xml_languagelist_node (priv, sub_node, variant_lang_name); ++ g_free (variant_lang_name); ++ continue; ++ } ++ } ++ if (name == NULL) { ++ g_warning ("No name in layout node"); ++ return NULL; ++ } ++ if (g_hash_table_lookup (priv->variant_desc, name) != NULL) { ++ /* This is an expected case. */ ++ return name; ++ } ++ variant_lang_name = g_strdup_printf ("%s(%s)", layout_name, name); ++ g_hash_table_insert (priv->variant_desc, ++ (gpointer) variant_lang_name, ++ (gpointer) g_strdup (description)); ++ return name; ++} ++ ++static const gchar * ++parse_xkb_xml_variant_node (IBusXKBConfigRegistryPrivate *priv, ++ XMLNode *parent_node, ++ const gchar *layout_name) ++{ ++ XMLNode *node = parent_node; ++ XMLNode *sub_node; ++ GList *p; ++ const gchar *variant_name = NULL; ++ ++ g_assert (node != NULL); ++ g_assert (layout_name != NULL); ++ for (p = node->sub_nodes; p; p = p->next) { ++ sub_node = (XMLNode *) p->data; ++ if (g_strcmp0 (sub_node->name, "configItem") == 0) { ++ variant_name = parse_xkb_xml_variant_configitem_node (priv, sub_node, layout_name); ++ continue; ++ } ++ } ++ return variant_name; ++} ++ ++static GList * ++parse_xkb_xml_variantlist_node (IBusXKBConfigRegistryPrivate *priv, ++ XMLNode *parent_node, ++ const gchar *layout_name, ++ GList *variant_list) ++{ ++ XMLNode *node = parent_node; ++ XMLNode *sub_node; ++ GList *p; ++ const gchar *variant_name = NULL; ++ ++ g_assert (node != NULL); ++ g_assert (layout_name != NULL); ++ for (p = node->sub_nodes; p; p = p->next) { ++ sub_node = (XMLNode *) p->data; ++ if (g_strcmp0 (sub_node->name, "variant") == 0) { ++ variant_name = parse_xkb_xml_variant_node (priv, sub_node, layout_name); ++ if (variant_name != NULL) { ++ variant_list = g_list_append (variant_list, ++ (gpointer) g_strdup (variant_name)); ++ } ++ continue; ++ } ++ } ++ return variant_list; ++} ++ ++static void ++parse_xkb_xml_layout_node (IBusXKBConfigRegistryPrivate *priv, ++ XMLNode *parent_node) ++{ ++ XMLNode *node = parent_node; ++ XMLNode *sub_node; ++ GList *p; ++ const gchar *name = NULL; ++ GList *variant_list = NULL; ++ ++ g_assert (node != NULL); ++ for (p = node->sub_nodes; p; p = p->next) { ++ sub_node = (XMLNode *) p->data; ++ if (g_strcmp0 (sub_node->name, "configItem") == 0) { ++ name = parse_xkb_xml_configitem_node (priv, sub_node); ++ continue; ++ } ++ if (g_strcmp0 (sub_node->name, "variantList") == 0) { ++ if (name == NULL) { ++ g_warning ("layout name is NULL in node %s", node->name); ++ continue; ++ } ++ variant_list = parse_xkb_xml_variantlist_node (priv, sub_node, ++ name, ++ variant_list); ++ continue; ++ } ++ } ++ if (g_hash_table_lookup (priv->layout_list, name) != NULL) { ++ g_warning ("duplicated name %s exists", name); ++ return; ++ } ++ g_hash_table_insert (priv->layout_list, ++ (gpointer) g_strdup (name), ++ (gpointer) variant_list); ++} ++ ++static void ++parse_xkb_xml_top_node (IBusXKBConfigRegistryPrivate *priv, ++ XMLNode *parent_node) ++{ ++ XMLNode *node = parent_node; ++ XMLNode *sub_node; ++ GList *p; ++ ++ g_assert (priv != NULL); ++ g_assert (node != NULL); ++ ++ if (g_strcmp0 (node->name, "xkbConfigRegistry") != 0) { ++ g_warning ("node has no xkbConfigRegistry name"); ++ return; ++ } ++ for (p = node->sub_nodes; p; p = p->next) { ++ sub_node = (XMLNode *) p->data; ++ if (g_strcmp0 (sub_node->name, "layoutList") == 0) { ++ break; ++ } ++ } ++ if (p == NULL) { ++ g_warning ("xkbConfigRegistry node has no layoutList node"); ++ return; ++ } ++ node = sub_node; ++ for (p = node->sub_nodes; p; p = p->next) { ++ sub_node = (XMLNode *) p->data; ++ if (g_strcmp0 (sub_node->name, "layout") == 0) { ++ parse_xkb_xml_layout_node (priv, sub_node); ++ continue; ++ } ++ } ++} ++ ++static void ++free_lang_list (GList *list) ++{ ++ GList *l = list; ++ while (l) { ++ g_free (l->data); ++ l->data = NULL; ++ l = l->next; ++ } ++ g_list_free (list); ++} ++ ++static void ++parse_xkb_config_registry_file (IBusXKBConfigRegistryPrivate *priv, ++ const gchar *file) ++{ ++ XMLNode *node; ++ ++ g_assert (file != NULL); ++ ++ priv->layout_list = g_hash_table_new_full (g_str_hash, ++ (GEqualFunc) g_str_equal, ++ (GDestroyNotify) g_free, ++ (GDestroyNotify) free_lang_list); ++ priv->layout_desc = g_hash_table_new_full (g_str_hash, ++ (GEqualFunc) g_str_equal, ++ (GDestroyNotify) g_free, ++ (GDestroyNotify) g_free); ++ priv->layout_lang = g_hash_table_new_full (g_str_hash, ++ (GEqualFunc) g_str_equal, ++ (GDestroyNotify) g_free, ++ (GDestroyNotify) free_lang_list); ++ priv->variant_desc = g_hash_table_new_full (g_str_hash, ++ (GEqualFunc) g_str_equal, ++ (GDestroyNotify) g_free, ++ (GDestroyNotify) g_free); ++ node = ibus_xml_parse_file (file); ++ parse_xkb_xml_top_node (priv, node); ++ ibus_xml_free (node); ++} ++ ++static void ++ibus_xkb_config_registry_init (IBusXKBConfigRegistry *xkb_config) ++{ ++ IBusXKBConfigRegistryPrivate *priv; ++ const gchar *file = XKB_RULES_XML_FILE; ++ ++ priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config); ++ parse_xkb_config_registry_file (priv, file); ++} ++ ++static void ++ibus_xkb_config_registry_destroy (IBusXKBConfigRegistry *xkb_config) ++{ ++ IBusXKBConfigRegistryPrivate *priv; ++ ++ g_return_if_fail (xkb_config != NULL); ++ ++ priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config); ++ ++ g_hash_table_destroy (priv->layout_list); ++ priv->layout_list = NULL; ++ g_hash_table_destroy (priv->layout_lang); ++ priv->layout_lang= NULL; ++ g_hash_table_destroy (priv->layout_desc); ++ priv->layout_desc= NULL; ++ g_hash_table_destroy (priv->variant_desc); ++ priv->variant_desc = NULL; ++ ++ IBUS_OBJECT_CLASS(ibus_xkb_config_registry_parent_class)->destroy (IBUS_OBJECT (xkb_config)); ++} ++ ++static void ++ibus_xkb_config_registry_class_init (IBusXKBConfigRegistryClass *klass) ++{ ++ IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); ++ ++ g_type_class_add_private (klass, sizeof (IBusXKBConfigRegistryPrivate)); ++ ++ ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_xkb_config_registry_destroy; ++} ++ ++IBusXKBConfigRegistry * ++ibus_xkb_config_registry_new (void) ++{ ++ IBusXKBConfigRegistry *xkb_config; ++ ++ xkb_config = IBUS_XKB_CONFIG_REGISTRY (g_object_new (IBUS_TYPE_XKB_CONFIG_REGISTRY, NULL)); ++ return xkb_config; ++} ++ ++#define TABLE_FUNC(field_name) const GHashTable * \ ++ibus_xkb_config_registry_get_##field_name (IBusXKBConfigRegistry *xkb_config) \ ++{ \ ++ IBusXKBConfigRegistryPrivate *priv; \ ++ \ ++ g_return_val_if_fail (xkb_config != NULL, NULL); \ ++ priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config); \ ++ return priv->field_name; \ ++} ++ ++TABLE_FUNC (layout_list) ++TABLE_FUNC (layout_lang) ++TABLE_FUNC (layout_desc) ++TABLE_FUNC (variant_desc) ++ ++#undef TABLE_FUNC ++ ++GList * ++ibus_xkb_config_registry_layout_list_get_layouts (IBusXKBConfigRegistry *xkb_config) ++{ ++ GHashTable *table; ++ GList *list = NULL; ++ ++ table = (GHashTable *) ++ ibus_xkb_config_registry_get_layout_list (xkb_config); ++ list = (GList *) g_hash_table_get_keys (table); ++ return list; ++} ++ ++/* vala could use GLib.List for the returned pointer and ++ * the declaration calls g_list_foreach (retval, g_free, NULL). ++ * When I think about GLib.List v.s. GLib.List, probably ++ * I think GLib.List is better for the function and set ++ * g_strdup() here. I do not know about GJS implementation. ++ */ ++#define TABLE_LOOKUP_LIST_FUNC(field_name, value) GList * \ ++ibus_xkb_config_registry_##field_name##_get_##value (IBusXKBConfigRegistry *xkb_config, const gchar *key) \ ++{ \ ++ GHashTable *table; \ ++ GList *list = NULL; \ ++ GList *retval= NULL; \ ++ GList *p = NULL; \ ++ \ ++ table = (GHashTable *) \ ++ ibus_xkb_config_registry_get_##field_name (xkb_config); \ ++ list = (GList *) g_hash_table_lookup (table, key); \ ++ retval = g_list_copy (list); \ ++ for (p = retval; p; p = p->next) { \ ++ p->data = g_strdup (p->data); \ ++ } \ ++ return retval; \ ++} ++ ++#define TABLE_LOOKUP_STRING_FUNC(field_name, value) gchar * \ ++ibus_xkb_config_registry_##field_name##_get_##value (IBusXKBConfigRegistry *xkb_config, const gchar *key) \ ++{ \ ++ GHashTable *table; \ ++ const gchar *desc = NULL; \ ++ \ ++ table = (GHashTable *) \ ++ ibus_xkb_config_registry_get_##field_name (xkb_config); \ ++ desc = (const gchar *) g_hash_table_lookup (table, key); \ ++ return g_strdup (desc); \ ++} ++ ++TABLE_LOOKUP_LIST_FUNC (layout_list, variants) ++TABLE_LOOKUP_LIST_FUNC (layout_lang, langs) ++TABLE_LOOKUP_STRING_FUNC (layout_desc, desc) ++TABLE_LOOKUP_STRING_FUNC (variant_desc, desc) ++ ++#undef TABLE_LOOKUP_LIST_FUNC ++#undef TABLE_LOOKUP_STRING_FUNC +diff --git a/src/ibusxkbxml.h b/src/ibusxkbxml.h +new file mode 100644 +index 0000000..5aa486d +--- /dev/null ++++ b/src/ibusxkbxml.h +@@ -0,0 +1,187 @@ ++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ ++/* vim:set et sts=4: */ ++/* bus - The Input Bus ++ * Copyright (C) 2013 Takao Fujiwara ++ * Copyright (C) 2013 Peng Huang ++ * Copyright (C) 2013 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 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., 59 Temple Place - Suite 330, ++ * Boston, MA 02111-1307, USA. ++ */ ++#ifndef __IBUS_XKBXML_H_ ++#define __IBUS_XKBXML_H_ ++ ++#if !defined (__IBUS_H_INSIDE__) && !defined (IBUS_COMPILATION) ++#error "Only can be included directly" ++#endif ++ ++#include "ibus.h" ++ ++/* ++ * Type macros. ++ */ ++/* define IBusXKBConfigRegistry macros */ ++#define IBUS_TYPE_XKB_CONFIG_REGISTRY \ ++ (ibus_xkb_config_registry_get_type ()) ++#define IBUS_XKB_CONFIG_REGISTRY(obj) \ ++ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistry)) ++#define IBUS_XKB_CONFIG_REGISTRY_CLASS(klass) \ ++ (G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass)) ++#define IBUS_IS_XKB_CONFIG_REGISTRY(obj) \ ++ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY)) ++#define IBUS_IS_XKB_CONFIG_REGISTRY_CLASS(klass) \ ++ (G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY)) ++#define IBUS_XKB_CONFIG_REGISTRY_GET_CLASS(obj) \ ++ (G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass)) ++ ++G_BEGIN_DECLS ++ ++typedef struct _IBusXKBConfigRegistry IBusXKBConfigRegistry; ++typedef struct _IBusXKBConfigRegistryClass IBusXKBConfigRegistryClass; ++ ++struct _IBusXKBConfigRegistry { ++ IBusObject parent; ++}; ++ ++struct _IBusXKBConfigRegistryClass { ++ IBusObjectClass parent; ++ /* signals */ ++ /*< private >*/ ++ /* padding */ ++ gpointer pdummy[8]; ++}; ++ ++ ++GType ibus_xkb_config_registry_get_type ++ (void); ++ ++/** ++ * ibus_xkb_config_registry_new: ++ * @returns: A newly allocated IBusXKBConfigRegistry ++ * ++ * New an IBusXKBConfigRegistry. ++ */ ++IBusXKBConfigRegistry * ++ ibus_xkb_config_registry_new ++ (void); ++ ++/** ++ * ibus_xkb_config_registry_get_layout_list: (skip) ++ * @xkb_config: An IBusXKBConfigRegistry. ++ * @returns: A const GHashTable ++ * ++ * a const GHashTable ++ */ ++const GHashTable * ++ ibus_xkb_config_registry_get_layout_list ++ (IBusXKBConfigRegistry *xkb_config); ++ ++/** ++ * ibus_xkb_config_registry_get_layout_lang: (skip) ++ * @xkb_config: An IBusXKBConfigRegistry. ++ * @returns: A const GHashTable ++ * ++ * a const GHashTable ++ */ ++const GHashTable * ++ ibus_xkb_config_registry_get_layout_lang ++ (IBusXKBConfigRegistry *xkb_config); ++ ++/** ++ * ibus_xkb_config_registry_get_layout_desc: (skip) ++ * @xkb_config: An IBusXKBConfigRegistry. ++ * @returns: A const GHashTable ++ * ++ * a const GHashTable ++ */ ++const GHashTable * ++ ibus_xkb_config_registry_get_layout_desc ++ (IBusXKBConfigRegistry *xkb_config); ++ ++/** ++ * ibus_xkb_config_registry_get_variant_desc: (skip) ++ * @xkb_config: An IBusXKBConfigRegistry. ++ * @returns: A const GHashTable ++ * ++ * a const GHashTable ++ */ ++const GHashTable * ++ ibus_xkb_config_registry_get_variant_desc ++ (IBusXKBConfigRegistry *xkb_config); ++ ++/** ++ * ibus_xkb_config_registry_layout_list_get_layouts: ++ * @xkb_config: An IBusXKBConfigRegistry. ++ * @returns: (transfer container) (element-type utf8): A GList of layouts ++ * ++ * a GList of layouts ++ */ ++GList * ++ ibus_xkb_config_registry_layout_list_get_layouts ++ (IBusXKBConfigRegistry *xkb_config); ++ ++/** ++ * ibus_xkb_config_registry_layout_list_get_variants: ++ * @xkb_config: An IBusXKBConfigRegistry. ++ * @layout: A layout. ++ * @returns: (transfer container) (element-type utf8): A GList ++ * ++ * a GList ++ */ ++GList * ++ ibus_xkb_config_registry_layout_list_get_variants ++ (IBusXKBConfigRegistry *xkb_config, ++ const gchar *layout); ++ ++/** ++ * ibus_xkb_config_registry_layout_lang_get_langs: ++ * @xkb_config: An IBusXKBConfigRegistry. ++ * @layout: A layout. ++ * @returns: (transfer container) (element-type utf8): A GList ++ * ++ * a GList ++ */ ++GList * ++ ibus_xkb_config_registry_layout_lang_get_langs ++ (IBusXKBConfigRegistry *xkb_config, ++ const gchar *layout); ++ ++/** ++ * ibus_xkb_config_registry_layout_desc_get_desc: ++ * @xkb_config: An IBusXKBConfigRegistry. ++ * @layout: A layout. ++ * @returns: A layout description ++ * ++ * a layout description ++ */ ++gchar * ++ ibus_xkb_config_registry_layout_desc_get_desc ++ (IBusXKBConfigRegistry *xkb_config, ++ const gchar *layout); ++ ++/** ++ * ibus_xkb_config_registry_variant_desc_get_desc: ++ * @xkb_config: An IBusXKBConfigRegistry. ++ * @variant: A variant. ++ * @returns: A variant description ++ * ++ * a variant description ++ */ ++gchar * ++ ibus_xkb_config_registry_variant_desc_get_desc ++ (IBusXKBConfigRegistry *xkb_config, ++ const gchar *variant); ++G_END_DECLS ++#endif +diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am +index 2038814..aa79eee 100644 +--- a/ui/gtk3/Makefile.am ++++ b/ui/gtk3/Makefile.am +@@ -45,6 +45,8 @@ USE_SYMBOL_ICON = FALSE + + USE_SYMBOL_ICON = FALSE + ++HAVE_IBUS_GKBD_C = $(strip $(subst false, FALSE, $(subst true, TRUE, $(HAVE_IBUS_GKBD)))) ++ + AM_CFLAGS = \ + @GLIB2_CFLAGS@ \ + @GIO2_CFLAGS@ \ +@@ -56,6 +58,8 @@ AM_CFLAGS = \ + -DBINDIR=\"$(bindir)\" \ + -DIBUS_DISABLE_DEPRECATED \ + -DSWITCHER_USE_SYMBOL_ICON=$(USE_SYMBOL_ICON) \ ++ -DHAVE_IBUS_GKBD=$(HAVE_IBUS_GKBD_C) \ ++ -DXKB_LAYOUTS_MAX_LENGTH=4 \ + -Wno-unused-variable \ + -Wno-unused-but-set-variable \ + -Wno-unused-function \ +@@ -98,12 +102,40 @@ AM_VALAFLAGS += \ + $(NULL) + endif + ++if ENABLE_LIBGNOMEKBD ++AM_CFLAGS += \ ++ @LIBGNOMEKBDUI_CFLAGS@ \ ++ @ATK_CFLAGS@ \ ++ $(NULL) ++ ++AM_LDADD += \ ++ @LIBGNOMEKBDUI_LIBS@ \ ++ @ATK_LIBS@ \ ++ $(NULL) ++ ++AM_VALAFLAGS += \ ++ --vapidir=. \ ++ --metadatadir=$(top_srcdir)/bindings/vala \ ++ --pkg=glib-2.0 \ ++ --pkg=gmodule-2.0 \ ++ --pkg=gkbd \ ++ --pkg=Xkl-1.0 \ ++ $(NULL) ++ ++$(srcdir)/gkbdlayout.vala: $(top_builddir)/bindings/vala/gkbd.vapi ++ @cp $(srcdir)/gkbdlayout.vala.true $(srcdir)/gkbdlayout.vala ++else ++$(srcdir)/gkbdlayout.vala: ++ @cp $(srcdir)/gkbdlayout.vala.false $(srcdir)/gkbdlayout.vala ++endif ++ + libexec_PROGRAMS = ibus-ui-gtk3 + + ibus_ui_gtk3_SOURCES = \ + application.vala \ + candidatearea.vala \ + candidatepanel.vala \ ++ gkbdlayout.vala \ + handle.vala \ + iconwidget.vala \ + keybindingmanager.vala \ +@@ -112,6 +144,7 @@ ibus_ui_gtk3_SOURCES = \ + property.vala \ + separator.vala \ + switcher.vala \ ++ xkblayout.vala \ + $(NULL) + + ibus_ui_gtk3_LDADD = \ +@@ -120,9 +153,12 @@ ibus_ui_gtk3_LDADD = \ + + CLEANFILES = \ + gtkpanel.xml \ ++ gkbdlayout.vala \ + $(NULL) + + EXTRA_DIST = \ ++ gkbdlayout.vala.false \ ++ gkbdlayout.vala.true \ + gtkpanel.xml.in \ + $(NULL) + +diff --git a/ui/gtk3/gkbdlayout.vala.false b/ui/gtk3/gkbdlayout.vala.false +new file mode 100644 +index 0000000..506aff2 +--- /dev/null ++++ b/ui/gtk3/gkbdlayout.vala.false +@@ -0,0 +1,63 @@ ++/* vim:set et sts=4 sw=4: ++ * ++ * ibus - The Input Bus ++ * ++ * Copyright(c) 2013 Red Hat, Inc. ++ * Copyright(c) 2013 Peng Huang ++ * Copyright(c) 2013 Takao Fujiwara ++ * ++ * 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 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 program; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307 USA ++ */ ++ ++public class GkbdLayout ++{ ++ public signal void changed(); ++ public signal void group_changed (int object); ++ ++ public GkbdLayout() { ++ } ++ ++ public string[] get_layouts() { ++ return new string[0]; ++ } ++ ++ public string[] get_group_names() { ++ return new string[0]; ++ } ++ ++ public void lock_group(int id) { ++ } ++ ++ public void start_listen() { ++ } ++ ++ public void stop_listen() { ++ } ++ ++ /* ++ public static int main(string[] args) { ++ GkbdLayout ibus_layouts = new GkbdLayout(); ++ ++ string[] layouts = ibus_layouts.get_layouts(); ++ string[] names = ibus_layouts.get_group_names(); ++ for (int i = 0; layouts != null && i < layouts.length; i++) { ++ stdout.printf("%s %s\n", layouts[i], names[i]); ++ } ++ ++ return 0; ++ } ++ */ ++} +diff --git a/ui/gtk3/gkbdlayout.vala.true b/ui/gtk3/gkbdlayout.vala.true +new file mode 100644 +index 0000000..a6e0f8d +--- /dev/null ++++ b/ui/gtk3/gkbdlayout.vala.true +@@ -0,0 +1,108 @@ ++/* vim:set et sts=4 sw=4: ++ * ++ * ibus - The Input Bus ++ * ++ * Copyright(c) 2013 Red Hat, Inc. ++ * Copyright(c) 2013 Peng Huang ++ * Copyright(c) 2013 Takao Fujiwara ++ * ++ * 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 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 program; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307 USA ++ */ ++ ++public class GkbdLayout ++{ ++ public signal void changed(); ++ public signal void group_changed (int object); ++ ++ private Gkbd.Configuration m_config = null; ++ ++ public GkbdLayout() { ++ m_config = Gkbd.Configuration.get(); ++ if (m_config != null) { ++ m_config.changed.connect(config_changed_cb); ++ m_config.group_changed.connect(config_group_changed_cb); ++ } ++ } ++ ++ ~GkbdLayout() { ++ if (m_config != null) { ++ m_config.changed.disconnect(config_changed_cb); ++ m_config.group_changed.disconnect(config_group_changed_cb); ++ /* gkbd_configuration_get reuses the object and do not ++ * destroy m_config here. */ ++ m_config.ref(); ++ m_config = null; ++ } ++ } ++ ++ private void config_changed_cb() { ++ changed(); ++ } ++ ++ private void config_group_changed_cb(int object) { ++ group_changed(object); ++ } ++ ++ public string[] get_layouts() { ++ if (m_config == null) { ++ return new string[0]; ++ } ++ return m_config.get_short_group_names(); ++ } ++ ++ public string[] get_group_names() { ++ if (m_config == null) { ++ return new string[0]; ++ } ++ return m_config.get_group_names(); ++ } ++ ++ public void lock_group(int id) { ++ if (m_config == null) { ++ return; ++ } ++ m_config.lock_group(id); ++ } ++ ++ public void start_listen() { ++ if (m_config == null) { ++ return; ++ } ++ m_config.start_listen(); ++ } ++ ++ public void stop_listen() { ++ if (m_config == null) { ++ return; ++ } ++ m_config.stop_listen(); ++ } ++ ++ /* ++ public static int main(string[] args) { ++ Gtk.init(ref args); ++ GkbdLayout ibus_layouts = new GkbdLayout(); ++ ++ string[] layouts = ibus_layouts.get_layouts(); ++ string[] names = ibus_layouts.get_group_names(); ++ for (int i = 0; layouts != null && i < layouts.length; i++) { ++ stdout.printf("%s %s\n", layouts[i], names[i]); ++ } ++ ++ return 0; ++ } ++ */ ++} +diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala +index 39aca08..fb012c3 100644 +--- a/ui/gtk3/panel.vala ++++ b/ui/gtk3/panel.vala +@@ -49,6 +49,13 @@ class Panel : IBus.PanelService { + private Gtk.CssProvider m_css_provider; + private int m_switcher_delay_time = 400; + private bool m_use_system_keyboard_layout = false; ++ private GkbdLayout m_gkbdlayout = null; ++ private XKBLayout m_xkblayout = null; ++ private string[] m_layouts = {}; ++ private string[] m_variants = {}; ++ private int m_fallback_lock_id = -1; ++ private bool m_changed_xkb_option = false; ++ private GLib.Timer m_changed_layout_timer; + private const string ACCELERATOR_SWITCH_IME_FOREWARD = "space"; + + private GLib.List m_keybindings = new GLib.List(); +@@ -91,6 +98,14 @@ class Panel : IBus.PanelService { + + ~Panel() { + unbind_switch_shortcut(); ++ ++ if (HAVE_IBUS_GKBD && m_gkbdlayout != null) { ++ m_gkbdlayout.changed.disconnect(gkbdlayout_changed_cb); ++ m_gkbdlayout.stop_listen(); ++ m_gkbdlayout = null; ++ } ++ ++ m_xkblayout = null; + } + + private void keybinding_manager_bind(KeybindingManager keybinding_manager, +@@ -405,6 +420,7 @@ class Panel : IBus.PanelService { + m_config.watch("general/hotkey", "triggers"); + m_config.watch("panel", "custom_font"); + m_config.watch("panel", "use_custom_font"); ++ init_engines_order(); + // Update m_use_system_keyboard_layout before update_engines() + // is called. + set_use_system_keyboard_layout(null); +@@ -422,6 +438,204 @@ class Panel : IBus.PanelService { + } + } + ++ private void gkbdlayout_changed_cb() { ++ /* The callback is called four times after set_layout is called ++ * so check the elapsed and take the first signal only. */ ++ double elapsed = m_changed_layout_timer.elapsed(); ++ if (elapsed < 1.0 && elapsed > 0.0) { ++ return; ++ } ++ ++ if (m_fallback_lock_id != -1) { ++ /* Call lock_group only when set_layout is called. */ ++ m_gkbdlayout.lock_group(m_fallback_lock_id); ++ m_fallback_lock_id = -1; ++ } else { ++ /* Reset default layout when gnome-control-center is called. */ ++ m_xkblayout.reset_layout(); ++ } ++ ++ update_xkb_engines(); ++ m_changed_layout_timer.reset(); ++ } ++ ++ private void init_gkbd() { ++ m_gkbdlayout = new GkbdLayout(); ++ m_gkbdlayout.changed.connect(gkbdlayout_changed_cb); ++ ++ /* Probably we cannot support both keyboard and ibus indicators ++ * How can I get the engine from keymap of group_id? ++ * e.g. 'en' could be owned by xkb:en and pinyin engines. */ ++ //m_gkbdlayout.group_changed.connect((object) => {}); ++ ++ m_changed_layout_timer = new GLib.Timer(); ++ m_changed_layout_timer.start(); ++ m_gkbdlayout.start_listen(); ++ } ++ ++ private void init_engines_order() { ++ if (m_config == null) { ++ return; ++ } ++ ++ m_xkblayout = new XKBLayout(m_config); ++ string session = Environment.get_variable("DESKTOP_SESSION"); ++ ++ if (HAVE_IBUS_GKBD && ++ session != null && session.length >= 5 && ++ session[0:5] == "gnome") { ++ init_gkbd(); ++ } ++ ++ update_xkb_engines(); ++ } ++ ++ private void update_xkb_engines() { ++ string var_layout = m_xkblayout.get_layout(); ++ string var_variant = m_xkblayout.get_variant(); ++ if (var_layout == "") { ++ return; ++ } ++ ++ m_layouts = var_layout.split(","); ++ m_variants = var_variant.split(","); ++ ++ IBus.XKBConfigRegistry registry = new IBus.XKBConfigRegistry(); ++ string[] var_xkb_engine_names = {}; ++ for (int i = 0; i < m_layouts.length; i++) { ++ string name = m_layouts[i]; ++ string lang = null; ++ ++ if (i < m_variants.length && m_variants[i] != "") { ++ name = "%s:%s".printf(name, m_variants[i]); ++ string layout = "%s(%s)".printf(name, m_variants[i]); ++ GLib.List langs = ++ registry.layout_lang_get_langs(layout); ++ if (langs.length() != 0) { ++ lang = langs.data; ++ } ++ } else { ++ name = "%s:".printf(name); ++ } ++ ++ if (lang == null) { ++ GLib.List langs = ++ registry.layout_lang_get_langs(m_layouts[i]); ++ if (langs.length() != 0) { ++ lang = langs.data; ++ } ++ } ++ ++ var_xkb_engine_names += "%s:%s:%s".printf("xkb", name, lang); ++ } ++ ++ GLib.Variant var_engines = ++ m_config.get_value("general", "preload_engines"); ++ string[] engine_names = {}; ++ bool updated_engine_names = false; ++ ++ if (var_engines != null) { ++ engine_names = var_engines.dup_strv(); ++ } ++ ++ foreach (string name in var_xkb_engine_names) { ++ if (name in engine_names) ++ continue; ++ updated_engine_names = true; ++ engine_names += name; ++ } ++ ++ if (updated_engine_names) { ++ m_config.set_value("general", ++ "preload_engines", ++ new GLib.Variant.strv(engine_names)); ++ } ++ ++ GLib.Variant var_order = ++ m_config.get_value("general", "engines_order"); ++ string[] order_names = {}; ++ bool updated_order_names = false; ++ ++ if (var_order != null) { ++ order_names = var_order.dup_strv(); ++ } ++ ++ foreach (var name in var_xkb_engine_names) { ++ if (name in order_names) ++ continue; ++ order_names += name; ++ updated_order_names = true; ++ } ++ ++ if (updated_order_names) { ++ m_config.set_value("general", ++ "engines_order", ++ new GLib.Variant.strv(order_names)); ++ } ++ } ++ ++ private void set_xkb_group_layout(IBus.EngineDesc engine) { ++ int[] retval = m_xkblayout.set_layout(engine, true); ++ if (retval[0] >= 0) { ++ /* If an XKB keymap is added into the XKB group, ++ * this._gkbdlayout.lock_group will be called after ++ * 'group-changed' signal is received. */ ++ m_fallback_lock_id = retval[0]; ++ m_changed_xkb_option = (retval[1] != 0) ? true : false; ++ } ++ } ++ ++ private bool set_gkbd_layout(IBus.EngineDesc engine) { ++ string layout = engine.get_layout(); ++ string variant = engine.get_layout_variant(); ++ ++ /* If a previous ibus engine changed XKB options, need to set the ++ * default XKB option. */ ++ if (m_changed_xkb_option == true) { ++ m_changed_xkb_option = false; ++ return false; ++ } ++ ++ if (variant != "" && variant != "default") { ++ layout = "%s(%s)".printf(layout, variant); ++ } ++ ++ int gkbd_len = m_gkbdlayout.get_group_names().length; ++ for (int i = 0; i < m_layouts.length && i < gkbd_len; i++) { ++ string sys_layout = m_layouts[i]; ++ if (i < m_variants.length && m_variants[i] != "") { ++ sys_layout = "%s(%s)".printf(sys_layout, m_variants[i]); ++ } ++ if (sys_layout == layout) { ++ m_gkbdlayout.lock_group(i); ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ private void set_layout(IBus.EngineDesc engine) { ++ string layout = engine.get_layout(); ++ ++ if (layout == "" || layout == null) { ++ return; ++ } ++ ++ if (m_xkblayout == null) { ++ init_engines_order(); ++ } ++ ++ if (HAVE_IBUS_GKBD && m_gkbdlayout != null) { ++ if (set_gkbd_layout(engine)) { ++ return; ++ } ++ set_xkb_group_layout(engine); ++ return; ++ } ++ ++ m_xkblayout.set_layout(engine); ++ } ++ + private void exec_setxkbmap(IBus.EngineDesc engine) { + string layout = engine.get_layout(); + string variant = engine.get_layout_variant(); +@@ -482,7 +696,7 @@ class Panel : IBus.PanelService { + } + // set xkb layout + if (!m_use_system_keyboard_layout) { +- exec_setxkbmap(engine); ++ set_layout(engine); + } + } + +diff --git a/ui/gtk3/xkblayout.vala b/ui/gtk3/xkblayout.vala +new file mode 100644 +index 0000000..b4b54ff +--- /dev/null ++++ b/ui/gtk3/xkblayout.vala +@@ -0,0 +1,431 @@ ++/* vim:set et sts=4 sw=4: ++ * ++ * ibus - The Input Bus ++ * ++ * Copyright(c) 2013 Red Hat, Inc. ++ * Copyright(c) 2013 Peng Huang ++ * Copyright(c) 2013 Takao Fujiwara ++ * ++ * 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 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 program; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307 USA ++ */ ++ ++public extern const bool HAVE_IBUS_GKBD; ++public extern const int XKB_LAYOUTS_MAX_LENGTH; ++ ++class XKBLayout ++{ ++ string m_xkb_command = "setxkbmap"; ++ IBus.Config m_config = null; ++ string[] m_xkb_latin_layouts = {}; ++ GLib.Pid m_xkb_pid = -1; ++ GLib.Pid m_xmodmap_pid = -1; ++ string m_xmodmap_command = "xmodmap"; ++ bool m_use_xmodmap = true; ++ string[] m_xmodmap_known_files = {".xmodmap", ".xmodmaprc", ++ ".Xmodmap", ".Xmodmaprc"}; ++ string m_default_layout = ""; ++ string m_default_variant = ""; ++ string m_default_option = ""; ++ ++ public XKBLayout(IBus.Config? config) { ++ m_config = config; ++ ++ if (config != null) { ++ var value = config.get_value("general", "xkb_latin_layouts"); ++ for (int i = 0; value != null && i < value.n_children(); i++) { ++ m_xkb_latin_layouts += ++ value.get_child_value(i).dup_string(); ++ } ++ if (m_use_xmodmap) { ++ m_use_xmodmap = config.get_value("general", "use_xmodmap").get_boolean(); ++ } ++ } ++ } ++ ++ private string get_output_from_cmdline(string arg, string element) { ++ string[] exec_command = {}; ++ exec_command += m_xkb_command; ++ exec_command += arg; ++ string standard_output = null; ++ string standard_error = null; ++ int exit_status = 0; ++ string retval = ""; ++ try { ++ GLib.Process.spawn_sync(null, ++ exec_command, ++ null, ++ GLib.SpawnFlags.SEARCH_PATH, ++ null, ++ out standard_output, ++ out standard_error, ++ out exit_status); ++ } catch (GLib.SpawnError err) { ++ stderr.printf("IBUS_ERROR: %s\n", err.message); ++ } ++ if (exit_status != 0) { ++ stderr.printf("IBUS_ERROR: %s\n", standard_error ?? ""); ++ } ++ if (standard_output == null) { ++ return ""; ++ } ++ foreach (string line in standard_output.split("\n")) { ++ if (element.length <= line.length && ++ line[0:element.length] == element) { ++ retval = line[element.length:line.length]; ++ if (retval == null) { ++ retval = ""; ++ } else { ++ retval = retval.strip(); ++ } ++ } ++ } ++ return retval; ++ } ++ ++ private void set_layout_cb(GLib.Pid pid, int status) { ++ if (m_xkb_pid != pid) { ++ stderr.printf("IBUS_ERROR: set_layout_cb has another pid\n"); ++ return; ++ } ++ GLib.Process.close_pid(m_xkb_pid); ++ m_xkb_pid = -1; ++ set_xmodmap(); ++ } ++ ++ private void set_xmodmap_cb(GLib.Pid pid, int status) { ++ if (m_xmodmap_pid != pid) { ++ stderr.printf("IBUS_ERROR: set_xmodmap_cb has another pid\n"); ++ return; ++ } ++ GLib.Process.close_pid(m_xmodmap_pid); ++ m_xmodmap_pid = -1; ++ } ++ ++ private string get_fullpath(string command) { ++ string envpath = GLib.Environment.get_variable("PATH"); ++ foreach (string dir in envpath.split(":")) { ++ string filepath = GLib.Path.build_filename(dir, command); ++ if (GLib.FileUtils.test(filepath, GLib.FileTest.EXISTS)) { ++ return filepath; ++ } ++ } ++ return ""; ++ } ++ ++ private string[] get_xkb_group_layout (string layout, ++ string variant, ++ int layouts_max_length) { ++ int group_id = 0; ++ int i = 0; ++ string[] layouts = m_default_layout.split(","); ++ string[] variants = m_default_variant.split(","); ++ string group_layouts = ""; ++ string group_variants = ""; ++ bool has_variant = false; ++ bool include_keymap = false; ++ ++ for (i = 0; i < layouts.length; i++) { ++ if (i >= layouts_max_length - 1) { ++ break; ++ } ++ ++ if (i == 0) { ++ group_layouts = layouts[i]; ++ } else { ++ group_layouts = "%s,%s".printf(group_layouts, layouts[i]); ++ } ++ ++ if (i >= variants.length) { ++ if (i == 0) { ++ group_variants = ""; ++ } else { ++ group_variants += ","; ++ } ++ if (layout == layouts[i] && variant == "") { ++ include_keymap = true; ++ group_id = i; ++ } ++ continue; ++ } ++ if (layout == layouts[i] && variant == variants[i]) { ++ include_keymap = true; ++ group_id = i; ++ } ++ ++ if (variants[i] != "") { ++ has_variant = true; ++ } ++ ++ if (i == 0) { ++ group_variants = variants[i]; ++ } else { ++ group_variants = "%s,%s".printf(group_variants, variants[i]); ++ } ++ } ++ ++ if (variant != "") { ++ has_variant = true; ++ } ++ ++ if (!include_keymap) { ++ group_layouts = "%s,%s".printf(group_layouts, layout); ++ group_variants = "%s,%s".printf(group_variants, variant); ++ group_id = i; ++ } ++ ++ if (!has_variant) { ++ group_variants = null; ++ } ++ ++ return {group_layouts, group_variants, group_id.to_string()}; ++ } ++ ++ public string[] get_variant_from_layout(string layout) { ++ int left_bracket = layout.index_of("("); ++ int right_bracket = layout.index_of(")"); ++ if (left_bracket >= 0 && right_bracket > left_bracket) { ++ return {layout[0:left_bracket] + ++ layout[right_bracket + 1:layout.length], ++ layout[left_bracket + 1:right_bracket]}; ++ } ++ return {layout, "default"}; ++ } ++ ++ public string[] get_option_from_layout(string layout) { ++ int left_bracket = layout.index_of("["); ++ int right_bracket = layout.index_of("]"); ++ if (left_bracket >= 0 && right_bracket > left_bracket) { ++ return {layout[0:left_bracket] + ++ layout[right_bracket + 1:layout.length], ++ layout[left_bracket + 1:right_bracket]}; ++ } ++ return {layout, "default"}; ++ } ++ ++ public string get_layout() { ++ return get_output_from_cmdline("-query", "layout: "); ++ } ++ ++ public string get_variant() { ++ return get_output_from_cmdline("-query", "variant: "); ++ } ++ ++ public string get_option() { ++ return get_output_from_cmdline("-query", "options: "); ++ } ++ ++ /* ++ public string get_group() { ++ return get_output_from_cmdline("--get-group", "group: "); ++ } ++ */ ++ ++ public int[] set_layout(IBus.EngineDesc engine, ++ bool use_group_layout=false) { ++ string layout = engine.get_layout(); ++ string variant = engine.get_layout_variant(); ++ string option = engine.get_layout_option(); ++ ++ assert (layout != null); ++ ++ int xkb_group_id = 0; ++ int changed_option = 0; ++ ++ if (m_xkb_pid != -1) { ++ return {-1, 0}; ++ } ++ ++ if (layout == "default" && ++ (variant == "default" || variant == "") && ++ (option == "default" || option == "")) { ++ return {-1, 0}; ++ } ++ ++ bool need_us_layout = false; ++ foreach (string latin_layout in m_xkb_latin_layouts) { ++ if (layout == latin_layout && variant != "eng") { ++ need_us_layout = true; ++ break; ++ } ++ if (variant != null && ++ "%s(%s)".printf(layout, variant) == latin_layout) { ++ need_us_layout = true; ++ break; ++ } ++ } ++ ++ int layouts_max_length = XKB_LAYOUTS_MAX_LENGTH; ++ if (need_us_layout) { ++ layouts_max_length--; ++ } ++ ++ if (m_default_layout == "") { ++ m_default_layout = get_layout(); ++ } ++ if (m_default_variant == "") { ++ m_default_variant = get_variant(); ++ } ++ if (m_default_option == "") { ++ m_default_option = get_option(); ++ } ++ ++ if (layout == "default") { ++ layout = m_default_layout; ++ variant = m_default_variant; ++ } else { ++ if (use_group_layout) { ++ if (variant == "default") { ++ variant = ""; ++ } ++ string[] retval = get_xkb_group_layout (layout, variant, ++ layouts_max_length); ++ layout = retval[0]; ++ variant = retval[1]; ++ xkb_group_id = int.parse(retval[2]); ++ } ++ } ++ ++ if (layout == "") { ++ warning("Could not get the correct layout"); ++ return {-1, 0}; ++ } ++ ++ if (variant == "default" || variant == "") { ++ variant = null; ++ } ++ ++ if (option == "default" || option == "") { ++ option = m_default_option; ++ } else { ++ if (!(option in m_default_option.split(","))) { ++ option = "%s,%s".printf(m_default_option, option); ++ changed_option = 1; ++ } else { ++ option = m_default_option; ++ } ++ } ++ ++ if (option == "") { ++ option = null; ++ } ++ ++ if (need_us_layout) { ++ layout += ",us"; ++ if (variant != null) { ++ variant += ","; ++ } ++ } ++ ++ string[] args = {}; ++ args += m_xkb_command; ++ args += "-layout"; ++ args += layout; ++ if (variant != null) { ++ args += "-variant"; ++ args += variant; ++ } ++ if (option != null) { ++ /* TODO: Need to get the session XKB options */ ++ args += "-option"; ++ args += "-option"; ++ args += option; ++ } ++ ++ GLib.Pid child_pid; ++ try { ++ GLib.Process.spawn_async(null, ++ args, ++ null, ++ GLib.SpawnFlags.DO_NOT_REAP_CHILD | ++ GLib.SpawnFlags.SEARCH_PATH, ++ null, ++ out child_pid); ++ } catch (GLib.SpawnError err) { ++ stderr.printf("Execute setxkbmap failed: %s\n", err.message); ++ return {-1, 0}; ++ } ++ m_xkb_pid = child_pid; ++ GLib.ChildWatch.add(m_xkb_pid, set_layout_cb); ++ ++ return {xkb_group_id, changed_option}; ++ } ++ ++ public void set_xmodmap() { ++ if (!m_use_xmodmap) { ++ return; ++ } ++ ++ if (m_xmodmap_pid != -1) { ++ return; ++ } ++ ++ string xmodmap_cmdpath = get_fullpath(m_xmodmap_command); ++ if (xmodmap_cmdpath == "") { ++ xmodmap_cmdpath = m_xmodmap_command; ++ } ++ string homedir = GLib.Environment.get_home_dir(); ++ foreach (string xmodmap_file in m_xmodmap_known_files) { ++ string xmodmap_filepath = GLib.Path.build_filename(homedir, xmodmap_file); ++ if (!GLib.FileUtils.test(xmodmap_filepath, GLib.FileTest.EXISTS)) { ++ continue; ++ } ++ string[] args = {xmodmap_cmdpath, xmodmap_filepath}; ++ ++ GLib.Pid child_pid; ++ try { ++ GLib.Process.spawn_async(null, ++ args, ++ null, ++ GLib.SpawnFlags.DO_NOT_REAP_CHILD | ++ GLib.SpawnFlags.SEARCH_PATH, ++ null, ++ out child_pid); ++ } catch (GLib.SpawnError err) { ++ stderr.printf("IBUS_ERROR: %s\n", err.message); ++ return; ++ } ++ m_xmodmap_pid = child_pid; ++ GLib.ChildWatch.add(m_xmodmap_pid, set_xmodmap_cb); ++ ++ break; ++ } ++ } ++ ++ public void reset_layout() { ++ m_default_layout = get_layout(); ++ m_default_variant = get_variant(); ++ m_default_option = get_option(); ++ } ++ ++ /* ++ public static int main(string[] args) { ++ IBus.Bus bus = new IBus.Bus(); ++ IBus.Config config = bus.get_config(); ++ XKBLayout xkblayout = new XKBLayout(config); ++ stdout.printf ("layout: %s\n", xkblayout.get_layout()); ++ stdout.printf ("variant: %s\n", xkblayout.get_variant()); ++ stdout.printf ("option: %s\n", xkblayout.get_option()); ++ xkblayout.set_layout("jp"); ++ if (config != null) { ++ IBus.main(); ++ } else { ++ Gtk.init (ref args); ++ Gtk.main(); ++ } ++ return 0; ++ } ++ */ ++} +-- +1.8.1.6 + diff --git a/ibus.changes b/ibus.changes index fd47487..6b251c3 100644 --- a/ibus.changes +++ b/ibus.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Thu Sep 12 17:29:24 UTC 2013 - ftake@geeko.jp + +- Add patches to configure ibus automatically according to the + current environment at the first login + * Add reload-preload-engines-until-users-customize-the-list-95fd937e.patch + * Add add-libgnomekbd-and-load-preload-engines-69171c9c.patch +- Enable the surrounding text feature + ------------------------------------------------------------------- Tue Jul 30 17:19:47 UTC 2013 - ftake@geeko.jp diff --git a/ibus.spec b/ibus.spec index 1c109ef..9ab8db8 100644 --- a/ibus.spec +++ b/ibus.spec @@ -29,6 +29,10 @@ Source2: ibus-README.suse Source3: xim.ibus.suse.template Source4: xim.d-ibus-121 Patch0: ibus-python-install-dir.patch +# PATCH-FEATURE-UPSTREAM add-libgnomekbd-and-load-preload-engines-69171c9c.patch Issue 1641 ftake@geeko.jp +Patch1: add-libgnomekbd-and-load-preload-engines-69171c9c.patch +# PATCH-FEATURE-UPSTREAM reload-preload-engines-until-users-customize-the-list-95fd937e.patch Issue 1641 ftake@geeko.jp +Patch2: reload-preload-engines-until-users-customize-the-list-95fd937e.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build PreReq: /usr/bin/touch Provides: locale(ja;ko;zh) @@ -36,6 +40,7 @@ BuildRequires: dbus-1-glib-devel BuildRequires: dbus-1-python-devel >= 0.83.0 BuildRequires: dconf-devel >= 0.7.5 BuildRequires: fdupes +BuildRequires: gconf2-devel BuildRequires: gettext-devel BuildRequires: glib2-devel >= 2.26.0 BuildRequires: gobject-introspection-devel >= 0.9.6 @@ -43,6 +48,7 @@ BuildRequires: gtk-doc >= 1.9 BuildRequires: gtk2-devel BuildRequires: intltool BuildRequires: iso-codes-devel +BuildRequires: libgnomekbd-devel BuildRequires: libnotify-devel >= 0.7 BuildRequires: libtool BuildRequires: pkg-config @@ -141,6 +147,8 @@ docs for ibus. %prep %setup -q %patch0 -p1 +%patch1 -p1 +%patch2 -p1 %build autoreconf -fi @@ -153,6 +161,8 @@ intltoolize -f --disable-gconf \ --enable-dconf \ --enable-gtk-doc \ + --enable-libgnomekbd \ + --enable-surrounding-text \ --libexecdir=%{_prefix}/%{_lib}/ibus make %{?jobs:-j %jobs} diff --git a/reload-preload-engines-until-users-customize-the-list-95fd937e.patch b/reload-preload-engines-until-users-customize-the-list-95fd937e.patch new file mode 100644 index 0000000..2ce32df --- /dev/null +++ b/reload-preload-engines-until-users-customize-the-list-95fd937e.patch @@ -0,0 +1,425 @@ +From 95fd937e14ec2d2f1de0f5fa0e186cfdf996c921 Mon Sep 17 00:00:00 2001 +From: fujiwarat +Date: Wed, 1 May 2013 13:35:01 +0900 +Subject: [PATCH] Reload preload engines until users customize the list. + +This patch was generated by ftake@geeko.jp from a git repository maintained by +a upstream developer (fujiwarat). +https://github.com/fujiwarat/ibus/commits/gtk3-vala + +The following description is from the original commit: + +The idea is, if users don't customize the preload_engines with ibus-setup, +users would prefer to load the system default engines again by login. +The gconf value 'preload_engine_mode' is +IBUS_PRELOAD_ENGINE_MODE_USER by default but set +IBUS_PRELOAD_ENGINE_MODE_LANG_RELATIVE for the initial login. +If preload_engine_mode is IBUS_PRELOAD_ENGINE_MODE_LANG_RELATIVE, +ibus-daemon loads the system preload engines by langs. +If preload_engine_mode is IBUS_PRELOAD_ENGINE_MODE_USER, +ibus-daemon do not update the gconf value preload_engines. +On the other hand, if users enable the customized engine checkbutton +on ibus-setup, ibus-setup sets 'preload_engine_mode' as +IBUS_PRELOAD_ENGINE_MODE_USER and users can customize the value +'preload_engines'. +--- + data/ibus.schemas.in | 24 +++++++++ + setup/main.py | 73 ++++++++++++++++++++++++--- + setup/setup.ui | 22 +++++++-- + src/ibustypes.h | 10 ++++ + ui/gtk3/panel.vala | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 254 insertions(+), 11 deletions(-) + +diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in +index d0b3db8..52ece27 100644 +--- a/data/ibus.schemas.in ++++ b/data/ibus.schemas.in +@@ -2,6 +2,30 @@ + + + ++ /schemas/desktop/ibus/general/preload_engine_mode ++ /desktop/ibus/general/preload_engine_mode ++ ibus ++ int ++ 0 ++ ++ Preload engine mode ++ Preload engines are loaded with this mode. ++ 0 = user customized engines. ++ 1 = language related engines. ++ ++ ++ ++ /schemas/desktop/ibus/general/preload_engines_inited ++ /desktop/ibus/general/preload_engines_inited ++ ibus ++ bool ++ false ++ ++ The key preload_engines is initialized ++ The key preload_engines is initialized ++ ++ ++ + /schemas/desktop/ibus/general/preload_engines + /desktop/ibus/general/preload_engines + ibus +diff --git a/setup/main.py b/setup/main.py +index 0281ac7..ba78eeb 100644 +--- a/setup/main.py ++++ b/setup/main.py +@@ -190,16 +190,27 @@ def __init_general(self): + self.__checkbutton_use_global_engine.connect("toggled", + self.__checkbutton_use_global_engine_toggled_cb) + ++ # set preload mode ++ preload_engine_mode = IBus.PreloadEngineMode.USER ++ variant = self.__config.get_value("general", "preload_engine_mode") ++ if variant != None: ++ preload_engine_mode = variant.get_int32() ++ button = self.__builder.get_object("checkbutton_preload_engine_mode") ++ if preload_engine_mode == IBus.PreloadEngineMode.USER: ++ button.set_active(True) ++ self.__builder.get_object("hbox_customize_active_input_methods").set_sensitive(True) ++ else: ++ button.set_active(False) ++ self.__builder.get_object("hbox_customize_active_input_methods").set_sensitive(False) ++ button.connect("toggled", self.__checkbutton_preload_engine_mode_toggled_cb) ++ + # init engine page + self.__engines = self.__bus.list_engines() + self.__combobox = self.__builder.get_object("combobox_engines") + self.__combobox.set_engines(self.__engines) + +- tmp_dict = {} +- for e in self.__engines: +- tmp_dict[e.get_name()] = e + engine_names = values.get("preload_engines", []) +- engines = [tmp_dict[name] for name in engine_names if name in tmp_dict] ++ engines = self.__get_engine_descs_from_names(engine_names) + + self.__treeview = self.__builder.get_object("treeview_engines") + self.__treeview.set_engines(engines) +@@ -243,6 +254,7 @@ def __init_ui(self): + self.__checkbutton_auto_start_toggled_cb) + + self.__config = self.__bus.get_config() ++ self.__config.connect("value-changed", self.__config_value_changed_cb) + + self.__init_hotkey() + self.__init_panel() +@@ -251,8 +263,8 @@ def __init_ui(self): + def __combobox_notify_active_engine_cb(self, combobox, property): + engine = self.__combobox.get_active_engine() + button = self.__builder.get_object("button_engine_add") +- button.set_sensitive( +- engine != None and engine not in self.__treeview.get_engines()) ++ button.set_sensitive(engine != None and \ ++ engine.get_name() not in map(lambda e: e.get_name(), self.__treeview.get_engines())) + + def __get_engine_setup_exec_args(self, engine): + args = [] +@@ -272,6 +284,13 @@ def __get_engine_setup_exec_args(self, engine): + args.append(path.basename(setup_path)) + return args + ++ def __get_engine_descs_from_names(self, engine_names): ++ tmp_dict = {} ++ for e in self.__engines: ++ tmp_dict[e.get_name()] = e ++ engines = [tmp_dict[name] for name in engine_names if name in tmp_dict] ++ return engines ++ + def __treeview_notify_cb(self, treeview, prop): + if prop.name not in ("active-engine", "engines"): + return +@@ -324,6 +343,34 @@ def __button_engine_preferences_cb(self, button): + del self.__engine_setup_exec_list[name] + self.__engine_setup_exec_list[name] = os.spawnl(os.P_NOWAIT, *args) + ++ def __checkbutton_preload_engine_mode_toggled_cb(self, button): ++ if button.get_active(): ++ variant = GLib.Variant.new_int32(IBus.PreloadEngineMode.USER) ++ self.__config.set_value("general", ++ "preload_engine_mode", ++ variant) ++ self.__builder.get_object("hbox_customize_active_input_methods").set_sensitive(True) ++ self.__treeview.notify("engines") ++ else: ++ message = _("The list of your saved input methods will be " \ ++ "cleared immediately and the list will be " \ ++ "configured by the login language every time. " \ ++ "Do you agree with this?") ++ dlg = Gtk.MessageDialog(type = Gtk.MessageType.QUESTION, ++ buttons = Gtk.ButtonsType.YES_NO, ++ message_format = message) ++ id = dlg.run() ++ dlg.destroy() ++ self.__flush_gtk_events() ++ if id != Gtk.ResponseType.YES: ++ button.set_active(True) ++ return ++ variant = GLib.Variant.new_int32(IBus.PreloadEngineMode.LANG_RELATIVE) ++ self.__config.set_value("general", ++ "preload_engine_mode", ++ variant) ++ self.__builder.get_object("hbox_customize_active_input_methods").set_sensitive(False) ++ + def __init_bus(self): + self.__bus = IBus.Bus() + if self.__bus.is_connected(): +@@ -538,8 +585,18 @@ def __checkbutton_use_global_engine_toggled_cb(self, button): + value = GLib.Variant.new_boolean(value) + self.__config.set_value("general", "use_global_engine", value) + +- def __config_value_changed_cb(self, bus, section, name, value): +- pass ++ def __config_value_changed_cb(self, bus, section, name, variant): ++ if section == 'general' and name == 'preload_engines': ++ value = [] ++ if variant != None: ++ value = variant.unpack() ++ engines = self.__get_engine_descs_from_names(value) ++ current_engines = self.__treeview.get_engines() ++ engines_csv = str.join(',', map(lambda e: e.get_name(), engines)) ++ current_engines_csv = \ ++ str.join(',', map(lambda e: e.get_name(), current_engines)) ++ if engines_csv != current_engines_csv: ++ self.__treeview.set_engines(engines) + + def __config_reloaded_cb(self, bus): + pass +diff --git a/setup/setup.ui b/setup/setup.ui +index e56e917..b3165aa 100644 +--- a/setup/setup.ui ++++ b/setup/setup.ui +@@ -669,7 +669,23 @@ + True + False + +- ++ ++ True ++ Customize active input _methods ++ True ++ True ++ False ++ Customize active input methods ++ True ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ + True + False + +@@ -858,7 +874,7 @@ + + True + True +- 0 ++ 1 + + + +@@ -905,7 +921,7 @@ You may use up/down buttons to change it.</i></small> + + False + True +- 1 ++ 2 + + + +diff --git a/src/ibustypes.h b/src/ibustypes.h +index 737b3e2..8ce5a16 100644 +--- a/src/ibustypes.h ++++ b/src/ibustypes.h +@@ -204,6 +204,16 @@ + } IBusError; + + /** ++ * IBusPreloadEngineMode: ++ * @IBUS_PRELOAD_ENGINE_MODE_USER: user custimized engines ++ * @IBUS_PRELOAD_ENGINE_MODE_LANG_RELATIVE: language related engines. ++ */ ++typedef enum { ++ IBUS_PRELOAD_ENGINE_MODE_USER = 0, ++ IBUS_PRELOAD_ENGINE_MODE_LANG_RELATIVE = 1, ++} IBusPreloadEngineMode; ++ ++/** + * IBusRectangle: + * @x: x coordinate. + * @y: y coordinate. +diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala +index fb012c3..54251e7 100644 +--- a/ui/gtk3/panel.vala ++++ b/ui/gtk3/panel.vala +@@ -413,6 +413,8 @@ class Panel : IBus.PanelService { + if (m_config != null) { + m_config.value_changed.connect(config_value_changed_cb); + m_config.watch("general", "preload_engines"); ++ m_config.watch("general", "preload_engines_inited"); ++ m_config.watch("general", "preload_engine_mode"); + m_config.watch("general", "embed_preedit_text"); + m_config.watch("general", "engines_order"); + m_config.watch("general", "switcher_delay_time"); +@@ -487,7 +489,136 @@ class Panel : IBus.PanelService { + init_gkbd(); + } + ++ GLib.Variant var_engines = ++ m_config.get_value("general", "preload_engines"); ++ string[] preload_engines = {}; ++ ++ if (var_engines != null) { ++ preload_engines = var_engines.dup_strv(); ++ } ++ ++ bool preload_engines_inited = false; ++ GLib.Variant var_preload_engines_inited = ++ m_config.get_value("general", "preload_engines_inited"); ++ ++ if (var_preload_engines_inited != null) { ++ preload_engines_inited = var_preload_engines_inited.get_boolean(); ++ } ++ ++ // Set preload_engines_inited = true for back compatibility ++ if (preload_engines.length != 0 && !preload_engines_inited) { ++ preload_engines_inited = true; ++ m_config.set_value("general", ++ "preload_engines_inited", ++ new GLib.Variant.boolean(true)); ++ } ++ + update_xkb_engines(); ++ ++ // Before update preload_engine_mode, update_xkb_engines() is called ++ // because config_value_changed_cb() calls update_im_engines(). ++ if (!preload_engines_inited) { ++ GLib.Variant variant = new GLib.Variant.int32( ++ IBus.PreloadEngineMode.LANG_RELATIVE); ++ m_config.set_value("general", ++ "preload_engine_mode", ++ variant); ++ } ++ ++ update_im_engines(); ++ ++ if (!preload_engines_inited) { ++ m_config.set_value("general", ++ "preload_engines_inited", ++ new GLib.Variant.boolean(true)); ++ } ++ } ++ ++ private bool set_lang_relative_preload_engines() { ++ string locale = Intl.setlocale(LocaleCategory.CTYPE, null); ++ ++ if (locale == null) { ++ locale = "C"; ++ } ++ ++ string lang = locale.split(".")[0]; ++ GLib.List engines = m_bus.list_engines(); ++ string[] im_engines = {}; ++ ++ for (unowned GLib.List p = engines; ++ p != null; ++ p = p.next) { ++ unowned IBus.EngineDesc engine = p.data; ++ if (engine.get_language() == lang && ++ engine.get_rank() > 0) { ++ im_engines += engine.get_name(); ++ } ++ } ++ ++ lang = lang.split("_")[0]; ++ if (im_engines.length == 0) { ++ for (unowned GLib.List p = engines; ++ p != null; ++ p = p.next) { ++ unowned IBus.EngineDesc engine = p.data; ++ if (engine.get_language() == lang && ++ engine.get_rank() > 0) { ++ im_engines += engine.get_name(); ++ } ++ } ++ } ++ ++ if (im_engines.length == 0) { ++ return false; ++ } ++ ++ GLib.Variant var_engines = ++ m_config.get_value("general", "preload_engines"); ++ string[] orig_preload_engines = {}; ++ string[] preload_engines = {}; ++ ++ if (var_engines != null) { ++ orig_preload_engines = var_engines.dup_strv(); ++ } ++ ++ // clear input method engines ++ foreach (string name in orig_preload_engines) { ++ if (name.ascii_ncasecmp("xkb:", 4) != 0) { ++ continue; ++ } ++ preload_engines += name; ++ } ++ ++ foreach (string name in im_engines) { ++ if (!(name in preload_engines)) { ++ preload_engines += name; ++ } ++ } ++ ++ if ("".joinv(",", orig_preload_engines) != ++ "".joinv(",", preload_engines)) { ++ m_config.set_value("general", ++ "preload_engines", ++ new GLib.Variant.strv(preload_engines)); ++ } ++ ++ return true; ++ } ++ ++ private void update_im_engines() { ++ int preload_engine_mode = IBus.PreloadEngineMode.USER; ++ GLib.Variant var_preload_engine_mode = ++ m_config.get_value("general", "preload_engine_mode"); ++ ++ if (var_preload_engine_mode != null) { ++ preload_engine_mode = var_preload_engine_mode.get_int32(); ++ } ++ ++ if (preload_engine_mode == IBus.PreloadEngineMode.USER) { ++ return; ++ } ++ ++ set_lang_relative_preload_engines(); + } + + private void update_xkb_engines() { +@@ -704,6 +835,11 @@ class Panel : IBus.PanelService { + string section, + string name, + Variant variant) { ++ if (section == "general" && name == "preload_engine_mode") { ++ update_im_engines(); ++ return; ++ } ++ + if (section == "general" && name == "preload_engines") { + update_engines(variant, null); + return; +-- +1.8.1.6 +