diff options
Diffstat (limited to 'panels/common/cc-language-chooser.c')
-rw-r--r-- | panels/common/cc-language-chooser.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/panels/common/cc-language-chooser.c b/panels/common/cc-language-chooser.c new file mode 100644 index 0000000..1aa2ec4 --- /dev/null +++ b/panels/common/cc-language-chooser.c @@ -0,0 +1,343 @@ +/* -*- 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, see <http://www.gnu.org/licenses/>. + * + * Written by: Matthias Clasen <mclasen@redhat.com> + */ + +#define _GNU_SOURCE +#include <config.h> +#include "cc-language-chooser.h" +#include "cc-language-row.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 "cc-common-language.h" +#include "cc-util.h" + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include <libgnome-desktop/gnome-languages.h> + +struct _CcLanguageChooser { + GtkDialog parent_instance; + + GtkSearchEntry *language_filter_entry; + GtkListBox *language_listbox; + GtkListBoxRow *more_row; + GtkSearchBar *search_bar; + GtkButton *select_button; + + gboolean showing_extra; + gchar *language; + gchar **filter_words; +}; + +G_DEFINE_TYPE (CcLanguageChooser, cc_language_chooser, GTK_TYPE_DIALOG) + +static void +add_all_languages (CcLanguageChooser *self) +{ + g_auto(GStrv) locale_ids = NULL; + g_autoptr(GHashTable) initial = NULL; + + locale_ids = gnome_get_all_locales (); + initial = cc_common_language_get_initial_languages (); + for (int i = 0; locale_ids[i] != NULL; i++) { + CcLanguageRow *row; + gboolean is_initial; + + if (!cc_common_language_has_font (locale_ids[i])) + continue; + + row = cc_language_row_new (locale_ids[i]); + gtk_widget_show (GTK_WIDGET (row)); + is_initial = (g_hash_table_lookup (initial, locale_ids[i]) != NULL); + cc_language_row_set_is_extra (row, !is_initial); + gtk_list_box_prepend (self->language_listbox, GTK_WIDGET (row)); + } +} + +static gboolean +match_all (gchar **words, + const gchar *str) +{ + gchar **w; + + if (str == NULL) + return FALSE; + + for (w = words; *w; ++w) + if (!strstr (str, *w)) + return FALSE; + + return TRUE; +} + +static gboolean +language_visible (GtkListBoxRow *row, + gpointer user_data) +{ + CcLanguageChooser *self = user_data; + g_autofree gchar *language = NULL; + g_autofree gchar *country = NULL; + g_autofree gchar *language_local = NULL; + g_autofree gchar *country_local = NULL; + gboolean visible; + + if (row == self->more_row) + return !self->showing_extra; + + if (!CC_IS_LANGUAGE_ROW (row)) + return TRUE; + + if (!self->showing_extra && cc_language_row_get_is_extra (CC_LANGUAGE_ROW (row))) + return FALSE; + + if (!self->filter_words) + return TRUE; + + language = + cc_util_normalize_casefold_and_unaccent (cc_language_row_get_language (CC_LANGUAGE_ROW (row))); + visible = match_all (self->filter_words, language); + if (visible) + return TRUE; + + country = + cc_util_normalize_casefold_and_unaccent (cc_language_row_get_country (CC_LANGUAGE_ROW (row))); + visible = match_all (self->filter_words, country); + if (visible) + return TRUE; + + language_local = + cc_util_normalize_casefold_and_unaccent (cc_language_row_get_language_local (CC_LANGUAGE_ROW (row))); + visible = match_all (self->filter_words, language_local); + if (visible) + return TRUE; + + country_local = + cc_util_normalize_casefold_and_unaccent (cc_language_row_get_country_local (CC_LANGUAGE_ROW (row))); + return match_all (self->filter_words, country_local); +} + +static gint +sort_languages (GtkListBoxRow *a, + GtkListBoxRow *b, + gpointer data) +{ + int d; + + if (!CC_IS_LANGUAGE_ROW (a)) + return 1; + if (!CC_IS_LANGUAGE_ROW (b)) + return -1; + + d = g_strcmp0 (cc_language_row_get_language (CC_LANGUAGE_ROW (a)), cc_language_row_get_language (CC_LANGUAGE_ROW (b))); + if (d != 0) + return d; + + return g_strcmp0 (cc_language_row_get_country (CC_LANGUAGE_ROW (a)), cc_language_row_get_country (CC_LANGUAGE_ROW (b))); +} + +static void +language_filter_entry_search_changed_cb (CcLanguageChooser *self) +{ + g_autofree gchar *filter_contents = NULL; + + g_clear_pointer (&self->filter_words, g_strfreev); + + filter_contents = + cc_util_normalize_casefold_and_unaccent (gtk_editable_get_text (GTK_EDITABLE (self->language_filter_entry))); + if (!filter_contents) { + gtk_list_box_invalidate_filter (self->language_listbox); + return; + } + self->filter_words = g_strsplit_set (g_strstrip (filter_contents), " ", 0); + gtk_list_box_invalidate_filter (self->language_listbox); +} + +static void +show_more (CcLanguageChooser *self, gboolean visible) +{ + gint width, height; + + gtk_window_get_default_size (GTK_WINDOW (self), &width, &height); + gtk_widget_set_size_request (GTK_WIDGET (self), width, height); + + gtk_search_bar_set_search_mode (self->search_bar, visible); + gtk_widget_grab_focus (visible ? GTK_WIDGET (self->language_filter_entry) : GTK_WIDGET (self->language_listbox)); + + self->showing_extra = visible; + + gtk_list_box_invalidate_filter (self->language_listbox); +} + +static void +set_locale_id (CcLanguageChooser *self, + const gchar *locale_id) +{ + GtkWidget *child; + + gtk_widget_set_sensitive (GTK_WIDGET (self->select_button), FALSE); + + for (child = gtk_widget_get_first_child (GTK_WIDGET (self->language_listbox)); + child; + child = gtk_widget_get_next_sibling (child)) { + CcLanguageRow *row; + + if (!CC_IS_LANGUAGE_ROW (child)) + continue; + + row = CC_LANGUAGE_ROW (child); + if (g_strcmp0 (locale_id, cc_language_row_get_locale_id (row)) == 0) { + cc_language_row_set_checked (row, TRUE); + gtk_widget_set_sensitive (GTK_WIDGET (self->select_button), TRUE); + + /* make sure the selected language is shown */ + if (!self->showing_extra && cc_language_row_get_is_extra (row)) { + cc_language_row_set_is_extra (row, FALSE); + gtk_list_box_invalidate_filter (self->language_listbox); + } + } else { + cc_language_row_set_checked (row, FALSE); + } + } + + g_free (self->language); + self->language = g_strdup (locale_id); +} + +static void +language_listbox_row_activated_cb (CcLanguageChooser *self, GtkListBoxRow *row) +{ + const gchar *new_locale_id; + + if (row == self->more_row) { + show_more (self, TRUE); + return; + } + + if (!CC_IS_LANGUAGE_ROW (row)) + return; + + new_locale_id = cc_language_row_get_locale_id (CC_LANGUAGE_ROW (row)); + if (g_strcmp0 (new_locale_id, self->language) == 0) { + gtk_dialog_response (GTK_DIALOG (self), + gtk_dialog_get_response_for_widget (GTK_DIALOG (self), + GTK_WIDGET (self->select_button))); + } else { + set_locale_id (self, new_locale_id); + } +} + +static void +activate_default_cb (CcLanguageChooser *self) +{ + GtkWidget *focus; + + focus = gtk_window_get_focus (GTK_WINDOW (self)); + if (!focus || !CC_IS_LANGUAGE_ROW (focus)) + return; + + if (g_strcmp0 (cc_language_row_get_locale_id (CC_LANGUAGE_ROW (focus)), self->language) == 0) + return; + + g_signal_stop_emission_by_name (GTK_WINDOW (self), "activate-default"); + gtk_widget_activate (focus); +} + +void +cc_language_chooser_init (CcLanguageChooser *self) +{ + g_resources_register (cc_common_get_resource ()); + + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_list_box_set_sort_func (self->language_listbox, + sort_languages, self, NULL); + gtk_list_box_set_filter_func (self->language_listbox, + language_visible, self, NULL); + add_all_languages (self); + + gtk_list_box_invalidate_filter (self->language_listbox); +} + +static void +cc_language_chooser_dispose (GObject *object) +{ + CcLanguageChooser *self = CC_LANGUAGE_CHOOSER (object); + + g_clear_pointer (&self->filter_words, g_strfreev); + g_clear_pointer (&self->language, g_free); + + G_OBJECT_CLASS (cc_language_chooser_parent_class)->dispose (object); +} + +void +cc_language_chooser_class_init (CcLanguageChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = cc_language_chooser_dispose; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/common/cc-language-chooser.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, language_filter_entry); + gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, language_listbox); + gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, more_row); + gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, search_bar); + gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, select_button); + + gtk_widget_class_bind_template_callback (widget_class, activate_default_cb); + gtk_widget_class_bind_template_callback (widget_class, language_filter_entry_search_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, language_listbox_row_activated_cb); +} + +CcLanguageChooser * +cc_language_chooser_new (void) +{ + return CC_LANGUAGE_CHOOSER (g_object_new (CC_TYPE_LANGUAGE_CHOOSER, + "use-header-bar", 1, + NULL)); +} + +void +cc_language_chooser_clear_filter (CcLanguageChooser *self) +{ + g_return_if_fail (CC_IS_LANGUAGE_CHOOSER (self)); + gtk_editable_set_text (GTK_EDITABLE (self->language_filter_entry), ""); + show_more (self, FALSE); +} + +const gchar * +cc_language_chooser_get_language (CcLanguageChooser *self) +{ + g_return_val_if_fail (CC_IS_LANGUAGE_CHOOSER (self), NULL); + return self->language; +} + +void +cc_language_chooser_set_language (CcLanguageChooser *self, + const gchar *language) +{ + g_return_if_fail (CC_IS_LANGUAGE_CHOOSER (self)); + set_locale_id (self, language); +} |