[gnome-initial-setup] keyboard: Turn into a list box



commit 30dcfa867ca2891b3f18bdfd496f45c5a9a8cbe8
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Dec 7 01:36:59 2013 -0500

    keyboard: Turn into a list box
    
    This is a major refactoring of the input sources page. We
    no longer pop up a dialog, instead, we show the input sources
    in a list, similar to the way we treat languages and regions
    in the previous pages. Only single selection is allowed, and
    we don't pay much attention to previous configuration, since
    this tool is intended for first-time setup.
    
    Still todo: How to find the 'most common' input sources for
    a given locale, and show those initially. This also includes
    filtering out 'alternative' approaches such as dvorak.

 gnome-initial-setup/pages/keyboard/Makefile.am     |    1 +
 .../pages/keyboard/cc-common-language.c            |  326 ++++
 .../pages/keyboard/cc-common-language.h            |   36 +
 gnome-initial-setup/pages/keyboard/cc-ibus-utils.c |    4 +
 .../pages/keyboard/cc-input-chooser.c              | 1617 ++++++++------------
 .../pages/keyboard/cc-input-chooser.h              |   48 +-
 gnome-initial-setup/pages/keyboard/cc-util.c       |    2 +-
 .../pages/keyboard/gis-keyboard-page.c             | 1080 ++------------
 .../pages/keyboard/gis-keyboard-page.ui            |  265 +---
 .../pages/keyboard/input-chooser.ui                |   92 +-
 10 files changed, 1251 insertions(+), 2220 deletions(-)
---
diff --git a/gnome-initial-setup/pages/keyboard/Makefile.am b/gnome-initial-setup/pages/keyboard/Makefile.am
index 4fd4d11..e7d74eb 100644
--- a/gnome-initial-setup/pages/keyboard/Makefile.am
+++ b/gnome-initial-setup/pages/keyboard/Makefile.am
@@ -17,6 +17,7 @@ BUILT_SOURCES += keyboard-resources.c keyboard-resources.h
 
 libgiskeyboard_la_SOURCES =                            \
        cc-input-chooser.c cc-input-chooser.h           \
+       cc-common-language.c cc-common-language.h       \
        cc-ibus-utils.c cc-ibus-utils.h                 \
        cc-util.c cc-util.h                             \
        gis-keyboard-page.c gis-keyboard-page.h         \
diff --git a/gnome-initial-setup/pages/keyboard/cc-common-language.c 
b/gnome-initial-setup/pages/keyboard/cc-common-language.c
new file mode 100644
index 0000000..04f0c84
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-common-language.c
@@ -0,0 +1,326 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2009-2010  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.
+ *
+ * Written by: Matthias Clasen <mclasen redhat com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <fontconfig/fontconfig.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-languages.h>
+
+#include "cc-common-language.h"
+
+static char *get_lang_for_user_object_path (const char *path);
+
+gboolean
+cc_common_language_has_font (const gchar *locale)
+{
+        const FcCharSet *charset;
+        FcPattern       *pattern;
+        FcObjectSet     *object_set;
+        FcFontSet       *font_set;
+        gchar           *language_code;
+        gboolean         is_displayable;
+
+        is_displayable = FALSE;
+        pattern = NULL;
+        object_set = NULL;
+        font_set = NULL;
+
+        if (!gnome_parse_locale (locale, &language_code, NULL, NULL, NULL))
+                return FALSE;
+
+        charset = FcLangGetCharSet ((FcChar8 *) language_code);
+        if (!charset) {
+                /* fontconfig does not know about this language */
+                is_displayable = TRUE;
+        }
+        else {
+                /* see if any fonts support rendering it */
+                pattern = FcPatternBuild (NULL, FC_LANG, FcTypeString, language_code, NULL);
+
+                if (pattern == NULL)
+                        goto done;
+
+                object_set = FcObjectSetCreate ();
+
+                if (object_set == NULL)
+                        goto done;
+
+                font_set = FcFontList (NULL, pattern, object_set);
+
+                if (font_set == NULL)
+                        goto done;
+
+                is_displayable = (font_set->nfont > 0);
+        }
+
+ done:
+        if (font_set != NULL)
+                FcFontSetDestroy (font_set);
+
+        if (object_set != NULL)
+                FcObjectSetDestroy (object_set);
+
+        if (pattern != NULL)
+                FcPatternDestroy (pattern);
+
+        g_free (language_code);
+
+        return is_displayable;
+}
+
+gchar *
+cc_common_language_get_current_language (void)
+{
+        gchar *language;
+        char *path;
+        const gchar *locale;
+
+       path = g_strdup_printf ("/org/freedesktop/Accounts/User%d", getuid ());
+        language = get_lang_for_user_object_path (path);
+        g_free (path);
+        if (language != NULL && *language != '\0')
+                return language;
+
+        locale = (const gchar *) setlocale (LC_MESSAGES, NULL);
+        if (locale)
+                language = gnome_normalize_locale (locale);
+        else
+                language = NULL;
+
+        return language;
+}
+
+static gboolean
+user_language_has_translations (const char *locale)
+{
+        char *name, *language_code, *territory_code;
+        gboolean ret;
+
+        gnome_parse_locale (locale,
+                            &language_code,
+                            &territory_code,
+                            NULL, NULL);
+        name = g_strdup_printf ("%s%s%s",
+                                language_code,
+                                territory_code != NULL? "_" : "",
+                                territory_code != NULL? territory_code : "");
+        g_free (language_code);
+        g_free (territory_code);
+        ret = gnome_language_has_translations (name);
+        g_free (name);
+
+        return ret;
+}
+
+static char *
+get_lang_for_user_object_path (const char *path)
+{
+       GError *error = NULL;
+       GDBusProxy *user;
+       GVariant *props;
+       char *lang;
+
+       user = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                             G_DBUS_PROXY_FLAGS_NONE,
+                                             NULL,
+                                             "org.freedesktop.Accounts",
+                                             path,
+                                             "org.freedesktop.Accounts.User",
+                                             NULL,
+                                             &error);
+       if (user == NULL) {
+               g_warning ("Failed to get proxy for user '%s': %s",
+                          path, error->message);
+               g_error_free (error);
+               return NULL;
+       }
+       props = g_dbus_proxy_get_cached_property (user, "Language");
+       lang = g_variant_dup_string (props, NULL);
+
+       g_variant_unref (props);
+       g_object_unref (user);
+       return lang;
+}
+
+static void
+add_other_users_language (GHashTable *ht)
+{
+        GVariant *variant;
+        GVariantIter *vi;
+        GError *error = NULL;
+        const char *str;
+        GDBusProxy *proxy;
+
+        proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                               G_DBUS_PROXY_FLAGS_NONE,
+                                               NULL,
+                                               "org.freedesktop.Accounts",
+                                               "/org/freedesktop/Accounts",
+                                               "org.freedesktop.Accounts",
+                                               NULL,
+                                               NULL);
+
+        if (proxy == NULL)
+                return;
+
+        variant = g_dbus_proxy_call_sync (proxy,
+                                          "ListCachedUsers",
+                                          NULL,
+                                          G_DBUS_CALL_FLAGS_NONE,
+                                          -1,
+                                          NULL,
+                                          &error);
+        if (variant == NULL) {
+                g_warning ("Failed to list existing users: %s", error->message);
+                g_error_free (error);
+                g_object_unref (proxy);
+                return;
+        }
+        g_variant_get (variant, "(ao)", &vi);
+        while (g_variant_iter_loop (vi, "o", &str)) {
+                char *lang;
+                char *name;
+                char *language;
+
+                lang = get_lang_for_user_object_path (str);
+                if (lang != NULL && *lang != '\0' &&
+                    cc_common_language_has_font (lang) &&
+                    user_language_has_translations (lang)) {
+                        name = gnome_normalize_locale (lang);
+                        if (!g_hash_table_lookup (ht, name)) {
+                                language = gnome_get_language_from_locale (name, NULL);
+                                g_hash_table_insert (ht, name, language);
+                        }
+                        else {
+                                g_free (name);
+                        }
+                }
+                g_free (lang);
+        }
+        g_variant_iter_free (vi);
+        g_variant_unref (variant);
+
+        g_object_unref (proxy);
+}
+
+static void
+insert_language_internal (GHashTable *ht,
+                          const char *lang)
+{
+        gboolean has_translations;
+        char *label_own_lang;
+        char *label_current_lang;
+        char *label_untranslated;
+        char *key;
+
+        has_translations = gnome_language_has_translations (lang);
+        if (!has_translations) {
+                char *lang_code = g_strndup (lang, 2);
+                has_translations = gnome_language_has_translations (lang_code);
+                g_free (lang_code);
+
+                if (!has_translations)
+                        return;
+        }
+
+        g_debug ("We have translations for %s", lang);
+
+        key = g_strdup (lang);
+
+        label_own_lang = gnome_get_language_from_locale (key, key);
+        label_current_lang = gnome_get_language_from_locale (key, NULL);
+        label_untranslated = gnome_get_language_from_locale (key, "C");
+
+        /* We don't have a translation for the label in
+         * its own language? */
+        if (g_strcmp0 (label_own_lang, label_untranslated) == 0) {
+                if (g_strcmp0 (label_current_lang, label_untranslated) == 0)
+                        g_hash_table_insert (ht, key, g_strdup (label_untranslated));
+                else
+                        g_hash_table_insert (ht, key, g_strdup (label_current_lang));
+        } else {
+                g_hash_table_insert (ht, key, g_strdup (label_own_lang));
+        }
+
+        g_free (label_own_lang);
+        g_free (label_current_lang);
+        g_free (label_untranslated);
+}
+
+static void
+insert_language (GHashTable *ht,
+                 const char *lang)
+{
+        char *utf8_variant = g_strconcat (lang, ".utf8", NULL);
+        insert_language_internal (ht, lang);
+        insert_language_internal (ht, utf8_variant);
+        g_free (utf8_variant);
+}
+
+static void
+insert_user_languages (GHashTable *ht)
+{
+        char *name;
+        char *language;
+
+        /* Add the languages used by other users on the system */
+        add_other_users_language (ht);
+
+        /* Add current locale */
+        name = cc_common_language_get_current_language ();
+        if (g_hash_table_lookup (ht, name) == NULL) {
+                language = gnome_get_language_from_locale (name, NULL);
+                g_hash_table_insert (ht, name, language);
+        } else {
+                g_free (name);
+        }
+}
+
+GHashTable *
+cc_common_language_get_initial_languages (void)
+{
+        GHashTable *ht;
+
+        ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+        insert_language (ht, "en_US");
+        insert_language (ht, "en_GB");
+        insert_language (ht, "de_DE");
+        insert_language (ht, "fr_FR");
+        insert_language (ht, "es_ES");
+        insert_language (ht, "zh_CN");
+        insert_language (ht, "ja_JP");
+        insert_language (ht, "ru_RU");
+        insert_language (ht, "ar_EG");
+
+        insert_user_languages (ht);
+
+        return ht;
+}
diff --git a/gnome-initial-setup/pages/keyboard/cc-common-language.h 
b/gnome-initial-setup/pages/keyboard/cc-common-language.h
new file mode 100644
index 0000000..7f32cab
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-common-language.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2009-2010  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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ * Written by: Matthias Clasen <mclasen redhat com>
+ */
+
+#ifndef __CC_COMMON_LANGUAGE_H__
+#define __CC_COMMON_LANGUAGE_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+gboolean cc_common_language_has_font                (const gchar  *locale);
+gchar   *cc_common_language_get_current_language    (void);
+GHashTable *cc_common_language_get_initial_languages   (void);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-initial-setup/pages/keyboard/cc-ibus-utils.c 
b/gnome-initial-setup/pages/keyboard/cc-ibus-utils.c
index 54f9fb7..dc070b8 100644
--- a/gnome-initial-setup/pages/keyboard/cc-ibus-utils.c
+++ b/gnome-initial-setup/pages/keyboard/cc-ibus-utils.c
@@ -28,11 +28,15 @@ engine_get_display_name (IBusEngineDesc *engine_desc)
         const gchar *name;
         const gchar *language_code;
         const gchar *language;
