2 * Copyright (C) 2002-2007 Imendio AB
3 * Copyright (C) 2007-2010 Collabora Ltd.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301 USA
20 * Authors: Xavier Claessens <xclaesse@gmail.com>
21 * Danielle Madeley <danielle.madeley@collabora.co.uk>
25 #include "empathy-roster-window.h"
28 #include <glib/gi18n.h>
29 #include <tp-account-widgets/tpaw-builder.h>
31 #include "empathy-about-dialog.h"
32 #include "empathy-accounts-dialog.h"
33 #include "empathy-call-observer.h"
34 #include "empathy-chat-manager.h"
35 #include "empathy-chat-window.h"
36 #include "empathy-chatroom-manager.h"
37 #include "empathy-chatrooms-window.h"
38 #include "empathy-client-factory.h"
39 #include "empathy-contact-blocking-dialog.h"
40 #include "empathy-contact-search-dialog.h"
41 #include "empathy-event-manager.h"
42 #include "empathy-ft-manager.h"
43 #include "empathy-geometry.h"
44 #include "empathy-gsettings.h"
45 #include "empathy-gsettings.h"
46 #include "empathy-gtk-enum-types.h"
47 #include "empathy-individual-dialogs.h"
48 #include "empathy-log-window.h"
49 #include "empathy-new-call-dialog.h"
50 #include "empathy-new-chatroom-dialog.h"
51 #include "empathy-new-message-dialog.h"
52 #include "empathy-preferences.h"
53 #include "empathy-presence-chooser.h"
54 #include "empathy-presence-manager.h"
55 #include "empathy-request-util.h"
56 #include "empathy-roster-model-manager.h"
57 #include "empathy-roster-view.h"
58 #include "empathy-status-presets.h"
59 #include "empathy-theme-manager.h"
60 #include "empathy-theme-manager.h"
61 #include "empathy-ui-utils.h"
62 #include "empathy-utils.h"
64 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
65 #include "empathy-debug.h"
67 /* Flashing delay for icons (milliseconds). */
68 #define FLASH_TIMEOUT 500
70 /* Minimum width of roster window if something goes wrong. */
73 /* Accels (menu shortcuts) can be configured and saved */
74 #define ACCELS_FILENAME "accels.txt"
76 /* Name in the geometry file */
77 #define GEOMETRY_NAME "roster-window"
81 PAGE_CONTACT_LIST = 0,
91 G_DEFINE_TYPE (EmpathyRosterWindow, empathy_roster_window, GTK_TYPE_APPLICATION_WINDOW)
93 struct _EmpathyRosterWindowPriv {
94 EmpathyRosterView *view;
95 TpAccountManager *account_manager;
96 EmpathyChatManager *chat_manager;
97 EmpathyThemeManager *theme_manager;
98 EmpathyChatroomManager *chatroom_manager;
99 EmpathyEventManager *event_manager;
100 EmpathySoundManager *sound_mgr;
101 EmpathyCallObserver *call_observer;
102 EmpathyIndividualManager *individual_manager;
103 guint flash_timeout_id;
106 GSettings *gsettings_ui;
108 GtkWidget *preferences;
109 GtkWidget *main_vbox;
111 GtkWidget *presence_toolbar;
112 GtkWidget *presence_chooser;
113 GtkWidget *errors_vbox;
114 GtkWidget *auth_vbox;
115 GtkWidget *search_bar;
117 GtkWidget *no_entry_label;
118 GtkWidget *button_account_settings;
119 GtkWidget *button_online;
120 GtkWidget *button_show_offline;
121 GtkWidget *button_add_contact;
122 GtkWidget *spinner_loading;
123 GtkWidget *tooltip_widget;
124 GtkWidget *chat_window;
127 GMenu *rooms_section;
129 GtkWidget *balance_vbox;
131 guint size_timeout_id;
133 /* reffed TpAccount* => visible GtkInfoBar* */
136 /* EmpathyEvent* => visible GtkInfoBar* */
139 /* stores a mapping from TpAccount to Handler ID to prevent
140 * to listen more than once to the status-changed signal */
141 GHashTable *status_changed_handlers;
143 /* Actions that are enabled when there are connected accounts */
144 GList *actions_connected;
146 gboolean shell_running;
150 roster_window_remove_auth (EmpathyRosterWindow *self,
153 GtkWidget *error_widget;
155 error_widget = g_hash_table_lookup (self->priv->auths, event);
156 if (error_widget != NULL)
158 gtk_widget_destroy (error_widget);
159 g_hash_table_remove (self->priv->auths, event);
164 roster_window_auth_add_clicked_cb (GtkButton *button,
165 EmpathyRosterWindow *self)
169 event = g_object_get_data (G_OBJECT (button), "event");
171 empathy_event_approve (event);
173 roster_window_remove_auth (self, event);
177 roster_window_auth_close_clicked_cb (GtkButton *button,
178 EmpathyRosterWindow *self)
182 event = g_object_get_data (G_OBJECT (button), "event");
184 empathy_event_decline (event);
185 roster_window_remove_auth (self, event);
189 roster_window_auth_display (EmpathyRosterWindow *self,
192 TpAccount *account = event->account;
194 GtkWidget *content_area;
197 GtkWidget *add_button;
198 GtkWidget *close_button;
199 GtkWidget *action_area;
200 GtkWidget *action_grid;
201 const gchar *icon_name;
204 if (g_hash_table_lookup (self->priv->auths, event) != NULL)
207 info_bar = gtk_info_bar_new ();
208 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_QUESTION);
210 gtk_widget_set_no_show_all (info_bar, TRUE);
211 gtk_box_pack_start (GTK_BOX (self->priv->auth_vbox), info_bar, FALSE, TRUE, 0);
212 gtk_widget_show (info_bar);
214 icon_name = tp_account_get_icon_name (account);
215 image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR);
216 gtk_widget_show (image);
218 str = g_markup_printf_escaped ("<b>%s</b>\n%s",
219 tp_account_get_display_name (account),
220 _("Password required"));
222 label = gtk_label_new (str);
223 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
224 gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
225 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
226 gtk_widget_show (label);
230 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
231 gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0);
232 gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0);
234 image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
235 add_button = gtk_button_new ();
236 gtk_button_set_image (GTK_BUTTON (add_button), image);
237 gtk_widget_set_tooltip_text (add_button, _("Provide Password"));
238 gtk_widget_show (add_button);
240 image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON);
241 close_button = gtk_button_new ();
242 gtk_button_set_image (GTK_BUTTON (close_button), image);
243 gtk_widget_set_tooltip_text (close_button, _("Disconnect"));
244 gtk_widget_show (close_button);
246 action_grid = gtk_grid_new ();
247 gtk_grid_set_column_spacing (GTK_GRID (action_grid), 6);
248 gtk_widget_show (action_grid);
250 action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar));
251 gtk_box_pack_start (GTK_BOX (action_area), action_grid, FALSE, FALSE, 0);
253 gtk_grid_attach (GTK_GRID (action_grid), add_button, 0, 0, 1, 1);
254 gtk_grid_attach (GTK_GRID (action_grid), close_button, 1, 0, 1, 1);
256 g_object_set_data_full (G_OBJECT (info_bar),
257 "event", event, NULL);
258 g_object_set_data_full (G_OBJECT (add_button),
259 "event", event, NULL);
260 g_object_set_data_full (G_OBJECT (close_button),
261 "event", event, NULL);
263 g_signal_connect (add_button, "clicked",
264 G_CALLBACK (roster_window_auth_add_clicked_cb), self);
265 g_signal_connect (close_button, "clicked",
266 G_CALLBACK (roster_window_auth_close_clicked_cb), self);
268 gtk_widget_show (self->priv->auth_vbox);
270 g_hash_table_insert (self->priv->auths, event, info_bar);
273 static FolksIndividual *
274 ensure_individual_for_event (EmpathyEvent *event)
278 contact = empathy_contact_get_tp_contact (event->contact);
282 return empathy_ensure_individual_from_tp_contact (contact);
286 roster_window_event_added_cb (EmpathyEventManager *manager,
288 EmpathyRosterWindow *self)
292 FolksIndividual *individual;
294 individual = ensure_individual_for_event (event);
295 if (individual == NULL)
298 event->roster_view_id = empathy_roster_view_add_event (self->priv->view,
299 individual, event->icon_name, event);
301 g_object_unref (individual);
303 else if (event->type == EMPATHY_EVENT_TYPE_AUTH)
305 roster_window_auth_display (self, event);
310 roster_window_event_removed_cb (EmpathyEventManager *manager,
312 EmpathyRosterWindow *self)
314 if (event->type == EMPATHY_EVENT_TYPE_AUTH)
316 roster_window_remove_auth (self, event);
320 empathy_roster_view_remove_event (self->priv->view, event->roster_view_id);
324 roster_window_load_events_idle_cb (gpointer user_data)
326 EmpathyRosterWindow *self = user_data;
329 l = empathy_event_manager_get_events (self->priv->event_manager);
332 roster_window_event_added_cb (self->priv->event_manager, l->data,
341 hide_search_bar (EmpathyRosterWindow *roster_window)
343 if (TPAW_IS_LIVE_SEARCH (roster_window->priv->search_bar) &&
344 gtk_widget_is_visible (roster_window->priv->search_bar))
345 gtk_widget_hide (roster_window->priv->search_bar);
349 individual_activated_cb (EmpathyRosterView *self,
350 FolksIndividual *individual,
353 EmpathyContact *contact;
355 contact = empathy_contact_dup_best_for_action (individual,
356 EMPATHY_ACTION_CHAT);
361 DEBUG ("Starting a chat");
363 empathy_chat_with_contact (contact, gtk_get_current_event_time ());
365 g_object_unref (contact);
367 /* Hide the search-bar upon hitting "Enter" on an individual */
368 hide_search_bar (EMPATHY_ROSTER_WINDOW (user_data));
372 event_activated_cb (EmpathyRosterView *self,
373 FolksIndividual *individual,
377 empathy_event_activate (event);
379 /* Hide the search-bar upon an event activation */
380 hide_search_bar (EMPATHY_ROSTER_WINDOW (user_data));
384 button_account_settings_clicked_cb (GtkButton *button,
385 EmpathyRosterWindow *self)
387 empathy_accounts_dialog_show_application (gdk_screen_get_default (),
392 button_online_clicked_cb (GtkButton *button,
393 EmpathyRosterWindow *self)
395 EmpathyPresenceManager *mgr;
397 mgr = empathy_presence_manager_dup_singleton ();
399 empathy_presence_manager_set_state (mgr,
400 TP_CONNECTION_PRESENCE_TYPE_AVAILABLE);
402 g_object_unref (mgr);
406 button_show_offline_clicked_cb (GtkButton *button,
407 EmpathyRosterWindow *self)
409 g_settings_set_boolean (self->priv->gsettings_ui,
410 EMPATHY_PREFS_UI_SHOW_OFFLINE, TRUE);
414 button_add_contact_clicked_cb (GtkButton *button,
415 EmpathyRosterWindow *self)
417 empathy_new_individual_dialog_show (GTK_WINDOW (self));
422 PAGE_MESSAGE_FLAG_NONE = 0,
423 PAGE_MESSAGE_FLAG_ACCOUNTS = 1 << 0,
424 PAGE_MESSAGE_FLAG_SPINNER = 1 << 2,
425 PAGE_MESSAGE_FLAG_ONLINE = 1 << 3,
426 PAGE_MESSAGE_FLAG_SHOW_OFFLINE = 1 << 4,
427 PAGE_MESSAGE_FLAG_ADD_CONTACT = 1 << 5,
431 can_add_contact (EmpathyRosterWindow *self)
434 gboolean result = FALSE;
436 accounts = tp_account_manager_dup_valid_accounts (
437 self->priv->account_manager);
438 for (l = accounts; l != NULL && !result; l = g_list_next (l))
440 TpAccount *account = TP_ACCOUNT (l->data);
443 conn = tp_account_get_connection (account);
447 if (tp_connection_get_can_change_contact_list (conn))
451 g_list_free_full (accounts, g_object_unref);
456 display_page_message (EmpathyRosterWindow *self,
458 PageMessageFlags flags)
464 tmp = g_strdup_printf ("<b><span size='xx-large'>%s</span></b>", msg);
466 gtk_label_set_markup (GTK_LABEL (self->priv->no_entry_label), tmp);
469 gtk_label_set_line_wrap (GTK_LABEL (self->priv->no_entry_label), TRUE);
470 gtk_widget_show (self->priv->no_entry_label);
474 gtk_widget_hide (self->priv->no_entry_label);
477 gtk_widget_set_visible (self->priv->button_account_settings,
478 (flags & PAGE_MESSAGE_FLAG_ACCOUNTS) != 0);
479 gtk_widget_set_visible (self->priv->spinner_loading,
480 (flags & PAGE_MESSAGE_FLAG_SPINNER) != 0);
481 gtk_widget_set_visible (self->priv->button_online,
482 (flags & PAGE_MESSAGE_FLAG_ONLINE) != 0);
483 gtk_widget_set_visible (self->priv->button_show_offline,
484 (flags & PAGE_MESSAGE_FLAG_SHOW_OFFLINE) != 0);
485 gtk_widget_set_visible (self->priv->button_add_contact,
486 (flags & PAGE_MESSAGE_FLAG_ADD_CONTACT) != 0);
488 if ((flags & PAGE_MESSAGE_FLAG_ADD_CONTACT) != 0)
489 gtk_widget_set_sensitive (self->priv->button_add_contact,
490 can_add_contact (self));
492 gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook),
497 display_page_no_account (EmpathyRosterWindow *self)
499 display_page_message (self,
500 _("You need to set up an account to see contacts here."),
501 PAGE_MESSAGE_FLAG_ACCOUNTS);
505 display_page_contact_list (EmpathyRosterWindow *self)
507 if (!empathy_individual_manager_get_contacts_loaded (
508 self->priv->individual_manager))
509 /* We'll display the contact list once we're done loading */
512 gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook),
517 roster_window_remove_error (EmpathyRosterWindow *self,
520 GtkWidget *error_widget;
522 error_widget = g_hash_table_lookup (self->priv->errors, account);
523 if (error_widget != NULL)
525 gtk_widget_destroy (error_widget);
526 g_hash_table_remove (self->priv->errors, account);
531 roster_window_account_disabled_cb (TpAccountManager *manager,
533 EmpathyRosterWindow *self)
535 roster_window_remove_error (self, account);
540 ERROR_RESPONSE_RETRY,
542 ERROR_RESPONSE_CLOSE,
543 ERROR_RESPONSE_UPGRADE,
547 roster_window_error_response_cb (GtkInfoBar *infobar,
549 EmpathyRosterWindow *self)
553 account = g_object_get_data (G_OBJECT (infobar), "account");
557 case ERROR_RESPONSE_RETRY:
558 tp_account_reconnect_async (account, NULL, NULL);
561 case ERROR_RESPONSE_EDIT:
562 empathy_accounts_dialog_show_application (
563 gtk_widget_get_screen (GTK_WIDGET (infobar)),
564 account, FALSE, FALSE);
567 case ERROR_RESPONSE_CLOSE:
570 case ERROR_RESPONSE_UPGRADE:
574 dialog = gtk_message_dialog_new (GTK_WINDOW (self),
575 GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
576 _("Sorry, %s accounts can’t be used until your %s software is updated."),
577 tp_account_get_protocol_name (account),
578 tp_account_get_protocol_name (account));
580 g_signal_connect_swapped (dialog, "response",
581 G_CALLBACK (gtk_widget_destroy),
584 gtk_widget_show (dialog);
588 roster_window_remove_error (self, account);
592 roster_window_error_create_info_bar (EmpathyRosterWindow *self,
594 GtkMessageType message_type,
595 const gchar *message_markup)
598 GtkWidget *content_area;
599 GtkWidget *action_area;
602 const gchar *icon_name;
604 /* If there are other errors, remove them */
605 roster_window_remove_error (self, account);
607 info_bar = gtk_info_bar_new ();
608 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), message_type);
610 gtk_widget_set_no_show_all (info_bar, TRUE);
611 gtk_box_pack_start (GTK_BOX (self->priv->errors_vbox), info_bar, FALSE, TRUE, 0);
612 gtk_widget_show (info_bar);
614 icon_name = tp_account_get_icon_name (account);
615 image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR);
616 gtk_widget_show (image);
618 label = gtk_label_new (message_markup);
619 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
620 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
621 gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
622 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
623 gtk_widget_show (label);
625 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
626 gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0);
627 gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0);
629 action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar));
630 gtk_orientable_set_orientation (GTK_ORIENTABLE (action_area),
631 GTK_ORIENTATION_HORIZONTAL);
632 gtk_style_context_add_class (gtk_widget_get_style_context (action_area),
633 "empathy-roster-window-error-button");
635 g_object_set_data_full (G_OBJECT (info_bar),
636 "account", g_object_ref (account),
639 g_signal_connect (info_bar, "response",
640 G_CALLBACK (roster_window_error_response_cb), self);
642 gtk_widget_show (self->priv->errors_vbox);
644 g_hash_table_insert (self->priv->errors, g_object_ref (account), info_bar);
650 roster_window_error_add_stock_button (GtkInfoBar *info_bar,
651 const gchar *stock_id,
652 const gchar *tooltip,
653 ErrorResponse response_id)
658 image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
659 button = gtk_button_new ();
660 gtk_button_set_image (GTK_BUTTON (button), image);
661 gtk_widget_set_tooltip_text (button, tooltip);
662 gtk_widget_show (button);
664 gtk_info_bar_add_action_widget (info_bar, button, response_id);
669 uoa_account_display_string (TpAccount *account)
671 const gchar *service;
673 service = tp_account_get_service (account);
675 /* Use well known service name, if available */
676 if (!tp_strdiff (service, "windows-live"))
677 return _("Windows Live");
678 else if (!tp_strdiff (service, "google-talk"))
679 return _("Google Talk");
680 else if (!tp_strdiff (service, "facebook"))
681 return _("Facebook");
683 return tp_account_get_display_name (account);
687 roster_window_uoa_auth_error (EmpathyRosterWindow *self,
695 /* translators: %s is an account name like 'Facebook' or 'Google Talk' */
696 str = g_strdup_printf (_("%s account requires authorisation"),
697 uoa_account_display_string (account));
699 info_bar = roster_window_error_create_info_bar (self, account,
700 GTK_MESSAGE_OTHER, str);
703 image = gtk_image_new_from_icon_name ("credentials-preferences",
704 GTK_ICON_SIZE_BUTTON);
705 button = gtk_button_new ();
706 gtk_button_set_image (GTK_BUTTON (button), image);
707 gtk_widget_set_tooltip_text (button, _("Online Accounts"));
708 gtk_widget_show (button);
710 gtk_info_bar_add_action_widget (GTK_INFO_BAR (info_bar), button,
711 ERROR_RESPONSE_EDIT);
716 roster_window_error_display (EmpathyRosterWindow *self,
719 const gchar *error_message;
720 gboolean user_requested;
725 empathy_account_get_error_message (account, &user_requested);
731 if (!tp_strdiff (TP_ERROR_STR_AUTHENTICATION_FAILED,
732 tp_account_get_detailed_error (account, NULL)) &&
733 !tp_strdiff (tp_account_get_storage_provider (account),
734 EMPATHY_UOA_PROVIDER))
736 roster_window_uoa_auth_error (self, account);
741 str = g_markup_printf_escaped ("<b>%s</b>\n%s",
742 tp_account_get_display_name (account), error_message);
744 info_bar = roster_window_error_create_info_bar (self, account,
745 GTK_MESSAGE_ERROR, str);
748 gtk_widget_set_tooltip_text (self->priv->errors_vbox, error_message);
750 if (!tp_strdiff (TP_ERROR_STR_SOFTWARE_UPGRADE_REQUIRED,
751 tp_account_get_detailed_error (account, NULL)))
753 roster_window_error_add_stock_button (GTK_INFO_BAR (info_bar),
754 GTK_STOCK_REFRESH, _("Update software…"),
755 ERROR_RESPONSE_RETRY);
759 roster_window_error_add_stock_button (GTK_INFO_BAR (info_bar),
760 GTK_STOCK_REFRESH, _("Reconnect"),
761 ERROR_RESPONSE_RETRY);
763 roster_window_error_add_stock_button (GTK_INFO_BAR (info_bar),
764 GTK_STOCK_EDIT, _("Edit Account"),
765 ERROR_RESPONSE_EDIT);
768 roster_window_error_add_stock_button (GTK_INFO_BAR (info_bar),
769 GTK_STOCK_CLOSE, _("Close"),
770 ERROR_RESPONSE_CLOSE);
774 roster_window_update_status (EmpathyRosterWindow *self)
776 gboolean connected, connecting;
780 connected = empathy_account_manager_get_accounts_connected (&connecting);
782 /* Update the spinner state */
785 gtk_spinner_start (GTK_SPINNER (self->priv->throbber));
786 gtk_widget_show (self->priv->throbber);
790 gtk_spinner_stop (GTK_SPINNER (self->priv->throbber));
791 gtk_widget_hide (self->priv->throbber);
794 /* Update widgets sensibility */
795 for (l = self->priv->actions_connected; l; l = l->next)
796 g_simple_action_set_enabled (l->data, connected);
798 action = g_action_map_lookup_action (G_ACTION_MAP (self), "chat_add_contact");
799 if (!can_add_contact (self))
800 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
804 roster_window_balance_update_balance (EmpathyRosterWindow *self,
810 guint scale = G_MAXINT32;
811 const gchar *currency = "";
814 conn = tp_account_get_connection (account);
818 if (!tp_connection_get_balance (conn, &amount, &scale, ¤cy))
822 scale == G_MAXINT32 &&
823 tp_str_empty (currency))
825 /* unknown balance */
826 money = g_strdup ("--");
830 char *tmp = empathy_format_currency (amount, scale, currency);
832 money = g_strdup_printf ("%s %s", currency, tmp);
836 /* update the money label in the roster */
837 label = g_object_get_data (G_OBJECT (account), "balance-money-label");
839 gtk_label_set_text (GTK_LABEL (label), money);
844 roster_window_balance_changed_cb (TpConnection *conn,
847 const gchar *currency,
848 EmpathyRosterWindow *self)
852 account = tp_connection_get_account (conn);
856 roster_window_balance_update_balance (self, account);
860 roster_window_setup_balance (EmpathyRosterWindow *self,
863 TpConnection *conn = tp_account_get_connection (account);
864 GtkWidget *hbox, *image, *label;
870 if (!tp_proxy_is_prepared (conn, TP_CONNECTION_FEATURE_BALANCE))
873 DEBUG ("Setting up balance for acct: %s",
874 tp_account_get_display_name (account));
876 /* create the display widget */
877 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
878 gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
881 image = gtk_image_new ();
882 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
883 g_object_bind_property (account, "icon-name", image, "icon-name",
884 G_BINDING_SYNC_CREATE);
886 /* account name label */
887 label = gtk_label_new ("");
888 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
889 gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
890 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
891 g_object_bind_property (account, "display-name", label, "label",
892 G_BINDING_SYNC_CREATE);
895 label = gtk_label_new ("");
896 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
897 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
900 uri = tp_connection_get_balance_uri (conn);
902 if (!tp_str_empty (uri))
906 button = gtk_button_new ();
907 gtk_container_add (GTK_CONTAINER (button),
908 gtk_image_new_from_icon_name ("emblem-symbolic-link",
909 GTK_ICON_SIZE_SMALL_TOOLBAR));
910 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
911 gtk_widget_set_tooltip_text (button, _("Top up account"));
912 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
914 g_signal_connect_data (button, "clicked",
915 G_CALLBACK (empathy_url_show),
916 g_strdup (uri), (GClosureNotify) g_free,
920 gtk_box_pack_start (GTK_BOX (self->priv->balance_vbox), hbox, FALSE, TRUE, 0);
921 gtk_widget_show_all (hbox);
923 g_object_set_data (G_OBJECT (account), "balance-money-label", label);
924 g_object_set_data (G_OBJECT (account), "balance-money-hbox", hbox);
926 roster_window_balance_update_balance (self, account);
928 g_signal_connect (conn, "balance-changed",
929 G_CALLBACK (roster_window_balance_changed_cb), self);
933 roster_window_remove_balance_action (EmpathyRosterWindow *self,
937 g_object_get_data (G_OBJECT (account), "balance-money-hbox");
942 g_return_if_fail (GTK_IS_BOX (hbox));
944 gtk_widget_destroy (hbox);
947 static void set_notebook_page (EmpathyRosterWindow *self);
950 roster_window_connection_changed_cb (TpAccount *account,
954 gchar *dbus_error_name,
956 EmpathyRosterWindow *self)
958 roster_window_update_status (self);
959 set_notebook_page (self);
961 if (current == TP_CONNECTION_STATUS_DISCONNECTED &&
962 reason != TP_CONNECTION_STATUS_REASON_REQUESTED)
964 roster_window_error_display (self, account);
967 if (current == TP_CONNECTION_STATUS_DISCONNECTED)
969 empathy_sound_manager_play (self->priv->sound_mgr, GTK_WIDGET (self),
970 EMPATHY_SOUND_ACCOUNT_DISCONNECTED);
973 if (current == TP_CONNECTION_STATUS_CONNECTED)
975 empathy_sound_manager_play (self->priv->sound_mgr, GTK_WIDGET (self),
976 EMPATHY_SOUND_ACCOUNT_CONNECTED);
978 /* Account connected without error, remove error message if any */
979 roster_window_remove_error (self, account);
984 roster_window_accels_load (void)
988 filename = g_build_filename (g_get_user_config_dir (),
989 PACKAGE_NAME, ACCELS_FILENAME, NULL);
990 if (g_file_test (filename, G_FILE_TEST_EXISTS))
992 DEBUG ("Loading from:'%s'", filename);
993 gtk_accel_map_load (filename);
1000 roster_window_accels_save (void)
1003 gchar *file_with_path;
1005 dir = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL);
1006 g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
1007 file_with_path = g_build_filename (dir, ACCELS_FILENAME, NULL);
1010 DEBUG ("Saving to:'%s'", file_with_path);
1011 gtk_accel_map_save (file_with_path);
1013 g_free (file_with_path);
1017 empathy_roster_window_finalize (GObject *window)
1019 EmpathyRosterWindow *self = EMPATHY_ROSTER_WINDOW (window);
1020 GHashTableIter iter;
1021 gpointer key, value;
1023 /* Save user-defined accelerators. */
1024 roster_window_accels_save ();
1026 g_list_free (self->priv->actions_connected);
1028 g_object_unref (self->priv->account_manager);
1029 g_object_unref (self->priv->sound_mgr);
1030 g_hash_table_unref (self->priv->errors);
1031 g_hash_table_unref (self->priv->auths);
1033 /* disconnect all handlers of status-changed signal */
1034 g_hash_table_iter_init (&iter, self->priv->status_changed_handlers);
1035 while (g_hash_table_iter_next (&iter, &key, &value))
1036 g_signal_handler_disconnect (TP_ACCOUNT (key), GPOINTER_TO_UINT (value));
1038 g_hash_table_unref (self->priv->status_changed_handlers);
1040 g_object_unref (self->priv->call_observer);
1041 g_object_unref (self->priv->event_manager);
1042 g_object_unref (self->priv->chat_manager);
1043 g_object_unref (self->priv->theme_manager);
1044 g_object_unref (self->priv->chatroom_manager);
1046 g_object_unref (self->priv->gsettings_ui);
1047 g_object_unref (self->priv->individual_manager);
1049 g_object_unref (self->priv->menumodel);
1050 g_object_unref (self->priv->rooms_section);
1052 g_clear_object (&self->priv->tooltip_widget);
1054 G_OBJECT_CLASS (empathy_roster_window_parent_class)->finalize (window);
1058 roster_window_key_press_event_cb (GtkWidget *window,
1060 EmpathyRosterWindow *self)
1062 if (event->keyval == GDK_KEY_T
1063 && event->state & GDK_SHIFT_MASK
1064 && event->state & GDK_CONTROL_MASK)
1065 empathy_chat_manager_call_undo_closed_chat ();
1067 if (event->keyval == GDK_KEY_f
1068 && event->state & GDK_CONTROL_MASK)
1069 gtk_widget_show (self->priv->search_bar);
1075 unprepare_cb (GObject *source,
1076 GAsyncResult *result,
1079 GtkWidget *self = user_data;
1081 gtk_widget_destroy (self);
1085 roster_window_chat_quit_cb (GSimpleAction *action,
1086 GVariant *parameter,
1089 EmpathyRosterWindow *self = user_data;
1091 /* Destroying the window will make us leave the main loop and so exit the
1092 * process. Before doing so we want to unprepare the individual manager.
1093 * Just hide the window now and actually destroy it once Folks is done.
1095 gtk_widget_hide (GTK_WIDGET (self));
1097 empathy_individual_manager_unprepare_async (self->priv->individual_manager,
1098 unprepare_cb, self);
1102 roster_window_view_history_cb (GSimpleAction *action,
1103 GVariant *parameter,
1106 EmpathyRosterWindow *self = user_data;
1108 empathy_log_window_show (NULL, NULL, FALSE, GTK_WINDOW (self));
1112 roster_window_chat_new_message_cb (GSimpleAction *action,
1113 GVariant *parameter,
1116 EmpathyRosterWindow *self = user_data;
1118 empathy_new_message_dialog_show (GTK_WINDOW (self));
1122 roster_window_chat_new_call_cb (GSimpleAction *action,
1123 GVariant *parameter,
1126 EmpathyRosterWindow *self = user_data;
1128 empathy_new_call_dialog_show (GTK_WINDOW (self));
1132 roster_window_chat_add_contact_cb (GSimpleAction *action,
1133 GVariant *parameter,
1136 EmpathyRosterWindow *self = user_data;
1138 empathy_new_individual_dialog_show (GTK_WINDOW (self));
1142 roster_window_chat_search_contacts_cb (GSimpleAction *action,
1143 GVariant *parameter,
1146 EmpathyRosterWindow *self = user_data;
1149 dialog = empathy_contact_search_dialog_new (
1152 gtk_widget_show (dialog);
1156 roster_window_view_show_ft_manager (GSimpleAction *action,
1157 GVariant *parameter,
1160 empathy_ft_manager_show ();
1164 join_chatroom (EmpathyChatroom *chatroom,
1170 account = empathy_chatroom_get_account (chatroom);
1171 room = empathy_chatroom_get_room (chatroom);
1173 DEBUG ("Requesting channel for '%s'", room);
1174 empathy_join_muc (account, room, timestamp);
1180 EmpathyChatroom *chatroom;
1184 } join_fav_account_sig_ctx;
1186 static join_fav_account_sig_ctx *
1187 join_fav_account_sig_ctx_new (TpAccount *account,
1188 EmpathyChatroom *chatroom,
1191 join_fav_account_sig_ctx *ctx = g_slice_new0 (
1192 join_fav_account_sig_ctx);
1194 ctx->account = g_object_ref (account);
1195 ctx->chatroom = g_object_ref (chatroom);
1196 ctx->timestamp = timestamp;
1201 join_fav_account_sig_ctx_free (join_fav_account_sig_ctx *ctx)
1203 g_object_unref (ctx->account);
1204 g_object_unref (ctx->chatroom);
1205 g_slice_free (join_fav_account_sig_ctx, ctx);
1209 account_status_changed_cb (TpAccount *account,
1210 TpConnectionStatus old_status,
1211 TpConnectionStatus new_status,
1213 gchar *dbus_error_name,
1214 GHashTable *details,
1217 join_fav_account_sig_ctx *ctx = user_data;
1221 case TP_CONNECTION_STATUS_DISCONNECTED:
1222 /* Don't wait any longer */
1226 case TP_CONNECTION_STATUS_CONNECTING:
1230 case TP_CONNECTION_STATUS_CONNECTED:
1231 /* We can join the room */
1235 g_assert_not_reached ();
1238 join_chatroom (ctx->chatroom, ctx->timestamp);
1241 g_source_remove (ctx->timeout);
1242 g_signal_handler_disconnect (account, ctx->sig_id);
1245 #define JOIN_FAVORITE_TIMEOUT 5
1248 join_favorite_timeout_cb (gpointer data)
1250 join_fav_account_sig_ctx *ctx = data;
1252 /* stop waiting for joining the favorite room */
1253 g_signal_handler_disconnect (ctx->account, ctx->sig_id);
1258 roster_window_favorite_chatroom_join (EmpathyChatroom *chatroom)
1262 account = empathy_chatroom_get_account (chatroom);
1263 if (tp_account_get_connection_status (account, NULL) !=
1264 TP_CONNECTION_STATUS_CONNECTED)
1266 join_fav_account_sig_ctx *ctx;
1268 ctx = join_fav_account_sig_ctx_new (account, chatroom,
1269 empathy_get_current_action_time ());
1271 ctx->sig_id = g_signal_connect_data (account, "status-changed",
1272 G_CALLBACK (account_status_changed_cb), ctx,
1273 (GClosureNotify) join_fav_account_sig_ctx_free, 0);
1275 ctx->timeout = g_timeout_add_seconds (JOIN_FAVORITE_TIMEOUT,
1276 join_favorite_timeout_cb, ctx);
1280 join_chatroom (chatroom, empathy_get_current_action_time ());
1284 roster_window_join_chatroom_menu_activate_cb (GSimpleAction *action,
1285 GVariant *parameter,
1288 EmpathyRosterWindow *self = user_data;
1289 const gchar *room, *path;
1290 EmpathyClientFactory *factory;
1292 GError *error = NULL;
1293 EmpathyChatroom *chatroom;
1295 g_variant_get (parameter, "(&s&s)", &room, &path);
1297 factory = empathy_client_factory_dup ();
1299 account = tp_simple_client_factory_ensure_account (
1300 TP_SIMPLE_CLIENT_FACTORY (factory), path, NULL, &error);
1301 if (account == NULL)
1303 DEBUG ("Failed to get account '%s': %s", path, error->message);
1304 g_error_free (error);
1308 chatroom = empathy_chatroom_manager_find (self->priv->chatroom_manager,
1310 if (chatroom == NULL)
1312 DEBUG ("Failed to get chatroom '%s' on '%s'",
1317 roster_window_favorite_chatroom_join (chatroom);
1320 g_object_unref (factory);
1324 roster_window_favorite_chatroom_menu_add (EmpathyRosterWindow *self,
1325 EmpathyChatroom *chatroom)
1328 const gchar *name, *account_name, *account_path;
1332 account = empathy_chatroom_get_account (chatroom);
1334 name = empathy_chatroom_get_name (chatroom);
1335 account_name = tp_account_get_display_name (account);
1336 account_path = tp_proxy_get_object_path (account);
1338 label = g_strdup_printf ("%s (%s)", name, account_name);
1340 item = g_menu_item_new (label, NULL);
1341 g_menu_item_set_action_and_target (item, "win.join", "(ss)",
1342 name, account_path);
1343 g_menu_item_set_attribute (item, "room-name", "s", name);
1344 g_menu_item_set_attribute (item, "account-path", "s", account_path);
1345 g_menu_append_item (self->priv->rooms_section, item);
1351 roster_window_favorite_chatroom_menu_added_cb (EmpathyChatroomManager *manager,
1352 EmpathyChatroom *chatroom,
1353 EmpathyRosterWindow *self)
1355 roster_window_favorite_chatroom_menu_add (self, chatroom);
1359 roster_window_favorite_chatroom_menu_removed_cb (
1360 EmpathyChatroomManager *manager,
1361 EmpathyChatroom *chatroom,
1362 EmpathyRosterWindow *self)
1367 const gchar *account_path;
1369 account = empathy_chatroom_get_account (chatroom);
1370 account_path = tp_proxy_get_object_path (account);
1372 n = g_menu_model_get_n_items (G_MENU_MODEL (self->priv->rooms_section));
1374 for (i = 0; i < n; i++)
1378 if (!g_menu_model_get_item_attribute (
1379 G_MENU_MODEL (self->priv->rooms_section), i,
1380 "room-name", "s", &tmp))
1383 if (tp_strdiff (tmp, empathy_chatroom_get_name (chatroom)))
1391 if (!g_menu_model_get_item_attribute (
1392 G_MENU_MODEL (self->priv->rooms_section), i,
1393 "account-path", "s", &tmp))
1396 if (tp_strdiff (tmp, account_path))
1402 g_menu_remove (self->priv->rooms_section, i);
1407 chatrooms = empathy_chatroom_manager_get_chatrooms (
1408 self->priv->chatroom_manager, NULL);
1410 g_list_free (chatrooms);
1414 roster_window_favorite_chatroom_menu_setup (EmpathyRosterWindow *self)
1416 GList *chatrooms, *l;
1418 self->priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1420 chatrooms = empathy_chatroom_manager_get_chatrooms (
1421 self->priv->chatroom_manager, NULL);
1423 for (l = chatrooms; l; l = l->next)
1424 roster_window_favorite_chatroom_menu_add (self, l->data);
1426 g_signal_connect (self->priv->chatroom_manager, "chatroom-added",
1427 G_CALLBACK (roster_window_favorite_chatroom_menu_added_cb),
1430 g_signal_connect (self->priv->chatroom_manager, "chatroom-removed",
1431 G_CALLBACK (roster_window_favorite_chatroom_menu_removed_cb),
1434 g_list_free (chatrooms);
1438 roster_window_room_join_new_cb (GSimpleAction *action,
1439 GVariant *parameter,
1442 EmpathyRosterWindow *self = user_data;
1444 empathy_new_chatroom_dialog_show (GTK_WINDOW (self));
1448 roster_window_room_join_favorites_cb (GSimpleAction *action,
1449 GVariant *parameter,
1452 EmpathyRosterWindow *self = user_data;
1453 GList *chatrooms, *l;
1455 chatrooms = empathy_chatroom_manager_get_chatrooms (self->priv->chatroom_manager,
1458 for (l = chatrooms; l; l = l->next)
1459 roster_window_favorite_chatroom_join (l->data);
1461 g_list_free (chatrooms);
1465 roster_window_room_manage_favorites_cb (GSimpleAction *action,
1466 GVariant *parameter,
1469 EmpathyRosterWindow *self = user_data;
1471 empathy_chatrooms_window_show (GTK_WINDOW (self));
1475 roster_window_edit_accounts_cb (GSimpleAction *action,
1476 GVariant *parameter,
1479 empathy_accounts_dialog_show_application (gdk_screen_get_default (),
1480 NULL, FALSE, FALSE);
1484 roster_window_edit_blocked_contacts_cb (GSimpleAction *action,
1485 GVariant *parameter,
1488 EmpathyRosterWindow *self = user_data;
1491 dialog = empathy_contact_blocking_dialog_new (GTK_WINDOW (self));
1492 gtk_widget_show (dialog);
1494 g_signal_connect (dialog, "response",
1495 G_CALLBACK (gtk_widget_destroy), NULL);
1499 empathy_roster_window_show_preferences (EmpathyRosterWindow *self,
1502 if (self->priv->preferences == NULL)
1504 self->priv->preferences = empathy_preferences_new (GTK_WINDOW (self),
1505 self->priv->shell_running);
1506 g_object_add_weak_pointer (G_OBJECT (self->priv->preferences),
1507 (gpointer) &self->priv->preferences);
1509 gtk_widget_show (self->priv->preferences);
1513 gtk_window_present (GTK_WINDOW (self->priv->preferences));
1517 empathy_preferences_show_tab (
1518 EMPATHY_PREFERENCES (self->priv->preferences), tab);
1522 roster_window_edit_preferences_cb (GSimpleAction *action,
1523 GVariant *parameter,
1526 EmpathyRosterWindow *self = user_data;
1528 empathy_roster_window_show_preferences (self, NULL);
1532 roster_window_help_about_cb (GSimpleAction *action,
1533 GVariant *parameter,
1536 EmpathyRosterWindow *self = user_data;
1538 empathy_about_dialog_new (GTK_WINDOW (self));
1542 roster_window_help_contents_cb (GSimpleAction *action,
1543 GVariant *parameter,
1546 EmpathyRosterWindow *self = user_data;
1548 empathy_url_show (GTK_WIDGET (self), "help:empathy");
1552 next_tab_cb (GSimpleAction *action,
1553 GVariant *parameter,
1556 EmpathyRosterWindow *self = user_data;
1557 empathy_chat_window_next_tab (EMPATHY_CHAT_WINDOW (self->priv->chat_window));
1561 prev_tab_cb (GSimpleAction *action,
1562 GVariant *parameter,
1565 EmpathyRosterWindow *self = user_data;
1566 empathy_chat_window_prev_tab (EMPATHY_CHAT_WINDOW (self->priv->chat_window));
1571 roster_window_throbber_button_press_event_cb (GtkWidget *throbber,
1572 GdkEventButton *event,
1573 EmpathyRosterWindow *self)
1575 if (event->type != GDK_BUTTON_PRESS ||
1579 empathy_accounts_dialog_show_application (
1580 gtk_widget_get_screen (GTK_WIDGET (throbber)),
1581 NULL, FALSE, FALSE);
1587 roster_window_account_removed_cb (TpAccountManager *manager,
1589 EmpathyRosterWindow *self)
1591 /* remove errors if any */
1592 roster_window_remove_error (self, account);
1594 /* remove the balance action if required */
1595 roster_window_remove_balance_action (self, account);
1599 account_connection_notify_cb (TpAccount *account,
1601 EmpathyRosterWindow *self)
1605 conn = tp_account_get_connection (account);
1609 roster_window_setup_balance (self, account);
1613 /* remove balance action if required */
1614 roster_window_remove_balance_action (self, account);
1619 add_account (EmpathyRosterWindow *self,
1624 handler_id = GPOINTER_TO_UINT (g_hash_table_lookup (
1625 self->priv->status_changed_handlers, account));
1627 /* connect signal only if it was not connected yet */
1628 if (handler_id != 0)
1631 handler_id = g_signal_connect (account, "status-changed",
1632 G_CALLBACK (roster_window_connection_changed_cb), self);
1634 g_hash_table_insert (self->priv->status_changed_handlers,
1635 account, GUINT_TO_POINTER (handler_id));
1637 /* roster_window_setup_balance() relies on the TpConnection to be ready on
1638 * the TpAccount so we connect this signal as well. */
1639 tp_g_signal_connect_object (account, "notify::connection",
1640 G_CALLBACK (account_connection_notify_cb), self, 0);
1642 roster_window_setup_balance (self, account);
1645 /* @account: if not %NULL, the only account which can be enabled */
1647 display_page_account_not_enabled (EmpathyRosterWindow *self,
1650 if (account == NULL)
1652 display_page_message (self,
1653 _("You need to enable one of your accounts to see contacts here."),
1654 PAGE_MESSAGE_FLAG_ACCOUNTS);
1660 /* translators: argument is an account name */
1661 tmp = g_strdup_printf (_("You need to enable %s to see contacts here."),
1662 tp_account_get_display_name (account));
1664 display_page_message (self, tmp, PAGE_MESSAGE_FLAG_ACCOUNTS);
1669 has_enabled_account (GList *accounts)
1673 for (l = accounts; l != NULL; l = g_list_next (l))
1675 TpAccount *account = l->data;
1677 if (tp_account_is_enabled (account))
1685 set_notebook_page (EmpathyRosterWindow *self)
1689 TpConnectionPresenceType presence;
1690 gboolean connected, connecting;
1692 connected = empathy_account_manager_get_accounts_connected (&connecting);
1694 /* Display the loading page if either:
1695 * - We are fetching contacts from Folks (startup)
1696 * - There is no account connected but at least one is connecting
1698 if (!empathy_individual_manager_get_contacts_loaded (
1699 self->priv->individual_manager) ||
1700 (!connected && connecting))
1702 display_page_message (self, NULL, PAGE_MESSAGE_FLAG_SPINNER);
1703 gtk_spinner_start (GTK_SPINNER (self->priv->spinner_loading));
1707 gtk_spinner_stop (GTK_SPINNER (self->priv->spinner_loading));
1709 accounts = tp_account_manager_dup_valid_accounts (
1710 self->priv->account_manager);
1712 len = g_list_length (accounts);
1717 display_page_no_account (self);
1721 if (!has_enabled_account (accounts))
1723 TpAccount *account = NULL;
1725 /* Pass the account if there is only one which can be enabled */
1727 account = accounts->data;
1729 display_page_account_not_enabled (self, account);
1733 presence = tp_account_manager_get_most_available_presence (
1734 self->priv->account_manager, NULL, NULL);
1736 if (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE)
1738 display_page_message (self,
1739 _("Change your presence to see contacts here"),
1740 PAGE_MESSAGE_FLAG_ONLINE);
1744 if (empathy_roster_view_is_empty (self->priv->view))
1746 if (empathy_roster_view_is_searching (self->priv->view))
1748 display_page_message (self, _("No match found"),
1749 PAGE_MESSAGE_FLAG_NONE);
1753 if (g_settings_get_boolean (self->priv->gsettings_ui,
1754 EMPATHY_PREFS_UI_SHOW_OFFLINE))
1755 display_page_message (self, _("You haven't added any contacts yet"),
1756 PAGE_MESSAGE_FLAG_ADD_CONTACT);
1758 display_page_message (self, _("No online contacts"),
1759 PAGE_MESSAGE_FLAG_SHOW_OFFLINE);
1764 display_page_contact_list (self);
1767 g_list_free_full (accounts, g_object_unref);
1771 roster_window_account_validity_changed_cb (TpAccountManager *manager,
1774 EmpathyRosterWindow *self)
1777 add_account (self, account);
1779 roster_window_account_removed_cb (manager, account, self);
1781 set_notebook_page (self);
1785 roster_window_connection_items_setup (EmpathyRosterWindow *self)
1788 const gchar *actions_connected[] = {
1790 "room_join_favorites",
1793 "chat_search_contacts",
1795 "edit_blocked_contacts",
1798 for (i = 0; i < G_N_ELEMENTS (actions_connected); i++)
1802 action = g_action_map_lookup_action (G_ACTION_MAP (self),
1803 actions_connected[i]);
1805 self->priv->actions_connected = g_list_prepend (
1806 self->priv->actions_connected, action);
1811 account_enabled_cb (TpAccountManager *manager,
1813 EmpathyRosterWindow *self)
1815 set_notebook_page (self);
1819 account_disabled_cb (TpAccountManager *manager,
1821 EmpathyRosterWindow *self)
1823 set_notebook_page (self);
1827 account_removed_cb (TpAccountManager *manager,
1829 EmpathyRosterWindow *self)
1831 set_notebook_page (self);
1835 account_manager_prepared_cb (GObject *source_object,
1836 GAsyncResult *result,
1839 GList *accounts, *j;
1840 TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
1841 EmpathyRosterWindow *self = user_data;
1842 GError *error = NULL;
1844 if (!tp_proxy_prepare_finish (manager, result, &error))
1846 DEBUG ("Failed to prepare account manager: %s", error->message);
1847 g_error_free (error);
1851 accounts = tp_account_manager_dup_valid_accounts (
1852 self->priv->account_manager);
1853 for (j = accounts; j != NULL; j = j->next)
1855 TpAccount *account = TP_ACCOUNT (j->data);
1857 add_account (self, account);
1860 g_signal_connect (manager, "account-validity-changed",
1861 G_CALLBACK (roster_window_account_validity_changed_cb), self);
1862 tp_g_signal_connect_object (manager, "account-removed",
1863 G_CALLBACK (account_removed_cb), self, 0);
1864 tp_g_signal_connect_object (manager, "account-disabled",
1865 G_CALLBACK (account_disabled_cb), self, 0);
1866 tp_g_signal_connect_object (manager, "account-enabled",
1867 G_CALLBACK (account_enabled_cb), self, 0);
1869 roster_window_update_status (self);
1871 set_notebook_page (self);
1873 g_list_free_full (accounts, g_object_unref);
1877 empathy_roster_window_set_shell_running (EmpathyRosterWindow *self,
1878 gboolean shell_running)
1880 if (self->priv->shell_running == shell_running)
1883 self->priv->shell_running = shell_running;
1884 g_object_notify (G_OBJECT (self), "shell-running");
1888 empathy_roster_window_constructor (GType type,
1889 guint n_construct_params,
1890 GObjectConstructParam *construct_params)
1892 static GObject *window = NULL;
1895 return g_object_ref (window);
1897 window = G_OBJECT_CLASS (empathy_roster_window_parent_class)->constructor (
1898 type, n_construct_params, construct_params);
1900 g_object_add_weak_pointer (window, (gpointer) &window);
1905 static GActionEntry menubar_entries[] = {
1906 { "chat_new_message", roster_window_chat_new_message_cb, NULL, NULL, NULL },
1907 { "chat_new_call", roster_window_chat_new_call_cb, NULL, NULL, NULL },
1908 { "chat_add_contact", roster_window_chat_add_contact_cb, NULL, NULL, NULL },
1909 { "chat_search_contacts", roster_window_chat_search_contacts_cb, NULL, NULL, NULL },
1910 { "chat_quit", roster_window_chat_quit_cb, NULL, NULL, NULL },
1912 { "edit_accounts", roster_window_edit_accounts_cb, NULL, NULL, NULL },
1913 { "edit_blocked_contacts", roster_window_edit_blocked_contacts_cb, NULL, NULL, NULL },
1914 { "edit_preferences", roster_window_edit_preferences_cb, NULL, NULL, NULL },
1916 { "view_history", roster_window_view_history_cb, NULL, NULL, NULL },
1917 { "view_show_ft_manager", roster_window_view_show_ft_manager, NULL, NULL, NULL },
1919 { "room_join_new", roster_window_room_join_new_cb, NULL, NULL, NULL },
1920 { "room_join_favorites", roster_window_room_join_favorites_cb, NULL, NULL, NULL },
1921 { "join", roster_window_join_chatroom_menu_activate_cb, "(ss)", NULL, NULL },
1922 { "room_manage_favorites", roster_window_room_manage_favorites_cb, NULL, NULL, NULL },
1924 { "help_contents", roster_window_help_contents_cb, NULL, NULL, NULL },
1925 { "help_about", roster_window_help_about_cb, NULL, NULL, NULL },
1928 static GActionEntry app_entries[] =
1930 { "tab_next", next_tab_cb, NULL, NULL, NULL },
1931 { "tab_prev", prev_tab_cb, NULL, NULL, NULL }
1936 empathy_roster_window_set_property (GObject *object,
1938 const GValue *value,
1941 EmpathyRosterWindow *self = EMPATHY_ROSTER_WINDOW (object);
1943 switch (property_id)
1945 case PROP_SHELL_RUNNING:
1946 self->priv->shell_running = g_value_get_boolean (value);
1949 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1955 empathy_roster_window_get_property (GObject *object,
1960 EmpathyRosterWindow *self = EMPATHY_ROSTER_WINDOW (object);
1962 switch (property_id)
1964 case PROP_SHELL_RUNNING:
1965 g_value_set_boolean (value, self->priv->shell_running);
1968 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1974 empathy_roster_window_constructed (GObject *self)
1976 G_OBJECT_CLASS (empathy_roster_window_parent_class)->constructed (self);
1980 empathy_roster_window_class_init (EmpathyRosterWindowClass *klass)
1982 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1985 object_class->finalize = empathy_roster_window_finalize;
1986 object_class->constructor = empathy_roster_window_constructor;
1987 object_class->constructed = empathy_roster_window_constructed;
1989 object_class->set_property = empathy_roster_window_set_property;
1990 object_class->get_property = empathy_roster_window_get_property;
1992 pspec = g_param_spec_boolean ("shell-running",
1994 "Whether the Shell is running or not",
1996 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1997 g_object_class_install_property (object_class, PROP_SHELL_RUNNING, pspec);
1999 g_type_class_add_private (object_class, sizeof (EmpathyRosterWindowPriv));
2003 contacts_loaded_cb (EmpathyIndividualManager *manager,
2004 EmpathyRosterWindow *self)
2006 set_notebook_page (self);
2010 roster_window_setup_actions (EmpathyRosterWindow *self)
2014 #define ADD_GSETTINGS_ACTION(schema, key) \
2015 action = g_settings_create_action (self->priv->gsettings_##schema, \
2016 EMPATHY_PREFS_##key); \
2017 g_action_map_add_action (G_ACTION_MAP (self), action); \
2018 g_object_unref (action);
2020 ADD_GSETTINGS_ACTION (ui, UI_SHOW_OFFLINE);
2022 #undef ADD_GSETTINGS_ACTION
2026 menu_deactivate_cb (GtkMenuShell *menushell,
2029 /* FIXME: we shouldn't have to disconnect the signal (bgo #641327) */
2030 g_signal_handlers_disconnect_by_func (menushell,
2031 menu_deactivate_cb, user_data);
2033 gtk_menu_detach (GTK_MENU (menushell));
2037 menu_item_activated_cb (GtkMenuShell *menushell,
2040 EmpathyRosterWindow *roster_window = EMPATHY_ROSTER_WINDOW (user_data);
2042 hide_search_bar (roster_window);
2046 popup_individual_menu_cb (EmpathyRosterView *view,
2047 const gchar *active_group,
2048 FolksIndividual *individual,
2054 EmpathyIndividualFeatureFlags features = EMPATHY_INDIVIDUAL_FEATURE_CHAT |
2055 EMPATHY_INDIVIDUAL_FEATURE_CALL |
2056 EMPATHY_INDIVIDUAL_FEATURE_EDIT |
2057 EMPATHY_INDIVIDUAL_FEATURE_INFO |
2058 EMPATHY_INDIVIDUAL_FEATURE_LOG |
2059 EMPATHY_INDIVIDUAL_FEATURE_SMS |
2060 EMPATHY_INDIVIDUAL_FEATURE_CALL_PHONE |
2061 EMPATHY_INDIVIDUAL_FEATURE_REMOVE |
2062 EMPATHY_INDIVIDUAL_FEATURE_FILE_TRANSFER;
2064 menu = empathy_individual_menu_new (individual, active_group,
2067 /* menu is initially unowned but gtk_menu_attach_to_widget() takes its
2068 * floating ref. We can either wait for the view to release its ref
2069 * when it is destroyed (when leaving Empathy) or explicitly
2070 * detach the menu when it's not displayed any more.
2071 * We go for the latter as we don't want to keep useless menus in memory
2072 * during the whole lifetime of Empathy. */
2073 g_signal_connect (menu, "deactivate", G_CALLBACK (menu_deactivate_cb),
2075 g_signal_connect (menu, "menu-item-activated",
2076 G_CALLBACK (menu_item_activated_cb), user_data);
2078 gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (view), NULL);
2079 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
2083 view_empty_cb (EmpathyRosterView *view,
2085 EmpathyRosterWindow *self)
2087 set_notebook_page (self);
2089 if (!empathy_roster_view_is_empty (view))
2091 gtk_widget_grab_focus (GTK_WIDGET (self->priv->view));
2093 /* The store is being filled, it will be done after an idle cb.
2094 * So we can then get events. If we do that too soon, event's
2095 * contact is not yet in the store and it won't get marked as
2097 g_idle_add (roster_window_load_events_idle_cb, self);
2102 tooltip_destroy_cb (GtkWidget *widget,
2103 EmpathyRosterWindow *self)
2105 g_clear_object (&self->priv->tooltip_widget);
2109 individual_tooltip_cb (EmpathyRosterView *view,
2110 FolksIndividual *individual,
2111 gboolean keyboard_mode,
2112 GtkTooltip *tooltip,
2113 EmpathyRosterWindow *self)
2115 if (self->priv->tooltip_widget == NULL)
2117 self->priv->tooltip_widget = empathy_individual_widget_new (individual,
2118 EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP |
2119 EMPATHY_INDIVIDUAL_WIDGET_SHOW_LOCATION |
2120 EMPATHY_INDIVIDUAL_WIDGET_SHOW_CLIENT_TYPES);
2122 gtk_container_set_border_width (
2123 GTK_CONTAINER (self->priv->tooltip_widget), 8);
2125 g_object_ref (self->priv->tooltip_widget);
2127 tp_g_signal_connect_object (self->priv->tooltip_widget, "destroy",
2128 G_CALLBACK (tooltip_destroy_cb), self, 0);
2130 gtk_widget_show (self->priv->tooltip_widget);
2134 empathy_individual_widget_set_individual (
2135 EMPATHY_INDIVIDUAL_WIDGET (self->priv->tooltip_widget), individual);
2138 gtk_tooltip_set_custom (tooltip, self->priv->tooltip_widget);
2145 DND_DRAG_TYPE_INVALID = -1,
2146 DND_DRAG_TYPE_URI_LIST,
2149 #define DRAG_TYPE(T,I) \
2150 { (gchar *) T, 0, I }
2152 static const GtkTargetEntry drag_types_dest[] = {
2153 DRAG_TYPE ("text/path-list", DND_DRAG_TYPE_URI_LIST),
2154 DRAG_TYPE ("text/uri-list", DND_DRAG_TYPE_URI_LIST),
2157 static GdkAtom drag_atoms_dest[G_N_ELEMENTS (drag_types_dest)];
2160 get_drag_type (GtkWidget *widget,
2161 GdkDragContext *context)
2166 target = gtk_drag_dest_find_target (widget, context, NULL);
2168 for (i = 0; i < G_N_ELEMENTS (drag_atoms_dest); i++)
2170 if (target == drag_atoms_dest[i])
2171 return drag_types_dest[i].info;
2174 return DND_DRAG_TYPE_INVALID;
2178 individual_supports_ft (FolksIndividual *individual)
2180 EmpathyContact *contact;
2181 EmpathyCapabilities caps;
2184 contact = empathy_contact_dup_from_folks_individual (individual);
2185 if (contact == NULL)
2188 caps = empathy_contact_get_capabilities (contact);
2189 result = (caps & EMPATHY_CAPABILITIES_FT);
2191 g_object_unref (contact);
2196 view_drag_motion_cb (GtkWidget *widget,
2197 GdkDragContext *context,
2201 EmpathyRosterWindow *self)
2205 type = get_drag_type (widget, context);
2207 if (type == DND_DRAG_TYPE_URI_LIST)
2209 /* Check if contact supports FT */
2210 FolksIndividual *individual;
2213 individual = empathy_roster_view_get_individual_at_y (self->priv->view,
2215 if (individual == NULL)
2218 if (!individual_supports_ft (individual))
2221 gtk_list_box_drag_highlight_row (GTK_LIST_BOX (widget), row);
2226 gtk_list_box_drag_unhighlight_row (GTK_LIST_BOX (widget));
2231 view_drag_drop_cb (GtkWidget *widget,
2232 GdkDragContext *context,
2236 EmpathyRosterWindow *self)
2239 FolksIndividual *individual;
2241 type = get_drag_type (widget, context);
2242 if (type == DND_DRAG_TYPE_INVALID)
2245 individual = empathy_roster_view_get_individual_at_y (self->priv->view, y,
2247 if (individual == NULL)
2250 if (!individual_supports_ft (individual))
2253 gtk_drag_get_data (widget, context,
2254 gtk_drag_dest_find_target (widget, context, NULL), time_);
2260 view_drag_data_received_cb (GtkWidget *widget,
2261 GdkDragContext *context,
2264 GtkSelectionData *selection,
2267 EmpathyRosterWindow *self)
2269 gboolean success = FALSE;
2271 if (selection == NULL)
2274 if (info == DND_DRAG_TYPE_URI_LIST)
2277 FolksIndividual *individual;
2278 EmpathyContact *contact;
2280 individual = empathy_roster_view_get_individual_at_y (self->priv->view,
2282 g_return_if_fail (individual != NULL);
2284 path = (const gchar *) gtk_selection_data_get_data (selection);
2286 contact = empathy_contact_dup_from_folks_individual (individual);
2287 empathy_send_file_from_uri_list (contact, path);
2289 g_object_unref (contact);
2295 gtk_drag_finish (context, success, FALSE, time_);
2299 roster_window_most_available_presence_changed_cb (TpAccountManager *manager,
2300 TpConnectionPresenceType presence,
2301 const gchar *status,
2302 const gchar *message,
2303 EmpathyRosterWindow *self)
2305 set_notebook_page (self);
2309 show_offline_changed_cb (GSettings *settings,
2311 EmpathyRosterWindow *self)
2313 set_notebook_page (self);
2317 empathy_roster_window_init (EmpathyRosterWindow *self)
2322 GtkWidget *header_bar;
2323 GtkWidget *new_conversation_button;
2325 GtkWidget *chat_vbox;
2327 EmpathyRosterModel *model;
2329 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
2330 EMPATHY_TYPE_ROSTER_WINDOW, EmpathyRosterWindowPriv);
2332 empathy_set_css_provider (GTK_WIDGET (self));
2334 self->priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2336 self->priv->sound_mgr = empathy_sound_manager_dup_singleton ();
2338 gtk_window_set_title (GTK_WINDOW (self), _("Contact List"));
2339 gtk_window_set_role (GTK_WINDOW (self), "contact_list");
2340 gtk_window_set_default_size (GTK_WINDOW (self), 225, 325);
2342 /* don't finalize the widget on delete-event, just hide it */
2343 g_signal_connect (self, "delete-event",
2344 G_CALLBACK (gtk_widget_hide_on_delete), NULL);
2346 /* Set up interface */
2347 filename = empathy_file_lookup ("empathy-roster-window.ui", "src");
2348 gui = tpaw_builder_get_file (filename,
2349 "main_vbox", &self->priv->main_vbox,
2350 "chat_vbox", &chat_vbox,
2351 "balance_vbox", &self->priv->balance_vbox,
2352 "errors_vbox", &self->priv->errors_vbox,
2353 "auth_vbox", &self->priv->auth_vbox,
2354 "presence_toolbar", &self->priv->presence_toolbar,
2355 "notebook", &self->priv->notebook,
2356 "no_entry_label", &self->priv->no_entry_label,
2357 "roster_scrolledwindow", &sw,
2358 "button_account_settings", &self->priv->button_account_settings,
2359 "button_online", &self->priv->button_online,
2360 "button_show_offline", &self->priv->button_show_offline,
2361 "button_add_contact", &self->priv->button_add_contact,
2362 "spinner_loading", &self->priv->spinner_loading,
2366 header_bar = gtk_header_bar_new ();
2367 gtk_header_bar_set_title (GTK_HEADER_BAR(header_bar), _("Conversations"));
2368 gtk_header_bar_set_show_close_button (GTK_HEADER_BAR(header_bar), TRUE);
2370 image = gtk_image_new_from_icon_name ("list-add-symbolic", GTK_ICON_SIZE_BUTTON);
2371 new_conversation_button = gtk_button_new ();
2372 g_signal_connect (new_conversation_button, "clicked",
2373 G_CALLBACK (roster_window_chat_new_message_cb), self);
2374 gtk_button_set_image (GTK_BUTTON (new_conversation_button), image);
2375 gtk_widget_set_tooltip_text (new_conversation_button, _("New Conversation"));
2376 gtk_window_set_titlebar (GTK_WINDOW (self), header_bar);
2377 gtk_container_add (GTK_CONTAINER (header_bar), new_conversation_button);
2378 gtk_widget_show_all (header_bar);
2380 gtk_container_add (GTK_CONTAINER (self), self->priv->main_vbox);
2381 gtk_widget_show (self->priv->main_vbox);
2383 g_signal_connect (self, "key-press-event",
2384 G_CALLBACK (roster_window_key_press_event_cb), self);
2386 g_object_unref (gui);
2388 self->priv->account_manager = tp_account_manager_dup ();
2390 tp_proxy_prepare_async (self->priv->account_manager, NULL,
2391 account_manager_prepared_cb, self);
2393 self->priv->errors = g_hash_table_new_full (g_direct_hash, g_direct_equal,
2394 g_object_unref, NULL);
2396 self->priv->auths = g_hash_table_new (NULL, NULL);
2398 self->priv->status_changed_handlers = g_hash_table_new_full (g_direct_hash,
2399 g_direct_equal, NULL, NULL);
2401 /* set up accelerators */
2402 g_action_map_add_action_entries (G_ACTION_MAP (self),
2403 app_entries, G_N_ELEMENTS (app_entries), self);
2406 g_action_map_add_action_entries (G_ACTION_MAP (self),
2407 menubar_entries, G_N_ELEMENTS (menubar_entries), self);
2408 roster_window_setup_actions (self);
2410 filename = empathy_file_lookup ("empathy-roster-window-menubar.ui", "src");
2411 gui = tpaw_builder_get_file (filename,
2412 "appmenu", &self->priv->menumodel,
2413 "rooms", &self->priv->rooms_section,
2417 g_object_ref (self->priv->menumodel);
2418 g_object_ref (self->priv->rooms_section);
2420 /* Set up connection related actions. */
2421 roster_window_connection_items_setup (self);
2422 roster_window_favorite_chatroom_menu_setup (self);
2424 g_object_unref (gui);
2426 /* Set up contact list. */
2427 empathy_status_presets_get_all ();
2429 /* Set up presence chooser */
2430 self->priv->presence_chooser = empathy_presence_chooser_new ();
2431 gtk_widget_show (self->priv->presence_chooser);
2432 gtk_box_pack_start (GTK_BOX (self->priv->presence_toolbar),
2433 self->priv->presence_chooser,
2436 /* Set up the throbber */
2437 self->priv->throbber = gtk_spinner_new ();
2438 gtk_widget_set_size_request (self->priv->throbber, 16, -1);
2439 gtk_widget_set_events (self->priv->throbber, GDK_BUTTON_PRESS_MASK);
2440 g_signal_connect (self->priv->throbber, "button-press-event",
2441 G_CALLBACK (roster_window_throbber_button_press_event_cb),
2443 gtk_box_pack_start (GTK_BOX (self->priv->presence_toolbar),
2444 self->priv->throbber,
2447 self->priv->individual_manager = empathy_individual_manager_dup_singleton ();
2449 model = EMPATHY_ROSTER_MODEL (empathy_roster_model_manager_new (self->priv->individual_manager));
2451 tp_g_signal_connect_object (self->priv->individual_manager,
2452 "contacts-loaded", G_CALLBACK (contacts_loaded_cb), self, 0);
2454 self->priv->view = EMPATHY_ROSTER_VIEW (
2455 empathy_roster_view_new (model));
2458 g_object_unref (model);
2460 gtk_widget_show (GTK_WIDGET (self->priv->view));
2462 gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (self->priv->view));
2464 g_signal_connect (self->priv->view, "individual-activated",
2465 G_CALLBACK (individual_activated_cb), self);
2466 g_signal_connect (self->priv->view, "event-activated",
2467 G_CALLBACK (event_activated_cb), self);
2468 g_signal_connect (self->priv->view, "popup-individual-menu",
2469 G_CALLBACK (popup_individual_menu_cb), self);
2470 g_signal_connect (self->priv->view, "notify::empty",
2471 G_CALLBACK (view_empty_cb), self);
2472 g_signal_connect (self->priv->view, "individual-tooltip",
2473 G_CALLBACK (individual_tooltip_cb), self);
2475 /* DnD - destination */
2476 gtk_drag_dest_set (GTK_WIDGET (self->priv->view), GTK_DEST_DEFAULT_MOTION,
2477 drag_types_dest, G_N_ELEMENTS (drag_types_dest), GDK_ACTION_COPY);
2479 for (i = 0; i < G_N_ELEMENTS (drag_types_dest); ++i)
2480 drag_atoms_dest[i] = gdk_atom_intern (drag_types_dest[i].target, FALSE);
2482 g_signal_connect (self->priv->view, "drag-motion",
2483 G_CALLBACK (view_drag_motion_cb), self);
2484 g_signal_connect (self->priv->view, "drag-drop",
2485 G_CALLBACK (view_drag_drop_cb), self);
2486 g_signal_connect (self->priv->view, "drag-data-received",
2487 G_CALLBACK (view_drag_data_received_cb), self);
2489 gtk_widget_set_has_tooltip (GTK_WIDGET (self->priv->view), TRUE);
2491 /* Set up search bar */
2492 self->priv->search_bar = tpaw_live_search_new (
2493 GTK_WIDGET (self->priv->view));
2494 empathy_roster_view_set_live_search (self->priv->view,
2495 TPAW_LIVE_SEARCH (self->priv->search_bar));
2497 g_signal_connect_swapped (self, "map",
2498 G_CALLBACK (gtk_widget_grab_focus), self->priv->view);
2500 /* Load user-defined accelerators. */
2501 roster_window_accels_load ();
2503 gtk_window_set_default_size (GTK_WINDOW (self), -1, 600);
2504 /* Set window size. */
2505 empathy_geometry_bind (GTK_WINDOW (self), GEOMETRY_NAME);
2507 self->priv->chat_window = GTK_WIDGET (empathy_chat_window_new ());
2508 gtk_widget_show (GTK_WIDGET (self->priv->chat_window) );
2509 gtk_box_pack_start (GTK_BOX (chat_vbox), self->priv->chat_window, TRUE, TRUE, 0);
2511 /* Enable event handling */
2512 self->priv->call_observer = empathy_call_observer_dup_singleton ();
2513 self->priv->event_manager = empathy_event_manager_dup_singleton ();
2514 self->priv->chat_manager = empathy_chat_manager_dup_singleton ();
2516 self->priv->theme_manager = empathy_theme_manager_dup_singleton ();
2518 tp_g_signal_connect_object (self->priv->event_manager, "event-added",
2519 G_CALLBACK (roster_window_event_added_cb), self, 0);
2520 tp_g_signal_connect_object (self->priv->event_manager, "event-removed",
2521 G_CALLBACK (roster_window_event_removed_cb), self, 0);
2523 g_signal_connect (self->priv->account_manager, "account-validity-changed",
2524 G_CALLBACK (roster_window_account_validity_changed_cb), self);
2525 g_signal_connect (self->priv->account_manager, "account-removed",
2526 G_CALLBACK (roster_window_account_removed_cb), self);
2527 g_signal_connect (self->priv->account_manager, "account-disabled",
2528 G_CALLBACK (roster_window_account_disabled_cb), self);
2529 g_signal_connect (self->priv->account_manager,
2530 "most-available-presence-changed",
2531 G_CALLBACK (roster_window_most_available_presence_changed_cb), self);
2533 g_settings_bind (self->priv->gsettings_ui, EMPATHY_PREFS_UI_SHOW_OFFLINE,
2534 self->priv->view, "show-offline",
2535 G_SETTINGS_BIND_GET);
2536 tp_g_signal_connect_object (self->priv->gsettings_ui,
2537 "changed::" EMPATHY_PREFS_UI_SHOW_OFFLINE,
2538 G_CALLBACK (show_offline_changed_cb), self, 0);
2539 g_settings_bind (self->priv->gsettings_ui, EMPATHY_PREFS_UI_SHOW_GROUPS,
2540 self->priv->view, "show-groups",
2541 G_SETTINGS_BIND_GET);
2542 g_settings_bind (self->priv->gsettings_ui, "show-balance-in-roster",
2543 self->priv->balance_vbox, "visible",
2544 G_SETTINGS_BIND_GET);
2546 g_signal_connect (self->priv->button_account_settings, "clicked",
2547 G_CALLBACK (button_account_settings_clicked_cb), self);
2548 g_signal_connect (self->priv->button_online, "clicked",
2549 G_CALLBACK (button_online_clicked_cb), self);
2550 g_signal_connect (self->priv->button_show_offline, "clicked",
2551 G_CALLBACK (button_show_offline_clicked_cb), self);
2552 g_signal_connect (self->priv->button_add_contact, "clicked",
2553 G_CALLBACK (button_add_contact_clicked_cb), self);
2557 empathy_roster_window_new (GtkApplication *app)
2559 return g_object_new (EMPATHY_TYPE_ROSTER_WINDOW,
2565 empathy_roster_window_get_menu_model (EmpathyRosterWindow *self)
2567 g_return_val_if_fail (EMPATHY_IS_ROSTER_WINDOW (self), NULL);
2569 return G_MENU_MODEL (self->priv->menumodel);