GCC Code Coverage Report


Directory: ./
File: panels/common/cc-language-chooser.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 158 0.0%
Functions: 0 19 0.0%
Branches: 0 79 0.0%

Line Branch Exec Source
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2013 Red Hat, Inc
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Written by: Matthias Clasen <mclasen@redhat.com>
19 */
20
21 #define _GNU_SOURCE
22 #include <config.h>
23 #include "cc-language-chooser.h"
24 #include "cc-language-row.h"
25 #include "cc-common-resources.h"
26
27 #include <locale.h>
28 #include <string.h>
29 #include <glib/gi18n.h>
30 #include <gio/gio.h>
31 #include <gtk/gtk.h>
32
33 #include "cc-common-language.h"
34 #include "cc-util.h"
35
36 #define GNOME_DESKTOP_USE_UNSTABLE_API
37 #include <libgnome-desktop/gnome-languages.h>
38
39 struct _CcLanguageChooser {
40 GtkDialog parent_instance;
41
42 GtkSearchEntry *language_filter_entry;
43 GtkListBox *language_listbox;
44 GtkListBoxRow *more_row;
45 GtkSearchBar *search_bar;
46 GtkButton *select_button;
47
48 gboolean showing_extra;
49 gchar *language;
50 gchar **filter_words;
51 };
52
53 G_DEFINE_TYPE (CcLanguageChooser, cc_language_chooser, GTK_TYPE_DIALOG)
54
55 static void
56 add_all_languages (CcLanguageChooser *self)
57 {
58 g_auto(GStrv) locale_ids = NULL;
59 g_autoptr(GHashTable) initial = NULL;
60
61 locale_ids = gnome_get_all_locales ();
62 initial = cc_common_language_get_initial_languages ();
63 for (int i = 0; locale_ids[i] != NULL; i++) {
64 CcLanguageRow *row;
65 gboolean is_initial;
66
67 if (!cc_common_language_has_font (locale_ids[i]))
68 continue;
69
70 row = cc_language_row_new (locale_ids[i]);
71 is_initial = (g_hash_table_lookup (initial, locale_ids[i]) != NULL);
72 cc_language_row_set_is_extra (row, !is_initial);
73 gtk_list_box_prepend (self->language_listbox, GTK_WIDGET (row));
74 }
75 }
76
77 static gboolean
78 match_all (gchar **words,
79 const gchar *str)
80 {
81 gchar **w;
82
83 if (str == NULL)
84 return FALSE;
85
86 for (w = words; *w; ++w)
87 if (!strstr (str, *w))
88 return FALSE;
89
90 return TRUE;
91 }
92
93 static gboolean
94 language_visible (GtkListBoxRow *row,
95 gpointer user_data)
96 {
97 CcLanguageChooser *self = user_data;
98 g_autofree gchar *language = NULL;
99 g_autofree gchar *country = NULL;
100 g_autofree gchar *language_local = NULL;
101 g_autofree gchar *country_local = NULL;
102 gboolean visible;
103
104 if (row == self->more_row)
105 return !self->showing_extra;
106
107 if (!CC_IS_LANGUAGE_ROW (row))
108 return TRUE;
109
110 if (!self->showing_extra && cc_language_row_get_is_extra (CC_LANGUAGE_ROW (row)))
111 return FALSE;
112
113 if (!self->filter_words)
114 return TRUE;
115
116 language =
117 cc_util_normalize_casefold_and_unaccent (cc_language_row_get_language (CC_LANGUAGE_ROW (row)));
118 visible = match_all (self->filter_words, language);
119 if (visible)
120 return TRUE;
121
122 country =
123 cc_util_normalize_casefold_and_unaccent (cc_language_row_get_country (CC_LANGUAGE_ROW (row)));
124 visible = match_all (self->filter_words, country);
125 if (visible)
126 return TRUE;
127
128 language_local =
129 cc_util_normalize_casefold_and_unaccent (cc_language_row_get_language_local (CC_LANGUAGE_ROW (row)));
130 visible = match_all (self->filter_words, language_local);
131 if (visible)
132 return TRUE;
133
134 country_local =
135 cc_util_normalize_casefold_and_unaccent (cc_language_row_get_country_local (CC_LANGUAGE_ROW (row)));
136 return match_all (self->filter_words, country_local);
137 }
138
139 static gint
140 sort_languages (GtkListBoxRow *a,
141 GtkListBoxRow *b,
142 gpointer data)
143 {
144 int d;
145
146 if (!CC_IS_LANGUAGE_ROW (a))
147 return 1;
148 if (!CC_IS_LANGUAGE_ROW (b))
149 return -1;
150
151 d = g_strcmp0 (cc_language_row_get_language (CC_LANGUAGE_ROW (a)), cc_language_row_get_language (CC_LANGUAGE_ROW (b)));
152 if (d != 0)
153 return d;
154
155 return g_strcmp0 (cc_language_row_get_country (CC_LANGUAGE_ROW (a)), cc_language_row_get_country (CC_LANGUAGE_ROW (b)));
156 }
157
158 static void
159 language_filter_entry_search_changed_cb (CcLanguageChooser *self)
160 {
161 g_autofree gchar *filter_contents = NULL;
162
163 g_clear_pointer (&self->filter_words, g_strfreev);
164
165 filter_contents =
166 cc_util_normalize_casefold_and_unaccent (gtk_editable_get_text (GTK_EDITABLE (self->language_filter_entry)));
167 if (!filter_contents) {
168 gtk_list_box_invalidate_filter (self->language_listbox);
169 return;
170 }
171 self->filter_words = g_strsplit_set (g_strstrip (filter_contents), " ", 0);
172 gtk_list_box_invalidate_filter (self->language_listbox);
173 }
174
175 static void
176 show_more (CcLanguageChooser *self, gboolean visible)
177 {
178 gint width, height;
179
180 gtk_window_get_default_size (GTK_WINDOW (self), &width, &height);
181 gtk_widget_set_size_request (GTK_WIDGET (self), width, height);
182
183 gtk_search_bar_set_search_mode (self->search_bar, visible);
184 gtk_widget_grab_focus (visible ? GTK_WIDGET (self->language_filter_entry) : GTK_WIDGET (self->language_listbox));
185
186 self->showing_extra = visible;
187
188 gtk_list_box_invalidate_filter (self->language_listbox);
189 }
190
191 static void
192 set_locale_id (CcLanguageChooser *self,
193 const gchar *locale_id)
194 {
195 GtkWidget *child;
196
197 gtk_widget_set_sensitive (GTK_WIDGET (self->select_button), FALSE);
198
199 for (child = gtk_widget_get_first_child (GTK_WIDGET (self->language_listbox));
200 child;
201 child = gtk_widget_get_next_sibling (child)) {
202 CcLanguageRow *row;
203
204 if (!CC_IS_LANGUAGE_ROW (child))
205 continue;
206
207 row = CC_LANGUAGE_ROW (child);
208 if (g_strcmp0 (locale_id, cc_language_row_get_locale_id (row)) == 0) {
209 cc_language_row_set_checked (row, TRUE);
210 gtk_widget_set_sensitive (GTK_WIDGET (self->select_button), TRUE);
211
212 /* make sure the selected language is shown */
213 if (!self->showing_extra && cc_language_row_get_is_extra (row)) {
214 cc_language_row_set_is_extra (row, FALSE);
215 gtk_list_box_invalidate_filter (self->language_listbox);
216 }
217 } else {
218 cc_language_row_set_checked (row, FALSE);
219 }
220 }
221
222 g_free (self->language);
223 self->language = g_strdup (locale_id);
224 }
225
226 static void
227 language_listbox_row_activated_cb (CcLanguageChooser *self, GtkListBoxRow *row)
228 {
229 const gchar *new_locale_id;
230
231 if (row == self->more_row) {
232 show_more (self, TRUE);
233 return;
234 }
235
236 if (!CC_IS_LANGUAGE_ROW (row))
237 return;
238
239 new_locale_id = cc_language_row_get_locale_id (CC_LANGUAGE_ROW (row));
240 if (g_strcmp0 (new_locale_id, self->language) == 0) {
241 gtk_dialog_response (GTK_DIALOG (self),
242 gtk_dialog_get_response_for_widget (GTK_DIALOG (self),
243 GTK_WIDGET (self->select_button)));
244 } else {
245 set_locale_id (self, new_locale_id);
246 }
247 }
248
249 static void
250 activate_default_cb (CcLanguageChooser *self)
251 {
252 GtkWidget *focus;
253
254 focus = gtk_window_get_focus (GTK_WINDOW (self));
255 if (!focus || !CC_IS_LANGUAGE_ROW (focus))
256 return;
257
258 if (g_strcmp0 (cc_language_row_get_locale_id (CC_LANGUAGE_ROW (focus)), self->language) == 0)
259 return;
260
261 g_signal_stop_emission_by_name (GTK_WINDOW (self), "activate-default");
262 gtk_widget_activate (focus);
263 }
264
265 void
266 cc_language_chooser_init (CcLanguageChooser *self)
267 {
268 g_resources_register (cc_common_get_resource ());
269
270 gtk_widget_init_template (GTK_WIDGET (self));
271
272 gtk_list_box_set_sort_func (self->language_listbox,
273 sort_languages, self, NULL);
274 gtk_list_box_set_filter_func (self->language_listbox,
275 language_visible, self, NULL);
276 add_all_languages (self);
277
278 gtk_list_box_invalidate_filter (self->language_listbox);
279 }
280
281 static void
282 cc_language_chooser_dispose (GObject *object)
283 {
284 CcLanguageChooser *self = CC_LANGUAGE_CHOOSER (object);
285
286 g_clear_pointer (&self->filter_words, g_strfreev);
287 g_clear_pointer (&self->language, g_free);
288
289 G_OBJECT_CLASS (cc_language_chooser_parent_class)->dispose (object);
290 }
291
292 void
293 cc_language_chooser_class_init (CcLanguageChooserClass *klass)
294 {
295 GObjectClass *object_class = G_OBJECT_CLASS (klass);
296 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
297
298 object_class->dispose = cc_language_chooser_dispose;
299
300 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/common/cc-language-chooser.ui");
301
302 gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, language_filter_entry);
303 gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, language_listbox);
304 gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, more_row);
305 gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, search_bar);
306 gtk_widget_class_bind_template_child (widget_class, CcLanguageChooser, select_button);
307
308 gtk_widget_class_bind_template_callback (widget_class, activate_default_cb);
309 gtk_widget_class_bind_template_callback (widget_class, language_filter_entry_search_changed_cb);
310 gtk_widget_class_bind_template_callback (widget_class, language_listbox_row_activated_cb);
311 }
312
313 CcLanguageChooser *
314 cc_language_chooser_new (void)
315 {
316 return CC_LANGUAGE_CHOOSER (g_object_new (CC_TYPE_LANGUAGE_CHOOSER,
317 "use-header-bar", 1,
318 NULL));
319 }
320
321 void
322 cc_language_chooser_clear_filter (CcLanguageChooser *self)
323 {
324 g_return_if_fail (CC_IS_LANGUAGE_CHOOSER (self));
325 gtk_editable_set_text (GTK_EDITABLE (self->language_filter_entry), "");
326 show_more (self, FALSE);
327 }
328
329 const gchar *
330 cc_language_chooser_get_language (CcLanguageChooser *self)
331 {
332 g_return_val_if_fail (CC_IS_LANGUAGE_CHOOSER (self), NULL);
333 return self->language;
334 }
335
336 void
337 cc_language_chooser_set_language (CcLanguageChooser *self,
338 const gchar *language)
339 {
340 g_return_if_fail (CC_IS_LANGUAGE_CHOOSER (self));
341 set_locale_id (self, language);
342 }
343