+        const gchar *textdomain;
         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);
+        textdomain = ibus_engine_desc_get_textdomain (engine_desc);
+        if (*textdomain != '\0' && *name != '\0')
+                name = g_dgettext (textdomain, name);
         display_name = g_strdup_printf ("%s (%s)", language, name);
 
         return display_name;
diff --git a/gnome-initial-setup/pages/keyboard/cc-input-chooser.c 
b/gnome-initial-setup/pages/keyboard/cc-input-chooser.c
index 3b0371f..298a6bd 100644
--- a/gnome-initial-setup/pages/keyboard/cc-input-chooser.c
+++ b/gnome-initial-setup/pages/keyboard/cc-input-chooser.c
@@ -15,1136 +15,865 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jasper St. Pierre <jstpierre mecheye net>
+ *     Matthias Clasen <mclasen redhat com>
  */
 
-#include <config.h>
+#include "config.h"
+#include "cc-input-chooser.h"
+
 #include <locale.h>
 #include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <gtk/gtk.h>
 
 #define GNOME_DESKTOP_USE_UNSTABLE_API
 #include <libgnome-desktop/gnome-languages.h>
-
-#include <../language/cc-common-language.h>
-#include "cc-input-chooser.h"
-#include "cc-util.h"
+#include <libgnome-desktop/gnome-xkb-info.h>
 
 #ifdef HAVE_IBUS
 #include <ibus.h>
 #include "cc-ibus-utils.h"
-#endif  /* HAVE_IBUS */
+#endif
+
+#include "cc-common-language.h"
+#include "cc-util.h"
+#include "cc-ibus-utils.h"
+
+#include <glib-object.h>
 
 #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 MIN_ROWS 6
 
-#define MAIN_WINDOW_WIDTH_RATIO 0.60
+struct _CcInputChooserPrivate
+{
+        GtkWidget *filter_entry;
+        GtkWidget *input_list;
+       GHashTable *inputs;
 
-typedef enum {
-  ROW_TRAVEL_DIRECTION_NONE,
-  ROW_TRAVEL_DIRECTION_FORWARD,
-  ROW_TRAVEL_DIRECTION_BACKWARD
-} RowTravelDirection;
+        GtkWidget *scrolled_window;
+        GtkWidget *no_results;
+        GtkWidget *more_item;
 
-typedef enum {
-  ROW_LABEL_POSITION_START,
-  ROW_LABEL_POSITION_CENTER,
-  ROW_LABEL_POSITION_END
-} RowLabelPosition;
+        gboolean showing_extra;
+       gchar *locale;
+        gchar *id;
+       gchar *type;
+       GnomeXkbInfo *xkb_info;
+#ifdef HAVE_IBUS
+        IBusBus *ibus;
+        GHashTable *ibus_engines;
+        GCancellable *ibus_cancellable;
+#endif
+};
+typedef struct _CcInputChooserPrivate CcInputChooserPrivate;
+G_DEFINE_TYPE_WITH_PRIVATE (CcInputChooser, cc_input_chooser, GTK_TYPE_BOX);
 
-typedef struct {
-  /* Not owned */
-  GtkWidget *add_button;
-  GtkWidget *filter_entry;
-  GtkWidget *list;
-  GtkWidget *scrolledwindow;
-  GtkAdjustment *adjustment;
-  GnomeXkbInfo *xkb_info;
-  GHashTable *ibus_engines;
-
-  /* Owned */
-  GtkWidget *more_item;
-  GtkWidget *no_results;
-  GHashTable *locales;
-  GHashTable *locales_by_language;
-  gboolean showing_extra;
-  gchar **filter_words;
-} CcInputChooserPrivate;
-
-#define GET_PRIVATE(chooser) ((CcInputChooserPrivate *) g_object_get_data (G_OBJECT (chooser), "private"))
-#define WID(name) ((GtkWidget *) gtk_builder_get_object (builder, name))
+enum {
+        PROP_0,
+        PROP_SHOWING_EXTRA,
+        PROP_LAST
+};
+
+static GParamSpec *obj_props[PROP_LAST];
+
+enum {
+       CHANGED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
 
 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;
+        GtkWidget *box;
+        GtkWidget *label;
+        GtkWidget *checkmark;
 
-static void
-locale_info_free (gpointer data)
+        gchar *id;
+        gchar *type;
+        gchar *name;
+        gboolean is_extra;
+} InputWidget;
+
+static InputWidget *
+get_input_widget (GtkWidget *widget)
 {
-  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);
+        return g_object_get_data (G_OBJECT (widget), "input-widget");
 }
 
-static void
-set_row_widget_margins (GtkWidget *widget)
+static GtkWidget *
+padded_label_new (char *text)
 {
-  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);
+        GtkWidget *widget;
+        widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+        gtk_widget_set_halign (widget, GTK_ALIGN_CENTER);
+        gtk_widget_set_margin_top (widget, 10);
+        gtk_widget_set_margin_bottom (widget, 10);
+        gtk_box_pack_start (GTK_BOX (widget), gtk_label_new (text), FALSE, FALSE, 0);
+        return widget;
 }
 
-static GtkWidget *
-padded_label_new (const gchar        *text,
-                  RowLabelPosition    position,
-                  RowTravelDirection  direction,
-                  gboolean            dim_label)
+static void
+input_widget_free (gpointer data)
 {
-  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;
+        InputWidget *widget = data;
+
+        g_free (widget->id);
+        g_free (widget->type);
+        g_free (widget->name);
+        g_free (widget);
 }
 
-static GtkWidget *
-more_widget_new (void)
+static gboolean
+get_layout (CcInputChooser *chooser,
+            const gchar    *type,
+           const gchar    *id,
+           const gchar   **layout,
+            const gchar   **variant)
 {
-  GtkWidget *widget;
-  GtkWidget *arrow;
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
 
-  widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
-  gtk_widget_set_tooltip_text (widget, _("More…"));
+       *layout = "";
+       *variant = "";
 
-  arrow = gtk_image_new_from_icon_name ("view-more-symbolic", GTK_ICON_SIZE_MENU);
-  gtk_style_context_add_class (gtk_widget_get_style_context (arrow), "dim-label");
-  set_row_widget_margins (arrow);
-  gtk_misc_set_alignment (GTK_MISC (arrow), 0.5, 0.5);
-  gtk_box_pack_start (GTK_BOX (widget), arrow, TRUE, TRUE, 0);
+       if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) {
+               gnome_xkb_info_get_layout_info (priv->xkb_info,
+                                               id, NULL, NULL,
+                                               layout, variant);
+#ifdef HAVE_IBUS
+       } else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) {
+                IBusEngineDesc *engine_desc = NULL;
 
-  return widget;
-}
+               if (priv->ibus_engines)
+                       engine_desc = g_hash_table_lookup (priv->ibus_engines, id);
+               else
+                       return FALSE;
 
-static GtkWidget *
-no_results_widget_new (void)
-{
-  return padded_label_new (_("No input sources found"), ROW_LABEL_POSITION_CENTER, 
ROW_TRAVEL_DIRECTION_NONE, TRUE);
+               if (engine_desc) {
+                       *layout = ibus_engine_desc_get_layout (engine_desc);
+                       *variant = "";
+               } else {
+                       return FALSE;
+               }
+       }
+#endif
+       return TRUE;
 }
 
-static GtkWidget *
-back_widget_new (const gchar *text)
+static gboolean
+preview_cb (GtkLabel       *label,
+           const gchar    *uri,
+           CcInputChooser *chooser)
 {
-  return padded_label_new (text, ROW_LABEL_POSITION_CENTER, ROW_TRAVEL_DIRECTION_BACKWARD, TRUE);
-}
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+       GtkWidget *row;
+       InputWidget *widget;
+       const gchar *layout;
+       const gchar *variant;
+       gchar *commandline;
 
