[gnome-initial-setup] Add a region chooser



commit 18cac64c2eca8189779873d8979980a0cf64da67
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Dec 6 09:45:29 2013 -0500

    Add a region chooser
    
    Now that the language chooser only shows languages, we add
    a separate step to select a region for given language. Under
    the covers, both steps select a complete locale. The language
    page just selects the first locale with a given locale. The
    region page shows all the locales with the same language.
    
    If there is only one region, we skip this page altogether.

 configure.ac                                       |    1 +
 gnome-initial-setup/Makefile.am                    |    1 +
 gnome-initial-setup/gnome-initial-setup.c          |    2 +
 gnome-initial-setup/pages/Makefile.am              |    1 +
 gnome-initial-setup/pages/region/Makefile.am       |   30 +
 .../pages/region/cc-common-language.c              |  326 ++++++++++
 .../pages/region/cc-common-language.h              |   36 +
 .../pages/region/cc-region-chooser.c               |  671 ++++++++++++++++++++
 .../pages/region/cc-region-chooser.h               |   64 ++
 gnome-initial-setup/pages/region/gis-region-page.c |  279 ++++++++
 gnome-initial-setup/pages/region/gis-region-page.h |   59 ++
 .../pages/region/gis-region-page.ui                |   62 ++
 gnome-initial-setup/pages/region/region-chooser.ui |   37 ++
 .../pages/region/region.gresource.xml              |    9 +
 14 files changed, 1578 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 9ed91df..6d6eab0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -101,6 +101,7 @@ data/Makefile
 gnome-initial-setup/Makefile
 gnome-initial-setup/pages/Makefile
 gnome-initial-setup/pages/language/Makefile
+gnome-initial-setup/pages/region/Makefile
 gnome-initial-setup/pages/keyboard/Makefile
 gnome-initial-setup/pages/eulas/Makefile
 gnome-initial-setup/pages/network/Makefile
diff --git a/gnome-initial-setup/Makefile.am b/gnome-initial-setup/Makefile.am
index 478f7f5..34a3da9 100644
--- a/gnome-initial-setup/Makefile.am
+++ b/gnome-initial-setup/Makefile.am
@@ -29,6 +29,7 @@ gnome_initial_setup_SOURCES = \
 
 gnome_initial_setup_LDADD =    \
        pages/language/libgislanguage.la \
+       pages/region/libgisregion.la \
        pages/keyboard/libgiskeyboard.la \
        pages/eulas/libgiseulas.la \
        pages/network/libgisnetwork.la \
diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c
index c0ae5a7..33f7968 100644
--- a/gnome-initial-setup/gnome-initial-setup.c
+++ b/gnome-initial-setup/gnome-initial-setup.c
@@ -39,6 +39,7 @@
 #endif
 
 #include "pages/language/gis-language-page.h"
+#include "pages/region/gis-region-page.h"
 #include "pages/keyboard/gis-keyboard-page.h"
 #include "pages/eulas/gis-eula-pages.h"
 #include "pages/network/gis-network-page.h"
