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