-static GtkWidget *
-locale_widget_new (const gchar *text)
-{
-  return padded_label_new (text, ROW_LABEL_POSITION_CENTER, ROW_TRAVEL_DIRECTION_NONE, FALSE);
-}
+       row = gtk_widget_get_parent (GTK_WIDGET (label));
+       widget = get_input_widget (row);
 
-static GtkWidget *
-locale_separator_widget_new (const gchar *text)
-{
-  GtkWidget *widget;
-
-  widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-  gtk_box_pack_start (GTK_BOX (widget),
-                      gtk_separator_new (GTK_ORIENTATION_HORIZONTAL),
-                      FALSE, FALSE, 0);
-  gtk_box_pack_start (GTK_BOX (widget),
-                      padded_label_new (text, ROW_LABEL_POSITION_CENTER, ROW_TRAVEL_DIRECTION_NONE, TRUE),
-                      FALSE, FALSE, 0);
-  gtk_box_pack_start (GTK_BOX (widget),
-                      gtk_separator_new (GTK_ORIENTATION_HORIZONTAL),
-                      FALSE, FALSE, 0);
-  return widget;
+       if (!get_layout (chooser, widget->type, widget->id, &layout, &variant))
+               return TRUE;
+
+       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);
+
+       return 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;
+input_widget_new (CcInputChooser *chooser,
+                  const char *type,
+                  const char *id,
+                   gboolean    is_extra)
+{
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+        gchar *locale_name, *locale_current_name, *locale_untranslated_name;
+        GtkWidget *checkmark;
+       GtkWidget *label;
+        InputWidget *widget = g_new0 (InputWidget, 1);
+       const gchar *name;
+       gchar *text;
+
+       if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) {
+               gnome_xkb_info_get_layout_info (priv->xkb_info, id, &name, NULL, NULL, NULL);
+       } else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) {
+               name = engine_get_display_name (g_hash_table_lookup (priv->ibus_engines, id));
+       }
+       else {
+               name = "ERROR";
+       }
+
+        widget->id = g_strdup (id);
+       widget->type = g_strdup (type);
+       widget->name = g_strdup (name);
+       widget->is_extra = is_extra;
+
+       widget->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+       gtk_widget_set_halign (widget->box, GTK_ALIGN_FILL);
+       gtk_widget_set_margin_top (widget->box, 10);
+       gtk_widget_set_margin_bottom (widget->box, 10);
+       gtk_widget_set_margin_start (widget->box, 10);
+       gtk_widget_set_margin_end (widget->box, 10);
+       widget->label = gtk_label_new (name);
+       gtk_misc_set_alignment (GTK_MISC (widget->label), 0, 0.5);
+       gtk_label_set_width_chars (GTK_LABEL (widget->label), 40);
+       gtk_box_pack_start (GTK_BOX (widget->box), widget->label, FALSE, FALSE, 0);
+       widget->checkmark = gtk_image_new_from_icon_name ("object-select-symbolic", GTK_ICON_SIZE_MENU);
+       gtk_box_pack_start (GTK_BOX (widget->box), widget->checkmark, TRUE, TRUE, 0);
+       gtk_widget_set_margin_start (widget->checkmark, 10);
+       gtk_widget_set_margin_end (widget->checkmark, 10);
+       gtk_widget_set_halign (widget->box, GTK_ALIGN_START);
+
+       text = g_strdup_printf ("<a href='preview'>%s</a>", _("Preview"));
+       label = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (label), text);
+       g_free (text);
+       g_signal_connect (label, "activate-link",
+                         G_CALLBACK (preview_cb), chooser);
+       gtk_box_pack_start (GTK_BOX (widget->box), label, TRUE, TRUE, 0);
+
+       gtk_widget_show_all (widget->box);
+
+       g_object_set_data_full (G_OBJECT (widget->box), "input-widget", widget,
+                               input_widget_free);
+
+       return widget->box;
 }
 
 static void
-remove_all_children (GtkContainer *container)
+sync_checkmark (GtkWidget *row,
+                gpointer   user_data)
 {
-  GList *list, *l;
+       CcInputChooser *chooser = user_data;
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+        GtkWidget *child;
+        InputWidget *widget;
+        gboolean should_be_visible;
 
-  list = gtk_container_get_children (container);
-  for (l = list; l; l = l->next)
-    gtk_container_remove (container, (GtkWidget *) l->data);
-  g_list_free (list);
+        child = gtk_bin_get_child (GTK_BIN (row));
+        widget = get_input_widget (child);
+
+        if (widget == NULL)
+                return;
+
+       if (priv->id == NULL || priv->type == NULL)
+               should_be_visible = FALSE;
+       else
+               should_be_visible = g_str_equal (widget->id, priv->id) && g_str_equal (widget->type, 
priv->type);
+        gtk_widget_set_opacity (widget->checkmark, should_be_visible ? 1.0 : 0.0);
 }
 
 static void
-set_fixed_size (GtkWidget *chooser)
+sync_all_checkmarks (CcInputChooser *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);
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+
+        gtk_container_foreach (GTK_CONTAINER (priv->input_list),
+                               sync_checkmark, chooser);
 }
 
-static void
-update_header_func (GtkListBoxRow *child,
-                    GtkListBoxRow *before,
-                    gpointer       user_data)
+static GtkWidget *
+more_widget_new (void)
 {
-  GtkWidget *header;
+        GtkWidget *widget;
+        GtkWidget *arrow;
+
+        widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+        gtk_widget_set_tooltip_text (widget, _("More…"));
 
-  if (before == NULL)
-    return;
+        arrow = gtk_image_new_from_icon_name ("view-more-symbolic", GTK_ICON_SIZE_MENU);
+        gtk_style_context_add_class (gtk_widget_get_style_context (arrow), "dim-label");
+        gtk_widget_set_margin_top (widget, 10);
+        gtk_widget_set_margin_bottom (widget, 10);
+        gtk_misc_set_alignment (GTK_MISC (arrow), 0.5, 0.5);
+        gtk_box_pack_start (GTK_BOX (widget), arrow, TRUE, TRUE, 0);
+       gtk_widget_show_all (widget);
 
-  header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
-  gtk_list_box_row_set_header (child, header);
-  gtk_widget_show (header);
+        return widget;
 }
 
-static void
-add_input_source_widgets_for_locale (GtkWidget  *chooser,
-                                     LocaleInfo *info)
+static GtkWidget *
+no_results_widget_new (void)
 {
-  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
-  GtkWidget *widget;
-  GHashTableIter iter;
-  const gchar *id;
+        GtkWidget *widget;
 
-  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);
+        widget = padded_label_new (_("No inputs found"));
+        gtk_widget_set_sensitive (widget, FALSE);
+       gtk_widget_show_all (widget);
+        return 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
+add_rows_to_list (CcInputChooser  *chooser,
+                 GList            *list,
+                 const gchar      *type,
+                 const gchar      *default_id)
+{
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+       const gchar *id;
+       GtkWidget *widget;
+       gboolean is_extra;
+       gchar *key;
+
+       for (; list; list = list->next) {
+               id = (const gchar *) list->data;
+
+               if (g_strcmp0 (id, default_id) == 0)
+                       continue;
+
+               key = g_strdup_printf ("%s::%s", type, id);
+               if (g_hash_table_contains (priv->inputs, key)) {
+                       g_free (key);
+                       continue;
+               }
+               g_hash_table_add (priv->inputs, key);
+
+               if (g_hash_table_size (priv->inputs) > MIN_ROWS)
+                       is_extra = TRUE;
+               else
+                       is_extra = FALSE;
+               widget = input_widget_new (chooser, type, id, is_extra);
+               gtk_container_add (GTK_CONTAINER (priv->input_list), widget);
+       }
 }
 
 static void
-show_input_sources_for_locale (GtkWidget   *chooser,
-                               LocaleInfo  *info)
+add_row_to_list (CcInputChooser *chooser,
+                const gchar     *type,
+                const gchar     *id)
 {
-  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+       GList tmp = { 0 };
+       tmp.data = (gpointer)id;
+       add_rows_to_list (chooser, &tmp, type, NULL);
+}
 
-  set_fixed_size (chooser);
+static void
+get_locale_infos (CcInputChooser *chooser)
+{
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+       const gchar *type, *id;
+       gchar *lang, *country;
+       GList *list;
 
-  remove_all_children (GTK_CONTAINER (priv->list));
+       if (gnome_get_input_source_from_locale (priv->locale, &type, &id)) { 
+               if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB))
+                       add_row_to_list (chooser, type, id);
+               if (!priv->id) {
+                       priv->id = g_strdup (id);
+                       priv->type = g_strdup (type);
+               }
+       }
 
-  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);
+       if (!gnome_parse_locale (priv->locale, &lang, &country, NULL, NULL))
+               goto out;
 
-  add_input_source_widgets_for_locale (chooser, info);
+       list = gnome_xkb_info_get_layouts_for_language (priv->xkb_info, lang);
+       add_rows_to_list (chooser, list, INPUT_SOURCE_TYPE_XKB, id);
+       g_list_free (list);
 
-  gtk_widget_show_all (priv->list);
+       list = gnome_xkb_info_get_layouts_for_country (priv->xkb_info, country);
+       add_rows_to_list (chooser, list, INPUT_SOURCE_TYPE_XKB, id);
+       g_list_free (list);
 
-  gtk_adjustment_set_value (priv->adjustment,
-                            gtk_adjustment_get_lower (priv->adjustment));
-  gtk_list_box_set_header_func (GTK_LIST_BOX (priv->list), update_header_func, NULL, NULL);
-  gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->list), GTK_SELECTION_SINGLE);
+        gtk_widget_show_all (priv->input_list);
 
