gnome-control-center/panels/common/cc-language-chooser.c
Bastien Nocera 897243858f region: Reinstate the separator in the languages list
The separator went missing when we started adding the popular
languages to the list. Make it come back to separate the user
languages from the popular ones.
2012-12-11 09:15:55 +01:00

337 lines
10 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright 2009-2010 Red Hat, Inc,
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Written by: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include <stdlib.h>
#include <locale.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <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);
if (gtk_tree_selection_get_selected (selection, &model, &iter))
gtk_tree_model_get (model, &iter, LOCALE_COL, &lang, -1);
else
lang = NULL;
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;
gtk_tree_model_get (model, iter, DISPLAY_LOCALE_COL, &locale, -1);
if (locale == NULL)
return FALSE;
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;
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) {
gtk_window_set_title (GTK_WINDOW (chooser), _("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, NULL);
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;
}