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