[gnome-initial-setup] language: Split the language chooser out into its own widget



commit 1f5640bc298d70b14e37b42d05846024300d4122
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Fri Mar 15 17:14:41 2013 -0400

    language: Split the language chooser out into its own widget
    
    This will hopefully make it easier to share with the control center.

 gnome-initial-setup/pages/language/Makefile.am     |    1 +
 .../pages/language/gis-language-chooser.c          |  586 ++++++++++++++++++++
 .../pages/language/gis-language-chooser.h          |   65 +++
 .../pages/language/gis-language-chooser.ui         |   40 ++
 .../pages/language/gis-language-page.c             |  438 +--------------
 .../pages/language/gis-language-page.ui            |   28 +-
 .../pages/language/language.gresource.xml          |    1 +
 7 files changed, 707 insertions(+), 452 deletions(-)
---
diff --git a/gnome-initial-setup/pages/language/Makefile.am b/gnome-initial-setup/pages/language/Makefile.am
index b70f919..ed15e7e 100644
--- a/gnome-initial-setup/pages/language/Makefile.am
+++ b/gnome-initial-setup/pages/language/Makefile.am
@@ -20,6 +20,7 @@ BUILT_SOURCES += language-resources.c language-resources.h
 libgislanguage_la_SOURCES =                            \
        cc-common-language.c cc-common-language.h       \
        cc-util.c cc-util.h                             \
+       gis-language-chooser.c gis-language-chooser.h   \
        gis-language-page.c gis-language-page.h         \
        $(BUILT_SOURCES)
 