-  if (gtk_widget_is_visible (priv->filter_entry))
-    gtk_widget_grab_focus (priv->filter_entry);
+out:
+       g_free (lang);
+       g_free (country);
 }
 
 static gboolean
-is_current_locale (const gchar *locale)
+input_visible (GtkListBoxRow *row,
+                  gpointer       user_data)
 {
-  return g_strcmp0 (setlocale (LC_CTYPE, NULL), locale) == 0;
-}
+        CcInputChooser *chooser = user_data;
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+        InputWidget *widget;
+        gboolean visible;
+        GtkWidget *child;
+        const char *search_term;
 
-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);
-    }
+        child = gtk_bin_get_child (GTK_BIN (row));
+        if (child == priv->more_item)
+                return !priv->showing_extra && g_hash_table_size (priv->inputs) > MIN_ROWS;
 
-  gtk_container_add (GTK_CONTAINER (priv->list), priv->more_item);
+        widget = get_input_widget (child);
 
-  gtk_widget_show_all (priv->list);
+        if (!priv->showing_extra && widget->is_extra)
+                return FALSE;
 
-  gtk_adjustment_set_value (priv->adjustment,
-                            gtk_adjustment_get_lower (priv->adjustment));
-  gtk_list_box_set_header_func (GTK_LIST_BOX (priv->list), update_header_func, NULL, NULL);
-  gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->list), GTK_SELECTION_NONE);
+        search_term = gtk_entry_get_text (GTK_ENTRY (priv->filter_entry));
+        if (!search_term || !*search_term)
+                return TRUE;
 
-  if (gtk_widget_is_visible (priv->filter_entry))
-    gtk_widget_grab_focus (priv->filter_entry);
+        visible = FALSE;
 
-  if (!priv->showing_extra)
-    g_hash_table_destroy (initial);
+        visible = g_str_match_string (search_term, widget->name, TRUE);
 
-  return;
+ out:
+        return visible;
 }
 
 static gint
-list_sort (GtkListBoxRow *a,
-           GtkListBoxRow *b,
-           gpointer data)
+sort_inputs (GtkListBoxRow *a,
+                GtkListBoxRow *b,
+                gpointer       data)
 {
-  GtkWidget *chooser = data;
-  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
-  GtkWidget *wa, *wb;
-  LocaleInfo *ia;
-  LocaleInfo *ib;
-  const gchar *la;
-  const gchar *lb;
-  gint retval;
-
-  wa = gtk_bin_get_child (GTK_BIN (a));
-  wb = gtk_bin_get_child (GTK_BIN (b));
-
-  /* Always goes at the end */
-  if (wa == priv->more_item)
-    return 1;
-  if (wb == priv->more_item)
-    return -1;
-
-  ia = g_object_get_data (G_OBJECT (wa), "locale-info");
-  ib = g_object_get_data (G_OBJECT (wb), "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);
-}
+        InputWidget *la, *lb;
 
-static gboolean
-match_all (gchar       **words,
-           const gchar  *str)
-{
-  gchar **w;
+        la = get_input_widget (gtk_bin_get_child (GTK_BIN (a)));
+        lb = get_input_widget (gtk_bin_get_child (GTK_BIN (b)));
+
+        if (la == NULL)
+                return 1;
+
+        if (lb == NULL)
+                return -1;
 
-  for (w = words; *w; ++w)
-    if (!strstr (str, *w))
-      return FALSE;
+        if (la->is_extra && !lb->is_extra)
+                return 1;
 
-  return TRUE;
+        if (!la->is_extra && lb->is_extra)
+                return -1;
+
+        return strcmp (la->name, lb->name);
 }
 
-static gboolean
-list_filter (GtkListBoxRow *row,
-             gpointer user_data)
+static void
+filter_changed (GtkEntry        *entry,
+                CcInputChooser *chooser)
+{
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+        gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->input_list));
+}
+
+static void
+show_more (CcInputChooser *chooser)
 {
-  GtkDialog *chooser = user_data;
-  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
-  LocaleInfo *info;
-  gboolean is_extra;
-  const gchar *source_name;
-  GtkWidget *child;
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
 
-  child = gtk_bin_get_child (GTK_BIN (row));
+       if (g_hash_table_size (priv->inputs) <= MIN_ROWS)
+               return;
 
-  if (child == priv->more_item)
-    return !priv->showing_extra;
+        gtk_widget_show (priv->filter_entry);
+        gtk_widget_grab_focus (priv->filter_entry);
 
-  is_extra = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (child), "is-extra"));
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
+                                       GTK_POLICY_NEVER,
+                                       GTK_POLICY_AUTOMATIC);
+       gtk_widget_set_valign (GTK_WIDGET (chooser), GTK_ALIGN_FILL);
 
-  if (!priv->showing_extra && is_extra)
-    return FALSE;
+        priv->showing_extra = TRUE;
+        gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->input_list));
+        g_object_notify_by_pspec (G_OBJECT (chooser), obj_props[PROP_SHOWING_EXTRA]);
+}
 
-  if (!priv->filter_words)
-    return TRUE;
+static void
+set_input (CcInputChooser *chooser,
+           const gchar    *id,
+          const gchar    *type)
+{
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
 
-  info = g_object_get_data (G_OBJECT (child), "locale-info");
+        if (g_strcmp0 (priv->id, id) == 0 &&
+            g_strcmp0 (priv->type, type) == 0)
+                return;
 
-  if (match_all (priv->filter_words, info->unaccented_name))
-    return TRUE;
+        g_free (priv->id);
+       g_free (priv->type);
+        priv->id = g_strdup (id);
+       priv->type = g_strdup (type);
 
-  if (match_all (priv->filter_words, info->untranslated_name))
-    return TRUE;
+        sync_all_checkmarks (chooser);
 
-  source_name = g_object_get_data (G_OBJECT (child), "unaccented-name");
-  if (source_name && match_all (priv->filter_words, source_name))
-    return TRUE;
+       g_signal_emit (chooser, signals[CHANGED], 0);
+}
 
-  return FALSE;
+static void
+row_activated (GtkListBox        *box,
+               GtkListBoxRow     *row,
+               CcInputChooser *chooser)
+{
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+        GtkWidget *child;
+        InputWidget *widget;
+
+        if (row == NULL)
+                return;
+
+        child = gtk_bin_get_child (GTK_BIN (row));
+        if (child == priv->more_item) {
+                show_more (chooser);
+        } else {
+                widget = get_input_widget (child);
+                if (widget == NULL)
+                        return;
+                set_input (chooser, widget->id, widget->type);
+        }
 }
 
 static void
-update_header_func_filter (GtkListBoxRow *row,
-                           GtkListBoxRow *row_before,
-                           gpointer       user_data)
+update_header_func (GtkListBoxRow *child,
+                    GtkListBoxRow *before,
+                    gpointer       user_data)
 {
-  GtkWidget *child;
-  GtkWidget *before;
+        GtkWidget *header;
 
-  LocaleInfo *child_info = NULL;
-  LocaleInfo *before_info = NULL;
+        if (before == NULL)
+                return;
 
-  GtkWidget *header;
-
-  child = gtk_bin_get_child (GTK_BIN (row));
-  before = gtk_bin_get_child (GTK_BIN (row_before));
-
-  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");
+        header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+        gtk_list_box_row_set_header (child, header);
+        gtk_widget_show (header);
+}
 
-  if (!child_info || !before_info)
-    return;
+#ifdef HAVE_IBUS
+static void
+update_ibus_active_sources (CcInputChooser *chooser)
+{
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+        GList *rows, *l;
+        InputWidget *row;
+        const gchar *type;
+        const gchar *id;
+        IBusEngineDesc *engine_desc;
+        gchar *name;
+
+        rows = gtk_container_get_children (GTK_CONTAINER (priv->input_list));
+        for (l = rows; l; l = l->next) {
+               row = get_input_widget (gtk_bin_get_child (GTK_BIN (l->data)));
+               if (row == NULL)
+                       continue;
+
+                type = row->type;
+                id = 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) {
+                        name = engine_get_display_name (engine_desc);
+                        gtk_label_set_text (GTK_LABEL (row->label), name);
+                        g_free (name);
+                }
+        }
+        g_list_free (rows);
+}
 
-  if (child_info == before_info)
-    header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
-  else
-    header = locale_separator_widget_new (child_info->name);
+static void
+get_ibus_locale_infos (CcInputChooser *chooser)
+{
+       CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+       GHashTableIter iter;
+       const gchar *engine_id;
+       IBusEngineDesc *engine;
+       gchar *lang, *country;
+
+       if (!priv->ibus_engines)
+               return;
+
+       if (!gnome_parse_locale (priv->locale, &lang, &country, NULL, NULL))
+               goto out;
+
+       g_hash_table_iter_init (&iter, priv->ibus_engines);
+       while (g_hash_table_iter_next (&iter, (gpointer *) &engine_id, (gpointer *) &engine)) {
+               gchar *ibus_lang = NULL;
+               gchar *ibus_country = NULL;
+               const gchar *locale;
+
+               locale = ibus_engine_desc_get_language (engine);
+
+               if (gnome_parse_locale (locale, &ibus_lang, &ibus_country, NULL, NULL)) {
+                       if (g_strcmp0 (lang, ibus_lang) == 0 &&
+                           (ibus_country == NULL || g_strcmp0 (country, ibus_country) == 0))
+                               add_row_to_list (chooser, INPUT_SOURCE_TYPE_IBUS, engine_id);
+               }
+               g_free (ibus_lang);
+               g_free (ibus_country);
+        }
 
-  gtk_widget_show_all (header);
-  gtk_list_box_row_set_header (row, header);
+out:
+       g_free (lang);
+       g_free (country);
 }
 
 static void
