/* -*- 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 .
*
* Written by: Matthias Clasen
*/
#define _GNU_SOURCE
#include
#include "cc-language-chooser.h"
#include "cc-language-row.h"
#include "cc-common-resources.h"
#include
#include
#include
#include
#include
#include "cc-common-language.h"
#include "cc-util.h"
#define GNOME_DESKTOP_USE_UNSTABLE_API
#include
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);
}