[gnome-control-center/wip/region-panel: 39/39] region: New 'Add Input Source' dialog design



commit 0e1fa1fc779ee5027e1093e4c5c6c5158db6e297
Author: Rui Matos <tiagomatos gmail com>
Date:   Wed Feb 6 19:41:01 2013 +0100

    region: New 'Add Input Source' dialog design

 panels/region/cc-input-chooser.c |  703 ++++++++++++++++++++++++++++----------
 panels/region/cc-input-chooser.h |    2 +
 panels/region/cc-region-panel.c  |   24 +-
 panels/region/input-chooser.ui   |  164 +++------
 4 files changed, 605 insertions(+), 288 deletions(-)
---
diff --git a/panels/region/cc-input-chooser.c b/panels/region/cc-input-chooser.c
index b250cc3..7f521c6 100644
--- a/panels/region/cc-input-chooser.c
+++ b/panels/region/cc-input-chooser.c
@@ -21,6 +21,10 @@
 #include <config.h>
 #include <glib/gi18n.h>
 
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-languages.h>
+
+#include "egg-list-box/egg-list-box.h"
 #include "cc-input-chooser.h"
 
 #ifdef HAVE_IBUS
@@ -31,192 +35,500 @@
 #define INPUT_SOURCE_TYPE_XKB "xkb"
 #define INPUT_SOURCE_TYPE_IBUS "ibus"
 
+typedef struct {
+  /* Not owned */
+  GtkWidget *filter_entry;
+  GtkWidget *list;
+  GtkWidget *scrolledwindow;
+  GtkWidget *back_button;
+  GnomeXkbInfo *xkb_info;
+  GHashTable *ibus_engines;
+
+  /* Owned */
+  GHashTable *locales;
+  GHashTable *locales_by_language;
+} CcInputChooserPrivate;
+
+#define GET_PRIVATE(chooser) ((CcInputChooserPrivate *) g_object_get_data (G_OBJECT (chooser), "private"))
 #define WID(name) ((GtkWidget *) gtk_builder_get_object (builder, name))
 
-enum {
-        NAME_COLUMN,
-        TYPE_COLUMN,
-        ID_COLUMN,
-        SETUP_COLUMN,
-        N_COLUMNS
-};
+typedef struct {
+  gchar *id;
+  const gchar *default_input_source_type;
+  const gchar *default_input_source_id;
+  GHashTable *layout_ids;
+  GHashTable *engine_ids;
+} LocaleInfo;
 
 static void
-filter_clear (GtkEntry             *entry,
-              GtkEntryIconPosition  icon_pos,
-              GdkEvent             *event,
-              gpointer              user_data)
+locale_info_free (gpointer data)
 {
-  gtk_entry_set_text (entry, "");
-}
+  LocaleInfo *info = data;
 
-static gchar **search_pattern_list;
+  g_free (info->id);
+  g_hash_table_destroy (info->layout_ids);
+  g_hash_table_destroy (info->engine_ids);
+  g_free (info);
+}
 
-static void
-filter_changed (GtkBuilder *builder)
+static GtkWidget *
+padded_label_new (const gchar *text)
 {
-  GtkTreeModelFilter *filtered_model;
-  GtkTreeView *tree_view;
-  GtkTreeSelection *selection;
-  GtkTreeIter selected_iter;
-  GtkWidget *filter_entry;
-  const gchar *pattern;
-  gchar *upattern;
+  GtkWidget *widget;
+  GtkWidget *label;
 
-  filter_entry = WID ("input_source_filter");
-  pattern = gtk_entry_get_text (GTK_ENTRY (filter_entry));
-  upattern = g_utf8_strup (pattern, -1);
-  if (!g_strcmp0 (pattern, ""))
-    g_object_set (G_OBJECT (filter_entry),
-                  "secondary-icon-name", "edit-find-symbolic",
-                  "secondary-icon-activatable", FALSE,
-                  "secondary-icon-sensitive", FALSE,
-                  NULL);
-  else
-    g_object_set (G_OBJECT (filter_entry),
-                  "secondary-icon-name", "edit-clear-symbolic",
-                  "secondary-icon-activatable", TRUE,
-                  "secondary-icon-sensitive", TRUE,
-                  NULL);
+  widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
 
-  if (search_pattern_list != NULL)
-    g_strfreev (search_pattern_list);
+  label = gtk_label_new (text);
+  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+  gtk_widget_set_margin_left (label, 20);
+  gtk_widget_set_margin_right (label, 20);
+  gtk_widget_set_margin_top (label, 6);
+  gtk_widget_set_margin_bottom (label, 6);
+  gtk_box_pack_start (GTK_BOX (widget), label, TRUE, TRUE, 0);
 
-  search_pattern_list = g_strsplit (upattern, " ", -1);
-  g_free (upattern);
-  filtered_model = GTK_TREE_MODEL_FILTER (gtk_builder_get_object (builder, "filtered_input_source_model"));
-  gtk_tree_model_filter_refilter (filtered_model);
+  return widget;
+}
 
