Line |
Branch |
Exec |
Source |
1 |
|
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- |
2 |
|
|
* |
3 |
|
|
* Copyright 2009-2010 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 |
|
|
#include "config.h" |
22 |
|
|
|
23 |
|
|
#include <stdlib.h> |
24 |
|
|
#include <locale.h> |
25 |
|
|
|
26 |
|
|
#include <glib.h> |
27 |
|
|
#include <glib/gi18n.h> |
28 |
|
|
#include <gtk/gtk.h> |
29 |
|
|
|
30 |
|
|
#include <fontconfig/fontconfig.h> |
31 |
|
|
|
32 |
|
|
#define GNOME_DESKTOP_USE_UNSTABLE_API |
33 |
|
|
#include <libgnome-desktop/gnome-languages.h> |
34 |
|
|
|
35 |
|
|
#include "cc-common-language.h" |
36 |
|
|
#include "shell/cc-object-storage.h" |
37 |
|
|
|
38 |
|
|
static char *get_lang_for_user_object_path (const char *path); |
39 |
|
|
|
40 |
|
|
static gboolean |
41 |
|
✗ |
iter_for_language (GtkTreeModel *model, |
42 |
|
|
const gchar *lang, |
43 |
|
|
GtkTreeIter *iter, |
44 |
|
|
gboolean region) |
45 |
|
|
{ |
46 |
|
✗ |
g_autofree gchar *name = NULL; |
47 |
|
|
|
48 |
|
✗ |
g_assert (gtk_tree_model_get_iter_first (model, iter)); |
49 |
|
|
do { |
50 |
|
✗ |
g_autofree gchar *l = NULL; |
51 |
|
✗ |
gtk_tree_model_get (model, iter, LOCALE_COL, &l, -1); |
52 |
|
✗ |
if (g_strcmp0 (l, lang) == 0) |
53 |
|
✗ |
return TRUE; |
54 |
|
✗ |
} while (gtk_tree_model_iter_next (model, iter)); |
55 |
|
|
|
56 |
|
✗ |
name = gnome_normalize_locale (lang); |
57 |
|
✗ |
if (name != NULL) { |
58 |
|
✗ |
g_autofree gchar *language = NULL; |
59 |
|
|
|
60 |
|
✗ |
if (region) { |
61 |
|
✗ |
language = gnome_get_country_from_locale (name, NULL); |
62 |
|
|
} |
63 |
|
|
else { |
64 |
|
✗ |
language = gnome_get_language_from_locale (name, NULL); |
65 |
|
|
} |
66 |
|
|
|
67 |
|
✗ |
gtk_list_store_insert_with_values (GTK_LIST_STORE (model), |
68 |
|
|
iter, |
69 |
|
|
-1, |
70 |
|
|
LOCALE_COL, name, |
71 |
|
|
DISPLAY_LOCALE_COL, language, |
72 |
|
|
-1); |
73 |
|
✗ |
return TRUE; |
74 |
|
|
} |
75 |
|
|
|
76 |
|
✗ |
return FALSE; |
77 |
|
|
} |
78 |
|
|
|
79 |
|
|
gboolean |
80 |
|
✗ |
cc_common_language_get_iter_for_language (GtkTreeModel *model, |
81 |
|
|
const gchar *lang, |
82 |
|
|
GtkTreeIter *iter) |
83 |
|
|
{ |
84 |
|
✗ |
return iter_for_language (model, lang, iter, FALSE); |
85 |
|
|
} |
86 |
|
|
|
87 |
|
|
gboolean |
88 |
|
✗ |
cc_common_language_has_font (const gchar *locale) |
89 |
|
|
{ |
90 |
|
|
const FcCharSet *charset; |
91 |
|
|
FcPattern *pattern; |
92 |
|
|
FcObjectSet *object_set; |
93 |
|
|
FcFontSet *font_set; |
94 |
|
✗ |
g_autofree gchar *language_code = NULL; |
95 |
|
|
gboolean is_displayable; |
96 |
|
|
|
97 |
|
✗ |
is_displayable = FALSE; |
98 |
|
✗ |
pattern = NULL; |
99 |
|
✗ |
object_set = NULL; |
100 |
|
✗ |
font_set = NULL; |
101 |
|
|
|
102 |
|
✗ |
if (!gnome_parse_locale (locale, &language_code, NULL, NULL, NULL)) |
103 |
|
✗ |
return FALSE; |
104 |
|
|
|
105 |
|
✗ |
charset = FcLangGetCharSet ((FcChar8 *) language_code); |
106 |
|
✗ |
if (!charset) { |
107 |
|
|
/* fontconfig does not know about this language */ |
108 |
|
✗ |
is_displayable = TRUE; |
109 |
|
|
} |
110 |
|
|
else { |
111 |
|
|
/* see if any fonts support rendering it */ |
112 |
|
✗ |
pattern = FcPatternBuild (NULL, FC_LANG, FcTypeString, language_code, NULL); |
113 |
|
|
|
114 |
|
✗ |
if (pattern == NULL) |
115 |
|
✗ |
goto done; |
116 |
|
|
|
117 |
|
✗ |
object_set = FcObjectSetCreate (); |
118 |
|
|
|
119 |
|
✗ |
if (object_set == NULL) |
120 |
|
✗ |
goto done; |
121 |
|
|
|
122 |
|
✗ |
font_set = FcFontList (NULL, pattern, object_set); |
123 |
|
|
|
124 |
|
✗ |
if (font_set == NULL) |
125 |
|
✗ |
goto done; |
126 |
|
|
|
127 |
|
✗ |
is_displayable = (font_set->nfont > 0); |
128 |
|
|
} |
129 |
|
|
|
130 |
|
✗ |
done: |
131 |
|
✗ |
if (font_set != NULL) |
132 |
|
✗ |
FcFontSetDestroy (font_set); |
133 |
|
|
|
134 |
|
✗ |
if (object_set != NULL) |
135 |
|
✗ |
FcObjectSetDestroy (object_set); |
136 |
|
|
|
137 |
|
✗ |
if (pattern != NULL) |
138 |
|
✗ |
FcPatternDestroy (pattern); |
139 |
|
|
|
140 |
|
✗ |
return is_displayable; |
141 |
|
|
} |
142 |
|
|
|
143 |
|
|
gchar * |
144 |
|
✗ |
cc_common_language_get_current_language (void) |
145 |
|
|
{ |
146 |
|
|
gchar *language; |
147 |
|
✗ |
g_autofree gchar *path = NULL; |
148 |
|
|
const gchar *locale; |
149 |
|
|
|
150 |
|
✗ |
path = g_strdup_printf ("/org/freedesktop/Accounts/User%d", getuid ()); |
151 |
|
✗ |
language = get_lang_for_user_object_path (path); |
152 |
|
✗ |
if (language != NULL && *language != '\0') |
153 |
|
✗ |
return language; |
154 |
|
|
|
155 |
|
✗ |
locale = (const gchar *) setlocale (LC_MESSAGES, NULL); |
156 |
|
✗ |
if (locale) |
157 |
|
✗ |
language = gnome_normalize_locale (locale); |
158 |
|
|
else |
159 |
|
✗ |
language = NULL; |
160 |
|
|
|
161 |
|
✗ |
return language; |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
static char * |
165 |
|
✗ |
get_lang_for_user_object_path (const char *path) |
166 |
|
|
{ |
167 |
|
✗ |
g_autoptr(GError) error = NULL; |
168 |
|
✗ |
g_autoptr(GDBusProxy) user = NULL; |
169 |
|
✗ |
g_autoptr(GVariant) props = NULL; |
170 |
|
|
char *lang; |
171 |
|
|
|
172 |
|
✗ |
user = cc_object_storage_create_dbus_proxy_sync (G_BUS_TYPE_SYSTEM, |
173 |
|
|
G_DBUS_PROXY_FLAGS_NONE, |
174 |
|
|
"org.freedesktop.Accounts", |
175 |
|
|
path, |
176 |
|
|
"org.freedesktop.Accounts.User", |
177 |
|
|
NULL, |
178 |
|
|
&error); |
179 |
|
✗ |
if (user == NULL) { |
180 |
|
✗ |
g_warning ("Failed to get proxy for user '%s': %s", |
181 |
|
|
path, error->message); |
182 |
|
✗ |
return NULL; |
183 |
|
|
} |
184 |
|
|
|
185 |
|
✗ |
props = g_dbus_proxy_get_cached_property (user, "Language"); |
186 |
|
✗ |
if (props == NULL) |
187 |
|
✗ |
return NULL; |
188 |
|
✗ |
lang = g_variant_dup_string (props, NULL); |
189 |
|
|
|
190 |
|
✗ |
return lang; |
191 |
|
|
} |
192 |
|
|
|
193 |
|
|
/* |
194 |
|
|
* Note that @lang needs to be formatted like the locale strings |
195 |
|
|
* returned by gnome_get_all_locales(). |
196 |
|
|
*/ |
197 |
|
|
static void |
198 |
|
✗ |
insert_language (GHashTable *ht, |
199 |
|
|
const char *lang) |
200 |
|
|
{ |
201 |
|
✗ |
g_autofree gchar *label_own_lang = NULL; |
202 |
|
✗ |
g_autofree gchar *label_current_lang = NULL; |
203 |
|
✗ |
g_autofree gchar *label_untranslated = NULL; |
204 |
|
|
|
205 |
|
✗ |
label_own_lang = gnome_get_language_from_locale (lang, lang); |
206 |
|
✗ |
label_current_lang = gnome_get_language_from_locale (lang, NULL); |
207 |
|
✗ |
label_untranslated = gnome_get_language_from_locale (lang, "C"); |
208 |
|
|
|
209 |
|
|
/* We don't have a translation for the label in |
210 |
|
|
* its own language? */ |
211 |
|
✗ |
if (g_strcmp0 (label_own_lang, label_untranslated) == 0) { |
212 |
|
✗ |
if (g_strcmp0 (label_current_lang, label_untranslated) == 0) |
213 |
|
✗ |
g_hash_table_insert (ht, g_strdup (lang), g_strdup (label_untranslated)); |
214 |
|
|
else |
215 |
|
✗ |
g_hash_table_insert (ht, g_strdup (lang), g_strdup (label_current_lang)); |
216 |
|
|
} else { |
217 |
|
✗ |
g_hash_table_insert (ht, g_strdup (lang), g_strdup (label_own_lang)); |
218 |
|
|
} |
219 |
|
✗ |
} |
220 |
|
|
|
221 |
|
|
GHashTable * |
222 |
|
✗ |
cc_common_language_get_initial_languages (void) |
223 |
|
|
{ |
224 |
|
|
GHashTable *ht; |
225 |
|
|
|
226 |
|
✗ |
ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); |
227 |
|
|
|
228 |
|
✗ |
insert_language (ht, "en_US.UTF-8"); |
229 |
|
✗ |
insert_language (ht, "en_GB.UTF-8"); |
230 |
|
✗ |
insert_language (ht, "de_DE.UTF-8"); |
231 |
|
✗ |
insert_language (ht, "fr_FR.UTF-8"); |
232 |
|
✗ |
insert_language (ht, "es_ES.UTF-8"); |
233 |
|
✗ |
insert_language (ht, "zh_CN.UTF-8"); |
234 |
|
✗ |
insert_language (ht, "ja_JP.UTF-8"); |
235 |
|
✗ |
insert_language (ht, "ru_RU.UTF-8"); |
236 |
|
✗ |
insert_language (ht, "ar_EG.UTF-8"); |
237 |
|
|
|
238 |
|
✗ |
return ht; |
239 |
|
|
} |
240 |
|
|
|
241 |
|
|
static void |
242 |
|
✗ |
foreach_user_lang_cb (gpointer key, |
243 |
|
|
gpointer value, |
244 |
|
|
gpointer user_data) |
245 |
|
|
{ |
246 |
|
✗ |
GtkListStore *store = (GtkListStore *) user_data; |
247 |
|
✗ |
const char *locale = (const char *) key; |
248 |
|
✗ |
const char *display_locale = (const char *) value; |
249 |
|
|
GtkTreeIter iter; |
250 |
|
|
|
251 |
|
✗ |
gtk_list_store_append (store, &iter); |
252 |
|
✗ |
gtk_list_store_set (store, &iter, |
253 |
|
|
LOCALE_COL, locale, |
254 |
|
|
DISPLAY_LOCALE_COL, display_locale, |
255 |
|
|
-1); |
256 |
|
✗ |
} |
257 |
|
|
|
258 |
|
|
void |
259 |
|
✗ |
cc_common_language_add_user_languages (GtkTreeModel *model) |
260 |
|
|
{ |
261 |
|
✗ |
g_autofree gchar *name = NULL; |
262 |
|
|
GtkTreeIter iter; |
263 |
|
✗ |
GtkListStore *store = GTK_LIST_STORE (model); |
264 |
|
|
GHashTable *user_langs; |
265 |
|
|
const char *display; |
266 |
|
|
|
267 |
|
✗ |
gtk_list_store_clear (store); |
268 |
|
|
|
269 |
|
✗ |
user_langs = cc_common_language_get_initial_languages (); |
270 |
|
|
|
271 |
|
|
/* Add the current locale first */ |
272 |
|
✗ |
name = cc_common_language_get_current_language (); |
273 |
|
✗ |
display = g_hash_table_lookup (user_langs, name); |
274 |
|
✗ |
if (!display) { |
275 |
|
✗ |
g_autofree gchar *language = NULL; |
276 |
|
✗ |
g_autofree gchar *country = NULL; |
277 |
|
✗ |
g_autofree gchar *codeset = NULL; |
278 |
|
|
|
279 |
|
✗ |
gnome_parse_locale (name, &language, &country, &codeset, NULL); |
280 |
|
|
|
281 |
|
✗ |
if (!codeset || !g_str_equal (codeset, "UTF-8")) |
282 |
|
✗ |
g_warning ("Current user locale codeset isn't UTF-8"); |
283 |
|
|
|
284 |
|
✗ |
g_free (name); |
285 |
|
✗ |
name = g_strdup_printf ("%s_%s.UTF-8", language, country); |
286 |
|
|
|
287 |
|
✗ |
insert_language (user_langs, name); |
288 |
|
✗ |
display = g_hash_table_lookup (user_langs, name); |
289 |
|
|
} |
290 |
|
|
|
291 |
|
✗ |
gtk_list_store_append (store, &iter); |
292 |
|
✗ |
gtk_list_store_set (store, &iter, LOCALE_COL, name, DISPLAY_LOCALE_COL, display, -1); |
293 |
|
✗ |
g_hash_table_remove (user_langs, name); |
294 |
|
|
|
295 |
|
|
/* The rest of the languages */ |
296 |
|
✗ |
g_hash_table_foreach (user_langs, (GHFunc) foreach_user_lang_cb, store); |
297 |
|
|
|
298 |
|
|
/* And now the "Other…" selection */ |
299 |
|
✗ |
gtk_list_store_append (store, &iter); |
300 |
|
✗ |
gtk_list_store_set (store, &iter, LOCALE_COL, NULL, DISPLAY_LOCALE_COL, _("Other…"), -1); |
301 |
|
|
|
302 |
|
✗ |
g_hash_table_destroy (user_langs); |
303 |
|
✗ |
} |
304 |
|
|
|
305 |
|
|
|