diff --git a/gnome-initial-setup/pages/language/gis-language-chooser.c 
b/gnome-initial-setup/pages/language/gis-language-chooser.c
new file mode 100644
index 0000000..c0b0e73
--- /dev/null
+++ b/gnome-initial-setup/pages/language/gis-language-chooser.c
@@ -0,0 +1,586 @@
+/* -*- 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:
+ *     Jasper St. Pierre <jstpierre mecheye net>
+ *     Matthias Clasen <mclasen redhat com>
+ */
+
+#include "config.h"
+#include "gis-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>
+
+#include <egg-list-box.h>
+
+G_DEFINE_TYPE (GisLanguageChooser, gis_language_chooser, GTK_TYPE_BIN);
+
+#define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GIS_TYPE_LANGUAGE_CHOOSER, 
GisLanguageChooserPrivate))
+
+enum {
+  PROP_0,
+  PROP_LANGUAGE,
+  PROP_LAST,
+};
+
+static GParamSpec *obj_props[PROP_LAST];
+
+struct _GisLanguageChooserPrivate
+{
+  GtkWidget *no_results;
+  GtkWidget *more_item;
+  GtkWidget *chooser;
+  GtkWidget *filter_entry;
+  GtkWidget *language_list;
+  gboolean adding_languages;
+  gboolean showing_extra;
+  gchar **filter_words;
+  gchar *language;
+};
+
+typedef struct {
+  GtkWidget *box;
+  GtkWidget *checkmark;
+
+  gchar *locale_id;
+  gchar *locale_name;
+  gchar *locale_current_name;
+  gchar *locale_untranslated_name;
+  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, 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);
+  gtk_widget_show_all (widget);
+  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);
+}
+
+static GtkWidget *
+language_widget_new (const char *locale_id,
+                     gboolean    is_extra)
+{
+  gchar *locale_name, *locale_current_name, *locale_untranslated_name;
+  LanguageWidget *widget = g_new0 (LanguageWidget, 1);
+
+  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 = padded_label_new (locale_name);
+  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,
+                      FALSE, FALSE, 0);
+
+  g_object_set_data_full (G_OBJECT (widget->box), "language-widget", widget,
+                          language_widget_free);
+
+  return widget->box;
+}
+
+static void
+sync_checkmark (GtkWidget *child,
+                gpointer   user_data)
+{
+  LanguageWidget *widget = get_language_widget (child);
+  gchar *locale_id;
+  gboolean should_be_visible;
+
+  if (widget == NULL)
+    return;
+
+  locale_id = user_data;
+  should_be_visible = g_str_equal (widget->locale_id, locale_id);
+  gtk_widget_set_visible (widget->checkmark, should_be_visible);
+}
+
+static void
+sync_all_checkmarks (GisLanguageChooser *chooser)
+{
+  GisLanguageChooserPrivate *priv = chooser->priv;
+
+  gtk_container_foreach (GTK_CONTAINER (priv->language_list),
+                         sync_checkmark, priv->language);
+}
+
+static GtkWidget *
+more_widget_new (void)
+{
+  GtkWidget *widget = padded_label_new ("…");
+  gtk_widget_set_tooltip_text (widget, _("More…"));
+  return widget;
+}
+
+static GtkWidget *
+no_results_widget_new (void)
+{
+  GtkWidget *widget = padded_label_new (_("No languages found"));
+  gtk_widget_set_sensitive (widget, FALSE);
+  return widget;
+}
+
+static void
+add_languages (GisLanguageChooser  *chooser,
+               char               **locale_ids,
+               GHashTable          *initial)
+{
+  GisLanguageChooserPrivate *priv = chooser->priv;
+
+  priv->adding_languages = TRUE;
+
+  while (*locale_ids) {
+    const gchar *locale_id;
+    gboolean is_extra;
+    GtkWidget *widget;
+
+    locale_id = *locale_ids;
+
+    locale_ids ++;
+
+    if (!cc_common_language_has_font (locale_id))
+      continue;
+
+    is_extra = (g_hash_table_lookup (initial, locale_id) != NULL);
+
+    widget = language_widget_new (locale_id, is_extra);
+
+    gtk_container_add (GTK_CONTAINER (priv->language_list),
+                       widget);
+  }
+
+  gtk_container_add (GTK_CONTAINER (priv->language_list),
+                     priv->more_item);
+  gtk_container_add (GTK_CONTAINER (priv->language_list),
+                     priv->no_results);
+
+  gtk_widget_show (priv->language_list);
+
+  priv->adding_languages = FALSE;
+}
+
+static void
+add_all_languages (GisLanguageChooser *chooser)
+{
+  char **locale_ids = gnome_get_all_locales ();
+  GHashTable *initial = cc_common_language_get_initial_languages ();
+
+  add_languages (chooser, locale_ids, initial);
+
+  g_hash_table_destroy (initial);
+  g_strfreev (locale_ids);
+}
+
+static gboolean
+match_all (gchar **words,
+           const gchar *str)
+{
+  gchar **w;
+
+  for (w = words; *w; ++w)
+    if (!strstr (str, *w))
+      return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+language_visible (GtkWidget *child,
+                  gpointer   user_data)
+{
+  GisLanguageChooser *chooser = user_data;
+  GisLanguageChooserPrivate *priv = chooser->priv;
+  gchar *locale_name = NULL;
+  gchar *locale_current_name = NULL;
+  gchar *locale_untranslated_name = NULL;
+  LanguageWidget *widget;
+  gboolean visible;
+
+  if (child == priv->more_item)
+    return !priv->showing_extra;
+
+  /* We hide this in the after-refilter handler below. */
+  if (child == priv->no_results)
+    return TRUE;
+
+  widget = get_language_widget (child);
+
+  if (!priv->showing_extra && !widget->is_extra)
+    return FALSE;
+
+  if (!priv->filter_words)
+    return TRUE;
+
+  visible = FALSE;
+
+  locale_name = cc_util_normalize_casefold_and_unaccent (widget->locale_name);
+  visible = match_all (priv->filter_words, locale_name);
+  if (visible)
+    goto out;
+
+  locale_current_name = cc_util_normalize_casefold_and_unaccent (widget->locale_current_name);
+  visible = match_all (priv->filter_words, locale_current_name);
+  if (visible)
+    goto out;
+
+  locale_untranslated_name = cc_util_normalize_casefold_and_unaccent (widget->locale_untranslated_name);
+  visible = match_all (priv->filter_words, locale_untranslated_name);
+  if (visible)
+    goto out;
+
+ out:
+  g_free (locale_untranslated_name);
+  g_free (locale_current_name);
+  g_free (locale_name);
+  return visible;
+}
+
+static gint
+sort_languages (gconstpointer a,
+                gconstpointer b,
+                gpointer      data)
+{
+  LanguageWidget *la, *lb;
+
+  la = get_language_widget (GTK_WIDGET (a));
+  lb = get_language_widget (GTK_WIDGET (b));
+
+  if (la == NULL)
+    return 1;
+
+  if (lb == NULL)
+    return -1;
+
+  return strcmp (la->locale_name, lb->locale_name);
+}
+
+static void
+filter_changed (GtkEntry        *entry,
+                GisLanguageChooser *chooser)
+{
+  GisLanguageChooserPrivate *priv = chooser->priv;
+  gchar *filter_contents = NULL;
+
+  g_clear_pointer (&priv->filter_words, g_strfreev);
+
+  filter_contents =
+    cc_util_normalize_casefold_and_unaccent (gtk_entry_get_text (GTK_ENTRY (priv->filter_entry)));
+  if (!filter_contents) {
+    egg_list_box_refilter (EGG_LIST_BOX (priv->language_list));
+    return;
+  }
+  priv->filter_words = g_strsplit_set (g_strstrip (filter_contents), " ", 0);
+  g_free (filter_contents);
+  egg_list_box_refilter (EGG_LIST_BOX (priv->language_list));
+}
+
+static void
+show_more (GisLanguageChooser *chooser)
+{
+  GisLanguageChooserPrivate *priv = chooser->priv;
+
+  gtk_widget_show (priv->filter_entry);
+  gtk_widget_grab_focus (priv->filter_entry);
+
+  priv->showing_extra = TRUE;
+
+  egg_list_box_refilter (EGG_LIST_BOX (priv->language_list));
+}
+
+static void
+set_locale_id (GisLanguageChooser *chooser,
+               const gchar        *new_locale_id)
+{
+  GisLanguageChooserPrivate *priv = chooser->priv;
+
+  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 void
+child_activated (EggListBox      *box,
+                 GtkWidget       *child,
+                 GisLanguageChooser *chooser)
+{
+  if (chooser->priv->adding_languages)
+    return;
+
+  if (child == NULL)
+    return;
+  else if (child == chooser->priv->no_results)
+    return;
+  else if (child == chooser->priv->more_item)
+    show_more (chooser);
+  else
+    {
+      LanguageWidget *widget = get_language_widget (child);
+      set_locale_id (chooser, widget->locale_id);
+    }
+}
+
+typedef struct {
+  gint count;
+  GtkWidget *ignore;
+} CountChildrenData;
+
+static void
+count_visible_children (GtkWidget *widget,
+                        gpointer   user_data)
+{
+  CountChildrenData *data = user_data;
+  if (widget != data->ignore &&
+      gtk_widget_get_child_visible (widget) &&
+      gtk_widget_get_visible (widget))
+    data->count++;
+}
+
+static void
+end_refilter (EggListBox *list_box,
+              gpointer    user_data)
+{
+  GisLanguageChooser *chooser = GIS_LANGUAGE_CHOOSER (user_data);
+  GisLanguageChooserPrivate *priv = chooser->priv;
+
+  CountChildrenData data = { 0 };
+
+  data.ignore = priv->no_results;
+
+  gtk_container_foreach (GTK_CONTAINER (list_box),
+                         count_visible_children, &data);
+
+  gtk_widget_set_visible (priv->no_results, (data.count == 0));
+}
+
+static void
+update_separator_func (GtkWidget **separator,
+                       GtkWidget  *child,
+                       GtkWidget  *before,
+                       gpointer    user_data)
+{
+  if (before == NULL)
+    return;
+
+  if (*separator == NULL)
+    {
+      *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+      g_object_ref_sink (*separator);
+      gtk_widget_show (*separator);
+    }
+}
+
+#define WID(name) ((GtkWidget *) gtk_builder_get_object (builder, name))
+
+static void
+gis_language_chooser_constructed (GObject *object)
+{
+  GtkBuilder *builder;
+  GisLanguageChooser *chooser = GIS_LANGUAGE_CHOOSER (object);
+  GisLanguageChooserPrivate *priv = chooser->priv;
+  GError *error = NULL;
+
+  G_OBJECT_CLASS (gis_language_chooser_parent_class)->constructed (object);
+
+  builder = gtk_builder_new ();
+  if (gtk_builder_add_from_resource (builder, "/org/gnome/initial-setup/gis-language-chooser.ui", &error) == 
0) {
+    g_object_unref (builder);
+    g_warning ("failed to load language chooser: %s", error->message);
+    g_error_free (error);
+    return;
+  }
+
+  gtk_container_add (GTK_CONTAINER (chooser), WID ("language-chooser"));
+
+  priv->filter_entry = WID ("language-filter-entry");
+  priv->language_list = WID ("language-list");
+  priv->more_item = more_widget_new ();
+  priv->no_results = no_results_widget_new ();
+
+  egg_list_box_set_adjustment (EGG_LIST_BOX (priv->language_list),
+                               gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (WID 
("language-scrolledwindow"))));
+
+  egg_list_box_set_sort_func (EGG_LIST_BOX (priv->language_list),
+                              sort_languages, chooser, NULL);
+  egg_list_box_set_filter_func (EGG_LIST_BOX (priv->language_list),
+                                language_visible, chooser, NULL);
+  egg_list_box_set_separator_funcs (EGG_LIST_BOX (priv->language_list),
+                                    update_separator_func, chooser, NULL);
+
+  egg_list_box_set_selection_mode (EGG_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, "child-activated",
+                    G_CALLBACK (child_activated), chooser);
+
+  g_signal_connect_after (priv->language_list, "refilter",
+                          G_CALLBACK (end_refilter), chooser);
+
+  egg_list_box_refilter (EGG_LIST_BOX (priv->language_list));
+
+  if (priv->language == NULL)
+    priv->language = cc_common_language_get_current_language ();
+
+  sync_all_checkmarks (chooser);
+}
+
+static void
+gis_language_chooser_get_property (GObject      *object,
+                                   guint         prop_id,
+                                   GValue       *value,
+                                   GParamSpec   *pspec)
+{
+  GisLanguageChooser *chooser = GIS_LANGUAGE_CHOOSER (object);
+  switch (prop_id)
+    {
+    case PROP_LANGUAGE:
+      g_value_set_string (value, gis_language_chooser_get_language (chooser));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gis_language_chooser_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  GisLanguageChooser *chooser = GIS_LANGUAGE_CHOOSER (object);
+  switch (prop_id)
+    {
+    case PROP_LANGUAGE:
+      gis_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
+gis_language_chooser_finalize (GObject *object)
+{
+  GisLanguageChooser *chooser = GIS_LANGUAGE_CHOOSER (object);
+  GisLanguageChooserPrivate *priv = chooser->priv;
+
+  g_strfreev (priv->filter_words);
+}
+
+static void
+gis_language_chooser_class_init (GisLanguageChooserClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (object_class, sizeof(GisLanguageChooserPrivate));
+
+  object_class->get_property = gis_language_chooser_get_property;
+  object_class->set_property = gis_language_chooser_set_property;
+  object_class->finalize = gis_language_chooser_finalize;
+  object_class->constructed = gis_language_chooser_constructed;
+
+  obj_props[PROP_LANGUAGE] =
+    g_param_spec_string ("language", "", "", "",
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, PROP_LAST, obj_props);
+}
+
+void
+gis_language_chooser_init (GisLanguageChooser *chooser)
+{
+  chooser->priv = GET_PRIVATE (chooser);
+}
+
+void
+gis_language_chooser_clear_filter (GisLanguageChooser *chooser)
+{
+  GisLanguageChooserPrivate *priv = chooser->priv;
+  gtk_entry_set_text (GTK_ENTRY (priv->filter_entry), "");
+}
+
+const gchar *
+gis_language_chooser_get_language (GisLanguageChooser *chooser)
+{
+  GisLanguageChooserPrivate *priv = chooser->priv;
+  return priv->language;
+}
+
+void
+gis_language_chooser_set_language (GisLanguageChooser *chooser,
+                                   const gchar        *language)
+{
+  set_locale_id (chooser, language);
+}
diff --git a/gnome-initial-setup/pages/language/gis-language-chooser.h 
b/gnome-initial-setup/pages/language/gis-language-chooser.h
new file mode 100644
index 0000000..bfece3a
--- /dev/null
+++ b/gnome-initial-setup/pages/language/gis-language-chooser.h
@@ -0,0 +1,65 @@
+/* -*- 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:
+ *     Jasper St. Pierre <jstpierre mecheye net>
+ *     Matthias Clasen <mclasen redhat com>
+ */
+
+#ifndef __GIS_LANGUAGE_CHOOSER_H__
+#define __GIS_LANGUAGE_CHOOSER_H__
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+#define GIS_TYPE_LANGUAGE_CHOOSER            (gis_language_chooser_get_type ())
+#define GIS_LANGUAGE_CHOOSER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_LANGUAGE_CHOOSER, 
GisLanguageChooser))
+#define GIS_LANGUAGE_CHOOSER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GIS_TYPE_LANGUAGE_CHOOSER, 
GisLanguageChooserClass))
+#define GIS_IS_LANGUAGE_CHOOSER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_LANGUAGE_CHOOSER))
+#define GIS_IS_LANGUAGE_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GIS_TYPE_LANGUAGE_CHOOSER))
+#define GIS_LANGUAGE_CHOOSER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GIS_TYPE_LANGUAGE_CHOOSER, 
GisLanguageChooserClass))
+
+G_BEGIN_DECLS
+
+typedef struct _GisLanguageChooser        GisLanguageChooser;
+typedef struct _GisLanguageChooserClass   GisLanguageChooserClass;
+typedef struct _GisLanguageChooserPrivate GisLanguageChooserPrivate;
+
+struct _GisLanguageChooser
+{
+  GtkBin parent;
+
+  GisLanguageChooserPrivate *priv;
+};
+
+struct _GisLanguageChooserClass
+{
+  GtkBinClass parent_class;
+};
+
+GType gis_language_chooser_get_type (void);
+
+void          gis_language_chooser_clear_filter (GisLanguageChooser *chooser);
+const gchar * gis_language_chooser_get_language (GisLanguageChooser *chooser);
+void          gis_language_chooser_set_language (GisLanguageChooser *chooser,
+                                                 const gchar        *language);
+
+G_END_DECLS
+
+#endif /* __GIS_LANGUAGE_CHOOSER_H__ */
diff --git a/gnome-initial-setup/pages/language/gis-language-chooser.ui 
b/gnome-initial-setup/pages/language/gis-language-chooser.ui
new file mode 100644
index 0000000..77cb99d
--- /dev/null
+++ b/gnome-initial-setup/pages/language/gis-language-chooser.ui
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <object class="GtkBox" id="language-chooser">
+    <property name="name">language-chooser</property>
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <property name="spacing">10</property>
+    <child>
+      <object class="GtkScrolledWindow" id="language-scrolledwindow">
+        <property name="visible">True</property>
+        <property name="hscrollbar-policy">never</property>
+        <property name="vscrollbar-policy">automatic</property>
+        <property name="shadow-type">in</property>
+        <child>
+          <object class="GtkViewport" id="language-viewport">
+            <property name="visible">True</property>
+            <child>
+              <object class="EggListBox" id="language-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>
+    </child>
+    <child>
+      <object class="GtkSearchEntry" id="language-filter-entry">
+        <property name="visible">False</property>
+        <property name="hexpand">True</property>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/gnome-initial-setup/pages/language/gis-language-page.c 
b/gnome-initial-setup/pages/language/gis-language-page.c
index ad94fe0..8f551a9 100644
--- a/gnome-initial-setup/pages/language/gis-language-page.c
+++ b/gnome-initial-setup/pages/language/gis-language-page.c
@@ -27,391 +27,32 @@
 
 #include "config.h"
 #include "language-resources.h"