-  tree_view = GTK_TREE_VIEW (WID ("filtered_input_source_list"));
-  selection = gtk_tree_view_get_selection (tree_view);
-  if (gtk_tree_selection_get_selected (selection, NULL, &selected_iter))
+static GtkWidget *
+input_source_widget_new (GtkWidget   *chooser,
+                         const gchar *type,
+                         const gchar *id)
+{
+  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+  GtkWidget *widget;
+
+  if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB))
     {
-      GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (filtered_model),
-                                                   &selected_iter);
-      gtk_tree_view_scroll_to_cell (tree_view, path, NULL, TRUE, 0.5, 0.5);
-      gtk_tree_path_free (path);
+      const gchar *display_name;
+
+      gnome_xkb_info_get_layout_info (priv->xkb_info, id, &display_name, NULL, NULL, NULL);
+
+      widget = padded_label_new (display_name);
+      g_object_set_data (G_OBJECT (widget), "name", (gpointer) display_name);
     }
-  else
+  else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS))
+    {
+#ifdef HAVE_IBUS
+      gchar *display_name;
+
+      display_name = engine_get_display_name (g_hash_table_lookup (priv->ibus_engines, id));
+
+      widget = padded_label_new (display_name);
+      g_object_set_data_full (G_OBJECT (widget), "name", display_name, g_free);
+#else
+      widget = NULL;
+#endif
+    }
+
+  if (widget)
     {
-      GtkTreeIter iter;
-      if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (filtered_model), &iter))
-        gtk_tree_selection_select_iter (selection, &iter);
+      g_object_set_data (G_OBJECT (widget), "type", (gpointer) type);
+      g_object_set_data (G_OBJECT (widget), "id", (gpointer) id);
     }
+
+  return widget;
 }
 
-static void
-selection_changed (GtkTreeSelection *selection,
-                   GtkBuilder       *builder)
+static gint
+sort_input_sources (gconstpointer a,
+                    gconstpointer b,
+                    gpointer      data)
 {
-  gtk_widget_set_sensitive (WID ("ok-button"),
-                            gtk_tree_selection_get_selected (selection, NULL, NULL));
+  const gchar *la;
+  const gchar *lb;
+
+  if (g_object_get_data (G_OBJECT (a), "default"))
+    return -1;
+
+  if (g_object_get_data (G_OBJECT (b), "default"))
+    return 1;
+
+  la = g_object_get_data (G_OBJECT (a), "name");
+  lb = g_object_get_data (G_OBJECT (b), "name");
+
+  return g_strcmp0 (la, lb);
 }
 
 static void
-row_activated (GtkTreeView       *tree_view,
-               GtkTreePath       *path,
-               GtkTreeViewColumn *column,
-               GtkBuilder        *builder)
+show_input_sources_for_locale (GtkWidget  *chooser,
+                               LocaleInfo *info)
 {
-  GtkWidget *add_button;
-  GtkWidget *dialog;
+  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+  GtkWidget *widget;
+  GHashTableIter iter;
+  const gchar *id;
+
+  gtk_container_foreach (GTK_CONTAINER (priv->list),
+                         (GtkCallback) gtk_widget_destroy,
+                         NULL);
+  egg_list_box_set_sort_func (EGG_LIST_BOX (priv->list), sort_input_sources, chooser, NULL);
+  egg_list_box_set_selection_mode (EGG_LIST_BOX (priv->list), GTK_SELECTION_SINGLE);
+
+  if (info->default_input_source_type)
+    {
+      widget = input_source_widget_new (chooser,
+                                        info->default_input_source_type,
+                                        info->default_input_source_id);
+      if (widget)
+        {
+          g_object_set_data (G_OBJECT (widget), "default", GINT_TO_POINTER (TRUE));
+          gtk_container_add (GTK_CONTAINER (priv->list), widget);
+        }
+    }
 
-  add_button = WID ("ok-button");
-  dialog = WID ("input_source_chooser");
-  if (gtk_widget_is_sensitive (add_button))
-    gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+  g_hash_table_iter_init (&iter, info->layout_ids);
+  while (g_hash_table_iter_next (&iter, (gpointer *) &id, NULL))
+    {
+      if (g_strcmp0 (info->default_input_source_type, INPUT_SOURCE_TYPE_XKB) == 0 &&
+          g_strcmp0 (info->default_input_source_id, id) == 0)
+        continue;
+
+      widget = input_source_widget_new (chooser, INPUT_SOURCE_TYPE_XKB, id);
+      if (widget)
+        gtk_container_add (GTK_CONTAINER (priv->list), widget);
+    }
+
+  g_hash_table_iter_init (&iter, info->engine_ids);
+  while (g_hash_table_iter_next (&iter, (gpointer *) &id, NULL))
+    {
+      if (g_strcmp0 (info->default_input_source_type, INPUT_SOURCE_TYPE_IBUS) == 0 &&
+          g_strcmp0 (info->default_input_source_id, id) == 0)
+        continue;
+
+      widget = input_source_widget_new (chooser, INPUT_SOURCE_TYPE_IBUS, id);
+      if (widget)
+        gtk_container_add (GTK_CONTAINER (priv->list), widget);
+    }
+
+  gtk_widget_show_all (priv->list);
+  gtk_widget_grab_focus (priv->filter_entry);
+  gtk_widget_show (priv->back_button);
 }
 
 static void
