X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=src%2Fempathy-chat-window.c;h=2f41d3d9b887d5b258dcf4b819727531701fd6cd;hp=eb2e4b1330d2cc9182663cd360e3e926474b15a0;hb=15aa6a83ff96fad9f959762f2b57646f9e735cd1;hpb=bf6dffc184ccc08ec6bf38c4a59898d137375097 diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index eb2e4b13..2f41d3d9 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -1,7 +1,6 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2003-2007 Imendio AB - * Copyright (C) 2007-2010 Collabora Ltd. + * Copyright (C) 2007-2012 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 @@ -26,326 +25,323 @@ * Rômulo Fernandes Machado */ -#include - -#include +#include "config.h" +#include "empathy-chat-window.h" -#include -#include -#include #include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include -#include "empathy-chat-manager.h" -#include "empathy-chat-window.h" #include "empathy-about-dialog.h" +#include "empathy-chat-manager.h" +#include "empathy-chatroom-manager.h" +#include "empathy-client-factory.h" +#include "empathy-geometry.h" +#include "empathy-gsettings.h" +#include "empathy-images.h" #include "empathy-invite-participant-dialog.h" -#include "gedit-close-button.h" +#include "empathy-notify-manager.h" +#include "empathy-request-util.h" +#include "empathy-sound-manager.h" +#include "empathy-ui-utils.h" +#include "empathy-utils.h" +#include "empathy-new-message-dialog.h" #define DEBUG_FLAG EMPATHY_DEBUG_CHAT -#include +#include "empathy-debug.h" /* Macro to compare guint32 X timestamps, while accounting for wrapping around */ #define X_EARLIER_OR_EQL(t1, t2) \ - ((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2)) \ - || (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \ - ) - -#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow) -typedef struct { - EmpathyChat *current_chat; - GList *chats; - gboolean page_added; - gboolean dnd_same_window; - EmpathyChatroomManager *chatroom_manager; - EmpathyNotifyManager *notify_mgr; - GtkWidget *dialog; - GtkWidget *notebook; - NotifyNotification *notification; - - GtkTargetList *contact_targets; - GtkTargetList *file_targets; - - EmpathyChatManager *chat_manager; - gulong chat_manager_chats_changed_id; - - /* Menu items. */ - GtkUIManager *ui_manager; - GtkAction *menu_conv_insert_smiley; - GtkAction *menu_conv_favorite; - GtkAction *menu_conv_always_urgent; - GtkAction *menu_conv_toggle_contacts; - - GtkAction *menu_edit_cut; - GtkAction *menu_edit_copy; - GtkAction *menu_edit_paste; - GtkAction *menu_edit_find; - - GtkAction *menu_tabs_next; - GtkAction *menu_tabs_prev; - GtkAction *menu_tabs_undo_close_tab; - GtkAction *menu_tabs_left; - GtkAction *menu_tabs_right; - GtkAction *menu_tabs_detach; - - /* Last user action time we acted upon to show a tab */ - guint32 x_user_action_time; - - GSettings *gsettings_chat; - GSettings *gsettings_notif; - GSettings *gsettings_ui; - - EmpathySoundManager *sound_mgr; -} EmpathyChatWindowPriv; + ((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2)) \ + || (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \ + ) + +enum +{ + PROP_INDIVIDUAL_MGR = 1 +}; + +struct _EmpathyChatWindowPriv +{ + EmpathyChat *current_chat; + GList *chats; + gboolean page_added; + gboolean dnd_same_window; + EmpathyChatroomManager *chatroom_manager; + EmpathyNotifyManager *notify_mgr; + EmpathyIndividualManager *individual_mgr; + GtkWidget *notebook; + NotifyNotification *notification; + + GtkTargetList *contact_targets; + GtkTargetList *file_targets; + + EmpathyChatManager *chat_manager; + gulong chat_manager_chats_changed_id; + + /* Menu items. */ + GtkUIManager *ui_manager; + GtkAction *menu_conv_insert_smiley; + GtkAction *menu_conv_favorite; + GtkAction *menu_conv_join_chat; + GtkAction *menu_conv_leave_chat; + GtkAction *menu_conv_always_urgent; + GtkAction *menu_conv_toggle_contacts; + + GtkAction *menu_edit_cut; + GtkAction *menu_edit_copy; + GtkAction *menu_edit_paste; + GtkAction *menu_edit_find; + + GtkAction *menu_tabs_next; + GtkAction *menu_tabs_prev; + GtkAction *menu_tabs_undo_close_tab; + GtkAction *menu_tabs_left; + GtkAction *menu_tabs_right; + GtkAction *menu_tabs_detach; + + /* Last user action time we acted upon to show a tab */ + guint32 x_user_action_time; + + GSettings *gsettings_chat; + GSettings *gsettings_notif; + GSettings *gsettings_ui; + + EmpathySoundManager *sound_mgr; + + gboolean updating_menu; +}; static GList *chat_windows = NULL; -static const guint tab_accel_keys[] = { - GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5, - GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0 +static const guint tab_accel_keys[] = +{ + GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5, + GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0 }; -typedef enum { - DND_DRAG_TYPE_CONTACT_ID, - DND_DRAG_TYPE_INDIVIDUAL_ID, - DND_DRAG_TYPE_URI_LIST, - DND_DRAG_TYPE_TAB +typedef enum +{ + DND_DRAG_TYPE_CONTACT_ID, + DND_DRAG_TYPE_INDIVIDUAL_ID, + DND_DRAG_TYPE_URI_LIST, + DND_DRAG_TYPE_TAB } DndDragType; -static const GtkTargetEntry drag_types_dest[] = { - { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID }, - { "text/x-individual-id", 0, DND_DRAG_TYPE_INDIVIDUAL_ID }, - { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB }, - { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST }, - { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST }, +static const GtkTargetEntry drag_types_dest[] = +{ + { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID }, + { "text/x-individual-id", 0, DND_DRAG_TYPE_INDIVIDUAL_ID }, + { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB }, + /* FIXME: disabled because of bug #640513 + { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST }, + { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST }, + */ }; -static const GtkTargetEntry drag_types_dest_contact[] = { - { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID }, - { "text/x-individual-id", 0, DND_DRAG_TYPE_INDIVIDUAL_ID }, +static const GtkTargetEntry drag_types_dest_contact[] = +{ + { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID }, + { "text/x-individual-id", 0, DND_DRAG_TYPE_INDIVIDUAL_ID }, }; -static const GtkTargetEntry drag_types_dest_file[] = { - /* must be first to be prioritized, in order to receive the - * note's file path from Tomboy instead of an URI */ - { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST }, - { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST }, +static const GtkTargetEntry drag_types_dest_file[] = +{ + /* must be first to be prioritized, in order to receive the + * note's file path from Tomboy instead of an URI */ + { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST }, + { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST }, }; static void chat_window_update (EmpathyChatWindow *window, - gboolean update_contact_menu); + gboolean update_contact_menu); static void empathy_chat_window_add_chat (EmpathyChatWindow *window, - EmpathyChat *chat); + EmpathyChat *chat); static void empathy_chat_window_remove_chat (EmpathyChatWindow *window, - EmpathyChat *chat); + EmpathyChat *chat); static void empathy_chat_window_move_chat (EmpathyChatWindow *old_window, - EmpathyChatWindow *new_window, - EmpathyChat *chat); + EmpathyChatWindow *new_window, + EmpathyChat *chat); static void empathy_chat_window_get_nb_chats (EmpathyChatWindow *self, - guint *nb_rooms, - guint *nb_private); + guint *nb_rooms, + guint *nb_private); -G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT); +G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, GTK_TYPE_BIN) static void -chat_window_accel_cb (GtkAccelGroup *accelgroup, - GObject *object, - guint key, - GdkModifierType mod, - EmpathyChatWindow *window) +chat_window_accel_cb (GtkAccelGroup *accelgroup, + GObject *object, + guint key, + GdkModifierType mod, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; - gint num = -1; - guint i; + gint num = -1; + guint i; - priv = GET_PRIV (window); + for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) + { + if (tab_accel_keys[i] == key) + { + num = i; + break; + } + } - for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) { - if (tab_accel_keys[i] == key) { - num = i; - break; - } - } - - if (num != -1) { - gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num); - } + if (num != -1) + gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), num); } static EmpathyChatWindow * chat_window_find_chat (EmpathyChat *chat) { - EmpathyChatWindowPriv *priv; - GList *l, *ll; + GList *l, *ll; + + for (l = chat_windows; l; l = l->next) + { + EmpathyChatWindow *window = l->data; - for (l = chat_windows; l; l = l->next) { - priv = GET_PRIV (l->data); - ll = g_list_find (priv->chats, chat); - if (ll) { - return l->data; - } - } + ll = g_list_find (window->priv->chats, chat); + if (ll) + return l->data; + } - return NULL; + return NULL; } static void -remove_all_chats (EmpathyChatWindow *window) +remove_all_chats (EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; - - priv = GET_PRIV (window); - g_object_ref (window); + g_object_ref (self); - while (priv->chats) { - empathy_chat_window_remove_chat (window, priv->chats->data); - } + while (self->priv->chats) + empathy_chat_window_remove_chat (self, self->priv->chats->data); - g_object_unref (window); + g_object_unref (self); } 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 + 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 *self, + gboolean close_window, + guint n_rooms, + EmpathyChat *chat) +{ + 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); + + /* If there are no chats in this window, how could we possibly have got + * here? + */ + g_return_if_fail (self->priv->chats != NULL); + + /* Treat closing a window which only has one tab exactly like closing + * that tab. + */ + if (close_window && self->priv->chats->next == NULL) + { + close_window = FALSE; + chat = self->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 (gtk_widget_get_toplevel (GTK_WIDGET (self))), + 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), self); + + 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. @@ -353,2407 +349,2589 @@ confirm_close (EmpathyChatWindow *window, static gboolean chat_needs_close_confirmation (EmpathyChat *chat) { - return (empathy_chat_is_room (chat) - && empathy_chat_get_tp_chat (chat) != NULL); + return (empathy_chat_is_room (chat) && + empathy_chat_get_tp_chat (chat) != NULL); } static void maybe_close_chat (EmpathyChatWindow *window, - EmpathyChat *chat) + EmpathyChat *chat) { - g_return_if_fail (chat != NULL); + 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); - } + 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) +chat_window_close_clicked_cb (GtkAction *action, + EmpathyChat *chat) { - EmpathyChatWindow *window; + EmpathyChatWindow *window; - window = chat_window_find_chat (chat); - maybe_close_chat (window, chat); + window = chat_window_find_chat (chat); + maybe_close_chat (window, chat); } static void chat_tab_style_updated_cb (GtkWidget *hbox, - gpointer user_data) + gpointer user_data) { - GtkWidget *button; - int char_width, h, w; - PangoContext *context; - const PangoFontDescription *font_desc; - PangoFontMetrics *metrics; + GtkWidget *button; + int char_width, h, w; + PangoContext *context; + PangoFontDescription *font_desc; + PangoFontMetrics *metrics; - button = g_object_get_data (G_OBJECT (user_data), - "chat-window-tab-close-button"); - context = gtk_widget_get_pango_context (hbox); + button = g_object_get_data (G_OBJECT (user_data), + "chat-window-tab-close-button"); + context = gtk_widget_get_pango_context (hbox); - font_desc = gtk_style_context_get_font (gtk_widget_get_style_context (hbox), - GTK_STATE_FLAG_NORMAL); + gtk_style_context_get (gtk_widget_get_style_context (hbox), + GTK_STATE_FLAG_NORMAL, + "font", &font_desc, + NULL); - metrics = pango_context_get_metrics (context, font_desc, - pango_context_get_language (context)); - char_width = pango_font_metrics_get_approximate_char_width (metrics); - pango_font_metrics_unref (metrics); + metrics = pango_context_get_metrics (context, font_desc, + pango_context_get_language (context)); + char_width = pango_font_metrics_get_approximate_char_width (metrics); + pango_font_metrics_unref (metrics); - gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button), - GTK_ICON_SIZE_MENU, &w, &h); + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button), + GTK_ICON_SIZE_MENU, &w, &h); - /* Request at least about 12 chars width plus at least space for the status - * image and the close button */ - gtk_widget_set_size_request (hbox, - 12 * PANGO_PIXELS (char_width) + 2 * w, -1); + /* Request at least about 12 chars width plus at least space for the status + * image and the close button */ + gtk_widget_set_size_request (hbox, + 12 * PANGO_PIXELS (char_width) + 2 * w, -1); - gtk_widget_set_size_request (button, w, h); + gtk_widget_set_size_request (button, w, h); + pango_font_description_free (font_desc); +} + +static GtkWidget * +create_close_button (void) +{ + GtkWidget *button, *image; + GtkStyleContext *context; + + button = gtk_button_new (); + + context = gtk_widget_get_style_context (button); + gtk_style_context_add_class (context, "empathy-tab-close-button"); + + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE); + + /* We don't want focus/keynav for the button to avoid clutter, and + * Ctrl-W works anyway. + */ + gtk_widget_set_can_focus (button, FALSE); + gtk_widget_set_can_default (button, FALSE); + + image = gtk_image_new_from_icon_name ("window-close-symbolic", + GTK_ICON_SIZE_MENU); + gtk_widget_show (image); + + gtk_container_add (GTK_CONTAINER (button), image); + + return button; } static GtkWidget * chat_window_create_label (EmpathyChatWindow *window, - EmpathyChat *chat, - gboolean is_tab_label) + EmpathyChat *chat, + gboolean is_tab_label) { - GtkWidget *hbox; - GtkWidget *name_label; - GtkWidget *status_image; - GtkWidget *event_box; - GtkWidget *event_box_hbox; - PangoAttrList *attr_list; - PangoAttribute *attr; + GtkWidget *hbox; + GtkWidget *name_label; + GtkWidget *status_image; + GtkWidget *event_box; + GtkWidget *event_box_hbox; + PangoAttrList *attr_list; + PangoAttribute *attr; - /* The spacing between the button and the label. */ - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + /* The spacing between the button and the label. */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - event_box = gtk_event_box_new (); - gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE); + event_box = gtk_event_box_new (); + gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE); - name_label = gtk_label_new (NULL); - if (is_tab_label) - gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END); + name_label = gtk_label_new (NULL); + if (is_tab_label) + gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END); - attr_list = pango_attr_list_new (); - attr = pango_attr_scale_new (1/1.2); - attr->start_index = 0; - attr->end_index = -1; - pango_attr_list_insert (attr_list, attr); - gtk_label_set_attributes (GTK_LABEL (name_label), attr_list); - pango_attr_list_unref (attr_list); - - gtk_misc_set_padding (GTK_MISC (name_label), 2, 0); - gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5); - g_object_set_data (G_OBJECT (chat), - is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label", - name_label); + attr_list = pango_attr_list_new (); + attr = pango_attr_scale_new (1/1.2); + attr->start_index = 0; + attr->end_index = -1; + pango_attr_list_insert (attr_list, attr); + gtk_label_set_attributes (GTK_LABEL (name_label), attr_list); + pango_attr_list_unref (attr_list); - status_image = gtk_image_new (); + gtk_misc_set_padding (GTK_MISC (name_label), 2, 0); + gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5); + g_object_set_data (G_OBJECT (chat), + is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label", + name_label); - /* Spacing between the icon and label. */ - event_box_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + status_image = gtk_image_new (); - gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0); + /* Spacing between the icon and label. */ + event_box_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - g_object_set_data (G_OBJECT (chat), - is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image", - status_image); - g_object_set_data (G_OBJECT (chat), - is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget", - event_box); + gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0); - gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox); - gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0); + g_object_set_data (G_OBJECT (chat), + is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image", + status_image); + g_object_set_data (G_OBJECT (chat), + is_tab_label ? "chat-window-tab-tooltip-widget" : + "chat-window-menu-tooltip-widget", + event_box); - if (is_tab_label) { - GtkWidget *close_button; - GtkWidget *sending_spinner; + gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox); + gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0); - sending_spinner = gtk_spinner_new (); + if (is_tab_label) + { + GtkWidget *close_button; + GtkWidget *sending_spinner; - gtk_box_pack_start (GTK_BOX (hbox), sending_spinner, - FALSE, FALSE, 0); - g_object_set_data (G_OBJECT (chat), - "chat-window-tab-sending-spinner", - sending_spinner); + sending_spinner = gtk_spinner_new (); - close_button = gedit_close_button_new (); - g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button); + gtk_box_pack_start (GTK_BOX (hbox), sending_spinner, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (chat), + "chat-window-tab-sending-spinner", + sending_spinner); - /* We don't want focus/keynav for the button to avoid clutter, and - * Ctrl-W works anyway. - */ - gtk_widget_set_can_focus (close_button, FALSE); - gtk_widget_set_can_default (close_button, FALSE); + close_button = create_close_button (); + g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", + close_button); - gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0); - g_signal_connect (close_button, - "clicked", - G_CALLBACK (chat_window_close_clicked_cb), - chat); + g_signal_connect (close_button, + "clicked", + G_CALLBACK (chat_window_close_clicked_cb), chat); - /* React to theme changes and also setup the size correctly. */ - g_signal_connect (hbox, - "style-updated", - G_CALLBACK (chat_tab_style_updated_cb), - chat); - } + /* React to theme changes and also setup the size correctly. */ + g_signal_connect (hbox, "style-updated", + G_CALLBACK (chat_tab_style_updated_cb), chat); + } - gtk_widget_show_all (hbox); + gtk_widget_show_all (hbox); - return hbox; + return hbox; } static void -_submenu_notify_visible_changed_cb (GObject *object, - GParamSpec *pspec, - gpointer userdata) +_submenu_notify_visible_changed_cb (GObject *object, + GParamSpec *pspec, + gpointer userdata) { - g_signal_handlers_disconnect_by_func (object, - _submenu_notify_visible_changed_cb, - userdata); - chat_window_update (EMPATHY_CHAT_WINDOW (userdata), TRUE); + g_signal_handlers_disconnect_by_func (object, + _submenu_notify_visible_changed_cb, userdata); + + chat_window_update (EMPATHY_CHAT_WINDOW (userdata), TRUE); } static void -chat_window_menu_context_update (EmpathyChatWindowPriv *priv, - gint num_pages) -{ - gboolean first_page; - gboolean last_page; - gboolean wrap_around; - gboolean is_connected; - gint page_num; - - page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); - first_page = (page_num == 0); - last_page = (page_num == (num_pages - 1)); - g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around", - &wrap_around, NULL); - is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL; - - gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page || - wrap_around)); - gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page || - wrap_around)); - gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1); - gtk_action_set_sensitive (priv->menu_tabs_left, !first_page); - gtk_action_set_sensitive (priv->menu_tabs_right, !last_page); - gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected); +chat_window_menu_context_update (EmpathyChatWindow *self, + gint num_pages) +{ + gboolean first_page; + gboolean last_page; + gboolean wrap_around; + gboolean is_connected; + gint page_num; + + page_num = gtk_notebook_get_current_page ( + GTK_NOTEBOOK (self->priv->notebook)); + first_page = (page_num == 0); + last_page = (page_num == (num_pages - 1)); + g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around", + &wrap_around, NULL); + is_connected = empathy_chat_get_tp_chat (self->priv->current_chat) != NULL; + + gtk_action_set_sensitive (self->priv->menu_tabs_next, (!last_page || + wrap_around)); + gtk_action_set_sensitive (self->priv->menu_tabs_prev, (!first_page || + wrap_around)); + gtk_action_set_sensitive (self->priv->menu_tabs_detach, num_pages > 1); + gtk_action_set_sensitive (self->priv->menu_tabs_left, !first_page); + gtk_action_set_sensitive (self->priv->menu_tabs_right, !last_page); + gtk_action_set_sensitive (self->priv->menu_conv_insert_smiley, is_connected); } static void -chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv, - EmpathyChatWindow *self) +chat_window_conversation_menu_update (EmpathyChatWindow *self) { - EmpathyTpChat *tp_chat; - TpConnection *connection; - GtkAction *action; - gboolean sensitive = FALSE; + EmpathyTpChat *tp_chat; + TpConnection *connection; + GtkAction *action; + gboolean sensitive = FALSE; - g_return_if_fail (priv->current_chat != NULL); + g_return_if_fail (self->priv->current_chat != NULL); - action = gtk_ui_manager_get_action (priv->ui_manager, - "/chats_menubar/menu_conv/menu_conv_invite_participant"); - tp_chat = empathy_chat_get_tp_chat (priv->current_chat); + action = gtk_ui_manager_get_action (self->priv->ui_manager, + "/chats_menubar/menu_conv/menu_conv_invite_participant"); + tp_chat = empathy_chat_get_tp_chat (self->priv->current_chat); - if (tp_chat != NULL) { - connection = tp_channel_borrow_connection (TP_CHANNEL (tp_chat)); + if (tp_chat != NULL) + { + connection = tp_channel_get_connection (TP_CHANNEL (tp_chat)); - sensitive = empathy_tp_chat_can_add_contact (tp_chat) && - (tp_connection_get_status (connection, NULL) == - TP_CONNECTION_STATUS_CONNECTED); - } + sensitive = empathy_tp_chat_can_add_contact (tp_chat) && + (tp_connection_get_status (connection, NULL) == + TP_CONNECTION_STATUS_CONNECTED); + } - gtk_action_set_sensitive (action, sensitive); + gtk_action_set_sensitive (action, sensitive); } static void -chat_window_contact_menu_update (EmpathyChatWindowPriv *priv, - EmpathyChatWindow *window) +chat_window_contact_menu_update (EmpathyChatWindow *self) { - GtkWidget *menu, *submenu, *orig_submenu; + GtkWidget *menu, *submenu, *orig_submenu; - menu = gtk_ui_manager_get_widget (priv->ui_manager, - "/chats_menubar/menu_contact"); - orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu)); + if (self->priv->current_chat == NULL) + return; - if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) { - submenu = empathy_chat_get_contact_menu (priv->current_chat); + if (self->priv->updating_menu) + return; + self->priv->updating_menu = TRUE; - if (submenu != NULL) { - /* gtk_menu_attach_to_widget () doesn't behave nicely here */ - g_object_set_data (G_OBJECT (submenu), "window", priv->dialog); + menu = gtk_ui_manager_get_widget (self->priv->ui_manager, + "/chats_menubar/menu_contact"); + orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu)); - gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu); - gtk_widget_show (menu); - gtk_widget_set_sensitive (menu, TRUE); - } else { - gtk_widget_set_sensitive (menu, FALSE); - } - } else { - tp_g_signal_connect_object (orig_submenu, - "notify::visible", - (GCallback)_submenu_notify_visible_changed_cb, - window, 0); - } -} + if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) + { + submenu = empathy_chat_get_contact_menu (self->priv->current_chat); -static guint -get_all_unread_messages (EmpathyChatWindowPriv *priv) -{ - GList *l; - guint nb = 0; + if (submenu != NULL) + { + /* gtk_menu_attach_to_widget () doesn't behave nicely here */ + g_object_set_data (G_OBJECT (submenu), "window", self); - for (l = priv->chats; l != NULL; l = g_list_next (l)) - nb += empathy_chat_get_nb_unread_messages (EMPATHY_CHAT (l->data)); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu); + gtk_widget_show (menu); + gtk_widget_set_sensitive (menu, TRUE); + } + else + { + gtk_widget_set_sensitive (menu, FALSE); + } + } + else + { + tp_g_signal_connect_object (orig_submenu, + "notify::visible", + (GCallback)_submenu_notify_visible_changed_cb, self, 0); + } - return nb; + self->priv->updating_menu = FALSE; } -static gchar * -get_window_title_name (EmpathyChatWindowPriv *priv) +static guint +get_all_unread_messages (EmpathyChatWindow *self) { - gchar *active_name, *ret; - guint nb_chats; - guint current_unread_msgs; - - nb_chats = g_list_length (priv->chats); - g_assert (nb_chats > 0); - - active_name = empathy_chat_dup_name (priv->current_chat); - - current_unread_msgs = empathy_chat_get_nb_unread_messages ( - priv->current_chat); + GList *l; + guint nb = 0; - if (nb_chats == 1) { - /* only one tab */ - if (current_unread_msgs == 0) - ret = g_strdup (active_name); - else - ret = g_strdup_printf (ngettext ( - "%s (%d unread)", - "%s (%d unread)", current_unread_msgs), - active_name, current_unread_msgs); - } else { - guint nb_others = nb_chats - 1; - guint all_unread_msgs; - - all_unread_msgs = get_all_unread_messages (priv); - - if (all_unread_msgs == 0) { - /* no unread message */ - ret = g_strdup_printf (ngettext ( - "%s (and %u other)", - "%s (and %u others)", nb_others), - active_name, nb_others); - } - - else if (all_unread_msgs == current_unread_msgs) { - /* unread messages are in the current tab */ - ret = g_strdup_printf (ngettext ( - "%s (%d unread)", - "%s (%d unread)", current_unread_msgs), - active_name, current_unread_msgs); - } + for (l = self->priv->chats; l != NULL; l = g_list_next (l)) + nb += empathy_chat_get_nb_unread_messages (EMPATHY_CHAT (l->data)); - else if (current_unread_msgs == 0) { - /* unread messages are in other tabs */ - ret = g_strdup_printf (ngettext ( - "%s (%d unread from others)", - "%s (%d unread from others)", - all_unread_msgs), - active_name, all_unread_msgs); - } - - else { - /* unread messages are in all the tabs */ - ret = g_strdup_printf (ngettext ( - "%s (%d unread from all)", - "%s (%d unread from all)", - all_unread_msgs), - active_name, all_unread_msgs); - } - } - - g_free (active_name); + return nb; +} - return ret; +static gchar * +get_window_title_name (EmpathyChatWindow *self) +{ + gchar *active_name, *ret; + guint nb_chats; + guint current_unread_msgs; + + nb_chats = g_list_length (self->priv->chats); + g_assert (nb_chats > 0); + + active_name = empathy_chat_dup_name (self->priv->current_chat); + + current_unread_msgs = empathy_chat_get_nb_unread_messages ( + self->priv->current_chat); + + if (nb_chats == 1) + { + /* only one tab */ + if (current_unread_msgs == 0) + ret = g_strdup (active_name); + else + ret = g_strdup_printf (ngettext ( + "%s (%d unread)", + "%s (%d unread)", current_unread_msgs), + active_name, current_unread_msgs); + } + else + { + guint nb_others = nb_chats - 1; + guint all_unread_msgs; + + all_unread_msgs = get_all_unread_messages (self); + + if (all_unread_msgs == 0) + { + /* no unread message */ + ret = g_strdup_printf (ngettext ( + "%s (and %u other)", + "%s (and %u others)", nb_others), + active_name, nb_others); + } + else if (all_unread_msgs == current_unread_msgs) + { + /* unread messages are in the current tab */ + ret = g_strdup_printf (ngettext ( + "%s (%d unread)", + "%s (%d unread)", current_unread_msgs), + active_name, current_unread_msgs); + } + else if (current_unread_msgs == 0) + { + /* unread messages are in other tabs */ + ret = g_strdup_printf (ngettext ( + "%s (%d unread from others)", + "%s (%d unread from others)", + all_unread_msgs), + active_name, all_unread_msgs); + } + else + { + /* unread messages are in all the tabs */ + ret = g_strdup_printf (ngettext ( + "%s (%d unread from all)", + "%s (%d unread from all)", + all_unread_msgs), + active_name, all_unread_msgs); + } + } + + g_free (active_name); + + return ret; } static void -chat_window_title_update (EmpathyChatWindowPriv *priv) +chat_window_title_update (EmpathyChatWindow *self) { - gchar *name; + gchar *name; - name = get_window_title_name (priv); - gtk_window_set_title (GTK_WINDOW (priv->dialog), name); - g_free (name); + name = get_window_title_name (self); + //gtk_window_set_title (GTK_WINDOW (self), name); + g_free (name); } static void -chat_window_icon_update (EmpathyChatWindowPriv *priv, gboolean new_messages) -{ - GdkPixbuf *icon; - EmpathyContact *remote_contact; - gboolean avatar_in_icon; - guint n_chats; - - n_chats = g_list_length (priv->chats); - - /* Update window icon */ - if (new_messages) { - gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), - EMPATHY_IMAGE_MESSAGE); - } else { - avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat, - EMPATHY_PREFS_CHAT_AVATAR_IN_ICON); - - if (n_chats == 1 && avatar_in_icon) { - remote_contact = empathy_chat_get_remote_contact (priv->current_chat); - icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0); - gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon); - - if (icon != NULL) { - g_object_unref (icon); - } - } else { - gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL); - } - } +chat_window_icon_update (EmpathyChatWindow *self, + gboolean new_messages) +{ + GdkPixbuf *icon; + EmpathyContact *remote_contact; + gboolean avatar_in_icon; + guint n_chats; + + n_chats = g_list_length (self->priv->chats); + + /* Update window icon */ + if (new_messages) + { + //gtk_window_set_icon_name (GTK_WINDOW (self), + // EMPATHY_IMAGE_MESSAGE); + } + else + { + avatar_in_icon = g_settings_get_boolean (self->priv->gsettings_chat, + EMPATHY_PREFS_CHAT_AVATAR_IN_ICON); + + if (n_chats == 1 && avatar_in_icon) + { + remote_contact = empathy_chat_get_remote_contact (self->priv->current_chat); + icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, + 0, 0); + //gtk_window_set_icon (GTK_WINDOW (self), icon); + + if (icon != NULL) + g_object_unref (icon); + } + else + { + //gtk_window_set_icon_name (GTK_WINDOW (self), NULL); + } + } } static void -chat_window_close_button_update (EmpathyChatWindowPriv *priv, - gint num_pages) -{ - GtkWidget *chat; - GtkWidget *chat_close_button; - gint i; - - if (num_pages == 1) { - chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0); - chat_close_button = g_object_get_data (G_OBJECT (chat), - "chat-window-tab-close-button"); - gtk_widget_hide (chat_close_button); - } else { - for (i=0; inotebook), i); - chat_close_button = g_object_get_data (G_OBJECT (chat), - "chat-window-tab-close-button"); - gtk_widget_show (chat_close_button); - } - } +chat_window_close_button_update (EmpathyChatWindow *self, + gint num_pages) +{ + GtkWidget *chat; + GtkWidget *chat_close_button; + gint i; + + if (num_pages == 1) + { + chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (self->priv->notebook), 0); + chat_close_button = g_object_get_data (G_OBJECT (chat), + "chat-window-tab-close-button"); + gtk_widget_hide (chat_close_button); + } + else + { + for (i=0; ipriv->notebook), i); + chat_close_button = g_object_get_data (G_OBJECT (chat), + "chat-window-tab-close-button"); + gtk_widget_show (chat_close_button); + } + } } static void -chat_window_update (EmpathyChatWindow *window, - gboolean update_contact_menu) +chat_window_update (EmpathyChatWindow *self, + gboolean update_contact_menu) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - gint num_pages; + gint num_pages; - num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)); + num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (self->priv->notebook)); - /* Update Tab menu */ - chat_window_menu_context_update (priv, - num_pages); + /* Update Tab menu */ + chat_window_menu_context_update (self, num_pages); - chat_window_conversation_menu_update (priv, window); + chat_window_conversation_menu_update (self); - /* If this update is due to a focus-in event, we know the menu will be - the same as when we last left it, so no work to do. Besides, if we - swap out the menu on a focus-in, we may confuse any external global - menu watching. */ - if (update_contact_menu) { - chat_window_contact_menu_update (priv, - window); - } + /* If this update is due to a focus-in event, we know the menu will be + the same as when we last left it, so no work to do. Besides, if we + swap out the menu on a focus-in, we may confuse any external global + menu watching. */ + if (update_contact_menu) + { + chat_window_contact_menu_update (self); + } - chat_window_title_update (priv); + chat_window_title_update (self); - chat_window_icon_update (priv, get_all_unread_messages (priv) > 0); + chat_window_icon_update (self, get_all_unread_messages (self) > 0); - chat_window_close_button_update (priv, - num_pages); + chat_window_close_button_update (self, num_pages); } static void -append_markup_printf (GString *string, - const char *format, - ...) +append_markup_printf (GString *string, + const char *format, + ...) { - gchar *tmp; - va_list args; + gchar *tmp; + va_list args; - va_start (args, format); + va_start (args, format); - tmp = g_markup_vprintf_escaped (format, args); - g_string_append (string, tmp); - g_free (tmp); + tmp = g_markup_vprintf_escaped (format, args); + g_string_append (string, tmp); + g_free (tmp); - va_end (args); + va_end (args); } static void chat_window_update_chat_tab_full (EmpathyChat *chat, - gboolean update_contact_menu) -{ - EmpathyChatWindow *window; - EmpathyChatWindowPriv *priv; - EmpathyContact *remote_contact; - gchar *name; - const gchar *id; - TpAccount *account; - const gchar *subject; - const gchar *status = NULL; - GtkWidget *widget; - GString *tooltip; - gchar *markup; - const gchar *icon_name; - GtkWidget *tab_image; - GtkWidget *menu_image; - GtkWidget *sending_spinner; - guint nb_sending; - - window = chat_window_find_chat (chat); - if (!window) { - return; - } - priv = GET_PRIV (window); - - /* Get information */ - name = empathy_chat_dup_name (chat); - account = empathy_chat_get_account (chat); - subject = empathy_chat_get_subject (chat); - remote_contact = empathy_chat_get_remote_contact (chat); - - DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p", - name, tp_proxy_get_object_path (account), subject, remote_contact); - - /* Update tab image */ - if (empathy_chat_get_tp_chat (chat) == NULL) { - /* No TpChat, we are disconnected */ - icon_name = NULL; - } - else if (empathy_chat_get_nb_unread_messages (chat) > 0) { - icon_name = EMPATHY_IMAGE_MESSAGE; - } - else if (remote_contact && empathy_chat_is_composing (chat)) { - icon_name = EMPATHY_IMAGE_TYPING; - } - else if (empathy_chat_is_sms_channel (chat)) { - icon_name = EMPATHY_IMAGE_SMS; - } - else if (remote_contact) { - icon_name = empathy_icon_name_for_contact (remote_contact); - } else { - icon_name = EMPATHY_IMAGE_GROUP_MESSAGE; - } - - tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image"); - menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image"); - if (icon_name != NULL) { - gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU); - gtk_widget_show (tab_image); - gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU); - gtk_widget_show (menu_image); - } else { - gtk_widget_hide (tab_image); - gtk_widget_hide (menu_image); - } - - /* Update the sending spinner */ - nb_sending = empathy_chat_get_n_messages_sending (chat); - sending_spinner = g_object_get_data (G_OBJECT (chat), - "chat-window-tab-sending-spinner"); - - g_object_set (sending_spinner, - "active", nb_sending > 0, - "visible", nb_sending > 0, - NULL); - - /* Update tab tooltip */ - tooltip = g_string_new (NULL); - - if (remote_contact) { - id = empathy_contact_get_id (remote_contact); - status = empathy_contact_get_presence_message (remote_contact); - } else { - id = name; - } - - if (empathy_chat_is_sms_channel (chat)) { - append_markup_printf (tooltip, "%s ", _("SMS:")); - } - - append_markup_printf (tooltip, - "%s (%s)", - id, - tp_account_get_display_name (account)); - - if (nb_sending > 0) { - char *tmp = g_strdup_printf ( - ngettext ("Sending %d message", - "Sending %d messages", - nb_sending), - nb_sending); - - g_string_append (tooltip, "\n"); - g_string_append (tooltip, tmp); - - gtk_widget_set_tooltip_text (sending_spinner, tmp); - g_free (tmp); - } - - if (!EMP_STR_EMPTY (status)) { - append_markup_printf (tooltip, "\n%s", status); - } - - if (subject) { - append_markup_printf (tooltip, "\n%s %s", - _("Topic:"), subject); - } - - if (remote_contact && empathy_chat_is_composing (chat)) { - append_markup_printf (tooltip, "\n%s", _("Typing a message.")); - } - - if (remote_contact != NULL) { - const gchar * const *types; - - types = empathy_contact_get_client_types (remote_contact); - if (types != NULL && !tp_strdiff (types[0], "phone")) { - /* I'm on a phone ! */ - gchar *tmp = name; - - name = g_strdup_printf ("☎ %s", name); - g_free (tmp); - } - } - - markup = g_string_free (tooltip, FALSE); - widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget"); - gtk_widget_set_tooltip_markup (widget, markup); - widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget"); - gtk_widget_set_tooltip_markup (widget, markup); - g_free (markup); - - /* Update tab and menu label */ - if (empathy_chat_is_highlighted (chat)) { - markup = g_markup_printf_escaped ( - "%s", - name); - } else { - markup = g_markup_escape_text (name, -1); - } - - 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 (markup); - - /* Update the window if it's the current chat */ - if (priv->current_chat == chat) { - chat_window_update (window, update_contact_menu); - } - - g_free (name); + gboolean update_contact_menu) +{ + EmpathyChatWindow *self; + EmpathyContact *remote_contact; + gchar *name; + const gchar *id; + TpAccount *account; + const gchar *subject; + const gchar *status = NULL; + GtkWidget *widget; + GString *tooltip; + gchar *markup; + const gchar *icon_name; + GtkWidget *tab_image; + GtkWidget *menu_image; + GtkWidget *sending_spinner; + guint nb_sending; + + self = chat_window_find_chat (chat); + if (!self) + return; + + /* Get information */ + name = empathy_chat_dup_name (chat); + account = empathy_chat_get_account (chat); + subject = empathy_chat_get_subject (chat); + remote_contact = empathy_chat_get_remote_contact (chat); + + DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, " + "remote_contact=%p", + name, tp_proxy_get_object_path (account), subject, remote_contact); + + /* Update tab image */ + if (empathy_chat_get_tp_chat (chat) == NULL) + { + /* No TpChat, we are disconnected */ + icon_name = NULL; + } + else if (empathy_chat_get_nb_unread_messages (chat) > 0) + { + icon_name = EMPATHY_IMAGE_MESSAGE; + } + else if (remote_contact && empathy_chat_is_composing (chat)) + { + icon_name = EMPATHY_IMAGE_TYPING; + } + else if (empathy_chat_is_sms_channel (chat)) + { + icon_name = EMPATHY_IMAGE_SMS; + } + else if (remote_contact) + { + icon_name = empathy_icon_name_for_contact (remote_contact); + } + else + { + icon_name = EMPATHY_IMAGE_GROUP_MESSAGE; + } + + tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image"); + menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image"); + + if (icon_name != NULL) + { + gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, + GTK_ICON_SIZE_MENU); + gtk_widget_show (tab_image); + gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, + GTK_ICON_SIZE_MENU); + gtk_widget_show (menu_image); + } + else + { + gtk_widget_hide (tab_image); + gtk_widget_hide (menu_image); + } + + /* Update the sending spinner */ + nb_sending = empathy_chat_get_n_messages_sending (chat); + sending_spinner = g_object_get_data (G_OBJECT (chat), + "chat-window-tab-sending-spinner"); + + g_object_set (sending_spinner, + "active", nb_sending > 0, + "visible", nb_sending > 0, + NULL); + + /* Update tab tooltip */ + tooltip = g_string_new (NULL); + + if (remote_contact) + { + id = empathy_contact_get_id (remote_contact); + status = empathy_contact_get_presence_message (remote_contact); + } + else + { + id = name; + } + + if (empathy_chat_is_sms_channel (chat)) + append_markup_printf (tooltip, "%s ", _("SMS:")); + + append_markup_printf (tooltip, "%s (%s)", + id, tp_account_get_display_name (account)); + + if (nb_sending > 0) + { + char *tmp = g_strdup_printf ( + ngettext ("Sending %d message", + "Sending %d messages", + nb_sending), + nb_sending); + + g_string_append (tooltip, "\n"); + g_string_append (tooltip, tmp); + + gtk_widget_set_tooltip_text (sending_spinner, tmp); + g_free (tmp); + } + + if (!TPAW_STR_EMPTY (status)) + append_markup_printf (tooltip, "\n%s", status); + + if (!TPAW_STR_EMPTY (subject)) + append_markup_printf (tooltip, "\n%s %s", + _("Topic:"), subject); + + if (remote_contact && empathy_chat_is_composing (chat)) + append_markup_printf (tooltip, "\n%s", _("Typing a message.")); + + if (remote_contact != NULL) + { + const gchar * const *types; + + types = empathy_contact_get_client_types (remote_contact); + if (empathy_client_types_contains_mobile_device ((GStrv) types)) + { + /* I'm on a mobile device ! */ + gchar *tmp = name; + + name = g_strdup_printf ("☎ %s", name); + g_free (tmp); + } + } + + markup = g_string_free (tooltip, FALSE); + widget = g_object_get_data (G_OBJECT (chat), + "chat-window-tab-tooltip-widget"); + gtk_widget_set_tooltip_markup (widget, markup); + + widget = g_object_get_data (G_OBJECT (chat), + "chat-window-menu-tooltip-widget"); + gtk_widget_set_tooltip_markup (widget, markup); + g_free (markup); + + /* Update tab and menu label */ + if (empathy_chat_is_highlighted (chat)) + { + markup = g_markup_printf_escaped ( + "%s", + name); + } + else + { + markup = g_markup_escape_text (name, -1); + } + + 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 (markup); + + /* Update the window if it's the current chat */ + if (self->priv->current_chat == chat) + chat_window_update (self, update_contact_menu); + + g_free (name); } static void chat_window_update_chat_tab (EmpathyChat *chat) { - chat_window_update_chat_tab_full (chat, TRUE); + chat_window_update_chat_tab_full (chat, TRUE); } static void chat_window_chat_notify_cb (EmpathyChat *chat) { - EmpathyChatWindow *window; - EmpathyContact *old_remote_contact; - EmpathyContact *remote_contact = NULL; + EmpathyChatWindow *window; + EmpathyContact *old_remote_contact; + EmpathyContact *remote_contact = NULL; + + old_remote_contact = g_object_get_data (G_OBJECT (chat), + "chat-window-remote-contact"); + remote_contact = empathy_chat_get_remote_contact (chat); - old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact"); - remote_contact = empathy_chat_get_remote_contact (chat); + if (old_remote_contact != remote_contact) + { + /* The remote-contact associated with the chat changed, we need + * to keep track of any change of that contact and update the + * window each time. */ + if (remote_contact) + g_signal_connect_swapped (remote_contact, "notify", + G_CALLBACK (chat_window_update_chat_tab), chat); - if (old_remote_contact != remote_contact) { - /* The remote-contact associated with the chat changed, we need - * to keep track of any change of that contact and update the - * window each time. */ - if (remote_contact) { - g_signal_connect_swapped (remote_contact, "notify", - G_CALLBACK (chat_window_update_chat_tab), - chat); - } - if (old_remote_contact) { - g_signal_handlers_disconnect_by_func (old_remote_contact, - chat_window_update_chat_tab, - chat); - } + if (old_remote_contact) + g_signal_handlers_disconnect_by_func (old_remote_contact, + chat_window_update_chat_tab, chat); - g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact", - g_object_ref (remote_contact), (GDestroyNotify) g_object_unref); - } + g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact", + g_object_ref (remote_contact), (GDestroyNotify) g_object_unref); + } - chat_window_update_chat_tab (chat); + chat_window_update_chat_tab (chat); - window = chat_window_find_chat (chat); - if (window != NULL) { - chat_window_update (window, FALSE); - } + window = chat_window_find_chat (chat); + if (window != NULL) + chat_window_update (window, FALSE); } static void chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager, - EmpathySmiley *smiley, - gpointer window) + EmpathySmiley *smiley, + gpointer user_data) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - EmpathyChat *chat; - GtkTextBuffer *buffer; - GtkTextIter iter; + EmpathyChatWindow *self = user_data; + EmpathyChat *chat; + GtkTextBuffer *buffer; - chat = priv->current_chat; + chat = self->priv->current_chat; + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); - gtk_text_buffer_get_end_iter (buffer, &iter); - gtk_text_buffer_insert (buffer, &iter, smiley->str, -1); + empathy_chat_insert_smiley (buffer, smiley); } static void -chat_window_conv_activate_cb (GtkAction *action, - EmpathyChatWindow *window) -{ - EmpathyChatWindowPriv *priv = GET_PRIV (window); - gboolean is_room; - gboolean active; - EmpathyContact *remote_contact = NULL; - - /* Favorite room menu */ - is_room = empathy_chat_is_room (priv->current_chat); - if (is_room) { - const gchar *room; - TpAccount *account; - gboolean found = FALSE; - EmpathyChatroom *chatroom; - - room = empathy_chat_get_id (priv->current_chat); - account = empathy_chat_get_account (priv->current_chat); - chatroom = empathy_chatroom_manager_find (priv->chatroom_manager, - account, room); - if (chatroom != NULL) - found = empathy_chatroom_is_favorite (chatroom); - - DEBUG ("This room %s favorite", found ? "is" : "is not"); - gtk_toggle_action_set_active ( - GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found); - - if (chatroom != NULL) - found = empathy_chatroom_is_always_urgent (chatroom); - - gtk_toggle_action_set_active ( - GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent), - found); - } - gtk_action_set_visible (priv->menu_conv_favorite, is_room); - gtk_action_set_visible (priv->menu_conv_always_urgent, is_room); - - /* Show contacts menu */ - g_object_get (priv->current_chat, - "remote-contact", &remote_contact, - "show-contacts", &active, - NULL); - if (remote_contact == NULL) { - gtk_toggle_action_set_active ( - GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts), - active); - } - gtk_action_set_visible (priv->menu_conv_toggle_contacts, - (remote_contact == NULL)); - if (remote_contact != NULL) { - g_object_unref (remote_contact); - } +chat_window_conv_activate_cb (GtkAction *action, + EmpathyChatWindow *self) +{ + gboolean is_room; + gboolean active; + EmpathyContact *remote_contact = NULL; + gboolean disconnected; + + /* Favorite room menu */ + is_room = empathy_chat_is_room (self->priv->current_chat); + if (is_room) + { + const gchar *room; + TpAccount *account; + gboolean found = FALSE; + EmpathyChatroom *chatroom; + + room = empathy_chat_get_id (self->priv->current_chat); + account = empathy_chat_get_account (self->priv->current_chat); + chatroom = empathy_chatroom_manager_find (self->priv->chatroom_manager, + account, room); + + if (chatroom != NULL) + found = empathy_chatroom_is_favorite (chatroom); + + DEBUG ("This room %s favorite", found ? "is" : "is not"); + gtk_toggle_action_set_active ( + GTK_TOGGLE_ACTION (self->priv->menu_conv_favorite), found); + + if (chatroom != NULL) + found = empathy_chatroom_is_always_urgent (chatroom); + + gtk_toggle_action_set_active ( + GTK_TOGGLE_ACTION (self->priv->menu_conv_always_urgent), found); + } + + gtk_action_set_visible (self->priv->menu_conv_favorite, is_room); + gtk_action_set_visible (self->priv->menu_conv_always_urgent, is_room); + + /* Show contacts menu */ + g_object_get (self->priv->current_chat, + "remote-contact", &remote_contact, + "show-contacts", &active, + NULL); + + if (remote_contact == NULL) + { + gtk_toggle_action_set_active ( + GTK_TOGGLE_ACTION (self->priv->menu_conv_toggle_contacts), active); + } + + /* Menu-items to be visible for MUCs only */ + gtk_action_set_visible (self->priv->menu_conv_toggle_contacts, + (remote_contact == NULL)); + + disconnected = (empathy_chat_get_tp_chat (self->priv->current_chat) == NULL); + if (disconnected) + { + gtk_action_set_visible (self->priv->menu_conv_join_chat, TRUE); + gtk_action_set_visible (self->priv->menu_conv_leave_chat, FALSE); + } + else + { + TpChannel *channel = NULL; + TpContact *self_contact = NULL; + TpHandle self_handle = 0; + + channel = (TpChannel *) (empathy_chat_get_tp_chat ( + self->priv->current_chat)); + self_contact = tp_channel_group_get_self_contact (channel); + if (self_contact == NULL) + { + /* The channel may not be a group */ + gtk_action_set_visible (self->priv->menu_conv_leave_chat, FALSE); + } + else + { + self_handle = tp_contact_get_handle (self_contact); + /* There is sometimes a lag between the members-changed signal + emitted on tp-chat and invalidated signal being emitted on the channel. + Leave Chat menu-item should be sensitive only till our self-handle is + a part of channel-members */ + gtk_action_set_visible (self->priv->menu_conv_leave_chat, + self_handle != 0); + } + + /* Join Chat is insensitive for a connected chat */ + gtk_action_set_visible (self->priv->menu_conv_join_chat, FALSE); + } + + if (remote_contact != NULL) + g_object_unref (remote_contact); } static void -chat_window_clear_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_clear_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - - empathy_chat_clear (priv->current_chat); + empathy_chat_clear (self->priv->current_chat); } static void -chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action, - EmpathyChatWindow *window) +chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - gboolean active; - TpAccount *account; - gchar *name; - const gchar *room; - EmpathyChatroom *chatroom; + gboolean active; + TpAccount *account; + gchar *name; + const gchar *room; + EmpathyChatroom *chatroom; - active = gtk_toggle_action_get_active (toggle_action); - account = empathy_chat_get_account (priv->current_chat); - room = empathy_chat_get_id (priv->current_chat); - name = empathy_chat_dup_name (priv->current_chat); + active = gtk_toggle_action_get_active (toggle_action); + account = empathy_chat_get_account (self->priv->current_chat); + room = empathy_chat_get_id (self->priv->current_chat); + name = empathy_chat_dup_name (self->priv->current_chat); - chatroom = empathy_chatroom_manager_ensure_chatroom ( - priv->chatroom_manager, - account, - room, - name); + chatroom = empathy_chatroom_manager_ensure_chatroom (self->priv->chatroom_manager, + account, room, name); - empathy_chatroom_set_favorite (chatroom, active); - g_object_unref (chatroom); - g_free (name); + empathy_chatroom_set_favorite (chatroom, active); + g_object_unref (chatroom); + g_free (name); } static void -chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action, - EmpathyChatWindow *window) +chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - gboolean active; - TpAccount *account; - gchar *name; - const gchar *room; - EmpathyChatroom *chatroom; + gboolean active; + TpAccount *account; + gchar *name; + const gchar *room; + EmpathyChatroom *chatroom; - active = gtk_toggle_action_get_active (toggle_action); - account = empathy_chat_get_account (priv->current_chat); - room = empathy_chat_get_id (priv->current_chat); - name = empathy_chat_dup_name (priv->current_chat); + active = gtk_toggle_action_get_active (toggle_action); + account = empathy_chat_get_account (self->priv->current_chat); + room = empathy_chat_get_id (self->priv->current_chat); + name = empathy_chat_dup_name (self->priv->current_chat); - chatroom = empathy_chatroom_manager_ensure_chatroom ( - priv->chatroom_manager, - account, - room, - name); + chatroom = empathy_chatroom_manager_ensure_chatroom (self->priv->chatroom_manager, + account, room, name); - empathy_chatroom_set_always_urgent (chatroom, active); - g_object_unref (chatroom); - g_free (name); + empathy_chatroom_set_always_urgent (chatroom, active); + g_object_unref (chatroom); + g_free (name); } static void -chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action, - EmpathyChatWindow *window) +chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - gboolean active; + gboolean active; - active = gtk_toggle_action_get_active (toggle_action); + active = gtk_toggle_action_get_active (toggle_action); - empathy_chat_set_show_contacts (priv->current_chat, active); + empathy_chat_set_show_contacts (self->priv->current_chat, active); } static void -chat_window_invite_participant_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_invite_participant_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; - GtkWidget *dialog; - EmpathyTpChat *tp_chat; - int response; + GtkWidget *dialog; + EmpathyTpChat *tp_chat; + int response; - priv = GET_PRIV (window); + g_return_if_fail (self->priv->current_chat != NULL); - g_return_if_fail (priv->current_chat != NULL); + tp_chat = empathy_chat_get_tp_chat (self->priv->current_chat); - tp_chat = empathy_chat_get_tp_chat (priv->current_chat); + dialog = empathy_invite_participant_dialog_new ( + GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))), tp_chat); - dialog = empathy_invite_participant_dialog_new ( - GTK_WINDOW (priv->dialog), tp_chat); - gtk_widget_show (dialog); + gtk_widget_show (dialog); - response = gtk_dialog_run (GTK_DIALOG (dialog)); + response = gtk_dialog_run (GTK_DIALOG (dialog)); - if (response == GTK_RESPONSE_ACCEPT) { - TpContact *tp_contact; - EmpathyContact *contact; + if (response == GTK_RESPONSE_ACCEPT) + { + TpContact *tp_contact; + EmpathyContact *contact; - tp_contact = empathy_invite_participant_dialog_get_selected ( - EMPATHY_INVITE_PARTICIPANT_DIALOG (dialog)); - if (tp_contact == NULL) goto out; + tp_contact = empathy_invite_participant_dialog_get_selected ( + EMPATHY_INVITE_PARTICIPANT_DIALOG (dialog)); + if (tp_contact == NULL) + goto out; - contact = empathy_contact_dup_from_tp_contact (tp_contact); + contact = empathy_contact_dup_from_tp_contact (tp_contact); - empathy_tp_chat_add (tp_chat, contact, _("Inviting you to this room")); + empathy_tp_chat_add (tp_chat, contact, _("Inviting you to this room")); - g_object_unref (contact); - } + g_object_unref (contact); + } out: - gtk_widget_destroy (dialog); + gtk_widget_destroy (dialog); } static void -chat_window_close_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_join_chat_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; - - priv = GET_PRIV (window); + g_return_if_fail (self->priv->current_chat != NULL); - g_return_if_fail (priv->current_chat != NULL); - - maybe_close_chat (window, priv->current_chat); + empathy_chat_join_muc (self->priv->current_chat, + empathy_chat_get_id (self->priv->current_chat)); } static void -chat_window_edit_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_leave_chat_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; - GtkClipboard *clipboard; - GtkTextBuffer *buffer; - gboolean text_available; - - priv = GET_PRIV (window); + EmpathyTpChat * tp_chat; - g_return_if_fail (priv->current_chat != NULL); + g_return_if_fail (self->priv->current_chat != NULL); - if (!empathy_chat_get_tp_chat (priv->current_chat)) { - gtk_action_set_sensitive (priv->menu_edit_copy, FALSE); - gtk_action_set_sensitive (priv->menu_edit_cut, FALSE); - gtk_action_set_sensitive (priv->menu_edit_paste, FALSE); - return; - } - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view)); - if (gtk_text_buffer_get_has_selection (buffer)) { - gtk_action_set_sensitive (priv->menu_edit_copy, TRUE); - gtk_action_set_sensitive (priv->menu_edit_cut, TRUE); - } else { - gboolean selection; - - selection = empathy_chat_view_get_has_selection (priv->current_chat->view); + tp_chat = empathy_chat_get_tp_chat (self->priv->current_chat); + if (tp_chat != NULL) + empathy_tp_chat_leave (tp_chat, ""); +} - gtk_action_set_sensitive (priv->menu_edit_cut, FALSE); - gtk_action_set_sensitive (priv->menu_edit_copy, selection); - } +static void +chat_window_close_activate_cb (GtkAction *action, + EmpathyChatWindow *self) +{ + g_return_if_fail (self->priv->current_chat != NULL); - clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); - text_available = gtk_clipboard_wait_is_text_available (clipboard); - gtk_action_set_sensitive (priv->menu_edit_paste, text_available); + maybe_close_chat (self, self->priv->current_chat); } static void -chat_window_cut_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_edit_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; + GtkClipboard *clipboard; + GtkTextBuffer *buffer; + gboolean text_available; - g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window)); + g_return_if_fail (self->priv->current_chat != NULL); - priv = GET_PRIV (window); + if (!empathy_chat_get_tp_chat (self->priv->current_chat)) + { + gtk_action_set_sensitive (self->priv->menu_edit_copy, FALSE); + gtk_action_set_sensitive (self->priv->menu_edit_cut, FALSE); + gtk_action_set_sensitive (self->priv->menu_edit_paste, FALSE); + return; + } - empathy_chat_cut (priv->current_chat); -} + buffer = gtk_text_view_get_buffer ( + GTK_TEXT_VIEW (self->priv->current_chat->input_text_view)); -static void -chat_window_copy_activate_cb (GtkAction *action, - EmpathyChatWindow *window) -{ - EmpathyChatWindowPriv *priv; + if (gtk_text_buffer_get_has_selection (buffer)) + { + gtk_action_set_sensitive (self->priv->menu_edit_copy, TRUE); + gtk_action_set_sensitive (self->priv->menu_edit_cut, TRUE); + } + else + { + gboolean selection; - g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window)); + selection = empathy_theme_adium_get_has_selection ( + self->priv->current_chat->view); - priv = GET_PRIV (window); + gtk_action_set_sensitive (self->priv->menu_edit_cut, FALSE); + gtk_action_set_sensitive (self->priv->menu_edit_copy, selection); + } - empathy_chat_copy (priv->current_chat); + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + text_available = gtk_clipboard_wait_is_text_available (clipboard); + gtk_action_set_sensitive (self->priv->menu_edit_paste, text_available); } static void -chat_window_paste_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_cut_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; + g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (self)); - g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window)); + empathy_chat_cut (self->priv->current_chat); +} - priv = GET_PRIV (window); +static void +chat_window_copy_activate_cb (GtkAction *action, + EmpathyChatWindow *self) +{ + g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (self)); - empathy_chat_paste (priv->current_chat); + empathy_chat_copy (self->priv->current_chat); } static void -chat_window_find_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_paste_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; + g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (self)); - g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window)); + empathy_chat_paste (self->priv->current_chat); +} - priv = GET_PRIV (window); +static void +chat_window_find_activate_cb (GtkAction *action, + EmpathyChatWindow *self) +{ + g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (self)); - empathy_chat_find (priv->current_chat); + empathy_chat_find (self->priv->current_chat); } static void -chat_window_tabs_next_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_tabs_next_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; - gint index_, numPages; - gboolean wrap_around; - - priv = GET_PRIV (window); + gint index_, numPages; + gboolean wrap_around; - g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around", - &wrap_around, NULL); + g_object_get (gtk_settings_get_default (), + "gtk-keynav-wrap-around", &wrap_around, + NULL); - index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); - numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)); + index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (self->priv->notebook)); + numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (self->priv->notebook)); - if (index_ == (numPages - 1) && wrap_around) { - gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0); - return; - } + if (index_ == (numPages - 1) && wrap_around) + { + gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), 0); + return; + } - gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook)); + gtk_notebook_next_page (GTK_NOTEBOOK (self->priv->notebook)); } static void -chat_window_tabs_previous_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_tabs_previous_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; - gint index_, numPages; - gboolean wrap_around; + gint index_, numPages; + gboolean wrap_around; - priv = GET_PRIV (window); + g_object_get (gtk_settings_get_default (), + "gtk-keynav-wrap-around", &wrap_around, + NULL); - g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around", - &wrap_around, NULL); + index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (self->priv->notebook)); + numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (self->priv->notebook)); - index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); - numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)); + if (index_ <= 0 && wrap_around) + { + gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), + numPages - 1); + return; + } - if (index_ <= 0 && wrap_around) { - gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1); - return; - } + gtk_notebook_prev_page (GTK_NOTEBOOK (self->priv->notebook)); +} - gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook)); +void +empathy_chat_window_next_tab (EmpathyChatWindow *self) +{ + chat_window_tabs_next_activate_cb (NULL, self); } -static void -chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +void +empathy_chat_window_prev_tab (EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - empathy_chat_manager_undo_closed_chat (priv->chat_manager, - empathy_get_current_action_time ()); + chat_window_tabs_previous_activate_cb (NULL, self); } + static void -chat_window_tabs_left_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; - EmpathyChat *chat; - gint index_, num_pages; + empathy_chat_manager_undo_closed_chat (self->priv->chat_manager, + empathy_get_current_action_time ()); +} - priv = GET_PRIV (window); +static void +chat_window_tabs_left_activate_cb (GtkAction *action, + EmpathyChatWindow *self) +{ + EmpathyChat *chat; + gint index_, num_pages; - chat = priv->current_chat; - index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); - if (index_ <= 0) { - return; - } + chat = self->priv->current_chat; + index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (self->priv->notebook)); + if (index_ <= 0) + return; - gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook), - GTK_WIDGET (chat), - index_ - 1); + gtk_notebook_reorder_child (GTK_NOTEBOOK (self->priv->notebook), GTK_WIDGET (chat), + index_ - 1); - num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)); - chat_window_menu_context_update (priv, num_pages); + num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (self->priv->notebook)); + chat_window_menu_context_update (self, num_pages); } static void -chat_window_tabs_right_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_tabs_right_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; - EmpathyChat *chat; - gint index_, num_pages; - - priv = GET_PRIV (window); + EmpathyChat *chat; + gint index_, num_pages; - chat = priv->current_chat; - index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); + chat = self->priv->current_chat; + index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (self->priv->notebook)); - gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook), - GTK_WIDGET (chat), - index_ + 1); + gtk_notebook_reorder_child (GTK_NOTEBOOK (self->priv->notebook), GTK_WIDGET (chat), + index_ + 1); - num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)); - chat_window_menu_context_update (priv, num_pages); + num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (self->priv->notebook)); + chat_window_menu_context_update (self, num_pages); } -static EmpathyChatWindow * +EmpathyChatWindow * empathy_chat_window_new (void) { - return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL)); + return g_object_new (EMPATHY_TYPE_CHAT_WINDOW, + NULL); } static void -chat_window_detach_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_detach_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; - EmpathyChatWindow *new_window; - EmpathyChat *chat; + EmpathyChatWindow *new_window; + EmpathyChat *chat; - priv = GET_PRIV (window); + chat = self->priv->current_chat; + new_window = empathy_chat_window_new (); - chat = priv->current_chat; - new_window = empathy_chat_window_new (); + empathy_chat_window_move_chat (self, new_window, chat); - empathy_chat_window_move_chat (window, new_window, chat); - - priv = GET_PRIV (new_window); - gtk_widget_show (priv->dialog); + gtk_widget_show (GTK_WIDGET (new_window)); } static void -chat_window_help_contents_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_help_contents_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - - empathy_url_show (priv->dialog, "help:empathy"); + empathy_url_show (GTK_WIDGET (self), "help:empathy"); } static void -chat_window_help_about_activate_cb (GtkAction *action, - EmpathyChatWindow *window) +chat_window_help_about_activate_cb (GtkAction *action, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - - empathy_about_dialog_new (GTK_WINDOW (priv->dialog)); + empathy_about_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)))); } static gboolean -chat_window_delete_event_cb (GtkWidget *dialog, - GdkEvent *event, - EmpathyChatWindow *window) +chat_window_delete_event_cb (GtkWidget *dialog, + GdkEvent *event, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - EmpathyChat *chat = NULL; - guint n_rooms = 0; - GList *l; + EmpathyChat *chat = NULL; + guint n_rooms = 0; + GList *l; - DEBUG ("Delete event received"); + DEBUG ("Delete event received"); - for (l = priv->chats; l != NULL; l = l->next) { - if (chat_needs_close_confirmation (l->data)) { - chat = l->data; - n_rooms++; - } - } + for (l = self->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); - } + if (n_rooms > 0) + { + confirm_close (self, TRUE, n_rooms, (n_rooms == 1 ? chat : NULL)); + } + else + { + remove_all_chats (self); + } - return TRUE; + return TRUE; } static void -chat_window_composing_cb (EmpathyChat *chat, - gboolean is_composing, - EmpathyChatWindow *window) +chat_window_composing_cb (EmpathyChat *chat, + gboolean is_composing, + EmpathyChatWindow *self) { - chat_window_update_chat_tab (chat); + chat_window_update_chat_tab (chat); } static void -chat_window_set_urgency_hint (EmpathyChatWindow *window, - gboolean urgent) +chat_window_set_urgency_hint (EmpathyChatWindow *self, + gboolean urgent) { - EmpathyChatWindowPriv *priv; - - priv = GET_PRIV (window); - - gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent); + //gtk_window_set_urgency_hint (GTK_WINDOW (self), urgent); } static void chat_window_notification_closed_cb (NotifyNotification *notify, - EmpathyChatWindow *self) -{ - EmpathyChatWindowPriv *priv = GET_PRIV (self); - - g_object_unref (notify); - if (priv->notification == notify) { - priv->notification = NULL; - } -} - -static void -chat_window_show_or_update_notification (EmpathyChatWindow *window, - EmpathyMessage *message, - EmpathyChat *chat) -{ - EmpathyContact *sender; - const gchar *header; - char *escaped; - const char *body; - GdkPixbuf *pixbuf; - EmpathyChatWindowPriv *priv = GET_PRIV (window); - gboolean res, has_x_canonical_append; - NotifyNotification *notification = priv->notification; - - if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) { - return; - } else { - res = g_settings_get_boolean (priv->gsettings_notif, - EMPATHY_PREFS_NOTIFICATIONS_FOCUS); - - if (!res) { - return; - } - } - - sender = empathy_message_get_sender (message); - header = empathy_contact_get_alias (sender); - body = empathy_message_get_body (message); - escaped = g_markup_escape_text (body, -1); - has_x_canonical_append = empathy_notify_manager_has_capability ( - priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND); - - if (notification != NULL && !has_x_canonical_append) { - /* if the notification server supports x-canonical-append, it is - better to not use notify_notification_update to avoid - overwriting the current notification message */ - notify_notification_update (notification, - header, escaped, NULL); - } else { - /* if the notification server supports x-canonical-append, - the hint will be added, so that the message from the - just created notification will be automatically appended - to an existing notification with the same title. - In this way the previous message will not be lost: the new - message will appear below it, in the same notification */ - notification = notify_notification_new (header, escaped, NULL); - - if (priv->notification == NULL) { - priv->notification = notification; - } - - notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT); - - tp_g_signal_connect_object (notification, "closed", - G_CALLBACK (chat_window_notification_closed_cb), window, 0); - - if (has_x_canonical_append) { - /* We have to set a not empty string to keep libnotify happy */ - notify_notification_set_hint_string (notification, - EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "1"); - } - - { - const gchar *category = empathy_chat_is_room (chat) - ? EMPATHY_NOTIFICATION_CATEGORY_MENTIONED - : EMPATHY_NOTIFICATION_CATEGORY_CHAT; - notify_notification_set_hint (notification, - EMPATHY_NOTIFY_MANAGER_CAP_CATEGORY, - g_variant_new_string (category)); - } - } - - pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr, - sender, EMPATHY_IMAGE_NEW_MESSAGE); - - if (pixbuf != NULL) { - notify_notification_set_icon_from_pixbuf (notification, pixbuf); - g_object_unref (pixbuf); - } - - notify_notification_show (notification, NULL); - - g_free (escaped); + EmpathyChatWindow *self) +{ + g_object_unref (notify); + if (self->priv->notification == notify) + self->priv->notification = NULL; +} + +static void +chat_window_show_or_update_notification (EmpathyChatWindow *self, + EmpathyMessage *message, + EmpathyChat *chat) +{ + EmpathyContact *sender; + const gchar *header; + char *escaped; + const char *body; + GdkPixbuf *pixbuf; + gboolean res, has_x_canonical_append; + NotifyNotification *notification = self->priv->notification; + + if (!empathy_notify_manager_notification_is_enabled (self->priv->notify_mgr)) + return; + + res = g_settings_get_boolean (self->priv->gsettings_notif, + EMPATHY_PREFS_NOTIFICATIONS_FOCUS); + + if (!res) + return; + + sender = empathy_message_get_sender (message); + header = empathy_contact_get_alias (sender); + body = empathy_message_get_body (message); + escaped = g_markup_escape_text (body, -1); + + has_x_canonical_append = empathy_notify_manager_has_capability ( + self->priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND); + + if (notification != NULL && !has_x_canonical_append) + { + /* if the notification server supports x-canonical-append, it is + better to not use notify_notification_update to avoid + overwriting the current notification message */ + notify_notification_update (notification, + header, escaped, NULL); + } + else + { + /* if the notification server supports x-canonical-append, + the hint will be added, so that the message from the + just created notification will be automatically appended + to an existing notification with the same title. + In this way the previous message will not be lost: the new + message will appear below it, in the same notification */ + const gchar *category = empathy_chat_is_room (chat) + ? EMPATHY_NOTIFICATION_CATEGORY_MENTIONED + : EMPATHY_NOTIFICATION_CATEGORY_CHAT; + + notification = empathy_notify_manager_create_notification (header, + escaped, NULL); + + if (self->priv->notification == NULL) + self->priv->notification = notification; + + tp_g_signal_connect_object (notification, "closed", + G_CALLBACK (chat_window_notification_closed_cb), self, 0); + + if (has_x_canonical_append) + { + /* We have to set a not empty string to keep libnotify happy */ + notify_notification_set_hint_string (notification, + EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "1"); + } + + notify_notification_set_hint (notification, + EMPATHY_NOTIFY_MANAGER_CAP_CATEGORY, g_variant_new_string (category)); + } + + pixbuf = empathy_notify_manager_get_pixbuf_for_notification (self->priv->notify_mgr, + sender, EMPATHY_IMAGE_NEW_MESSAGE); + + if (pixbuf != NULL) + { + notify_notification_set_icon_from_pixbuf (notification, pixbuf); + g_object_unref (pixbuf); + } + + notify_notification_show (notification, NULL); + + g_free (escaped); } static gboolean -empathy_chat_window_has_focus (EmpathyChatWindow *window) +empathy_chat_window_has_focus (EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; - gboolean has_focus; + gboolean has_focus; - g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE); + g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (self), FALSE); - priv = GET_PRIV (window); + g_object_get ( gtk_widget_get_toplevel (GTK_WIDGET (self)), "has-toplevel-focus", &has_focus, NULL); - g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL); + return has_focus; +} - return has_focus; +static void +chat_window_new_message_cb (EmpathyChat *chat, + EmpathyMessage *message, + gboolean pending, + gboolean should_highlight, + EmpathyChatWindow *self) +{ + gboolean has_focus; + gboolean needs_urgency; + EmpathyContact *sender; + + has_focus = empathy_chat_window_has_focus (self); + + /* - if we're the sender, we play the sound if it's specified in the + * preferences and we're not away. + * - if we receive a message, we play the sound if it's specified in the + * preferences and the window does not have focus on the chat receiving + * the message. + */ + + sender = empathy_message_get_sender (message); + + if (empathy_contact_is_user (sender)) + { + empathy_sound_manager_play (self->priv->sound_mgr, GTK_WIDGET (self), + EMPATHY_SOUND_MESSAGE_OUTGOING); + return; + } + + if (has_focus && self->priv->current_chat == chat) + { + /* window and tab are focused so consider the message to be read */ + + /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */ + empathy_chat_messages_read (chat); + return; + } + + /* Update the chat tab if this is the first unread message */ + if (empathy_chat_get_nb_unread_messages (chat) == 1) + { + chat_window_update_chat_tab (chat); + } + + /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC. + * If empathy_chat_get_remote_contact () returns NULL, that means it's + * an unamed MUC (msn-like). + * In case of a MUC, we set urgency if either: + * a) the chatroom's always_urgent property is TRUE + * b) the message contains our alias + */ + if (empathy_chat_is_room (chat)) + { + TpAccount *account; + const gchar *room; + EmpathyChatroom *chatroom; + + account = empathy_chat_get_account (chat); + room = empathy_chat_get_id (chat); + + chatroom = empathy_chatroom_manager_find (self->priv->chatroom_manager, + account, room); + + if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) + needs_urgency = TRUE; + else + needs_urgency = should_highlight; + } + else + { + needs_urgency = TRUE; + } + + if (needs_urgency) + { + if (!has_focus) + chat_window_set_urgency_hint (self, TRUE); + + /* Pending messages have already been displayed and notified in the + * approver, so we don't display a notification and play a sound + * for those */ + if (!pending) + { + empathy_sound_manager_play (self->priv->sound_mgr, + GTK_WIDGET (self), EMPATHY_SOUND_MESSAGE_INCOMING); + + chat_window_show_or_update_notification (self, message, chat); + } + } + + /* update the number of unread messages and the window icon */ + chat_window_title_update (self); + chat_window_icon_update (self, TRUE); } static void -chat_window_new_message_cb (EmpathyChat *chat, - EmpathyMessage *message, - gboolean pending, - gboolean should_highlight, - EmpathyChatWindow *window) +chat_window_command_part (EmpathyChat *chat, + GStrv strv) { - EmpathyChatWindowPriv *priv; - gboolean has_focus; - gboolean needs_urgency; - EmpathyContact *sender; - - priv = GET_PRIV (window); - - has_focus = empathy_chat_window_has_focus (window); - - /* - if we're the sender, we play the sound if it's specified in the - * preferences and we're not away. - * - if we receive a message, we play the sound if it's specified in the - * preferences and the window does not have focus on the chat receiving - * the message. - */ + EmpathyChat *chat_to_be_parted; + EmpathyTpChat *tp_chat = NULL; - sender = empathy_message_get_sender (message); + if (strv[1] == NULL) + { + /* No chatroom ID specified */ + tp_chat = empathy_chat_get_tp_chat (chat); - if (empathy_contact_is_user (sender)) { - empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog), - EMPATHY_SOUND_MESSAGE_OUTGOING); - } + if (tp_chat) + empathy_tp_chat_leave (tp_chat, ""); - if (has_focus && priv->current_chat == chat) { - /* window and tab are focused so consider the message to be read */ + return; + } - /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */ - empathy_chat_messages_read (chat); - return; - } + chat_to_be_parted = empathy_chat_window_find_chat ( + empathy_chat_get_account (chat), strv[1], FALSE); - /* Update the chat tab if this is the first unread message */ - if (empathy_chat_get_nb_unread_messages (chat) == 1) { - chat_window_update_chat_tab (chat); - } + if (chat_to_be_parted != NULL) + { + /* Found a chatroom matching the specified ID */ + tp_chat = empathy_chat_get_tp_chat (chat_to_be_parted); - /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC. - * If empathy_chat_get_remote_contact () returns NULL, that means it's - * an unamed MUC (msn-like). - * In case of a MUC, we set urgency if either: - * a) the chatroom's always_urgent property is TRUE - * b) the message contains our alias - */ - if (empathy_chat_is_room (chat)) { - TpAccount *account; - const gchar *room; - EmpathyChatroom *chatroom; + if (tp_chat) + empathy_tp_chat_leave (tp_chat, strv[2]); + } + else + { + gchar *message; - account = empathy_chat_get_account (chat); - room = empathy_chat_get_id (chat); + /* Going by the syntax of PART command: + * + * /PART [] [] + * + * Chatroom-ID is not a must to specify a reason. + * If strv[1] (chatroom-ID) is not a valid identifier for a connected + * MUC then the current chatroom should be parted and srtv[1] should + * be treated as part of the optional part-message. */ + message = g_strconcat (strv[1], " ", strv[2], NULL); + tp_chat = empathy_chat_get_tp_chat (chat); - chatroom = empathy_chatroom_manager_find (priv->chatroom_manager, - account, room); + if (tp_chat) + empathy_tp_chat_leave (tp_chat, message); - if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) { - needs_urgency = TRUE; - } else { - needs_urgency = should_highlight; - } - } else { - needs_urgency = TRUE; - } - - if (needs_urgency) { - if (!has_focus) { - chat_window_set_urgency_hint (window, TRUE); - } - - /* Pending messages have already been displayed and notified in the - * approver, so we don't display a notification and play a sound for those */ - if (!pending) { - empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog), - EMPATHY_SOUND_MESSAGE_INCOMING); - - chat_window_show_or_update_notification (window, message, chat); - } - } - - /* update the number of unread messages and the window icon */ - chat_window_title_update (priv); - chat_window_icon_update (priv, TRUE); -} - -static void -chat_window_command_part (EmpathyChat *chat, - GStrv strv) -{ - EmpathyChat *chat_to_be_parted; - EmpathyTpChat *tp_chat = NULL; - - if (strv[1] == NULL) { - /* No chatroom ID specified */ - tp_chat = empathy_chat_get_tp_chat (chat); - if (tp_chat) - empathy_tp_chat_leave (tp_chat, ""); - return; - } - chat_to_be_parted = empathy_chat_window_find_chat ( - empathy_chat_get_account (chat), strv[1], FALSE); - - if (chat_to_be_parted != NULL) { - /* Found a chatroom matching the specified ID */ - tp_chat = empathy_chat_get_tp_chat (chat_to_be_parted); - if (tp_chat) - empathy_tp_chat_leave (tp_chat, strv[2]); - } else { - gchar *message; - - /* Going by the syntax of PART command: - * - * /PART [] [] - * - * Chatroom-ID is not a must to specify a reason. - * If strv[1] (chatroom-ID) is not a valid identifier for a connected - * MUC then the current chatroom should be parted and srtv[1] should - * be treated as part of the optional part-message. */ - message = g_strconcat (strv[1], " ", strv[2], NULL); - tp_chat = empathy_chat_get_tp_chat (chat); - if (tp_chat) - empathy_tp_chat_leave (tp_chat, message); - - g_free (message); - } + g_free (message); + } } static GtkNotebook * notebook_create_window_cb (GtkNotebook *source, - GtkWidget *page, - gint x, - gint y, - gpointer user_data) + GtkWidget *page, + gint x, + gint y, + gpointer user_data) { - EmpathyChatWindowPriv *priv; - EmpathyChatWindow *window, *new_window; - EmpathyChat *chat; + EmpathyChatWindow *window, *new_window; + EmpathyChat *chat; - chat = EMPATHY_CHAT (page); - window = chat_window_find_chat (chat); + chat = EMPATHY_CHAT (page); + window = chat_window_find_chat (chat); - new_window = empathy_chat_window_new (); - priv = GET_PRIV (new_window); + new_window = empathy_chat_window_new (); - DEBUG ("Detach hook called"); + DEBUG ("Detach hook called"); - empathy_chat_window_move_chat (window, new_window, chat); + empathy_chat_window_move_chat (window, new_window, chat); - gtk_widget_show (priv->dialog); - gtk_window_move (GTK_WINDOW (priv->dialog), x, y); + gtk_widget_show (GTK_WIDGET (new_window)); + //gtk_window_move (GTK_WINDOW (new_window), x, y); - return NULL; + return NULL; } static void -chat_window_page_switched_cb (GtkNotebook *notebook, - GtkWidget *child, - gint page_num, - EmpathyChatWindow *window) +chat_window_page_switched_cb (GtkNotebook *notebook, + GtkWidget *child, + gint page_num, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - EmpathyChat *chat = EMPATHY_CHAT (child); + EmpathyChat *chat = EMPATHY_CHAT (child); - DEBUG ("Page switched"); + DEBUG ("Page switched"); - if (priv->page_added) { - priv->page_added = FALSE; - empathy_chat_scroll_down (chat); - } - else if (priv->current_chat == chat) { - return; - } + if (self->priv->page_added) + { + self->priv->page_added = FALSE; + empathy_chat_scroll_down (chat); + } + else if (self->priv->current_chat == chat) + { + return; + } - priv->current_chat = chat; - empathy_chat_messages_read (chat); + self->priv->current_chat = chat; + empathy_chat_messages_read (chat); - chat_window_update_chat_tab (chat); + chat_window_update_chat_tab (chat); } static void -chat_window_page_added_cb (GtkNotebook *notebook, - GtkWidget *child, - guint page_num, - EmpathyChatWindow *window) -{ - EmpathyChatWindowPriv *priv; - EmpathyChat *chat; - - priv = GET_PRIV (window); - - /* If we just received DND to the same window, we don't want - * to do anything here like removing the tab and then readding - * it, so we return here and in "page-added". - */ - if (priv->dnd_same_window) { - DEBUG ("Page added (back to the same window)"); - priv->dnd_same_window = FALSE; - return; - } - - DEBUG ("Page added"); - - /* Get chat object */ - chat = EMPATHY_CHAT (child); - - /* Connect chat signals for this window */ - g_signal_connect (chat, "composing", - G_CALLBACK (chat_window_composing_cb), - window); - g_signal_connect (chat, "new-message", - G_CALLBACK (chat_window_new_message_cb), - window); - g_signal_connect (chat, "part-command-entered", - G_CALLBACK (chat_window_command_part), - NULL); - g_signal_connect (chat, "notify::tp-chat", - G_CALLBACK (chat_window_update_chat_tab), - window); - - /* Set flag so we know to perform some special operations on - * switch page due to the new page being added. - */ - priv->page_added = TRUE; - - /* Get list of chats up to date */ - priv->chats = g_list_append (priv->chats, chat); - - chat_window_update_chat_tab (chat); +chat_window_page_added_cb (GtkNotebook *notebook, + GtkWidget *child, + guint page_num, + EmpathyChatWindow *self) +{ + EmpathyChat *chat; + + /* If we just received DND to the same window, we don't want + * to do anything here like removing the tab and then readding + * it, so we return here and in "page-added". + */ + if (self->priv->dnd_same_window) + { + DEBUG ("Page added (back to the same window)"); + self->priv->dnd_same_window = FALSE; + return; + } + + DEBUG ("Page added"); + + /* Get chat object */ + chat = EMPATHY_CHAT (child); + + /* Connect chat signals for this window */ + g_signal_connect (chat, "composing", + G_CALLBACK (chat_window_composing_cb), self); + g_signal_connect (chat, "new-message", + G_CALLBACK (chat_window_new_message_cb), self); + g_signal_connect (chat, "part-command-entered", + G_CALLBACK (chat_window_command_part), NULL); + g_signal_connect (chat, "notify::tp-chat", + G_CALLBACK (chat_window_update_chat_tab), self); + + /* Set flag so we know to perform some special operations on + * switch page due to the new page being added. + */ + self->priv->page_added = TRUE; + + /* Get list of chats up to date */ + self->priv->chats = g_list_append (self->priv->chats, chat); + + chat_window_update_chat_tab (chat); } static void -chat_window_page_removed_cb (GtkNotebook *notebook, - GtkWidget *child, - guint page_num, - EmpathyChatWindow *window) -{ - EmpathyChatWindowPriv *priv; - EmpathyChat *chat; - - priv = GET_PRIV (window); - - /* If we just received DND to the same window, we don't want - * to do anything here like removing the tab and then readding - * it, so we return here and in "page-added". - */ - if (priv->dnd_same_window) { - DEBUG ("Page removed (and will be readded to same window)"); - return; - } - - DEBUG ("Page removed"); - - /* Get chat object */ - chat = EMPATHY_CHAT (child); - - /* Disconnect all signal handlers for this chat and this window */ - g_signal_handlers_disconnect_by_func (chat, - G_CALLBACK (chat_window_composing_cb), - window); - g_signal_handlers_disconnect_by_func (chat, - G_CALLBACK (chat_window_new_message_cb), - window); - g_signal_handlers_disconnect_by_func (chat, - G_CALLBACK (chat_window_update_chat_tab), - window); - - /* Keep list of chats up to date */ - priv->chats = g_list_remove (priv->chats, chat); - empathy_chat_messages_read (chat); - - if (priv->chats == NULL) { - g_object_unref (window); - } else { - chat_window_update (window, TRUE); - } +chat_window_page_removed_cb (GtkNotebook *notebook, + GtkWidget *child, + guint page_num, + EmpathyChatWindow *self) +{ + EmpathyChat *chat; + + /* If we just received DND to the same window, we don't want + * to do anything here like removing the tab and then readding + * it, so we return here and in "page-added". + */ + if (self->priv->dnd_same_window) + { + DEBUG ("Page removed (and will be readded to same window)"); + return; + } + + DEBUG ("Page removed"); + + /* Get chat object */ + chat = EMPATHY_CHAT (child); + + /* Disconnect all signal handlers for this chat and this window */ + g_signal_handlers_disconnect_by_func (chat, + G_CALLBACK (chat_window_composing_cb), self); + g_signal_handlers_disconnect_by_func (chat, + G_CALLBACK (chat_window_new_message_cb), self); + g_signal_handlers_disconnect_by_func (chat, + G_CALLBACK (chat_window_update_chat_tab), self); + + /* Keep list of chats up to date */ + self->priv->chats = g_list_remove (self->priv->chats, chat); + empathy_chat_messages_read (chat); + + if (self->priv->chats == NULL) + { + gtk_widget_destroy (GTK_WIDGET (self)); + } + else + { + chat_window_update (self, TRUE); + } } static gboolean -chat_window_focus_in_event_cb (GtkWidget *widget, - GdkEvent *event, - EmpathyChatWindow *window) +chat_window_focus_in_event_cb (GtkWidget *widget, + GdkEvent *event, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv; + if (self->priv->current_chat == NULL) { + return FALSE; + } + empathy_chat_messages_read (self->priv->current_chat); - priv = GET_PRIV (window); + chat_window_set_urgency_hint (self, FALSE); - empathy_chat_messages_read (priv->current_chat); + /* Update the title, since we now mark all unread messages as read. */ + chat_window_update_chat_tab_full (self->priv->current_chat, FALSE); - chat_window_set_urgency_hint (window, FALSE); + return FALSE; +} - /* Update the title, since we now mark all unread messages as read. */ - chat_window_update_chat_tab_full (priv->current_chat, FALSE); +static void +contacts_loaded_cb (EmpathyIndividualManager *mgr, + EmpathyChatWindow *self) +{ + chat_window_contact_menu_update (self); +} - return FALSE; +static gboolean +chat_window_focus_out_event_cb (GtkWidget *widget, + GdkEvent *event, + EmpathyChatWindow *self) +{ + if (self->priv->individual_mgr != NULL) + return FALSE; + + /* Keep the individual manager alive so we won't fetch everything from Folks + * each time we need to use it. Loading FolksAggregator can takes quite a + * while (if user has a huge LDAP abook for example) and it blocks + * the mainloop during most of this loading. We workaround this by loading + * it when the chat window has been unfocused and so, hopefully, not impact + * the reactivity of the chat window too much. + * + * The individual manager (and so Folks) is needed to know to which + * FolksIndividual a TpContact belongs, including: + * - empathy_chat_get_contact_menu: to list all the personas of the contact + * - empathy_display_individual_info: to invoke gnome-contacts with the + * FolksIndividual.id of the contact + * - drag_data_received_individual_id: to find the individual associated + * with the ID we received from the DnD in order to invite him. + */ + self->priv->individual_mgr = empathy_individual_manager_dup_singleton (); + + if (!empathy_individual_manager_get_contacts_loaded ( + self->priv->individual_mgr)) + { + /* We want to update the contact menu when Folks is loaded so we can + * list all the personas of the contact. */ + tp_g_signal_connect_object (self->priv->individual_mgr, "contacts-loaded", + G_CALLBACK (contacts_loaded_cb), self, 0); + } + + g_object_notify (G_OBJECT (self), "individual-manager"); + + return FALSE; } static gboolean -chat_window_drag_drop (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - guint time_, - EmpathyChatWindow *window) +chat_window_drag_drop (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + guint time_, + EmpathyChatWindow *self) { - GdkAtom target; - EmpathyChatWindowPriv *priv; + GdkAtom target; - priv = GET_PRIV (window); + target = gtk_drag_dest_find_target (widget, context, self->priv->file_targets); + if (target == GDK_NONE) + target = gtk_drag_dest_find_target (widget, context, self->priv->contact_targets); - target = gtk_drag_dest_find_target (widget, context, priv->file_targets); - if (target == GDK_NONE) - target = gtk_drag_dest_find_target (widget, context, priv->contact_targets); + if (target != GDK_NONE) + { + gtk_drag_get_data (widget, context, target, time_); + return TRUE; + } - if (target != GDK_NONE) { - gtk_drag_get_data (widget, context, target, time_); - return TRUE; - } - - return FALSE; + return FALSE; } static gboolean -chat_window_drag_motion (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - guint time_, - EmpathyChatWindow *window) -{ - GdkAtom target; - EmpathyChatWindowPriv *priv; - - priv = GET_PRIV (window); - - target = gtk_drag_dest_find_target (widget, context, priv->file_targets); - if (target != GDK_NONE) { - /* This is a file drag. Ensure the contact is online and set the - drag type to COPY. Note that it's possible that the tab will - be switched by GTK+ after a timeout from drag_motion without - getting another drag_motion to disable the drop. You have - to hold your mouse really still. - */ - EmpathyContact *contact; - - priv = GET_PRIV (window); - contact = empathy_chat_get_remote_contact (priv->current_chat); - /* contact is NULL for multi-user chats. We don't do - * file transfers to MUCs. We also don't send files - * to offline contacts or contacts that don't support - * file transfer. - */ - if ((contact == NULL) || !empathy_contact_is_online (contact)) { - gdk_drag_status (context, 0, time_); - return FALSE; - } - if (!(empathy_contact_get_capabilities (contact) - & EMPATHY_CAPABILITIES_FT)) { - gdk_drag_status (context, 0, time_); - return FALSE; - } - gdk_drag_status (context, GDK_ACTION_COPY, time_); - return TRUE; - } - - target = gtk_drag_dest_find_target (widget, context, priv->contact_targets); - if (target != GDK_NONE) { - /* This is a drag of a contact from a contact list. Set to COPY. - FIXME: If this drag is to a MUC window, it invites the user. - Otherwise, it opens a chat. Should we use a different drag - type for invites? Should we allow ASK? - */ - gdk_drag_status (context, GDK_ACTION_COPY, time_); - return TRUE; - } - - return FALSE; +chat_window_drag_motion (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + guint time_, + EmpathyChatWindow *self) +{ + GdkAtom target; + + target = gtk_drag_dest_find_target (widget, context, self->priv->file_targets); + + if (target != GDK_NONE) + { + /* This is a file drag. Ensure the contact is online and set the + drag type to COPY. Note that it's possible that the tab will + be switched by GTK+ after a timeout from drag_motion without + getting another drag_motion to disable the drop. You have + to hold your mouse really still. + */ + EmpathyContact *contact; + + contact = empathy_chat_get_remote_contact (self->priv->current_chat); + + /* contact is NULL for multi-user chats. We don't do + * file transfers to MUCs. We also don't send files + * to offline contacts or contacts that don't support + * file transfer. + */ + if ((contact == NULL) || !empathy_contact_is_online (contact)) + { + gdk_drag_status (context, 0, time_); + return FALSE; + } + + if (!(empathy_contact_get_capabilities (contact) + & EMPATHY_CAPABILITIES_FT)) + { + gdk_drag_status (context, 0, time_); + return FALSE; + } + + gdk_drag_status (context, GDK_ACTION_COPY, time_); + return TRUE; + } + + target = gtk_drag_dest_find_target (widget, context, self->priv->contact_targets); + if (target != GDK_NONE) + { + /* This is a drag of a contact from a contact list. Set to COPY. + FIXME: If this drag is to a MUC window, it invites the user. + Otherwise, it opens a chat. Should we use a different drag + type for invites? Should we allow ASK? + */ + gdk_drag_status (context, GDK_ACTION_COPY, time_); + return TRUE; + } + + return FALSE; } static void drag_data_received_individual_id (EmpathyChatWindow *self, - GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - GtkSelectionData *selection, - guint info, - guint time_) -{ - const gchar *id; - EmpathyIndividualManager *manager = NULL; - FolksIndividual *individual; - EmpathyChatWindowPriv *priv = GET_PRIV (self); - EmpathyTpChat *chat; - TpContact *tp_contact; - TpConnection *conn; - EmpathyContact *contact; - - id = (const gchar *) gtk_selection_data_get_data (selection); - - DEBUG ("DND invididual %s", id); - - if (priv->current_chat == NULL) - goto out; - - chat = empathy_chat_get_tp_chat (priv->current_chat); - if (chat == NULL) - goto out; - - if (!empathy_tp_chat_can_add_contact (chat)) { - DEBUG ("Can't invite contact to %s", - tp_proxy_get_object_path (chat)); - goto out; - } - - manager = empathy_individual_manager_dup_singleton (); - - individual = empathy_individual_manager_lookup_member (manager, id); - if (individual == NULL) { - DEBUG ("Failed to find individual %s", id); - goto out; - } - - conn = tp_channel_borrow_connection ((TpChannel *) chat); - tp_contact = empathy_get_tp_contact_for_individual (individual, conn); - if (tp_contact == NULL) { - DEBUG ("Can't find a TpContact on connection %s for %s", - tp_proxy_get_object_path (conn), id); - goto out; - } - - DEBUG ("Inviting %s to join %s", tp_contact_get_identifier (tp_contact), - tp_channel_get_identifier ((TpChannel *) chat)); - - contact = empathy_contact_dup_from_tp_contact (tp_contact); - empathy_tp_chat_add (chat, contact, NULL); - g_object_unref (contact); + GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + GtkSelectionData *selection, + guint info, + guint time_) +{ + const gchar *id; + FolksIndividual *individual; + EmpathyTpChat *chat; + TpContact *tp_contact; + TpConnection *conn; + EmpathyContact *contact; + + id = (const gchar *) gtk_selection_data_get_data (selection); + + DEBUG ("DND invididual %s", id); + + if (self->priv->current_chat == NULL) + goto out; + + chat = empathy_chat_get_tp_chat (self->priv->current_chat); + if (chat == NULL) + goto out; + + if (!empathy_tp_chat_can_add_contact (chat)) + { + DEBUG ("Can't invite contact to %s", + tp_proxy_get_object_path (chat)); + goto out; + } + + if (self->priv->individual_mgr == NULL) + /* Not likely as we have to focus out the chat window in order to start + * the DnD but best to be safe. */ + goto out; + + individual = empathy_individual_manager_lookup_member ( + self->priv->individual_mgr, id); + if (individual == NULL) + { + DEBUG ("Failed to find individual %s", id); + goto out; + } + + conn = tp_channel_get_connection ((TpChannel *) chat); + tp_contact = empathy_get_tp_contact_for_individual (individual, conn); + if (tp_contact == NULL) + { + DEBUG ("Can't find a TpContact on connection %s for %s", + tp_proxy_get_object_path (conn), id); + goto out; + } + + DEBUG ("Inviting %s to join %s", tp_contact_get_identifier (tp_contact), + tp_channel_get_identifier ((TpChannel *) chat)); + + contact = empathy_contact_dup_from_tp_contact (tp_contact); + empathy_tp_chat_add (chat, contact, NULL); + g_object_unref (contact); out: - gtk_drag_finish (context, TRUE, FALSE, time_); - tp_clear_object (&manager); -} - -static void -chat_window_drag_data_received (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - GtkSelectionData *selection, - guint info, - guint time_, - EmpathyChatWindow *window) -{ - if (info == DND_DRAG_TYPE_CONTACT_ID) { - EmpathyChat *chat = NULL; - EmpathyChatWindow *old_window; - TpAccount *account = NULL; - EmpathyClientFactory *factory; - const gchar *id; - gchar **strv; - const gchar *account_id; - const gchar *contact_id; - - id = (const gchar*) gtk_selection_data_get_data (selection); - - factory = empathy_client_factory_dup (); - - DEBUG ("DND contact from roster with id:'%s'", id); - - strv = g_strsplit (id, ":", 2); - if (g_strv_length (strv) == 2) { - account_id = strv[0]; - contact_id = strv[1]; - account = - tp_simple_client_factory_ensure_account ( - TP_SIMPLE_CLIENT_FACTORY (factory), account_id, - NULL, NULL); - - g_object_unref (factory); - if (account != NULL) - chat = empathy_chat_window_find_chat (account, contact_id, FALSE); - } - - if (account == NULL) { - g_strfreev (strv); - gtk_drag_finish (context, FALSE, FALSE, time_); - return; - } - - if (!chat) { - empathy_chat_with_contact_id ( - account, contact_id, - empathy_get_current_action_time (), - NULL, NULL); - - g_strfreev (strv); - return; - } - g_strfreev (strv); - - old_window = chat_window_find_chat (chat); - if (old_window) { - if (old_window == window) { - gtk_drag_finish (context, TRUE, FALSE, time_); - return; - } - - empathy_chat_window_move_chat (old_window, window, chat); - } else { - empathy_chat_window_add_chat (window, chat); - } - - /* Added to take care of any outstanding chat events */ - empathy_chat_window_present_chat (chat, - TP_USER_ACTION_TIME_NOT_USER_ACTION); - - /* We should return TRUE to remove the data when doing - * GDK_ACTION_MOVE, but we don't here otherwise it has - * weird consequences, and we handle that internally - * anyway with add_chat () and remove_chat (). - */ - gtk_drag_finish (context, TRUE, FALSE, time_); - } - else if (info == DND_DRAG_TYPE_INDIVIDUAL_ID) { - drag_data_received_individual_id (window, widget, context, x, y, - selection, info, time_); - } - else if (info == DND_DRAG_TYPE_URI_LIST) { - EmpathyChatWindowPriv *priv; - EmpathyContact *contact; - const gchar *data; - - priv = GET_PRIV (window); - contact = empathy_chat_get_remote_contact (priv->current_chat); - - /* contact is NULL when current_chat is a multi-user chat. - * We don't do file transfers to MUCs, so just cancel the drag. - */ - if (contact == NULL) { - gtk_drag_finish (context, TRUE, FALSE, time_); - return; - } - - data = (const gchar *) gtk_selection_data_get_data (selection); - empathy_send_file_from_uri_list (contact, data); - - gtk_drag_finish (context, TRUE, FALSE, time_); - } - else if (info == DND_DRAG_TYPE_TAB) { - EmpathyChat **chat; - EmpathyChatWindow *old_window = NULL; - - DEBUG ("DND tab"); - - chat = (void *) gtk_selection_data_get_data (selection); - old_window = chat_window_find_chat (*chat); - - if (old_window) { - EmpathyChatWindowPriv *priv; - - priv = GET_PRIV (window); - priv->dnd_same_window = (old_window == window); - DEBUG ("DND tab (within same window: %s)", - priv->dnd_same_window ? "Yes" : "No"); - } - } else { - DEBUG ("DND from unknown source"); - gtk_drag_finish (context, FALSE, FALSE, time_); - } + gtk_drag_finish (context, TRUE, FALSE, time_); +} + +static void +chat_window_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + GtkSelectionData *selection, + guint info, + guint time_, + EmpathyChatWindow *self) +{ + if (info == DND_DRAG_TYPE_CONTACT_ID) + { + EmpathyChat *chat = NULL; + EmpathyChatWindow *old_window; + TpAccount *account = NULL; + EmpathyClientFactory *factory; + const gchar *id; + gchar **strv; + const gchar *account_id; + const gchar *contact_id; + + id = (const gchar*) gtk_selection_data_get_data (selection); + + factory = empathy_client_factory_dup (); + + DEBUG ("DND contact from roster with id:'%s'", id); + + strv = g_strsplit (id, ":", 2); + if (g_strv_length (strv) == 2) + { + account_id = strv[0]; + contact_id = strv[1]; + + account = tp_simple_client_factory_ensure_account ( + TP_SIMPLE_CLIENT_FACTORY (factory), account_id, NULL, NULL); + + g_object_unref (factory); + if (account != NULL) + chat = empathy_chat_window_find_chat (account, contact_id, FALSE); + } + + if (account == NULL) + { + g_strfreev (strv); + gtk_drag_finish (context, FALSE, FALSE, time_); + return; + } + + if (!chat) + { + empathy_chat_with_contact_id (account, contact_id, + empathy_get_current_action_time (), NULL, NULL); + + g_strfreev (strv); + return; + } + + g_strfreev (strv); + + old_window = chat_window_find_chat (chat); + if (old_window) + { + if (old_window == self) + { + gtk_drag_finish (context, TRUE, FALSE, time_); + return; + } + + empathy_chat_window_move_chat (old_window, self, chat); + } + else + { + empathy_chat_window_add_chat (self, chat); + } + + /* Added to take care of any outstanding chat events */ + empathy_chat_window_present_chat (chat, + TP_USER_ACTION_TIME_NOT_USER_ACTION); + + /* We should return TRUE to remove the data when doing + * GDK_ACTION_MOVE, but we don't here otherwise it has + * weird consequences, and we handle that internally + * anyway with add_chat () and remove_chat (). + */ + gtk_drag_finish (context, TRUE, FALSE, time_); + } + else if (info == DND_DRAG_TYPE_INDIVIDUAL_ID) + { + drag_data_received_individual_id (self, widget, context, x, y, + selection, info, time_); + } + else if (info == DND_DRAG_TYPE_URI_LIST) + { + EmpathyContact *contact; + const gchar *data; + + contact = empathy_chat_get_remote_contact (self->priv->current_chat); + + /* contact is NULL when current_chat is a multi-user chat. + * We don't do file transfers to MUCs, so just cancel the drag. + */ + if (contact == NULL) + { + gtk_drag_finish (context, TRUE, FALSE, time_); + return; + } + + data = (const gchar *) gtk_selection_data_get_data (selection); + empathy_send_file_from_uri_list (contact, data); + + gtk_drag_finish (context, TRUE, FALSE, time_); + } + else if (info == DND_DRAG_TYPE_TAB) + { + EmpathyChat **chat; + EmpathyChatWindow *old_window = NULL; + + DEBUG ("DND tab"); + + chat = (void *) gtk_selection_data_get_data (selection); + old_window = chat_window_find_chat (*chat); + + if (old_window) + { + self->priv->dnd_same_window = (old_window == self); + + DEBUG ("DND tab (within same window: %s)", + self->priv->dnd_same_window ? "Yes" : "No"); + } + } + else + { + DEBUG ("DND from unknown source"); + gtk_drag_finish (context, FALSE, FALSE, time_); + } } static void chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager, - guint num_chats_in_manager, - EmpathyChatWindow *window) + guint num_chats_in_manager, + EmpathyChatWindow *self) { - EmpathyChatWindowPriv *priv = GET_PRIV (window); - - gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab, - num_chats_in_manager > 0); + gtk_action_set_sensitive (self->priv->menu_tabs_undo_close_tab, + num_chats_in_manager > 0); } static void chat_window_finalize (GObject *object) { - EmpathyChatWindow *window; - EmpathyChatWindowPriv *priv; + EmpathyChatWindow *self = EMPATHY_CHAT_WINDOW (object); - window = EMPATHY_CHAT_WINDOW (object); - priv = GET_PRIV (window); + DEBUG ("Finalized: %p", object); - DEBUG ("Finalized: %p", object); + g_object_unref (self->priv->ui_manager); + g_object_unref (self->priv->chatroom_manager); + g_object_unref (self->priv->notify_mgr); + g_object_unref (self->priv->gsettings_chat); + g_object_unref (self->priv->gsettings_notif); + g_object_unref (self->priv->gsettings_ui); + g_object_unref (self->priv->sound_mgr); + g_clear_object (&self->priv->individual_mgr); - g_object_unref (priv->ui_manager); - g_object_unref (priv->chatroom_manager); - g_object_unref (priv->notify_mgr); - g_object_unref (priv->gsettings_chat); - g_object_unref (priv->gsettings_notif); - g_object_unref (priv->gsettings_ui); - g_object_unref (priv->sound_mgr); + if (self->priv->notification != NULL) + { + notify_notification_close (self->priv->notification, NULL); + self->priv->notification = NULL; + } - if (priv->notification != NULL) { - notify_notification_close (priv->notification, NULL); - priv->notification = NULL; - } + if (self->priv->contact_targets) + gtk_target_list_unref (self->priv->contact_targets); - if (priv->contact_targets) { - gtk_target_list_unref (priv->contact_targets); - } - if (priv->file_targets) { - gtk_target_list_unref (priv->file_targets); - } + if (self->priv->file_targets) + gtk_target_list_unref (self->priv->file_targets); - if (priv->chat_manager) { - g_signal_handler_disconnect (priv->chat_manager, - priv->chat_manager_chats_changed_id); - g_object_unref (priv->chat_manager); - priv->chat_manager = NULL; - } + if (self->priv->chat_manager) + { + g_signal_handler_disconnect (self->priv->chat_manager, + self->priv->chat_manager_chats_changed_id); + g_object_unref (self->priv->chat_manager); + self->priv->chat_manager = NULL; + } - chat_windows = g_list_remove (chat_windows, window); - gtk_widget_destroy (priv->dialog); + chat_windows = g_list_remove (chat_windows, self); - G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object); + G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object); } static void -empathy_chat_window_class_init (EmpathyChatWindowClass *klass) +chat_window_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = chat_window_finalize; - - g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv)); -} - -static void -empathy_chat_window_init (EmpathyChatWindow *window) -{ - GtkBuilder *gui; - GtkAccelGroup *accel_group; - GClosure *closure; - GtkWidget *menu; - GtkWidget *submenu; - guint i; - GtkWidget *chat_vbox; - gchar *filename; - EmpathySmileyManager *smiley_manager; - EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window, - EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv); - - window->priv = priv; - filename = empathy_file_lookup ("empathy-chat-window.ui", "src"); - gui = empathy_builder_get_file (filename, - "chat_window", &priv->dialog, - "chat_vbox", &chat_vbox, - "ui_manager", &priv->ui_manager, - "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley, - "menu_conv_favorite", &priv->menu_conv_favorite, - "menu_conv_always_urgent", &priv->menu_conv_always_urgent, - "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts, - "menu_edit_cut", &priv->menu_edit_cut, - "menu_edit_copy", &priv->menu_edit_copy, - "menu_edit_paste", &priv->menu_edit_paste, - "menu_edit_find", &priv->menu_edit_find, - "menu_tabs_next", &priv->menu_tabs_next, - "menu_tabs_prev", &priv->menu_tabs_prev, - "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab, - "menu_tabs_left", &priv->menu_tabs_left, - "menu_tabs_right", &priv->menu_tabs_right, - "menu_tabs_detach", &priv->menu_tabs_detach, - NULL); - g_free (filename); - - empathy_builder_connect (gui, window, - "menu_conv", "activate", chat_window_conv_activate_cb, - "menu_conv_clear", "activate", chat_window_clear_activate_cb, - "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb, - "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb, - "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb, - "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb, - "menu_conv_close", "activate", chat_window_close_activate_cb, - "menu_edit", "activate", chat_window_edit_activate_cb, - "menu_edit_cut", "activate", chat_window_cut_activate_cb, - "menu_edit_copy", "activate", chat_window_copy_activate_cb, - "menu_edit_paste", "activate", chat_window_paste_activate_cb, - "menu_edit_find", "activate", chat_window_find_activate_cb, - "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb, - "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb, - "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb, - "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb, - "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb, - "menu_tabs_detach", "activate", chat_window_detach_activate_cb, - "menu_help_contents", "activate", chat_window_help_contents_activate_cb, - "menu_help_about", "activate", chat_window_help_about_activate_cb, - NULL); - - g_object_ref (priv->ui_manager); - g_object_unref (gui); - - priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA); - priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA); - priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA); - priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL); - - priv->sound_mgr = empathy_sound_manager_dup_singleton (); - - priv->notebook = gtk_notebook_new (); - - g_signal_connect (priv->notebook, "create-window", - G_CALLBACK (notebook_create_window_cb), window); - - gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook), - "EmpathyChatWindow"); - gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE); - gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook)); - gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0); - gtk_widget_show (priv->notebook); - - /* Set up accels */ - accel_group = gtk_accel_group_new (); - gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group); - - for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) { - closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb), - window, - NULL); - gtk_accel_group_connect (accel_group, - tab_accel_keys[i], - GDK_MOD1_MASK, - 0, - closure); - } - - g_object_unref (accel_group); - - /* Set up drag target lists */ - priv->contact_targets = gtk_target_list_new (drag_types_dest_contact, - G_N_ELEMENTS (drag_types_dest_contact)); - priv->file_targets = gtk_target_list_new (drag_types_dest_file, - G_N_ELEMENTS (drag_types_dest_file)); - - /* Set up smiley menu */ - smiley_manager = empathy_smiley_manager_dup_singleton (); - submenu = empathy_smiley_menu_new (smiley_manager, - chat_window_insert_smiley_activate_cb, - window); - menu = gtk_ui_manager_get_widget (priv->ui_manager, - "/chats_menubar/menu_conv/menu_conv_insert_smiley"); - gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu); - g_object_unref (smiley_manager); - - /* Set up signals we can't do with ui file since we may need to - * block/unblock them at some later stage. - */ - - g_signal_connect (priv->dialog, - "delete_event", - G_CALLBACK (chat_window_delete_event_cb), - window); - g_signal_connect (priv->dialog, - "focus_in_event", - G_CALLBACK (chat_window_focus_in_event_cb), - window); - g_signal_connect_after (priv->notebook, - "switch_page", - G_CALLBACK (chat_window_page_switched_cb), - window); - g_signal_connect (priv->notebook, - "page_added", - G_CALLBACK (chat_window_page_added_cb), - window); - g_signal_connect (priv->notebook, - "page_removed", - G_CALLBACK (chat_window_page_removed_cb), - window); - - /* Set up drag and drop */ - gtk_drag_dest_set (GTK_WIDGET (priv->notebook), - GTK_DEST_DEFAULT_HIGHLIGHT, - drag_types_dest, - G_N_ELEMENTS (drag_types_dest), - GDK_ACTION_MOVE | GDK_ACTION_COPY); - - /* connect_after to allow GtkNotebook's built-in tab switching */ - g_signal_connect_after (priv->notebook, - "drag-motion", - G_CALLBACK (chat_window_drag_motion), - window); - g_signal_connect (priv->notebook, - "drag-data-received", - G_CALLBACK (chat_window_drag_data_received), - window); - g_signal_connect (priv->notebook, - "drag-drop", - G_CALLBACK (chat_window_drag_drop), - window); - - chat_windows = g_list_prepend (chat_windows, window); - - /* Set up private details */ - priv->chats = NULL; - priv->current_chat = NULL; - priv->notification = NULL; - - priv->notify_mgr = empathy_notify_manager_dup_singleton (); - - priv->chat_manager = empathy_chat_manager_dup_singleton (); - priv->chat_manager_chats_changed_id = - g_signal_connect (priv->chat_manager, "closed-chats-changed", - G_CALLBACK (chat_window_chat_manager_chats_changed_cb), - window); - - chat_window_chat_manager_chats_changed_cb (priv->chat_manager, - empathy_chat_manager_get_num_closed_chats (priv->chat_manager), - window); + EmpathyChatWindow *self = EMPATHY_CHAT_WINDOW (object); + + switch (property_id) + { + case PROP_INDIVIDUAL_MGR: + g_value_set_object (value, self->priv->individual_mgr); + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } } -/* Returns the window to open a new tab in if there is a suitable window, - * otherwise, returns NULL indicating that a new window should be added. - */ -static EmpathyChatWindow * -empathy_chat_window_get_default (gboolean room) +static void +empathy_chat_window_class_init (EmpathyChatWindowClass *klass) { - GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA); - GList *l; - gboolean separate_windows = TRUE; - - separate_windows = g_settings_get_boolean (gsettings, - EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *spec; - g_object_unref (gsettings); + object_class->get_property = chat_window_get_property; + object_class->finalize = chat_window_finalize; - if (separate_windows) { - /* Always create a new window */ - return NULL; - } + spec = g_param_spec_object ("individual-manager", "individual-manager", + "EmpathyIndividualManager", + EMPATHY_TYPE_INDIVIDUAL_MANAGER, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_INDIVIDUAL_MGR, spec); - for (l = chat_windows; l; l = l->next) { - EmpathyChatWindow *chat_window; - guint nb_rooms, nb_private; - - chat_window = l->data; - - empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private); + g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv)); +} - /* Skip the window if there aren't any rooms in it */ - if (room && nb_rooms == 0) - continue; +static void +chat_window_chat_new_message_cb (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + EmpathyChatWindow *self = user_data; - /* Skip the window if there aren't any 1-1 chats in it */ - if (!room && nb_private == 0) - continue; + //empathy_new_message_dialog_show (GTK_WINDOW (self)); +} - return chat_window; - } - return NULL; +static void +empathy_chat_window_init (EmpathyChatWindow *self) +{ + GtkBuilder *gui; + GtkAccelGroup *accel_group; + GClosure *closure; + GtkWidget *menu; + GtkWidget *submenu; + guint i; + GtkWidget *chat_vbox; + GtkWidget *main_box; + gchar *filename; + EmpathySmileyManager *smiley_manager; + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv); + + filename = empathy_file_lookup ("empathy-chat-window.ui", "src"); + gui = tpaw_builder_get_file (filename, + "chat_vbox", &chat_vbox, + "main_box", &main_box, + "ui_manager", &self->priv->ui_manager, + "menu_conv_insert_smiley", &self->priv->menu_conv_insert_smiley, + "menu_conv_favorite", &self->priv->menu_conv_favorite, + "menu_conv_join_chat", &self->priv->menu_conv_join_chat, + "menu_conv_leave_chat", &self->priv->menu_conv_leave_chat, + "menu_conv_always_urgent", &self->priv->menu_conv_always_urgent, + "menu_conv_toggle_contacts", &self->priv->menu_conv_toggle_contacts, + "menu_edit_cut", &self->priv->menu_edit_cut, + "menu_edit_copy", &self->priv->menu_edit_copy, + "menu_edit_paste", &self->priv->menu_edit_paste, + "menu_edit_find", &self->priv->menu_edit_find, + "menu_tabs_next", &self->priv->menu_tabs_next, + "menu_tabs_prev", &self->priv->menu_tabs_prev, + "menu_tabs_undo_close_tab", &self->priv->menu_tabs_undo_close_tab, + "menu_tabs_left", &self->priv->menu_tabs_left, + "menu_tabs_right", &self->priv->menu_tabs_right, + "menu_tabs_detach", &self->priv->menu_tabs_detach, + NULL); + g_free (filename); + + tpaw_builder_connect (gui, self, + "menu_conv", "activate", chat_window_conv_activate_cb, + "menu_conv_clear", "activate", chat_window_clear_activate_cb, + "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb, + "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb, + "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb, + "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb, + "menu_conv_join_chat", "activate", chat_window_join_chat_activate_cb, + "menu_conv_leave_chat", "activate", chat_window_leave_chat_activate_cb, + "menu_conv_close", "activate", chat_window_close_activate_cb, + "menu_edit", "activate", chat_window_edit_activate_cb, + "menu_edit_cut", "activate", chat_window_cut_activate_cb, + "menu_edit_copy", "activate", chat_window_copy_activate_cb, + "menu_edit_paste", "activate", chat_window_paste_activate_cb, + "menu_edit_find", "activate", chat_window_find_activate_cb, + "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb, + "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb, + "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb, + "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb, + "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb, + "menu_tabs_detach", "activate", chat_window_detach_activate_cb, + "menu_help_contents", "activate", chat_window_help_contents_activate_cb, + "menu_help_about", "activate", chat_window_help_about_activate_cb, + NULL); + + empathy_set_css_provider (GTK_WIDGET (self)); + + self->priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA); + self->priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA); + self->priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA); + self->priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL); + + self->priv->sound_mgr = empathy_sound_manager_dup_singleton (); + + self->priv->notebook = gtk_notebook_new (); + //gtk_notebook_set_show_tabs (GTK_NOTEBOOK (self->priv->notebook), FALSE); + + g_signal_connect (self->priv->notebook, "create-window", + G_CALLBACK (notebook_create_window_cb), self); + + gtk_container_add (GTK_CONTAINER (self), main_box); + + gtk_notebook_set_group_name (GTK_NOTEBOOK (self->priv->notebook), + "EmpathyChatWindow"); + gtk_notebook_set_scrollable (GTK_NOTEBOOK (self->priv->notebook), TRUE); + gtk_notebook_popup_enable (GTK_NOTEBOOK (self->priv->notebook)); + gtk_box_pack_start (GTK_BOX (chat_vbox), self->priv->notebook, TRUE, TRUE, 0); + gtk_widget_show (self->priv->notebook); + +#if 0 /* no top level window yet at this point */ + /* Set up accels */ + accel_group = gtk_accel_group_new (); + gtk_window_add_accel_group (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))), accel_group); + + for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) + { + closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb), self, + NULL); + + gtk_accel_group_connect (accel_group, tab_accel_keys[i], GDK_MOD1_MASK, 0, + closure); + } + + g_object_unref (accel_group); +#endif + + /* Set up drag target lists */ + self->priv->contact_targets = gtk_target_list_new (drag_types_dest_contact, + G_N_ELEMENTS (drag_types_dest_contact)); + + self->priv->file_targets = gtk_target_list_new (drag_types_dest_file, + G_N_ELEMENTS (drag_types_dest_file)); + + /* Set up smiley menu */ + smiley_manager = empathy_smiley_manager_dup_singleton (); + submenu = empathy_smiley_menu_new (smiley_manager, + chat_window_insert_smiley_activate_cb, self); + + menu = gtk_ui_manager_get_widget (self->priv->ui_manager, + "/chats_menubar/menu_conv/menu_conv_insert_smiley"); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu); + g_object_unref (smiley_manager); + + /* Set up signals we can't do with ui file since we may need to + * block/unblock them at some later stage. + */ + + g_signal_connect (self, "delete_event", + G_CALLBACK (chat_window_delete_event_cb), self); + g_signal_connect (self, "focus_in_event", + G_CALLBACK (chat_window_focus_in_event_cb), self); + g_signal_connect (self, "focus_out_event", + G_CALLBACK (chat_window_focus_out_event_cb), self); + g_signal_connect_after (self->priv->notebook, "switch_page", + G_CALLBACK (chat_window_page_switched_cb), self); + g_signal_connect (self->priv->notebook, "page_added", + G_CALLBACK (chat_window_page_added_cb), self); + g_signal_connect (self->priv->notebook, "page_removed", + G_CALLBACK (chat_window_page_removed_cb), self); + + /* Set up drag and drop */ + gtk_drag_dest_set (GTK_WIDGET (self->priv->notebook), + GTK_DEST_DEFAULT_HIGHLIGHT, + drag_types_dest, + G_N_ELEMENTS (drag_types_dest), + GDK_ACTION_MOVE | GDK_ACTION_COPY); + + /* connect_after to allow GtkNotebook's built-in tab switching */ + g_signal_connect_after (self->priv->notebook, "drag-motion", + G_CALLBACK (chat_window_drag_motion), self); + g_signal_connect (self->priv->notebook, "drag-data-received", + G_CALLBACK (chat_window_drag_data_received), self); + g_signal_connect (self->priv->notebook, "drag-drop", + G_CALLBACK (chat_window_drag_drop), self); + + chat_windows = g_list_prepend (chat_windows, self); + + /* Set up private details */ + self->priv->chats = NULL; + self->priv->current_chat = NULL; + self->priv->notification = NULL; + + self->priv->notify_mgr = empathy_notify_manager_dup_singleton (); + + self->priv->chat_manager = empathy_chat_manager_dup_singleton (); + self->priv->chat_manager_chats_changed_id = g_signal_connect ( + self->priv->chat_manager, "closed-chats-changed", + G_CALLBACK (chat_window_chat_manager_chats_changed_cb), self); + + chat_window_chat_manager_chats_changed_cb (self->priv->chat_manager, + empathy_chat_manager_get_num_closed_chats (self->priv->chat_manager), self); + + g_object_ref (self->priv->ui_manager); + g_object_unref (gui); } -static void -empathy_chat_window_add_chat (EmpathyChatWindow *window, - EmpathyChat *chat) +/* Returns the window to open a new tab in if there is a suitable window, + * otherwise, returns NULL indicating that a new window should be added. + */ +static EmpathyChatWindow * +empathy_chat_window_get_default (gboolean room) { - EmpathyChatWindowPriv *priv; - GtkWidget *label; - GtkWidget *popup_label; - GtkWidget *child; - GValue value = { 0, }; + GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA); + GList *l; + gboolean separate_windows = TRUE; - g_return_if_fail (window != NULL); - g_return_if_fail (EMPATHY_IS_CHAT (chat)); + separate_windows = g_settings_get_boolean (gsettings, + EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS); + separate_windows = FALSE; - priv = GET_PRIV (window); + g_object_unref (gsettings); - /* Reference the chat object */ - g_object_ref (chat); + if (separate_windows) + /* Always create a new window */ + return NULL; - /* If this window has just been created, position it */ - if (priv->chats == NULL) { - const gchar *name = "chat-window"; - gboolean separate_windows; + for (l = chat_windows; l; l = l->next) + { + EmpathyChatWindow *chat_window; + guint nb_rooms, nb_private; - separate_windows = g_settings_get_boolean (priv->gsettings_ui, - EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS); + chat_window = l->data; - if (empathy_chat_is_room (chat)) - name = "room-window"; +#if 0 + empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private); - if (separate_windows) { - gint x, y; + /* Skip the window if there aren't any rooms in it */ + if (room && nb_rooms == 0) + continue; - /* Save current position of the window */ - gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y); + /* Skip the window if there aren't any 1-1 chats in it */ + if (!room && nb_private == 0) + continue; +#endif - /* First bind to the 'generic' name. So new window for which we didn't - * save a geometry yet will have the geometry of the last saved - * window (bgo #601191). */ - empathy_geometry_bind (GTK_WINDOW (priv->dialog), name); + return chat_window; + } - /* Restore previous position of the window so the newly created window - * won't be in the same position as the latest saved window and so - * completely hide it. */ - gtk_window_move (GTK_WINDOW (priv->dialog), x, y); + return NULL; +} - /* Then bind it to the name of the contact/room so we'll save the - * geometry specific to this window */ - name = empathy_chat_get_id (chat); - } +static void +empathy_chat_window_add_chat (EmpathyChatWindow *self, + EmpathyChat *chat) +{ + GtkWidget *label; + GtkWidget *popup_label; + GtkWidget *child; + GValue value = { 0, }; + + g_return_if_fail (self != NULL); + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + + /* Reference the chat object */ + g_object_ref (chat); + + /* If this window has just been created, position it */ + if (self->priv->chats == NULL) + { + const gchar *name = "chat-window"; + gboolean separate_windows; + + separate_windows = g_settings_get_boolean (self->priv->gsettings_ui, + EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS); + separate_windows = FALSE; + + if (empathy_chat_is_room (chat)) + name = "room-window"; + + if (separate_windows) + { + gint x, y; + + /* Save current position of the window */ + //gtk_window_get_position (GTK_WINDOW (self), &x, &y); + + /* First bind to the 'generic' name. So new window for which we didn't + * save a geometry yet will have the geometry of the last saved + * window (bgo #601191). */ + //empathy_geometry_bind (GTK_WINDOW (self), name); + + /* Restore previous position of the window so the newly created window + * won't be in the same position as the latest saved window and so + * completely hide it. */ + //gtk_window_move (GTK_WINDOW (self), x, y); + + /* Then bind it to the name of the contact/room so we'll save the + * geometry specific to this window */ + name = empathy_chat_get_id (chat); + } + + //empathy_geometry_bind (GTK_WINDOW (self), name); + } + + child = GTK_WIDGET (chat); + label = chat_window_create_label (self, chat, TRUE); + popup_label = chat_window_create_label (self, chat, FALSE); + gtk_widget_show (child); + + g_signal_connect (chat, "notify::name", + G_CALLBACK (chat_window_chat_notify_cb), NULL); + g_signal_connect (chat, "notify::subject", + G_CALLBACK (chat_window_chat_notify_cb), NULL); + g_signal_connect (chat, "notify::remote-contact", + G_CALLBACK (chat_window_chat_notify_cb), NULL); + g_signal_connect (chat, "notify::sms-channel", + G_CALLBACK (chat_window_chat_notify_cb), NULL); + g_signal_connect (chat, "notify::n-messages-sending", + G_CALLBACK (chat_window_chat_notify_cb), NULL); + g_signal_connect (chat, "notify::nb-unread-messages", + G_CALLBACK (chat_window_chat_notify_cb), NULL); + chat_window_chat_notify_cb (chat); + + gtk_notebook_append_page_menu (GTK_NOTEBOOK (self->priv->notebook), child, label, + popup_label); + gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (self->priv->notebook), child, TRUE); + gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (self->priv->notebook), child, TRUE); + g_value_init (&value, G_TYPE_BOOLEAN); + g_value_set_boolean (&value, TRUE); + gtk_container_child_set_property (GTK_CONTAINER (self->priv->notebook), + child, "tab-expand" , &value); + gtk_container_child_set_property (GTK_CONTAINER (self->priv->notebook), + child, "tab-fill" , &value); + g_value_unset (&value); + + DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count); +} - empathy_geometry_bind (GTK_WINDOW (priv->dialog), name); - } +static void +empathy_chat_window_remove_chat (EmpathyChatWindow *self, + EmpathyChat *chat) +{ + gint position; + EmpathyContact *remote_contact; + EmpathyChatManager *chat_manager; - child = GTK_WIDGET (chat); - label = chat_window_create_label (window, chat, TRUE); - popup_label = chat_window_create_label (window, chat, FALSE); - gtk_widget_show (child); + g_return_if_fail (self != NULL); + g_return_if_fail (EMPATHY_IS_CHAT (chat)); - g_signal_connect (chat, "notify::name", - G_CALLBACK (chat_window_chat_notify_cb), - NULL); - g_signal_connect (chat, "notify::subject", - G_CALLBACK (chat_window_chat_notify_cb), - NULL); - g_signal_connect (chat, "notify::remote-contact", - G_CALLBACK (chat_window_chat_notify_cb), - NULL); - g_signal_connect (chat, "notify::sms-channel", - G_CALLBACK (chat_window_chat_notify_cb), - NULL); - g_signal_connect (chat, "notify::n-messages-sending", - G_CALLBACK (chat_window_chat_notify_cb), - NULL); - g_signal_connect (chat, "notify::nb-unread-messages", - G_CALLBACK (chat_window_chat_notify_cb), - NULL); - chat_window_chat_notify_cb (chat); + g_signal_handlers_disconnect_by_func (chat, + chat_window_chat_notify_cb, NULL); - gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label); - gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE); - gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE); - g_value_init (&value, G_TYPE_BOOLEAN); - g_value_set_boolean (&value, TRUE); - gtk_container_child_set_property (GTK_CONTAINER (priv->notebook), - child, "tab-expand" , &value); - gtk_container_child_set_property (GTK_CONTAINER (priv->notebook), - child, "tab-fill" , &value); - g_value_unset (&value); + remote_contact = g_object_get_data (G_OBJECT (chat), + "chat-window-remote-contact"); - DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count); -} + if (remote_contact) + { + g_signal_handlers_disconnect_by_func (remote_contact, + chat_window_update_chat_tab, chat); + } -static void -empathy_chat_window_remove_chat (EmpathyChatWindow *window, - EmpathyChat *chat) -{ - EmpathyChatWindowPriv *priv; - gint position; - EmpathyContact *remote_contact; - EmpathyChatManager *chat_manager; + chat_manager = empathy_chat_manager_dup_singleton (); + empathy_chat_manager_closed_chat (chat_manager, chat); + g_object_unref (chat_manager); + + position = gtk_notebook_page_num (GTK_NOTEBOOK (self->priv->notebook), + GTK_WIDGET (chat)); + gtk_notebook_remove_page (GTK_NOTEBOOK (self->priv->notebook), position); + + DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1); - g_return_if_fail (window != NULL); - g_return_if_fail (EMPATHY_IS_CHAT (chat)); - - priv = GET_PRIV (window); - - g_signal_handlers_disconnect_by_func (chat, - chat_window_chat_notify_cb, - NULL); - remote_contact = g_object_get_data (G_OBJECT (chat), - "chat-window-remote-contact"); - if (remote_contact) { - g_signal_handlers_disconnect_by_func (remote_contact, - chat_window_update_chat_tab, - chat); - } - - chat_manager = empathy_chat_manager_dup_singleton (); - empathy_chat_manager_closed_chat (chat_manager, chat); - g_object_unref (chat_manager); - - position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook), - GTK_WIDGET (chat)); - gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position); - - DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1); - - g_object_unref (chat); + g_object_unref (chat); } static void empathy_chat_window_move_chat (EmpathyChatWindow *old_window, - EmpathyChatWindow *new_window, - EmpathyChat *chat) + EmpathyChatWindow *new_window, + EmpathyChat *chat) { - GtkWidget *widget; + GtkWidget *widget; - g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window)); - g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window)); - g_return_if_fail (EMPATHY_IS_CHAT (chat)); + g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window)); + g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window)); + g_return_if_fail (EMPATHY_IS_CHAT (chat)); - widget = GTK_WIDGET (chat); + widget = GTK_WIDGET (chat); - DEBUG ("Chat moving with widget:%p (%d references)", widget, - G_OBJECT (widget)->ref_count); + DEBUG ("Chat moving with widget:%p (%d references)", widget, + G_OBJECT (widget)->ref_count); - /* We reference here to make sure we don't loose the widget - * and the EmpathyChat object during the move. - */ - g_object_ref (chat); - g_object_ref (widget); + /* We reference here to make sure we don't loose the widget + * and the EmpathyChat object during the move. + */ + g_object_ref (chat); + g_object_ref (widget); - empathy_chat_window_remove_chat (old_window, chat); - empathy_chat_window_add_chat (new_window, chat); + empathy_chat_window_remove_chat (old_window, chat); + empathy_chat_window_add_chat (new_window, chat); - g_object_unref (widget); - g_object_unref (chat); + g_object_unref (widget); + g_object_unref (chat); } static void -empathy_chat_window_switch_to_chat (EmpathyChatWindow *window, - EmpathyChat *chat) +empathy_chat_window_switch_to_chat (EmpathyChatWindow *self, + EmpathyChat *chat) { - EmpathyChatWindowPriv *priv; - gint page_num; + gint page_num; - g_return_if_fail (window != NULL); - g_return_if_fail (EMPATHY_IS_CHAT (chat)); + g_return_if_fail (self != NULL); + g_return_if_fail (EMPATHY_IS_CHAT (chat)); - priv = GET_PRIV (window); + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (self->priv->notebook), + GTK_WIDGET (chat)); - page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook), - GTK_WIDGET (chat)); - gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), - page_num); + gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), + page_num); } EmpathyChat * -empathy_chat_window_find_chat (TpAccount *account, - const gchar *id, - gboolean sms_channel) +empathy_chat_window_find_chat (TpAccount *account, + const gchar *id, + gboolean sms_channel) { - GList *l; + GList *l; - g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL); + g_return_val_if_fail (!TPAW_STR_EMPTY (id), NULL); - for (l = chat_windows; l; l = l->next) { - EmpathyChatWindowPriv *priv; - EmpathyChatWindow *window; - GList *ll; + for (l = chat_windows; l; l = l->next) + { + EmpathyChatWindow *window = l->data; + GList *ll; - window = l->data; - priv = GET_PRIV (window); + for (ll = window->priv->chats; ll; ll = ll->next) + { + EmpathyChat *chat; - for (ll = priv->chats; ll; ll = ll->next) { - EmpathyChat *chat; + chat = ll->data; - chat = ll->data; + if (account == empathy_chat_get_account (chat) && + !tp_strdiff (id, empathy_chat_get_id (chat)) && + sms_channel == empathy_chat_is_sms_channel (chat)) + return chat; + } + } - if (account == empathy_chat_get_account (chat) && - !tp_strdiff (id, empathy_chat_get_id (chat)) && - sms_channel == empathy_chat_is_sms_channel (chat)) { - return chat; - } - } - } - - return NULL; + return NULL; } -void +EmpathyChatWindow * empathy_chat_window_present_chat (EmpathyChat *chat, - gint64 timestamp) + gint64 timestamp) { - EmpathyChatWindow *window; - EmpathyChatWindowPriv *priv; - guint32 x_timestamp; + EmpathyChatWindow *self; + guint32 x_timestamp; - g_return_if_fail (EMPATHY_IS_CHAT (chat)); + if (chat == NULL) { + /* initial window */ + self = empathy_chat_window_new (); + gtk_widget_show (GTK_WIDGET (self)); + return self; + } - window = chat_window_find_chat (chat); + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); - /* If the chat has no window, create one */ - if (window == NULL) { - window = empathy_chat_window_get_default (empathy_chat_is_room (chat)); - if (!window) { - window = empathy_chat_window_new (); + self = chat_window_find_chat (chat); - /* we want to display the newly created window even if we don't present - * it */ - priv = GET_PRIV (window); - gtk_widget_show (priv->dialog); - } + /* If the chat has no window, create one */ + if (self == NULL) + { + self = empathy_chat_window_get_default (empathy_chat_is_room (chat)); + if (!self) + { + self = empathy_chat_window_new (); - empathy_chat_window_add_chat (window, chat); - } + /* we want to display the newly created window even if we + * don't present it */ + gtk_widget_show (GTK_WIDGET (self)); + } - /* Don't force the window to show itself when it wasn't - * an action by the user - */ - if (!tp_user_action_time_should_present (timestamp, &x_timestamp)) - return; + empathy_chat_window_add_chat (self, chat); + } - priv = GET_PRIV (window); + /* Don't force the window to show itself when it wasn't + * an action by the user + */ + if (!tp_user_action_time_should_present (timestamp, &x_timestamp)) + return self; - if (x_timestamp != GDK_CURRENT_TIME) { - /* Don't present or switch tab if the action was earlier than the - * last actions X time, accounting for overflow and the first ever - * presentation */ + if (x_timestamp != GDK_CURRENT_TIME) + { + /* Don't present or switch tab if the action was earlier than the + * last actions X time, accounting for overflow and the first ever + * presentation */ - if (priv->x_user_action_time != 0 - && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time)) - return; + if (self->priv->x_user_action_time != 0 + && X_EARLIER_OR_EQL (x_timestamp, self->priv->x_user_action_time)) + return self; - priv->x_user_action_time = x_timestamp; - } + self->priv->x_user_action_time = x_timestamp; + } - empathy_chat_window_switch_to_chat (window, chat); + empathy_chat_window_switch_to_chat (self, chat); - /* 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); + /* Don't use tpaw_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 (gtk_widget_get_toplevel(GTK_WIDGET(self))), x_timestamp); - gtk_widget_grab_focus (chat->input_text_view); + gtk_widget_grab_focus (chat->input_text_view); + return self; } static void empathy_chat_window_get_nb_chats (EmpathyChatWindow *self, - guint *nb_rooms, - guint *nb_private) -{ - EmpathyChatWindowPriv *priv = GET_PRIV (self); - GList *l; - guint _nb_rooms = 0, _nb_private = 0; - - for (l = priv->chats; l != NULL; l = g_list_next (l)) { - if (empathy_chat_is_room (EMPATHY_CHAT (l->data))) - _nb_rooms++; - else - _nb_private++; - } - - if (nb_rooms != NULL) - *nb_rooms = _nb_rooms; - if (nb_private != NULL) - *nb_private = _nb_private; + guint *nb_rooms, + guint *nb_private) +{ + GList *l; + guint _nb_rooms = 0, _nb_private = 0; + + for (l = self->priv->chats; l != NULL; l = g_list_next (l)) + { + if (empathy_chat_is_room (EMPATHY_CHAT (l->data))) + _nb_rooms++; + else + _nb_private++; + } + + if (nb_rooms != NULL) + *nb_rooms = _nb_rooms; + if (nb_private != NULL) + *nb_private = _nb_private; +} + +EmpathyIndividualManager * +empathy_chat_window_get_individual_manager (EmpathyChatWindow *self) +{ + return self->priv->individual_mgr; }