summaryrefslogtreecommitdiffstats
path: root/gnome-initial-setup/pages/language
diff options
context:
space:
mode:
Diffstat (limited to 'gnome-initial-setup/pages/language')
-rw-r--r--gnome-initial-setup/pages/language/cc-language-chooser.c597
-rw-r--r--gnome-initial-setup/pages/language/cc-language-chooser.h63
-rw-r--r--gnome-initial-setup/pages/language/cc-util.c105
-rw-r--r--gnome-initial-setup/pages/language/cc-util.h28
-rw-r--r--gnome-initial-setup/pages/language/gis-language-page.c312
-rw-r--r--gnome-initial-setup/pages/language/gis-language-page.h57
-rw-r--r--gnome-initial-setup/pages/language/gis-language-page.ui48
-rw-r--r--gnome-initial-setup/pages/language/gis-welcome-widget.c242
-rw-r--r--gnome-initial-setup/pages/language/gis-welcome-widget.h56
-rw-r--r--gnome-initial-setup/pages/language/gis-welcome-widget.ui14
-rw-r--r--gnome-initial-setup/pages/language/language-chooser.ui22
-rw-r--r--gnome-initial-setup/pages/language/language.gresource.xml10
-rw-r--r--gnome-initial-setup/pages/language/meson.build16
13 files changed, 1570 insertions, 0 deletions
diff --git a/gnome-initial-setup/pages/language/cc-language-chooser.c b/gnome-initial-setup/pages/language/cc-language-chooser.c
new file mode 100644
index 0000000..309704c
--- /dev/null
+++ b/gnome-initial-setup/pages/language/cc-language-chooser.c
@@ -0,0 +1,597 @@
+/* -*- 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Jasper St. Pierre <jstpierre@mecheye.net>
+ * Matthias Clasen <mclasen@redhat.com>
+ */
+
+#include "config.h"
+#include "cc-language-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 "cc-util.h"
+
+#include <glib-object.h>
+
+struct _CcLanguageChooserPrivate
+{
+ GtkWidget *filter_entry;
+ GtkWidget *language_list;
+
+ GtkWidget *no_results;
+ GtkWidget *more_item;
+
+ gboolean showing_extra;
+ gchar *language;
+};
+typedef struct _CcLanguageChooserPrivate CcLanguageChooserPrivate;
+G_DEFINE_TYPE_WITH_PRIVATE (CcLanguageChooser, cc_language_chooser, GTK_TYPE_BOX);
+
+enum {
+ PROP_0,
+ PROP_LANGUAGE,
+ PROP_SHOWING_EXTRA,
+ PROP_LAST,
+};
+
+static GParamSpec *obj_props[PROP_LAST];
+
+enum {
+ CONFIRM,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct {
+ GtkWidget *box;
+ GtkWidget *checkmark;
+
+ gchar *locale_id;
+ gchar *locale_name;
+ gchar *locale_current_name;
+ gchar *locale_untranslated_name;
+ gchar *sort_key;
+ gboolean is_extra;
+} LanguageWidget;
+
+static LanguageWidget *
+get_language_widget (GtkWidget *widget)
+{
+ return g_object_get_data (G_OBJECT (widget), "language-widget");
+}
+
+static GtkWidget *
+padded_label_new (char *text)
+{
+ GtkWidget *widget;
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_widget_set_halign (widget, GTK_ALIGN_CENTER);
+ gtk_widget_set_margin_top (widget, 12);
+ gtk_widget_set_margin_bottom (widget, 12);
+ gtk_box_append (GTK_BOX (widget), gtk_label_new (text));
+ return widget;
+}
+
+static void
+language_widget_free (gpointer data)
+{
+ LanguageWidget *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->sort_key);
+ g_free (widget);
+}
+
+static GtkWidget *
+language_widget_new (const char *locale_id,
+ gboolean is_extra)
+{
+ GtkWidget *label;
+ gchar *locale_name, *locale_current_name, *locale_untranslated_name;
+ gchar *language = NULL;
+ gchar *language_name;
+ gchar *country = NULL;
+ gchar *country_name = NULL;
+ LanguageWidget *widget = g_new0 (LanguageWidget, 1);
+
+ if (!gnome_parse_locale (locale_id, &language, &country, NULL, NULL))
+ return NULL;
+
+ language_name = gnome_get_language_from_code (language, locale_id);
+ if (language_name == NULL)
+ language_name = gnome_get_language_from_code (language, NULL);
+
+ if (country) {
+ country_name = gnome_get_country_from_code (country, locale_id);
+ if (country_name == NULL)
+ country_name = gnome_get_country_from_code (country, NULL);
+ }
+
+ locale_name = gnome_get_language_from_locale (locale_id, locale_id);
+ locale_current_name = gnome_get_language_from_locale (locale_id, NULL);
+ locale_untranslated_name = gnome_get_language_from_locale (locale_id, "C");
+
+ widget->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_widget_set_margin_top (widget->box, 12);
+ gtk_widget_set_margin_bottom (widget->box, 12);
+ gtk_widget_set_margin_start (widget->box, 12);
+ gtk_widget_set_margin_end (widget->box, 12);
+ gtk_widget_set_halign (widget->box, GTK_ALIGN_FILL);
+
+ label = gtk_label_new (language_name);
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+ gtk_label_set_max_width_chars (GTK_LABEL (label), 30);
+ gtk_label_set_xalign (GTK_LABEL (label), 0);
+ gtk_box_append (GTK_BOX (widget->box), label);
+
+ widget->checkmark = gtk_image_new_from_icon_name ("object-select-symbolic");
+ gtk_box_append (GTK_BOX (widget->box), widget->checkmark);
+
+ if (country_name) {
+ label = gtk_label_new (country_name);
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+ gtk_label_set_max_width_chars (GTK_LABEL (label), 30);
+ gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
+ gtk_label_set_xalign (GTK_LABEL (label), 0);
+ gtk_widget_set_hexpand (label, TRUE);
+ gtk_widget_set_halign (label, GTK_ALIGN_END);
+ gtk_box_append (GTK_BOX (widget->box), label);
+ }
+
+ 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->sort_key = cc_util_normalize_casefold_and_unaccent (locale_name);
+
+ g_object_set_data_full (G_OBJECT (widget->box), "language-widget", widget,
+ language_widget_free);
+
+ g_free (language);
+ g_free (language_name);
+ g_free (country);
+ g_free (country_name);
+
+ return widget->box;
+}
+
+static void
+sync_all_checkmarks (CcLanguageChooser *chooser)
+{
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+ GtkWidget *row;
+
+ row = gtk_widget_get_first_child (priv->language_list);
+ while (row) {
+ LanguageWidget *widget;
+ GtkWidget *child;
+ gboolean should_be_visible;
+
+ child = gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (row));
+ widget = get_language_widget (child);
+
+ if (widget == NULL)
+ return;
+
+ should_be_visible = g_str_equal (widget->locale_id, priv->language);
+ gtk_widget_set_opacity (widget->checkmark, should_be_visible ? 1.0 : 0.0);
+
+ row = gtk_widget_get_next_sibling (row);
+ }
+}
+
+static GtkWidget *
+more_widget_new (void)
+{
+ GtkWidget *widget;
+ GtkWidget *arrow;
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_widget_set_tooltip_text (widget, _("Moreā€¦"));
+
+ arrow = g_object_new (GTK_TYPE_IMAGE,
+ "icon-name", "view-more-symbolic",
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_CENTER,
+ NULL);
+ gtk_style_context_add_class (gtk_widget_get_style_context (arrow), "dim-label");
+ gtk_widget_set_margin_top (widget, 12);
+ gtk_widget_set_margin_bottom (widget, 12);
+ gtk_box_append (GTK_BOX (widget), arrow);
+
+ return widget;
+}
+
+static GtkWidget *
+no_results_widget_new (void)
+{
+ GtkWidget *widget;
+
+ widget = padded_label_new (_("No languages found"));
+ gtk_widget_set_sensitive (widget, FALSE);
+
+ return widget;
+}
+
+static void
+add_one_language (CcLanguageChooser *chooser,
+ const char *locale_id,
+ gboolean is_initial)
+{
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+ GtkWidget *widget;
+
+ if (!cc_common_language_has_font (locale_id)) {
+ return;
+ }
+
+ widget = language_widget_new (locale_id, !is_initial);
+ if (widget)
+ gtk_list_box_append (GTK_LIST_BOX (priv->language_list), widget);
+}
+
+static void
+add_languages (CcLanguageChooser *chooser,
+ char **locale_ids,
+ GHashTable *initial)
+{
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+ GHashTableIter iter;
+ gchar *key;
+
+ g_hash_table_iter_init (&iter, initial);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, NULL)) {
+ add_one_language (chooser, key, TRUE);
+ }
+
+ while (*locale_ids) {
+ const gchar *locale_id;
+
+ locale_id = *locale_ids;
+ locale_ids ++;
+
+ if (!g_hash_table_lookup (initial, locale_id))
+ add_one_language (chooser, locale_id, FALSE);
+ }
+
+ gtk_list_box_append (GTK_LIST_BOX (priv->language_list), priv->more_item);
+ gtk_list_box_set_placeholder (GTK_LIST_BOX (priv->language_list), priv->no_results);
+}
+
+static void
+add_all_languages (CcLanguageChooser *chooser)
+{
+ g_auto(GStrv) locale_ids = NULL;
+ g_autoptr(GHashTable) initial = NULL;
+
+ locale_ids = gnome_get_all_locales ();
+ initial = cc_common_language_get_initial_languages ();
+ add_languages (chooser, locale_ids, initial);
+}
+
+static gboolean
+language_visible (GtkListBoxRow *row,
+ gpointer user_data)
+{
+ CcLanguageChooser *chooser = user_data;
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+ LanguageWidget *widget;
+ gboolean visible;
+ GtkWidget *child;
+ const char *search_term;
+
+ child = gtk_list_box_row_get_child (row);
+ if (child == priv->more_item)
+ return !priv->showing_extra;
+
+ widget = get_language_widget (child);
+
+ if (!priv->showing_extra && widget->is_extra)
+ return FALSE;
+
+ search_term = gtk_editable_get_text (GTK_EDITABLE (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_languages (GtkListBoxRow *a,
+ GtkListBoxRow *b,
+ gpointer data)
+{
+ LanguageWidget *la, *lb;
+ int ret;
+
+ la = get_language_widget (gtk_list_box_row_get_child (a));
+ lb = get_language_widget (gtk_list_box_row_get_child (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;
+
+ ret = g_strcmp0 (la->sort_key, lb->sort_key);
+ if (ret != 0)
+ return ret;
+
+ return g_strcmp0 (la->locale_id, lb->locale_id);
+}
+
+static void
+filter_changed (GtkEntry *entry,
+ CcLanguageChooser *chooser)
+{
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+ gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->language_list));
+}
+
+static void
+show_more (CcLanguageChooser *chooser)
+{
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+
+ gtk_widget_grab_focus (priv->filter_entry);
+
+ gtk_widget_set_valign (GTK_WIDGET (chooser), GTK_ALIGN_FILL);
+
+ priv->showing_extra = TRUE;
+ gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->language_list));
+ g_object_notify_by_pspec (G_OBJECT (chooser), obj_props[PROP_SHOWING_EXTRA]);
+}
+
+static void
+set_locale_id (CcLanguageChooser *chooser,
+ const gchar *new_locale_id)
+{
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+
+ if (g_strcmp0 (priv->language, new_locale_id) == 0)
+ return;
+
+ g_free (priv->language);
+ priv->language = g_strdup (new_locale_id);
+
+ sync_all_checkmarks (chooser);
+
+ g_object_notify_by_pspec (G_OBJECT (chooser), obj_props[PROP_LANGUAGE]);
+}
+
+static gboolean
+confirm_choice (gpointer data)
+{
+ GtkWidget *widget = data;
+
+ g_signal_emit (widget, signals[CONFIRM], 0);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+row_activated (GtkListBox *box,
+ GtkListBoxRow *row,
+ CcLanguageChooser *chooser)
+{
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+ GtkWidget *child;
+ LanguageWidget *widget;
+
+ if (row == NULL)
+ return;
+
+ child = gtk_list_box_row_get_child (row);
+ if (child == priv->more_item) {
+ show_more (chooser);
+ } else {
+ widget = get_language_widget (child);
+ if (widget == NULL)
+ return;
+ if (g_strcmp0 (priv->language, widget->locale_id) == 0)
+ g_idle_add (confirm_choice, chooser);
+ else
+ set_locale_id (chooser, widget->locale_id);
+ }
+}
+
+static void
+cc_language_chooser_constructed (GObject *object)
+{
+ CcLanguageChooser *chooser = CC_LANGUAGE_CHOOSER (object);
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+
+ G_OBJECT_CLASS (cc_language_chooser_parent_class)->constructed (object);
+
+ priv->more_item = more_widget_new ();
+ priv->no_results = no_results_widget_new ();
+
+ gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->language_list),
+ sort_languages, chooser, NULL);
+ gtk_list_box_set_filter_func (GTK_LIST_BOX (priv->language_list),
+ language_visible, chooser, NULL);
+ gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->language_list),
+ GTK_SELECTION_NONE);
+ add_all_languages (chooser);
+
+ g_signal_connect (priv->filter_entry, "changed",
+ G_CALLBACK (filter_changed),
+ chooser);
+
+ g_signal_connect (priv->language_list, "row-activated",
+ G_CALLBACK (row_activated), chooser);
+
+ if (priv->language == NULL)
+ priv->language = cc_common_language_get_current_language ();
+
+ sync_all_checkmarks (chooser);
+}
+
+static void
+cc_language_chooser_finalize (GObject *object)
+{
+ CcLanguageChooser *chooser = CC_LANGUAGE_CHOOSER (object);
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+
+ g_free (priv->language);
+
+ G_OBJECT_CLASS (cc_language_chooser_parent_class)->finalize (object);
+}
+
+static void
+cc_language_chooser_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CcLanguageChooser *chooser = CC_LANGUAGE_CHOOSER (object);
+ switch (prop_id) {
+ case PROP_LANGUAGE:
+ g_value_set_string (value, cc_language_chooser_get_language (chooser));
+ break;
+ case PROP_SHOWING_EXTRA:
+ g_value_set_boolean (value, cc_language_chooser_get_showing_extra (chooser));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_language_chooser_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcLanguageChooser *chooser = CC_LANGUAGE_CHOOSER (object);
+ switch (prop_id) {
+ case PROP_LANGUAGE:
+ cc_language_chooser_set_language (chooser, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_language_chooser_class_init (CcLanguageChooserClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/control-center/language-chooser.ui");
+
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), CcLanguageChooser, filter_entry);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), CcLanguageChooser, language_list);
+
+ object_class->finalize = cc_language_chooser_finalize;
+ object_class->get_property = cc_language_chooser_get_property;
+ object_class->set_property = cc_language_chooser_set_property;
+ object_class->constructed = cc_language_chooser_constructed;
+
+ signals[CONFIRM] = g_signal_new ("confirm",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (CcLanguageChooserClass, confirm),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ obj_props[PROP_LANGUAGE] =
+ g_param_spec_string ("language", "", "", "",
+ 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_language_chooser_init (CcLanguageChooser *chooser)
+{
+ gtk_widget_init_template (GTK_WIDGET (chooser));
+}
+
+void
+cc_language_chooser_clear_filter (CcLanguageChooser *chooser)
+{
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+ gtk_editable_set_text (GTK_EDITABLE (priv->filter_entry), "");
+}
+
+const gchar *
+cc_language_chooser_get_language (CcLanguageChooser *chooser)
+{
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+ return priv->language;
+}
+
+void
+cc_language_chooser_set_language (CcLanguageChooser *chooser,
+ const gchar *language)
+{
+ set_locale_id (chooser, language);
+}
+
+gboolean
+cc_language_chooser_get_showing_extra (CcLanguageChooser *chooser)
+{
+ CcLanguageChooserPrivate *priv = cc_language_chooser_get_instance_private (chooser);
+ return priv->showing_extra;
+}
diff --git a/gnome-initial-setup/pages/language/cc-language-chooser.h b/gnome-initial-setup/pages/language/cc-language-chooser.h
new file mode 100644
index 0000000..749af78
--- /dev/null
+++ b/gnome-initial-setup/pages/language/cc-language-chooser.h
@@ -0,0 +1,63 @@
+/* -*- 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Jasper St. Pierre <jstpierre@mecheye.net>
+ * Matthias Clasen <mclasen@redhat.com>
+ */
+
+#ifndef __CC_LANGUAGE_CHOOSER_H__
+#define __CC_LANGUAGE_CHOOSER_H__
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+#define CC_TYPE_LANGUAGE_CHOOSER (cc_language_chooser_get_type ())
+#define CC_LANGUAGE_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_LANGUAGE_CHOOSER, CcLanguageChooser))
+#define CC_LANGUAGE_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CC_TYPE_LANGUAGE_CHOOSER, CcLanguageChooserClass))
+#define CC_IS_LANGUAGE_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_LANGUAGE_CHOOSER))
+#define CC_IS_LANGUAGE_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CC_TYPE_LANGUAGE_CHOOSER))
+#define CC_LANGUAGE_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CC_TYPE_LANGUAGE_CHOOSER, CcLanguageChooserClass))
+
+G_BEGIN_DECLS
+
+typedef struct _CcLanguageChooser CcLanguageChooser;
+typedef struct _CcLanguageChooserClass CcLanguageChooserClass;
+
+struct _CcLanguageChooser
+{
+ GtkBox parent;
+};
+
+struct _CcLanguageChooserClass
+{
+ GtkBoxClass parent_class;
+
+ void (*confirm) (CcLanguageChooser *chooser);
+};
+
+GType cc_language_chooser_get_type (void);
+
+void cc_language_chooser_clear_filter (CcLanguageChooser *chooser);
+const gchar * cc_language_chooser_get_language (CcLanguageChooser *chooser);
+void cc_language_chooser_set_language (CcLanguageChooser *chooser,
+ const gchar *language);
+gboolean cc_language_chooser_get_showing_extra (CcLanguageChooser *chooser);
+
+G_END_DECLS
+
+#endif /* __CC_LANGUAGE_CHOOSER_H__ */
diff --git a/gnome-initial-setup/pages/language/cc-util.c b/gnome-initial-setup/pages/language/cc-util.c
new file mode 100644
index 0000000..e51a9d2
--- /dev/null
+++ b/gnome-initial-setup/pages/language/cc-util.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2012 Giovanni Campagna <scampa.giovanni@gmail.com>
+ *
+ * The Control Center 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.
+ *
+ * The Control Center 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 the Control Center; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+
+#include "cc-util.h"
+
+/* Combining diacritical mark?
+ * Basic range: [0x0300,0x036F]
+ * Supplement: [0x1DC0,0x1DFF]
+ * For Symbols: [0x20D0,0x20FF]
+ * Half marks: [0xFE20,0xFE2F]
+ */
+#define IS_CDM_UCS4(c) (((c) >= 0x0300 && (c) <= 0x036F) || \
+ ((c) >= 0x1DC0 && (c) <= 0x1DFF) || \
+ ((c) >= 0x20D0 && (c) <= 0x20FF) || \
+ ((c) >= 0xFE20 && (c) <= 0xFE2F))
+
+/* Copied from tracker/src/libtracker-fts/tracker-parser-glib.c under the GPL
+ * And then from gnome-shell/src/shell-util.c
+ *
+ * Originally written by Aleksander Morgado <aleksander@gnu.org>
+ */
+char *
+cc_util_normalize_casefold_and_unaccent (const char *str)
+{
+ char *normalized, *tmp;
+ int i = 0, j = 0, ilen;
+
+ if (str == NULL)
+ return NULL;
+
+ normalized = g_utf8_normalize (str, -1, G_NORMALIZE_NFKD);
+ tmp = g_utf8_casefold (normalized, -1);
+ g_free (normalized);
+
+ ilen = strlen (tmp);
+
+ while (i < ilen)
+ {
+ gunichar unichar;
+ gchar *next_utf8;
+ gint utf8_len;
+
+ /* Get next character of the word as UCS4 */
+ unichar = g_utf8_get_char_validated (&tmp[i], -1);
+
+ /* Invalid UTF-8 character or end of original string. */
+ if (unichar == (gunichar) -1 ||
+ unichar == (gunichar) -2)
+ {
+ break;
+ }
+
+ /* Find next UTF-8 character */
+ next_utf8 = g_utf8_next_char (&tmp[i]);
+ utf8_len = next_utf8 - &tmp[i];
+
+ if (IS_CDM_UCS4 ((guint32) unichar))
+ {
+ /* If the given unichar is a combining diacritical mark,
+ * just update the original index, not the output one */
+ i += utf8_len;
+ continue;
+ }
+
+ /* If already found a previous combining
+ * diacritical mark, indexes are different so
+ * need to copy characters. As output and input
+ * buffers may overlap, need to use memmove
+ * instead of memcpy */
+ if (i != j)
+ {
+ memmove (&tmp[j], &tmp[i], utf8_len);
+ }
+
+ /* Update both indexes */
+ i += utf8_len;
+ j += utf8_len;
+ }
+
+ /* Force proper string end */
+ tmp[j] = '\0';
+
+ return tmp;
+}
diff --git a/gnome-initial-setup/pages/language/cc-util.h b/gnome-initial-setup/pages/language/cc-util.h
new file mode 100644
index 0000000..42b09ff
--- /dev/null
+++ b/gnome-initial-setup/pages/language/cc-util.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2012 Giovanni Campagna <scampa.giovanni@gmail.com>
+ *
+ * The Control Center 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.
+ *
+ * The Control Center 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 the Control Center; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+
+#ifndef _CC_UTIL_H
+#define _CC_UTIL_H
+
+#include <glib.h>
+
+char *cc_util_normalize_casefold_and_unaccent (const char *str);
+
+#endif
diff --git a/gnome-initial-setup/pages/language/gis-language-page.c b/gnome-initial-setup/pages/language/gis-language-page.c
new file mode 100644
index 0000000..f62b2ad
--- /dev/null
+++ b/gnome-initial-setup/pages/language/gis-language-page.c
@@ -0,0 +1,312 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2012 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Jasper St. Pierre <jstpierre@mecheye.net>
+ * Michael Wood <michael.g.wood@intel.com>
+ *
+ * Based on gnome-control-center cc-region-panel.c
+ */
+
+/* Language page {{{1 */
+
+#define PAGE_ID "language"
+
+#define GNOME_SYSTEM_LOCALE_DIR "org.gnome.system.locale"
+#define REGION_KEY "region"
+
+#include "config.h"
+#include "language-resources.h"
+#include "gis-welcome-widget.h"
+#include "cc-language-chooser.h"
+#include "gis-language-page.h"
+
+#include <act/act-user-manager.h>
+#include <polkit/polkit.h>
+#include <locale.h>
+#include <gtk/gtk.h>
+
+struct _GisLanguagePagePrivate
+{
+ GtkWidget *logo;
+ GtkWidget *welcome_widget;
+ GtkWidget *language_chooser;
+
+ GDBusProxy *localed;
+ GPermission *permission;
+ const gchar *new_locale_id;
+
+ GCancellable *cancellable;
+};
+typedef struct _GisLanguagePagePrivate GisLanguagePagePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GisLanguagePage, gis_language_page, GIS_TYPE_PAGE);
+
+static void
+set_localed_locale (GisLanguagePage *self)
+{
+ GisLanguagePagePrivate *priv = gis_language_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)
+{
+ GisLanguagePage *page = GIS_LANGUAGE_PAGE (data);
+ GisLanguagePagePrivate *priv = gis_language_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", 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
+language_changed (CcLanguageChooser *chooser,
+ GParamSpec *pspec,
+ GisLanguagePage *page)
+{
+ GisLanguagePagePrivate *priv = gis_language_page_get_instance_private (page);
+ GisDriver *driver;
+ GSettings *region_settings;
+ ActUser *user;
+
+ priv->new_locale_id = cc_language_chooser_get_language (chooser);
+ driver = GIS_PAGE (page)->driver;
+
+ gis_driver_set_user_language (driver, priv->new_locale_id, TRUE);
+ gtk_widget_set_default_direction (gtk_get_locale_direction ());
+
+ 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);
+ }
+ }
+
+ /* Ensure we won't override the selected language for format strings */
+ region_settings = g_settings_new (GNOME_SYSTEM_LOCALE_DIR);
+ g_settings_reset (region_settings, REGION_KEY);
+ g_object_unref (region_settings);
+
+ 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_welcome_widget_show_locale (GIS_WELCOME_WIDGET (priv->welcome_widget),
+ priv->new_locale_id);
+}
+
+static void
+localed_proxy_ready (GObject *source,
+ GAsyncResult *res,
+ gpointer data)
+{
+ GisLanguagePage *self = data;
+ GisLanguagePagePrivate *priv = gis_language_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", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ priv->localed = proxy;
+}
+
+static void
+update_distro_logo (GisLanguagePage *page)
+{
+ GisLanguagePagePrivate *priv = gis_language_page_get_instance_private (page);
+ g_autofree char *id = g_get_os_info (G_OS_INFO_KEY_ID);
+ gsize i;
+
+ static const struct {
+ const char *id;
+ const char *logo;
+ } id_to_logo[] = {
+ { "debian", "emblem-debian" },
+ { "fedora", "fedora-logo-icon" },
+ { "ubuntu", "ubuntu-logo-icon" },
+ { "openSUSE Tumbleweed", "opensuse-logo-icon" },
+ { "openSUSE Leap", "opensuse-logo-icon" },
+ { "SLED", "suse-logo-icon" },
+ { "SLES", "suse-logo-icon" },
+ };
+
+ for (i = 0; i < G_N_ELEMENTS (id_to_logo); i++)
+ {
+ if (g_strcmp0 (id, id_to_logo[i].id) == 0)
+ {
+ g_object_set (priv->logo, "icon-name", id_to_logo[i].logo, NULL);
+ break;
+ }
+ }
+}
+
+static void
+language_confirmed (CcLanguageChooser *chooser,
+ GisLanguagePage *page)
+{
+ gis_assistant_next_page (gis_driver_get_assistant (GIS_PAGE (page)->driver));
+}
+
+static void
+gis_language_page_constructed (GObject *object)
+{
+ GisLanguagePage *page = GIS_LANGUAGE_PAGE (object);
+ GisLanguagePagePrivate *priv = gis_language_page_get_instance_private (page);
+ GDBusConnection *bus;
+
+ g_type_ensure (CC_TYPE_LANGUAGE_CHOOSER);
+
+ G_OBJECT_CLASS (gis_language_page_parent_class)->constructed (object);
+
+ update_distro_logo (page);
+
+ g_signal_connect (priv->language_chooser, "notify::language",
+ G_CALLBACK (language_changed), page);
+ g_signal_connect (priv->language_chooser, "confirm",
+ G_CALLBACK (language_confirmed), 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);
+ gtk_widget_show (GTK_WIDGET (page));
+}
+
+static void
+gis_language_page_locale_changed (GisPage *page)
+{
+ gis_page_set_title (GIS_PAGE (page), _("Welcome"));
+}
+
+static void
+gis_language_page_dispose (GObject *object)
+{
+ GisLanguagePage *page = GIS_LANGUAGE_PAGE (object);
+ GisLanguagePagePrivate *priv = gis_language_page_get_instance_private (page);
+
+ g_clear_object (&priv->permission);
+ g_clear_object (&priv->localed);
+ g_clear_object (&priv->cancellable);
+
+ G_OBJECT_CLASS (gis_language_page_parent_class)->dispose (object);
+}
+
+static void
+gis_language_page_class_init (GisLanguagePageClass *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-language-page.ui");
+
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisLanguagePage, welcome_widget);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisLanguagePage, language_chooser);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisLanguagePage, logo);
+
+ page_class->page_id = PAGE_ID;
+ page_class->locale_changed = gis_language_page_locale_changed;
+ object_class->constructed = gis_language_page_constructed;
+ object_class->dispose = gis_language_page_dispose;
+}
+
+static void
+gis_language_page_init (GisLanguagePage *page)
+{
+ g_resources_register (language_get_resource ());
+ g_type_ensure (GIS_TYPE_WELCOME_WIDGET);
+ g_type_ensure (CC_TYPE_LANGUAGE_CHOOSER);
+
+ gtk_widget_init_template (GTK_WIDGET (page));
+}
+
+GisPage *
+gis_prepare_language_page (GisDriver *driver)
+{
+ return g_object_new (GIS_TYPE_LANGUAGE_PAGE,
+ "driver", driver,
+ NULL);
+}
diff --git a/gnome-initial-setup/pages/language/gis-language-page.h b/gnome-initial-setup/pages/language/gis-language-page.h
new file mode 100644
index 0000000..7636021
--- /dev/null
+++ b/gnome-initial-setup/pages/language/gis-language-page.h
@@ -0,0 +1,57 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2012 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Jasper St. Pierre <jstpierre@mecheye.net>
+ */
+
+#ifndef __GIS_LANGUAGE_PAGE_H__
+#define __GIS_LANGUAGE_PAGE_H__
+
+#include <glib-object.h>
+
+#include "gnome-initial-setup.h"
+
+G_BEGIN_DECLS
+
+#define GIS_TYPE_LANGUAGE_PAGE (gis_language_page_get_type ())
+#define GIS_LANGUAGE_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_LANGUAGE_PAGE, GisLanguagePage))
+#define GIS_LANGUAGE_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_LANGUAGE_PAGE, GisLanguagePageClass))
+#define GIS_IS_LANGUAGE_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_LANGUAGE_PAGE))
+#define GIS_IS_LANGUAGE_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_LANGUAGE_PAGE))
+#define GIS_LANGUAGE_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_LANGUAGE_PAGE, GisLanguagePageClass))
+
+typedef struct _GisLanguagePage GisLanguagePage;
+typedef struct _GisLanguagePageClass GisLanguagePageClass;
+
+struct _GisLanguagePage
+{
+ GisPage parent;
+};
+
+struct _GisLanguagePageClass
+{
+ GisPageClass parent_class;
+};
+
+GType gis_language_page_get_type (void);
+
+GisPage *gis_prepare_language_page (GisDriver *driver);
+
+G_END_DECLS
+
+#endif /* __GIS_LANGUAGE_PAGE_H__ */
diff --git a/gnome-initial-setup/pages/language/gis-language-page.ui b/gnome-initial-setup/pages/language/gis-language-page.ui
new file mode 100644
index 0000000..307360c
--- /dev/null
+++ b/gnome-initial-setup/pages/language/gis-language-page.ui
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<interface>
+ <template class="GisLanguagePage" parent="GisPage">
+ <child>
+ <object class="AdwPreferencesPage">
+ <child>
+ <object class="AdwPreferencesGroup">
+
+ <child>
+ <object class="GtkBox" id="box">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="hexpand" bind-source="language_chooser" bind-property="visible" bind-flags="invert-boolean|sync-create"/>
+ <property name="vexpand" bind-source="language_chooser" bind-property="visible" bind-flags="invert-boolean|sync-create"/>
+ <child>
+ <object class="GtkImage" id="logo">
+ <property name="visible" bind-source="GisLanguagePage" bind-property="small-screen" bind-flags="invert-boolean|sync-create"/>
+ <property name="margin_top">24</property>
+ <property name="pixel_size">96</property>
+ <property name="icon_name">start-here-symbolic</property>
+ </object>
+ </child>
+ <child>
+ <object class="GisWelcomeWidget" id="welcome_widget">
+ <property name="margin_top">18</property>
+ <property name="margin_bottom">40</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="CcLanguageChooser" id="language_chooser">
+ <property name="margin_bottom">18</property>
+ <property name="width_request">400</property>
+ <property name="valign">start</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/gnome-initial-setup/pages/language/gis-welcome-widget.c b/gnome-initial-setup/pages/language/gis-welcome-widget.c
new file mode 100644
index 0000000..4d58a37
--- /dev/null
+++ b/gnome-initial-setup/pages/language/gis-welcome-widget.c
@@ -0,0 +1,242 @@
+/* -*- 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Jasper St. Pierre <jstpierre@mecheye.net>
+ */
+
+#include "config.h"
+#include "gis-welcome-widget.h"
+
+#include <errno.h>
+#include <locale.h>
+#include <glib/gi18n.h>
+
+#include "cc-common-language.h"
+
+struct _GisWelcomeWidgetPrivate
+{
+ AdwCarousel *carousel;
+ GHashTable *translation_widgets; /* (element-type owned utf8 unowned GtkWidget) (owned) */
+
+ guint timeout_id;
+};
+typedef struct _GisWelcomeWidgetPrivate GisWelcomeWidgetPrivate;
+
+#define TIMEOUT 5
+
+G_DEFINE_TYPE_WITH_PRIVATE (GisWelcomeWidget, gis_welcome_widget, ADW_TYPE_BIN);
+
+static gboolean
+advance_stack (gpointer user_data)
+{
+ GisWelcomeWidget *widget = user_data;
+ GisWelcomeWidgetPrivate *priv = gis_welcome_widget_get_instance_private (widget);
+ GtkWidget *child;
+ unsigned int next_page;
+ unsigned int n_pages;
+ double current_page;
+
+ n_pages = adw_carousel_get_n_pages (priv->carousel);
+ if (n_pages == 0)
+ goto out;
+
+ current_page = ceil (adw_carousel_get_position (priv->carousel));
+ next_page = ((int) current_page + 1) % n_pages;
+
+ child = gtk_widget_get_first_child (GTK_WIDGET (priv->carousel));
+ while (next_page-- > 0)
+ child = gtk_widget_get_next_sibling (child);
+
+ adw_carousel_scroll_to (priv->carousel, child, TRUE);
+
+ out:
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+gis_welcome_widget_start (GisWelcomeWidget *widget)
+{
+ GisWelcomeWidgetPrivate *priv = gis_welcome_widget_get_instance_private (widget);
+
+ if (priv->timeout_id > 0)
+ return;
+
+ priv->timeout_id = g_timeout_add_seconds (5, advance_stack, widget);
+}
+
+static void
+gis_welcome_widget_stop (GisWelcomeWidget *widget)
+{
+ GisWelcomeWidgetPrivate *priv = gis_welcome_widget_get_instance_private (widget);
+
+ if (priv->timeout_id == 0)
+ return;
+
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = 0;
+}
+
+static void
+gis_welcome_widget_map (GtkWidget *widget)
+{
+ GTK_WIDGET_CLASS (gis_welcome_widget_parent_class)->map (widget);
+ gis_welcome_widget_start (GIS_WELCOME_WIDGET (widget));
+}
+
+static void
+gis_welcome_widget_unmap (GtkWidget *widget)
+{
+ GTK_WIDGET_CLASS (gis_welcome_widget_parent_class)->unmap (widget);
+ gis_welcome_widget_stop (GIS_WELCOME_WIDGET (widget));
+}
+
+static const char *
+welcome (const char *locale_id)
+{
+ locale_t locale;
+ locale_t old_locale;
+ const char *welcome;
+
+ locale = newlocale (LC_MESSAGES_MASK, locale_id, (locale_t) 0);
+ if (locale == (locale_t) 0)
+ {
+ if (errno == ENOENT)
+ g_debug ("Failed to create locale %s: %s", locale_id, g_strerror (errno));
+ else
+ g_warning ("Failed to create locale %s: %s", locale_id, g_strerror (errno));
+
+ return "Welcome!";
+ }
+
+ old_locale = uselocale (locale);
+
+ /* Translators: This is meant to be a warm, engaging welcome message,
+ * like greeting somebody at the door. If the exclamation mark is not
+ * suitable for this in your language you may replace it.
+ */
+ welcome = _("Welcome!");
+
+ uselocale (old_locale);
+ freelocale (locale);
+
+ return welcome;
+}
+
+static GtkWidget *
+big_label (const char *text)
+{
+ GtkWidget *label = gtk_label_new (text);
+
+ gtk_style_context_add_class (gtk_widget_get_style_context (label), "title-1");
+
+ return label;
+}
+
+static void
+fill_carousel (GisWelcomeWidget *widget)
+{
+ GisWelcomeWidgetPrivate *priv = gis_welcome_widget_get_instance_private (widget);
+ g_autoptr(GHashTable) initial = cc_common_language_get_initial_languages ();
+ GHashTableIter iter;
+ gpointer key, value;
+ g_autoptr(GHashTable) added_translations = NULL;
+
+ added_translations = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_iter_init (&iter, initial);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ const char *locale_id = key;
+ const char *text;
+ GtkWidget *label;
+
+ if (!cc_common_language_has_font (locale_id))
+ continue;
+
+ text = welcome (locale_id);
+ label = g_hash_table_lookup (added_translations, text);
+ if (label == NULL) {
+ label = big_label (text);
+ adw_carousel_append (priv->carousel, label);
+ g_hash_table_insert (added_translations, (gpointer) text, label);
+ }
+
+ g_hash_table_insert (priv->translation_widgets, g_strdup (locale_id), label);
+ }
+}
+
+static void
+gis_welcome_widget_constructed (GObject *object)
+{
+ G_OBJECT_CLASS (gis_welcome_widget_parent_class)->constructed (object);
+
+ fill_carousel (GIS_WELCOME_WIDGET (object));
+}
+
+static void
+gis_welcome_widget_dispose (GObject *object)
+{
+ GisWelcomeWidget *widget = GIS_WELCOME_WIDGET (object);
+ GisWelcomeWidgetPrivate *priv = gis_welcome_widget_get_instance_private (widget);
+
+ g_clear_pointer (&priv->translation_widgets, g_hash_table_unref);
+
+ G_OBJECT_CLASS (gis_welcome_widget_parent_class)->dispose (object);
+}
+
+static void
+gis_welcome_widget_class_init (GisWelcomeWidgetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/initial-setup/gis-welcome-widget.ui");
+
+ gtk_widget_class_bind_template_child_private (widget_class, GisWelcomeWidget, carousel);
+
+ object_class->constructed = gis_welcome_widget_constructed;
+ object_class->dispose = gis_welcome_widget_dispose;
+ widget_class->map = gis_welcome_widget_map;
+ widget_class->unmap = gis_welcome_widget_unmap;
+}
+
+static void
+gis_welcome_widget_init (GisWelcomeWidget *widget)
+{
+ GisWelcomeWidgetPrivate *priv = gis_welcome_widget_get_instance_private (widget);
+
+ priv->translation_widgets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ gtk_widget_init_template (GTK_WIDGET (widget));
+}
+
+void
+gis_welcome_widget_show_locale (GisWelcomeWidget *widget,
+ const char *locale_id)
+{
+ GisWelcomeWidgetPrivate *priv = gis_welcome_widget_get_instance_private (widget);
+ GtkWidget *label;
+
+ /* Restart the widget to reset the timer. */
+ gis_welcome_widget_stop (widget);
+ gis_welcome_widget_start (widget);
+
+ label = g_hash_table_lookup (priv->translation_widgets, locale_id);
+ if (label)
+ adw_carousel_scroll_to (priv->carousel, label, FALSE);
+}
diff --git a/gnome-initial-setup/pages/language/gis-welcome-widget.h b/gnome-initial-setup/pages/language/gis-welcome-widget.h
new file mode 100644
index 0000000..212b45f
--- /dev/null
+++ b/gnome-initial-setup/pages/language/gis-welcome-widget.h
@@ -0,0 +1,56 @@
+/* -*- 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Jasper St. Pierre <jstpierre@mecheye.net>
+ */
+
+#ifndef __GIS_WELCOME_WIDGET_H__
+#define __GIS_WELCOME_WIDGET_H__
+
+#include <adwaita.h>
+
+G_BEGIN_DECLS
+
+#define GIS_TYPE_WELCOME_WIDGET (gis_welcome_widget_get_type ())
+#define GIS_WELCOME_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_WELCOME_WIDGET, GisWelcomeWidget))
+#define GIS_WELCOME_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_WELCOME_WIDGET, GisWelcomeWidgetClass))
+#define GIS_IS_WELCOME_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_WELCOME_WIDGET))
+#define GIS_IS_WELCOME_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_WELCOME_WIDGET))
+#define GIS_WELCOME_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_WELCOME_WIDGET, GisWelcomeWidgetClass))
+
+typedef struct _GisWelcomeWidget GisWelcomeWidget;
+typedef struct _GisWelcomeWidgetClass GisWelcomeWidgetClass;
+
+struct _GisWelcomeWidget
+{
+ AdwBin parent;
+};
+
+struct _GisWelcomeWidgetClass
+{
+ AdwBinClass parent_class;
+};
+
+GType gis_welcome_widget_get_type (void);
+
+void gis_welcome_widget_show_locale (GisWelcomeWidget *widget,
+ const char *locale_id);
+
+G_END_DECLS
+
+#endif /* __GIS_WELCOME_WIDGET_H__ */
diff --git a/gnome-initial-setup/pages/language/gis-welcome-widget.ui b/gnome-initial-setup/pages/language/gis-welcome-widget.ui
new file mode 100644
index 0000000..f6a26df
--- /dev/null
+++ b/gnome-initial-setup/pages/language/gis-welcome-widget.ui
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="3.0"/>
+ <template class="GisWelcomeWidget" parent="AdwBin">
+ <property name="margin-top">10</property>
+ <property name="margin-bottom">10</property>
+ <child>
+ <object class="AdwCarousel" id="carousel">
+ <property name="halign">center</property>
+ <property name="interactive">False</property>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/gnome-initial-setup/pages/language/language-chooser.ui b/gnome-initial-setup/pages/language/language-chooser.ui
new file mode 100644
index 0000000..90ee096
--- /dev/null
+++ b/gnome-initial-setup/pages/language/language-chooser.ui
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<interface>
+ <template class="CcLanguageChooser" parent="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkSearchEntry" id="filter_entry">
+ <property name="hexpand">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBox" id="language_list">
+ <property name="vexpand">True</property>
+ <property name="halign">fill</property>
+ <property name="valign">start</property>
+ <style>
+ <class name="boxed-list" />
+ </style>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/gnome-initial-setup/pages/language/language.gresource.xml b/gnome-initial-setup/pages/language/language.gresource.xml
new file mode 100644
index 0000000..de0688d
--- /dev/null
+++ b/gnome-initial-setup/pages/language/language.gresource.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/initial-setup">
+ <file preprocess="xml-stripblanks" alias="gis-language-page.ui">gis-language-page.ui</file>
+ <file preprocess="xml-stripblanks" alias="gis-welcome-widget.ui">gis-welcome-widget.ui</file>
+ </gresource>
+ <gresource prefix="/org/gnome/control-center">
+ <file preprocess="xml-stripblanks" alias="language-chooser.ui">language-chooser.ui</file>
+ </gresource>
+</gresources>
diff --git a/gnome-initial-setup/pages/language/meson.build b/gnome-initial-setup/pages/language/meson.build
new file mode 100644
index 0000000..ef6ba3f
--- /dev/null
+++ b/gnome-initial-setup/pages/language/meson.build
@@ -0,0 +1,16 @@
+sources += gnome.compile_resources(
+ 'language-resources',
+ files('language.gresource.xml'),
+ c_name: 'language'
+)
+
+sources += files(
+ 'cc-language-chooser.c',
+ 'cc-language-chooser.h',
+ 'cc-util.c',
+ 'cc-util.h',
+ 'gis-welcome-widget.c',
+ 'gis-welcome-widget.h',
+ 'gis-language-page.c',
+ 'gis-language-page.h',
+)