-show_filter_widgets (GtkWidget *chooser)
-{
-  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
-  LocaleInfo *info;
-  GHashTableIter iter;
+fetch_ibus_engines_result (GObject       *object,
+                           GAsyncResult  *result,
+                           CcInputChooser *chooser)
+{
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+        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;
+        }
 
-  remove_all_children (GTK_CONTAINER (priv->list));
+        /* 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);
 
-  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);
+        for (l = list; l; l = l->next) {
+                IBusEngineDesc *engine = l->data;
+                const gchar *engine_id;
 
-  gtk_widget_show_all (priv->list);
+               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);
 
-  gtk_adjustment_set_value (priv->adjustment,
-                            gtk_adjustment_get_lower (priv->adjustment));
-  gtk_list_box_set_header_func (GTK_LIST_BOX (priv->list),
-                                update_header_func_filter, NULL, NULL);
-  gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->list), GTK_SELECTION_SINGLE);
-  gtk_list_box_set_placeholder (GTK_LIST_BOX (priv->list), priv->no_results);
+       update_ibus_active_sources (chooser);
+       get_ibus_locale_infos (chooser);
 
-  if (gtk_widget_is_visible (priv->filter_entry))
-    gtk_widget_grab_focus (priv->filter_entry);
+        sync_all_checkmarks (chooser);
 }
+#endif
 
-static gboolean
-strvs_differ (gchar **av,
-              gchar **bv)
+static void
+fetch_ibus_engines (CcInputChooser *chooser)
 {
-  gchar **a, **b;
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
 
-  for (a = av, b = bv; *a && *b; ++a, ++b)
-    if (!g_str_equal (*a, *b))
-      return TRUE;
+        priv->ibus_cancellable = g_cancellable_new ();
 
-  if (*a == NULL && *b == NULL)
-    return FALSE;
+        ibus_bus_list_engines_async (priv->ibus,
+                                     -1,
+                                     priv->ibus_cancellable,
+                                     (GAsyncReadyCallback)fetch_ibus_engines_result,
+                                     chooser);
 
-  return TRUE;
+       /* We've got everything we needed, don't want to be called again. */
+       g_signal_handlers_disconnect_by_func (priv->ibus, fetch_ibus_engines, chooser);
 }
 
 static void
-filter_changed (GtkWidget *chooser)
+maybe_start_ibus (void)
 {
-  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))
-        gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->list));
-    }
-
-  g_strfreev (previous_words);
+        /* 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 void
-show_more (GtkWidget *chooser)
+cc_input_chooser_constructed (GObject *object)
 {
-  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
+        CcInputChooser *chooser = CC_INPUT_CHOOSER (object);
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
 
-  set_fixed_size (chooser);
+        G_OBJECT_CLASS (cc_input_chooser_parent_class)->constructed (object);
 
-  gtk_widget_show (priv->filter_entry);
-  gtk_widget_grab_focus (priv->filter_entry);
+       priv->xkb_info = gnome_xkb_info_new ();
 
-  priv->showing_extra = TRUE;
+#ifdef HAVE_IBUS
+        ibus_init ();
+        if (!priv->ibus) {
+                priv->ibus = ibus_bus_new_async ();
+                if (ibus_bus_is_connected (priv->ibus))
+                        fetch_ibus_engines (chooser);
+                else
+                        g_signal_connect_swapped (priv->ibus, "connected",
+                                                  G_CALLBACK (fetch_ibus_engines), chooser);
+        }
+        maybe_start_ibus ();
+#endif
+
+       priv->inputs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+        priv->more_item = more_widget_new ();
+        priv->no_results = no_results_widget_new ();
+
+        gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->input_list),
+                                    sort_inputs, chooser, NULL);
+        gtk_list_box_set_filter_func (GTK_LIST_BOX (priv->input_list),
+                                      input_visible, chooser, NULL);
+        gtk_list_box_set_header_func (GTK_LIST_BOX (priv->input_list),
+                                      update_header_func, chooser, NULL);
+        gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->input_list),
+                                         GTK_SELECTION_NONE);
+
+       if (priv->locale == NULL) {
+               priv->locale = cc_common_language_get_current_language ();
+       }
+
+        get_locale_infos (chooser);
+#ifdef HAVE_IBUS
+       get_ibus_locale_infos (chooser);
+#endif
 
-  gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->list));
-}
+        gtk_container_add (GTK_CONTAINER (priv->input_list), priv->more_item);
+        gtk_list_box_set_placeholder (GTK_LIST_BOX (priv->input_list), priv->no_results);
 
-static void
-row_activated (GtkListBox *box,
-               GtkListBoxRow *row,
-               GtkWidget  *chooser)
-{
-  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
-  gpointer data;
-  GtkWidget *child;
-
-  if (!row)
-    return;
-
-  child = gtk_bin_get_child (GTK_BIN (row));
-
-  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;
-    }
-}
+        g_signal_connect (priv->filter_entry, "changed",
+                          G_CALLBACK (filter_changed),
+                          chooser);
 
-static void
-row_selected (GtkListBox *box,
-              GtkListBoxRow *row,
-              GtkWidget  *chooser)
-{
-  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
-  gtk_widget_set_sensitive (priv->add_button, row != NULL);
+        g_signal_connect (priv->input_list, "row-activated",
+                          G_CALLBACK (row_activated), chooser);
+
+        if (priv->locale == NULL)
+                priv->locale = cc_common_language_get_current_language ();
+
+        sync_all_checkmarks (chooser);
 }
 
 static void
-add_default_widget (GtkWidget   *chooser,
-                    LocaleInfo  *info,
-                    const gchar *type,
-                    const gchar *id)
+cc_input_chooser_finalize (GObject *object)
 {
-  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);
-    }
+       CcInputChooser *chooser = CC_INPUT_CHOOSER (object);
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+
+       g_clear_object (&priv->xkb_info);
+       g_hash_table_unref (priv->inputs);
+#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_OBJECT_CLASS (cc_input_chooser_parent_class)->finalize (object);
 }
 
 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));
-            }
+cc_input_chooser_get_property (GObject      *object,
+                                  guint         prop_id,
+                                  GValue       *value,
+                                  GParamSpec   *pspec)
+{
+        CcInputChooser *chooser = CC_INPUT_CHOOSER (object);
+        switch (prop_id) {
+        case PROP_SHOWING_EXTRA:
+                g_value_set_boolean (value, cc_input_chooser_get_showing_extra (chooser));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
         }
-      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);
+cc_input_chooser_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+        CcInputChooser *chooser = CC_INPUT_CHOOSER (object);
+        switch (prop_id) {
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
 }
 
 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)
+cc_input_chooser_class_init (CcInputChooserClass *klass)
 {
-  const gchar *type, *id;
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  if (!gnome_get_input_source_from_locale (info->id, &type, &id))
-    return FALSE;
+        gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), 
"/org/gnome/initial-setup/input-chooser.ui");
 
-  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;
-    }
+        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), CcInputChooser, 
filter_entry);
+        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), CcInputChooser, input_list);
+        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), CcInputChooser, 
scrolled_window);
 
-  return FALSE;
-}
+       object_class->finalize = cc_input_chooser_finalize;
+        object_class->get_property = cc_input_chooser_get_property;
+        object_class->set_property = cc_input_chooser_set_property;
+        object_class->constructed = cc_input_chooser_constructed;
 
-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);
-            }
+        obj_props[PROP_SHOWING_EXTRA] =
+                g_param_spec_string ("showing-extra", "", "", "",
+                                     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
-          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);
-        }
+       signals[CHANGED] = 
+               g_signal_new ("changed",
+                             G_TYPE_FROM_CLASS (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             0,
+                             NULL, NULL,
+                             g_cclosure_marshal_VOID__VOID,
+                             G_TYPE_NONE, 0);
 
-      g_free (country_code);
-      g_free (lang_code);
-    }
+        g_object_class_install_properties (object_class, PROP_LAST, obj_props);
 }
-#endif  /* HAVE_IBUS */
 
 static void
-add_locale_to_table (GHashTable  *table,
-                     const gchar *lang_code,
-                     LocaleInfo  *info)
+cc_input_chooser_init (CcInputChooser *chooser)
 {
-  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);
+        gtk_widget_init_template (GTK_WIDGET (chooser));
 }
 
-static void
-add_ids_to_set (GHashTable *set,
-                GList      *list)
+void
+cc_input_chooser_clear_filter (CcInputChooser *chooser)
 {
-  while (list)
-    {
-      g_hash_table_add (set, list->data);
-      list = list->next;
-    }
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+        gtk_entry_set_text (GTK_ENTRY (priv->filter_entry), "");
 }
 
-static void
-get_locale_infos (GtkWidget *chooser)
+const gchar *
+cc_input_chooser_get_input_id (CcInputChooser *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);
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+        return priv->id;
 }
 
-static void
-cc_input_chooser_private_free (gpointer data)
+const gchar *
+cc_input_chooser_get_input_type (CcInputChooser *chooser)
 {
-  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);
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+        return priv->type;
 }
 
-GtkWidget *
-cc_input_chooser_new (GtkWindow    *main_window,
-                      GnomeXkbInfo *xkb_info,
-                      GHashTable   *ibus_engines)
+void
+cc_input_chooser_get_layout (CcInputChooser *chooser,
+                            const gchar    **layout,
+                            const gchar    **variant)
 {
-  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 ());
-
-  gtk_list_box_set_filter_func (GTK_LIST_BOX (priv->list), list_filter, chooser, NULL);
-  gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->list), list_sort, chooser, NULL);
-  g_signal_connect (priv->list, "row-activated", G_CALLBACK (row_activated), chooser);
-  g_signal_connect (priv->list, "row-selected", G_CALLBACK (row_selected), 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);
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
 
-  gtk_window_set_transient_for (GTK_WINDOW (chooser), main_window);
-
-  return chooser;
+       get_layout (chooser, priv->type, priv->id, layout, variant);
 }
 
 void
