]> git.0d.be Git - empathy.git/blob - src/empathy-preferences.c
Move all data about an adium theme into EmpathyAdiumData struct and share it with...
[empathy.git] / src / empathy-preferences.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2003-2007 Imendio AB
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (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 GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301  USA
19  *
20  * Authors: Mikael Hallendal <micke@imendio.com>
21  *          Richard Hult <richard@imendio.com>
22  *          Martyn Russell <martyn@imendio.com>
23  */
24
25 #include <config.h>
26
27 #include <string.h>
28
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31
32 #include <libempathy/empathy-utils.h>
33
34 #include <libempathy-gtk/empathy-conf.h>
35 #include <libempathy-gtk/empathy-ui-utils.h>
36 #include <libempathy-gtk/empathy-theme-manager.h>
37 #include <libempathy-gtk/empathy-spell.h>
38 #include <libempathy-gtk/empathy-contact-list-store.h>
39 #include <libempathy-gtk/empathy-gtk-enum-types.h>
40
41 #ifdef HAVE_WEBKIT
42 #include <libempathy-gtk/empathy-theme-adium.h>
43 #endif
44
45 #include "empathy-preferences.h"
46
47 typedef struct {
48         GtkWidget *dialog;
49
50         GtkWidget *notebook;
51
52         GtkWidget *checkbutton_show_avatars;
53         GtkWidget *checkbutton_compact_contact_list;
54         GtkWidget *checkbutton_show_smileys;
55         GtkWidget *checkbutton_show_contacts_in_rooms;
56         GtkWidget *combobox_chat_theme;
57         GtkWidget *hbox_adium_theme;
58         GtkWidget *filechooserbutton_adium_theme;
59         GtkWidget *label_invalid_adium_theme;
60         GtkWidget *checkbutton_separate_chat_windows;
61         GtkWidget *checkbutton_autoconnect;
62         GtkWidget *radiobutton_contact_list_sort_by_name;
63         GtkWidget *radiobutton_contact_list_sort_by_state;
64
65         GtkWidget *checkbutton_sounds_enabled;
66         GtkWidget *checkbutton_sounds_disabled_away;
67         GtkWidget *treeview_sounds;
68
69         GtkWidget *checkbutton_notifications_enabled;
70         GtkWidget *checkbutton_notifications_disabled_away;
71         GtkWidget *checkbutton_notifications_focus;
72
73         GtkWidget *treeview_spell_checker;
74
75         GtkWidget *checkbutton_location_publish;
76         GtkWidget *checkbutton_location_reduce_accuracy;
77         GtkWidget *checkbutton_location_resource_network;
78         GtkWidget *checkbutton_location_resource_cell;
79         GtkWidget *checkbutton_location_resource_gps;
80
81         GList     *notify_ids;
82 } EmpathyPreferences;
83
84 static void     preferences_setup_widgets                (EmpathyPreferences      *preferences);
85 static void     preferences_languages_setup              (EmpathyPreferences      *preferences);
86 static void     preferences_languages_add                (EmpathyPreferences      *preferences);
87 static void     preferences_languages_save               (EmpathyPreferences      *preferences);
88 static gboolean preferences_languages_save_foreach       (GtkTreeModel           *model,
89                                                           GtkTreePath            *path,
90                                                           GtkTreeIter            *iter,
91                                                           gchar                 **languages);
92 static void     preferences_languages_load               (EmpathyPreferences      *preferences);
93 static gboolean preferences_languages_load_foreach       (GtkTreeModel           *model,
94                                                           GtkTreePath            *path,
95                                                           GtkTreeIter            *iter,
96                                                           gchar                 **languages);
97 static void     preferences_languages_cell_toggled_cb    (GtkCellRendererToggle  *cell,
98                                                           gchar                  *path_string,
99                                                           EmpathyPreferences      *preferences);
100 static void     preferences_widget_sync_bool             (const gchar            *key,
101                                                           GtkWidget              *widget);
102 static void     preferences_widget_sync_string           (const gchar            *key,
103                                                           GtkWidget              *widget);
104 static void     preferences_notify_string_cb             (EmpathyConf             *conf,
105                                                           const gchar            *key,
106                                                           gpointer                user_data);
107 static void     preferences_notify_bool_cb               (EmpathyConf             *conf,
108                                                           const gchar            *key,
109                                                           gpointer                user_data);
110 static void     preferences_notify_sensitivity_cb        (EmpathyConf             *conf,
111                                                           const gchar            *key,
112                                                           gpointer                user_data);
113 static void     preferences_hookup_toggle_button         (EmpathyPreferences      *preferences,
114                                                           const gchar            *key,
115                                                           GtkWidget              *widget);
116 static void     preferences_hookup_radio_button          (EmpathyPreferences      *preferences,
117                                                           const gchar            *key,
118                                                           GtkWidget              *widget);
119 static void     preferences_hookup_sensitivity           (EmpathyPreferences      *preferences,
120                                                           const gchar            *key,
121                                                           GtkWidget              *widget);
122 static void     preferences_toggle_button_toggled_cb     (GtkWidget              *button,
123                                                           gpointer                user_data);
124 static void     preferences_radio_button_toggled_cb      (GtkWidget              *button,
125                                                           gpointer                user_data);
126 static void     preferences_destroy_cb                   (GtkWidget              *widget,
127                                                           EmpathyPreferences      *preferences);
128 static void     preferences_response_cb                  (GtkWidget              *widget,
129                                                           gint                    response,
130                                                           EmpathyPreferences      *preferences);
131
132 enum {
133         COL_LANG_ENABLED,
134         COL_LANG_CODE,
135         COL_LANG_NAME,
136         COL_LANG_COUNT
137 };
138
139 enum {
140         COL_COMBO_VISIBLE_NAME,
141         COL_COMBO_NAME,
142         COL_COMBO_COUNT
143 };
144
145 enum {
146         COL_SOUND_ENABLED,
147         COL_SOUND_NAME,
148         COL_SOUND_KEY,
149         COL_SOUND_COUNT
150 };
151
152 typedef struct {
153         const char *name;
154         const char *key;
155 } SoundEventEntry;
156
157 /* TODO: add phone related sounds also? */
158 static SoundEventEntry sound_entries [] = {
159         { N_("Message received"), EMPATHY_PREFS_SOUNDS_INCOMING_MESSAGE },
160         { N_("Message sent"), EMPATHY_PREFS_SOUNDS_OUTGOING_MESSAGE },
161         { N_("New conversation"), EMPATHY_PREFS_SOUNDS_NEW_CONVERSATION },
162         { N_("Contact goes online"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGIN },
163         { N_("Contact goes offline"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGOUT },
164         { N_("Account connected"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGIN },
165         { N_("Account disconnected"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGOUT }
166 };
167
168 static void
169 preferences_add_id (EmpathyPreferences *preferences, guint id)
170 {
171         preferences->notify_ids = g_list_prepend (preferences->notify_ids,
172                                                   GUINT_TO_POINTER (id));
173 }
174
175 static void
176 preferences_compact_contact_list_changed_cb (EmpathyConf *conf,
177                                              const gchar *key,
178                                              gpointer     user_data)
179 {
180         EmpathyPreferences *preferences = user_data;
181         gboolean            value;
182
183         if (empathy_conf_get_bool (empathy_conf_get (), key, &value)) {
184                 gtk_widget_set_sensitive (preferences->checkbutton_show_avatars,
185                                           !value);
186         }
187 }
188
189 static void
190 preferences_setup_widgets (EmpathyPreferences *preferences)
191 {
192         guint id;
193
194         preferences_hookup_toggle_button (preferences,
195                                           EMPATHY_PREFS_NOTIFICATIONS_ENABLED,
196                                           preferences->checkbutton_notifications_enabled);
197         preferences_hookup_toggle_button (preferences,
198                                           EMPATHY_PREFS_NOTIFICATIONS_DISABLED_AWAY,
199                                           preferences->checkbutton_notifications_disabled_away);
200         preferences_hookup_toggle_button (preferences,
201                                           EMPATHY_PREFS_NOTIFICATIONS_FOCUS,
202                                           preferences->checkbutton_notifications_focus);
203
204         preferences_hookup_sensitivity (preferences,
205                                         EMPATHY_PREFS_NOTIFICATIONS_ENABLED,
206                                         preferences->checkbutton_notifications_disabled_away);
207         preferences_hookup_sensitivity (preferences,
208                                         EMPATHY_PREFS_NOTIFICATIONS_ENABLED,
209                                         preferences->checkbutton_notifications_focus);
210
211         preferences_hookup_toggle_button (preferences,
212                                           EMPATHY_PREFS_SOUNDS_ENABLED,
213                                           preferences->checkbutton_sounds_enabled);
214         preferences_hookup_toggle_button (preferences,
215                                           EMPATHY_PREFS_SOUNDS_DISABLED_AWAY,
216                                           preferences->checkbutton_sounds_disabled_away);
217
218         preferences_hookup_sensitivity (preferences,
219                                         EMPATHY_PREFS_SOUNDS_ENABLED,
220                                         preferences->checkbutton_sounds_disabled_away);
221         preferences_hookup_sensitivity (preferences,
222                                         EMPATHY_PREFS_SOUNDS_ENABLED,
223                                         preferences->treeview_sounds);
224
225         preferences_hookup_toggle_button (preferences,
226                                           EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS,
227                                           preferences->checkbutton_separate_chat_windows);
228
229         preferences_hookup_toggle_button (preferences,
230                                           EMPATHY_PREFS_UI_SHOW_AVATARS,
231                                           preferences->checkbutton_show_avatars);
232
233         preferences_hookup_toggle_button (preferences,
234                                           EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST,
235                                           preferences->checkbutton_compact_contact_list);
236
237         preferences_hookup_toggle_button (preferences,
238                                           EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
239                                           preferences->checkbutton_show_smileys);
240
241         preferences_hookup_toggle_button (preferences,
242                                           EMPATHY_PREFS_CHAT_SHOW_CONTACTS_IN_ROOMS,
243                                           preferences->checkbutton_show_contacts_in_rooms);
244
245         preferences_hookup_radio_button (preferences,
246                                          EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM,
247                                          preferences->radiobutton_contact_list_sort_by_name);
248
249         preferences_hookup_toggle_button (preferences,
250                                           EMPATHY_PREFS_AUTOCONNECT,
251                                           preferences->checkbutton_autoconnect);
252
253         preferences_hookup_toggle_button (preferences,
254                                           EMPATHY_PREFS_LOCATION_PUBLISH,
255                                           preferences->checkbutton_location_publish);
256
257         preferences_hookup_toggle_button (preferences,
258                                           EMPATHY_PREFS_LOCATION_RESOURCE_NETWORK,
259                                           preferences->checkbutton_location_resource_network);
260         preferences_hookup_sensitivity (preferences,
261                                         EMPATHY_PREFS_LOCATION_PUBLISH,
262                                         preferences->checkbutton_location_resource_network);
263
264         preferences_hookup_toggle_button (preferences,
265                                           EMPATHY_PREFS_LOCATION_RESOURCE_CELL,
266                                           preferences->checkbutton_location_resource_cell);
267         preferences_hookup_sensitivity (preferences,
268                                         EMPATHY_PREFS_LOCATION_PUBLISH,
269                                         preferences->checkbutton_location_resource_cell);
270
271         preferences_hookup_toggle_button (preferences,
272                                           EMPATHY_PREFS_LOCATION_RESOURCE_GPS,
273                                           preferences->checkbutton_location_resource_gps);
274         preferences_hookup_sensitivity (preferences,
275                                         EMPATHY_PREFS_LOCATION_PUBLISH,
276                                         preferences->checkbutton_location_resource_gps);
277
278         preferences_hookup_toggle_button (preferences,
279                                           EMPATHY_PREFS_LOCATION_REDUCE_ACCURACY,
280                                           preferences->checkbutton_location_reduce_accuracy);
281         preferences_hookup_sensitivity (preferences,
282                                         EMPATHY_PREFS_LOCATION_PUBLISH,
283                                         preferences->checkbutton_location_reduce_accuracy);
284
285         id = empathy_conf_notify_add (empathy_conf_get (),
286                                       EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST,
287                                       preferences_compact_contact_list_changed_cb,
288                                       preferences);
289         if (id) {
290                 preferences_add_id (preferences, id);
291         }
292         preferences_compact_contact_list_changed_cb (empathy_conf_get (),
293                                                      EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST,
294                                                      preferences);
295 }
296
297 static void
298 preferences_sound_cell_toggled_cb (GtkCellRendererToggle *toggle,
299                                    char *path_string,
300                                    EmpathyPreferences *preferences)
301 {
302         GtkTreePath *path;
303         gboolean toggled, instore;
304         GtkTreeIter iter;
305         GtkTreeView *view;
306         GtkTreeModel *model;
307         char *key;
308
309         view = GTK_TREE_VIEW (preferences->treeview_sounds);
310         model = gtk_tree_view_get_model (view);
311
312         path = gtk_tree_path_new_from_string (path_string);
313         toggled = gtk_cell_renderer_toggle_get_active (toggle);
314
315         gtk_tree_model_get_iter (model, &iter, path);
316         gtk_tree_model_get (model, &iter, COL_SOUND_KEY, &key,
317                             COL_SOUND_ENABLED, &instore, -1);
318
319         instore ^= 1;
320
321         gtk_list_store_set (GTK_LIST_STORE (model), &iter,
322                             COL_SOUND_ENABLED, instore, -1);
323
324         empathy_conf_set_bool (empathy_conf_get (), key, instore);
325
326         g_free (key);
327         gtk_tree_path_free (path);
328 }
329
330 static void
331 preferences_sound_load (EmpathyPreferences *preferences)
332 {
333         int i;
334         GtkTreeView *view;
335         GtkListStore *store;
336         GtkTreeIter iter;
337         gboolean set;
338         EmpathyConf *conf;
339
340         view = GTK_TREE_VIEW (preferences->treeview_sounds);
341         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
342         conf = empathy_conf_get ();
343
344         for (i = 0; i < G_N_ELEMENTS (sound_entries); i++) {
345                 empathy_conf_get_bool (conf, sound_entries[i].key, &set);
346
347                 gtk_list_store_insert_with_values (store, &iter, i,
348                                                    COL_SOUND_NAME, gettext (sound_entries[i].name),
349                                                    COL_SOUND_KEY, sound_entries[i].key,
350                                                    COL_SOUND_ENABLED, set, -1);
351         }
352 }
353
354 static void
355 preferences_sound_setup (EmpathyPreferences *preferences)
356 {
357         GtkTreeView *view;
358         GtkListStore *store;
359         GtkCellRenderer *renderer;
360         GtkTreeViewColumn *column;
361
362         view = GTK_TREE_VIEW (preferences->treeview_sounds);
363
364         store = gtk_list_store_new (COL_SOUND_COUNT,
365                                     G_TYPE_BOOLEAN, /* enabled */
366                                     G_TYPE_STRING,  /* name */
367                                     G_TYPE_STRING); /* key */
368
369         gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
370
371         renderer = gtk_cell_renderer_toggle_new ();
372         g_signal_connect (renderer, "toggled",
373                           G_CALLBACK (preferences_sound_cell_toggled_cb),
374                           preferences);
375
376         column = gtk_tree_view_column_new ();
377         gtk_tree_view_column_pack_start (column, renderer, FALSE);
378         gtk_tree_view_column_add_attribute (column, renderer,
379                                             "active", COL_SOUND_ENABLED);
380
381         renderer = gtk_cell_renderer_text_new ();
382         gtk_tree_view_column_pack_start (column, renderer, FALSE);
383         gtk_tree_view_column_add_attribute (column, renderer,
384                                             "text", COL_SOUND_NAME);
385
386         gtk_tree_view_append_column (view, column);
387
388         gtk_tree_view_column_set_resizable (column, FALSE);
389         gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
390
391         g_object_unref (store);
392 }
393
394 static void
395 preferences_languages_setup (EmpathyPreferences *preferences)
396 {
397         GtkTreeView       *view;
398         GtkListStore      *store;
399         GtkTreeSelection  *selection;
400         GtkTreeModel      *model;
401         GtkTreeViewColumn *column;
402         GtkCellRenderer   *renderer;
403         guint              col_offset;
404
405         view = GTK_TREE_VIEW (preferences->treeview_spell_checker);
406
407         store = gtk_list_store_new (COL_LANG_COUNT,
408                                     G_TYPE_BOOLEAN,  /* enabled */
409                                     G_TYPE_STRING,   /* code */
410                                     G_TYPE_STRING);  /* name */
411
412         gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
413
414         selection = gtk_tree_view_get_selection (view);
415         gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
416
417         model = GTK_TREE_MODEL (store);
418
419         renderer = gtk_cell_renderer_toggle_new ();
420         g_signal_connect (renderer, "toggled",
421                           G_CALLBACK (preferences_languages_cell_toggled_cb),
422                           preferences);
423
424         column = gtk_tree_view_column_new_with_attributes (NULL, renderer,
425                                                            "active", COL_LANG_ENABLED,
426                                                            NULL);
427
428         gtk_tree_view_append_column (view, column);
429
430         renderer = gtk_cell_renderer_text_new ();
431         col_offset = gtk_tree_view_insert_column_with_attributes (view,
432                                                                   -1, _("Language"),
433                                                                   renderer,
434                                                                   "text", COL_LANG_NAME,
435                                                                   NULL);
436
437         g_object_set_data (G_OBJECT (renderer),
438                            "column", GINT_TO_POINTER (COL_LANG_NAME));
439
440         column = gtk_tree_view_get_column (view, col_offset - 1);
441         gtk_tree_view_column_set_sort_column_id (column, COL_LANG_NAME);
442         gtk_tree_view_column_set_resizable (column, FALSE);
443         gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
444
445         g_object_unref (store);
446 }
447
448 static void
449 preferences_languages_add (EmpathyPreferences *preferences)
450 {
451         GtkTreeView  *view;
452         GtkListStore *store;
453         GList        *codes, *l;
454
455         view = GTK_TREE_VIEW (preferences->treeview_spell_checker);
456         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
457
458         codes = empathy_spell_get_language_codes ();
459
460         empathy_conf_set_bool (empathy_conf_get (),
461                                EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
462                                codes != NULL);
463         if (!codes) {
464                 gtk_widget_set_sensitive (preferences->treeview_spell_checker, FALSE);
465         }               
466
467         for (l = codes; l; l = l->next) {
468                 GtkTreeIter  iter;
469                 const gchar *code;
470                 const gchar *name;
471
472                 code = l->data;
473                 name = empathy_spell_get_language_name (code);
474                 if (!name) {
475                         continue;
476                 }
477
478                 gtk_list_store_append (store, &iter);
479                 gtk_list_store_set (store, &iter,
480                                     COL_LANG_CODE, code,
481                                     COL_LANG_NAME, name,
482                                     -1);
483         }
484
485         empathy_spell_free_language_codes (codes);
486 }
487
488 static void
489 preferences_languages_save (EmpathyPreferences *preferences)
490 {
491         GtkTreeView       *view;
492         GtkTreeModel      *model;
493
494         gchar             *languages = NULL;
495
496         view = GTK_TREE_VIEW (preferences->treeview_spell_checker);
497         model = gtk_tree_view_get_model (view);
498
499         gtk_tree_model_foreach (model,
500                                 (GtkTreeModelForeachFunc) preferences_languages_save_foreach,
501                                 &languages);
502
503         /* if user selects no languages, we don't want spell check */
504         empathy_conf_set_bool (empathy_conf_get (),
505                                EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
506                                languages != NULL);
507
508         empathy_conf_set_string (empathy_conf_get (),
509                                  EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES,
510                                  languages ? languages : "");
511
512         g_free (languages);
513 }
514
515 static gboolean
516 preferences_languages_save_foreach (GtkTreeModel  *model,
517                                     GtkTreePath   *path,
518                                     GtkTreeIter   *iter,
519                                     gchar        **languages)
520 {
521         gboolean  enabled;
522         gchar    *code;
523
524         if (!languages) {
525                 return TRUE;
526         }
527
528         gtk_tree_model_get (model, iter, COL_LANG_ENABLED, &enabled, -1);
529         if (!enabled) {
530                 return FALSE;
531         }
532
533         gtk_tree_model_get (model, iter, COL_LANG_CODE, &code, -1);
534         if (!code) {
535                 return FALSE;
536         }
537
538         if (!(*languages)) {
539                 *languages = g_strdup (code);
540         } else {
541                 gchar *str = *languages;
542                 *languages = g_strdup_printf ("%s,%s", str, code);
543                 g_free (str);
544         }
545
546         g_free (code);
547
548         return FALSE;
549 }
550
551 static void
552 preferences_languages_load (EmpathyPreferences *preferences)
553 {
554         GtkTreeView   *view;
555         GtkTreeModel  *model;
556         gchar         *value;
557         gchar        **vlanguages;
558
559         if (!empathy_conf_get_string (empathy_conf_get (),
560                                       EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES,
561                                       &value) || !value) {
562                 return;
563         }
564
565         vlanguages = g_strsplit (value, ",", -1);
566         g_free (value);
567
568         view = GTK_TREE_VIEW (preferences->treeview_spell_checker);
569         model = gtk_tree_view_get_model (view);
570
571         gtk_tree_model_foreach (model,
572                                 (GtkTreeModelForeachFunc) preferences_languages_load_foreach,
573                                 vlanguages);
574
575         g_strfreev (vlanguages);
576 }
577
578 static gboolean
579 preferences_languages_load_foreach (GtkTreeModel  *model,
580                                     GtkTreePath   *path,
581                                     GtkTreeIter   *iter,
582                                     gchar        **languages)
583 {
584         gchar    *code;
585         gchar    *lang;
586         gint      i;
587         gboolean  found = FALSE;
588
589         if (!languages) {
590                 return TRUE;
591         }
592
593         gtk_tree_model_get (model, iter, COL_LANG_CODE, &code, -1);
594         if (!code) {
595                 return FALSE;
596         }
597
598         for (i = 0, lang = languages[i]; lang; lang = languages[++i]) {
599                 if (strcmp (lang, code) == 0) {
600                         found = TRUE;
601                 }
602         }
603
604         g_free (code);
605         gtk_list_store_set (GTK_LIST_STORE (model), iter, COL_LANG_ENABLED, found, -1);
606         return FALSE;
607 }
608
609 static void
610 preferences_languages_cell_toggled_cb (GtkCellRendererToggle *cell,
611                                        gchar                 *path_string,
612                                        EmpathyPreferences     *preferences)
613 {
614         GtkTreeView  *view;
615         GtkTreeModel *model;
616         GtkListStore *store;
617         GtkTreePath  *path;
618         GtkTreeIter   iter;
619         gboolean      enabled;
620
621         view = GTK_TREE_VIEW (preferences->treeview_spell_checker);
622         model = gtk_tree_view_get_model (view);
623         store = GTK_LIST_STORE (model);
624
625         path = gtk_tree_path_new_from_string (path_string);
626
627         gtk_tree_model_get_iter (model, &iter, path);
628         gtk_tree_model_get (model, &iter, COL_LANG_ENABLED, &enabled, -1);
629
630         enabled ^= 1;
631
632         gtk_list_store_set (store, &iter, COL_LANG_ENABLED, enabled, -1);
633         gtk_tree_path_free (path);
634
635         preferences_languages_save (preferences);
636 }
637
638 static void
639 preferences_widget_sync_bool (const gchar *key, GtkWidget *widget)
640 {
641         gboolean value;
642
643         if (empathy_conf_get_bool (empathy_conf_get (), key, &value)) {
644                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
645         }
646 }
647
648 static void
649 preferences_widget_sync_string (const gchar *key, GtkWidget *widget)
650 {
651         gchar *value;
652
653         if (empathy_conf_get_string (empathy_conf_get (), key, &value) && value) {
654                 if (GTK_IS_ENTRY (widget)) {
655                         gtk_entry_set_text (GTK_ENTRY (widget), value);
656                 } else if (GTK_IS_RADIO_BUTTON (widget)) {
657                         if (strcmp (key, EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM) == 0) {
658                                 GType        type;
659                                 GEnumClass  *enum_class;
660                                 GEnumValue  *enum_value;
661                                 GSList      *list;
662                                 GtkWidget   *toggle_widget;
663                                 
664                                 /* Get index from new string */
665                                 type = empathy_contact_list_store_sort_get_type ();
666                                 enum_class = G_ENUM_CLASS (g_type_class_peek (type));
667                                 enum_value = g_enum_get_value_by_nick (enum_class, value);
668                                 
669                                 if (enum_value) {
670                                         list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (widget));
671                                         toggle_widget = g_slist_nth_data (list, enum_value->value);
672                                         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle_widget), TRUE);
673                                 }
674                         } else {
675                                 g_warning ("Unhandled key:'%s' just had string change", key);
676                         }
677                 }
678
679                 g_free (value);
680         }
681 }
682
683 static void
684 preferences_notify_string_cb (EmpathyConf  *conf,
685                               const gchar *key,
686                               gpointer     user_data)
687 {
688         preferences_widget_sync_string (key, user_data);
689 }
690
691
692 static void
693 preferences_notify_bool_cb (EmpathyConf  *conf,
694                             const gchar *key,
695                             gpointer     user_data)
696 {
697         preferences_widget_sync_bool (key, user_data);
698 }
699
700 static void
701 preferences_notify_sensitivity_cb (EmpathyConf  *conf,
702                                    const gchar *key,
703                                    gpointer     user_data)
704 {
705         gboolean value;
706
707         if (empathy_conf_get_bool (conf, key, &value)) {
708                 gtk_widget_set_sensitive (GTK_WIDGET (user_data), value);
709         }
710 }
711
712 #if 0
713 static void
714 preferences_widget_sync_int (const gchar *key, GtkWidget *widget)
715 {
716         gint value;
717
718         if (empathy_conf_get_int (empathy_conf_get (), key, &value)) {
719                 if (GTK_IS_SPIN_BUTTON (widget)) {
720                         gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value);
721                 }
722         }
723 }
724
725 static void
726 preferences_notify_int_cb (EmpathyConf  *conf,
727                            const gchar *key,
728                            gpointer     user_data)
729 {
730         preferences_widget_sync_int (key, user_data);   
731 }
732
733 static void
734 preferences_hookup_spin_button (EmpathyPreferences *preferences,
735                                 const gchar       *key,
736                                 GtkWidget         *widget)
737 {
738         guint id;
739
740         preferences_widget_sync_int (key, widget);
741
742         g_object_set_data_full (G_OBJECT (widget), "key",
743                                 g_strdup (key), g_free);
744
745         g_signal_connect (widget,
746                           "value_changed",
747                           G_CALLBACK (preferences_spin_button_value_changed_cb),
748                           NULL);
749
750         id = empathy_conf_notify_add (empathy_conf_get (),
751                                       key,
752                                       preferences_notify_int_cb,
753                                       widget);
754         if (id) {
755                 preferences_add_id (preferences, id);
756         }
757 }
758
759 static void
760 preferences_hookup_entry (EmpathyPreferences *preferences,
761                           const gchar       *key,
762                           GtkWidget         *widget)
763 {
764         guint id;
765
766         preferences_widget_sync_string (key, widget);
767
768         g_object_set_data_full (G_OBJECT (widget), "key",
769                                 g_strdup (key), g_free);
770
771         g_signal_connect (widget,
772                           "changed",
773                           G_CALLBACK (preferences_entry_value_changed_cb),
774                           NULL);
775
776         id = empathy_conf_notify_add (empathy_conf_get (),
777                                       key,
778                                       preferences_notify_string_cb,
779                                       widget);
780         if (id) {
781                 preferences_add_id (preferences, id);
782         }
783 }
784
785 static void
786 preferences_spin_button_value_changed_cb (GtkWidget *button,
787                                           gpointer   user_data)
788 {
789         const gchar *key;
790
791         key = g_object_get_data (G_OBJECT (button), "key");
792
793         empathy_conf_set_int (empathy_conf_get (),
794                               key,
795                               gtk_spin_button_get_value (GTK_SPIN_BUTTON (button)));
796 }
797
798 static void
799 preferences_entry_value_changed_cb (GtkWidget *entry,
800                                     gpointer   user_data)
801 {
802         const gchar *key;
803
804         key = g_object_get_data (G_OBJECT (entry), "key");
805
806         empathy_conf_set_string (empathy_conf_get (),
807                                  key,
808                                  gtk_entry_get_text (GTK_ENTRY (entry)));
809 }
810 #endif
811
812 static void
813 preferences_hookup_toggle_button (EmpathyPreferences *preferences,
814                                   const gchar       *key,
815                                   GtkWidget         *widget)
816 {
817         guint id;
818
819         preferences_widget_sync_bool (key, widget);
820
821         g_object_set_data_full (G_OBJECT (widget), "key",
822                                 g_strdup (key), g_free);
823
824         g_signal_connect (widget,
825                           "toggled",
826                           G_CALLBACK (preferences_toggle_button_toggled_cb),
827                           NULL);
828
829         id = empathy_conf_notify_add (empathy_conf_get (),
830                                      key,
831                                      preferences_notify_bool_cb,
832                                      widget);
833         if (id) {
834                 preferences_add_id (preferences, id);
835         }
836 }
837
838 static void
839 preferences_hookup_radio_button (EmpathyPreferences *preferences,
840                                  const gchar       *key,
841                                  GtkWidget         *widget)
842 {
843         GSList *group, *l;
844         guint   id;
845
846         preferences_widget_sync_string (key, widget);
847
848         group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (widget));
849         for (l = group; l; l = l->next) {
850                 g_signal_connect (l->data,
851                                   "toggled",
852                                   G_CALLBACK (preferences_radio_button_toggled_cb),
853                                   NULL);
854
855                 g_object_set_data_full (G_OBJECT (l->data), "key",
856                                         g_strdup (key), g_free);
857         }
858
859         id = empathy_conf_notify_add (empathy_conf_get (),
860                                      key,
861                                      preferences_notify_string_cb,
862                                      widget);
863         if (id) {
864                 preferences_add_id (preferences, id);
865         }
866 }
867
868 static void
869 preferences_hookup_sensitivity (EmpathyPreferences *preferences,
870                                 const gchar       *key,
871                                 GtkWidget         *widget)
872 {
873         gboolean value;
874         guint    id;
875
876         if (empathy_conf_get_bool (empathy_conf_get (), key, &value)) {
877                 gtk_widget_set_sensitive (widget, value);
878         }
879
880         id = empathy_conf_notify_add (empathy_conf_get (),
881                                       key,
882                                       preferences_notify_sensitivity_cb,
883                                       widget);
884         if (id) {
885                 preferences_add_id (preferences, id);
886         }
887 }
888
889 static void
890 preferences_toggle_button_toggled_cb (GtkWidget *button,
891                                       gpointer   user_data)
892 {
893         const gchar *key;
894
895         key = g_object_get_data (G_OBJECT (button), "key");
896
897         empathy_conf_set_bool (empathy_conf_get (),
898                                key,
899                                gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
900 }
901
902 static void
903 preferences_radio_button_toggled_cb (GtkWidget *button,
904                                      gpointer   user_data)
905 {
906         const gchar *key;
907         const gchar *value = NULL;
908
909         if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
910                 return;
911         }
912
913         key = g_object_get_data (G_OBJECT (button), "key");
914
915         if (key && strcmp (key, EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM) == 0) {
916                 GSList      *group;
917                 GType        type;
918                 GEnumClass  *enum_class;
919                 GEnumValue  *enum_value;
920                 
921                 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
922                 
923                 /* Get string from index */
924                 type = empathy_contact_list_store_sort_get_type ();
925                 enum_class = G_ENUM_CLASS (g_type_class_peek (type));
926                 enum_value = g_enum_get_value (enum_class, g_slist_index (group, button));
927                 
928                 if (!enum_value) {
929                         g_warning ("No GEnumValue for EmpathyContactListSort with GtkRadioButton index:%d",
930                                    g_slist_index (group, button));
931                         return;
932                 }
933
934                 value = enum_value->value_nick;
935         } else if (key && strcmp (key, EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM) == 0) {
936            return;
937         }
938
939         empathy_conf_set_string (empathy_conf_get (), key, value);
940 }
941
942
943 static void
944 preferences_theme_adium_update_visibility (EmpathyPreferences *preferences,
945                                            const gchar        *name)
946 {
947         if (name && strcmp (name, "adium") == 0) {
948                 gtk_widget_show (preferences->hbox_adium_theme);
949         } else {
950                 gtk_widget_hide (preferences->hbox_adium_theme);
951                 gtk_widget_hide (preferences->label_invalid_adium_theme);
952         }
953 }
954
955 static void
956 preferences_theme_adium_update_validity (EmpathyPreferences *preferences,
957                                          const gchar        *path)
958 {
959 #ifdef HAVE_WEBKIT
960         if (empathy_adium_path_is_valid (path)) {
961                 gtk_widget_hide (preferences->label_invalid_adium_theme);
962         } else {
963                 gtk_widget_show (preferences->label_invalid_adium_theme);
964         }
965 #endif
966 }
967
968 static void
969 preferences_theme_adium_path_notify_cb (EmpathyConf *conf,
970                                         const gchar *key,
971                                         gpointer     user_data)
972 {
973         EmpathyPreferences *preferences = user_data;
974         GtkFileChooser     *chooser;
975         gchar              *value;
976         const gchar        *path;
977
978         if (!empathy_conf_get_string (conf, key, &value)) {
979                 return;
980         }
981
982         if (EMP_STR_EMPTY (value)) {
983                 path = g_get_home_dir ();
984         } else {
985                 path = value;
986         }
987
988         chooser = GTK_FILE_CHOOSER (preferences->filechooserbutton_adium_theme);
989         gtk_file_chooser_set_current_folder (chooser, path);
990         preferences_theme_adium_update_validity (preferences, path);
991         g_free (value);
992 }
993
994 static void
995 preferences_theme_adium_file_set_cb (GtkFileChooser     *chooser,
996                                      EmpathyPreferences *preferences)
997 {
998         gchar *path;
999
1000         path = gtk_file_chooser_get_current_folder (chooser);
1001         empathy_conf_set_string (empathy_conf_get (),
1002                                  EMPATHY_PREFS_CHAT_ADIUM_PATH,
1003                                  path);
1004         preferences_theme_adium_update_validity (preferences, path);
1005
1006         g_free (path);
1007 }
1008
1009 static void
1010 preferences_theme_notify_cb (EmpathyConf *conf,
1011                              const gchar *key,
1012                              gpointer     user_data)
1013 {
1014         EmpathyPreferences *preferences = user_data;
1015         GtkComboBox        *combo;
1016         gchar              *value;
1017         GtkTreeModel       *model;
1018         GtkTreeIter         iter;
1019         gboolean            found = FALSE;
1020
1021         if (!empathy_conf_get_string (conf, key, &value)) {
1022                 return;
1023         }
1024
1025         preferences_theme_adium_update_visibility (preferences, value);
1026
1027         combo = GTK_COMBO_BOX (preferences->combobox_chat_theme);
1028         model = gtk_combo_box_get_model (combo);
1029         if (value && gtk_tree_model_get_iter_first (model, &iter)) {
1030                 gchar *name;
1031
1032                 do {
1033                         gtk_tree_model_get (model, &iter,
1034                                             COL_COMBO_NAME, &name,
1035                                             -1);
1036
1037                         if (strcmp (name, value) == 0) {
1038                                 found = TRUE;
1039                                 gtk_combo_box_set_active_iter (combo, &iter);
1040                                 break;
1041                         }
1042
1043                         g_free (name);
1044                 } while (gtk_tree_model_iter_next (model, &iter));
1045         }
1046
1047         /* Fallback to the first one. */
1048         if (!found) {
1049                 if (gtk_tree_model_get_iter_first (model, &iter)) {
1050                         gtk_combo_box_set_active_iter (combo, &iter);
1051                 }
1052         }
1053
1054         g_free (value);
1055 }
1056
1057 static void
1058 preferences_theme_changed_cb (GtkComboBox        *combo,
1059                               EmpathyPreferences *preferences)
1060 {
1061         GtkTreeModel *model;
1062         GtkTreeIter   iter;
1063         gchar        *name;
1064
1065         if (gtk_combo_box_get_active_iter (combo, &iter)) {
1066                 model = gtk_combo_box_get_model (combo);
1067
1068                 gtk_tree_model_get (model, &iter,
1069                                     COL_COMBO_NAME, &name,
1070                                     -1);
1071
1072                 preferences_theme_adium_update_visibility (preferences, name);
1073
1074                 empathy_conf_set_string (empathy_conf_get (),
1075                                          EMPATHY_PREFS_CHAT_THEME,
1076                                          name);
1077                 g_free (name);
1078         }
1079 }
1080
1081 static void
1082 preferences_themes_setup (EmpathyPreferences *preferences)
1083 {
1084         GtkComboBox   *combo;
1085         GtkCellLayout *cell_layout;
1086         GtkCellRenderer *renderer;
1087         GtkListStore  *store;
1088         const gchar  **themes;
1089         gint           i;
1090         guint          id;
1091
1092         combo = GTK_COMBO_BOX (preferences->combobox_chat_theme);
1093         cell_layout = GTK_CELL_LAYOUT (combo);
1094
1095         /* Create the model */
1096         store = gtk_list_store_new (COL_COMBO_COUNT,
1097                                     G_TYPE_STRING,  /* Display name */
1098                                     G_TYPE_STRING); /* Theme name */
1099
1100         /* Fill the model */
1101         themes = empathy_theme_manager_get_themes ();
1102         for (i = 0; themes[i]; i += 2) {
1103                 gtk_list_store_insert_with_values (store, NULL, -1,
1104                         COL_COMBO_VISIBLE_NAME, _(themes[i + 1]),
1105                         COL_COMBO_NAME, themes[i],
1106                         -1);
1107         }
1108
1109         /* Add cell renderer */
1110         renderer = gtk_cell_renderer_text_new ();
1111         gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
1112         gtk_cell_layout_set_attributes (cell_layout, renderer,
1113                 "text", COL_COMBO_VISIBLE_NAME, NULL);
1114
1115         gtk_combo_box_set_model (combo, GTK_TREE_MODEL (store));
1116         g_object_unref (store);
1117
1118         g_signal_connect (combo, "changed",
1119                           G_CALLBACK (preferences_theme_changed_cb),
1120                           preferences);
1121
1122         /* Select the theme from the gconf key and track changes */
1123         preferences_theme_notify_cb (empathy_conf_get (),
1124                                      EMPATHY_PREFS_CHAT_THEME,
1125                                      preferences);
1126         id = empathy_conf_notify_add (empathy_conf_get (),
1127                                       EMPATHY_PREFS_CHAT_THEME,
1128                                       preferences_theme_notify_cb,
1129                                       preferences);
1130         if (id) {
1131                 preferences_add_id (preferences, id);
1132         }
1133
1134         g_signal_connect (preferences->filechooserbutton_adium_theme,
1135                           "file-set",
1136                           G_CALLBACK (preferences_theme_adium_file_set_cb),
1137                           preferences);
1138         /* Select the adium path from the gconf key and track changes */
1139         preferences_theme_adium_path_notify_cb (empathy_conf_get (),
1140                                                 EMPATHY_PREFS_CHAT_ADIUM_PATH,
1141                                                 preferences);
1142         id = empathy_conf_notify_add (empathy_conf_get (),
1143                                       EMPATHY_PREFS_CHAT_ADIUM_PATH,
1144                                       preferences_theme_adium_path_notify_cb,
1145                                       preferences);
1146         if (id) {
1147                 preferences_add_id (preferences, id);
1148         }
1149 }
1150
1151 static void
1152 preferences_response_cb (GtkWidget         *widget,
1153                          gint               response,
1154                          EmpathyPreferences *preferences)
1155 {
1156         gtk_widget_destroy (widget);
1157 }
1158
1159 static void
1160 preferences_destroy_cb (GtkWidget         *widget,
1161                         EmpathyPreferences *preferences)
1162 {
1163         GList *l;
1164
1165         for (l = preferences->notify_ids; l; l = l->next) {
1166                 guint id;
1167
1168                 id = GPOINTER_TO_UINT (l->data);
1169                 empathy_conf_notify_remove (empathy_conf_get (), id);
1170         }
1171
1172         g_list_free (preferences->notify_ids);
1173         g_free (preferences);
1174 }
1175
1176 GtkWidget *
1177 empathy_preferences_show (GtkWindow *parent)
1178 {
1179         static EmpathyPreferences *preferences;
1180         GtkBuilder                *gui;
1181         gchar                     *filename;
1182         GtkWidget                 *page;
1183
1184         if (preferences) {
1185                 gtk_window_present (GTK_WINDOW (preferences->dialog));
1186                 return preferences->dialog;
1187         }
1188
1189         preferences = g_new0 (EmpathyPreferences, 1);
1190
1191         filename = empathy_file_lookup ("empathy-preferences.ui", "src");
1192         gui = empathy_builder_get_file (filename,
1193                 "preferences_dialog", &preferences->dialog,
1194                 "notebook", &preferences->notebook,
1195                 "checkbutton_show_avatars", &preferences->checkbutton_show_avatars,
1196                 "checkbutton_compact_contact_list", &preferences->checkbutton_compact_contact_list,
1197                 "checkbutton_show_smileys", &preferences->checkbutton_show_smileys,
1198                 "checkbutton_show_contacts_in_rooms", &preferences->checkbutton_show_contacts_in_rooms,
1199                 "combobox_chat_theme", &preferences->combobox_chat_theme,
1200                 "hbox_adium_theme", &preferences->hbox_adium_theme,
1201                 "filechooserbutton_adium_theme", &preferences->filechooserbutton_adium_theme,
1202                 "label_invalid_adium_theme", &preferences->label_invalid_adium_theme,
1203                 "checkbutton_separate_chat_windows", &preferences->checkbutton_separate_chat_windows,
1204                 "checkbutton_autoconnect", &preferences->checkbutton_autoconnect,
1205                 "radiobutton_contact_list_sort_by_name", &preferences->radiobutton_contact_list_sort_by_name,
1206                 "radiobutton_contact_list_sort_by_state", &preferences->radiobutton_contact_list_sort_by_state,
1207                 "checkbutton_notifications_enabled", &preferences->checkbutton_notifications_enabled,
1208                 "checkbutton_notifications_disabled_away", &preferences->checkbutton_notifications_disabled_away,
1209                 "checkbutton_notifications_focus", &preferences->checkbutton_notifications_focus,
1210                 "checkbutton_sounds_enabled", &preferences->checkbutton_sounds_enabled,
1211                 "checkbutton_sounds_disabled_away", &preferences->checkbutton_sounds_disabled_away,
1212                 "treeview_sounds", &preferences->treeview_sounds,
1213                 "treeview_spell_checker", &preferences->treeview_spell_checker,
1214                 "checkbutton_location_publish", &preferences->checkbutton_location_publish,
1215                 "checkbutton_location_reduce_accuracy", &preferences->checkbutton_location_reduce_accuracy,
1216                 "checkbutton_location_resource_network", &preferences->checkbutton_location_resource_network,
1217                 "checkbutton_location_resource_cell", &preferences->checkbutton_location_resource_cell,
1218                 "checkbutton_location_resource_gps", &preferences->checkbutton_location_resource_gps,
1219                 NULL);
1220         g_free (filename);
1221
1222         empathy_builder_connect (gui, preferences,
1223                               "preferences_dialog", "destroy", preferences_destroy_cb,
1224                               "preferences_dialog", "response", preferences_response_cb,
1225                               NULL);
1226
1227         g_object_unref (gui);
1228
1229         g_object_add_weak_pointer (G_OBJECT (preferences->dialog), (gpointer) &preferences);
1230
1231         preferences_themes_setup (preferences);
1232
1233         preferences_setup_widgets (preferences);
1234
1235         preferences_languages_setup (preferences);
1236         preferences_languages_add (preferences);
1237         preferences_languages_load (preferences);
1238
1239         preferences_sound_setup (preferences);
1240         preferences_sound_load (preferences);
1241
1242         if (empathy_spell_supported ()) {
1243                 GtkWidget *page;
1244
1245                 page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (preferences->notebook), 2);
1246                 gtk_widget_show (page);
1247         }
1248
1249         page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (preferences->notebook), 3);
1250 #if HAVE_GEOCLUE
1251         gtk_widget_show (page);
1252 #else
1253         gtk_widget_hide (page);
1254 #endif
1255
1256
1257         if (parent) {
1258                 gtk_window_set_transient_for (GTK_WINDOW (preferences->dialog),
1259                                               GTK_WINDOW (parent));
1260         }
1261
1262         gtk_widget_show (preferences->dialog);
1263
1264         return preferences->dialog;
1265 }
1266