[gnome-initial-setup] keyboard: Update the input sources page to bring up to date w/GNOME 3.8



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)", &current_locale);
+
+        if (!gnome_parse_locale (current_locale,
+                                 &current_lang_code,
+                                 &current_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]