@@ -61,6 +62,7 @@ typedef struct {
 
 static PageData page_table[] = {
   PAGE (language, FALSE),
+  PAGE (region,   FALSE),
   PAGE (keyboard, FALSE),
   PAGE (eula,     FALSE),
   PAGE (network,  FALSE),
diff --git a/gnome-initial-setup/pages/Makefile.am b/gnome-initial-setup/pages/Makefile.am
index c52def6..9292b05 100644
--- a/gnome-initial-setup/pages/Makefile.am
+++ b/gnome-initial-setup/pages/Makefile.am
@@ -1,6 +1,7 @@
 
 SUBDIRS = \
        language \
+       region \
        keyboard \
        eulas \
        network \
diff --git a/gnome-initial-setup/pages/region/Makefile.am b/gnome-initial-setup/pages/region/Makefile.am
new file mode 100644
index 0000000..95a971c
--- /dev/null
+++ b/gnome-initial-setup/pages/region/Makefile.am
@@ -0,0 +1,30 @@
+
+noinst_LTLIBRARIES = libgisregion.la
+
+AM_CPPFLAGS = \
+       -I"$(top_srcdir)" \
+       -I"$(top_srcdir)/gnome-initial-setup" \
+       -I"$(top_builddir)" \
+       -DDATADIR=\""$(datadir)"\" \
+       -DGNOMELOCALEDIR=\""$(datadir)/locale"\"
+
+BUILT_SOURCES =
+
+resource_files = $(shell glib-compile-resources --sourcedir=$(srcdir) --generate-dependencies 
$(srcdir)/region.gresource.xml)
+region-resources.c: region.gresource.xml $(resource_files)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-source $<
+region-resources.h: region.gresource.xml $(resource_files)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-header $<
+BUILT_SOURCES += region-resources.c region-resources.h
+
+libgisregion_la_SOURCES =                              \
+       cc-common-language.c cc-common-language.h       \
+       cc-region-chooser.c cc-region-chooser.h \
+       gis-region-page.c gis-region-page.h             \
+       $(BUILT_SOURCES)
+
+libgisregion_la_CFLAGS = $(INITIAL_SETUP_CFLAGS)
+libgisregion_la_LIBADD = $(INITIAL_SETUP_LIBS)
+libgisregion_la_LDFLAGS = -export_dynamic -avoid-version -module -no-undefined
+
+EXTRA_DIST = region.gresource.xml $(resource_files)
diff --git a/gnome-initial-setup/pages/region/cc-common-language.c 
b/gnome-initial-setup/pages/region/cc-common-language.c
new file mode 100644
index 0000000..04f0c84
--- /dev/null
+++ b/gnome-initial-setup/pages/region/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/region/cc-common-language.h 
b/gnome-initial-setup/pages/region/cc-common-language.h
new file mode 100644
index 0000000..7f32cab
--- /dev/null
+++ b/gnome-initial-setup/pages/region/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/region/cc-region-chooser.c 
b/gnome-initial-setup/pages/region/cc-region-chooser.c
new file mode 100644
index 0000000..3b98847
--- /dev/null
+++ b/gnome-initial-setup/pages/region/cc-region-chooser.c
@@ -0,0 +1,671 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Red Hat
+ *
+ * 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:
+ *     Jasper St. Pierre <jstpierre mecheye net>
+ *     Matthias Clasen <mclasen redhat com>
+ */
+
+#include "config.h"
+#include "cc-region-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 "cc-common-language.h"
+
+#include <glib-object.h>
+
+#define MIN_ROWS 6
+
+struct _CcRegionChooserPrivate
+{
+        GtkWidget *filter_entry;
+        GtkWidget *region_list;
+
+        GtkWidget *scrolled_window;
+        GtkWidget *no_results;
+        GtkWidget *more_item;
+
+       GHashTable *regions;
+
+        gboolean showing_extra;
+        gchar *locale;
+       gchar *lang;
+};
+typedef struct _CcRegionChooserPrivate CcRegionChooserPrivate;
+G_DEFINE_TYPE_WITH_PRIVATE (CcRegionChooser, cc_region_chooser, GTK_TYPE_BOX);
+
+enum {
+        PROP_0,
+        PROP_LOCALE,
+        PROP_SHOWING_EXTRA,
+        PROP_LAST,
+};
+
+static GParamSpec *obj_props[PROP_LAST];
+
+typedef struct {
+        GtkWidget *box;
+        GtkWidget *checkmark;
+
+        gchar *locale_id;
+        gchar *locale_name;
+        gchar *locale_current_name;
+        gchar *locale_untranslated_name;
+        gboolean is_extra;
+} RegionWidget;
+
+static RegionWidget *
+get_region_widget (GtkWidget *widget)
+{
+        return g_object_get_data (G_OBJECT (widget), "region-widget");
+}
+
+static GtkWidget *
+padded_label_new (char *text)
+{
+        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 void
+region_widget_free (gpointer data)
+{
+        RegionWidget *widget = data;
+
+        /* This is called when the box is destroyed,
+         * so don't bother destroying the widget and
+         * children again. */
+        g_free (widget->locale_id);
+        g_free (widget->locale_name);
+        g_free (widget->locale_current_name);
+        g_free (widget->locale_untranslated_name);
+        g_free (widget);
+}
+
+static char *
+get_country_name (const char *locale,
+                 const char *translation)
+{
+       char *name;
+       char *p;
+
+        name = gnome_get_country_from_locale (locale, translation);
+       p = strstr (name, " (");
+       if (p)
+               *p = '\0';
+
+       return name;
+}
+
+static GtkWidget *
+region_widget_new (const char *locale_id,
+                  const char *region,
+                   gboolean    is_extra)
+{
+        gchar *locale_name, *locale_current_name, *locale_untranslated_name;
+        GtkWidget *checkmark;
+       GtkWidget *label;
+        RegionWidget *widget = g_new0 (RegionWidget, 1);
+
+        locale_name = get_country_name (locale_id, locale_id);
+        locale_current_name = get_country_name (locale_id, NULL);
+        locale_untranslated_name = get_country_name (locale_id, "C");
+
+        widget->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+        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);
+        gtk_widget_set_halign (widget->box, GTK_ALIGN_FILL);
+       label = gtk_label_new (locale_name);
+       gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+       gtk_label_set_width_chars (GTK_LABEL (label), 40);
+        gtk_box_pack_start (GTK_BOX (widget->box), label, FALSE, FALSE, 0);
+        widget->locale_id = g_strdup (locale_id);
+        widget->locale_name = locale_name;
+        widget->locale_current_name = locale_current_name;
+        widget->locale_untranslated_name = locale_untranslated_name;
+        widget->is_extra = is_extra;
+
+        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_halign (widget->checkmark, GTK_ALIGN_END);
+        gtk_widget_show_all (widget->box);
+
+        g_object_set_data_full (G_OBJECT (widget->box), "region-widget", widget,
+                                region_widget_free);
+
+        return widget->box;
+}
+
+static void
+sync_checkmark (GtkWidget *row,
+                gpointer   user_data)
+{
+        GtkWidget *child;
+        RegionWidget *widget;
+        gchar *locale_id;
+        gboolean should_be_visible;
+
+        child = gtk_bin_get_child (GTK_BIN (row));
+        widget = get_region_widget (child);
+
+        if (widget == NULL)
+                return;
+
+        locale_id = user_data;
+        should_be_visible = g_str_equal (widget->locale_id, locale_id);
+        gtk_widget_set_opacity (widget->checkmark, should_be_visible ? 1.0 : 0.0);
+}
+
+static void
+sync_all_checkmarks (CcRegionChooser *chooser)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+
+        gtk_container_foreach (GTK_CONTAINER (priv->region_list),
+                               sync_checkmark, priv->locale);
+}
+
+static GtkWidget *
+more_widget_new (void)
+{
+        GtkWidget *widget;
+        GtkWidget *arrow;
+
+        widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+        gtk_widget_set_tooltip_text (widget, _("Moreā€¦"));
+
+        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);
+
+        return widget;
+}
+
+static GtkWidget *
+no_results_widget_new (void)
+{
+        GtkWidget *widget;
+
+        widget = padded_label_new (_("No regions found"));
+        gtk_widget_set_sensitive (widget, FALSE);
+        return widget;
+}
+
+static void
+add_one_region (CcRegionChooser *chooser,
+                const char        *locale_id)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+       GtkWidget *widget;
+       gchar *lang = NULL;
+       gchar *region = NULL;
+       gboolean is_extra;
+       
+       if (!g_str_has_suffix (locale_id, "utf8")) {
+               return;
+       }
+
+       if (!cc_common_language_has_font (locale_id)) {
+               return;
+       }
+
+       if (!gnome_parse_locale (locale_id, &lang, &region, NULL, NULL)) {
+               goto out;
+       }
+
+       if (g_strcmp0 (priv->lang, lang) != 0) {
+               goto out;
+       }
+
+       if (region == NULL) {
+               goto out;
+       }
+
+       if (g_hash_table_contains (priv->regions, region)) {
+               goto out;
+       }
+       g_hash_table_add (priv->regions, g_strdup (region));
+       if (g_hash_table_size (priv->regions) > MIN_ROWS)
+               is_extra = TRUE;
+       else
+               is_extra = FALSE;
+
+       widget = region_widget_new (locale_id, region, is_extra);
+       gtk_container_add (GTK_CONTAINER (priv->region_list), widget);
+
+out:
+       g_free (lang);
+       g_free (region);
+}
+
+static void
+add_regions (CcRegionChooser  *chooser,
+               char               **locale_ids,
+               GHashTable          *initial)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+       GHashTableIter iter;
+       gchar *key;
+
+       add_one_region (chooser, priv->locale);
+
+       g_hash_table_iter_init (&iter, initial);
+       while (g_hash_table_iter_next (&iter, (gpointer *)&key, NULL)) {
+               add_one_region (chooser, key);
+       }
+
+        while (*locale_ids) {
+                const gchar *locale_id;
+
+                locale_id = *locale_ids;
+                locale_ids ++;
+
+               add_one_region (chooser, locale_id);
+        }
+
+        gtk_container_add (GTK_CONTAINER (priv->region_list), priv->more_item);
+
+        gtk_widget_show_all (priv->region_list);
+}
+
+static void
+add_all_regions (CcRegionChooser *chooser)
+{
+        char **locale_ids;
+        GHashTable *initial;
+
+        locale_ids = gnome_get_all_locales ();
+        initial = cc_common_language_get_initial_languages ();
+        add_regions (chooser, locale_ids, initial);
+        g_hash_table_destroy (initial);
+        g_strfreev (locale_ids);
+}
+
+static gboolean
+region_visible (GtkListBoxRow *row,
+                  gpointer       user_data)
+{
+        CcRegionChooser *chooser = user_data;
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+        RegionWidget *widget;
+        gboolean visible;
+        GtkWidget *child;
+        const char *search_term;
+
+        child = gtk_bin_get_child (GTK_BIN (row));
+        if (child == priv->more_item)
+                return !priv->showing_extra && g_hash_table_size (priv->regions) > MIN_ROWS;
+
+        widget = get_region_widget (child);
+
+        if (!priv->showing_extra && widget->is_extra)
+                return FALSE;
+
+        search_term = gtk_entry_get_text (GTK_ENTRY (priv->filter_entry));
+        if (!search_term || !*search_term)
+                return TRUE;
+
+        visible = FALSE;
+
+        visible = g_str_match_string (search_term, widget->locale_name, TRUE);
+        if (visible)
+                goto out;
+
+        visible = g_str_match_string (search_term, widget->locale_current_name, TRUE);
+        if (visible)
+                goto out;
+
+        visible = g_str_match_string (search_term, widget->locale_untranslated_name, TRUE);
+        if (visible)
+                goto out;
+
+ out:
+        return visible;
+}
+
+static gint
+sort_regions (GtkListBoxRow *a,
+                GtkListBoxRow *b,
+                gpointer       data)
+{
+        RegionWidget *la, *lb;
+
+        la = get_region_widget (gtk_bin_get_child (GTK_BIN (a)));
+        lb = get_region_widget (gtk_bin_get_child (GTK_BIN (b)));
+
+        if (la == NULL)
+                return 1;
+
+        if (lb == NULL)
+                return -1;
+
+        if (la->is_extra && !lb->is_extra)
+                return 1;
+
+        if (!la->is_extra && lb->is_extra)
+                return -1;
+
+        return strcmp (la->locale_name, lb->locale_name);
+}
+
+static void
+filter_changed (GtkEntry        *entry,
+                CcRegionChooser *chooser)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+        gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->region_list));
+}
+
+static void
+show_more (CcRegionChooser *chooser)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+       if (g_hash_table_size (priv->regions) <= MIN_ROWS)
+               return;
+
+        gtk_widget_show (priv->filter_entry);
+        gtk_widget_grab_focus (priv->filter_entry);
+
+       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);
+
+        priv->showing_extra = TRUE;
+        gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->region_list));
+        g_object_notify_by_pspec (G_OBJECT (chooser), obj_props[PROP_SHOWING_EXTRA]);
+}
+
+static void
+show_less (CcRegionChooser *chooser)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+
+        gtk_widget_hide (priv->filter_entry);
+
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
+                                       GTK_POLICY_NEVER,
+                                       GTK_POLICY_NEVER);
+       gtk_widget_set_valign (GTK_WIDGET (chooser), GTK_ALIGN_START);
+
+        priv->showing_extra = FALSE;
+        gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->region_list));
+        g_object_notify_by_pspec (G_OBJECT (chooser), obj_props[PROP_SHOWING_EXTRA]);
+}
+
+static void
+remove_regions (CcRegionChooser *chooser)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+       GtkContainer *container;
+       GList *children, *l;
+
+       container = GTK_CONTAINER (priv->region_list);
+
+       children = gtk_container_get_children (container);
+       for (l = children; l; l = l->next) {
+               if (l->data != gtk_widget_get_parent (priv->more_item))
+                       gtk_container_remove (container, l->data);
+       }
+       g_list_free (children);
+
+       g_hash_table_remove_all (priv->regions);
+}
+
+static void
+set_locale_id (CcRegionChooser *chooser,
+               const gchar       *new_locale_id)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+       gchar *new_lang;
+
+        if (g_strcmp0 (priv->locale, new_locale_id) == 0)
+                return;
+
+        g_free (priv->locale);
+        priv->locale = g_strdup (new_locale_id);
+
+       gnome_parse_locale (new_locale_id, &new_lang, NULL, NULL, NULL);
+       if (g_strcmp0 (priv->lang, new_lang) != 0) {
+               g_free (priv->lang);
+               priv->lang = g_strdup (new_lang);
+
+               remove_regions (chooser);
+               show_less (chooser);
+               add_all_regions (chooser);
+       }
+       g_free (new_lang);
+
+        sync_all_checkmarks (chooser);
+
+        g_object_notify_by_pspec (G_OBJECT (chooser), obj_props[PROP_LOCALE]);
+}
+
+static void
+row_activated (GtkListBox        *box,
+               GtkListBoxRow     *row,
+               CcRegionChooser *chooser)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+        GtkWidget *child;
+        RegionWidget *widget;
+
+        if (row == NULL)
+                return;
+
+        child = gtk_bin_get_child (GTK_BIN (row));
+        if (child == priv->more_item) {
+                show_more (chooser);
+        } else {
+                widget = get_region_widget (child);
+                if (widget == NULL)
+                        return;
+                set_locale_id (chooser, widget->locale_id);
+        }
+}
+
+static void
+update_header_func (GtkListBoxRow *child,
+                    GtkListBoxRow *before,
+                    gpointer       user_data)
+{
+        GtkWidget *header;
+
+        if (before == NULL)
+                return;
+
+        header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+        gtk_list_box_row_set_header (child, header);
+        gtk_widget_show (header);
+}
+
+static void
+cc_region_chooser_constructed (GObject *object)
+{
+        CcRegionChooser *chooser = CC_REGION_CHOOSER (object);
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+
+        G_OBJECT_CLASS (cc_region_chooser_parent_class)->constructed (object);
+
+        priv->more_item = more_widget_new ();
+        priv->no_results = no_results_widget_new ();
+
+       priv->regions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+        gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->region_list),
+                                    sort_regions, chooser, NULL);
+        gtk_list_box_set_filter_func (GTK_LIST_BOX (priv->region_list),
+                                      region_visible, chooser, NULL);
+        gtk_list_box_set_header_func (GTK_LIST_BOX (priv->region_list),
+                                      update_header_func, chooser, NULL);
+        gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->region_list),
+                                         GTK_SELECTION_NONE);
+
+        gtk_list_box_set_placeholder (GTK_LIST_BOX (priv->region_list), priv->no_results);
+
+        if (priv->locale == NULL) {
+               priv->locale = cc_common_language_get_current_language ();
+               gnome_parse_locale (priv->locale, &priv->lang, NULL, NULL, NULL);
+       }
+
+       add_all_regions (chooser);
+
+        g_signal_connect (priv->filter_entry, "changed",
+                          G_CALLBACK (filter_changed),
+                          chooser);
+
+        g_signal_connect (priv->region_list, "row-activated",
+                          G_CALLBACK (row_activated), chooser);
+
+        sync_all_checkmarks (chooser);
+}
+
+static void
+cc_region_chooser_finalize (GObject *object)
+{
+       CcRegionChooser *chooser = CC_REGION_CHOOSER (object);
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+
+       g_hash_table_unref (priv->regions);
+
+       G_OBJECT_CLASS (cc_region_chooser_parent_class)->finalize (object);
+}
+
+static void
+cc_region_chooser_get_property (GObject      *object,
+                                  guint         prop_id,
+                                  GValue       *value,
+                                  GParamSpec   *pspec)
+{
+        CcRegionChooser *chooser = CC_REGION_CHOOSER (object);
+        switch (prop_id) {
+        case PROP_LOCALE:
+                g_value_set_string (value, cc_region_chooser_get_locale (chooser));
+                break;
+        case PROP_SHOWING_EXTRA:
+                g_value_set_boolean (value, cc_region_chooser_get_showing_extra (chooser));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+cc_region_chooser_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+        CcRegionChooser *chooser = CC_REGION_CHOOSER (object);
+        switch (prop_id) {
+        case PROP_LOCALE:
+                cc_region_chooser_set_locale (chooser, g_value_get_string (value));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+cc_region_chooser_class_init (CcRegionChooserClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), 
"/org/gnome/control-center/region-chooser.ui");
+
+        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), CcRegionChooser, 
filter_entry);
+        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), CcRegionChooser, 
region_list);
+        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), CcRegionChooser, 
scrolled_window);
+
+       object_class->finalize = cc_region_chooser_finalize;
+        object_class->get_property = cc_region_chooser_get_property;
+        object_class->set_property = cc_region_chooser_set_property;
+        object_class->constructed = cc_region_chooser_constructed;
+
+        obj_props[PROP_LOCALE] =
+                g_param_spec_string ("locale", "", "", "",
+                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+        obj_props[PROP_SHOWING_EXTRA] =
+                g_param_spec_string ("showing-extra", "", "", "",
+                                     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+        g_object_class_install_properties (object_class, PROP_LAST, obj_props);
+}
+
+static void
+cc_region_chooser_init (CcRegionChooser *chooser)
+{
+        gtk_widget_init_template (GTK_WIDGET (chooser));
+}
+
+void
+cc_region_chooser_clear_filter (CcRegionChooser *chooser)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+        gtk_entry_set_text (GTK_ENTRY (priv->filter_entry), "");
+}
+
+const gchar *
+cc_region_chooser_get_locale (CcRegionChooser *chooser)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+        return priv->locale;
+}
+
+void
+cc_region_chooser_set_locale (CcRegionChooser *chooser,
+                              const gchar     *locale)
+{
+        set_locale_id (chooser, locale);
+}
+
+gboolean
+cc_region_chooser_get_showing_extra (CcRegionChooser *chooser)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+        return priv->showing_extra;
+}
+
+gint
+cc_region_chooser_get_n_regions (CcRegionChooser *chooser)
+{
+        CcRegionChooserPrivate *priv = cc_region_chooser_get_instance_private (chooser);
+       return g_hash_table_size (priv->regions);
+}
diff --git a/gnome-initial-setup/pages/region/cc-region-chooser.h 
b/gnome-initial-setup/pages/region/cc-region-chooser.h
new file mode 100644
index 0000000..62545fb
--- /dev/null
+++ b/gnome-initial-setup/pages/region/cc-region-chooser.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Red Hat
+ *
+ * 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:
+ *     Jasper St. Pierre <jstpierre mecheye net>
+ *     Matthias Clasen <mclasen redhat com>
+ */
+
+#ifndef __CC_REGION_CHOOSER_H__
+#define __CC_REGION_CHOOSER_H__
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+#define CC_TYPE_REGION_CHOOSER            (cc_region_chooser_get_type ())
+#define CC_REGION_CHOOSER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_REGION_CHOOSER, 
CcRegionChooser))
+#define CC_REGION_CHOOSER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  CC_TYPE_REGION_CHOOSER, 
CcRegionChooserClass))
+#define CC_IS_REGION_CHOOSER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_REGION_CHOOSER))
+#define CC_IS_REGION_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  CC_TYPE_REGION_CHOOSER))
+#define CC_REGION_CHOOSER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  CC_TYPE_REGION_CHOOSER, 
CcRegionChooserClass))
+
+G_BEGIN_DECLS
+
+typedef struct _CcRegionChooser        CcRegionChooser;
+typedef struct _CcRegionChooserClass   CcRegionChooserClass;
+
+struct _CcRegionChooser
+{
+        GtkBox parent;
+};
+
+struct _CcRegionChooserClass
+{
+        GtkBoxClass parent_class;
+};
+
+GType cc_region_chooser_get_type (void);
+
+void          cc_region_chooser_clear_filter (CcRegionChooser *chooser);
+const gchar * cc_region_chooser_get_locale   (CcRegionChooser *chooser);
+void          cc_region_chooser_set_locale   (CcRegionChooser *chooser,
+                                              const gchar     *locale);
+gboolean      cc_region_chooser_get_showing_extra (CcRegionChooser *chooser);
+gint          cc_region_chooser_get_n_regions (CcRegionChooser *chooser);
+
+G_END_DECLS
+
+#endif /* __CC_REGION_CHOOSER_H__ */
diff --git a/gnome-initial-setup/pages/region/gis-region-page.c 
b/gnome-initial-setup/pages/region/gis-region-page.c
new file mode 100644
index 0000000..bd6ea53
--- /dev/null
+++ b/gnome-initial-setup/pages/region/gis-region-page.c
@@ -0,0 +1,279 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2013 Red Hat
+ *
+ * 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>
+ *
+ * Based on gnome-control-center cc-region-panel.c
+ */
+
+/* Region page {{{1 */
+
+#define PAGE_ID "region"
+
+#include "config.h"
+#include "region-resources.h"
+#include "cc-region-chooser.h"
+#include "cc-common-language.h"
+#include "gis-region-page.h"
+
+#include <act/act-user-manager.h>
+#include <polkit/polkit.h>
+#include <locale.h>
+#include <gtk/gtk.h>
+
+struct _GisRegionPagePrivate
+{
+  GtkWidget *region_chooser;
+
+  GDBusProxy *localed;
+  GPermission *permission;
+  const gchar *new_locale_id;
+  gboolean updating;
+
+  GCancellable *cancellable;
+};
+typedef struct _GisRegionPagePrivate GisRegionPagePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GisRegionPage, gis_region_page, GIS_TYPE_PAGE);
+
+static void
+set_localed_locale (GisRegionPage *self)
+{
+  GisRegionPagePrivate *priv = gis_region_page_get_instance_private (self);
+  GVariantBuilder *b;
+  gchar *s;
+
+  b = g_variant_builder_new (G_VARIANT_TYPE ("as"));
+  s = g_strconcat ("LANG=", priv->new_locale_id, NULL);
+  g_variant_builder_add (b, "s", s);
+  g_free (s);
+
+  g_dbus_proxy_call (priv->localed,
+                     "SetLocale",
+                     g_variant_new ("(asb)", b, TRUE),
+                     G_DBUS_CALL_FLAGS_NONE,
+                     -1, NULL, NULL, NULL);
+  g_variant_builder_unref (b);
+}
+
+static void
+change_locale_permission_acquired (GObject      *source,
+                                   GAsyncResult *res,
+                                   gpointer      data)
+{
+  GisRegionPage *page = GIS_REGION_PAGE (data);
+  GisRegionPagePrivate *priv = gis_region_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;
+  }
+
+  if (allowed)
+    set_localed_locale (page);
+}
+
+static void
+user_loaded (GObject    *object,
+             GParamSpec *pspec,
+             gpointer    user_data)
+{
+  gchar *new_locale_id = user_data;
+
+  act_user_set_language (ACT_USER (object), new_locale_id);
+
+  g_free (new_locale_id);
+}
+
+static void
+region_changed (CcRegionChooser  *chooser,
+                  GParamSpec         *pspec,
+                  GisRegionPage    *page)
+{
+  GisRegionPagePrivate *priv = gis_region_page_get_instance_private (page);
+  ActUser *user;
+  GisDriver *driver;
+
+  if (priv->updating)
+    return;
+
+  priv->new_locale_id = cc_region_chooser_get_locale (chooser);
+  driver = GIS_PAGE (page)->driver;
+
+  setlocale (LC_MESSAGES, priv->new_locale_id);
+  gis_driver_locale_changed (driver);
+
+  if (gis_driver_get_mode (driver) == GIS_DRIVER_MODE_NEW_USER) {
+      if (g_permission_get_allowed (priv->permission)) {
+          set_localed_locale (page);
+      }
+      else if (g_permission_get_can_acquire (priv->permission)) {
+          g_permission_acquire_async (priv->permission,
+                                      NULL,
+                                      change_locale_permission_acquired,
+                                      page);
+      }
+  }
+  user = act_user_manager_get_user (act_user_manager_get_default (),
+                                    g_get_user_name ());
+  if (act_user_is_loaded (user))
+    act_user_set_language (user, priv->new_locale_id);
+  else
+    g_signal_connect (user,
+                      "notify::is-loaded",
+                      G_CALLBACK (user_loaded),
+                      g_strdup (priv->new_locale_id));
+
+  gis_driver_set_user_language (driver, priv->new_locale_id);
+}
+
+static void
+localed_proxy_ready (GObject      *source,
+                     GAsyncResult *res,
+                     gpointer      data)
+{
+  GisRegionPage *self = data;
+  GisRegionPagePrivate *priv = gis_region_page_get_instance_private (self);
+  GDBusProxy *proxy;
+  GError *error = NULL;
+
+  proxy = g_dbus_proxy_new_finish (res, &error);
+
+  if (!proxy) {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("Failed to contact localed: %s\n", error->message);
+      g_error_free (error);
+      return;
+  }
+
+  priv->localed = proxy;
+}
+
+static void
+gis_region_page_constructed (GObject *object)
+{
+  GisRegionPage *page = GIS_REGION_PAGE (object);
+  GisRegionPagePrivate *priv = gis_region_page_get_instance_private (page);
+  GDBusConnection *bus;
+
+  g_type_ensure (CC_TYPE_REGION_CHOOSER);
+
+  G_OBJECT_CLASS (gis_region_page_parent_class)->constructed (object);
+
+  g_signal_connect (priv->region_chooser, "notify::locale",
+                    G_CALLBACK (region_changed), page);
+
+  /* If we're in new user mode then we're manipulating system settings */
+  if (gis_driver_get_mode (GIS_PAGE (page)->driver) == GIS_DRIVER_MODE_NEW_USER)
+    {
+      priv->permission = polkit_permission_new_sync ("org.freedesktop.locale1.set-locale", NULL, NULL, NULL);
+
+      bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
+      g_dbus_proxy_new (bus,
+                        G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
+                        NULL,
+                        "org.freedesktop.locale1",
+                        "/org/freedesktop/locale1",
+                        "org.freedesktop.locale1",
+                        priv->cancellable,
+                        (GAsyncReadyCallback) localed_proxy_ready,
+                        object);
+      g_object_unref (bus);
+  }
+
+  gis_page_set_complete (GIS_PAGE (page), TRUE);
+  if (cc_region_chooser_get_n_regions (CC_REGION_CHOOSER (priv->region_chooser)) > 1)
+    gtk_widget_show (GTK_WIDGET (page));
+  else
+    gtk_widget_hide (GTK_WIDGET (page));
+}
+
+static void
+gis_region_page_locale_changed (GisPage *page)
+{
+  GisRegionPagePrivate *priv = gis_region_page_get_instance_private (GIS_REGION_PAGE (page));
+  char *locale;
+
+  gis_page_set_title (page, _("Region"));
+
+  locale = g_strdup (setlocale (LC_MESSAGES, NULL));
+
+  priv->updating = TRUE;
+  cc_region_chooser_set_locale (CC_REGION_CHOOSER (priv->region_chooser), locale);
+  priv->updating = FALSE;
+  g_free (locale);
+
+  if (cc_region_chooser_get_n_regions (CC_REGION_CHOOSER (priv->region_chooser)) > 1)
+    gtk_widget_show (GTK_WIDGET (page));
+  else
+    gtk_widget_hide (GTK_WIDGET (page));
+}
+
+static void
+gis_region_page_dispose (GObject *object)
+{
+  GisRegionPage *page = GIS_REGION_PAGE (object);
+  GisRegionPagePrivate *priv = gis_region_page_get_instance_private (page);
+
+  g_clear_object (&priv->permission);
+  g_clear_object (&priv->localed);
+  g_clear_object (&priv->cancellable);
+
+  G_OBJECT_CLASS (gis_region_page_parent_class)->dispose (object);
+}
+
+static void
+gis_region_page_class_init (GisRegionPageClass *klass)
+{
+  GisPageClass *page_class = GIS_PAGE_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), 
"/org/gnome/initial-setup/gis-region-page.ui");
+
+  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisRegionPage, region_chooser);
+
+  page_class->page_id = PAGE_ID;
+  page_class->locale_changed = gis_region_page_locale_changed;
+  object_class->constructed = gis_region_page_constructed;
+  object_class->dispose = gis_region_page_dispose;
+}
+
+static void
+gis_region_page_init (GisRegionPage *page)
+{
+  g_resources_register (region_get_resource ());
+  g_type_ensure (CC_TYPE_REGION_CHOOSER);
+
+  gtk_widget_init_template (GTK_WIDGET (page));
+}
+
+void
+gis_prepare_region_page (GisDriver *driver)
+{
+  gis_driver_add_page (driver,
+                       g_object_new (GIS_TYPE_REGION_PAGE,
+                                     "driver", driver,
+                                     NULL));
+}
diff --git a/gnome-initial-setup/pages/region/gis-region-page.h 
b/gnome-initial-setup/pages/region/gis-region-page.h
new file mode 100644
index 0000000..5e7cdea
--- /dev/null
+++ b/gnome-initial-setup/pages/region/gis-region-page.h
@@ -0,0 +1,59 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2013 Red Hat
+ *
+ * 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>
+ */
+
+#ifndef __GIS_REGION_PAGE_H__
+#define __GIS_REGION_PAGE_H__
+
+#include <glib-object.h>
+
+#include "gnome-initial-setup.h"
+
+G_BEGIN_DECLS
+
+#define GIS_TYPE_REGION_PAGE               (gis_region_page_get_type ())
+#define GIS_REGION_PAGE(obj)                           (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIS_TYPE_REGION_PAGE, GisRegionPage))
+#define GIS_REGION_PAGE_CLASS(klass)                   (G_TYPE_CHECK_CLASS_CAST ((klass),  
GIS_TYPE_REGION_PAGE, GisRegionPageClass))
+#define GIS_IS_REGION_PAGE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_REGION_PAGE))
+#define GIS_IS_REGION_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GIS_TYPE_REGION_PAGE))
+#define GIS_REGION_PAGE_GET_CLASS(obj)                 (G_TYPE_INSTANCE_GET_CLASS ((obj),  
GIS_TYPE_REGION_PAGE, GisRegionPageClass))
+
+typedef struct _GisRegionPage        GisRegionPage;
+typedef struct _GisRegionPageClass   GisRegionPageClass;
+
+struct _GisRegionPage
+{
+  GisPage parent;
+};
+
+struct _GisRegionPageClass
+{
+  GisPageClass parent_class;
+};
+
+GType gis_region_page_get_type (void);
+
+void gis_prepare_region_page (GisDriver *driver);
+
+G_END_DECLS
+
+#endif /* __GIS_REGION_PAGE_H__ */
diff --git a/gnome-initial-setup/pages/region/gis-region-page.ui 
b/gnome-initial-setup/pages/region/gis-region-page.ui
new file mode 100644
index 0000000..abfc634
--- /dev/null
+++ b/gnome-initial-setup/pages/region/gis-region-page.ui
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <template class="GisRegionPage" parent="GisPage">
+    <child>
+      <object class="GtkBox" id="box">
+        <property name="visible">True</property>
+        <property name="halign">center</property>
+        <property name="valign">fill</property>
+        <property name="orientation">vertical</property>
+       <child>
+          <object class="GtkImage" id="image1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="pixel_size">96</property>
+            <property name="icon_name">preferences-desktop-locale-symbolic</property>
+            <property name="icon_size">1</property>
+            <property name="margin_top">54</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="halign">center</property>
+            <property name="valign">start</property>
+            <property name="margin_top">26</property>
+            <property name="label" translatable="yes">Region</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+              <attribute name="scale" value="1.8"/>
+            </attributes>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="valign">start</property>
+            <property name="margin_top">14</property>
+            <property name="label" translatable="yes">Choose your country or region.</property>
+            <property name="justify">center</property>
+            <property name="wrap">True</property>
+            <property name="max-width-chars">50</property>
+          </object>
+        </child>
+        <child>
+          <object class="CcRegionChooser" id="region_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>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/gnome-initial-setup/pages/region/region-chooser.ui 
b/gnome-initial-setup/pages/region/region-chooser.ui
new file mode 100644
index 0000000..f74616e
--- /dev/null
+++ b/gnome-initial-setup/pages/region/region-chooser.ui
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <template class="CcRegionChooser" 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="hscrollbar-policy">never</property>
+        <property name="vscrollbar-policy">never</property>
+        <property name="shadow-type">in</property>
+        <child>
+          <object class="GtkViewport" id="viewport">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkListBox" id="region_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>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/gnome-initial-setup/pages/region/region.gresource.xml 
b/gnome-initial-setup/pages/region/region.gresource.xml
new file mode 100644
index 0000000..1ed8f38
--- /dev/null
+++ b/gnome-initial-setup/pages/region/region.gresource.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/initial-setup">
+    <file preprocess="xml-stripblanks" alias="gis-region-page.ui">gis-region-page.ui</file>
+  </gresource>
+  <gresource prefix="/org/gnome/control-center">
+    <file preprocess="xml-stripblanks" alias="region-chooser.ui">region-chooser.ui</file>
+  </gresource>
+</gresources>


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