[gnome-control-center] region: Add IBus input sources



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]