-cc_input_chooser_set_ibus_engines (GtkWidget  *chooser,
-                                   GHashTable *ibus_engines)
+cc_input_chooser_set_input (CcInputChooser *chooser,
+                            const gchar    *id,
+                           const gchar    *type)
 {
-#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 */
+        set_input (chooser, id, type);
 }
 
 gboolean
-cc_input_chooser_get_selected (GtkWidget  *chooser,
-                               gchar     **type,
-                               gchar     **id,
-                               gchar     **name)
+cc_input_chooser_get_showing_extra (CcInputChooser *chooser)
 {
-  CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
-  GtkListBoxRow *row;
-  GtkWidget *selected;
-  const gchar *t, *i, *n;
-
-  row = gtk_list_box_get_selected_row (GTK_LIST_BOX (priv->list));
-  if (!row)
-    return FALSE;
-
-  selected = gtk_bin_get_child (GTK_BIN (row));
-  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;
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+        return priv->showing_extra;
 }
diff --git a/gnome-initial-setup/pages/keyboard/cc-input-chooser.h 
b/gnome-initial-setup/pages/keyboard/cc-input-chooser.h
index a9edc65..8b2035a 100644
--- a/gnome-initial-setup/pages/keyboard/cc-input-chooser.h
+++ b/gnome-initial-setup/pages/keyboard/cc-input-chooser.h
@@ -15,28 +15,52 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jasper St. Pierre <jstpierre mecheye net>
+ *     Matthias Clasen <mclasen redhat com>
  */
 
 #ifndef __GIS_INPUT_CHOOSER_H__
 #define __GIS_INPUT_CHOOSER_H__
 
 #include <gtk/gtk.h>
+#include <glib-object.h>
 
-#define GNOME_DESKTOP_USE_UNSTABLE_API
-#include <libgnome-desktop/gnome-xkb-info.h>
-
+#define CC_TYPE_INPUT_CHOOSER            (cc_input_chooser_get_type ())
+#define CC_INPUT_CHOOSER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_INPUT_CHOOSER, 
CcInputChooser))
+#define CC_INPUT_CHOOSER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  CC_TYPE_INPUT_CHOOSER, 
CcInputChooserClass))
+#define CC_IS_INPUT_CHOOSER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_INPUT_CHOOSER))
+#define CC_IS_INPUT_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  CC_TYPE_INPUT_CHOOSER))
+#define CC_INPUT_CHOOSER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  CC_TYPE_INPUT_CHOOSER, 
CcInputChooserClass))
 
 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);
+typedef struct _CcInputChooser        CcInputChooser;
+typedef struct _CcInputChooserClass   CcInputChooserClass;
+
+struct _CcInputChooser
+{
+        GtkBox parent;
+};
+
+struct _CcInputChooserClass
+{
+        GtkBoxClass parent_class;
+};
+
+GType cc_input_chooser_get_type (void);
+
+void          cc_input_chooser_clear_filter (CcInputChooser *chooser);
+const gchar * cc_input_chooser_get_input_id (CcInputChooser  *chooser);
+const gchar * cc_input_chooser_get_input_type (CcInputChooser  *chooser);
+void          cc_input_chooser_set_input (CcInputChooser *chooser,
+                                          const gchar    *id,
+                                          const gchar    *type);
+void         cc_input_chooser_get_layout (CcInputChooser *chooser,
+                                          const gchar    **layout,
+                                          const gchar    **variant);
+gboolean      cc_input_chooser_get_showing_extra (CcInputChooser *chooser);
 
 G_END_DECLS
 
diff --git a/gnome-initial-setup/pages/keyboard/cc-util.c b/gnome-initial-setup/pages/keyboard/cc-util.c
index 7a3366c..e51a9d2 100644
--- a/gnome-initial-setup/pages/keyboard/cc-util.c
+++ b/gnome-initial-setup/pages/keyboard/cc-util.c
@@ -20,7 +20,7 @@
 #include "config.h"
 
 #include <string.h>
-#include <glib/gi18n.h>
+
 
 #include "cc-util.h"
 
diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c 
b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
index 91b10e7..1437510 100644
--- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
+++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
@@ -28,7 +28,6 @@
 #include <locale.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
-#include <gio/gdesktopappinfo.h>
 #include <gtk/gtk.h>
 #include <polkit/polkit.h>
 
@@ -36,49 +35,17 @@
 #include "keyboard-resources.h"
 #include "cc-input-chooser.h"
 
-#define GNOME_DESKTOP_USE_UNSTABLE_API
-#include <libgnome-desktop/gnome-xkb-info.h>
-#include <libgnome-desktop/gnome-languages.h>
-
-#ifdef HAVE_IBUS
-#include <ibus.h>
-#include "cc-ibus-utils.h"
-#endif
-
-#include <act/act.h>
-#include <unistd.h>
-
 #define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.gnome.desktop.input-sources"
 #define KEY_CURRENT_INPUT_SOURCE "current"
 #define KEY_INPUT_SOURCES        "sources"
 
-#define INPUT_SOURCE_TYPE_XKB "xkb"
-#define INPUT_SOURCE_TYPE_IBUS "ibus"
-
-#define MAX_INPUT_ROWS_VISIBLE 5
-
 struct _GisKeyboardPagePrivate {
-        GtkWidget *input_list;
-        GtkWidget *input_source_add;
-        GtkWidget *input_source_remove;
-        GtkWidget *show_config;
-        GtkWidget *show_layout;
-        GtkWidget *scrolled_window;
-
-        GDBusProxy  *localed;
-        GCancellable *cancellable;
-
-        guint n_input_rows;
-        GPid gkbd_pid;
-        GPermission *permission;
+        GtkWidget *input_chooser;
 
+       GDBusProxy *localed;
+       GCancellable *cancellable;
+       GPermission *permission;
         GSettings *input_settings;
-        GnomeXkbInfo *xkb_info;
-#ifdef HAVE_IBUS
-        IBusBus *ibus;
-        GHashTable *ibus_engines;
-        GCancellable *ibus_cancellable;
-#endif
 };
 typedef struct _GisKeyboardPagePrivate GisKeyboardPagePrivate;
 
@@ -90,1014 +57,181 @@ gis_keyboard_page_finalize (GObject *object)
        GisKeyboardPage *self = GIS_KEYBOARD_PAGE (object);
         GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
 
-        g_cancellable_cancel (priv->cancellable);
-        g_clear_object (&priv->cancellable);
+       if (priv->cancellable)
+               g_cancellable_cancel (priv->cancellable);
+       g_clear_object (&priv->cancellable);
 
-        g_clear_object (&priv->permission);
-        g_clear_object (&priv->localed);
-        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_clear_object (&priv->permission);
+       g_clear_object (&priv->localed);
+       g_clear_object (&priv->input_settings);
 
        G_OBJECT_CLASS (gis_keyboard_page_parent_class)->finalize (object);
 }
 