+#include "gis-language-chooser.h"
 #include "gis-language-page.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>
-
-#include <egg-list-box.h>
-
 G_DEFINE_TYPE (GisLanguagePage, gis_language_page, GIS_TYPE_PAGE);
 
 #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GIS_TYPE_LANGUAGE_PAGE, GisLanguagePagePrivate))
 
 struct _GisLanguagePagePrivate
 {
-  GtkWidget *no_results;
-  GtkWidget *more_item;
-  GtkWidget *page;
-  GtkWidget *filter_entry;
-  GtkWidget *language_list;
-  gboolean adding_languages;
-  gboolean showing_extra;
-  gchar **filter_words;
+  GtkWidget *language_chooser;
 };
 
 #define OBJ(type,name) ((type)gtk_builder_get_object(GIS_PAGE (page)->builder,(name)))
 #define WID(name) OBJ(GtkWidget*,name)
 
-typedef struct {
-  GtkWidget *box;
-  GtkWidget *checkmark;
-
-  gchar *locale_id;
-  gchar *locale_name;
-  gchar *locale_current_name;
-  gchar *locale_untranslated_name;
-  gboolean is_extra;
-} LanguageWidget;
-
-static LanguageWidget *
-get_language_widget (GtkWidget *widget)
-{
-  return g_object_get_data (G_OBJECT (widget), "language-widget");
-}
-
-static void
-set_locale_id (GisLanguagePage *page,
-               gchar           *new_locale_id)
-{
-  gchar *old_locale_id = cc_common_language_get_current_language ();
-  if (g_strcmp0 (old_locale_id, new_locale_id) != 0) {
-    setlocale (LC_MESSAGES, new_locale_id);
-    gis_driver_locale_changed (GIS_PAGE (page)->driver);
-  }
-  g_free (old_locale_id);
-}
-
-static gint
-sort_languages (gconstpointer a,
-                gconstpointer b,
-                gpointer      data)
-{
-  LanguageWidget *la, *lb;
-
-  la = get_language_widget (GTK_WIDGET (a));
-  lb = get_language_widget (GTK_WIDGET (b));
-
-  if (la == NULL)
-    return 1;
-
-  if (lb == NULL)
-    return -1;
-
-  return strcmp (la->locale_name, lb->locale_name);
-}
-
-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);
-  gtk_widget_show_all (widget);
-  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);
-}
-
-static void
-language_widget_sync_show_checkmark (LanguageWidget *widget)
-{
-  gchar *current_locale_id = cc_common_language_get_current_language ();
-  gboolean should_be_visible = g_str_equal (widget->locale_id, current_locale_id);
-  gtk_widget_set_visible (widget->checkmark, should_be_visible);
-  g_free (current_locale_id);
-}
-
-static GtkWidget *
-language_widget_new (const char *locale_id,
-                     gboolean    is_extra)
-{
-  gchar *locale_name, *locale_current_name, *locale_untranslated_name;
-  LanguageWidget *widget = g_new0 (LanguageWidget, 1);
-
-  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 = padded_label_new (locale_name);
-  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,
-                      FALSE, FALSE, 0);
-
-  language_widget_sync_show_checkmark (widget);
-
-  g_object_set_data_full (G_OBJECT (widget->box), "language-widget", widget,
-                          language_widget_free);
-
-  return widget->box;
-}
-
-static GtkWidget *
-more_widget_new (void)
-{
-  GtkWidget *widget = padded_label_new ("…");
-  gtk_widget_set_tooltip_text (widget, _("More…"));
-  return widget;
-}
-
-static GtkWidget *
-no_results_widget_new (void)
-{
-  GtkWidget *widget = padded_label_new (_("No languages found"));
-  gtk_widget_set_sensitive (widget, FALSE);
-  return widget;
-}
-
 static void