-entry_activated (GtkBuilder *builder,
-                 gpointer    data)
+child_activated (EggListBox *box,
+                 GtkWidget  *child,
+                 GtkWidget  *chooser)
 {
-  row_activated (NULL, NULL, NULL, builder);
+  gpointer data;
+
+  if (!child)
+    return;
+
+  data = g_object_get_data (G_OBJECT (child), "locale-info");
+  if (data)
+    {
+      show_input_sources_for_locale (chooser, (LocaleInfo *) data);
+      return;
+    }
 }
 
-static gboolean
-filter_func (GtkTreeModel *model,
-             GtkTreeIter  *iter,
-             gpointer      data)
+static gint
+sort_locales (gconstpointer a,
+              gconstpointer b,
+              gpointer      data)
 {
-  gchar *name = NULL;
-  gchar **pattern;
-  gboolean rv = TRUE;
-
-  if (search_pattern_list == NULL || search_pattern_list[0] == NULL)
-    return TRUE;
-
-  gtk_tree_model_get (model, iter,
-                      NAME_COLUMN, &name,
-                      -1);
-
-  pattern = search_pattern_list;
-  do {
-    gboolean is_pattern_found = FALSE;
-    gchar *udesc = g_utf8_strup (name, -1);
-    if (udesc != NULL && g_strstr_len (udesc, -1, *pattern))
-      {
-        is_pattern_found = TRUE;
-      }
-    g_free (udesc);
-
-    if (!is_pattern_found)
-      {
-        rv = FALSE;
-        break;
-      }
-
-  } while (*++pattern != NULL);
-  g_free (name);
-
-  return rv;
+  const gchar *id;
+  const gchar *la;
+  const gchar *lb;
+
+  id = g_object_get_data (G_OBJECT (a), "locale-id");
+  if (!id[0])
+    return 1;
+
+  id = g_object_get_data (G_OBJECT (b), "locale-id");
+  if (!id[0])
+    return -1;
+
+  la = g_object_get_data (G_OBJECT (a), "locale-name");
+  lb = g_object_get_data (G_OBJECT (b), "locale-name");
+
+  return g_strcmp0 (la, lb);
 }
 
