]> git.0d.be Git - empathy.git/blob - src/empathy-preferences.c
preferences: remove location resources BIND_SENSITIVE calls
[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 #include "empathy-preferences.h"
28
29 #include <glib/gi18n.h>
30 #include <tp-account-widgets/tpaw-builder.h>
31
32 #include "empathy-client-factory.h"
33 #include "empathy-gsettings.h"
34 #include "empathy-spell.h"
35 #include "empathy-theme-manager.h"
36 #include "empathy-ui-utils.h"
37 #include "empathy-utils.h"
38
39 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
40 #include "empathy-debug.h"
41
42 G_DEFINE_TYPE (EmpathyPreferences, empathy_preferences, GTK_TYPE_DIALOG);
43
44 #define GET_PRIV(self) ((EmpathyPreferencesPriv *)((EmpathyPreferences *) self)->priv)
45
46 static const gchar * empathy_preferences_tabs[] =
47 {
48   "general",
49   "notifications",
50   "sounds",
51   "calls",
52   "location",
53   "spell",
54   "themes",
55 };
56
57 struct _EmpathyPreferencesPriv {
58         GtkWidget *notebook;
59
60         GtkWidget *label_general_behavior;
61         GtkWidget *checkbutton_events_notif_area;
62         GtkWidget *checkbutton_autoconnect;
63
64         GtkWidget *treeview_sounds;
65         GtkWidget *treeview_spell_checker;
66
67         GtkWidget *vbox_chat_theme;
68         GtkWidget *combobox_chat_theme;
69         GtkWidget *combobox_chat_theme_variant;
70         GtkWidget *hbox_chat_theme_variant;
71         GtkWidget *sw_chat_theme_preview;
72         EmpathyThemeAdium *chat_theme_preview;
73         EmpathyThemeManager *theme_manager;
74
75         GSettings *gsettings;
76         GSettings *gsettings_chat;
77         GSettings *gsettings_call;
78         GSettings *gsettings_loc;
79         GSettings *gsettings_notify;
80         GSettings *gsettings_sound;
81         GSettings *gsettings_ui;
82         GSettings *gsettings_logger;
83 };
84
85 static void     preferences_setup_widgets                (EmpathyPreferences      *preferences,
86                                                           GtkBuilder              *gui);
87 static void     preferences_languages_setup              (EmpathyPreferences      *preferences);
88 static void     preferences_languages_add                (EmpathyPreferences      *preferences);
89 static void     preferences_languages_save               (EmpathyPreferences      *preferences);
90 static gboolean preferences_languages_save_foreach       (GtkTreeModel           *model,
91                                                           GtkTreePath            *path,
92                                                           GtkTreeIter            *iter,
93                                                           gchar                 **languages);
94 static void     preferences_languages_load               (EmpathyPreferences      *preferences);
95 static gboolean preferences_languages_load_foreach       (GtkTreeModel           *model,
96                                                           GtkTreePath            *path,
97                                                           GtkTreeIter            *iter,
98                                                           GList                  *languages);
99 static void     preferences_languages_cell_toggled_cb    (GtkCellRendererToggle  *cell,
100                                                           gchar                  *path_string,
101                                                           EmpathyPreferences      *preferences);
102
103 enum {
104         COL_LANG_ENABLED,
105         COL_LANG_CODE,
106         COL_LANG_NAME,
107         COL_LANG_COUNT
108 };
109
110 enum {
111         COL_THEME_VISIBLE_NAME,
112         COL_THEME_ADIUM_NAME,
113         COL_THEME_ADIUM_INFO,
114         COL_THEME_COUNT
115 };
116
117 enum {
118         COL_VARIANT_NAME,
119         COL_VARIANT_DEFAULT,
120         COL_VARIANT_COUNT
121 };
122
123 enum {
124         COL_SOUND_ENABLED,
125         COL_SOUND_NAME,
126         COL_SOUND_KEY,
127         COL_SOUND_COUNT
128 };
129
130 typedef struct {
131         const char *name;
132         const char *key;
133 } SoundEventEntry;
134
135 /* TODO: add phone related sounds also? */
136 static SoundEventEntry sound_entries [] = {
137         { N_("Message received"), EMPATHY_PREFS_SOUNDS_INCOMING_MESSAGE },
138         { N_("Message sent"), EMPATHY_PREFS_SOUNDS_OUTGOING_MESSAGE },
139         { N_("New conversation"), EMPATHY_PREFS_SOUNDS_NEW_CONVERSATION },
140         { N_("Contact comes online"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGIN },
141         { N_("Contact goes offline"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGOUT },
142         { N_("Account connected"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGIN },
143         { N_("Account disconnected"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGOUT }
144 };
145
146 static void
147 preferences_setup_widgets (EmpathyPreferences *preferences,
148                            GtkBuilder *gui)
149 {
150         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
151 #define BIND_ACTIVE(schema, key, widget) \
152         g_settings_bind (priv->gsettings_##schema, EMPATHY_PREFS_##key, \
153                          gtk_builder_get_object (gui, widget), "active", \
154                          G_SETTINGS_BIND_DEFAULT);
155
156         BIND_ACTIVE (notify, NOTIFICATIONS_ENABLED,
157                      "checkbutton_notifications_enabled");
158         BIND_ACTIVE (notify, NOTIFICATIONS_DISABLED_AWAY,
159                      "checkbutton_notifications_disabled_away");
160         BIND_ACTIVE (notify, NOTIFICATIONS_FOCUS,
161                      "checkbutton_notifications_focus");
162         BIND_ACTIVE (notify, NOTIFICATIONS_CONTACT_SIGNIN,
163                      "checkbutton_notifications_contact_signin");
164         BIND_ACTIVE (notify, NOTIFICATIONS_CONTACT_SIGNOUT,
165                      "checkbutton_notifications_contact_signout");
166
167         BIND_ACTIVE (sound, SOUNDS_ENABLED,
168                      "checkbutton_sounds_enabled");
169         BIND_ACTIVE (sound, SOUNDS_DISABLED_AWAY,
170                      "checkbutton_sounds_disabled_away");
171
172         BIND_ACTIVE (ui, UI_SHOW_OFFLINE,
173                      "checkbutton_show_offline");
174         BIND_ACTIVE (ui, UI_SHOW_GROUPS,
175                      "checkbutton_show_groups");
176         BIND_ACTIVE (ui, UI_SEPARATE_CHAT_WINDOWS,
177                      "radiobutton_chats_new_windows");
178         BIND_ACTIVE (ui, UI_EVENTS_NOTIFY_AREA,
179                      "checkbutton_events_notif_area");
180         BIND_ACTIVE (ui, UI_SHOW_BALANCES,
181                      "checkbutton_show_balances");
182
183         BIND_ACTIVE (chat, CHAT_SHOW_SMILEYS,
184                      "checkbutton_show_smileys");
185         BIND_ACTIVE (chat, CHAT_SHOW_CONTACTS_IN_ROOMS,
186                      "checkbutton_show_contacts_in_rooms");
187
188         BIND_ACTIVE (call, CALL_ECHO_CANCELLATION,
189                      "call_echo_cancellation");
190
191         BIND_ACTIVE (loc, LOCATION_PUBLISH,
192                      "checkbutton_location_publish");
193         BIND_ACTIVE (loc, LOCATION_REDUCE_ACCURACY,
194                      "checkbutton_location_reduce_accuracy");
195
196         BIND_ACTIVE (logger, LOGGER_ENABLED,
197                      "checkbutton_logging");
198
199 #undef BIND_ACTIVE
200
201 #define BIND_SENSITIVE(schema, key, widget) \
202         g_settings_bind (priv->gsettings_##schema, EMPATHY_PREFS_##key, \
203                          gtk_builder_get_object (gui, widget), "sensitive", \
204                         G_SETTINGS_BIND_GET);
205         /* N.B. BIND_SENSITIVE() is in addition to the sensitivity setting of
206          *      BIND_ACTIVE() */
207         BIND_SENSITIVE (notify, NOTIFICATIONS_ENABLED,
208                         "checkbutton_notifications_disabled_away");
209         BIND_SENSITIVE (notify, NOTIFICATIONS_ENABLED,
210                         "checkbutton_notifications_focus");
211         BIND_SENSITIVE (notify, NOTIFICATIONS_ENABLED,
212                         "checkbutton_notifications_contact_signin");
213         BIND_SENSITIVE (notify, NOTIFICATIONS_ENABLED,
214                         "checkbutton_notifications_contact_signout");
215
216         BIND_SENSITIVE (sound, SOUNDS_ENABLED,
217                         "checkbutton_sounds_disabled_away");
218         BIND_SENSITIVE (sound, SOUNDS_ENABLED,
219                         "treeview_sounds");
220
221         BIND_SENSITIVE (loc, LOCATION_PUBLISH,
222                         "checkbutton_location_reduce_accuracy");
223
224 #undef BIND_SENSITIVE
225
226         g_settings_bind (priv->gsettings,
227                          EMPATHY_PREFS_AUTOCONNECT,
228                          gtk_builder_get_object (gui,
229                                                  "checkbutton_autoconnect"),
230                          "active",
231                          G_SETTINGS_BIND_DEFAULT);
232 }
233
234 static void
235 preferences_sound_cell_toggled_cb (GtkCellRendererToggle *toggle,
236                                    char *path_string,
237                                    EmpathyPreferences *preferences)
238 {
239         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
240         GtkTreePath *path;
241         gboolean instore;
242         GtkTreeIter iter;
243         GtkTreeView *view;
244         GtkTreeModel *model;
245         char *key;
246
247         view = GTK_TREE_VIEW (priv->treeview_sounds);
248         model = gtk_tree_view_get_model (view);
249
250         path = gtk_tree_path_new_from_string (path_string);
251
252         gtk_tree_model_get_iter (model, &iter, path);
253         gtk_tree_model_get (model, &iter, COL_SOUND_KEY, &key,
254                             COL_SOUND_ENABLED, &instore, -1);
255
256         instore ^= 1;
257
258         gtk_list_store_set (GTK_LIST_STORE (model), &iter,
259                             COL_SOUND_ENABLED, instore, -1);
260
261         g_settings_set_boolean (priv->gsettings_sound, key, instore);
262
263         g_free (key);
264         gtk_tree_path_free (path);
265 }
266
267 static void
268 preferences_sound_load (EmpathyPreferences *preferences)
269 {
270         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
271         guint i;
272         GtkTreeView *view;
273         GtkListStore *store;
274         GtkTreeIter iter;
275         gboolean set;
276
277         view = GTK_TREE_VIEW (priv->treeview_sounds);
278         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
279
280         for (i = 0; i < G_N_ELEMENTS (sound_entries); i++) {
281                 set = g_settings_get_boolean (priv->gsettings_sound,
282                                               sound_entries[i].key);
283
284                 gtk_list_store_insert_with_values (store, &iter, i,
285                                                    COL_SOUND_NAME, gettext (sound_entries[i].name),
286                                                    COL_SOUND_KEY, sound_entries[i].key,
287                                                    COL_SOUND_ENABLED, set, -1);
288         }
289 }
290
291 static void
292 preferences_sound_setup (EmpathyPreferences *preferences)
293 {
294         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
295         GtkTreeView *view;
296         GtkListStore *store;
297         GtkCellRenderer *renderer;
298         GtkTreeViewColumn *column;
299
300         view = GTK_TREE_VIEW (priv->treeview_sounds);
301
302         store = gtk_list_store_new (COL_SOUND_COUNT,
303                                     G_TYPE_BOOLEAN, /* enabled */
304                                     G_TYPE_STRING,  /* name */
305                                     G_TYPE_STRING); /* key */
306
307         gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
308
309         renderer = gtk_cell_renderer_toggle_new ();
310         g_signal_connect (renderer, "toggled",
311                           G_CALLBACK (preferences_sound_cell_toggled_cb),
312                           preferences);
313
314         column = gtk_tree_view_column_new ();
315         gtk_tree_view_column_pack_start (column, renderer, FALSE);
316         gtk_tree_view_column_add_attribute (column, renderer,
317                                             "active", COL_SOUND_ENABLED);
318
319         renderer = gtk_cell_renderer_text_new ();
320         gtk_tree_view_column_pack_start (column, renderer, FALSE);
321         gtk_tree_view_column_add_attribute (column, renderer,
322                                             "text", COL_SOUND_NAME);
323
324         gtk_tree_view_append_column (view, column);
325
326         gtk_tree_view_column_set_resizable (column, FALSE);
327         gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
328
329         g_object_unref (store);
330 }
331
332 static void
333 preferences_languages_setup (EmpathyPreferences *preferences)
334 {
335         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
336         GtkTreeView       *view;
337         GtkListStore      *store;
338         GtkTreeSelection  *selection;
339         GtkTreeViewColumn *column;
340         GtkCellRenderer   *renderer;
341         guint              col_offset;
342
343         view = GTK_TREE_VIEW (priv->treeview_spell_checker);
344
345         store = gtk_list_store_new (COL_LANG_COUNT,
346                                     G_TYPE_BOOLEAN,  /* enabled */
347                                     G_TYPE_STRING,   /* code */
348                                     G_TYPE_STRING);  /* name */
349
350         gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
351
352         selection = gtk_tree_view_get_selection (view);
353         gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
354
355         renderer = gtk_cell_renderer_toggle_new ();
356         g_signal_connect (renderer, "toggled",
357                           G_CALLBACK (preferences_languages_cell_toggled_cb),
358                           preferences);
359
360         column = gtk_tree_view_column_new_with_attributes (NULL, renderer,
361                                                            "active", COL_LANG_ENABLED,
362                                                            NULL);
363
364         gtk_tree_view_append_column (view, column);
365
366         renderer = gtk_cell_renderer_text_new ();
367         col_offset = gtk_tree_view_insert_column_with_attributes (view,
368                                                                   -1, _("Language"),
369                                                                   renderer,
370                                                                   "text", COL_LANG_NAME,
371                                                                   NULL);
372
373         g_object_set_data (G_OBJECT (renderer),
374                            "column", GINT_TO_POINTER (COL_LANG_NAME));
375
376         column = gtk_tree_view_get_column (view, col_offset - 1);
377         gtk_tree_view_column_set_sort_column_id (column, COL_LANG_NAME);
378         gtk_tree_view_column_set_resizable (column, FALSE);
379         gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
380
381         g_object_unref (store);
382 }
383
384 static void
385 preferences_languages_add (EmpathyPreferences *preferences)
386 {
387         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
388         GtkTreeView  *view;
389         GtkListStore *store;
390         GList        *codes, *l;
391
392         view = GTK_TREE_VIEW (priv->treeview_spell_checker);
393         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
394
395         codes = empathy_spell_get_language_codes ();
396
397         if (!codes) {
398                 gtk_widget_set_sensitive (priv->treeview_spell_checker, FALSE);
399         }
400
401         for (l = codes; l; l = l->next) {
402                 const gchar *code;
403                 const gchar *name;
404
405                 code = l->data;
406                 name = empathy_spell_get_language_name (code);
407                 if (!name) {
408                         continue;
409                 }
410
411                 gtk_list_store_insert_with_values (store, NULL, -1,
412                                     COL_LANG_CODE, code,
413                                     COL_LANG_NAME, name,
414                                     -1);
415         }
416
417         empathy_spell_free_language_codes (codes);
418 }
419
420 static void
421 preferences_languages_save (EmpathyPreferences *preferences)
422 {
423         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
424         GtkTreeView       *view;
425         GtkTreeModel      *model;
426
427         gchar             *languages = NULL;
428
429         view = GTK_TREE_VIEW (priv->treeview_spell_checker);
430         model = gtk_tree_view_get_model (view);
431
432         gtk_tree_model_foreach (model,
433                                 (GtkTreeModelForeachFunc) preferences_languages_save_foreach,
434                                 &languages);
435
436         /* if user selects no languages, we don't want spell check */
437         g_settings_set_boolean (priv->gsettings_chat,
438                                 EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
439                                 languages != NULL);
440
441         g_settings_set_string (priv->gsettings_chat,
442                                EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES,
443                                languages != NULL ? languages : "");
444
445         g_free (languages);
446 }
447
448 static gboolean
449 preferences_languages_save_foreach (GtkTreeModel  *model,
450                                     GtkTreePath   *path,
451                                     GtkTreeIter   *iter,
452                                     gchar        **languages)
453 {
454         gboolean  enabled;
455         gchar    *code;
456
457         if (!languages) {
458                 return TRUE;
459         }
460
461         gtk_tree_model_get (model, iter, COL_LANG_ENABLED, &enabled, -1);
462         if (!enabled) {
463                 return FALSE;
464         }
465
466         gtk_tree_model_get (model, iter, COL_LANG_CODE, &code, -1);
467         if (!code) {
468                 return FALSE;
469         }
470
471         if (!(*languages)) {
472                 *languages = g_strdup (code);
473         } else {
474                 gchar *str = *languages;
475                 *languages = g_strdup_printf ("%s,%s", str, code);
476                 g_free (str);
477         }
478
479         g_free (code);
480
481         return FALSE;
482 }
483
484 static void
485 preferences_languages_load (EmpathyPreferences *preferences)
486 {
487         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
488         GtkTreeView   *view;
489         GtkTreeModel  *model;
490         GList         *enabled_codes;
491
492         enabled_codes = empathy_spell_get_enabled_language_codes ();
493
494         g_settings_set_boolean (priv->gsettings_chat,
495                                 EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
496                                 enabled_codes != NULL);
497
498         if (enabled_codes == NULL)
499                 return;
500
501         view = GTK_TREE_VIEW (priv->treeview_spell_checker);
502         model = gtk_tree_view_get_model (view);
503
504         gtk_tree_model_foreach (model,
505                                 (GtkTreeModelForeachFunc) preferences_languages_load_foreach,
506                                 enabled_codes);
507
508         g_list_free (enabled_codes);
509 }
510
511 static gboolean
512 preferences_languages_load_foreach (GtkTreeModel  *model,
513                                     GtkTreePath   *path,
514                                     GtkTreeIter   *iter,
515                                     GList         *languages)
516 {
517         gchar    *code;
518         gboolean  found = FALSE;
519
520         if (!languages) {
521                 return TRUE;
522         }
523
524         gtk_tree_model_get (model, iter, COL_LANG_CODE, &code, -1);
525         if (!code) {
526                 return FALSE;
527         }
528
529         if (g_list_find_custom (languages, code, (GCompareFunc) strcmp)) {
530                 found = TRUE;
531         }
532
533         g_free (code);
534         gtk_list_store_set (GTK_LIST_STORE (model), iter, COL_LANG_ENABLED, found, -1);
535         return FALSE;
536 }
537
538 static void
539 preferences_languages_cell_toggled_cb (GtkCellRendererToggle *cell,
540                                        gchar                 *path_string,
541                                        EmpathyPreferences     *preferences)
542 {
543         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
544         GtkTreeView  *view;
545         GtkTreeModel *model;
546         GtkListStore *store;
547         GtkTreePath  *path;
548         GtkTreeIter   iter;
549         gboolean      enabled;
550
551         view = GTK_TREE_VIEW (priv->treeview_spell_checker);
552         model = gtk_tree_view_get_model (view);
553         store = GTK_LIST_STORE (model);
554
555         path = gtk_tree_path_new_from_string (path_string);
556
557         gtk_tree_model_get_iter (model, &iter, path);
558         gtk_tree_model_get (model, &iter, COL_LANG_ENABLED, &enabled, -1);
559
560         enabled ^= 1;
561
562         gtk_list_store_set (store, &iter, COL_LANG_ENABLED, enabled, -1);
563         gtk_tree_path_free (path);
564
565         preferences_languages_save (preferences);
566 }
567
568 static void
569 preferences_preview_theme_append_message (EmpathyThemeAdium *view,
570                                           EmpathyContact *sender,
571                                           EmpathyContact *receiver,
572                                           const gchar *text,
573                                           gboolean should_highlight)
574 {
575         EmpathyMessage *message;
576
577         message = g_object_new (EMPATHY_TYPE_MESSAGE,
578                 "sender", sender,
579                 "receiver", receiver,
580                 "body", text,
581                 NULL);
582
583         empathy_theme_adium_append_message (view, message, should_highlight);
584         g_object_unref (message);
585 }
586
587 static void
588 preferences_preview_theme_changed_cb (EmpathyThemeManager *manager,
589                                       EmpathyPreferences  *preferences)
590 {
591         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
592         EmpathyClientFactory *factory;
593         TpAccount *account;
594         EmpathyContact *juliet;
595         EmpathyContact *romeo;
596
597         DEBUG ("Theme changed, update preview widget");
598
599         if (priv->chat_theme_preview != NULL) {
600                 gtk_widget_destroy (GTK_WIDGET (priv->chat_theme_preview));
601         }
602         priv->chat_theme_preview = empathy_theme_manager_create_view (manager);
603         gtk_container_add (GTK_CONTAINER (priv->sw_chat_theme_preview),
604                            GTK_WIDGET (priv->chat_theme_preview));
605         gtk_widget_show (GTK_WIDGET (priv->chat_theme_preview));
606
607         /* FIXME: It is ugly to add a fake conversation like that.
608          * Would be cool if we could request a TplLogManager for a fake
609          * conversation */
610         factory = empathy_client_factory_dup ();
611
612         account = tp_simple_client_factory_ensure_account (
613                 TP_SIMPLE_CLIENT_FACTORY (factory),
614                 TP_ACCOUNT_OBJECT_PATH_BASE "cm/jabber/account", NULL, NULL);
615         juliet = g_object_new (EMPATHY_TYPE_CONTACT,
616                 "account", account,
617                 "id", "juliet",
618                 /* translators: Contact name for the chat theme preview */
619                 "alias", _("Juliet"),
620                 "is-user", FALSE,
621                 NULL);
622         romeo = g_object_new (EMPATHY_TYPE_CONTACT,
623                 "account", account,
624                 "id", "romeo",
625                 /* translators: Contact name for the chat theme preview */
626                 "alias", _("Romeo"),
627                 "is-user", TRUE,
628                 NULL);
629
630         preferences_preview_theme_append_message (priv->chat_theme_preview,
631                 /* translators: Quote from Romeo & Julier, for chat theme preview */
632                 juliet, romeo, _("O Romeo, Romeo, wherefore art thou Romeo?"),
633                 TRUE /* this message mentions Romeo */);
634         preferences_preview_theme_append_message (priv->chat_theme_preview,
635                 /* translators: Quote from Romeo & Julier, for chat theme preview */
636                 juliet, romeo, _("Deny thy father and refuse thy name;"), FALSE);
637         preferences_preview_theme_append_message (priv->chat_theme_preview,
638                 /* translators: Quote from Romeo & Julier, for chat theme preview */
639                 juliet, romeo, _("Or if thou wilt not, be but sworn my love"), FALSE);
640         preferences_preview_theme_append_message (priv->chat_theme_preview,
641                 /* translators: Quote from Romeo & Julier, for chat theme preview */
642                 juliet, romeo, _("And I'll no longer be a Capulet."), FALSE);
643         preferences_preview_theme_append_message (priv->chat_theme_preview,
644                 /* translators: Quote from Romeo & Julier, for chat theme preview */
645                 romeo, juliet, _("Shall I hear more, or shall I speak at this?"), FALSE);
646
647         /* translators: Quote from Romeo & Julier, for chat theme preview */
648         empathy_theme_adium_append_event (priv->chat_theme_preview, _("Juliet has disconnected"));
649
650         g_object_unref (juliet);
651         g_object_unref (romeo);
652         g_object_unref (account);
653         g_object_unref (factory);
654 }
655
656 static void
657 preferences_theme_variant_changed_cb (GtkComboBox        *combo,
658                                       EmpathyPreferences *preferences)
659 {
660         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
661         GtkTreeIter   iter;
662
663         if (gtk_combo_box_get_active_iter (combo, &iter)) {
664                 GtkTreeModel *model;
665                 gchar        *name;
666
667                 model = gtk_combo_box_get_model (combo);
668                 gtk_tree_model_get (model, &iter,
669                                     COL_VARIANT_NAME, &name,
670                                     -1);
671
672                 g_settings_set_string (priv->gsettings_chat,
673                                        EMPATHY_PREFS_CHAT_THEME_VARIANT,
674                                        name);
675
676                 g_free (name);
677         }
678 }
679
680 static void
681 preferences_theme_variant_notify_cb (GSettings   *gsettings,
682                                      const gchar *key,
683                                      gpointer     user_data)
684 {
685         EmpathyPreferences *preferences = user_data;
686         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
687         GtkComboBox        *combo;
688         gchar              *conf_name;
689         GtkTreeModel       *model;
690         GtkTreeIter         iter;
691         GtkTreeIter         default_iter;
692         gboolean            found_default = FALSE;
693         gboolean            found = FALSE;
694         gboolean            ok;
695
696         conf_name = g_settings_get_string (gsettings, EMPATHY_PREFS_CHAT_THEME_VARIANT);
697         combo = GTK_COMBO_BOX (priv->combobox_chat_theme_variant);
698         model = gtk_combo_box_get_model (combo);
699
700         for (ok = gtk_tree_model_get_iter_first (model, &iter);
701              ok && !found;
702              ok = gtk_tree_model_iter_next (model, &iter)) {
703                 gchar *name;
704                 gboolean is_default;
705
706                 gtk_tree_model_get (model, &iter,
707                                     COL_VARIANT_NAME, &name,
708                                     COL_VARIANT_DEFAULT, &is_default,
709                                     -1);
710
711                 if (!tp_strdiff (name, conf_name)) {
712                         found = TRUE;
713                         gtk_combo_box_set_active_iter (combo, &iter);
714                 }
715                 if (is_default) {
716                         found_default = TRUE;
717                         default_iter = iter;
718                 }
719
720                 g_free (name);
721         }
722
723         /* Fallback to the first one. */
724         if (!found) {
725                 if (found_default) {
726                         gtk_combo_box_set_active_iter (combo, &default_iter);
727                 } else if (gtk_tree_model_get_iter_first (model, &iter)) {
728                         gtk_combo_box_set_active_iter (combo, &iter);
729                 }
730         }
731
732         g_free (conf_name);
733 }
734
735 /* return TRUE if we added at least one variant */
736 static gboolean
737 preferences_theme_variants_fill (EmpathyPreferences *preferences,
738                                  GHashTable         *info)
739 {
740         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
741         GtkTreeModel *model;
742         GtkListStore *store;
743         GPtrArray    *variants;
744         const gchar  *default_variant;
745         guint         i;
746         gboolean      result = FALSE;
747
748         model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->combobox_chat_theme_variant));
749         store = GTK_LIST_STORE (model);
750         gtk_list_store_clear (store);
751
752         variants = empathy_adium_info_get_available_variants (info);
753         default_variant = empathy_adium_info_get_default_variant (info);
754         for (i = 0; i < variants->len; i++) {
755                 gchar *name = g_ptr_array_index (variants, i);
756
757                 gtk_list_store_insert_with_values (store, NULL, -1,
758                         COL_VARIANT_NAME, name,
759                         COL_VARIANT_DEFAULT, !tp_strdiff (name, default_variant),
760                         -1);
761
762                 result = TRUE;
763         }
764
765         /* Select the variant from the GSetting key */
766         preferences_theme_variant_notify_cb (priv->gsettings_chat,
767                                              EMPATHY_PREFS_CHAT_THEME_VARIANT,
768                                              preferences);
769
770         return result;
771 }
772
773 static void
774 preferences_theme_variants_setup (EmpathyPreferences *preferences)
775 {
776         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
777         GtkComboBox   *combo;
778         GtkCellLayout *cell_layout;
779         GtkCellRenderer *renderer;
780         GtkListStore  *store;
781
782         combo = GTK_COMBO_BOX (priv->combobox_chat_theme_variant);
783         cell_layout = GTK_CELL_LAYOUT (combo);
784
785         /* Create the model */
786         store = gtk_list_store_new (COL_VARIANT_COUNT,
787                                     G_TYPE_STRING,      /* name */
788                                     G_TYPE_BOOLEAN);    /* is default */
789         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
790                 COL_VARIANT_NAME, GTK_SORT_ASCENDING);
791
792         /* Add cell renderer */
793         renderer = gtk_cell_renderer_text_new ();
794         gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
795         gtk_cell_layout_set_attributes (cell_layout, renderer,
796                 "text", COL_VARIANT_NAME, NULL);
797
798         gtk_combo_box_set_model (combo, GTK_TREE_MODEL (store));
799         g_object_unref (store);
800
801         g_signal_connect (combo, "changed",
802                           G_CALLBACK (preferences_theme_variant_changed_cb),
803                           preferences);
804
805         /* Track changes of the GSetting key */
806         g_signal_connect (priv->gsettings_chat,
807                           "changed::" EMPATHY_PREFS_CHAT_THEME_VARIANT,
808                           G_CALLBACK (preferences_theme_variant_notify_cb),
809                           preferences);
810 }
811
812 static void
813 preferences_theme_changed_cb (GtkComboBox        *combo,
814                               EmpathyPreferences *preferences)
815 {
816         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
817         GtkTreeIter   iter;
818
819         if (gtk_combo_box_get_active_iter (combo, &iter)) {
820                 GtkTreeModel *model;
821                 gchar        *name;
822                 GHashTable   *info;
823                 gboolean variant;
824
825                 model = gtk_combo_box_get_model (combo);
826                 gtk_tree_model_get (model, &iter,
827                                     COL_THEME_ADIUM_NAME, &name,
828                                     COL_THEME_ADIUM_INFO, &info,
829                                     -1);
830
831                 g_settings_set_string (priv->gsettings_chat,
832                                        EMPATHY_PREFS_CHAT_THEME,
833                                        name);
834
835                 variant = preferences_theme_variants_fill (preferences, info);
836                 gtk_widget_set_visible (priv->hbox_chat_theme_variant, variant);
837
838                 g_free (name);
839                 tp_clear_pointer (&info, g_hash_table_unref);
840         }
841 }
842
843 static void
844 preferences_theme_notify_cb (GSettings   *gsettings,
845                              const gchar *key,
846                              gpointer     user_data)
847 {
848         EmpathyPreferences *preferences = user_data;
849         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
850         GtkComboBox        *combo;
851         gchar              *theme;
852         GtkTreeModel       *model;
853         GtkTreeIter         iter;
854         gboolean            found = FALSE;
855         gboolean            ok;
856
857         theme = g_settings_get_string (gsettings, key);
858
859         combo = GTK_COMBO_BOX (priv->combobox_chat_theme);
860         model = gtk_combo_box_get_model (combo);
861         for (ok = gtk_tree_model_get_iter_first (model, &iter);
862              ok && !found;
863              ok = gtk_tree_model_iter_next (model, &iter)) {
864                 gchar *name;
865
866                 gtk_tree_model_get (model, &iter,
867                                     COL_THEME_ADIUM_NAME, &name,
868                                     -1);
869
870                 if (!tp_strdiff (name, theme)) {
871                         found = TRUE;
872                         gtk_combo_box_set_active_iter (combo, &iter);
873                 }
874
875                 g_free (name);
876         }
877
878         /* Fallback to the first one. */
879         if (!found) {
880                 if (gtk_tree_model_get_iter_first (model, &iter)) {
881                         gtk_combo_box_set_active_iter (combo, &iter);
882                 }
883         }
884
885         g_free (theme);
886 }
887
888 static void
889 preferences_themes_setup (EmpathyPreferences *preferences)
890 {
891         EmpathyPreferencesPriv *priv = GET_PRIV (preferences);
892         GtkComboBox   *combo;
893         GtkCellLayout *cell_layout;
894         GtkCellRenderer *renderer;
895         GtkListStore  *store;
896         GList         *adium_themes;
897
898         preferences_theme_variants_setup (preferences);
899
900         combo = GTK_COMBO_BOX (priv->combobox_chat_theme);
901         cell_layout = GTK_CELL_LAYOUT (combo);
902
903         /* Create the model */
904         store = gtk_list_store_new (COL_THEME_COUNT,
905                                     G_TYPE_STRING,      /* Display name */
906                                     G_TYPE_STRING,      /* Adium theme path */
907                                     G_TYPE_HASH_TABLE); /* Adium theme info */
908         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
909                 COL_THEME_VISIBLE_NAME, GTK_SORT_ASCENDING);
910
911         /* Fill the model */
912         adium_themes = empathy_theme_manager_get_adium_themes ();
913         while (adium_themes != NULL) {
914                 GHashTable *info;
915                 const gchar *visible_name;
916                 gchar *name;
917
918                 info = adium_themes->data;
919                 visible_name = tp_asv_get_string (info, "CFBundleName");
920                 name = empathy_theme_manager_dup_theme_name_from_path (
921                         tp_asv_get_string (info, "path"));
922
923                 if (visible_name != NULL && name != NULL) {
924                         gtk_list_store_insert_with_values (store, NULL, -1,
925                                 COL_THEME_VISIBLE_NAME, visible_name,
926                                 COL_THEME_ADIUM_NAME, name,
927                                 COL_THEME_ADIUM_INFO, info,
928                                 -1);
929                 }
930                 g_hash_table_unref (info);
931                 g_free (name);
932                 adium_themes = g_list_delete_link (adium_themes, adium_themes);
933         }
934
935         /* Add cell renderer */
936         renderer = gtk_cell_renderer_text_new ();
937         gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
938         gtk_cell_layout_set_attributes (cell_layout, renderer,
939                 "text", COL_THEME_VISIBLE_NAME, NULL);
940
941         gtk_combo_box_set_model (combo, GTK_TREE_MODEL (store));
942         g_object_unref (store);
943
944         g_signal_connect (combo, "changed",
945                           G_CALLBACK (preferences_theme_changed_cb),
946                           preferences);
947
948         /* Select the theme from the GSetting key and track changes */
949         preferences_theme_notify_cb (priv->gsettings_chat,
950                                      EMPATHY_PREFS_CHAT_THEME,
951                                      preferences);
952
953         g_signal_connect (priv->gsettings_chat,
954                           "changed::" EMPATHY_PREFS_CHAT_THEME,
955                           G_CALLBACK (preferences_theme_notify_cb),
956                           preferences);
957 }
958
959 static void
960 empathy_preferences_response (GtkDialog *widget,
961                               gint response)
962 {
963         gtk_widget_destroy (GTK_WIDGET (widget));
964 }
965
966 static void
967 empathy_preferences_finalize (GObject *self)
968 {
969         EmpathyPreferencesPriv *priv = GET_PRIV (self);
970
971         g_object_unref (priv->theme_manager);
972
973         g_object_unref (priv->gsettings);
974         g_object_unref (priv->gsettings_chat);
975         g_object_unref (priv->gsettings_call);
976         g_object_unref (priv->gsettings_loc);
977         g_object_unref (priv->gsettings_notify);
978         g_object_unref (priv->gsettings_sound);
979         g_object_unref (priv->gsettings_ui);
980         g_object_unref (priv->gsettings_logger);
981
982         G_OBJECT_CLASS (empathy_preferences_parent_class)->finalize (self);
983 }
984
985 static void
986 empathy_preferences_class_init (EmpathyPreferencesClass *klass)
987 {
988         GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
989         GObjectClass *object_class = G_OBJECT_CLASS (klass);
990
991         dialog_class->response = empathy_preferences_response;
992
993         object_class->finalize = empathy_preferences_finalize;
994
995         g_type_class_add_private (object_class,
996                                   sizeof (EmpathyPreferencesPriv));
997 }
998
999 static void
1000 empathy_preferences_init (EmpathyPreferences *preferences)
1001 {
1002         EmpathyPreferencesPriv    *priv;
1003         GtkBuilder                *gui;
1004         gchar                     *filename;
1005         GtkWidget                 *page;
1006
1007         priv = preferences->priv = G_TYPE_INSTANCE_GET_PRIVATE (preferences,
1008                         EMPATHY_TYPE_PREFERENCES, EmpathyPreferencesPriv);
1009
1010         gtk_dialog_add_button (GTK_DIALOG (preferences),
1011                                GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
1012
1013         gtk_container_set_border_width (GTK_CONTAINER (preferences), 5);
1014         gtk_window_set_title (GTK_WINDOW (preferences), _("Preferences"));
1015         gtk_window_set_role (GTK_WINDOW (preferences), "preferences");
1016         gtk_window_set_position (GTK_WINDOW (preferences),
1017                                  GTK_WIN_POS_CENTER_ON_PARENT);
1018         gtk_window_set_icon_name (GTK_WINDOW (preferences), "preferences-desktop");
1019
1020         filename = empathy_file_lookup ("empathy-preferences.ui", "src");
1021         gui = tpaw_builder_get_file (filename,
1022                 "notebook", &priv->notebook,
1023                 "vbox_chat_theme", &priv->vbox_chat_theme,
1024                 "combobox_chat_theme", &priv->combobox_chat_theme,
1025                 "combobox_chat_theme_variant", &priv->combobox_chat_theme_variant,
1026                 "hbox_chat_theme_variant", &priv->hbox_chat_theme_variant,
1027                 "sw_chat_theme_preview", &priv->sw_chat_theme_preview,
1028                 "treeview_sounds", &priv->treeview_sounds,
1029                 "treeview_spell_checker", &priv->treeview_spell_checker,
1030                 "label_general_behavior", &priv->label_general_behavior,
1031                 "checkbutton_events_notif_area", &priv->checkbutton_events_notif_area,
1032                 "checkbutton_autoconnect", &priv->checkbutton_autoconnect,
1033                 NULL);
1034         g_free (filename);
1035
1036         gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (preferences))), priv->notebook);
1037         gtk_widget_show (priv->notebook);
1038
1039         priv->gsettings = g_settings_new (EMPATHY_PREFS_SCHEMA);
1040         priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
1041         priv->gsettings_call = g_settings_new (EMPATHY_PREFS_CALL_SCHEMA);
1042         priv->gsettings_loc = g_settings_new (EMPATHY_PREFS_LOCATION_SCHEMA);
1043         priv->gsettings_notify = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1044         priv->gsettings_sound = g_settings_new (EMPATHY_PREFS_SOUNDS_SCHEMA);
1045         priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1046         priv->gsettings_logger = g_settings_new (EMPATHY_PREFS_LOGGER_SCHEMA);
1047
1048         /* Create chat theme preview, and track changes */
1049         priv->theme_manager = empathy_theme_manager_dup_singleton ();
1050         tp_g_signal_connect_object (priv->theme_manager, "theme-changed",
1051                           G_CALLBACK (preferences_preview_theme_changed_cb),
1052                           preferences, 0);
1053         preferences_preview_theme_changed_cb (priv->theme_manager, preferences);
1054
1055         preferences_themes_setup (preferences);
1056
1057         preferences_setup_widgets (preferences, gui);
1058
1059         preferences_languages_setup (preferences);
1060         preferences_languages_add (preferences);
1061         preferences_languages_load (preferences);
1062
1063         preferences_sound_setup (preferences);
1064         preferences_sound_load (preferences);
1065
1066         g_object_unref (gui);
1067
1068         if (empathy_spell_supported ()) {
1069                 page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), EMPATHY_PREFERENCES_TAB_SPELL);
1070                 gtk_widget_show (page);
1071         }
1072
1073         page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), EMPATHY_PREFERENCES_TAB_LOCATION);
1074 #ifdef HAVE_GEOCLUE
1075         gtk_widget_show (page);
1076 #else
1077         gtk_widget_hide (page);
1078 #endif
1079 }
1080
1081 static EmpathyPreferencesTab
1082 empathy_preferences_tab_from_string (const gchar *str)
1083 {
1084   guint i;
1085
1086   for (i = 0; i < G_N_ELEMENTS (empathy_preferences_tabs); i++)
1087     {
1088       if (!tp_strdiff (str, empathy_preferences_tabs[i]))
1089         return i;
1090     }
1091
1092   g_warn_if_reached ();
1093   return -1;
1094 }
1095
1096 const gchar *
1097 empathy_preferences_tab_to_string (EmpathyPreferencesTab tab)
1098 {
1099   g_return_val_if_fail (tab < G_N_ELEMENTS (empathy_preferences_tabs), NULL);
1100
1101   return empathy_preferences_tabs[tab];
1102 }
1103
1104 GtkWidget *
1105 empathy_preferences_new (GtkWindow *parent,
1106                          gboolean  shell_running)
1107 {
1108         GtkWidget              *self;
1109         EmpathyPreferencesPriv *priv;
1110         GtkWidget              *notif_page;
1111
1112         g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
1113
1114         self = g_object_new (EMPATHY_TYPE_PREFERENCES, NULL);
1115
1116         if (parent != NULL) {
1117                 gtk_window_set_transient_for (GTK_WINDOW (self),
1118                                               parent);
1119         }
1120
1121         /* when running in Gnome Shell we must hide these options since they
1122          * are meaningless in that context:
1123          * - General->Behavior label
1124          * - 'Display incoming events in the notification area' (General->Behavior)
1125          * - 'Automatically connect at startup' (General->Behavior)
1126          * - 'Notifications' tab
1127          */
1128         priv = GET_PRIV (self);
1129         if (shell_running) {
1130                 /* Behavior */
1131                 gtk_widget_hide (priv->label_general_behavior);
1132                 gtk_widget_hide (priv->checkbutton_events_notif_area);
1133                 gtk_widget_hide (priv->checkbutton_autoconnect);
1134
1135                 /* Notifications tab */
1136                 notif_page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook),
1137                                                         EMPATHY_PREFERENCES_TAB_NOTIFICATIONS);
1138                 gtk_widget_hide (notif_page);
1139         }
1140
1141         return self;
1142 }
1143
1144 void
1145 empathy_preferences_show_tab (EmpathyPreferences *self,
1146                               const gchar *page)
1147 {
1148         EmpathyPreferencesPriv *priv = GET_PRIV (self);
1149
1150         gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
1151                                        empathy_preferences_tab_from_string (page));
1152 }