-add_languages (GisLanguagePage *page,
-               char           **locale_ids,
-               GHashTable      *initial)
+language_changed (GisLanguageChooser *chooser,
+                  GParamSpec         *pspec,
+                  GisLanguagePage    *page)
 {
-  GisLanguagePagePrivate *priv = page->priv;
-
-  priv->adding_languages = TRUE;
-
-  while (*locale_ids) {
-    const gchar *locale_id;
-    gboolean is_extra;
-    GtkWidget *widget;
-
-    locale_id = *locale_ids;
-
-    locale_ids ++;
-
-    if (!cc_common_language_has_font (locale_id))
-      continue;
-
-    is_extra = (g_hash_table_lookup (initial, locale_id) != NULL);
-
-    widget = language_widget_new (locale_id, is_extra);
-
-    gtk_container_add (GTK_CONTAINER (priv->language_list),
-                       widget);
-  }
-
-  gtk_container_add (GTK_CONTAINER (priv->language_list),
-                     priv->more_item);
-  gtk_container_add (GTK_CONTAINER (priv->language_list),
-                     priv->no_results);
-
-  gtk_widget_show (priv->language_list);
-
-  priv->adding_languages = FALSE;
-}
-
-static void
-add_all_languages (GisLanguagePage *page)
-{
-  char **locale_ids = gnome_get_all_locales ();
-  GHashTable *initial =  cc_common_language_get_initial_languages ();
-
-  add_languages (page, locale_ids, initial);
-
-  g_hash_table_destroy (initial);
-  g_strfreev (locale_ids);
-}
-
-static gboolean
-match_all (gchar **words,
-           const gchar *str)
-{
-  gchar **w;
-
-  for (w = words; *w; ++w)
-    if (!strstr (str, *w))
-      return FALSE;
-
-  return TRUE;
-}
-
-static gboolean
-language_visible (GtkWidget *child,
-                  gpointer   user_data)
-{
-  GisLanguagePage *page = user_data;
-  GisLanguagePagePrivate *priv = page->priv;
-  gchar *locale_name = NULL;
-  gchar *locale_current_name = NULL;
-  gchar *locale_untranslated_name = NULL;
-  LanguageWidget *widget;
-  gboolean visible;
-
-  if (child == priv->more_item)
-    return !priv->showing_extra;
-
-  /* We hide this in the after-refilter handler below. */
-  if (child == priv->no_results)
-    return TRUE;
-
-  widget = get_language_widget (child);
-
-  if (!priv->showing_extra && !widget->is_extra)
-    return FALSE;
-
-  if (!priv->filter_words)
-    return TRUE;
-
-  visible = FALSE;
-
-  locale_name = cc_util_normalize_casefold_and_unaccent (widget->locale_name);
-  visible = match_all (priv->filter_words, locale_name);
-  if (visible)
-    goto out;
-
-  locale_current_name = cc_util_normalize_casefold_and_unaccent (widget->locale_current_name);
-  visible = match_all (priv->filter_words, locale_current_name);
-  if (visible)
-    goto out;
-
-  locale_untranslated_name = cc_util_normalize_casefold_and_unaccent (widget->locale_untranslated_name);
-  visible = match_all (priv->filter_words, locale_untranslated_name);
-  if (visible)
-    goto out;
-
- out:
-  g_free (locale_untranslated_name);
-  g_free (locale_current_name);
-  g_free (locale_name);
-  return visible;
-}
-
-static void
-filter_changed (GtkEntry        *entry,
-                GisLanguagePage *page)
-{
-  GisLanguagePagePrivate *priv = page->priv;
-  gchar *filter_contents = NULL;
-
-  g_clear_pointer (&priv->filter_words, g_strfreev);
-
-  filter_contents =
-    cc_util_normalize_casefold_and_unaccent (gtk_entry_get_text (GTK_ENTRY (priv->filter_entry)));
-  if (!filter_contents) {
-    egg_list_box_refilter (EGG_LIST_BOX (priv->language_list));
-    return;
-  }
-  priv->filter_words = g_strsplit_set (g_strstrip (filter_contents), " ", 0);
-  g_free (filter_contents);
-  egg_list_box_refilter (EGG_LIST_BOX (priv->language_list));
-}
-
-static void
-show_more (GisLanguagePage *page)
-{
-  GisLanguagePagePrivate *priv = page->priv;
-
-  gtk_widget_show (priv->filter_entry);
-  gtk_widget_grab_focus (priv->filter_entry);
-
-  priv->showing_extra = TRUE;
-
-  egg_list_box_refilter (EGG_LIST_BOX (priv->language_list));
-}
-
-static void
-child_activated (EggListBox      *box,
-                 GtkWidget       *child,
-                 GisLanguagePage *page)
-{
-  if (page->priv->adding_languages)
-    return;
-
-  if (child == NULL)
-    return;
-  else if (child == page->priv->no_results)
-    return;
-  else if (child == page->priv->more_item)
-    show_more (page);
-  else
-    {
-      LanguageWidget *widget = get_language_widget (child);
-      set_locale_id (page, widget->locale_id);
-    }
-}
-
-typedef struct {
-  gint count;
-  GtkWidget *ignore;
-} CountChildrenData;
-
-static void
-count_visible_children (GtkWidget *widget,
-                        gpointer   user_data)
-{
-  CountChildrenData *data = user_data;
-  if (widget != data->ignore &&
-      gtk_widget_get_child_visible (widget) &&
-      gtk_widget_get_visible (widget))
-    data->count++;
-}
-
-static void
-end_refilter (EggListBox *list_box,
-              gpointer    user_data)
-{
-  GisLanguagePage *page = GIS_LANGUAGE_PAGE (user_data);
-  GisLanguagePagePrivate *priv = page->priv;
-
-  CountChildrenData data = { 0 };
-
-  data.ignore = priv->no_results;
-
-  gtk_container_foreach (GTK_CONTAINER (list_box),
-                         count_visible_children, &data);
-
-  gtk_widget_set_visible (priv->no_results, (data.count == 0));
-}
-
-static void
-update_separator_func (GtkWidget **separator,
-                       GtkWidget  *child,
-                       GtkWidget  *before,
-                       gpointer    user_data)
-{
-  if (before == NULL)
-    return;
-
-  if (*separator == NULL)
-    {
-      *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
-      g_object_ref_sink (*separator);
-      gtk_widget_show (*separator);
-    }
+  const char *new_locale_id = gis_language_chooser_get_language (chooser);
+  setlocale (LC_MESSAGES, new_locale_id);
+  gis_driver_locale_changed (GIS_PAGE (page)->driver);
 }
 
 static void