-static void localed_proxy_ready (GObject *source, GAsyncResult *res, gpointer data);
-static void setup_input_section (GisKeyboardPage *self);
-static void update_input (GisKeyboardPage *self);
-
-static gboolean
-gis_keyboard_page_apply (GisPage      *page,
-                         GCancellable *cancellable)
-{
-        update_input (GIS_KEYBOARD_PAGE (page));
-        return FALSE;
-}
-
-static void
-gis_keyboard_page_constructed (GObject *object)
-{
-        GisKeyboardPage *self = GIS_KEYBOARD_PAGE (object);
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-
-        G_OBJECT_CLASS (gis_keyboard_page_parent_class)->constructed (object);
-
-        setup_input_section (self);
-
-        priv->cancellable = g_cancellable_new ();
-
-        g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
-                                  G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
-                                  NULL,
-                                  "org.freedesktop.locale1",
-                                  "/org/freedesktop/locale1",
-                                  "org.freedesktop.locale1",
-                                  priv->cancellable,
-                                  (GAsyncReadyCallback) localed_proxy_ready,
-                                  self);
-
-        /* If we're in new user mode then we're manipulating system settings */
-        if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER)
-                priv->permission = polkit_permission_new_sync ("org.freedesktop.locale1.set-keyboard", NULL, 
NULL, NULL);
-
-        gis_page_set_complete (GIS_PAGE (self), TRUE);
-        gtk_widget_show (GTK_WIDGET (self));
-}
-
-static void
-gis_keyboard_page_locale_changed (GisPage *page)
-{
-        gis_page_set_title (GIS_PAGE (page), _("Typing"));
-}
-
-static void
-gis_keyboard_page_class_init (GisKeyboardPageClass * klass)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS (klass);
-       GisPageClass * page_class = GIS_PAGE_CLASS (klass);
-
-        gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), 
"/org/gnome/initial-setup/gis-keyboard-page.ui");
-
-        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisKeyboardPage, input_list);
-        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisKeyboardPage, 
input_source_add);
-        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisKeyboardPage, 
input_source_remove);
-        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisKeyboardPage, 
show_layout);
-        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisKeyboardPage, 
show_config);
-        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisKeyboardPage, 
scrolled_window);
-
-        page_class->page_id = PAGE_ID;
-        page_class->apply = gis_keyboard_page_apply;
-        page_class->locale_changed = gis_keyboard_page_locale_changed;
-        object_class->constructed = gis_keyboard_page_constructed;
-       object_class->finalize = gis_keyboard_page_finalize;
-}
-
-static void
-update_header_func (GtkListBoxRow  *row,
-                    GtkListBoxRow  *before,
-                    gpointer    user_data)
-{
-  GtkWidget *current;
-
-  if (before == NULL)
-    return;
-
-  current = gtk_list_box_row_get_header (row);
-  if (current == NULL)
-    {
-      current = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
-      gtk_widget_show (current);
-      gtk_list_box_row_set_header (row, current);
-    }
-}
-
-
-static void show_input_chooser (GisKeyboardPage *self);
-static void remove_selected_input (GisKeyboardPage *self);
-
-#ifdef HAVE_IBUS
-static void
-update_ibus_active_sources (GisKeyboardPage *self)
-{
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        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 = gis_keyboard_page_get_instance_private (self);
-        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 = gis_keyboard_page_get_instance_private (self);
-        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 = gis_keyboard_page_get_instance_private (self);
-
-        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 = gis_keyboard_page_get_instance_private (self);
-
-        if (priv->n_input_rows >= MAX_INPUT_ROWS_VISIBLE) {
-                GtkWidget *parent;
-                gint height;
-
-                parent = gtk_widget_get_parent (priv->scrolled_window);
-                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->scrolled_window),
-                                                GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
-        } else {
-                gtk_widget_set_size_request (gtk_widget_get_parent (priv->scrolled_window), -1, -1);
-                gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
-                                                GTK_POLICY_NEVER, GTK_POLICY_NEVER);
-        }
-}
-
-static void
-remove_no_input_row (GtkContainer *list)
-{
-        GList *l;
-
-        l = gtk_container_get_children (list);
-        if (!l)
-                return;
-        if (l->next != NULL)
-                goto out;
-        if (g_strcmp0 (g_object_get_data (G_OBJECT (l->data), "type"), "none") == 0)
-                gtk_container_remove (list, GTK_WIDGET (l->data));
-out:
-        g_list_free (l);
-}
-
-static GtkWidget *
-add_input_row (GisKeyboardPage   *self,
-               const gchar     *type,
-               const gchar     *id,
-               const gchar     *name,
-               GDesktopAppInfo *app_info)
-{
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        GtkWidget *row;
-        GtkWidget *box;
-        GtkWidget *label;
-        GtkWidget *image;
-
-        remove_no_input_row (GTK_CONTAINER (priv->input_list));
-
-        row = gtk_list_box_row_new ();
-        box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
-        gtk_container_add (GTK_CONTAINER (row), box);
-        label = gtk_label_new (name);
-        gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
-        gtk_widget_set_margin_start (label, 20);
-        gtk_widget_set_margin_end (label, 20);
-        gtk_widget_set_margin_top (label, 6);
-        gtk_widget_set_margin_bottom (label, 6);
-        gtk_box_pack_start (GTK_BOX (box), 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_start (image, 20);
-                gtk_widget_set_margin_end (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 (box), image, FALSE, TRUE, 0);
-        }
-
-        gtk_widget_show_all (row);
-        gtk_container_add (GTK_CONTAINER (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_source (GisKeyboardPage *self,
-                  const gchar     *type,
-                  const gchar     *id)
-{
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        const gchar *name;
-        gchar *display_name = NULL;
-        GDesktopAppInfo *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);
-                        return;
-                }
-                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);
-                return;
-        }
-
-        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_no_input_row (GisKeyboardPage *self)
-{
-        add_input_row (self, "none", "none", _("No input source selected"), NULL);
-}
-
-static void
-add_input_sources (GisKeyboardPage *self,
-                   GVariant        *sources)
-{
-        GVariantIter iter;
-        const gchar *type;
-        const gchar *id;
-
-        if (g_variant_n_children (sources) < 1) {
-                add_no_input_row (self);
-                return;
-        }
-
-        g_variant_iter_init (&iter, sources);
-        while (g_variant_iter_next (&iter, "(&s&s)", &type, &id))
-                add_input_source (self, type, id);
-}
-
-static void
-add_input_sources_from_settings (GisKeyboardPage *self)
-{
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        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 = gis_keyboard_page_get_instance_private (self);
-        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)
-                gtk_list_box_select_row (GTK_LIST_BOX (gtk_widget_get_parent (row)), GTK_LIST_BOX_ROW (row));
-}
-
-static void
-select_input (GisKeyboardPage *self,
-              const gchar   *id)
-{
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-
-        gtk_container_foreach (GTK_CONTAINER (priv->input_list),
-                               select_by_id, (gpointer)id);
-}
-
-static void
-input_sources_changed (GSettings     *settings,
-                       const gchar   *key,
-                       GisKeyboardPage *self)
-{
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        GtkListBoxRow *selected;
-        gchar *id = NULL;
-
-        selected = gtk_list_box_get_selected_row (GTK_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)
+set_input_settings (GisKeyboardPage *self)
 {
         GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        GtkListBoxRow *selected;
-        GList *children;
-        gboolean multiple_sources;
-
-        children = gtk_container_get_children (GTK_CONTAINER (priv->input_list));
-        multiple_sources = g_list_next (children) != NULL;
-        g_list_free (children);
+        GVariantBuilder builder;
 
-        selected = gtk_list_box_get_selected_row (GTK_LIST_BOX (priv->input_list));
-        if (selected == NULL) {
-                gtk_widget_set_visible (priv->show_config, FALSE);
-                gtk_widget_set_sensitive (priv->input_source_remove, FALSE);
-                gtk_widget_set_sensitive (priv->show_layout, FALSE);
-        } else {
-                GDesktopAppInfo *app_info;
+        g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)"));
+       g_variant_builder_add (&builder, "(ss)",
+               cc_input_chooser_get_input_type (CC_INPUT_CHOOSER (priv->input_chooser)),
+               cc_input_chooser_get_input_id (CC_INPUT_CHOOSER (priv->input_chooser)));
 
-                app_info = (GDesktopAppInfo *)g_object_get_data (G_OBJECT (selected), "app-info");
+       g_settings_set_value (priv->input_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder));
+       g_settings_set_uint (priv->input_settings, KEY_CURRENT_INPUT_SOURCE, 0);
 
-                gtk_widget_set_visible (priv->show_config, app_info != NULL);
-                gtk_widget_set_sensitive (priv->show_layout, TRUE);
-                gtk_widget_set_sensitive (priv->input_source_remove, multiple_sources);
-        }
+       g_settings_apply (priv->input_settings);
 }
 
 static void
-set_input_settings (GisKeyboardPage *self)
+set_localed_input (GisKeyboardPage *self)
 {
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        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;
+       GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
+       const gchar *layout, *variant;
 
-        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);
+       cc_input_chooser_get_layout (CC_INPUT_CHOOSER (priv->input_chooser), &layout, &variant);
 
-        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);
+        g_dbus_proxy_call (priv->localed,
+                           "SetX11Keyboard",
+                           g_variant_new ("(ssssbb)", layout, "", variant, "", TRUE, TRUE),
+                           G_DBUS_CALL_FLAGS_NONE,
+                           -1, NULL, NULL, NULL);
 }
 
-
-static void set_localed_input (GisKeyboardPage *self);
-
 static void
 change_locale_permission_acquired (GObject      *source,
-                                   GAsyncResult *res,
-                                   gpointer      data)
+                                  GAsyncResult *res,
+                                  gpointer      data)
 {
-  GisKeyboardPage *page = GIS_KEYBOARD_PAGE (data);
-  GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (page);
-  GError *error = NULL;
-  gboolean allowed;
+       GisKeyboardPage *page = GIS_KEYBOARD_PAGE (data);
+       GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (page);
+       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;
-  }
+       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)
-    set_localed_input (GIS_KEYBOARD_PAGE (data));
+       if (allowed)
+               set_localed_input (GIS_KEYBOARD_PAGE (data));
 }
 
-
 static void
 update_input (GisKeyboardPage *self)
 {
-  GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
+       GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
 
-  set_input_settings (self);
+       set_input_settings (self);
 
-  if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) {
-      if (g_permission_get_allowed (priv->permission)) {
-          set_localed_input (self);
-      }
-      else if (g_permission_get_can_acquire (priv->permission)) {
-          g_permission_acquire_async (priv->permission,
-                                      NULL,
-                                      change_locale_permission_acquired,
-                                      self);
-      }
-  }
+       if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) {
+               if (g_permission_get_allowed (priv->permission)) {
+                       set_localed_input (self);
+               } else if (g_permission_get_can_acquire (priv->permission)) {
+                       g_permission_acquire_async (priv->permission,
+                                                   NULL,
+                                                   change_locale_permission_acquired,
+                                                   self);
+               }
+       }
 }
 
 static gboolean
-input_source_already_added (GisKeyboardPage *self,
-                            const gchar   *id)
+gis_keyboard_page_apply (GisPage      *page,
+                         GCancellable *cancellable)
 {
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        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;
+       update_input (GIS_KEYBOARD_PAGE (page));
+        return FALSE;
 }
 
 static void
-input_response (GtkWidget *chooser, gint response_id, gpointer data)
+localed_proxy_ready (GObject      *source,
+                    GAsyncResult *res,
+                    gpointer      data)
 {
        GisKeyboardPage *self = data;
-        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;
-                        }
-
-                        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 = gis_keyboard_page_get_instance_private (self);
-        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)
-{
-        show_input_chooser (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 = gis_keyboard_page_get_instance_private (self);
-        GtkListBoxRow *selected;
-        GtkWidget *sibling;
-
-        selected = gtk_list_box_get_selected_row (GTK_LIST_BOX (priv->input_list));
-        if (selected == NULL)
-                return;
-
-        sibling = find_sibling (GTK_CONTAINER (priv->input_list), GTK_WIDGET (selected));
-        gtk_container_remove (GTK_CONTAINER (priv->input_list), GTK_WIDGET (selected));
-        gtk_list_box_select_row (GTK_LIST_BOX (priv->input_list), GTK_LIST_BOX_ROW (sibling));
-
-        priv->n_input_rows -= 1;
-        adjust_input_list_scrolling (self);
+       GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
+       GDBusProxy *proxy;
+       GError *error = NULL;
 
-        update_buttons (self);
-        update_input (self);
-}
-
-static void
-remove_selected_input (GisKeyboardPage *self)
-{
-        do_remove_selected_input (self);
-}
-
-static void
-show_selected_settings (GisKeyboardPage *self)
-{
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        GtkListBoxRow *selected;
-        GdkAppLaunchContext *ctx;
-        GDesktopAppInfo *app_info;
-        const gchar *id;
-        GError *error = NULL;
-
-        selected = gtk_list_box_get_selected_row (GTK_LIST_BOX (priv->input_list));
-        if (selected == NULL)
-                return;
+       proxy = g_dbus_proxy_new_finish (res, &error);
 
-        app_info = (GDesktopAppInfo *)g_object_get_data (G_OBJECT (selected), "app-info");
-        if  (app_info == NULL)
-                return;
+       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;
+       }
 
-        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);
+       priv->localed = proxy;
 }
 
 static void
