]> git.0d.be Git - empathy.git/blob - src/empathy-main-window.c
Use EmpathyLiveSearch in main window's contact list
[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  */
23
24 #include <config.h>
25
26 #include <sys/stat.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <glib/gi18n.h>
30
31 #include <telepathy-glib/account-manager.h>
32
33 #include <libempathy/empathy-contact.h>
34 #include <libempathy/empathy-idle.h>
35 #include <libempathy/empathy-utils.h>
36 #include <libempathy/empathy-dispatcher.h>
37 #include <libempathy/empathy-chatroom-manager.h>
38 #include <libempathy/empathy-chatroom.h>
39 #include <libempathy/empathy-contact-list.h>
40 #include <libempathy/empathy-contact-manager.h>
41 #include <libempathy/empathy-status-presets.h>
42
43 #include <libempathy-gtk/empathy-conf.h>
44 #include <libempathy-gtk/empathy-contact-dialogs.h>
45 #include <libempathy-gtk/empathy-contact-list-store.h>
46 #include <libempathy-gtk/empathy-contact-list-view.h>
47 #include <libempathy-gtk/empathy-live-search.h>
48 #include <libempathy-gtk/empathy-geometry.h>
49 #include <libempathy-gtk/empathy-gtk-enum-types.h>
50 #include <libempathy-gtk/empathy-new-message-dialog.h>
51 #include <libempathy-gtk/empathy-new-call-dialog.h>
52 #include <libempathy-gtk/empathy-log-window.h>
53 #include <libempathy-gtk/empathy-presence-chooser.h>
54 #include <libempathy-gtk/empathy-sound.h>
55 #include <libempathy-gtk/empathy-ui-utils.h>
56
57 #include "empathy-accounts-dialog.h"
58 #include "empathy-chat-manager.h"
59 #include "empathy-main-window.h"
60 #include "empathy-preferences.h"
61 #include "empathy-about-dialog.h"
62 #include "empathy-debug-window.h"
63 #include "empathy-new-chatroom-dialog.h"
64 #include "empathy-map-view.h"
65 #include "empathy-chatrooms-window.h"
66 #include "empathy-event-manager.h"
67 #include "empathy-ft-manager.h"
68 #include "empathy-migrate-butterfly-logs.h"
69
70 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
71 #include <libempathy/empathy-debug.h>
72
73 /* Flashing delay for icons (milliseconds). */
74 #define FLASH_TIMEOUT 500
75
76 /* Minimum width of roster window if something goes wrong. */
77 #define MIN_WIDTH 50
78
79 /* Accels (menu shortcuts) can be configured and saved */
80 #define ACCELS_FILENAME "accels.txt"
81
82 /* Name in the geometry file */
83 #define GEOMETRY_NAME "main-window"
84
85 typedef struct {
86         EmpathyContactListView  *list_view;
87         EmpathyContactListStore *list_store;
88         TpAccountManager        *account_manager;
89         EmpathyChatroomManager  *chatroom_manager;
90         EmpathyEventManager     *event_manager;
91         guint                    flash_timeout_id;
92         gboolean                 flash_on;
93
94         GtkWidget              *window;
95         GtkWidget              *main_vbox;
96         GtkWidget              *throbber;
97         GtkWidget              *throbber_tool_item;
98         GtkWidget              *presence_toolbar;
99         GtkWidget              *presence_chooser;
100         GtkWidget              *errors_vbox;
101         GtkWidget              *search_bar;
102
103         GtkToggleAction        *show_protocols;
104         GtkRadioAction         *sort_by_name;
105         GtkRadioAction         *sort_by_status;
106         GtkRadioAction         *normal_with_avatars;
107         GtkRadioAction         *normal_size;
108         GtkRadioAction         *compact_size;
109
110         GtkUIManager           *ui_manager;
111         GtkAction              *view_history;
112         GtkAction              *room_join_favorites;
113         GtkWidget              *room_menu;
114         GtkWidget              *room_separator;
115         GtkWidget              *edit_context;
116         GtkWidget              *edit_context_separator;
117
118         guint                   size_timeout_id;
119         GHashTable             *errors;
120
121         /* stores a mapping from TpAccount to Handler ID to prevent
122          * to listen more than once to the status-changed signal */
123         GHashTable             *status_changed_handlers;
124
125         /* Actions that are enabled when there are connected accounts */
126         GList                  *actions_connected;
127
128         /* The idle event source to migrate butterfly's logs */
129         guint butterfly_log_migration_members_changed_id;
130 } EmpathyMainWindow;
131
132 static EmpathyMainWindow *main_window = NULL;
133
134 static void
135 main_window_flash_stop (EmpathyMainWindow *window)
136 {
137         if (window->flash_timeout_id == 0) {
138                 return;
139         }
140
141         DEBUG ("Stop flashing");
142         g_source_remove (window->flash_timeout_id);
143         window->flash_timeout_id = 0;
144         window->flash_on = FALSE;
145 }
146
147 typedef struct {
148         EmpathyEvent       *event;
149         gboolean            on;
150         EmpathyMainWindow  *window;
151 } FlashForeachData;
152
153 static gboolean
154 main_window_flash_foreach (GtkTreeModel *model,
155                            GtkTreePath  *path,
156                            GtkTreeIter  *iter,
157                            gpointer      user_data)
158 {
159         FlashForeachData *data = (FlashForeachData *) user_data;
160         EmpathyContact   *contact;
161         const gchar      *icon_name;
162         GtkTreePath      *parent_path = NULL;
163         GtkTreeIter       parent_iter;
164         GdkPixbuf        *pixbuf = NULL;
165
166         /* To be used with gtk_tree_model_foreach, update the status icon
167          * of the contact to show the event icon (on=TRUE) or the presence
168          * (on=FALSE) */
169         gtk_tree_model_get (model, iter,
170                             EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
171                             -1);
172
173         if (contact != data->event->contact) {
174                 if (contact) {
175                         g_object_unref (contact);
176                 }
177                 return FALSE;
178         }
179
180         if (data->on) {
181                 icon_name = data->event->icon_name;
182                 pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
183         } else {
184                 pixbuf = contact_list_store_get_contact_status_icon (
185                                                 data->window->list_store,
186                                                 contact);
187         }
188
189         gtk_tree_store_set (GTK_TREE_STORE (model), iter,
190                             EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, pixbuf,
191                             -1);
192
193         /* To make sure the parent is shown correctly, we emit
194          * the row-changed signal on the parent so it prompts
195          * it to be refreshed by the filter func.
196          */
197         if (gtk_tree_model_iter_parent (model, &parent_iter, iter)) {
198                 parent_path = gtk_tree_model_get_path (model, &parent_iter);
199         }
200         if (parent_path) {
201                 gtk_tree_model_row_changed (model, parent_path, &parent_iter);
202                 gtk_tree_path_free (parent_path);
203         }
204
205         g_object_unref (contact);
206
207         return FALSE;
208 }
209
210 static gboolean
211 main_window_flash_cb (EmpathyMainWindow *window)
212 {
213         GtkTreeModel     *model;
214         GSList           *events, *l;
215         gboolean          found_event = FALSE;
216         FlashForeachData  data;
217
218         window->flash_on = !window->flash_on;
219         data.on = window->flash_on;
220         model = GTK_TREE_MODEL (window->list_store);
221
222         events = empathy_event_manager_get_events (window->event_manager);
223         for (l = events; l; l = l->next) {
224                 data.event = l->data;
225                 data.window = window;
226                 if (!data.event->contact || !data.event->must_ack) {
227                         continue;
228                 }
229
230                 found_event = TRUE;
231                 gtk_tree_model_foreach (model,
232                                         main_window_flash_foreach,
233                                         &data);
234         }
235
236         if (!found_event) {
237                 main_window_flash_stop (window);
238         }
239
240         return TRUE;
241 }
242
243 static void
244 main_window_flash_start (EmpathyMainWindow *window)
245 {
246         if (window->flash_timeout_id != 0) {
247                 return;
248         }
249
250         DEBUG ("Start flashing");
251         window->flash_timeout_id = g_timeout_add (FLASH_TIMEOUT,
252                                                   (GSourceFunc) main_window_flash_cb,
253                                                   window);
254         main_window_flash_cb (window);
255 }
256
257 static void
258 main_window_event_added_cb (EmpathyEventManager *manager,
259                             EmpathyEvent        *event,
260                             EmpathyMainWindow   *window)
261 {
262         if (event->contact) {
263                 main_window_flash_start (window);
264         }
265 }
266
267 static void
268 main_window_event_removed_cb (EmpathyEventManager *manager,
269                               EmpathyEvent        *event,
270                               EmpathyMainWindow   *window)
271 {
272         FlashForeachData data;
273
274         if (!event->contact) {
275                 return;
276         }
277
278         data.on = FALSE;
279         data.event = event;
280         data.window = window;
281         gtk_tree_model_foreach (GTK_TREE_MODEL (window->list_store),
282                                 main_window_flash_foreach,
283                                 &data);
284 }
285
286 static void
287 main_window_row_activated_cb (EmpathyContactListView *view,
288                               GtkTreePath            *path,
289                               GtkTreeViewColumn      *col,
290                               EmpathyMainWindow      *window)
291 {
292         EmpathyContact *contact;
293         GtkTreeModel   *model;
294         GtkTreeIter     iter;
295         GSList         *events, *l;
296
297         model = gtk_tree_view_get_model (GTK_TREE_VIEW (window->list_view));
298         gtk_tree_model_get_iter (model, &iter, path);
299         gtk_tree_model_get (model, &iter,
300                             EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
301                             -1);
302
303         if (!contact) {
304                 return;
305         }
306
307         /* If the contact has an event activate it, otherwise the
308          * default handler of row-activated will be called. */
309         events = empathy_event_manager_get_events (window->event_manager);
310         for (l = events; l; l = l->next) {
311                 EmpathyEvent *event = l->data;
312
313                 if (event->contact == contact) {
314                         DEBUG ("Activate event");
315                         empathy_event_activate (event);
316
317                         /* We don't want the default handler of this signal
318                          * (e.g. open a chat) */
319                         g_signal_stop_emission_by_name (view, "row-activated");
320                         break;
321                 }
322         }
323
324         g_object_unref (contact);
325 }
326
327 static void
328 main_window_remove_error (EmpathyMainWindow *window,
329                           TpAccount         *account)
330 {
331         GtkWidget *error_widget;
332
333         error_widget = g_hash_table_lookup (window->errors, account);
334         if (error_widget != NULL) {
335                 gtk_widget_destroy (error_widget);
336                 g_hash_table_remove (window->errors, account);
337         }
338 }
339
340 static void
341 main_window_account_disabled_cb (TpAccountManager  *manager,
342                                  TpAccount         *account,
343                                  EmpathyMainWindow *window)
344 {
345         main_window_remove_error (window, account);
346 }
347
348 static void
349 main_window_error_retry_clicked_cb (GtkButton         *button,
350                                     EmpathyMainWindow *window)
351 {
352         TpAccount *account;
353
354         account = g_object_get_data (G_OBJECT (button), "account");
355         tp_account_reconnect_async (account, NULL, NULL);
356
357         main_window_remove_error (window, account);
358 }
359
360 static void
361 main_window_error_edit_clicked_cb (GtkButton         *button,
362                                    EmpathyMainWindow *window)
363 {
364         TpAccount *account;
365
366         account = g_object_get_data (G_OBJECT (button), "account");
367
368         empathy_accounts_dialog_show_application (
369                         gtk_widget_get_screen (GTK_WIDGET (button)),
370                         account, FALSE, FALSE);
371
372         main_window_remove_error (window, account);
373 }
374
375 static void
376 main_window_error_close_clicked_cb (GtkButton         *button,
377                                     EmpathyMainWindow *window)
378 {
379         TpAccount *account;
380
381         account = g_object_get_data (G_OBJECT (button), "account");
382         main_window_remove_error (window, account);
383 }
384
385 static void
386 main_window_error_display (EmpathyMainWindow *window,
387                            TpAccount         *account,
388                            const gchar       *message)
389 {
390         GtkWidget *info_bar;
391         GtkWidget *content_area;
392         GtkWidget *label;
393         GtkWidget *image;
394         GtkWidget *retry_button;
395         GtkWidget *edit_button;
396         GtkWidget *close_button;
397         GtkWidget *action_area;
398         GtkWidget *action_table;
399         gchar     *str;
400         const gchar     *icon_name;
401
402         str = g_markup_printf_escaped ("<b>%s</b>\n%s",
403                                                tp_account_get_display_name (account),
404                                                message);
405
406         info_bar = g_hash_table_lookup (window->errors, account);
407         if (info_bar) {
408                 label = g_object_get_data (G_OBJECT (info_bar), "label");
409
410                 /* Just set the latest error and return */
411                 gtk_label_set_markup (GTK_LABEL (label), str);
412                 g_free (str);
413
414                 return;
415         }
416
417         info_bar = gtk_info_bar_new ();
418         gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_ERROR);
419
420         gtk_widget_set_no_show_all (info_bar, TRUE);
421         gtk_box_pack_start (GTK_BOX (window->errors_vbox), info_bar, FALSE, TRUE, 0);
422         gtk_widget_show (info_bar);
423
424         icon_name = tp_account_get_icon_name (account);
425         image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR);
426         gtk_widget_show (image);
427
428         label = gtk_label_new (str);
429         gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
430         gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
431         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
432         gtk_widget_show (label);
433         g_free (str);
434
435         content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
436         gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0);
437         gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0);
438
439         image = gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON);
440         retry_button = gtk_button_new ();
441         gtk_button_set_image (GTK_BUTTON (retry_button), image);
442         gtk_widget_set_tooltip_text (retry_button, _("Reconnect"));
443         gtk_widget_show (retry_button);
444
445         image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON);
446         edit_button = gtk_button_new ();
447         gtk_button_set_image (GTK_BUTTON (edit_button), image);
448         gtk_widget_set_tooltip_text (edit_button, _("Edit Account"));
449         gtk_widget_show (edit_button);
450
451         image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON);
452         close_button = gtk_button_new ();
453         gtk_button_set_image (GTK_BUTTON (close_button), image);
454         gtk_widget_set_tooltip_text (close_button, _("Close"));
455         gtk_widget_show (close_button);
456
457         action_table = gtk_table_new (1, 3, FALSE);
458         gtk_table_set_col_spacings (GTK_TABLE (action_table), 2);
459         gtk_widget_show (action_table);
460
461         action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar));
462         gtk_box_pack_start (GTK_BOX (action_area), action_table, FALSE, FALSE, 0);
463
464         gtk_table_attach (GTK_TABLE (action_table), retry_button, 0, 1, 0, 1,
465                                                                                 (GtkAttachOptions) (GTK_SHRINK),
466                                                                                 (GtkAttachOptions) (GTK_SHRINK), 0, 0);
467         gtk_table_attach (GTK_TABLE (action_table), edit_button, 1, 2, 0, 1,
468                                                                                 (GtkAttachOptions) (GTK_SHRINK),
469                                                                                 (GtkAttachOptions) (GTK_SHRINK), 0, 0);
470         gtk_table_attach (GTK_TABLE (action_table), close_button, 2, 3, 0, 1,
471                                                                                 (GtkAttachOptions) (GTK_SHRINK),
472                                                                                 (GtkAttachOptions) (GTK_SHRINK), 0, 0);
473
474         g_object_set_data (G_OBJECT (info_bar), "label", label);
475         g_object_set_data_full (G_OBJECT (info_bar),
476                                 "account", g_object_ref (account),
477                                 g_object_unref);
478         g_object_set_data_full (G_OBJECT (edit_button),
479                                 "account", g_object_ref (account),
480                                 g_object_unref);
481         g_object_set_data_full (G_OBJECT (close_button),
482                                 "account", g_object_ref (account),
483                                 g_object_unref);
484         g_object_set_data_full (G_OBJECT (retry_button),
485                                 "account", g_object_ref (account),
486                                 g_object_unref);
487
488         g_signal_connect (edit_button, "clicked",
489                           G_CALLBACK (main_window_error_edit_clicked_cb),
490                           window);
491         g_signal_connect (close_button, "clicked",
492                           G_CALLBACK (main_window_error_close_clicked_cb),
493                           window);
494         g_signal_connect (retry_button, "clicked",
495                           G_CALLBACK (main_window_error_retry_clicked_cb),
496                           window);
497
498         gtk_widget_show (window->errors_vbox);
499
500         g_hash_table_insert (window->errors, g_object_ref (account), info_bar);
501 }
502
503 static void
504 main_window_update_status (EmpathyMainWindow *window)
505 {
506         gboolean connected, connecting;
507         GList *l;
508
509         connected = empathy_account_manager_get_accounts_connected (&connecting);
510
511         /* Update the spinner state */
512         if (connecting) {
513                 gtk_spinner_start (GTK_SPINNER (window->throbber));
514                 gtk_widget_show (window->throbber_tool_item);
515         } else {
516                 gtk_spinner_stop (GTK_SPINNER (window->throbber));
517                 gtk_widget_hide (window->throbber_tool_item);
518         }
519
520         /* Update widgets sensibility */
521         for (l = window->actions_connected; l; l = l->next) {
522                 gtk_action_set_sensitive (l->data, connected);
523         }
524 }
525
526 static void
527 main_window_connection_changed_cb (TpAccount  *account,
528                                    guint       old_status,
529                                    guint       current,
530                                    guint       reason,
531                                    gchar      *dbus_error_name,
532                                    GHashTable *details,
533                                    EmpathyMainWindow *window)
534 {
535         main_window_update_status (window);
536
537         if (current == TP_CONNECTION_STATUS_DISCONNECTED &&
538             reason != TP_CONNECTION_STATUS_REASON_REQUESTED) {
539                 const gchar *message;
540
541                 message = empathy_status_reason_get_default_message (reason);
542
543                 main_window_error_display (window, account, message);
544         }
545
546         if (current == TP_CONNECTION_STATUS_DISCONNECTED) {
547                 empathy_sound_play (GTK_WIDGET (window->window),
548                                     EMPATHY_SOUND_ACCOUNT_DISCONNECTED);
549         }
550
551         if (current == TP_CONNECTION_STATUS_CONNECTED) {
552                 empathy_sound_play (GTK_WIDGET (window->window),
553                                     EMPATHY_SOUND_ACCOUNT_CONNECTED);
554
555                 /* Account connected without error, remove error message if any */
556                 main_window_remove_error (window, account);
557         }
558 }
559
560 static void
561 main_window_accels_load (void)
562 {
563         gchar *filename;
564
565         filename = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, ACCELS_FILENAME, NULL);
566         if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
567                 DEBUG ("Loading from:'%s'", filename);
568                 gtk_accel_map_load (filename);
569         }
570
571         g_free (filename);
572 }
573
574 static void
575 main_window_accels_save (void)
576 {
577         gchar *dir;
578         gchar *file_with_path;
579
580         dir = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL);
581         g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
582         file_with_path = g_build_filename (dir, ACCELS_FILENAME, NULL);
583         g_free (dir);
584
585         DEBUG ("Saving to:'%s'", file_with_path);
586         gtk_accel_map_save (file_with_path);
587
588         g_free (file_with_path);
589 }
590
591 static void
592 main_window_destroy_cb (GtkWidget         *widget,
593                         EmpathyMainWindow *window)
594 {
595         GHashTableIter iter;
596         gpointer key, value;
597
598         /* Save user-defined accelerators. */
599         main_window_accels_save ();
600
601         g_list_free (window->actions_connected);
602
603         g_object_unref (window->account_manager);
604         g_object_unref (window->list_store);
605         g_hash_table_destroy (window->errors);
606
607         /* disconnect all handlers of status-changed signal */
608         g_hash_table_iter_init (&iter, window->status_changed_handlers);
609         while (g_hash_table_iter_next (&iter, &key, &value))
610                 g_signal_handler_disconnect (TP_ACCOUNT (key),
611                                              GPOINTER_TO_UINT (value));
612
613         g_hash_table_destroy (window->status_changed_handlers);
614
615         g_signal_handlers_disconnect_by_func (window->event_manager,
616                                               main_window_event_added_cb,
617                                               window);
618         g_signal_handlers_disconnect_by_func (window->event_manager,
619                                               main_window_event_removed_cb,
620                                               window);
621         g_object_unref (window->event_manager);
622         g_object_unref (window->ui_manager);
623         g_object_unref (window->chatroom_manager);
624
625         g_free (window);
626 }
627
628 static gboolean
629 main_window_key_press_event_cb  (GtkWidget   *window,
630                                  GdkEventKey *event,
631                                  gpointer     user_data)
632 {
633         EmpathyChatManager *chat_manager;
634
635         if (event->keyval == GDK_T
636             && event->state & GDK_SHIFT_MASK
637             && event->state & GDK_CONTROL_MASK) {
638                 chat_manager = empathy_chat_manager_dup_singleton ();
639                 empathy_chat_manager_undo_closed_chat (chat_manager);
640                 g_object_unref (chat_manager);
641         }
642         return FALSE;
643 }
644
645 static void
646 main_window_chat_quit_cb (GtkAction         *action,
647                           EmpathyMainWindow *window)
648 {
649         gtk_main_quit ();
650 }
651
652 static void
653 main_window_view_history_cb (GtkAction         *action,
654                              EmpathyMainWindow *window)
655 {
656         empathy_log_window_show (NULL, NULL, FALSE, GTK_WINDOW (window->window));
657 }
658
659 static void
660 main_window_chat_new_message_cb (GtkAction         *action,
661                                  EmpathyMainWindow *window)
662 {
663         empathy_new_message_dialog_show (GTK_WINDOW (window->window));
664 }
665
666 static void
667 main_window_chat_new_call_cb (GtkAction         *action,
668                               EmpathyMainWindow *window)
669 {
670         empathy_new_call_dialog_show (GTK_WINDOW (window->window));
671 }
672
673 static void
674 main_window_chat_add_contact_cb (GtkAction         *action,
675                                  EmpathyMainWindow *window)
676 {
677         empathy_new_contact_dialog_show (GTK_WINDOW (window->window));
678 }
679
680 static void
681 main_window_view_show_ft_manager (GtkAction         *action,
682                                   EmpathyMainWindow *window)
683 {
684         empathy_ft_manager_show ();
685 }
686
687 static void
688 main_window_view_show_offline_cb (GtkToggleAction   *action,
689                                   EmpathyMainWindow *window)
690 {
691         gboolean current;
692
693         current = gtk_toggle_action_get_active (action);
694         empathy_conf_set_bool (empathy_conf_get (),
695                               EMPATHY_PREFS_UI_SHOW_OFFLINE,
696                               current);
697
698         /* Turn off sound just while we alter the contact list. */
699         // FIXME: empathy_sound_set_enabled (FALSE);
700         empathy_contact_list_store_set_show_offline (window->list_store, current);
701         //empathy_sound_set_enabled (TRUE);
702 }
703
704 static void
705 main_window_notify_sort_contact_cb (EmpathyConf       *conf,
706                                     const gchar       *key,
707                                     EmpathyMainWindow *window)
708 {
709         gchar *str = NULL;
710
711         if (empathy_conf_get_string (conf, key, &str) && str) {
712                 GType       type;
713                 GEnumClass *enum_class;
714                 GEnumValue *enum_value;
715
716                 type = empathy_contact_list_store_sort_get_type ();
717                 enum_class = G_ENUM_CLASS (g_type_class_peek (type));
718                 enum_value = g_enum_get_value_by_nick (enum_class, str);
719                 if (enum_value) {
720                         /* By changing the value of the GtkRadioAction,
721                            it emits a signal that calls main_window_view_sort_contacts_cb
722                            which updates the contacts list */
723                         gtk_radio_action_set_current_value (window->sort_by_name,
724                                                             enum_value->value);
725                 } else {
726                         g_warning ("Wrong value for sort_criterium configuration : %s", str);
727                 }
728                 g_free (str);
729         }
730 }
731
732 static void
733 main_window_view_sort_contacts_cb (GtkRadioAction    *action,
734                                    GtkRadioAction    *current,
735                                    EmpathyMainWindow *window)
736 {
737         EmpathyContactListStoreSort value;
738         GSList      *group;
739         GType        type;
740         GEnumClass  *enum_class;
741         GEnumValue  *enum_value;
742
743         value = gtk_radio_action_get_current_value (action);
744         group = gtk_radio_action_get_group (action);
745
746         /* Get string from index */
747         type = empathy_contact_list_store_sort_get_type ();
748         enum_class = G_ENUM_CLASS (g_type_class_peek (type));
749         enum_value = g_enum_get_value (enum_class, g_slist_index (group, current));
750
751         if (!enum_value) {
752                 g_warning ("No GEnumValue for EmpathyContactListSort with GtkRadioAction index:%d",
753                            g_slist_index (group, action));
754         } else {
755                 empathy_conf_set_string (empathy_conf_get (),
756                                          EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM,
757                                          enum_value->value_nick);
758         }
759         empathy_contact_list_store_set_sort_criterium (window->list_store, value);
760 }
761
762 static void
763 main_window_view_show_protocols_cb (GtkToggleAction   *action,
764                                     EmpathyMainWindow *window)
765 {
766         gboolean value;
767
768         value = gtk_toggle_action_get_active (action);
769
770         empathy_conf_set_bool (empathy_conf_get (),
771                                          EMPATHY_PREFS_UI_SHOW_PROTOCOLS,
772                                          value == TRUE);
773         empathy_contact_list_store_set_show_protocols (window->list_store,
774                                          value == TRUE);
775 }
776
777 /* Matches GtkRadioAction values set in empathy-main-window.ui */
778 #define CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS           0
779 #define CONTACT_LIST_NORMAL_SIZE                        1
780 #define CONTACT_LIST_COMPACT_SIZE                       2
781
782 static void
783 main_window_view_contacts_list_size_cb (GtkRadioAction    *action,
784                                         GtkRadioAction    *current,
785                                         EmpathyMainWindow *window)
786 {
787         gint     value;
788
789         value = gtk_radio_action_get_current_value (action);
790
791         empathy_conf_set_bool (empathy_conf_get (),
792                                EMPATHY_PREFS_UI_SHOW_AVATARS,
793                                value == CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS);
794         empathy_conf_set_bool (empathy_conf_get (),
795                                EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST,
796                                value == CONTACT_LIST_COMPACT_SIZE);
797
798         empathy_contact_list_store_set_show_avatars (window->list_store,
799                                                      value == CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS);
800         empathy_contact_list_store_set_is_compact (window->list_store,
801                                                    value == CONTACT_LIST_COMPACT_SIZE);
802 }
803
804 static void main_window_notify_show_protocols_cb (EmpathyConf       *conf,
805                                                   const gchar       *key,
806                                                   EmpathyMainWindow *window)
807 {
808         gboolean show_protocols;
809
810         if (empathy_conf_get_bool (conf,
811                                    EMPATHY_PREFS_UI_SHOW_PROTOCOLS,
812                                    &show_protocols)) {
813                 gtk_toggle_action_set_active (window->show_protocols,
814                                               show_protocols);
815         }
816 }
817
818
819 static void
820 main_window_notify_contact_list_size_cb (EmpathyConf       *conf,
821                                          const gchar       *key,
822                                          EmpathyMainWindow *window)
823 {
824         gboolean show_avatars;
825         gboolean compact_contact_list;
826         gint value = CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS;
827
828         if (empathy_conf_get_bool (conf,
829                                    EMPATHY_PREFS_UI_SHOW_AVATARS,
830                                    &show_avatars)
831             && empathy_conf_get_bool (conf,
832                                       EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST,
833                                       &compact_contact_list)) {
834                 if (compact_contact_list) {
835                         value = CONTACT_LIST_COMPACT_SIZE;
836                 } else if (show_avatars) {
837                         value = CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS;
838                 } else {
839                         value = CONTACT_LIST_NORMAL_SIZE;
840                 }
841         }
842         /* By changing the value of the GtkRadioAction,
843            it emits a signal that calls main_window_view_contacts_list_size_cb
844            which updates the contacts list */
845         gtk_radio_action_set_current_value (window->normal_with_avatars, value);
846 }
847
848 static void
849 main_window_view_show_map_cb (GtkCheckMenuItem  *item,
850                               EmpathyMainWindow *window)
851 {
852 #if HAVE_LIBCHAMPLAIN
853         empathy_map_view_show ();
854 #endif
855 }
856
857 static void
858 join_chatroom (EmpathyChatroom *chatroom,
859                gint64 timestamp)
860 {
861         TpAccount      *account;
862         TpConnection   *connection;
863         const gchar    *room;
864
865         account = empathy_chatroom_get_account (chatroom);
866         connection = tp_account_get_connection (account);
867         g_assert (connection != NULL);
868         room = empathy_chatroom_get_room (chatroom);
869
870         DEBUG ("Requesting channel for '%s'", room);
871         empathy_dispatcher_join_muc (connection, room, timestamp, NULL, NULL);
872 }
873
874 typedef struct
875 {
876         TpAccount *account;
877         EmpathyChatroom *chatroom;
878         gint64 timestamp;
879         glong sig_id;
880         guint timeout;
881 } join_fav_account_sig_ctx;
882
883 static join_fav_account_sig_ctx *
884 join_fav_account_sig_ctx_new (TpAccount *account,
885                              EmpathyChatroom *chatroom,
886                               gint64 timestamp)
887 {
888         join_fav_account_sig_ctx *ctx = g_slice_new0 (
889                 join_fav_account_sig_ctx);
890
891         ctx->account = g_object_ref (account);
892         ctx->chatroom = g_object_ref (chatroom);
893         ctx->timestamp = timestamp;
894         return ctx;
895 }
896
897 static void
898 join_fav_account_sig_ctx_free (join_fav_account_sig_ctx *ctx)
899 {
900         g_object_unref (ctx->account);
901         g_object_unref (ctx->chatroom);
902         g_slice_free (join_fav_account_sig_ctx, ctx);
903 }
904
905 static void
906 account_status_changed_cb (TpAccount  *account,
907                            TpConnectionStatus old_status,
908                            TpConnectionStatus new_status,
909                            guint reason,
910                            gchar *dbus_error_name,
911                            GHashTable *details,
912                            gpointer user_data)
913 {
914         join_fav_account_sig_ctx *ctx = user_data;
915
916         switch (new_status) {
917                 case TP_CONNECTION_STATUS_DISCONNECTED:
918                         /* Don't wait any longer */
919                         goto finally;
920                         break;
921
922                 case TP_CONNECTION_STATUS_CONNECTING:
923                         /* Wait a bit */
924                         return;
925
926                 case TP_CONNECTION_STATUS_CONNECTED:
927                         /* We can join the room */
928                         break;
929         }
930
931         join_chatroom (ctx->chatroom, ctx->timestamp);
932
933 finally:
934         g_source_remove (ctx->timeout);
935         g_signal_handler_disconnect (account, ctx->sig_id);
936 }
937
938 #define JOIN_FAVORITE_TIMEOUT 5
939
940 static gboolean
941 join_favorite_timeout_cb (gpointer data)
942 {
943         join_fav_account_sig_ctx *ctx = data;
944
945         /* stop waiting for joining the favorite room */
946         g_signal_handler_disconnect (ctx->account, ctx->sig_id);
947         return FALSE;
948 }
949
950 static void
951 main_window_favorite_chatroom_join (EmpathyChatroom *chatroom)
952 {
953         TpAccount      *account;
954
955         account = empathy_chatroom_get_account (chatroom);
956         if (tp_account_get_connection_status (account, NULL) !=
957                                              TP_CONNECTION_STATUS_CONNECTED) {
958                 join_fav_account_sig_ctx *ctx;
959
960                 ctx = join_fav_account_sig_ctx_new (account, chatroom,
961                         gtk_get_current_event_time ());
962
963                 ctx->sig_id = g_signal_connect_data (account, "status-changed",
964                         G_CALLBACK (account_status_changed_cb), ctx,
965                         (GClosureNotify) join_fav_account_sig_ctx_free, 0);
966
967                 ctx->timeout = g_timeout_add_seconds (JOIN_FAVORITE_TIMEOUT,
968                         join_favorite_timeout_cb, ctx);
969                 return;
970         }
971
972         join_chatroom (chatroom, gtk_get_current_event_time ());
973 }
974
975 static void
976 main_window_favorite_chatroom_menu_activate_cb (GtkMenuItem     *menu_item,
977                                                 EmpathyChatroom *chatroom)
978 {
979         main_window_favorite_chatroom_join (chatroom);
980 }
981
982 static void
983 main_window_favorite_chatroom_menu_add (EmpathyMainWindow *window,
984                                         EmpathyChatroom   *chatroom)
985 {
986         GtkWidget   *menu_item;
987         const gchar *name;
988
989         if (g_object_get_data (G_OBJECT (chatroom), "menu_item")) {
990                 return;
991         }
992
993         name = empathy_chatroom_get_name (chatroom);
994         menu_item = gtk_menu_item_new_with_label (name);
995
996         g_object_set_data (G_OBJECT (chatroom), "menu_item", menu_item);
997         g_signal_connect (menu_item, "activate",
998                           G_CALLBACK (main_window_favorite_chatroom_menu_activate_cb),
999                           chatroom);
1000
1001         gtk_menu_shell_insert (GTK_MENU_SHELL (window->room_menu),
1002                                menu_item, 4);
1003
1004         gtk_widget_show (menu_item);
1005 }
1006
1007 static void
1008 main_window_favorite_chatroom_menu_added_cb (EmpathyChatroomManager *manager,
1009                                              EmpathyChatroom        *chatroom,
1010                                              EmpathyMainWindow      *window)
1011 {
1012         main_window_favorite_chatroom_menu_add (window, chatroom);
1013         gtk_widget_show (window->room_separator);
1014         gtk_action_set_sensitive (window->room_join_favorites, TRUE);
1015 }
1016
1017 static void
1018 main_window_favorite_chatroom_menu_removed_cb (EmpathyChatroomManager *manager,
1019                                                EmpathyChatroom        *chatroom,
1020                                                EmpathyMainWindow      *window)
1021 {
1022         GtkWidget *menu_item;
1023         GList *chatrooms;
1024
1025         menu_item = g_object_get_data (G_OBJECT (chatroom), "menu_item");
1026         g_object_set_data (G_OBJECT (chatroom), "menu_item", NULL);
1027         gtk_widget_destroy (menu_item);
1028
1029         chatrooms = empathy_chatroom_manager_get_chatrooms (window->chatroom_manager, NULL);
1030         if (chatrooms) {
1031                 gtk_widget_show (window->room_separator);
1032         } else {
1033                 gtk_widget_hide (window->room_separator);
1034         }
1035
1036         gtk_action_set_sensitive (window->room_join_favorites, chatrooms != NULL);
1037         g_list_free (chatrooms);
1038 }
1039
1040 static void
1041 main_window_favorite_chatroom_menu_setup (EmpathyMainWindow *window)
1042 {
1043         GList *chatrooms, *l;
1044         GtkWidget *room;
1045
1046         window->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1047         chatrooms = empathy_chatroom_manager_get_chatrooms (window->chatroom_manager, NULL);
1048         room = gtk_ui_manager_get_widget (window->ui_manager,
1049                 "/menubar/room");
1050         window->room_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (room));
1051         window->room_separator = gtk_ui_manager_get_widget (window->ui_manager,
1052                 "/menubar/room/room_separator");
1053
1054         for (l = chatrooms; l; l = l->next) {
1055                 main_window_favorite_chatroom_menu_add (window, l->data);
1056         }
1057
1058         if (!chatrooms) {
1059                 gtk_widget_hide (window->room_separator);
1060         }
1061
1062         gtk_action_set_sensitive (window->room_join_favorites, chatrooms != NULL);
1063
1064         g_signal_connect (window->chatroom_manager, "chatroom-added",
1065                           G_CALLBACK (main_window_favorite_chatroom_menu_added_cb),
1066                           window);
1067         g_signal_connect (window->chatroom_manager, "chatroom-removed",
1068                           G_CALLBACK (main_window_favorite_chatroom_menu_removed_cb),
1069                           window);
1070
1071         g_list_free (chatrooms);
1072 }
1073
1074 static void
1075 main_window_room_join_new_cb (GtkAction         *action,
1076                               EmpathyMainWindow *window)
1077 {
1078         empathy_new_chatroom_dialog_show (GTK_WINDOW (window->window));
1079 }
1080
1081 static void
1082 main_window_room_join_favorites_cb (GtkAction         *action,
1083                                     EmpathyMainWindow *window)
1084 {
1085         GList *chatrooms, *l;
1086
1087         chatrooms = empathy_chatroom_manager_get_chatrooms (window->chatroom_manager, NULL);
1088         for (l = chatrooms; l; l = l->next) {
1089                 main_window_favorite_chatroom_join (l->data);
1090         }
1091         g_list_free (chatrooms);
1092 }
1093
1094 static void
1095 main_window_room_manage_favorites_cb (GtkAction         *action,
1096                                       EmpathyMainWindow *window)
1097 {
1098         empathy_chatrooms_window_show (GTK_WINDOW (window->window));
1099 }
1100
1101 static void
1102 main_window_edit_cb (GtkAction         *action,
1103                      EmpathyMainWindow *window)
1104 {
1105         GtkWidget *submenu;
1106
1107         /* FIXME: It should use the UIManager to merge the contact/group submenu */
1108         submenu = empathy_contact_list_view_get_contact_menu (window->list_view);
1109         if (submenu) {
1110                 GtkMenuItem *item;
1111                 GtkWidget   *label;
1112
1113                 item = GTK_MENU_ITEM (window->edit_context);
1114                 label = gtk_bin_get_child (GTK_BIN (item));
1115                 gtk_label_set_text (GTK_LABEL (label), _("Contact"));
1116
1117                 gtk_widget_show (window->edit_context);
1118                 gtk_widget_show (window->edit_context_separator);
1119
1120                 gtk_menu_item_set_submenu (item, submenu);
1121
1122                 return;
1123         }
1124
1125         submenu = empathy_contact_list_view_get_group_menu (window->list_view);
1126         if (submenu) {
1127                 GtkMenuItem *item;
1128                 GtkWidget   *label;
1129
1130                 item = GTK_MENU_ITEM (window->edit_context);
1131                 label = gtk_bin_get_child (GTK_BIN (item));
1132                 gtk_label_set_text (GTK_LABEL (label), _("Group"));
1133
1134                 gtk_widget_show (window->edit_context);
1135                 gtk_widget_show (window->edit_context_separator);
1136
1137                 gtk_menu_item_set_submenu (item, submenu);
1138
1139                 return;
1140         }
1141
1142         gtk_widget_hide (window->edit_context);
1143         gtk_widget_hide (window->edit_context_separator);
1144
1145         return;
1146 }
1147
1148 static void
1149 main_window_edit_accounts_cb (GtkAction         *action,
1150                               EmpathyMainWindow *window)
1151 {
1152         empathy_accounts_dialog_show_application (gdk_screen_get_default (),
1153                         NULL, FALSE, FALSE);
1154 }
1155
1156 static void
1157 main_window_edit_personal_information_cb (GtkAction         *action,
1158                                           EmpathyMainWindow *window)
1159 {
1160         empathy_contact_personal_dialog_show (GTK_WINDOW (window->window));
1161 }
1162
1163 static void
1164 main_window_edit_preferences_cb (GtkAction         *action,
1165                                  EmpathyMainWindow *window)
1166 {
1167         empathy_preferences_show (GTK_WINDOW (window->window));
1168 }
1169
1170 static void
1171 main_window_help_about_cb (GtkAction         *action,
1172                            EmpathyMainWindow *window)
1173 {
1174         empathy_about_dialog_new (GTK_WINDOW (window->window));
1175 }
1176
1177 static void
1178 main_window_help_debug_cb (GtkAction         *action,
1179                            EmpathyMainWindow *window)
1180 {
1181         GdkScreen *screen = gdk_screen_get_default ();
1182         GError *error = NULL;
1183         gchar *argv[2] = { NULL, };
1184         gint i = 0;
1185         gchar *path;
1186
1187         g_return_if_fail (GDK_IS_SCREEN (screen));
1188
1189         /* Try to run from source directory if possible */
1190         path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "src",
1191                         "empathy-debugger", NULL);
1192
1193         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
1194                 g_free (path);
1195                 path = g_build_filename (BIN_DIR, "empathy-debugger", NULL);
1196         }
1197
1198         argv[i++] = path;
1199
1200         gdk_spawn_on_screen (screen, NULL, argv, NULL,
1201                         G_SPAWN_SEARCH_PATH,
1202                         NULL, NULL, NULL, &error);
1203
1204         if (error) {
1205                 g_warning ("Failed to open debug window: %s", error->message);
1206                 g_error_free (error);
1207         }
1208
1209         g_free (path);
1210 }
1211
1212 static void
1213 main_window_help_contents_cb (GtkAction         *action,
1214                               EmpathyMainWindow *window)
1215 {
1216         empathy_url_show (window->window, "ghelp:empathy");
1217 }
1218
1219 static gboolean
1220 main_window_throbber_button_press_event_cb (GtkWidget         *throbber,
1221                                             GdkEventButton    *event,
1222                                             EmpathyMainWindow *window)
1223 {
1224         if (event->type != GDK_BUTTON_PRESS ||
1225             event->button != 1) {
1226                 return FALSE;
1227         }
1228
1229         empathy_accounts_dialog_show_application (
1230                         gtk_widget_get_screen (GTK_WIDGET (throbber)),
1231                         NULL, FALSE, FALSE);
1232
1233         return FALSE;
1234 }
1235
1236 static void
1237 main_window_account_removed_cb (TpAccountManager  *manager,
1238                                 TpAccount         *account,
1239                                 EmpathyMainWindow *window)
1240 {
1241         GList *a;
1242
1243         a = tp_account_manager_get_valid_accounts (manager);
1244
1245         gtk_action_set_sensitive (window->view_history,
1246                 g_list_length (a) > 0);
1247
1248         g_list_free (a);
1249
1250         /* remove errors if any */
1251         main_window_remove_error (window, account);
1252 }
1253
1254 static void
1255 main_window_account_validity_changed_cb (TpAccountManager  *manager,
1256                                          TpAccount         *account,
1257                                          gboolean           valid,
1258                                          EmpathyMainWindow *window)
1259 {
1260         if (valid) {
1261                 gulong handler_id;
1262                 handler_id = GPOINTER_TO_UINT (g_hash_table_lookup (
1263                         window->status_changed_handlers, account));
1264
1265                 /* connect signal only if it was not connected yet */
1266                 if (handler_id == 0) {
1267                         handler_id = g_signal_connect (account,
1268                                 "status-changed",
1269                                 G_CALLBACK (main_window_connection_changed_cb),
1270                                 window);
1271                         g_hash_table_insert (window->status_changed_handlers,
1272                                 account, GUINT_TO_POINTER (handler_id));
1273                 }
1274         }
1275
1276         main_window_account_removed_cb (manager, account, window);
1277 }
1278
1279 static void
1280 main_window_notify_show_offline_cb (EmpathyConf *conf,
1281                                     const gchar *key,
1282                                     gpointer     toggle_action)
1283 {
1284         gboolean show_offline;
1285
1286         if (empathy_conf_get_bool (conf, key, &show_offline)) {
1287                 gtk_toggle_action_set_active (toggle_action, show_offline);
1288         }
1289 }
1290
1291 static void
1292 main_window_connection_items_setup (EmpathyMainWindow *window,
1293                                     GtkBuilder        *gui)
1294 {
1295         GList         *list;
1296         GObject       *action;
1297         guint          i;
1298         const gchar *actions_connected[] = {
1299                 "room",
1300                 "chat_new_message",
1301                 "chat_new_call",
1302                 "chat_add_contact",
1303                 "edit_personal_information"
1304         };
1305
1306         for (i = 0, list = NULL; i < G_N_ELEMENTS (actions_connected); i++) {
1307                 action = gtk_builder_get_object (gui, actions_connected[i]);
1308                 list = g_list_prepend (list, action);
1309         }
1310
1311         window->actions_connected = list;
1312 }
1313
1314 GtkWidget *
1315 empathy_main_window_get (void)
1316 {
1317   return main_window != NULL ? main_window->window : NULL;
1318 }
1319
1320 static void
1321 account_manager_prepared_cb (GObject      *source_object,
1322                              GAsyncResult *result,
1323                              gpointer      user_data)
1324 {
1325         GList *accounts, *j;
1326         TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
1327         EmpathyMainWindow *window = user_data;
1328         GError *error = NULL;
1329
1330         if (!tp_account_manager_prepare_finish (manager, result, &error)) {
1331                 DEBUG ("Failed to prepare account manager: %s", error->message);
1332                 g_error_free (error);
1333                 return;
1334         }
1335
1336         accounts = tp_account_manager_get_valid_accounts (window->account_manager);
1337         for (j = accounts; j != NULL; j = j->next) {
1338                 TpAccount *account = TP_ACCOUNT (j->data);
1339                 gulong handler_id;
1340
1341                 handler_id = g_signal_connect (account, "status-changed",
1342                                   G_CALLBACK (main_window_connection_changed_cb),
1343                                   window);
1344                 g_hash_table_insert (window->status_changed_handlers,
1345                                      account, GUINT_TO_POINTER (handler_id));
1346         }
1347
1348         g_signal_connect (manager, "account-validity-changed",
1349                           G_CALLBACK (main_window_account_validity_changed_cb),
1350                           window);
1351
1352         main_window_update_status (window);
1353
1354         /* Disable the "Previous Conversations" menu entry if there is no account */
1355         gtk_action_set_sensitive (window->view_history,
1356                 g_list_length (accounts) > 0);
1357
1358         g_list_free (accounts);
1359 }
1360
1361 static void
1362 main_window_members_changed_cb (EmpathyContactList *list,
1363                                 EmpathyContact     *contact,
1364                                 EmpathyContact     *actor,
1365                                 guint               reason,
1366                                 gchar              *message,
1367                                 gboolean            is_member,
1368                                 EmpathyMainWindow  *window)
1369 {
1370         if (!is_member)
1371                 return;
1372
1373         if (!empathy_migrate_butterfly_logs (contact)) {
1374                 g_signal_handler_disconnect (list,
1375                         window->butterfly_log_migration_members_changed_id);
1376                 window->butterfly_log_migration_members_changed_id = 0;
1377         }
1378 }
1379
1380 GtkWidget *
1381 empathy_main_window_show (void)
1382 {
1383         EmpathyMainWindow        *window;
1384         EmpathyContactList       *list_iface;
1385         GtkBuilder               *gui;
1386         EmpathyConf              *conf;
1387         GtkWidget                *sw;
1388         GtkToggleAction          *show_offline_widget;
1389         GtkAction                *show_map_widget;
1390         GtkToolItem              *item;
1391         gboolean                  show_offline;
1392         gchar                    *filename;
1393         GSList                   *l;
1394
1395         if (main_window) {
1396                 empathy_window_present (GTK_WINDOW (main_window->window));
1397                 return main_window->window;
1398         }
1399
1400         main_window = g_new0 (EmpathyMainWindow, 1);
1401         window = main_window;
1402
1403         /* Set up interface */
1404         filename = empathy_file_lookup ("empathy-main-window.ui", "src");
1405         gui = empathy_builder_get_file (filename,
1406                                        "main_window", &window->window,
1407                                        "main_vbox", &window->main_vbox,
1408                                        "errors_vbox", &window->errors_vbox,
1409                                        "ui_manager", &window->ui_manager,
1410                                        "view_show_offline", &show_offline_widget,
1411                                        "view_show_protocols", &window->show_protocols,
1412                                        "view_sort_by_name", &window->sort_by_name,
1413                                        "view_sort_by_status", &window->sort_by_status,
1414                                        "view_normal_size_with_avatars", &window->normal_with_avatars,
1415                                        "view_normal_size", &window->normal_size,
1416                                        "view_compact_size", &window->compact_size,
1417                                        "view_history", &window->view_history,
1418                                        "view_show_map", &show_map_widget,
1419                                        "room_join_favorites", &window->room_join_favorites,
1420                                        "presence_toolbar", &window->presence_toolbar,
1421                                        "roster_scrolledwindow", &sw,
1422                                        NULL);
1423         g_free (filename);
1424
1425         empathy_builder_connect (gui, window,
1426                               "main_window", "destroy", main_window_destroy_cb,
1427                               "main_window", "key-press-event", main_window_key_press_event_cb,
1428                               "chat_quit", "activate", main_window_chat_quit_cb,
1429                               "chat_new_message", "activate", main_window_chat_new_message_cb,
1430                               "chat_new_call", "activate", main_window_chat_new_call_cb,
1431                               "view_history", "activate", main_window_view_history_cb,
1432                               "room_join_new", "activate", main_window_room_join_new_cb,
1433                               "room_join_favorites", "activate", main_window_room_join_favorites_cb,
1434                               "room_manage_favorites", "activate", main_window_room_manage_favorites_cb,
1435                               "chat_add_contact", "activate", main_window_chat_add_contact_cb,
1436                               "view_show_ft_manager", "activate", main_window_view_show_ft_manager,
1437                               "view_show_offline", "toggled", main_window_view_show_offline_cb,
1438                               "view_show_protocols", "toggled", main_window_view_show_protocols_cb,
1439                               "view_sort_by_name", "changed", main_window_view_sort_contacts_cb,
1440                               "view_normal_size_with_avatars", "changed", main_window_view_contacts_list_size_cb,
1441                               "view_show_map", "activate", main_window_view_show_map_cb,
1442                               "edit", "activate", main_window_edit_cb,
1443                               "edit_accounts", "activate", main_window_edit_accounts_cb,
1444                               "edit_personal_information", "activate", main_window_edit_personal_information_cb,
1445                               "edit_preferences", "activate", main_window_edit_preferences_cb,
1446                               "help_about", "activate", main_window_help_about_cb,
1447                               "help_debug", "activate", main_window_help_debug_cb,
1448                               "help_contents", "activate", main_window_help_contents_cb,
1449                               NULL);
1450
1451         /* Set up connection related widgets. */
1452         main_window_connection_items_setup (window, gui);
1453
1454         g_object_ref (window->ui_manager);
1455         g_object_unref (gui);
1456
1457 #if !HAVE_LIBCHAMPLAIN
1458         gtk_action_set_visible (show_map_widget, FALSE);
1459 #endif
1460
1461         window->account_manager = tp_account_manager_dup ();
1462
1463         tp_account_manager_prepare_async (window->account_manager, NULL,
1464                                           account_manager_prepared_cb, window);
1465
1466         window->errors = g_hash_table_new_full (g_direct_hash,
1467                                                 g_direct_equal,
1468                                                 g_object_unref,
1469                                                 NULL);
1470
1471         window->status_changed_handlers = g_hash_table_new_full (g_direct_hash,
1472                                                                  g_direct_equal,
1473                                                                  NULL,
1474                                                                  NULL);
1475
1476         /* Set up menu */
1477         main_window_favorite_chatroom_menu_setup (window);
1478
1479         window->edit_context = gtk_ui_manager_get_widget (window->ui_manager,
1480                 "/menubar/edit/edit_context");
1481         window->edit_context_separator = gtk_ui_manager_get_widget (window->ui_manager,
1482                 "/menubar/edit/edit_context_separator");
1483         gtk_widget_hide (window->edit_context);
1484         gtk_widget_hide (window->edit_context_separator);
1485
1486         /* Set up contact list. */
1487         empathy_status_presets_get_all ();
1488
1489         /* Set up presence chooser */
1490         window->presence_chooser = empathy_presence_chooser_new ();
1491         gtk_widget_show (window->presence_chooser);
1492         item = gtk_tool_item_new ();
1493         gtk_widget_show (GTK_WIDGET (item));
1494         gtk_container_add (GTK_CONTAINER (item), window->presence_chooser);
1495         gtk_tool_item_set_is_important (item, TRUE);
1496         gtk_tool_item_set_expand (item, TRUE);
1497         gtk_toolbar_insert (GTK_TOOLBAR (window->presence_toolbar), item, -1);
1498
1499         /* Set up the throbber */
1500         window->throbber = gtk_spinner_new ();
1501         gtk_widget_set_size_request (window->throbber, 16, -1);
1502         gtk_widget_set_tooltip_text (window->throbber, _("Show and edit accounts"));
1503         gtk_widget_set_has_window (GTK_WIDGET (window->throbber), TRUE);
1504         gtk_widget_set_events (window->throbber, GDK_BUTTON_PRESS_MASK);
1505         g_signal_connect (window->throbber, "button-press-event",
1506                 G_CALLBACK (main_window_throbber_button_press_event_cb),
1507                 window);
1508         gtk_widget_show (window->throbber);
1509
1510         item = gtk_tool_item_new ();
1511         gtk_container_set_border_width (GTK_CONTAINER (item), 6);
1512         gtk_toolbar_insert (GTK_TOOLBAR (window->presence_toolbar), item, -1);
1513         gtk_container_add (GTK_CONTAINER (item), window->throbber);
1514         window->throbber_tool_item = GTK_WIDGET (item);
1515
1516         list_iface = EMPATHY_CONTACT_LIST (empathy_contact_manager_dup_singleton ());
1517         window->list_store = empathy_contact_list_store_new (list_iface);
1518         window->list_view = empathy_contact_list_view_new (window->list_store,
1519                                                            EMPATHY_CONTACT_LIST_FEATURE_ALL,
1520                                                            EMPATHY_CONTACT_FEATURE_ALL);
1521
1522         window->butterfly_log_migration_members_changed_id = g_signal_connect (
1523                 list_iface, "members-changed",
1524                 G_CALLBACK (main_window_members_changed_cb), window);
1525
1526         g_object_unref (list_iface);
1527
1528         gtk_widget_show (GTK_WIDGET (window->list_view));
1529         gtk_container_add (GTK_CONTAINER (sw),
1530                            GTK_WIDGET (window->list_view));
1531         g_signal_connect (window->list_view, "row-activated",
1532                           G_CALLBACK (main_window_row_activated_cb),
1533                           window);
1534
1535         /* Set up search bar */
1536         window->search_bar = empathy_live_search_new (
1537                 GTK_WIDGET (window->list_view));
1538         empathy_contact_list_view_set_live_search (window->list_view,
1539                 EMPATHY_LIVE_SEARCH (window->search_bar));
1540         gtk_box_pack_start (GTK_BOX (window->main_vbox), window->search_bar,
1541                 FALSE, TRUE, 0);
1542
1543         /* Load user-defined accelerators. */
1544         main_window_accels_load ();
1545
1546         /* Set window size. */
1547         empathy_geometry_bind (GTK_WINDOW (window->window), GEOMETRY_NAME);
1548
1549         /* Enable event handling */
1550         window->event_manager = empathy_event_manager_dup_singleton ();
1551         g_signal_connect (window->event_manager, "event-added",
1552                           G_CALLBACK (main_window_event_added_cb),
1553                           window);
1554         g_signal_connect (window->event_manager, "event-removed",
1555                           G_CALLBACK (main_window_event_removed_cb),
1556                           window);
1557
1558         g_signal_connect (window->account_manager, "account-validity-changed",
1559                           G_CALLBACK (main_window_account_validity_changed_cb),
1560                           window);
1561         g_signal_connect (window->account_manager, "account-removed",
1562                           G_CALLBACK (main_window_account_removed_cb),
1563                           window);
1564         g_signal_connect (window->account_manager, "account-disabled",
1565                           G_CALLBACK (main_window_account_disabled_cb),
1566                           window);
1567
1568         l = empathy_event_manager_get_events (window->event_manager);
1569         while (l) {
1570                 main_window_event_added_cb (window->event_manager,
1571                                             l->data, window);
1572                 l = l->next;
1573         }
1574
1575         conf = empathy_conf_get ();
1576
1577         /* Show offline ? */
1578         empathy_conf_get_bool (conf,
1579                               EMPATHY_PREFS_UI_SHOW_OFFLINE,
1580                               &show_offline);
1581         empathy_conf_notify_add (conf,
1582                                 EMPATHY_PREFS_UI_SHOW_OFFLINE,
1583                                 main_window_notify_show_offline_cb,
1584                                 show_offline_widget);
1585
1586         gtk_toggle_action_set_active (show_offline_widget, show_offline);
1587
1588         /* Show protocol ? */
1589         empathy_conf_notify_add (conf,
1590                                  EMPATHY_PREFS_UI_SHOW_PROTOCOLS,
1591                                  (EmpathyConfNotifyFunc) main_window_notify_show_protocols_cb,
1592                                  window);
1593
1594         main_window_notify_show_protocols_cb (conf,
1595                                             EMPATHY_PREFS_UI_SHOW_PROTOCOLS,
1596                                             window);
1597
1598         /* Sort by name / by status ? */
1599         empathy_conf_notify_add (conf,
1600                                  EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM,
1601                                  (EmpathyConfNotifyFunc) main_window_notify_sort_contact_cb,
1602                                  window);
1603
1604         main_window_notify_sort_contact_cb (conf,
1605                                             EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM,
1606                                             window);
1607
1608         /* Contacts list size */
1609         empathy_conf_notify_add (conf,
1610                                  EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST,
1611                                  (EmpathyConfNotifyFunc) main_window_notify_contact_list_size_cb,
1612                                  window);
1613         empathy_conf_notify_add (conf,
1614                                  EMPATHY_PREFS_UI_SHOW_AVATARS,
1615                                  (EmpathyConfNotifyFunc) main_window_notify_contact_list_size_cb,
1616                                  window);
1617
1618         main_window_notify_contact_list_size_cb (conf,
1619                                                  EMPATHY_PREFS_UI_SHOW_AVATARS,
1620                                                  window);
1621
1622         return window->window;
1623 }
1624