346c935e407a2fc62ee3f764c84b7d252e31d321
[slovak-l10n] / ukeyboard / cpanel / lang.c
1 /*
2  *  Copyright (c) 2008 Jiri Benc <jbenc@upir.cz>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License version 2 as
6  *  published by the Free Software Foundation.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16  */
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <glib.h>
23 #include <gtk/gtk.h>
24 #include <hildon/hildon-caption.h>
25 #include <hildon/hildon-banner.h>
26 #include <libosso.h>
27 #include <gconf/gconf.h>
28 #include <gconf/gconf-client.h>
29
30 #ifdef HAVE_MAEMO5
31 #define GETTEXT_PACKAGE "osso-applet-textinput"
32
33 #include <hildon/hildon.h>
34 #include <glib/gi18n-lib.h>
35 #endif
36
37 #include "prefs.h"
38 #include "lang.h"
39 #include "langset.h"
40
41 struct langlink {
42         gchar *src;
43         gchar *dest;
44 };
45
46 struct data {
47         GConfClient *client;
48         GtkWidget *win;
49         GList *langs;
50         GList *langlinks;
51 #ifdef HAVE_MAEMO5
52         HildonCheckButton *word_compl;
53         HildonCheckButton *auto_cap;
54         HildonCheckButton *sp_after;
55         HildonPickerButton *sec_dictsel;
56         HildonTouchSelector *langsel[2];
57         HildonTouchSelector *dictsel[2];
58         HildonCheckButton *dual;
59 #else
60         GtkComboBox *langsel[2];
61         GtkButton *settings[2];
62         GtkToggleButton *dual;
63 #endif
64         int num_langs;
65 };
66
67 static gchar *get_lang(GConfClient *client, int n)
68 {
69         char *tmp = g_strdup_printf("/apps/osso/inputmethod/hildon-im-languages/language-%d", n);
70         gchar *res;
71
72         res = gconf_client_get_string(client, tmp, NULL);
73         g_free(tmp);
74         return res;
75 }
76
77 static void set_lang(GConfClient *client, int n, gchar *val)
78 {
79         char *tmp = g_strdup_printf("/apps/osso/inputmethod/hildon-im-languages/language-%d", n);
80
81         gconf_client_set_string(client, tmp, val, NULL);
82         g_free(tmp);
83 }
84
85 static gchar *read_vkb_str(FILE *f)
86 {
87         gchar *res;
88         uint8_t len;
89
90         if (!fread(&len, 1, 1, f) || !len)
91                 return NULL;
92         res = g_malloc(len + 1);
93         if (fread(res, 1, len, f) != len) {
94                 g_free(res);
95                 return NULL;
96         }
97         res[len] = '\0';
98         return res;
99 }
100
101 static struct lang *read_vkb(gchar *path)
102 {
103         FILE *f;
104         struct lang *res = NULL;
105         uint8_t ver;
106         gchar *desc, *code;
107
108         f = fopen(path, "r");
109         if (!f)
110                 return NULL;
111         /* version */
112         if (!fread(&ver, 1, 1, f) || ver != 1)
113                 goto no;
114         /* number of sections */
115         if (!fread(&ver, 1, 1, f))
116                 goto no;
117         /* descriptive name */
118         desc = read_vkb_str(f);
119         if (!desc)
120                 goto no;
121         /* locale name */
122         code = read_vkb_str(f);
123         if (!code) {
124                 g_free(desc);
125                 goto no;
126         }
127
128         res = g_malloc(sizeof(struct lang));
129         res->desc = desc;
130         res->code = code;
131 no:
132         fclose(f);
133         return res;
134 }
135
136 static GList *add_lang_link(gchar *path, gchar *dest, GList *list)
137 /* 'dest' has to be allocated; it's eaten by this function! */
138 {
139         struct langlink *langlink;
140         gchar *link, *tmp;
141
142         link = g_file_read_link(path, NULL);
143         if (link) {
144                 langlink = g_malloc(sizeof(struct langlink));
145                 langlink->dest = dest;
146                 tmp = strrchr(link, '/');
147                 langlink->src = tmp ? tmp + 1 : link;
148                 tmp = strrchr(link, '.');
149                 if (tmp)
150                         *tmp = '\0';
151                 langlink->src = g_strdup(langlink->src);
152                 g_free(link);
153                 list = g_list_append(list, langlink);
154         }
155         return list;
156 }
157
158 static GList *get_langs(gchar *path, GList **langlinks, GList *list)
159 {
160         GDir *d;
161         const gchar *fname;
162         gchar *fullpath, *shortpath, *tmp;
163         struct lang *lang;
164
165         d = g_dir_open(path, 0, NULL);
166         if (!d)
167                 return list;
168         while ((fname = g_dir_read_name(d))) {
169                 if (!g_str_has_suffix(fname, ".vkb"))
170                         continue;
171                 fullpath = g_strjoin("/", path, fname, NULL);
172                 shortpath = g_strdup(fname);
173                 tmp = strrchr(shortpath, '.');
174                 if (tmp)
175                         *tmp = '\0';
176                 if (langlinks && g_file_test(fullpath, G_FILE_TEST_IS_SYMLINK)) {
177                         *langlinks = add_lang_link(fullpath, shortpath, *langlinks);
178                         g_free(fullpath);
179                         continue;
180                 }
181                 lang = read_vkb(fullpath);
182                 if (lang) {
183                         lang->fname = shortpath;
184                         lang->ext = !langlinks;
185                         list = g_list_append(list, lang);
186                 } else
187                         g_free(shortpath);
188                 g_free(fullpath);
189         }
190         g_dir_close(d);
191         return list;
192 }
193
194 static struct lang *get_def_lang(gchar *code, GList *langs, GList *langlinks)
195 {
196         GList *item;
197         struct lang *lang;
198         struct langlink *langlink;
199
200         if (!code || !*code)
201                 return NULL;
202         for (item = langs; item; item = g_list_next(item)) {
203                 lang = item->data;
204                 if (!strcmp(lang->fname, code))
205                         return lang;
206         }
207         if (langlinks)
208                 for (item = langlinks; item; item = g_list_next(item)) {
209                         langlink = item->data;
210                         if (!strcmp(langlink->dest, code))
211                                 return get_def_lang(langlink->src, langs, NULL);
212                 }
213         return NULL;
214 }
215
216 static void free_langs(GList *list)
217 {
218         GList *item;
219         struct lang *lang;
220
221         for (item = list; item; item = g_list_next(item)) {
222                 lang = item->data;
223                 g_free(lang->fname);
224                 g_free(lang->desc);
225                 g_free(lang->code);
226                 g_free(lang);
227         }
228         g_list_free(list);
229 }
230
231 static void free_langlinks(GList *list)
232 {
233         GList *item;
234         struct langlink *langlink;
235
236         for (item = list; item; item = g_list_next(item)) {
237                 langlink = item->data;
238                 g_free(langlink->src);
239                 g_free(langlink->dest);
240                 g_free(langlink);
241         }
242         g_list_free(list);
243 }
244
245 #ifdef HAVE_MAEMO5
246 static void fill_langsel(HildonTouchSelector *combo, GList *langs, struct lang *deflang, gboolean empty)
247 {
248         GList *item;
249         struct lang *lang;
250         unsigned i;
251
252         for (item = langs, i = 0; item; item = g_list_next(item), i++) {
253                 lang = item->data;
254                 hildon_touch_selector_append_text(combo, lang->desc);
255                 if (lang == deflang)
256                         hildon_touch_selector_set_active(combo, 0, i);
257         }
258         if (empty) {
259                 hildon_touch_selector_append_text(combo, _("tein_fi_not_in_use"));
260                 if (!deflang)
261                         hildon_touch_selector_set_active(combo, 0, i);
262         }
263 }
264 #else
265 static void fill_langsel(GtkComboBox *combo, GList *langs, struct lang *deflang, gboolean empty)
266 {
267         GList *item;
268         struct lang *lang;
269         unsigned i;
270
271         for (item = langs, i = 0; item; item = g_list_next(item), i++) {
272                 lang = item->data;
273                 gtk_combo_box_append_text(combo, lang->desc);
274                 if (lang == deflang)
275                         gtk_combo_box_set_active(combo, i);
276         }
277         if (empty) {
278                 gtk_combo_box_append_text(combo, "");
279                 if (!deflang)
280                         gtk_combo_box_set_active(combo, i);
281         }
282 }
283 #endif
284
285 static void sensitivity_langsel(struct data *d)
286 {
287         gboolean sens;
288
289 #ifdef HAVE_MAEMO5
290         sens = (hildon_touch_selector_get_active(d->langsel[1], 0) < d->num_langs);
291         gtk_widget_set_sensitive(GTK_WIDGET(d->dual), sens);
292
293         if (sens)
294                 gtk_widget_show(GTK_WIDGET(d->sec_dictsel));
295         else
296                 gtk_widget_hide(GTK_WIDGET(d->sec_dictsel));
297 #else
298         sens = (gtk_combo_box_get_active(d->langsel[1]) < d->num_langs);
299         gtk_widget_set_sensitive(GTK_WIDGET(d->settings[1]), sens);
300         gtk_widget_set_sensitive(GTK_WIDGET(d->dual), sens);
301 #endif
302 }
303
304
305 #ifdef HAVE_MAEMO5
306 static void verify_langsel(HildonTouchSelector *combo, gint column, struct data *d)
307 {
308         struct lang *lang[2];
309         int res;
310         unsigned i;
311
312         (void)combo;
313         for (i = 0; i < 2; i++) {
314                 res = hildon_touch_selector_get_active(d->langsel[i], column);
315                 lang[i] = (res >= 0) ? g_list_nth_data(d->langs, res) : NULL;
316         }
317         if (lang[0] && lang[1] && !strcmp(lang[0]->code, lang[1]->code)) {
318                 hildon_banner_show_information(d->win, "gtk-dialog-error",
319                         "Impossible combination of languages");
320                 hildon_touch_selector_set_active(d->langsel[1], column, d->num_langs);
321         }
322         sensitivity_langsel(d);
323 }
324 #else
325 static void verify_langsel(GtkComboBox *combo, struct data *d)
326 {
327         struct lang *lang[2];
328         int res;
329         unsigned i;
330
331         (void)combo;
332         for (i = 0; i < 2; i++) {
333                 res = gtk_combo_box_get_active(d->langsel[i]);
334                 lang[i] = (res >= 0) ? g_list_nth_data(d->langs, res) : NULL;
335         }
336         if (lang[0] && lang[1] && !strcmp(lang[0]->code, lang[1]->code)) {
337                 hildon_banner_show_information(d->win, "gtk-dialog-error",
338                         "Impossible combination of languages");
339                 gtk_combo_box_set_active(d->langsel[1], d->num_langs);
340         }
341         sensitivity_langsel(d);
342 }
343
344 static void do_settings(GtkButton *button, struct data *d)
345 {
346         int which;
347
348         which = (button == d->settings[0]) ? 0 : 1;
349         lang_settings(d->client, d->win, d->langs, gtk_combo_box_get_active(d->langsel[which]));
350 }
351 #endif
352
353 #ifdef HAVE_MAEMO5
354 static GtkWidget *start(GConfClient *client, GtkWidget *win, void **data)
355 {
356         struct data *d;
357         gchar *tmp;
358         GtkWidget *vbox, *table;
359         struct lang *lang;
360         unsigned i;
361
362         d = g_new0(struct data, 1);
363         d->client = client;
364         d->win = win;
365
366         d->langs = get_langs("/usr/share/keyboards", &d->langlinks, NULL);
367         d->langs = get_langs("/usr/share/ukeyboard", NULL, d->langs);
368         d->num_langs = g_list_length(d->langs);
369
370         vbox = gtk_vbox_new(FALSE, 0);
371
372         d->word_compl = HILDON_CHECK_BUTTON(hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT));
373         gtk_button_set_label (GTK_BUTTON (d->word_compl), _("tein_fi_settings_word_completion"));
374         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(d->word_compl), TRUE, TRUE, 0);
375
376         d->auto_cap = HILDON_CHECK_BUTTON(hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT));
377         gtk_button_set_label (GTK_BUTTON (d->auto_cap), _("tein_fi_settings_auto_capitalization"));
378         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(d->auto_cap), TRUE, TRUE, 0);
379
380         d->sp_after = HILDON_CHECK_BUTTON(hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT));
381         gtk_button_set_label (GTK_BUTTON (d->sp_after), _("tein_fi_settings_space_after_word"));
382         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(d->sp_after), TRUE, TRUE, 0);
383
384         table = gtk_table_new(2, 2, TRUE);
385         gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
386
387         for (i = 0; i < 2; i++) {
388                 GtkWidget *button;
389                 gchar *code;
390
391                 d->langsel[i] = HILDON_TOUCH_SELECTOR(hildon_touch_selector_new_text());
392                 code = get_lang(client, i);
393                 fill_langsel(d->langsel[i], d->langs, get_def_lang(code, d->langs, d->langlinks), i == 1);
394                 g_signal_connect(G_OBJECT(d->langsel[i]), "changed", G_CALLBACK(verify_langsel), d);
395
396                 button = hildon_picker_button_new(HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
397                 hildon_button_set_title(HILDON_BUTTON(button), i == 0 ? _("tein_fi_primary_language") : _("tein_fi_secondary_language"));
398                 hildon_picker_button_set_selector(HILDON_PICKER_BUTTON (button), d->langsel[i]);
399                 hildon_button_set_alignment (HILDON_BUTTON (button), 0.0, 0.5, 1.0, 0.0);
400                 hildon_button_set_title_alignment(HILDON_BUTTON(button), 0.0, 0.5);
401                 hildon_button_set_value_alignment (HILDON_BUTTON (button), 0.0, 0.5);
402                 gtk_table_attach_defaults(GTK_TABLE(table), button, 0, 1, i, i + 1);
403
404                 d->dictsel[i] = HILDON_TOUCH_SELECTOR(hildon_touch_selector_new_text());
405                 tmp = get_l_str(client, code, "dictionary");
406                 lang = get_def_lang(code, d->langs, d->langlinks);
407
408                 /* If tmp is NULL (i.e. the gconf key is unset), try to use the same 
409                  * dictionary as the keyboard. But don't do this if the keyboard is 
410                  * from our package. */
411                 fill_dict(d->dictsel[i], d->langs, (tmp || (lang && lang->ext)) ? tmp : code);
412                 if (tmp)
413                         g_free(tmp);
414
415                 button = hildon_picker_button_new(HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
416                 hildon_button_set_title(HILDON_BUTTON(button), _("tein_fi_settings_dictionary"));
417                 hildon_picker_button_set_selector(HILDON_PICKER_BUTTON (button), d->dictsel[i]);
418                 hildon_button_set_alignment (HILDON_BUTTON (button), 0.0, 0.5, 1.0, 0.0);
419                 hildon_button_set_title_alignment(HILDON_BUTTON(button), 0.0, 0.5);
420                 hildon_button_set_value_alignment (HILDON_BUTTON (button), 0.0, 0.5);
421                 gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, i, i + 1);
422
423                 if (i == 1)
424                         d->sec_dictsel = HILDON_PICKER_BUTTON(button);
425                 else {
426                         hildon_check_button_set_active(d->word_compl, get_l_bool(client, code, "word-completion"));
427                         hildon_check_button_set_active(d->auto_cap, get_l_bool(client, code, "auto-capitalisation"));
428                         hildon_check_button_set_active(d->sp_after, get_l_bool(client, code, "insert-space-after-word"));
429                 }
430
431                 if (code)
432                         g_free(code);
433         }
434
435         d->dual = HILDON_CHECK_BUTTON(hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT));
436         hildon_check_button_set_active(d->dual, get_bool(client, "dual-dictionary"));
437         gtk_button_set_label (GTK_BUTTON (d->dual), _("tein_fi_dual_dictionary_use"));
438         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(d->dual), TRUE, TRUE, 0);
439
440         gtk_widget_show_all(vbox);
441
442         sensitivity_langsel(d);
443
444         *data = d;
445
446         return vbox;
447 }
448 #else
449 static GtkWidget *start(GConfClient *client, GtkWidget *win, void **data)
450 {
451         struct data *d;
452         gchar *tmp;
453         GtkBox *vbox;
454         GtkSizeGroup *group;
455         GtkWidget *align;
456         unsigned i;
457
458         d = g_new0(struct data, 1);
459         d->client = client;
460         d->win = win;
461
462         d->langs = get_langs("/usr/share/keyboards", &d->langlinks, NULL);
463         d->langs = get_langs("/usr/share/ukeyboard", NULL, d->langs);
464         d->num_langs = g_list_length(d->langs);
465
466         vbox = GTK_BOX(gtk_vbox_new(FALSE, 0));
467         gtk_box_set_spacing(vbox, 5);
468         group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
469
470         for (i = 0; i < 2; i++) {
471                 d->langsel[i] = GTK_COMBO_BOX(gtk_combo_box_new_text());
472
473                 tmp = get_lang(client, i);
474                 fill_langsel(d->langsel[i], d->langs, get_def_lang(tmp, d->langs, d->langlinks), i == 1);
475                 if (tmp)
476                         g_free(tmp);
477
478                 g_signal_connect(G_OBJECT(d->langsel[i]), "changed", G_CALLBACK(verify_langsel), d);
479                 gtk_box_pack_start_defaults(vbox, hildon_caption_new(group, i == 0 ? "1st language" : "2nd language",
480                         GTK_WIDGET(d->langsel[i]), NULL, HILDON_CAPTION_MANDATORY));
481
482                 d->settings[i] = GTK_BUTTON(gtk_button_new_with_label("Settings"));
483                 g_signal_connect(G_OBJECT(d->settings[i]), "clicked", G_CALLBACK(do_settings), d);
484                 gtk_box_pack_start_defaults(vbox, hildon_caption_new(group, NULL,
485                         GTK_WIDGET(d->settings[i]), NULL, HILDON_CAPTION_OPTIONAL));
486         }
487
488         d->dual = GTK_TOGGLE_BUTTON(gtk_check_button_new());
489         gtk_toggle_button_set_active(d->dual, get_bool(client, "dual-dictionary"));
490         gtk_box_pack_start_defaults(vbox, hildon_caption_new(group, "Use dual dictionaries",
491                 GTK_WIDGET(d->dual), NULL, HILDON_CAPTION_MANDATORY));
492
493         sensitivity_langsel(d);
494
495         g_object_unref(G_OBJECT(group));
496         *data = d;
497
498         align = gtk_alignment_new(0, 0, 1, 0);
499         gtk_container_add(GTK_CONTAINER(align), GTK_WIDGET(vbox));
500         return align;
501 }
502 #endif
503
504 static void action(GConfClient *client, void *data)
505 {
506         struct data *d = data;
507         struct lang *lang;
508         gchar *tmp;
509         int res;
510         unsigned i;
511
512         for (i = 0; i < 2; i++) {
513 #ifdef HAVE_MAEMO5
514                 struct lang *dict;
515
516                 res = hildon_touch_selector_get_active(d->langsel[i], 0);
517 #else
518                 res = gtk_combo_box_get_active(d->langsel[i]);
519 #endif
520                 if (res < 0)
521                         continue;
522                 lang = g_list_nth_data(d->langs, res);
523                 if (lang) {
524                         if (lang->ext) {
525                                 tmp = g_strdup_printf("sudo /usr/libexec/ukeyboard-set %s %s", lang->fname, lang->code);
526                                 if (system(tmp))
527                                         hildon_banner_show_information(d->win, "gtk-dialog-warning",
528                                                 "Setting failed");
529                                 g_free(tmp);
530                         }
531                         /* When the newly set layout has the same lang code as the
532                          * previous one (but the underlaying file changed), setting
533                          * the same value won't trigger layout reload. Workaround
534                          * that by setting "en_GB" lang code first. */
535                         set_lang(client, i, "en_GB");
536                         set_lang(client, i, lang->code);
537 #ifdef HAVE_MAEMO5
538                         set_l_bool(client, lang->code, "auto-capitalisation", hildon_check_button_get_active(d->auto_cap));
539                         set_l_bool(client, lang->code, "word-completion", hildon_check_button_get_active(d->word_compl));
540                         set_l_bool(client, lang->code, "insert-space-after-word", hildon_check_button_get_active(d->sp_after));
541
542                         res = hildon_touch_selector_get_active(d->dictsel[i], 0);
543                         if (res >= 0)
544                                 dict = g_list_nth_data(d->langs, res);
545
546                         if (dict && !dict->ext)
547                                 set_l_str(client, lang->code, "dictionary", dict->code);
548                         else
549                                 set_l_str(client, lang->code, "dictionary", "");
550 #endif
551                 } else
552                         set_lang(client, i, "");
553         }
554 #ifdef HAVE_MAEMO5
555         set_bool(client, "dual-dictionary", hildon_check_button_get_active(d->dual));
556 #else
557         set_bool(client, "dual-dictionary", gtk_toggle_button_get_active(d->dual));
558 #endif
559 }
560
561 static void stop(GConfClient *client, void *data)
562 {
563         struct data *d = data;
564
565         (void)client;
566
567         free_langs(d->langs);
568         free_langlinks(d->langlinks);
569         g_free(d);
570 }
571
572 void prefs_lang_init(struct prefs *prefs)
573 {
574         prefs->start = start;
575         prefs->action = action;
576         prefs->stop = stop;
577         prefs->name = "Languages";
578 }