@@ -420,40 +61,15 @@ gis_language_page_constructed (GObject *object)
   GisLanguagePage *page = GIS_LANGUAGE_PAGE (object);
   GisLanguagePagePrivate *priv = page->priv;
 
+  g_type_ensure (GIS_TYPE_LANGUAGE_CHOOSER);
+
   G_OBJECT_CLASS (gis_language_page_parent_class)->constructed (object);
 
   gtk_container_add (GTK_CONTAINER (page), WID ("language-page"));
 
-  priv->filter_entry = WID ("language-filter-entry");
-  priv->language_list = WID ("language-list");
-  priv->more_item = more_widget_new ();
-  priv->no_results = no_results_widget_new ();
-
-  egg_list_box_set_adjustment (EGG_LIST_BOX (priv->language_list),
-                               gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (WID 
("language-scrolledwindow"))));
-
-  egg_list_box_set_sort_func (EGG_LIST_BOX (priv->language_list),
-                              sort_languages, page, NULL);
-  egg_list_box_set_filter_func (EGG_LIST_BOX (priv->language_list),
-                                language_visible, page, NULL);
-  egg_list_box_set_separator_funcs (EGG_LIST_BOX (priv->language_list),
-                                    update_separator_func, page, NULL);
-
-  egg_list_box_set_selection_mode (EGG_LIST_BOX (priv->language_list),
-                                   GTK_SELECTION_NONE);
-  add_all_languages (page);
-
-  g_signal_connect (priv->filter_entry, "changed",
-                    G_CALLBACK (filter_changed),
-                    page);
-
-  g_signal_connect (priv->language_list, "child-activated",
-                    G_CALLBACK (child_activated), page);
-
-  g_signal_connect_after (priv->language_list, "refilter",
-                          G_CALLBACK (end_refilter), page);
-
-  egg_list_box_refilter (EGG_LIST_BOX (priv->language_list));
+  priv->language_chooser = WID ("language-chooser");
+  g_signal_connect (priv->language_chooser, "notify::language",
+                    G_CALLBACK (language_changed), page);
 
   gis_page_set_complete (GIS_PAGE (page), TRUE);
 
