[gnome-initial-setup] Add liblanguage, pulled from gnome-control-center
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-initial-setup] Add liblanguage, pulled from gnome-control-center
- Date: Fri, 13 Jul 2012 03:35:28 +0000 (UTC)
commit 7f5b7470b80a62ea8624526fb6bcb55cda38962f
Author: Jasper St. Pierre <jstpierre mecheye net>
Date: Thu Jul 12 15:59:17 2012 -0400
Add liblanguage, pulled from gnome-control-center
.gitignore | 2 +
configure.ac | 8 +
gnome-initial-setup/Makefile.am | 2 +
gnome-initial-setup/languages/Makefile.am | 27 +
gnome-initial-setup/languages/cc-common-language.c | 589 +++++++++
gnome-initial-setup/languages/cc-common-language.h | 58 +
.../languages/cc-language-chooser.c | 343 +++++
.../languages/cc-language-chooser.h | 38 +
gnome-initial-setup/languages/gdm-languages.c | 1326 ++++++++++++++++++++
gnome-initial-setup/languages/gdm-languages.h | 44 +
gnome-initial-setup/languages/language-chooser.ui | 114 ++
gnome-initial-setup/languages/list-languages.c | 36 +
gnome-initial-setup/languages/locarchive.h | 97 ++
13 files changed, 2684 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 3a5e806..4bb6108 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,4 +54,6 @@ gnome-initial-setup/timedated.h
gnome-initial-setup/setup_resources.c
gnome-initial-setup/gnome-initial-setup
+gnome-initial-setup/languages/list-languages
+
gnome-initial-setup.desktop
diff --git a/configure.ac b/configure.ac
index fbead81..a48db2a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -31,6 +31,7 @@ PKG_CHECK_MODULES(INITIAL_SETUP,
cheese-gtk >= 3.3.5
clutter-gtk-1.0
clutter-1.0 >= $CLUTTER_REQUIRED_VERSION
+ fontconfig
geoclue
gweather-3.0
goa-1.0
@@ -47,6 +48,12 @@ if test x$have_pwquality = xyes ; then
INITIAL_SETUP_LIBS="$INITIAL_SETUP_LIBS -lpwquality"
fi
+# This is a hard-dependency for the region and user-accounts panels
+PKG_CHECK_MODULES(ISOCODES, iso-codes)
+
+AC_DEFINE_UNQUOTED([ISO_CODES_PREFIX],["`$PKG_CONFIG --variable=prefix iso-codes`"],[ISO codes prefix])
+ISO_CODES=iso-codes
+
AC_SUBST(INITIAL_SETUP_CFLAGS)
AC_SUBST(INITIAL_SETUP_LIBS)
@@ -67,6 +74,7 @@ AC_CONFIG_FILES([
Makefile
data/Makefile
gnome-initial-setup/Makefile
+gnome-initial-setup/languages/Makefile
po/Makefile.in
])
AC_OUTPUT
diff --git a/gnome-initial-setup/Makefile.am b/gnome-initial-setup/Makefile.am
index f78ddc3..7db2c47 100644
--- a/gnome-initial-setup/Makefile.am
+++ b/gnome-initial-setup/Makefile.am
@@ -1,3 +1,5 @@
+SUBDIRS = languages
+
uidir = $(datadir)/gnome-initial-setup
# GNOMECC_DATA_DIR is used in tz.c
diff --git a/gnome-initial-setup/languages/Makefile.am b/gnome-initial-setup/languages/Makefile.am
new file mode 100644
index 0000000..0aa166e
--- /dev/null
+++ b/gnome-initial-setup/languages/Makefile.am
@@ -0,0 +1,27 @@
+noinst_LTLIBRARIES = liblanguage.la
+noinst_PROGRAMS = list-languages
+
+AM_CPPFLAGS = \
+ $(INITIAL_SETUP_CFLAGS) \
+ -DDATADIR=\""$(datadir)"\" \
+ -DUIDIR=\""$(pkgdatadir)/ui"\" \
+ -DLIBLOCALEDIR=\""$(prefix)/lib/locale"\" \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -DUM_PIXMAP_DIR=\""$(pkgdatadir)/pixmaps"\"
+
+liblanguage_la_SOURCES = \
+ gdm-languages.h \
+ gdm-languages.c \
+ locarchive.h \
+ cc-common-language.c \
+ cc-common-language.h \
+ cc-language-chooser.c \
+ cc-language-chooser.h
+
+liblanguage_la_LIBADD = \
+ $(INITIAL_SETUP_LIBS)
+
+liblanguage_la_LDFLAGS = -export_dynamic -avoid-version -module -no-undefined
+
+list_languages_SOURCES = list-languages.c
+list_languages_LDADD = liblanguage.la
diff --git a/gnome-initial-setup/languages/cc-common-language.c b/gnome-initial-setup/languages/cc-common-language.c
new file mode 100644
index 0000000..28973a8
--- /dev/null
+++ b/gnome-initial-setup/languages/cc-common-language.c
@@ -0,0 +1,589 @@
+/* -*- 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 3 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>
+
+#include "cc-common-language.h"
+
+#include "gdm-languages.h"
+
+static gint
+cc_common_language_sort_languages (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer data)
+{
+ char *ca, *cb;
+ char *la, *lb;
+ gboolean sa, ula;
+ gboolean sb, ulb;
+ gint result;
+
+ gtk_tree_model_get (model, a,
+ LOCALE_COL, &ca,
+ DISPLAY_LOCALE_COL, &la,
+ SEPARATOR_COL, &sa,
+ USER_LANGUAGE, &ula,
+ -1);
+ gtk_tree_model_get (model, b,
+ LOCALE_COL, &cb,
+ DISPLAY_LOCALE_COL, &lb,
+ SEPARATOR_COL, &sb,
+ USER_LANGUAGE, &ulb,
+ -1);
+
+ /* Sort before and after separator first */
+ if (sa && sb)
+ return 0;
+ if (sa)
+ return ulb ? 1 : -1;
+ if (sb)
+ return ula ? -1 : 1;
+
+ /* Sort user-languages first */
+ if (ula != ulb) {
+ if (ula)
+ return -1;
+ else
+ return 1;
+ }
+
+ if (!ca)
+ result = 1;
+ else if (!cb)
+ result = -1;
+ else
+ result = strcmp (la, lb);
+
+ g_free (ca);
+ g_free (cb);
+ g_free (la);
+ g_free (lb);
+
+ return result;
+}
+
+static gboolean
+iter_for_language (GtkTreeModel *model,
+ const gchar *lang,
+ GtkTreeIter *iter,
+ gboolean region)
+{
+ char *l;
+ char *name;
+ char *language;
+
+ gtk_tree_model_get_iter_first (model, iter);
+ do {
+ gtk_tree_model_get (model, iter, LOCALE_COL, &l, -1);
+ if (g_strcmp0 (l, lang) == 0) {
+ g_free (l);
+ return TRUE;
+ }
+ g_free (l);
+ } while (gtk_tree_model_iter_next (model, iter));
+
+ name = gdm_normalize_language_name (lang);
+ if (name != NULL) {
+ if (region) {
+ language = gdm_get_region_from_name (name, NULL);
+ }
+ else {
+ language = gdm_get_language_from_name (name, NULL);
+ }
+
+ gtk_list_store_insert_with_values (GTK_LIST_STORE (model),
+ iter,
+ -1,
+ LOCALE_COL, name,
+ DISPLAY_LOCALE_COL, language,
+ -1);
+ g_free (name);
+ g_free (language);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+cc_common_language_get_iter_for_language (GtkTreeModel *model,
+ const gchar *lang,
+ GtkTreeIter *iter)
+{
+ return iter_for_language (model, lang, iter, FALSE);
+}
+
+gboolean
+cc_common_language_get_iter_for_region (GtkTreeModel *model,
+ const gchar *lang,
+ GtkTreeIter *iter)
+{
+ return iter_for_language (model, lang, iter, TRUE);
+}
+
+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 (!gdm_parse_language_name (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;
+}
+
+typedef struct
+{
+ GtkListStore *store;
+ GHashTable *user_langs;
+ gchar **languages;
+ gboolean regions;
+ gint position;
+} AsyncLangData;
+
+static void
+async_lang_data_free (AsyncLangData *data)
+{
+ g_object_unref (data->store);
+ g_hash_table_unref (data->user_langs);
+ g_strfreev (data->languages);
+ g_free (data);
+}
+
+static gboolean
+add_one_language (gpointer d)
+{
+ AsyncLangData *data = d;
+ char *name;
+ char *language;
+ GtkTreeIter iter;
+
+ if (data->languages[data->position] == NULL) {
+ /* we are done */
+ async_lang_data_free (data);
+ return FALSE;
+ }
+
+ name = gdm_normalize_language_name (data->languages[data->position]);
+ if (g_hash_table_lookup (data->user_langs, name) != NULL) {
+ g_free (name);
+ goto next;
+ }
+
+ if (!cc_common_language_has_font (data->languages[data->position])) {
+ g_free (name);
+ goto next;
+ }
+
+ if (data->regions) {
+ language = gdm_get_region_from_name (name, NULL);
+ }
+ else {
+ language = gdm_get_language_from_name (name, NULL);
+ }
+ if (!language) {
+ g_debug ("Ignoring '%s' as a locale, because we couldn't figure the language name", name);
+ g_free (name);
+ goto next;
+ }
+
+ /* Add separator between initial languages and new additions */
+ if (g_object_get_data (G_OBJECT (data->store), "needs-separator")) {
+ GtkTreeIter iter;
+
+ gtk_list_store_insert_with_values (GTK_LIST_STORE (data->store),
+ &iter,
+ -1,
+ LOCALE_COL, NULL,
+ DISPLAY_LOCALE_COL, "Don't show",
+ SEPARATOR_COL, TRUE,
+ USER_LANGUAGE, FALSE,
+ -1);
+ g_object_set_data (G_OBJECT (data->store), "needs-separator", NULL);
+ }
+
+ gtk_list_store_insert_with_values (data->store,
+ &iter,
+ -1,
+ LOCALE_COL, name,
+ DISPLAY_LOCALE_COL, language,
+ -1);
+
+ g_free (name);
+ g_free (language);
+
+ next:
+ data->position++;
+
+ return TRUE;
+}
+
+guint
+cc_common_language_add_available_languages (GtkListStore *store,
+ gboolean regions,
+ GHashTable *user_langs)
+{
+ AsyncLangData *data;
+
+ data = g_new0 (AsyncLangData, 1);
+
+ data->store = g_object_ref (store);
+ data->user_langs = g_hash_table_ref (user_langs);
+ data->languages = gdm_get_all_language_names ();
+ data->regions = regions;
+ data->position = 0;
+
+ return gdk_threads_add_idle (add_one_language, data);
+}
+
+gchar *
+cc_common_language_get_current_language (void)
+{
+ gchar *language;
+ const gchar *locale;
+
+ locale = (const gchar *) setlocale (LC_MESSAGES, NULL);
+ if (locale)
+ language = gdm_normalize_language_name (locale);
+ else
+ language = NULL;
+
+ return language;
+}
+
+static void
+languages_foreach_cb (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GtkListStore *store = (GtkListStore *) user_data;
+ const char *locale = (const char *) key;
+ const char *display_locale = (const char *) value;
+ GtkTreeIter iter;
+
+ gtk_list_store_insert_with_values (store,
+ &iter,
+ -1,
+ LOCALE_COL, locale,
+ DISPLAY_LOCALE_COL, display_locale,
+ SEPARATOR_COL, FALSE,
+ USER_LANGUAGE, TRUE,
+ -1);
+}
+
+static gboolean
+separator_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gboolean is_sep;
+
+ gtk_tree_model_get (model, iter,
+ SEPARATOR_COL, &is_sep,
+ -1);
+
+ return is_sep;
+}
+
+void
+cc_common_language_setup_list (GtkWidget *treeview,
+ GHashTable *initial)
+{
+ GtkCellRenderer *cell;
+ GtkTreeViewColumn *column;
+ GtkListStore *store;
+
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell,
+ "width-chars", 40,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
+ column = gtk_tree_view_column_new_with_attributes (NULL, cell, "text", DISPLAY_LOCALE_COL, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+ store = gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+ gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (store),
+ cc_common_language_sort_languages, NULL, NULL);
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+ GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
+ GTK_SORT_ASCENDING);
+ gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (treeview),
+ separator_func,
+ NULL, NULL);
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
+
+
+ /* Add languages from the initial hashtable */
+ g_hash_table_foreach (initial, (GHFunc) languages_foreach_cb, store);
+
+ /* Mark the need for a separator if we had any languages added */
+ if (initial != NULL &&
+ g_hash_table_size (initial) > 0) {
+ g_object_set_data (G_OBJECT (store), "needs-separator", GINT_TO_POINTER (TRUE));
+ }
+}
+
+void
+cc_common_language_select_current_language (GtkTreeView *treeview)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean cont;
+ char *lang;
+
+ lang = cc_common_language_get_current_language ();
+ model = gtk_tree_view_get_model (treeview);
+ cont = gtk_tree_model_get_iter_first (model, &iter);
+ while (cont) {
+ char *locale;
+
+ gtk_tree_model_get (model, &iter,
+ LOCALE_COL, &locale,
+ -1);
+ if (locale != NULL &&
+ g_str_equal (locale, lang)) {
+ GtkTreeSelection *selection;
+ selection = gtk_tree_view_get_selection (treeview);
+ gtk_tree_selection_select_iter (selection, &iter);
+ g_free (locale);
+ break;
+ }
+ g_free (locale);
+
+ cont = gtk_tree_model_iter_next (model, &iter);
+ }
+ g_free (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)) {
+ GDBusProxy *user;
+ GVariant *props;
+ const char *lang;
+ char *name;
+ char *language;
+
+ user = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.Accounts",
+ str,
+ "org.freedesktop.Accounts.User",
+ NULL,
+ &error);
+ if (user == NULL) {
+ g_warning ("Failed to get proxy for user '%s': %s",
+ str, error->message);
+ g_error_free (error);
+ error = NULL;
+ continue;
+ }
+ props = g_dbus_proxy_get_cached_property (user, "Language");
+ lang = g_variant_get_string (props, NULL);
+ if (lang != NULL && *lang != '\0' &&
+ cc_common_language_has_font (lang) &&
+ gdm_language_has_translations (lang)) {
+ name = gdm_normalize_language_name (lang);
+ if (!g_hash_table_lookup (ht, name)) {
+ language = gdm_get_language_from_name (name, NULL);
+ g_hash_table_insert (ht, name, language);
+ }
+ else {
+ g_free (name);
+ }
+ }
+ g_variant_unref (props);
+ g_object_unref (user);
+ }
+ g_variant_iter_free (vi);
+ g_variant_unref (variant);
+
+ g_object_unref (proxy);
+}
+
+GHashTable *
+cc_common_language_get_initial_languages (void)
+{
+ GHashTable *ht;
+ char *name;
+ char *language;
+
+ ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ /* Add some common languages first */
+ g_hash_table_insert (ht, g_strdup ("en_US.utf8"), g_strdup (_("English")));
+ if (gdm_language_has_translations ("en_GB"))
+ g_hash_table_insert (ht, g_strdup ("en_GB.utf8"), g_strdup (_("British English")));
+ if (gdm_language_has_translations ("de") ||
+ gdm_language_has_translations ("de_DE"))
+ g_hash_table_insert (ht, g_strdup ("de_DE.utf8"), g_strdup (_("German")));
+ if (gdm_language_has_translations ("fr") ||
+ gdm_language_has_translations ("fr_FR"))
+ g_hash_table_insert (ht, g_strdup ("fr_FR.utf8"), g_strdup (_("French")));
+ if (gdm_language_has_translations ("es") ||
+ gdm_language_has_translations ("es_ES"))
+ g_hash_table_insert (ht, g_strdup ("es_ES.utf8"), g_strdup (_("Spanish")));
+ if (gdm_language_has_translations ("zh_CN"))
+ g_hash_table_insert (ht, g_strdup ("zh_CN.utf8"), g_strdup (_("Chinese (simplified)")));
+
+ /* 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 = gdm_get_language_from_name (name, NULL);
+ g_hash_table_insert (ht, name, language);
+ } else {
+ g_free (name);
+ }
+
+ return ht;
+}
+
+GHashTable *
+cc_common_language_get_initial_regions (const gchar *lang)
+{
+ GHashTable *ht;
+ char *language;
+ gchar **langs;
+ gint i;
+
+ ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+#if 0
+ /* Add some common regions */
+ g_hash_table_insert (ht, g_strdup ("en_US.utf8"), g_strdup (_("United States")));
+ g_hash_table_insert (ht, g_strdup ("de_DE.utf8"), g_strdup (_("Germany")));
+ g_hash_table_insert (ht, g_strdup ("fr_FR.utf8"), g_strdup (_("France")));
+ g_hash_table_insert (ht, g_strdup ("es_ES.utf8"), g_strdup (_("Spain")));
+ g_hash_table_insert (ht, g_strdup ("zh_CN.utf8"), g_strdup (_("China")));
+#endif
+
+ gdm_parse_language_name (lang, &language, NULL, NULL, NULL);
+ langs = gdm_get_all_language_names ();
+ for (i = 0; langs[i]; i++) {
+ gchar *l, *s;
+ gdm_parse_language_name (langs[i], &l, NULL, NULL, NULL);
+ if (g_strcmp0 (language, l) == 0) {
+ if (!g_hash_table_lookup (ht, langs[i])) {
+ s = gdm_get_region_from_name (langs[i], NULL);
+ g_hash_table_insert (ht, g_strdup (langs[i]), s);
+ }
+ }
+ g_free (l);
+ }
+ g_strfreev (langs);
+ g_free (language);
+
+ return ht;
+}
diff --git a/gnome-initial-setup/languages/cc-common-language.h b/gnome-initial-setup/languages/cc-common-language.h
new file mode 100644
index 0000000..169eee4
--- /dev/null
+++ b/gnome-initial-setup/languages/cc-common-language.h
@@ -0,0 +1,58 @@
+/* -*- 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 3 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 __CC_COMMON_LANGUAGE_H__
+#define __CC_COMMON_LANGUAGE_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+enum {
+ LOCALE_COL,
+ DISPLAY_LOCALE_COL,
+ SEPARATOR_COL,
+ USER_LANGUAGE,
+ NUM_COLS
+};
+
+gboolean cc_common_language_get_iter_for_language (GtkTreeModel *model,
+ const gchar *lang,
+ GtkTreeIter *iter);
+gboolean cc_common_language_get_iter_for_region (GtkTreeModel *model,
+ const gchar *lang,
+ GtkTreeIter *iter);
+guint cc_common_language_add_available_languages (GtkListStore *store,
+ gboolean regions,
+ GHashTable *user_langs);
+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);
+GHashTable *cc_common_language_get_initial_regions (const gchar *lang);
+
+void cc_common_language_setup_list (GtkWidget *treeview,
+ GHashTable *initial);
+void cc_common_language_select_current_language (GtkTreeView *treeview);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-initial-setup/languages/cc-language-chooser.c b/gnome-initial-setup/languages/cc-language-chooser.c
new file mode 100644
index 0000000..b2f692d
--- /dev/null
+++ b/gnome-initial-setup/languages/cc-language-chooser.c
@@ -0,0 +1,343 @@
+/* -*- 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 3 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 <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include <fontconfig/fontconfig.h>
+
+#include "cc-language-chooser.h"
+#include "cc-common-language.h"
+#include "gdm-languages.h"
+
+gchar *
+cc_language_chooser_get_language (GtkWidget *chooser)
+{
+ GtkTreeView *tv;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *lang;
+
+ tv = (GtkTreeView *) g_object_get_data (G_OBJECT (chooser), "list");
+ selection = gtk_tree_view_get_selection (tv);
+
+ gdk_threads_enter ();
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ gtk_tree_model_get (model, &iter, LOCALE_COL, &lang, -1);
+ else
+ lang = NULL;
+ gdk_threads_leave ();
+
+ return lang;
+}
+
+void
+cc_language_chooser_clear_filter (GtkWidget *chooser)
+{
+ GtkEntry *entry;
+
+ entry = (GtkEntry *) g_object_get_data (G_OBJECT (chooser), "filter-entry");
+ gtk_entry_set_text (entry, "");
+}
+
+static void
+row_activated (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ GtkWidget *chooser)
+{
+ gtk_dialog_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK);
+}
+
+static void
+languages_foreach_cb (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GtkListStore *store = (GtkListStore *) user_data;
+ const char *locale = (const char *) key;
+ const char *display_locale = (const char *) value;
+ GtkTreeIter iter;
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ LOCALE_COL, locale,
+ DISPLAY_LOCALE_COL, display_locale,
+ -1);
+}
+
+void
+cc_add_user_languages (GtkTreeModel *model)
+{
+ char *name;
+ GtkTreeIter iter;
+ GtkListStore *store = GTK_LIST_STORE (model);
+ GHashTable *user_langs;
+ const char *display;
+
+ gtk_list_store_clear (store);
+
+ user_langs = cc_common_language_get_initial_languages ();
+
+ /* Add the current locale first */
+ name = cc_common_language_get_current_language ();
+ display = g_hash_table_lookup (user_langs, name);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, LOCALE_COL, name, DISPLAY_LOCALE_COL, display, -1);
+ g_hash_table_remove (user_langs, name);
+ g_free (name);
+
+ /* The rest of the languages */
+ g_hash_table_foreach (user_langs, (GHFunc) languages_foreach_cb, store);
+
+ /* And now the "Other..." selection */
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, LOCALE_COL, NULL, DISPLAY_LOCALE_COL, _("Other..."), -1);
+
+ g_hash_table_destroy (user_langs);
+}
+
+static void
+remove_timeout (gpointer data,
+ GObject *where_the_object_was)
+{
+ guint timeout = GPOINTER_TO_UINT (data);
+ g_source_remove (timeout);
+}
+
+static void
+remove_async (gpointer data)
+{
+ guint async_id = GPOINTER_TO_UINT (data);
+
+ g_source_remove (async_id);
+}
+
+static void
+selection_changed (GtkTreeSelection *selection,
+ GtkWidget *chooser)
+{
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (chooser),
+ GTK_RESPONSE_OK,
+ gtk_tree_selection_get_selected (selection, NULL, NULL));
+}
+
+static gboolean
+finish_language_chooser (gpointer user_data)
+{
+ GtkWidget *chooser = (GtkWidget *) user_data;
+ GtkWidget *list;
+ GtkTreeModel *model;
+ GtkWindow *parent;
+ GHashTable *user_langs;
+ guint timeout;
+ guint async_id;
+ GtkTreeSelection *selection;
+ gboolean regions;
+
+ /* Did we get called after the widget was destroyed? */
+ if (chooser == NULL)
+ return FALSE;
+
+ regions = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (chooser), "regions"));
+
+ list = g_object_get_data (G_OBJECT (chooser), "list");
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
+ model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+ user_langs = g_object_get_data (G_OBJECT (chooser), "user-langs");
+
+ async_id = cc_common_language_add_available_languages (GTK_LIST_STORE (model), regions, user_langs);
+ g_object_set_data_full (G_OBJECT (chooser), "language-async", GUINT_TO_POINTER (async_id), remove_async);
+
+ parent = gtk_window_get_transient_for (GTK_WINDOW (chooser));
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (parent)), NULL);
+
+ g_object_set_data (G_OBJECT (chooser), "user-langs", NULL);
+ timeout = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (chooser), "timeout"));
+ g_object_weak_unref (G_OBJECT (chooser), (GWeakNotify) remove_timeout, GUINT_TO_POINTER (timeout));
+
+ /* And now listen for changes */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
+ g_signal_connect (G_OBJECT (selection), "changed",
+ G_CALLBACK (selection_changed), chooser);
+
+ return FALSE;
+}
+
+static void
+filter_clear (GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event, gpointer user_data)
+{
+ gtk_entry_set_text (entry, "");
+}
+
+static void
+filter_changed (GtkWidget *entry, GParamSpec *pspec, GtkWidget *list)
+{
+ const gchar *pattern;
+ GtkTreeModel *filter_model;
+ GtkTreeModel *model;
+
+ pattern = gtk_entry_get_text (GTK_ENTRY (entry));
+
+ filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
+ model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
+
+ if (g_strcmp0 (pattern, "") == 0) {
+ g_object_set (G_OBJECT (entry),
+ "secondary-icon-name", "edit-find-symbolic",
+ "secondary-icon-activatable", FALSE,
+ "secondary-icon-sensitive", FALSE,
+ NULL);
+
+ g_object_set_data_full (G_OBJECT (model), "filter-string",
+ g_strdup (""), g_free);
+
+ } else {
+ g_object_set (G_OBJECT (entry),
+ "secondary-icon-name", "edit-clear-symbolic",
+ "secondary-icon-activatable", TRUE,
+ "secondary-icon-sensitive", TRUE,
+ NULL);
+
+ g_object_set_data_full (G_OBJECT (model), "filter-string",
+ g_utf8_casefold (pattern, -1), g_free);
+ }
+
+ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
+}
+
+static gboolean
+filter_languages (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ const gchar *filter_string;
+ gchar *locale, *l;
+ gboolean visible;
+
+ filter_string = g_object_get_data (G_OBJECT (model), "filter-string");
+
+ if (filter_string == NULL) {
+ return TRUE;
+ }
+
+ gdk_threads_enter ();
+ gtk_tree_model_get (model, iter, DISPLAY_LOCALE_COL, &locale, -1);
+ gdk_threads_leave ();
+
+ l = g_utf8_casefold (locale, -1);
+
+ visible = strstr (l, filter_string) != NULL;
+
+ g_free (locale);
+ g_free (l);
+
+ return visible;
+}
+
+GtkWidget *
+cc_language_chooser_new (GtkWidget *parent, gboolean regions)
+{
+ GtkBuilder *builder;
+ const char *filename;
+ GError *error = NULL;
+ GtkWidget *chooser;
+ GtkWidget *list;
+ GtkWidget *button;
+ GtkWidget *entry;
+ GtkWidget *widget;
+ GHashTable *user_langs;
+ GdkCursor *cursor;
+ guint timeout;
+ GtkTreeModel *model;
+ GtkTreeModel *filter_model;
+
+ builder = gtk_builder_new ();
+ filename = UIDIR "/language-chooser.ui";
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+ filename = "data/language-chooser.ui";
+ if (!gtk_builder_add_from_file (builder, filename, &error)) {
+ g_warning ("failed to load language chooser: %s", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ chooser = (GtkWidget *) gtk_builder_get_object (builder, "dialog");
+
+ if (regions) {
+ widget = (GtkWidget *) gtk_builder_get_object (builder, "title");
+ gtk_label_set_text (GTK_LABEL (widget), _("Select a region"));
+
+ /* communicate the preference to finish_language_chooser() */
+ g_object_set_data (G_OBJECT (chooser), "regions", GINT_TO_POINTER (TRUE));
+ }
+
+ list = (GtkWidget *) gtk_builder_get_object (builder, "language-list");
+ g_object_set_data (G_OBJECT (chooser), "list", list);
+ g_signal_connect (list, "row-activated",
+ G_CALLBACK (row_activated), chooser);
+
+ button = (GtkWidget *) gtk_builder_get_object (builder, "ok-button");
+ gtk_widget_grab_default (button);
+
+ entry = (GtkWidget *) gtk_builder_get_object (builder, "filter-entry");
+ g_object_set_data (G_OBJECT (chooser), "filter-entry", entry);
+ g_signal_connect (entry, "notify::text",
+ G_CALLBACK (filter_changed), list);
+ g_signal_connect (entry, "icon-release",
+ G_CALLBACK (filter_clear), NULL);
+ gtk_widget_grab_focus (entry);
+
+ user_langs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ cc_common_language_setup_list (list, user_langs);
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
+ filter_model = gtk_tree_model_filter_new (model, NULL);
+ gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model), filter_languages,
+ NULL, NULL);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (list), filter_model);
+
+ /* Setup so that the list is added after the dialogue is shown */
+ cursor = gdk_cursor_new (GDK_WATCH);
+ gdk_window_set_cursor (gtk_widget_get_window (parent), cursor);
+ g_object_unref (cursor);
+
+ gtk_window_set_transient_for (GTK_WINDOW (chooser), GTK_WINDOW (parent));
+
+ g_object_set_data_full (G_OBJECT (chooser), "user-langs",
+ user_langs, (GDestroyNotify) g_hash_table_destroy);
+ timeout = g_idle_add ((GSourceFunc) finish_language_chooser, chooser);
+ g_object_set_data (G_OBJECT (chooser), "timeout", GUINT_TO_POINTER (timeout));
+ g_object_weak_ref (G_OBJECT (chooser), (GWeakNotify) remove_timeout, GUINT_TO_POINTER (timeout));
+
+ g_object_unref (builder);
+
+ return chooser;
+}
diff --git a/gnome-initial-setup/languages/cc-language-chooser.h b/gnome-initial-setup/languages/cc-language-chooser.h
new file mode 100644
index 0000000..7cd6873
--- /dev/null
+++ b/gnome-initial-setup/languages/cc-language-chooser.h
@@ -0,0 +1,38 @@
+/* -*- 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 3 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 __CC_LANGUAGE_CHOOSER_H__
+#define __CC_LANGUAGE_CHOOSER_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void cc_add_user_languages (GtkTreeModel *model);
+
+GtkWidget *cc_language_chooser_new (GtkWidget *parent,
+ gboolean regions);
+void cc_language_chooser_clear_filter (GtkWidget *chooser);
+gchar *cc_language_chooser_get_language (GtkWidget *chooser);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-initial-setup/languages/gdm-languages.c b/gnome-initial-setup/languages/gdm-languages.c
new file mode 100644
index 0000000..64c72e4
--- /dev/null
+++ b/gnome-initial-setup/languages/gdm-languages.c
@@ -0,0 +1,1326 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2008 Red Hat, Inc,
+ * 2007 William Jon McCann <mccann jhu edu>
+ *
+ * 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 : William Jon McCann <mccann jhu edu>
+ * Ray Strode <rstrode redhat com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include "gdm-languages.h"
+
+#include <langinfo.h>
+#ifndef __LC_LAST
+#define __LC_LAST 13
+#endif
+#include "locarchive.h"
+
+#define ALIASES_FILE DATADIR "/gdm/locale.alias"
+#define ARCHIVE_FILE LIBLOCALEDIR "/locale-archive"
+#define SYSTEM_ARCHIVE_FILE "/usr/lib/locale/locale-archive"
+#define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes"
+#define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale"
+
+typedef struct _GdmLocale {
+ char *id;
+ char *name;
+ char *language_code;
+ char *territory_code;
+ char *codeset;
+ char *modifier;
+} GdmLocale;
+
+static GHashTable *gdm_languages_map;
+static GHashTable *gdm_territories_map;
+static GHashTable *gdm_available_locales_map;
+static GHashTable *gdm_language_count_map;
+static GHashTable *gdm_territory_count_map;
+
+static char * construct_language_name (const char *language,
+ const char *territory,
+ const char *codeset,
+ const char *modifier);
+
+static gboolean language_name_is_valid (const char *language_name);
+
+static void
+gdm_locale_free (GdmLocale *locale)
+{
+ if (locale == NULL) {
+ return;
+ }
+
+ g_free (locale->id);
+ g_free (locale->name);
+ g_free (locale->codeset);
+ g_free (locale->modifier);
+ g_free (locale);
+}
+
+static char *
+normalize_codeset (const char *codeset)
+{
+ char *normalized_codeset;
+ const char *p;
+ char *q;
+
+ normalized_codeset = g_strdup (codeset);
+
+ if (codeset != NULL) {
+ for (p = codeset, q = normalized_codeset;
+ *p != '\0'; p++) {
+
+ if (*p == '-' || *p == '_') {
+ continue;
+ }
+
+ *q = g_ascii_tolower (*p);
+ q++;
+ }
+ *q = '\0';
+ }
+
+ return normalized_codeset;
+}
+
+/*
+ * According to http://en.wikipedia.org/wiki/Locale
+ * locale names are of the form:
+ * [language[_territory][ codeset][ modifier]]
+ */
+gboolean
+gdm_parse_language_name (const char *name,
+ char **language_codep,
+ char **territory_codep,
+ char **codesetp,
+ char **modifierp)
+{
+ GRegex *re;
+ GMatchInfo *match_info;
+ gboolean res;
+ GError *error;
+ gchar *normalized_codeset = NULL;
+ gchar *normalized_name = NULL;
+ gboolean retval;
+
+ match_info = NULL;
+ retval = FALSE;
+
+ error = NULL;
+ re = g_regex_new ("^(?P<language>[^_ [:space:]]+)"
+ "(_(?P<territory>[[:upper:]]+))?"
+ "(\\.(?P<codeset>[-_0-9a-zA-Z]+))?"
+ "(@(?P<modifier>[[:ascii:]]+))?$",
+ 0, 0, &error);
+ if (re == NULL) {
+ g_warning ("%s", error->message);
+ goto out;
+ }
+
+ if (!g_regex_match (re, name, 0, &match_info) ||
+ g_match_info_is_partial_match (match_info)) {
+ g_warning ("locale '%s' isn't valid\n", name);
+ goto out;
+ }
+
+ res = g_match_info_matches (match_info);
+ if (! res) {
+ g_warning ("Unable to parse locale: %s", name);
+ goto out;
+ }
+
+ retval = TRUE;
+
+ if (language_codep != NULL) {
+ *language_codep = g_match_info_fetch_named (match_info, "language");
+ }
+
+ if (territory_codep != NULL) {
+ *territory_codep = g_match_info_fetch_named (match_info, "territory");
+
+ if (*territory_codep != NULL &&
+ *territory_codep[0] == '\0') {
+ g_free (*territory_codep);
+ *territory_codep = NULL;
+ }
+ }
+
+ if (codesetp != NULL) {
+ *codesetp = g_match_info_fetch_named (match_info, "codeset");
+
+ if (*codesetp != NULL &&
+ *codesetp[0] == '\0') {
+ g_free (*codesetp);
+ *codesetp = NULL;
+ }
+ }
+
+ if (modifierp != NULL) {
+ *modifierp = g_match_info_fetch_named (match_info, "modifier");
+
+ if (*modifierp != NULL &&
+ *modifierp[0] == '\0') {
+ g_free (*modifierp);
+ *modifierp = NULL;
+ }
+ }
+
+ if (codesetp != NULL && *codesetp != NULL) {
+ normalized_codeset = normalize_codeset (*codesetp);
+ normalized_name = construct_language_name (language_codep ? *language_codep : NULL,
+ territory_codep ? *territory_codep : NULL,
+ normalized_codeset,
+ modifierp ? *modifierp : NULL);
+
+ if (language_name_is_valid (normalized_name)) {
+ g_free (*codesetp);
+ *codesetp = normalized_codeset;
+ } else {
+ g_free (normalized_codeset);
+ }
+ g_free (normalized_name);
+ }
+
+ out:
+ g_match_info_free (match_info);
+ g_regex_unref (re);
+
+ return retval;
+}
+
+static char *
+construct_language_name (const char *language,
+ const char *territory,
+ const char *codeset,
+ const char *modifier)
+{
+ char *name;
+
+ g_assert (language[0] != 0);
+ g_assert (territory == NULL || territory[0] != 0);
+ g_assert (codeset == NULL || codeset[0] != 0);
+ g_assert (modifier == NULL || modifier[0] != 0);
+
+ name = g_strdup_printf ("%s%s%s%s%s%s%s",
+ language,
+ territory != NULL? "_" : "",
+ territory != NULL? territory : "",
+ codeset != NULL? "." : "",
+ codeset != NULL? codeset : "",
+ modifier != NULL? "@" : "",
+ modifier != NULL? modifier : "");
+
+ return name;
+}
+
+char *
+gdm_normalize_language_name (const char *name)
+{
+ char *normalized_name;
+ char *language_code;
+ char *territory_code;
+ char *codeset;
+ char *modifier;
+
+ if (name[0] == '\0') {
+ return NULL;
+ }
+
+ gdm_parse_language_name (name,
+ &language_code,
+ &territory_code,
+ &codeset, &modifier);
+
+ normalized_name = construct_language_name (language_code,
+ territory_code,
+ codeset, modifier);
+ g_free (language_code);
+ g_free (territory_code);
+ g_free (codeset);
+ g_free (modifier);
+
+ return normalized_name;
+}
+
+static gboolean
+language_name_is_valid (const char *language_name)
+{
+ char *old_locale;
+ gboolean is_valid;
+#ifdef WITH_INCOMPLETE_LOCALES
+ int lc_type_id = LC_CTYPE;
+#else
+ int lc_type_id = LC_MESSAGES;
+#endif
+
+ old_locale = g_strdup (setlocale (lc_type_id, NULL));
+ is_valid = setlocale (lc_type_id, language_name) != NULL;
+ setlocale (lc_type_id, old_locale);
+ g_free (old_locale);
+
+ return is_valid;
+}
+
+static void
+language_name_get_codeset_details (const char *language_name,
+ char **pcodeset,
+ gboolean *is_utf8)
+{
+ char *old_locale;
+ char *codeset;
+
+ old_locale = g_strdup (setlocale (LC_CTYPE, NULL));
+
+ if (setlocale (LC_CTYPE, language_name) == NULL) {
+ g_free (old_locale);
+ return;
+ }
+
+ codeset = nl_langinfo (CODESET);
+
+ if (pcodeset != NULL) {
+ *pcodeset = g_strdup (codeset);
+ }
+
+ if (is_utf8 != NULL) {
+ codeset = normalize_codeset (codeset);
+
+ *is_utf8 = strcmp (codeset, "utf8") == 0;
+ g_free (codeset);
+ }
+
+ setlocale (LC_CTYPE, old_locale);
+ g_free (old_locale);
+}
+
+gboolean
+gdm_language_has_translations (const char *language_name)
+{
+ GDir *dir;
+ char *path;
+ const char *name;
+ gboolean has_translations;
+
+ path = g_build_filename (GNOMELOCALEDIR, language_name, "LC_MESSAGES", NULL);
+
+ has_translations = FALSE;
+ dir = g_dir_open (path, 0, NULL);
+ g_free (path);
+
+ if (dir == NULL) {
+ goto out;
+ }
+
+ do {
+ name = g_dir_read_name (dir);
+
+ if (name == NULL) {
+ break;
+ }
+
+ if (g_str_has_suffix (name, ".mo")) {
+ has_translations = TRUE;
+ break;
+ }
+ } while (name != NULL);
+ g_dir_close (dir);
+
+out:
+ return has_translations;
+}
+
+static gboolean
+add_locale (const char *language_name,
+ gboolean utf8_only)
+{
+ GdmLocale *locale;
+ GdmLocale *old_locale;
+ char *name;
+ gboolean is_utf8;
+
+ g_return_val_if_fail (language_name != NULL, FALSE);
+ g_return_val_if_fail (*language_name != '\0', FALSE);
+
+ language_name_get_codeset_details (language_name, NULL, &is_utf8);
+
+ if (is_utf8) {
+ name = g_strdup (language_name);
+ } else if (utf8_only) {
+ name = g_strdup_printf ("%s.utf8", language_name);
+
+ language_name_get_codeset_details (name, NULL, &is_utf8);
+ if (!is_utf8) {
+ g_free (name);
+ return FALSE;
+ }
+ } else {
+ name = g_strdup (language_name);
+ }
+
+ if (!language_name_is_valid (name)) {
+ g_debug ("Ignoring '%s' as a locale, since it's invalid", name);
+ g_free (name);
+ return FALSE;
+ }
+
+ locale = g_new0 (GdmLocale, 1);
+ gdm_parse_language_name (name,
+ &locale->language_code,
+ &locale->territory_code,
+ &locale->codeset,
+ &locale->modifier);
+ g_free (name);
+ name = NULL;
+
+#ifdef WITH_INCOMPLETE_LOCALES
+ if (utf8_only) {
+ if (locale->territory_code == NULL || locale->modifier) {
+ g_debug ("Ignoring '%s' as a locale, since it lacks territory code or modifier", name);
+ gdm_locale_free (locale);
+ return FALSE;
+ }
+ }
+#endif
+
+ locale->id = construct_language_name (locale->language_code, locale->territory_code,
+ NULL, locale->modifier);
+ locale->name = construct_language_name (locale->language_code, locale->territory_code,
+ locale->codeset, locale->modifier);
+
+#ifndef WITH_INCOMPLETE_LOCALES
+ if (!gdm_language_has_translations (locale->name) &&
+ !gdm_language_has_translations (locale->id) &&
+ !gdm_language_has_translations (locale->language_code) &&
+ utf8_only) {
+ g_debug ("Ignoring '%s' as a locale, since it lacks translations", locale->name);
+ gdm_locale_free (locale);
+ return FALSE;
+ }
+#endif
+
+ if (!utf8_only) {
+ g_free (locale->id);
+ locale->id = g_strdup (locale->name);
+ }
+
+ old_locale = g_hash_table_lookup (gdm_available_locales_map, locale->id);
+ if (old_locale != NULL) {
+ if (strlen (old_locale->name) > strlen (locale->name)) {
+ gdm_locale_free (locale);
+ return FALSE;
+ }
+ }
+
+ g_hash_table_insert (gdm_available_locales_map, g_strdup (locale->id), locale);
+
+ return TRUE;
+}
+
+struct nameent
+{
+ char *name;
+ uint32_t locrec_offset;
+};
+
+static gboolean
+collect_locales_from_archive (void)
+{
+ GMappedFile *mapped;
+ GError *error;
+ char *addr;
+ struct locarhead *head;
+ struct namehashent *namehashtab;
+ struct nameent *names;
+ uint32_t used;
+ uint32_t cnt;
+ gsize len;
+ gboolean locales_collected;
+
+ error = NULL;
+ mapped = g_mapped_file_new (ARCHIVE_FILE, FALSE, &error);
+ if (mapped == NULL) {
+ mapped = g_mapped_file_new (SYSTEM_ARCHIVE_FILE, FALSE, NULL);
+ if (mapped == NULL) {
+ g_warning ("Mapping failed for %s: %s", ARCHIVE_FILE, error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ g_error_free (error);
+ }
+
+ locales_collected = FALSE;
+
+ addr = g_mapped_file_get_contents (mapped);
+ len = g_mapped_file_get_length (mapped);
+
+ head = (struct locarhead *) addr;
+ if (head->namehash_offset + head->namehash_size > len
+ || head->string_offset + head->string_size > len
+ || head->locrectab_offset + head->locrectab_size > len
+ || head->sumhash_offset + head->sumhash_size > len) {
+ goto out;
+ }
+
+ namehashtab = (struct namehashent *) (addr + head->namehash_offset);
+
+ names = (struct nameent *) g_new0 (struct nameent, head->namehash_used);
+ for (cnt = used = 0; cnt < head->namehash_size; ++cnt) {
+ if (namehashtab[cnt].locrec_offset != 0) {
+ names[used].name = addr + namehashtab[cnt].name_offset;
+ names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
+ }
+ }
+
+ for (cnt = 0; cnt < used; ++cnt) {
+ add_locale (names[cnt].name, TRUE);
+ }
+
+ g_free (names);
+
+ locales_collected = TRUE;
+ out:
+
+ g_mapped_file_unref (mapped);
+ return locales_collected;
+}
+
+static int
+select_dirs (const struct dirent *dirent)
+{
+ int result = 0;
+
+ if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0) {
+ mode_t mode = 0;
+
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK) {
+ mode = DTTOIF (dirent->d_type);
+ } else
+#endif
+ {
+ struct stat st;
+ char *path;
+
+ path = g_build_filename (LIBLOCALEDIR, dirent->d_name, NULL);
+ if (g_stat (path, &st) == 0) {
+ mode = st.st_mode;
+ }
+ g_free (path);
+ }
+
+ result = S_ISDIR (mode);
+ }
+
+ return result;
+}
+
+static void
+collect_locales_from_directory (void)
+{
+ struct dirent **dirents;
+ int ndirents;
+ int cnt;
+
+ ndirents = scandir (LIBLOCALEDIR, &dirents, select_dirs, alphasort);
+
+ for (cnt = 0; cnt < ndirents; ++cnt) {
+ add_locale (dirents[cnt]->d_name, TRUE);
+ }
+
+ if (ndirents > 0) {
+ free (dirents);
+ }
+}
+
+static void
+collect_locales_from_locale_file (const char *locale_file)
+{
+ FILE *langlist;
+ char curline[256];
+ char *getsret;
+
+ if (locale_file == NULL)
+ return;
+
+ langlist = fopen (locale_file, "r");
+
+ if (langlist == NULL)
+ return;
+
+ for (;;) {
+ char *name;
+ char *lang;
+ char **lang_list;
+ int i;
+
+ getsret = fgets (curline, sizeof (curline), langlist);
+ if (getsret == NULL)
+ break;
+
+ if (curline[0] <= ' ' ||
+ curline[0] == '#')
+ continue;
+
+ name = strtok (curline, " \t\r\n");
+ if (name == NULL)
+ continue;
+
+ lang = strtok (NULL, " \t\r\n");
+ if (lang == NULL)
+ continue;
+
+ lang_list = g_strsplit (lang, ",", -1);
+ if (lang_list == NULL)
+ continue;
+
+ lang = NULL;
+ for (i = 0; lang_list[i] != NULL; i++) {
+ if (add_locale (lang_list[i], FALSE)) {
+ break;
+ }
+ }
+ g_strfreev (lang_list);
+ }
+
+ fclose (langlist);
+}
+
+static void
+count_languages_and_territories (void)
+{
+ gpointer value;
+ GHashTableIter iter;
+
+ gdm_language_count_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ gdm_territory_count_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ g_hash_table_iter_init (&iter, gdm_available_locales_map);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ GdmLocale *locale;
+
+ locale = (GdmLocale *) value;
+
+ if (locale->language_code != NULL) {
+ int count;
+
+ count = GPOINTER_TO_INT (g_hash_table_lookup (gdm_language_count_map, locale->language_code));
+ count++;
+ g_hash_table_insert (gdm_language_count_map, g_strdup (locale->language_code), GINT_TO_POINTER (count));
+ }
+
+ if (locale->territory_code != NULL) {
+ int count;
+
+ count = GPOINTER_TO_INT (g_hash_table_lookup (gdm_territory_count_map, locale->territory_code));
+ count++;
+ g_hash_table_insert (gdm_territory_count_map, g_strdup (locale->territory_code), GINT_TO_POINTER (count));
+ }
+ }
+}
+
+static void
+collect_locales (void)
+{
+
+ if (gdm_available_locales_map == NULL) {
+ gdm_available_locales_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gdm_locale_free);
+ }
+
+ if (!collect_locales_from_archive ()) {
+#ifndef WITH_INCOMPLETE_LOCALES
+ g_warning ("Could not read list of available locales from libc, "
+ "guessing possible locales from available translations, "
+ "but list may be incomplete!");
+#endif
+ }
+
+ collect_locales_from_directory ();
+
+ collect_locales_from_locale_file (ALIASES_FILE);
+
+ count_languages_and_territories ();
+}
+
+static gint
+get_language_count (const char *language)
+{
+ if (gdm_language_count_map == NULL) {
+ collect_locales ();
+ }
+
+ return GPOINTER_TO_INT (g_hash_table_lookup (gdm_language_count_map, language));
+}
+
+static gboolean
+is_unique_language (const char *language)
+{
+ return get_language_count (language) == 1;
+}
+
+static gint
+get_territory_count (const char *territory)
+{
+ if (gdm_territory_count_map == NULL) {
+ collect_locales ();
+ }
+
+ return GPOINTER_TO_INT (g_hash_table_lookup (gdm_territory_count_map, territory));
+}
+
+static gboolean
+is_unique_territory (const char *territory)
+{
+ return get_territory_count (territory) == 1;
+}
+
+static gboolean
+is_fallback_language (const char *code)
+{
+ const char *fallback_language_names[] = { "C", "POSIX", NULL };
+ int i;
+
+ for (i = 0; fallback_language_names[i] != NULL; i++) {
+ if (strcmp (code, fallback_language_names[i]) == 0) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static const char *
+get_language (const char *code)
+{
+ const char *name;
+ int len;
+
+ g_assert (code != NULL);
+
+ if (is_fallback_language (code)) {
+ return "Unspecified";
+ }
+
+ len = strlen (code);
+ if (len != 2 && len != 3) {
+ return NULL;
+ }
+
+ name = (const char *) g_hash_table_lookup (gdm_languages_map, code);
+
+ return name;
+}
+
+static char *
+get_first_item_in_semicolon_list (const char *list)
+{
+ char **items;
+ char *item;
+
+ /* Some entries in iso codes have multiple values, separated
+ * by semicolons. Not really sure which one to pick, so
+ * we just arbitrarily pick the first one.
+ */
+ items = g_strsplit (list, "; ", 2);
+
+ item = g_strdup (items[0]);
+ g_strfreev (items);
+
+ return item;
+}
+
+static char *
+get_translated_language (const char *code,
+ const char *locale)
+{
+ const char *language;
+ char *name;
+
+ language = get_language (code);
+
+ name = NULL;
+ if (language != NULL) {
+ const char *translated_name;
+ char *old_locale;
+
+ if (locale != NULL) {
+ old_locale = g_strdup (setlocale (LC_MESSAGES, NULL));
+ setlocale (LC_MESSAGES, locale);
+ }
+
+ if (is_fallback_language (code)) {
+ name = g_strdup (_("Unspecified"));
+ } else {
+ translated_name = dgettext ("iso_639", language);
+ name = get_first_item_in_semicolon_list (translated_name);
+ }
+
+ if (locale != NULL) {
+ setlocale (LC_MESSAGES, old_locale);
+ g_free (old_locale);
+ }
+ }
+
+ return name;
+}
+
+static const char *
+get_territory (const char *code)
+{
+ const char *name;
+ int len;
+
+ g_assert (code != NULL);
+
+ len = strlen (code);
+ if (len != 2 && len != 3) {
+ return NULL;
+ }
+
+ name = (const char *) g_hash_table_lookup (gdm_territories_map, code);
+
+ return name;
+}
+
+static char *
+get_translated_territory (const char *code,
+ const char *locale)
+{
+ const char *territory;
+ char *name;
+
+ territory = get_territory (code);
+
+ name = NULL;
+ if (territory != NULL) {
+ const char *translated_territory;
+ char *old_locale;
+
+ if (locale != NULL) {
+ old_locale = g_strdup (setlocale (LC_MESSAGES, NULL));
+ setlocale (LC_MESSAGES, locale);
+ }
+
+ translated_territory = dgettext ("iso_3166", territory);
+ name = get_first_item_in_semicolon_list (translated_territory);
+
+ if (locale != NULL) {
+ setlocale (LC_MESSAGES, old_locale);
+ g_free (old_locale);
+ }
+ }
+
+ return name;
+}
+
+static void
+languages_parse_start_tag (GMarkupParseContext *ctx,
+ const char *element_name,
+ const char **attr_names,
+ const char **attr_values,
+ gpointer user_data,
+ GError **error)
+{
+ const char *ccode_longB;
+ const char *ccode_longT;
+ const char *ccode;
+ const char *ccode_id;
+ const char *lang_name;
+
+ if (! (g_str_equal (element_name, "iso_639_entry") || g_str_equal (element_name, "iso_639_3_entry"))
+ || attr_names == NULL || attr_values == NULL) {
+ return;
+ }
+
+ ccode = NULL;
+ ccode_longB = NULL;
+ ccode_longT = NULL;
+ ccode_id = NULL;
+ lang_name = NULL;
+
+ while (*attr_names && *attr_values) {
+ if (g_str_equal (*attr_names, "iso_639_1_code")) {
+ /* skip if empty */
+ if (**attr_values) {
+ if (strlen (*attr_values) != 2) {
+ return;
+ }
+ ccode = *attr_values;
+ }
+ } else if (g_str_equal (*attr_names, "iso_639_2B_code")) {
+ /* skip if empty */
+ if (**attr_values) {
+ if (strlen (*attr_values) != 3) {
+ return;
+ }
+ ccode_longB = *attr_values;
+ }
+ } else if (g_str_equal (*attr_names, "iso_639_2T_code")) {
+ /* skip if empty */
+ if (**attr_values) {
+ if (strlen (*attr_values) != 3) {
+ return;
+ }
+ ccode_longT = *attr_values;
+ }
+ } else if (g_str_equal (*attr_names, "id")) {
+ /* skip if empty */
+ if (**attr_values) {
+ if (strlen (*attr_values) != 2 &&
+ strlen (*attr_values) != 3) {
+ return;
+ }
+ ccode_id = *attr_values;
+ }
+ } else if (g_str_equal (*attr_names, "name")) {
+ lang_name = *attr_values;
+ }
+
+ ++attr_names;
+ ++attr_values;
+ }
+
+ if (lang_name == NULL) {
+ return;
+ }
+
+ if (ccode != NULL) {
+ g_hash_table_insert (gdm_languages_map,
+ g_strdup (ccode),
+ g_strdup (lang_name));
+ }
+ if (ccode_longB != NULL) {
+ g_hash_table_insert (gdm_languages_map,
+ g_strdup (ccode_longB),
+ g_strdup (lang_name));
+ }
+ if (ccode_longT != NULL) {
+ g_hash_table_insert (gdm_languages_map,
+ g_strdup (ccode_longT),
+ g_strdup (lang_name));
+ }
+ if (ccode_id != NULL) {
+ g_hash_table_insert (gdm_languages_map,
+ g_strdup (ccode_id),
+ g_strdup (lang_name));
+ }
+}
+
+static void
+territories_parse_start_tag (GMarkupParseContext *ctx,
+ const char *element_name,
+ const char **attr_names,
+ const char **attr_values,
+ gpointer user_data,
+ GError **error)
+{
+ const char *acode_2;
+ const char *acode_3;
+ const char *ncode;
+ const char *territory_common_name;
+ const char *territory_name;
+
+ if (! g_str_equal (element_name, "iso_3166_entry") || attr_names == NULL || attr_values == NULL) {
+ return;
+ }
+
+ acode_2 = NULL;
+ acode_3 = NULL;
+ ncode = NULL;
+ territory_common_name = NULL;
+ territory_name = NULL;
+
+ while (*attr_names && *attr_values) {
+ if (g_str_equal (*attr_names, "alpha_2_code")) {
+ /* skip if empty */
+ if (**attr_values) {
+ if (strlen (*attr_values) != 2) {
+ return;
+ }
+ acode_2 = *attr_values;
+ }
+ } else if (g_str_equal (*attr_names, "alpha_3_code")) {
+ /* skip if empty */
+ if (**attr_values) {
+ if (strlen (*attr_values) != 3) {
+ return;
+ }
+ acode_3 = *attr_values;
+ }
+ } else if (g_str_equal (*attr_names, "numeric_code")) {
+ /* skip if empty */
+ if (**attr_values) {
+ if (strlen (*attr_values) != 3) {
+ return;
+ }
+ ncode = *attr_values;
+ }
+ } else if (g_str_equal (*attr_names, "common_name")) {
+ /* skip if empty */
+ if (**attr_values) {
+ territory_common_name = *attr_values;
+ }
+ } else if (g_str_equal (*attr_names, "name")) {
+ territory_name = *attr_values;
+ }
+
+ ++attr_names;
+ ++attr_values;
+ }
+
+ if (territory_common_name != NULL) {
+ territory_name = territory_common_name;
+ }
+
+ if (territory_name == NULL) {
+ return;
+ }
+
+ if (acode_2 != NULL) {
+ g_hash_table_insert (gdm_territories_map,
+ g_strdup (acode_2),
+ g_strdup (territory_name));
+ }
+ if (acode_3 != NULL) {
+ g_hash_table_insert (gdm_territories_map,
+ g_strdup (acode_3),
+ g_strdup (territory_name));
+ }
+ if (ncode != NULL) {
+ g_hash_table_insert (gdm_territories_map,
+ g_strdup (ncode),
+ g_strdup (territory_name));
+ }
+}
+
+static void
+languages_variant_init (const char *variant)
+{
+ GError *error;
+ gboolean res;
+ char *buf;
+ gsize buf_len;
+ char *filename;
+
+ bindtextdomain (variant, ISO_CODES_LOCALESDIR);
+ bind_textdomain_codeset (variant, "UTF-8");
+
+ error = NULL;
+ filename = g_strdup_printf (ISO_CODES_DATADIR "/%s.xml", variant);
+ res = g_file_get_contents (filename,
+ &buf,
+ &buf_len,
+ &error);
+ if (res) {
+ GMarkupParseContext *ctx;
+ GMarkupParser parser = { languages_parse_start_tag, NULL, NULL, NULL, NULL };
+
+ ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL);
+
+ error = NULL;
+ res = g_markup_parse_context_parse (ctx, buf, buf_len, &error);
+
+ if (! res) {
+ g_warning ("Failed to parse '%s': %s\n",
+ filename,
+ error->message);
+ g_error_free (error);
+ g_free (filename);
+ }
+
+ g_markup_parse_context_free (ctx);
+ g_free (buf);
+ } else {
+ g_warning ("Failed to load '%s': %s\n",
+ filename,
+ error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+languages_init (void)
+{
+ gdm_languages_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ languages_variant_init ("iso_639");
+ languages_variant_init ("iso_639_3");
+}
+
+static void
+territories_init (void)
+{
+ GError *error;
+ gboolean res;
+ char *buf;
+ gsize buf_len;
+
+ bindtextdomain ("iso_3166", ISO_CODES_LOCALESDIR);
+ bind_textdomain_codeset ("iso_3166", "UTF-8");
+
+ gdm_territories_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ error = NULL;
+ res = g_file_get_contents (ISO_CODES_DATADIR "/iso_3166.xml",
+ &buf,
+ &buf_len,
+ &error);
+ if (res) {
+ GMarkupParseContext *ctx;
+ GMarkupParser parser = { territories_parse_start_tag, NULL, NULL, NULL, NULL };
+
+ ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL);
+
+ error = NULL;
+ res = g_markup_parse_context_parse (ctx, buf, buf_len, &error);
+
+ if (! res) {
+ g_warning ("Failed to parse '%s': %s\n",
+ ISO_CODES_DATADIR "/iso_3166.xml",
+ error->message);
+ g_error_free (error);
+ }
+
+ g_markup_parse_context_free (ctx);
+ g_free (buf);
+ } else {
+ g_warning ("Failed to load '%s': %s\n",
+ ISO_CODES_DATADIR "/iso_3166.xml",
+ error->message);
+ g_error_free (error);
+ }
+}
+
+char *
+gdm_get_language_from_name (const char *name,
+ const char *locale)
+{
+ GString *full_language;
+ char *language_code;
+ char *territory_code;
+ char *codeset_code;
+ char *langinfo_codeset;
+ char *translated_language;
+ char *translated_territory;
+ gboolean is_utf8 = TRUE;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (*name != '\0', NULL);
+
+ translated_territory = NULL;
+ translated_language = NULL;
+ langinfo_codeset = NULL;
+
+ full_language = g_string_new (NULL);
+
+ if (gdm_languages_map == NULL) {
+ languages_init ();
+ }
+
+ if (gdm_territories_map == NULL) {
+ territories_init ();
+ }
+
+ language_code = NULL;
+ territory_code = NULL;
+ codeset_code = NULL;
+
+ gdm_parse_language_name (name,
+ &language_code,
+ &territory_code,
+ &codeset_code,
+ NULL);
+
+ if (language_code == NULL) {
+ goto out;
+ }
+
+ translated_language = get_translated_language (language_code, locale);
+ if (translated_language == NULL) {
+ goto out;
+ }
+
+ full_language = g_string_append (full_language, translated_language);
+
+ if (is_unique_language (language_code)) {
+ goto out;
+ }
+
+ if (territory_code != NULL) {
+ translated_territory = get_translated_territory (territory_code, locale);
+ }
+ if (translated_territory != NULL) {
+ g_string_append_printf (full_language,
+ " (%s)",
+ translated_territory);
+ }
+
+ language_name_get_codeset_details (name, &langinfo_codeset, &is_utf8);
+
+ if (codeset_code == NULL && langinfo_codeset != NULL) {
+ codeset_code = g_strdup (langinfo_codeset);
+ }
+
+ if (!is_utf8 && codeset_code) {
+ g_string_append_printf (full_language,
+ " [%s]",
+ codeset_code);
+ }
+
+out:
+ g_free (language_code);
+ g_free (territory_code);
+ g_free (codeset_code);
+ g_free (langinfo_codeset);
+ g_free (translated_language);
+ g_free (translated_territory);
+
+ if (full_language->len == 0) {
+ g_string_free (full_language, TRUE);
+ return NULL;
+ }
+
+ return g_string_free (full_language, FALSE);
+}
+
+char *
+gdm_get_region_from_name (const char *name,
+ const char *locale)
+{
+ GString *full_name;
+ char *language_code;
+ char *territory_code;
+ char *codeset_code;
+ char *langinfo_codeset;
+ char *translated_language;
+ char *translated_territory;
+ gboolean is_utf8 = TRUE;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (*name != '\0', NULL);
+
+ translated_territory = NULL;
+ translated_language = NULL;
+ langinfo_codeset = NULL;
+
+ full_name = g_string_new (NULL);
+
+ if (gdm_languages_map == NULL) {
+ languages_init ();
+ }
+
+ if (gdm_territories_map == NULL) {
+ territories_init ();
+ }
+
+ language_code = NULL;
+ territory_code = NULL;
+ codeset_code = NULL;
+
+ gdm_parse_language_name (name,
+ &language_code,
+ &territory_code,
+ &codeset_code,
+ NULL);
+
+ if (territory_code == NULL) {
+ goto out;
+ }
+
+ translated_territory = get_translated_territory (territory_code, locale);
+ g_string_append (full_name, translated_territory);
+
+ if (is_unique_territory (territory_code)) {
+ goto out;
+ }
+
+ if (language_code != NULL) {
+ translated_language = get_translated_language (language_code, locale);
+ }
+ if (translated_language != NULL) {
+ g_string_append_printf (full_name,
+ " (%s)",
+ translated_language);
+ }
+
+ language_name_get_codeset_details (name, &langinfo_codeset, &is_utf8);
+
+ if (codeset_code == NULL && langinfo_codeset != NULL) {
+ codeset_code = g_strdup (langinfo_codeset);
+ }
+
+ if (!is_utf8 && codeset_code) {
+ g_string_append_printf (full_name,
+ " [%s]",
+ codeset_code);
+ }
+
+out:
+ g_free (language_code);
+ g_free (territory_code);
+ g_free (codeset_code);
+ g_free (langinfo_codeset);
+ g_free (translated_language);
+ g_free (translated_territory);
+
+ if (full_name->len == 0) {
+ g_string_free (full_name, TRUE);
+ return NULL;
+ }
+
+ return g_string_free (full_name, FALSE);
+}
+
+char **
+gdm_get_all_language_names (void)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+ GPtrArray *array;
+
+ if (gdm_available_locales_map == NULL) {
+ collect_locales ();
+ }
+
+ array = g_ptr_array_new ();
+ g_hash_table_iter_init (&iter, gdm_available_locales_map);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GdmLocale *locale;
+
+ locale = (GdmLocale *) value;
+
+ g_ptr_array_add (array, g_strdup (locale->name));
+ }
+ g_ptr_array_add (array, NULL);
+
+ return (char **) g_ptr_array_free (array, FALSE);
+}
diff --git a/gnome-initial-setup/languages/gdm-languages.h b/gnome-initial-setup/languages/gdm-languages.h
new file mode 100644
index 0000000..5ee33b3
--- /dev/null
+++ b/gnome-initial-setup/languages/gdm-languages.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2008 Red Hat, Inc.
+ * Copyright 2007 William Jon McCann <mccann jhu edu>
+ *
+ * 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: Ray Strode
+ * William Jon McCann
+ */
+
+#ifndef __GDM_LANGUAGES_H
+#define __GDM_LANGUAGES_H
+
+G_BEGIN_DECLS
+
+char * gdm_get_language_from_name (const char *name,
+ const char *locale);
+char * gdm_get_region_from_name (const char *name,
+ const char *locale);
+char ** gdm_get_all_language_names (void);
+gboolean gdm_parse_language_name (const char *name,
+ char **language_codep,
+ char **territory_codep,
+ char **codesetp,
+ char **modifierp);
+char * gdm_normalize_language_name (const char *name);
+gboolean gdm_language_has_translations (const char *language_name);
+
+G_END_DECLS
+
+#endif /* __GDM_LANGUAGE_CHOOSER_WIDGET_H */
diff --git a/gnome-initial-setup/languages/language-chooser.ui b/gnome-initial-setup/languages/language-chooser.ui
new file mode 100644
index 0000000..a6ef96a
--- /dev/null
+++ b/gnome-initial-setup/languages/language-chooser.ui
@@ -0,0 +1,114 @@
+<?xml version="1.0"?>
+<interface>
+ <object class="GtkDialog" id="dialog">
+ <property name="height_request">400</property>
+ <property name="border_width">5</property>
+ <property name="modal">True</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="type_hint">dialog</property>
+ <property name="title"> </property>
+ <property name="icon_name">system-users</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="content-area">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="margin_left">10</property>
+ <property name="margin_right">10</property>
+ <child>
+ <object class="GtkLabel" id="title">
+ <property name="visible">True</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Select a language</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="language-list">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="enable_search">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="filter-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="secondary_icon_name">edit-find-symbolic</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="secondary_icon_sensitive">False</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="action-area">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="cancel-button">
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok-button">
+ <property name="label" translatable="yes">_Select</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">cancel-button</action-widget>
+ <action-widget response="-5">ok-button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/gnome-initial-setup/languages/list-languages.c b/gnome-initial-setup/languages/list-languages.c
new file mode 100644
index 0000000..29ebc46
--- /dev/null
+++ b/gnome-initial-setup/languages/list-languages.c
@@ -0,0 +1,36 @@
+#include <glib.h>
+#include <glib-object.h>
+#include "gdm-languages.h"
+
+int main (int argc, char **argv)
+{
+ char **langs;
+ guint i;
+
+ g_type_init ();
+
+ if (argc > 1) {
+ guint i;
+ for (i = 1; i < argc; i++) {
+ char *lang;
+ lang = gdm_get_language_from_name (argv[i], NULL);
+ g_print ("%s == %s\n", argv[i], lang);
+ g_free (lang);
+ }
+ return 0;
+ }
+
+ langs = gdm_get_all_language_names ();
+ if (langs == NULL) {
+ g_warning ("No languages found");
+ return 1;
+ }
+
+ for (i = 0; langs[i] != NULL; i++)
+ g_print ("%s == %s\n", langs[i], gdm_get_language_from_name (langs[i], NULL));
+
+ g_strfreev (langs);
+
+ return 0;
+}
+
diff --git a/gnome-initial-setup/languages/locarchive.h b/gnome-initial-setup/languages/locarchive.h
new file mode 100644
index 0000000..f933f4d
--- /dev/null
+++ b/gnome-initial-setup/languages/locarchive.h
@@ -0,0 +1,97 @@
+/* Definitions for locale archive handling.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _LOCARCHIVE_H
+#define _LOCARCHIVE_H 1
+
+#include <stdint.h>
+
+#define AR_MAGIC 0xde020109
+
+struct locarhead
+{
+ uint32_t magic;
+ /* Serial number. */
+ uint32_t serial;
+ /* Name hash table. */
+ uint32_t namehash_offset;
+ uint32_t namehash_used;
+ uint32_t namehash_size;
+ /* String table. */
+ uint32_t string_offset;
+ uint32_t string_used;
+ uint32_t string_size;
+ /* Table with locale records. */
+ uint32_t locrectab_offset;
+ uint32_t locrectab_used;
+ uint32_t locrectab_size;
+ /* MD5 sum hash table. */
+ uint32_t sumhash_offset;
+ uint32_t sumhash_used;
+ uint32_t sumhash_size;
+};
+
+
+struct namehashent
+{
+ /* Hash value of the name. */
+ uint32_t hashval;
+ /* Offset of the name in the string table. */
+ uint32_t name_offset;
+ /* Offset of the locale record. */
+ uint32_t locrec_offset;
+};
+
+
+struct sumhashent
+{
+ /* MD5 sum. */
+ char sum[16];
+ /* Offset of the file in the archive. */
+ uint32_t file_offset;
+};
+
+struct locrecent
+{
+ uint32_t refs; /* # of namehashent records that point here */
+ struct
+ {
+ uint32_t offset;
+ uint32_t len;
+ } record[__LC_LAST];
+};
+
+
+struct locarhandle
+{
+ int fd;
+ void *addr;
+ size_t len;
+};
+
+
+/* In memory data for the locales with their checksums. */
+typedef struct locale_category_data
+{
+ off_t size;
+ void *addr;
+ char sum[16];
+} locale_data_t[__LC_LAST];
+
+#endif /* locarchive.h */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]