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>
28 #include <gdk/gdkkeysyms.h>
29 #include <glib/gi18n.h>
31 #include <telepathy-glib/account-manager.h>
32 #include <telepathy-glib/util.h>
33 #include <folks/folks.h>
35 #include <libempathy/empathy-contact.h>
36 #include <libempathy/empathy-utils.h>
37 #include <libempathy/empathy-request-util.h>
38 #include <libempathy/empathy-chatroom-manager.h>
39 #include <libempathy/empathy-chatroom.h>
40 #include <libempathy/empathy-contact-list.h>
41 #include <libempathy/empathy-gsettings.h>
42 #include <libempathy/empathy-individual-manager.h>
43 #include <libempathy/empathy-gsettings.h>
44 #include <libempathy/empathy-status-presets.h>
45 #include <libempathy/empathy-tp-contact-factory.h>
47 #include <libempathy-gtk/empathy-contact-dialogs.h>
48 #include <libempathy-gtk/empathy-live-search.h>
49 #include <libempathy-gtk/empathy-contact-blocking-dialog.h>
50 #include <libempathy-gtk/empathy-contact-search-dialog.h>
51 #include <libempathy-gtk/empathy-geometry.h>
52 #include <libempathy-gtk/empathy-gtk-enum-types.h>
53 #include <libempathy-gtk/empathy-individual-dialogs.h>
54 #include <libempathy-gtk/empathy-individual-store.h>
55 #include <libempathy-gtk/empathy-individual-store-manager.h>
56 #include <libempathy-gtk/empathy-individual-view.h>
57 #include <libempathy-gtk/empathy-new-message-dialog.h>
58 #include <libempathy-gtk/empathy-new-call-dialog.h>
59 #include <libempathy-gtk/empathy-log-window.h>
60 #include <libempathy-gtk/empathy-presence-chooser.h>
61 #include <libempathy-gtk/empathy-sound-manager.h>
62 #include <libempathy-gtk/empathy-ui-utils.h>
64 #include "empathy-accounts-dialog.h"
65 #include "empathy-call-observer.h"
66 #include "empathy-chat-manager.h"
67 #include "empathy-roster-window.h"
68 #include "empathy-preferences.h"
69 #include "empathy-about-dialog.h"
70 #include "empathy-debug-window.h"
71 #include "empathy-new-chatroom-dialog.h"
72 #include "empathy-map-view.h"
73 #include "empathy-chatrooms-window.h"
74 #include "empathy-event-manager.h"
75 #include "empathy-ft-manager.h"
77 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
78 #include <libempathy/empathy-debug.h>
80 /* Flashing delay for icons (milliseconds). */
81 #define FLASH_TIMEOUT 500
83 /* Minimum width of roster window if something goes wrong. */
86 /* Accels (menu shortcuts) can be configured and saved */
87 #define ACCELS_FILENAME "accels.txt"
89 /* Name in the geometry file */
90 #define GEOMETRY_NAME "roster-window"
94 PAGE_CONTACT_LIST
= 0,
104 G_DEFINE_TYPE (EmpathyRosterWindow
, empathy_roster_window
, GTK_TYPE_WINDOW
);
106 struct _EmpathyRosterWindowPriv
{
107 EmpathyIndividualStore
*individual_store
;
108 EmpathyIndividualView
*individual_view
;
109 TpAccountManager
*account_manager
;
110 EmpathyChatroomManager
*chatroom_manager
;
111 EmpathyEventManager
*event_manager
;
112 EmpathySoundManager
*sound_mgr
;
113 EmpathyCallObserver
*call_observer
;
114 EmpathyIndividualManager
*individual_manager
;
115 guint flash_timeout_id
;
119 GSettings
*gsettings_ui
;
120 GSettings
*gsettings_contacts
;
122 GtkWidget
*preferences
;
123 GtkWidget
*main_vbox
;
125 GtkWidget
*throbber_tool_item
;
126 GtkWidget
*presence_toolbar
;
127 GtkWidget
*presence_chooser
;
128 GtkWidget
*errors_vbox
;
129 GtkWidget
*auth_vbox
;
130 GtkWidget
*search_bar
;
132 GtkWidget
*no_entry_label
;
133 GtkWidget
*button_account_settings
;
134 GtkWidget
*spinner_loading
;
136 GtkToggleAction
*show_protocols
;
137 GtkRadioAction
*sort_by_name
;
138 GtkRadioAction
*sort_by_status
;
139 GtkRadioAction
*normal_with_avatars
;
140 GtkRadioAction
*normal_size
;
141 GtkRadioAction
*compact_size
;
143 GtkUIManager
*ui_manager
;
144 GtkAction
*view_history
;
145 GtkAction
*room_join_favorites
;
146 GtkWidget
*room_menu
;
147 GtkWidget
*room_separator
;
148 GtkWidget
*edit_context
;
149 GtkWidget
*edit_context_separator
;
151 GtkActionGroup
*balance_action_group
;
152 GtkAction
*view_balance_show_in_roster
;
153 GtkWidget
*balance_vbox
;
155 guint size_timeout_id
;
157 /* reffed TpAccount* => visible GtkInfoBar* */
160 /* EmpathyEvent* => visible GtkInfoBar* */
163 /* stores a mapping from TpAccount to Handler ID to prevent
164 * to listen more than once to the status-changed signal */
165 GHashTable
*status_changed_handlers
;
167 /* Actions that are enabled when there are connected accounts */
168 GList
*actions_connected
;
170 gboolean shell_running
;
174 roster_window_flash_stop (EmpathyRosterWindow
*self
)
176 if (self
->priv
->flash_timeout_id
== 0)
179 DEBUG ("Stop flashing");
180 g_source_remove (self
->priv
->flash_timeout_id
);
181 self
->priv
->flash_timeout_id
= 0;
182 self
->priv
->flash_on
= FALSE
;
189 EmpathyRosterWindow
*self
;
193 roster_window_flash_foreach (GtkTreeModel
*model
,
198 FlashForeachData
*data
= (FlashForeachData
*) user_data
;
199 FolksIndividual
*individual
;
200 EmpathyContact
*contact
;
201 const gchar
*icon_name
;
202 GtkTreePath
*parent_path
= NULL
;
203 GtkTreeIter parent_iter
;
204 GdkPixbuf
*pixbuf
= NULL
;
206 gtk_tree_model_get (model
, iter
,
207 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL
, &individual
,
210 if (individual
== NULL
)
213 contact
= empathy_contact_dup_from_folks_individual (individual
);
214 if (contact
!= data
->event
->contact
)
219 icon_name
= data
->event
->icon_name
;
220 pixbuf
= empathy_pixbuf_from_icon_name (icon_name
, GTK_ICON_SIZE_MENU
);
224 pixbuf
= empathy_individual_store_get_individual_status_icon (
225 data
->self
->priv
->individual_store
,
228 g_object_ref (pixbuf
);
231 gtk_tree_store_set (GTK_TREE_STORE (model
), iter
,
232 EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS
, pixbuf
,
235 /* To make sure the parent is shown correctly, we emit
236 * the row-changed signal on the parent so it prompts
237 * it to be refreshed by the filter func.
239 if (gtk_tree_model_iter_parent (model
, &parent_iter
, iter
))
241 parent_path
= gtk_tree_model_get_path (model
, &parent_iter
);
244 if (parent_path
!= NULL
)
246 gtk_tree_model_row_changed (model
, parent_path
, &parent_iter
);
247 gtk_tree_path_free (parent_path
);
251 g_object_unref (individual
);
252 tp_clear_object (&contact
);
253 tp_clear_object (&pixbuf
);
259 roster_window_flash_cb (EmpathyRosterWindow
*self
)
263 gboolean found_event
= FALSE
;
264 FlashForeachData data
;
266 self
->priv
->flash_on
= !self
->priv
->flash_on
;
267 data
.on
= self
->priv
->flash_on
;
268 model
= GTK_TREE_MODEL (self
->priv
->individual_store
);
270 events
= empathy_event_manager_get_events (self
->priv
->event_manager
);
271 for (l
= events
; l
; l
= l
->next
)
273 data
.event
= l
->data
;
275 if (!data
.event
->contact
|| !data
.event
->must_ack
)
279 gtk_tree_model_foreach (model
,
280 roster_window_flash_foreach
,
285 roster_window_flash_stop (self
);
291 roster_window_flash_start (EmpathyRosterWindow
*self
)
293 if (self
->priv
->flash_timeout_id
!= 0)
296 DEBUG ("Start flashing");
297 self
->priv
->flash_timeout_id
= g_timeout_add (FLASH_TIMEOUT
,
298 (GSourceFunc
) roster_window_flash_cb
, self
);
300 roster_window_flash_cb (self
);
304 roster_window_remove_auth (EmpathyRosterWindow
*self
,
307 GtkWidget
*error_widget
;
309 error_widget
= g_hash_table_lookup (self
->priv
->auths
, event
);
310 if (error_widget
!= NULL
)
312 gtk_widget_destroy (error_widget
);
313 g_hash_table_remove (self
->priv
->auths
, event
);
318 roster_window_auth_add_clicked_cb (GtkButton
*button
,
319 EmpathyRosterWindow
*self
)
323 event
= g_object_get_data (G_OBJECT (button
), "event");
325 empathy_event_approve (event
);
327 roster_window_remove_auth (self
, event
);
331 roster_window_auth_close_clicked_cb (GtkButton
*button
,
332 EmpathyRosterWindow
*self
)
336 event
= g_object_get_data (G_OBJECT (button
), "event");
338 empathy_event_decline (event
);
339 roster_window_remove_auth (self
, event
);
343 roster_window_auth_display (EmpathyRosterWindow
*self
,
346 TpAccount
*account
= event
->account
;
348 GtkWidget
*content_area
;
351 GtkWidget
*add_button
;
352 GtkWidget
*close_button
;
353 GtkWidget
*action_area
;
354 GtkWidget
*action_grid
;
355 const gchar
*icon_name
;
358 if (g_hash_table_lookup (self
->priv
->auths
, event
) != NULL
)
361 info_bar
= gtk_info_bar_new ();
362 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar
), GTK_MESSAGE_QUESTION
);
364 gtk_widget_set_no_show_all (info_bar
, TRUE
);
365 gtk_box_pack_start (GTK_BOX (self
->priv
->auth_vbox
), info_bar
, FALSE
, TRUE
, 0);
366 gtk_widget_show (info_bar
);
368 icon_name
= tp_account_get_icon_name (account
);
369 image
= gtk_image_new_from_icon_name (icon_name
, GTK_ICON_SIZE_SMALL_TOOLBAR
);
370 gtk_widget_show (image
);
372 str
= g_markup_printf_escaped ("<b>%s</b>\n%s",
373 tp_account_get_display_name (account
),
374 _("Password required"));
376 label
= gtk_label_new (str
);
377 gtk_label_set_use_markup (GTK_LABEL (label
), TRUE
);
378 gtk_label_set_ellipsize (GTK_LABEL (label
), PANGO_ELLIPSIZE_END
);
379 gtk_misc_set_alignment (GTK_MISC (label
), 0, 0.5);
380 gtk_widget_show (label
);
384 content_area
= gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar
));
385 gtk_box_pack_start (GTK_BOX (content_area
), image
, FALSE
, FALSE
, 0);
386 gtk_box_pack_start (GTK_BOX (content_area
), label
, TRUE
, TRUE
, 0);
388 image
= gtk_image_new_from_stock (GTK_STOCK_ADD
, GTK_ICON_SIZE_BUTTON
);
389 add_button
= gtk_button_new ();
390 gtk_button_set_image (GTK_BUTTON (add_button
), image
);
391 gtk_widget_set_tooltip_text (add_button
, _("Provide Password"));
392 gtk_widget_show (add_button
);
394 image
= gtk_image_new_from_stock (GTK_STOCK_CLOSE
, GTK_ICON_SIZE_BUTTON
);
395 close_button
= gtk_button_new ();
396 gtk_button_set_image (GTK_BUTTON (close_button
), image
);
397 gtk_widget_set_tooltip_text (close_button
, _("Disconnect"));
398 gtk_widget_show (close_button
);
400 action_grid
= gtk_grid_new ();
401 gtk_grid_set_column_spacing (GTK_GRID (action_grid
), 6);
402 gtk_widget_show (action_grid
);
404 action_area
= gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar
));
405 gtk_box_pack_start (GTK_BOX (action_area
), action_grid
, FALSE
, FALSE
, 0);
407 gtk_grid_attach (GTK_GRID (action_grid
), add_button
, 0, 0, 1, 1);
408 gtk_grid_attach (GTK_GRID (action_grid
), close_button
, 1, 0, 1, 1);
410 g_object_set_data_full (G_OBJECT (info_bar
),
411 "event", event
, NULL
);
412 g_object_set_data_full (G_OBJECT (add_button
),
413 "event", event
, NULL
);
414 g_object_set_data_full (G_OBJECT (close_button
),
415 "event", event
, NULL
);
417 g_signal_connect (add_button
, "clicked",
418 G_CALLBACK (roster_window_auth_add_clicked_cb
), self
);
419 g_signal_connect (close_button
, "clicked",
420 G_CALLBACK (roster_window_auth_close_clicked_cb
), self
);
422 gtk_widget_show (self
->priv
->auth_vbox
);
424 g_hash_table_insert (self
->priv
->auths
, event
, info_bar
);
428 modify_event_count (GtkTreeModel
*model
,
433 FolksIndividual
*individual
;
434 EmpathyContact
*contact
;
437 gtk_tree_model_get (model
, iter
,
438 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL
, &individual
,
439 EMPATHY_INDIVIDUAL_STORE_COL_EVENT_COUNT
, &count
,
442 if (individual
== NULL
)
445 increase
? count
++ : count
--;
447 contact
= empathy_contact_dup_from_folks_individual (individual
);
448 if (contact
== event
->contact
)
449 gtk_tree_store_set (GTK_TREE_STORE (model
), iter
,
450 EMPATHY_INDIVIDUAL_STORE_COL_EVENT_COUNT
, count
, -1);
452 tp_clear_object (&contact
);
453 g_object_unref (individual
);
457 increase_event_count_foreach (GtkTreeModel
*model
,
462 EmpathyEvent
*event
= user_data
;
464 modify_event_count (model
, iter
, event
, TRUE
);
470 increase_event_count (EmpathyRosterWindow
*self
,
475 model
= GTK_TREE_MODEL (self
->priv
->individual_store
);
477 gtk_tree_model_foreach (model
, increase_event_count_foreach
, event
);
481 decrease_event_count_foreach (GtkTreeModel
*model
,
486 EmpathyEvent
*event
= user_data
;
488 modify_event_count (model
, iter
, event
, FALSE
);
494 decrease_event_count (EmpathyRosterWindow
*self
,
499 model
= GTK_TREE_MODEL (self
->priv
->individual_store
);
501 gtk_tree_model_foreach (model
, decrease_event_count_foreach
, event
);
505 roster_window_event_added_cb (EmpathyEventManager
*manager
,
507 EmpathyRosterWindow
*self
)
511 increase_event_count (self
, event
);
513 roster_window_flash_start (self
);
515 else if (event
->type
== EMPATHY_EVENT_TYPE_AUTH
)
517 roster_window_auth_display (self
, event
);
522 roster_window_event_removed_cb (EmpathyEventManager
*manager
,
524 EmpathyRosterWindow
*self
)
526 FlashForeachData data
;
528 if (event
->type
== EMPATHY_EVENT_TYPE_AUTH
)
530 roster_window_remove_auth (self
, event
);
537 decrease_event_count (self
, event
);
543 gtk_tree_model_foreach (GTK_TREE_MODEL (self
->priv
->individual_store
),
544 roster_window_flash_foreach
,
549 roster_window_load_events_idle_cb (gpointer user_data
)
551 EmpathyRosterWindow
*self
= user_data
;
554 l
= empathy_event_manager_get_events (self
->priv
->event_manager
);
557 roster_window_event_added_cb (self
->priv
->event_manager
, l
->data
,
566 roster_window_row_activated_cb (EmpathyIndividualView
*view
,
568 GtkTreeViewColumn
*col
,
569 EmpathyRosterWindow
*self
)
571 EmpathyContact
*contact
= NULL
;
572 FolksIndividual
*individual
;
577 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (self
->priv
->individual_view
));
578 gtk_tree_model_get_iter (model
, &iter
, path
);
580 gtk_tree_model_get (model
, &iter
, EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL
,
583 if (individual
!= NULL
)
584 contact
= empathy_contact_dup_from_folks_individual (individual
);
589 /* If the contact has an event activate it, otherwise the
590 * default handler of row-activated will be called. */
591 events
= empathy_event_manager_get_events (self
->priv
->event_manager
);
592 for (l
= events
; l
; l
= l
->next
)
594 EmpathyEvent
*event
= l
->data
;
596 if (event
->contact
== contact
)
598 DEBUG ("Activate event");
599 empathy_event_activate (event
);
601 /* We don't want the default handler of this signal
602 * (e.g. open a chat) */
603 g_signal_stop_emission_by_name (view
, "row-activated");
608 g_object_unref (contact
);
610 tp_clear_object (&individual
);
614 button_account_settings_clicked_cb (GtkButton
*button
,
615 EmpathyRosterWindow
*self
)
617 empathy_accounts_dialog_show_application (gdk_screen_get_default (),
622 display_page_message (EmpathyRosterWindow
*self
,
624 gboolean display_accounts_button
,
625 gboolean display_spinner
)
631 tmp
= g_strdup_printf ("<b><span size='xx-large'>%s</span></b>", msg
);
633 gtk_label_set_markup (GTK_LABEL (self
->priv
->no_entry_label
), tmp
);
636 gtk_label_set_line_wrap (GTK_LABEL (self
->priv
->no_entry_label
), TRUE
);
637 gtk_widget_show (self
->priv
->no_entry_label
);
641 gtk_widget_hide (self
->priv
->no_entry_label
);
644 gtk_widget_set_visible (self
->priv
->button_account_settings
,
645 display_accounts_button
);
646 gtk_widget_set_visible (self
->priv
->spinner_loading
,
649 gtk_notebook_set_current_page (GTK_NOTEBOOK (self
->priv
->notebook
),
654 display_page_no_account (EmpathyRosterWindow
*self
)
656 display_page_message (self
,
657 _("You need to setup an account to see contacts here."), TRUE
, FALSE
);
661 roster_window_row_deleted_cb (GtkTreeModel
*model
,
663 EmpathyRosterWindow
*self
)
665 GtkTreeIter help_iter
;
667 if (!gtk_tree_model_get_iter_first (model
, &help_iter
))
669 self
->priv
->empty
= TRUE
;
671 if (empathy_individual_view_is_searching (self
->priv
->individual_view
))
673 display_page_message (self
, _("No match found"), FALSE
, FALSE
);
679 display_page_contact_list (EmpathyRosterWindow
*self
)
681 if (!empathy_individual_manager_get_contacts_loaded (
682 self
->priv
->individual_manager
))
683 /* We'll display the contact list once we're done loading */
686 gtk_notebook_set_current_page (GTK_NOTEBOOK (self
->priv
->notebook
),
691 roster_window_row_inserted_cb (GtkTreeModel
*model
,
694 EmpathyRosterWindow
*self
)
696 if (self
->priv
->empty
)
698 self
->priv
->empty
= FALSE
;
700 display_page_contact_list (self
);
701 gtk_widget_grab_focus (GTK_WIDGET (self
->priv
->individual_view
));
703 /* The store is being filled, it will be done after an idle cb.
704 * So we can then get events. If we do that too soon, event's
705 * contact is not yet in the store and it won't get marked as
707 g_idle_add (roster_window_load_events_idle_cb
, self
);
712 roster_window_remove_error (EmpathyRosterWindow
*self
,
715 GtkWidget
*error_widget
;
717 error_widget
= g_hash_table_lookup (self
->priv
->errors
, account
);
718 if (error_widget
!= NULL
)
720 gtk_widget_destroy (error_widget
);
721 g_hash_table_remove (self
->priv
->errors
, account
);
726 roster_window_account_disabled_cb (TpAccountManager
*manager
,
728 EmpathyRosterWindow
*self
)
730 roster_window_remove_error (self
, account
);
734 roster_window_error_retry_clicked_cb (GtkButton
*button
,
735 EmpathyRosterWindow
*self
)
739 account
= g_object_get_data (G_OBJECT (button
), "account");
740 tp_account_reconnect_async (account
, NULL
, NULL
);
742 roster_window_remove_error (self
, account
);
746 roster_window_error_edit_clicked_cb (GtkButton
*button
,
747 EmpathyRosterWindow
*self
)
751 account
= g_object_get_data (G_OBJECT (button
), "account");
753 empathy_accounts_dialog_show_application (
754 gtk_widget_get_screen (GTK_WIDGET (button
)),
755 account
, FALSE
, FALSE
);
757 roster_window_remove_error (self
, account
);
761 roster_window_error_close_clicked_cb (GtkButton
*button
,
762 EmpathyRosterWindow
*self
)
766 account
= g_object_get_data (G_OBJECT (button
), "account");
767 roster_window_remove_error (self
, account
);
771 roster_window_error_upgrade_sw_clicked_cb (GtkButton
*button
,
772 EmpathyRosterWindow
*self
)
777 account
= g_object_get_data (G_OBJECT (button
), "account");
778 roster_window_remove_error (self
, account
);
780 dialog
= gtk_message_dialog_new (GTK_WINDOW (self
),
781 GTK_DIALOG_MODAL
, GTK_MESSAGE_ERROR
,
783 _("Sorry, %s accounts can’t be used until your %s software is updated."),
784 tp_account_get_protocol (account
),
785 tp_account_get_protocol (account
));
787 g_signal_connect_swapped (dialog
, "response",
788 G_CALLBACK (gtk_widget_destroy
),
791 gtk_widget_show (dialog
);
795 roster_window_upgrade_software_error (EmpathyRosterWindow
*self
,
799 GtkWidget
*content_area
;
802 GtkWidget
*upgrade_button
;
803 GtkWidget
*close_button
;
804 GtkWidget
*action_area
;
805 GtkWidget
*action_grid
;
807 const gchar
*icon_name
;
808 const gchar
*error_message
;
809 gboolean user_requested
;
812 empathy_account_get_error_message (account
, &user_requested
);
817 str
= g_markup_printf_escaped ("<b>%s</b>\n%s",
818 tp_account_get_display_name (account
),
821 /* If there are other errors, remove them */
822 roster_window_remove_error (self
, account
);
824 info_bar
= gtk_info_bar_new ();
825 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar
), GTK_MESSAGE_ERROR
);
827 gtk_widget_set_no_show_all (info_bar
, TRUE
);
828 gtk_box_pack_start (GTK_BOX (self
->priv
->errors_vbox
), info_bar
, FALSE
, TRUE
, 0);
829 gtk_widget_show (info_bar
);
831 icon_name
= tp_account_get_icon_name (account
);
832 image
= gtk_image_new_from_icon_name (icon_name
, GTK_ICON_SIZE_SMALL_TOOLBAR
);
833 gtk_widget_show (image
);
835 label
= gtk_label_new (str
);
836 gtk_label_set_use_markup (GTK_LABEL (label
), TRUE
);
837 gtk_label_set_ellipsize (GTK_LABEL (label
), PANGO_ELLIPSIZE_END
);
838 gtk_misc_set_alignment (GTK_MISC (label
), 0, 0.5);
839 gtk_widget_show (label
);
842 content_area
= gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar
));
843 gtk_box_pack_start (GTK_BOX (content_area
), image
, FALSE
, FALSE
, 0);
844 gtk_box_pack_start (GTK_BOX (content_area
), label
, TRUE
, TRUE
, 0);
846 image
= gtk_image_new_from_stock (GTK_STOCK_REFRESH
, GTK_ICON_SIZE_BUTTON
);
847 upgrade_button
= gtk_button_new ();
848 gtk_button_set_image (GTK_BUTTON (upgrade_button
), image
);
849 gtk_widget_set_tooltip_text (upgrade_button
, _("Update software..."));
850 gtk_widget_show (upgrade_button
);
852 image
= gtk_image_new_from_stock (GTK_STOCK_CLOSE
, GTK_ICON_SIZE_BUTTON
);
853 close_button
= gtk_button_new ();
854 gtk_button_set_image (GTK_BUTTON (close_button
), image
);
855 gtk_widget_set_tooltip_text (close_button
, _("Close"));
856 gtk_widget_show (close_button
);
858 action_grid
= gtk_grid_new ();
859 gtk_grid_set_column_spacing (GTK_GRID (action_grid
), 2);
860 gtk_widget_show (action_grid
);
862 action_area
= gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar
));
863 gtk_box_pack_start (GTK_BOX (action_area
), action_grid
, FALSE
, FALSE
, 0);
865 gtk_grid_attach (GTK_GRID (action_grid
), upgrade_button
, 0, 0, 1, 1);
866 gtk_grid_attach (GTK_GRID (action_grid
), close_button
, 1, 0, 1, 1);
868 g_object_set_data (G_OBJECT (info_bar
), "label", label
);
869 g_object_set_data_full (G_OBJECT (info_bar
),
870 "account", g_object_ref (account
),
872 g_object_set_data_full (G_OBJECT (upgrade_button
),
873 "account", g_object_ref (account
),
875 g_object_set_data_full (G_OBJECT (close_button
),
876 "account", g_object_ref (account
),
879 g_signal_connect (upgrade_button
, "clicked",
880 G_CALLBACK (roster_window_error_upgrade_sw_clicked_cb
), self
);
881 g_signal_connect (close_button
, "clicked",
882 G_CALLBACK (roster_window_error_close_clicked_cb
), self
);
884 gtk_widget_set_tooltip_text (self
->priv
->errors_vbox
, error_message
);
885 gtk_widget_show (self
->priv
->errors_vbox
);
887 g_hash_table_insert (self
->priv
->errors
, g_object_ref (account
), info_bar
);
891 roster_window_error_display (EmpathyRosterWindow
*self
,
895 GtkWidget
*content_area
;
898 GtkWidget
*retry_button
;
899 GtkWidget
*edit_button
;
900 GtkWidget
*close_button
;
901 GtkWidget
*action_area
;
902 GtkWidget
*action_grid
;
904 const gchar
*icon_name
;
905 const gchar
*error_message
;
906 gboolean user_requested
;
908 if (!tp_strdiff (TP_ERROR_STR_SOFTWARE_UPGRADE_REQUIRED
,
909 tp_account_get_detailed_error (account
, NULL
)))
911 roster_window_upgrade_software_error (self
, account
);
915 error_message
= empathy_account_get_error_message (account
, &user_requested
);
920 str
= g_markup_printf_escaped ("<b>%s</b>\n%s",
921 tp_account_get_display_name (account
), error_message
);
923 info_bar
= g_hash_table_lookup (self
->priv
->errors
, account
);
926 label
= g_object_get_data (G_OBJECT (info_bar
), "label");
928 /* Just set the latest error and return */
929 gtk_label_set_markup (GTK_LABEL (label
), str
);
935 info_bar
= gtk_info_bar_new ();
936 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar
), GTK_MESSAGE_ERROR
);
938 gtk_widget_set_no_show_all (info_bar
, TRUE
);
939 gtk_box_pack_start (GTK_BOX (self
->priv
->errors_vbox
), info_bar
, FALSE
, TRUE
, 0);
940 gtk_widget_show (info_bar
);
942 icon_name
= tp_account_get_icon_name (account
);
943 image
= gtk_image_new_from_icon_name (icon_name
, GTK_ICON_SIZE_SMALL_TOOLBAR
);
944 gtk_widget_show (image
);
946 label
= gtk_label_new (str
);
947 gtk_label_set_use_markup (GTK_LABEL (label
), TRUE
);
948 gtk_label_set_ellipsize (GTK_LABEL (label
), PANGO_ELLIPSIZE_END
);
949 gtk_misc_set_alignment (GTK_MISC (label
), 0, 0.5);
950 gtk_widget_show (label
);
953 content_area
= gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar
));
954 gtk_box_pack_start (GTK_BOX (content_area
), image
, FALSE
, FALSE
, 0);
955 gtk_box_pack_start (GTK_BOX (content_area
), label
, TRUE
, TRUE
, 0);
957 image
= gtk_image_new_from_stock (GTK_STOCK_REFRESH
, GTK_ICON_SIZE_BUTTON
);
958 retry_button
= gtk_button_new ();
959 gtk_button_set_image (GTK_BUTTON (retry_button
), image
);
960 gtk_widget_set_tooltip_text (retry_button
, _("Reconnect"));
961 gtk_widget_show (retry_button
);
963 image
= gtk_image_new_from_stock (GTK_STOCK_EDIT
, GTK_ICON_SIZE_BUTTON
);
964 edit_button
= gtk_button_new ();
965 gtk_button_set_image (GTK_BUTTON (edit_button
), image
);
966 gtk_widget_set_tooltip_text (edit_button
, _("Edit Account"));
967 gtk_widget_show (edit_button
);
969 image
= gtk_image_new_from_stock (GTK_STOCK_CLOSE
, GTK_ICON_SIZE_BUTTON
);
970 close_button
= gtk_button_new ();
971 gtk_button_set_image (GTK_BUTTON (close_button
), image
);
972 gtk_widget_set_tooltip_text (close_button
, _("Close"));
973 gtk_widget_show (close_button
);
975 action_grid
= gtk_grid_new ();
976 gtk_grid_set_column_spacing (GTK_GRID (action_grid
), 2);
977 gtk_widget_show (action_grid
);
979 action_area
= gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar
));
980 gtk_box_pack_start (GTK_BOX (action_area
), action_grid
, FALSE
, FALSE
, 0);
982 gtk_grid_attach (GTK_GRID (action_grid
), retry_button
, 0, 0, 1, 1);
983 gtk_grid_attach (GTK_GRID (action_grid
), edit_button
, 1, 0, 1, 1);
984 gtk_grid_attach (GTK_GRID (action_grid
), close_button
, 2, 0, 1, 1);
986 g_object_set_data (G_OBJECT (info_bar
), "label", label
);
987 g_object_set_data_full (G_OBJECT (info_bar
),
988 "account", g_object_ref (account
),
990 g_object_set_data_full (G_OBJECT (edit_button
),
991 "account", g_object_ref (account
),
993 g_object_set_data_full (G_OBJECT (close_button
),
994 "account", g_object_ref (account
),
996 g_object_set_data_full (G_OBJECT (retry_button
),
997 "account", g_object_ref (account
),
1000 g_signal_connect (edit_button
, "clicked",
1001 G_CALLBACK (roster_window_error_edit_clicked_cb
), self
);
1002 g_signal_connect (close_button
, "clicked",
1003 G_CALLBACK (roster_window_error_close_clicked_cb
), self
);
1004 g_signal_connect (retry_button
, "clicked",
1005 G_CALLBACK (roster_window_error_retry_clicked_cb
), self
);
1007 gtk_widget_set_tooltip_text (self
->priv
->errors_vbox
, error_message
);
1008 gtk_widget_show (self
->priv
->errors_vbox
);
1010 g_hash_table_insert (self
->priv
->errors
, g_object_ref (account
), info_bar
);
1014 roster_window_update_status (EmpathyRosterWindow
*self
)
1016 gboolean connected
, connecting
;
1017 GList
*l
, *children
;
1019 connected
= empathy_account_manager_get_accounts_connected (&connecting
);
1021 /* Update the spinner state */
1024 gtk_spinner_start (GTK_SPINNER (self
->priv
->throbber
));
1025 gtk_widget_show (self
->priv
->throbber_tool_item
);
1029 gtk_spinner_stop (GTK_SPINNER (self
->priv
->throbber
));
1030 gtk_widget_hide (self
->priv
->throbber_tool_item
);
1033 /* Update widgets sensibility */
1034 for (l
= self
->priv
->actions_connected
; l
; l
= l
->next
)
1035 gtk_action_set_sensitive (l
->data
, connected
);
1037 /* Update favourite rooms sensitivity */
1038 children
= gtk_container_get_children (GTK_CONTAINER (self
->priv
->room_menu
));
1039 for (l
= children
; l
!= NULL
; l
= l
->next
)
1041 if (g_object_get_data (G_OBJECT (l
->data
), "is_favorite") != NULL
)
1042 gtk_widget_set_sensitive (GTK_WIDGET (l
->data
), connected
);
1045 g_list_free (children
);
1049 roster_window_account_to_action_name (TpAccount
*account
)
1053 /* action names can't have '/' in them, replace it with '.' */
1054 r
= g_strdup (tp_account_get_path_suffix (account
));
1055 r
= g_strdelimit (r
, "/", '.');
1061 roster_window_balance_activate_cb (GtkAction
*action
,
1062 EmpathyRosterWindow
*self
)
1066 uri
= g_object_get_data (G_OBJECT (action
), "manage-credit-uri");
1068 if (!tp_str_empty (uri
))
1070 DEBUG ("Top-up credit URI: %s", uri
);
1071 empathy_url_show (GTK_WIDGET (self
), uri
);
1075 DEBUG ("unknown protocol for top-up");
1080 roster_window_balance_update_balance (GtkAction
*action
,
1083 TpAccount
*account
= tp_connection_get_account (conn
);
1086 guint scale
= G_MAXINT32
;
1087 const gchar
*currency
= "";
1090 if (!tp_connection_get_balance (conn
, &amount
, &scale
, ¤cy
))
1094 scale
== G_MAXINT32
&&
1095 tp_str_empty (currency
))
1097 /* unknown balance */
1098 money
= g_strdup ("--");
1102 char *tmp
= empathy_format_currency (amount
, scale
, currency
);
1104 money
= g_strdup_printf ("%s %s", currency
, tmp
);
1108 /* Translators: this string will be something like:
1109 * Top up My Account ($1.23)..." */
1110 str
= g_strdup_printf (_("Top up %s (%s)..."),
1111 tp_account_get_display_name (account
), money
);
1113 gtk_action_set_label (action
, str
);
1116 /* update the money label in the roster */
1117 label
= g_object_get_data (G_OBJECT (action
), "money-label");
1119 gtk_label_set_text (GTK_LABEL (label
), money
);
1124 roster_window_balance_changed_cb (TpConnection
*conn
,
1127 const gchar
*currency
,
1130 roster_window_balance_update_balance (action
, conn
);
1134 roster_window_setup_balance_create_action (EmpathyRosterWindow
*self
,
1140 GError
*error
= NULL
;
1142 /* create the action group if required */
1143 if (self
->priv
->balance_action_group
== NULL
)
1145 self
->priv
->balance_action_group
=
1146 gtk_action_group_new ("balance-action-group");
1148 gtk_ui_manager_insert_action_group (self
->priv
->ui_manager
,
1149 self
->priv
->balance_action_group
, -1);
1152 /* create the action */
1153 name
= roster_window_account_to_action_name (account
);
1154 action
= gtk_action_new (name
,
1155 tp_account_get_display_name (account
),
1156 _("Top up account credit"), NULL
);
1158 g_object_bind_property (account
, "icon-name", action
, "icon-name",
1159 G_BINDING_SYNC_CREATE
);
1161 g_signal_connect (action
, "activate",
1162 G_CALLBACK (roster_window_balance_activate_cb
), self
);
1164 gtk_action_group_add_action (self
->priv
->balance_action_group
, action
);
1165 g_object_unref (action
);
1167 ui
= g_strdup_printf (
1169 " <menubar name='menubar'>"
1170 " <menu action='view'>"
1171 " <placeholder name='view_balance_placeholder'>"
1172 " <menuitem action='%s'/>"
1179 merge_id
= gtk_ui_manager_add_ui_from_string (self
->priv
->ui_manager
,
1184 DEBUG ("Failed to add balance UI for %s: %s",
1185 tp_account_get_display_name (account
),
1187 g_error_free (error
);
1190 g_object_set_data (G_OBJECT (action
),
1191 "merge-id", GUINT_TO_POINTER (merge_id
));
1200 roster_window_setup_balance_create_widget (EmpathyRosterWindow
*self
,
1204 GtkWidget
*hbox
, *image
, *label
, *button
;
1206 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 6);
1209 image
= gtk_image_new ();
1210 gtk_box_pack_start (GTK_BOX (hbox
), image
, FALSE
, TRUE
, 0);
1211 g_object_bind_property (action
, "icon-name", image
, "icon-name",
1212 G_BINDING_SYNC_CREATE
);
1214 /* account name label */
1215 label
= gtk_label_new ("");
1216 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1217 gtk_box_pack_start (GTK_BOX (hbox
), label
, TRUE
, TRUE
, 0);
1218 g_object_bind_property (account
, "display-name", label
, "label",
1219 G_BINDING_SYNC_CREATE
);
1222 label
= gtk_label_new ("");
1223 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1224 gtk_box_pack_start (GTK_BOX (hbox
), label
, FALSE
, TRUE
, 0);
1225 g_object_set_data (G_OBJECT (action
), "money-label", label
);
1228 button
= gtk_button_new_with_label (_("Top Up..."));
1229 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, TRUE
, 0);
1230 g_signal_connect_swapped (button
, "clicked",
1231 G_CALLBACK (gtk_action_activate
), action
);
1233 gtk_box_pack_start (GTK_BOX (self
->priv
->balance_vbox
), hbox
, FALSE
, TRUE
, 0);
1234 gtk_widget_show_all (hbox
);
1236 /* tie the lifetime of the widget to the lifetime of the action */
1237 g_object_weak_ref (G_OBJECT (action
),
1238 (GWeakNotify
) gtk_widget_destroy
, hbox
);
1244 roster_window_setup_balance (EmpathyRosterWindow
*self
,
1247 TpConnection
*conn
= tp_account_get_connection (account
);
1254 if (!tp_proxy_is_prepared (conn
, TP_CONNECTION_FEATURE_BALANCE
))
1257 DEBUG ("Setting up balance for acct: %s",
1258 tp_account_get_display_name (account
));
1260 /* create the action */
1261 action
= roster_window_setup_balance_create_action (self
, account
);
1266 gtk_action_set_visible (self
->priv
->view_balance_show_in_roster
, TRUE
);
1268 /* create the display widget */
1269 roster_window_setup_balance_create_widget (self
, action
, account
);
1271 /* check the current balance and monitor for any changes */
1272 uri
= tp_connection_get_balance_uri (conn
);
1274 g_object_set_data_full (G_OBJECT (action
), "manage-credit-uri",
1275 g_strdup (uri
), g_free
);
1276 gtk_action_set_sensitive (GTK_ACTION (action
), !tp_str_empty (uri
));
1278 roster_window_balance_update_balance (GTK_ACTION (action
), conn
);
1280 g_signal_connect (conn
, "balance-changed",
1281 G_CALLBACK (roster_window_balance_changed_cb
), action
);
1285 roster_window_remove_balance_action (EmpathyRosterWindow
*self
,
1292 if (self
->priv
->balance_action_group
== NULL
)
1295 name
= roster_window_account_to_action_name (account
);
1297 action
= gtk_action_group_get_action (
1298 self
->priv
->balance_action_group
, name
);
1304 DEBUG ("Removing action");
1306 merge_id
= GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (action
),
1309 gtk_ui_manager_remove_ui (self
->priv
->ui_manager
,
1311 gtk_action_group_remove_action (
1312 self
->priv
->balance_action_group
, action
);
1317 a
= gtk_action_group_list_actions (
1318 self
->priv
->balance_action_group
);
1320 gtk_action_set_visible (self
->priv
->view_balance_show_in_roster
,
1321 g_list_length (a
) > 0);
1327 roster_window_connection_changed_cb (TpAccount
*account
,
1331 gchar
*dbus_error_name
,
1332 GHashTable
*details
,
1333 EmpathyRosterWindow
*self
)
1335 roster_window_update_status (self
);
1337 if (current
== TP_CONNECTION_STATUS_DISCONNECTED
&&
1338 reason
!= TP_CONNECTION_STATUS_REASON_REQUESTED
)
1340 roster_window_error_display (self
, account
);
1343 if (current
== TP_CONNECTION_STATUS_DISCONNECTED
)
1345 empathy_sound_manager_play (self
->priv
->sound_mgr
, GTK_WIDGET (self
),
1346 EMPATHY_SOUND_ACCOUNT_DISCONNECTED
);
1349 if (current
== TP_CONNECTION_STATUS_CONNECTED
)
1351 empathy_sound_manager_play (self
->priv
->sound_mgr
, GTK_WIDGET (self
),
1352 EMPATHY_SOUND_ACCOUNT_CONNECTED
);
1354 /* Account connected without error, remove error message if any */
1355 roster_window_remove_error (self
, account
);
1360 roster_window_accels_load (void)
1364 filename
= g_build_filename (g_get_user_config_dir (),
1365 PACKAGE_NAME
, ACCELS_FILENAME
, NULL
);
1366 if (g_file_test (filename
, G_FILE_TEST_EXISTS
))
1368 DEBUG ("Loading from:'%s'", filename
);
1369 gtk_accel_map_load (filename
);
1376 roster_window_accels_save (void)
1379 gchar
*file_with_path
;
1381 dir
= g_build_filename (g_get_user_config_dir (), PACKAGE_NAME
, NULL
);
1382 g_mkdir_with_parents (dir
, S_IRUSR
| S_IWUSR
| S_IXUSR
);
1383 file_with_path
= g_build_filename (dir
, ACCELS_FILENAME
, NULL
);
1386 DEBUG ("Saving to:'%s'", file_with_path
);
1387 gtk_accel_map_save (file_with_path
);
1389 g_free (file_with_path
);
1393 empathy_roster_window_finalize (GObject
*window
)
1395 EmpathyRosterWindow
*self
= EMPATHY_ROSTER_WINDOW (window
);
1396 GHashTableIter iter
;
1397 gpointer key
, value
;
1399 /* Save user-defined accelerators. */
1400 roster_window_accels_save ();
1402 g_list_free (self
->priv
->actions_connected
);
1404 g_object_unref (self
->priv
->account_manager
);
1405 g_object_unref (self
->priv
->individual_store
);
1406 g_object_unref (self
->priv
->sound_mgr
);
1407 g_hash_table_unref (self
->priv
->errors
);
1408 g_hash_table_unref (self
->priv
->auths
);
1410 /* disconnect all handlers of status-changed signal */
1411 g_hash_table_iter_init (&iter
, self
->priv
->status_changed_handlers
);
1412 while (g_hash_table_iter_next (&iter
, &key
, &value
))
1413 g_signal_handler_disconnect (TP_ACCOUNT (key
), GPOINTER_TO_UINT (value
));
1415 g_hash_table_unref (self
->priv
->status_changed_handlers
);
1417 g_signal_handlers_disconnect_by_func (self
->priv
->event_manager
,
1418 roster_window_event_added_cb
, self
);
1419 g_signal_handlers_disconnect_by_func (self
->priv
->event_manager
,
1420 roster_window_event_removed_cb
, self
);
1422 g_object_unref (self
->priv
->call_observer
);
1423 g_object_unref (self
->priv
->event_manager
);
1424 g_object_unref (self
->priv
->ui_manager
);
1425 g_object_unref (self
->priv
->chatroom_manager
);
1427 g_object_unref (self
->priv
->gsettings_ui
);
1428 g_object_unref (self
->priv
->gsettings_contacts
);
1429 g_object_unref (self
->priv
->individual_manager
);
1431 G_OBJECT_CLASS (empathy_roster_window_parent_class
)->finalize (window
);
1435 roster_window_key_press_event_cb (GtkWidget
*window
,
1439 if (event
->keyval
== GDK_KEY_T
1440 && event
->state
& GDK_SHIFT_MASK
1441 && event
->state
& GDK_CONTROL_MASK
)
1442 empathy_chat_manager_call_undo_closed_chat ();
1448 roster_window_chat_quit_cb (GtkAction
*action
,
1449 EmpathyRosterWindow
*self
)
1451 gtk_widget_destroy (GTK_WIDGET (self
));
1455 roster_window_view_history_cb (GtkAction
*action
,
1456 EmpathyRosterWindow
*self
)
1458 empathy_log_window_show (NULL
, NULL
, FALSE
, GTK_WINDOW (self
));
1462 roster_window_chat_new_message_cb (GtkAction
*action
,
1463 EmpathyRosterWindow
*self
)
1465 empathy_new_message_dialog_show (GTK_WINDOW (self
));
1469 roster_window_chat_new_call_cb (GtkAction
*action
,
1470 EmpathyRosterWindow
*self
)
1472 empathy_new_call_dialog_show (GTK_WINDOW (self
));
1476 roster_window_chat_add_contact_cb (GtkAction
*action
,
1477 EmpathyRosterWindow
*self
)
1479 empathy_new_individual_dialog_show (GTK_WINDOW (self
));
1483 roster_window_chat_search_contacts_cb (GtkAction
*action
,
1484 EmpathyRosterWindow
*self
)
1486 GtkWidget
*dialog
= empathy_contact_search_dialog_new (
1489 gtk_widget_show (dialog
);
1493 roster_window_view_show_ft_manager (GtkAction
*action
,
1494 EmpathyRosterWindow
*self
)
1496 empathy_ft_manager_show ();
1500 roster_window_view_show_offline_cb (GtkToggleAction
*action
,
1501 EmpathyRosterWindow
*self
)
1505 current
= gtk_toggle_action_get_active (action
);
1506 g_settings_set_boolean (self
->priv
->gsettings_ui
,
1507 EMPATHY_PREFS_UI_SHOW_OFFLINE
,
1510 empathy_individual_view_set_show_offline (self
->priv
->individual_view
,
1515 roster_window_notify_sort_contact_cb (GSettings
*gsettings
,
1517 EmpathyRosterWindow
*self
)
1521 str
= g_settings_get_string (gsettings
, key
);
1526 GEnumClass
*enum_class
;
1527 GEnumValue
*enum_value
;
1529 type
= empathy_individual_store_sort_get_type ();
1530 enum_class
= G_ENUM_CLASS (g_type_class_peek (type
));
1531 enum_value
= g_enum_get_value_by_nick (enum_class
, str
);
1534 /* By changing the value of the GtkRadioAction,
1535 it emits a signal that calls roster_window_view_sort_contacts_cb
1536 which updates the contacts list */
1537 gtk_radio_action_set_current_value (self
->priv
->sort_by_name
,
1542 g_warning ("Wrong value for sort_criterium configuration : %s", str
);
1549 roster_window_view_sort_contacts_cb (GtkRadioAction
*action
,
1550 GtkRadioAction
*current
,
1551 EmpathyRosterWindow
*self
)
1553 EmpathyIndividualStoreSort value
;
1556 GEnumClass
*enum_class
;
1557 GEnumValue
*enum_value
;
1559 value
= gtk_radio_action_get_current_value (action
);
1560 group
= gtk_radio_action_get_group (action
);
1562 /* Get string from index */
1563 type
= empathy_individual_store_sort_get_type ();
1564 enum_class
= G_ENUM_CLASS (g_type_class_peek (type
));
1565 enum_value
= g_enum_get_value (enum_class
, g_slist_index (group
, current
));
1568 g_warning ("No GEnumValue for EmpathyContactListSort with GtkRadioAction index:%d",
1569 g_slist_index (group
, action
));
1571 g_settings_set_string (self
->priv
->gsettings_contacts
,
1572 EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM
,
1573 enum_value
->value_nick
);
1575 empathy_individual_store_set_sort_criterium (self
->priv
->individual_store
,
1580 roster_window_view_show_protocols_cb (GtkToggleAction
*action
,
1581 EmpathyRosterWindow
*self
)
1585 value
= gtk_toggle_action_get_active (action
);
1587 g_settings_set_boolean (self
->priv
->gsettings_ui
,
1588 EMPATHY_PREFS_UI_SHOW_PROTOCOLS
,
1591 empathy_individual_store_set_show_protocols (self
->priv
->individual_store
,
1595 /* Matches GtkRadioAction values set in empathy-roster-window.ui */
1596 #define CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS 0
1597 #define CONTACT_LIST_NORMAL_SIZE 1
1598 #define CONTACT_LIST_COMPACT_SIZE 2
1601 roster_window_view_contacts_list_size_cb (GtkRadioAction
*action
,
1602 GtkRadioAction
*current
,
1603 EmpathyRosterWindow
*self
)
1605 GSettings
*gsettings_ui
;
1608 value
= gtk_radio_action_get_current_value (action
);
1609 /* create a new GSettings, so we can delay the setting until both
1611 gsettings_ui
= g_settings_new (EMPATHY_PREFS_UI_SCHEMA
);
1613 DEBUG ("radio button toggled, value = %i", value
);
1615 g_settings_delay (gsettings_ui
);
1616 g_settings_set_boolean (gsettings_ui
, EMPATHY_PREFS_UI_SHOW_AVATARS
,
1617 value
== CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS
);
1619 g_settings_set_boolean (gsettings_ui
, EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST
,
1620 value
== CONTACT_LIST_COMPACT_SIZE
);
1621 g_settings_apply (gsettings_ui
);
1623 /* FIXME: these enums probably have the wrong namespace */
1624 empathy_individual_store_set_show_avatars (self
->priv
->individual_store
,
1625 value
== CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS
);
1626 empathy_individual_store_set_is_compact (self
->priv
->individual_store
,
1627 value
== CONTACT_LIST_COMPACT_SIZE
);
1629 g_object_unref (gsettings_ui
);
1632 static void roster_window_notify_show_protocols_cb (GSettings
*gsettings
,
1634 EmpathyRosterWindow
*self
)
1636 gtk_toggle_action_set_active (self
->priv
->show_protocols
,
1637 g_settings_get_boolean (gsettings
, EMPATHY_PREFS_UI_SHOW_PROTOCOLS
));
1642 roster_window_notify_contact_list_size_cb (GSettings
*gsettings
,
1644 EmpathyRosterWindow
*self
)
1648 if (g_settings_get_boolean (gsettings
,
1649 EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST
))
1650 value
= CONTACT_LIST_COMPACT_SIZE
;
1651 else if (g_settings_get_boolean (gsettings
,
1652 EMPATHY_PREFS_UI_SHOW_AVATARS
))
1653 value
= CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS
;
1655 value
= CONTACT_LIST_NORMAL_SIZE
;
1657 DEBUG ("setting changed, value = %i", value
);
1659 /* By changing the value of the GtkRadioAction,
1660 it emits a signal that calls roster_window_view_contacts_list_size_cb
1661 which updates the contacts list */
1662 gtk_radio_action_set_current_value (self
->priv
->normal_with_avatars
, value
);
1666 roster_window_edit_search_contacts_cb (GtkCheckMenuItem
*item
,
1667 EmpathyRosterWindow
*self
)
1669 empathy_individual_view_start_search (self
->priv
->individual_view
);
1673 roster_window_view_show_map_cb (GtkCheckMenuItem
*item
,
1674 EmpathyRosterWindow
*self
)
1676 #ifdef HAVE_LIBCHAMPLAIN
1677 empathy_map_view_show ();
1682 join_chatroom (EmpathyChatroom
*chatroom
,
1688 account
= empathy_chatroom_get_account (chatroom
);
1689 room
= empathy_chatroom_get_room (chatroom
);
1691 DEBUG ("Requesting channel for '%s'", room
);
1692 empathy_join_muc (account
, room
, timestamp
);
1698 EmpathyChatroom
*chatroom
;
1702 } join_fav_account_sig_ctx
;
1704 static join_fav_account_sig_ctx
*
1705 join_fav_account_sig_ctx_new (TpAccount
*account
,
1706 EmpathyChatroom
*chatroom
,
1709 join_fav_account_sig_ctx
*ctx
= g_slice_new0 (
1710 join_fav_account_sig_ctx
);
1712 ctx
->account
= g_object_ref (account
);
1713 ctx
->chatroom
= g_object_ref (chatroom
);
1714 ctx
->timestamp
= timestamp
;
1719 join_fav_account_sig_ctx_free (join_fav_account_sig_ctx
*ctx
)
1721 g_object_unref (ctx
->account
);
1722 g_object_unref (ctx
->chatroom
);
1723 g_slice_free (join_fav_account_sig_ctx
, ctx
);
1727 account_status_changed_cb (TpAccount
*account
,
1728 TpConnectionStatus old_status
,
1729 TpConnectionStatus new_status
,
1731 gchar
*dbus_error_name
,
1732 GHashTable
*details
,
1735 join_fav_account_sig_ctx
*ctx
= user_data
;
1739 case TP_CONNECTION_STATUS_DISCONNECTED
:
1740 /* Don't wait any longer */
1744 case TP_CONNECTION_STATUS_CONNECTING
:
1748 case TP_CONNECTION_STATUS_CONNECTED
:
1749 /* We can join the room */
1753 g_assert_not_reached ();
1756 join_chatroom (ctx
->chatroom
, ctx
->timestamp
);
1759 g_source_remove (ctx
->timeout
);
1760 g_signal_handler_disconnect (account
, ctx
->sig_id
);
1763 #define JOIN_FAVORITE_TIMEOUT 5
1766 join_favorite_timeout_cb (gpointer data
)
1768 join_fav_account_sig_ctx
*ctx
= data
;
1770 /* stop waiting for joining the favorite room */
1771 g_signal_handler_disconnect (ctx
->account
, ctx
->sig_id
);
1776 roster_window_favorite_chatroom_join (EmpathyChatroom
*chatroom
)
1780 account
= empathy_chatroom_get_account (chatroom
);
1781 if (tp_account_get_connection_status (account
, NULL
) !=
1782 TP_CONNECTION_STATUS_CONNECTED
)
1784 join_fav_account_sig_ctx
*ctx
;
1786 ctx
= join_fav_account_sig_ctx_new (account
, chatroom
,
1787 empathy_get_current_action_time ());
1789 ctx
->sig_id
= g_signal_connect_data (account
, "status-changed",
1790 G_CALLBACK (account_status_changed_cb
), ctx
,
1791 (GClosureNotify
) join_fav_account_sig_ctx_free
, 0);
1793 ctx
->timeout
= g_timeout_add_seconds (JOIN_FAVORITE_TIMEOUT
,
1794 join_favorite_timeout_cb
, ctx
);
1798 join_chatroom (chatroom
, empathy_get_current_action_time ());
1802 roster_window_favorite_chatroom_menu_activate_cb (GtkMenuItem
*menu_item
,
1803 EmpathyChatroom
*chatroom
)
1805 roster_window_favorite_chatroom_join (chatroom
);
1809 roster_window_favorite_chatroom_menu_add (EmpathyRosterWindow
*self
,
1810 EmpathyChatroom
*chatroom
)
1812 GtkWidget
*menu_item
;
1813 const gchar
*name
, *account_name
;
1816 if (g_object_get_data (G_OBJECT (chatroom
), "menu_item"))
1819 name
= empathy_chatroom_get_name (chatroom
);
1820 account_name
= tp_account_get_display_name (
1821 empathy_chatroom_get_account (chatroom
));
1822 label
= g_strdup_printf ("%s (%s)", name
, account_name
);
1823 menu_item
= gtk_menu_item_new_with_label (label
);
1825 g_object_set_data (G_OBJECT (menu_item
), "is_favorite",
1826 GUINT_TO_POINTER (TRUE
));
1828 g_object_set_data (G_OBJECT (chatroom
), "menu_item", menu_item
);
1829 g_signal_connect (menu_item
, "activate",
1830 G_CALLBACK (roster_window_favorite_chatroom_menu_activate_cb
),
1833 gtk_menu_shell_insert (GTK_MENU_SHELL (self
->priv
->room_menu
),
1836 gtk_widget_show (menu_item
);
1840 roster_window_favorite_chatroom_menu_added_cb (EmpathyChatroomManager
*manager
,
1841 EmpathyChatroom
*chatroom
,
1842 EmpathyRosterWindow
*self
)
1844 roster_window_favorite_chatroom_menu_add (self
, chatroom
);
1845 gtk_widget_show (self
->priv
->room_separator
);
1846 gtk_action_set_sensitive (self
->priv
->room_join_favorites
, TRUE
);
1850 roster_window_favorite_chatroom_menu_removed_cb (
1851 EmpathyChatroomManager
*manager
,
1852 EmpathyChatroom
*chatroom
,
1853 EmpathyRosterWindow
*self
)
1855 GtkWidget
*menu_item
;
1858 menu_item
= g_object_get_data (G_OBJECT (chatroom
), "menu_item");
1859 g_object_set_data (G_OBJECT (chatroom
), "menu_item", NULL
);
1860 gtk_widget_destroy (menu_item
);
1862 chatrooms
= empathy_chatroom_manager_get_chatrooms (self
->priv
->chatroom_manager
,
1865 gtk_widget_show (self
->priv
->room_separator
);
1867 gtk_widget_hide (self
->priv
->room_separator
);
1869 gtk_action_set_sensitive (self
->priv
->room_join_favorites
, chatrooms
!= NULL
);
1870 g_list_free (chatrooms
);
1874 roster_window_favorite_chatroom_menu_setup (EmpathyRosterWindow
*self
)
1876 GList
*chatrooms
, *l
;
1879 self
->priv
->chatroom_manager
= empathy_chatroom_manager_dup_singleton (NULL
);
1880 chatrooms
= empathy_chatroom_manager_get_chatrooms (
1881 self
->priv
->chatroom_manager
, NULL
);
1882 room
= gtk_ui_manager_get_widget (self
->priv
->ui_manager
,
1884 self
->priv
->room_menu
= gtk_menu_item_get_submenu (GTK_MENU_ITEM (room
));
1885 self
->priv
->room_separator
= gtk_ui_manager_get_widget (self
->priv
->ui_manager
,
1886 "/menubar/room/room_separator");
1888 for (l
= chatrooms
; l
; l
= l
->next
)
1889 roster_window_favorite_chatroom_menu_add (self
, l
->data
);
1892 gtk_widget_hide (self
->priv
->room_separator
);
1894 gtk_action_set_sensitive (self
->priv
->room_join_favorites
, chatrooms
!= NULL
);
1896 g_signal_connect (self
->priv
->chatroom_manager
, "chatroom-added",
1897 G_CALLBACK (roster_window_favorite_chatroom_menu_added_cb
),
1900 g_signal_connect (self
->priv
->chatroom_manager
, "chatroom-removed",
1901 G_CALLBACK (roster_window_favorite_chatroom_menu_removed_cb
),
1904 g_list_free (chatrooms
);
1908 roster_window_room_join_new_cb (GtkAction
*action
,
1909 EmpathyRosterWindow
*self
)
1911 empathy_new_chatroom_dialog_show (GTK_WINDOW (self
));
1915 roster_window_room_join_favorites_cb (GtkAction
*action
,
1916 EmpathyRosterWindow
*self
)
1918 GList
*chatrooms
, *l
;
1920 chatrooms
= empathy_chatroom_manager_get_chatrooms (self
->priv
->chatroom_manager
,
1923 for (l
= chatrooms
; l
; l
= l
->next
)
1924 roster_window_favorite_chatroom_join (l
->data
);
1926 g_list_free (chatrooms
);
1930 roster_window_room_manage_favorites_cb (GtkAction
*action
,
1931 EmpathyRosterWindow
*self
)
1933 empathy_chatrooms_window_show (GTK_WINDOW (self
));
1937 roster_window_edit_cb (GtkAction
*action
,
1938 EmpathyRosterWindow
*self
)
1942 /* FIXME: It should use the UIManager to merge the contact/group submenu */
1943 submenu
= empathy_individual_view_get_individual_menu (
1944 self
->priv
->individual_view
);
1950 item
= GTK_MENU_ITEM (self
->priv
->edit_context
);
1951 label
= gtk_bin_get_child (GTK_BIN (item
));
1952 gtk_label_set_text (GTK_LABEL (label
), _("Contact"));
1954 gtk_widget_show (self
->priv
->edit_context
);
1955 gtk_widget_show (self
->priv
->edit_context_separator
);
1957 gtk_menu_item_set_submenu (item
, submenu
);
1962 submenu
= empathy_individual_view_get_group_menu (self
->priv
->individual_view
);
1968 item
= GTK_MENU_ITEM (self
->priv
->edit_context
);
1969 label
= gtk_bin_get_child (GTK_BIN (item
));
1970 gtk_label_set_text (GTK_LABEL (label
), _("Group"));
1972 gtk_widget_show (self
->priv
->edit_context
);
1973 gtk_widget_show (self
->priv
->edit_context_separator
);
1975 gtk_menu_item_set_submenu (item
, submenu
);
1980 gtk_widget_hide (self
->priv
->edit_context
);
1981 gtk_widget_hide (self
->priv
->edit_context_separator
);
1987 roster_window_edit_accounts_cb (GtkAction
*action
,
1988 EmpathyRosterWindow
*self
)
1990 empathy_accounts_dialog_show_application (gdk_screen_get_default (),
1991 NULL
, FALSE
, FALSE
);
1995 roster_window_edit_blocked_contacts_cb (GtkAction
*action
,
1996 EmpathyRosterWindow
*self
)
2000 dialog
= empathy_contact_blocking_dialog_new (GTK_WINDOW (self
));
2001 gtk_widget_show (dialog
);
2003 g_signal_connect (dialog
, "response",
2004 G_CALLBACK (gtk_widget_destroy
), NULL
);
2008 empathy_roster_window_show_preferences (EmpathyRosterWindow
*self
,
2011 if (self
->priv
->preferences
== NULL
)
2013 self
->priv
->preferences
= empathy_preferences_new (GTK_WINDOW (self
),
2014 self
->priv
->shell_running
);
2015 g_object_add_weak_pointer (G_OBJECT (self
->priv
->preferences
),
2016 (gpointer
) &self
->priv
->preferences
);
2018 gtk_widget_show (self
->priv
->preferences
);
2022 gtk_window_present (GTK_WINDOW (self
->priv
->preferences
));
2026 empathy_preferences_show_tab (
2027 EMPATHY_PREFERENCES (self
->priv
->preferences
), tab
);
2031 roster_window_edit_preferences_cb (GtkAction
*action
,
2032 EmpathyRosterWindow
*self
)
2034 empathy_roster_window_show_preferences (self
, NULL
);
2038 roster_window_help_about_cb (GtkAction
*action
,
2039 EmpathyRosterWindow
*self
)
2041 empathy_about_dialog_new (GTK_WINDOW (self
));
2045 roster_window_help_debug_cb (GtkAction
*action
,
2046 EmpathyRosterWindow
*self
)
2048 empathy_launch_program (BIN_DIR
, "empathy-debugger", NULL
);
2052 roster_window_help_contents_cb (GtkAction
*action
,
2053 EmpathyRosterWindow
*self
)
2055 empathy_url_show (GTK_WIDGET (self
), "help:empathy");
2059 roster_window_throbber_button_press_event_cb (GtkWidget
*throbber
,
2060 GdkEventButton
*event
,
2061 EmpathyRosterWindow
*self
)
2063 if (event
->type
!= GDK_BUTTON_PRESS
||
2067 empathy_accounts_dialog_show_application (
2068 gtk_widget_get_screen (GTK_WIDGET (throbber
)),
2069 NULL
, FALSE
, FALSE
);
2075 roster_window_account_removed_cb (TpAccountManager
*manager
,
2077 EmpathyRosterWindow
*self
)
2081 a
= tp_account_manager_get_valid_accounts (manager
);
2083 gtk_action_set_sensitive (self
->priv
->view_history
,
2084 g_list_length (a
) > 0);
2088 /* remove errors if any */
2089 roster_window_remove_error (self
, account
);
2091 /* remove the balance action if required */
2092 roster_window_remove_balance_action (self
, account
);
2096 account_connection_notify_cb (TpAccount
*account
,
2098 EmpathyRosterWindow
*self
)
2102 conn
= tp_account_get_connection (account
);
2106 roster_window_setup_balance (self
, account
);
2110 /* remove balance action if required */
2111 roster_window_remove_balance_action (self
, account
);
2116 add_account (EmpathyRosterWindow
*self
,
2121 handler_id
= GPOINTER_TO_UINT (g_hash_table_lookup (
2122 self
->priv
->status_changed_handlers
, account
));
2124 /* connect signal only if it was not connected yet */
2125 if (handler_id
!= 0)
2128 handler_id
= g_signal_connect (account
, "status-changed",
2129 G_CALLBACK (roster_window_connection_changed_cb
), self
);
2131 g_hash_table_insert (self
->priv
->status_changed_handlers
,
2132 account
, GUINT_TO_POINTER (handler_id
));
2134 /* roster_window_setup_balance() relies on the TpConnection to be ready on
2135 * the TpAccount so we connect this signal as well. */
2136 tp_g_signal_connect_object (account
, "notify::connection",
2137 G_CALLBACK (account_connection_notify_cb
), self
, 0);
2139 roster_window_setup_balance (self
, account
);
2142 /* @account: if not %NULL, the only account which can be enabled */
2144 display_page_account_not_enabled (EmpathyRosterWindow
*self
,
2147 if (account
== NULL
)
2149 display_page_message (self
,
2150 _("You need to enable one of your accounts to see contacts here."),
2157 /* translators: argument is an account name */
2158 tmp
= g_strdup_printf (_("You need to enable %s to see contacts here."),
2159 tp_account_get_display_name (account
));
2161 display_page_message (self
, tmp
, TRUE
, FALSE
);
2166 has_enabled_account (GList
*accounts
)
2170 for (l
= accounts
; l
!= NULL
; l
= g_list_next (l
))
2172 TpAccount
*account
= l
->data
;
2174 if (tp_account_is_enabled (account
))
2182 set_notebook_page (EmpathyRosterWindow
*self
)
2187 accounts
= tp_account_manager_get_valid_accounts (
2188 self
->priv
->account_manager
);
2190 len
= g_list_length (accounts
);
2195 display_page_no_account (self
);
2199 if (!has_enabled_account (accounts
))
2201 TpAccount
*account
= NULL
;
2203 /* Pass the account if there is only one which can be enabled */
2205 account
= accounts
->data
;
2207 display_page_account_not_enabled (self
, account
);
2211 display_page_contact_list (self
);
2214 g_list_free (accounts
);
2218 roster_window_account_validity_changed_cb (TpAccountManager
*manager
,
2221 EmpathyRosterWindow
*self
)
2224 add_account (self
, account
);
2226 roster_window_account_removed_cb (manager
, account
, self
);
2228 set_notebook_page (self
);
2232 roster_window_notify_show_offline_cb (GSettings
*gsettings
,
2234 gpointer toggle_action
)
2236 gtk_toggle_action_set_active (toggle_action
,
2237 g_settings_get_boolean (gsettings
, key
));
2241 roster_window_connection_items_setup (EmpathyRosterWindow
*self
,
2247 const gchar
*actions_connected
[] = {
2249 "room_join_favorites",
2252 "chat_search_contacts",
2254 "edit_blocked_contacts",
2255 "edit_search_contacts"
2258 for (i
= 0, list
= NULL
; i
< G_N_ELEMENTS (actions_connected
); i
++)
2260 action
= gtk_builder_get_object (gui
, actions_connected
[i
]);
2261 list
= g_list_prepend (list
, action
);
2264 self
->priv
->actions_connected
= list
;
2268 account_enabled_cb (TpAccountManager
*manager
,
2270 EmpathyRosterWindow
*self
)
2272 set_notebook_page (self
);
2276 account_disabled_cb (TpAccountManager
*manager
,
2278 EmpathyRosterWindow
*self
)
2280 set_notebook_page (self
);
2284 account_manager_prepared_cb (GObject
*source_object
,
2285 GAsyncResult
*result
,
2288 GList
*accounts
, *j
;
2289 TpAccountManager
*manager
= TP_ACCOUNT_MANAGER (source_object
);
2290 EmpathyRosterWindow
*self
= user_data
;
2291 GError
*error
= NULL
;
2293 if (!tp_proxy_prepare_finish (manager
, result
, &error
))
2295 DEBUG ("Failed to prepare account manager: %s", error
->message
);
2296 g_error_free (error
);
2300 accounts
= tp_account_manager_get_valid_accounts (self
->priv
->account_manager
);
2301 for (j
= accounts
; j
!= NULL
; j
= j
->next
)
2303 TpAccount
*account
= TP_ACCOUNT (j
->data
);
2305 add_account (self
, account
);
2308 g_signal_connect (manager
, "account-validity-changed",
2309 G_CALLBACK (roster_window_account_validity_changed_cb
), self
);
2310 tp_g_signal_connect_object (manager
, "account-disabled",
2311 G_CALLBACK (account_disabled_cb
), self
, 0);
2312 tp_g_signal_connect_object (manager
, "account-enabled",
2313 G_CALLBACK (account_enabled_cb
), self
, 0);
2315 roster_window_update_status (self
);
2317 /* Disable the "Previous Conversations" menu entry if there is no account */
2318 gtk_action_set_sensitive (self
->priv
->view_history
,
2319 g_list_length (accounts
) > 0);
2321 set_notebook_page (self
);
2323 g_list_free (accounts
);
2327 empathy_roster_window_set_shell_running (EmpathyRosterWindow
*self
,
2328 gboolean shell_running
)
2330 if (self
->priv
->shell_running
== shell_running
)
2333 self
->priv
->shell_running
= shell_running
;
2334 g_object_notify (G_OBJECT (self
), "shell-running");
2338 empathy_roster_window_constructor (GType type
,
2339 guint n_construct_params
,
2340 GObjectConstructParam
*construct_params
)
2342 static GObject
*window
= NULL
;
2345 return g_object_ref (window
);
2347 window
= G_OBJECT_CLASS (empathy_roster_window_parent_class
)->constructor (
2348 type
, n_construct_params
, construct_params
);
2350 g_object_add_weak_pointer (window
, (gpointer
) &window
);
2356 empathy_roster_window_set_property (GObject
*object
,
2358 const GValue
*value
,
2361 EmpathyRosterWindow
*self
= EMPATHY_ROSTER_WINDOW (object
);
2363 switch (property_id
)
2365 case PROP_SHELL_RUNNING
:
2366 self
->priv
->shell_running
= g_value_get_boolean (value
);
2369 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
2375 empathy_roster_window_get_property (GObject
*object
,
2380 EmpathyRosterWindow
*self
= EMPATHY_ROSTER_WINDOW (object
);
2382 switch (property_id
)
2384 case PROP_SHELL_RUNNING
:
2385 g_value_set_boolean (value
, self
->priv
->shell_running
);
2388 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
2394 empathy_roster_window_class_init (EmpathyRosterWindowClass
*klass
)
2396 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
2399 object_class
->finalize
= empathy_roster_window_finalize
;
2400 object_class
->constructor
= empathy_roster_window_constructor
;
2402 object_class
->set_property
= empathy_roster_window_set_property
;
2403 object_class
->get_property
= empathy_roster_window_get_property
;
2405 pspec
= g_param_spec_boolean ("shell-running",
2407 "Whether the Shell is running or not",
2409 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2410 g_object_class_install_property (object_class
, PROP_SHELL_RUNNING
, pspec
);
2412 g_type_class_add_private (object_class
, sizeof (EmpathyRosterWindowPriv
));
2416 show_contacts_loading (EmpathyRosterWindow
*self
)
2418 display_page_message (self
, NULL
, FALSE
, TRUE
);
2420 gtk_spinner_start (GTK_SPINNER (self
->priv
->spinner_loading
));
2424 hide_contacts_loading (EmpathyRosterWindow
*self
)
2426 gtk_spinner_stop (GTK_SPINNER (self
->priv
->spinner_loading
));
2428 set_notebook_page (self
);
2432 contacts_loaded_cb (EmpathyIndividualManager
*manager
,
2433 EmpathyRosterWindow
*self
)
2435 hide_contacts_loading (self
);
2439 empathy_roster_window_init (EmpathyRosterWindow
*self
)
2441 GtkBuilder
*gui
, *gui_mgr
;
2443 GtkToggleAction
*show_offline_widget
;
2444 GtkAction
*show_map_widget
;
2446 gboolean show_offline
;
2448 GtkTreeModel
*model
;
2449 GtkWidget
*search_vbox
;
2452 self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
2453 EMPATHY_TYPE_ROSTER_WINDOW
, EmpathyRosterWindowPriv
);
2455 self
->priv
->gsettings_ui
= g_settings_new (EMPATHY_PREFS_UI_SCHEMA
);
2456 self
->priv
->gsettings_contacts
= g_settings_new (EMPATHY_PREFS_CONTACTS_SCHEMA
);
2458 self
->priv
->sound_mgr
= empathy_sound_manager_dup_singleton ();
2460 gtk_window_set_title (GTK_WINDOW (self
), _("Contact List"));
2461 gtk_window_set_role (GTK_WINDOW (self
), "contact_list");
2462 gtk_window_set_default_size (GTK_WINDOW (self
), 225, 325);
2464 /* don't finalize the widget on delete-event, just hide it */
2465 g_signal_connect (self
, "delete-event",
2466 G_CALLBACK (gtk_widget_hide_on_delete
), NULL
);
2468 /* Set up interface */
2469 filename
= empathy_file_lookup ("empathy-roster-window.ui", "src");
2470 gui
= empathy_builder_get_file (filename
,
2471 "main_vbox", &self
->priv
->main_vbox
,
2472 "balance_vbox", &self
->priv
->balance_vbox
,
2473 "errors_vbox", &self
->priv
->errors_vbox
,
2474 "auth_vbox", &self
->priv
->auth_vbox
,
2475 "search_vbox", &search_vbox
,
2476 "presence_toolbar", &self
->priv
->presence_toolbar
,
2477 "notebook", &self
->priv
->notebook
,
2478 "no_entry_label", &self
->priv
->no_entry_label
,
2479 "roster_scrolledwindow", &sw
,
2480 "button_account_settings", &self
->priv
->button_account_settings
,
2481 "spinner_loading", &self
->priv
->spinner_loading
,
2485 /* Set UI manager */
2486 filename
= empathy_file_lookup ("empathy-roster-window-menubar.ui", "src");
2487 gui_mgr
= empathy_builder_get_file (filename
,
2488 "ui_manager", &self
->priv
->ui_manager
,
2489 "view_show_offline", &show_offline_widget
,
2490 "view_show_protocols", &self
->priv
->show_protocols
,
2491 "view_sort_by_name", &self
->priv
->sort_by_name
,
2492 "view_sort_by_status", &self
->priv
->sort_by_status
,
2493 "view_normal_size_with_avatars", &self
->priv
->normal_with_avatars
,
2494 "view_normal_size", &self
->priv
->normal_size
,
2495 "view_compact_size", &self
->priv
->compact_size
,
2496 "view_history", &self
->priv
->view_history
,
2497 "view_show_map", &show_map_widget
,
2498 "room_join_favorites", &self
->priv
->room_join_favorites
,
2499 "view_balance_show_in_roster", &self
->priv
->view_balance_show_in_roster
,
2500 "menubar", &menubar
,
2504 /* The UI manager is living in its own .ui file as Glade doesn't support
2505 * those. The GtkMenubar has to be in this file as well to we manually add
2506 * it to the first position of the vbox. */
2507 gtk_box_pack_start (GTK_BOX (self
->priv
->main_vbox
), menubar
, FALSE
, FALSE
, 0);
2508 gtk_box_reorder_child (GTK_BOX (self
->priv
->main_vbox
), menubar
, 0);
2510 gtk_container_add (GTK_CONTAINER (self
), self
->priv
->main_vbox
);
2511 gtk_widget_show (self
->priv
->main_vbox
);
2513 g_signal_connect (self
, "key-press-event",
2514 G_CALLBACK (roster_window_key_press_event_cb
), NULL
);
2516 empathy_builder_connect (gui_mgr
, self
,
2517 "chat_quit", "activate", roster_window_chat_quit_cb
,
2518 "chat_new_message", "activate", roster_window_chat_new_message_cb
,
2519 "chat_new_call", "activate", roster_window_chat_new_call_cb
,
2520 "view_history", "activate", roster_window_view_history_cb
,
2521 "room_join_new", "activate", roster_window_room_join_new_cb
,
2522 "room_join_favorites", "activate", roster_window_room_join_favorites_cb
,
2523 "room_manage_favorites", "activate", roster_window_room_manage_favorites_cb
,
2524 "chat_add_contact", "activate", roster_window_chat_add_contact_cb
,
2525 "chat_search_contacts", "activate", roster_window_chat_search_contacts_cb
,
2526 "view_show_ft_manager", "activate", roster_window_view_show_ft_manager
,
2527 "view_show_offline", "toggled", roster_window_view_show_offline_cb
,
2528 "view_show_protocols", "toggled", roster_window_view_show_protocols_cb
,
2529 "view_sort_by_name", "changed", roster_window_view_sort_contacts_cb
,
2530 "view_normal_size_with_avatars", "changed", roster_window_view_contacts_list_size_cb
,
2531 "view_show_map", "activate", roster_window_view_show_map_cb
,
2532 "edit", "activate", roster_window_edit_cb
,
2533 "edit_accounts", "activate", roster_window_edit_accounts_cb
,
2534 "edit_blocked_contacts", "activate", roster_window_edit_blocked_contacts_cb
,
2535 "edit_preferences", "activate", roster_window_edit_preferences_cb
,
2536 "edit_search_contacts", "activate", roster_window_edit_search_contacts_cb
,
2537 "help_about", "activate", roster_window_help_about_cb
,
2538 "help_debug", "activate", roster_window_help_debug_cb
,
2539 "help_contents", "activate", roster_window_help_contents_cb
,
2542 /* Set up connection related widgets. */
2543 roster_window_connection_items_setup (self
, gui_mgr
);
2545 g_object_ref (self
->priv
->ui_manager
);
2546 g_object_unref (gui
);
2547 g_object_unref (gui_mgr
);
2549 #ifndef HAVE_LIBCHAMPLAIN
2550 gtk_action_set_visible (show_map_widget
, FALSE
);
2553 self
->priv
->account_manager
= tp_account_manager_dup ();
2555 tp_proxy_prepare_async (self
->priv
->account_manager
, NULL
,
2556 account_manager_prepared_cb
, self
);
2558 self
->priv
->errors
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
2559 g_object_unref
, NULL
);
2561 self
->priv
->auths
= g_hash_table_new (NULL
, NULL
);
2563 self
->priv
->status_changed_handlers
= g_hash_table_new_full (g_direct_hash
,
2564 g_direct_equal
, NULL
, NULL
);
2567 roster_window_favorite_chatroom_menu_setup (self
);
2569 self
->priv
->edit_context
= gtk_ui_manager_get_widget (self
->priv
->ui_manager
,
2570 "/menubar/edit/edit_context");
2571 self
->priv
->edit_context_separator
= gtk_ui_manager_get_widget (
2572 self
->priv
->ui_manager
,
2573 "/menubar/edit/edit_context_separator");
2574 gtk_widget_hide (self
->priv
->edit_context
);
2575 gtk_widget_hide (self
->priv
->edit_context_separator
);
2577 /* Set up contact list. */
2578 empathy_status_presets_get_all ();
2580 /* Set up presence chooser */
2581 self
->priv
->presence_chooser
= empathy_presence_chooser_new ();
2582 gtk_widget_show (self
->priv
->presence_chooser
);
2583 item
= gtk_tool_item_new ();
2584 gtk_widget_show (GTK_WIDGET (item
));
2585 gtk_widget_set_size_request (self
->priv
->presence_chooser
, 10, -1);
2586 gtk_container_add (GTK_CONTAINER (item
), self
->priv
->presence_chooser
);
2587 gtk_tool_item_set_is_important (item
, TRUE
);
2588 gtk_tool_item_set_expand (item
, TRUE
);
2589 gtk_toolbar_insert (GTK_TOOLBAR (self
->priv
->presence_toolbar
), item
, -1);
2591 /* Set up the throbber */
2592 self
->priv
->throbber
= gtk_spinner_new ();
2593 gtk_widget_set_size_request (self
->priv
->throbber
, 16, -1);
2594 gtk_widget_set_events (self
->priv
->throbber
, GDK_BUTTON_PRESS_MASK
);
2595 g_signal_connect (self
->priv
->throbber
, "button-press-event",
2596 G_CALLBACK (roster_window_throbber_button_press_event_cb
),
2598 gtk_widget_show (self
->priv
->throbber
);
2600 item
= gtk_tool_item_new ();
2601 gtk_container_set_border_width (GTK_CONTAINER (item
), 6);
2602 gtk_toolbar_insert (GTK_TOOLBAR (self
->priv
->presence_toolbar
), item
, -1);
2603 gtk_container_add (GTK_CONTAINER (item
), self
->priv
->throbber
);
2604 self
->priv
->throbber_tool_item
= GTK_WIDGET (item
);
2606 /* XXX: this class is designed to live for the duration of the program,
2607 * so it's got a race condition between its signal handlers and its
2608 * finalization. The class is planned to be removed, so we won't fix
2609 * this before then. */
2610 self
->priv
->individual_manager
= empathy_individual_manager_dup_singleton ();
2612 if (!empathy_individual_manager_get_contacts_loaded (
2613 self
->priv
->individual_manager
))
2615 show_contacts_loading (self
);
2617 tp_g_signal_connect_object (self
->priv
->individual_manager
,
2618 "contacts-loaded", G_CALLBACK (contacts_loaded_cb
), self
, 0);
2621 self
->priv
->individual_store
= EMPATHY_INDIVIDUAL_STORE (
2622 empathy_individual_store_manager_new (self
->priv
->individual_manager
));
2624 /* For the moment, we disallow Persona drops onto the roster contact list
2625 * (e.g. from things such as the EmpathyPersonaView in the linking dialogue).
2626 * No code is hooked up to do anything on a Persona drop, so allowing them
2627 * would achieve nothing except confusion. */
2628 self
->priv
->individual_view
= empathy_individual_view_new (
2629 self
->priv
->individual_store
,
2630 /* EmpathyIndividualViewFeatureFlags */
2631 EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_SAVE
|
2632 EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_RENAME
|
2633 EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_REMOVE
|
2634 EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_CHANGE
|
2635 EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_REMOVE
|
2636 EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_DROP
|
2637 EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_DRAG
|
2638 EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_TOOLTIP
|
2639 EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_CALL
|
2640 EMPATHY_INDIVIDUAL_VIEW_FEATURE_FILE_DROP
,
2641 /* EmpathyIndividualFeatureFlags */
2642 EMPATHY_INDIVIDUAL_FEATURE_CHAT
|
2643 EMPATHY_INDIVIDUAL_FEATURE_CALL
|
2644 EMPATHY_INDIVIDUAL_FEATURE_EDIT
|
2645 EMPATHY_INDIVIDUAL_FEATURE_INFO
|
2646 EMPATHY_INDIVIDUAL_FEATURE_SMS
|
2647 EMPATHY_INDIVIDUAL_FEATURE_CALL_PHONE
);
2649 gtk_widget_show (GTK_WIDGET (self
->priv
->individual_view
));
2650 gtk_container_add (GTK_CONTAINER (sw
),
2651 GTK_WIDGET (self
->priv
->individual_view
));
2652 g_signal_connect (self
->priv
->individual_view
, "row-activated",
2653 G_CALLBACK (roster_window_row_activated_cb
), self
);
2655 /* Set up search bar */
2656 self
->priv
->search_bar
= empathy_live_search_new (
2657 GTK_WIDGET (self
->priv
->individual_view
));
2658 empathy_individual_view_set_live_search (self
->priv
->individual_view
,
2659 EMPATHY_LIVE_SEARCH (self
->priv
->search_bar
));
2660 gtk_box_pack_start (GTK_BOX (search_vbox
), self
->priv
->search_bar
,
2663 g_signal_connect_swapped (self
, "map",
2664 G_CALLBACK (gtk_widget_grab_focus
), self
->priv
->individual_view
);
2666 /* Connect to proper signals to check if contact list is empty or not */
2667 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (self
->priv
->individual_view
));
2668 self
->priv
->empty
= TRUE
;
2669 g_signal_connect (model
, "row-inserted",
2670 G_CALLBACK (roster_window_row_inserted_cb
), self
);
2671 g_signal_connect (model
, "row-deleted",
2672 G_CALLBACK (roster_window_row_deleted_cb
), self
);
2674 /* Load user-defined accelerators. */
2675 roster_window_accels_load ();
2677 /* Set window size. */
2678 empathy_geometry_bind (GTK_WINDOW (self
), GEOMETRY_NAME
);
2680 /* bind view_balance_show_in_roster */
2681 g_settings_bind (self
->priv
->gsettings_ui
, "show-balance-in-roster",
2682 self
->priv
->view_balance_show_in_roster
, "active",
2683 G_SETTINGS_BIND_DEFAULT
);
2684 g_object_bind_property (self
->priv
->view_balance_show_in_roster
, "active",
2685 self
->priv
->balance_vbox
, "visible",
2686 G_BINDING_SYNC_CREATE
);
2688 g_settings_bind (self
->priv
->gsettings_ui
, "show-groups",
2689 self
->priv
->individual_store
, "show-groups", G_SETTINGS_BIND_DEFAULT
);
2691 /* Enable event handling */
2692 self
->priv
->call_observer
= empathy_call_observer_dup_singleton ();
2693 self
->priv
->event_manager
= empathy_event_manager_dup_singleton ();
2695 g_signal_connect (self
->priv
->event_manager
, "event-added",
2696 G_CALLBACK (roster_window_event_added_cb
), self
);
2697 g_signal_connect (self
->priv
->event_manager
, "event-removed",
2698 G_CALLBACK (roster_window_event_removed_cb
), self
);
2699 g_signal_connect (self
->priv
->account_manager
, "account-validity-changed",
2700 G_CALLBACK (roster_window_account_validity_changed_cb
), self
);
2701 g_signal_connect (self
->priv
->account_manager
, "account-removed",
2702 G_CALLBACK (roster_window_account_removed_cb
), self
);
2703 g_signal_connect (self
->priv
->account_manager
, "account-disabled",
2704 G_CALLBACK (roster_window_account_disabled_cb
), self
);
2706 /* Show offline ? */
2707 show_offline
= g_settings_get_boolean (self
->priv
->gsettings_ui
,
2708 EMPATHY_PREFS_UI_SHOW_OFFLINE
);
2710 g_signal_connect (self
->priv
->gsettings_ui
,
2711 "changed::" EMPATHY_PREFS_UI_SHOW_OFFLINE
,
2712 G_CALLBACK (roster_window_notify_show_offline_cb
), show_offline_widget
);
2714 gtk_toggle_action_set_active (show_offline_widget
, show_offline
);
2716 /* Show protocol ? */
2717 g_signal_connect (self
->priv
->gsettings_ui
,
2718 "changed::" EMPATHY_PREFS_UI_SHOW_PROTOCOLS
,
2719 G_CALLBACK (roster_window_notify_show_protocols_cb
), self
);
2721 roster_window_notify_show_protocols_cb (self
->priv
->gsettings_ui
,
2722 EMPATHY_PREFS_UI_SHOW_PROTOCOLS
, self
);
2724 /* Sort by name / by status ? */
2725 g_signal_connect (self
->priv
->gsettings_contacts
,
2726 "changed::" EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM
,
2727 G_CALLBACK (roster_window_notify_sort_contact_cb
), self
);
2729 roster_window_notify_sort_contact_cb (self
->priv
->gsettings_contacts
,
2730 EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM
, self
);
2732 /* Contacts list size */
2733 g_signal_connect (self
->priv
->gsettings_ui
,
2734 "changed::" EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST
,
2735 G_CALLBACK (roster_window_notify_contact_list_size_cb
), self
);
2737 g_signal_connect (self
->priv
->gsettings_ui
,
2738 "changed::" EMPATHY_PREFS_UI_SHOW_AVATARS
,
2739 G_CALLBACK (roster_window_notify_contact_list_size_cb
),
2742 roster_window_notify_contact_list_size_cb (self
->priv
->gsettings_ui
,
2743 EMPATHY_PREFS_UI_SHOW_AVATARS
, self
);
2745 g_signal_connect (self
->priv
->button_account_settings
, "clicked",
2746 G_CALLBACK (button_account_settings_clicked_cb
), self
);
2750 empathy_roster_window_dup (void)
2752 return g_object_new (EMPATHY_TYPE_ROSTER_WINDOW
, NULL
);