@@ -461,36 +77,9 @@ gis_language_page_constructed (GObject *object)
 }
 
 static void
-sync_checkmark (GtkWidget *child,
-                gpointer   user_data)
-{
-  LanguageWidget *widget = get_language_widget (child);
-
-  if (widget == NULL)
-    return;
-
-  language_widget_sync_show_checkmark (widget);
-}
-
-static void
 gis_language_page_locale_changed (GisPage *page)
 {
-  GisLanguagePagePrivate *priv = GIS_LANGUAGE_PAGE (page)->priv;
-
   gis_page_set_title (GIS_PAGE (page), _("Welcome"));
-
-  if (priv->language_list)
-    gtk_container_foreach (GTK_CONTAINER (priv->language_list),
-                           sync_checkmark, NULL);
-}
-
-static void
-gis_language_page_finalize (GObject *object)
-{
-  GisLanguagePage *page = GIS_LANGUAGE_PAGE (object);
-  GisLanguagePagePrivate *priv = page->priv;
-
-  g_strfreev (priv->filter_words);
 }
 
 static void
@@ -501,7 +90,6 @@ gis_language_page_class_init (GisLanguagePageClass *klass)
 
   page_class->page_id = PAGE_ID;
   page_class->locale_changed = gis_language_page_locale_changed;
