392 lines
13 KiB
C
392 lines
13 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, 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 "list-box-helper.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;
|
|
|
|
GtkWidget *select_button;
|
|
GtkListBoxRow *more_item;
|
|
GtkWidget *search_bar;
|
|
GtkWidget *language_filter_entry;
|
|
GtkWidget *language_listbox;
|
|
gboolean showing_extra;
|
|
gchar *language;
|
|
gchar **filter_words;
|
|
};
|
|
|
|
G_DEFINE_TYPE (CcLanguageChooser, cc_language_chooser, GTK_TYPE_DIALOG)
|
|
|
|
static GtkListBoxRow *
|
|
more_widget_new (void)
|
|
{
|
|
GtkWidget *box, *row;
|
|
GtkWidget *arrow;
|
|
|
|
row = gtk_list_box_row_new ();
|
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
|
|
gtk_widget_show (box);
|
|
gtk_container_add (GTK_CONTAINER (row), box);
|
|
gtk_widget_set_tooltip_text (box, _("More…"));
|
|
|
|
arrow = gtk_image_new_from_icon_name ("view-more-symbolic", GTK_ICON_SIZE_MENU);
|
|
gtk_widget_show (arrow);
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (arrow), "dim-label");
|
|
gtk_widget_set_margin_top (box, 10);
|
|
gtk_widget_set_margin_bottom (box, 10);
|
|
gtk_box_pack_start (GTK_BOX (box), arrow, TRUE, TRUE, 0);
|
|
|
|
return GTK_LIST_BOX_ROW (row);
|
|
}
|
|
|
|
static void
|
|
add_languages (CcLanguageChooser *chooser,
|
|
gchar **locale_ids,
|
|
GHashTable *initial)
|
|
{
|
|
while (*locale_ids) {
|
|
gchar *locale_id;
|
|
gboolean is_initial;
|
|
CcLanguageRow *row;
|
|
|
|
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);
|
|
row = cc_language_row_new (locale_id);
|
|
cc_language_row_set_is_extra (row, !is_initial);
|
|
gtk_widget_show (GTK_WIDGET (row));
|
|
gtk_container_add (GTK_CONTAINER (chooser->language_listbox), GTK_WIDGET (row));
|
|
}
|
|
|
|
gtk_container_add (GTK_CONTAINER (chooser->language_listbox), GTK_WIDGET (chooser->more_item));
|
|
}
|
|
|
|
static void
|
|
add_all_languages (CcLanguageChooser *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);
|
|
g_hash_table_destroy (initial);
|
|
g_strfreev (locale_ids);
|
|
}
|
|
|
|
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 *chooser = 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 == chooser->more_item)
|
|
return !chooser->showing_extra;
|
|
|
|
if (!CC_IS_LANGUAGE_ROW (row))
|
|
return TRUE;
|
|
|
|
if (!chooser->showing_extra && cc_language_row_get_is_extra (CC_LANGUAGE_ROW (row)))
|
|
return FALSE;
|
|
|
|
if (!chooser->filter_words)
|
|
return TRUE;
|
|
|
|
language =
|
|
cc_util_normalize_casefold_and_unaccent (cc_language_row_get_language (CC_LANGUAGE_ROW (row)));
|
|
visible = match_all (chooser->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 (chooser->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 (chooser->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 (chooser->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
|
|
filter_changed (CcLanguageChooser *chooser)
|
|
{
|
|
g_autofree gchar *filter_contents = NULL;
|
|
|
|
g_clear_pointer (&chooser->filter_words, g_strfreev);
|
|
|
|
filter_contents =
|
|
cc_util_normalize_casefold_and_unaccent (gtk_entry_get_text (GTK_ENTRY (chooser->language_filter_entry)));
|
|
if (!filter_contents) {
|
|
gtk_list_box_invalidate_filter (GTK_LIST_BOX (chooser->language_listbox));
|
|
return;
|
|
}
|
|
chooser->filter_words = g_strsplit_set (g_strstrip (filter_contents), " ", 0);
|
|
gtk_list_box_invalidate_filter (GTK_LIST_BOX (chooser->language_listbox));
|
|
}
|
|
|
|
static void
|
|
show_more (CcLanguageChooser *chooser, gboolean visible)
|
|
{
|
|
gint width, height;
|
|
|
|
gtk_window_get_size (GTK_WINDOW (chooser), &width, &height);
|
|
gtk_widget_set_size_request (GTK_WIDGET (chooser), width, height);
|
|
|
|
gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (chooser->search_bar), visible);
|
|
gtk_widget_grab_focus (visible ? chooser->language_filter_entry : chooser->language_listbox);
|
|
|
|
chooser->showing_extra = visible;
|
|
|
|
gtk_list_box_invalidate_filter (GTK_LIST_BOX (chooser->language_listbox));
|
|
}
|
|
|
|
static void
|
|
set_locale_id (CcLanguageChooser *chooser,
|
|
const gchar *locale_id)
|
|
{
|
|
g_autoptr(GList) children = NULL;
|
|
GList *l;
|
|
|
|
gtk_widget_set_sensitive (chooser->select_button, FALSE);
|
|
|
|
children = gtk_container_get_children (GTK_CONTAINER (chooser->language_listbox));
|
|
for (l = children; l; l = l->next) {
|
|
GtkListBoxRow *row = l->data;
|
|
|
|
if (!CC_IS_LANGUAGE_ROW (row))
|
|
continue;
|
|
|
|
if (g_strcmp0 (locale_id, cc_language_row_get_locale_id (CC_LANGUAGE_ROW (row))) == 0) {
|
|
cc_language_row_set_checked (CC_LANGUAGE_ROW (row), TRUE);
|
|
gtk_widget_set_sensitive (chooser->select_button, TRUE);
|
|
|
|
/* make sure the selected language is shown */
|
|
if (!chooser->showing_extra && cc_language_row_get_is_extra (CC_LANGUAGE_ROW (row))) {
|
|
cc_language_row_set_is_extra (CC_LANGUAGE_ROW (row), FALSE);
|
|
gtk_list_box_invalidate_filter (GTK_LIST_BOX (chooser->language_listbox));
|
|
}
|
|
} else {
|
|
cc_language_row_set_checked (CC_LANGUAGE_ROW (row), FALSE);
|
|
}
|
|
}
|
|
|
|
g_free (chooser->language);
|
|
chooser->language = g_strdup (locale_id);
|
|
}
|
|
|
|
static void
|
|
row_activated (CcLanguageChooser *chooser,
|
|
GtkListBoxRow *row)
|
|
{
|
|
const gchar *new_locale_id;
|
|
|
|
if (row == chooser->more_item) {
|
|
show_more (chooser, 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, chooser->language) == 0) {
|
|
gtk_dialog_response (GTK_DIALOG (chooser),
|
|
gtk_dialog_get_response_for_widget (GTK_DIALOG (chooser),
|
|
chooser->select_button));
|
|
} else {
|
|
set_locale_id (chooser, new_locale_id);
|
|
}
|
|
}
|
|
|
|
static void
|
|
activate_default (CcLanguageChooser *chooser)
|
|
{
|
|
GtkWidget *focus;
|
|
|
|
focus = gtk_window_get_focus (GTK_WINDOW (chooser));
|
|
if (!focus || !CC_IS_LANGUAGE_ROW (focus))
|
|
return;
|
|
|
|
if (g_strcmp0 (cc_language_row_get_locale_id (CC_LANGUAGE_ROW (focus)), chooser->language) == 0)
|
|
return;
|
|
|
|
g_signal_stop_emission_by_name (GTK_WINDOW (chooser), "activate-default");
|
|
gtk_widget_activate (focus);
|
|
}
|
|
|
|
void
|
|
cc_language_chooser_init (CcLanguageChooser *chooser)
|
|
{
|
|
g_resources_register (cc_common_get_resource ());
|
|
|
|
gtk_widget_init_template (GTK_WIDGET (chooser));
|
|
|
|
chooser->more_item = more_widget_new ();
|
|
gtk_widget_show (GTK_WIDGET (chooser->more_item));
|
|
|
|
gtk_list_box_set_sort_func (GTK_LIST_BOX (chooser->language_listbox),
|
|
sort_languages, chooser, NULL);
|
|
gtk_list_box_set_filter_func (GTK_LIST_BOX (chooser->language_listbox),
|
|
language_visible, chooser, NULL);
|
|
gtk_list_box_set_selection_mode (GTK_LIST_BOX (chooser->language_listbox),
|
|
GTK_SELECTION_NONE);
|
|
gtk_list_box_set_header_func (GTK_LIST_BOX (chooser->language_listbox),
|
|
cc_list_box_update_header_func, NULL, NULL);
|
|
add_all_languages (chooser);
|
|
|
|
g_signal_connect_object (chooser->language_filter_entry, "search-changed",
|
|
G_CALLBACK (filter_changed), chooser, G_CONNECT_SWAPPED);
|
|
|
|
g_signal_connect_object (chooser->language_listbox, "row-activated",
|
|
G_CALLBACK (row_activated), chooser, G_CONNECT_SWAPPED);
|
|
|
|
gtk_list_box_invalidate_filter (GTK_LIST_BOX (chooser->language_listbox));
|
|
|
|
g_signal_connect (chooser, "activate-default",
|
|
G_CALLBACK (activate_default), NULL);
|
|
}
|
|
|
|
static void
|
|
cc_language_chooser_dispose (GObject *object)
|
|
{
|
|
CcLanguageChooser *chooser = CC_LANGUAGE_CHOOSER (object);
|
|
|
|
g_clear_pointer (&chooser->filter_words, g_strfreev);
|
|
g_clear_pointer (&chooser->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, select_button);
|
|
gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, search_bar);
|
|
gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, language_filter_entry);
|
|
gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, language_listbox);
|
|
}
|
|
|
|
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 *chooser)
|
|
{
|
|
g_return_if_fail (CC_IS_LANGUAGE_CHOOSER (chooser));
|
|
gtk_entry_set_text (GTK_ENTRY (chooser->language_filter_entry), "");
|
|
show_more (chooser, FALSE);
|
|
}
|
|
|
|
const gchar *
|
|
cc_language_chooser_get_language (CcLanguageChooser *chooser)
|
|
{
|
|
g_return_val_if_fail (CC_IS_LANGUAGE_CHOOSER (chooser), NULL);
|
|
return chooser->language;
|
|
}
|
|
|
|
void
|
|
cc_language_chooser_set_language (CcLanguageChooser *chooser,
|
|
const gchar *language)
|
|
{
|
|
g_return_if_fail (CC_IS_LANGUAGE_CHOOSER (chooser));
|
|
set_locale_id (chooser, language);
|
|
}
|