[gnome-control-center] region: Add IBus input sources
- From: Bastien Nocera <hadess src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center] region: Add IBus input sources
- Date: Mon, 16 Jul 2012 14:17:29 +0000 (UTC)
commit 412e53079f051c7358f0200c38cd5957dc082e94
Author: Rui Matos <tiagomatos gmail com>
Date: Tue May 22 19:14:40 2012 +0200
region: Add IBus input sources
We query IBus for the available engines and present them alongside XKB
layouts.
https://bugzilla.gnome.org/show_bug.cgi?id=662489
configure.ac | 22 ++-
panels/region/gnome-region-panel-input.c | 356 +++++++++++++++++++++++++++---
2 files changed, 348 insertions(+), 30 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 4f36f1c..c091bf4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,6 +62,20 @@ else
SYSTEMD=
fi
+# IBus support
+AC_ARG_ENABLE(ibus,
+ AS_HELP_STRING([--disable-ibus],
+ [Disable IBus support]),
+ enable_ibus=$enableval,
+ enable_ibus=yes)
+
+if test "x$enable_ibus" = "xyes" ; then
+ IBUS_MODULE=ibus-1.0
+ AC_DEFINE(HAVE_IBUS, 1, [Defined if IBus support is enabled])
+else
+ IBUS_MODULE=
+fi
+
dnl ==============================================
dnl Check that we meet the dependencies
dnl ==============================================
@@ -116,7 +130,8 @@ PKG_CHECK_MODULES(PRINTERS_PANEL, $COMMON_MODULES
polkit-gobject-1 >= $POLKIT_REQUIRED_VERSION)
PKG_CHECK_MODULES(REGION_PANEL, $COMMON_MODULES
polkit-gobject-1 >= $POLKIT_REQUIRED_VERSION
- gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION)
+ gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION
+ $IBUS_MODULE)
PKG_CHECK_MODULES(SCREEN_PANEL, $COMMON_MODULES)
PKG_CHECK_MODULES(SOUND_PANEL, $COMMON_MODULES libxml-2.0
libcanberra-gtk3 >= $CANBERRA_REQUIRED_VERSION
@@ -500,4 +515,9 @@ if test "x$have_wacom" = "xyes"; then
else
AC_MSG_NOTICE([ Wacom panel disabled])
fi
+if test "x$enable_ibus" == "xyes"; then
+ AC_MSG_NOTICE([** IBus (Region panel IBus support)])
+else
+ AC_MSG_NOTICE([ Region panel IBus support disabled])
+fi
AC_MSG_NOTICE([End options])
diff --git a/panels/region/gnome-region-panel-input.c b/panels/region/gnome-region-panel-input.c
index 9e972d8..cd670d0 100644
--- a/panels/region/gnome-region-panel-input.c
+++ b/panels/region/gnome-region-panel-input.c
@@ -29,6 +29,11 @@
#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <libgnome-desktop/gnome-xkb-info.h>
+#ifdef HAVE_IBUS
+#include <ibus.h>
+#endif
+
+#include "gdm-languages.h"
#include "gnome-region-panel-input.h"
#define WID(s) GTK_WIDGET(gtk_builder_get_object (builder, s))
@@ -37,7 +42,8 @@
#define KEY_INPUT_SOURCES "sources"
-#define INPUT_SOURCE_TYPE_XKB "xkb"
+#define INPUT_SOURCE_TYPE_XKB "xkb"
+#define INPUT_SOURCE_TYPE_IBUS "ibus"
enum {
NAME_COLUMN,
@@ -50,11 +56,135 @@ static GSettings *input_sources_settings = NULL;
static gulong input_sources_settings_changed_id = 0;
static GnomeXkbInfo *xkb_info = NULL;
+#ifdef HAVE_IBUS
+static IBusBus *ibus = NULL;
+static GHashTable *ibus_engines = NULL;
+
+static const gchar *supported_ibus_engines[] = {
+ "bopomofo",
+ "pinyin",
+ "chewing",
+ "anthy",
+ "hangul",
+ NULL
+};
+#endif /* HAVE_IBUS */
+
static GtkWidget *input_chooser_new (GtkWindow *main_window,
GtkListStore *active_sources);
static gboolean input_chooser_get_selected (GtkWidget *chooser,
GtkTreeModel **model,
GtkTreeIter *iter);
+static GtkTreeModel *tree_view_get_actual_model (GtkTreeView *tv);
+
+#ifdef HAVE_IBUS
+static void
+clear_ibus (void)
+{
+ g_clear_pointer (&ibus_engines, g_hash_table_destroy);
+ g_clear_object (&ibus);
+}
+
+static gchar *
+engine_get_display_name (IBusEngineDesc *engine_desc)
+{
+ const gchar *name;
+ const gchar *language_code;
+ gchar *language;
+ gchar *display_name;
+
+ name = ibus_engine_desc_get_longname (engine_desc);
+ language_code = ibus_engine_desc_get_language (engine_desc);
+
+ language = gdm_get_language_from_name (language_code, NULL);
+
+ display_name = g_strdup_printf ("%s (%s)", language, name);
+
+ g_free (language);
+
+ return display_name;
+}
+
+static void
+update_ibus_active_sources (GtkBuilder *builder)
+{
+ GtkTreeView *tv;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *type, *id;
+
+ tv = GTK_TREE_VIEW (WID ("active_input_sources"));
+ model = tree_view_get_actual_model (tv);
+
+ gtk_tree_model_get_iter_first (model, &iter);
+ do
+ {
+ gtk_tree_model_get (model, &iter,
+ TYPE_COLUMN, &type,
+ ID_COLUMN, &id,
+ -1);
+
+ if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS))
+ {
+ IBusEngineDesc *engine_desc = NULL;
+ gchar *display_name = NULL;
+
+ engine_desc = g_hash_table_lookup (ibus_engines, id);
+ if (engine_desc)
+ {
+ display_name = engine_get_display_name (engine_desc);
+
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ NAME_COLUMN, display_name,
+ -1);
+ g_free (display_name);
+ }
+ }
+
+ g_free (type);
+ g_free (id);
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static void
+fetch_ibus_engines (GtkBuilder *builder)
+{
+ IBusEngineDesc **engines, **iter;
+ const gchar *name;
+
+ ibus_engines = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+
+ engines = ibus_bus_get_engines_by_names (ibus, supported_ibus_engines);
+ for (iter = engines; *iter; ++iter)
+ {
+ name = ibus_engine_desc_get_name (*iter);
+ g_hash_table_replace (ibus_engines, (gpointer)name, *iter);
+ }
+
+ g_free (engines);
+
+ update_ibus_active_sources (builder);
+
+ /* We've got everything we needed, don't want to be called again. */
+ g_signal_handlers_disconnect_by_func (ibus, fetch_ibus_engines, builder);
+}
+
+static void
+maybe_start_ibus (void)
+{
+ /* IBus doesn't export API in the session bus. The only thing
+ * we have there is a well known name which we can use as a
+ * sure-fire way to activate it. */
+ g_bus_unwatch_name (g_bus_watch_name (G_BUS_TYPE_SESSION,
+ IBUS_SERVICE_IBUS,
+ G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
+ NULL,
+ NULL,
+ NULL,
+ NULL));
+}
+#endif /* HAVE_IBUS */
static gboolean
add_source_to_table (GtkTreeModel *model,
@@ -118,6 +248,39 @@ populate_model (GtkListStore *store,
g_free (source_id);
g_list_free (sources);
+
+#ifdef HAVE_IBUS
+ if (ibus_engines)
+ {
+ gchar *display_name;
+
+ sources = g_hash_table_get_keys (ibus_engines);
+
+ source_id = NULL;
+ for (tmp = sources; tmp; tmp = tmp->next)
+ {
+ g_free (source_id);
+ source_id = g_strconcat (INPUT_SOURCE_TYPE_IBUS, tmp->data, NULL);
+
+ if (g_hash_table_contains (active_sources_table, source_id))
+ continue;
+
+ display_name = engine_get_display_name (g_hash_table_lookup (ibus_engines, tmp->data));
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ NAME_COLUMN, display_name,
+ TYPE_COLUMN, INPUT_SOURCE_TYPE_IBUS,
+ ID_COLUMN, tmp->data,
+ -1);
+ g_free (display_name);
+ }
+ g_free (source_id);
+
+ g_list_free (sources);
+ }
+#endif
+
g_hash_table_destroy (active_sources_table);
}
@@ -129,6 +292,7 @@ populate_with_active_sources (GtkListStore *store)
const gchar *name;
const gchar *type;
const gchar *id;
+ gchar *display_name;
GtkTreeIter tree_iter;
sources = g_settings_get_value (input_sources_settings, KEY_INPUT_SOURCES);
@@ -136,26 +300,46 @@ populate_with_active_sources (GtkListStore *store)
g_variant_iter_init (&iter, sources);
while (g_variant_iter_next (&iter, "(&s&s)", &type, &id))
{
- if (!g_str_equal (type, INPUT_SOURCE_TYPE_XKB))
+ display_name = NULL;
+
+ if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB))
{
- g_warning ("Unknown input source type '%s'", type);
- continue;
+ gnome_xkb_info_get_layout_info (xkb_info, id, &name, NULL, NULL, NULL);
+ if (!name)
+ {
+ g_warning ("Couldn't find XKB input source '%s'", id);
+ continue;
+ }
+ display_name = g_strdup (name);
}
+ else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS))
+ {
+#ifdef HAVE_IBUS
+ IBusEngineDesc *engine_desc = NULL;
- gnome_xkb_info_get_layout_info (xkb_info, id, &name, NULL, NULL, NULL);
+ if (ibus_engines)
+ engine_desc = g_hash_table_lookup (ibus_engines, id);
- if (!name)
+ if (engine_desc)
+ display_name = engine_get_display_name (engine_desc);
+#else
+ g_warning ("IBus input source type specified but IBus support was not compiled");
+ continue;
+#endif
+ }
+ else
{
- g_warning ("Couldn't find XKB input source '%s'", id);
+ g_warning ("Unknown input source type '%s'", type);
continue;
}
gtk_list_store_append (store, &tree_iter);
gtk_list_store_set (store, &tree_iter,
- NAME_COLUMN, name,
+ NAME_COLUMN, display_name,
TYPE_COLUMN, type,
ID_COLUMN, id,
-1);
+ g_free (display_name);
}
g_variant_unref (sources);
@@ -258,6 +442,16 @@ set_selected_path (GtkBuilder *builder,
gtk_tree_selection_select_path (selection, path);
}
+static GtkTreeModel *
+tree_view_get_actual_model (GtkTreeView *tv)
+{
+ GtkTreeModel *filtered_store;
+
+ filtered_store = gtk_tree_view_get_model (tv);
+
+ return gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filtered_store));
+}
+
static void
chooser_response (GtkWidget *chooser, gint response_id, gpointer data)
{
@@ -270,9 +464,9 @@ chooser_response (GtkWidget *chooser, gint response_id, gpointer data)
if (input_chooser_get_selected (chooser, &model, &iter))
{
- GtkTreeView *my_tv;
- GtkListStore *my_model;
- GtkTreeIter my_iter;
+ GtkTreeView *tv;
+ GtkListStore *child_model;
+ GtkTreeIter child_iter, filter_iter;
gchar *name;
gchar *type;
gchar *id;
@@ -283,12 +477,12 @@ chooser_response (GtkWidget *chooser, gint response_id, gpointer data)
ID_COLUMN, &id,
-1);
- my_tv = GTK_TREE_VIEW (WID ("active_input_sources"));
- my_model = GTK_LIST_STORE (gtk_tree_view_get_model (my_tv));
+ tv = GTK_TREE_VIEW (WID ("active_input_sources"));
+ child_model = GTK_LIST_STORE (tree_view_get_actual_model (tv));
- gtk_list_store_append (my_model, &my_iter);
+ gtk_list_store_append (child_model, &child_iter);
- gtk_list_store_set (my_model, &my_iter,
+ gtk_list_store_set (child_model, &child_iter,
NAME_COLUMN, name,
TYPE_COLUMN, type,
ID_COLUMN, id,
@@ -297,10 +491,13 @@ chooser_response (GtkWidget *chooser, gint response_id, gpointer data)
g_free (type);
g_free (id);
- gtk_tree_selection_select_iter (gtk_tree_view_get_selection (my_tv), &my_iter);
+ gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (tv)),
+ &filter_iter,
+ &child_iter);
+ gtk_tree_selection_select_iter (gtk_tree_view_get_selection (tv), &filter_iter);
update_button_sensitivity (builder);
- update_configuration (GTK_TREE_MODEL (my_model));
+ update_configuration (GTK_TREE_MODEL (child_model));
}
else
{
@@ -324,7 +521,7 @@ add_input (GtkButton *button, gpointer data)
toplevel = gtk_widget_get_toplevel (WID ("region_notebook"));
treeview = WID ("active_input_sources");
- active_sources = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)));
+ active_sources = GTK_LIST_STORE (tree_view_get_actual_model (GTK_TREE_VIEW (treeview)));
chooser = input_chooser_new (GTK_WINDOW (toplevel), active_sources);
g_signal_connect (chooser, "response",
@@ -336,7 +533,9 @@ remove_selected_input (GtkButton *button, gpointer data)
{
GtkBuilder *builder = data;
GtkTreeModel *model;
+ GtkTreeModel *child_model;
GtkTreeIter iter;
+ GtkTreeIter child_iter;
GtkTreePath *path;
g_debug ("remove selected input source");
@@ -345,7 +544,12 @@ remove_selected_input (GtkButton *button, gpointer data)
return;
path = gtk_tree_model_get_path (model, &iter);
- gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+
+ child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
+ &child_iter,
+ &iter);
+ gtk_list_store_remove (GTK_LIST_STORE (child_model), &child_iter);
if (!gtk_tree_model_get_iter (model, &iter, path))
gtk_tree_path_prev (path);
@@ -355,7 +559,7 @@ remove_selected_input (GtkButton *button, gpointer data)
gtk_tree_path_free (path);
update_button_sensitivity (builder);
- update_configuration (model);
+ update_configuration (child_model);
}
static void
@@ -363,7 +567,9 @@ move_selected_input_up (GtkButton *button, gpointer data)
{
GtkBuilder *builder = data;
GtkTreeModel *model;
+ GtkTreeModel *child_model;
GtkTreeIter iter, prev;
+ GtkTreeIter child_iter, child_prev;
GtkTreePath *path;
g_debug ("move selected input source up");
@@ -377,12 +583,20 @@ move_selected_input_up (GtkButton *button, gpointer data)
path = gtk_tree_model_get_path (model, &prev);
- gtk_list_store_swap (GTK_LIST_STORE (model), &iter, &prev);
+ child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
+ &child_iter,
+ &iter);
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
+ &child_prev,
+ &prev);
+ gtk_list_store_swap (GTK_LIST_STORE (child_model), &child_iter, &child_prev);
+
set_selected_path (builder, path);
gtk_tree_path_free (path);
update_button_sensitivity (builder);
- update_configuration (model);
+ update_configuration (child_model);
}
static void
@@ -390,7 +604,9 @@ move_selected_input_down (GtkButton *button, gpointer data)
{
GtkBuilder *builder = data;
GtkTreeModel *model;
+ GtkTreeModel *child_model;
GtkTreeIter iter, next;
+ GtkTreeIter child_iter, child_next;
GtkTreePath *path;
g_debug ("move selected input source down");
@@ -403,12 +619,21 @@ move_selected_input_down (GtkButton *button, gpointer data)
return;
path = gtk_tree_model_get_path (model, &next);
- gtk_list_store_swap (GTK_LIST_STORE (model), &iter, &next);
+
+ child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
+ &child_iter,
+ &iter);
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
+ &child_next,
+ &next);
+ gtk_list_store_swap (GTK_LIST_STORE (child_model), &child_iter, &child_next);
+
set_selected_path (builder, path);
gtk_tree_path_free (path);
update_button_sensitivity (builder);
- update_configuration (model);
+ update_configuration (child_model);
}
static void
@@ -417,6 +642,7 @@ show_selected_layout (GtkButton *button, gpointer data)
GtkBuilder *builder = data;
GtkTreeModel *model;
GtkTreeIter iter;
+ gchar *type;
gchar *id;
gchar *kbd_viewer_args;
const gchar *xkb_layout;
@@ -428,14 +654,46 @@ show_selected_layout (GtkButton *button, gpointer data)
return;
gtk_tree_model_get (model, &iter,
+ TYPE_COLUMN, &type,
ID_COLUMN, &id,
-1);
- gnome_xkb_info_get_layout_info (xkb_info, id, NULL, NULL, &xkb_layout, &xkb_variant);
+ if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB))
+ {
+ gnome_xkb_info_get_layout_info (xkb_info, id, NULL, NULL, &xkb_layout, &xkb_variant);
- if (!xkb_layout || !xkb_layout[0])
+ if (!xkb_layout || !xkb_layout[0])
+ {
+ g_warning ("Couldn't find XKB input source '%s'", id);
+ goto exit;
+ }
+ }
+ else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS))
+ {
+#ifdef HAVE_IBUS
+ IBusEngineDesc *engine_desc = NULL;
+
+ if (ibus_engines)
+ engine_desc = g_hash_table_lookup (ibus_engines, id);
+
+ if (engine_desc)
+ {
+ xkb_layout = ibus_engine_desc_get_layout (engine_desc);
+ xkb_variant = "";
+ }
+ else
+ {
+ g_warning ("Couldn't find IBus input source '%s'", id);
+ goto exit;
+ }
+#else
+ g_warning ("IBus input source type specified but IBus support was not compiled");
+ goto exit;
+#endif
+ }
+ else
{
- g_warning ("Couldn't find XKB input source '%s'", id);
+ g_warning ("Unknown input source type '%s'", type);
goto exit;
}
@@ -450,6 +708,7 @@ show_selected_layout (GtkButton *button, gpointer data)
g_free (kbd_viewer_args);
exit:
+ g_free (type);
g_free (id);
}
@@ -480,7 +739,7 @@ input_sources_changed (GSettings *settings,
GtkTreeModel *store;
treeview = WID("active_input_sources");
- store = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
+ store = tree_view_get_actual_model (GTK_TREE_VIEW (treeview));
gtk_list_store_clear (GTK_LIST_STORE (store));
populate_with_active_sources (GTK_LIST_STORE (store));
}
@@ -530,6 +789,23 @@ update_shortcuts (GtkBuilder *builder)
g_free (next);
}
+static gboolean
+active_sources_visible_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gchar *display_name;
+
+ gtk_tree_model_get (model, iter, NAME_COLUMN, &display_name, -1);
+
+ if (!display_name)
+ return FALSE;
+
+ g_free (display_name);
+
+ return TRUE;
+}
+
void
setup_input_tabs (GtkBuilder *builder,
CcRegionPanel *panel)
@@ -538,11 +814,24 @@ setup_input_tabs (GtkBuilder *builder,
GtkTreeViewColumn *column;
GtkCellRenderer *cell;
GtkListStore *store;
+ GtkTreeModel *filtered_store;
GtkTreeSelection *selection;
input_sources_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR);
xkb_info = gnome_xkb_info_new ();
+#ifdef HAVE_IBUS
+ ibus_init ();
+ if (!ibus)
+ {
+ ibus = ibus_bus_new_async ();
+ g_signal_connect_swapped (ibus, "connected",
+ G_CALLBACK (fetch_ibus_engines), builder);
+ g_object_weak_ref (G_OBJECT (builder), (GWeakNotify) clear_ibus, NULL);
+ }
+ maybe_start_ibus ();
+#endif
+
/* set up the list of active inputs */
treeview = WID("active_input_sources");
column = gtk_tree_view_column_new ();
@@ -559,7 +848,16 @@ setup_input_tabs (GtkBuilder *builder,
g_signal_connect_swapped (selection, "changed",
G_CALLBACK (update_button_sensitivity), builder);
- gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
+ /* Some input source types might have their info loaded
+ * asynchronously. In that case we don't want to show them
+ * immediately so we use a filter model on top of the real model
+ * which mirrors the GSettings key. */
+ filtered_store = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
+ gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filtered_store),
+ active_sources_visible_func,
+ NULL,
+ NULL);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), filtered_store);
/* set up the buttons */
g_signal_connect (WID("input_source_add"), "clicked",
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]