-static void
-populate_model (GtkListStore *store,
-                GnomeXkbInfo *xkb_info,
-                GHashTable   *ibus_engines)
+static GtkWidget *
+locale_widget_new (const gchar *locale_id)
 {
-  GtkTreeIter iter;
-  const gchar *name;
-  GList *sources, *tmp;
+  gchar *locale_name;
+  gchar *locale_untranslated_name;
+  GtkWidget *widget;
+  GtkWidget *arrow;
+  const gchar *icon_name;
 
-  sources = gnome_xkb_info_get_all_layouts (xkb_info);
-  for (tmp = sources; tmp; tmp = tmp->next)
+  if (locale_id[0])
+    {
+      locale_name = gnome_get_language_from_locale (locale_id, NULL);
+      locale_untranslated_name = gnome_get_country_from_locale (locale_id, "C");
+    }
+  else
     {
-      gnome_xkb_info_get_layout_info (xkb_info, (const gchar *)tmp->data,
-                                      &name, NULL, NULL, NULL);
-      gtk_list_store_append (store, &iter);
-      gtk_list_store_set (store, &iter,
-                          NAME_COLUMN, name,
-                          TYPE_COLUMN, INPUT_SOURCE_TYPE_XKB,
-                          ID_COLUMN, tmp->data,
-                          -1);
+      locale_name = g_strdup (_("Other"));
+      locale_untranslated_name = g_strdup ("");
     }
-  g_list_free (sources);
+
+  widget = padded_label_new (locale_name);
+
+  if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL)
+    icon_name = "go-previous-symbolic";
+  else
+    icon_name = "go-next-symbolic";
+
+  arrow = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
+  gtk_widget_set_margin_left (arrow, 20);
+  gtk_widget_set_margin_right (arrow, 20);
+  gtk_widget_set_margin_top (arrow, 6);
+  gtk_widget_set_margin_bottom (arrow, 6);
+  gtk_box_pack_start (GTK_BOX (widget), arrow, FALSE, TRUE, 0);
+
+  g_object_set_data_full (G_OBJECT (widget), "locale-id", g_strdup (locale_id), g_free);
+  g_object_set_data_full (G_OBJECT (widget), "locale-name", locale_name, g_free);
+  g_object_set_data_full (G_OBJECT (widget), "locale-untranslated-name", locale_untranslated_name, g_free);
+
+  return widget;
+}
+
+static void
+show_locale_widgets (GtkWidget *chooser)
+{
+  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+  LocaleInfo *info;
+  GtkWidget *widget;
+  GHashTableIter iter;
+
+  gtk_container_foreach (GTK_CONTAINER (priv->list),
+                         (GtkCallback) gtk_widget_destroy,
+                         NULL);
+  egg_list_box_set_sort_func (EGG_LIST_BOX (priv->list), sort_locales, chooser, NULL);
+  egg_list_box_set_selection_mode (EGG_LIST_BOX (priv->list), GTK_SELECTION_NONE);
+
+  g_hash_table_iter_init (&iter, priv->locales);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
+    {
+      if (!info->default_input_source_type &&
+          !g_hash_table_size (info->layout_ids) &&
+          !g_hash_table_size (info->engine_ids))
+        continue;
+
+      widget = locale_widget_new (info->id);
+      gtk_container_add (GTK_CONTAINER (priv->list), widget);
+
+      g_object_set_data (G_OBJECT (widget), "locale-info", info);
+    }
+
+  gtk_widget_show_all (priv->list);
+  gtk_widget_grab_focus (priv->filter_entry);
+  gtk_widget_hide (priv->back_button);
+}
 
 #ifdef HAVE_IBUS
-  if (ibus_engines)
+static void
+get_ibus_locale_infos (GtkWidget *chooser)
+{
+  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+  GHashTableIter iter;
+  LocaleInfo *info;
+  const gchar *engine_id;
+  IBusEngineDesc *engine;
+
+  if (!priv->ibus_engines)
+    return;
+
+  g_hash_table_iter_init (&iter, priv->ibus_engines);
+  while (g_hash_table_iter_next (&iter, (gpointer *) &engine_id, (gpointer *) &engine))
     {
-      gchar *display_name;
+      gchar *lang_code = NULL;
+      gchar *country_code = NULL;
+      const gchar *ibus_locale = ibus_engine_desc_get_language (engine);
 
-      sources = g_hash_table_get_keys (ibus_engines);
-      for (tmp = sources; tmp; tmp = tmp->next)
+      if (gnome_parse_locale (ibus_locale, &lang_code, &country_code, NULL, NULL) &&
+          lang_code != NULL &&
+          country_code != NULL)
+        {
+          gchar *locale = g_strdup_printf ("%s_%s.utf8", lang_code, country_code);
+
+          info = g_hash_table_lookup (priv->locales, locale);
+          if (info)
+            {
+              const gchar *type, *id;
+
+              g_hash_table_add (info->engine_ids, (gpointer) engine_id);
+
+              if (gnome_get_input_source_from_locale (locale, &type, &id) &&
+                  g_str_equal (type, INPUT_SOURCE_TYPE_IBUS) &&
+                  g_str_equal (id, engine_id))
+                {
+                  info->default_input_source_type = type;
+                  info->default_input_source_id = id;
+                }
+            }
+          else
+            {
+              g_warning ("IBus returned locale '%s' that we don't know about", locale);
+            }
+
+          g_free (locale);
+        }
+      else if (lang_code != NULL)
+        {
+          GHashTableIter iter;
+          GHashTable *locales_for_language;
+          gchar *language;
+
+          language = gnome_get_language_from_code (lang_code, NULL);
+          if (language)
+            locales_for_language = g_hash_table_lookup (priv->locales_by_language, language);
+          else
+            locales_for_language = NULL;
+          g_free (language);
+
+          if (locales_for_language)
+            {
+              g_hash_table_iter_init (&iter, locales_for_language);
+              while (g_hash_table_iter_next (&iter, (gpointer *) &info, NULL))
+                g_hash_table_add (info->engine_ids, (gpointer) engine_id);
+            }
+        }
+      else
         {
-          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);
+          /* Add it to the "Other" locale */
+          info = g_hash_table_lookup (priv->locales, "");
+          g_hash_table_add (info->engine_ids, (gpointer) engine_id);
         }
-      g_list_free (sources);
+
+      g_free (country_code);
+      g_free (lang_code);
     }
