[gnome-initial-setup] keyboard: Update the input sources page to bring up to date w/GNOME 3.8
- From: Michael Wood <mwood src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-initial-setup] keyboard: Update the input sources page to bring up to date w/GNOME 3.8
- Date: Tue, 9 Apr 2013 10:26:13 +0000 (UTC)
commit 947b3d58ef9a83a6fa3c6ec6164e7951f0e8c389
Author: Michael Wood <michael g wood intel com>
Date: Mon Apr 8 17:28:46 2013 +0100
keyboard: Update the input sources page to bring up to date w/GNOME 3.8
Import latest backend and design changes from gnome-control-center for
the input sources selection.
https://bugzilla.gnome.org/show_bug.cgi?id=696196
gnome-initial-setup/pages/keyboard/Makefile.am | 8 +-
gnome-initial-setup/pages/keyboard/cc-ibus-utils.c | 41 +
gnome-initial-setup/pages/keyboard/cc-ibus-utils.h | 31 +
.../pages/keyboard/cc-input-chooser.c | 1151 +++++++++++++++
.../pages/keyboard/cc-input-chooser.h | 43 +
.../pages/keyboard/gis-keyboard-page.c | 1496 +++++++++++++++++++-
.../pages/keyboard/gis-keyboard-page.h | 13 +-
.../pages/keyboard/gis-keyboard-page.ui | 393 +++---
.../pages/keyboard/input-chooser.ui | 81 ++
.../pages/keyboard/keyboard.gresource.xml | 4 +-
po/POTFILES.in | 4 +-
11 files changed, 2989 insertions(+), 276 deletions(-)
---
diff --git a/gnome-initial-setup/pages/keyboard/Makefile.am b/gnome-initial-setup/pages/keyboard/Makefile.am
index 6ce8c5a..ffefc55 100644
--- a/gnome-initial-setup/pages/keyboard/Makefile.am
+++ b/gnome-initial-setup/pages/keyboard/Makefile.am
@@ -3,6 +3,7 @@ noinst_LTLIBRARIES = libgiskeyboard.la
AM_CPPFLAGS = \
$(INITIAL_SETUP_CFLAGS) \
+ -I$(top_srcdir)/libgd \
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
-DUIDIR="\"$(uidir)\""
@@ -15,9 +16,10 @@ keyboard-resources.h: keyboard.gresource.xml $(resource_files)
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-header $<
BUILT_SOURCES += keyboard-resources.c keyboard-resources.h
-libgiskeyboard_la_SOURCES = \
- gis-keyboard-page.c gis-keyboard-page.h \
- gnome-region-panel-input.c gnome-region-panel-input.h \
+libgiskeyboard_la_SOURCES = \
+ cc-input-chooser.c cc-input-chooser.h \
+ cc-ibus-utils.c cc-ibus-utils.h \
+ gis-keyboard-page.c gis-keyboard-page.h \
$(BUILT_SOURCES)
libgiskeyboard_la_CFLAGS = $(INITIAL_SETUP_CFLAGS) -I "$(srcdir)/../.."
diff --git a/gnome-initial-setup/pages/keyboard/cc-ibus-utils.c
b/gnome-initial-setup/pages/keyboard/cc-ibus-utils.c
new file mode 100644
index 0000000..54f9fb7
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-ibus-utils.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#ifdef HAVE_IBUS
+#include "cc-ibus-utils.h"
+
+gchar *
+engine_get_display_name (IBusEngineDesc *engine_desc)
+{
+ const gchar *name;
+ const gchar *language_code;
+ const gchar *language;
+ gchar *display_name;
+
+ name = ibus_engine_desc_get_longname (engine_desc);
+ language_code = ibus_engine_desc_get_language (engine_desc);
+ language = ibus_get_language_name (language_code);
+ display_name = g_strdup_printf ("%s (%s)", language, name);
+
+ return display_name;
+}
+
+#endif /* HAVE_IBUS */
diff --git a/gnome-initial-setup/pages/keyboard/cc-ibus-utils.h
b/gnome-initial-setup/pages/keyboard/cc-ibus-utils.h
new file mode 100644
index 0000000..bcd0c68
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-ibus-utils.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GIS_IBUS_UTILS_H__
+#define __GIS_IBUS_UTILS_H__
+
+#include <ibus.h>
+
+G_BEGIN_DECLS
+
+gchar *engine_get_display_name (IBusEngineDesc *engine_desc);
+
+G_END_DECLS
+
+#endif /* __GIS_IBUS_UTILS_H__ */
diff --git a/gnome-initial-setup/pages/keyboard/cc-input-chooser.c
b/gnome-initial-setup/pages/keyboard/cc-input-chooser.c
new file mode 100644
index 0000000..1a7e38a
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-input-chooser.c
@@ -0,0 +1,1151 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#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.h>
+
+#include <../language/cc-common-language.h>
+#include "cc-input-chooser.h"
+
+#ifdef HAVE_IBUS
+#include <ibus.h>
+#include "cc-ibus-utils.h"
+#endif /* HAVE_IBUS */
+
+#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))
+
+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
+locale_info_free (gpointer data)
+{
+ LocaleInfo *info = data;
+
+ 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
+set_row_widget_margins (GtkWidget *widget)
+{
+ 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
+ alignment = 1.0;
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+
+ if (direction == ROW_TRAVEL_DIRECTION_BACKWARD)
+ {
+ 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);
+ }
+
+ 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)
+ {
+ 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 = NULL;
+
+ 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_style_context_add_class (gtk_widget_get_style_context (image), "dim-label");
+ 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 /* HAVE_IBUS */
+ }
+
+ 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
+remove_all_children (GtkContainer *container)
+{
+ 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
+set_fixed_size (GtkWidget *chooser)
+{
+ 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 GIS 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);
+
+ 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
+show_locale_widgets (GtkWidget *chooser)
+{
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ GHashTable *initial = NULL;
+ 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);
+
+ return;
+}
+
+static gint
+list_sort (gconstpointer a,
+ gconstpointer b,
+ gpointer data)
+{
+ 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
+match_all (gchar **words,
+ const gchar *str)
+{
+ gchar **w;
+
+ 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;
+
+ is_extra = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (child), "is-extra"));
+
+ if (!priv->showing_extra && is_extra)
+ return FALSE;
+
+ 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 == NULL && *b == NULL)
+ 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);
+
+ priv->showing_extra = TRUE;
+
+ egg_list_box_refilter (EGG_LIST_BOX (priv->list));
+}
+
+static void
+child_activated (EggListBox *box,
+ GtkWidget *child,
+ GtkWidget *chooser)
+{
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ gpointer data;
+
+ 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)
+ {
+ /* It's an input source, we just want to select it */
+ return;
+ }
+
+ 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;
+ }
+}
+
+static void
+add_widget (GtkWidget *chooser,
+ LocaleInfo *info,
+ const gchar *type,
+ const gchar *id)
+{
+ GList tmp = { 0 };
+ tmp.data = (gpointer) id;
+ add_widgets_to_table (chooser, info, &tmp, type, NULL);
+}
+
+static void
+add_widget_other (GtkWidget *chooser,
+ const gchar *type,
+ const gchar *id)
+{
+ CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+ LocaleInfo *info = g_hash_table_lookup (priv->locales, "");
+ add_widget (chooser, info, type, id);
+}
+
+#ifdef HAVE_IBUS
+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)
+ {
+ 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
+ {
+ add_widget (chooser, info, INPUT_SOURCE_TYPE_IBUS, engine_id);
+ }
+ }
+ else
+ {
+ add_widget_other (chooser, INPUT_SOURCE_TYPE_IBUS, engine_id);
+ }
+
+ g_free (locale);
+ }
+ else if (lang_code != NULL)
+ {
+ 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))
+ add_widget (chooser, info, INPUT_SOURCE_TYPE_IBUS, engine_id);
+ }
+ else
+ {
+ add_widget_other (chooser, INPUT_SOURCE_TYPE_IBUS, engine_id);
+ }
+ }
+ else
+ {
+ add_widget_other (chooser, INPUT_SOURCE_TYPE_IBUS, engine_id);
+ }
+
+ g_free (country_code);
+ g_free (lang_code);
+ }
+}
+#endif /* HAVE_IBUS */
+
+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;
+ gchar *tmp;
+ 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);
+ tmp = gnome_get_language_from_locale (simple_locale, "C");
+ info->untranslated_name = cc_util_normalize_casefold_and_unaccent (tmp);
+ g_free (tmp);
+
+ 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))
+ add_widget_other (chooser, INPUT_SOURCE_TYPE_XKB, 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_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,
+ GnomeXkbInfo *xkb_info,
+ GHashTable *ibus_engines)
+{
+ GtkBuilder *builder;
+ GtkWidget *chooser;
+ CcInputChooserPrivate *priv;
+ gint width;
+ GError *error = NULL;
+
+ builder = gtk_builder_new ();
+ if (gtk_builder_add_from_resource (builder, "/org/gnome/initial-setup/input-chooser.ui", &error) == 0)
+ {
+ 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->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));
+
+ priv->more_item = g_object_ref_sink (more_widget_new ());
+ priv->no_results = g_object_ref_sink (no_results_widget_new ());
+
+ 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_swapped (priv->filter_entry, "changed", G_CALLBACK (filter_changed), chooser);
+
+ get_locale_infos (chooser);
+#ifdef HAVE_IBUS
+ get_ibus_locale_infos (chooser);
+#endif /* HAVE_IBUS */
+ show_locale_widgets (chooser);
+
+ /* 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);
+
+ 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 /* HAVE_IBUS */
+}
+
+gboolean
+cc_input_chooser_get_selected (GtkWidget *chooser,
+ gchar **type,
+ gchar **id,
+ gchar **name)
+{
+ 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 TRUE;
+}
diff --git a/gnome-initial-setup/pages/keyboard/cc-input-chooser.h
b/gnome-initial-setup/pages/keyboard/cc-input-chooser.h
new file mode 100644
index 0000000..a9edc65
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-input-chooser.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GIS_INPUT_CHOOSER_H__
+#define __GIS_INPUT_CHOOSER_H__
+
+#include <gtk/gtk.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-xkb-info.h>
+
+
+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,
+ gchar **name);
+
+G_END_DECLS
+
+#endif /* __GIS_INPUT_CHOOSER_H__ */
diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
index bed6773..278a5d3 100644
--- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
+++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2012 Intel, Inc
- * Copyright (C) 2013 Red Hat, Inc.
+ * Copyright (C) 2010 Intel, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,67 +15,1506 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
- * Authors:
- * Michael Wood <michael g wood intel com>
- * Jasper St. Pierre <jstpierre mecheye net>
- *
- * Based on gnome-control-center region page by:
- * Sergey Udaltsov <svu gnome org>
+ * Author: Sergey Udaltsov <svu gnome org>
*
*/
#define PAGE_ID "keyboard"
#include "config.h"
-#include "keyboard-resources.h"
-#include "gis-keyboard-page.h"
+#include <locale.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
#include <gtk/gtk.h>
-#include "gnome-region-panel-input.h"
+#include "gis-keyboard-page.h"
+#include "keyboard-resources.h"
+#include "cc-input-chooser.h"
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-languages.h>
+#include <libgnome-desktop/gnome-xkb-info.h>
+
+#ifdef HAVE_IBUS
+#include <ibus.h>
+#include "cc-ibus-utils.h"
+#endif
+
+#include <act/act.h>
+
+#include <egg-list-box.h>
+#include <libgd/gd.h>
+
+#define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.gnome.desktop.input-sources"
+#define KEY_CURRENT_INPUT_SOURCE "current"
+#define KEY_INPUT_SOURCES "sources"
+
+#define GNOME_SYSTEM_LOCALE_DIR "org.gnome.system.locale"
+#define KEY_REGION "region"
+
+#define INPUT_SOURCE_TYPE_XKB "xkb"
+#define INPUT_SOURCE_TYPE_IBUS "ibus"
+
+#define MAX_INPUT_ROWS_VISIBLE 5
G_DEFINE_TYPE (GisKeyboardPage, gis_keyboard_page, GIS_TYPE_PAGE)
-#define OBJ(type,name) ((type)gtk_builder_get_object(GIS_PAGE(page)->builder,(name)))
-#define WID(name) OBJ(GtkWidget*,name)
+#define WID(s) GTK_WIDGET (gtk_builder_get_object (self->priv->builder, s))
+
+#define KEYBOARD_PAGE_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GIS_TYPE_KEYBOARD_PAGE,
GisKeyboardPagePrivate))
+
+typedef enum {
+ CHOOSE_LANGUAGE,
+ ADD_INPUT,
+ REMOVE_INPUT
+} SystemOp;
+
+struct _GisKeyboardPagePrivate {
+ GtkBuilder *builder;
+
+ GtkWidget *login_button;
+ GtkWidget *login_label;
+ gboolean login;
+ GPermission *permission;
+ SystemOp op;
+ GDBusProxy *localed;
+ GDBusProxy *session;
+ GCancellable *cancellable;
+
+ GtkWidget *overlay;
+ GtkWidget *notification;
+
+ GtkWidget *language_section;
+ GtkWidget *language_row;
+ GtkWidget *language_label;
+ GtkWidget *formats_row;
+ GtkWidget *formats_label;
+
+ ActUserManager *user_manager;
+ ActUser *user;
+ GSettings *locale_settings;
+
+ gchar *language;
+ gchar *region;
+ gchar *system_language;
+ gchar *system_region;
+
+ GtkWidget *input_section;
+ GtkWidget *input_list;
+ GtkWidget *add_input;
+ GtkWidget *remove_input;
+ GtkWidget *show_config;
+ GtkWidget *show_layout;
+ GtkWidget *input_scrolledwindow;
+ guint n_input_rows;
+
+ GSettings *input_settings;
+ GnomeXkbInfo *xkb_info;
+#ifdef HAVE_IBUS
+ IBusBus *ibus;
+ GHashTable *ibus_engines;
+ GCancellable *ibus_cancellable;
+#endif
+};
static void
-gis_keyboard_page_constructed (GObject *object)
+gis_keyboard_page_finalize (GObject *object)
{
- GisKeyboardPage *page = GIS_KEYBOARD_PAGE (object);
+ GisKeyboardPage *self = GIS_KEYBOARD_PAGE (object);
+ GisKeyboardPagePrivate *priv = self->priv;
- G_OBJECT_CLASS (gis_keyboard_page_parent_class)->constructed (object);
+ g_cancellable_cancel (priv->cancellable);
+ g_clear_object (&priv->cancellable);
- setup_input_tabs (GIS_PAGE (page)->builder, GIS_KEYBOARD_PAGE (page));
+ if (priv->user_manager) {
+ g_signal_handlers_disconnect_by_data (priv->user_manager, self);
+ priv->user_manager = NULL;
+ }
- gtk_container_add (GTK_CONTAINER (page), WID("keyboard-page"));
+ if (priv->user) {
+ g_signal_handlers_disconnect_by_data (priv->user, self);
+ priv->user = NULL;
+ }
- gis_page_set_complete (GIS_PAGE (page), TRUE);
+ g_clear_object (&priv->permission);
+ g_clear_object (&priv->localed);
+ g_clear_object (&priv->session);
+ g_clear_object (&priv->builder);
+ g_clear_object (&priv->locale_settings);
+ g_clear_object (&priv->input_settings);
+ g_clear_object (&priv->xkb_info);
+#ifdef HAVE_IBUS
+ g_clear_object (&priv->ibus);
+ if (priv->ibus_cancellable)
+ g_cancellable_cancel (priv->ibus_cancellable);
+ g_clear_object (&priv->ibus_cancellable);
+ g_clear_pointer (&priv->ibus_engines, g_hash_table_destroy);
+#endif
+ g_free (priv->language);
+ g_free (priv->region);
+ g_free (priv->system_language);
+ g_free (priv->system_region);
- gtk_widget_show (GTK_WIDGET (page));
+ G_OBJECT_CLASS (gis_keyboard_page_parent_class)->finalize (object);
}
static void
-gis_keyboard_page_locale_changed (GisPage *page)
+gis_keyboard_page_constructed (GObject *object)
+{
+ GisKeyboardPage *self = GIS_KEYBOARD_PAGE (object);
+ GisKeyboardPagePrivate *priv = self->priv;
+
+ G_OBJECT_CLASS (gis_keyboard_page_parent_class)->constructed (object);
+
+ gis_page_set_complete (GIS_PAGE (self), TRUE);
+
+ gtk_widget_show (GTK_WIDGET (self));
+}
+
+static const char *
+gis_keyboard_page_get_help_uri (GisPage *page)
+{
+ return "help:gnome-help/prefs-language";
+}
+
+static GtkBuilder *
+gis_keyboard_page_get_builder (GisPage *page)
{
- gis_page_set_title (GIS_PAGE (page), _("Keyboard Layout"));
+ GisKeyboardPage *self = GIS_KEYBOARD_PAGE (page);
+ GisKeyboardPagePrivate *priv = self->priv;
+
+ return priv->builder;
}
static void
gis_keyboard_page_class_init (GisKeyboardPageClass * klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GisPageClass * page_class = GIS_PAGE_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GisPageClass * page_class = GIS_PAGE_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GisKeyboardPagePrivate));
+
+ page_class->page_id = PAGE_ID;
+ page_class->get_builder = gis_keyboard_page_get_builder;
+
+ object_class->constructed = gis_keyboard_page_constructed;
+ object_class->finalize = gis_keyboard_page_finalize;
+}
+
+static void
+restart_now (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+
+ gd_notification_dismiss (GD_NOTIFICATION (self->priv->notification));
+
+ g_dbus_proxy_call (priv->session,
+ "Logout",
+ g_variant_new ("(u)", 0),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+}
+
+static void
+show_restart_notification (GisKeyboardPage *self,
+ const gchar *locale)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GtkWidget *box;
+ GtkWidget *label;
+ GtkWidget *button;
+ gchar *current_locale;
+
+ if (priv->notification)
+ return;
+
+ if (locale) {
+ current_locale = g_strdup (setlocale (LC_MESSAGES, NULL));
+ setlocale (LC_MESSAGES, locale);
+ }
+
+ priv->notification = gd_notification_new ();
+ g_object_add_weak_pointer (G_OBJECT (priv->notification),
+ (gpointer *)&priv->notification);
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 24);
+ gtk_widget_set_margin_left (box, 12);
+ gtk_widget_set_margin_right (box, 12);
+ gtk_widget_set_margin_top (box, 6);
+ gtk_widget_set_margin_bottom (box, 6);
+ label = gtk_label_new (_("Your session needs to be restarted for changes to take effect"));
+ button = gtk_button_new_with_label (_("Restart Now"));
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (restart_now), self);
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+ gtk_widget_show_all (box);
+
+ gtk_container_add (GTK_CONTAINER (priv->notification), box);
+ gtk_overlay_add_overlay (GTK_OVERLAY (self->priv->overlay), priv->notification);
+ gtk_widget_show (priv->notification);
+
+ if (locale) {
+ setlocale (LC_MESSAGES, current_locale);
+ g_free (current_locale);
+ }
+}
+
+static void
+update_separator_func (GtkWidget **separator,
+ GtkWidget *child,
+ GtkWidget *before,
+ gpointer user_data)
+{
+ if (before == NULL)
+ return;
+
+ if (*separator == NULL) {
+ *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ g_object_ref_sink (*separator);
+ gtk_widget_show (*separator);
+ }
+}
+
+typedef struct {
+ GisKeyboardPage *self;
+ int category;
+ gchar *target_locale;
+} MaybeNotifyData;
+
+static void
+maybe_notify_finish (GObject *source,
+ GAsyncResult *res,
+ gpointer data)
+{
+ MaybeNotifyData *mnd = data;
+ GisKeyboardPage *self = mnd->self;
+ GError *error = NULL;
+ GVariant *retval = NULL;
+ gchar *current_lang_code = NULL;
+ gchar *current_country_code = NULL;
+ gchar *target_lang_code = NULL;
+ gchar *target_country_code = NULL;
+ const gchar *current_locale = NULL;
+
+ retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
+ if (!retval) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to get locale: %s\n", error->message);
+ goto out;
+ }
+
+ g_variant_get (retval, "(&s)", ¤t_locale);
+
+ if (!gnome_parse_locale (current_locale,
+ ¤t_lang_code,
+ ¤t_country_code,
+ NULL,
+ NULL))
+ goto out;
+
+ if (!gnome_parse_locale (mnd->target_locale,
+ &target_lang_code,
+ &target_country_code,
+ NULL,
+ NULL))
+ goto out;
+
+ if (g_str_equal (current_lang_code, target_lang_code) == FALSE ||
+ g_str_equal (current_country_code, target_country_code) == FALSE)
+ show_restart_notification (self,
+ mnd->category == LC_MESSAGES ? mnd->target_locale : NULL);
+out:
+ g_free (target_country_code);
+ g_free (target_lang_code);
+ g_free (current_country_code);
+ g_free (current_lang_code);
+ g_clear_pointer (&retval, g_variant_unref);
+ g_clear_error (&error);
+ g_free (mnd->target_locale);
+ g_free (mnd);
+}
+
+static void
+maybe_notify (GisKeyboardPage *self,
+ int category,
+ const gchar *target_locale)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ MaybeNotifyData *mnd;
+
+ mnd = g_new0 (MaybeNotifyData, 1);
+ mnd->self = self;
+ mnd->category = category;
+ mnd->target_locale = g_strdup (target_locale);
+
+ g_dbus_proxy_call (priv->session,
+ "GetLocale",
+ g_variant_new ("(i)", category),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ priv->cancellable,
+ maybe_notify_finish,
+ mnd);
+}
+
+static void set_localed_locale (GisKeyboardPage *self);
+
+static void
+update_region (GisKeyboardPage *self,
+ const gchar *region)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+
+ if (g_strcmp0 (region, priv->region) == 0)
+ return;
+
+ g_settings_set_string (priv->locale_settings, KEY_REGION, region);
+ maybe_notify (self, LC_TIME, region);
+}
+
+static void show_input_chooser (GisKeyboardPage *self);
+static void remove_selected_input (GisKeyboardPage *self);
+
+static void
+permission_acquired (GObject *source,
+ GAsyncResult *res,
+ gpointer data)
+{
+ GisKeyboardPage *self = data;
+ GisKeyboardPagePrivate *priv = self->priv;
+ GError *error = NULL;
+ gboolean allowed;
+
+ allowed = g_permission_acquire_finish (priv->permission, res, &error);
+ if (error) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to acquire permission: %s\n", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (allowed) {
+ switch (priv->op) {
+ case ADD_INPUT:
+ show_input_chooser (self);
+ break;
+ case REMOVE_INPUT:
+ remove_selected_input (self);
+ break;
+ default:
+ g_warning ("Unknown privileged operation: %d\n", priv->op);
+ break;
+ }
+ }
+}
+
+static void
+update_region_label (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ const gchar *region;
+ gchar *name;
+
+ if (priv->region == NULL || priv->region[0] == '\0')
+ region = priv->language;
+ else
+ region = priv->region;
+
+ name = gnome_get_country_from_locale (region, region);
+ gtk_label_set_label (GTK_LABEL (priv->formats_label), name);
+ g_free (name);
+}
+
+static void
+update_region_from_setting (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+
+ g_free (priv->region);
+ priv->region = g_settings_get_string (priv->locale_settings, KEY_REGION);
+ update_region_label (self);
+}
+
+static void
+update_language_label (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ const gchar *language;
+ gchar *name;
+
+ if (priv->login)
+ language = priv->system_language;
+ else
+ language = priv->language;
+ if (language)
+ name = gnome_get_language_from_locale (language, language);
+ else
+ name = g_strdup (C_("Language", "None"));
+ gtk_label_set_label (GTK_LABEL (priv->language_label), name);
+ g_free (name);
+
+ /* Formats will change too if not explicitly set. */
+ update_region_label (self);
+}
+
+#ifdef HAVE_IBUS
+static void
+update_ibus_active_sources (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GList *rows, *l;
+ GtkWidget *row;
+ const gchar *type;
+ const gchar *id;
+ IBusEngineDesc *engine_desc;
+ gchar *display_name;
+ GtkWidget *label;
+
+ rows = gtk_container_get_children (GTK_CONTAINER (priv->input_list));
+ for (l = rows; l; l = l->next) {
+ row = l->data;
+ type = g_object_get_data (G_OBJECT (row), "type");
+ id = g_object_get_data (G_OBJECT (row), "id");
+ if (g_strcmp0 (type, INPUT_SOURCE_TYPE_IBUS) != 0)
+ continue;
+
+ engine_desc = g_hash_table_lookup (priv->ibus_engines, id);
+ if (engine_desc) {
+ display_name = engine_get_display_name (engine_desc);
+ label = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "label"));
+ gtk_label_set_text (GTK_LABEL (label), display_name);
+ g_free (display_name);
+ }
+ }
+ g_list_free (rows);
+}
+
+static void
+update_input_chooser (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *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,
+ GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GList *list, *l;
+ GError *error;
+
+ error = NULL;
+ list = ibus_bus_list_engines_async_finish (priv->ibus, result, &error);
+ g_clear_object (&priv->ibus_cancellable);
+ if (!list && error) {
+ g_warning ("Couldn't finish IBus request: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* Maps engine ids to engine description objects */
+ priv->ibus_engines = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+
+ for (l = list; l; l = l->next) {
+ IBusEngineDesc *engine = l->data;
+ const gchar *engine_id = ibus_engine_desc_get_name (engine);
+
+ if (g_str_has_prefix (engine_id, "xkb:"))
+ g_object_unref (engine);
+ else
+ g_hash_table_replace (priv->ibus_engines, (gpointer)engine_id, engine);
+ }
+ g_list_free (list);
+
+ update_ibus_active_sources (self);
+ update_input_chooser (self);
+}
+
+static void
+fetch_ibus_engines (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+
+ priv->ibus_cancellable = g_cancellable_new ();
+
+ ibus_bus_list_engines_async (priv->ibus,
+ -1,
+ priv->ibus_cancellable,
+ (GAsyncReadyCallback)fetch_ibus_engines_result,
+ self);
+
+ /* We've got everything we needed, don't want to be called again. */
+ g_signal_handlers_disconnect_by_func (priv->ibus, fetch_ibus_engines, self);
+}
+
+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));
+}
+
+static GDesktopAppInfo *
+setup_app_info_for_id (const gchar *id)
+{
+ GDesktopAppInfo *app_info;
+ gchar *desktop_file_name;
+ gchar **strv;
+
+ strv = g_strsplit (id, ":", 2);
+ desktop_file_name = g_strdup_printf ("ibus-setup-%s.desktop", strv[0]);
+ g_strfreev (strv);
+
+ app_info = g_desktop_app_info_new (desktop_file_name);
+ g_free (desktop_file_name);
+
+ return app_info;
+}
+#endif
+
+static void
+adjust_input_list_scrolling (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+
+ if (priv->n_input_rows >= MAX_INPUT_ROWS_VISIBLE) {
+ GtkWidget *parent;
+ gint height;
+
+ parent = gtk_widget_get_parent (priv->input_scrolledwindow);
+ gtk_widget_get_preferred_height (parent, NULL, &height);
+ gtk_widget_set_size_request (parent, -1, height);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->input_scrolledwindow),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ } else {
+ gtk_widget_set_size_request (gtk_widget_get_parent (priv->input_scrolledwindow), -1, -1);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->input_scrolledwindow),
+ GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+ }
+}
+
+static GtkWidget *
+add_input_row (GisKeyboardPage *self,
+ const gchar *type,
+ const gchar *id,
+ const gchar *name,
+ GDesktopAppInfo *app_info)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GtkWidget *row;
+ GtkWidget *label;
+ GtkWidget *image;
+
+ if (priv->login) {
+ GList *l;
+ l = gtk_container_get_children (GTK_CONTAINER (priv->input_list));
+ if (l && l->next == NULL &&
+ g_strcmp0 (g_object_get_data (G_OBJECT (l->data), "type"), "none") == 0)
+ gtk_container_remove (GTK_CONTAINER (priv->input_list), GTK_WIDGET (l->data));
+ g_list_free (l);
+ }
+
+ row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ label = gtk_label_new (name);
+ 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 (row), label, TRUE, TRUE, 0);
+
+ if (strcmp (type, INPUT_SOURCE_TYPE_IBUS) == 0) {
+ image = gtk_image_new_from_icon_name ("system-run-symbolic", GTK_ICON_SIZE_BUTTON);
+ gtk_widget_set_margin_left (image, 20);
+ gtk_widget_set_margin_right (image, 20);
+ gtk_widget_set_margin_top (image, 6);
+ gtk_widget_set_margin_bottom (image, 6);
+ gtk_style_context_add_class (gtk_widget_get_style_context (image), "dim-label");
+ gtk_box_pack_start (GTK_BOX (row), image, FALSE, TRUE, 0);
+ }
+
+ gtk_widget_show_all (row);
+ gtk_container_add (GTK_CONTAINER (self->priv->input_list), row);
+
+ g_object_set_data (G_OBJECT (row), "label", label);
+ g_object_set_data (G_OBJECT (row), "type", (gpointer)type);
+ g_object_set_data_full (G_OBJECT (row), "id", g_strdup (id), g_free);
+ if (app_info) {
+ g_object_set_data_full (G_OBJECT (row), "app-info", g_object_ref (app_info), g_object_unref);
+ }
+
+ priv->n_input_rows += 1;
+ adjust_input_list_scrolling (self);
+
+ return row;
+}
+
+static void
+add_input_sources (GisKeyboardPage *self,
+ GVariant *sources)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GVariantIter iter;
+ const gchar *type;
+ const gchar *id;
+ const gchar *name;
+ gchar *display_name;
+ GDesktopAppInfo *app_info;
+
+ g_variant_iter_init (&iter, sources);
+ while (g_variant_iter_next (&iter, "(&s&s)", &type, &id)) {
+ display_name = NULL;
+ app_info = NULL;
+
+ if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) {
+ gnome_xkb_info_get_layout_info (priv->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);
+ type = INPUT_SOURCE_TYPE_XKB;
+#ifdef HAVE_IBUS
+ } else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) {
+ IBusEngineDesc *engine_desc = NULL;
+
+ if (priv->ibus_engines)
+ engine_desc = g_hash_table_lookup (priv->ibus_engines, id);
+ if (engine_desc)
+ display_name = engine_get_display_name (engine_desc);
+
+ app_info = setup_app_info_for_id (id);
+ type = INPUT_SOURCE_TYPE_IBUS;
+#endif
+ } else {
+ g_warning ("Unhandled input source type '%s'", type);
+ continue;
+ }
+
+ add_input_row (self, type, id, display_name ? display_name : id, app_info);
+ g_free (display_name);
+ g_clear_object (&app_info);
+ }
+}
+
+static void
+add_input_sources_from_settings (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GVariant *sources;
+ sources = g_settings_get_value (priv->input_settings, "sources");
+ add_input_sources (self, sources);
+ g_variant_unref (sources);
+}
+
+static void
+clear_input_sources (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GList *list, *l;
+ list = gtk_container_get_children (GTK_CONTAINER (priv->input_list));
+ for (l = list; l; l = l->next) {
+ gtk_container_remove (GTK_CONTAINER (priv->input_list), GTK_WIDGET (l->data));
+ }
+ g_list_free (list);
+
+ priv->n_input_rows = 0;
+ adjust_input_list_scrolling (self);
+}
+
+static void
+select_by_id (GtkWidget *row,
+ gpointer data)
+{
+ const gchar *id = data;
+ const gchar *row_id;
+
+ row_id = (const gchar *)g_object_get_data (G_OBJECT (row), "id");
+ if (g_strcmp0 (row_id, id) == 0)
+ egg_list_box_select_child (EGG_LIST_BOX (gtk_widget_get_parent (row)), row);
+}
+
+static void
+select_input (GisKeyboardPage *self,
+ const gchar *id)
+{
+ gtk_container_foreach (GTK_CONTAINER (self->priv->input_list),
+ select_by_id, (gpointer)id);
+}
+
+static void
+input_sources_changed (GSettings *settings,
+ const gchar *key,
+ GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GtkWidget *selected;
+ gchar *id = NULL;
+
+ selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->input_list));
+ if (selected)
+ id = g_strdup (g_object_get_data (G_OBJECT (selected), "id"));
+ clear_input_sources (self);
+ add_input_sources_from_settings (self);
+ if (id) {
+ select_input (self, id);
+ g_free (id);
+ }
+}
+
+
+static void
+update_buttons (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GtkWidget *selected;
+ GList *children;
+ gboolean multiple_sources;
- page_class->page_id = PAGE_ID;
- page_class->locale_changed = gis_keyboard_page_locale_changed;
- object_class->constructed = gis_keyboard_page_constructed;
+ children = gtk_container_get_children (GTK_CONTAINER (priv->input_list));
+ multiple_sources = g_list_next (children) != NULL;
+ g_list_free (children);
+
+ selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->input_list));
+ if (selected == NULL) {
+ gtk_widget_set_visible (priv->show_config, FALSE);
+ gtk_widget_set_sensitive (priv->remove_input, FALSE);
+ gtk_widget_set_sensitive (priv->show_layout, FALSE);
+ } else {
+ GDesktopAppInfo *app_info;
+
+ app_info = (GDesktopAppInfo *)g_object_get_data (G_OBJECT (selected), "app-info");
+
+ gtk_widget_set_visible (priv->show_config, app_info != NULL);
+ gtk_widget_set_sensitive (priv->show_layout, TRUE);
+ gtk_widget_set_sensitive (priv->remove_input, multiple_sources);
+ }
}
static void
-gis_keyboard_page_init (GisKeyboardPage * self)
+set_input_settings (GisKeyboardPage *self)
{
- g_resources_register (keyboard_get_resource ());
+ GisKeyboardPagePrivate *priv = self->priv;
+ const gchar *type;
+ const gchar *id;
+ GVariantBuilder builder;
+ GVariant *old_sources;
+ const gchar *old_current_type;
+ const gchar *old_current_id;
+ guint old_current;
+ guint old_n_sources;
+ guint index;
+ GList *list, *l;
+
+ old_sources = g_settings_get_value (priv->input_settings, KEY_INPUT_SOURCES);
+ old_current = g_settings_get_uint (priv->input_settings, KEY_CURRENT_INPUT_SOURCE);
+ old_n_sources = g_variant_n_children (old_sources);
+
+ if (old_n_sources > 0 && old_current < old_n_sources) {
+ g_variant_get_child (old_sources, old_current,
+ "(&s&s)", &old_current_type, &old_current_id);
+ } else {
+ old_current_type = "";
+ old_current_id = "";
+ }
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)"));
+ index = 0;
+ list = gtk_container_get_children (GTK_CONTAINER (priv->input_list));
+ for (l = list; l; l = l->next) {
+ type = (const gchar *)g_object_get_data (G_OBJECT (l->data), "type");
+ id = (const gchar *)g_object_get_data (G_OBJECT (l->data), "id");
+ if (index != old_current &&
+ g_str_equal (type, old_current_type) &&
+ g_str_equal (id, old_current_id)) {
+ g_settings_set_uint (priv->input_settings, KEY_CURRENT_INPUT_SOURCE, index);
+ }
+ g_variant_builder_add (&builder, "(ss)", type, id);
+ index += 1;
+ }
+ g_list_free (list);
+
+ g_settings_set_value (priv->input_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder));
+ g_settings_apply (priv->input_settings);
+
+ g_variant_unref (old_sources);
+}
+
+static void set_localed_input (GisKeyboardPage *self);
+
+static void
+update_input (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+
+ if (priv->login) {
+ set_localed_input (self);
+ } else {
+ set_input_settings (self);
+ }
+}
+
+static void
+apologize_for_no_ibus_login (GisKeyboardPage *self)
+{
+ GtkWidget *dialog;
+ GtkWidget *toplevel;
+ GtkWidget *image;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_OTHER,
+ GTK_BUTTONS_OK,
+ _("Sorry"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", _("Input methods can't be used on the login
screen"));
+ image = gtk_image_new_from_icon_name ("face-sad-symbolic",
+ GTK_ICON_SIZE_DIALOG);
+ gtk_widget_show (image);
+ gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+static gboolean
+input_source_already_added (GisKeyboardPage *self,
+ const gchar *id)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GList *list, *l;
+ gboolean retval = FALSE;
+
+ list = gtk_container_get_children (GTK_CONTAINER (priv->input_list));
+ for (l = list; l; l = l->next)
+ if (g_str_equal (id, (const gchar *) g_object_get_data (G_OBJECT (l->data), "id"))) {
+ retval = TRUE;
+ break;
+ }
+ g_list_free (list);
+
+ return retval;
+}
+
+static void
+input_response (GtkWidget *chooser, gint response_id, gpointer data)
+{
+ GisKeyboardPage *self = data;
+ GisKeyboardPagePrivate *priv = self->priv;
+ gchar *type;
+ gchar *id;
+ gchar *name;
+ GDesktopAppInfo *app_info = NULL;
+
+ if (response_id == GTK_RESPONSE_OK) {
+ if (cc_input_chooser_get_selected (chooser, &type, &id, &name) &&
+ !input_source_already_added (self, id)) {
+ if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) {
+ g_free (type);
+ type = INPUT_SOURCE_TYPE_IBUS;
+#ifdef HAVE_IBUS
+ app_info = setup_app_info_for_id (id);
+#endif
+ } else {
+ g_free (type);
+ type = INPUT_SOURCE_TYPE_XKB;
+ }
+
+ if (priv->login && g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) {
+ apologize_for_no_ibus_login (self);
+ } else {
+ add_input_row (self, type, id, name, app_info);
+ update_buttons (self);
+ update_input (self);
+ }
+ g_free (id);
+ g_free (name);
+ g_clear_object (&app_info);
+ }
+ }
+ gtk_widget_destroy (chooser);
+ g_object_set_data (G_OBJECT (self), "input-chooser", NULL);
+}
+
+static void
+show_input_chooser (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GtkWidget *chooser;
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+ chooser = cc_input_chooser_new (GTK_WINDOW (toplevel),
+ priv->xkb_info,
+#ifdef HAVE_IBUS
+ priv->ibus_engines
+#else
+ NULL
+#endif
+ );
+ 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
+add_input (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+
+ if (!priv->login) {
+ show_input_chooser (self);
+ } else if (g_permission_get_allowed (priv->permission)) {
+ show_input_chooser (self);
+ } else if (g_permission_get_can_acquire (priv->permission)) {
+ priv->op = ADD_INPUT;
+ g_permission_acquire_async (priv->permission,
+ NULL,
+ permission_acquired,
+ self);
+ }
+}
+
+static GtkWidget *
+find_sibling (GtkContainer *container, GtkWidget *child)
+{
+ GList *list, *c;
+ GList *l;
+ GtkWidget *sibling;
+
+ list = gtk_container_get_children (container);
+ c = g_list_find (list, child);
+
+ for (l = c->next; l; l = l->next) {
+ sibling = l->data;
+ if (gtk_widget_get_visible (sibling) && gtk_widget_get_child_visible (sibling))
+ goto out;
+ }
+
+ for (l = c->prev; l; l = l->prev) {
+ sibling = l->data;
+ if (gtk_widget_get_visible (sibling) && gtk_widget_get_child_visible (sibling))
+ goto out;
+ }
+
+ sibling = NULL;
+
+out:
+ g_list_free (list);
+
+ return sibling;
+}
+
+static void
+do_remove_selected_input (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GtkWidget *selected;
+ GtkWidget *sibling;
+
+ selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->input_list));
+ if (selected == NULL)
+ return;
+
+ sibling = find_sibling (GTK_CONTAINER (priv->input_list), selected);
+ gtk_container_remove (GTK_CONTAINER (priv->input_list), selected);
+ egg_list_box_select_child (EGG_LIST_BOX (priv->input_list), sibling);
+
+ priv->n_input_rows -= 1;
+ adjust_input_list_scrolling (self);
+
+ update_buttons (self);
+ update_input (self);
+}
+
+static void
+remove_selected_input (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+
+ if (!priv->login) {
+ do_remove_selected_input (self);
+ } else if (g_permission_get_allowed (priv->permission)) {
+ do_remove_selected_input (self);
+ } else if (g_permission_get_can_acquire (priv->permission)) {
+ priv->op = REMOVE_INPUT;
+ g_permission_acquire_async (priv->permission,
+ NULL,
+ permission_acquired,
+ self);
+ }
+}
+
+static void
+show_selected_settings (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GtkWidget *selected;
+ GdkAppLaunchContext *ctx;
+ GDesktopAppInfo *app_info;
+ const gchar *id;
+ GError *error = NULL;
+
+ selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->input_list));
+ if (selected == NULL)
+ return;
+
+ app_info = (GDesktopAppInfo *)g_object_get_data (G_OBJECT (selected), "app-info");
+ if (app_info == NULL)
+ return;
+
+ ctx = gdk_display_get_app_launch_context (gdk_display_get_default ());
+ gdk_app_launch_context_set_timestamp (ctx, gtk_get_current_event_time ());
+
+ id = (const gchar *)g_object_get_data (G_OBJECT (selected), "id");
+ g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (ctx),
+ "IBUS_ENGINE_NAME", id);
+
+ if (!g_app_info_launch (G_APP_INFO (app_info), NULL, G_APP_LAUNCH_CONTEXT (ctx), &error)) {
+ g_warning ("Failed to launch input source setup: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (ctx);
+}
+
+static void
+show_selected_layout (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GtkWidget *selected;
+ const gchar *type;
+ const gchar *id;
+ const gchar *layout;
+ const gchar *variant;
+ gchar *commandline;
+
+ selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->input_list));
+ if (selected == NULL)
+ return;
+
+ type = (const gchar *)g_object_get_data (G_OBJECT (selected), "type");
+ id = (const gchar *)g_object_get_data (G_OBJECT (selected), "id");
+
+ if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) {
+ gnome_xkb_info_get_layout_info (priv->xkb_info,
+ id, NULL, NULL,
+ &layout, &variant);
+
+ if (!layout || !layout[0]) {
+ g_warning ("Couldn't find XKB input source '%s'", id);
+ return;
+ }
+#ifdef HAVE_IBUS
+ } else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) {
+ IBusEngineDesc *engine_desc = NULL;
+
+ if (priv->ibus_engines)
+ engine_desc = g_hash_table_lookup (priv->ibus_engines, id);
+
+ if (engine_desc) {
+ layout = ibus_engine_desc_get_layout (engine_desc);
+ variant = "";
+ } else {
+ g_warning ("Couldn't find IBus input source '%s'", id);
+ return;
+ }
+#endif
+ } else {
+ g_warning ("Unhandled input source type '%s'", type);
+ return;
+ }
+
+ if (variant[0])
+ commandline = g_strdup_printf ("gkbd-keyboard-display -l \"%s\t%s\"",
+ layout, variant);
+ else
+ commandline = g_strdup_printf ("gkbd-keyboard-display -l %s",
+ layout);
+
+ g_spawn_command_line_async (commandline, NULL);
+ g_free (commandline);
+}
+
+static void
+setup_input_section (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+
+ priv->input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR);
+ g_settings_delay (priv->input_settings);
+
+ priv->xkb_info = gnome_xkb_info_new ();
+
+#ifdef HAVE_IBUS
+ ibus_init ();
+ if (!priv->ibus) {
+ priv->ibus = ibus_bus_new_async ();
+ if (ibus_bus_is_connected (priv->ibus))
+ fetch_ibus_engines (self);
+ else
+ g_signal_connect_swapped (priv->ibus, "connected",
+ G_CALLBACK (fetch_ibus_engines), self);
+ }
+ maybe_start_ibus ();
+#endif
+
+ priv->input_section = WID ("input_section");
+ priv->input_list = WID ("input_list");
+ priv->add_input = WID ("input_source_add");
+ priv->remove_input = WID ("input_source_remove");
+ priv->show_config = WID ("input_source_config");
+ priv->show_layout = WID ("input_source_layout");
+ priv->input_scrolledwindow = WID ("input_scrolledwindow");
+
+ g_signal_connect_swapped (priv->add_input, "clicked",
+ G_CALLBACK (add_input), self);
+ g_signal_connect_swapped (priv->remove_input, "clicked",
+ G_CALLBACK (remove_selected_input), self);
+ g_signal_connect_swapped (priv->show_config, "clicked",
+ G_CALLBACK (show_selected_settings), self);
+ g_signal_connect_swapped (priv->show_layout, "clicked",
+ G_CALLBACK (show_selected_layout), self);
+
+ egg_list_box_set_selection_mode (EGG_LIST_BOX (priv->input_list),
+ GTK_SELECTION_SINGLE);
+ egg_list_box_set_separator_funcs (EGG_LIST_BOX (priv->input_list),
+ update_separator_func,
+ NULL, NULL);
+ g_signal_connect_swapped (priv->input_list, "child-selected",
+ G_CALLBACK (update_buttons), self);
+
+ g_signal_connect (priv->input_settings, "changed::" KEY_INPUT_SOURCES,
+ G_CALLBACK (input_sources_changed), self);
+
+ add_input_sources_from_settings (self);
+ update_buttons (self);
+}
+
+static void
+on_localed_properties_changed (GDBusProxy *proxy,
+ GVariant *changed_properties,
+ const gchar **invalidated_properties,
+ GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GVariant *v;
+
+ v = g_dbus_proxy_get_cached_property (proxy, "Locale");
+ if (v) {
+ const gchar **strv;
+ gsize len;
+ gint i;
+ const gchar *lang, *messages, *time;
+
+ strv = g_variant_get_strv (v, &len);
+
+ lang = messages = time = NULL;
+ for (i = 0; strv[i]; i++) {
+ if (g_str_has_prefix (strv[i], "LANG=")) {
+ lang = strv[i] + strlen ("LANG=");
+ } else if (g_str_has_prefix (strv[i], "LC_MESSAGES=")) {
+ messages = strv[i] + strlen ("LC_MESSAGES=");
+ } else if (g_str_has_prefix (strv[i], "LC_TIME=")) {
+ time = strv[i] + strlen ("LC_TIME=");
+ }
+ }
+ if (!lang) {
+ lang = "";
+ }
+ if (!messages) {
+ messages = lang;
+ }
+ if (!time) {
+ time = lang;
+ }
+ g_free (priv->system_language);
+ priv->system_language = g_strdup (messages);
+ g_free (priv->system_region);
+ priv->system_region = g_strdup (time);
+ g_variant_unref (v);
+
+ update_language_label (self);
+ }
+}
+
+static void
+add_input_sources_from_localed (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GVariant *v;
+ const gchar *s;
+ gchar **layouts = NULL;
+ gchar **variants = NULL;
+ gint i, n;
+
+ if (!priv->localed)
+ return;
+
+ v = g_dbus_proxy_get_cached_property (priv->localed, "X11Layout");
+ if (v) {
+ s = g_variant_get_string (v, NULL);
+ layouts = g_strsplit (s, ",", -1);
+ g_variant_unref (v);
+ }
+
+ v = g_dbus_proxy_get_cached_property (priv->localed, "X11Variant");
+ if (v) {
+ s = g_variant_get_string (v, NULL);
+ if (s && *s)
+ variants = g_strsplit (s, ",", -1);
+ g_variant_unref (v);
+ }
+
+ if (variants && variants[0])
+ n = MIN (g_strv_length (layouts), g_strv_length (variants));
+ else if (layouts && layouts[0])
+ n = g_strv_length (layouts);
+ else
+ n = 0;
+
+ for (i = 0; i < n && layouts[i][0]; i++) {
+ const gchar *name;
+ gchar *id;
+
+ if (variants && variants[i] && variants[i][0])
+ id = g_strdup_printf ("%s+%s", layouts[i], variants[i]);
+ else
+ id = g_strdup (layouts[i]);
+
+ gnome_xkb_info_get_layout_info (priv->xkb_info, id, &name, NULL, NULL, NULL);
+
+ add_input_row (self, INPUT_SOURCE_TYPE_XKB, id, name ? name : id, NULL);
+
+ g_free (id);
+ }
+ if (n == 0) {
+ add_input_row (self, "none", "none", _("No input source selected"), NULL);
+ }
+
+ g_strfreev (variants);
+ g_strfreev (layouts);
+}
+
+static void
+set_localed_locale (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GVariantBuilder *b;
+ gchar *s;
+
+ b = g_variant_builder_new (G_VARIANT_TYPE ("as"));
+ s = g_strconcat ("LANG=", priv->system_language, NULL);
+ g_variant_builder_add (b, "s", s);
+ g_free (s);
+
+ if (g_strcmp0 (priv->system_language, priv->system_region) != 0) {
+ s = g_strconcat ("LC_TIME=", priv->system_region, NULL);
+ g_variant_builder_add (b, "s", s);
+ g_free (s);
+ s = g_strconcat ("LC_NUMERIC=", priv->system_region, NULL);
+ g_variant_builder_add (b, "s", s);
+ g_free (s);
+ s = g_strconcat ("LC_MONETARY=", priv->system_region, NULL);
+ g_variant_builder_add (b, "s", s);
+ g_free (s);
+ s = g_strconcat ("LC_MEASUREMENT=", priv->system_region, NULL);
+ g_variant_builder_add (b, "s", s);
+ g_free (s);
+ s = g_strconcat ("LC_PAPER=", priv->system_region, NULL);
+ g_variant_builder_add (b, "s", s);
+ g_free (s);
+ }
+ g_dbus_proxy_call (priv->localed,
+ "SetLocale",
+ g_variant_new ("(asb)", b, TRUE),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+ g_variant_builder_unref (b);
+}
+
+static void
+set_localed_input (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ GString *layouts;
+ GString *variants;
+ const gchar *type, *id;
+ GList *list, *li;
+ const gchar *l, *v;
+
+ layouts = g_string_new ("");
+ variants = g_string_new ("");
+
+ list = gtk_container_get_children (GTK_CONTAINER (priv->input_list));
+ for (li = list; li; li = li->next) {
+ type = (const gchar *)g_object_get_data (G_OBJECT (li->data), "type");
+ id = (const gchar *)g_object_get_data (G_OBJECT (li->data), "id");
+ if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS))
+ continue;
+
+ if (gnome_xkb_info_get_layout_info (priv->xkb_info, id, NULL, NULL, &l, &v)) {
+ if (layouts->str[0]) {
+ g_string_append_c (layouts, ',');
+ g_string_append_c (variants, ',');
+ }
+ g_string_append (layouts, l);
+ g_string_append (variants, v);
+ }
+ }
+ g_list_free (list);
+
+ g_dbus_proxy_call (priv->localed,
+ "SetX11Keyboard",
+ g_variant_new ("(ssssbb)", layouts->str, "", variants->str, "", TRUE, TRUE),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+
+ g_string_free (layouts, TRUE);
+ g_string_free (variants, TRUE);
+}
+
+static void
+localed_proxy_ready (GObject *source,
+ GAsyncResult *res,
+ gpointer data)
+{
+ GisKeyboardPage *self = data;
+ GisKeyboardPagePrivate *priv;
+ GDBusProxy *proxy;
+ GError *error = NULL;
+
+ proxy = g_dbus_proxy_new_finish (res, &error);
+
+ if (!proxy) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to contact localed: %s\n", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ priv = self->priv;
+ priv->localed = proxy;
+
+ gtk_widget_set_sensitive (priv->login_button, TRUE);
+
+ g_signal_connect (priv->localed, "g-properties-changed",
+ G_CALLBACK (on_localed_properties_changed), self);
+ on_localed_properties_changed (priv->localed, NULL, NULL, self);
+}
+
+static void
+login_changed (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv = self->priv;
+ gboolean can_acquire;
+
+ priv->login = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->login_button));
+ gtk_widget_set_visible (priv->formats_row, !priv->login);
+ gtk_widget_set_visible (priv->login_label, priv->login);
+
+ can_acquire = priv->permission &&
+ (g_permission_get_allowed (priv->permission) ||
+ g_permission_get_can_acquire (priv->permission));
+ /* FIXME: insensitive doesn't look quite right for this */
+ gtk_widget_set_sensitive (priv->language_section, !priv->login || can_acquire);
+ gtk_widget_set_sensitive (priv->input_section, !priv->login || can_acquire);
+
+ clear_input_sources (self);
+ if (priv->login)
+ add_input_sources_from_localed (self);
+ else
+ add_input_sources_from_settings (self);
+
+ update_language_label (self);
+ update_buttons (self);
+}
+
+
+static void
+session_proxy_ready (GObject *source,
+ GAsyncResult *res,
+ gpointer data)
+{
+ GisKeyboardPage *self = data;
+ GDBusProxy *proxy;
+ GError *error = NULL;
+
+ proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+
+ if (!proxy) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to contact gnome-session: %s\n", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ self->priv->session = proxy;
+}
+
+static void
+gis_keyboard_page_init (GisKeyboardPage *self)
+{
+ GisKeyboardPagePrivate *priv;
+ GError *error = NULL;
+
+ priv = self->priv = KEYBOARD_PAGE_PRIVATE (self);
+ g_resources_register (keyboard_get_resource ());
+
+ priv->builder = gtk_builder_new ();
+
+ gtk_builder_add_from_resource (priv->builder,
+ "/org/gnome/initial-setup/gis-keyboard-page.ui",
+ &error);
+ if (error != NULL) {
+ g_warning ("Error loading UI file: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ priv->user_manager = act_user_manager_get_default ();
+
+ priv->cancellable = g_cancellable_new ();
+
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.gnome.SessionManager",
+ "/org/gnome/SessionManager",
+ "org.gnome.SessionManager",
+ priv->cancellable,
+ session_proxy_ready,
+ self);
+
+ setup_input_section (self);
+
+ priv->overlay = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "keyboard_page"));
+ gtk_container_add (GTK_CONTAINER (self), priv->overlay);
}
void
diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.h
b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.h
index e215e34..807d87c 100644
--- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.h
+++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.h
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2012 Intel, Inc
- * Copyright (C) 2013 Red Hat, Inc.
+ * Copyright (C) 2010 Intel, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,15 +15,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
- * Authors:
- * Michael Wood <michael g wood intel com>
- * Jasper St. Pierre <jstpierre mecheye net>
- *
- * Based on gnome-control-center region page by:
- * Sergey Udaltsov <svu gnome org>
+ * Author: Sergey Udaltsov <svu gnome org>
*
*/
+
#ifndef _GIS_KEYBOARD_PAGE_H
#define _GIS_KEYBOARD_PAGE_H
@@ -72,7 +67,7 @@ struct _GisKeyboardPageClass
GType gis_keyboard_page_get_type (void) G_GNUC_CONST;
-void gis_prepare_keyboard_page (GisDriver *driver);
+void gis_prepare_keyboard_page (GisDriver *driver);
G_END_DECLS
diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
index 4d9ace5..f8c20fc 100644
--- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
+++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
@@ -1,302 +1,227 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <!-- interface-requires gtk+ 3.0 -->
- <object class="GtkVBox" id="keyboard-page">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin-left">80</property>
- <property name="margin-right">80</property>
- <child>
- <object class="GtkLabel" id="label24">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Select input sources</property>
- <property name="halign">start</property>
- <property name="valign">start</property>
- <property name="margin-bottom">18</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- <attribute name="scale" value="1.2"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkVBox" id="vbox6">
+ <object class="GtkBox" id="keyboard_page">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkScrolledWindow" id="input_sources_swindow">
+ <object class="GtkBox" id="input_section">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="shadow_type">in</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">80</property>
+ <property name="margin_right">80</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkTreeView" id="active_input_sources">
+ <object class="GtkBox" id="input_heading_row">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="headers_visible">False</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection"/>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel" id="input_heading">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Select input sources</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
</child>
- </object>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
</child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolbar" id="input-toolbar">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="toolbar_style">icons</property>
- <property name="show_arrow">False</property>
- <property name="icon_size">1</property>
- <style>
- <class name="inline-toolbar"/>
- </style>
<child>
- <object class="GtkToolItem" id="i_s_ar_item">
- <property name="use_action_appearance">False</property>
+ <object class="GtkFrame" id="input_frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
<child>
- <object class="GtkBox" id="i_s_ar_box">
+ <object class="GtkScrolledWindow" id="input_scrolledwindow">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="hscrollbar-policy">never</property>
+ <property name="vscrollbar-policy">never</property>
<child>
- <object class="GtkButton" id="input_source_add">
- <property name="use_action_appearance">False</property>
+ <object class="GtkViewport" id="input_viewport">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
- <child internal-child="accessible">
- <object class="AtkObject" id="i_s_a_a11y">
- <property name="accessible-name" translatable="yes">Add Input Source</property>
- </object>
- </child>
<child>
- <object class="GtkImage" id="i_s_a_image">
+ <object class="EggListBox" id="input_list">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">list-add-symbolic</property>
- <property name="icon-size">1</property>
+ <property name="can_focus">True</property>
</object>
</child>
-
</object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="input_source_remove">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
- <child internal-child="accessible">
- <object class="AtkObject" id="i_s_r_a11y">
- <property name="accessible-name" translatable="yes">Remove Input
Source</property>
- </object>
- </child>
- <child>
- <object class="GtkImage" id="i_s_r_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">list-remove-symbolic</property>
- <property name="icon-size">1</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
</child>
</object>
</child>
</object>
<packing>
- <property name="expand">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkSeparatorToolItem" id="sep1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="draw">False</property>
- </object>
- <packing>
<property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
</packing>
</child>
<child>
- <object class="GtkToolItem" id="i_s_ud_item">
- <property name="use_action_appearance">False</property>
+ <object class="GtkToolbar" id="input_toolbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
+ <property name="toolbar_style">icons</property>
+ <property name="show_arrow">False</property>
+ <property name="icon_size">1</property>
+ <style>
+ <class name="inline-toolbar"/>
+ </style>
<child>
- <object class="GtkBox" id="i_s_ud_box">
+ <object class="GtkToolItem" id="i_s_ar_item">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
- <object class="GtkButton" id="input_source_move_up">
- <property name="use_action_appearance">False</property>
+ <object class="GtkBox" id="i_s_ar_box">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
- <child internal-child="accessible">
- <object class="AtkObject" id="i_s_u_a11y">
- <property name="accessible-name" translatable="yes">Move Input Source
Up</property>
- </object>
- </child>
+ <property name="can_focus">False</property>
<child>
- <object class="GtkImage" id="i_s_u_image">
+ <object class="GtkButton" id="input_source_add">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">go-up-symbolic</property>
- <property name="icon-size">1</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="input_source_move_down">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
- <child internal-child="accessible">
- <object class="AtkObject" id="i_s_d_a11y">
- <property name="accessible-name" translatable="yes">Move Input Source
Down</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="i_s_a_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">list-add-symbolic</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
</object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
</child>
<child>
- <object class="GtkImage" id="i_s_d_image">
+ <object class="GtkButton" id="input_source_remove">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">go-down-symbolic</property>
- <property name="icon-size">1</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="i_s_r_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">list-remove-symbolic</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
</object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
</child>
</object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
</child>
</object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="sep1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="draw">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ </packing>
</child>
- </object>
- <packing>
- <property name="expand">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkSeparatorToolItem" id="sep2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hexpand">True</property>
- <property name="draw">False</property>
- </object>
- <packing>
- <property name="expand">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolItem" id="i_s_sp_item">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
<child>
- <object class="GtkBox" id="i_s_sp_box">
+ <object class="GtkToolItem" id="i_s_sc_item">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
- <object class="GtkButton" id="input_source_settings">
- <property name="use_action_appearance">False</property>
+ <object class="GtkBox" id="i_s_sc_box">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
- <child internal-child="accessible">
-
- <object class="AtkObject" id="i_s_s_a11y">
- <property name="accessible-name" translatable="yes">Input Source
Settings</property>
- </object>
- </child>
+ <property name="can_focus">False</property>
<child>
- <object class="GtkImage" id="i_s_s_image">
+ <object class="GtkButton" id="input_source_config">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="pixel_size">16</property>
- <property name="icon_name">preferences-system-symbolic</property>
- <property name="icon-size">1</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="i_s_sc_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
</object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
</child>
</object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
</child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="i_s_sl_item">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
<child>
- <object class="GtkButton" id="input_source_show">
- <property name="use_action_appearance">False</property>
+ <object class="GtkBox" id="i_s_sl_box">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
- <child internal-child="accessible">
- <object class="AtkObject" id="i_s_p_a11y">
- <property name="accessible-name" translatable="yes">Show Keyboard
Layout</property>
- </object>
- </child>
+ <property name="can_focus">False</property>
<child>
- <object class="GtkImage" id="i_s_p_image">
+ <object class="GtkButton" id="input_source_layout">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">input-keyboard-symbolic</property>
- <property name="icon-size">1</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="i_s_sl_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">input-keyboard-symbolic</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
</object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
</child>
</object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
</child>
</object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
</child>
</object>
<packing>
<property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
</packing>
</child>
</object>
@@ -306,12 +231,16 @@
<property name="position">1</property>
</packing>
</child>
+ <child>
+ <object class="GtkLabel" id="login-label">
+ <property name="valign">end</property>
+ <property name="vexpand">True</property>
+ <property name="label" translatable="yes">Login settings are used by all users when logging into
the system</property>
+ <property name="margin_bottom">12</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
</object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
</interface>
diff --git a/gnome-initial-setup/pages/keyboard/input-chooser.ui
b/gnome-initial-setup/pages/keyboard/input-chooser.ui
new file mode 100644
index 0000000..02496e0
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/input-chooser.ui
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkDialog" id="input-dialog">
+ <property name="title" translatable="yes">Add an Input Source</property>
+ <property name="default_height">350</property>
+ <property name="modal">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="resizable">True</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow">
+ <property name="visible">True</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="GtkViewport" id="viewport">
+ <property name="visible">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>
+ </child>
+ </object>
+ </child>
+ <child>
+ <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="orientation">horizontal</property>
+ <child>
+ <object class="GtkButton" id="cancel-button">
+ <property name="visible">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="use_underline" >True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="add-button">
+ <property name="visible">True</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>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">add-button</action-widget>
+ <action-widget response="-6">cancel-button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml
b/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml
index e3a4612..103b3f1 100644
--- a/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml
+++ b/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/initial-setup">
- <file preprocess="xml-stripblanks" alias="gis-keyboard-page.ui">gis-keyboard-page.ui</file>
- <file preprocess="xml-stripblanks"
alias="gnome-region-panel-input-chooser.ui">gnome-region-panel-input-chooser.ui</file>
+ <file preprocess="xml-stripblanks">gis-keyboard-page.ui</file>
+ <file preprocess="xml-stripblanks">input-chooser.ui</file>
</gresource>
</gresources>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7f8928e..2e1442b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -13,9 +13,11 @@ gnome-initial-setup/pages/eulas/gis-eula-pages.c
gnome-initial-setup/pages/goa/cc-online-accounts-add-account-dialog.c
gnome-initial-setup/pages/goa/gis-goa-page.c
[type: gettext/glade]gnome-initial-setup/pages/goa/gis-goa-page.ui
+gnome-initial-setup/pages/language/cc-util.c
+gnome-initial-setup/pages/keyboard/cc-input-chooser.c
gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
[type: gettext/glade]gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
-[type: gettext/glade]gnome-initial-setup/pages/keyboard/gnome-region-panel-input-chooser.ui
+[type: gettext/glade]gnome-initial-setup/pages/keyboard/input-chooser.ui
gnome-initial-setup/pages/language/cc-common-language.c
gnome-initial-setup/pages/language/cc-language-chooser.c
gnome-initial-setup/pages/language/gis-language-page.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]