-  object_class->finalize = gis_language_page_finalize;
   object_class->constructed = gis_language_page_constructed;
 
   g_type_class_add_private (object_class, sizeof(GisLanguagePagePrivate));
diff --git a/gnome-initial-setup/pages/language/gis-language-page.ui 
b/gnome-initial-setup/pages/language/gis-language-page.ui
index f0ae737..eec0aee 100644
--- a/gnome-initial-setup/pages/language/gis-language-page.ui
+++ b/gnome-initial-setup/pages/language/gis-language-page.ui
@@ -5,37 +5,11 @@
     <property name="name">language-page</property>
     <property name="visible">True</property>
     <property name="orientation">vertical</property>
-    <property name="spacing">10</property>
     <property name="margin-left">80</property>
     <property name="margin-right">80</property>
     <child>
-      <object class="GtkScrolledWindow" id="language-scrolledwindow">
+      <object class="GisLanguageChooser" id="language-chooser">
         <property name="visible">True</property>
-        <property name="hscrollbar-policy">never</property>
-        <property name="vscrollbar-policy">automatic</property>
-        <property name="shadow-type">in</property>
-        <child>
-          <object class="GtkViewport" id="language-viewport">
-            <property name="visible">True</property>
-            <child>
-              <object class="EggListBox" id="language-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>
-    </child>
-    <child>
-      <object class="GtkSearchEntry" id="language-filter-entry">
-        <property name="visible">False</property>
-        <property name="hexpand">True</property>
       </object>
     </child>
   </object>
diff --git a/gnome-initial-setup/pages/language/language.gresource.xml 
b/gnome-initial-setup/pages/language/language.gresource.xml
index 06a124f..276232b 100644
--- a/gnome-initial-setup/pages/language/language.gresource.xml
+++ b/gnome-initial-setup/pages/language/language.gresource.xml
@@ -2,5 +2,6 @@
 <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-language-chooser.ui">gis-language-chooser.ui</file>
   </gresource>
 </gresources>


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