+}
 #endif
+
+static void
+add_locale_to_table (GHashTable  *table,
+                     const gchar *lang_code,
+                     LocaleInfo  *info)
+{
+  GHashTable *set;
+  gchar *language;
+
+  language = gnome_get_language_from_code (lang_code, NULL);
+
+  set = g_hash_table_lookup (table, language);
+  if (!set)
+    {
+      set = g_hash_table_new (NULL, NULL);
+      g_hash_table_replace (table, g_strdup (language), set);
+    }
+  g_hash_table_add (set, info);
+
+  g_free (language);
+}
+
+static void
+add_ids_to_set (GHashTable *set,
+                GList      *list)
+{
+  while (list)
+    {
+      g_hash_table_add (set, list->data);
+      list = list->next;
+    }
 }
 
+static void
+get_locale_infos (GtkWidget *chooser)
+{
+  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+  GHashTable *layouts_with_locale;
+  LocaleInfo *info;
+  gchar **locale_ids;
+  gchar **locale;
+  GList *list, *l;
+
+  priv->locales = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                         NULL, locale_info_free);
+  priv->locales_by_language = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                     g_free, (GDestroyNotify) g_hash_table_destroy);
+
+  layouts_with_locale = g_hash_table_new (g_str_hash, g_str_equal);
+
+  locale_ids = gnome_get_all_locales ();
+  for (locale = locale_ids; *locale; ++locale)
+    {
+      const gchar *type, *id;
+      gchar *lang_code, *country_code;
+      gchar *simple_locale;
+
+      if (!gnome_parse_locale (*locale, &lang_code, &country_code, NULL, NULL))
+        continue;
+
+      simple_locale = g_strdup_printf ("%s_%s.utf8", lang_code, country_code);
+      if (g_hash_table_contains (priv->locales, simple_locale))
+        {
+          g_free (simple_locale);
+          g_free (country_code);
+          g_free (lang_code);
+          continue;
+        }
+
+      info = g_new0 (LocaleInfo, 1);
+      info->id = simple_locale; /* Take ownership */
+      g_hash_table_replace (priv->locales, simple_locale, info);
+      add_locale_to_table (priv->locales_by_language, lang_code, info);
+
+      if (gnome_get_input_source_from_locale (simple_locale, &type, &id) &&
+          g_str_equal (type, INPUT_SOURCE_TYPE_XKB))
+        {
+          g_hash_table_add (layouts_with_locale, (gpointer) id);
+          info->default_input_source_type = type;
+          info->default_input_source_id = id;
+        }
+
+      /* We don't own these ids */
+      info->layout_ids = g_hash_table_new (g_str_hash, g_str_equal);
+      info->engine_ids = g_hash_table_new (g_str_hash, g_str_equal);
+
+      list = gnome_xkb_info_get_layouts_for_language (priv->xkb_info, lang_code);
+      add_ids_to_set (info->layout_ids, list);
+      add_ids_to_set (layouts_with_locale, list);
+      g_list_free (list);
+
+      list = gnome_xkb_info_get_layouts_for_country (priv->xkb_info, country_code);
+      add_ids_to_set (info->layout_ids, list);
+      add_ids_to_set (layouts_with_locale, list);
+      g_list_free (list);
+
+      g_free (lang_code);
+      g_free (country_code);
+    }
+  /* String ownership went to the LocaleInfo structs */
+  g_strfreev (locale_ids);
+
+  /* Add a "Other" locale to hold the remaining input sources */
+  info = g_new0 (LocaleInfo, 1);
+  info->id = g_strdup ("");
+  g_hash_table_replace (priv->locales, info->id, info);
+
+  info->layout_ids = g_hash_table_new (g_str_hash, g_str_equal);
+  info->engine_ids = g_hash_table_new (g_str_hash, g_str_equal);
+
+  list = gnome_xkb_info_get_all_layouts (priv->xkb_info);
+  for (l = list; l; l = l->next)
+    if (!g_hash_table_contains (layouts_with_locale, l->data))
+      g_hash_table_add (info->layout_ids, l->data);
+  g_list_free (list);
+
+  g_hash_table_destroy (layouts_with_locale);
+}
+
+static void
+cc_input_chooser_private_free (gpointer data)
+{
+  CcInputChooserPrivate *priv = data;
+
+  g_hash_table_destroy (priv->locales);
+  g_hash_table_destroy (priv->locales_by_language);
+  g_free (priv);
+}
 
 GtkWidget *
 cc_input_chooser_new (GtkWindow    *main_window,
@@ -225,21 +537,52 @@ cc_input_chooser_new (GtkWindow    *main_window,
 {
   GtkBuilder *builder;
   GtkWidget *chooser;
-  GtkWidget *filtered_list;
-  GtkWidget *filter_entry;
-  GtkTreeViewColumn *visible_column;
-  GtkTreeSelection *selection;
-  GtkListStore *model;
-  GtkTreeModelFilter *filtered_model;
-  GtkTreeIter iter;
+  CcInputChooserPrivate *priv;
+  GError *error = NULL;
 
   builder = gtk_builder_new ();
-  gtk_builder_add_from_resource (builder,
-                                 "/org/gnome/control-center/region/input-chooser.ui",
-                                 NULL);
-  chooser = WID ("input_source_chooser");
+  gtk_builder_add_from_resource (builder, "/org/gnome/control-center/region/input-chooser.ui", &error);
+  if (error)
+    {
+      g_object_unref (builder);
+      g_warning ("failed to load input chooser: %s", error->message);
+      g_error_free (error);
+      return NULL;
+    }
+  chooser = WID ("input-dialog");
+  priv = g_new0 (CcInputChooserPrivate, 1);
+  g_object_set_data_full (G_OBJECT (chooser), "private", priv, cc_input_chooser_private_free);
   g_object_set_data_full (G_OBJECT (chooser), "builder", builder, g_object_unref);
 
+  priv->xkb_info = xkb_info;
+  priv->ibus_engines = ibus_engines;
+
+  priv->filter_entry = WID ("filter-entry");
+  priv->list = WID ("list");
+  priv->scrolledwindow = WID ("scrolledwindow");
+  priv->back_button = WID ("back-button");
+
+  gtk_widget_set_size_request (chooser, 400, 500);
+  gtk_window_set_resizable (GTK_WINDOW (chooser), TRUE);
+
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolledwindow),
+                                  GTK_POLICY_NEVER,
+                                  GTK_POLICY_AUTOMATIC);
+
+  egg_list_box_set_adjustment (EGG_LIST_BOX (priv->list),
+                               gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow)));
+  g_signal_connect (priv->list, "child-activated", G_CALLBACK (child_activated), chooser);
+
+  gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (WID ("action-area")), priv->back_button, TRUE);
+  g_signal_connect_swapped (priv->back_button, "clicked", G_CALLBACK (show_locale_widgets), chooser);
+
+  get_locale_infos (chooser);
+#ifdef HAVE_IBUS
+  get_ibus_locale_infos (chooser);
+#endif
+  show_locale_widgets (chooser);
+
+#if 0
   filtered_list = WID ("filtered_input_source_list");
   filter_entry = WID ("input_source_filter");
 