-show_selected_layout (GisKeyboardPage *self)
-{
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        GtkListBoxRow *selected;
-        const gchar *type;
-        const gchar *id;
-        const gchar *layout;
-        const gchar *variant;
-        gchar *commandline;
-
-        selected = gtk_list_box_get_selected_row (GTK_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
-add_default_input_source_for_locale (GisKeyboardPage *self)
-{
-        const gchar *locale;
-        const gchar *type;
-        const gchar *id;
-
-        locale = gis_driver_get_user_language (GIS_PAGE (self)->driver);
-
-        if (!gnome_get_input_source_from_locale (locale, &type, &id))
-                return;
-
-        add_input_source (self, type, id);
-}
-
-static void
-setup_input_section (GisKeyboardPage *self)
-{
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-
-        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
-
-        g_signal_connect_swapped (priv->input_source_add, "clicked",
-                                  G_CALLBACK (add_input), self);
-        g_signal_connect_swapped (priv->input_source_remove, "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);
-
-        gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->input_list),
-                                         GTK_SELECTION_SINGLE);
-        gtk_list_box_set_header_func (GTK_LIST_BOX (priv->input_list),
-                                      update_header_func,
-                                      NULL, NULL);
-        g_signal_connect_swapped (priv->input_list, "row-selected",
-                                  G_CALLBACK (update_buttons), self);
-
-        g_signal_connect (priv->input_settings, "changed::" KEY_INPUT_SOURCES,
-                          G_CALLBACK (input_sources_changed), self);
-
-        add_default_input_source_for_locale (self);
-        update_buttons (self);
-}
-
-static void
-add_input_sources_from_localed (GisKeyboardPage *self)
+gis_keyboard_page_constructed (GObject *object)
 {
+        GisKeyboardPage *self = GIS_KEYBOARD_PAGE (object);
         GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        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);
-        }
+       g_type_ensure (CC_TYPE_INPUT_CHOOSER);
 
-        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;
+        G_OBJECT_CLASS (gis_keyboard_page_parent_class)->constructed (object);
 
-        for (i = 0; i < n && layouts[i][0]; i++) {
-                const gchar *name;
-                gchar *id;
+       priv->input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR);
+       g_settings_delay (priv->input_settings);
 
-                if (variants && variants[i] && variants[i][0])
-                        id = g_strdup_printf ("%s+%s", layouts[i], variants[i]);
-                else
-                        id = g_strdup (layouts[i]);
+       priv->cancellable = g_cancellable_new ();
 
-                if (!input_source_already_added (self, id)) {
-                        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_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+                                 G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
+                                 NULL,
+                                 "org.freedesktop.locale1",
+                                 "/org/freedesktop/locale1",
+                                 "org.freedesktop.locale1",
+                                 priv->cancellable,
+                                 (GAsyncReadyCallback) localed_proxy_ready,
+                                 self);
 
-                g_free (id);
-        }
+       /* If we're in new user mode then we're manipulating system settings */
+       if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER)
+               priv->permission = polkit_permission_new_sync ("org.freedesktop.locale1.set-keyboard", NULL, 
NULL, NULL);
 
-        g_strfreev (variants);
-        g_strfreev (layouts);
+        gis_page_set_complete (GIS_PAGE (self), TRUE);
+        gtk_widget_show (GTK_WIDGET (self));
 }
 
 static void
-set_localed_input (GisKeyboardPage *self)
+gis_keyboard_page_locale_changed (GisPage *page)
 {
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        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);
+        gis_page_set_title (GIS_PAGE (page), _("Typing"));
 }
 
 static void
-localed_proxy_ready (GObject      *source,
-                     GAsyncResult *res,
-                     gpointer      data)
+gis_keyboard_page_class_init (GisKeyboardPageClass * klass)
 {
-        GisKeyboardPage *self = data;
-        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
-        GDBusProxy *proxy;
-        GError *error = NULL;
-
-        proxy = g_dbus_proxy_new_finish (res, &error);
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GisPageClass * page_class = GIS_PAGE_CLASS (klass);
 
-        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;
-        }
+        gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), 
"/org/gnome/initial-setup/gis-keyboard-page.ui");
 
-        priv->localed = proxy;
+        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisKeyboardPage, 
input_chooser);
 
-        add_input_sources_from_localed (self);
-        update_buttons (self);
+        page_class->page_id = PAGE_ID;
+        page_class->apply = gis_keyboard_page_apply;
+        page_class->locale_changed = gis_keyboard_page_locale_changed;
+        object_class->constructed = gis_keyboard_page_constructed;
+       object_class->finalize = gis_keyboard_page_finalize;
 }
 
 static void
 gis_keyboard_page_init (GisKeyboardPage *self)
 {
         g_resources_register (keyboard_get_resource ());
+       g_type_ensure (CC_TYPE_INPUT_CHOOSER);
 
         gtk_widget_init_template (GTK_WIDGET (self));
 }
diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui 
b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
index 046c336..994af6e 100644
--- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
+++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
@@ -5,233 +5,54 @@
       <object class="GtkBox" id="page">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
+       <property name="halign">center</property>
+        <property name="valign">fill</property>
         <property name="orientation">vertical</property>
         <child>
-          <object class="GtkBox" id="input_section">
+          <object class="GtkImage" id="image1">
             <property name="visible">True</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="GtkBox" id="input_heading_row">
-                <property name="visible">True</property>
-                <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">Add keyboard layouts or other input 
methods</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>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">True</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkFrame" id="input_frame">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">in</property>
-                <child>
-                  <object class="GtkScrolledWindow" id="scrolled_window">
-                    <property name="visible">True</property>
-                    <property name="hscrollbar-policy">never</property>
-                    <property name="vscrollbar-policy">never</property>
-                    <child>
-                      <object class="GtkViewport" id="input_viewport">
-                        <property name="visible">True</property>
-                        <child>
-                          <object class="GtkListBox" id="input_list">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="expand">True</property>
-                <property name="fill">True</property>
-                <property name="position">1</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="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <child>
-                      <object class="GtkBox" id="i_s_ar_box">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <child>
-                          <object class="GtkButton" id="input_source_add">
-                            <property name="visible">True</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="GtkButton" id="input_source_remove">
-                            <property name="visible">True</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>
-                    </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>
-                <child>
-                  <object class="GtkToolItem" id="i_s_sc_item">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <child>
-                      <object class="GtkBox" id="i_s_sc_box">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <child>
-                          <object class="GtkButton" id="show_config">
-                            <property name="visible">True</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>
-                    </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="GtkBox" id="i_s_sl_box">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <child>
-                          <object class="GtkButton" id="show_layout">
-                            <property name="visible">True</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>
-                    </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>
+            <property name="margin_top">54</property>
+            <property name="pixel_size">96</property>
+            <property name="icon_name">input-keyboard-symbolic</property>
+            <style>
+              <class name="dim-label" />
+            </style>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel" id="title">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_top">26</property>
+            <property name="halign">center</property>
+            <property name="valign">start</property>
+            <property name="label" translatable="yes">Typing</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+              <attribute name="scale" value="1.8"/>
+            </attributes>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel" id="subtitle">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_top">14</property>
+            <property name="label" translatable="yes">Select your keyboard layout or an input 
method.</property>
+            <property name="justify">center</property>
+            <property name="wrap">True</property>
+            <property name="max-width-chars">50</property>
+          </object>
+        </child>
+        <child>
+          <object class="CcInputChooser" id="input_chooser">
+            <property name="margin_top">26</property>
+            <property name="margin_bottom">32</property>
+            <property name="visible">True</property>
+            <property name="halign">center</property>
+            <property name="valign">start</property>
           </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-            <property name="position">1</property>
-          </packing>
         </child>
       </object>
     </child>
diff --git a/gnome-initial-setup/pages/keyboard/input-chooser.ui 
b/gnome-initial-setup/pages/keyboard/input-chooser.ui
index 00ed75a..01b2bb0 100644
--- a/gnome-initial-setup/pages/keyboard/input-chooser.ui
+++ b/gnome-initial-setup/pages/keyboard/input-chooser.ui
@@ -1,81 +1,37 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0"?>
 <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">
+  <requires lib="gtk+" version="3.0"/>
+  <template class="CcInputChooser" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <property name="spacing">10</property>
+    <child>
+      <object class="GtkScrolledWindow" id="scrolled_window">
         <property name="visible">True</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">0</property>
+        <property name="hscrollbar-policy">never</property>
+        <property name="vscrollbar-policy">never</property>
+        <property name="shadow-type">in</property>
         <child>
-          <object class="GtkScrolledWindow" id="scrolledwindow">
+          <object class="GtkViewport" id="viewport">
             <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">
+              <object class="GtkListBox" id="input_list">
                 <property name="visible">True</property>
-                <child>
-                  <object class="GtkListBox" 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>
+                <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>
-    <action-widgets>
-      <action-widget response="-5">add-button</action-widget>
-      <action-widget response="-6">cancel-button</action-widget>
-    </action-widgets>
-  </object>
+    <child>
+      <object class="GtkSearchEntry" id="filter_entry">
+        <property name="visible">False</property>
+        <property name="hexpand">True</property>
+      </object>
+    </child>
+  </template>
 </interface>



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