]> git.0d.be Git - empathy.git/blob - src/empathy-preferences.c
Merge branch 'debug-av-599166'
[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  *          Danielle Madeley <danielle.madeley@collabora.co.uk>
24  */
25
26 #include <config.h>
27
28 #include <string.h>
29
30 #include <gtk/gtk.h>
31 #include <glib/gi18n.h>
32 #include <telepathy-glib/dbus.h>
33 #include <telepathy-glib/util.h>
34
35 #include <libempathy/empathy-gsettings.h>
36 #include <libempathy/empathy-utils.h>
37
38 #include <libempathy-gtk/empathy-ui-utils.h>
39 #include <libempathy-gtk/empathy-theme-manager.h>
40 #include <libempathy-gtk/empathy-spell.h>
41 #include <libempathy-gtk/empathy-contact-list-store.h>
42 #include <libempathy-gtk/empathy-gtk-enum-types.h>
43
44 #ifdef HAVE_WEBKIT
45 #include <libempathy-gtk/empathy-theme-adium.h>
46 #endif
47
48 #include "empathy-preferences.h"
49
50 G_DEFINE_TYPE (EmpathyPreferences, empathy_preferences, GTK_TYPE_DIALOG);
51
52 #define GET_PRIV(self) ((EmpathyPreferencesPriv *)((EmpathyPreferences *) self)->priv)
53
54 struct _EmpathyPreferencesPriv {
55         GtkWidget *notebook;
56
57         GtkWidget *checkbutton_show_smileys;
58         GtkWidget *checkbutton_show_contacts_in_rooms;
59         GtkWidget *combobox_chat_theme;
60         GtkWidget *checkbutton_separate_chat_windows;
61         GtkWidget *checkbutton_events_notif_area;
62         GtkWidget *checkbutton_autoconnect;
63
64         GtkWidget *checkbutton_sounds_enabled;
65         GtkWidget *checkbutton_sounds_disabled_away;
66         GtkWidget *treeview_sounds;
67
68         GtkWidget *checkbutton_notifications_enabled;
69         GtkWidget *checkbutton_notifications_disabled_away;
70         GtkWidget *checkbutton_notifications_focus;
71         GtkWidget *checkbutton_notifications_contact_signin;
72         GtkWidget *checkbutton_notifications_contact_signout;
73
74         GtkWidget *treeview_spell_checker;
75
76         GtkWidget *checkbutton_location_publish;
77         GtkWidget *checkbutton_location_reduce_accuracy;
78         GtkWidget *checkbutton_location_resource_network;
79         GtkWidget *checkbutton_location_resource_cell;
80         GtkWidget *checkbutton_location_resource_gps;
81
82         GSettings *gsettings;
83         GSettings *gsettings_chat;
84         GSettings *gsettings_loc;
85         GSettings *gsettings_notify;
86         GSettings *gsettings_sound;
87         GSettings *gsettings_ui;
88 };
89
90 static void     preferences_setup_widgets                (EmpathyPreferences      *preferences);
91 static void     preferences_languages_setup              (EmpathyPreferences      *preferences);
92 static void     preferences_languages_add                (EmpathyPreferences      *preferences);
93 static void     preferences_languages_save               (EmpathyPreferences      *preferences);
94 static gboolean preferences_languages_save_foreach       (GtkTreeModel           *model,
95                                                           GtkTreePath            *path,
96                                                           GtkTreeIter            *iter,
97                                                           gchar                 **languages);
98 static void     preferences_languages_load               (EmpathyPreferences      *preferences);
99 static gboolean preferences_languages_load_foreach       (GtkTreeModel           *model,
100                                                           GtkTreePath            *path,
101                                                           GtkTreeIter            *iter,
102                                                           gchar                 **languages);
103 static void     preferences_languages_cell_toggled_cb    (GtkCellRendererToggle  *cell,
104                                                           gchar                  *path_string,
105                                                           EmpathyPreferences      *preferences);
106
107 enum {
108         COL_LANG_ENABLED,
109         COL_LANG_CODE,
110         COL_LANG_NAME,
111         COL_LANG_COUNT
112 };
113
114 enum {
115         COL_COMBO_IS_ADIUM,
116         COL_COMBO_VISIBLE_NAME,
117         COL_COMBO_NAME,
118         COL_COMBO_PATH,
119         COL_COMBO_COUNT
120 };
121
122 enum {
123         COL_SOUND_ENABLED,
124         COL_SOUND_NAME,
125         COL_SOUND_KEY,
126         COL_SOUND_COUNT
127 };
128
129 typedef struct {
130         const char *name;
131         const char *key;
132 } SoundEventEntry;
133
134 /* TODO: add phone related sounds also? */
135 static SoundEventEntry sound_entries [] = {
136         { N_("Message received"), EMPATHY_PREFS_SOUNDS_INCOMING_MESSAGE },
137         { N_("Message sent"), EMPATHY_PREFS_SOUNDS_OUTGOING_MESSAGE },
138         { N_("New conversation"), EMPATHY_PREFS_SOUNDS_NEW_CONVERSATION },
139         { N_("Contact goes online"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGIN },
140         { N_("Contact goes offline"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGOUT },
141         { N_("Account connected"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGIN },
142         { N_("Account disconnected"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGOUT }
143 };
144
145 static void
146 preferences_setup_widgets (EmpathyPreferences *preferences)
147 {
148         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
149
150         g_settings_bind (priv->gsettings_notify,
151                          EMPATHY_PREFS_NOTIFICATIONS_ENABLED,
152                          priv->checkbutton_notifications_enabled,
153                          "active",
154                          G_SETTINGS_BIND_DEFAULT);
155         g_settings_bind (priv->gsettings_notify,
156                          EMPATHY_PREFS_NOTIFICATIONS_DISABLED_AWAY,
157                          priv->checkbutton_notifications_disabled_away,
158                          "active",
159                          G_SETTINGS_BIND_DEFAULT);
160         g_settings_bind (priv->gsettings_notify,
161                          EMPATHY_PREFS_NOTIFICATIONS_FOCUS,
162                          priv->checkbutton_notifications_focus,
163                          "active",
164                          G_SETTINGS_BIND_DEFAULT);
165         g_settings_bind (priv->gsettings_notify,
166                          EMPATHY_PREFS_NOTIFICATIONS_CONTACT_SIGNIN,
167                          priv->checkbutton_notifications_contact_signin,
168                          "active",
169                          G_SETTINGS_BIND_DEFAULT);
170         g_settings_bind (priv->gsettings_notify,
171                          EMPATHY_PREFS_NOTIFICATIONS_CONTACT_SIGNOUT,
172                          priv->checkbutton_notifications_contact_signout,
173                          "active",
174                          G_SETTINGS_BIND_DEFAULT);
175
176         g_settings_bind (priv->gsettings_notify,
177                          EMPATHY_PREFS_NOTIFICATIONS_ENABLED,
178                          priv->checkbutton_notifications_disabled_away,
179                          "sensitive",
180                          G_SETTINGS_BIND_GET);
181         g_settings_bind (priv->gsettings_notify,
182                          EMPATHY_PREFS_NOTIFICATIONS_ENABLED,
183                          priv->checkbutton_notifications_focus,
184                          "sensitive",
185                          G_SETTINGS_BIND_GET);
186         g_settings_bind (priv->gsettings_notify,
187                          EMPATHY_PREFS_NOTIFICATIONS_ENABLED,
188                          priv->checkbutton_notifications_contact_signin,
189                          "sensitive",
190                          G_SETTINGS_BIND_GET);
191         g_settings_bind (priv->gsettings_notify,
192                          EMPATHY_PREFS_NOTIFICATIONS_ENABLED,
193                          priv->checkbutton_notifications_contact_signout,
194                          "sensitive",
195                          G_SETTINGS_BIND_GET);
196
197         g_settings_bind (priv->gsettings_sound,
198                          EMPATHY_PREFS_SOUNDS_ENABLED,
199                          priv->checkbutton_sounds_enabled,
200                          "active",
201                          G_SETTINGS_BIND_DEFAULT);
202         g_settings_bind (priv->gsettings_sound,
203                          EMPATHY_PREFS_SOUNDS_DISABLED_AWAY,
204                          priv->checkbutton_sounds_disabled_away,
205                          "active",
206                          G_SETTINGS_BIND_DEFAULT);
207
208         g_settings_bind (priv->gsettings_sound,
209                          EMPATHY_PREFS_SOUNDS_ENABLED,
210                          priv->checkbutton_sounds_disabled_away,
211                          "sensitive",
212                          G_SETTINGS_BIND_GET);
213         g_settings_bind (priv->gsettings_sound,
214                         EMPATHY_PREFS_SOUNDS_ENABLED,
215                         priv->treeview_sounds,
216                         "sensitive",
217                         G_SETTINGS_BIND_GET);
218
219         g_settings_bind (priv->gsettings_ui,
220                          EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS,
221                          priv->checkbutton_separate_chat_windows,
222                          "active",
223                          G_SETTINGS_BIND_DEFAULT);
224
225         g_settings_bind (priv->gsettings_ui,
226                          EMPATHY_PREFS_UI_EVENTS_NOTIFY_AREA,
227                          priv->checkbutton_events_notif_area,
228                          "active",
229                          G_SETTINGS_BIND_DEFAULT);
230
231         g_settings_bind (priv->gsettings_chat,
232                          EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
233                          priv->checkbutton_show_smileys,
234                          "active",
235                          G_SETTINGS_BIND_DEFAULT);
236         g_settings_bind (priv->gsettings_chat,
237                          EMPATHY_PREFS_CHAT_SHOW_CONTACTS_IN_ROOMS,
238                          priv->checkbutton_show_contacts_in_rooms,
239                          "active",
240                          G_SETTINGS_BIND_DEFAULT);
241
242         g_settings_bind (priv->gsettings,
243                          EMPATHY_PREFS_AUTOCONNECT,
244                          priv->checkbutton_autoconnect,
245                          "active",
246                          G_SETTINGS_BIND_DEFAULT);
247
248         g_settings_bind (priv->gsettings_loc,
249                          EMPATHY_PREFS_LOCATION_PUBLISH,
250                          priv->checkbutton_location_publish,
251                          "active",
252                          G_SETTINGS_BIND_DEFAULT);
253
254         g_settings_bind (priv->gsettings_loc,
255                          EMPATHY_PREFS_LOCATION_RESOURCE_NETWORK,
256                          priv->checkbutton_location_resource_network,
257                          "active",
258                          G_SETTINGS_BIND_DEFAULT);
259         g_settings_bind (priv->gsettings_loc,
260                          EMPATHY_PREFS_LOCATION_PUBLISH,
261                          priv->checkbutton_location_resource_network,
262                          "sensitive",
263                          G_SETTINGS_BIND_GET);
264
265         g_settings_bind (priv->gsettings_loc,
266                          EMPATHY_PREFS_LOCATION_RESOURCE_CELL,
267                          priv->checkbutton_location_resource_cell,
268                          "active",
269                          G_SETTINGS_BIND_DEFAULT);
270         g_settings_bind (priv->gsettings_loc,
271                          EMPATHY_PREFS_LOCATION_PUBLISH,
272                          priv->checkbutton_location_resource_cell,
273                          "sensitive",
274                          G_SETTINGS_BIND_GET);
275
276         g_settings_bind (priv->gsettings_loc,
277                          EMPATHY_PREFS_LOCATION_RESOURCE_GPS,
278                          priv->checkbutton_location_resource_gps,
279                          "active",
280                          G_SETTINGS_BIND_DEFAULT);
281         g_settings_bind (priv->gsettings_loc,
282                          EMPATHY_PREFS_LOCATION_PUBLISH,
283                          priv->checkbutton_location_resource_gps,
284                          "sensitive",
285                          G_SETTINGS_BIND_GET);
286
287         g_settings_bind (priv->gsettings_loc,
288                          EMPATHY_PREFS_LOCATION_REDUCE_ACCURACY,
289                          priv->checkbutton_location_reduce_accuracy,
290                          "active",
291                          G_SETTINGS_BIND_DEFAULT);
292         g_settings_bind (priv->gsettings_loc,
293                          EMPATHY_PREFS_LOCATION_PUBLISH,
294                          priv->checkbutton_location_reduce_accuracy,
295                          "sensitive",
296                          G_SETTINGS_BIND_GET);
297 }
298
299 static void
300 preferences_sound_cell_toggled_cb (GtkCellRendererToggle *toggle,
301                                    char *path_string,
302                                    EmpathyPreferences *preferences)
303 {
304         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
305         GtkTreePath *path;
306         gboolean toggled, instore;
307         GtkTreeIter iter;
308         GtkTreeView *view;
309         GtkTreeModel *model;
310         char *key;
311
312         view = GTK_TREE_VIEW (priv->treeview_sounds);
313         model = gtk_tree_view_get_model (view);
314
315         path = gtk_tree_path_new_from_string (path_string);
316         toggled = gtk_cell_renderer_toggle_get_active (toggle);
317
318         gtk_tree_model_get_iter (model, &iter, path);
319         gtk_tree_model_get (model, &iter, COL_SOUND_KEY, &key,
320                             COL_SOUND_ENABLED, &instore, -1);
321
322         instore ^= 1;
323
324         gtk_list_store_set (GTK_LIST_STORE (model), &iter,
325                             COL_SOUND_ENABLED, instore, -1);
326
327         g_settings_set_boolean (priv->gsettings_sound, key, instore);
328
329         g_free (key);
330         gtk_tree_path_free (path);
331 }
332
333 static void
334 preferences_sound_load (EmpathyPreferences *preferences)
335 {
336         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
337         guint i;
338         GtkTreeView *view;
339         GtkListStore *store;
340         GtkTreeIter iter;
341         gboolean set;
342
343         view = GTK_TREE_VIEW (priv->treeview_sounds);
344         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
345
346         for (i = 0; i < G_N_ELEMENTS (sound_entries); i++) {
347                 set = g_settings_get_boolean (priv->gsettings_sound,
348                                               sound_entries[i].key);
349
350                 gtk_list_store_insert_with_values (store, &iter, i,
351                                                    COL_SOUND_NAME, gettext (sound_entries[i].name),
352                                                    COL_SOUND_KEY, sound_entries[i].key,
353                                                    COL_SOUND_ENABLED, set, -1);
354         }
355 }
356
357 static void
358 preferences_sound_setup (EmpathyPreferences *preferences)
359 {
360         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
361         GtkTreeView *view;
362         GtkListStore *store;
363         GtkCellRenderer *renderer;
364         GtkTreeViewColumn *column;
365
366         view = GTK_TREE_VIEW (priv->treeview_sounds);
367
368         store = gtk_list_store_new (COL_SOUND_COUNT,
369                                     G_TYPE_BOOLEAN, /* enabled */
370                                     G_TYPE_STRING,  /* name */
371                                     G_TYPE_STRING); /* key */
372
373         gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
374
375         renderer = gtk_cell_renderer_toggle_new ();
376         g_signal_connect (renderer, "toggled",
377                           G_CALLBACK (preferences_sound_cell_toggled_cb),
378                           preferences);
379
380         column = gtk_tree_view_column_new ();
381         gtk_tree_view_column_pack_start (column, renderer, FALSE);
382         gtk_tree_view_column_add_attribute (column, renderer,
383                                             "active", COL_SOUND_ENABLED);
384
385         renderer = gtk_cell_renderer_text_new ();
386         gtk_tree_view_column_pack_start (column, renderer, FALSE);
387         gtk_tree_view_column_add_attribute (column, renderer,
388                                             "text", COL_SOUND_NAME);
389
390         gtk_tree_view_append_column (view, column);
391
392         gtk_tree_view_column_set_resizable (column, FALSE);
393         gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
394
395         g_object_unref (store);
396 }
397
398 static void
399 preferences_languages_setup (EmpathyPreferences *preferences)
400 {
401         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
402         GtkTreeView       *view;
403         GtkListStore      *store;
404         GtkTreeSelection  *selection;
405         GtkTreeModel      *model;
406         GtkTreeViewColumn *column;
407         GtkCellRenderer   *renderer;
408         guint              col_offset;
409
410         view = GTK_TREE_VIEW (priv->treeview_spell_checker);
411
412         store = gtk_list_store_new (COL_LANG_COUNT,
413                                     G_TYPE_BOOLEAN,  /* enabled */
414                                     G_TYPE_STRING,   /* code */
415                                     G_TYPE_STRING);  /* name */
416
417         gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
418
419         selection = gtk_tree_view_get_selection (view);
420         gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
421
422         model = GTK_TREE_MODEL (store);
423
424         renderer = gtk_cell_renderer_toggle_new ();
425         g_signal_connect (renderer, "toggled",
426                           G_CALLBACK (preferences_languages_cell_toggled_cb),
427                           preferences);
428
429         column = gtk_tree_view_column_new_with_attributes (NULL, renderer,
430                                                            "active", COL_LANG_ENABLED,
431                                                            NULL);
432
433         gtk_tree_view_append_column (view, column);
434
435         renderer = gtk_cell_renderer_text_new ();
436         col_offset = gtk_tree_view_insert_column_with_attributes (view,
437                                                                   -1, _("Language"),
438                                                                   renderer,
439                                                                   "text", COL_LANG_NAME,
440                                                                   NULL);
441
442         g_object_set_data (G_OBJECT (renderer),
443                            "column", GINT_TO_POINTER (COL_LANG_NAME));
444
445         column = gtk_tree_view_get_column (view, col_offset - 1);
446         gtk_tree_view_column_set_sort_column_id (column, COL_LANG_NAME);
447         gtk_tree_view_column_set_resizable (column, FALSE);
448         gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
449
450         g_object_unref (store);
451 }
452
453 static void
454 preferences_languages_add (EmpathyPreferences *preferences)
455 {
456         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
457         GtkTreeView  *view;
458         GtkListStore *store;
459         GList        *codes, *l;
460
461         view = GTK_TREE_VIEW (priv->treeview_spell_checker);
462         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
463
464         codes = empathy_spell_get_language_codes ();
465
466         g_settings_set_boolean (priv->gsettings_chat,
467                                 EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
468                                 codes != NULL);
469         if (!codes) {
470                 gtk_widget_set_sensitive (priv->treeview_spell_checker, FALSE);
471         }
472
473         for (l = codes; l; l = l->next) {
474                 GtkTreeIter  iter;
475                 const gchar *code;
476                 const gchar *name;
477
478                 code = l->data;
479                 name = empathy_spell_get_language_name (code);
480                 if (!name) {
481                         continue;
482                 }
483
484                 gtk_list_store_append (store, &iter);
485                 gtk_list_store_set (store, &iter,
486                                     COL_LANG_CODE, code,
487                                     COL_LANG_NAME, name,
488                                     -1);
489         }
490
491         empathy_spell_free_language_codes (codes);
492 }
493
494 static void
495 preferences_languages_save (EmpathyPreferences *preferences)
496 {
497         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
498         GtkTreeView       *view;
499         GtkTreeModel      *model;
500
501         gchar             *languages = NULL;
502
503         view = GTK_TREE_VIEW (priv->treeview_spell_checker);
504         model = gtk_tree_view_get_model (view);
505
506         gtk_tree_model_foreach (model,
507                                 (GtkTreeModelForeachFunc) preferences_languages_save_foreach,
508                                 &languages);
509
510         /* if user selects no languages, we don't want spell check */
511         g_settings_set_boolean (priv->gsettings_chat,
512                                 EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
513                                 languages != NULL);
514
515         g_settings_set_string (priv->gsettings_chat,
516                                EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES,
517                                languages != NULL ? languages : "");
518
519         g_free (languages);
520 }
521
522 static gboolean
523 preferences_languages_save_foreach (GtkTreeModel  *model,
524                                     GtkTreePath   *path,
525                                     GtkTreeIter   *iter,
526                                     gchar        **languages)
527 {
528         gboolean  enabled;
529         gchar    *code;
530
531         if (!languages) {
532                 return TRUE;
533         }
534
535         gtk_tree_model_get (model, iter, COL_LANG_ENABLED, &enabled, -1);
536         if (!enabled) {
537                 return FALSE;
538         }
539
540         gtk_tree_model_get (model, iter, COL_LANG_CODE, &code, -1);
541         if (!code) {
542                 return FALSE;
543         }
544
545         if (!(*languages)) {
546                 *languages = g_strdup (code);
547         } else {
548                 gchar *str = *languages;
549                 *languages = g_strdup_printf ("%s,%s", str, code);
550                 g_free (str);
551         }
552
553         g_free (code);
554
555         return FALSE;
556 }
557
558 static void
559 preferences_languages_load (EmpathyPreferences *preferences)
560 {
561         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
562         GtkTreeView   *view;
563         GtkTreeModel  *model;
564         gchar         *value;
565         gchar        **vlanguages;
566
567         value = g_settings_get_string (priv->gsettings_chat,
568                                        EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES);
569
570         if (value == NULL)
571                 return;
572
573         vlanguages = g_strsplit (value, ",", -1);
574         g_free (value);
575
576         view = GTK_TREE_VIEW (priv->treeview_spell_checker);
577         model = gtk_tree_view_get_model (view);
578
579         gtk_tree_model_foreach (model,
580                                 (GtkTreeModelForeachFunc) preferences_languages_load_foreach,
581                                 vlanguages);
582
583         g_strfreev (vlanguages);
584 }
585
586 static gboolean
587 preferences_languages_load_foreach (GtkTreeModel  *model,
588                                     GtkTreePath   *path,
589                                     GtkTreeIter   *iter,
590                                     gchar        **languages)
591 {
592         gchar    *code;
593         gchar    *lang;
594         gint      i;
595         gboolean  found = FALSE;
596
597         if (!languages) {
598                 return TRUE;
599         }
600
601         gtk_tree_model_get (model, iter, COL_LANG_CODE, &code, -1);
602         if (!code) {
603                 return FALSE;
604         }
605
606         for (i = 0, lang = languages[i]; lang; lang = languages[++i]) {
607                 if (!tp_strdiff (lang, code)) {
608                         found = TRUE;
609                 }
610         }
611
612         g_free (code);
613         gtk_list_store_set (GTK_LIST_STORE (model), iter, COL_LANG_ENABLED, found, -1);
614         return FALSE;
615 }
616
617 static void
618 preferences_languages_cell_toggled_cb (GtkCellRendererToggle *cell,
619                                        gchar                 *path_string,
620                                        EmpathyPreferences     *preferences)
621 {
622         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
623         GtkTreeView  *view;
624         GtkTreeModel *model;
625         GtkListStore *store;
626         GtkTreePath  *path;
627         GtkTreeIter   iter;
628         gboolean      enabled;
629
630         view = GTK_TREE_VIEW (priv->treeview_spell_checker);
631         model = gtk_tree_view_get_model (view);
632         store = GTK_LIST_STORE (model);
633
634         path = gtk_tree_path_new_from_string (path_string);
635
636         gtk_tree_model_get_iter (model, &iter, path);
637         gtk_tree_model_get (model, &iter, COL_LANG_ENABLED, &enabled, -1);
638
639         enabled ^= 1;
640
641         gtk_list_store_set (store, &iter, COL_LANG_ENABLED, enabled, -1);
642         gtk_tree_path_free (path);
643
644         preferences_languages_save (preferences);
645 }
646
647 static void
648 preferences_theme_notify_cb (GSettings   *gsettings,
649                              const gchar *key,
650                              gpointer     user_data)
651 {
652         EmpathyPreferences *preferences = user_data;
653         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
654         GtkComboBox        *combo;
655         gchar              *conf_name;
656         gchar              *conf_path;
657         GtkTreeModel       *model;
658         GtkTreeIter         iter;
659         gboolean            found = FALSE;
660
661         conf_name = g_settings_get_string (gsettings, EMPATHY_PREFS_CHAT_THEME);
662         conf_path = g_settings_get_string (gsettings, EMPATHY_PREFS_CHAT_ADIUM_PATH);
663
664         combo = GTK_COMBO_BOX (priv->combobox_chat_theme);
665         model = gtk_combo_box_get_model (combo);
666         if (gtk_tree_model_get_iter_first (model, &iter)) {
667                 gboolean is_adium;
668                 gchar *name;
669                 gchar *path;
670
671                 do {
672                         gtk_tree_model_get (model, &iter,
673                                             COL_COMBO_IS_ADIUM, &is_adium,
674                                             COL_COMBO_NAME, &name,
675                                             COL_COMBO_PATH, &path,
676                                             -1);
677
678                         if (!tp_strdiff (name, conf_name)) {
679                                 if (tp_strdiff (name, "adium") ||
680                                     !tp_strdiff (path, conf_path)) {
681                                         found = TRUE;
682                                         gtk_combo_box_set_active_iter (combo, &iter);
683                                         g_free (name);
684                                         g_free (path);
685                                         break;
686                                 }
687                         }
688
689                         g_free (name);
690                         g_free (path);
691                 } while (gtk_tree_model_iter_next (model, &iter));
692         }
693
694         /* Fallback to the first one. */
695         if (!found) {
696                 if (gtk_tree_model_get_iter_first (model, &iter)) {
697                         gtk_combo_box_set_active_iter (combo, &iter);
698                 }
699         }
700
701         g_free (conf_name);
702         g_free (conf_path);
703 }
704
705 static void
706 preferences_theme_changed_cb (GtkComboBox        *combo,
707                               EmpathyPreferences *preferences)
708 {
709         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
710         GtkTreeModel *model;
711         GtkTreeIter   iter;
712         gboolean      is_adium;
713         gchar        *name;
714         gchar        *path;
715
716         if (gtk_combo_box_get_active_iter (combo, &iter)) {
717                 model = gtk_combo_box_get_model (combo);
718
719                 gtk_tree_model_get (model, &iter,
720                                     COL_COMBO_IS_ADIUM, &is_adium,
721                                     COL_COMBO_NAME, &name,
722                                     COL_COMBO_PATH, &path,
723                                     -1);
724
725                 g_settings_set_string (priv->gsettings_chat,
726                                        EMPATHY_PREFS_CHAT_THEME,
727                                        name);
728                 if (is_adium == TRUE)
729                         g_settings_set_string (priv->gsettings_chat,
730                                                EMPATHY_PREFS_CHAT_ADIUM_PATH,
731                                                path);
732                 g_free (name);
733                 g_free (path);
734         }
735 }
736
737 static void
738 preferences_themes_setup (EmpathyPreferences *preferences)
739 {
740         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
741         GtkComboBox   *combo;
742         GtkCellLayout *cell_layout;
743         GtkCellRenderer *renderer;
744         GtkListStore  *store;
745         const gchar  **themes;
746         GList         *adium_themes;
747         gint           i;
748
749         combo = GTK_COMBO_BOX (priv->combobox_chat_theme);
750         cell_layout = GTK_CELL_LAYOUT (combo);
751
752         /* Create the model */
753         store = gtk_list_store_new (COL_COMBO_COUNT,
754                                     G_TYPE_BOOLEAN, /* Is an Adium theme */
755                                     G_TYPE_STRING,  /* Display name */
756                                     G_TYPE_STRING,  /* Theme name */
757                                     G_TYPE_STRING); /* Theme path */
758         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
759                 COL_COMBO_VISIBLE_NAME, GTK_SORT_ASCENDING);
760
761         /* Fill the model */
762         themes = empathy_theme_manager_get_themes ();
763         for (i = 0; themes[i]; i += 2) {
764                 gtk_list_store_insert_with_values (store, NULL, -1,
765                         COL_COMBO_IS_ADIUM, FALSE,
766                         COL_COMBO_VISIBLE_NAME, _(themes[i + 1]),
767                         COL_COMBO_NAME, themes[i],
768                         COL_COMBO_PATH, NULL,
769                         -1);
770         }
771
772         adium_themes = empathy_theme_manager_get_adium_themes ();
773         while (adium_themes != NULL) {
774                 GHashTable *info;
775                 const gchar *name;
776                 const gchar *path;
777
778                 info = adium_themes->data;
779                 name = tp_asv_get_string (info, "CFBundleName");
780                 path = tp_asv_get_string (info, "path");
781
782                 if (name != NULL && path != NULL) {
783                         gtk_list_store_insert_with_values (store, NULL, -1,
784                                 COL_COMBO_IS_ADIUM, TRUE,
785                                 COL_COMBO_VISIBLE_NAME, name,
786                                 COL_COMBO_NAME, "adium",
787                                 COL_COMBO_PATH, path,
788                                 -1);
789                 }
790                 g_hash_table_unref (info);
791                 adium_themes = g_list_delete_link (adium_themes, adium_themes);
792         }
793
794         /* Add cell renderer */
795         renderer = gtk_cell_renderer_text_new ();
796         gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
797         gtk_cell_layout_set_attributes (cell_layout, renderer,
798                 "text", COL_COMBO_VISIBLE_NAME, NULL);
799
800         gtk_combo_box_set_model (combo, GTK_TREE_MODEL (store));
801         g_object_unref (store);
802
803         g_signal_connect (combo, "changed",
804                           G_CALLBACK (preferences_theme_changed_cb),
805                           preferences);
806
807         /* Select the theme from the GSetting key and track changes */
808         preferences_theme_notify_cb (priv->gsettings_chat,
809                                      EMPATHY_PREFS_CHAT_THEME,
810                                      preferences);
811         g_signal_connect (priv->gsettings_chat,
812                           "changed::" EMPATHY_PREFS_CHAT_THEME,
813                           G_CALLBACK (preferences_theme_notify_cb),
814                           preferences);
815
816         g_signal_connect (priv->gsettings_chat,
817                           "changed::" EMPATHY_PREFS_CHAT_ADIUM_PATH,
818                           G_CALLBACK (preferences_theme_notify_cb),
819                           preferences);
820 }
821
822 static void
823 empathy_preferences_response (GtkDialog *widget,
824                               gint response)
825 {
826         gtk_widget_destroy (GTK_WIDGET (widget));
827 }
828
829 static void
830 empathy_preferences_finalize (GObject *self)
831 {
832         EmpathyPreferencesPriv *priv = GET_PRIV (self);
833
834         g_object_unref (priv->gsettings);
835         g_object_unref (priv->gsettings_chat);
836         g_object_unref (priv->gsettings_loc);
837         g_object_unref (priv->gsettings_notify);
838         g_object_unref (priv->gsettings_sound);
839         g_object_unref (priv->gsettings_ui);
840
841         G_OBJECT_CLASS (empathy_preferences_parent_class)->finalize (self);
842 }
843
844 static void
845 empathy_preferences_class_init (EmpathyPreferencesClass *klass)
846 {
847         GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
848         GObjectClass *object_class = G_OBJECT_CLASS (klass);
849
850         dialog_class->response = empathy_preferences_response;
851
852         object_class->finalize = empathy_preferences_finalize;
853
854         g_type_class_add_private (object_class,
855                                   sizeof (EmpathyPreferencesPriv));
856 }
857
858 static void
859 empathy_preferences_init (EmpathyPreferences *preferences)
860 {
861         EmpathyPreferencesPriv    *priv;
862         GtkBuilder                *gui;
863         gchar                     *filename;
864         GtkWidget                 *page;
865
866         priv = preferences->priv = G_TYPE_INSTANCE_GET_PRIVATE (preferences,
867                         EMPATHY_TYPE_PREFERENCES, EmpathyPreferencesPriv);
868
869         gtk_dialog_add_button (GTK_DIALOG (preferences),
870                                GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
871
872         gtk_container_set_border_width (GTK_CONTAINER (preferences), 5);
873         gtk_window_set_title (GTK_WINDOW (preferences), _("Preferences"));
874         gtk_window_set_role (GTK_WINDOW (preferences), "preferences");
875         gtk_window_set_position (GTK_WINDOW (preferences),
876                                  GTK_WIN_POS_CENTER_ON_PARENT);
877         gtk_window_set_icon_name (GTK_WINDOW (preferences), "gtk-preferences");
878         gtk_dialog_set_has_separator (GTK_DIALOG (preferences), FALSE);
879
880         filename = empathy_file_lookup ("empathy-preferences.ui", "src");
881         gui = empathy_builder_get_file (filename,
882                 "notebook", &priv->notebook,
883                 "checkbutton_show_smileys", &priv->checkbutton_show_smileys,
884                 "checkbutton_show_contacts_in_rooms", &priv->checkbutton_show_contacts_in_rooms,
885                 "combobox_chat_theme", &priv->combobox_chat_theme,
886                 "checkbutton_separate_chat_windows", &priv->checkbutton_separate_chat_windows,
887                 "checkbutton_events_notif_area", &priv->checkbutton_events_notif_area,
888                 "checkbutton_autoconnect", &priv->checkbutton_autoconnect,
889                 "checkbutton_notifications_enabled", &priv->checkbutton_notifications_enabled,
890                 "checkbutton_notifications_disabled_away", &priv->checkbutton_notifications_disabled_away,
891                 "checkbutton_notifications_focus", &priv->checkbutton_notifications_focus,
892                 "checkbutton_notifications_contact_signin", &priv->checkbutton_notifications_contact_signin,
893                 "checkbutton_notifications_contact_signout", &priv->checkbutton_notifications_contact_signout,
894                 "checkbutton_sounds_enabled", &priv->checkbutton_sounds_enabled,
895                 "checkbutton_sounds_disabled_away", &priv->checkbutton_sounds_disabled_away,
896                 "treeview_sounds", &priv->treeview_sounds,
897                 "treeview_spell_checker", &priv->treeview_spell_checker,
898                 "checkbutton_location_publish", &priv->checkbutton_location_publish,
899                 "checkbutton_location_reduce_accuracy", &priv->checkbutton_location_reduce_accuracy,
900                 "checkbutton_location_resource_network", &priv->checkbutton_location_resource_network,
901                 "checkbutton_location_resource_cell", &priv->checkbutton_location_resource_cell,
902                 "checkbutton_location_resource_gps", &priv->checkbutton_location_resource_gps,
903                 NULL);
904         g_free (filename);
905
906         gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (preferences))), priv->notebook);
907         gtk_widget_show (priv->notebook);
908
909         g_object_unref (gui);
910
911         priv->gsettings = g_settings_new (EMPATHY_PREFS_SCHEMA);
912         priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
913         priv->gsettings_loc = g_settings_new (EMPATHY_PREFS_LOCATION_SCHEMA);
914         priv->gsettings_notify = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
915         priv->gsettings_sound = g_settings_new (EMPATHY_PREFS_SOUNDS_SCHEMA);
916         priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
917
918         preferences_themes_setup (preferences);
919
920         preferences_setup_widgets (preferences);
921
922         preferences_languages_setup (preferences);
923         preferences_languages_add (preferences);
924         preferences_languages_load (preferences);
925
926         preferences_sound_setup (preferences);
927         preferences_sound_load (preferences);
928
929         if (empathy_spell_supported ()) {
930                 page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 2);
931                 gtk_widget_show (page);
932         }
933
934         page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 3);
935 #if HAVE_GEOCLUE
936         gtk_widget_show (page);
937 #else
938         gtk_widget_hide (page);
939 #endif
940 }
941
942 GtkWidget *
943 empathy_preferences_new (GtkWindow *parent)
944 {
945         GtkWidget *self;
946
947         g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
948
949         self = g_object_new (EMPATHY_TYPE_PREFERENCES, NULL);
950
951         if (parent != NULL) {
952                 gtk_window_set_transient_for (GTK_WINDOW (self),
953                                               parent);
954         }
955
956         return self;
957 }