@@ -251,8 +594,6 @@ cc_input_chooser_new (GtkWindow    *main_window,
                                               "text", NAME_COLUMN,
                                               NULL);
 
-  gtk_window_set_transient_for (GTK_WINDOW (chooser), main_window);
-
   gtk_tree_view_append_column (GTK_TREE_VIEW (filtered_list),
                                visible_column);
   /* We handle searching ourselves, thank you. */
@@ -291,36 +632,54 @@ cc_input_chooser_new (GtkWindow    *main_window,
 
   g_signal_connect (G_OBJECT (filtered_list), "row-activated",
                     G_CALLBACK (row_activated), builder);
+#endif
 
-  gtk_widget_grab_focus (filter_entry);
-
-  gtk_widget_show (chooser);
+  gtk_window_set_transient_for (GTK_WINDOW (chooser), main_window);
 
   return chooser;
 }
 
+void
+cc_input_chooser_set_ibus_engines (GtkWidget  *chooser,
+                                   GHashTable *ibus_engines)
+{
+#ifdef HAVE_IBUS
+  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+
+  /* This should only be called once when IBus shows up in case it
+     wasn't up yet when the user opened the input chooser dialog. */
+  g_return_if_fail (priv->ibus_engines == NULL);
+
+  priv->ibus_engines = ibus_engines;
+  get_ibus_locale_infos (chooser);
+  show_locale_widgets (chooser);
+#endif
+}
+
 gboolean
 cc_input_chooser_get_selected (GtkWidget  *chooser,
                                gchar     **type,
                                gchar     **id,
                                gchar     **name)
 {
-  GtkWidget *tv;
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  GtkTreeSelection *selection;
-
-  tv = g_object_get_data (G_OBJECT (chooser), "filtered_input_source_list");
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
-  if (gtk_tree_selection_get_selected (selection, &model, &iter))
-    {
-      gtk_tree_model_get (model, &iter,
-                          TYPE_COLUMN, type,
-                          ID_COLUMN, id,
-                          NAME_COLUMN, name,
-                          -1);
-      return TRUE;
-    }
+  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+  GtkWidget *selected;
+  const gchar *t, *i, *n;
+
+  selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->list));
+  if (!selected)
+    return FALSE;
+
+  t = g_object_get_data (G_OBJECT (selected), "type");
+  i = g_object_get_data (G_OBJECT (selected), "id");
+  n = g_object_get_data (G_OBJECT (selected), "name");
+
+  if (!t || !i || !n)
+    return FALSE;
+
+  *type = g_strdup (t);
+  *id = g_strdup (i);
+  *name = g_strdup (n);
 
