diff options
Diffstat (limited to 'src')
39 files changed, 3981 insertions, 4863 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index c6855d338..8623ac2dd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,9 +32,7 @@ noinst_LTLIBRARIES = libempathy-accounts-common.la libempathy_accounts_common_la_SOURCES = \ empathy-accounts-common.c empathy-accounts-common.h \ - empathy-account-assistant.c empathy-account-assistant.h \ empathy-accounts-dialog.c empathy-accounts-dialog.h \ - empathy-auto-salut-account-helper.c empathy-auto-salut-account-helper.h \ empathy-import-dialog.c empathy-import-dialog.h \ empathy-import-pidgin.c empathy-import-pidgin.h \ empathy-import-widget.c empathy-import-widget.h \ @@ -166,7 +164,7 @@ empathy_handwritten_source = \ empathy-event-manager.c empathy-event-manager.h \ empathy-ft-manager.c empathy-ft-manager.h \ empathy-invite-participant-dialog.c empathy-invite-participant-dialog.h \ - empathy-main-window.c empathy-main-window.h \ + empathy-roster-window.c empathy-roster-window.h \ empathy-new-chatroom-dialog.c empathy-new-chatroom-dialog.h \ empathy-notifications-approver.c empathy-notifications-approver.h \ empathy-call-observer.c empathy-call-observer.h \ @@ -207,8 +205,8 @@ ui_DATA = \ empathy-chatrooms-window.ui \ empathy-ft-manager.ui \ empathy-import-dialog.ui \ - empathy-main-window.ui \ - empathy-main-window-menubar.ui \ + empathy-roster-window.ui \ + empathy-roster-window-menubar.ui \ empathy-new-chatroom-dialog.ui \ empathy-preferences.ui \ empathy-status-icon.ui diff --git a/src/empathy-about-dialog.c b/src/empathy-about-dialog.c index 04b2753b6..3204d0f08 100644 --- a/src/empathy-about-dialog.c +++ b/src/empathy-about-dialog.c @@ -88,24 +88,26 @@ static const char *license[] = { "GNU General Public License for more details."), N_("You should have received a copy of the GNU General Public License " "along with Empathy; if not, write to the Free Software Foundation, Inc., " - "51 Franklin Street, Fifth Floor, Boston, MA 02110-130159 USA") + "51 Franklin Street, Fifth Floor, Boston, MA 02110-130159 USA"), + NULL }; void empathy_about_dialog_new (GtkWindow *parent) { - gchar *license_trans; + GString *license_trans = g_string_new (NULL); + int i; - license_trans = g_strconcat (_(license[0]), "\n\n", - _(license[1]), "\n\n", - _(license[2]), "\n\n", - NULL); + for (i = 0; license[i] != NULL; i++) { + g_string_append (license_trans, _(license[i])); + g_string_append (license_trans, "\n\n"); + } gtk_show_about_dialog (parent, "artists", artists, "authors", authors, "comments", _("An Instant Messaging client for GNOME"), - "license", license_trans, + "license", license_trans->str, "wrap-license", TRUE, "copyright", "Imendio AB 2002-2007\nCollabora Ltd 2007-2011", "documenters", documenters, @@ -115,7 +117,7 @@ empathy_about_dialog_new (GtkWindow *parent) "website", WEB_SITE, NULL); - g_free (license_trans); + g_string_free (license_trans, TRUE); } diff --git a/src/empathy-account-assistant.c b/src/empathy-account-assistant.c deleted file mode 100644 index ecfd78344..000000000 --- a/src/empathy-account-assistant.c +++ /dev/null @@ -1,1307 +0,0 @@ -/* - * Copyright (C) 2009 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> - */ - -/* empathy-account-assistant.c */ - -#include <config.h> -#include <glib/gi18n-lib.h> -#include <telepathy-glib/util.h> -#include <gdk/gdkkeysyms.h> - -#include "empathy-account-assistant.h" -#include "empathy-import-widget.h" -#include "empathy-import-utils.h" -#include "empathy-auto-salut-account-helper.h" - -#include <libempathy/empathy-account-settings.h> -#include <libempathy/empathy-utils.h> - -#include <libempathy-gtk/empathy-account-widget.h> -#include <libempathy-gtk/empathy-protocol-chooser.h> -#include <libempathy-gtk/empathy-ui-utils.h> - -#define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT -#include <libempathy/empathy-debug.h> - -G_DEFINE_TYPE (EmpathyAccountAssistant, empathy_account_assistant, - GTK_TYPE_ASSISTANT) - -#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountAssistant) - -typedef enum { - RESPONSE_IMPORT = 1, - RESPONSE_ENTER_ACCOUNT = 2, - RESPONSE_CREATE_ACCOUNT = 3, - RESPONSE_SALUT_ONLY = 4 -} FirstPageResponse; - -typedef enum { - RESPONSE_CREATE_AGAIN = 1, - RESPONSE_CREATE_STOP = 2 -} CreateEnterPageResponse; - -typedef enum { - PAGE_INTRO = 0, - PAGE_IMPORT = 1, - PAGE_ENTER_CREATE = 2, - PAGE_SALUT = 3, -} PageID; - -enum { - PROP_PARENT = 1, - PROP_CONNECTION_MGRS, -}; - -typedef struct { - FirstPageResponse first_resp; - CreateEnterPageResponse create_enter_resp; - gboolean enter_create_forward; - TpAccountManager *account_mgr; - EmpathyConnectionManagers *connection_mgrs; - PageID current_page_id; - - /* enter or create page */ - GtkWidget *enter_or_create_page; - GtkWidget *current_account_widget; - EmpathyAccountWidget *current_widget_object; - GtkWidget *first_label; - GtkWidget *second_label; - GtkWidget *chooser; - GtkWidget *create_again_radio; - EmpathyAccountSettings *settings; - gboolean is_creating; - - /* import page */ - EmpathyImportWidget *iw; - GtkWidget *import_page; - - /* salut page */ - GtkWidget *salut_page; - EmpathyAccountSettings *salut_settings; - GtkWidget *salut_account_widget; - gboolean create_salut_account; - gboolean display_salut_page; - - GtkWindow *parent_window; - - gboolean dispose_run; -} EmpathyAccountAssistantPriv; - -static GtkWidget * account_assistant_build_enter_or_create_page ( - EmpathyAccountAssistant *self); -static void account_assistant_finish_enter_or_create_page ( - EmpathyAccountAssistant *self, - gboolean is_enter); - -static void do_constructed (GObject *object); - -static GtkWidget * -build_error_vbox (const gchar *primary_message, - const gchar *secondary_message) -{ - GtkWidget *main_vbox, *w, *hbox; - PangoAttrList *list; - - main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); - gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); - gtk_widget_show (main_vbox); - - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); - gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show (hbox); - - w = gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR, - GTK_ICON_SIZE_DIALOG); - gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0); - gtk_widget_show (w); - - w = gtk_label_new (primary_message); - gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0); - list = pango_attr_list_new (); - pango_attr_list_insert (list, pango_attr_scale_new (PANGO_SCALE_LARGE)); - pango_attr_list_insert (list, pango_attr_weight_new (PANGO_WEIGHT_BOLD)); - gtk_label_set_attributes (GTK_LABEL (w), list); - gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5); - gtk_label_set_line_wrap (GTK_LABEL (w), TRUE); - gtk_widget_show (w); - - pango_attr_list_unref (list); - - w = gtk_label_new (secondary_message); - gtk_label_set_use_markup (GTK_LABEL (w), TRUE); - gtk_box_pack_start (GTK_BOX (main_vbox), w, FALSE, FALSE, 0); - gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5); - gtk_label_set_line_wrap (GTK_LABEL (w), TRUE); - gtk_widget_show (w); - - return main_vbox; -} - -static GtkWidget * -account_assistant_build_error_page (EmpathyAccountAssistant *self, - GError *error, gint page_num) -{ - GtkWidget *main_vbox, *w; - const char *primary_message; - gchar *secondary_message, *markup; - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - - if (page_num == PAGE_IMPORT) - primary_message = _("There was an error while importing the accounts."); - else if (page_num >= PAGE_ENTER_CREATE && - priv->first_resp == RESPONSE_ENTER_ACCOUNT) - primary_message = _("There was an error while parsing the account details."); - else if (page_num >= PAGE_ENTER_CREATE && - priv->first_resp == RESPONSE_CREATE_ACCOUNT) - primary_message = _("There was an error while creating the account."); - else - primary_message = _("There was an error."); - - markup = g_markup_printf_escaped ("<span style=\"italic\">%s</span>", - error->message); - secondary_message = g_strdup_printf (_("The error message was: %s"), markup); - - main_vbox = build_error_vbox (primary_message, secondary_message); - - w = gtk_label_new (_("You can either go back and try to enter your " - "accounts' details again or quit this assistant and add accounts " - "later from the Edit menu.")); - gtk_box_pack_start (GTK_BOX (main_vbox), w, FALSE, FALSE, 6); - gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5); - gtk_label_set_line_wrap (GTK_LABEL (w), TRUE); - gtk_widget_show (w); - - g_free (markup); - g_free (secondary_message); - return main_vbox; -} - -static void -account_assistant_back_button_clicked_cb (GtkButton *button, - EmpathyAccountAssistant *self) -{ - gint page_num; - - page_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), - "page-num")); - gtk_assistant_remove_action_widget (GTK_ASSISTANT (self), - GTK_WIDGET (button)); - gtk_assistant_set_current_page (GTK_ASSISTANT (self), page_num); -} - -static void -account_assistant_present_error_page (EmpathyAccountAssistant *self, - GError *error, gint page_num) -{ - GtkWidget *error_page, *back_button; - gint num; - - error_page = account_assistant_build_error_page (self, error, - page_num); - num = gtk_assistant_append_page (GTK_ASSISTANT (self), error_page); - gtk_assistant_set_page_title (GTK_ASSISTANT (self), error_page, - _("An error occurred")); - gtk_assistant_set_page_type (GTK_ASSISTANT (self), error_page, - GTK_ASSISTANT_PAGE_SUMMARY); - - back_button = gtk_button_new_from_stock (GTK_STOCK_GO_BACK); - gtk_assistant_add_action_widget (GTK_ASSISTANT (self), back_button); - g_object_set_data (G_OBJECT (back_button), - "page-num", GINT_TO_POINTER (page_num)); - g_signal_connect (back_button, "clicked", - G_CALLBACK (account_assistant_back_button_clicked_cb), self); - gtk_widget_show (back_button); - - gtk_assistant_set_current_page (GTK_ASSISTANT (self), num); -} - -static void -update_create_page_buttons (EmpathyAccountAssistant *self) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - GtkAssistantPageType type; - - if (priv->display_salut_page || - priv->create_enter_resp == RESPONSE_CREATE_AGAIN) - type = GTK_ASSISTANT_PAGE_CONTENT; - else - type = GTK_ASSISTANT_PAGE_CONFIRM; - - gtk_assistant_set_page_type (GTK_ASSISTANT (self), priv->enter_or_create_page, - type); -} - -static void -account_assistant_reset_enter_create_page (EmpathyAccountAssistant *self) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - GtkWidget *page; - gint idx; - - page = account_assistant_build_enter_or_create_page (self); - idx = gtk_assistant_append_page (GTK_ASSISTANT (self), page); - priv->enter_or_create_page = page; - update_create_page_buttons (self); - - gtk_assistant_set_current_page (GTK_ASSISTANT (self), idx); - - account_assistant_finish_enter_or_create_page (self, - priv->first_resp == RESPONSE_ENTER_ACCOUNT ? - TRUE : FALSE); -} - -static void -account_assistant_account_enabled_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - GError *error = NULL; - EmpathyAccountAssistant *self = user_data; - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - const gchar *protocol; - TpAccount *account = TP_ACCOUNT (source); - gint current_idx; - gboolean salut_created = FALSE; - - tp_account_set_enabled_finish (account, result, &error); - - if (error) - { - g_warning ("Error enabling an account: %s", error->message); - g_error_free (error); - } - - protocol = tp_account_get_protocol (account); - if (!tp_strdiff (protocol, "local-xmpp")) - { - salut_created = TRUE; - } - - empathy_connect_new_account (account, priv->account_mgr); - - current_idx = gtk_assistant_get_current_page (GTK_ASSISTANT (self)); - if (current_idx == PAGE_SALUT && !salut_created) - /* We are on the Salut page and aren't creating the salut account so don't - * terminate the assistant. */ - return; - - if (priv->create_enter_resp == RESPONSE_CREATE_STOP) - g_signal_emit_by_name (self, "close"); - else - account_assistant_reset_enter_create_page (self); -} - -static void -account_assistant_apply_account_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - GError *error = NULL; - EmpathyAccountAssistant *self = user_data; - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (source); - TpAccount *account; - - empathy_account_settings_apply_finish (settings, result, NULL, &error); - - priv->is_creating = FALSE; - - if (error != NULL) - { - account_assistant_present_error_page (self, error, - gtk_assistant_get_current_page (GTK_ASSISTANT (self))); - g_error_free (error); - return; - } - - /* enable the newly created account */ - account = empathy_account_settings_get_account (settings); - tp_account_set_enabled_async (account, TRUE, - account_assistant_account_enabled_cb, self); -} - -static void -account_assistant_apply_account_and_finish (EmpathyAccountAssistant *self, - EmpathyAccountSettings *settings, - gboolean set_display_name) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - - if (settings == NULL) - return; - - priv->is_creating = TRUE; - - if (set_display_name) - { - gchar *display_name; - - display_name = empathy_account_widget_get_default_display_name ( - priv->current_widget_object); - - empathy_account_settings_set_display_name_async (settings, - display_name, NULL, NULL); - - g_free (display_name); - } - - empathy_account_settings_apply_async (settings, - account_assistant_apply_account_cb, self); -} - -static void -account_assistant_handle_apply_cb (EmpathyAccountWidget *widget_object, - gboolean is_valid, - EmpathyAccountAssistant *self) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - - gtk_assistant_set_page_complete (GTK_ASSISTANT (self), - priv->enter_or_create_page, is_valid); -} - -static void -account_assistant_protocol_changed_cb (GtkComboBox *chooser, - EmpathyAccountAssistant *self) -{ - EmpathyAccountSettings *settings; - EmpathyAccountAssistantPriv *priv; - GtkWidget *account_widget; - EmpathyAccountWidget *widget_object = NULL; - - priv = GET_PRIV (self); - - settings = empathy_protocol_chooser_create_account_settings ( - EMPATHY_PROTOCOL_CHOOSER (chooser)); - - if (settings == NULL) - return; - - if (priv->first_resp == RESPONSE_CREATE_ACCOUNT) - empathy_account_settings_set_boolean (settings, "register", TRUE); - - widget_object = empathy_account_widget_new_for_protocol (settings, TRUE); - account_widget = empathy_account_widget_get_widget (widget_object); - - if (priv->current_account_widget != NULL) - { - g_signal_handlers_disconnect_by_func (priv->current_widget_object, - account_assistant_handle_apply_cb, self); - gtk_widget_destroy (priv->current_account_widget); - } - - priv->current_account_widget = account_widget; - priv->current_widget_object = widget_object; - - if (priv->settings != NULL) - g_object_unref (priv->settings); - - priv->settings = settings; - - g_signal_connect (priv->current_widget_object, "handle-apply", - G_CALLBACK (account_assistant_handle_apply_cb), self); - - if (empathy_account_settings_is_valid (settings)) - { - gtk_assistant_set_page_complete (GTK_ASSISTANT (self), - priv->enter_or_create_page, TRUE); - } - - gtk_box_pack_start (GTK_BOX (priv->enter_or_create_page), account_widget, - FALSE, FALSE, 0); - gtk_widget_show (account_widget); -} - -static gboolean -account_assistant_chooser_enter_details_filter_func ( - TpConnectionManager *cm, - TpConnectionManagerProtocol *protocol, - const gchar *service, - gpointer user_data) -{ - if (!tp_strdiff (protocol->name, "local-xmpp")) - return FALSE; - - return TRUE; -} - -static gboolean -account_assistant_chooser_create_account_filter_func ( - TpConnectionManager *cm, - TpConnectionManagerProtocol *protocol, - const gchar *service, - gpointer user_data) -{ - if (service != NULL) - return FALSE; - - return tp_connection_manager_protocol_can_register (protocol); -} - -static void -account_assistant_finish_enter_or_create_page (EmpathyAccountAssistant *self, - gboolean is_enter) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - - if (is_enter) - { - gtk_label_set_label (GTK_LABEL (priv->first_label), - _("What kind of chat account do you have?")); - /* gtk_label_set_label (GTK_LABEL (priv->second_label), - _("If you have other accounts to set up, you can do " - "that at any time from the Edit menu.")); - */ - gtk_label_set_label (GTK_LABEL (priv->second_label), - _("Do you have any other chat accounts you want to set up?")); - empathy_protocol_chooser_set_visible ( - EMPATHY_PROTOCOL_CHOOSER (priv->chooser), - account_assistant_chooser_enter_details_filter_func, self); - - gtk_assistant_set_page_title (GTK_ASSISTANT (self), - priv->enter_or_create_page, _("Enter your account details")); - } - else - { - gtk_label_set_label (GTK_LABEL (priv->first_label), - _("What kind of chat account do you want to create?")); - /* gtk_label_set_label (GTK_LABEL (priv->second_label), - _("You can register other accounts, or setup " - "an existing one at any time from the Edit menu.")); - */ - gtk_label_set_label (GTK_LABEL (priv->second_label), - _("Do you want to create other chat accounts?")); - empathy_protocol_chooser_set_visible ( - EMPATHY_PROTOCOL_CHOOSER (priv->chooser), - account_assistant_chooser_create_account_filter_func, self); - - gtk_assistant_set_page_title (GTK_ASSISTANT (self), - priv->enter_or_create_page, - _("Enter the details for the new account")); - } - - g_signal_connect (priv->chooser, "changed", - G_CALLBACK (account_assistant_protocol_changed_cb), self); - - /* trigger show the first account widget */ - account_assistant_protocol_changed_cb (GTK_COMBO_BOX (priv->chooser), self); -} - -static gint -account_assistant_page_forward_func (gint current_page, - gpointer user_data) -{ - EmpathyAccountAssistant *self = user_data; - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - gint retval; - - retval = current_page; - - if (current_page == PAGE_INTRO) - { - if (priv->first_resp == RESPONSE_ENTER_ACCOUNT || - priv->first_resp == RESPONSE_CREATE_ACCOUNT) - retval = PAGE_ENTER_CREATE; - else if (priv->first_resp == RESPONSE_IMPORT) - retval = PAGE_IMPORT; - else if (priv->first_resp == RESPONSE_SALUT_ONLY) - retval = PAGE_SALUT; - } - else if (current_page == PAGE_IMPORT) - { - if (priv->display_salut_page) - retval = PAGE_SALUT; - else - /* Don't go forward */ - retval = -1; - } - else if (current_page == PAGE_SALUT) - { - /* Don't go forward */ - retval = -1; - } - else if (current_page >= PAGE_ENTER_CREATE) - { - if (priv->create_enter_resp == RESPONSE_CREATE_AGAIN) - { - priv->enter_create_forward = TRUE; - retval = current_page; - } - else if (priv->display_salut_page) - { - retval = PAGE_SALUT; - } - else - { - /* Don't go forward */ - retval = -1; - } - } - - return retval; -} - -static void -update_intro_page_buttons (EmpathyAccountAssistant *self) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - GtkWidget *intro_page; - - intro_page = gtk_assistant_get_nth_page (GTK_ASSISTANT (self), - PAGE_INTRO); - - if (priv->first_resp == RESPONSE_SALUT_ONLY && - !priv->display_salut_page) - gtk_assistant_set_page_type (GTK_ASSISTANT (self), intro_page, - GTK_ASSISTANT_PAGE_SUMMARY); - else - gtk_assistant_set_page_type (GTK_ASSISTANT (self), intro_page, - GTK_ASSISTANT_PAGE_INTRO); -} - -static void -account_assistant_radio_choice_toggled_cb (GtkToggleButton *button, - EmpathyAccountAssistant *self) -{ - FirstPageResponse response; - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - - response = GPOINTER_TO_INT (g_object_get_data - (G_OBJECT (button), "response")); - - priv->first_resp = response; - - update_intro_page_buttons (self); -} - -static GtkWidget * -account_assistant_build_introduction_page (EmpathyAccountAssistant *self) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - GtkWidget *main_vbox, *hbox_1, *w, *vbox_1; - GtkWidget *radio = NULL; - GdkPixbuf *pix; - const gchar *str; - - main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); - gtk_widget_show (main_vbox); - gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); - - hbox_1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); - gtk_box_pack_start (GTK_BOX (main_vbox), hbox_1, TRUE, TRUE, 0); - gtk_widget_show (hbox_1); - - w = gtk_label_new ( - _("With Empathy you can chat with people " - "online nearby and with friends and colleagues " - "who use Google Talk, AIM, Windows Live " - "and many other chat programs. With a microphone " - "or a webcam you can also have audio or video calls.")); - gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5); - gtk_label_set_line_wrap (GTK_LABEL (w), TRUE); - gtk_box_pack_start (GTK_BOX (hbox_1), w, FALSE, FALSE, 0); - gtk_widget_show (w); - - pix = empathy_pixbuf_from_icon_name_sized ("empathy", 80); - w = gtk_image_new_from_pixbuf (pix); - gtk_box_pack_start (GTK_BOX (hbox_1), w, FALSE, FALSE, 6); - gtk_widget_show (w); - - g_object_unref (pix); - - w = gtk_label_new (_("Do you have an account you've been using " - "with another chat program?")); - gtk_misc_set_alignment (GTK_MISC (w), 0, 0); - gtk_label_set_line_wrap (GTK_LABEL (w), TRUE); - gtk_box_pack_start (GTK_BOX (main_vbox), w, FALSE, FALSE, 0); - gtk_widget_show (w); - - w = gtk_alignment_new (0, 0, 0, 0); - gtk_alignment_set_padding (GTK_ALIGNMENT (w), 0, 0, 12, 0); - gtk_box_pack_start (GTK_BOX (main_vbox), w, TRUE, TRUE, 0); - gtk_widget_show (w); - - vbox_1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_box_set_homogeneous (GTK_BOX (vbox_1), TRUE); - gtk_container_add (GTK_CONTAINER (w), vbox_1); - gtk_widget_show (vbox_1); - - if (empathy_import_accounts_to_import ()) - { - hbox_1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start (GTK_BOX (vbox_1), hbox_1, TRUE, TRUE, 0); - gtk_widget_show (hbox_1); - - radio = gtk_radio_button_new_with_label (NULL, - _("Yes, import my account details from ")); - gtk_box_pack_start (GTK_BOX (hbox_1), radio, TRUE, TRUE, 0); - g_object_set_data (G_OBJECT (radio), "response", - GINT_TO_POINTER (RESPONSE_IMPORT)); - gtk_widget_show (radio); - - w = gtk_combo_box_text_new (); - gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), "Pidgin"); - gtk_box_pack_start (GTK_BOX (hbox_1), w, TRUE, TRUE, 0); - gtk_combo_box_set_active (GTK_COMBO_BOX (w), 0); - gtk_widget_show (w); - - g_signal_connect (radio, "clicked", - G_CALLBACK (account_assistant_radio_choice_toggled_cb), self); - priv->first_resp = RESPONSE_IMPORT; - } - else - { - priv->first_resp = RESPONSE_ENTER_ACCOUNT; - } - - str = _("Yes, I'll enter my account details now"); - - if (radio == NULL) - { - radio = gtk_radio_button_new_with_label (NULL, str); - w = radio; - } - else - { - w = gtk_radio_button_new_with_label_from_widget ( - GTK_RADIO_BUTTON (radio), str); - } - - gtk_box_pack_start (GTK_BOX (vbox_1), w, TRUE, TRUE, 0); - g_object_set_data (G_OBJECT (w), "response", - GINT_TO_POINTER (RESPONSE_ENTER_ACCOUNT)); - gtk_widget_show (w); - - g_signal_connect (w, "clicked", - G_CALLBACK (account_assistant_radio_choice_toggled_cb), self); - - w = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio), - _("No, I want a new account")); - gtk_box_pack_start (GTK_BOX (vbox_1), w, TRUE, TRUE, 0); - g_object_set_data (G_OBJECT (w), "response", - GINT_TO_POINTER (RESPONSE_CREATE_ACCOUNT)); - gtk_widget_show (w); - - g_signal_connect (w, "clicked", - G_CALLBACK (account_assistant_radio_choice_toggled_cb), self); - - w = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio), - _("No, I just want to see people online nearby for now")); - gtk_box_pack_start (GTK_BOX (vbox_1), w, TRUE, TRUE, 0); - g_object_set_data (G_OBJECT (w), "response", - GINT_TO_POINTER (RESPONSE_SALUT_ONLY)); - gtk_widget_show (w); - - g_signal_connect (w, "clicked", - G_CALLBACK (account_assistant_radio_choice_toggled_cb), self); - - return main_vbox; -} - -static GtkWidget * -account_assistant_build_import_page (EmpathyAccountAssistant *self) -{ - GtkWidget *main_vbox, *w, *import; - EmpathyImportWidget *iw; - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - - main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); - gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); - w = gtk_label_new (_("Select the accounts you want to import:")); - gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5); - gtk_widget_show (w); - gtk_box_pack_start (GTK_BOX (main_vbox), w, FALSE, FALSE, 6); - - w = gtk_alignment_new (0, 0, 1, 1); - gtk_alignment_set_padding (GTK_ALIGNMENT (w), 0, 0, 12, 0); - gtk_box_pack_start (GTK_BOX (main_vbox), w, TRUE, TRUE, 0); - gtk_widget_show (w); - - /* NOTE: this is hardcoded as we support pidgin only */ - iw = empathy_import_widget_new (EMPATHY_IMPORT_APPLICATION_PIDGIN); - import = empathy_import_widget_get_widget (iw); - gtk_container_add (GTK_CONTAINER (w), import); - gtk_widget_show (import); - - priv->iw = iw; - - gtk_widget_show (main_vbox); - - return main_vbox; -} - -static void -account_assistant_radio_create_again_clicked_cb (GtkButton *button, - EmpathyAccountAssistant *self) -{ - CreateEnterPageResponse response; - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - - response = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), - "response")); - - priv->create_enter_resp = response; - - update_create_page_buttons (self); -} - -static GtkWidget * -account_assistant_build_enter_or_create_page (EmpathyAccountAssistant *self) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - GtkWidget *main_vbox, *w, *chooser, *vbox, *hbox, *radio; - - main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); - gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); - gtk_widget_show (main_vbox); - - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show (hbox); - - w = gtk_label_new (NULL); - gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0); - gtk_widget_show (w); - priv->first_label = w; - - w = gtk_alignment_new (0, 0, 0, 0); - gtk_alignment_set_padding (GTK_ALIGNMENT (w), 0, 0, 12, 0); - gtk_box_pack_start (GTK_BOX (main_vbox), w, FALSE, FALSE, 0); - gtk_widget_show (w); - - chooser = empathy_protocol_chooser_new (); - gtk_box_pack_start (GTK_BOX (hbox), chooser, FALSE, FALSE, 0); - gtk_widget_show (chooser); - priv->chooser = chooser; - - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); - gtk_box_pack_end (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0); - gtk_widget_show (vbox); - - w = gtk_label_new (NULL); - gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0); - gtk_label_set_line_wrap (GTK_LABEL (w), TRUE); - gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5); - gtk_widget_show (w); - priv->second_label = w; - - w = gtk_alignment_new (0, 0, 0, 0); - gtk_alignment_set_padding (GTK_ALIGNMENT (w), 0, 0, 12, 0); - gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0); - gtk_widget_show (w); - - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - gtk_container_add (GTK_CONTAINER (w), hbox); - gtk_widget_show (hbox); - - radio = gtk_radio_button_new_with_label (NULL, _("Yes")); - gtk_box_pack_start (GTK_BOX (hbox), radio, FALSE, FALSE, 0); - g_object_set_data (G_OBJECT (radio), "response", - GINT_TO_POINTER (RESPONSE_CREATE_AGAIN)); - gtk_widget_show (radio); - - w = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio), - _("No, that's all for now")); - gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0); - g_object_set_data (G_OBJECT (w), "response", - GINT_TO_POINTER (RESPONSE_CREATE_STOP)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), TRUE); - priv->create_enter_resp = RESPONSE_CREATE_STOP; - priv->create_again_radio = w; - gtk_widget_show (w); - - g_signal_connect (w, "clicked", - G_CALLBACK (account_assistant_radio_create_again_clicked_cb), self); - g_signal_connect (radio, "clicked", - G_CALLBACK (account_assistant_radio_create_again_clicked_cb), self); - - return main_vbox; -} - -static void -account_assistant_close_cb (GtkAssistant *assistant, - gpointer user_data) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (assistant); - - if (priv->is_creating) - return; - - gtk_widget_destroy (GTK_WIDGET (assistant)); -} - -static void -impl_signal_apply (GtkAssistant *assistant) -{ - EmpathyAccountAssistant *self = EMPATHY_ACCOUNT_ASSISTANT (assistant); - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - gint current_page; - - current_page = gtk_assistant_get_current_page (assistant); - - if (current_page == PAGE_SALUT) - { - if (priv->create_salut_account) - /* create_salut_account_settings() already set the display name of the - * account so there is no need to set it again. */ - account_assistant_apply_account_and_finish (self, priv->salut_settings, - FALSE); - return; - } - else if (current_page >= PAGE_ENTER_CREATE && - priv->settings != NULL && - empathy_account_settings_is_valid (priv->settings)) - { - account_assistant_apply_account_and_finish (self, priv->settings, TRUE); - g_object_unref (priv->settings); - priv->settings = NULL; - } - else if (current_page == PAGE_IMPORT) - { - empathy_import_widget_add_selected_accounts (priv->iw); - } -} - -static void -impl_signal_cancel (GtkAssistant *assistant) -{ - gtk_widget_destroy (GTK_WIDGET (assistant)); -} - -static void -impl_signal_prepare (GtkAssistant *assistant, - GtkWidget *current_page) -{ - EmpathyAccountAssistant *self = EMPATHY_ACCOUNT_ASSISTANT (assistant); - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - gint current_idx; - - /* check from which page we are coming from */ - if (priv->current_page_id == PAGE_IMPORT) - empathy_import_widget_add_selected_accounts (priv->iw); - - current_idx = gtk_assistant_get_current_page (assistant); - priv->current_page_id = current_idx; - - if (current_idx >= PAGE_ENTER_CREATE) - { - if (!priv->enter_create_forward && current_idx != PAGE_SALUT) - { - account_assistant_finish_enter_or_create_page (self, - priv->first_resp == RESPONSE_ENTER_ACCOUNT ? - TRUE : FALSE); - } - else - { - priv->enter_create_forward = FALSE; - } - - if (priv->settings != NULL && - empathy_account_settings_is_valid (priv->settings)) - { - account_assistant_apply_account_and_finish (self, priv->settings, - TRUE); - g_object_unref (priv->settings); - priv->settings = NULL; - } - } -} - -static void -do_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (object); - - switch (property_id) - { - case PROP_PARENT: - g_value_set_object (value, priv->parent_window); - break; - case PROP_CONNECTION_MGRS: - g_value_set_object (value, priv->connection_mgrs); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -} - -static void -do_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (object); - - switch (property_id) - { - case PROP_PARENT: - priv->parent_window = g_value_get_object (value); - break; - case PROP_CONNECTION_MGRS: - priv->connection_mgrs = g_value_dup_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -} - -static void -do_dispose (GObject *obj) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (obj); - - if (priv->dispose_run) - return; - - priv->dispose_run = TRUE; - - if (priv->settings != NULL) - { - g_object_unref (priv->settings); - priv->settings = NULL; - } - - g_object_unref (priv->account_mgr); - priv->account_mgr = NULL; - - g_object_unref (priv->connection_mgrs); - priv->connection_mgrs = NULL; - - if (G_OBJECT_CLASS (empathy_account_assistant_parent_class)->dispose != NULL) - G_OBJECT_CLASS (empathy_account_assistant_parent_class)->dispose (obj); -} - -static void -empathy_account_assistant_class_init (EmpathyAccountAssistantClass *klass) -{ - GObjectClass *oclass = G_OBJECT_CLASS (klass); - GtkAssistantClass *gtkclass = GTK_ASSISTANT_CLASS (klass); - GParamSpec *param_spec; - - oclass->get_property = do_get_property; - oclass->set_property = do_set_property; - oclass->constructed = do_constructed; - oclass->dispose = do_dispose; - - gtkclass->apply = impl_signal_apply; - gtkclass->prepare = impl_signal_prepare; - gtkclass->cancel = impl_signal_cancel; - - param_spec = g_param_spec_object ("parent-window", - "parent-window", "The parent window", - GTK_TYPE_WINDOW, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); - g_object_class_install_property (oclass, PROP_PARENT, param_spec); - - param_spec = g_param_spec_object ("connection-managers", - "connection-managers", "A EmpathyConnectionManagers", - EMPATHY_TYPE_CONNECTION_MANAGERS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); - g_object_class_install_property (oclass, PROP_CONNECTION_MGRS, param_spec); - - g_type_class_add_private (klass, sizeof (EmpathyAccountAssistantPriv)); -} - -static void -create_salut_check_box_toggled_cb (GtkWidget *widget, - EmpathyAccountAssistant *self) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - gboolean sensitive; - gboolean page_valid; - - sensitive = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); - - gtk_widget_set_sensitive (priv->salut_account_widget, sensitive); - - if (!sensitive) - { - page_valid = TRUE; - priv->create_salut_account = FALSE; - } - else - { - /* page is complete if the account is valid */ - page_valid = empathy_account_settings_is_valid (priv->salut_settings); - priv->create_salut_account = TRUE; - } - - gtk_assistant_set_page_complete (GTK_ASSISTANT (self), priv->salut_page, - page_valid); -} - -static void -account_assistant_salut_handle_apply_cb (EmpathyAccountWidget *widget_object, - gboolean is_valid, - EmpathyAccountAssistant *self) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - - gtk_assistant_set_page_complete (GTK_ASSISTANT (self), - priv->salut_page, is_valid); -} - -static GtkWidget * -account_assistant_build_salut_page (EmpathyAccountAssistant *self) -{ - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - GtkWidget *main_vbox, *hbox_1, *w; - GdkPixbuf *pix; - EmpathyAccountSettings *settings; - GtkWidget *account_widget; - EmpathyAccountWidget *widget_object; - gchar *markup; - - main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); - gtk_widget_show (main_vbox); - gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); - - hbox_1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); - gtk_box_pack_start (GTK_BOX (main_vbox), hbox_1, TRUE, TRUE, 0); - gtk_widget_show (hbox_1); - - w = gtk_label_new (NULL); - markup = g_strdup_printf ("%s (<span style=\"italic\">%s</span>).", - _("Empathy can automatically discover and chat with the people " - "connected on the same network as you. " - "If you want to use this feature, please check that the " - "details below are correct. " - "You can easily change these details later or disable this feature " - "by using the 'Accounts' dialog"), - _("Edit->Accounts")); - gtk_label_set_markup (GTK_LABEL (w), markup); - g_free (markup); - gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5); - gtk_label_set_line_wrap (GTK_LABEL (w), TRUE); - gtk_box_pack_start (GTK_BOX (hbox_1), w, FALSE, FALSE, 0); - gtk_widget_show (w); - - pix = empathy_pixbuf_from_icon_name_sized ("im-local-xmpp", 80); - w = gtk_image_new_from_pixbuf (pix); - gtk_box_pack_start (GTK_BOX (hbox_1), w, FALSE, FALSE, 6); - gtk_widget_show (w); - - g_object_unref (pix); - - w = gtk_check_button_new_with_mnemonic ( - _("I do _not want to enable this feature for now")); - gtk_box_pack_start (GTK_BOX (main_vbox), w, FALSE, FALSE, 0); - g_signal_connect (w, "toggled", - G_CALLBACK (create_salut_check_box_toggled_cb), self); - gtk_widget_show (w); - - w = gtk_alignment_new (0, 0, 0, 0); - gtk_alignment_set_padding (GTK_ALIGNMENT (w), 0, 0, 12, 0); - gtk_box_pack_start (GTK_BOX (main_vbox), w, TRUE, TRUE, 0); - gtk_widget_show (w); - - settings = create_salut_account_settings (); - - widget_object = empathy_account_widget_new_for_protocol (settings, TRUE); - account_widget = empathy_account_widget_get_widget (widget_object); - - priv->salut_settings = settings; - priv->salut_account_widget = account_widget; - - g_signal_connect (widget_object, "handle-apply", - G_CALLBACK (account_assistant_salut_handle_apply_cb), self); - - gtk_box_pack_start (GTK_BOX (main_vbox), account_widget, - FALSE, FALSE, 0); - gtk_widget_show (account_widget); - - return main_vbox; -} - -static GtkWidget * -account_assistant_build_salut_error_page (EmpathyAccountAssistant *self) -{ - GtkWidget *vbox; - gchar *markup; - - markup = g_strdup_printf ("%s (<span style=\"italic\">%s</span>).", - _("You won't be able to chat with people connected to your local " - "network, as telepathy-salut is not installed. If you want to enable " - "this feature, please install the telepathy-salut package and create " - "a People Nearby account from the Accounts dialog"), - _("Edit->Accounts")); - - vbox = build_error_vbox (_("telepathy-salut not installed"), markup); - g_free (markup); - return vbox; -} - -static void -account_mgr_prepare_cb (GObject *source_object, - GAsyncResult *result, - gpointer user_data) -{ - EmpathyAccountAssistant *self = user_data; - EmpathyAccountAssistantPriv *priv = GET_PRIV (self); - TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object); - GError *error = NULL; - - if (!tp_proxy_prepare_finish (manager, result, &error)) - { - DEBUG ("Failed to prepare account manager: %s", error->message); - g_error_free (error); - return; - } - - if (!should_create_salut_account (manager)) - { - DEBUG ("No need to create a Salut account"); - priv->display_salut_page = FALSE; - - update_intro_page_buttons (self); - - gtk_assistant_set_page_type (GTK_ASSISTANT (self), priv->import_page, - GTK_ASSISTANT_PAGE_CONFIRM); - - update_create_page_buttons (self); - } -} - -static void -empathy_account_assistant_init (EmpathyAccountAssistant *self) -{ - EmpathyAccountAssistantPriv *priv; - - priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_ACCOUNT_ASSISTANT, - EmpathyAccountAssistantPriv); - self->priv = priv; - - gtk_window_set_title (GTK_WINDOW (self), - _("Messaging and VoIP Accounts Assistant")); - - priv->account_mgr = tp_account_manager_dup (); -} - -static void -do_constructed (GObject *object) -{ - GtkAssistant *assistant = GTK_ASSISTANT (object); - EmpathyAccountAssistant *self = EMPATHY_ACCOUNT_ASSISTANT (object); - EmpathyAccountAssistantPriv *priv = GET_PRIV (object); - GtkWidget *page; - - /* set us as transient for the parent window if any */ - if (priv->parent_window) - gtk_window_set_transient_for (GTK_WINDOW (object), - priv->parent_window); - - /* set the dialog hint, so this will be centered over the parent window */ - gtk_window_set_type_hint (GTK_WINDOW (object), GDK_WINDOW_TYPE_HINT_DIALOG); - - g_assert (priv->connection_mgrs != NULL); - g_assert (empathy_connection_managers_is_ready (priv->connection_mgrs)); - - g_signal_connect (self, "close", - G_CALLBACK (account_assistant_close_cb), NULL); - - gtk_assistant_set_forward_page_func (assistant, - account_assistant_page_forward_func, self, NULL); - - /* first page (introduction) */ - page = account_assistant_build_introduction_page (self); - gtk_assistant_append_page (assistant, page); - gtk_assistant_set_page_title (assistant, page, - _("Welcome to Empathy")); - gtk_assistant_set_page_type (assistant, page, - GTK_ASSISTANT_PAGE_INTRO); - gtk_assistant_set_page_complete (assistant, page, TRUE); - - /* second page (import accounts) */ - page = account_assistant_build_import_page (self); - gtk_assistant_append_page (assistant, page); - gtk_assistant_set_page_title (assistant, page, - _("Import your existing accounts")); - gtk_assistant_set_page_complete (assistant, page, TRUE); - gtk_assistant_set_page_type (assistant, page, GTK_ASSISTANT_PAGE_CONTENT); - priv->import_page = page; - - /* third page (enter account details) */ - page = account_assistant_build_enter_or_create_page (self); - gtk_assistant_append_page (assistant, page); - gtk_assistant_set_page_type (assistant, page, GTK_ASSISTANT_PAGE_CONTENT); - priv->enter_or_create_page = page; - - /* fourth page (salut details) */ - if (empathy_connection_managers_get_cm (priv->connection_mgrs, "salut") - != NULL) - { - page = account_assistant_build_salut_page (self); - gtk_assistant_append_page (assistant, page); - gtk_assistant_set_page_title (assistant, page, - _("Please enter personal details")); - gtk_assistant_set_page_type (assistant, page, GTK_ASSISTANT_PAGE_CONFIRM); - - priv->create_salut_account = TRUE; - - if (empathy_account_settings_is_valid (priv->salut_settings)) - { - gtk_assistant_set_page_complete (GTK_ASSISTANT (self), - page, TRUE); - } - } - else - { - page = account_assistant_build_salut_error_page (self); - gtk_assistant_append_page (assistant, page); - gtk_assistant_set_page_title (assistant, page, _("An error occurred")); - gtk_assistant_set_page_type (assistant, page, GTK_ASSISTANT_PAGE_SUMMARY); - - priv->create_salut_account = FALSE; - } - - priv->salut_page = page; - priv->display_salut_page = TRUE; - - tp_proxy_prepare_async (priv->account_mgr, NULL, - account_mgr_prepare_cb, self); - - gtk_window_set_resizable (GTK_WINDOW (self), FALSE); -} - -GtkWidget * -empathy_account_assistant_show (GtkWindow *window, - EmpathyConnectionManagers *connection_mgrs) -{ - static GtkWidget *dialog = NULL; - - if (dialog == NULL) - { - dialog = g_object_new (EMPATHY_TYPE_ACCOUNT_ASSISTANT, - "parent-window", window, - "connection-managers", connection_mgrs, - NULL); - g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer *) &dialog); - } - - gtk_window_present (GTK_WINDOW (dialog)); - - return dialog; -} - - diff --git a/src/empathy-account-assistant.h b/src/empathy-account-assistant.h deleted file mode 100644 index d99475fe3..000000000 --- a/src/empathy-account-assistant.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2009 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> - */ - -/* empathy-account-assistant.h */ - -#ifndef __EMPATHY_ACCOUNT_ASSISTANT_H__ -#define __EMPATHY_ACCOUNT_ASSISTANT_H__ - -#include <glib-object.h> -#include <gtk/gtk.h> - -#include <libempathy/empathy-connection-managers.h> - -G_BEGIN_DECLS - -#define EMPATHY_TYPE_ACCOUNT_ASSISTANT empathy_account_assistant_get_type() -#define EMPATHY_ACCOUNT_ASSISTANT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), EMPATHY_TYPE_ACCOUNT_ASSISTANT,\ - EmpathyAccountAssistant)) -#define EMPATHY_ACCOUNT_ASSISTANT_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), EMPATHY_TYPE_ACCOUNT_ASSISTANT,\ - EmpathyAccountAssistantClass)) -#define EMPATHY_IS_ACCOUNT_ASSISTANT(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EMPATHY_TYPE_ACCOUNT_ASSISTANT)) -#define EMPATHY_IS_ACCOUNT_ASSISTANT_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), EMPATHY_TYPE_ACCOUNT_ASSISTANT)) -#define EMPATHY_ACCOUNT_ASSISTANT_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_ACCOUNT_ASSISTANT,\ - EmpathyAccountAssistantClass)) - -typedef struct { - GtkAssistant parent; - - /* private */ - gpointer priv; -} EmpathyAccountAssistant; - -typedef struct { - GtkAssistantClass parent_class; -} EmpathyAccountAssistantClass; - -GType empathy_account_assistant_get_type (void); - -GtkWidget *empathy_account_assistant_show (GtkWindow *parent, - EmpathyConnectionManagers *connection_mgrs); - -G_END_DECLS - -#endif /* __EMPATHY_ACCOUNT_ASSISTANT_H__ */ diff --git a/src/empathy-accounts-common.c b/src/empathy-accounts-common.c index 88d14c321..f6bb40278 100644 --- a/src/empathy-accounts-common.c +++ b/src/empathy-accounts-common.c @@ -41,8 +41,6 @@ #include "empathy-accounts-common.h" #include "empathy-accounts-dialog.h" -#include "empathy-account-assistant.h" -#include "empathy-auto-salut-account-helper.h" #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT #include <libempathy/empathy-debug.h> @@ -82,13 +80,16 @@ empathy_accounts_has_accounts (TpAccountManager *manager) return has_accounts; } -static void -do_show_accounts_ui (TpAccountManager *manager, +void +empathy_accounts_show_accounts_ui (TpAccountManager *manager, TpAccount *account, GApplication *app) { static GtkWidget *accounts_window = NULL; + g_return_if_fail (TP_IS_ACCOUNT_MANAGER (manager)); + g_return_if_fail (!account || TP_IS_ACCOUNT (account)); + if (accounts_window == NULL) { accounts_window = empathy_accounts_dialog_show (NULL, account); @@ -99,63 +100,3 @@ do_show_accounts_ui (TpAccountManager *manager, gtk_window_present (GTK_WINDOW (accounts_window)); } - -static GtkWidget * -show_account_assistant (EmpathyConnectionManagers *connection_mgrs, - GApplication *app) -{ - GtkWidget *assistant; - - assistant = empathy_account_assistant_show (NULL, connection_mgrs); - - gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (assistant)); - - return assistant; -} - -static void -connection_managers_prepare_for_accounts (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - EmpathyConnectionManagers *cm_mgr = EMPATHY_CONNECTION_MANAGERS (source); - GApplication *app = user_data; - - if (!empathy_connection_managers_prepare_finish (cm_mgr, result, NULL)) - goto out; - - show_account_assistant (cm_mgr, app); - DEBUG ("would show the account assistant"); - -out: - g_object_unref (cm_mgr); - g_application_release (app); -} - -void -empathy_accounts_show_accounts_ui (TpAccountManager *manager, - TpAccount *account, - gboolean assistant, - GApplication *app) -{ - g_return_if_fail (TP_IS_ACCOUNT_MANAGER (manager)); - g_return_if_fail (!account || TP_IS_ACCOUNT (account)); - - if ((empathy_accounts_has_non_salut_accounts (manager) && !assistant) || - account != NULL) - { - do_show_accounts_ui (manager, account, app); - } - else - { - EmpathyConnectionManagers *cm_mgr; - - cm_mgr = empathy_connection_managers_dup_singleton (); - - /* Hold the application while preparing cm_mgr */ - g_application_hold (app); - - empathy_connection_managers_prepare_async (cm_mgr, - connection_managers_prepare_for_accounts, app); - } -} diff --git a/src/empathy-accounts-common.h b/src/empathy-accounts-common.h index f9294fbd2..39d094d22 100644 --- a/src/empathy-accounts-common.h +++ b/src/empathy-accounts-common.h @@ -28,7 +28,6 @@ gboolean empathy_accounts_has_accounts (TpAccountManager *manager); void empathy_accounts_show_accounts_ui (TpAccountManager *manager, TpAccount *account, - gboolean assistant, GApplication *app); #endif /* __EMPATHY_ACCOUNTS_COMMON_H__ */ diff --git a/src/empathy-accounts-dialog.c b/src/empathy-accounts-dialog.c index 9e2fea781..b2b57dec1 100644 --- a/src/empathy-accounts-dialog.c +++ b/src/empathy-accounts-dialog.c @@ -21,6 +21,7 @@ * Xavier Claessens <xclaesse@gmail.com> * Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> * Jonathan Tellier <jonathan.tellier@gmail.com> + * Danielle Madeley <danielle.madeley@collabora.co.uk> */ #include <config.h> @@ -31,6 +32,7 @@ #include <gtk/gtk.h> #include <glib/gi18n-lib.h> #include <dbus/dbus-glib.h> +#include <gio/gdesktopappinfo.h> #include <telepathy-glib/account-manager.h> #include <telepathy-glib/defs.h> @@ -39,6 +41,7 @@ #include <libempathy/empathy-utils.h> #include <libempathy/empathy-connection-managers.h> #include <libempathy/empathy-connectivity.h> +#include <libempathy/empathy-tp-contact-factory.h> #include <libempathy-gtk/empathy-ui-utils.h> #include <libempathy-gtk/empathy-protocol-chooser.h> @@ -46,8 +49,12 @@ #include <libempathy-gtk/empathy-account-widget-irc.h> #include <libempathy-gtk/empathy-account-widget-sip.h> #include <libempathy-gtk/empathy-cell-renderer-activatable.h> +#include <libempathy-gtk/empathy-contact-widget.h> #include <libempathy-gtk/empathy-images.h> +#include <libempathy-gtk/empathy-local-xmpp-assistant-widget.h> +#include <libempathy-gtk/empathy-new-account-dialog.h> +#include "empathy-accounts-common.h" #include "empathy-accounts-dialog.h" #include "empathy-import-dialog.h" #include "empathy-import-utils.h" @@ -73,7 +80,8 @@ G_DEFINE_TYPE (EmpathyAccountsDialog, empathy_accounts_dialog, GTK_TYPE_DIALOG); enum { NOTEBOOK_PAGE_ACCOUNT = 0, - NOTEBOOK_PAGE_LOADING + NOTEBOOK_PAGE_LOADING, + NOTEBOOK_PAGE_NO_PROTOCOL }; typedef struct { @@ -86,7 +94,6 @@ typedef struct { GtkWidget *image_status; GtkWidget *throbber; GtkWidget *enabled_switch; - GtkWidget *frame_no_protocol; GtkWidget *treeview; @@ -94,13 +101,10 @@ typedef struct { GtkWidget *button_remove; GtkWidget *button_import; - GtkWidget *combobox_protocol; - GtkWidget *hbox_protocol; - GtkWidget *image_type; GtkWidget *label_name; GtkWidget *label_type; - GtkWidget *settings_widget; + GtkWidget *dialog_content; GtkWidget *notebook_account; GtkWidget *spinner; @@ -157,21 +161,13 @@ enum { static EmpathyAccountSettings * accounts_dialog_model_get_selected_settings ( EmpathyAccountsDialog *dialog); -static gboolean accounts_dialog_get_settings_iter ( - EmpathyAccountsDialog *dialog, - EmpathyAccountSettings *settings, - GtkTreeIter *iter); - static void accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog); static void accounts_dialog_update_settings (EmpathyAccountsDialog *dialog, EmpathyAccountSettings *settings); -static void accounts_dialog_add (EmpathyAccountsDialog *dialog, - EmpathyAccountSettings *settings); - static void accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog, - EmpathyAccountSettings *settings); + TpAccount *account); static void accounts_dialog_connection_changed_cb (TpAccount *account, guint old_status, @@ -191,6 +187,9 @@ static void accounts_dialog_model_selection_changed ( GtkTreeSelection *selection, EmpathyAccountsDialog *dialog); +static gboolean accounts_dialog_has_pending_change ( + EmpathyAccountsDialog *dialog, TpAccount **account); + static void accounts_dialog_update_name_label (EmpathyAccountsDialog *dialog, const gchar *display_name) @@ -213,6 +212,9 @@ accounts_dialog_status_infobar_set_message (EmpathyAccountsDialog *dialog, message_markup = g_markup_printf_escaped ("<i>%s</i>", message); gtk_label_set_markup (GTK_LABEL (priv->label_status), message_markup); + + gtk_widget_set_tooltip_text (priv->label_status, message); + g_free (message_markup); } @@ -497,28 +499,6 @@ empathy_account_dialog_widget_cancelled_cb ( empathy_account_dialog_cancel (dialog); } -static void -empathy_account_dialog_account_created_cb (EmpathyAccountWidget *widget_object, - TpAccount *account, - EmpathyAccountsDialog *dialog) -{ - EmpathyAccountSettings *settings = - accounts_dialog_model_get_selected_settings (dialog); - EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); - - accounts_dialog_update_settings (dialog, settings); - accounts_dialog_update_status_infobar (dialog, - empathy_account_settings_get_account (settings)); - - gtk_widget_set_sensitive (priv->treeview, TRUE); - gtk_widget_set_sensitive (priv->button_add, TRUE); - gtk_widget_set_sensitive (priv->button_remove, TRUE); - gtk_widget_set_sensitive (priv->button_import, TRUE); - - if (settings) - g_object_unref (settings); -} - static gboolean accounts_dialog_has_valid_accounts (EmpathyAccountsDialog *dialog) { @@ -542,19 +522,21 @@ accounts_dialog_has_valid_accounts (EmpathyAccountsDialog *dialog) } static void -account_dialog_create_settings_widget (EmpathyAccountsDialog *dialog, - EmpathyAccountSettings *settings) +account_dialog_create_edit_params_dialog (EmpathyAccountsDialog *dialog) { EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); - const gchar *icon_name; - TpAccount *account; + EmpathyAccountSettings *settings; + GtkWidget *subdialog, *content, *content_area, *align; - if (priv->setting_widget_object != NULL) - g_object_remove_weak_pointer (G_OBJECT (priv->setting_widget_object), - (gpointer *) &priv->setting_widget_object); + settings = accounts_dialog_model_get_selected_settings (dialog); + + subdialog = gtk_dialog_new_with_buttons (_("Edit Connection Parameters"), + GTK_WINDOW (dialog), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, NULL); priv->setting_widget_object = - empathy_account_widget_new_for_protocol (settings, FALSE); + empathy_account_widget_new_for_protocol (settings, FALSE); g_object_add_weak_pointer (G_OBJECT (priv->setting_widget_object), (gpointer *) &priv->setting_widget_object); @@ -563,17 +545,250 @@ account_dialog_create_settings_widget (EmpathyAccountsDialog *dialog, empathy_account_widget_set_other_accounts_exist ( priv->setting_widget_object, TRUE); - priv->settings_widget = - empathy_account_widget_get_widget (priv->setting_widget_object); + content = empathy_account_widget_get_widget (priv->setting_widget_object); - g_signal_connect (priv->setting_widget_object, "account-created", - G_CALLBACK (empathy_account_dialog_account_created_cb), dialog); g_signal_connect (priv->setting_widget_object, "cancelled", G_CALLBACK (empathy_account_dialog_widget_cancelled_cb), dialog); + g_signal_connect_swapped (priv->setting_widget_object, "close", + G_CALLBACK (gtk_widget_destroy), subdialog); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (subdialog)); + + align = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_alignment_set_padding (GTK_ALIGNMENT (align), 6, 0, 6, 6); + + gtk_container_add (GTK_CONTAINER (align), content); + gtk_box_pack_start (GTK_BOX (content_area), align, TRUE, TRUE, 0); + + gtk_widget_show (content); + gtk_widget_show (align); + gtk_widget_show (subdialog); +} + +static void +start_external_app (GAppInfo *app_info) +{ + GError *error = NULL; + GdkAppLaunchContext *context = NULL; + GdkDisplay *display; + + display = gdk_display_get_default (); + context = gdk_display_get_app_launch_context (display); + + if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context, + &error)) + { + g_critical ("Failed to bisho: %s", error->message); + g_clear_error (&error); + } + + tp_clear_object (&context); +} + +static void +use_external_storage_provider (EmpathyAccountsDialog *self, + TpAccount *account) +{ + const gchar *provider; + + provider = tp_account_get_storage_provider (account); + if (!tp_strdiff (provider, "com.meego.libsocialweb")) + { + GDesktopAppInfo *desktop_info; + gchar *cmd; + GAppInfo *app_info; + GError *error = NULL; + + desktop_info = g_desktop_app_info_new ("gnome-control-center.desktop"); + if (desktop_info == NULL) + { + g_critical ("Could not locate 'gnome-control-center.desktop'"); + return; + } + + /* glib doesn't have API to start a desktop file with args... (#637875) */ + cmd = g_strdup_printf ("%s bisho.desktop", g_app_info_get_commandline ( + (GAppInfo *) desktop_info)); + + app_info = g_app_info_create_from_commandline (cmd, NULL, 0, &error); + + if (app_info == NULL) + { + DEBUG ("Failed to create app info: %s", error->message); + g_error_free (error); + } + else + { + start_external_app (app_info); + g_object_unref (app_info); + } + + g_object_unref (desktop_info); + g_free (cmd); + return; + } + else if (!tp_strdiff (provider, "org.gnome.OnlineAccounts")) + { + GDesktopAppInfo *desktop_info; + + desktop_info = g_desktop_app_info_new ( + "gnome-online-accounts-panel.desktop"); + if (desktop_info == NULL) + { + g_critical ("Could not locate 'gnome-online-accounts-panel.desktop'"); + } + else + { + start_external_app (G_APP_INFO (desktop_info)); + g_object_unref (desktop_info); + } + + return; + } + else + { + DEBUG ("Don't know how to handle %s", provider); + return; + } +} + +static void +account_dialow_show_edit_params_dialog (EmpathyAccountsDialog *dialog, + GtkButton *button) +{ + EmpathyAccountSettings *settings; + TpAccount *account; + TpStorageRestrictionFlags storage_restrictions; + + settings = accounts_dialog_model_get_selected_settings (dialog); + + account = empathy_account_settings_get_account (settings); + g_return_if_fail (account != NULL); + + storage_restrictions = tp_account_get_storage_restrictions (account); + + /* Empathy can only edit accounts without the Cannot_Set_Parameters flag */ + if (storage_restrictions & TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS) + { + DEBUG ("Account is provided by an external storage provider"); + + use_external_storage_provider (dialog, account); + } + else + { + account_dialog_create_edit_params_dialog (dialog); + } +} + +static void +account_dialog_show_contact_details_failed (EmpathyAccountsDialog *dialog, + gboolean error) +{ + EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); + GtkWidget *infobar, *label; + + infobar = gtk_info_bar_new (); + + if (error) + { + gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_ERROR); + label = gtk_label_new (_("Failed to retrieve your personal information " + "from the server.")); + } + else + { + gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_INFO); + label = gtk_label_new (_("Go online to edit your personal information.")); + } + + gtk_container_add ( + GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar))), + label); + gtk_box_pack_start (GTK_BOX (priv->dialog_content), infobar, FALSE, FALSE, 0); + gtk_widget_show_all (infobar); +} + +static void +account_dialog_got_self_contact (TpConnection *conn, + EmpathyContact *contact, + const GError *in_error, + gpointer user_data, + GObject *dialog) +{ + EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); + GtkWidget *editor, *alig; + + if (in_error != NULL) + { + DEBUG ("Failed to get self-contact: %s", in_error->message); + account_dialog_show_contact_details_failed ( + EMPATHY_ACCOUNTS_DIALOG (dialog), TRUE); + return; + } + + alig = gtk_alignment_new (0.5, 0, 1, 1); + + /* create the contact info editor for this account */ + editor = empathy_contact_widget_new (contact, + EMPATHY_CONTACT_WIDGET_EDIT_ALIAS | + EMPATHY_CONTACT_WIDGET_EDIT_AVATAR | + EMPATHY_CONTACT_WIDGET_NO_STATUS | + EMPATHY_CONTACT_WIDGET_EDIT_DETAILS); + + gtk_box_pack_start (GTK_BOX (priv->dialog_content), alig, TRUE, TRUE, 0); + gtk_container_add (GTK_CONTAINER (alig), editor); + gtk_widget_show (alig); + gtk_widget_show (editor); +} + +static void +account_dialog_create_dialog_content (EmpathyAccountsDialog *dialog, + EmpathyAccountSettings *settings) +{ + EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); + const gchar *icon_name; + TpAccount *account; + TpConnection *conn = NULL; + GtkWidget *bbox, *button; + + account = empathy_account_settings_get_account (settings); + + // if (priv->setting_widget_object != NULL) + // g_object_remove_weak_pointer (G_OBJECT (priv->setting_widget_object), + // (gpointer *) &priv->setting_widget_object); + + priv->dialog_content = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_add (GTK_CONTAINER (priv->alignment_settings), - priv->settings_widget); - gtk_widget_show (priv->settings_widget); + priv->dialog_content); + gtk_widget_show (priv->dialog_content); + + /* request the self contact */ + if (account != NULL) + conn = tp_account_get_connection (account); + + if (conn != NULL) + { + empathy_tp_contact_factory_get_from_handle (conn, + tp_connection_get_self_handle (conn), + account_dialog_got_self_contact, + NULL, NULL, G_OBJECT (dialog)); + } + else + { + account_dialog_show_contact_details_failed (dialog, FALSE); + } + + bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); + gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END); + gtk_box_pack_end (GTK_BOX (priv->dialog_content), bbox, FALSE, TRUE, 0); + gtk_widget_show (bbox); + + button = gtk_button_new_with_mnemonic (_("_Edit Connection Parameters...")); + gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 0); + gtk_widget_show (button); + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (account_dialow_show_edit_params_dialog), dialog); icon_name = empathy_account_settings_get_icon_name (settings); @@ -594,7 +809,6 @@ account_dialog_create_settings_widget (EmpathyAccountsDialog *dialog, accounts_dialog_update_name_label (dialog, empathy_account_settings_get_display_name (settings)); - account = empathy_account_settings_get_account (settings); accounts_dialog_update_status_infobar (dialog, account); } @@ -604,7 +818,7 @@ account_dialog_settings_ready_cb (EmpathyAccountSettings *settings, EmpathyAccountsDialog *dialog) { if (empathy_account_settings_is_ready (settings)) - account_dialog_create_settings_widget (dialog, settings); + account_dialog_create_dialog_content (dialog, settings); } static void @@ -651,97 +865,6 @@ accounts_dialog_has_pending_change (EmpathyAccountsDialog *dialog, } static void -accounts_dialog_setup_ui_to_add_account (EmpathyAccountsDialog *dialog) -{ - EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); - EmpathyAccountSettings *settings; - - settings = empathy_protocol_chooser_create_account_settings ( - EMPATHY_PROTOCOL_CHOOSER (priv->combobox_protocol)); - if (settings == NULL) - return; - - accounts_dialog_add (dialog, settings); - accounts_dialog_model_set_selected (dialog, settings); - - gtk_widget_show_all (priv->hbox_protocol); - - g_object_unref (settings); -} - -static void -accounts_dialog_protocol_changed_cb (GtkWidget *widget, - EmpathyAccountsDialog *dialog) -{ - EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter iter; - gboolean creating; - EmpathyAccountSettings *settings; - gchar *account = NULL, *password = NULL; - - /* The "changed" signal is fired during the initiation of the - * EmpathyProtocolChooser while populating the widget. Such signals should - * be ignored so we check if we are actually creating a new account. */ - if (priv->setting_widget_object == NULL) - return; - - g_object_get (priv->setting_widget_object, - "creating-account", &creating, NULL); - if (!creating) - return; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview)); - - if (!gtk_tree_selection_get_selected (selection, &model, &iter)) - return; - - /* Save "account" and "password" parameters */ - g_object_get (priv->setting_widget_object, "settings", &settings, NULL); - - if (settings != NULL) - { - account = g_strdup (empathy_account_settings_get_string (settings, - "account")); - password = g_strdup (empathy_account_settings_get_string (settings, - "password")); - g_object_unref (settings); - } - - /* We are creating a new widget to replace the current one, don't ask - * confirmation to the user. */ - priv->force_change_row = TRUE; - - /* We'll update the selection after we create the new account widgets; - * updating it right now causes problems for the # of accounts = zero case */ - g_signal_handlers_block_by_func (selection, - accounts_dialog_model_selection_changed, dialog); - - gtk_list_store_remove (GTK_LIST_STORE (model), &iter); - - g_signal_handlers_unblock_by_func (selection, - accounts_dialog_model_selection_changed, dialog); - - accounts_dialog_setup_ui_to_add_account (dialog); - - /* Restore "account" and "password" parameters in the new widget */ - if (account != NULL) - { - empathy_account_widget_set_account_param (priv->setting_widget_object, - account); - g_free (account); - } - - if (password != NULL) - { - empathy_account_widget_set_password_param (priv->setting_widget_object, - password); - g_free (password); - } -} - -static void accounts_dialog_show_question_dialog (EmpathyAccountsDialog *dialog, const gchar *primary_text, const gchar *secondary_text, @@ -781,24 +904,6 @@ accounts_dialog_show_question_dialog (EmpathyAccountsDialog *dialog, gtk_widget_show (message_dialog); } -static void -accounts_dialog_add_pending_changes_response_cb (GtkDialog *message_dialog, - gint response_id, - gpointer *user_data) -{ - EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data); - EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); - - gtk_widget_destroy (GTK_WIDGET (message_dialog)); - - if (response_id == GTK_RESPONSE_YES) - { - empathy_account_widget_discard_pending_changes ( - priv->setting_widget_object); - accounts_dialog_setup_ui_to_add_account (dialog); - } -} - static gchar * get_dialog_primary_text (TpAccount *account) { @@ -817,37 +922,32 @@ get_dialog_primary_text (TpAccount *account) static void accounts_dialog_button_add_clicked_cb (GtkWidget *button, - EmpathyAccountsDialog *dialog) + EmpathyAccountsDialog *self) { - TpAccount *account = NULL; - EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); + GtkWidget *dialog; + gint response; - if (accounts_dialog_has_pending_change (dialog, &account)) - { - gchar *question_dialog_primary_text = get_dialog_primary_text (account); + dialog = empathy_new_account_dialog_new (GTK_WINDOW (self)); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); - accounts_dialog_show_question_dialog (dialog, - question_dialog_primary_text, - _("You are about to create a new account, which will discard\n" - "your changes. Are you sure you want to proceed?"), - G_CALLBACK (accounts_dialog_add_pending_changes_response_cb), - dialog, - GTK_STOCK_CANCEL, GTK_RESPONSE_NO, - GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL); + response = gtk_dialog_run (GTK_DIALOG (dialog)); - g_free (question_dialog_primary_text); - } - else + if (response == GTK_RESPONSE_OK) { - accounts_dialog_setup_ui_to_add_account (dialog); - gtk_widget_set_sensitive (priv->treeview, FALSE); - gtk_widget_set_sensitive (priv->button_add, FALSE); - gtk_widget_set_sensitive (priv->button_remove, FALSE); - gtk_widget_set_sensitive (priv->button_import, FALSE); + EmpathyAccountSettings *settings; + TpAccount *account; + + settings = empathy_new_account_dialog_get_settings ( + EMPATHY_NEW_ACCOUNT_DIALOG (dialog)); + + /* The newly created account has already been added by + * accounts_dialog_account_validity_changed_cb so we just + * have to select it. */ + account = empathy_account_settings_get_account (settings); + accounts_dialog_model_set_selected (self, account); } - if (account != NULL) - g_object_unref (account); + gtk_widget_destroy (dialog); } static void @@ -883,38 +983,29 @@ accounts_dialog_update_settings (EmpathyAccountsDialog *dialog, return; } - if (empathy_connection_managers_get_cms_num (priv->cms) > 0) - { - /* We have no account configured but we have some - * profiles installed. The user obviously wants to add - * an account. Click on the Add button for him. */ - accounts_dialog_button_add_clicked_cb (priv->button_add, - dialog); - return; - } /* No account and no profile, warn the user */ gtk_widget_hide (priv->vbox_details); - gtk_widget_show (priv->frame_no_protocol); gtk_widget_set_sensitive (priv->button_add, FALSE); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook_account), + NOTEBOOK_PAGE_NO_PROTOCOL); return; } /* We have an account selected, destroy old settings and create a new * one for the account selected */ - gtk_widget_hide (priv->frame_no_protocol); gtk_widget_show (priv->vbox_details); - gtk_widget_hide (priv->hbox_protocol); - if (priv->settings_widget) + if (priv->dialog_content) { - gtk_widget_destroy (priv->settings_widget); - priv->settings_widget = NULL; + gtk_widget_destroy (priv->dialog_content); + priv->dialog_content = NULL; } if (empathy_account_settings_is_ready (settings)) { - account_dialog_create_settings_widget (dialog, settings); + account_dialog_create_dialog_content (dialog, settings); } else { @@ -1484,41 +1575,6 @@ accounts_dialog_model_setup (EmpathyAccountsDialog *dialog) } static gboolean -accounts_dialog_get_settings_iter (EmpathyAccountsDialog *dialog, - EmpathyAccountSettings *settings, - GtkTreeIter *iter) -{ - GtkTreeView *view; - GtkTreeModel *model; - gboolean ok; - EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); - - /* Update the status in the model */ - view = GTK_TREE_VIEW (priv->treeview); - model = gtk_tree_view_get_model (view); - - for (ok = gtk_tree_model_get_iter_first (model, iter); - ok; - ok = gtk_tree_model_iter_next (model, iter)) - { - EmpathyAccountSettings *this_settings; - gboolean equal; - - gtk_tree_model_get (model, iter, - COL_ACCOUNT_SETTINGS, &this_settings, - -1); - - equal = (this_settings == settings); - g_object_unref (this_settings); - - if (equal) - return TRUE; - } - - return FALSE; -} - -static gboolean accounts_dialog_get_account_iter (EmpathyAccountsDialog *dialog, TpAccount *account, GtkTreeIter *iter) @@ -1536,15 +1592,15 @@ accounts_dialog_get_account_iter (EmpathyAccountsDialog *dialog, ok; ok = gtk_tree_model_iter_next (model, iter)) { - EmpathyAccountSettings *settings; + TpAccount *this_account; gboolean equal; gtk_tree_model_get (model, iter, - COL_ACCOUNT_SETTINGS, &settings, + COL_ACCOUNT, &this_account, -1); - equal = empathy_account_settings_has_account (settings, account); - g_object_unref (settings); + equal = (this_account == account); + g_object_unref (this_account); if (equal) return TRUE; @@ -1577,11 +1633,11 @@ select_and_scroll_to_iter (EmpathyAccountsDialog *dialog, static void accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog, - EmpathyAccountSettings *settings) + TpAccount *account) { GtkTreeIter iter; - if (accounts_dialog_get_settings_iter (dialog, settings, &iter)) + if (accounts_dialog_get_account_iter (dialog, account, &iter)) select_and_scroll_to_iter (dialog, &iter); } @@ -1680,27 +1736,6 @@ finally: } static void -accounts_dialog_add (EmpathyAccountsDialog *dialog, - EmpathyAccountSettings *settings) -{ - GtkTreeModel *model; - GtkTreeIter iter; - const gchar *name; - EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview)); - name = empathy_account_settings_get_display_name (settings); - - gtk_list_store_append (GTK_LIST_STORE (model), &iter); - - gtk_list_store_set (GTK_LIST_STORE (model), &iter, - COL_NAME, name, - COL_STATUS, TP_CONNECTION_STATUS_DISCONNECTED, - COL_ACCOUNT_SETTINGS, settings, - -1); -} - -static void accounts_dialog_connection_changed_cb (TpAccount *account, guint old_status, guint current, @@ -1824,6 +1859,7 @@ accounts_dialog_add_account (EmpathyAccountsDialog *dialog, const gchar *name; EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); gboolean selected = FALSE; + GtkTreeSelection *selection; model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview)); status = tp_account_get_connection_status (account, NULL); @@ -1831,15 +1867,18 @@ accounts_dialog_add_account (EmpathyAccountsDialog *dialog, settings = empathy_account_settings_new_for_account (account); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview)); + if (!accounts_dialog_get_account_iter (dialog, account, &iter)) { + /* Select the account if it's the first added */ + if (gtk_tree_selection_count_selected_rows (selection) == 0) + selected = TRUE; + gtk_list_store_append (GTK_LIST_STORE (model), &iter); } else { - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview)); selected = gtk_tree_selection_iter_is_selected (selection, &iter); } @@ -1879,31 +1918,12 @@ accounts_dialog_add_account (EmpathyAccountsDialog *dialog, } static void -account_prepare_cb (GObject *source_object, - GAsyncResult *result, - gpointer user_data) -{ - EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data); - TpAccount *account = TP_ACCOUNT (source_object); - GError *error = NULL; - - if (!tp_proxy_prepare_finish (account, result, &error)) - { - DEBUG ("Failed to prepare account: %s", error->message); - g_error_free (error); - return; - } - - accounts_dialog_add_account (dialog, account); -} - -static void accounts_dialog_account_validity_changed_cb (TpAccountManager *manager, TpAccount *account, gboolean valid, EmpathyAccountsDialog *dialog) { - tp_proxy_prepare_async (account, NULL, account_prepare_cb, dialog); + accounts_dialog_add_account (dialog, account); } static void @@ -1982,15 +2002,24 @@ accounts_dialog_account_enabled_cb (TpAccountManager *manager, enable_or_disable_account (dialog, account, TRUE); } -static void -accounts_dialog_button_import_clicked_cb (GtkWidget *button, - EmpathyAccountsDialog *dialog) +static GtkWidget * +display_import_dialog (EmpathyAccountsDialog *dialog) { + EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); GtkWidget *import_dialog; import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog), - FALSE); + FALSE, priv->cms); gtk_widget_show (import_dialog); + + return import_dialog; +} + +static void +accounts_dialog_button_import_clicked_cb (GtkWidget *button, + EmpathyAccountsDialog *dialog) +{ + display_import_dialog (dialog); } static void @@ -2026,6 +2055,85 @@ accounts_dialog_set_selected_account (EmpathyAccountsDialog *dialog, } static void +salut_valid_cb (GtkWidget *widget, + gboolean valid, + GtkWidget *button) +{ + gtk_widget_set_sensitive (button, valid); +} + +static void +maybe_show_salut_dialog (EmpathyAccountsDialog *self) +{ + EmpathyAccountsDialogPriv *priv = GET_PRIV (self); + GtkWidget *dialog, *widget, *content, *button; + gint response; + + if (!empathy_local_xmpp_assistant_widget_should_create_account ( + priv->account_manager)) + return; + + widget = empathy_local_xmpp_assistant_widget_new (); + gtk_widget_show (widget); + + dialog = gtk_dialog_new (); + + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + + gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Skip"), + GTK_RESPONSE_NO); + + button = gtk_dialog_add_button (GTK_DIALOG (dialog), + _("_Connect"), GTK_RESPONSE_YES); + gtk_widget_set_sensitive (button, FALSE); + + g_signal_connect (widget, "valid", G_CALLBACK (salut_valid_cb), + button); + + content = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + gtk_box_pack_start (GTK_BOX (content), widget, TRUE, TRUE, 0); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (response == GTK_RESPONSE_YES) + { + empathy_local_xmpp_assistant_widget_create_account ( + EMPATHY_LOCAL_XMPP_ASSISTANT_WIDGET (widget)); + } + + gtk_widget_destroy (dialog); +} + +static void +import_dialog_response_cb (GtkDialog *dialog, + gint response_id, + EmpathyAccountsDialog *self) +{ + maybe_show_salut_dialog (self); +} + +static void +maybe_show_import_dialog (EmpathyAccountsDialog *self) +{ + EmpathyAccountsDialogPriv *priv = GET_PRIV (self); + GtkWidget *dialog; + + if (empathy_accounts_has_non_salut_accounts (priv->account_manager)) + return; + + if (!empathy_import_accounts_to_import ()) + { + maybe_show_salut_dialog (self); + return; + } + + dialog = display_import_dialog (self); + + tp_g_signal_connect_object (dialog, "response", + G_CALLBACK (import_dialog_response_cb), self, 0); +} + +static void finished_loading (EmpathyAccountsDialog *self) { EmpathyAccountsDialogPriv *priv = GET_PRIV (self); @@ -2046,6 +2154,8 @@ finished_loading (EmpathyAccountsDialog *self) gtk_spinner_stop (GTK_SPINNER (priv->spinner)); gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook_account), NOTEBOOK_PAGE_ACCOUNT); + + maybe_show_import_dialog (self); } static void @@ -2188,14 +2298,12 @@ accounts_dialog_build_ui (EmpathyAccountsDialog *dialog) gui = empathy_builder_get_file (filename, "accounts_dialog_hbox", &top_hbox, "vbox_details", &priv->vbox_details, - "frame_no_protocol", &priv->frame_no_protocol, "alignment_settings", &priv->alignment_settings, "alignment_infobar", &priv->alignment_infobar, "treeview", &priv->treeview, "button_add", &priv->button_add, "button_remove", &priv->button_remove, "button_import", &priv->button_import, - "hbox_protocol", &priv->hbox_protocol, "notebook_account", &priv->notebook_account, "alignment_loading", &alig, "accounts_sw", &sw, @@ -2203,8 +2311,6 @@ accounts_dialog_build_ui (EmpathyAccountsDialog *dialog) NULL); g_free (filename); - gtk_widget_set_no_show_all (priv->frame_no_protocol, TRUE); - empathy_builder_connect (gui, dialog, "button_add", "clicked", accounts_dialog_button_add_clicked_cb, "button_remove", "clicked", accounts_dialog_button_remove_clicked_cb, @@ -2248,13 +2354,6 @@ accounts_dialog_build_ui (EmpathyAccountsDialog *dialog) gtk_widget_set_sensitive (priv->button_import, FALSE); gtk_widget_set_sensitive (priv->treeview, FALSE); - priv->combobox_protocol = empathy_protocol_chooser_new (); - gtk_box_pack_start (GTK_BOX (priv->hbox_protocol), priv->combobox_protocol, - TRUE, TRUE, 0); - g_signal_connect (priv->combobox_protocol, "changed", - G_CALLBACK (accounts_dialog_protocol_changed_cb), - dialog); - if (priv->parent_window) gtk_window_set_transient_for (GTK_WINDOW (dialog), priv->parent_window); @@ -2293,7 +2392,7 @@ accounts_dialog_build_ui (EmpathyAccountsDialog *dialog) TP_CONNECTION_PRESENCE_TYPE_OFFLINE), GTK_ICON_SIZE_SMALL_TOOLBAR); priv->label_status = gtk_label_new (NULL); - gtk_label_set_line_wrap (GTK_LABEL (priv->label_status), TRUE); + gtk_label_set_ellipsize (GTK_LABEL (priv->label_status), PANGO_ELLIPSIZE_END); gtk_box_pack_start (GTK_BOX (hbox), priv->throbber, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), priv->image_status, FALSE, FALSE, 0); diff --git a/src/empathy-accounts-dialog.ui b/src/empathy-accounts-dialog.ui index 4a1b8bbb3..549a92651 100644 --- a/src/empathy-accounts-dialog.ui +++ b/src/empathy-accounts-dialog.ui @@ -112,31 +112,6 @@ <property name="visible">True</property> <property name="spacing">18</property> <child> - <object class="GtkHBox" id="hbox_protocol"> - <property name="visible">True</property> - <property name="spacing">6</property> - <child> - <object class="GtkLabel" id="label_protocol"> - <property name="visible">True</property> - <property name="label" translatable="yes">Protocol:</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> <object class="GtkVBox" id="vbox_details"> <property name="visible">True</property> <property name="spacing">18</property> @@ -173,43 +148,6 @@ <property name="position">1</property> </packing> </child> - <child> - <object class="GtkFrame" id="frame_no_protocol"> - <property name="label_xalign">0</property> - <property name="shadow_type">none</property> - <child> - <object class="GtkAlignment" id="alignment21"> - <property name="visible">True</property> - <property name="top_padding">6</property> - <property name="left_padding">12</property> - <child> - <object class="GtkLabel" id="label4"> - <property name="visible">True</property> - <property name="label" translatable="yes">To add a new account, you first have to install a backend for each protocol you want to use.</property> - <property name="wrap">True</property> - </object> - </child> - </object> - </child> - <child type="label"> - <object class="GtkLabel" id="label_no_protocol"> - <property name="visible">True</property> - <property name="label" translatable="yes">No protocol installed</property> - <attributes> - <attribute name="weight" value="bold"/> - </attributes> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - <child> - <placeholder/> - </child> </object> </child> <child type="tab"> @@ -254,6 +192,49 @@ <property name="position">1</property> </packing> </child> + <child> + <object class="GtkVBox" id="vbox_no_protocol"> + <property name="visible">True</property> + <property name="spacing">18</property> + <child> + <object class="GtkFrame" id="frame_no_protocol"> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment21"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="label" translatable="yes">To add a new account, you first have to install a backend for each protocol you want to use.</property> + <property name="wrap">True</property> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label_no_protocol"> + <property name="visible">True</property> + <property name="label" translatable="yes">No protocol backends installed</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </object> + </child> </object> <packing> <property name="position">1</property> diff --git a/src/empathy-accounts.c b/src/empathy-accounts.c index 3b25201c5..6b51820f7 100644 --- a/src/empathy-accounts.c +++ b/src/empathy-accounts.c @@ -43,8 +43,6 @@ #include "empathy-accounts.h" #include "empathy-accounts-common.h" #include "empathy-accounts-dialog.h" -#include "empathy-account-assistant.h" -#include "empathy-auto-salut-account-helper.h" #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT #include <libempathy/empathy-debug.h> @@ -54,7 +52,6 @@ static gboolean only_if_needed = FALSE; static gboolean hidden = FALSE; static gchar *selected_account_name = NULL; -static gboolean assistant = FALSE; static void maybe_show_accounts_ui (TpAccountManager *manager, @@ -66,7 +63,7 @@ maybe_show_accounts_ui (TpAccountManager *manager, if (only_if_needed && empathy_accounts_has_non_salut_accounts (manager)) return; - empathy_accounts_show_accounts_ui (manager, NULL, assistant, app); + empathy_accounts_show_accounts_ui (manager, NULL, app); } static TpAccount * @@ -123,7 +120,7 @@ account_manager_ready_for_accounts_cb (GObject *source_object, if (account != NULL) { - empathy_accounts_show_accounts_ui (manager, account, assistant, app); + empathy_accounts_show_accounts_ui (manager, account, app); goto out; } else @@ -191,10 +188,6 @@ local_cmdline (GApplication *app, N_("Initially select given account (eg, " "gabble/jabber/foo_40example_2eorg0)"), N_("<account-id>") }, - { "assistant", 'a', - 0, G_OPTION_ARG_NONE, &assistant, - N_("Show account assistant"), - NULL }, { NULL } }; diff --git a/src/empathy-audio-sink.c b/src/empathy-audio-sink.c index 5ab14b541..fbffa4885 100644 --- a/src/empathy-audio-sink.c +++ b/src/empathy-audio-sink.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <config.h> #include <stdio.h> #include <stdlib.h> @@ -222,7 +223,10 @@ create_sink (EmpathyGstAudioSink *self) /* Set latency (buffering on the PulseAudio side) of 40ms and transfer data * in 10ms chunks */ - g_object_set (sink, "buffer-time", 40000, "latency-time", 10000, NULL); + g_object_set (sink, + "buffer-time", (gint64) 40000, + "latency-time", (gint64) 10000, + NULL); return sink; } diff --git a/src/empathy-audio-src.c b/src/empathy-audio-src.c index aa271fa1c..9a882c146 100644 --- a/src/empathy-audio-src.c +++ b/src/empathy-audio-src.c @@ -332,7 +332,7 @@ create_src (void) empathy_call_set_stream_properties (src, TRUE); /* Set latency (buffering on the PulseAudio side) of 20ms */ - g_object_set (src, "buffer-time", 20000, NULL); + g_object_set (src, "buffer-time", (gint64) 20000, NULL); return src; } diff --git a/src/empathy-auth-client.c b/src/empathy-auth-client.c index 5b3e48ce8..22a625229 100644 --- a/src/empathy-auth-client.c +++ b/src/empathy-auth-client.c @@ -231,6 +231,8 @@ auth_factory_new_sasl_handler_cb (EmpathyAuthFactory *factory, /* If the handler has the password it will deal with it itself. */ if (!empathy_server_sasl_handler_has_password (handler)) { + DEBUG ("SASL handler doesn't have a password, prompt for one"); + dialog = empathy_password_dialog_new (handler); gtk_widget_show (dialog); } diff --git a/src/empathy-auto-salut-account-helper.c b/src/empathy-auto-salut-account-helper.c deleted file mode 100644 index 409b6936c..000000000 --- a/src/empathy-auto-salut-account-helper.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2007-2010 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Xavier Claessens <xclaesse@gmail.com> - * Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> - */ - -#include <config.h> - -#include <glib.h> -#include <glib/gi18n-lib.h> - -#include <telepathy-glib/account-manager.h> -#include <telepathy-glib/util.h> - -#if HAVE_EDS -#include <libebook/e-book.h> -#endif - -#include <libempathy/empathy-account-settings.h> - -#define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT -#include <libempathy/empathy-debug.h> - -#include "empathy-auto-salut-account-helper.h" - -/* Salut account creation. The TpAccountManager first argument - * must already be prepared when calling this function. */ -gboolean -should_create_salut_account (TpAccountManager *manager) -{ - gboolean salut_created = FALSE; - GList *accounts, *l; - - accounts = tp_account_manager_get_valid_accounts (manager); - - for (l = accounts; l != NULL; l = g_list_next (l)) - { - TpAccount *account = TP_ACCOUNT (l->data); - - if (!tp_strdiff (tp_account_get_protocol (account), "local-xmpp")) - { - salut_created = TRUE; - break; - } - } - - g_list_free (accounts); - - return !salut_created; -} - -EmpathyAccountSettings * -create_salut_account_settings (void) -{ - EmpathyAccountSettings *settings; -#if HAVE_EDS - EBook *book; - EContact *contact; - gchar *nickname = NULL; - gchar *first_name = NULL; - gchar *last_name = NULL; - gchar *email = NULL; - gchar *jid = NULL; - GError *error = NULL; -#endif - - settings = empathy_account_settings_new ("salut", "local-xmpp", NULL, - _("People nearby")); - -#if HAVE_EDS - /* Get self EContact from EDS */ - if (!e_book_get_self (&contact, &book, &error)) - { - DEBUG ("Failed to get self econtact: %s", error->message); - g_error_free (error); - return settings; - } - - nickname = e_contact_get (contact, E_CONTACT_NICKNAME); - first_name = e_contact_get (contact, E_CONTACT_GIVEN_NAME); - last_name = e_contact_get (contact, E_CONTACT_FAMILY_NAME); - email = e_contact_get (contact, E_CONTACT_EMAIL_1); - jid = e_contact_get (contact, E_CONTACT_IM_JABBER_HOME_1); - - if (!tp_strdiff (nickname, "nickname")) - { - g_free (nickname); - nickname = NULL; - } - - DEBUG ("Salut account created:\nnickname=%s\nfirst-name=%s\n" - "last-name=%s\nemail=%s\njid=%s\n", - nickname, first_name, last_name, email, jid); - - empathy_account_settings_set_string (settings, - "nickname", nickname ? nickname : ""); - empathy_account_settings_set_string (settings, - "first-name", first_name ? first_name : ""); - empathy_account_settings_set_string (settings, - "last-name", last_name ? last_name : ""); - empathy_account_settings_set_string (settings, "email", email ? email : ""); - empathy_account_settings_set_string (settings, "jid", jid ? jid : ""); - - g_free (nickname); - g_free (first_name); - g_free (last_name); - g_free (email); - g_free (jid); - g_object_unref (contact); - g_object_unref (book); -#endif - - return settings; -} diff --git a/src/empathy-auto-salut-account-helper.h b/src/empathy-auto-salut-account-helper.h deleted file mode 100644 index f0409d01d..000000000 --- a/src/empathy-auto-salut-account-helper.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2007-2010 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Xavier Claessens <xclaesse@gmail.com> - * Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> - */ - -/* Helper functions to automatically create a Salut account */ - -#ifndef __AUTO_SALUT_ACCOUNT_HELPER_H__ -#define __AUTO_SALUT_ACCOUNT_HELPER_H__ - -#include <telepathy-glib/account-manager.h> - -#include <libempathy/empathy-connection-managers.h> -#include <libempathy/empathy-account-settings.h> - -gboolean should_create_salut_account (TpAccountManager *manager); - -EmpathyAccountSettings * create_salut_account_settings (void); - -#endif diff --git a/src/empathy-call-window.c b/src/empathy-call-window.c index 7a4994c93..451722839 100644 --- a/src/empathy-call-window.c +++ b/src/empathy-call-window.c @@ -439,8 +439,11 @@ create_video_output_widget (EmpathyCallWindow *self) clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (priv->video_output), TRUE); - priv->video_output_sink = clutter_gst_video_sink_new ( - CLUTTER_TEXTURE (priv->video_output)); + priv->video_output_sink = gst_element_factory_make ("cluttersink", NULL); + if (priv->video_output_sink == NULL) + g_error ("Missing cluttersink"); + else + g_object_set (priv->video_output_sink, "texture", priv->video_output, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (priv->video_box), priv->video_output); @@ -1113,8 +1116,12 @@ create_video_preview (EmpathyCallWindow *self) preview = empathy_rounded_texture_new (); clutter_actor_set_size (preview, SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGHT); - priv->video_preview_sink = clutter_gst_video_sink_new ( - CLUTTER_TEXTURE (preview)); + + priv->video_preview_sink = gst_element_factory_make ("cluttersink", NULL); + if (priv->video_preview_sink == NULL) + g_error ("Missing cluttersink"); + else + g_object_set (priv->video_preview_sink, "texture", preview, NULL); /* Add a little offset to the video preview */ layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, @@ -3365,7 +3372,7 @@ empathy_call_window_src_added_cb (EmpathyCallHandler *handler, G_CALLBACK (empathy_call_window_video_probe_cb), self); if (priv->got_video_src > 0) g_source_remove (priv->got_video_src); - priv->got_video_src = g_timeout_add_seconds (5, + priv->got_video_src = g_timeout_add_seconds (1, empathy_call_window_check_video_cb, self); break; default: diff --git a/src/empathy-call.c b/src/empathy-call.c index 2ff49a81b..71d9db707 100644 --- a/src/empathy-call.c +++ b/src/empathy-call.c @@ -30,6 +30,10 @@ #include <clutter-gtk/clutter-gtk.h> #include <clutter-gst/clutter-gst.h> +#ifdef CLUTTER_WINDOWING_X11 +#include <X11/Xlib.h> +#endif + #include <telepathy-glib/debug-sender.h> #include <telepathy-yell/telepathy-yell.h> @@ -187,6 +191,12 @@ main (int argc, /* Init */ g_thread_init (NULL); +#ifdef GDK_WINDOWING_X11 + /* We can't call clutter_gst_init() before gtk_clutter_init(), so no choice + * but to intiialise X11 threading ourself */ + XInitThreads (); +#endif + /* Clutter needs this */ gdk_disable_multidevice (); diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index 9ad6b2975..4e3f04807 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -213,13 +213,168 @@ chat_window_find_chat (EmpathyChat *chat) } static void +remove_all_chats (EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + priv = GET_PRIV (window); + g_object_ref (window); + + while (priv->chats) { + empathy_chat_window_remove_chat (window, priv->chats->data); + } + + g_object_unref (window); +} + +static void +confirm_close_response_cb (GtkWidget *dialog, + int response, + EmpathyChatWindow *window) +{ + EmpathyChat *chat; + + chat = g_object_get_data (G_OBJECT (dialog), "chat"); + + gtk_widget_destroy (dialog); + + if (response != GTK_RESPONSE_ACCEPT) + return; + + if (chat != NULL) { + empathy_chat_window_remove_chat (window, chat); + } else { + remove_all_chats (window); + } +} + +static void +confirm_close (EmpathyChatWindow *window, + gboolean close_window, + guint n_rooms, + EmpathyChat *chat) +{ + EmpathyChatWindowPriv *priv; + GtkWidget *dialog; + gchar *primary, *secondary; + + g_return_if_fail (n_rooms > 0); + + if (n_rooms > 1) { + g_return_if_fail (chat == NULL); + } else { + g_return_if_fail (chat != NULL); + } + + priv = GET_PRIV (window); + + /* If there are no chats in this window, how could we possibly have got + * here? + */ + g_return_if_fail (priv->chats != NULL); + + /* Treat closing a window which only has one tab exactly like closing + * that tab. + */ + if (close_window && priv->chats->next == NULL) { + close_window = FALSE; + chat = priv->chats->data; + } + + if (close_window) { + primary = g_strdup (_("Close this window?")); + + if (n_rooms == 1) { + gchar *chat_name = empathy_chat_dup_name (chat); + secondary = g_strdup_printf ( + _("Closing this window will leave %s. You will " + "not receive any further messages until you " + "rejoin it."), + chat_name); + g_free (chat_name); + } else { + secondary = g_strdup_printf ( + /* Note to translators: the number of chats will + * always be at least 2. + */ + ngettext ( + "Closing this window will leave a chat room. You will " + "not receive any further messages until you rejoin it.", + "Closing this window will leave %u chat rooms. You will " + "not receive any further messages until you rejoin them.", + n_rooms), + n_rooms); + } + } else { + gchar *chat_name = empathy_chat_dup_name (chat); + primary = g_strdup_printf (_("Leave %s?"), chat_name); + secondary = g_strdup (_("You will not receive any further messages from this chat " + "room until you rejoin it.")); + g_free (chat_name); + } + + dialog = gtk_message_dialog_new ( + GTK_WINDOW (priv->dialog), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_CANCEL, + "%s", primary); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + g_object_set (dialog, "secondary-text", secondary, NULL); + + g_free (primary); + g_free (secondary); + + gtk_dialog_add_button (GTK_DIALOG (dialog), + close_window ? _("Close window") : _("Leave room"), + GTK_RESPONSE_ACCEPT); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), + GTK_RESPONSE_ACCEPT); + + if (!close_window) { + g_object_set_data (G_OBJECT (dialog), "chat", chat); + } + + g_signal_connect (dialog, "response", + G_CALLBACK (confirm_close_response_cb), window); + + gtk_window_present (GTK_WINDOW (dialog)); +} + +/* Returns TRUE if we should check if the user really wants to leave. If it's + * a multi-user chat, and it has a TpChat (so there's an underlying channel, so + * the user is actually in the room as opposed to having been kicked or gone + * offline or something), then we should check. + */ +static gboolean +chat_needs_close_confirmation (EmpathyChat *chat) +{ + return (empathy_chat_is_room (chat) + && empathy_chat_get_tp_chat (chat) != NULL); +} + +static void +maybe_close_chat (EmpathyChatWindow *window, + EmpathyChat *chat) +{ + g_return_if_fail (chat != NULL); + + if (chat_needs_close_confirmation (chat)) { + confirm_close (window, FALSE, 1, chat); + } else { + empathy_chat_window_remove_chat (window, chat); + } +} + +static void chat_window_close_clicked_cb (GtkAction *action, EmpathyChat *chat) { EmpathyChatWindow *window; window = chat_window_find_chat (chat); - empathy_chat_window_remove_chat (window, chat); + maybe_close_chat (window, chat); } static void @@ -773,10 +928,19 @@ chat_window_update_chat_tab_full (EmpathyChat *chat, g_free (markup); /* Update tab and menu label */ + if (empathy_chat_is_highlighted (chat)) { + markup = g_markup_printf_escaped ( + "<span color=\"red\" weight=\"bold\">%s</span>", + name); + } else { + markup = g_markup_escape_text (name, -1); + } + widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label"); - gtk_label_set_text (GTK_LABEL (widget), name); + gtk_label_set_markup (GTK_LABEL (widget), markup); widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label"); - gtk_label_set_text (GTK_LABEL (widget), name); + gtk_label_set_markup (GTK_LABEL (widget), markup); + g_free (markup); /* Update the window if it's the current chat */ if (priv->current_chat == chat) { @@ -1027,7 +1191,7 @@ chat_window_close_activate_cb (GtkAction *action, g_return_if_fail (priv->current_chat != NULL); - empathy_chat_window_remove_chat (window, priv->current_chat); + maybe_close_chat (window, priv->current_chat); } static void @@ -1271,14 +1435,25 @@ chat_window_delete_event_cb (GtkWidget *dialog, EmpathyChatWindow *window) { EmpathyChatWindowPriv *priv = GET_PRIV (window); + EmpathyChat *chat = NULL; + guint n_rooms = 0; + GList *l; DEBUG ("Delete event received"); - g_object_ref (window); - while (priv->chats) { - empathy_chat_window_remove_chat (window, priv->chats->data); + for (l = priv->chats; l != NULL; l = l->next) { + if (chat_needs_close_confirmation (l->data)) { + chat = l->data; + n_rooms++; + } + } + + if (n_rooms > 0) { + confirm_close (window, TRUE, n_rooms, + (n_rooms == 1 ? chat : NULL)); + } else { + remove_all_chats (window); } - g_object_unref (window); return TRUE; } @@ -1394,30 +1569,6 @@ chat_window_show_or_update_notification (EmpathyChatWindow *window, g_free (escaped); } -static void -chat_window_set_highlight_room_labels (EmpathyChat *chat) -{ - gchar *markup, *name; - GtkWidget *widget; - - if (!empathy_chat_is_room (chat)) - return; - - name = empathy_chat_dup_name (chat); - markup = g_markup_printf_escaped ( - "<span color=\"red\" weight=\"bold\">%s</span>", - name); - - widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label"); - gtk_label_set_markup (GTK_LABEL (widget), markup); - - widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label"); - gtk_label_set_markup (GTK_LABEL (widget), markup); - - g_free (name); - g_free (markup); -} - static gboolean empathy_chat_window_has_focus (EmpathyChatWindow *window) { @@ -1437,6 +1588,7 @@ static void chat_window_new_message_cb (EmpathyChat *chat, EmpathyMessage *message, gboolean pending, + gboolean should_highlight, EmpathyChatWindow *window) { EmpathyChatWindowPriv *priv; @@ -1496,15 +1648,13 @@ chat_window_new_message_cb (EmpathyChat *chat, if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) { needs_urgency = TRUE; } else { - needs_urgency = empathy_message_should_highlight (message); + needs_urgency = should_highlight; } } else { needs_urgency = TRUE; } if (needs_urgency) { - chat_window_set_highlight_room_labels (chat); - if (!has_focus) { chat_window_set_urgency_hint (window, TRUE); } @@ -2488,8 +2638,12 @@ empathy_chat_window_present_chat (EmpathyChat *chat, } empathy_chat_window_switch_to_chat (window, chat); - empathy_window_present_with_time (GTK_WINDOW (priv->dialog), - x_timestamp); + + /* Don't use empathy_window_present_with_time () which would move the window + * to our current desktop but move to the window's desktop instead. This is + * more coherent with Shell's 'app is ready' notication which moves the view + * to the app desktop rather than moving the app itself. */ + empathy_move_to_window_desktop (GTK_WINDOW (priv->dialog), x_timestamp); gtk_widget_grab_focus (chat->input_text_view); } diff --git a/src/empathy-chat-window.ui b/src/empathy-chat-window.ui index 25749861c..bf95bff16 100644 --- a/src/empathy-chat-window.ui +++ b/src/empathy-chat-window.ui @@ -205,8 +205,8 @@ <object class="GtkWindow" id="chat_window"> <property name="title" translatable="yes">Chat</property> <property name="role">chat</property> - <property name="default_width">350</property> - <property name="default_height">250</property> + <property name="default_width">580</property> + <property name="default_height">480</property> <child> <object class="GtkVBox" id="chat_vbox"> <property name="visible">True</property> diff --git a/src/empathy-chat.c b/src/empathy-chat.c index 43ab00270..bebca4382 100644 --- a/src/empathy-chat.c +++ b/src/empathy-chat.c @@ -31,6 +31,7 @@ #include <telepathy-glib/debug-sender.h> #include <libempathy/empathy-presence-manager.h> +#include <libempathy/empathy-individual-manager.h> #include <libempathy-gtk/empathy-theme-manager.h> #include <libempathy-gtk/empathy-ui-utils.h> @@ -100,6 +101,7 @@ main (int argc, EmpathyPresenceManager *presence_mgr; EmpathyThemeManager *theme_mgr; gint retval; + EmpathyIndividualManager *individual_mgr; /* Init */ g_thread_init (NULL); @@ -143,6 +145,10 @@ main (int argc, /* Keep the theme manager alive as it does some caching */ theme_mgr = empathy_theme_manager_dup_singleton (); + /* Keep the individual manager alive so we won't fetch everything from Folks + * each time we need to use it. */ + individual_mgr = empathy_individual_manager_dup_singleton (); + if (g_getenv ("EMPATHY_PERSIST") != NULL) { DEBUG ("Disable timer"); @@ -163,6 +169,7 @@ main (int argc, g_object_unref (presence_mgr); g_object_unref (theme_mgr); tp_clear_object (&chat_mgr); + g_object_unref (individual_mgr); #ifdef ENABLE_DEBUG g_object_unref (debug_sender); diff --git a/src/empathy-event-manager.c b/src/empathy-event-manager.c index da677d42d..321cd1cca 100644 --- a/src/empathy-event-manager.c +++ b/src/empathy-event-manager.c @@ -33,10 +33,9 @@ #include <libempathy/empathy-presence-manager.h> #include <libempathy/empathy-tp-contact-factory.h> -#include <libempathy/empathy-contact-manager.h> +#include <libempathy/empathy-connection-aggregator.h> #include <libempathy/empathy-tp-chat.h> #include <libempathy/empathy-tp-streamed-media.h> -#include <libempathy/empathy-tp-file.h> #include <libempathy/empathy-utils.h> #include <libempathy/empathy-gsettings.h> @@ -48,7 +47,7 @@ #include <libempathy-gtk/empathy-ui-utils.h> #include "empathy-event-manager.h" -#include "empathy-main-window.h" +#include "empathy-roster-window.h" #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER #include <libempathy/empathy-debug.h> @@ -81,7 +80,7 @@ typedef struct { typedef struct { TpBaseClient *approver; TpBaseClient *auth_approver; - EmpathyContactManager *contact_manager; + EmpathyConnectionAggregator *conn_aggregator; GSList *events; /* Ongoing approvals */ GSList *approvals; @@ -92,6 +91,9 @@ typedef struct { GSettings *gsettings_ui; EmpathySoundManager *sound_mgr; + + /* TpContact -> EmpathyContact */ + GHashTable *contacts; } EmpathyEventManagerPriv; typedef struct _EventPriv EventPriv; @@ -422,9 +424,9 @@ reject_channel_claim_cb (GObject *source, { empathy_tp_chat_leave (user_data, ""); } - else if (EMPATHY_IS_TP_FILE (user_data)) + else if (TP_IS_FILE_TRANSFER_CHANNEL (user_data)) { - empathy_tp_file_close (user_data); + tp_channel_close_async (user_data, NULL, NULL); } out: @@ -615,7 +617,7 @@ event_manager_chat_message_received_cb (EmpathyTpChat *tp_chat, EMPATHY_EVENT_TYPE_CHAT, EMPATHY_IMAGE_NEW_MESSAGE, header, msg, approval, event_text_channel_process_func, NULL); - window = empathy_main_window_dup (); + window = empathy_roster_window_dup (); empathy_sound_manager_play (priv->sound_mgr, window, EMPATHY_SOUND_CONVERSATION_NEW); @@ -721,7 +723,7 @@ event_manager_call_channel_got_contact_cb (TpConnection *connection, approval->handler = g_signal_connect (call, "state-changed", G_CALLBACK (event_manager_call_state_changed_cb), approval); - window = empathy_main_window_dup (); + window = empathy_roster_window_dup (); approval->contact = g_object_ref (contact); g_object_get (G_OBJECT (call), "initial-video", &video, NULL); @@ -750,7 +752,7 @@ static void event_manager_media_channel_got_contact (EventManagerApproval *approval) { EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager); - GtkWidget *window = empathy_main_window_dup (); + GtkWidget *window = empathy_roster_window_dup (); gchar *header; EmpathyTpStreamedMedia *call; gboolean video; @@ -870,7 +872,7 @@ event_room_channel_process_func (EventPriv *event) static void display_invite_room_dialog (EventManagerApproval *approval) { - GtkWidget *window = empathy_main_window_dup (); + GtkWidget *window = empathy_roster_window_dup (); const gchar *invite_msg; gchar *msg; TpHandle self_handle; @@ -933,7 +935,7 @@ event_manager_ft_got_contact_cb (TpConnection *connection, GObject *object) { EventManagerApproval *approval = (EventManagerApproval *) user_data; - GtkWidget *window = empathy_main_window_dup (); + GtkWidget *window = empathy_roster_window_dup (); char *header; EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager); @@ -1115,7 +1117,7 @@ approve_channels (TpSimpleApprover *approver, event_manager_call_channel_got_contact_cb, approval, NULL, G_OBJECT (self)); } - else if (EMPATHY_IS_TP_FILE (channel)) + else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER) { TpHandle handle; @@ -1179,15 +1181,20 @@ event_pending_subscribe_func (EventPriv *event) } static void -event_manager_pendings_changed_cb (EmpathyContactList *list, - EmpathyContact *contact, EmpathyContact *actor, - guint reason, gchar *message, gboolean is_pending, - EmpathyEventManager *manager) +check_publish_state (EmpathyEventManager *self, + TpContact *tp_contact) { - EmpathyEventManagerPriv *priv = GET_PRIV (manager); - gchar *header, *event_msg; + EmpathyEventManagerPriv *priv = GET_PRIV (self); + gchar *header, *event_msg; + TpSubscriptionState state; + EmpathyContact *contact; + const gchar *message; + + state = tp_contact_get_publish_state (tp_contact); + + contact = empathy_contact_dup_from_tp_contact (tp_contact); - if (!is_pending) + if (state != TP_SUBSCRIPTION_STATE_ASK) { GSList *l; @@ -1203,24 +1210,37 @@ event_manager_pendings_changed_cb (EmpathyContactList *list, } } - return; + goto out; } header = g_strdup_printf ( _("%s would like permission to see when you are online"), empathy_contact_get_alias (contact)); + message = tp_contact_get_publish_request (tp_contact); + if (!EMP_STR_EMPTY (message)) event_msg = g_strdup_printf (_("\nMessage: %s"), message); else event_msg = NULL; - event_manager_add (manager, NULL, contact, EMPATHY_EVENT_TYPE_SUBSCRIPTION, + event_manager_add (self, NULL, contact, EMPATHY_EVENT_TYPE_SUBSCRIPTION, GTK_STOCK_DIALOG_QUESTION, header, event_msg, NULL, event_pending_subscribe_func, NULL); g_free (event_msg); g_free (header); + +out: + g_object_unref (contact); +} + +static void +event_manager_publish_state_changed_cb (TpContact *contact, + GParamSpec *spec, + EmpathyEventManager *self) +{ + check_publish_state (self, contact); } static void @@ -1232,7 +1252,7 @@ event_manager_presence_changed_cb (EmpathyContact *contact, EmpathyEventManagerPriv *priv = GET_PRIV (manager); TpAccount *account; EmpathyPresenceManager *presence_mgr; - GtkWidget *window = empathy_main_window_dup (); + GtkWidget *window = empathy_roster_window_dup (); account = empathy_contact_get_account (contact); presence_mgr = empathy_presence_manager_dup_singleton (); @@ -1289,23 +1309,6 @@ out: g_object_unref (window); } -static void -event_manager_members_changed_cb (EmpathyContactList *list, - EmpathyContact *contact, - EmpathyContact *actor, - guint reason, - gchar *message, - gboolean is_member, - EmpathyEventManager *manager) -{ - if (is_member) - g_signal_connect (contact, "presence-changed", - G_CALLBACK (event_manager_presence_changed_cb), manager); - else - g_signal_handlers_disconnect_by_func (contact, - event_manager_presence_changed_cb, manager); -} - static GObject * event_manager_constructor (GType type, guint n_props, @@ -1338,12 +1341,13 @@ event_manager_finalize (GObject *object) g_slist_free (priv->events); g_slist_foreach (priv->approvals, (GFunc) event_manager_approval_free, NULL); g_slist_free (priv->approvals); - g_object_unref (priv->contact_manager); + g_object_unref (priv->conn_aggregator); g_object_unref (priv->approver); g_object_unref (priv->auth_approver); g_object_unref (priv->gsettings_notif); g_object_unref (priv->gsettings_ui); g_object_unref (priv->sound_mgr); + g_hash_table_unref (priv->contacts); } static void @@ -1386,12 +1390,63 @@ empathy_event_manager_class_init (EmpathyEventManagerClass *klass) } static void +contact_list_changed_cb (EmpathyConnectionAggregator *aggregator, + GPtrArray *added, + GPtrArray *removed, + EmpathyEventManager *self) +{ + EmpathyEventManagerPriv *priv = GET_PRIV (self); + guint i; + + for (i = 0; i < added->len; i++) + { + TpContact *tp_contact = g_ptr_array_index (added, i); + EmpathyContact *contact; + + if (g_hash_table_lookup (priv->contacts, tp_contact) != NULL) + continue; + + contact = empathy_contact_dup_from_tp_contact (tp_contact); + + tp_g_signal_connect_object (contact, "presence-changed", + G_CALLBACK (event_manager_presence_changed_cb), self, 0); + + tp_g_signal_connect_object (tp_contact, "notify::publish-state", + G_CALLBACK (event_manager_publish_state_changed_cb), self, 0); + + check_publish_state (self, tp_contact); + + /* Pass ownership to the hash table */ + g_hash_table_insert (priv->contacts, g_object_ref (tp_contact), contact); + } + + for (i = 0; i < removed->len; i++) + { + TpContact *tp_contact = g_ptr_array_index (removed, i); + EmpathyContact *contact; + + contact = g_hash_table_lookup (priv->contacts, tp_contact); + if (contact == NULL) + continue; + + g_signal_handlers_disconnect_by_func (contact, + event_manager_presence_changed_cb, self); + + g_signal_handlers_disconnect_by_func (tp_contact, + event_manager_publish_state_changed_cb, self); + + g_hash_table_remove (priv->contacts, tp_contact); + } +} + +static void empathy_event_manager_init (EmpathyEventManager *manager) { EmpathyEventManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, EMPATHY_TYPE_EVENT_MANAGER, EmpathyEventManagerPriv); GError *error = NULL; TpAccountManager *am; + GPtrArray *contacts, *empty; manager->priv = priv; @@ -1400,12 +1455,23 @@ empathy_event_manager_init (EmpathyEventManager *manager) priv->sound_mgr = empathy_sound_manager_dup_singleton (); - priv->contact_manager = empathy_contact_manager_dup_singleton (); - g_signal_connect (priv->contact_manager, "pendings-changed", - G_CALLBACK (event_manager_pendings_changed_cb), manager); + priv->contacts = g_hash_table_new_full (NULL, NULL, g_object_unref, + g_object_unref); + + priv->conn_aggregator = empathy_connection_aggregator_dup_singleton (); + + tp_g_signal_connect_object (priv->conn_aggregator, "contact-list-changed", + G_CALLBACK (contact_list_changed_cb), manager, 0); + + contacts = empathy_connection_aggregator_dup_all_contacts ( + priv->conn_aggregator); + + empty = g_ptr_array_new (); + + contact_list_changed_cb (priv->conn_aggregator, contacts, empty, manager); - g_signal_connect (priv->contact_manager, "members-changed", - G_CALLBACK (event_manager_members_changed_cb), manager); + g_ptr_array_unref (contacts); + g_ptr_array_unref (empty); am = tp_account_manager_dup (); diff --git a/src/empathy-ft-manager.c b/src/empathy-ft-manager.c index 606600e99..18ca1938a 100644 --- a/src/empathy-ft-manager.c +++ b/src/empathy-ft-manager.c @@ -37,7 +37,6 @@ #define DEBUG_FLAG EMPATHY_DEBUG_FT #include <libempathy/empathy-debug.h> -#include <libempathy/empathy-tp-file.h> #include <libempathy/empathy-utils.h> #include <libempathy-gtk/empathy-ui-utils.h> @@ -518,7 +517,7 @@ do_real_transfer_done (EmpathyFTManager *manager, static void ft_handler_transfer_done_cb (EmpathyFTHandler *handler, - EmpathyTpFile *tp_file, + TpFileTransferChannel *channel, EmpathyFTManager *manager) { if (empathy_ft_handler_is_incoming (handler) && @@ -574,7 +573,7 @@ ft_handler_transfer_progress_cb (EmpathyFTHandler *handler, static void ft_handler_transfer_started_cb (EmpathyFTHandler *handler, - EmpathyTpFile *tp_file, + TpFileTransferChannel *channel, EmpathyFTManager *manager) { guint64 transferred_bytes, total_bytes; diff --git a/src/empathy-import-dialog.c b/src/empathy-import-dialog.c index 77d11298f..4e00bd11a 100644 --- a/src/empathy-import-dialog.c +++ b/src/empathy-import-dialog.c @@ -39,7 +39,8 @@ enum { PROP_PARENT = 1, - PROP_SHOW_WARNING + PROP_SHOW_WARNING, + PROP_CMS, }; typedef struct { @@ -48,6 +49,7 @@ typedef struct { EmpathyImportWidget *iw; gboolean show_warning; + EmpathyConnectionManagers *cms; } EmpathyImportDialogPriv; G_DEFINE_TYPE (EmpathyImportDialog, empathy_import_dialog, GTK_TYPE_DIALOG) @@ -62,15 +64,17 @@ import_dialog_add_import_widget (EmpathyImportDialog *self) area = gtk_dialog_get_content_area (GTK_DIALOG (self)); - iw = empathy_import_widget_new (EMPATHY_IMPORT_APPLICATION_ALL); + iw = empathy_import_widget_new (EMPATHY_IMPORT_APPLICATION_ALL, priv->cms); widget = empathy_import_widget_get_widget (iw); - gtk_box_pack_start (GTK_BOX (area), widget, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (area), widget, TRUE, TRUE, 0); gtk_widget_show (widget); priv->iw = iw; - gtk_dialog_add_buttons (GTK_DIALOG (self), GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + gtk_dialog_add_buttons (GTK_DIALOG (self), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("_Import"), GTK_RESPONSE_OK, + NULL); } static void @@ -134,6 +138,9 @@ do_get_property (GObject *object, case PROP_SHOW_WARNING: g_value_set_boolean (value, priv->show_warning); break; + case PROP_CMS: + g_value_set_object (value, priv->cms); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -155,6 +162,9 @@ do_set_property (GObject *object, case PROP_SHOW_WARNING: priv->show_warning = g_value_get_boolean (value); break; + case PROP_CMS: + priv->cms = g_value_dup_object (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -199,6 +209,15 @@ empathy_import_dialog_init (EmpathyImportDialog *self) gtk_window_set_title (GTK_WINDOW (self), _("Import Accounts")); gtk_window_set_modal (GTK_WINDOW (self), TRUE); } +static void +do_dispose (GObject *obj) +{ + EmpathyImportDialogPriv *priv = GET_PRIV (obj); + + g_clear_object (&priv->cms); + + G_OBJECT_CLASS (empathy_import_dialog_parent_class)->dispose (obj); +} static void empathy_import_dialog_class_init (EmpathyImportDialogClass *klass) @@ -210,6 +229,7 @@ empathy_import_dialog_class_init (EmpathyImportDialogClass *klass) oclass->constructed = do_constructed; oclass->get_property = do_get_property; oclass->set_property = do_set_property; + oclass->dispose = do_dispose; gtkclass->response = impl_signal_response; @@ -226,13 +246,25 @@ empathy_import_dialog_class_init (EmpathyImportDialogClass *klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (oclass, PROP_SHOW_WARNING, param_spec); + param_spec = g_param_spec_object ("cms", + "EmpathyConnectionManagers", "EmpathyConnectionManager", + EMPATHY_TYPE_CONNECTION_MANAGERS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (oclass, PROP_CMS, param_spec); + g_type_class_add_private (klass, sizeof (EmpathyImportDialogPriv)); } GtkWidget * empathy_import_dialog_new (GtkWindow *parent, - gboolean warning) + gboolean warning, + EmpathyConnectionManagers *cms) { - return g_object_new (EMPATHY_TYPE_IMPORT_DIALOG, "parent-window", - parent, "show-warning", warning, NULL); + g_return_val_if_fail (EMPATHY_IS_CONNECTION_MANAGERS (cms), NULL); + + return g_object_new (EMPATHY_TYPE_IMPORT_DIALOG, + "parent-window", parent, + "show-warning", warning, + "cms", cms, + NULL); } diff --git a/src/empathy-import-dialog.h b/src/empathy-import-dialog.h index 0e9d225c4..a412037d1 100644 --- a/src/empathy-import-dialog.h +++ b/src/empathy-import-dialog.h @@ -25,6 +25,8 @@ #ifndef __EMPATHY_IMPORT_DIALOG_H__ #define __EMPATHY_IMPORT_DIALOG_H__ +#include <libempathy/empathy-connection-managers.h> + G_BEGIN_DECLS #define EMPATHY_TYPE_IMPORT_DIALOG empathy_import_dialog_get_type() @@ -56,7 +58,8 @@ typedef struct { GType empathy_import_dialog_get_type (void); GtkWidget* empathy_import_dialog_new (GtkWindow *parent_window, - gboolean show_warning); + gboolean show_warning, + EmpathyConnectionManagers *cms); G_END_DECLS diff --git a/src/empathy-import-widget.c b/src/empathy-import-widget.c index 9a8917ffc..041c51003 100644 --- a/src/empathy-import-widget.c +++ b/src/empathy-import-widget.c @@ -55,7 +55,8 @@ enum }; enum { - PROP_APPLICATION_ID = 1 + PROP_APPLICATION_ID = 1, + PROP_CMS }; typedef struct { @@ -200,7 +201,7 @@ import_widget_add_account (EmpathyImportWidget *self, EmpathyImportAccountData *data) { TpAccountManager *account_manager; - gchar *display_name; + gchar *display_name = NULL; GHashTable *properties; GValue *username; @@ -210,9 +211,23 @@ import_widget_add_account (EmpathyImportWidget *self, /* Set the display name of the account */ username = g_hash_table_lookup (data->settings, "account"); - display_name = g_strdup_printf ("%s (%s)", - data->protocol, - g_value_get_string (username)); + + if (!tp_strdiff (data->protocol, "irc")) + { + const gchar *server; + + server = tp_asv_get_string (data->settings, "server"); + + if (server != NULL) + display_name = g_strdup_printf ("%s on %s", + g_value_get_string (username), server); + } + + if (display_name == NULL) + { + display_name = g_strdup_printf ("%s (%s)", + data->protocol, g_value_get_string (username)); + } DEBUG ("display name: %s\n", display_name); @@ -345,20 +360,6 @@ import_widget_set_up_account_list (EmpathyImportWidget *self) } static void -import_widget_cms_prepare_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - EmpathyImportWidget *self = user_data; - - if (!empathy_connection_managers_prepare_finish ( - EMPATHY_CONNECTION_MANAGERS (source), result, NULL)) - return; - - import_widget_set_up_account_list (self); -} - -static void import_widget_destroy_cb (GtkWidget *w, EmpathyImportWidget *self) { @@ -378,6 +379,9 @@ do_get_property (GObject *object, case PROP_APPLICATION_ID: g_value_set_int (value, priv->app_id); break; + case PROP_CMS: + g_value_set_object (value, priv->cms); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -396,6 +400,9 @@ do_set_property (GObject *object, case PROP_APPLICATION_ID: priv->app_id = g_value_get_int (value); break; + case PROP_CMS: + priv->cms = g_value_dup_object (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -454,8 +461,7 @@ do_constructed (GObject *obj) g_signal_connect (priv->vbox, "destroy", G_CALLBACK (import_widget_destroy_cb), self); - empathy_connection_managers_prepare_async (priv->cms, - import_widget_cms_prepare_cb, self); + import_widget_set_up_account_list (self); } static void @@ -476,6 +482,12 @@ empathy_import_widget_class_init (EmpathyImportWidgetClass *klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (oclass, PROP_APPLICATION_ID, param_spec); + param_spec = g_param_spec_object ("cms", + "EmpathyConnectionManagers", "EmpathyConnectionManager", + EMPATHY_TYPE_CONNECTION_MANAGERS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (oclass, PROP_CMS, param_spec); + g_type_class_add_private (klass, sizeof (EmpathyImportWidgetPriv)); } @@ -487,14 +499,18 @@ empathy_import_widget_init (EmpathyImportWidget *self) EmpathyImportWidgetPriv); self->priv = priv; - - priv->cms = empathy_connection_managers_dup_singleton (); } EmpathyImportWidget * -empathy_import_widget_new (EmpathyImportApplication id) +empathy_import_widget_new (EmpathyImportApplication id, + EmpathyConnectionManagers *cms) { - return g_object_new (EMPATHY_TYPE_IMPORT_WIDGET, "application-id", id, NULL); + g_return_val_if_fail (EMPATHY_IS_CONNECTION_MANAGERS (cms), NULL); + + return g_object_new (EMPATHY_TYPE_IMPORT_WIDGET, + "application-id", id, + "cms", cms, + NULL); } GtkWidget * diff --git a/src/empathy-import-widget.h b/src/empathy-import-widget.h index 48f2e1d48..14a01d79d 100644 --- a/src/empathy-import-widget.h +++ b/src/empathy-import-widget.h @@ -27,6 +27,8 @@ #include <glib-object.h> +#include <libempathy/empathy-connection-managers.h> + #include "empathy-import-utils.h" G_BEGIN_DECLS @@ -59,7 +61,8 @@ typedef struct { GType empathy_import_widget_get_type (void); -EmpathyImportWidget* empathy_import_widget_new (EmpathyImportApplication id); +EmpathyImportWidget* empathy_import_widget_new (EmpathyImportApplication id, + EmpathyConnectionManagers *cms); GtkWidget * empathy_import_widget_get_widget (EmpathyImportWidget *self); diff --git a/src/empathy-invite-participant-dialog.c b/src/empathy-invite-participant-dialog.c index f905c827e..ece86f1b6 100644 --- a/src/empathy-invite-participant-dialog.c +++ b/src/empathy-invite-participant-dialog.c @@ -114,6 +114,8 @@ get_tp_contact_for_chat (EmpathyInviteParticipantDialog *self, TpConnection *chat_conn; chat_conn = tp_channel_borrow_connection (TP_CHANNEL (self->priv->tp_chat)); + if (chat_conn == NULL) + return NULL; return empathy_get_tp_contact_for_individual (individual, chat_conn); } diff --git a/src/empathy-main-window.c b/src/empathy-main-window.c deleted file mode 100644 index 2f73acf92..000000000 --- a/src/empathy-main-window.c +++ /dev/null @@ -1,2586 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2002-2007 Imendio AB - * Copyright (C) 2007-2010 Collabora Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - * - * Authors: Xavier Claessens <xclaesse@gmail.com> - * Danielle Madeley <danielle.madeley@collabora.co.uk> - */ - -#include <config.h> - -#include <sys/stat.h> -#include <gtk/gtk.h> -#include <gdk/gdkkeysyms.h> -#include <glib/gi18n.h> - -#include <telepathy-glib/account-manager.h> -#include <telepathy-glib/util.h> -#include <folks/folks.h> - -#include <libempathy/empathy-contact.h> -#include <libempathy/empathy-utils.h> -#include <libempathy/empathy-request-util.h> -#include <libempathy/empathy-chatroom-manager.h> -#include <libempathy/empathy-chatroom.h> -#include <libempathy/empathy-contact-list.h> -#include <libempathy/empathy-contact-manager.h> -#include <libempathy/empathy-gsettings.h> -#include <libempathy/empathy-individual-manager.h> -#include <libempathy/empathy-gsettings.h> -#include <libempathy/empathy-status-presets.h> -#include <libempathy/empathy-tp-contact-factory.h> - -#include <libempathy-gtk/empathy-contact-dialogs.h> -#include <libempathy-gtk/empathy-live-search.h> -#include <libempathy-gtk/empathy-contact-blocking-dialog.h> -#include <libempathy-gtk/empathy-contact-search-dialog.h> -#include <libempathy-gtk/empathy-geometry.h> -#include <libempathy-gtk/empathy-gtk-enum-types.h> -#include <libempathy-gtk/empathy-individual-dialogs.h> -#include <libempathy-gtk/empathy-individual-store.h> -#include <libempathy-gtk/empathy-individual-store-manager.h> -#include <libempathy-gtk/empathy-individual-view.h> -#include <libempathy-gtk/empathy-new-message-dialog.h> -#include <libempathy-gtk/empathy-new-call-dialog.h> -#include <libempathy-gtk/empathy-log-window.h> -#include <libempathy-gtk/empathy-presence-chooser.h> -#include <libempathy-gtk/empathy-sound-manager.h> -#include <libempathy-gtk/empathy-ui-utils.h> - -#include "empathy-accounts-dialog.h" -#include "empathy-call-observer.h" -#include "empathy-chat-manager.h" -#include "empathy-main-window.h" -#include "empathy-preferences.h" -#include "empathy-about-dialog.h" -#include "empathy-debug-window.h" -#include "empathy-new-chatroom-dialog.h" -#include "empathy-map-view.h" -#include "empathy-chatrooms-window.h" -#include "empathy-event-manager.h" -#include "empathy-ft-manager.h" - -#define DEBUG_FLAG EMPATHY_DEBUG_OTHER -#include <libempathy/empathy-debug.h> - -/* Flashing delay for icons (milliseconds). */ -#define FLASH_TIMEOUT 500 - -/* Minimum width of roster window if something goes wrong. */ -#define MIN_WIDTH 50 - -/* Accels (menu shortcuts) can be configured and saved */ -#define ACCELS_FILENAME "accels.txt" - -/* Name in the geometry file */ -#define GEOMETRY_NAME "main-window" - -enum { - PAGE_CONTACT_LIST = 0, - PAGE_NO_MATCH -}; - -enum { - PROP_0, - PROP_SHELL_RUNNING -}; - -G_DEFINE_TYPE (EmpathyMainWindow, empathy_main_window, GTK_TYPE_WINDOW); - -#define GET_PRIV(self) ((EmpathyMainWindowPriv *)((EmpathyMainWindow *) self)->priv) - -struct _EmpathyMainWindowPriv { - EmpathyContactList *contact_manager; - EmpathyIndividualStore *individual_store; - EmpathyIndividualView *individual_view; - TpAccountManager *account_manager; - EmpathyChatroomManager *chatroom_manager; - EmpathyEventManager *event_manager; - EmpathySoundManager *sound_mgr; - EmpathyCallObserver *call_observer; - guint flash_timeout_id; - gboolean flash_on; - gboolean empty; - - GSettings *gsettings_ui; - GSettings *gsettings_contacts; - - GtkWidget *preferences; - GtkWidget *main_vbox; - GtkWidget *throbber; - GtkWidget *throbber_tool_item; - GtkWidget *presence_toolbar; - GtkWidget *presence_chooser; - GtkWidget *errors_vbox; - GtkWidget *auth_vbox; - GtkWidget *search_bar; - GtkWidget *notebook; - GtkWidget *no_entry_label; - - GtkToggleAction *show_protocols; - GtkRadioAction *sort_by_name; - GtkRadioAction *sort_by_status; - GtkRadioAction *normal_with_avatars; - GtkRadioAction *normal_size; - GtkRadioAction *compact_size; - - GtkUIManager *ui_manager; - GtkAction *view_history; - GtkAction *room_join_favorites; - GtkWidget *room_menu; - GtkWidget *room_separator; - GtkWidget *edit_context; - GtkWidget *edit_context_separator; - - GtkActionGroup *balance_action_group; - GtkAction *view_balance_show_in_roster; - GtkWidget *balance_vbox; - - guint size_timeout_id; - - /* reffed TpAccount* => visible GtkInfoBar* */ - GHashTable *errors; - - /* EmpathyEvent* => visible GtkInfoBar* */ - GHashTable *auths; - - /* stores a mapping from TpAccount to Handler ID to prevent - * to listen more than once to the status-changed signal */ - GHashTable *status_changed_handlers; - - /* Actions that are enabled when there are connected accounts */ - GList *actions_connected; - - gboolean shell_running; -}; - -static void -main_window_flash_stop (EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - - if (priv->flash_timeout_id == 0) { - return; - } - - DEBUG ("Stop flashing"); - g_source_remove (priv->flash_timeout_id); - priv->flash_timeout_id = 0; - priv->flash_on = FALSE; -} - -typedef struct { - EmpathyEvent *event; - gboolean on; - EmpathyMainWindow *window; -} FlashForeachData; - -static gboolean -main_window_flash_foreach (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - FlashForeachData *data = (FlashForeachData *) user_data; - FolksIndividual *individual; - EmpathyContact *contact; - const gchar *icon_name; - GtkTreePath *parent_path = NULL; - GtkTreeIter parent_iter; - GdkPixbuf *pixbuf = NULL; - - gtk_tree_model_get (model, iter, - EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual, - -1); - - if (individual == NULL) - return FALSE; - - contact = empathy_contact_dup_from_folks_individual (individual); - if (contact != data->event->contact) - goto out; - - if (data->on) { - icon_name = data->event->icon_name; - pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU); - } else { - pixbuf = empathy_individual_store_get_individual_status_icon ( - GET_PRIV (data->window)->individual_store, - individual); - if (pixbuf != NULL) - g_object_ref (pixbuf); - } - - gtk_tree_store_set (GTK_TREE_STORE (model), iter, - EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS, pixbuf, - -1); - - /* To make sure the parent is shown correctly, we emit - * the row-changed signal on the parent so it prompts - * it to be refreshed by the filter func. - */ - if (gtk_tree_model_iter_parent (model, &parent_iter, iter)) { - parent_path = gtk_tree_model_get_path (model, &parent_iter); - } - if (parent_path) { - gtk_tree_model_row_changed (model, parent_path, &parent_iter); - gtk_tree_path_free (parent_path); - } - -out: - g_object_unref (individual); - tp_clear_object (&contact); - tp_clear_object (&pixbuf); - - return FALSE; -} - -static gboolean -main_window_flash_cb (EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkTreeModel *model; - GSList *events, *l; - gboolean found_event = FALSE; - FlashForeachData data; - - priv->flash_on = !priv->flash_on; - data.on = priv->flash_on; - model = GTK_TREE_MODEL (priv->individual_store); - - events = empathy_event_manager_get_events (priv->event_manager); - for (l = events; l; l = l->next) { - data.event = l->data; - data.window = window; - if (!data.event->contact || !data.event->must_ack) { - continue; - } - - found_event = TRUE; - gtk_tree_model_foreach (model, - main_window_flash_foreach, - &data); - } - - if (!found_event) { - main_window_flash_stop (window); - } - - return TRUE; -} - -static void -main_window_flash_start (EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - - if (priv->flash_timeout_id != 0) { - return; - } - - DEBUG ("Start flashing"); - priv->flash_timeout_id = g_timeout_add (FLASH_TIMEOUT, - (GSourceFunc) main_window_flash_cb, - window); - main_window_flash_cb (window); -} - -static void -main_window_remove_auth (EmpathyMainWindow *window, - EmpathyEvent *event) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkWidget *error_widget; - - error_widget = g_hash_table_lookup (priv->auths, event); - if (error_widget != NULL) { - gtk_widget_destroy (error_widget); - g_hash_table_remove (priv->auths, event); - } -} - -static void -main_window_auth_add_clicked_cb (GtkButton *button, - EmpathyMainWindow *window) -{ - EmpathyEvent *event; - - event = g_object_get_data (G_OBJECT (button), "event"); - - empathy_event_approve (event); - - main_window_remove_auth (window, event); -} - -static void -main_window_auth_close_clicked_cb (GtkButton *button, - EmpathyMainWindow *window) -{ - EmpathyEvent *event; - - event = g_object_get_data (G_OBJECT (button), "event"); - - empathy_event_decline (event); - main_window_remove_auth (window, event); -} - -static void -main_window_auth_display (EmpathyMainWindow *window, - EmpathyEvent *event) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - TpAccount *account = event->account; - GtkWidget *info_bar; - GtkWidget *content_area; - GtkWidget *image; - GtkWidget *label; - GtkWidget *add_button; - GtkWidget *close_button; - GtkWidget *action_area; - GtkWidget *action_grid; - const gchar *icon_name; - gchar *str; - - if (g_hash_table_lookup (priv->auths, event) != NULL) { - return; - } - - info_bar = gtk_info_bar_new (); - gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_QUESTION); - - gtk_widget_set_no_show_all (info_bar, TRUE); - gtk_box_pack_start (GTK_BOX (priv->auth_vbox), info_bar, FALSE, TRUE, 0); - gtk_widget_show (info_bar); - - icon_name = tp_account_get_icon_name (account); - image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_widget_show (image); - - str = g_markup_printf_escaped ("<b>%s</b>\n%s", - tp_account_get_display_name (account), - _("Password required")); - - label = gtk_label_new (str); - gtk_label_set_use_markup (GTK_LABEL (label), TRUE); - gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); - gtk_widget_show (label); - - g_free (str); - - content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar)); - gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0); - - image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON); - add_button = gtk_button_new (); - gtk_button_set_image (GTK_BUTTON (add_button), image); - gtk_widget_set_tooltip_text (add_button, _("Provide Password")); - gtk_widget_show (add_button); - - image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON); - close_button = gtk_button_new (); - gtk_button_set_image (GTK_BUTTON (close_button), image); - gtk_widget_set_tooltip_text (close_button, _("Disconnect")); - gtk_widget_show (close_button); - - action_grid = gtk_grid_new (); - gtk_grid_set_column_spacing (GTK_GRID (action_grid), 6); - gtk_widget_show (action_grid); - - action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar)); - gtk_box_pack_start (GTK_BOX (action_area), action_grid, FALSE, FALSE, 0); - - gtk_grid_attach (GTK_GRID (action_grid), add_button, 0, 0, 1, 1); - gtk_grid_attach (GTK_GRID (action_grid), close_button, 1, 0, 1, 1); - - g_object_set_data_full (G_OBJECT (info_bar), - "event", event, NULL); - g_object_set_data_full (G_OBJECT (add_button), - "event", event, NULL); - g_object_set_data_full (G_OBJECT (close_button), - "event", event, NULL); - - g_signal_connect (add_button, "clicked", - G_CALLBACK (main_window_auth_add_clicked_cb), - window); - g_signal_connect (close_button, "clicked", - G_CALLBACK (main_window_auth_close_clicked_cb), - window); - - gtk_widget_show (priv->auth_vbox); - - g_hash_table_insert (priv->auths, event, info_bar); -} - -static void -modify_event_count (GtkTreeModel *model, - GtkTreeIter *iter, - EmpathyEvent *event, - gboolean increase) -{ - FolksIndividual *individual; - EmpathyContact *contact; - guint count; - - gtk_tree_model_get (model, iter, - EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual, - EMPATHY_INDIVIDUAL_STORE_COL_EVENT_COUNT, &count, - -1); - - if (individual == NULL) - return; - - increase ? count++ : count--; - - contact = empathy_contact_dup_from_folks_individual (individual); - if (contact == event->contact) { - gtk_tree_store_set (GTK_TREE_STORE (model), iter, - EMPATHY_INDIVIDUAL_STORE_COL_EVENT_COUNT, count, -1); - } - - tp_clear_object (&contact); - g_object_unref (individual); -} - -static gboolean -increase_event_count_foreach (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - EmpathyEvent *event = user_data; - - modify_event_count (model, iter, event, TRUE); - - return FALSE; -} - -static void -increase_event_count (EmpathyMainWindow *self, - EmpathyEvent *event) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (self); - GtkTreeModel *model; - - model = GTK_TREE_MODEL (priv->individual_store); - - gtk_tree_model_foreach (model, increase_event_count_foreach, event); -} - -static gboolean -decrease_event_count_foreach (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - EmpathyEvent *event = user_data; - - modify_event_count (model, iter, event, FALSE); - - return FALSE; -} - -static void -decrease_event_count (EmpathyMainWindow *self, - EmpathyEvent *event) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (self); - GtkTreeModel *model; - - model = GTK_TREE_MODEL (priv->individual_store); - - gtk_tree_model_foreach (model, decrease_event_count_foreach, event); -} - -static void -main_window_event_added_cb (EmpathyEventManager *manager, - EmpathyEvent *event, - EmpathyMainWindow *window) -{ - if (event->contact) { - increase_event_count (window, event); - - main_window_flash_start (window); - } else if (event->type == EMPATHY_EVENT_TYPE_AUTH) { - main_window_auth_display (window, event); - } -} - -static void -main_window_event_removed_cb (EmpathyEventManager *manager, - EmpathyEvent *event, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - FlashForeachData data; - - if (event->type == EMPATHY_EVENT_TYPE_AUTH) { - main_window_remove_auth (window, event); - return; - } - - if (!event->contact) { - return; - } - - decrease_event_count (window, event); - - data.on = FALSE; - data.event = event; - data.window = window; - gtk_tree_model_foreach (GTK_TREE_MODEL (priv->individual_store), - main_window_flash_foreach, - &data); -} - -static gboolean -main_window_load_events_idle_cb (gpointer user_data) -{ - EmpathyMainWindow *window = user_data; - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GSList *l; - - l = empathy_event_manager_get_events (priv->event_manager); - while (l) { - main_window_event_added_cb (priv->event_manager, l->data, - window); - l = l->next; - } - - return FALSE; -} - -static void -main_window_row_activated_cb (EmpathyIndividualView *view, - GtkTreePath *path, - GtkTreeViewColumn *col, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - EmpathyContact *contact = NULL; - FolksIndividual *individual; - GtkTreeModel *model; - GtkTreeIter iter; - GSList *events, *l; - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->individual_view)); - gtk_tree_model_get_iter (model, &iter, path); - - gtk_tree_model_get (model, &iter, - EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, - &individual, - -1); - - if (individual != NULL) { - contact = empathy_contact_dup_from_folks_individual (individual); - } - - if (!contact) { - goto OUT; - } - - /* If the contact has an event activate it, otherwise the - * default handler of row-activated will be called. */ - events = empathy_event_manager_get_events (priv->event_manager); - for (l = events; l; l = l->next) { - EmpathyEvent *event = l->data; - - if (event->contact == contact) { - DEBUG ("Activate event"); - empathy_event_activate (event); - - /* We don't want the default handler of this signal - * (e.g. open a chat) */ - g_signal_stop_emission_by_name (view, "row-activated"); - break; - } - } - - g_object_unref (contact); -OUT: - tp_clear_object (&individual); -} - -static void -main_window_row_deleted_cb (GtkTreeModel *model, - GtkTreePath *path, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkTreeIter help_iter; - - if (!gtk_tree_model_get_iter_first (model, &help_iter)) { - priv->empty = TRUE; - - if (empathy_individual_view_is_searching ( - priv->individual_view)) { - gchar *tmp; - - tmp = g_strdup_printf ("<b><span size='xx-large'>%s</span></b>", - _("No match found")); - - gtk_label_set_markup (GTK_LABEL (priv->no_entry_label), tmp); - g_free (tmp); - - gtk_label_set_line_wrap (GTK_LABEL (priv->no_entry_label), - TRUE); - - gtk_notebook_set_current_page ( - GTK_NOTEBOOK (priv->notebook), PAGE_NO_MATCH); - } - } -} - -static void -main_window_row_inserted_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - - if (priv->empty) { - priv->empty = FALSE; - gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), - PAGE_CONTACT_LIST); - gtk_widget_grab_focus (GTK_WIDGET (priv->individual_view)); - - /* The store is being filled, it will be done after an idle cb. - * So we can then get events. If we do that too soon, event's - * contact is not yet in the store and it won't get marked as - * having events. */ - g_idle_add (main_window_load_events_idle_cb, window); - } -} - -static void -main_window_remove_error (EmpathyMainWindow *window, - TpAccount *account) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkWidget *error_widget; - - error_widget = g_hash_table_lookup (priv->errors, account); - if (error_widget != NULL) { - gtk_widget_destroy (error_widget); - g_hash_table_remove (priv->errors, account); - } -} - -static void -main_window_account_disabled_cb (TpAccountManager *manager, - TpAccount *account, - EmpathyMainWindow *window) -{ - main_window_remove_error (window, account); -} - -static void -main_window_error_retry_clicked_cb (GtkButton *button, - EmpathyMainWindow *window) -{ - TpAccount *account; - - account = g_object_get_data (G_OBJECT (button), "account"); - tp_account_reconnect_async (account, NULL, NULL); - - main_window_remove_error (window, account); -} - -static void -main_window_error_edit_clicked_cb (GtkButton *button, - EmpathyMainWindow *window) -{ - TpAccount *account; - - account = g_object_get_data (G_OBJECT (button), "account"); - - empathy_accounts_dialog_show_application ( - gtk_widget_get_screen (GTK_WIDGET (button)), - account, FALSE, FALSE); - - main_window_remove_error (window, account); -} - -static void -main_window_error_close_clicked_cb (GtkButton *button, - EmpathyMainWindow *window) -{ - TpAccount *account; - - account = g_object_get_data (G_OBJECT (button), "account"); - main_window_remove_error (window, account); -} - -static void -main_window_error_upgrade_sw_clicked_cb (GtkButton *button, - EmpathyMainWindow *window) -{ - TpAccount *account; - GtkWidget *dialog; - - account = g_object_get_data (G_OBJECT (button), "account"); - main_window_remove_error (window, account); - - dialog = gtk_message_dialog_new (GTK_WINDOW (window), - GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - _("Sorry, %s accounts can’t be used until your %s software is updated."), - tp_account_get_protocol (account), - tp_account_get_protocol (account)); - - g_signal_connect_swapped (dialog, "response", - G_CALLBACK (gtk_widget_destroy), - dialog); - - gtk_widget_show (dialog); -} - -static void -main_window_upgrade_software_error (EmpathyMainWindow *window, - TpAccount *account) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkWidget *info_bar; - GtkWidget *content_area; - GtkWidget *label; - GtkWidget *image; - GtkWidget *upgrade_button; - GtkWidget *close_button; - GtkWidget *action_area; - GtkWidget *action_grid; - gchar *str; - const gchar *icon_name; - const gchar *error_message; - gboolean user_requested; - - error_message = - empathy_account_get_error_message (account, &user_requested); - - if (user_requested) { - return; - } - - str = g_markup_printf_escaped ("<b>%s</b>\n%s", - tp_account_get_display_name (account), - error_message); - - /* If there are other errors, remove them */ - main_window_remove_error (window, account); - - info_bar = gtk_info_bar_new (); - gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_ERROR); - - gtk_widget_set_no_show_all (info_bar, TRUE); - gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar, FALSE, TRUE, 0); - gtk_widget_show (info_bar); - - icon_name = tp_account_get_icon_name (account); - image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_widget_show (image); - - label = gtk_label_new (str); - gtk_label_set_use_markup (GTK_LABEL (label), TRUE); - gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); - gtk_widget_show (label); - g_free (str); - - content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar)); - gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0); - - image = gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON); - upgrade_button = gtk_button_new (); - gtk_button_set_image (GTK_BUTTON (upgrade_button), image); - gtk_widget_set_tooltip_text (upgrade_button, _("Update software...")); - gtk_widget_show (upgrade_button); - - image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON); - close_button = gtk_button_new (); - gtk_button_set_image (GTK_BUTTON (close_button), image); - gtk_widget_set_tooltip_text (close_button, _("Close")); - gtk_widget_show (close_button); - - action_grid = gtk_grid_new (); - gtk_grid_set_column_spacing (GTK_GRID (action_grid), 2); - gtk_widget_show (action_grid); - - action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar)); - gtk_box_pack_start (GTK_BOX (action_area), action_grid, FALSE, FALSE, 0); - - gtk_grid_attach (GTK_GRID (action_grid), upgrade_button, 0, 0, 1, 1); - gtk_grid_attach (GTK_GRID (action_grid), close_button, 1, 0, 1, 1); - - g_object_set_data (G_OBJECT (info_bar), "label", label); - g_object_set_data_full (G_OBJECT (info_bar), - "account", g_object_ref (account), - g_object_unref); - g_object_set_data_full (G_OBJECT (upgrade_button), - "account", g_object_ref (account), - g_object_unref); - g_object_set_data_full (G_OBJECT (close_button), - "account", g_object_ref (account), - g_object_unref); - - g_signal_connect (upgrade_button, "clicked", - G_CALLBACK (main_window_error_upgrade_sw_clicked_cb), - window); - g_signal_connect (close_button, "clicked", - G_CALLBACK (main_window_error_close_clicked_cb), - window); - - gtk_widget_set_tooltip_text (priv->errors_vbox, error_message); - gtk_widget_show (priv->errors_vbox); - - g_hash_table_insert (priv->errors, g_object_ref (account), info_bar); -} - -static void -main_window_error_display (EmpathyMainWindow *window, - TpAccount *account) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkWidget *info_bar; - GtkWidget *content_area; - GtkWidget *label; - GtkWidget *image; - GtkWidget *retry_button; - GtkWidget *edit_button; - GtkWidget *close_button; - GtkWidget *action_area; - GtkWidget *action_grid; - gchar *str; - const gchar *icon_name; - const gchar *error_message; - gboolean user_requested; - - if (!tp_strdiff (TP_ERROR_STR_SOFTWARE_UPGRADE_REQUIRED, - tp_account_get_detailed_error (account, NULL))) { - main_window_upgrade_software_error (window, account); - return; - } - - error_message = - empathy_account_get_error_message (account, &user_requested); - - if (user_requested) { - return; - } - - str = g_markup_printf_escaped ("<b>%s</b>\n%s", - tp_account_get_display_name (account), - error_message); - - info_bar = g_hash_table_lookup (priv->errors, account); - if (info_bar) { - label = g_object_get_data (G_OBJECT (info_bar), "label"); - - /* Just set the latest error and return */ - gtk_label_set_markup (GTK_LABEL (label), str); - g_free (str); - - return; - } - - info_bar = gtk_info_bar_new (); - gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_ERROR); - - gtk_widget_set_no_show_all (info_bar, TRUE); - gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar, FALSE, TRUE, 0); - gtk_widget_show (info_bar); - - icon_name = tp_account_get_icon_name (account); - image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_widget_show (image); - - label = gtk_label_new (str); - gtk_label_set_use_markup (GTK_LABEL (label), TRUE); - gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); - gtk_widget_show (label); - g_free (str); - - content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar)); - gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0); - - image = gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON); - retry_button = gtk_button_new (); - gtk_button_set_image (GTK_BUTTON (retry_button), image); - gtk_widget_set_tooltip_text (retry_button, _("Reconnect")); - gtk_widget_show (retry_button); - - image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON); - edit_button = gtk_button_new (); - gtk_button_set_image (GTK_BUTTON (edit_button), image); - gtk_widget_set_tooltip_text (edit_button, _("Edit Account")); - gtk_widget_show (edit_button); - - image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON); - close_button = gtk_button_new (); - gtk_button_set_image (GTK_BUTTON (close_button), image); - gtk_widget_set_tooltip_text (close_button, _("Close")); - gtk_widget_show (close_button); - - action_grid = gtk_grid_new (); - gtk_grid_set_column_spacing (GTK_GRID (action_grid), 2); - gtk_widget_show (action_grid); - - action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar)); - gtk_box_pack_start (GTK_BOX (action_area), action_grid, FALSE, FALSE, 0); - - gtk_grid_attach (GTK_GRID (action_grid), retry_button, 0, 0, 1, 1); - gtk_grid_attach (GTK_GRID (action_grid), edit_button, 1, 0, 1, 1); - gtk_grid_attach (GTK_GRID (action_grid), close_button, 2, 0, 1, 1); - - g_object_set_data (G_OBJECT (info_bar), "label", label); - g_object_set_data_full (G_OBJECT (info_bar), - "account", g_object_ref (account), - g_object_unref); - g_object_set_data_full (G_OBJECT (edit_button), - "account", g_object_ref (account), - g_object_unref); - g_object_set_data_full (G_OBJECT (close_button), - "account", g_object_ref (account), - g_object_unref); - g_object_set_data_full (G_OBJECT (retry_button), - "account", g_object_ref (account), - g_object_unref); - - g_signal_connect (edit_button, "clicked", - G_CALLBACK (main_window_error_edit_clicked_cb), - window); - g_signal_connect (close_button, "clicked", - G_CALLBACK (main_window_error_close_clicked_cb), - window); - g_signal_connect (retry_button, "clicked", - G_CALLBACK (main_window_error_retry_clicked_cb), - window); - - gtk_widget_set_tooltip_text (priv->errors_vbox, error_message); - gtk_widget_show (priv->errors_vbox); - - g_hash_table_insert (priv->errors, g_object_ref (account), info_bar); -} - -static void -main_window_update_status (EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - gboolean connected, connecting; - GList *l, *children; - - connected = empathy_account_manager_get_accounts_connected (&connecting); - - /* Update the spinner state */ - if (connecting) { - gtk_spinner_start (GTK_SPINNER (priv->throbber)); - gtk_widget_show (priv->throbber_tool_item); - } else { - gtk_spinner_stop (GTK_SPINNER (priv->throbber)); - gtk_widget_hide (priv->throbber_tool_item); - } - - /* Update widgets sensibility */ - for (l = priv->actions_connected; l; l = l->next) { - gtk_action_set_sensitive (l->data, connected); - } - - /* Update favourite rooms sensitivity */ - children = gtk_container_get_children (GTK_CONTAINER (priv->room_menu)); - for (l = children; l != NULL; l = l->next) { - if (g_object_get_data (G_OBJECT (l->data), "is_favorite") != NULL) { - gtk_widget_set_sensitive (GTK_WIDGET (l->data), connected); - } - } - g_list_free (children); -} - -static char * -main_window_account_to_action_name (TpAccount *account) -{ - char *r; - - /* action names can't have '/' in them, replace it with '.' */ - r = g_strdup (tp_account_get_path_suffix (account)); - r = g_strdelimit (r, "/", '.'); - - return r; -} - -static void -main_window_balance_activate_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - const char *uri; - - uri = g_object_get_data (G_OBJECT (action), "manage-credit-uri"); - - if (!tp_str_empty (uri)) { - DEBUG ("Top-up credit URI: %s", uri); - empathy_url_show (GTK_WIDGET (window), uri); - } else { - DEBUG ("unknown protocol for top-up"); - } -} - -static void -main_window_balance_update_balance (GtkAction *action, - TpConnection *conn) -{ - TpAccount *account = tp_connection_get_account (conn); - GtkWidget *label; - int amount = 0; - guint scale = G_MAXINT32; - const gchar *currency = ""; - char *money, *str; - - if (!tp_connection_get_balance (conn, &amount, &scale, ¤cy)) - return; - - if (amount == 0 && - scale == G_MAXINT32 && - tp_str_empty (currency)) { - /* unknown balance */ - money = g_strdup ("--"); - } else { - char *tmp = empathy_format_currency (amount, scale, currency); - - money = g_strdup_printf ("%s %s", currency, tmp); - g_free (tmp); - } - - /* Translators: this string will be something like: - * Top up My Account ($1.23)..." */ - str = g_strdup_printf (_("Top up %s (%s)..."), - tp_account_get_display_name (account), - money); - - gtk_action_set_label (action, str); - g_free (str); - - /* update the money label in the roster */ - label = g_object_get_data (G_OBJECT (action), "money-label"); - - gtk_label_set_text (GTK_LABEL (label), money); - g_free (money); -} - -static void -main_window_balance_changed_cb (TpConnection *conn, - guint balance, - guint scale, - const gchar *currency, - GtkAction *action) -{ - main_window_balance_update_balance (action, conn); -} - -static GtkAction * -main_window_setup_balance_create_action (EmpathyMainWindow *window, - TpAccount *account) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkAction *action; - char *name, *ui; - guint merge_id; - GError *error = NULL; - - /* create the action group if required */ - if (priv->balance_action_group == NULL) { - priv->balance_action_group = - gtk_action_group_new ("balance-action-group"); - - gtk_ui_manager_insert_action_group (priv->ui_manager, - priv->balance_action_group, -1); - } - - /* create the action */ - name = main_window_account_to_action_name (account); - action = gtk_action_new (name, - tp_account_get_display_name (account), - _("Top up account credit"), - NULL); - g_object_bind_property (account, "icon-name", action, "icon-name", - G_BINDING_SYNC_CREATE); - - g_signal_connect (action, "activate", - G_CALLBACK (main_window_balance_activate_cb), window); - - gtk_action_group_add_action (priv->balance_action_group, action); - g_object_unref (action); - - ui = g_strdup_printf ( - "<ui>" - " <menubar name='menubar'>" - " <menu action='view'>" - " <placeholder name='view_balance_placeholder'>" - " <menuitem action='%s'/>" - " </placeholder>" - " </menu>" - " </menubar>" - "</ui>", - name); - - merge_id = gtk_ui_manager_add_ui_from_string (priv->ui_manager, - ui, -1, &error); - if (error != NULL) { - DEBUG ("Failed to add balance UI for %s: %s", - tp_account_get_display_name (account), - error->message); - g_error_free (error); - } - - g_object_set_data (G_OBJECT (action), - "merge-id", GUINT_TO_POINTER (merge_id)); - - g_free (name); - g_free (ui); - - return action; -} - -static GtkWidget * -main_window_setup_balance_create_widget (EmpathyMainWindow *window, - GtkAction *action, - TpAccount *account) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkWidget *hbox, *image, *label, *button; - - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - - /* protocol icon */ - image = gtk_image_new (); - gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0); - g_object_bind_property (action, "icon-name", image, "icon-name", - G_BINDING_SYNC_CREATE); - - /* account name label */ - label = gtk_label_new (""); - gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); - g_object_bind_property (account, "display-name", label, "label", - G_BINDING_SYNC_CREATE); - - /* balance label */ - label = gtk_label_new (""); - gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); - g_object_set_data (G_OBJECT (action), "money-label", label); - - /* top up button */ - button = gtk_button_new_with_label (_("Top Up...")); - gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0); - g_signal_connect_swapped (button, "clicked", - G_CALLBACK (gtk_action_activate), action); - - gtk_box_pack_start (GTK_BOX (priv->balance_vbox), hbox, FALSE, TRUE, 0); - gtk_widget_show_all (hbox); - - /* tie the lifetime of the widget to the lifetime of the action */ - g_object_weak_ref (G_OBJECT (action), - (GWeakNotify) gtk_widget_destroy, hbox); - - return hbox; -} - -static void -main_window_setup_balance (EmpathyMainWindow *window, - TpAccount *account) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - TpConnection *conn = tp_account_get_connection (account); - GtkAction *action; - const gchar *uri; - - if (conn == NULL) - return; - - if (!tp_proxy_is_prepared (conn, TP_CONNECTION_FEATURE_BALANCE)) - return; - - DEBUG ("Setting up balance for acct: %s", - tp_account_get_display_name (account)); - - /* create the action */ - action = main_window_setup_balance_create_action (window, account); - - if (action == NULL) - return; - - gtk_action_set_visible (priv->view_balance_show_in_roster, TRUE); - - /* create the display widget */ - main_window_setup_balance_create_widget (window, action, account); - - /* check the current balance and monitor for any changes */ - uri = tp_connection_get_balance_uri (conn); - - g_object_set_data_full (G_OBJECT (action), "manage-credit-uri", - g_strdup (uri), g_free); - gtk_action_set_sensitive (GTK_ACTION (action), !tp_str_empty (uri)); - - main_window_balance_update_balance (GTK_ACTION (action), conn); - - g_signal_connect (conn, "balance-changed", - G_CALLBACK (main_window_balance_changed_cb), action); - -} - -static void -main_window_remove_balance_action (EmpathyMainWindow *window, - TpAccount *account) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkAction *action; - char *name; - GList *a; - - if (priv->balance_action_group == NULL) - return; - - name = main_window_account_to_action_name (account); - - action = gtk_action_group_get_action ( - priv->balance_action_group, name); - - if (action != NULL) { - guint merge_id; - - DEBUG ("Removing action"); - - merge_id = GPOINTER_TO_UINT (g_object_get_data ( - G_OBJECT (action), - "merge-id")); - - gtk_ui_manager_remove_ui (priv->ui_manager, - merge_id); - gtk_action_group_remove_action ( - priv->balance_action_group, action); - } - - g_free (name); - - a = gtk_action_group_list_actions ( - priv->balance_action_group); - - gtk_action_set_visible ( - priv->view_balance_show_in_roster, - g_list_length (a) > 0); - - g_list_free (a); -} - -static void -main_window_connection_changed_cb (TpAccount *account, - guint old_status, - guint current, - guint reason, - gchar *dbus_error_name, - GHashTable *details, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - - main_window_update_status (window); - - if (current == TP_CONNECTION_STATUS_DISCONNECTED && - reason != TP_CONNECTION_STATUS_REASON_REQUESTED) { - main_window_error_display (window, account); - } - - if (current == TP_CONNECTION_STATUS_DISCONNECTED) { - empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (window), - EMPATHY_SOUND_ACCOUNT_DISCONNECTED); - - /* remove balance action if required */ - main_window_remove_balance_action (window, account); - } - - if (current == TP_CONNECTION_STATUS_CONNECTED) { - empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (window), - EMPATHY_SOUND_ACCOUNT_CONNECTED); - - /* Account connected without error, remove error message if any */ - main_window_remove_error (window, account); - main_window_setup_balance (window, account); - } -} - -static void -main_window_accels_load (void) -{ - gchar *filename; - - filename = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, ACCELS_FILENAME, NULL); - if (g_file_test (filename, G_FILE_TEST_EXISTS)) { - DEBUG ("Loading from:'%s'", filename); - gtk_accel_map_load (filename); - } - - g_free (filename); -} - -static void -main_window_accels_save (void) -{ - gchar *dir; - gchar *file_with_path; - - dir = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL); - g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR); - file_with_path = g_build_filename (dir, ACCELS_FILENAME, NULL); - g_free (dir); - - DEBUG ("Saving to:'%s'", file_with_path); - gtk_accel_map_save (file_with_path); - - g_free (file_with_path); -} - -static void -empathy_main_window_finalize (GObject *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GHashTableIter iter; - gpointer key, value; - - /* Save user-defined accelerators. */ - main_window_accels_save (); - - g_list_free (priv->actions_connected); - - g_object_unref (priv->account_manager); - g_object_unref (priv->individual_store); - g_object_unref (priv->contact_manager); - g_object_unref (priv->sound_mgr); - g_hash_table_unref (priv->errors); - g_hash_table_unref (priv->auths); - - /* disconnect all handlers of status-changed signal */ - g_hash_table_iter_init (&iter, priv->status_changed_handlers); - while (g_hash_table_iter_next (&iter, &key, &value)) - g_signal_handler_disconnect (TP_ACCOUNT (key), - GPOINTER_TO_UINT (value)); - - g_hash_table_unref (priv->status_changed_handlers); - - g_signal_handlers_disconnect_by_func (priv->event_manager, - main_window_event_added_cb, - window); - g_signal_handlers_disconnect_by_func (priv->event_manager, - main_window_event_removed_cb, - window); - g_object_unref (priv->call_observer); - g_object_unref (priv->event_manager); - g_object_unref (priv->ui_manager); - g_object_unref (priv->chatroom_manager); - - g_object_unref (priv->gsettings_ui); - g_object_unref (priv->gsettings_contacts); - - G_OBJECT_CLASS (empathy_main_window_parent_class)->finalize (window); -} - -static gboolean -main_window_key_press_event_cb (GtkWidget *window, - GdkEventKey *event, - gpointer user_data) -{ - if (event->keyval == GDK_KEY_T - && event->state & GDK_SHIFT_MASK - && event->state & GDK_CONTROL_MASK) { - empathy_chat_manager_call_undo_closed_chat (); - } - return FALSE; -} - -static void -main_window_chat_quit_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - gtk_widget_destroy (GTK_WIDGET (window)); -} - -static void -main_window_view_history_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_log_window_show (NULL, NULL, FALSE, GTK_WINDOW (window)); -} - -static void -main_window_chat_new_message_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_new_message_dialog_show (GTK_WINDOW (window)); -} - -static void -main_window_chat_new_call_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_new_call_dialog_show (GTK_WINDOW (window)); -} - -static void -main_window_chat_add_contact_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_new_individual_dialog_show (GTK_WINDOW (window)); -} - -static void -main_window_chat_search_contacts_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - GtkWidget *dialog = empathy_contact_search_dialog_new ( - GTK_WINDOW (window)); - gtk_widget_show (dialog); -} - -static void -main_window_view_show_ft_manager (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_ft_manager_show (); -} - -static void -main_window_view_show_offline_cb (GtkToggleAction *action, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - gboolean current; - - current = gtk_toggle_action_get_active (action); - g_settings_set_boolean (priv->gsettings_ui, - EMPATHY_PREFS_UI_SHOW_OFFLINE, - current); - - empathy_individual_view_set_show_offline (priv->individual_view, - current); -} - -static void -main_window_notify_sort_contact_cb (GSettings *gsettings, - const gchar *key, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - gchar *str; - - str = g_settings_get_string (gsettings, key); - - if (str != NULL) { - GType type; - GEnumClass *enum_class; - GEnumValue *enum_value; - - type = empathy_individual_store_sort_get_type (); - enum_class = G_ENUM_CLASS (g_type_class_peek (type)); - enum_value = g_enum_get_value_by_nick (enum_class, str); - if (enum_value) { - /* By changing the value of the GtkRadioAction, - it emits a signal that calls main_window_view_sort_contacts_cb - which updates the contacts list */ - gtk_radio_action_set_current_value (priv->sort_by_name, - enum_value->value); - } else { - g_warning ("Wrong value for sort_criterium configuration : %s", str); - } - g_free (str); - } -} - -static void -main_window_view_sort_contacts_cb (GtkRadioAction *action, - GtkRadioAction *current, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - EmpathyIndividualStoreSort value; - GSList *group; - GType type; - GEnumClass *enum_class; - GEnumValue *enum_value; - - value = gtk_radio_action_get_current_value (action); - group = gtk_radio_action_get_group (action); - - /* Get string from index */ - type = empathy_individual_store_sort_get_type (); - enum_class = G_ENUM_CLASS (g_type_class_peek (type)); - enum_value = g_enum_get_value (enum_class, g_slist_index (group, current)); - - if (!enum_value) { - g_warning ("No GEnumValue for EmpathyContactListSort with GtkRadioAction index:%d", - g_slist_index (group, action)); - } else { - g_settings_set_string (priv->gsettings_contacts, - EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM, - enum_value->value_nick); - } - empathy_individual_store_set_sort_criterium (priv->individual_store, - value); -} - -static void -main_window_view_show_protocols_cb (GtkToggleAction *action, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - gboolean value; - - value = gtk_toggle_action_get_active (action); - - g_settings_set_boolean (priv->gsettings_ui, - EMPATHY_PREFS_UI_SHOW_PROTOCOLS, - value); - empathy_individual_store_set_show_protocols (priv->individual_store, - value); -} - -/* Matches GtkRadioAction values set in empathy-main-window.ui */ -#define CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS 0 -#define CONTACT_LIST_NORMAL_SIZE 1 -#define CONTACT_LIST_COMPACT_SIZE 2 - -static void -main_window_view_contacts_list_size_cb (GtkRadioAction *action, - GtkRadioAction *current, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GSettings *gsettings_ui; - gint value; - - value = gtk_radio_action_get_current_value (action); - /* create a new GSettings, so we can delay the setting until both - * values are set */ - gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA); - - DEBUG ("radio button toggled, value = %i", value); - - g_settings_delay (gsettings_ui); - g_settings_set_boolean (gsettings_ui, - EMPATHY_PREFS_UI_SHOW_AVATARS, - value == CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS); - - g_settings_set_boolean (gsettings_ui, - EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST, - value == CONTACT_LIST_COMPACT_SIZE); - g_settings_apply (gsettings_ui); - - /* FIXME: these enums probably have the wrong namespace */ - empathy_individual_store_set_show_avatars (priv->individual_store, - value == CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS); - empathy_individual_store_set_is_compact (priv->individual_store, - value == CONTACT_LIST_COMPACT_SIZE); - - g_object_unref (gsettings_ui); -} - -static void main_window_notify_show_protocols_cb (GSettings *gsettings, - const gchar *key, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - - gtk_toggle_action_set_active (priv->show_protocols, - g_settings_get_boolean (gsettings, - EMPATHY_PREFS_UI_SHOW_PROTOCOLS)); -} - - -static void -main_window_notify_contact_list_size_cb (GSettings *gsettings, - const gchar *key, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - gint value; - - if (g_settings_get_boolean (gsettings, - EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST)) { - value = CONTACT_LIST_COMPACT_SIZE; - } else if (g_settings_get_boolean (gsettings, - EMPATHY_PREFS_UI_SHOW_AVATARS)) { - value = CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS; - } else { - value = CONTACT_LIST_NORMAL_SIZE; - } - - DEBUG ("setting changed, value = %i", value); - - /* By changing the value of the GtkRadioAction, - it emits a signal that calls main_window_view_contacts_list_size_cb - which updates the contacts list */ - gtk_radio_action_set_current_value (priv->normal_with_avatars, value); -} - -static void -main_window_edit_search_contacts_cb (GtkCheckMenuItem *item, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - - empathy_individual_view_start_search (priv->individual_view); -} - -static void -main_window_view_show_map_cb (GtkCheckMenuItem *item, - EmpathyMainWindow *window) -{ -#ifdef HAVE_LIBCHAMPLAIN - empathy_map_view_show (); -#endif -} - -static void -join_chatroom (EmpathyChatroom *chatroom, - gint64 timestamp) -{ - TpAccount *account; - const gchar *room; - - account = empathy_chatroom_get_account (chatroom); - room = empathy_chatroom_get_room (chatroom); - - DEBUG ("Requesting channel for '%s'", room); - empathy_join_muc (account, room, timestamp); -} - -typedef struct -{ - TpAccount *account; - EmpathyChatroom *chatroom; - gint64 timestamp; - glong sig_id; - guint timeout; -} join_fav_account_sig_ctx; - -static join_fav_account_sig_ctx * -join_fav_account_sig_ctx_new (TpAccount *account, - EmpathyChatroom *chatroom, - gint64 timestamp) -{ - join_fav_account_sig_ctx *ctx = g_slice_new0 ( - join_fav_account_sig_ctx); - - ctx->account = g_object_ref (account); - ctx->chatroom = g_object_ref (chatroom); - ctx->timestamp = timestamp; - return ctx; -} - -static void -join_fav_account_sig_ctx_free (join_fav_account_sig_ctx *ctx) -{ - g_object_unref (ctx->account); - g_object_unref (ctx->chatroom); - g_slice_free (join_fav_account_sig_ctx, ctx); -} - -static void -account_status_changed_cb (TpAccount *account, - TpConnectionStatus old_status, - TpConnectionStatus new_status, - guint reason, - gchar *dbus_error_name, - GHashTable *details, - gpointer user_data) -{ - join_fav_account_sig_ctx *ctx = user_data; - - switch (new_status) { - case TP_CONNECTION_STATUS_DISCONNECTED: - /* Don't wait any longer */ - goto finally; - break; - - case TP_CONNECTION_STATUS_CONNECTING: - /* Wait a bit */ - return; - - case TP_CONNECTION_STATUS_CONNECTED: - /* We can join the room */ - break; - - default: - g_assert_not_reached (); - } - - join_chatroom (ctx->chatroom, ctx->timestamp); - -finally: - g_source_remove (ctx->timeout); - g_signal_handler_disconnect (account, ctx->sig_id); -} - -#define JOIN_FAVORITE_TIMEOUT 5 - -static gboolean -join_favorite_timeout_cb (gpointer data) -{ - join_fav_account_sig_ctx *ctx = data; - - /* stop waiting for joining the favorite room */ - g_signal_handler_disconnect (ctx->account, ctx->sig_id); - return FALSE; -} - -static void -main_window_favorite_chatroom_join (EmpathyChatroom *chatroom) -{ - TpAccount *account; - - account = empathy_chatroom_get_account (chatroom); - if (tp_account_get_connection_status (account, NULL) != - TP_CONNECTION_STATUS_CONNECTED) { - join_fav_account_sig_ctx *ctx; - - ctx = join_fav_account_sig_ctx_new (account, chatroom, - empathy_get_current_action_time ()); - - ctx->sig_id = g_signal_connect_data (account, "status-changed", - G_CALLBACK (account_status_changed_cb), ctx, - (GClosureNotify) join_fav_account_sig_ctx_free, 0); - - ctx->timeout = g_timeout_add_seconds (JOIN_FAVORITE_TIMEOUT, - join_favorite_timeout_cb, ctx); - return; - } - - join_chatroom (chatroom, empathy_get_current_action_time ()); -} - -static void -main_window_favorite_chatroom_menu_activate_cb (GtkMenuItem *menu_item, - EmpathyChatroom *chatroom) -{ - main_window_favorite_chatroom_join (chatroom); -} - -static void -main_window_favorite_chatroom_menu_add (EmpathyMainWindow *window, - EmpathyChatroom *chatroom) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkWidget *menu_item; - const gchar *name, *account_name; - gchar *label; - - - if (g_object_get_data (G_OBJECT (chatroom), "menu_item")) { - return; - } - - name = empathy_chatroom_get_name (chatroom); - account_name = tp_account_get_display_name ( - empathy_chatroom_get_account (chatroom)); - label = g_strdup_printf ("%s (%s)", name, account_name); - menu_item = gtk_menu_item_new_with_label (label); - g_free (label); - g_object_set_data (G_OBJECT (menu_item), "is_favorite", - GUINT_TO_POINTER (TRUE)); - - g_object_set_data (G_OBJECT (chatroom), "menu_item", menu_item); - g_signal_connect (menu_item, "activate", - G_CALLBACK (main_window_favorite_chatroom_menu_activate_cb), - chatroom); - - gtk_menu_shell_insert (GTK_MENU_SHELL (priv->room_menu), - menu_item, 4); - - gtk_widget_show (menu_item); -} - -static void -main_window_favorite_chatroom_menu_added_cb (EmpathyChatroomManager *manager, - EmpathyChatroom *chatroom, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - - main_window_favorite_chatroom_menu_add (window, chatroom); - gtk_widget_show (priv->room_separator); - gtk_action_set_sensitive (priv->room_join_favorites, TRUE); -} - -static void -main_window_favorite_chatroom_menu_removed_cb (EmpathyChatroomManager *manager, - EmpathyChatroom *chatroom, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkWidget *menu_item; - GList *chatrooms; - - menu_item = g_object_get_data (G_OBJECT (chatroom), "menu_item"); - g_object_set_data (G_OBJECT (chatroom), "menu_item", NULL); - gtk_widget_destroy (menu_item); - - chatrooms = empathy_chatroom_manager_get_chatrooms (priv->chatroom_manager, NULL); - if (chatrooms) { - gtk_widget_show (priv->room_separator); - } else { - gtk_widget_hide (priv->room_separator); - } - - gtk_action_set_sensitive (priv->room_join_favorites, chatrooms != NULL); - g_list_free (chatrooms); -} - -static void -main_window_favorite_chatroom_menu_setup (EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GList *chatrooms, *l; - GtkWidget *room; - - priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL); - chatrooms = empathy_chatroom_manager_get_chatrooms ( - priv->chatroom_manager, NULL); - room = gtk_ui_manager_get_widget (priv->ui_manager, - "/menubar/room"); - priv->room_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (room)); - priv->room_separator = gtk_ui_manager_get_widget (priv->ui_manager, - "/menubar/room/room_separator"); - - for (l = chatrooms; l; l = l->next) { - main_window_favorite_chatroom_menu_add (window, l->data); - } - - if (!chatrooms) { - gtk_widget_hide (priv->room_separator); - } - - gtk_action_set_sensitive (priv->room_join_favorites, chatrooms != NULL); - - g_signal_connect (priv->chatroom_manager, "chatroom-added", - G_CALLBACK (main_window_favorite_chatroom_menu_added_cb), - window); - g_signal_connect (priv->chatroom_manager, "chatroom-removed", - G_CALLBACK (main_window_favorite_chatroom_menu_removed_cb), - window); - - g_list_free (chatrooms); -} - -static void -main_window_room_join_new_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_new_chatroom_dialog_show (GTK_WINDOW (window)); -} - -static void -main_window_room_join_favorites_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GList *chatrooms, *l; - - chatrooms = empathy_chatroom_manager_get_chatrooms (priv->chatroom_manager, NULL); - for (l = chatrooms; l; l = l->next) { - main_window_favorite_chatroom_join (l->data); - } - g_list_free (chatrooms); -} - -static void -main_window_room_manage_favorites_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_chatrooms_window_show (GTK_WINDOW (window)); -} - -static void -main_window_edit_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GtkWidget *submenu; - - /* FIXME: It should use the UIManager to merge the contact/group submenu */ - submenu = empathy_individual_view_get_individual_menu ( - priv->individual_view); - if (submenu) { - GtkMenuItem *item; - GtkWidget *label; - - item = GTK_MENU_ITEM (priv->edit_context); - label = gtk_bin_get_child (GTK_BIN (item)); - gtk_label_set_text (GTK_LABEL (label), _("Contact")); - - gtk_widget_show (priv->edit_context); - gtk_widget_show (priv->edit_context_separator); - - gtk_menu_item_set_submenu (item, submenu); - - return; - } - - submenu = empathy_individual_view_get_group_menu ( - priv->individual_view); - if (submenu) { - GtkMenuItem *item; - GtkWidget *label; - - item = GTK_MENU_ITEM (priv->edit_context); - label = gtk_bin_get_child (GTK_BIN (item)); - gtk_label_set_text (GTK_LABEL (label), _("Group")); - - gtk_widget_show (priv->edit_context); - gtk_widget_show (priv->edit_context_separator); - - gtk_menu_item_set_submenu (item, submenu); - - return; - } - - gtk_widget_hide (priv->edit_context); - gtk_widget_hide (priv->edit_context_separator); - - return; -} - -static void -main_window_edit_accounts_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_accounts_dialog_show_application (gdk_screen_get_default (), - NULL, FALSE, FALSE); -} - -static void -main_window_edit_personal_information_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_contact_personal_dialog_show (GTK_WINDOW (window)); -} - -static void -main_window_edit_blocked_contacts_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - GtkWidget *dialog; - - dialog = empathy_contact_blocking_dialog_new (GTK_WINDOW (window)); - gtk_widget_show (dialog); - g_signal_connect (dialog, "response", - G_CALLBACK (gtk_widget_destroy), NULL); -} - -void -empathy_main_window_show_preferences (EmpathyMainWindow *window, - const gchar *tab) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - - if (priv->preferences == NULL) { - priv->preferences = empathy_preferences_new (GTK_WINDOW (window), - priv->shell_running); - g_object_add_weak_pointer (G_OBJECT (priv->preferences), - (gpointer) &priv->preferences); - - gtk_widget_show (priv->preferences); - } else { - gtk_window_present (GTK_WINDOW (priv->preferences)); - } - - if (tab != NULL) - empathy_preferences_show_tab ( - EMPATHY_PREFERENCES (priv->preferences), tab); -} - -static void -main_window_edit_preferences_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_main_window_show_preferences (window, NULL); -} - -static void -main_window_help_about_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_about_dialog_new (GTK_WINDOW (window)); -} - -static void -main_window_help_debug_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_launch_program (BIN_DIR, "empathy-debugger", NULL); -} - -static void -main_window_help_contents_cb (GtkAction *action, - EmpathyMainWindow *window) -{ - empathy_url_show (GTK_WIDGET (window), "ghelp:empathy"); -} - -static gboolean -main_window_throbber_button_press_event_cb (GtkWidget *throbber, - GdkEventButton *event, - EmpathyMainWindow *window) -{ - if (event->type != GDK_BUTTON_PRESS || - event->button != 1) { - return FALSE; - } - - empathy_accounts_dialog_show_application ( - gtk_widget_get_screen (GTK_WIDGET (throbber)), - NULL, FALSE, FALSE); - - return FALSE; -} - -static void -main_window_account_removed_cb (TpAccountManager *manager, - TpAccount *account, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GList *a; - - a = tp_account_manager_get_valid_accounts (manager); - - gtk_action_set_sensitive (priv->view_history, - g_list_length (a) > 0); - - g_list_free (a); - - /* remove errors if any */ - main_window_remove_error (window, account); - - /* remove the balance action if required */ - main_window_remove_balance_action (window, account); -} - -static void -main_window_account_validity_changed_cb (TpAccountManager *manager, - TpAccount *account, - gboolean valid, - EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - - if (valid) { - gulong handler_id; - handler_id = GPOINTER_TO_UINT (g_hash_table_lookup ( - priv->status_changed_handlers, account)); - - /* connect signal only if it was not connected yet */ - if (handler_id == 0) { - handler_id = g_signal_connect (account, - "status-changed", - G_CALLBACK (main_window_connection_changed_cb), - window); - g_hash_table_insert (priv->status_changed_handlers, - account, GUINT_TO_POINTER (handler_id)); - } - } - - main_window_account_removed_cb (manager, account, window); -} - -static void -main_window_notify_show_offline_cb (GSettings *gsettings, - const gchar *key, - gpointer toggle_action) -{ - gtk_toggle_action_set_active (toggle_action, - g_settings_get_boolean (gsettings, key)); -} - -static void -main_window_connection_items_setup (EmpathyMainWindow *window, - GtkBuilder *gui) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GList *list; - GObject *action; - guint i; - const gchar *actions_connected[] = { - "room_join_new", - "room_join_favorites", - "chat_new_message", - "chat_new_call", - "chat_search_contacts", - "chat_add_contact", - "edit_personal_information", - "edit_blocked_contacts", - "edit_search_contacts" - }; - - for (i = 0, list = NULL; i < G_N_ELEMENTS (actions_connected); i++) { - action = gtk_builder_get_object (gui, actions_connected[i]); - list = g_list_prepend (list, action); - } - - priv->actions_connected = list; -} - -static void -account_manager_prepared_cb (GObject *source_object, - GAsyncResult *result, - gpointer user_data) -{ - GList *accounts, *j; - TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object); - EmpathyMainWindow *window = user_data; - EmpathyMainWindowPriv *priv = GET_PRIV (window); - GError *error = NULL; - - if (!tp_proxy_prepare_finish (manager, result, &error)) { - DEBUG ("Failed to prepare account manager: %s", error->message); - g_error_free (error); - return; - } - - accounts = tp_account_manager_get_valid_accounts (priv->account_manager); - for (j = accounts; j != NULL; j = j->next) { - TpAccount *account = TP_ACCOUNT (j->data); - gulong handler_id; - - handler_id = g_signal_connect (account, "status-changed", - G_CALLBACK (main_window_connection_changed_cb), - window); - g_hash_table_insert (priv->status_changed_handlers, - account, GUINT_TO_POINTER (handler_id)); - - main_window_setup_balance (window, account); - } - - g_signal_connect (manager, "account-validity-changed", - G_CALLBACK (main_window_account_validity_changed_cb), - window); - - main_window_update_status (window); - - /* Disable the "Previous Conversations" menu entry if there is no account */ - gtk_action_set_sensitive (priv->view_history, - g_list_length (accounts) > 0); - - g_list_free (accounts); -} - -void -empathy_main_window_set_shell_running (EmpathyMainWindow *window, - gboolean shell_running) -{ - EmpathyMainWindowPriv *priv = GET_PRIV (window); - - if (priv->shell_running == shell_running) - return; - - priv->shell_running = shell_running; - g_object_notify (G_OBJECT (window), "shell-running"); -} - -static GObject * -empathy_main_window_constructor (GType type, - guint n_construct_params, - GObjectConstructParam *construct_params) -{ - static GObject *window = NULL; - - if (window != NULL) - return g_object_ref (window); - - window = G_OBJECT_CLASS (empathy_main_window_parent_class)->constructor ( - type, n_construct_params, construct_params); - - g_object_add_weak_pointer (window, (gpointer) &window); - - return window; -} - -static void -empathy_main_window_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - EmpathyMainWindow *self = EMPATHY_MAIN_WINDOW (object); - EmpathyMainWindowPriv *priv = GET_PRIV (self); - - switch (property_id) - { - case PROP_SHELL_RUNNING: - priv->shell_running = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -empathy_main_window_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - EmpathyMainWindow *self = EMPATHY_MAIN_WINDOW (object); - EmpathyMainWindowPriv *priv = GET_PRIV (self); - - switch (property_id) - { - case PROP_SHELL_RUNNING: - g_value_set_boolean (value, priv->shell_running); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -empathy_main_window_class_init (EmpathyMainWindowClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GParamSpec *pspec; - - object_class->finalize = empathy_main_window_finalize; - object_class->constructor = empathy_main_window_constructor; - - object_class->set_property = empathy_main_window_set_property; - object_class->get_property = empathy_main_window_get_property; - - pspec = g_param_spec_boolean ("shell-running", - "Shell running", - "Whether the Shell is running or not", - FALSE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_SHELL_RUNNING, pspec); - - g_type_class_add_private (object_class, sizeof (EmpathyMainWindowPriv)); -} - -static void -empathy_main_window_init (EmpathyMainWindow *window) -{ - EmpathyMainWindowPriv *priv; - EmpathyIndividualManager *individual_manager; - GtkBuilder *gui, *gui_mgr; - GtkWidget *sw; - GtkToggleAction *show_offline_widget; - GtkAction *show_map_widget; - GtkToolItem *item; - gboolean show_offline; - gchar *filename; - GtkTreeModel *model; - GtkWidget *search_vbox; - GtkWidget *menubar; - - priv = window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, - EMPATHY_TYPE_MAIN_WINDOW, EmpathyMainWindowPriv); - - priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA); - priv->gsettings_contacts = g_settings_new (EMPATHY_PREFS_CONTACTS_SCHEMA); - - priv->sound_mgr = empathy_sound_manager_dup_singleton (); - - gtk_window_set_title (GTK_WINDOW (window), _("Contact List")); - gtk_window_set_role (GTK_WINDOW (window), "contact_list"); - gtk_window_set_default_size (GTK_WINDOW (window), 225, 325); - - /* don't finalize the widget on delete-event, just hide it */ - g_signal_connect (window, "delete-event", - G_CALLBACK (gtk_widget_hide_on_delete), NULL); - - /* Set up interface */ - filename = empathy_file_lookup ("empathy-main-window.ui", "src"); - gui = empathy_builder_get_file (filename, - "main_vbox", &priv->main_vbox, - "balance_vbox", &priv->balance_vbox, - "errors_vbox", &priv->errors_vbox, - "auth_vbox", &priv->auth_vbox, - "search_vbox", &search_vbox, - "presence_toolbar", &priv->presence_toolbar, - "notebook", &priv->notebook, - "no_entry_label", &priv->no_entry_label, - "roster_scrolledwindow", &sw, - NULL); - g_free (filename); - - /* Set UI manager */ - filename = empathy_file_lookup ("empathy-main-window-menubar.ui", "src"); - gui_mgr = empathy_builder_get_file (filename, - "ui_manager", &priv->ui_manager, - "view_show_offline", &show_offline_widget, - "view_show_protocols", &priv->show_protocols, - "view_sort_by_name", &priv->sort_by_name, - "view_sort_by_status", &priv->sort_by_status, - "view_normal_size_with_avatars", &priv->normal_with_avatars, - "view_normal_size", &priv->normal_size, - "view_compact_size", &priv->compact_size, - "view_history", &priv->view_history, - "view_show_map", &show_map_widget, - "room_join_favorites", &priv->room_join_favorites, - "view_balance_show_in_roster", &priv->view_balance_show_in_roster, - "menubar", &menubar, - NULL); - g_free (filename); - - /* The UI manager is living in its own .ui file as Glade doesn't support - * those. The GtkMenubar has to be in this file as well to we manually add - * it to the first position of the vbox. */ - gtk_box_pack_start (GTK_BOX (priv->main_vbox), menubar, FALSE, FALSE, 0); - gtk_box_reorder_child (GTK_BOX (priv->main_vbox), menubar, 0); - - gtk_container_add (GTK_CONTAINER (window), priv->main_vbox); - gtk_widget_show (priv->main_vbox); - - g_signal_connect (window, "key-press-event", - G_CALLBACK (main_window_key_press_event_cb), NULL); - - empathy_builder_connect (gui_mgr, window, - "chat_quit", "activate", main_window_chat_quit_cb, - "chat_new_message", "activate", main_window_chat_new_message_cb, - "chat_new_call", "activate", main_window_chat_new_call_cb, - "view_history", "activate", main_window_view_history_cb, - "room_join_new", "activate", main_window_room_join_new_cb, - "room_join_favorites", "activate", main_window_room_join_favorites_cb, - "room_manage_favorites", "activate", main_window_room_manage_favorites_cb, - "chat_add_contact", "activate", main_window_chat_add_contact_cb, - "chat_search_contacts", "activate", main_window_chat_search_contacts_cb, - "view_show_ft_manager", "activate", main_window_view_show_ft_manager, - "view_show_offline", "toggled", main_window_view_show_offline_cb, - "view_show_protocols", "toggled", main_window_view_show_protocols_cb, - "view_sort_by_name", "changed", main_window_view_sort_contacts_cb, - "view_normal_size_with_avatars", "changed", main_window_view_contacts_list_size_cb, - "view_show_map", "activate", main_window_view_show_map_cb, - "edit", "activate", main_window_edit_cb, - "edit_accounts", "activate", main_window_edit_accounts_cb, - "edit_personal_information", "activate", main_window_edit_personal_information_cb, - "edit_blocked_contacts", "activate", main_window_edit_blocked_contacts_cb, - "edit_preferences", "activate", main_window_edit_preferences_cb, - "edit_search_contacts", "activate", main_window_edit_search_contacts_cb, - "help_about", "activate", main_window_help_about_cb, - "help_debug", "activate", main_window_help_debug_cb, - "help_contents", "activate", main_window_help_contents_cb, - NULL); - - /* Set up connection related widgets. */ - main_window_connection_items_setup (window, gui_mgr); - - g_object_ref (priv->ui_manager); - g_object_unref (gui); - g_object_unref (gui_mgr); - -#ifndef HAVE_LIBCHAMPLAIN - gtk_action_set_visible (show_map_widget, FALSE); -#endif - - priv->account_manager = tp_account_manager_dup (); - - tp_proxy_prepare_async (priv->account_manager, NULL, - account_manager_prepared_cb, window); - - priv->errors = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - g_object_unref, - NULL); - - priv->auths = g_hash_table_new (NULL, NULL); - - priv->status_changed_handlers = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - NULL); - - /* Set up menu */ - main_window_favorite_chatroom_menu_setup (window); - - priv->edit_context = gtk_ui_manager_get_widget (priv->ui_manager, - "/menubar/edit/edit_context"); - priv->edit_context_separator = gtk_ui_manager_get_widget ( - priv->ui_manager, - "/menubar/edit/edit_context_separator"); - gtk_widget_hide (priv->edit_context); - gtk_widget_hide (priv->edit_context_separator); - - /* Set up contact list. */ - empathy_status_presets_get_all (); - - /* Set up presence chooser */ - priv->presence_chooser = empathy_presence_chooser_new (); - gtk_widget_show (priv->presence_chooser); - item = gtk_tool_item_new (); - gtk_widget_show (GTK_WIDGET (item)); - gtk_widget_set_size_request (priv->presence_chooser, 10, -1); - gtk_container_add (GTK_CONTAINER (item), priv->presence_chooser); - gtk_tool_item_set_is_important (item, TRUE); - gtk_tool_item_set_expand (item, TRUE); - gtk_toolbar_insert (GTK_TOOLBAR (priv->presence_toolbar), item, -1); - - /* Set up the throbber */ - priv->throbber = gtk_spinner_new (); - gtk_widget_set_size_request (priv->throbber, 16, -1); - gtk_widget_set_events (priv->throbber, GDK_BUTTON_PRESS_MASK); - g_signal_connect (priv->throbber, "button-press-event", - G_CALLBACK (main_window_throbber_button_press_event_cb), - window); - gtk_widget_show (priv->throbber); - - item = gtk_tool_item_new (); - gtk_container_set_border_width (GTK_CONTAINER (item), 6); - gtk_toolbar_insert (GTK_TOOLBAR (priv->presence_toolbar), item, -1); - gtk_container_add (GTK_CONTAINER (item), priv->throbber); - priv->throbber_tool_item = GTK_WIDGET (item); - - /* XXX: this class is designed to live for the duration of the program, - * so it's got a race condition between its signal handlers and its - * finalization. The class is planned to be removed, so we won't fix - * this before then. */ - priv->contact_manager = EMPATHY_CONTACT_LIST ( - empathy_contact_manager_dup_singleton ()); - individual_manager = empathy_individual_manager_dup_singleton (); - priv->individual_store = EMPATHY_INDIVIDUAL_STORE ( - empathy_individual_store_manager_new (individual_manager)); - g_object_unref (individual_manager); - - /* For the moment, we disallow Persona drops onto the main contact list (e.g. from things such as - * the EmpathyPersonaView in the linking dialogue). No code is hooked up to do anything on a Persona - * drop, so allowing them would achieve nothing except confusion. */ - priv->individual_view = empathy_individual_view_new ( - priv->individual_store, - EMPATHY_INDIVIDUAL_VIEW_FEATURE_ALL ^ EMPATHY_INDIVIDUAL_VIEW_FEATURE_PERSONA_DROP, - EMPATHY_INDIVIDUAL_FEATURE_ALL ^ EMPATHY_INDIVIDUAL_FEATURE_ADD_CONTACT); - - gtk_widget_show (GTK_WIDGET (priv->individual_view)); - gtk_container_add (GTK_CONTAINER (sw), - GTK_WIDGET (priv->individual_view)); - g_signal_connect (priv->individual_view, "row-activated", - G_CALLBACK (main_window_row_activated_cb), - window); - - /* Set up search bar */ - priv->search_bar = empathy_live_search_new ( - GTK_WIDGET (priv->individual_view)); - empathy_individual_view_set_live_search (priv->individual_view, - EMPATHY_LIVE_SEARCH (priv->search_bar)); - gtk_box_pack_start (GTK_BOX (search_vbox), priv->search_bar, - FALSE, TRUE, 0); - - g_signal_connect_swapped (window, "map", - G_CALLBACK (gtk_widget_grab_focus), priv->individual_view); - - /* Connect to proper signals to check if contact list is empty or not */ - model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->individual_view)); - priv->empty = TRUE; - g_signal_connect (model, "row-inserted", - G_CALLBACK (main_window_row_inserted_cb), - window); - g_signal_connect (model, "row-deleted", - G_CALLBACK (main_window_row_deleted_cb), - window); - - /* Load user-defined accelerators. */ - main_window_accels_load (); - - /* Set window size. */ - empathy_geometry_bind (GTK_WINDOW (window), GEOMETRY_NAME); - - /* bind view_balance_show_in_roster */ - g_settings_bind (priv->gsettings_ui, "show-balance-in-roster", - priv->view_balance_show_in_roster, "active", - G_SETTINGS_BIND_DEFAULT); - g_object_bind_property (priv->view_balance_show_in_roster, "active", - priv->balance_vbox, "visible", - G_BINDING_SYNC_CREATE); - - /* Enable event handling */ - priv->call_observer = empathy_call_observer_dup_singleton (); - priv->event_manager = empathy_event_manager_dup_singleton (); - - g_signal_connect (priv->event_manager, "event-added", - G_CALLBACK (main_window_event_added_cb), window); - g_signal_connect (priv->event_manager, "event-removed", - G_CALLBACK (main_window_event_removed_cb), window); - g_signal_connect (priv->account_manager, "account-validity-changed", - G_CALLBACK (main_window_account_validity_changed_cb), - window); - g_signal_connect (priv->account_manager, "account-removed", - G_CALLBACK (main_window_account_removed_cb), - window); - g_signal_connect (priv->account_manager, "account-disabled", - G_CALLBACK (main_window_account_disabled_cb), - window); - - /* Show offline ? */ - show_offline = g_settings_get_boolean (priv->gsettings_ui, - EMPATHY_PREFS_UI_SHOW_OFFLINE); - g_signal_connect (priv->gsettings_ui, - "changed::" EMPATHY_PREFS_UI_SHOW_OFFLINE, - G_CALLBACK (main_window_notify_show_offline_cb), - show_offline_widget); - - gtk_toggle_action_set_active (show_offline_widget, show_offline); - - /* Show protocol ? */ - g_signal_connect (priv->gsettings_ui, - "changed::" EMPATHY_PREFS_UI_SHOW_PROTOCOLS, - G_CALLBACK (main_window_notify_show_protocols_cb), - window); - - main_window_notify_show_protocols_cb (priv->gsettings_ui, - EMPATHY_PREFS_UI_SHOW_PROTOCOLS, - window); - - /* Sort by name / by status ? */ - g_signal_connect (priv->gsettings_contacts, - "changed::" EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM, - G_CALLBACK (main_window_notify_sort_contact_cb), - window); - - main_window_notify_sort_contact_cb (priv->gsettings_contacts, - EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM, - window); - - /* Contacts list size */ - g_signal_connect (priv->gsettings_ui, - "changed::" EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST, - G_CALLBACK (main_window_notify_contact_list_size_cb), - window); - g_signal_connect (priv->gsettings_ui, - "changed::" EMPATHY_PREFS_UI_SHOW_AVATARS, - G_CALLBACK (main_window_notify_contact_list_size_cb), - window); - - main_window_notify_contact_list_size_cb (priv->gsettings_ui, - EMPATHY_PREFS_UI_SHOW_AVATARS, - window); -} - -GtkWidget * -empathy_main_window_dup (void) -{ - return g_object_new (EMPATHY_TYPE_MAIN_WINDOW, NULL); -} diff --git a/src/empathy-main-window.h b/src/empathy-main-window.h deleted file mode 100644 index af4c1924e..000000000 --- a/src/empathy-main-window.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2002-2007 Imendio AB - * Copyright (C) 2007-2008 Collabora Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - * - * Authors: Xavier Claessens <xclaesse@gmail.com> - * Danielle Madeley <danielle.madeley@collabora.co.uk> - */ - -#ifndef __EMPATHY_MAIN_WINDOW_H__ -#define __EMPATHY_MAIN_WINDOW_H__ - -#include <gtk/gtk.h> - -G_BEGIN_DECLS - -#define EMPATHY_TYPE_MAIN_WINDOW (empathy_main_window_get_type ()) -#define EMPATHY_MAIN_WINDOW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_MAIN_WINDOW, EmpathyMainWindow)) -#define EMPATHY_MAIN_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_MAIN_WINDOW, EmpathyMainWindowClass)) -#define EMPATHY_IS_MAIN_WINDOW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_MAIN_WINDOW)) -#define EMPATHY_IS_MAIN_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_MAIN_WINDOW)) -#define EMPATHY_MAIN_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_MAIN_WINDOW, EmpathyMainWindowClass)) - -typedef struct _EmpathyMainWindow EmpathyMainWindow; -typedef struct _EmpathyMainWindowClass EmpathyMainWindowClass; -typedef struct _EmpathyMainWindowPriv EmpathyMainWindowPriv; - -struct _EmpathyMainWindow { - GtkWindow parent; - gpointer priv; -}; - -struct _EmpathyMainWindowClass { - GtkWindowClass parent_class; -}; - -GType empathy_main_window_get_type (void); - -GtkWidget *empathy_main_window_dup (void); - -void empathy_main_window_show_preferences (EmpathyMainWindow *window, - const gchar *tab); -void empathy_main_window_set_shell_running (EmpathyMainWindow *window, - gboolean shell_running); - -G_END_DECLS - -#endif /* __EMPATHY_MAIN_WINDOW_H__ */ diff --git a/src/empathy-map-view.c b/src/empathy-map-view.c index 72aab0b2c..9ab82c091 100644 --- a/src/empathy-map-view.c +++ b/src/empathy-map-view.c @@ -31,11 +31,11 @@ #include <telepathy-glib/util.h> #include <libempathy/empathy-contact.h> -#include <libempathy/empathy-contact-manager.h> +#include <libempathy/empathy-connection-aggregator.h> #include <libempathy/empathy-utils.h> #include <libempathy/empathy-location.h> -#include <libempathy-gtk/empathy-contact-menu.h> +#include <libempathy-gtk/empathy-individual-menu.h> #include <libempathy-gtk/empathy-ui-utils.h> #include "empathy-map-view.h" @@ -48,7 +48,7 @@ G_DEFINE_TYPE (EmpathyMapView, empathy_map_view, GTK_TYPE_WINDOW); #define GET_PRIV(self) ((EmpathyMapViewPriv *)((EmpathyMapView *) self)->priv) struct _EmpathyMapViewPriv { - EmpathyContactList *contact_list; + EmpathyConnectionAggregator *aggregator; GtkWidget *zoom_in; GtkWidget *zoom_out; @@ -59,6 +59,9 @@ struct _EmpathyMapViewPriv { /* reffed (EmpathyContact *) => borrowed (ChamplainMarker *) */ GHashTable *markers; gulong members_changed_id; + + /* TpContact -> EmpathyContact */ + GHashTable *contacts; }; static void @@ -189,28 +192,41 @@ marker_clicked_cb (ChamplainMarker *marker, { GtkWidget *menu; EmpathyContact *contact; + TpContact *tp_contact; + FolksIndividual *individual; if (event->button != 3) return FALSE; contact = g_object_get_data (G_OBJECT (marker), "contact"); + if (contact == NULL) + return FALSE; - menu = empathy_contact_menu_new (contact, - EMPATHY_CONTACT_FEATURE_CHAT | - EMPATHY_CONTACT_FEATURE_CALL | - EMPATHY_CONTACT_FEATURE_LOG | - EMPATHY_CONTACT_FEATURE_FT | - EMPATHY_CONTACT_FEATURE_INFO); + tp_contact = empathy_contact_get_tp_contact (contact); + if (tp_contact == NULL) + return FALSE; - if (menu == NULL) + individual = empathy_create_individual_from_tp_contact (tp_contact); + if (individual == NULL) return FALSE; + menu = empathy_individual_menu_new (individual, + EMPATHY_INDIVIDUAL_FEATURE_CHAT | + EMPATHY_INDIVIDUAL_FEATURE_CALL | + EMPATHY_INDIVIDUAL_FEATURE_LOG | + EMPATHY_INDIVIDUAL_FEATURE_INFO, NULL); + + if (menu == NULL) + goto out; + gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (self), NULL); gtk_widget_show (menu); gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, event->time); +out: + g_object_unref (individual); return FALSE; } @@ -303,16 +319,6 @@ create_marker (EmpathyMapView *self, return marker; } -static void -contact_added (EmpathyMapView *self, - EmpathyContact *contact) -{ - g_signal_connect (contact, "notify::location", - G_CALLBACK (map_view_contact_location_notify), self); - - map_view_update_contact_position (self, contact); -} - static gboolean map_view_key_press_cb (GtkWidget *widget, GdkEventKey *event, @@ -344,36 +350,55 @@ map_view_tick (EmpathyMapView *self) } static void -contact_removed (EmpathyMapView *self, - EmpathyContact *contact) +contact_list_changed_cb (EmpathyConnectionAggregator *aggregator, + GPtrArray *added, + GPtrArray *removed, + EmpathyMapView *self) { EmpathyMapViewPriv *priv = GET_PRIV (self); - ClutterActor *marker; + guint i; - marker = g_hash_table_lookup (priv->markers, contact); - if (marker == NULL) - return; + for (i = 0; i < added->len; i++) + { + TpContact *tp_contact = g_ptr_array_index (added, i); + EmpathyContact *contact; - clutter_actor_destroy (marker); - g_hash_table_remove (priv->markers, contact); -} + if (g_hash_table_lookup (priv->contacts, tp_contact) != NULL) + continue; -static void -members_changed_cb (EmpathyContactList *list, - EmpathyContact *contact, - EmpathyContact *actor, - guint reason, - gchar *message, - gboolean is_member, - EmpathyMapView *self) -{ - if (is_member) - { - contact_added (self, contact); + contact = empathy_contact_dup_from_tp_contact (tp_contact); + + tp_g_signal_connect_object (contact, "notify::location", + G_CALLBACK (map_view_contact_location_notify), self, 0); + + map_view_update_contact_position (self, contact); + + /* Pass ownership to the hash table */ + g_hash_table_insert (priv->contacts, g_object_ref (tp_contact), + contact); } - else + + for (i = 0; i < removed->len; i++) { - contact_removed (self, contact); + TpContact *tp_contact = g_ptr_array_index (removed, i); + EmpathyContact *contact; + ClutterActor *marker; + + contact = g_hash_table_lookup (priv->contacts, tp_contact); + if (contact == NULL) + continue; + + marker = g_hash_table_lookup (priv->markers, contact); + if (marker != NULL) + { + clutter_actor_destroy (marker); + g_hash_table_remove (priv->markers, contact); + } + + g_signal_handlers_disconnect_by_func (contact, + map_view_contact_location_notify, self); + + g_hash_table_remove (priv->contacts, tp_contact); } } @@ -409,12 +434,10 @@ empathy_map_view_finalize (GObject *object) g_signal_handlers_disconnect_by_func (contact, map_view_contact_location_notify, object); - g_signal_handler_disconnect (priv->contact_list, - priv->members_changed_id); - g_hash_table_unref (priv->markers); - g_object_unref (priv->contact_list); + g_object_unref (priv->aggregator); g_object_unref (priv->layer); + g_hash_table_unref (priv->contacts); G_OBJECT_CLASS (empathy_map_view_parent_class)->finalize (object); } @@ -439,7 +462,7 @@ empathy_map_view_init (EmpathyMapView *self) GtkWidget *embed; GtkWidget *throbber_holder; gchar *filename; - GList *members, *l; + GPtrArray *contacts, *empty; GtkWidget *main_vbox; priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, @@ -474,12 +497,6 @@ empathy_map_view_init (EmpathyMapView *self) g_object_unref (gui); - priv->contact_list = EMPATHY_CONTACT_LIST ( - empathy_contact_manager_dup_singleton ()); - - priv->members_changed_id = g_signal_connect (priv->contact_list, - "members-changed", G_CALLBACK (members_changed_cb), self); - priv->throbber = gtk_spinner_new (); gtk_widget_set_size_request (priv->throbber, 16, 16); gtk_container_add (GTK_CONTAINER (throbber_holder), priv->throbber); @@ -506,14 +523,20 @@ empathy_map_view_init (EmpathyMapView *self) priv->markers = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) g_object_unref, NULL); - members = empathy_contact_list_get_members ( - priv->contact_list); - for (l = members; l != NULL; l = g_list_next (l)) - { - contact_added (self, l->data); - g_object_unref (l->data); - } - g_list_free (members); + priv->aggregator = empathy_connection_aggregator_dup_singleton (); + priv->contacts = g_hash_table_new_full (NULL, NULL, g_object_unref, + g_object_unref); + + tp_g_signal_connect_object (priv->aggregator, "contact-list-changed", + G_CALLBACK (contact_list_changed_cb), self, 0); + + contacts = empathy_connection_aggregator_dup_all_contacts (priv->aggregator); + empty = g_ptr_array_new (); + + contact_list_changed_cb (priv->aggregator, contacts, empty, self); + + g_ptr_array_unref (contacts); + g_ptr_array_unref (empty); /* Set up time updating loop */ priv->timeout_id = g_timeout_add_seconds (5, diff --git a/src/empathy-new-chatroom-dialog.c b/src/empathy-new-chatroom-dialog.c index 8176c4803..df30c2a85 100644 --- a/src/empathy-new-chatroom-dialog.c +++ b/src/empathy-new-chatroom-dialog.c @@ -395,13 +395,21 @@ static void update_join_button_sensitivity (EmpathyNewChatroomDialog *dialog) { const gchar *room; + const gchar *protocol; gboolean sensitive = FALSE; room = gtk_entry_get_text (GTK_ENTRY (dialog->entry_room)); + protocol = tp_account_get_protocol (dialog->account); if (EMP_STR_EMPTY (room)) goto out; + if (!tp_strdiff (protocol, "irc") && (!tp_strdiff (room, "#") || + !tp_strdiff (room, "&"))) + { + goto out; + } + if (dialog->account == NULL) goto out; @@ -441,10 +449,16 @@ new_chatroom_dialog_update_widgets (EmpathyNewChatroomDialog *dialog) gtk_widget_set_sensitive (dialog->entry_server, TRUE); } + if (!tp_strdiff (protocol, "irc")) + gtk_entry_set_text (GTK_ENTRY (dialog->entry_room), "#"); + else + gtk_entry_set_text (GTK_ENTRY (dialog->entry_room), ""); + update_join_button_sensitivity (dialog); /* Final set up of the dialog */ gtk_widget_grab_focus (dialog->entry_room); + gtk_editable_set_position (GTK_EDITABLE (dialog->entry_room), -1); } static void diff --git a/src/empathy-preferences.c b/src/empathy-preferences.c index 6775d4a24..77ec49c26 100644 --- a/src/empathy-preferences.c +++ b/src/empathy-preferences.c @@ -67,7 +67,7 @@ struct _EmpathyPreferencesPriv { GtkWidget *checkbutton_show_smileys; GtkWidget *checkbutton_show_contacts_in_rooms; - GtkWidget *checkbutton_separate_chat_windows; + GtkWidget *radiobutton_chats_new_windows; GtkWidget *checkbutton_events_notif_area; GtkWidget *checkbutton_autoconnect; GtkWidget *checkbutton_logging; @@ -166,7 +166,7 @@ static SoundEventEntry sound_entries [] = { { N_("Message received"), EMPATHY_PREFS_SOUNDS_INCOMING_MESSAGE }, { N_("Message sent"), EMPATHY_PREFS_SOUNDS_OUTGOING_MESSAGE }, { N_("New conversation"), EMPATHY_PREFS_SOUNDS_NEW_CONVERSATION }, - { N_("Contact goes online"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGIN }, + { N_("Contact comes online"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGIN }, { N_("Contact goes offline"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGOUT }, { N_("Account connected"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGIN }, { N_("Account disconnected"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGOUT } @@ -248,7 +248,7 @@ preferences_setup_widgets (EmpathyPreferences *preferences) g_settings_bind (priv->gsettings_ui, EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS, - priv->checkbutton_separate_chat_windows, + priv->radiobutton_chats_new_windows, "active", G_SETTINGS_BIND_DEFAULT); @@ -678,7 +678,8 @@ static void preferences_preview_theme_append_message (EmpathyChatView *view, EmpathyContact *sender, EmpathyContact *receiver, - const gchar *text) + const gchar *text, + gboolean should_highlight) { EmpathyMessage *message; @@ -688,7 +689,7 @@ preferences_preview_theme_append_message (EmpathyChatView *view, "body", text, NULL); - empathy_chat_view_append_message (view, message); + empathy_chat_view_append_message (view, message, should_highlight); g_object_unref (message); } @@ -735,19 +736,20 @@ preferences_preview_theme_changed_cb (EmpathyThemeManager *manager, preferences_preview_theme_append_message (priv->chat_theme_preview, /* translators: Quote from Romeo & Julier, for chat theme preview */ - juliet, romeo, _("O Romeo, Romeo, wherefore art thou Romeo?")); + juliet, romeo, _("O Romeo, Romeo, wherefore art thou Romeo?"), + TRUE /* this message mentions Romeo */); preferences_preview_theme_append_message (priv->chat_theme_preview, /* translators: Quote from Romeo & Julier, for chat theme preview */ - juliet, romeo, _("Deny thy father and refuse thy name;")); + juliet, romeo, _("Deny thy father and refuse thy name;"), FALSE); preferences_preview_theme_append_message (priv->chat_theme_preview, /* translators: Quote from Romeo & Julier, for chat theme preview */ - juliet, romeo, _("Or if thou wilt not, be but sworn my love")); + juliet, romeo, _("Or if thou wilt not, be but sworn my love"), FALSE); preferences_preview_theme_append_message (priv->chat_theme_preview, /* translators: Quote from Romeo & Julier, for chat theme preview */ - juliet, romeo, _("And I'll no longer be a Capulet.")); + juliet, romeo, _("And I'll no longer be a Capulet."), FALSE); preferences_preview_theme_append_message (priv->chat_theme_preview, /* translators: Quote from Romeo & Julier, for chat theme preview */ - romeo, juliet, _("Shall I hear more, or shall I speak at this?")); + romeo, juliet, _("Shall I hear more, or shall I speak at this?"), FALSE); /* translators: Quote from Romeo & Julier, for chat theme preview */ empathy_chat_view_append_event (priv->chat_theme_preview, _("Juliet has disconnected")); @@ -1170,7 +1172,7 @@ empathy_preferences_init (EmpathyPreferences *preferences) "combobox_chat_theme_variant", &priv->combobox_chat_theme_variant, "hbox_chat_theme_variant", &priv->hbox_chat_theme_variant, "sw_chat_theme_preview", &priv->sw_chat_theme_preview, - "checkbutton_separate_chat_windows", &priv->checkbutton_separate_chat_windows, + "radiobutton_chats_new_windows", &priv->radiobutton_chats_new_windows, "checkbutton_events_notif_area", &priv->checkbutton_events_notif_area, "checkbutton_autoconnect", &priv->checkbutton_autoconnect, "checkbutton_logging", &priv->checkbutton_logging, diff --git a/src/empathy-preferences.ui b/src/empathy-preferences.ui index 03a628cb6..f13f6c0b2 100644 --- a/src/empathy-preferences.ui +++ b/src/empathy-preferences.ui @@ -107,14 +107,47 @@ <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> - <object class="GtkCheckButton" id="checkbutton_separate_chat_windows"> - <property name="label" translatable="yes">_Open new chats in separate windows</property> + <object class="GtkBox" id="vbox2180"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_action_appearance">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="can_focus">False</property> + <property name="orientation">horizontal</property> + <property name="spacing">3</property> + <child> + <object class="GtkLabel" id="label1000"> + <property name="label" translatable="yes">Start chats in:</property> + <property name="visible">True</property> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="radiobutton_chats_new_tabs"> + <property name="label" translatable="yes">new ta_bs</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="radiobutton_chats_new_windows"> + <property name="label" translatable="yes">new _windows</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <property name="group">radiobutton_chats_new_tabs</property> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> </object> <packing> <property name="expand">False</property> diff --git a/src/empathy-main-window-menubar.ui b/src/empathy-roster-window-menubar.ui index 0bb5d14f0..d342abca8 100644 --- a/src/empathy-main-window-menubar.ui +++ b/src/empathy-roster-window-menubar.ui @@ -109,12 +109,6 @@ <accelerator key="F4" modifiers=""/> </child> <child> - <object class="GtkAction" id="edit_personal_information"> - <property name="name">edit_personal_information</property> - <property name="label" translatable="yes">_Personal Information</property> - </object> - </child> - <child> <object class="GtkAction" id="edit_blocked_contacts"> <property name="name">edit_blocked_contacts</property> <property name="label" translatable="yes">_Blocked Contacts</property> @@ -263,7 +257,6 @@ <menuitem action="edit_context"/> <separator name="edit_context_separator"/> <menuitem action="edit_accounts"/> - <menuitem action="edit_personal_information"/> <menuitem action="edit_search_contacts"/> <menuitem action="edit_blocked_contacts"/> <separator/> diff --git a/src/empathy-roster-window.c b/src/empathy-roster-window.c new file mode 100644 index 000000000..454cd256a --- /dev/null +++ b/src/empathy-roster-window.c @@ -0,0 +1,2753 @@ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007-2010 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + * Danielle Madeley <danielle.madeley@collabora.co.uk> + */ + +#include <config.h> + +#include <sys/stat.h> +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include <glib/gi18n.h> + +#include <telepathy-glib/account-manager.h> +#include <telepathy-glib/util.h> +#include <folks/folks.h> + +#include <libempathy/empathy-contact.h> +#include <libempathy/empathy-utils.h> +#include <libempathy/empathy-request-util.h> +#include <libempathy/empathy-chatroom-manager.h> +#include <libempathy/empathy-chatroom.h> +#include <libempathy/empathy-contact-list.h> +#include <libempathy/empathy-gsettings.h> +#include <libempathy/empathy-individual-manager.h> +#include <libempathy/empathy-gsettings.h> +#include <libempathy/empathy-status-presets.h> +#include <libempathy/empathy-tp-contact-factory.h> + +#include <libempathy-gtk/empathy-contact-dialogs.h> +#include <libempathy-gtk/empathy-live-search.h> +#include <libempathy-gtk/empathy-contact-blocking-dialog.h> +#include <libempathy-gtk/empathy-contact-search-dialog.h> +#include <libempathy-gtk/empathy-geometry.h> +#include <libempathy-gtk/empathy-gtk-enum-types.h> +#include <libempathy-gtk/empathy-individual-dialogs.h> +#include <libempathy-gtk/empathy-individual-store.h> +#include <libempathy-gtk/empathy-individual-store-manager.h> +#include <libempathy-gtk/empathy-individual-view.h> +#include <libempathy-gtk/empathy-new-message-dialog.h> +#include <libempathy-gtk/empathy-new-call-dialog.h> +#include <libempathy-gtk/empathy-log-window.h> +#include <libempathy-gtk/empathy-presence-chooser.h> +#include <libempathy-gtk/empathy-sound-manager.h> +#include <libempathy-gtk/empathy-ui-utils.h> + +#include "empathy-accounts-dialog.h" +#include "empathy-call-observer.h" +#include "empathy-chat-manager.h" +#include "empathy-roster-window.h" +#include "empathy-preferences.h" +#include "empathy-about-dialog.h" +#include "empathy-debug-window.h" +#include "empathy-new-chatroom-dialog.h" +#include "empathy-map-view.h" +#include "empathy-chatrooms-window.h" +#include "empathy-event-manager.h" +#include "empathy-ft-manager.h" + +#define DEBUG_FLAG EMPATHY_DEBUG_OTHER +#include <libempathy/empathy-debug.h> + +/* Flashing delay for icons (milliseconds). */ +#define FLASH_TIMEOUT 500 + +/* Minimum width of roster window if something goes wrong. */ +#define MIN_WIDTH 50 + +/* Accels (menu shortcuts) can be configured and saved */ +#define ACCELS_FILENAME "accels.txt" + +/* Name in the geometry file */ +#define GEOMETRY_NAME "roster-window" + +enum +{ + PAGE_CONTACT_LIST = 0, + PAGE_MESSAGE +}; + +enum +{ + PROP_0, + PROP_SHELL_RUNNING +}; + +G_DEFINE_TYPE (EmpathyRosterWindow, empathy_roster_window, GTK_TYPE_WINDOW); + +struct _EmpathyRosterWindowPriv { + EmpathyIndividualStore *individual_store; + EmpathyIndividualView *individual_view; + TpAccountManager *account_manager; + EmpathyChatroomManager *chatroom_manager; + EmpathyEventManager *event_manager; + EmpathySoundManager *sound_mgr; + EmpathyCallObserver *call_observer; + EmpathyIndividualManager *individual_manager; + guint flash_timeout_id; + gboolean flash_on; + gboolean empty; + + GSettings *gsettings_ui; + GSettings *gsettings_contacts; + + GtkWidget *preferences; + GtkWidget *main_vbox; + GtkWidget *throbber; + GtkWidget *throbber_tool_item; + GtkWidget *presence_toolbar; + GtkWidget *presence_chooser; + GtkWidget *errors_vbox; + GtkWidget *auth_vbox; + GtkWidget *search_bar; + GtkWidget *notebook; + GtkWidget *no_entry_label; + GtkWidget *button_account_settings; + GtkWidget *spinner_loading; + + GtkToggleAction *show_protocols; + GtkRadioAction *sort_by_name; + GtkRadioAction *sort_by_status; + GtkRadioAction *normal_with_avatars; + GtkRadioAction *normal_size; + GtkRadioAction *compact_size; + + GtkUIManager *ui_manager; + GtkAction *view_history; + GtkAction *room_join_favorites; + GtkWidget *room_menu; + GtkWidget *room_separator; + GtkWidget *edit_context; + GtkWidget *edit_context_separator; + + GtkActionGroup *balance_action_group; + GtkAction *view_balance_show_in_roster; + GtkWidget *balance_vbox; + + guint size_timeout_id; + + /* reffed TpAccount* => visible GtkInfoBar* */ + GHashTable *errors; + + /* EmpathyEvent* => visible GtkInfoBar* */ + GHashTable *auths; + + /* stores a mapping from TpAccount to Handler ID to prevent + * to listen more than once to the status-changed signal */ + GHashTable *status_changed_handlers; + + /* Actions that are enabled when there are connected accounts */ + GList *actions_connected; + + gboolean shell_running; +}; + +static void +roster_window_flash_stop (EmpathyRosterWindow *self) +{ + if (self->priv->flash_timeout_id == 0) + return; + + DEBUG ("Stop flashing"); + g_source_remove (self->priv->flash_timeout_id); + self->priv->flash_timeout_id = 0; + self->priv->flash_on = FALSE; +} + +typedef struct +{ + EmpathyEvent *event; + gboolean on; + EmpathyRosterWindow *self; +} FlashForeachData; + +static gboolean +roster_window_flash_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + FlashForeachData *data = (FlashForeachData *) user_data; + FolksIndividual *individual; + EmpathyContact *contact; + const gchar *icon_name; + GtkTreePath *parent_path = NULL; + GtkTreeIter parent_iter; + GdkPixbuf *pixbuf = NULL; + + gtk_tree_model_get (model, iter, + EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual, + -1); + + if (individual == NULL) + return FALSE; + + contact = empathy_contact_dup_from_folks_individual (individual); + if (contact != data->event->contact) + goto out; + + if (data->on) + { + icon_name = data->event->icon_name; + pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU); + } + else + { + pixbuf = empathy_individual_store_get_individual_status_icon ( + data->self->priv->individual_store, + individual); + if (pixbuf != NULL) + g_object_ref (pixbuf); + } + + gtk_tree_store_set (GTK_TREE_STORE (model), iter, + EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS, pixbuf, + -1); + + /* To make sure the parent is shown correctly, we emit + * the row-changed signal on the parent so it prompts + * it to be refreshed by the filter func. + */ + if (gtk_tree_model_iter_parent (model, &parent_iter, iter)) + { + parent_path = gtk_tree_model_get_path (model, &parent_iter); + } + + if (parent_path != NULL) + { + gtk_tree_model_row_changed (model, parent_path, &parent_iter); + gtk_tree_path_free (parent_path); + } + +out: + g_object_unref (individual); + tp_clear_object (&contact); + tp_clear_object (&pixbuf); + + return FALSE; +} + +static gboolean +roster_window_flash_cb (EmpathyRosterWindow *self) +{ + GtkTreeModel *model; + GSList *events, *l; + gboolean found_event = FALSE; + FlashForeachData data; + + self->priv->flash_on = !self->priv->flash_on; + data.on = self->priv->flash_on; + model = GTK_TREE_MODEL (self->priv->individual_store); + + events = empathy_event_manager_get_events (self->priv->event_manager); + for (l = events; l; l = l->next) + { + data.event = l->data; + data.self = self; + if (!data.event->contact || !data.event->must_ack) + continue; + + found_event = TRUE; + gtk_tree_model_foreach (model, + roster_window_flash_foreach, + &data); + } + + if (!found_event) + roster_window_flash_stop (self); + + return TRUE; +} + +static void +roster_window_flash_start (EmpathyRosterWindow *self) +{ + if (self->priv->flash_timeout_id != 0) + return; + + DEBUG ("Start flashing"); + self->priv->flash_timeout_id = g_timeout_add (FLASH_TIMEOUT, + (GSourceFunc) roster_window_flash_cb, self); + + roster_window_flash_cb (self); +} + +static void +roster_window_remove_auth (EmpathyRosterWindow *self, + EmpathyEvent *event) +{ + GtkWidget *error_widget; + + error_widget = g_hash_table_lookup (self->priv->auths, event); + if (error_widget != NULL) + { + gtk_widget_destroy (error_widget); + g_hash_table_remove (self->priv->auths, event); + } +} + +static void +roster_window_auth_add_clicked_cb (GtkButton *button, + EmpathyRosterWindow *self) +{ + EmpathyEvent *event; + + event = g_object_get_data (G_OBJECT (button), "event"); + + empathy_event_approve (event); + + roster_window_remove_auth (self, event); +} + +static void +roster_window_auth_close_clicked_cb (GtkButton *button, + EmpathyRosterWindow *self) +{ + EmpathyEvent *event; + + event = g_object_get_data (G_OBJECT (button), "event"); + + empathy_event_decline (event); + roster_window_remove_auth (self, event); +} + +static void +roster_window_auth_display (EmpathyRosterWindow *self, + EmpathyEvent *event) +{ + TpAccount *account = event->account; + GtkWidget *info_bar; + GtkWidget *content_area; + GtkWidget *image; + GtkWidget *label; + GtkWidget *add_button; + GtkWidget *close_button; + GtkWidget *action_area; + GtkWidget *action_grid; + const gchar *icon_name; + gchar *str; + + if (g_hash_table_lookup (self->priv->auths, event) != NULL) + return; + + info_bar = gtk_info_bar_new (); + gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_QUESTION); + + gtk_widget_set_no_show_all (info_bar, TRUE); + gtk_box_pack_start (GTK_BOX (self->priv->auth_vbox), info_bar, FALSE, TRUE, 0); + gtk_widget_show (info_bar); + + icon_name = tp_account_get_icon_name (account); + image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_show (image); + + str = g_markup_printf_escaped ("<b>%s</b>\n%s", + tp_account_get_display_name (account), + _("Password required")); + + label = gtk_label_new (str); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_widget_show (label); + + g_free (str); + + content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar)); + gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0); + + image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON); + add_button = gtk_button_new (); + gtk_button_set_image (GTK_BUTTON (add_button), image); + gtk_widget_set_tooltip_text (add_button, _("Provide Password")); + gtk_widget_show (add_button); + + image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON); + close_button = gtk_button_new (); + gtk_button_set_image (GTK_BUTTON (close_button), image); + gtk_widget_set_tooltip_text (close_button, _("Disconnect")); + gtk_widget_show (close_button); + + action_grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (action_grid), 6); + gtk_widget_show (action_grid); + + action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar)); + gtk_box_pack_start (GTK_BOX (action_area), action_grid, FALSE, FALSE, 0); + + gtk_grid_attach (GTK_GRID (action_grid), add_button, 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (action_grid), close_button, 1, 0, 1, 1); + + g_object_set_data_full (G_OBJECT (info_bar), + "event", event, NULL); + g_object_set_data_full (G_OBJECT (add_button), + "event", event, NULL); + g_object_set_data_full (G_OBJECT (close_button), + "event", event, NULL); + + g_signal_connect (add_button, "clicked", + G_CALLBACK (roster_window_auth_add_clicked_cb), self); + g_signal_connect (close_button, "clicked", + G_CALLBACK (roster_window_auth_close_clicked_cb), self); + + gtk_widget_show (self->priv->auth_vbox); + + g_hash_table_insert (self->priv->auths, event, info_bar); +} + +static void +modify_event_count (GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyEvent *event, + gboolean increase) +{ + FolksIndividual *individual; + EmpathyContact *contact; + guint count; + + gtk_tree_model_get (model, iter, + EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual, + EMPATHY_INDIVIDUAL_STORE_COL_EVENT_COUNT, &count, + -1); + + if (individual == NULL) + return; + + increase ? count++ : count--; + + contact = empathy_contact_dup_from_folks_individual (individual); + if (contact == event->contact) + gtk_tree_store_set (GTK_TREE_STORE (model), iter, + EMPATHY_INDIVIDUAL_STORE_COL_EVENT_COUNT, count, -1); + + tp_clear_object (&contact); + g_object_unref (individual); +} + +static gboolean +increase_event_count_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + EmpathyEvent *event = user_data; + + modify_event_count (model, iter, event, TRUE); + + return FALSE; +} + +static void +increase_event_count (EmpathyRosterWindow *self, + EmpathyEvent *event) +{ + GtkTreeModel *model; + + model = GTK_TREE_MODEL (self->priv->individual_store); + + gtk_tree_model_foreach (model, increase_event_count_foreach, event); +} + +static gboolean +decrease_event_count_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + EmpathyEvent *event = user_data; + + modify_event_count (model, iter, event, FALSE); + + return FALSE; +} + +static void +decrease_event_count (EmpathyRosterWindow *self, + EmpathyEvent *event) +{ + GtkTreeModel *model; + + model = GTK_TREE_MODEL (self->priv->individual_store); + + gtk_tree_model_foreach (model, decrease_event_count_foreach, event); +} + +static void +roster_window_event_added_cb (EmpathyEventManager *manager, + EmpathyEvent *event, + EmpathyRosterWindow *self) +{ + if (event->contact) + { + increase_event_count (self, event); + + roster_window_flash_start (self); + } + else if (event->type == EMPATHY_EVENT_TYPE_AUTH) + { + roster_window_auth_display (self, event); + } +} + +static void +roster_window_event_removed_cb (EmpathyEventManager *manager, + EmpathyEvent *event, + EmpathyRosterWindow *self) +{ + FlashForeachData data; + + if (event->type == EMPATHY_EVENT_TYPE_AUTH) + { + roster_window_remove_auth (self, event); + return; + } + + if (!event->contact) + return; + + decrease_event_count (self, event); + + data.on = FALSE; + data.event = event; + data.self = self; + + gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->individual_store), + roster_window_flash_foreach, + &data); +} + +static gboolean +roster_window_load_events_idle_cb (gpointer user_data) +{ + EmpathyRosterWindow *self = user_data; + GSList *l; + + l = empathy_event_manager_get_events (self->priv->event_manager); + while (l != NULL) + { + roster_window_event_added_cb (self->priv->event_manager, l->data, + self); + l = l->next; + } + + return FALSE; +} + +static void +roster_window_row_activated_cb (EmpathyIndividualView *view, + GtkTreePath *path, + GtkTreeViewColumn *col, + EmpathyRosterWindow *self) +{ + EmpathyContact *contact = NULL; + FolksIndividual *individual; + GtkTreeModel *model; + GtkTreeIter iter; + GSList *events, *l; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->individual_view)); + gtk_tree_model_get_iter (model, &iter, path); + + gtk_tree_model_get (model, &iter, EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, + &individual, -1); + + if (individual != NULL) + contact = empathy_contact_dup_from_folks_individual (individual); + + if (contact == NULL) + goto OUT; + + /* If the contact has an event activate it, otherwise the + * default handler of row-activated will be called. */ + events = empathy_event_manager_get_events (self->priv->event_manager); + for (l = events; l; l = l->next) + { + EmpathyEvent *event = l->data; + + if (event->contact == contact) + { + DEBUG ("Activate event"); + empathy_event_activate (event); + + /* We don't want the default handler of this signal + * (e.g. open a chat) */ + g_signal_stop_emission_by_name (view, "row-activated"); + break; + } + } + + g_object_unref (contact); +OUT: + tp_clear_object (&individual); +} + +static void +button_account_settings_clicked_cb (GtkButton *button, + EmpathyRosterWindow *self) +{ + empathy_accounts_dialog_show_application (gdk_screen_get_default (), + NULL, FALSE, FALSE); +} + +static void +display_page_message (EmpathyRosterWindow *self, + const gchar *msg, + gboolean display_accounts_button, + gboolean display_spinner) +{ + if (msg != NULL) + { + gchar *tmp; + + tmp = g_strdup_printf ("<b><span size='xx-large'>%s</span></b>", msg); + + gtk_label_set_markup (GTK_LABEL (self->priv->no_entry_label), tmp); + g_free (tmp); + + gtk_label_set_line_wrap (GTK_LABEL (self->priv->no_entry_label), TRUE); + gtk_widget_show (self->priv->no_entry_label); + } + else + { + gtk_widget_hide (self->priv->no_entry_label); + } + + gtk_widget_set_visible (self->priv->button_account_settings, + display_accounts_button); + gtk_widget_set_visible (self->priv->spinner_loading, + display_spinner); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), + PAGE_MESSAGE); +} + +static void +display_page_no_account (EmpathyRosterWindow *self) +{ + display_page_message (self, + _("You need to setup an account to see contacts here."), TRUE, FALSE); +} + +static void +roster_window_row_deleted_cb (GtkTreeModel *model, + GtkTreePath *path, + EmpathyRosterWindow *self) +{ + GtkTreeIter help_iter; + + if (!gtk_tree_model_get_iter_first (model, &help_iter)) + { + self->priv->empty = TRUE; + + if (empathy_individual_view_is_searching (self->priv->individual_view)) + { + display_page_message (self, _("No match found"), FALSE, FALSE); + } + } +} + +static void +display_page_contact_list (EmpathyRosterWindow *self) +{ + if (!empathy_individual_manager_get_contacts_loaded ( + self->priv->individual_manager)) + /* We'll display the contact list once we're done loading */ + return; + + gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), + PAGE_CONTACT_LIST); +} + +static void +roster_window_row_inserted_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + EmpathyRosterWindow *self) +{ + if (self->priv->empty) + { + self->priv->empty = FALSE; + + display_page_contact_list (self); + gtk_widget_grab_focus (GTK_WIDGET (self->priv->individual_view)); + + /* The store is being filled, it will be done after an idle cb. + * So we can then get events. If we do that too soon, event's + * contact is not yet in the store and it won't get marked as + * having events. */ + g_idle_add (roster_window_load_events_idle_cb, self); + } +} + +static void +roster_window_remove_error (EmpathyRosterWindow *self, + TpAccount *account) +{ + GtkWidget *error_widget; + + error_widget = g_hash_table_lookup (self->priv->errors, account); + if (error_widget != NULL) + { + gtk_widget_destroy (error_widget); + g_hash_table_remove (self->priv->errors, account); + } +} + +static void +roster_window_account_disabled_cb (TpAccountManager *manager, + TpAccount *account, + EmpathyRosterWindow *self) +{ + roster_window_remove_error (self, account); +} + +static void +roster_window_error_retry_clicked_cb (GtkButton *button, + EmpathyRosterWindow *self) +{ + TpAccount *account; + + account = g_object_get_data (G_OBJECT (button), "account"); + tp_account_reconnect_async (account, NULL, NULL); + + roster_window_remove_error (self, account); +} + +static void +roster_window_error_edit_clicked_cb (GtkButton *button, + EmpathyRosterWindow *self) +{ + TpAccount *account; + + account = g_object_get_data (G_OBJECT (button), "account"); + + empathy_accounts_dialog_show_application ( + gtk_widget_get_screen (GTK_WIDGET (button)), + account, FALSE, FALSE); + + roster_window_remove_error (self, account); +} + +static void +roster_window_error_close_clicked_cb (GtkButton *button, + EmpathyRosterWindow *self) +{ + TpAccount *account; + + account = g_object_get_data (G_OBJECT (button), "account"); + roster_window_remove_error (self, account); +} + +static void +roster_window_error_upgrade_sw_clicked_cb (GtkButton *button, + EmpathyRosterWindow *self) +{ + TpAccount *account; + GtkWidget *dialog; + + account = g_object_get_data (G_OBJECT (button), "account"); + roster_window_remove_error (self, account); + + dialog = gtk_message_dialog_new (GTK_WINDOW (self), + GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Sorry, %s accounts can’t be used until your %s software is updated."), + tp_account_get_protocol (account), + tp_account_get_protocol (account)); + + g_signal_connect_swapped (dialog, "response", + G_CALLBACK (gtk_widget_destroy), + dialog); + + gtk_widget_show (dialog); +} + +static void +roster_window_upgrade_software_error (EmpathyRosterWindow *self, + TpAccount *account) +{ + GtkWidget *info_bar; + GtkWidget *content_area; + GtkWidget *label; + GtkWidget *image; + GtkWidget *upgrade_button; + GtkWidget *close_button; + GtkWidget *action_area; + GtkWidget *action_grid; + gchar *str; + const gchar *icon_name; + const gchar *error_message; + gboolean user_requested; + + error_message = + empathy_account_get_error_message (account, &user_requested); + + if (user_requested) + return; + + str = g_markup_printf_escaped ("<b>%s</b>\n%s", + tp_account_get_display_name (account), + error_message); + + /* If there are other errors, remove them */ + roster_window_remove_error (self, account); + + info_bar = gtk_info_bar_new (); + gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_ERROR); + + gtk_widget_set_no_show_all (info_bar, TRUE); + gtk_box_pack_start (GTK_BOX (self->priv->errors_vbox), info_bar, FALSE, TRUE, 0); + gtk_widget_show (info_bar); + + icon_name = tp_account_get_icon_name (account); + image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_show (image); + + label = gtk_label_new (str); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_widget_show (label); + g_free (str); + + content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar)); + gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0); + + image = gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON); + upgrade_button = gtk_button_new (); + gtk_button_set_image (GTK_BUTTON (upgrade_button), image); + gtk_widget_set_tooltip_text (upgrade_button, _("Update software...")); + gtk_widget_show (upgrade_button); + + image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON); + close_button = gtk_button_new (); + gtk_button_set_image (GTK_BUTTON (close_button), image); + gtk_widget_set_tooltip_text (close_button, _("Close")); + gtk_widget_show (close_button); + + action_grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (action_grid), 2); + gtk_widget_show (action_grid); + + action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar)); + gtk_box_pack_start (GTK_BOX (action_area), action_grid, FALSE, FALSE, 0); + + gtk_grid_attach (GTK_GRID (action_grid), upgrade_button, 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (action_grid), close_button, 1, 0, 1, 1); + + g_object_set_data (G_OBJECT (info_bar), "label", label); + g_object_set_data_full (G_OBJECT (info_bar), + "account", g_object_ref (account), + g_object_unref); + g_object_set_data_full (G_OBJECT (upgrade_button), + "account", g_object_ref (account), + g_object_unref); + g_object_set_data_full (G_OBJECT (close_button), + "account", g_object_ref (account), + g_object_unref); + + g_signal_connect (upgrade_button, "clicked", + G_CALLBACK (roster_window_error_upgrade_sw_clicked_cb), self); + g_signal_connect (close_button, "clicked", + G_CALLBACK (roster_window_error_close_clicked_cb), self); + + gtk_widget_set_tooltip_text (self->priv->errors_vbox, error_message); + gtk_widget_show (self->priv->errors_vbox); + + g_hash_table_insert (self->priv->errors, g_object_ref (account), info_bar); +} + +static void +roster_window_error_display (EmpathyRosterWindow *self, + TpAccount *account) +{ + GtkWidget *info_bar; + GtkWidget *content_area; + GtkWidget *label; + GtkWidget *image; + GtkWidget *retry_button; + GtkWidget *edit_button; + GtkWidget *close_button; + GtkWidget *action_area; + GtkWidget *action_grid; + gchar *str; + const gchar *icon_name; + const gchar *error_message; + gboolean user_requested; + + if (!tp_strdiff (TP_ERROR_STR_SOFTWARE_UPGRADE_REQUIRED, + tp_account_get_detailed_error (account, NULL))) + { + roster_window_upgrade_software_error (self, account); + return; + } + + error_message = empathy_account_get_error_message (account, &user_requested); + + if (user_requested) + return; + + str = g_markup_printf_escaped ("<b>%s</b>\n%s", + tp_account_get_display_name (account), error_message); + + info_bar = g_hash_table_lookup (self->priv->errors, account); + if (info_bar) + { + label = g_object_get_data (G_OBJECT (info_bar), "label"); + + /* Just set the latest error and return */ + gtk_label_set_markup (GTK_LABEL (label), str); + g_free (str); + + return; + } + + info_bar = gtk_info_bar_new (); + gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_ERROR); + + gtk_widget_set_no_show_all (info_bar, TRUE); + gtk_box_pack_start (GTK_BOX (self->priv->errors_vbox), info_bar, FALSE, TRUE, 0); + gtk_widget_show (info_bar); + + icon_name = tp_account_get_icon_name (account); + image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_show (image); + + label = gtk_label_new (str); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_widget_show (label); + g_free (str); + + content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar)); + gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0); + + image = gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON); + retry_button = gtk_button_new (); + gtk_button_set_image (GTK_BUTTON (retry_button), image); + gtk_widget_set_tooltip_text (retry_button, _("Reconnect")); + gtk_widget_show (retry_button); + + image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON); + edit_button = gtk_button_new (); + gtk_button_set_image (GTK_BUTTON (edit_button), image); + gtk_widget_set_tooltip_text (edit_button, _("Edit Account")); + gtk_widget_show (edit_button); + + image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON); + close_button = gtk_button_new (); + gtk_button_set_image (GTK_BUTTON (close_button), image); + gtk_widget_set_tooltip_text (close_button, _("Close")); + gtk_widget_show (close_button); + + action_grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (action_grid), 2); + gtk_widget_show (action_grid); + + action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar)); + gtk_box_pack_start (GTK_BOX (action_area), action_grid, FALSE, FALSE, 0); + + gtk_grid_attach (GTK_GRID (action_grid), retry_button, 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (action_grid), edit_button, 1, 0, 1, 1); + gtk_grid_attach (GTK_GRID (action_grid), close_button, 2, 0, 1, 1); + + g_object_set_data (G_OBJECT (info_bar), "label", label); + g_object_set_data_full (G_OBJECT (info_bar), + "account", g_object_ref (account), + g_object_unref); + g_object_set_data_full (G_OBJECT (edit_button), + "account", g_object_ref (account), + g_object_unref); + g_object_set_data_full (G_OBJECT (close_button), + "account", g_object_ref (account), + g_object_unref); + g_object_set_data_full (G_OBJECT (retry_button), + "account", g_object_ref (account), + g_object_unref); + + g_signal_connect (edit_button, "clicked", + G_CALLBACK (roster_window_error_edit_clicked_cb), self); + g_signal_connect (close_button, "clicked", + G_CALLBACK (roster_window_error_close_clicked_cb), self); + g_signal_connect (retry_button, "clicked", + G_CALLBACK (roster_window_error_retry_clicked_cb), self); + + gtk_widget_set_tooltip_text (self->priv->errors_vbox, error_message); + gtk_widget_show (self->priv->errors_vbox); + + g_hash_table_insert (self->priv->errors, g_object_ref (account), info_bar); +} + +static void +roster_window_update_status (EmpathyRosterWindow *self) +{ + gboolean connected, connecting; + GList *l, *children; + + connected = empathy_account_manager_get_accounts_connected (&connecting); + + /* Update the spinner state */ + if (connecting) + { + gtk_spinner_start (GTK_SPINNER (self->priv->throbber)); + gtk_widget_show (self->priv->throbber_tool_item); + } + else + { + gtk_spinner_stop (GTK_SPINNER (self->priv->throbber)); + gtk_widget_hide (self->priv->throbber_tool_item); + } + + /* Update widgets sensibility */ + for (l = self->priv->actions_connected; l; l = l->next) + gtk_action_set_sensitive (l->data, connected); + + /* Update favourite rooms sensitivity */ + children = gtk_container_get_children (GTK_CONTAINER (self->priv->room_menu)); + for (l = children; l != NULL; l = l->next) + { + if (g_object_get_data (G_OBJECT (l->data), "is_favorite") != NULL) + gtk_widget_set_sensitive (GTK_WIDGET (l->data), connected); + } + + g_list_free (children); +} + +static char * +roster_window_account_to_action_name (TpAccount *account) +{ + char *r; + + /* action names can't have '/' in them, replace it with '.' */ + r = g_strdup (tp_account_get_path_suffix (account)); + r = g_strdelimit (r, "/", '.'); + + return r; +} + +static void +roster_window_balance_activate_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + const char *uri; + + uri = g_object_get_data (G_OBJECT (action), "manage-credit-uri"); + + if (!tp_str_empty (uri)) + { + DEBUG ("Top-up credit URI: %s", uri); + empathy_url_show (GTK_WIDGET (self), uri); + } + else + { + DEBUG ("unknown protocol for top-up"); + } +} + +static void +roster_window_balance_update_balance (GtkAction *action, + TpConnection *conn) +{ + TpAccount *account = tp_connection_get_account (conn); + GtkWidget *label; + int amount = 0; + guint scale = G_MAXINT32; + const gchar *currency = ""; + char *money, *str; + + if (!tp_connection_get_balance (conn, &amount, &scale, ¤cy)) + return; + + if (amount == 0 && + scale == G_MAXINT32 && + tp_str_empty (currency)) + { + /* unknown balance */ + money = g_strdup ("--"); + } + else + { + char *tmp = empathy_format_currency (amount, scale, currency); + + money = g_strdup_printf ("%s %s", currency, tmp); + g_free (tmp); + } + + /* Translators: this string will be something like: + * Top up My Account ($1.23)..." */ + str = g_strdup_printf (_("Top up %s (%s)..."), + tp_account_get_display_name (account), money); + + gtk_action_set_label (action, str); + g_free (str); + + /* update the money label in the roster */ + label = g_object_get_data (G_OBJECT (action), "money-label"); + + gtk_label_set_text (GTK_LABEL (label), money); + g_free (money); +} + +static void +roster_window_balance_changed_cb (TpConnection *conn, + guint balance, + guint scale, + const gchar *currency, + GtkAction *action) +{ + roster_window_balance_update_balance (action, conn); +} + +static GtkAction * +roster_window_setup_balance_create_action (EmpathyRosterWindow *self, + TpAccount *account) +{ + GtkAction *action; + char *name, *ui; + guint merge_id; + GError *error = NULL; + + /* create the action group if required */ + if (self->priv->balance_action_group == NULL) + { + self->priv->balance_action_group = + gtk_action_group_new ("balance-action-group"); + + gtk_ui_manager_insert_action_group (self->priv->ui_manager, + self->priv->balance_action_group, -1); + } + + /* create the action */ + name = roster_window_account_to_action_name (account); + action = gtk_action_new (name, + tp_account_get_display_name (account), + _("Top up account credit"), NULL); + + g_object_bind_property (account, "icon-name", action, "icon-name", + G_BINDING_SYNC_CREATE); + + g_signal_connect (action, "activate", + G_CALLBACK (roster_window_balance_activate_cb), self); + + gtk_action_group_add_action (self->priv->balance_action_group, action); + g_object_unref (action); + + ui = g_strdup_printf ( + "<ui>" + " <menubar name='menubar'>" + " <menu action='view'>" + " <placeholder name='view_balance_placeholder'>" + " <menuitem action='%s'/>" + " </placeholder>" + " </menu>" + " </menubar>" + "</ui>", + name); + + merge_id = gtk_ui_manager_add_ui_from_string (self->priv->ui_manager, + ui, -1, &error); + + if (error != NULL) + { + DEBUG ("Failed to add balance UI for %s: %s", + tp_account_get_display_name (account), + error->message); + g_error_free (error); + } + + g_object_set_data (G_OBJECT (action), + "merge-id", GUINT_TO_POINTER (merge_id)); + + g_free (name); + g_free (ui); + + return action; +} + +static GtkWidget * +roster_window_setup_balance_create_widget (EmpathyRosterWindow *self, + GtkAction *action, + TpAccount *account) +{ + GtkWidget *hbox, *image, *label, *button; + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + + /* protocol icon */ + image = gtk_image_new (); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0); + g_object_bind_property (action, "icon-name", image, "icon-name", + G_BINDING_SYNC_CREATE); + + /* account name label */ + label = gtk_label_new (""); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + g_object_bind_property (account, "display-name", label, "label", + G_BINDING_SYNC_CREATE); + + /* balance label */ + label = gtk_label_new (""); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); + g_object_set_data (G_OBJECT (action), "money-label", label); + + /* top up button */ + button = gtk_button_new_with_label (_("Top Up...")); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0); + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (gtk_action_activate), action); + + gtk_box_pack_start (GTK_BOX (self->priv->balance_vbox), hbox, FALSE, TRUE, 0); + gtk_widget_show_all (hbox); + + /* tie the lifetime of the widget to the lifetime of the action */ + g_object_weak_ref (G_OBJECT (action), + (GWeakNotify) gtk_widget_destroy, hbox); + + return hbox; +} + +static void +roster_window_setup_balance (EmpathyRosterWindow *self, + TpAccount *account) +{ + TpConnection *conn = tp_account_get_connection (account); + GtkAction *action; + const gchar *uri; + + if (conn == NULL) + return; + + if (!tp_proxy_is_prepared (conn, TP_CONNECTION_FEATURE_BALANCE)) + return; + + DEBUG ("Setting up balance for acct: %s", + tp_account_get_display_name (account)); + + /* create the action */ + action = roster_window_setup_balance_create_action (self, account); + + if (action == NULL) + return; + + gtk_action_set_visible (self->priv->view_balance_show_in_roster, TRUE); + + /* create the display widget */ + roster_window_setup_balance_create_widget (self, action, account); + + /* check the current balance and monitor for any changes */ + uri = tp_connection_get_balance_uri (conn); + + g_object_set_data_full (G_OBJECT (action), "manage-credit-uri", + g_strdup (uri), g_free); + gtk_action_set_sensitive (GTK_ACTION (action), !tp_str_empty (uri)); + + roster_window_balance_update_balance (GTK_ACTION (action), conn); + + g_signal_connect (conn, "balance-changed", + G_CALLBACK (roster_window_balance_changed_cb), action); +} + +static void +roster_window_remove_balance_action (EmpathyRosterWindow *self, + TpAccount *account) +{ + GtkAction *action; + char *name; + GList *a; + + if (self->priv->balance_action_group == NULL) + return; + + name = roster_window_account_to_action_name (account); + + action = gtk_action_group_get_action ( + self->priv->balance_action_group, name); + + if (action != NULL) + { + guint merge_id; + + DEBUG ("Removing action"); + + merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (action), + "merge-id")); + + gtk_ui_manager_remove_ui (self->priv->ui_manager, + merge_id); + gtk_action_group_remove_action ( + self->priv->balance_action_group, action); + } + + g_free (name); + + a = gtk_action_group_list_actions ( + self->priv->balance_action_group); + + gtk_action_set_visible (self->priv->view_balance_show_in_roster, + g_list_length (a) > 0); + + g_list_free (a); +} + +static void +roster_window_connection_changed_cb (TpAccount *account, + guint old_status, + guint current, + guint reason, + gchar *dbus_error_name, + GHashTable *details, + EmpathyRosterWindow *self) +{ + roster_window_update_status (self); + + if (current == TP_CONNECTION_STATUS_DISCONNECTED && + reason != TP_CONNECTION_STATUS_REASON_REQUESTED) + { + roster_window_error_display (self, account); + } + + if (current == TP_CONNECTION_STATUS_DISCONNECTED) + { + empathy_sound_manager_play (self->priv->sound_mgr, GTK_WIDGET (self), + EMPATHY_SOUND_ACCOUNT_DISCONNECTED); + } + + if (current == TP_CONNECTION_STATUS_CONNECTED) + { + empathy_sound_manager_play (self->priv->sound_mgr, GTK_WIDGET (self), + EMPATHY_SOUND_ACCOUNT_CONNECTED); + + /* Account connected without error, remove error message if any */ + roster_window_remove_error (self, account); + } +} + +static void +roster_window_accels_load (void) +{ + gchar *filename; + + filename = g_build_filename (g_get_user_config_dir (), + PACKAGE_NAME, ACCELS_FILENAME, NULL); + if (g_file_test (filename, G_FILE_TEST_EXISTS)) + { + DEBUG ("Loading from:'%s'", filename); + gtk_accel_map_load (filename); + } + + g_free (filename); +} + +static void +roster_window_accels_save (void) +{ + gchar *dir; + gchar *file_with_path; + + dir = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL); + g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR); + file_with_path = g_build_filename (dir, ACCELS_FILENAME, NULL); + g_free (dir); + + DEBUG ("Saving to:'%s'", file_with_path); + gtk_accel_map_save (file_with_path); + + g_free (file_with_path); +} + +static void +empathy_roster_window_finalize (GObject *window) +{ + EmpathyRosterWindow *self = EMPATHY_ROSTER_WINDOW (window); + GHashTableIter iter; + gpointer key, value; + + /* Save user-defined accelerators. */ + roster_window_accels_save (); + + g_list_free (self->priv->actions_connected); + + g_object_unref (self->priv->account_manager); + g_object_unref (self->priv->individual_store); + g_object_unref (self->priv->sound_mgr); + g_hash_table_unref (self->priv->errors); + g_hash_table_unref (self->priv->auths); + + /* disconnect all handlers of status-changed signal */ + g_hash_table_iter_init (&iter, self->priv->status_changed_handlers); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_signal_handler_disconnect (TP_ACCOUNT (key), GPOINTER_TO_UINT (value)); + + g_hash_table_unref (self->priv->status_changed_handlers); + + g_signal_handlers_disconnect_by_func (self->priv->event_manager, + roster_window_event_added_cb, self); + g_signal_handlers_disconnect_by_func (self->priv->event_manager, + roster_window_event_removed_cb, self); + + g_object_unref (self->priv->call_observer); + g_object_unref (self->priv->event_manager); + g_object_unref (self->priv->ui_manager); + g_object_unref (self->priv->chatroom_manager); + + g_object_unref (self->priv->gsettings_ui); + g_object_unref (self->priv->gsettings_contacts); + g_object_unref (self->priv->individual_manager); + + G_OBJECT_CLASS (empathy_roster_window_parent_class)->finalize (window); +} + +static gboolean +roster_window_key_press_event_cb (GtkWidget *window, + GdkEventKey *event, + gpointer user_data) +{ + if (event->keyval == GDK_KEY_T + && event->state & GDK_SHIFT_MASK + && event->state & GDK_CONTROL_MASK) + empathy_chat_manager_call_undo_closed_chat (); + + return FALSE; +} + +static void +roster_window_chat_quit_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + gtk_widget_destroy (GTK_WIDGET (self)); +} + +static void +roster_window_view_history_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_log_window_show (NULL, NULL, FALSE, GTK_WINDOW (self)); +} + +static void +roster_window_chat_new_message_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_new_message_dialog_show (GTK_WINDOW (self)); +} + +static void +roster_window_chat_new_call_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_new_call_dialog_show (GTK_WINDOW (self)); +} + +static void +roster_window_chat_add_contact_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_new_individual_dialog_show (GTK_WINDOW (self)); +} + +static void +roster_window_chat_search_contacts_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + GtkWidget *dialog = empathy_contact_search_dialog_new ( + GTK_WINDOW (self)); + + gtk_widget_show (dialog); +} + +static void +roster_window_view_show_ft_manager (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_ft_manager_show (); +} + +static void +roster_window_view_show_offline_cb (GtkToggleAction *action, + EmpathyRosterWindow *self) +{ + gboolean current; + + current = gtk_toggle_action_get_active (action); + g_settings_set_boolean (self->priv->gsettings_ui, + EMPATHY_PREFS_UI_SHOW_OFFLINE, + current); + + empathy_individual_view_set_show_offline (self->priv->individual_view, + current); +} + +static void +roster_window_notify_sort_contact_cb (GSettings *gsettings, + const gchar *key, + EmpathyRosterWindow *self) +{ + gchar *str; + + str = g_settings_get_string (gsettings, key); + + if (str != NULL) + { + GType type; + GEnumClass *enum_class; + GEnumValue *enum_value; + + type = empathy_individual_store_sort_get_type (); + enum_class = G_ENUM_CLASS (g_type_class_peek (type)); + enum_value = g_enum_get_value_by_nick (enum_class, str); + if (enum_value) + { + /* By changing the value of the GtkRadioAction, + it emits a signal that calls roster_window_view_sort_contacts_cb + which updates the contacts list */ + gtk_radio_action_set_current_value (self->priv->sort_by_name, + enum_value->value); + } + else + { + g_warning ("Wrong value for sort_criterium configuration : %s", str); + } + g_free (str); + } +} + +static void +roster_window_view_sort_contacts_cb (GtkRadioAction *action, + GtkRadioAction *current, + EmpathyRosterWindow *self) +{ + EmpathyIndividualStoreSort value; + GSList *group; + GType type; + GEnumClass *enum_class; + GEnumValue *enum_value; + + value = gtk_radio_action_get_current_value (action); + group = gtk_radio_action_get_group (action); + + /* Get string from index */ + type = empathy_individual_store_sort_get_type (); + enum_class = G_ENUM_CLASS (g_type_class_peek (type)); + enum_value = g_enum_get_value (enum_class, g_slist_index (group, current)); + + if (!enum_value) + g_warning ("No GEnumValue for EmpathyContactListSort with GtkRadioAction index:%d", + g_slist_index (group, action)); + else + g_settings_set_string (self->priv->gsettings_contacts, + EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM, + enum_value->value_nick); + + empathy_individual_store_set_sort_criterium (self->priv->individual_store, + value); +} + +static void +roster_window_view_show_protocols_cb (GtkToggleAction *action, + EmpathyRosterWindow *self) +{ + gboolean value; + + value = gtk_toggle_action_get_active (action); + + g_settings_set_boolean (self->priv->gsettings_ui, + EMPATHY_PREFS_UI_SHOW_PROTOCOLS, + value); + + empathy_individual_store_set_show_protocols (self->priv->individual_store, + value); +} + +/* Matches GtkRadioAction values set in empathy-roster-window.ui */ +#define CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS 0 +#define CONTACT_LIST_NORMAL_SIZE 1 +#define CONTACT_LIST_COMPACT_SIZE 2 + +static void +roster_window_view_contacts_list_size_cb (GtkRadioAction *action, + GtkRadioAction *current, + EmpathyRosterWindow *self) +{ + GSettings *gsettings_ui; + gint value; + + value = gtk_radio_action_get_current_value (action); + /* create a new GSettings, so we can delay the setting until both + * values are set */ + gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA); + + DEBUG ("radio button toggled, value = %i", value); + + g_settings_delay (gsettings_ui); + g_settings_set_boolean (gsettings_ui, EMPATHY_PREFS_UI_SHOW_AVATARS, + value == CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS); + + g_settings_set_boolean (gsettings_ui, EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST, + value == CONTACT_LIST_COMPACT_SIZE); + g_settings_apply (gsettings_ui); + + /* FIXME: these enums probably have the wrong namespace */ + empathy_individual_store_set_show_avatars (self->priv->individual_store, + value == CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS); + empathy_individual_store_set_is_compact (self->priv->individual_store, + value == CONTACT_LIST_COMPACT_SIZE); + + g_object_unref (gsettings_ui); +} + +static void roster_window_notify_show_protocols_cb (GSettings *gsettings, + const gchar *key, + EmpathyRosterWindow *self) +{ + gtk_toggle_action_set_active (self->priv->show_protocols, + g_settings_get_boolean (gsettings, EMPATHY_PREFS_UI_SHOW_PROTOCOLS)); +} + + +static void +roster_window_notify_contact_list_size_cb (GSettings *gsettings, + const gchar *key, + EmpathyRosterWindow *self) +{ + gint value; + + if (g_settings_get_boolean (gsettings, + EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST)) + value = CONTACT_LIST_COMPACT_SIZE; + else if (g_settings_get_boolean (gsettings, + EMPATHY_PREFS_UI_SHOW_AVATARS)) + value = CONTACT_LIST_NORMAL_SIZE_WITH_AVATARS; + else + value = CONTACT_LIST_NORMAL_SIZE; + + DEBUG ("setting changed, value = %i", value); + + /* By changing the value of the GtkRadioAction, + it emits a signal that calls roster_window_view_contacts_list_size_cb + which updates the contacts list */ + gtk_radio_action_set_current_value (self->priv->normal_with_avatars, value); +} + +static void +roster_window_edit_search_contacts_cb (GtkCheckMenuItem *item, + EmpathyRosterWindow *self) +{ + empathy_individual_view_start_search (self->priv->individual_view); +} + +static void +roster_window_view_show_map_cb (GtkCheckMenuItem *item, + EmpathyRosterWindow *self) +{ +#ifdef HAVE_LIBCHAMPLAIN + empathy_map_view_show (); +#endif +} + +static void +join_chatroom (EmpathyChatroom *chatroom, + gint64 timestamp) +{ + TpAccount *account; + const gchar *room; + + account = empathy_chatroom_get_account (chatroom); + room = empathy_chatroom_get_room (chatroom); + + DEBUG ("Requesting channel for '%s'", room); + empathy_join_muc (account, room, timestamp); +} + +typedef struct +{ + TpAccount *account; + EmpathyChatroom *chatroom; + gint64 timestamp; + glong sig_id; + guint timeout; +} join_fav_account_sig_ctx; + +static join_fav_account_sig_ctx * +join_fav_account_sig_ctx_new (TpAccount *account, + EmpathyChatroom *chatroom, + gint64 timestamp) +{ + join_fav_account_sig_ctx *ctx = g_slice_new0 ( + join_fav_account_sig_ctx); + + ctx->account = g_object_ref (account); + ctx->chatroom = g_object_ref (chatroom); + ctx->timestamp = timestamp; + return ctx; +} + +static void +join_fav_account_sig_ctx_free (join_fav_account_sig_ctx *ctx) +{ + g_object_unref (ctx->account); + g_object_unref (ctx->chatroom); + g_slice_free (join_fav_account_sig_ctx, ctx); +} + +static void +account_status_changed_cb (TpAccount *account, + TpConnectionStatus old_status, + TpConnectionStatus new_status, + guint reason, + gchar *dbus_error_name, + GHashTable *details, + gpointer user_data) +{ + join_fav_account_sig_ctx *ctx = user_data; + + switch (new_status) + { + case TP_CONNECTION_STATUS_DISCONNECTED: + /* Don't wait any longer */ + goto finally; + break; + + case TP_CONNECTION_STATUS_CONNECTING: + /* Wait a bit */ + return; + + case TP_CONNECTION_STATUS_CONNECTED: + /* We can join the room */ + break; + + default: + g_assert_not_reached (); + } + + join_chatroom (ctx->chatroom, ctx->timestamp); + +finally: + g_source_remove (ctx->timeout); + g_signal_handler_disconnect (account, ctx->sig_id); +} + +#define JOIN_FAVORITE_TIMEOUT 5 + +static gboolean +join_favorite_timeout_cb (gpointer data) +{ + join_fav_account_sig_ctx *ctx = data; + + /* stop waiting for joining the favorite room */ + g_signal_handler_disconnect (ctx->account, ctx->sig_id); + return FALSE; +} + +static void +roster_window_favorite_chatroom_join (EmpathyChatroom *chatroom) +{ + TpAccount *account; + + account = empathy_chatroom_get_account (chatroom); + if (tp_account_get_connection_status (account, NULL) != + TP_CONNECTION_STATUS_CONNECTED) + { + join_fav_account_sig_ctx *ctx; + + ctx = join_fav_account_sig_ctx_new (account, chatroom, + empathy_get_current_action_time ()); + + ctx->sig_id = g_signal_connect_data (account, "status-changed", + G_CALLBACK (account_status_changed_cb), ctx, + (GClosureNotify) join_fav_account_sig_ctx_free, 0); + + ctx->timeout = g_timeout_add_seconds (JOIN_FAVORITE_TIMEOUT, + join_favorite_timeout_cb, ctx); + return; + } + + join_chatroom (chatroom, empathy_get_current_action_time ()); +} + +static void +roster_window_favorite_chatroom_menu_activate_cb (GtkMenuItem *menu_item, + EmpathyChatroom *chatroom) +{ + roster_window_favorite_chatroom_join (chatroom); +} + +static void +roster_window_favorite_chatroom_menu_add (EmpathyRosterWindow *self, + EmpathyChatroom *chatroom) +{ + GtkWidget *menu_item; + const gchar *name, *account_name; + gchar *label; + + if (g_object_get_data (G_OBJECT (chatroom), "menu_item")) + return; + + name = empathy_chatroom_get_name (chatroom); + account_name = tp_account_get_display_name ( + empathy_chatroom_get_account (chatroom)); + label = g_strdup_printf ("%s (%s)", name, account_name); + menu_item = gtk_menu_item_new_with_label (label); + g_free (label); + g_object_set_data (G_OBJECT (menu_item), "is_favorite", + GUINT_TO_POINTER (TRUE)); + + g_object_set_data (G_OBJECT (chatroom), "menu_item", menu_item); + g_signal_connect (menu_item, "activate", + G_CALLBACK (roster_window_favorite_chatroom_menu_activate_cb), + chatroom); + + gtk_menu_shell_insert (GTK_MENU_SHELL (self->priv->room_menu), + menu_item, 4); + + gtk_widget_show (menu_item); +} + +static void +roster_window_favorite_chatroom_menu_added_cb (EmpathyChatroomManager *manager, + EmpathyChatroom *chatroom, + EmpathyRosterWindow *self) +{ + roster_window_favorite_chatroom_menu_add (self, chatroom); + gtk_widget_show (self->priv->room_separator); + gtk_action_set_sensitive (self->priv->room_join_favorites, TRUE); +} + +static void +roster_window_favorite_chatroom_menu_removed_cb ( + EmpathyChatroomManager *manager, + EmpathyChatroom *chatroom, + EmpathyRosterWindow *self) +{ + GtkWidget *menu_item; + GList *chatrooms; + + menu_item = g_object_get_data (G_OBJECT (chatroom), "menu_item"); + g_object_set_data (G_OBJECT (chatroom), "menu_item", NULL); + gtk_widget_destroy (menu_item); + + chatrooms = empathy_chatroom_manager_get_chatrooms (self->priv->chatroom_manager, + NULL); + if (chatrooms) + gtk_widget_show (self->priv->room_separator); + else + gtk_widget_hide (self->priv->room_separator); + + gtk_action_set_sensitive (self->priv->room_join_favorites, chatrooms != NULL); + g_list_free (chatrooms); +} + +static void +roster_window_favorite_chatroom_menu_setup (EmpathyRosterWindow *self) +{ + GList *chatrooms, *l; + GtkWidget *room; + + self->priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL); + chatrooms = empathy_chatroom_manager_get_chatrooms ( + self->priv->chatroom_manager, NULL); + room = gtk_ui_manager_get_widget (self->priv->ui_manager, + "/menubar/room"); + self->priv->room_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (room)); + self->priv->room_separator = gtk_ui_manager_get_widget (self->priv->ui_manager, + "/menubar/room/room_separator"); + + for (l = chatrooms; l; l = l->next) + roster_window_favorite_chatroom_menu_add (self, l->data); + + if (!chatrooms) + gtk_widget_hide (self->priv->room_separator); + + gtk_action_set_sensitive (self->priv->room_join_favorites, chatrooms != NULL); + + g_signal_connect (self->priv->chatroom_manager, "chatroom-added", + G_CALLBACK (roster_window_favorite_chatroom_menu_added_cb), + self); + + g_signal_connect (self->priv->chatroom_manager, "chatroom-removed", + G_CALLBACK (roster_window_favorite_chatroom_menu_removed_cb), + self); + + g_list_free (chatrooms); +} + +static void +roster_window_room_join_new_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_new_chatroom_dialog_show (GTK_WINDOW (self)); +} + +static void +roster_window_room_join_favorites_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + GList *chatrooms, *l; + + chatrooms = empathy_chatroom_manager_get_chatrooms (self->priv->chatroom_manager, + NULL); + + for (l = chatrooms; l; l = l->next) + roster_window_favorite_chatroom_join (l->data); + + g_list_free (chatrooms); +} + +static void +roster_window_room_manage_favorites_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_chatrooms_window_show (GTK_WINDOW (self)); +} + +static void +roster_window_edit_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + GtkWidget *submenu; + + /* FIXME: It should use the UIManager to merge the contact/group submenu */ + submenu = empathy_individual_view_get_individual_menu ( + self->priv->individual_view); + if (submenu) + { + GtkMenuItem *item; + GtkWidget *label; + + item = GTK_MENU_ITEM (self->priv->edit_context); + label = gtk_bin_get_child (GTK_BIN (item)); + gtk_label_set_text (GTK_LABEL (label), _("Contact")); + + gtk_widget_show (self->priv->edit_context); + gtk_widget_show (self->priv->edit_context_separator); + + gtk_menu_item_set_submenu (item, submenu); + + return; + } + + submenu = empathy_individual_view_get_group_menu (self->priv->individual_view); + if (submenu) + { + GtkMenuItem *item; + GtkWidget *label; + + item = GTK_MENU_ITEM (self->priv->edit_context); + label = gtk_bin_get_child (GTK_BIN (item)); + gtk_label_set_text (GTK_LABEL (label), _("Group")); + + gtk_widget_show (self->priv->edit_context); + gtk_widget_show (self->priv->edit_context_separator); + + gtk_menu_item_set_submenu (item, submenu); + + return; + } + + gtk_widget_hide (self->priv->edit_context); + gtk_widget_hide (self->priv->edit_context_separator); + + return; +} + +static void +roster_window_edit_accounts_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_accounts_dialog_show_application (gdk_screen_get_default (), + NULL, FALSE, FALSE); +} + +static void +roster_window_edit_blocked_contacts_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + GtkWidget *dialog; + + dialog = empathy_contact_blocking_dialog_new (GTK_WINDOW (self)); + gtk_widget_show (dialog); + + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); +} + +void +empathy_roster_window_show_preferences (EmpathyRosterWindow *self, + const gchar *tab) +{ + if (self->priv->preferences == NULL) + { + self->priv->preferences = empathy_preferences_new (GTK_WINDOW (self), + self->priv->shell_running); + g_object_add_weak_pointer (G_OBJECT (self->priv->preferences), + (gpointer) &self->priv->preferences); + + gtk_widget_show (self->priv->preferences); + } + else + { + gtk_window_present (GTK_WINDOW (self->priv->preferences)); + } + + if (tab != NULL) + empathy_preferences_show_tab ( + EMPATHY_PREFERENCES (self->priv->preferences), tab); +} + +static void +roster_window_edit_preferences_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_roster_window_show_preferences (self, NULL); +} + +static void +roster_window_help_about_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_about_dialog_new (GTK_WINDOW (self)); +} + +static void +roster_window_help_debug_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_launch_program (BIN_DIR, "empathy-debugger", NULL); +} + +static void +roster_window_help_contents_cb (GtkAction *action, + EmpathyRosterWindow *self) +{ + empathy_url_show (GTK_WIDGET (self), "ghelp:empathy"); +} + +static gboolean +roster_window_throbber_button_press_event_cb (GtkWidget *throbber, + GdkEventButton *event, + EmpathyRosterWindow *self) +{ + if (event->type != GDK_BUTTON_PRESS || + event->button != 1) + return FALSE; + + empathy_accounts_dialog_show_application ( + gtk_widget_get_screen (GTK_WIDGET (throbber)), + NULL, FALSE, FALSE); + + return FALSE; +} + +static void +roster_window_account_removed_cb (TpAccountManager *manager, + TpAccount *account, + EmpathyRosterWindow *self) +{ + GList *a; + + a = tp_account_manager_get_valid_accounts (manager); + + gtk_action_set_sensitive (self->priv->view_history, + g_list_length (a) > 0); + + g_list_free (a); + + /* remove errors if any */ + roster_window_remove_error (self, account); + + /* remove the balance action if required */ + roster_window_remove_balance_action (self, account); +} + +static void +account_connection_notify_cb (TpAccount *account, + GParamSpec *spec, + EmpathyRosterWindow *self) +{ + TpConnection *conn; + + conn = tp_account_get_connection (account); + + if (conn != NULL) + { + roster_window_setup_balance (self, account); + } + else + { + /* remove balance action if required */ + roster_window_remove_balance_action (self, account); + } +} + +static void +add_account (EmpathyRosterWindow *self, + TpAccount *account) +{ + gulong handler_id; + + handler_id = GPOINTER_TO_UINT (g_hash_table_lookup ( + self->priv->status_changed_handlers, account)); + + /* connect signal only if it was not connected yet */ + if (handler_id != 0) + return; + + handler_id = g_signal_connect (account, "status-changed", + G_CALLBACK (roster_window_connection_changed_cb), self); + + g_hash_table_insert (self->priv->status_changed_handlers, + account, GUINT_TO_POINTER (handler_id)); + + /* roster_window_setup_balance() relies on the TpConnection to be ready on + * the TpAccount so we connect this signal as well. */ + tp_g_signal_connect_object (account, "notify::connection", + G_CALLBACK (account_connection_notify_cb), self, 0); + + roster_window_setup_balance (self, account); +} + +/* @account: if not %NULL, the only account which can be enabled */ +static void +display_page_account_not_enabled (EmpathyRosterWindow *self, + TpAccount *account) +{ + if (account == NULL) + { + display_page_message (self, + _("You need to enable one of your accounts to see contacts here."), + TRUE, FALSE); + } + else + { + gchar *tmp; + + /* translators: argument is an account name */ + tmp = g_strdup_printf (_("You need to enable %s to see contacts here."), + tp_account_get_display_name (account)); + + display_page_message (self, tmp, TRUE, FALSE); + g_free (tmp); + } +} +static gboolean +has_enabled_account (GList *accounts) +{ + GList *l; + + for (l = accounts; l != NULL; l = g_list_next (l)) + { + TpAccount *account = l->data; + + if (tp_account_is_enabled (account)) + return TRUE; + } + + return FALSE; +} + +static void +set_notebook_page (EmpathyRosterWindow *self) +{ + GList *accounts; + guint len; + + accounts = tp_account_manager_get_valid_accounts ( + self->priv->account_manager); + + len = g_list_length (accounts); + + if (len == 0) + { + /* No account */ + display_page_no_account (self); + goto out; + } + + if (!has_enabled_account (accounts)) + { + TpAccount *account = NULL; + + /* Pass the account if there is only one which can be enabled */ + if (len == 1) + account = accounts->data; + + display_page_account_not_enabled (self, account); + goto out; + } + + display_page_contact_list (self); + +out: + g_list_free (accounts); +} + +static void +roster_window_account_validity_changed_cb (TpAccountManager *manager, + TpAccount *account, + gboolean valid, + EmpathyRosterWindow *self) +{ + if (valid) + add_account (self, account); + else + roster_window_account_removed_cb (manager, account, self); + + set_notebook_page (self); +} + +static void +roster_window_notify_show_offline_cb (GSettings *gsettings, + const gchar *key, + gpointer toggle_action) +{ + gtk_toggle_action_set_active (toggle_action, + g_settings_get_boolean (gsettings, key)); +} + +static void +roster_window_connection_items_setup (EmpathyRosterWindow *self, + GtkBuilder *gui) +{ + GList *list; + GObject *action; + guint i; + const gchar *actions_connected[] = { + "room_join_new", + "room_join_favorites", + "chat_new_message", + "chat_new_call", + "chat_search_contacts", + "chat_add_contact", + "edit_blocked_contacts", + "edit_search_contacts" + }; + + for (i = 0, list = NULL; i < G_N_ELEMENTS (actions_connected); i++) + { + action = gtk_builder_get_object (gui, actions_connected[i]); + list = g_list_prepend (list, action); + } + + self->priv->actions_connected = list; +} + +static void +account_enabled_cb (TpAccountManager *manager, + TpAccount *account, + EmpathyRosterWindow *self) +{ + set_notebook_page (self); +} + +static void +account_disabled_cb (TpAccountManager *manager, + TpAccount *account, + EmpathyRosterWindow *self) +{ + set_notebook_page (self); +} + +static void +account_manager_prepared_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GList *accounts, *j; + TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object); + EmpathyRosterWindow *self = user_data; + GError *error = NULL; + + if (!tp_proxy_prepare_finish (manager, result, &error)) + { + DEBUG ("Failed to prepare account manager: %s", error->message); + g_error_free (error); + return; + } + + accounts = tp_account_manager_get_valid_accounts (self->priv->account_manager); + for (j = accounts; j != NULL; j = j->next) + { + TpAccount *account = TP_ACCOUNT (j->data); + + add_account (self, account); + } + + g_signal_connect (manager, "account-validity-changed", + G_CALLBACK (roster_window_account_validity_changed_cb), self); + tp_g_signal_connect_object (manager, "account-disabled", + G_CALLBACK (account_disabled_cb), self, 0); + tp_g_signal_connect_object (manager, "account-enabled", + G_CALLBACK (account_enabled_cb), self, 0); + + roster_window_update_status (self); + + /* Disable the "Previous Conversations" menu entry if there is no account */ + gtk_action_set_sensitive (self->priv->view_history, + g_list_length (accounts) > 0); + + set_notebook_page (self); + + g_list_free (accounts); +} + +void +empathy_roster_window_set_shell_running (EmpathyRosterWindow *self, + gboolean shell_running) +{ + if (self->priv->shell_running == shell_running) + return; + + self->priv->shell_running = shell_running; + g_object_notify (G_OBJECT (self), "shell-running"); +} + +static GObject * +empathy_roster_window_constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + static GObject *window = NULL; + + if (window != NULL) + return g_object_ref (window); + + window = G_OBJECT_CLASS (empathy_roster_window_parent_class)->constructor ( + type, n_construct_params, construct_params); + + g_object_add_weak_pointer (window, (gpointer) &window); + + return window; +} + +static void +empathy_roster_window_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyRosterWindow *self = EMPATHY_ROSTER_WINDOW (object); + + switch (property_id) + { + case PROP_SHELL_RUNNING: + self->priv->shell_running = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +empathy_roster_window_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyRosterWindow *self = EMPATHY_ROSTER_WINDOW (object); + + switch (property_id) + { + case PROP_SHELL_RUNNING: + g_value_set_boolean (value, self->priv->shell_running); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +empathy_roster_window_class_init (EmpathyRosterWindowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + object_class->finalize = empathy_roster_window_finalize; + object_class->constructor = empathy_roster_window_constructor; + + object_class->set_property = empathy_roster_window_set_property; + object_class->get_property = empathy_roster_window_get_property; + + pspec = g_param_spec_boolean ("shell-running", + "Shell running", + "Whether the Shell is running or not", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_SHELL_RUNNING, pspec); + + g_type_class_add_private (object_class, sizeof (EmpathyRosterWindowPriv)); +} + +static void +show_contacts_loading (EmpathyRosterWindow *self) +{ + display_page_message (self, NULL, FALSE, TRUE); + + gtk_spinner_start (GTK_SPINNER (self->priv->spinner_loading)); +} + +static void +hide_contacts_loading (EmpathyRosterWindow *self) +{ + gtk_spinner_stop (GTK_SPINNER (self->priv->spinner_loading)); + + display_page_contact_list (self); +} + +static void +contacts_loaded_cb (EmpathyIndividualManager *manager, + EmpathyRosterWindow *self) +{ + hide_contacts_loading (self); +} + +static void +empathy_roster_window_init (EmpathyRosterWindow *self) +{ + GtkBuilder *gui, *gui_mgr; + GtkWidget *sw; + GtkToggleAction *show_offline_widget; + GtkAction *show_map_widget; + GtkToolItem *item; + gboolean show_offline; + gchar *filename; + GtkTreeModel *model; + GtkWidget *search_vbox; + GtkWidget *menubar; + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EMPATHY_TYPE_ROSTER_WINDOW, EmpathyRosterWindowPriv); + + self->priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA); + self->priv->gsettings_contacts = g_settings_new (EMPATHY_PREFS_CONTACTS_SCHEMA); + + self->priv->sound_mgr = empathy_sound_manager_dup_singleton (); + + gtk_window_set_title (GTK_WINDOW (self), _("Contact List")); + gtk_window_set_role (GTK_WINDOW (self), "contact_list"); + gtk_window_set_default_size (GTK_WINDOW (self), 225, 325); + + /* don't finalize the widget on delete-event, just hide it */ + g_signal_connect (self, "delete-event", + G_CALLBACK (gtk_widget_hide_on_delete), NULL); + + /* Set up interface */ + filename = empathy_file_lookup ("empathy-roster-window.ui", "src"); + gui = empathy_builder_get_file (filename, + "main_vbox", &self->priv->main_vbox, + "balance_vbox", &self->priv->balance_vbox, + "errors_vbox", &self->priv->errors_vbox, + "auth_vbox", &self->priv->auth_vbox, + "search_vbox", &search_vbox, + "presence_toolbar", &self->priv->presence_toolbar, + "notebook", &self->priv->notebook, + "no_entry_label", &self->priv->no_entry_label, + "roster_scrolledwindow", &sw, + "button_account_settings", &self->priv->button_account_settings, + "spinner_loading", &self->priv->spinner_loading, + NULL); + g_free (filename); + + /* Set UI manager */ + filename = empathy_file_lookup ("empathy-roster-window-menubar.ui", "src"); + gui_mgr = empathy_builder_get_file (filename, + "ui_manager", &self->priv->ui_manager, + "view_show_offline", &show_offline_widget, + "view_show_protocols", &self->priv->show_protocols, + "view_sort_by_name", &self->priv->sort_by_name, + "view_sort_by_status", &self->priv->sort_by_status, + "view_normal_size_with_avatars", &self->priv->normal_with_avatars, + "view_normal_size", &self->priv->normal_size, + "view_compact_size", &self->priv->compact_size, + "view_history", &self->priv->view_history, + "view_show_map", &show_map_widget, + "room_join_favorites", &self->priv->room_join_favorites, + "view_balance_show_in_roster", &self->priv->view_balance_show_in_roster, + "menubar", &menubar, + NULL); + g_free (filename); + + /* The UI manager is living in its own .ui file as Glade doesn't support + * those. The GtkMenubar has to be in this file as well to we manually add + * it to the first position of the vbox. */ + gtk_box_pack_start (GTK_BOX (self->priv->main_vbox), menubar, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (self->priv->main_vbox), menubar, 0); + + gtk_container_add (GTK_CONTAINER (self), self->priv->main_vbox); + gtk_widget_show (self->priv->main_vbox); + + g_signal_connect (self, "key-press-event", + G_CALLBACK (roster_window_key_press_event_cb), NULL); + + empathy_builder_connect (gui_mgr, self, + "chat_quit", "activate", roster_window_chat_quit_cb, + "chat_new_message", "activate", roster_window_chat_new_message_cb, + "chat_new_call", "activate", roster_window_chat_new_call_cb, + "view_history", "activate", roster_window_view_history_cb, + "room_join_new", "activate", roster_window_room_join_new_cb, + "room_join_favorites", "activate", roster_window_room_join_favorites_cb, + "room_manage_favorites", "activate", roster_window_room_manage_favorites_cb, + "chat_add_contact", "activate", roster_window_chat_add_contact_cb, + "chat_search_contacts", "activate", roster_window_chat_search_contacts_cb, + "view_show_ft_manager", "activate", roster_window_view_show_ft_manager, + "view_show_offline", "toggled", roster_window_view_show_offline_cb, + "view_show_protocols", "toggled", roster_window_view_show_protocols_cb, + "view_sort_by_name", "changed", roster_window_view_sort_contacts_cb, + "view_normal_size_with_avatars", "changed", roster_window_view_contacts_list_size_cb, + "view_show_map", "activate", roster_window_view_show_map_cb, + "edit", "activate", roster_window_edit_cb, + "edit_accounts", "activate", roster_window_edit_accounts_cb, + "edit_blocked_contacts", "activate", roster_window_edit_blocked_contacts_cb, + "edit_preferences", "activate", roster_window_edit_preferences_cb, + "edit_search_contacts", "activate", roster_window_edit_search_contacts_cb, + "help_about", "activate", roster_window_help_about_cb, + "help_debug", "activate", roster_window_help_debug_cb, + "help_contents", "activate", roster_window_help_contents_cb, + NULL); + + /* Set up connection related widgets. */ + roster_window_connection_items_setup (self, gui_mgr); + + g_object_ref (self->priv->ui_manager); + g_object_unref (gui); + g_object_unref (gui_mgr); + +#ifndef HAVE_LIBCHAMPLAIN + gtk_action_set_visible (show_map_widget, FALSE); +#endif + + self->priv->account_manager = tp_account_manager_dup (); + + tp_proxy_prepare_async (self->priv->account_manager, NULL, + account_manager_prepared_cb, self); + + self->priv->errors = g_hash_table_new_full (g_direct_hash, g_direct_equal, + g_object_unref, NULL); + + self->priv->auths = g_hash_table_new (NULL, NULL); + + self->priv->status_changed_handlers = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, NULL); + + /* Set up menu */ + roster_window_favorite_chatroom_menu_setup (self); + + self->priv->edit_context = gtk_ui_manager_get_widget (self->priv->ui_manager, + "/menubar/edit/edit_context"); + self->priv->edit_context_separator = gtk_ui_manager_get_widget ( + self->priv->ui_manager, + "/menubar/edit/edit_context_separator"); + gtk_widget_hide (self->priv->edit_context); + gtk_widget_hide (self->priv->edit_context_separator); + + /* Set up contact list. */ + empathy_status_presets_get_all (); + + /* Set up presence chooser */ + self->priv->presence_chooser = empathy_presence_chooser_new (); + gtk_widget_show (self->priv->presence_chooser); + item = gtk_tool_item_new (); + gtk_widget_show (GTK_WIDGET (item)); + gtk_widget_set_size_request (self->priv->presence_chooser, 10, -1); + gtk_container_add (GTK_CONTAINER (item), self->priv->presence_chooser); + gtk_tool_item_set_is_important (item, TRUE); + gtk_tool_item_set_expand (item, TRUE); + gtk_toolbar_insert (GTK_TOOLBAR (self->priv->presence_toolbar), item, -1); + + /* Set up the throbber */ + self->priv->throbber = gtk_spinner_new (); + gtk_widget_set_size_request (self->priv->throbber, 16, -1); + gtk_widget_set_events (self->priv->throbber, GDK_BUTTON_PRESS_MASK); + g_signal_connect (self->priv->throbber, "button-press-event", + G_CALLBACK (roster_window_throbber_button_press_event_cb), + self); + gtk_widget_show (self->priv->throbber); + + item = gtk_tool_item_new (); + gtk_container_set_border_width (GTK_CONTAINER (item), 6); + gtk_toolbar_insert (GTK_TOOLBAR (self->priv->presence_toolbar), item, -1); + gtk_container_add (GTK_CONTAINER (item), self->priv->throbber); + self->priv->throbber_tool_item = GTK_WIDGET (item); + + /* XXX: this class is designed to live for the duration of the program, + * so it's got a race condition between its signal handlers and its + * finalization. The class is planned to be removed, so we won't fix + * this before then. */ + self->priv->individual_manager = empathy_individual_manager_dup_singleton (); + + if (!empathy_individual_manager_get_contacts_loaded ( + self->priv->individual_manager)) + { + show_contacts_loading (self); + + tp_g_signal_connect_object (self->priv->individual_manager, + "contacts-loaded", G_CALLBACK (contacts_loaded_cb), self, 0); + } + + self->priv->individual_store = EMPATHY_INDIVIDUAL_STORE ( + empathy_individual_store_manager_new (self->priv->individual_manager)); + + /* For the moment, we disallow Persona drops onto the roster contact list + * (e.g. from things such as the EmpathyPersonaView in the linking dialogue). + * No code is hooked up to do anything on a Persona drop, so allowing them + * would achieve nothing except confusion. */ + self->priv->individual_view = empathy_individual_view_new ( + self->priv->individual_store, + /* EmpathyIndividualViewFeatureFlags */ + EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_SAVE | + EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_RENAME | + EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_REMOVE | + EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_CHANGE | + EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_REMOVE | + EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_DROP | + EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_DRAG | + EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_TOOLTIP | + EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_CALL | + EMPATHY_INDIVIDUAL_VIEW_FEATURE_FILE_DROP, + /* EmpathyIndividualFeatureFlags */ + EMPATHY_INDIVIDUAL_FEATURE_CHAT | + EMPATHY_INDIVIDUAL_FEATURE_CALL | + EMPATHY_INDIVIDUAL_FEATURE_EDIT | + EMPATHY_INDIVIDUAL_FEATURE_INFO | + EMPATHY_INDIVIDUAL_FEATURE_SMS | + EMPATHY_INDIVIDUAL_FEATURE_CALL_PHONE); + + gtk_widget_show (GTK_WIDGET (self->priv->individual_view)); + gtk_container_add (GTK_CONTAINER (sw), + GTK_WIDGET (self->priv->individual_view)); + g_signal_connect (self->priv->individual_view, "row-activated", + G_CALLBACK (roster_window_row_activated_cb), self); + + /* Set up search bar */ + self->priv->search_bar = empathy_live_search_new ( + GTK_WIDGET (self->priv->individual_view)); + empathy_individual_view_set_live_search (self->priv->individual_view, + EMPATHY_LIVE_SEARCH (self->priv->search_bar)); + gtk_box_pack_start (GTK_BOX (search_vbox), self->priv->search_bar, + FALSE, TRUE, 0); + + g_signal_connect_swapped (self, "map", + G_CALLBACK (gtk_widget_grab_focus), self->priv->individual_view); + + /* Connect to proper signals to check if contact list is empty or not */ + model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->individual_view)); + self->priv->empty = TRUE; + g_signal_connect (model, "row-inserted", + G_CALLBACK (roster_window_row_inserted_cb), self); + g_signal_connect (model, "row-deleted", + G_CALLBACK (roster_window_row_deleted_cb), self); + + /* Load user-defined accelerators. */ + roster_window_accels_load (); + + /* Set window size. */ + empathy_geometry_bind (GTK_WINDOW (self), GEOMETRY_NAME); + + /* bind view_balance_show_in_roster */ + g_settings_bind (self->priv->gsettings_ui, "show-balance-in-roster", + self->priv->view_balance_show_in_roster, "active", + G_SETTINGS_BIND_DEFAULT); + g_object_bind_property (self->priv->view_balance_show_in_roster, "active", + self->priv->balance_vbox, "visible", + G_BINDING_SYNC_CREATE); + + g_settings_bind (self->priv->gsettings_ui, "show-groups", + self->priv->individual_store, "show-groups", G_SETTINGS_BIND_DEFAULT); + + /* Enable event handling */ + self->priv->call_observer = empathy_call_observer_dup_singleton (); + self->priv->event_manager = empathy_event_manager_dup_singleton (); + + g_signal_connect (self->priv->event_manager, "event-added", + G_CALLBACK (roster_window_event_added_cb), self); + g_signal_connect (self->priv->event_manager, "event-removed", + G_CALLBACK (roster_window_event_removed_cb), self); + g_signal_connect (self->priv->account_manager, "account-validity-changed", + G_CALLBACK (roster_window_account_validity_changed_cb), self); + g_signal_connect (self->priv->account_manager, "account-removed", + G_CALLBACK (roster_window_account_removed_cb), self); + g_signal_connect (self->priv->account_manager, "account-disabled", + G_CALLBACK (roster_window_account_disabled_cb), self); + + /* Show offline ? */ + show_offline = g_settings_get_boolean (self->priv->gsettings_ui, + EMPATHY_PREFS_UI_SHOW_OFFLINE); + + g_signal_connect (self->priv->gsettings_ui, + "changed::" EMPATHY_PREFS_UI_SHOW_OFFLINE, + G_CALLBACK (roster_window_notify_show_offline_cb), show_offline_widget); + + gtk_toggle_action_set_active (show_offline_widget, show_offline); + + /* Show protocol ? */ + g_signal_connect (self->priv->gsettings_ui, + "changed::" EMPATHY_PREFS_UI_SHOW_PROTOCOLS, + G_CALLBACK (roster_window_notify_show_protocols_cb), self); + + roster_window_notify_show_protocols_cb (self->priv->gsettings_ui, + EMPATHY_PREFS_UI_SHOW_PROTOCOLS, self); + + /* Sort by name / by status ? */ + g_signal_connect (self->priv->gsettings_contacts, + "changed::" EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM, + G_CALLBACK (roster_window_notify_sort_contact_cb), self); + + roster_window_notify_sort_contact_cb (self->priv->gsettings_contacts, + EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM, self); + + /* Contacts list size */ + g_signal_connect (self->priv->gsettings_ui, + "changed::" EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST, + G_CALLBACK (roster_window_notify_contact_list_size_cb), self); + + g_signal_connect (self->priv->gsettings_ui, + "changed::" EMPATHY_PREFS_UI_SHOW_AVATARS, + G_CALLBACK (roster_window_notify_contact_list_size_cb), + self); + + roster_window_notify_contact_list_size_cb (self->priv->gsettings_ui, + EMPATHY_PREFS_UI_SHOW_AVATARS, self); + + g_signal_connect (self->priv->button_account_settings, "clicked", + G_CALLBACK (button_account_settings_clicked_cb), self); +} + +GtkWidget * +empathy_roster_window_dup (void) +{ + return g_object_new (EMPATHY_TYPE_ROSTER_WINDOW, NULL); +} diff --git a/src/empathy-roster-window.h b/src/empathy-roster-window.h new file mode 100644 index 000000000..54404ad38 --- /dev/null +++ b/src/empathy-roster-window.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + * Danielle Madeley <danielle.madeley@collabora.co.uk> + */ + +#ifndef __EMPATHY_ROSTER_WINDOW_H__ +#define __EMPATHY_ROSTER_WINDOW_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_ROSTER_WINDOW (empathy_roster_window_get_type ()) +#define EMPATHY_ROSTER_WINDOW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_ROSTER_WINDOW, EmpathyRosterWindow)) +#define EMPATHY_ROSTER_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_ROSTER_WINDOW, EmpathyRosterWindowClass)) +#define EMPATHY_IS_ROSTER_WINDOW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_ROSTER_WINDOW)) +#define EMPATHY_IS_ROSTER_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_ROSTER_WINDOW)) +#define EMPATHY_ROSTER_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_ROSTER_WINDOW, EmpathyRosterWindowClass)) + +typedef struct _EmpathyRosterWindow EmpathyRosterWindow; +typedef struct _EmpathyRosterWindowClass EmpathyRosterWindowClass; +typedef struct _EmpathyRosterWindowPriv EmpathyRosterWindowPriv; + +struct _EmpathyRosterWindow +{ + GtkWindow parent; + EmpathyRosterWindowPriv *priv; +}; + +struct _EmpathyRosterWindowClass +{ + GtkWindowClass parent_class; +}; + +GType empathy_roster_window_get_type (void); + +GtkWidget *empathy_roster_window_dup (void); + +void empathy_roster_window_show_preferences (EmpathyRosterWindow *self, + const gchar *tab); + +void empathy_roster_window_set_shell_running (EmpathyRosterWindow *self, + gboolean shell_running); + +G_END_DECLS + +#endif /* __EMPATHY_ROSTER_WINDOW_H__ */ diff --git a/src/empathy-main-window.ui b/src/empathy-roster-window.ui index a09d123f8..8c8f63001 100644 --- a/src/empathy-main-window.ui +++ b/src/empathy-roster-window.ui @@ -91,10 +91,82 @@ </object> </child> <child> - <object class="GtkLabel" id="no_entry_label"> + <object class="GtkBox" id="box1"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="yalign">0.30000001192092896</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkImage" id="image1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="yalign">1</property> + <property name="ypad">1</property> + <property name="icon_name">avatar-default</property> + <property name="icon-size">6</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="no_entry_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="yalign">0.30000001192092896</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="yalign">0</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <child> + <object class="GtkButton" id="button_account_settings"> + <property name="label" translatable="yes">Account settings</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="margin_left">6</property> + <property name="margin_right">6</property> + <property name="use_action_appearance">False</property> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="yalign">0</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <child> + <object class="GtkSpinner" id="spinner_loading"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> </object> <packing> <property name="position">1</property> diff --git a/src/empathy-rounded-actor.c b/src/empathy-rounded-actor.c index 0537dea96..045f08de7 100644 --- a/src/empathy-rounded-actor.c +++ b/src/empathy-rounded-actor.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "config.h" #include <clutter/clutter.h> #include <clutter-gtk/clutter-gtk.h> diff --git a/src/empathy-rounded-rectangle.c b/src/empathy-rounded-rectangle.c index e06889d58..4cce28b06 100644 --- a/src/empathy-rounded-rectangle.c +++ b/src/empathy-rounded-rectangle.c @@ -18,6 +18,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "config.h" + #include <math.h> #include <clutter/clutter.h> diff --git a/src/empathy.c b/src/empathy.c index b73bf1b03..f59a54f17 100644 --- a/src/empathy.c +++ b/src/empathy.c @@ -48,6 +48,8 @@ #include <telepathy-logger/log-manager.h> +#include <libempathy/empathy-client-factory.h> +#include <libempathy/empathy-connection-aggregator.h> #include <libempathy/empathy-presence-manager.h> #include <libempathy/empathy-utils.h> #include <libempathy/empathy-chatroom-manager.h> @@ -63,7 +65,7 @@ #include <libempathy-gtk/empathy-location-manager.h> #include <libempathy-gtk/empathy-notify-manager.h> -#include "empathy-main-window.h" +#include "empathy-roster-window.h" #include "empathy-accounts-common.h" #include "empathy-accounts-dialog.h" #include "empathy-status-icon.h" @@ -122,6 +124,7 @@ struct _EmpathyApp EmpathyConnectivity *connectivity; GSettings *gsettings; EmpathyNotificationsApprover *notifications_approver; + EmpathyConnectionAggregator *conn_aggregator; #ifdef HAVE_GEOCLUE EmpathyLocationManager *location_manager; #endif @@ -167,6 +170,7 @@ empathy_app_dispose (GObject *object) tp_clear_object (&self->ft_factory); tp_clear_object (&self->gsettings); tp_clear_object (&self->notifications_approver); + tp_clear_object (&self->conn_aggregator); if (dispose != NULL) dispose (object); @@ -285,8 +289,8 @@ out: /* Rely on GNOME Shell to watch session state */ empathy_presence_manager_set_auto_away (self->presence_mgr, FALSE); - empathy_main_window_set_shell_running (EMPATHY_MAIN_WINDOW (self->window), - TRUE); + empathy_roster_window_set_shell_running ( + EMPATHY_ROSTER_WINDOW (self->window), TRUE); } else { @@ -351,7 +355,7 @@ empathy_app_command_line (GApplication *app, self->activated = TRUE; /* Setting up UI */ - self->window = empathy_main_window_dup (); + self->window = empathy_roster_window_dup (); gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (self->window)); @@ -377,8 +381,8 @@ empathy_app_command_line (GApplication *app, } if (self->show_preferences) - empathy_main_window_show_preferences (EMPATHY_MAIN_WINDOW (self->window), - self->preferences_tab); + empathy_roster_window_show_preferences ( + EMPATHY_ROSTER_WINDOW (self->window), self->preferences_tab); if (!self->start_hidden) empathy_window_present (GTK_WINDOW (self->window)); @@ -808,11 +812,34 @@ empathy_app_constructed (GObject *object) self->location_manager = empathy_location_manager_dup_singleton (); #endif + self->conn_aggregator = empathy_connection_aggregator_dup_singleton (); + self->activated = FALSE; self->ft_factory = NULL; self->window = NULL; } +static void +add_empathy_features (void) +{ + /* Add 'empathy' specific feature before doing any preparation */ + EmpathyClientFactory *factory; + + factory = empathy_client_factory_dup (); + + tp_simple_client_factory_add_connection_features_varargs ( + TP_SIMPLE_CLIENT_FACTORY (factory), + /* empathy_connection_aggregator_get_all_groups(), used by + * EmpathyGroupsWidget relies on it */ + TP_CONNECTION_FEATURE_CONTACT_GROUPS, + /* empathy_connection_aggregator_dup_all_contacts(), used by + * EmpathyEventManager relies on it */ + TP_CONNECTION_FEATURE_CONTACT_LIST, + NULL); + + g_object_unref (factory); +} + int main (int argc, char *argv[]) { @@ -832,6 +859,8 @@ main (int argc, char *argv[]) gtk_init (&argc, &argv); empathy_gtk_init (); + add_empathy_features (); + app = g_object_new (EMPATHY_TYPE_APP, "application-id", EMPATHY_DBUS_NAME, "flags", G_APPLICATION_HANDLES_COMMAND_LINE, |