]> git.0d.be Git - empathy.git/blob - src/empathy-main-window.c
Add blocked contacts dialog to Empathy main window menu
[empathy.git] / src / empathy-main-window.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2002-2007 Imendio AB
4  * Copyright (C) 2007-2010 Collabora Ltd.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301  USA
20  *
21  * Authors: Xavier Claessens <xclaesse@gmail.com>
22  *          Danielle Madeley <danielle.madeley@collabora.co.uk>
23  */
24
25 #include <config.h>
26
27 #include <sys/stat.h>
28 #include <gtk/gtk.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <glib/gi18n.h>
31
32 #include <telepathy-glib/account-manager.h>
33 #include <telepathy-glib/util.h>
34 #include <folks/folks.h>
35
36 #include <libempathy/empathy-contact.h>
37 #include <libempathy/empathy-utils.h>
38 #include <libempathy/empathy-dispatcher.h>
39 #include <libempathy/empathy-chatroom-manager.h>
40 #include <libempathy/empathy-chatroom.h>
41 #include <libempathy/empathy-contact-list.h>
42 #include <libempathy/empathy-contact-manager.h>
43 #include <libempathy/empathy-gsettings.h>
44 #include <libempathy/empathy-individual-manager.h>
45 #include <libempathy/empathy-gsettings.h>
46 #include <libempathy/empathy-status-presets.h>
47 #include <libempathy/empathy-tp-contact-factory.h>
48
49 #include <libempathy-gtk/empathy-contact-dialogs.h>
50 #include <libempathy-gtk/empathy-contact-list-store.h>
51 #include <libempathy-gtk/empathy-contact-list-view.h>
52 #include <libempathy-gtk/empathy-live-search.h>
53 #include <libempathy-gtk/empathy-contact-blocking-dialog.h>
54 #include <libempathy-gtk/empathy-contact-search-dialog.h>
55 #include <libempathy-gtk/empathy-geometry.h>
56 #include <libempathy-gtk/empathy-gtk-enum-types.h>
57 #include <libempathy-gtk/empathy-individual-dialogs.h>
58 #include <libempathy-gtk/empathy-individual-store.h>
59 #include <libempathy-gtk/empathy-individual-view.h>
60 #include <libempathy-gtk/empathy-new-message-dialog.h>
61 #include <libempathy-gtk/empathy-new-call-dialog.h>
62 #include <libempathy-gtk/empathy-log-window.h>
63 #include <libempathy-gtk/empathy-presence-chooser.h>
64 #include <libempathy-gtk/empathy-sound-manager.h>
65 #include <libempathy-gtk/empathy-ui-utils.h>
66
67 #include "empathy-accounts-dialog.h"
68 #include "empathy-chat-manager.h"
69 #include "empathy-main-window.h"
70 #include "empathy-preferences.h"
71 #include "empathy-about-dialog.h"
72 #include "empathy-debug-window.h"
73 #include "empathy-new-chatroom-dialog.h"
74 #include "empathy-map-view.h"
75 #include "empathy-chatrooms-window.h"
76 #include "empathy-event-manager.h"
77 #include "empathy-ft-manager.h"
78 #include "empathy-migrate-butterfly-logs.h"
79
80 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
81 #include <libempathy/empathy-debug.h>
82
83 /* Flashing delay for icons (milliseconds). */
84 #define FLASH_TIMEOUT 500
85
86 /* Minimum width of roster window if something goes wrong. */
87 #define MIN_WIDTH 50
88
89 /* Accels (menu shortcuts) can be configured and saved */
90 #define ACCELS_FILENAME "accels.txt"
91
92 /* Name in the geometry file */
93 #define GEOMETRY_NAME "main-window"
94
95 enum {
96         PAGE_CONTACT_LIST = 0,
97         PAGE_NO_MATCH
98 };
99
100 G_DEFINE_TYPE (EmpathyMainWindow, empathy_main_window, GTK_TYPE_WINDOW);
101
102 #define GET_PRIV(self) ((EmpathyMainWindowPriv *)((EmpathyMainWindow *) self)->priv)
103
104 struct _EmpathyMainWindowPriv {
105         EmpathyContactList      *contact_manager;
106         EmpathyIndividualStore  *individual_store;
107         EmpathyIndividualView   *individual_view;
108         TpAccountManager        *account_manager;
109         EmpathyChatroomManager  *chatroom_manager;
110         EmpathyEventManager     *event_manager;
111         EmpathySoundManager     *sound_mgr;;
112         guint                    flash_timeout_id;
113         gboolean                 flash_on;
114         gboolean                 empty;
115
116         GSettings              *gsettings_ui;
117         GSettings              *gsettings_contacts;
118
119         GtkWidget              *preferences;
120         GtkWidget              *main_vbox;
121         GtkWidget              *throbber;
122         GtkWidget              *throbber_tool_item;
123         GtkWidget              *presence_toolbar;
124         GtkWidget              *presence_chooser;
125         GtkWidget              *errors_vbox;
126         GtkWidget              *auth_vbox;
127         GtkWidget              *search_bar;
128         GtkWidget              *notebook;
129         GtkWidget              *no_entry_label;
130
131         GtkToggleAction        *show_protocols;
132         GtkRadioAction         *sort_by_name;
133         GtkRadioAction         *sort_by_status;
134         GtkRadioAction         *normal_with_avatars;
135         GtkRadioAction         *normal_size;
136         GtkRadioAction         *compact_size;
137
138         GtkUIManager           *ui_manager;
139         GtkAction              *view_history;
140         GtkAction              *room_join_favorites;
141         GtkWidget              *room_menu;
142         GtkWidget              *room_separator;
143         GtkWidget              *edit_context;
144         GtkWidget              *edit_context_separator;
145
146         guint                   size_timeout_id;
147
148         /* reffed TpAccount* => visible GtkInfoBar* */
149         GHashTable             *errors;
150
151         /* EmpathyEvent* => visible GtkInfoBar* */
152         GHashTable             *auths;
153
154         /* stores a mapping from TpAccount to Handler ID to prevent
155          * to listen more than once to the status-changed signal */
156         GHashTable             *status_changed_handlers;
157
158         /* Actions that are enabled when there are connected accounts */
159         GList                  *actions_connected;
160
161         /* The idle event source to migrate butterfly's logs */
162         guint butterfly_log_migration_members_changed_id;
163 };
164
165 static void
166 main_window_flash_stop (EmpathyMainWindow *window)
167 {
168         EmpathyMainWindowPriv *priv = GET_PRIV (window);
169
170         if (priv->flash_timeout_id == 0) {
171                 return;
172         }
173
174         DEBUG ("Stop flashing");
175         g_source_remove (priv->flash_timeout_id);
176         priv->flash_timeout_id = 0;
177         priv->flash_on = FALSE;
178 }
179
180 typedef struct {
181         EmpathyEvent       *event;
182         gboolean            on;
183         EmpathyMainWindow  *window;
184 } FlashForeachData;
185
186 static gboolean
187 main_window_flash_foreach (GtkTreeModel *model,
188                            GtkTreePath  *path,
189                            GtkTreeIter  *iter,
190                            gpointer      user_data)
191 {
192         FlashForeachData *data = (FlashForeachData *) user_data;
193         FolksIndividual *individual;
194         EmpathyContact   *contact;
195         const gchar      *icon_name;
196         GtkTreePath      *parent_path = NULL;
197         GtkTreeIter       parent_iter;
198         GdkPixbuf        *pixbuf = NULL;
199
200         gtk_tree_model_get (model, iter,
201                             EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL,
202                                 &individual,
203                             -1);
204
205         if (individual == NULL)
206                 return FALSE;
207
208         contact = empathy_contact_dup_from_folks_individual (individual);
209         if (contact != data->event->contact) {
210                 tp_clear_object (&contact);
211                 return FALSE;
212         }
213
214         if (data->on) {
215                 icon_name = data->event->icon_name;
216                 pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
217         } else {
218                 pixbuf = empathy_individual_store_get_individual_status_icon (
219                                                 GET_PRIV (data->window)->individual_store,
220                                                 individual);
221         }
222
223         gtk_tree_store_set (GTK_TREE_STORE (model), iter,
224                             EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS, pixbuf,
225                             -1);
226
227         /* To make sure the parent is shown correctly, we emit
228          * the row-changed signal on the parent so it prompts
229          * it to be refreshed by the filter func.
230          */
231         if (gtk_tree_model_iter_parent (model, &parent_iter, iter)) {
232                 parent_path = gtk_tree_model_get_path (model, &parent_iter);
233         }
234         if (parent_path) {
235                 gtk_tree_model_row_changed (model, parent_path, &parent_iter);
236                 gtk_tree_path_free (parent_path);
237         }
238
239         g_object_unref (individual);
240         tp_clear_object (&contact);
241
242         return FALSE;
243 }
244
245 static gboolean
246 main_window_flash_cb (EmpathyMainWindow *window)
247 {
248         EmpathyMainWindowPriv *priv = GET_PRIV (window);
249         GtkTreeModel     *model;
250         GSList           *events, *l;
251         gboolean          found_event = FALSE;
252         FlashForeachData  data;
253
254         priv->flash_on = !priv->flash_on;
255         data.on = priv->flash_on;
256         model = GTK_TREE_MODEL (priv->individual_store);
257
258         events = empathy_event_manager_get_events (priv->event_manager);
259         for (l = events; l; l = l->next) {
260                 data.event = l->data;
261                 data.window = window;
262                 if (!data.event->contact || !data.event->must_ack) {
263                         continue;
264                 }
265
266                 found_event = TRUE;
267                 gtk_tree_model_foreach (model,
268                                         main_window_flash_foreach,
269                                         &data);
270         }
271
272         if (!found_event) {
273                 main_window_flash_stop (window);
274         }
275
276         return TRUE;
277 }
278
279 static void
280 main_window_flash_start (EmpathyMainWindow *window)
281 {
282         EmpathyMainWindowPriv *priv = GET_PRIV (window);
283
284         if (priv->flash_timeout_id != 0) {
285                 return;
286         }
287
288         DEBUG ("Start flashing");
289         priv->flash_timeout_id = g_timeout_add (FLASH_TIMEOUT,
290                                                 (GSourceFunc) main_window_flash_cb,
291                                                 window);
292         main_window_flash_cb (window);
293 }
294
295 static void
296 main_window_remove_auth (EmpathyMainWindow *window,
297                          EmpathyEvent      *event)
298 {
299         EmpathyMainWindowPriv *priv = GET_PRIV (window);
300         GtkWidget *error_widget;
301
302         error_widget = g_hash_table_lookup (priv->auths, event);
303         if (error_widget != NULL) {
304                 gtk_widget_destroy (error_widget);
305                 g_hash_table_remove (priv->auths, event);
306         }
307 }
308
309 static void
310 main_window_auth_add_clicked_cb (GtkButton         *button,
311                                  EmpathyMainWindow *window)
312 {
313         EmpathyEvent *event;
314
315         event = g_object_get_data (G_OBJECT (button), "event");
316
317         empathy_event_approve (event);
318
319         main_window_remove_auth (window, event);
320 }
321
322 static void
323 main_window_auth_close_clicked_cb (GtkButton         *button,
324                                    EmpathyMainWindow *window)
325 {
326         EmpathyEvent *event;
327
328         event = g_object_get_data (G_OBJECT (button), "event");
329
330         empathy_event_decline (event);
331         main_window_remove_auth (window, event);
332 }
333
334 static void
335 main_window_auth_display (EmpathyMainWindow *window,
336                           EmpathyEvent      *event)
337 {
338         EmpathyMainWindowPriv *priv = GET_PRIV (window);
339         TpAccount *account = event->account;
340         GtkWidget *info_bar;
341         GtkWidget *content_area;
342         GtkWidget *image;
343         GtkWidget *label;
344         GtkWidget *add_button;
345         GtkWidget *close_button;
346         GtkWidget *action_area;
347         GtkWidget *action_table;
348         const gchar *icon_name;
349         gchar *str;
350
351         if (g_hash_table_lookup (priv->auths, event) != NULL) {
352                 return;
353         }
354
355         info_bar = gtk_info_bar_new ();
356         gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_QUESTION);
357
358         gtk_widget_set_no_show_all (info_bar, TRUE);
359         gtk_box_pack_start (GTK_BOX (priv->auth_vbox), info_bar, FALSE, TRUE, 0);
360         gtk_widget_show (info_bar);
361
362         icon_name = tp_account_get_icon_name (account);
363         image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR);
364         gtk_widget_show (image);
365
366         str = g_markup_printf_escaped ("<b>%s</b>\n%s",
367                                        tp_account_get_display_name (account),
368                                        _("Password required"));
369
370         label = gtk_label_new (str);
371         gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
372         gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
373         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
374         gtk_widget_show (label);
375
376         g_free (str);
377
378         content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
379         gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0);
380         gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0);
381
382         image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
383         add_button = gtk_button_new ();
384         gtk_button_set_image (GTK_BUTTON (add_button), image);
385         gtk_widget_set_tooltip_text (add_button, _("Provide Password"));
386         gtk_widget_show (add_button);
387
388         image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON);
389         close_button = gtk_button_new ();
390         gtk_button_set_image (GTK_BUTTON (close_button), image);
391         gtk_widget_set_tooltip_text (close_button, _("Disconnect"));
392         gtk_widget_show (close_button);
393
394         action_table = gtk_table_new (1, 2, FALSE);
395         gtk_table_set_col_spacings (GTK_TABLE (action_table), 6);
396         gtk_widget_show (action_table);
397
398         action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar));
399         gtk_box_pack_start (GTK_BOX (action_area), action_table, FALSE, FALSE, 0);
400
401         gtk_table_attach (GTK_TABLE (action_table), add_button, 0, 1, 0, 1,
402                           (GtkAttachOptions) (GTK_SHRINK),
403                           (GtkAttachOptions) (GTK_SHRINK), 0, 0);
404         gtk_table_attach (GTK_TABLE (action_table), close_button, 1, 2, 0, 1,
405                           (GtkAttachOptions) (GTK_SHRINK),
406                           (GtkAttachOptions) (GTK_SHRINK), 0, 0);
407
408         g_object_set_data_full (G_OBJECT (info_bar),
409                                 "event", event, NULL);
410         g_object_set_data_full (G_OBJECT (add_button),
411                                 "event", event, NULL);
412         g_object_set_data_full (G_OBJECT (close_button),
413                                 "event", event, NULL);
414
415         g_signal_connect (add_button, "clicked",
416                           G_CALLBACK (main_window_auth_add_clicked_cb),
417                           window);
418         g_signal_connect (close_button, "clicked",
419                           G_CALLBACK (main_window_auth_close_clicked_cb),
420                           window);
421
422         gtk_widget_show (priv->auth_vbox);
423
424         g_hash_table_insert (priv->auths, event, info_bar);
425 }
426
427 static void
428 main_window_event_added_cb (EmpathyEventManager *manager,
429                             EmpathyEvent        *event,
430                             EmpathyMainWindow   *window)
431 {
432         if (event->contact) {
433                 main_window_flash_start (window);
434         } else if (event->type == EMPATHY_EVENT_TYPE_AUTH) {
435                 main_window_auth_display (window, event);
436         }
437 }
438
439 static void
440 main_window_event_removed_cb (EmpathyEventManager *manager,
441                               EmpathyEvent        *event,
442                               EmpathyMainWindow   *window)
443 {
444         EmpathyMainWindowPriv *priv = GET_PRIV (window);
445         FlashForeachData data;
446
447         if (event->type == EMPATHY_EVENT_TYPE_AUTH) {
448                 main_window_remove_auth (window, event);
449                 return;
450         }
451
452         if (!event->contact) {
453                 return;
454         }
455
456         data.on = FALSE;
457         data.event = event;
458         data.window = window;
459         gtk_tree_model_foreach (GTK_TREE_MODEL (priv->individual_store),
460                                 main_window_flash_foreach,
461                                 &data);
462 }
463
464 static void
465 main_window_row_activated_cb (EmpathyContactListView *view,
466                               GtkTreePath            *path,
467                               GtkTreeViewColumn      *col,
468                               EmpathyMainWindow      *window)
469 {
470         EmpathyMainWindowPriv *priv = GET_PRIV (window);
471         EmpathyContact *contact = NULL;
472         FolksIndividual *individual;
473         GtkTreeModel   *model;
474         GtkTreeIter     iter;
475         GSList         *events, *l;
476
477         model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->individual_view));
478         gtk_tree_model_get_iter (model, &iter, path);
479
480         gtk_tree_model_get (model, &iter,
481                             EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL,
482                                 &individual,
483                             -1);
484
485         if (individual != NULL) {
486                 contact = empathy_contact_dup_from_folks_individual (individual);
487         }
488
489         if (!contact) {
490                 goto OUT;
491         }
492
493         /* If the contact has an event activate it, otherwise the
494          * default handler of row-activated will be called. */
495         events = empathy_event_manager_get_events (priv->event_manager);
496         for (l = events; l; l = l->next) {
497                 EmpathyEvent *event = l->data;
498
499                 if (event->contact == contact) {
500                         DEBUG ("Activate event");
501                         empathy_event_activate (event);
502
503                         /* We don't want the default handler of this signal
504                          * (e.g. open a chat) */
505                         g_signal_stop_emission_by_name (view, "row-activated");
506                         break;
507                 }
508         }
509
510         g_object_unref (contact);
511 OUT:
512         tp_clear_object (&individual);
513 }
514
515 static void
516 main_window_row_deleted_cb (GtkTreeModel      *model,
517                             GtkTreePath       *path,
518                             EmpathyMainWindow *window)
519 {
520         EmpathyMainWindowPriv *priv = GET_PRIV (window);
521         GtkTreeIter help_iter;
522
523         if (!gtk_tree_model_get_iter_first (model, &help_iter)) {
524                 priv->empty = TRUE;
525
526                 if (empathy_individual_view_is_searching (
527                                 priv->individual_view)) {
528                         gchar *tmp;
529
530                         tmp = g_strdup_printf ("<b><span size='xx-large'>%s</span></b>",
531                                 _("No match found"));
532
533                         gtk_label_set_markup (GTK_LABEL (priv->no_entry_label), tmp);
534                         g_free (tmp);
535
536                         gtk_label_set_line_wrap (GTK_LABEL (priv->no_entry_label),
537                                 TRUE);
538
539                         gtk_notebook_set_current_page (
540                                         GTK_NOTEBOOK (priv->notebook), PAGE_NO_MATCH);
541                 }
542         }
543 }
544
545 static void
546 main_window_row_inserted_cb (GtkTreeModel      *model,
547                              GtkTreePath       *path,
548                              GtkTreeIter       *iter,
549                              EmpathyMainWindow *window)
550 {
551         EmpathyMainWindowPriv *priv = GET_PRIV (window);
552
553         if (priv->empty) {
554                 priv->empty = FALSE;
555                 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
556                                 PAGE_CONTACT_LIST);
557                 gtk_widget_grab_focus (GTK_WIDGET (priv->individual_view));
558         }
559 }
560
561 static void
562 main_window_remove_error (EmpathyMainWindow *window,
563                           TpAccount         *account)
564 {
565         EmpathyMainWindowPriv *priv = GET_PRIV (window);
566         GtkWidget *error_widget;
567
568         error_widget = g_hash_table_lookup (priv->errors, account);
569         if (error_widget != NULL) {
570                 gtk_widget_destroy (error_widget);
571                 g_hash_table_remove (priv->errors, account);
572         }
573 }
574
575 static void
576 main_window_account_disabled_cb (TpAccountManager  *manager,
577                                  TpAccount         *account,
578                                  EmpathyMainWindow *window)
579 {
580         main_window_remove_error (window, account);
581 }
582
583 static void
584 main_window_error_retry_clicked_cb (GtkButton         *button,
585                                     EmpathyMainWindow *window)
586 {
587         TpAccount *account;
588
589         account = g_object_get_data (G_OBJECT (button), "account");
590         tp_account_reconnect_async (account, NULL, NULL);
591
592         main_window_remove_error (window, account);
593 }
594
595 static void
596 main_window_error_edit_clicked_cb (GtkButton         *button,
597                                    EmpathyMainWindow *window)
598 {
599         TpAccount *account;
600
601         account = g_object_get_data (G_OBJECT (button), "account");
602
603         empathy_accounts_dialog_show_application (
604                         gtk_widget_get_screen (GTK_WIDGET (button)),
605                         account, FALSE, FALSE);
606
607         main_window_remove_error (window, account);
608 }
609
610 static void
611 main_window_error_close_clicked_cb (GtkButton         *button,
612                                     EmpathyMainWindow *window)
613 {
614         TpAccount *account;
615
616         account = g_object_get_data (G_OBJECT (button), "account");
617         main_window_remove_error (window, account);
618 }
619
620 static void
621 main_window_error_display (EmpathyMainWindow *window,
622                            TpAccount         *account)
623 {
624         EmpathyMainWindowPriv *priv = GET_PRIV (window);
625         GtkWidget *info_bar;
626         GtkWidget *content_area;
627         GtkWidget *label;
628         GtkWidget *image;
629         GtkWidget *retry_button;
630         GtkWidget *edit_button;
631         GtkWidget *close_button;
632         GtkWidget *action_area;
633         GtkWidget *action_table;
634         gchar     *str;
635         const gchar     *icon_name;
636         const gchar *error_message;
637         gboolean user_requested;
638
639         error_message =
640                 empathy_account_get_error_message (account, &user_requested);
641
642         if (user_requested) {
643                 return;
644         }
645
646         str = g_markup_printf_escaped ("<b>%s</b>\n%s",
647                                                tp_account_get_display_name (account),
648                                                error_message);
649
650         info_bar = g_hash_table_lookup (priv->errors, account);
651         if (info_bar) {
652                 label = g_object_get_data (G_OBJECT (info_bar), "label");
653
654                 /* Just set the latest error and return */
655                 gtk_label_set_markup (GTK_LABEL (label), str);
656                 g_free (str);
657
658                 return;
659         }
660
661         info_bar = gtk_info_bar_new ();
662         gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_ERROR);
663
664         gtk_widget_set_no_show_all (info_bar, TRUE);
665         gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar, FALSE, TRUE, 0);
666         gtk_widget_show (info_bar);
667
668         icon_name = tp_account_get_icon_name (account);
669         image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR);
670         gtk_widget_show (image);
671
672         label = gtk_label_new (str);
673         gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
674         gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
675         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
676         gtk_widget_show (label);
677         g_free (str);
678
679         content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
680         gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0);
681         gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0);
682
683         image = gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON);
684         retry_button = gtk_button_new ();
685         gtk_button_set_image (GTK_BUTTON (retry_button), image);
686         gtk_widget_set_tooltip_text (retry_button, _("Reconnect"));
687         gtk_widget_show (retry_button);
688
689         image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON);
690         edit_button = gtk_button_new ();
691         gtk_button_set_image (GTK_BUTTON (edit_button), image);
692         gtk_widget_set_tooltip_text (edit_button, _("Edit Account"));
693         gtk_widget_show (edit_button);
694
695         image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON);
696         close_button = gtk_button_new ();
697         gtk_button_set_image (GTK_BUTTON (close_button), image);
698         gtk_widget_set_tooltip_text (close_button, _("Close"));
699         gtk_widget_show (close_button);
700
701         action_table = gtk_table_new (1, 3, FALSE);
702         gtk_table_set_col_spacings (GTK_TABLE (action_table), 2);
703         gtk_widget_show (action_table);
704
705         action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar));
706         gtk_box_pack_start (GTK_BOX (action_area), action_table, FALSE, FALSE, 0);
707
708         gtk_table_attach (GTK_TABLE (action_table), retry_button, 0, 1, 0, 1,
709                                                                                 (GtkAttachOptions) (GTK_SHRINK),
710                                                                                 (GtkAttachOptions) (GTK_SHRINK), 0, 0);
711         gtk_table_attach (GTK_TABLE (action_table), edit_button, 1, 2, 0, 1,
712                                                                                 (GtkAttachOptions) (GTK_SHRINK),
713                                                                                 (GtkAttachOptions) (GTK_SHRINK), 0, 0);
714         gtk_table_attach (GTK_TABLE (action_table), close_button, 2, 3, 0, 1,
715                                                                                 (GtkAttachOptions) (GTK_SHRINK),
716                                                                                 (GtkAttachOptions) (GTK_SHRINK), 0, 0);
717
718         g_object_set_data (G_OBJECT (info_bar), "label", label);
719         g_object_set_data_full (G_OBJECT (info_bar),
720                                 "account", g_object_ref (account),
721                                 g_object_unref);
722         g_object_set_data_full (G_OBJECT (edit_button),
723                                 "account", g_object_ref (account),
724                                 g_object_unref);
725         g_object_set_data_full (G_OBJECT (close_button),
726                                 "account", g_object_ref (account),
727                                 g_object_unref);
728         g_object_set_data_full (G_OBJECT (retry_button),
729                                 "account", g_object_ref (account),
730                                 g_object_unref);
731
732         g_signal_connect (edit_button, "clicked",
733                           G_CALLBACK (main_window_error_edit_clicked_cb),
734                           window);
735         g_signal_connect (close_button, "clicked",
736                           G_CALLBACK (main_window_error_close_clicked_cb),
737                           window);
738         g_signal_connect (retry_button, "clicked",
739                           G_CALLBACK (main_window_error_retry_clicked_cb),
740                           window);
741
742         gtk_widget_set_tooltip_text (priv->errors_vbox, error_message);
743         gtk_widget_show (priv->errors_vbox);
744
745         g_hash_table_insert (priv->errors, g_object_ref (account), info_bar);
746 }
747
748 static void
749 main_window_update_status (EmpathyMainWindow *window)
750 {
751         EmpathyMainWindowPriv *priv = GET_PRIV (window);
752         gboolean connected, connecting;
753         GList *l, *children;
754
755         connected = empathy_account_manager_get_accounts_connected (&connecting);
756
757         /* Update the spinner state */
758         if (connecting) {
759                 gtk_spinner_start (GTK_SPINNER (priv->throbber));
760                 gtk_widget_show (priv->throbber_tool_item);
761         } else {
762                 gtk_spinner_stop (GTK_SPINNER (priv->throbber));
763                 gtk_widget_hide (priv->throbber_tool_item);
764         }
765
766         /* Update widgets sensibility */
767         for (l = priv->actions_connected; l; l = l->next) {
768                 gtk_action_set_sensitive (l->data, connected);
769         }
770
771         /* Update favourite rooms sensitivity */
772         children = gtk_container_get_children (GTK_CONTAINER (priv->room_menu));
773         for (l = children; l != NULL; l = l->next) {
774                 if (g_object_get_data (G_OBJECT (l->data), "is_favorite") != NULL) {
775                         gtk_widget_set_sensitive (GTK_WIDGET (l->data), connected);
776                 }
777         }
778         g_list_free (children);
779 }
780
781 static void
782 main_window_connection_changed_cb (TpAccount  *account,
783                                    guint       old_status,
784                                    guint       current,
785                                    guint       reason,
786                                    gchar      *dbus_error_name,
787                                    GHashTable *details,
788                                    EmpathyMainWindow *window)
789 {
790         EmpathyMainWindowPriv *priv = GET_PRIV (window);
791
792         main_window_update_status (window);
793
794         if (current == TP_CONNECTION_STATUS_DISCONNECTED &&
795             reason != TP_CONNECTION_STATUS_REASON_REQUESTED) {
796                 main_window_error_display (window, account);
797         }
798
799         if (current == TP_CONNECTION_STATUS_DISCONNECTED) {
800                 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (window),
801                                     EMPATHY_SOUND_ACCOUNT_DISCONNECTED);
802         }
803
804         if (current == TP_CONNECTION_STATUS_CONNECTED) {
805                 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (window),
806                                     EMPATHY_SOUND_ACCOUNT_CONNECTED);
807
808                 /* Account connected without error, remove error message if any */
809                 main_window_remove_error (window, account);
810         }
811 }
812
813 static void
814 main_window_accels_load (void)
815 {
816         gchar *filename;
817
818         filename = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, ACCELS_FILENAME, NULL);
819         if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
820                 DEBUG ("Loading from:'%s'", filename);
821                 gtk_accel_map_load (filename);
822         }
823
824         g_free (filename);
825 }
826
827 static void
828 main_window_accels_save (void)
829 {
830         gchar *dir;
831         gchar *file_with_path;
832
833         dir = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL);
834         g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
835         file_with_path = g_build_filename (dir, ACCELS_FILENAME, NULL);
836         g_free (dir);
837
838         DEBUG ("Saving to:'%s'", file_with_path);
839         gtk_accel_map_save (file_with_path);
840
841         g_free (file_with_path);
842 }
843
844 static void
845 empathy_main_window_finalize (GObject *window)
846 {
847         EmpathyMainWindowPriv *priv = GET_PRIV (window);
848         GHashTableIter iter;
849         gpointer key, value;
850
851         /* Save user-defined accelerators. */
852         main_window_accels_save ();
853
854         g_list_free (priv->actions_connected);
855
856         g_object_unref (priv->account_manager);
857         g_object_unref (priv->individual_store);
858         g_object_unref (priv->contact_manager);
859         g_object_unref (priv->sound_mgr);
860         g_hash_table_destroy (priv->errors);
861         g_hash_table_destroy (priv->auths);
862
863         /* disconnect all handlers of status-changed signal */
864         g_hash_table_iter_init (&iter, priv->status_changed_handlers);
865         while (g_hash_table_iter_next (&iter, &key, &value))
866                 g_signal_handler_disconnect (TP_ACCOUNT (key),
867                                              GPOINTER_TO_UINT (value));
868
869         g_hash_table_destroy (priv->status_changed_handlers);
870
871         g_signal_handlers_disconnect_by_func (priv->event_manager,
872                                               main_window_event_added_cb,
873                                               window);
874         g_signal_handlers_disconnect_by_func (priv->event_manager,
875                                               main_window_event_removed_cb,
876                                               window);
877         g_object_unref (priv->event_manager);
878         g_object_unref (priv->ui_manager);
879         g_object_unref (priv->chatroom_manager);
880
881         g_object_unref (priv->gsettings_ui);
882         g_object_unref (priv->gsettings_contacts);
883
884         G_OBJECT_CLASS (empathy_main_window_parent_class)->finalize (window);
885 }
886
887 static gboolean
888 main_window_key_press_event_cb  (GtkWidget   *window,
889                                  GdkEventKey *event,
890                                  gpointer     user_data)
891 {
892         EmpathyChatManager *chat_manager;
893
894         if (event->keyval == GDK_KEY_T
895             && event->state & GDK_SHIFT_MASK
896             && event->state & GDK_CONTROL_MASK) {
897                 chat_manager = empathy_chat_manager_dup_singleton ();
898                 empathy_chat_manager_undo_closed_chat (chat_manager);
899                 g_object_unref (chat_manager);
900         }
901         return FALSE;
902 }
903
904 static void
905 main_window_chat_quit_cb (GtkAction         *action,
906                           EmpathyMainWindow *window)
907 {
908         gtk_main_quit ();
909 }
910
911 static void
912 main_window_view_history_cb (GtkAction         *action,
913                              EmpathyMainWindow *window)
914 {
915         empathy_log_window_show (NULL, NULL, FALSE, GTK_WINDOW (window));
916 }
917
918 static void
919 main_window_chat_new_message_cb (GtkAction         *action,
920                                  EmpathyMainWindow *window)
921 {
922         empathy_new_message_dialog_show (GTK_WINDOW (window));
923 }
924
925 static void
926 main_window_chat_new_call_cb (GtkAction         *action,
927                               EmpathyMainWindow *window)
928 {
929         empathy_new_call_dialog_show (GTK_WINDOW (window));
930 }
931
932 static void
933 main_window_chat_add_contact_cb (GtkAction         *action,
934                                  EmpathyMainWindow *window)
935 {
936         empathy_new_individual_dialog_show (GTK_WINDOW (window));
937 }
938
939 static void
940 main_window_chat_search_contacts_cb (GtkAction         *action,
941                                      EmpathyMainWindow *window)
942 {
943         GtkWidget *dialog = empathy_contact_search_dialog_new (
944                         GTK_WINDOW (window));
945         gtk_widget_show (dialog);
946 }
947
948 static void
949 main_window_view_show_ft_manager (GtkAction         *action,
950                                   EmpathyMainWindow *window)
951 {
952         empathy_ft_manager_show ();
953 }
954
955 static void
956 main_window_view_show_offline_cb (GtkToggleAction   *action,
957                                   EmpathyMainWindow *window)
958 {
959         EmpathyMainWindowPriv *priv = GET_PRIV (window);
960         gboolean current;
961
962         current = gtk_toggle_action_get_active (action);
963         g_settings_set_boolean (priv->gsettings_ui,
964                                 EMPATHY_PREFS_UI_SHOW_OFFLINE,
965                                 current);
966
967         empathy_individual_view_set_show_offline (priv->individual_view,
968                         current);
969 }
970
971 static void
972 main_window_notify_sort_contact_cb (GSettings         *gsettings,
973                                     const gchar       *key,
974                                     EmpathyMainWindow *window)
975 {
976         EmpathyMainWindowPriv *priv = GET_PRIV (window);
977         gchar *str;
978
979         str = g_settings_get_string (gsettings, key);
980
981         if (str != NULL) {
982                 GType       type;
983                 GEnumClass *enum_class;
984                 GEnumValue *enum_value;
985
986                 type = empathy_individual_store_sort_get_type ();
987                 enum_class = G_ENUM_CLASS (g_type_class_peek (type));
988                 enum_value = g_enum_get_value_by_nick (enum_class, str);
989                 if (enum_value) {
990                         /* By changing the value of the GtkRadioAction,
991                            it emits a signal that calls main_window_view_sort_contacts_cb
992                            which updates the contacts list */
993                         gtk_radio_action_set_current_value (priv->sort_by_name,
994                                                             enum_value->value);
995                 } else {
996                         g_warning ("Wrong value for sort_criterium configuration : %s", str);
997                 }
998                 g_free (str);
999         }
1000 }
1001
1002 static void
1003 main_window_view_sort_contacts_cb (GtkRadioAction    *action,
1004                                    GtkRadioAction    *current,
1005                                    EmpathyMainWindow *window)
1006 {
1007         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1008         EmpathyContactListStoreSort value;
1009         GSList      *group;
1010         GType        type;
1011         GEnumClass  *enum_class;
1012         GEnumValue  *enum_value;
1013
1014         value = gtk_radio_action_get_current_value (action);
1015         group = gtk_radio_action_get_group (action);
1016
1017         /* Get string from index */
1018         type = empathy_individual_store_sort_get_type ();
1019         enum_class = G_ENUM_CLASS (g_type_class_peek (type));
1020         enum_value = g_enum_get_value (enum_class, g_slist_index (group, current));
1021
1022         if (!enum_value) {
1023                 g_warning ("No GEnumValue for EmpathyContactListSort with GtkRadioAction index:%d",
1024                            g_slist_index (group, action));
1025         } else {
1026                 g_settings_set_string (priv->gsettings_contacts,
1027                                        EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM,
1028                                        enum_value->value_nick);
1029         }
1030         empathy_individual_store_set_sort_criterium (priv->individual_store,
1031                         value);
1032 }
1033
1034 static void
1035 main_window_view_show_protocols_cb (GtkToggleAction   *action,
1036                                     EmpathyMainWindow *window)
1037 {
1038         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1039         gboolean value;
1040
1041         value = gtk_toggle_action_get_active (action);
1042
1043         g_settings_set_boolean (priv->gsettings_ui,
1044                                 EMPATHY_PREFS_UI_SHOW_PROTOCOLS,
1045                                 value);
1046         empathy_individual_store_set_show_protocols (priv->individual_store,
1047                                                      value);
1048 }
1049
1050 /* Matches GtkRadioAction values set in empathy-main-window.ui */
1051 #define CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS           0
1052 #define CONTACT_LIST_NORMAL_SIZE                        1
1053 #define CONTACT_LIST_COMPACT_SIZE                       2
1054
1055 static void
1056 main_window_view_contacts_list_size_cb (GtkRadioAction    *action,
1057                                         GtkRadioAction    *current,
1058                                         EmpathyMainWindow *window)
1059 {
1060         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1061         GSettings *gsettings_ui;
1062         gint value;
1063
1064         value = gtk_radio_action_get_current_value (action);
1065         /* create a new GSettings, so we can delay the setting until both
1066          * values are set */
1067         gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1068
1069         DEBUG ("radio button toggled, value = %i", value);
1070
1071         g_settings_delay (gsettings_ui);
1072         g_settings_set_boolean (gsettings_ui,
1073                                 EMPATHY_PREFS_UI_SHOW_AVATARS,
1074                                 value == CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS);
1075
1076         g_settings_set_boolean (gsettings_ui,
1077                                 EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST,
1078                                 value == CONTACT_LIST_COMPACT_SIZE);
1079         g_settings_apply (gsettings_ui);
1080
1081         /* FIXME: these enums probably have the wrong namespace */
1082         empathy_individual_store_set_show_avatars (priv->individual_store,
1083                         value == CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS);
1084         empathy_individual_store_set_is_compact (priv->individual_store,
1085                         value == CONTACT_LIST_COMPACT_SIZE);
1086
1087         g_object_unref (gsettings_ui);
1088 }
1089
1090 static void main_window_notify_show_protocols_cb (GSettings         *gsettings,
1091                                                   const gchar       *key,
1092                                                   EmpathyMainWindow *window)
1093 {
1094         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1095
1096         gtk_toggle_action_set_active (priv->show_protocols,
1097                         g_settings_get_boolean (gsettings,
1098                                 EMPATHY_PREFS_UI_SHOW_PROTOCOLS));
1099 }
1100
1101
1102 static void
1103 main_window_notify_contact_list_size_cb (GSettings         *gsettings,
1104                                          const gchar       *key,
1105                                          EmpathyMainWindow *window)
1106 {
1107         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1108         gint value;
1109
1110         if (g_settings_get_boolean (gsettings,
1111                         EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST)) {
1112                 value = CONTACT_LIST_COMPACT_SIZE;
1113         } else if (g_settings_get_boolean (gsettings,
1114                         EMPATHY_PREFS_UI_SHOW_AVATARS)) {
1115                 value = CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS;
1116         } else {
1117                 value = CONTACT_LIST_NORMAL_SIZE;
1118         }
1119
1120         DEBUG ("setting changed, value = %i", value);
1121
1122         /* By changing the value of the GtkRadioAction,
1123            it emits a signal that calls main_window_view_contacts_list_size_cb
1124            which updates the contacts list */
1125         gtk_radio_action_set_current_value (priv->normal_with_avatars, value);
1126 }
1127
1128 static void
1129 main_window_edit_search_contacts_cb (GtkCheckMenuItem  *item,
1130                                      EmpathyMainWindow *window)
1131 {
1132         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1133
1134         empathy_individual_view_start_search (priv->individual_view);
1135 }
1136
1137 static void
1138 main_window_view_show_map_cb (GtkCheckMenuItem  *item,
1139                               EmpathyMainWindow *window)
1140 {
1141 #ifdef HAVE_LIBCHAMPLAIN
1142         empathy_map_view_show ();
1143 #endif
1144 }
1145
1146 static void
1147 join_chatroom (EmpathyChatroom *chatroom,
1148                gint64 timestamp)
1149 {
1150         TpAccount      *account;
1151         const gchar    *room;
1152
1153         account = empathy_chatroom_get_account (chatroom);
1154         room = empathy_chatroom_get_room (chatroom);
1155
1156         DEBUG ("Requesting channel for '%s'", room);
1157         empathy_dispatcher_join_muc (account, room, timestamp);
1158 }
1159
1160 typedef struct
1161 {
1162         TpAccount *account;
1163         EmpathyChatroom *chatroom;
1164         gint64 timestamp;
1165         glong sig_id;
1166         guint timeout;
1167 } join_fav_account_sig_ctx;
1168
1169 static join_fav_account_sig_ctx *
1170 join_fav_account_sig_ctx_new (TpAccount *account,
1171                              EmpathyChatroom *chatroom,
1172                               gint64 timestamp)
1173 {
1174         join_fav_account_sig_ctx *ctx = g_slice_new0 (
1175                 join_fav_account_sig_ctx);
1176
1177         ctx->account = g_object_ref (account);
1178         ctx->chatroom = g_object_ref (chatroom);
1179         ctx->timestamp = timestamp;
1180         return ctx;
1181 }
1182
1183 static void
1184 join_fav_account_sig_ctx_free (join_fav_account_sig_ctx *ctx)
1185 {
1186         g_object_unref (ctx->account);
1187         g_object_unref (ctx->chatroom);
1188         g_slice_free (join_fav_account_sig_ctx, ctx);
1189 }
1190
1191 static void
1192 account_status_changed_cb (TpAccount  *account,
1193                            TpConnectionStatus old_status,
1194                            TpConnectionStatus new_status,
1195                            guint reason,
1196                            gchar *dbus_error_name,
1197                            GHashTable *details,
1198                            gpointer user_data)
1199 {
1200         join_fav_account_sig_ctx *ctx = user_data;
1201
1202         switch (new_status) {
1203                 case TP_CONNECTION_STATUS_DISCONNECTED:
1204                         /* Don't wait any longer */
1205                         goto finally;
1206                         break;
1207
1208                 case TP_CONNECTION_STATUS_CONNECTING:
1209                         /* Wait a bit */
1210                         return;
1211
1212                 case TP_CONNECTION_STATUS_CONNECTED:
1213                         /* We can join the room */
1214                         break;
1215
1216                 default:
1217                         g_assert_not_reached ();
1218         }
1219
1220         join_chatroom (ctx->chatroom, ctx->timestamp);
1221
1222 finally:
1223         g_source_remove (ctx->timeout);
1224         g_signal_handler_disconnect (account, ctx->sig_id);
1225 }
1226
1227 #define JOIN_FAVORITE_TIMEOUT 5
1228
1229 static gboolean
1230 join_favorite_timeout_cb (gpointer data)
1231 {
1232         join_fav_account_sig_ctx *ctx = data;
1233
1234         /* stop waiting for joining the favorite room */
1235         g_signal_handler_disconnect (ctx->account, ctx->sig_id);
1236         return FALSE;
1237 }
1238
1239 static void
1240 main_window_favorite_chatroom_join (EmpathyChatroom *chatroom)
1241 {
1242         TpAccount      *account;
1243
1244         account = empathy_chatroom_get_account (chatroom);
1245         if (tp_account_get_connection_status (account, NULL) !=
1246                                              TP_CONNECTION_STATUS_CONNECTED) {
1247                 join_fav_account_sig_ctx *ctx;
1248
1249                 ctx = join_fav_account_sig_ctx_new (account, chatroom,
1250                         gtk_get_current_event_time ());
1251
1252                 ctx->sig_id = g_signal_connect_data (account, "status-changed",
1253                         G_CALLBACK (account_status_changed_cb), ctx,
1254                         (GClosureNotify) join_fav_account_sig_ctx_free, 0);
1255
1256                 ctx->timeout = g_timeout_add_seconds (JOIN_FAVORITE_TIMEOUT,
1257                         join_favorite_timeout_cb, ctx);
1258                 return;
1259         }
1260
1261         join_chatroom (chatroom, gtk_get_current_event_time ());
1262 }
1263
1264 static void
1265 main_window_favorite_chatroom_menu_activate_cb (GtkMenuItem     *menu_item,
1266                                                 EmpathyChatroom *chatroom)
1267 {
1268         main_window_favorite_chatroom_join (chatroom);
1269 }
1270
1271 static void
1272 main_window_favorite_chatroom_menu_add (EmpathyMainWindow *window,
1273                                         EmpathyChatroom   *chatroom)
1274 {
1275         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1276         GtkWidget   *menu_item;
1277         const gchar *name;
1278
1279         if (g_object_get_data (G_OBJECT (chatroom), "menu_item")) {
1280                 return;
1281         }
1282
1283         name = empathy_chatroom_get_name (chatroom);
1284         menu_item = gtk_menu_item_new_with_label (name);
1285         g_object_set_data (G_OBJECT (menu_item), "is_favorite",
1286                         GUINT_TO_POINTER (TRUE));
1287
1288         g_object_set_data (G_OBJECT (chatroom), "menu_item", menu_item);
1289         g_signal_connect (menu_item, "activate",
1290                           G_CALLBACK (main_window_favorite_chatroom_menu_activate_cb),
1291                           chatroom);
1292
1293         gtk_menu_shell_insert (GTK_MENU_SHELL (priv->room_menu),
1294                                menu_item, 4);
1295
1296         gtk_widget_show (menu_item);
1297 }
1298
1299 static void
1300 main_window_favorite_chatroom_menu_added_cb (EmpathyChatroomManager *manager,
1301                                              EmpathyChatroom        *chatroom,
1302                                              EmpathyMainWindow      *window)
1303 {
1304         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1305
1306         main_window_favorite_chatroom_menu_add (window, chatroom);
1307         gtk_widget_show (priv->room_separator);
1308         gtk_action_set_sensitive (priv->room_join_favorites, TRUE);
1309 }
1310
1311 static void
1312 main_window_favorite_chatroom_menu_removed_cb (EmpathyChatroomManager *manager,
1313                                                EmpathyChatroom        *chatroom,
1314                                                EmpathyMainWindow      *window)
1315 {
1316         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1317         GtkWidget *menu_item;
1318         GList *chatrooms;
1319
1320         menu_item = g_object_get_data (G_OBJECT (chatroom), "menu_item");
1321         g_object_set_data (G_OBJECT (chatroom), "menu_item", NULL);
1322         gtk_widget_destroy (menu_item);
1323
1324         chatrooms = empathy_chatroom_manager_get_chatrooms (priv->chatroom_manager, NULL);
1325         if (chatrooms) {
1326                 gtk_widget_show (priv->room_separator);
1327         } else {
1328                 gtk_widget_hide (priv->room_separator);
1329         }
1330
1331         gtk_action_set_sensitive (priv->room_join_favorites, chatrooms != NULL);
1332         g_list_free (chatrooms);
1333 }
1334
1335 static void
1336 main_window_favorite_chatroom_menu_setup (EmpathyMainWindow *window)
1337 {
1338         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1339         GList *chatrooms, *l;
1340         GtkWidget *room;
1341
1342         priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1343         chatrooms = empathy_chatroom_manager_get_chatrooms (
1344                 priv->chatroom_manager, NULL);
1345         room = gtk_ui_manager_get_widget (priv->ui_manager,
1346                 "/menubar/room");
1347         priv->room_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (room));
1348         priv->room_separator = gtk_ui_manager_get_widget (priv->ui_manager,
1349                 "/menubar/room/room_separator");
1350
1351         for (l = chatrooms; l; l = l->next) {
1352                 main_window_favorite_chatroom_menu_add (window, l->data);
1353         }
1354
1355         if (!chatrooms) {
1356                 gtk_widget_hide (priv->room_separator);
1357         }
1358
1359         gtk_action_set_sensitive (priv->room_join_favorites, chatrooms != NULL);
1360
1361         g_signal_connect (priv->chatroom_manager, "chatroom-added",
1362                           G_CALLBACK (main_window_favorite_chatroom_menu_added_cb),
1363                           window);
1364         g_signal_connect (priv->chatroom_manager, "chatroom-removed",
1365                           G_CALLBACK (main_window_favorite_chatroom_menu_removed_cb),
1366                           window);
1367
1368         g_list_free (chatrooms);
1369 }
1370
1371 static void
1372 main_window_room_join_new_cb (GtkAction         *action,
1373                               EmpathyMainWindow *window)
1374 {
1375         empathy_new_chatroom_dialog_show (GTK_WINDOW (window));
1376 }
1377
1378 static void
1379 main_window_room_join_favorites_cb (GtkAction         *action,
1380                                     EmpathyMainWindow *window)
1381 {
1382         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1383         GList *chatrooms, *l;
1384
1385         chatrooms = empathy_chatroom_manager_get_chatrooms (priv->chatroom_manager, NULL);
1386         for (l = chatrooms; l; l = l->next) {
1387                 main_window_favorite_chatroom_join (l->data);
1388         }
1389         g_list_free (chatrooms);
1390 }
1391
1392 static void
1393 main_window_room_manage_favorites_cb (GtkAction         *action,
1394                                       EmpathyMainWindow *window)
1395 {
1396         empathy_chatrooms_window_show (GTK_WINDOW (window));
1397 }
1398
1399 static void
1400 main_window_edit_cb (GtkAction         *action,
1401                      EmpathyMainWindow *window)
1402 {
1403         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1404         GtkWidget *submenu;
1405
1406         /* FIXME: It should use the UIManager to merge the contact/group submenu */
1407         submenu = empathy_individual_view_get_individual_menu (
1408                         priv->individual_view);
1409         if (submenu) {
1410                 GtkMenuItem *item;
1411                 GtkWidget   *label;
1412
1413                 item = GTK_MENU_ITEM (priv->edit_context);
1414                 label = gtk_bin_get_child (GTK_BIN (item));
1415                 gtk_label_set_text (GTK_LABEL (label), _("Contact"));
1416
1417                 gtk_widget_show (priv->edit_context);
1418                 gtk_widget_show (priv->edit_context_separator);
1419
1420                 gtk_menu_item_set_submenu (item, submenu);
1421
1422                 return;
1423         }
1424
1425         submenu = empathy_individual_view_get_group_menu (
1426                         priv->individual_view);
1427         if (submenu) {
1428                 GtkMenuItem *item;
1429                 GtkWidget   *label;
1430
1431                 item = GTK_MENU_ITEM (priv->edit_context);
1432                 label = gtk_bin_get_child (GTK_BIN (item));
1433                 gtk_label_set_text (GTK_LABEL (label), _("Group"));
1434
1435                 gtk_widget_show (priv->edit_context);
1436                 gtk_widget_show (priv->edit_context_separator);
1437
1438                 gtk_menu_item_set_submenu (item, submenu);
1439
1440                 return;
1441         }
1442
1443         gtk_widget_hide (priv->edit_context);
1444         gtk_widget_hide (priv->edit_context_separator);
1445
1446         return;
1447 }
1448
1449 static void
1450 main_window_edit_accounts_cb (GtkAction         *action,
1451                               EmpathyMainWindow *window)
1452 {
1453         empathy_accounts_dialog_show_application (gdk_screen_get_default (),
1454                         NULL, FALSE, FALSE);
1455 }
1456
1457 static void
1458 main_window_edit_personal_information_cb (GtkAction         *action,
1459                                           EmpathyMainWindow *window)
1460 {
1461         empathy_contact_personal_dialog_show (GTK_WINDOW (window));
1462 }
1463
1464 static void
1465 main_window_edit_blocked_contacts_cb (GtkAction         *action,
1466                                       EmpathyMainWindow *window)
1467 {
1468         GtkWidget *dialog;
1469
1470         dialog = empathy_contact_blocking_dialog_new (GTK_WINDOW (window));
1471         gtk_widget_show (dialog);
1472         g_signal_connect (dialog, "response",
1473                         G_CALLBACK (gtk_widget_destroy), NULL);
1474 }
1475
1476 static void
1477 main_window_edit_preferences_cb (GtkAction         *action,
1478                                  EmpathyMainWindow *window)
1479 {
1480         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1481
1482         if (priv->preferences == NULL) {
1483                 priv->preferences = empathy_preferences_new (GTK_WINDOW (window));
1484                 g_object_add_weak_pointer (G_OBJECT (priv->preferences),
1485                                            (gpointer) &priv->preferences);
1486
1487                 gtk_widget_show (priv->preferences);
1488         } else {
1489                 gtk_window_present (GTK_WINDOW (priv->preferences));
1490         }
1491 }
1492
1493 static void
1494 main_window_help_about_cb (GtkAction         *action,
1495                            EmpathyMainWindow *window)
1496 {
1497         empathy_about_dialog_new (GTK_WINDOW (window));
1498 }
1499
1500 static void
1501 main_window_help_debug_cb (GtkAction         *action,
1502                            EmpathyMainWindow *window)
1503 {
1504         GdkDisplay *display;
1505         GError *error = NULL;
1506         gchar *path;
1507         GAppInfo *app_info;
1508         GdkAppLaunchContext *context = NULL;
1509
1510         /* Try to run from source directory if possible */
1511         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "src",
1512                         "empathy-debugger", NULL);
1513
1514         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
1515                 g_free (path);
1516                 path = g_build_filename (BIN_DIR, "empathy-debugger", NULL);
1517         }
1518
1519         app_info = g_app_info_create_from_commandline (path, NULL, 0, &error);
1520         if (app_info == NULL) {
1521                 DEBUG ("Failed to create app info: %s", error->message);
1522                 g_error_free (error);
1523                 goto out;
1524         }
1525
1526         display = gdk_display_get_default ();
1527         context = gdk_display_get_app_launch_context (display);
1528
1529         if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
1530                 &error)) {
1531                 g_warning ("Failed to open debug window: %s", error->message);
1532                 g_error_free (error);
1533                 goto out;
1534         }
1535
1536 out:
1537         tp_clear_object (&app_info);
1538         tp_clear_object (&context);
1539         g_free (path);
1540 }
1541
1542 static void
1543 main_window_help_contents_cb (GtkAction         *action,
1544                               EmpathyMainWindow *window)
1545 {
1546         empathy_url_show (GTK_WIDGET (window), "ghelp:empathy");
1547 }
1548
1549 static gboolean
1550 main_window_throbber_button_press_event_cb (GtkWidget         *throbber,
1551                                             GdkEventButton    *event,
1552                                             EmpathyMainWindow *window)
1553 {
1554         if (event->type != GDK_BUTTON_PRESS ||
1555             event->button != 1) {
1556                 return FALSE;
1557         }
1558
1559         empathy_accounts_dialog_show_application (
1560                         gtk_widget_get_screen (GTK_WIDGET (throbber)),
1561                         NULL, FALSE, FALSE);
1562
1563         return FALSE;
1564 }
1565
1566 static void
1567 main_window_account_removed_cb (TpAccountManager  *manager,
1568                                 TpAccount         *account,
1569                                 EmpathyMainWindow *window)
1570 {
1571         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1572         GList *a;
1573
1574         a = tp_account_manager_get_valid_accounts (manager);
1575
1576         gtk_action_set_sensitive (priv->view_history,
1577                 g_list_length (a) > 0);
1578
1579         g_list_free (a);
1580
1581         /* remove errors if any */
1582         main_window_remove_error (window, account);
1583 }
1584
1585 static void
1586 main_window_account_validity_changed_cb (TpAccountManager  *manager,
1587                                          TpAccount         *account,
1588                                          gboolean           valid,
1589                                          EmpathyMainWindow *window)
1590 {
1591         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1592
1593         if (valid) {
1594                 gulong handler_id;
1595                 handler_id = GPOINTER_TO_UINT (g_hash_table_lookup (
1596                         priv->status_changed_handlers, account));
1597
1598                 /* connect signal only if it was not connected yet */
1599                 if (handler_id == 0) {
1600                         handler_id = g_signal_connect (account,
1601                                 "status-changed",
1602                                 G_CALLBACK (main_window_connection_changed_cb),
1603                                 window);
1604                         g_hash_table_insert (priv->status_changed_handlers,
1605                                 account, GUINT_TO_POINTER (handler_id));
1606                 }
1607         }
1608
1609         main_window_account_removed_cb (manager, account, window);
1610 }
1611
1612 static void
1613 main_window_notify_show_offline_cb (GSettings   *gsettings,
1614                                     const gchar *key,
1615                                     gpointer     toggle_action)
1616 {
1617         gtk_toggle_action_set_active (toggle_action,
1618                         g_settings_get_boolean (gsettings, key));
1619 }
1620
1621 static void
1622 main_window_connection_items_setup (EmpathyMainWindow *window,
1623                                     GtkBuilder        *gui)
1624 {
1625         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1626         GList         *list;
1627         GObject       *action;
1628         guint          i;
1629         const gchar *actions_connected[] = {
1630                 "room_join_new",
1631                 "room_join_favorites",
1632                 "chat_new_message",
1633                 "chat_new_call",
1634                 "chat_add_contact",
1635                 "edit_personal_information",
1636                 "edit_blocked_contacts"
1637         };
1638
1639         for (i = 0, list = NULL; i < G_N_ELEMENTS (actions_connected); i++) {
1640                 action = gtk_builder_get_object (gui, actions_connected[i]);
1641                 list = g_list_prepend (list, action);
1642         }
1643
1644         priv->actions_connected = list;
1645 }
1646
1647 static void
1648 account_manager_prepared_cb (GObject      *source_object,
1649                              GAsyncResult *result,
1650                              gpointer      user_data)
1651 {
1652         GList *accounts, *j;
1653         TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
1654         EmpathyMainWindow *window = user_data;
1655         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1656         GError *error = NULL;
1657
1658         if (!tp_account_manager_prepare_finish (manager, result, &error)) {
1659                 DEBUG ("Failed to prepare account manager: %s", error->message);
1660                 g_error_free (error);
1661                 return;
1662         }
1663
1664         accounts = tp_account_manager_get_valid_accounts (priv->account_manager);
1665         for (j = accounts; j != NULL; j = j->next) {
1666                 TpAccount *account = TP_ACCOUNT (j->data);
1667                 gulong handler_id;
1668
1669                 handler_id = g_signal_connect (account, "status-changed",
1670                                   G_CALLBACK (main_window_connection_changed_cb),
1671                                   window);
1672                 g_hash_table_insert (priv->status_changed_handlers,
1673                                      account, GUINT_TO_POINTER (handler_id));
1674         }
1675
1676         g_signal_connect (manager, "account-validity-changed",
1677                           G_CALLBACK (main_window_account_validity_changed_cb),
1678                           window);
1679
1680         main_window_update_status (window);
1681
1682         /* Disable the "Previous Conversations" menu entry if there is no account */
1683         gtk_action_set_sensitive (priv->view_history,
1684                 g_list_length (accounts) > 0);
1685
1686         g_list_free (accounts);
1687 }
1688
1689 static void
1690 main_window_members_changed_cb (EmpathyContactList *list,
1691                                 EmpathyContact     *contact,
1692                                 EmpathyContact     *actor,
1693                                 guint               reason,
1694                                 gchar              *message,
1695                                 gboolean            is_member,
1696                                 EmpathyMainWindow  *window)
1697 {
1698         EmpathyMainWindowPriv *priv = GET_PRIV (window);
1699
1700         if (!is_member)
1701                 return;
1702
1703         if (!empathy_migrate_butterfly_logs (contact)) {
1704                 g_signal_handler_disconnect (list,
1705                         priv->butterfly_log_migration_members_changed_id);
1706                 priv->butterfly_log_migration_members_changed_id = 0;
1707         }
1708 }
1709
1710 static GObject *
1711 empathy_main_window_constructor (GType type,
1712                                  guint n_construct_params,
1713                                  GObjectConstructParam *construct_params)
1714 {
1715         static GObject *window = NULL;
1716
1717         if (window != NULL)
1718                 return g_object_ref (window);
1719
1720         window = G_OBJECT_CLASS (empathy_main_window_parent_class)->constructor (
1721                 type, n_construct_params, construct_params);
1722
1723         g_object_add_weak_pointer (window, (gpointer) &window);
1724
1725         return window;
1726 }
1727
1728 static void
1729 empathy_main_window_class_init (EmpathyMainWindowClass *klass)
1730 {
1731         GObjectClass *object_class = G_OBJECT_CLASS (klass);
1732
1733         object_class->finalize = empathy_main_window_finalize;
1734         object_class->constructor = empathy_main_window_constructor;
1735
1736         g_type_class_add_private (object_class, sizeof (EmpathyMainWindowPriv));
1737 }
1738
1739 static void
1740 empathy_main_window_init (EmpathyMainWindow *window)
1741 {
1742         EmpathyMainWindowPriv    *priv;
1743         EmpathyIndividualManager *individual_manager;
1744         GtkBuilder               *gui;
1745         GtkWidget                *sw;
1746         GtkToggleAction          *show_offline_widget;
1747         GtkAction                *show_map_widget;
1748         GtkToolItem              *item;
1749         gboolean                  show_offline;
1750         gchar                    *filename;
1751         GSList                   *l;
1752         GtkTreeModel             *model;
1753
1754         priv = window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1755                         EMPATHY_TYPE_MAIN_WINDOW, EmpathyMainWindowPriv);
1756
1757         priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1758         priv->gsettings_contacts = g_settings_new (EMPATHY_PREFS_CONTACTS_SCHEMA);
1759
1760         priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1761
1762         gtk_window_set_title (GTK_WINDOW (window), _("Contact List"));
1763         gtk_window_set_role (GTK_WINDOW (window), "contact_list");
1764         gtk_window_set_default_size (GTK_WINDOW (window), 225, 325);
1765
1766         /* Set up interface */
1767         filename = empathy_file_lookup ("empathy-main-window.ui", "src");
1768         gui = empathy_builder_get_file (filename,
1769                                        "main_vbox", &priv->main_vbox,
1770                                        "errors_vbox", &priv->errors_vbox,
1771                                        "auth_vbox", &priv->auth_vbox,
1772                                        "ui_manager", &priv->ui_manager,
1773                                        "view_show_offline", &show_offline_widget,
1774                                        "view_show_protocols", &priv->show_protocols,
1775                                        "view_sort_by_name", &priv->sort_by_name,
1776                                        "view_sort_by_status", &priv->sort_by_status,
1777                                        "view_normal_size_with_avatars", &priv->normal_with_avatars,
1778                                        "view_normal_size", &priv->normal_size,
1779                                        "view_compact_size", &priv->compact_size,
1780                                        "view_history", &priv->view_history,
1781                                        "view_show_map", &show_map_widget,
1782                                        "room_join_favorites", &priv->room_join_favorites,
1783                                        "presence_toolbar", &priv->presence_toolbar,
1784                                        "notebook", &priv->notebook,
1785                                        "no_entry_label", &priv->no_entry_label,
1786                                        "roster_scrolledwindow", &sw,
1787                                        NULL);
1788         g_free (filename);
1789
1790         gtk_container_add (GTK_CONTAINER (window), priv->main_vbox);
1791         gtk_widget_show (priv->main_vbox);
1792
1793         g_signal_connect (window, "key-press-event",
1794                           G_CALLBACK (main_window_key_press_event_cb), NULL);
1795
1796         empathy_builder_connect (gui, window,
1797                               "chat_quit", "activate", main_window_chat_quit_cb,
1798                               "chat_new_message", "activate", main_window_chat_new_message_cb,
1799                               "chat_new_call", "activate", main_window_chat_new_call_cb,
1800                               "view_history", "activate", main_window_view_history_cb,
1801                               "room_join_new", "activate", main_window_room_join_new_cb,
1802                               "room_join_favorites", "activate", main_window_room_join_favorites_cb,
1803                               "room_manage_favorites", "activate", main_window_room_manage_favorites_cb,
1804                               "chat_add_contact", "activate", main_window_chat_add_contact_cb,
1805                               "chat_search_contacts", "activate", main_window_chat_search_contacts_cb,
1806                               "view_show_ft_manager", "activate", main_window_view_show_ft_manager,
1807                               "view_show_offline", "toggled", main_window_view_show_offline_cb,
1808                               "view_show_protocols", "toggled", main_window_view_show_protocols_cb,
1809                               "view_sort_by_name", "changed", main_window_view_sort_contacts_cb,
1810                               "view_normal_size_with_avatars", "changed", main_window_view_contacts_list_size_cb,
1811                               "view_show_map", "activate", main_window_view_show_map_cb,
1812                               "edit", "activate", main_window_edit_cb,
1813                               "edit_accounts", "activate", main_window_edit_accounts_cb,
1814                               "edit_personal_information", "activate", main_window_edit_personal_information_cb,
1815                               "edit_blocked_contacts", "activate", main_window_edit_blocked_contacts_cb,
1816                               "edit_preferences", "activate", main_window_edit_preferences_cb,
1817                               "edit_search_contacts", "activate", main_window_edit_search_contacts_cb,
1818                               "help_about", "activate", main_window_help_about_cb,
1819                               "help_debug", "activate", main_window_help_debug_cb,
1820                               "help_contents", "activate", main_window_help_contents_cb,
1821                               NULL);
1822
1823         /* Set up connection related widgets. */
1824         main_window_connection_items_setup (window, gui);
1825
1826         g_object_ref (priv->ui_manager);
1827         g_object_unref (gui);
1828
1829 #ifndef HAVE_LIBCHAMPLAIN
1830         gtk_action_set_visible (show_map_widget, FALSE);
1831 #endif
1832
1833         priv->account_manager = tp_account_manager_dup ();
1834
1835         tp_account_manager_prepare_async (priv->account_manager, NULL,
1836                                           account_manager_prepared_cb, window);
1837
1838         priv->errors = g_hash_table_new_full (g_direct_hash,
1839                                               g_direct_equal,
1840                                               g_object_unref,
1841                                               NULL);
1842
1843         priv->auths = g_hash_table_new (NULL, NULL);
1844
1845         priv->status_changed_handlers = g_hash_table_new_full (g_direct_hash,
1846                                                                g_direct_equal,
1847                                                                NULL,
1848                                                                NULL);
1849
1850         /* Set up menu */
1851         main_window_favorite_chatroom_menu_setup (window);
1852
1853         priv->edit_context = gtk_ui_manager_get_widget (priv->ui_manager,
1854                 "/menubar/edit/edit_context");
1855         priv->edit_context_separator = gtk_ui_manager_get_widget (
1856                 priv->ui_manager,
1857                 "/menubar/edit/edit_context_separator");
1858         gtk_widget_hide (priv->edit_context);
1859         gtk_widget_hide (priv->edit_context_separator);
1860
1861         /* Set up contact list. */
1862         empathy_status_presets_get_all ();
1863
1864         /* Set up presence chooser */
1865         priv->presence_chooser = empathy_presence_chooser_new ();
1866         gtk_widget_show (priv->presence_chooser);
1867         item = gtk_tool_item_new ();
1868         gtk_widget_show (GTK_WIDGET (item));
1869         gtk_widget_set_size_request (priv->presence_chooser, 10, -1);
1870         gtk_container_add (GTK_CONTAINER (item), priv->presence_chooser);
1871         gtk_tool_item_set_is_important (item, TRUE);
1872         gtk_tool_item_set_expand (item, TRUE);
1873         gtk_toolbar_insert (GTK_TOOLBAR (priv->presence_toolbar), item, -1);
1874
1875         /* Set up the throbber */
1876         priv->throbber = gtk_spinner_new ();
1877         gtk_widget_set_size_request (priv->throbber, 16, -1);
1878         gtk_widget_set_tooltip_text (priv->throbber, _("Show and edit accounts"));
1879         gtk_widget_set_events (priv->throbber, GDK_BUTTON_PRESS_MASK);
1880         g_signal_connect (priv->throbber, "button-press-event",
1881                 G_CALLBACK (main_window_throbber_button_press_event_cb),
1882                 window);
1883         gtk_widget_show (priv->throbber);
1884
1885         item = gtk_tool_item_new ();
1886         gtk_container_set_border_width (GTK_CONTAINER (item), 6);
1887         gtk_toolbar_insert (GTK_TOOLBAR (priv->presence_toolbar), item, -1);
1888         gtk_container_add (GTK_CONTAINER (item), priv->throbber);
1889         priv->throbber_tool_item = GTK_WIDGET (item);
1890
1891         /* XXX: this class is designed to live for the duration of the program,
1892          * so it's got a race condition between its signal handlers and its
1893          * finalization. The class is planned to be removed, so we won't fix
1894          * this before then. */
1895         priv->contact_manager = EMPATHY_CONTACT_LIST (
1896                         empathy_contact_manager_dup_singleton ());
1897         individual_manager = empathy_individual_manager_dup_singleton ();
1898         priv->individual_store = empathy_individual_store_new (
1899                         individual_manager);
1900         g_object_unref (individual_manager);
1901
1902         /* For the moment, we disallow Persona drops onto the main contact list (e.g. from things such as
1903          * the EmpathyPersonaView in the linking dialogue). No code is hooked up to do anything on a Persona
1904          * drop, so allowing them would achieve nothing except confusion. */
1905         priv->individual_view = empathy_individual_view_new (
1906                         priv->individual_store,
1907                         EMPATHY_INDIVIDUAL_VIEW_FEATURE_ALL ^ EMPATHY_INDIVIDUAL_VIEW_FEATURE_PERSONA_DROP,
1908                         EMPATHY_INDIVIDUAL_FEATURE_ALL);
1909
1910         priv->butterfly_log_migration_members_changed_id = g_signal_connect (
1911                         priv->contact_manager, "members-changed",
1912                         G_CALLBACK (main_window_members_changed_cb), window);
1913
1914         gtk_widget_show (GTK_WIDGET (priv->individual_view));
1915         gtk_container_add (GTK_CONTAINER (sw),
1916                            GTK_WIDGET (priv->individual_view));
1917         g_signal_connect (priv->individual_view, "row-activated",
1918                           G_CALLBACK (main_window_row_activated_cb),
1919                           window);
1920
1921         /* Set up search bar */
1922         priv->search_bar = empathy_live_search_new (
1923                 GTK_WIDGET (priv->individual_view));
1924         empathy_individual_view_set_live_search (priv->individual_view,
1925                 EMPATHY_LIVE_SEARCH (priv->search_bar));
1926         gtk_box_pack_start (GTK_BOX (priv->main_vbox), priv->search_bar,
1927                 FALSE, TRUE, 0);
1928         g_signal_connect_swapped (window, "map",
1929                 G_CALLBACK (gtk_widget_grab_focus), priv->individual_view);
1930
1931         /* Connect to proper signals to check if contact list is empty or not */
1932         model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->individual_view));
1933         priv->empty = TRUE;
1934         g_signal_connect (model, "row-inserted",
1935                           G_CALLBACK (main_window_row_inserted_cb),
1936                           window);
1937         g_signal_connect (model, "row-deleted",
1938                           G_CALLBACK (main_window_row_deleted_cb),
1939                           window);
1940
1941         /* Load user-defined accelerators. */
1942         main_window_accels_load ();
1943
1944         /* Set window size. */
1945         empathy_geometry_bind (GTK_WINDOW (window), GEOMETRY_NAME);
1946
1947         /* Enable event handling */
1948         priv->event_manager = empathy_event_manager_dup_singleton ();
1949
1950         g_signal_connect (priv->event_manager, "event-added",
1951                           G_CALLBACK (main_window_event_added_cb), window);
1952         g_signal_connect (priv->event_manager, "event-removed",
1953                           G_CALLBACK (main_window_event_removed_cb), window);
1954         g_signal_connect (priv->account_manager, "account-validity-changed",
1955                           G_CALLBACK (main_window_account_validity_changed_cb),
1956                           window);
1957         g_signal_connect (priv->account_manager, "account-removed",
1958                           G_CALLBACK (main_window_account_removed_cb),
1959                           window);
1960         g_signal_connect (priv->account_manager, "account-disabled",
1961                           G_CALLBACK (main_window_account_disabled_cb),
1962                           window);
1963
1964         l = empathy_event_manager_get_events (priv->event_manager);
1965         while (l) {
1966                 main_window_event_added_cb (priv->event_manager, l->data,
1967                                 window);
1968                 l = l->next;
1969         }
1970
1971         /* Show offline ? */
1972         show_offline = g_settings_get_boolean (priv->gsettings_ui,
1973                                                EMPATHY_PREFS_UI_SHOW_OFFLINE);
1974         g_signal_connect (priv->gsettings_ui,
1975                           "changed::" EMPATHY_PREFS_UI_SHOW_OFFLINE,
1976                           G_CALLBACK (main_window_notify_show_offline_cb),
1977                           show_offline_widget);
1978
1979         gtk_toggle_action_set_active (show_offline_widget, show_offline);
1980
1981         /* Show protocol ? */
1982         g_signal_connect (priv->gsettings_ui,
1983                           "changed::" EMPATHY_PREFS_UI_SHOW_PROTOCOLS,
1984                           G_CALLBACK (main_window_notify_show_protocols_cb),
1985                           window);
1986
1987         main_window_notify_show_protocols_cb (priv->gsettings_ui,
1988                                               EMPATHY_PREFS_UI_SHOW_PROTOCOLS,
1989                                               window);
1990
1991         /* Sort by name / by status ? */
1992         g_signal_connect (priv->gsettings_contacts,
1993                           "changed::" EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM,
1994                           G_CALLBACK (main_window_notify_sort_contact_cb),
1995                           window);
1996
1997         main_window_notify_sort_contact_cb (priv->gsettings_contacts,
1998                                             EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM,
1999                                             window);
2000
2001         /* Contacts list size */
2002         g_signal_connect (priv->gsettings_ui,
2003                           "changed::" EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST,
2004                           G_CALLBACK (main_window_notify_contact_list_size_cb),
2005                           window);
2006         g_signal_connect (priv->gsettings_ui,
2007                           "changed::" EMPATHY_PREFS_UI_SHOW_AVATARS,
2008                           G_CALLBACK (main_window_notify_contact_list_size_cb),
2009                           window);
2010
2011         main_window_notify_contact_list_size_cb (priv->gsettings_ui,
2012                                                  EMPATHY_PREFS_UI_SHOW_AVATARS,
2013                                                  window);
2014 }
2015
2016 GtkWidget *
2017 empathy_main_window_dup (void)
2018 {
2019         return g_object_new (EMPATHY_TYPE_MAIN_WINDOW, NULL);
2020 }