-  return FALSE;
+  return TRUE;
 }
diff --git a/panels/region/cc-input-chooser.h b/panels/region/cc-input-chooser.h
index 394db82..05fa448 100644
--- a/panels/region/cc-input-chooser.h
+++ b/panels/region/cc-input-chooser.h
@@ -32,6 +32,8 @@ G_BEGIN_DECLS
 GtkWidget   *cc_input_chooser_new          (GtkWindow    *parent,
                                             GnomeXkbInfo *xkb_info,
                                             GHashTable   *ibus_engines);
+void         cc_input_chooser_set_ibus_engines (GtkWidget *chooser,
+                                                GHashTable *ibus_engines);
 gboolean     cc_input_chooser_get_selected (GtkWidget    *chooser,
                                             gchar       **type,
                                             gchar       **id,
diff --git a/panels/region/cc-region-panel.c b/panels/region/cc-region-panel.c
index e39afa2..1242c46 100644
--- a/panels/region/cc-region-panel.c
+++ b/panels/region/cc-region-panel.c
@@ -527,6 +527,19 @@ update_ibus_active_sources (CcRegionPanel *self)
 }
 
 static void
+update_input_chooser (CcRegionPanel *self)
+{
+        CcRegionPanelPrivate *priv = self->priv;
+        GtkWidget *chooser;
+
+        chooser = g_object_get_data (G_OBJECT (self), "input-chooser");
+        if (!chooser)
+                return;
+
+        cc_input_chooser_set_ibus_engines (chooser, priv->ibus_engines);
+}
+
+static void
 fetch_ibus_engines_result (GObject       *object,
                            GAsyncResult  *result,
                            CcRegionPanel *self)
@@ -559,6 +572,7 @@ fetch_ibus_engines_result (GObject       *object,
         g_list_free (list);
 
         update_ibus_active_sources (self);
+        update_input_chooser (self);
 }
 
 static void
@@ -902,9 +916,6 @@ input_response (GtkWidget *chooser, gint response_id, gpointer data)
 
         if (response_id == GTK_RESPONSE_OK) {
                 if (cc_input_chooser_get_selected (chooser, &type, &id, &name)) {
-
-                        gtk_widget_destroy (chooser);
-
                         if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) {
                                 g_free (type);
                                 type = INPUT_SOURCE_TYPE_IBUS;
@@ -927,9 +938,9 @@ input_response (GtkWidget *chooser, gint response_id, gpointer data)
                         g_free (name);
                         g_clear_object (&app_info);
                 }
-        } else {
-                gtk_widget_destroy (chooser);
         }
+        gtk_widget_destroy (chooser);
+        g_object_set_data (G_OBJECT (self), "input-chooser", NULL);
 }
 
 static void
@@ -950,6 +961,9 @@ show_input_chooser (CcRegionPanel *self)
                 );
         g_signal_connect (chooser, "response",
                           G_CALLBACK (input_response), self);
+        gtk_window_present (GTK_WINDOW (chooser));
+
+        g_object_set_data (G_OBJECT (self), "input-chooser", chooser);
 }
 
 static void
