[gnome-control-center/wip/region-panel: 38/43] region: New 'Add Input Source' dialog design
- From: Rui Matos <rtcm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center/wip/region-panel: 38/43] region: New 'Add Input Source' dialog design
- Date: Sun, 17 Feb 2013 00:33:10 +0000 (UTC)
commit f45b81b27e144137a14d88c6335cce920680da3d
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 | 1222 +++++++++++++++++++++++++++++++-------
panels/region/cc-input-chooser.h | 3 +-
panels/region/cc-region-panel.c | 24 +-
panels/region/input-chooser.ui | 160 ++----
4 files changed, 1086 insertions(+), 323 deletions(-)
---
diff --git a/panels/region/cc-input-chooser.c b/panels/region/cc-input-chooser.c
index b250cc3..ef57d61 100644
--- a/panels/region/cc-input-chooser.c
+++ b/panels/region/cc-input-chooser.c
@@ -17,10 +17,17 @@
* 02111-1307, USA.
*/
-#define _GNU_SOURCE
#include <config.h>
+#include <locale.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-common-language.h"
+#include "cc-util.h"
#include "cc-input-chooser.h"
#ifdef HAVE_IBUS
@@ -31,192 +38,997 @@
#define INPUT_SOURCE_TYPE_XKB "xkb"
#define INPUT_SOURCE_TYPE_IBUS "ibus"
+#define ARROW_NEXT "go-next-symbolic"
+#define ARROW_PREV "go-previous-symbolic"
+
+#define MAIN_WINDOW_WIDTH_RATIO 0.60
+
+typedef enum {
+ ROW_TRAVEL_DIRECTION_NONE,
+ ROW_TRAVEL_DIRECTION_FORWARD,
+ ROW_TRAVEL_DIRECTION_BACKWARD
+} RowTravelDirection;
+
+typedef enum {
+ ROW_LABEL_POSITION_START,
+ ROW_LABEL_POSITION_CENTER,
+ ROW_LABEL_POSITION_END
+} RowLabelPosition;
+
+typedef struct {
+ /* Not owned */
+ GtkWidget *add_button;
+ GtkWidget *filter_entry;
+ GtkWidget *list;
+ GtkWidget *scrolledwindow;
+ GtkAdjustment *adjustment;
+ GnomeXkbInfo *xkb_info;
+ GHashTable *ibus_engines;
+
+ /* Owned */
+ GtkWidget *more_item;
+ GtkWidget *no_results;
+ GHashTable *locales;
+ GHashTable *locales_by_language;
+ gboolean showing_extra;
+ gchar **filter_words;
+} 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;
+ gchar *name;
+ gchar *unaccented_name;
+ gchar *untranslated_name;
+ GtkWidget *default_input_source_widget;
+ GtkWidget *locale_widget;
+ GtkWidget *back_widget;
+ GHashTable *layout_widgets_by_id;
+ GHashTable *engine_widgets_by_id;
+} 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_free (info->name);
+ g_free (info->unaccented_name);
+ g_free (info->untranslated_name);
+ g_object_unref (info->default_input_source_widget);
+ g_object_unref (info->locale_widget);
+ g_object_unref (info->back_widget);
+ g_hash_table_destroy (info->layout_widgets_by_id);
+ g_hash_table_destroy (info->engine_widgets_by_id);
+ g_free (info);
+}
static void
-filter_changed (GtkBuilder *builder)
+set_row_widget_margins (GtkWidget *widget)
{
- GtkTreeModelFilter *filtered_model;
- GtkTreeView *tree_view;
- GtkTreeSelection *selection;
- GtkTreeIter selected_iter;
- GtkWidget *filter_entry;
- const gchar *pattern;
- gchar *upattern;
-
- 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);
+ gtk_widget_set_margin_left (widget, 20);
+ gtk_widget_set_margin_right (widget, 20);
+ gtk_widget_set_margin_top (widget, 6);
+ gtk_widget_set_margin_bottom (widget, 6);
+}
+
+static GtkWidget *
+padded_label_new (const gchar *text,
+ RowLabelPosition position,
+ RowTravelDirection direction,
+ gboolean dim_label)
+{
+ GtkWidget *widget;
+ GtkWidget *label;
+ GtkWidget *arrow;
+ gdouble alignment;
+ gboolean rtl;
+
+ rtl = (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL);
+
+ if (position == ROW_LABEL_POSITION_START)
+ alignment = 0.0;
+ else if (position == ROW_LABEL_POSITION_CENTER)
+ alignment = 0.5;
else
- g_object_set (G_OBJECT (filter_entry),
- "secondary-icon-name", "edit-clear-symbolic",
- "secondary-icon-activatable", TRUE,
- "secondary-icon-sensitive", TRUE,
- NULL);
-
- if (search_pattern_list != NULL)
- g_strfreev (search_pattern_list);
-
- 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);
-
- 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))
+ alignment = 1.0;
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+
+ if (direction == ROW_TRAVEL_DIRECTION_BACKWARD)
{
- 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);
+ arrow = gtk_image_new_from_icon_name (rtl ? ARROW_NEXT : ARROW_PREV,
+ GTK_ICON_SIZE_MENU);
+ gtk_box_pack_start (GTK_BOX (widget), arrow, FALSE, TRUE, 0);
}
- else
+
+ label = gtk_label_new (text);
+ gtk_misc_set_alignment (GTK_MISC (label), alignment, 0.5);
+ set_row_widget_margins (label);
+ gtk_box_pack_start (GTK_BOX (widget), label, TRUE, TRUE, 0);
+ if (dim_label)
+ gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
+
+ if (direction == ROW_TRAVEL_DIRECTION_FORWARD)
{
- GtkTreeIter iter;
- if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (filtered_model), &iter))
- gtk_tree_selection_select_iter (selection, &iter);
+ arrow = gtk_image_new_from_icon_name (rtl ? ARROW_PREV : ARROW_NEXT,
+ GTK_ICON_SIZE_MENU);
+ gtk_box_pack_start (GTK_BOX (widget), arrow, FALSE, TRUE, 0);
}
+
+ return widget;
+}
+
+static GtkWidget *
+more_widget_new (void)
+{
+ GtkWidget *widget;
+
+ widget = padded_label_new ("â", ROW_LABEL_POSITION_CENTER, ROW_TRAVEL_DIRECTION_NONE, FALSE);
+ gtk_widget_set_tooltip_text (widget, _("Moreâ"));
+
+ return widget;
+}
+
+static GtkWidget *
+no_results_widget_new (void)
+{
+ return padded_label_new (_("No input sources found"), ROW_LABEL_POSITION_CENTER,
ROW_TRAVEL_DIRECTION_NONE, TRUE);
+}
+
+static GtkWidget *
+back_widget_new (const gchar *text)
+{
+ return padded_label_new (text, ROW_LABEL_POSITION_CENTER, ROW_TRAVEL_DIRECTION_BACKWARD, TRUE);
+}
+
+static GtkWidget *
+locale_widget_new (const gchar *text)
+{
+ return padded_label_new (text, ROW_LABEL_POSITION_START, ROW_TRAVEL_DIRECTION_FORWARD, FALSE);
+}
+
+static GtkWidget *
+locale_separator_widget_new (const gchar *text)
+{
+ return padded_label_new (text, ROW_LABEL_POSITION_CENTER, ROW_TRAVEL_DIRECTION_NONE, TRUE);
+}
+
+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))
+ {
+ 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,
+ ROW_LABEL_POSITION_START,
+ ROW_TRAVEL_DIRECTION_NONE,
+ FALSE);
+ g_object_set_data (G_OBJECT (widget), "name", (gpointer) display_name);
+ g_object_set_data_full (G_OBJECT (widget), "unaccented-name",
+ cc_util_normalize_casefold_and_unaccent (display_name), g_free);
+ }
+ else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS))
+ {
+#ifdef HAVE_IBUS
+ gchar *display_name;
+ GtkWidget *image;
+
+ display_name = engine_get_display_name (g_hash_table_lookup (priv->ibus_engines, id));
+
+ widget = padded_label_new (display_name,
+ ROW_LABEL_POSITION_START,
+ ROW_TRAVEL_DIRECTION_NONE,
+ FALSE);
+ image = gtk_image_new_from_icon_name ("system-run-symbolic", GTK_ICON_SIZE_MENU);
+ set_row_widget_margins (image);
+ gtk_box_pack_start (GTK_BOX (widget), image, FALSE, TRUE, 0);
+
+ g_object_set_data_full (G_OBJECT (widget), "name", display_name, g_free);
+ g_object_set_data_full (G_OBJECT (widget), "unaccented-name",
+ cc_util_normalize_casefold_and_unaccent (display_name), g_free);
+#else
+ widget = NULL;
+#endif
+ }
+
+ if (widget)
+ {
+ 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)
+remove_all_children (GtkContainer *container)
{
- gtk_widget_set_sensitive (WID ("ok-button"),
- gtk_tree_selection_get_selected (selection, NULL, NULL));
+ GList *list, *l;
+
+ list = gtk_container_get_children (container);
+ for (l = list; l; l = l->next)
+ gtk_container_remove (container, (GtkWidget *) l->data);
+ g_list_free (list);
}
static void
-row_activated (GtkTreeView *tree_view,
- GtkTreePath *path,
- GtkTreeViewColumn *column,
- GtkBuilder *builder)
+set_fixed_size (GtkWidget *chooser)
{
- GtkWidget *add_button;
- GtkWidget *dialog;
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ GtkPolicyType policy;
+ gint width, height;
+
+ gtk_scrolled_window_get_policy (GTK_SCROLLED_WINDOW (priv->scrolledwindow), &policy, NULL);
+ if (policy == GTK_POLICY_AUTOMATIC)
+ return;
+
+ /* Don't let it automatically get wider than the main CC window nor
+ get taller than the initial height */
+ gtk_window_get_size (gtk_window_get_transient_for (GTK_WINDOW (chooser)),
+ &width, NULL);
+ gtk_window_get_size (GTK_WINDOW (chooser), NULL, &height);
+ gtk_widget_set_size_request (chooser, width * MAIN_WINDOW_WIDTH_RATIO, height);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolledwindow),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+}
+
+static void
+add_input_source_widgets_for_locale (GtkWidget *chooser,
+ LocaleInfo *info)
+{
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ GtkWidget *widget;
+ GHashTableIter iter;
+ const gchar *id;
+
+ if (info->default_input_source_widget)
+ gtk_container_add (GTK_CONTAINER (priv->list), info->default_input_source_widget);
+
+ g_hash_table_iter_init (&iter, info->layout_widgets_by_id);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &widget))
+ gtk_container_add (GTK_CONTAINER (priv->list), widget);
+
+ g_hash_table_iter_init (&iter, info->engine_widgets_by_id);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &widget))
+ gtk_container_add (GTK_CONTAINER (priv->list), widget);
+}
+
+static void
+show_input_sources_for_locale (GtkWidget *chooser,
+ LocaleInfo *info)
+{
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+
+ set_fixed_size (chooser);
+
+ remove_all_children (GTK_CONTAINER (priv->list));
+
+ if (!info->back_widget)
+ {
+ info->back_widget = g_object_ref_sink (back_widget_new (info->name));
+ g_object_set_data (G_OBJECT (info->back_widget), "back", GINT_TO_POINTER (TRUE));
+ g_object_set_data (G_OBJECT (info->back_widget), "locale-info", info);
+ }
+ gtk_container_add (GTK_CONTAINER (priv->list), info->back_widget);
+
+ add_input_source_widgets_for_locale (chooser, info);
- 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);
+ gtk_widget_show_all (priv->list);
+
+ gtk_adjustment_set_value (priv->adjustment,
+ gtk_adjustment_get_lower (priv->adjustment));
+ egg_list_box_set_separator_funcs (EGG_LIST_BOX (priv->list), NULL, NULL, NULL);
+ egg_list_box_refilter (EGG_LIST_BOX (priv->list));
+ egg_list_box_set_selection_mode (EGG_LIST_BOX (priv->list), GTK_SELECTION_SINGLE);
+
+ if (gtk_widget_is_visible (priv->filter_entry))
+ gtk_widget_grab_focus (priv->filter_entry);
+}
+
+static gboolean
+is_current_locale (const gchar *locale)
+{
+ return g_strcmp0 (setlocale (LC_CTYPE, NULL), locale) == 0;
}
static void
-entry_activated (GtkBuilder *builder,
- gpointer data)
+show_locale_widgets (GtkWidget *chooser)
+{
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ GHashTable *initial;
+ LocaleInfo *info;
+ GHashTableIter iter;
+
+ remove_all_children (GTK_CONTAINER (priv->list));
+
+ if (!priv->showing_extra)
+ initial = cc_common_language_get_initial_languages ();
+
+ g_hash_table_iter_init (&iter, priv->locales);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
+ {
+ if (!info->default_input_source_widget &&
+ !g_hash_table_size (info->layout_widgets_by_id) &&
+ !g_hash_table_size (info->engine_widgets_by_id))
+ continue;
+
+ if (!info->locale_widget)
+ {
+ info->locale_widget = g_object_ref_sink (locale_widget_new (info->name));
+ g_object_set_data (G_OBJECT (info->locale_widget), "locale-info", info);
+
+ if (!priv->showing_extra &&
+ !g_hash_table_contains (initial, info->id) &&
+ !is_current_locale (info->id))
+ g_object_set_data (G_OBJECT (info->locale_widget), "is-extra", GINT_TO_POINTER (TRUE));
+ }
+ gtk_container_add (GTK_CONTAINER (priv->list), info->locale_widget);
+ }
+
+ gtk_container_add (GTK_CONTAINER (priv->list), priv->more_item);
+
+ gtk_widget_show_all (priv->list);
+
+ gtk_adjustment_set_value (priv->adjustment,
+ gtk_adjustment_get_lower (priv->adjustment));
+ egg_list_box_set_separator_funcs (EGG_LIST_BOX (priv->list), NULL, NULL, NULL);
+ egg_list_box_refilter (EGG_LIST_BOX (priv->list));
+ egg_list_box_set_selection_mode (EGG_LIST_BOX (priv->list), GTK_SELECTION_NONE);
+
+ if (gtk_widget_is_visible (priv->filter_entry))
+ gtk_widget_grab_focus (priv->filter_entry);
+
+ if (!priv->showing_extra)
+ g_hash_table_destroy (initial);
+}
+
+static gint
+list_sort (gconstpointer a,
+ gconstpointer b,
+ gpointer data)
{
- row_activated (NULL, NULL, NULL, builder);
+ GtkWidget *chooser = data;
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ LocaleInfo *ia;
+ LocaleInfo *ib;
+ const gchar *la;
+ const gchar *lb;
+ gint retval;
+
+ /* Always goes at the start */
+ if (a == priv->no_results)
+ return -1;
+ if (b == priv->no_results)
+ return 1;
+
+ /* Always goes at the end */
+ if (a == priv->more_item)
+ return 1;
+ if (b == priv->more_item)
+ return -1;
+
+ ia = g_object_get_data (G_OBJECT (a), "locale-info");
+ ib = g_object_get_data (G_OBJECT (b), "locale-info");
+
+ /* The "Other" locale always goes at the end */
+ if (!ia->id[0] && ib->id[0])
+ return 1;
+ else if (ia->id[0] && !ib->id[0])
+ return -1;
+
+ retval = g_strcmp0 (ia->name, ib->name);
+ if (retval)
+ return retval;
+
+ la = g_object_get_data (G_OBJECT (a), "name");
+ lb = g_object_get_data (G_OBJECT (b), "name");
+
+ /* Only input sources have a "name" property and they should always
+ go after their respective heading */
+ if (la && !lb)
+ return 1;
+ else if (!la && lb)
+ return -1;
+ else if (!la && !lb)
+ return 0; /* Shouldn't happen */
+
+ /* The default input source always goes first in its group */
+ if (g_object_get_data (G_OBJECT (a), "default"))
+ return -1;
+ if (g_object_get_data (G_OBJECT (b), "default"))
+ return 1;
+
+ return g_strcmp0 (la, lb);
}
static gboolean
-filter_func (GtkTreeModel *model,
- GtkTreeIter *iter,
- gpointer data)
+match_all (gchar **words,
+ const gchar *str)
{
- gchar *name = NULL;
- gchar **pattern;
- gboolean rv = TRUE;
+ gchar **w;
- if (search_pattern_list == NULL || search_pattern_list[0] == NULL)
+ for (w = words; *w; ++w)
+ if (!strstr (str, *w))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+list_filter (GtkWidget *child,
+ gpointer user_data)
+{
+ GtkDialog *chooser = user_data;
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ LocaleInfo *info;
+ gboolean is_extra;
+ const gchar *source_name;
+
+ if (child == priv->more_item)
+ return !priv->showing_extra;
+
+ /* We hide this in the after-refilter handler below. */
+ if (child == priv->no_results)
return TRUE;
- gtk_tree_model_get (model, iter,
- NAME_COLUMN, &name,
- -1);
+ is_extra = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (child), "is-extra"));
- 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 (!priv->showing_extra && is_extra)
+ return FALSE;
- if (!is_pattern_found)
- {
- rv = FALSE;
- break;
- }
+ if (!priv->filter_words)
+ return TRUE;
+
+ info = g_object_get_data (G_OBJECT (child), "locale-info");
+
+ if (match_all (priv->filter_words, info->unaccented_name))
+ return TRUE;
+
+ if (match_all (priv->filter_words, info->untranslated_name))
+ return TRUE;
+
+ source_name = g_object_get_data (G_OBJECT (child), "unaccented-name");
+ if (source_name && match_all (priv->filter_words, source_name))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+update_separator (GtkWidget **separator,
+ GtkWidget *child,
+ GtkWidget *before,
+ gpointer user_data)
+{
+ LocaleInfo *child_info = NULL;
+ LocaleInfo *before_info = NULL;
+
+ if (*separator)
+ return;
+
+ if (child)
+ child_info = g_object_get_data (G_OBJECT (child), "locale-info");
+
+ if (before)
+ before_info = g_object_get_data (G_OBJECT (before), "locale-info");
+
+ if (child_info == before_info || !child_info)
+ return;
+
+ *separator = locale_separator_widget_new (child_info->name);
+ g_object_ref_sink (*separator);
+ gtk_widget_show_all (*separator);
+}
+
+static void
+show_filter_widgets (GtkWidget *chooser)
+{
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ LocaleInfo *info;
+ GHashTableIter iter;
+
+ remove_all_children (GTK_CONTAINER (priv->list));
+
+ gtk_container_add (GTK_CONTAINER (priv->list), priv->no_results);
+
+ g_hash_table_iter_init (&iter, priv->locales);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
+ add_input_source_widgets_for_locale (chooser, info);
+
+ gtk_widget_show_all (priv->list);
+
+ gtk_adjustment_set_value (priv->adjustment,
+ gtk_adjustment_get_lower (priv->adjustment));
+ egg_list_box_set_separator_funcs (EGG_LIST_BOX (priv->list), update_separator,
+ chooser, NULL);
+ egg_list_box_refilter (EGG_LIST_BOX (priv->list));
+ egg_list_box_set_selection_mode (EGG_LIST_BOX (priv->list), GTK_SELECTION_SINGLE);
+
+ if (gtk_widget_is_visible (priv->filter_entry))
+ gtk_widget_grab_focus (priv->filter_entry);
+}
+
+static gboolean
+strvs_differ (gchar **av,
+ gchar **bv)
+{
+ gchar **a, **b;
+
+ for (a = av, b = bv; *a && *b; ++a, ++b)
+ if (!g_str_equal (*a, *b))
+ return TRUE;
+
+ if (!*a && !*b)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+filter_changed (GtkWidget *chooser)
+{
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ gboolean was_filtering;
+ gchar **previous_words;
+ gchar *filter_contents = NULL;
+
+ previous_words = priv->filter_words;
+ was_filtering = previous_words != NULL;
+
+ filter_contents =
+ cc_util_normalize_casefold_and_unaccent (gtk_entry_get_text (GTK_ENTRY (priv->filter_entry)));
+
+ if (filter_contents)
+ {
+ priv->filter_words = g_strsplit_set (g_strstrip (filter_contents), " ", 0);
+ g_free (filter_contents);
+ }
+
+ if (!priv->filter_words || !priv->filter_words[0])
+ {
+ g_clear_pointer (&priv->filter_words, g_strfreev);
+ if (was_filtering)
+ show_locale_widgets (chooser);
+ }
+ else
+ {
+ if (!was_filtering)
+ show_filter_widgets (chooser);
+ else if (strvs_differ (priv->filter_words, previous_words))
+ egg_list_box_refilter (EGG_LIST_BOX (priv->list));
+ }
+
+ g_strfreev (previous_words);
+}
+
+typedef struct {
+ gint count;
+ GtkWidget *ignore;
+} CountChildrenData;
+
+static void
+count_visible_children (GtkWidget *widget,
+ gpointer user_data)
+{
+ CountChildrenData *data = user_data;
+ if (widget != data->ignore &&
+ gtk_widget_get_child_visible (widget) &&
+ gtk_widget_get_visible (widget))
+ data->count++;
+}
+
+static void
+end_refilter (EggListBox *list_box,
+ gpointer user_data)
+{
+ GtkDialog *chooser = user_data;
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ CountChildrenData data = { 0 };
+ gboolean visible;
+
+ data.ignore = priv->no_results;
+
+ gtk_container_foreach (GTK_CONTAINER (list_box),
+ count_visible_children, &data);
+
+ visible = (data.count == 0);
+
+ gtk_widget_set_visible (priv->no_results, visible);
+ egg_list_box_set_selection_mode (EGG_LIST_BOX (priv->list),
+ visible ? GTK_SELECTION_NONE : GTK_SELECTION_SINGLE);
+}
+
+static void
+show_more (GtkWidget *chooser)
+{
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+
+ set_fixed_size (chooser);
+
+ gtk_widget_show (priv->filter_entry);
+ gtk_widget_grab_focus (priv->filter_entry);
- } while (*++pattern != NULL);
- g_free (name);
+ priv->showing_extra = TRUE;
- return rv;
+ egg_list_box_refilter (EGG_LIST_BOX (priv->list));
}
static void
-populate_model (GtkListStore *store,
- GnomeXkbInfo *xkb_info,
- GHashTable *ibus_engines)
+child_activated (EggListBox *box,
+ GtkWidget *child,
+ GtkWidget *chooser)
{
- GtkTreeIter iter;
- const gchar *name;
- GList *sources, *tmp;
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ gpointer data;
- sources = gnome_xkb_info_get_all_layouts (xkb_info);
- for (tmp = sources; tmp; tmp = tmp->next)
+ if (!child)
+ return;
+
+ if (child == priv->more_item)
+ {
+ show_more (chooser);
+ return;
+ }
+
+ data = g_object_get_data (G_OBJECT (child), "back");
+ if (data)
+ {
+ show_locale_widgets (chooser);
+ return;
+ }
+
+ data = g_object_get_data (G_OBJECT (child), "name");
+ if (data)
{
- 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);
+ /* It's an input source, we just want to select it */
+ return;
}
- g_list_free (sources);
+
+ data = g_object_get_data (G_OBJECT (child), "locale-info");
+ if (data)
+ {
+ show_input_sources_for_locale (chooser, (LocaleInfo *) data);
+ return;
+ }
+}
+
+static void
+child_selected (EggListBox *box,
+ GtkWidget *child,
+ GtkWidget *chooser)
+{
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+
+ gtk_widget_set_sensitive (priv->add_button, child != NULL);
+}
+
+static void
+add_default_widget (GtkWidget *chooser,
+ LocaleInfo *info,
+ const gchar *type,
+ const gchar *id)
+{
+ info->default_input_source_widget = input_source_widget_new (chooser, type, id);
+ if (info->default_input_source_widget)
+ {
+ g_object_ref_sink (info->default_input_source_widget);
+ g_object_set_data (G_OBJECT (info->default_input_source_widget), "default", GINT_TO_POINTER (TRUE));
+ g_object_set_data (G_OBJECT (info->default_input_source_widget), "locale-info", info);
+ }
+}
+
+static void
+add_widgets_to_table (GtkWidget *chooser,
+ LocaleInfo *info,
+ GList *list,
+ const gchar *type,
+ const gchar *default_id)
+{
+ GHashTable *table;
+ GtkWidget *widget;
+ const gchar *id;
+
+ if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB))
+ table = info->layout_widgets_by_id;
+ else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS))
+ table = info->engine_widgets_by_id;
+ else
+ return;
+
+ while (list)
+ {
+ id = (const gchar *) list->data;
+
+ /* The widget for the default input source lives elsewhere */
+ if (g_strcmp0 (id, default_id))
+ {
+ widget = input_source_widget_new (chooser, type, id);
+ if (widget)
+ {
+ g_object_set_data (G_OBJECT (widget), "locale-info", info);
+ g_hash_table_replace (table, (gpointer) id, g_object_ref_sink (widget));
+ }
+ }
+ list = list->next;
+ }
+}
#ifdef HAVE_IBUS
- if (ibus_engines)
+static gboolean
+maybe_set_as_default (GtkWidget *chooser,
+ LocaleInfo *info,
+ const gchar *engine_id)
+{
+ const gchar *type, *id;
+
+ if (!gnome_get_input_source_from_locale (info->id, &type, &id))
+ return FALSE;
+
+ if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS) &&
+ g_str_equal (id, engine_id) &&
+ info->default_input_source_widget == NULL)
{
- gchar *display_name;
+ add_default_widget (chooser, info, type, id);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+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 *lang_code = NULL;
+ gchar *country_code = NULL;
+ const gchar *ibus_locale = ibus_engine_desc_get_language (engine);
+
+ 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;
+
+ if (gnome_get_input_source_from_locale (locale, &type, &id) &&
+ g_str_equal (type, INPUT_SOURCE_TYPE_IBUS) &&
+ g_str_equal (id, engine_id))
+ {
+ add_default_widget (chooser, info, type, id);
+ }
+ else
+ {
+ GList tmp = { 0 };
+ tmp.data = (gpointer) engine_id;
+ add_widgets_to_table (chooser, info, &tmp, INPUT_SOURCE_TYPE_IBUS, NULL);
+ }
+ }
+ else
+ {
+ g_warning ("IBus returned locale '%s' that we don't know about", locale);
+ }
- sources = g_hash_table_get_keys (ibus_engines);
- for (tmp = sources; tmp; tmp = tmp->next)
+ g_free (locale);
+ }
+ else if (lang_code != NULL)
{
- 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);
+ GHashTableIter iter;
+ GHashTable *locales_for_language;
+ gchar *language;
+
+ /* Most IBus engines only specify the language so we try to
+ add them to all locales for that 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))
+ {
+ if (!maybe_set_as_default (chooser, info, engine_id))
+ {
+ GList tmp = { 0 };
+ tmp.data = (gpointer) engine_id;
+ add_widgets_to_table (chooser, info, &tmp, INPUT_SOURCE_TYPE_IBUS, NULL);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Add it to the "Other" locale */
+ GList tmp = { 0 };
+ tmp.data = (gpointer) engine_id;
+ info = g_hash_table_lookup (priv->locales, "");
+ add_widgets_to_table (chooser, info, &tmp, INPUT_SOURCE_TYPE_IBUS, NULL);
}
- 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)
+ {
+ gchar *lang_code, *country_code;
+ gchar *simple_locale;
+ const gchar *type = NULL;
+ const gchar *id = NULL;
+
+ 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 */
+ info->name = gnome_get_language_from_locale (simple_locale, NULL);
+ info->unaccented_name = cc_util_normalize_casefold_and_unaccent (info->name);
+ info->untranslated_name = gnome_get_language_from_locale (simple_locale, "C");
+
+ 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))
+ {
+ add_default_widget (chooser, info, type, id);
+ g_hash_table_add (layouts_with_locale, (gpointer) id);
+ }
+
+ /* We don't own these ids */
+ info->layout_widgets_by_id = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, g_object_unref);
+ info->engine_widgets_by_id = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, g_object_unref);
+
+ list = gnome_xkb_info_get_layouts_for_language (priv->xkb_info, lang_code);
+ add_widgets_to_table (chooser, info, list, INPUT_SOURCE_TYPE_XKB, id);
+ 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_widgets_to_table (chooser, info, list, INPUT_SOURCE_TYPE_XKB, id);
+ add_ids_to_set (layouts_with_locale, list);
+ g_list_free (list);
+
+ g_free (lang_code);
+ g_free (country_code);
+ }
+ g_strfreev (locale_ids);
+
+ /* Add a "Other" locale to hold the remaining input sources */
+ info = g_new0 (LocaleInfo, 1);
+ info->id = g_strdup ("");
+ info->name = g_strdup (_("Other"));
+ info->unaccented_name = g_strdup ("");
+ info->untranslated_name = g_strdup ("");
+ g_hash_table_replace (priv->locales, info->id, info);
+
+ info->layout_widgets_by_id = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, g_object_unref);
+ info->engine_widgets_by_id = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, g_object_unref);
+
+ 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))
+ {
+ GList tmp = { 0 };
+ tmp.data = l->data;
+ add_widgets_to_table (chooser, info, &tmp, INPUT_SOURCE_TYPE_XKB, NULL);
+ }
+
+ g_list_free (list);
+
+ g_hash_table_destroy (layouts_with_locale);
+}
+
+static void
+cc_input_chooser_private_free (gpointer data)
+{
+ CcInputChooserPrivate *priv = data;
+
+ g_object_unref (priv->more_item);
+ g_object_unref (priv->no_results);
+ g_hash_table_destroy (priv->locales);
+ g_hash_table_destroy (priv->locales_by_language);
+ g_strfreev (priv->filter_words);
+ g_free (priv);
+}
GtkWidget *
cc_input_chooser_new (GtkWindow *main_window,
@@ -225,78 +1037,76 @@ 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;
+ gint width;
+ 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);
- filtered_list = WID ("filtered_input_source_list");
- filter_entry = WID ("input_source_filter");
+ priv->xkb_info = xkb_info;
+ priv->ibus_engines = ibus_engines;
- g_object_set_data (G_OBJECT (chooser),
- "filtered_input_source_list", filtered_list);
- visible_column =
- gtk_tree_view_column_new_with_attributes ("Input Sources",
- gtk_cell_renderer_text_new (),
- "text", NAME_COLUMN,
- NULL);
-
- gtk_window_set_transient_for (GTK_WINDOW (chooser), main_window);
+ priv->add_button = WID ("add-button");
+ priv->filter_entry = WID ("filter-entry");
+ priv->list = WID ("list");
+ priv->scrolledwindow = WID ("scrolledwindow");
+ priv->adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
- gtk_tree_view_append_column (GTK_TREE_VIEW (filtered_list),
- visible_column);
- /* We handle searching ourselves, thank you. */
- gtk_tree_view_set_enable_search (GTK_TREE_VIEW (filtered_list), FALSE);
- gtk_tree_view_set_search_column (GTK_TREE_VIEW (filtered_list), -1);
+ priv->more_item = g_object_ref_sink (more_widget_new ());
+ priv->no_results = g_object_ref_sink (no_results_widget_new ());
- g_signal_connect_swapped (G_OBJECT (filter_entry), "activate",
- G_CALLBACK (entry_activated), builder);
- g_signal_connect_swapped (G_OBJECT (filter_entry), "notify::text",
- G_CALLBACK (filter_changed), builder);
+ egg_list_box_set_adjustment (EGG_LIST_BOX (priv->list), priv->adjustment);
+ egg_list_box_set_filter_func (EGG_LIST_BOX (priv->list), list_filter, chooser, NULL);
+ egg_list_box_set_sort_func (EGG_LIST_BOX (priv->list), list_sort, chooser, NULL);
+ g_signal_connect (priv->list, "child-activated", G_CALLBACK (child_activated), chooser);
+ g_signal_connect (priv->list, "child-selected", G_CALLBACK (child_selected), chooser);
+ g_signal_connect_after (priv->list, "refilter", G_CALLBACK (end_refilter), chooser);
- g_signal_connect (G_OBJECT (filter_entry), "icon-release",
- G_CALLBACK (filter_clear), NULL);
+ g_signal_connect_swapped (priv->filter_entry, "changed", G_CALLBACK (filter_changed), chooser);
- filtered_model = GTK_TREE_MODEL_FILTER (gtk_builder_get_object (builder, "filtered_input_source_model"));
- model = GTK_LIST_STORE (gtk_builder_get_object (builder, "input_source_model"));
-
- g_object_set_data_full (G_OBJECT (chooser), "xkb-info", g_object_ref (xkb_info), g_object_unref);
- g_object_set_data_full (G_OBJECT (chooser), "ibus-engines", g_hash_table_ref (ibus_engines),
(GDestroyNotify)g_hash_table_unref);
-
- populate_model (model, xkb_info, ibus_engines);
-
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
- NAME_COLUMN, GTK_SORT_ASCENDING);
- gtk_tree_model_filter_set_visible_func (filtered_model,
- filter_func,
- NULL, NULL);
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (filtered_list));
+ get_locale_infos (chooser);
+#ifdef HAVE_IBUS
+ get_ibus_locale_infos (chooser);
+#endif
+ show_locale_widgets (chooser);
- g_signal_connect (G_OBJECT (selection), "changed",
- G_CALLBACK (selection_changed), builder);
+ /* Try to come up with a sensible width */
+ gtk_window_get_size (main_window, &width, NULL);
+ gtk_widget_set_size_request (chooser, width * MAIN_WINDOW_WIDTH_RATIO, -1);
+ gtk_window_set_resizable (GTK_WINDOW (chooser), TRUE);
- if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (filtered_model), &iter))
- gtk_tree_selection_select_iter (selection, &iter);
+ gtk_window_set_transient_for (GTK_WINDOW (chooser), main_window);
- g_signal_connect (G_OBJECT (filtered_list), "row-activated",
- G_CALLBACK (row_activated), builder);
+ return chooser;
+}
- gtk_widget_grab_focus (filter_entry);
+void
+cc_input_chooser_set_ibus_engines (GtkWidget *chooser,
+ GHashTable *ibus_engines)
+{
+#ifdef HAVE_IBUS
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
- gtk_widget_show (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);
- return chooser;
+ priv->ibus_engines = ibus_engines;
+ get_ibus_locale_infos (chooser);
+ show_locale_widgets (chooser);
+#endif
}
gboolean
@@ -305,22 +1115,24 @@ cc_input_chooser_get_selected (GtkWidget *chooser,
gchar **id,
gchar **name)
{
- GtkWidget *tv;
- GtkTreeModel *model;
- GtkTreeIter iter;
- GtkTreeSelection *selection;
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ GtkWidget *selected;
+ const gchar *t, *i, *n;
- 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;
- }
+ selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->list));
+ if (!selected)
+ return FALSE;
- 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 TRUE;
}
diff --git a/panels/region/cc-input-chooser.h b/panels/region/cc-input-chooser.h
index 394db82..72a8881 100644
--- a/panels/region/cc-input-chooser.h
+++ b/panels/region/cc-input-chooser.h
@@ -21,7 +21,6 @@
#define __CC_INPUT_CHOOSER_H__
#include <gtk/gtk.h>
-#include <glib-object.h>
#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <libgnome-desktop/gnome-xkb-info.h>
@@ -32,6 +31,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 628cd46..55e525e 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
@@ -916,9 +930,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) &&
!input_source_already_added (self, id)) {
-
- gtk_widget_destroy (chooser);
-
if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) {
g_free (type);
type = INPUT_SOURCE_TYPE_IBUS;
@@ -941,9 +952,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
@@ -964,6 +975,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..5d16271 100644
--- a/panels/region/input-chooser.ui
+++ b/panels/region/input-chooser.ui
@@ -1,143 +1,79 @@
<?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">True</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>
+ <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="cancel-button">
- <property name="label">gtk-cancel</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>
- </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>
- <child>
- <object class="GtkButton" id="ok-button">
- <property name="label">gtk-add</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>
+ <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">False</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="can_focus">False</property>
- <property name="border_width">5</property>
- <property name="spacing">6</property>
+ <property name="orientation">horizontal</property>
<child>
- <object class="GtkVBox" id="vbox1">
+ <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="add-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="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>
<action-widgets>
- <action-widget response="-5">ok-button</action-widget>
+ <action-widget response="-5">add-button</action-widget>
<action-widget response="-6">cancel-button</action-widget>
</action-widgets>
</object>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]