Added gettext support to ukeyboard
[ukeyboard.git] / cpanel / lang.c
blob091d369b28f1b3af5dcbfccc26b85aa77d977e92
1 /*
2 * Copyright (c) 2008 Jiri Benc <jbenc@upir.cz>
3 * Copyright (c) 2009 Roman Moravcik <roman.moravcik@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <glib.h>
24 #include <gtk/gtk.h>
25 #include <hildon/hildon-caption.h>
26 #include <hildon/hildon-banner.h>
27 #include <hildon/hildon.h>
28 #include <libosso.h>
29 #include <gconf/gconf.h>
30 #include <gconf/gconf-client.h>
31 #include "prefs.h"
32 #include "lang.h"
33 #include "langset.h"
35 #define GETTEXT_PACKAGE "ukeyboard"
36 #include <glib/gi18n-lib.h>
38 char *ignore_autocapitalisation[] = {"ar_AR", "fa_IR", "he_IL", "ka_GE", "km_KH", "pa_IN", "th_TH", NULL };
40 struct langlink {
41 gchar *src;
42 gchar *dest;
45 struct data {
46 GConfClient *client;
47 GtkWidget *win;
48 GList *langs;
49 GList *dicts;
50 GList *langlinks;
51 HildonCheckButton *word_compl;
52 HildonCheckButton *auto_cap;
53 HildonCheckButton *sp_after;
54 HildonPickerButton *sec_dictsel;
55 HildonTouchSelector *langsel[2];
56 HildonTouchSelector *dictsel[2];
57 HildonCheckButton *dual;
58 int num_langs;
61 static gchar *get_lang(GConfClient *client, int n)
63 char *tmp = g_strdup_printf("/apps/osso/inputmethod/hildon-im-languages/language-%d", n);
64 gchar *res;
66 res = gconf_client_get_string(client, tmp, NULL);
67 g_free(tmp);
68 return res;
71 static void set_lang(GConfClient *client, int n, gchar *val)
73 char *tmp = g_strdup_printf("/apps/osso/inputmethod/hildon-im-languages/language-%d", n);
75 gconf_client_set_string(client, tmp, val, NULL);
76 g_free(tmp);
79 static gchar *read_vkb_str(FILE *f)
81 gchar *res;
82 uint8_t len;
84 if (!fread(&len, 1, 1, f) || !len)
85 return NULL;
86 res = g_malloc(len + 1);
87 if (fread(res, 1, len, f) != len) {
88 g_free(res);
89 return NULL;
91 res[len] = '\0';
92 return res;
95 static struct lang *read_vkb(gchar *path)
97 FILE *f;
98 struct lang *res = NULL;
99 uint8_t ver;
100 gchar *desc, *code;
102 f = fopen(path, "r");
103 if (!f)
104 return NULL;
105 /* version */
106 if (!fread(&ver, 1, 1, f) || ver != 1)
107 goto no;
108 /* number of sections */
109 if (!fread(&ver, 1, 1, f))
110 goto no;
111 /* descriptive name */
112 desc = read_vkb_str(f);
113 if (!desc)
114 goto no;
115 /* locale name */
116 code = read_vkb_str(f);
117 if (!code) {
118 g_free(desc);
119 goto no;
122 res = g_malloc(sizeof(struct lang));
123 res->desc = desc;
124 res->code = code;
126 fclose(f);
127 return res;
130 static GList *add_lang_link(gchar *path, gchar *dest, GList *list)
131 /* 'dest' has to be allocated; it's eaten by this function! */
133 struct langlink *langlink;
134 gchar *link, *tmp;
136 link = g_file_read_link(path, NULL);
137 if (link) {
138 langlink = g_malloc(sizeof(struct langlink));
139 langlink->dest = dest;
140 tmp = strrchr(link, '/');
141 langlink->src = tmp ? tmp + 1 : link;
142 tmp = strrchr(link, '.');
143 if (tmp)
144 *tmp = '\0';
145 langlink->src = g_strdup(langlink->src);
146 g_free(link);
147 list = g_list_append(list, langlink);
149 return list;
152 static GList *get_langs(gchar *path, GList **langlinks, GList *list)
154 GDir *d;
155 const gchar *fname;
156 gchar *fullpath, *shortpath, *tmp;
157 struct lang *lang;
159 d = g_dir_open(path, 0, NULL);
160 if (!d)
161 return list;
162 while ((fname = g_dir_read_name(d))) {
163 if (!g_str_has_suffix(fname, ".vkb"))
164 continue;
165 fullpath = g_strjoin("/", path, fname, NULL);
166 shortpath = g_strdup(fname);
167 tmp = strrchr(shortpath, '.');
168 if (tmp)
169 *tmp = '\0';
170 if (langlinks && g_file_test(fullpath, G_FILE_TEST_IS_SYMLINK)) {
171 *langlinks = add_lang_link(fullpath, shortpath, *langlinks);
172 g_free(fullpath);
173 continue;
175 lang = read_vkb(fullpath);
176 if (lang) {
177 lang->fname = shortpath;
178 lang->ext = !langlinks;
179 list = g_list_append(list, lang);
180 } else
181 g_free(shortpath);
182 g_free(fullpath);
184 g_dir_close(d);
185 return list;
188 static struct lang *get_def_lang(gchar *code, GList *langs, GList *langlinks)
190 GList *item;
191 struct lang *lang;
192 struct langlink *langlink;
194 if (!code || !*code)
195 return NULL;
196 for (item = langs; item; item = g_list_next(item)) {
197 lang = item->data;
198 if (!strcmp(lang->fname, code))
199 return lang;
201 if (langlinks)
202 for (item = langlinks; item; item = g_list_next(item)) {
203 langlink = item->data;
204 if (!strcmp(langlink->dest, code))
205 return get_def_lang(langlink->src, langs, NULL);
207 return NULL;
210 static gboolean check_ukbd_layout(gchar *code, GList *langlinks)
212 GList *item;
213 struct langlink *langlink;
215 if (!code || !*code)
216 return FALSE;
217 if (langlinks)
218 for (item = langlinks; item; item = g_list_next(item)) {
219 langlink = item->data;
220 if (!strcmp(langlink->dest, code)) {
221 if (!strncmp(langlink->src, "ukbd", 4)) {
222 return TRUE;
226 return FALSE;
229 static void free_langs(GList *list)
231 GList *item;
232 struct lang *lang;
234 for (item = list; item; item = g_list_next(item)) {
235 lang = item->data;
236 g_free(lang->fname);
237 g_free(lang->desc);
238 g_free(lang->code);
239 g_free(lang);
241 g_list_free(list);
244 static void free_langlinks(GList *list)
246 GList *item;
247 struct langlink *langlink;
249 for (item = list; item; item = g_list_next(item)) {
250 langlink = item->data;
251 g_free(langlink->src);
252 g_free(langlink->dest);
253 g_free(langlink);
255 g_list_free(list);
258 static void fill_langsel(HildonTouchSelector *combo, GList *langs, struct lang *deflang, gboolean empty)
260 GList *item;
261 struct lang *lang;
262 unsigned i;
264 for (item = langs, i = 0; item; item = g_list_next(item), i++) {
265 lang = item->data;
266 hildon_touch_selector_append_text(combo, lang->desc);
267 if (lang == deflang)
268 hildon_touch_selector_set_active(combo, 0, i);
270 if (empty) {
271 hildon_touch_selector_append_text(combo, _TI("tein_fi_not_in_use"));
272 if (!deflang)
273 hildon_touch_selector_set_active(combo, 0, i);
277 static void sensitivity_langsel(struct data *d)
279 gboolean sens;
281 sens = (hildon_touch_selector_get_active(d->langsel[1], 0) < d->num_langs);
282 gtk_widget_set_sensitive(GTK_WIDGET(d->dual), sens);
284 if (sens)
285 gtk_widget_show(GTK_WIDGET(d->sec_dictsel));
286 else
287 gtk_widget_hide(GTK_WIDGET(d->sec_dictsel));
290 static void verify_langsel(HildonTouchSelector *combo, gint column, struct data *d)
292 struct lang *lang[2];
293 int res;
294 unsigned i;
296 (void)combo;
297 for (i = 0; i < 2; i++) {
298 res = hildon_touch_selector_get_active(d->langsel[i], column);
299 lang[i] = (res >= 0) ? g_list_nth_data(d->langs, res) : NULL;
301 if (lang[0] && lang[1] && !strcmp(lang[0]->code, lang[1]->code)) {
302 hildon_banner_show_information(d->win, "gtk-dialog-error",
303 "Impossible combination of languages");
304 hildon_touch_selector_set_active(d->langsel[1], column, d->num_langs);
306 sensitivity_langsel(d);
309 static gint langs_compare_func(gconstpointer a, gconstpointer b)
311 struct lang *lang_a = (struct lang *) a;
312 struct lang *lang_b = (struct lang *) b;
314 return g_utf8_collate (lang_a->desc, lang_b->desc);
317 static GtkWidget *start(GConfClient *client, GtkWidget *win, void **data)
319 struct data *d;
320 gchar *tmp;
321 GtkWidget *vbox, *table;
322 struct lang *lang;
323 unsigned i;
325 d = g_new0(struct data, 1);
326 d->client = client;
327 d->win = win;
329 d->langs = get_langs("/usr/share/keyboards", &d->langlinks, NULL);
330 d->langs = get_langs("/usr/share/ukeyboard", NULL, d->langs);
331 d->langs = g_list_sort(d->langs, langs_compare_func);
332 d->num_langs = g_list_length(d->langs);
334 d->dicts = get_dicts(d->langs);
336 vbox = gtk_vbox_new(FALSE, 0);
338 d->word_compl = HILDON_CHECK_BUTTON(hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT));
339 gtk_button_set_label (GTK_BUTTON (d->word_compl), _TI("tein_fi_settings_word_completion"));
340 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(d->word_compl), TRUE, TRUE, 0);
342 d->auto_cap = HILDON_CHECK_BUTTON(hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT));
343 gtk_button_set_label (GTK_BUTTON (d->auto_cap), _TI("tein_fi_settings_auto_capitalization"));
344 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(d->auto_cap), TRUE, TRUE, 0);
346 d->sp_after = HILDON_CHECK_BUTTON(hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT));
347 gtk_button_set_label (GTK_BUTTON (d->sp_after), _TI("tein_fi_settings_space_after_word"));
348 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(d->sp_after), TRUE, TRUE, 0);
350 table = gtk_table_new(2, 2, TRUE);
351 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
353 for (i = 0; i < 2; i++) {
354 GtkWidget *button;
355 gchar *code;
357 d->langsel[i] = HILDON_TOUCH_SELECTOR(hildon_touch_selector_new_text());
358 code = get_lang(client, i);
359 fill_langsel(d->langsel[i], d->langs, get_def_lang(code, d->langs, d->langlinks), i == 1);
360 g_signal_connect(G_OBJECT(d->langsel[i]), "changed", G_CALLBACK(verify_langsel), d);
362 button = hildon_picker_button_new(HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
363 hildon_picker_button_set_selector(HILDON_PICKER_BUTTON (button), d->langsel[i]);
364 hildon_button_set_title(HILDON_BUTTON(button), i == 0 ? _TI("tein_fi_primary_language") : _TI("tein_fi_secondary_language"));
366 /* if nothing was selected, try set selector button value text */
367 if (hildon_touch_selector_get_active(d->langsel[i], 0) < 0) {
368 if (check_ukbd_layout(code, d->langlinks))
369 hildon_button_set_value(HILDON_BUTTON(button), "UKeyboard Creator Layout");
370 else
371 hildon_button_set_value(HILDON_BUTTON(button), _TI("tein_fi_not_in_use"));
374 hildon_button_set_alignment (HILDON_BUTTON (button), 0.0, 0.5, 1.0, 0.0);
375 hildon_button_set_title_alignment(HILDON_BUTTON(button), 0.0, 0.5);
376 hildon_button_set_value_alignment (HILDON_BUTTON (button), 0.0, 0.5);
377 gtk_table_attach_defaults(GTK_TABLE(table), button, 0, 1, i, i + 1);
379 d->dictsel[i] = HILDON_TOUCH_SELECTOR(hildon_touch_selector_new_text());
380 tmp = get_l_str(client, code, "dictionary");
381 lang = get_def_lang(code, d->langs, d->langlinks);
383 /* If tmp is NULL (i.e. the gconf key is unset), try to use the same
384 * dictionary as the keyboard. But don't do this if the keyboard is
385 * from our package. */
386 fill_dict(d->dictsel[i], d->dicts, (tmp || (lang && lang->ext)) ? tmp : code);
387 if (tmp)
388 g_free(tmp);
390 button = hildon_picker_button_new(HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
391 hildon_button_set_title(HILDON_BUTTON(button), _TI("tein_fi_settings_dictionary"));
392 hildon_picker_button_set_selector(HILDON_PICKER_BUTTON (button), d->dictsel[i]);
393 hildon_button_set_alignment (HILDON_BUTTON (button), 0.0, 0.5, 1.0, 0.0);
394 hildon_button_set_title_alignment(HILDON_BUTTON(button), 0.0, 0.5);
395 hildon_button_set_value_alignment (HILDON_BUTTON (button), 0.0, 0.5);
396 gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, i, i + 1);
398 if (i == 1) {
399 /* secondary language */
400 d->sec_dictsel = HILDON_PICKER_BUTTON(button);
402 /* check if auto-capitalization for the second language isn't enabled */
403 if (get_l_bool(client, code, "auto-capitalisation")) {
404 if (!hildon_check_button_get_active(d->auto_cap))
405 hildon_check_button_set_active(d->auto_cap, TRUE);
407 } else {
408 /* primary language */
409 hildon_check_button_set_active(d->word_compl, get_l_bool(client, code, "word-completion"));
410 hildon_check_button_set_active(d->auto_cap, get_l_bool(client, code, "auto-capitalisation"));
411 hildon_check_button_set_active(d->sp_after, get_l_bool(client, code, "insert-space-after-word"));
414 if (code)
415 g_free(code);
418 d->dual = HILDON_CHECK_BUTTON(hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT));
419 hildon_check_button_set_active(d->dual, get_bool(client, "dual-dictionary"));
420 gtk_button_set_label (GTK_BUTTON (d->dual), _TI("tein_fi_dual_dictionary_use"));
421 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(d->dual), TRUE, TRUE, 0);
423 gtk_widget_show_all(vbox);
425 sensitivity_langsel(d);
427 *data = d;
429 return vbox;
432 static void action(GConfClient *client, void *data)
434 struct data *d = data;
435 struct lang *lang;
436 gchar *tmp;
437 int res;
438 unsigned i, j;
440 for (i = 0; i < 2; i++) {
441 struct lang *dict;
443 res = hildon_touch_selector_get_active(d->langsel[i], 0);
444 if (res < 0)
445 continue;
446 lang = g_list_nth_data(d->langs, res);
447 if (lang) {
448 if (lang->ext) {
449 if (inside_scratchbox)
450 tmp = g_strdup_printf("fakeroot /usr/libexec/ukeyboard-set %s %s", lang->fname, lang->code);
451 else
452 tmp = g_strdup_printf("sudo /usr/libexec/ukeyboard-set %s %s", lang->fname, lang->code);
454 if (system(tmp))
455 hildon_banner_show_information(d->win, "gtk-dialog-warning",
456 "Setting failed");
457 g_free(tmp);
459 /* When the newly set layout has the same lang code as the
460 * previous one (but the underlaying file changed), setting
461 * the same value won't trigger layout reload. Workaround
462 * that by setting "en_GB" lang code first. */
463 set_lang(client, i, "en_GB");
464 set_lang(client, i, lang->code);
466 set_l_bool(client, lang->code, "auto-capitalisation", hildon_check_button_get_active(d->auto_cap));
467 set_l_bool(client, lang->code, "word-completion", hildon_check_button_get_active(d->word_compl));
468 set_l_bool(client, lang->code, "insert-space-after-word", hildon_check_button_get_active(d->sp_after));
470 /* forced disabling of autocapitalization for selected language codes,
471 that doesn't support autocapitalization */
472 for (j = 0; ignore_autocapitalisation[j] != NULL; j++) {
473 if (!strcmp(lang->code, ignore_autocapitalisation[j])) {
474 set_l_bool(client, lang->code, "auto-capitalisation", FALSE);
478 res = hildon_touch_selector_get_active(d->dictsel[i], 0);
479 if (res >= 0)
480 dict = g_list_nth_data(d->dicts, res);
482 if (dict)
483 set_l_str(client, lang->code, "dictionary", dict->code);
484 else
485 set_l_str(client, lang->code, "dictionary", "");
486 } else
487 set_lang(client, i, "");
489 set_bool(client, "dual-dictionary", hildon_check_button_get_active(d->dual));
492 static void stop(GConfClient *client, void *data)
494 struct data *d = data;
496 (void)client;
498 free_langs(d->langs);
499 free_langs(d->dicts);
500 free_langlinks(d->langlinks);
501 g_free(d);
504 void prefs_lang_init(struct prefs *prefs)
506 prefs->start = start;
507 prefs->action = action;
508 prefs->stop = stop;
509 prefs->name = "Languages";