Commit 412e5307 authored by Rui Matos's avatar Rui Matos Committed by Bastien Nocera

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
parent 7ae3027b
......@@ -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])
......@@ -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",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment