gnome-control-center/panels/common/cc-language-chooser.c
2013-02-19 12:25:54 +01:00

426 lines
14 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2013 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>
*/
#define _GNU_SOURCE
#include <config.h>
#include "cc-language-chooser.h"
#include "cc-common-resources.h"
#include <locale.h>
#include <string.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <gtk/gtk.h>
#include "egg-list-box/egg-list-box.h"
#include "cc-common-language.h"
#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <libgnome-desktop/gnome-languages.h>
typedef struct {
GtkWidget *no_results;
GtkWidget *more_item;
GtkWidget *filter_entry;
GtkWidget *language_list;
GtkWidget *scrolledwindow;
gboolean adding_languages;
gboolean showing_extra;
gchar *language;
} CcLanguageChooserPrivate;
#define GET_PRIVATE(chooser) ((CcLanguageChooserPrivate *) g_object_get_data (G_OBJECT (chooser), "private"))
static void
set_locale_id (GtkDialog *chooser,
const gchar *locale_id)
{
CcLanguageChooserPrivate *priv = GET_PRIVATE (chooser);
GList *children, *l;
children = gtk_container_get_children (GTK_CONTAINER (priv->language_list));
for (l = children; l; l = l->next) {
GtkWidget *row = l->data;
GtkWidget *check = g_object_get_data (G_OBJECT (row), "check");
const gchar *language = g_object_get_data (G_OBJECT (row), "locale-id");
if (check == NULL || language == NULL)
continue;
if (strcmp (locale_id, language) == 0) {
gboolean is_extra;
gtk_image_set_from_icon_name (GTK_IMAGE (check), "object-select-symbolic", GTK_ICON_SIZE_MENU);
/* make sure the selected language is shown */
is_extra = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (row), "is-extra"));
if (!priv->showing_extra && is_extra) {
g_object_set_data (G_OBJECT (row), "is-extra", GINT_TO_POINTER (FALSE));
egg_list_box_refilter (EGG_LIST_BOX (priv->language_list));
}
} else {
gtk_image_clear (GTK_IMAGE (check));
g_object_set (check, "icon-size", GTK_ICON_SIZE_MENU, NULL);
}
}
g_list_free (children);
g_free (priv->language);
priv->language = g_strdup (locale_id);
}
static gint
sort_languages (gconstpointer a,
gconstpointer b,
gpointer data)
{
const gchar *la;
const gchar *lb;
gboolean iea;
gboolean ieb;
if (g_object_get_data (G_OBJECT (a), "locale-id") == NULL) {
return 1;
}
if (g_object_get_data (G_OBJECT (b), "locale-id") == NULL) {
return -1;
}
la = g_object_get_data (G_OBJECT (a), "locale-name");
lb = g_object_get_data (G_OBJECT (b), "locale-name");
iea = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (a), "is-extra"));
ieb = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (b), "is-extra"));
if (iea != ieb) {
return ieb - iea;
} else {
return strcmp (la, lb);
}
}
static GtkWidget *
padded_label_new (char *text, gboolean narrow)
{
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_widget_set_margin_left (widget, narrow ? 10 : 80);
gtk_widget_set_margin_right (widget, narrow ? 10 : 80);
gtk_box_pack_start (GTK_BOX (widget), gtk_label_new (text), FALSE, FALSE, 0);
return widget;
}
static GtkWidget *
language_widget_new (const gchar *locale_id,
const gchar *current_locale_id,
gboolean is_extra)
{
gchar *locale_name;
GtkWidget *widget;
GtkWidget *check;
locale_name = gnome_get_language_from_locale (locale_id, locale_id);
widget = padded_label_new (locale_name, is_extra);
check = gtk_image_new ();
g_object_set (check, "icon-size", GTK_ICON_SIZE_MENU, NULL);
gtk_box_pack_start (GTK_BOX (widget), check, FALSE, FALSE, 0);
if (g_strcmp0 (locale_id, current_locale_id) == 0) {
gtk_image_set_from_icon_name (GTK_IMAGE (check), "object-select-symbolic", GTK_ICON_SIZE_MENU);
}
g_object_set_data (G_OBJECT (widget), "check", check);
g_object_set_data_full (G_OBJECT (widget), "locale-id", g_strdup (locale_id), g_free);
g_object_set_data_full (G_OBJECT (widget), "locale-name", g_strdup (locale_name), g_free);
g_object_set_data (G_OBJECT (widget), "is-extra", GUINT_TO_POINTER (is_extra));
g_free (locale_name);
return widget;
}
static GtkWidget *
more_widget_new (void)
{
GtkWidget *widget;
widget = padded_label_new ("", FALSE);
gtk_widget_set_tooltip_text (widget, _("More…"));
return widget;
}
static GtkWidget *
no_results_widget_new (void)
{
GtkWidget *widget;
widget = padded_label_new (_("No languages found"), TRUE);
gtk_widget_set_sensitive (widget, FALSE);
return widget;
}
static void
add_languages (GtkDialog *chooser,
gchar **locale_ids,
GHashTable *initial)
{
CcLanguageChooserPrivate *priv = GET_PRIVATE (chooser);
priv->adding_languages = TRUE;
while (*locale_ids) {
gchar *locale_id;
gboolean is_initial;
GtkWidget *widget;
locale_id = *locale_ids;
locale_ids ++;
if (!cc_common_language_has_font (locale_id))
continue;
is_initial = (g_hash_table_lookup (initial, locale_id) != NULL);
widget = language_widget_new (locale_id, priv->language, !is_initial);
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_all (priv->language_list);
priv->adding_languages = FALSE;
}
static void
add_all_languages (GtkDialog *chooser)
{
gchar **locale_ids;
GHashTable *initial;
locale_ids = gnome_get_all_locales ();
initial = cc_common_language_get_initial_languages ();
add_languages (chooser, locale_ids, initial);
}
static gboolean
language_visible (GtkWidget *child,
gpointer user_data)
{
GtkDialog *chooser = user_data;
CcLanguageChooserPrivate *priv = GET_PRIVATE (chooser);
gchar *locale_name;
const gchar *filter_contents;
gboolean is_extra;
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;
is_extra = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (child), "is-extra"));
locale_name = g_object_get_data (G_OBJECT (child), "locale-name");
filter_contents = gtk_entry_get_text (GTK_ENTRY (priv->filter_entry));
if (*filter_contents && strcasestr (locale_name, filter_contents) == NULL)
return FALSE;
if (!priv->showing_extra && is_extra)
return FALSE;
return TRUE;
}
static void
show_more (GtkDialog *chooser)
{
CcLanguageChooserPrivate *priv = GET_PRIVATE (chooser);
GtkWidget *widget;
gint width, height;
gtk_window_get_size (GTK_WINDOW (chooser), &width, &height);
gtk_widget_set_size_request (GTK_WIDGET (chooser), width, height);
gtk_window_set_resizable (GTK_WINDOW (chooser), TRUE);
widget = priv->scrolledwindow;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget),
GTK_POLICY_NEVER,
GTK_POLICY_AUTOMATIC);
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,
GtkDialog *chooser)
{
CcLanguageChooserPrivate *priv = GET_PRIVATE (chooser);
gchar *new_locale_id;
if (priv->adding_languages)
return;
if (child == NULL)
return;
else if (child == priv->no_results)
return;
else if (child == priv->more_item)
show_more (chooser);
else {
new_locale_id = g_object_get_data (G_OBJECT (child), "locale-id");
set_locale_id (chooser, new_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)
{
GtkDialog *chooser = user_data;
CcLanguageChooserPrivate *priv = GET_PRIVATE (chooser);
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
cc_language_chooser_private_free (gpointer data)
{
CcLanguageChooserPrivate *priv = data;
g_free (priv->language);
g_free (priv);
}
#define WID(name) ((GtkWidget *) gtk_builder_get_object (builder, name))
GtkWidget *
cc_language_chooser_new (GtkWidget *parent)
{
GtkBuilder *builder;
GtkWidget *chooser;
CcLanguageChooserPrivate *priv;
GError *error = NULL;
g_resources_register (cc_common_get_resource ());
builder = gtk_builder_new ();
gtk_builder_add_from_resource (builder, "/org/gnome/control-center/common/language-chooser.ui", &error);
if (error) {
g_warning ("failed to load language chooser: %s", error->message);
g_error_free (error);
return NULL;
}
chooser = WID ("language-dialog");
priv = g_new0 (CcLanguageChooserPrivate, 1);
g_object_set_data_full (G_OBJECT (chooser), "private", priv, cc_language_chooser_private_free);
priv->filter_entry = WID ("language-filter-entry");
priv->language_list = WID ("language-list");
priv->scrolledwindow = WID ("language-scrolledwindow");
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 (priv->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_selection_mode (EGG_LIST_BOX (priv->language_list),
GTK_SELECTION_NONE);
add_all_languages (GTK_DIALOG (chooser));
g_signal_connect_swapped (priv->filter_entry, "changed",
G_CALLBACK (egg_list_box_refilter),
priv->language_list);
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));
gtk_window_set_transient_for (GTK_WINDOW (chooser), GTK_WINDOW (parent));
return chooser;
}
void
cc_language_chooser_clear_filter (GtkWidget *chooser)
{
CcLanguageChooserPrivate *priv = GET_PRIVATE (chooser);
gtk_entry_set_text (GTK_ENTRY (priv->filter_entry), "");
}
const gchar *
cc_language_chooser_get_language (GtkWidget *chooser)
{
CcLanguageChooserPrivate *priv = GET_PRIVATE (chooser);
return priv->language;
}
void
cc_language_chooser_set_language (GtkWidget *chooser,
const gchar *language)
{
set_locale_id (GTK_DIALOG (chooser), language);
}