diff --git a/panels/region/input-chooser.ui b/panels/region/input-chooser.ui
index 0a3f346..d2beffc 100644
--- a/panels/region/input-chooser.ui
+++ b/panels/region/input-chooser.ui
@@ -1,138 +1,80 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="2.16"/>
-  <object class="GtkListStore" id="input_source_model">
-    <columns>
-      <!-- display name -->
-      <column type="gchararray"/>
-      <!-- input source type -->
-      <column type="gchararray"/>
-      <!-- type specific identifier -->
-      <column type="gchararray"/>
-    </columns>
-  </object>
-  <object class="GtkTreeModelFilter" id="filtered_input_source_model">
-    <property name="child_model">input_source_model</property>
-  </object>
-  <object class="GtkDialog" id="input_source_chooser">
-    <property name="visible">False</property>
-    <property name="can_focus">False</property>
-    <property name="border_width">5</property>
-    <property name="title" translatable="yes">Select an input source</property>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkDialog" id="input-dialog">
+    <property name="title" translatable="yes">Add an Input Source</property>
     <property name="modal">True</property>
-    <property name="window_position">center-on-parent</property>
-    <property name="type_hint">dialog</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="resizable">False</property>
     <child internal-child="vbox">
-      <object class="GtkBox" id="dialog-vbox3">
+      <object class="GtkBox" id="vbox">
         <property name="visible">True</property>
-        <property name="can_focus">False</property>
         <property name="orientation">vertical</property>
-        <property name="spacing">2</property>
-        <child internal-child="action_area">
-          <object class="GtkButtonBox" id="hbtnBox">
+        <property name="spacing">0</property>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolledwindow">
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="layout_style">end</property>
-            <child>
-              <object class="GtkButton" id="cancel-button">
-                <property name="label">gtk-cancel</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="receives_default">False</property>
-                <property name="use_action_appearance">False</property>
-                <property name="use_stock">True</property>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="pack_type">end</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
+            <property name="hscrollbar-policy">never</property>
+            <property name="vscrollbar-policy">never</property>
+            <property name="shadow-type">in</property>
+            <property name="margin-left">6</property>
+            <property name="margin-right">6</property>
+            <property name="margin-top">6</property>
+            <property name="margin-bottom">6</property>
             <child>
-              <object class="GtkButton" id="ok-button">
-                <property name="label">gtk-add</property>
+              <object class="GtkViewport" id="viewport">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="receives_default">False</property>
-                <property name="use_action_appearance">False</property>
-                <property name="use_stock">True</property>
+                <child>
+                  <object class="EggListBox" id="list">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="vexpand">True</property>
+                    <property name="halign">fill</property>
+                    <property name="valign">fill</property>
+                  </object>
+                </child>
               </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="pack_type">end</property>
-                <property name="position">2</property>
-              </packing>
             </child>
           </object>
         </child>
         <child>
-          <object class="GtkVBox" id="vbox40">
+          <object class="GtkSearchEntry" id="filter-entry">
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="border_width">5</property>
-            <property name="spacing">6</property>
+            <property name="hexpand">True</property>
+            <property name="margin-left">6</property>
+            <property name="margin-right">6</property>
+            <property name="margin-top">6</property>
+            <property name="margin-bottom">6</property>
+          </object>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="action-area">
+            <property name="visible">True</property>
+            <property name="orientation">horizontal</property>
             <child>
-              <object class="GtkVBox" id="vbox1">
+              <object class="GtkButton" id="back-button">
+                <property name="visible">False</property>
+                <property name="label" translatable="yes">_Back</property>
+                <property name="use_underline" >True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton" id="cancel-button">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="spacing">6</property>
-                <child>
-                  <object class="GtkScrolledWindow" id="scrolledwindow1">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="hscrollbar_policy">never</property>
-                    <property name="shadow_type">etched-in</property>
-                    <property name="min_content_width">450</property>
-                    <property name="min_content_height">250</property>
-                    <child>
-                      <object class="GtkTreeView" id="filtered_input_source_list">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="model">filtered_input_source_model</property>
-                        <property name="headers_visible">False</property>
-                        <property name="search_column">0</property>
-                      </object>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="use_underline" >True</property>
               </object>
-              <packing>
-                <property name="expand">True</property>
-                <property name="fill">True</property>
-                <property name="position">0</property>
-              </packing>
             </child>
             <child>
-              <object class="GtkEntry" id="input_source_filter">
+              <object class="GtkButton" id="ok-button">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="invisible_char">â</property>
-                <property name="secondary-icon-name">edit-find-symbolic</property>
-                <property name="secondary-icon-activatable">False</property>
-                <property name="secondary-icon-sensitive">False</property>
+                <property name="label">gtk-add</property>
+                <property name="use_stock">True</property>
+                <property name="use_underline" >True</property>
               </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="pack_type">end</property>
-                <property name="position">1</property>
-              </packing>
             </child>
           </object>
-          <packing>
-            <property name="expand">True</property>
-            <property name="fill">True</property>
-            <property name="position">1</property>
-          </packing>
         </child>
       </object>
     </child>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]