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