* Martyn Russell <martyn@imendio.com>
* Geert-Jan Van den Bogaerde <geertjan@gnome.org>
* Xavier Claessens <xclaesse@gmail.com>
+ * RĂ´mulo Fernandes Machado <romulo@castorgroup.net>
*/
#include <config.h>
#include <glib/gi18n.h>
#include <libnotify/notification.h>
-#include <telepathy-glib/util.h>
+#include <telepathy-glib/telepathy-glib.h>
#include <libempathy/empathy-contact.h>
#include <libempathy/empathy-message.h>
-#include <libempathy/empathy-dispatcher.h>
#include <libempathy/empathy-chatroom-manager.h>
-#include <libempathy/empathy-account-manager.h>
#include <libempathy/empathy-utils.h>
+#include <libempathy/empathy-tp-contact-factory.h>
+#include <libempathy/empathy-contact-list.h>
#include <libempathy-gtk/empathy-images.h>
#include <libempathy-gtk/empathy-conf.h>
#include <libempathy-gtk/empathy-smiley-manager.h>
#include <libempathy-gtk/empathy-sound.h>
#include <libempathy-gtk/empathy-ui-utils.h>
+#include <libempathy-gtk/empathy-notify-manager.h>
#include "empathy-chat-window.h"
#include "empathy-about-dialog.h"
-#include "empathy-misc.h"
+#include "empathy-invite-participant-dialog.h"
#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
#include <libempathy/empathy-debug.h>
GList *chats_composing;
gboolean page_added;
gboolean dnd_same_window;
- guint save_geometry_id;
EmpathyChatroomManager *chatroom_manager;
+ EmpathyNotifyManager *notify_mgr;
GtkWidget *dialog;
GtkWidget *notebook;
NotifyNotification *notification;
NotificationData *notification_data;
+ GtkTargetList *contact_targets;
+ GtkTargetList *file_targets;
+
/* Menu items. */
GtkUIManager *ui_manager;
GtkAction *menu_conv_insert_smiley;
GtkAction *menu_edit_cut;
GtkAction *menu_edit_copy;
GtkAction *menu_edit_paste;
+ GtkAction *menu_edit_find;
GtkAction *menu_tabs_next;
GtkAction *menu_tabs_prev;
typedef enum {
DND_DRAG_TYPE_CONTACT_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 },
{ "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
+ { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
+};
+
+static const GtkTargetEntry drag_types_dest_contact[] = {
+ { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
};
+static const GtkTargetEntry drag_types_dest_file[] = {
+ { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
+};
+
+static void chat_window_update (EmpathyChatWindow *window);
+
G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
static void
{
EmpathyChatWindowPriv *priv;
gint num = -1;
- gint i;
+ guint i;
priv = GET_PRIV (window);
/* We don't want focus/keynav for the button to avoid clutter, and
* Ctrl-W works anyway.
*/
- GTK_WIDGET_UNSET_FLAGS (close_button, GTK_CAN_FOCUS);
- GTK_WIDGET_UNSET_FLAGS (close_button, GTK_CAN_DEFAULT);
+ gtk_widget_set_can_focus (close_button, FALSE);
+ gtk_widget_set_can_default (close_button, FALSE);
/* Set the name to make the special rc style match. */
gtk_widget_set_name (close_button, "empathy-close-button");
}
static void
-chat_window_update (EmpathyChatWindow *window)
+_submenu_notify_visible_changed_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer userdata)
{
- EmpathyChatWindowPriv *priv = GET_PRIV (window);
- gboolean first_page;
- gboolean last_page;
- gboolean is_connected;
- gint num_pages;
- gint page_num;
- gint i;
- const gchar *name;
- guint n_chats;
- GdkPixbuf *icon;
- EmpathyContact *remote_contact;
- gboolean avatar_in_icon;
- GtkWidget *chat;
- GtkWidget *chat_close_button;
- GtkWidget *submenu;
- GtkWidget *menu;
+ g_signal_handlers_disconnect_by_func (object,
+ _submenu_notify_visible_changed_cb,
+ userdata);
+ chat_window_update (EMPATHY_CHAT_WINDOW (userdata));
+}
+
+static void
+chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
+ gint num_pages)
+{
+ gboolean first_page;
+ gboolean last_page;
+ gboolean is_connected;
+ gint page_num;
- /* Get information */
page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
- num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
first_page = (page_num == 0);
last_page = (page_num == (num_pages - 1));
is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
- name = empathy_chat_get_name (priv->current_chat);
- n_chats = g_list_length (priv->chats);
- DEBUG ("Update window");
+ DEBUG ("Update window : Menu Contexts (Tabs & Conv)");
- /* Update menu */
- gtk_action_set_sensitive (priv->menu_tabs_next, !last_page);
- gtk_action_set_sensitive (priv->menu_tabs_prev, !first_page);
+ gtk_action_set_sensitive (priv->menu_tabs_next, TRUE);
+ gtk_action_set_sensitive (priv->menu_tabs_prev, TRUE);
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);
+}
+
+static void
+chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
+ EmpathyChatWindow *self)
+{
+ EmpathyTpChat *tp_chat;
+ TpConnection *connection;
+ GtkAction *action;
+ gboolean sensitive = FALSE;
+
+ g_return_if_fail (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);
+
+ if (tp_chat != NULL) {
+ connection = empathy_tp_chat_get_connection (tp_chat);
+
+ 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);
+}
+
+static void
+chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
+ EmpathyChatWindow *window)
+{
+ GtkWidget *menu, *submenu, *orig_submenu;
- /* Update Contact menu */
menu = gtk_ui_manager_get_widget (priv->ui_manager,
"/chats_menubar/menu_contact");
- submenu = empathy_chat_get_contact_menu (priv->current_chat);
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
- gtk_widget_show (menu);
+ orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
- /* Update window title */
- if (n_chats == 1) {
- gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
+ DEBUG ("Update window : Contact Menu");
+
+ if (orig_submenu == NULL || !GTK_WIDGET_VISIBLE (orig_submenu)) {
+ submenu = empathy_chat_get_contact_menu (priv->current_chat);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
+ gtk_widget_show (menu);
} else {
- gchar *title;
+ empathy_signal_connect_weak (orig_submenu,
+ "notify::visible",
+ (GCallback)_submenu_notify_visible_changed_cb,
+ G_OBJECT (window));
+ }
+}
+
+static guint
+get_all_unread_messages (EmpathyChatWindowPriv *priv)
+{
+ GList *l;
+ guint nb = 0;
- title = g_strdup_printf (_("Conversations (%d)"), n_chats);
- gtk_window_set_title (GTK_WINDOW (priv->dialog), title);
- g_free (title);
+ for (l = priv->chats_new_msg; l != NULL; l = g_list_next (l)) {
+ EmpathyChat *chat = l->data;
+
+ nb += empathy_chat_get_nb_unread_messages (chat);
}
+ return nb;
+}
+
+static gchar *
+get_window_title_name (EmpathyChatWindowPriv *priv)
+{
+ const gchar *active_name;
+ guint nb_chats;
+ guint current_unread_msgs;
+
+ nb_chats = g_list_length (priv->chats);
+ g_assert (nb_chats > 0);
+
+ active_name = empathy_chat_get_name (priv->current_chat);
+
+ current_unread_msgs = empathy_chat_get_nb_unread_messages (
+ priv->current_chat);
+
+ if (nb_chats == 1) {
+ /* only one tab */
+ if (current_unread_msgs == 0)
+ return g_strdup (active_name);
+ else
+ return 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 */
+ return 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 */
+ return 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 */
+ return 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 */
+ return g_strdup_printf (ngettext (
+ "%s (%d unread from all)",
+ "%s (%d unread from all)",
+ all_unread_msgs),
+ active_name, all_unread_msgs);
+ }
+ }
+}
+
+static void
+chat_window_title_update (EmpathyChatWindowPriv *priv)
+{
+ gchar *name;
+
+ DEBUG ("Update window : Title");
+
+ name = get_window_title_name (priv);
+ gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
+ g_free (name);
+}
+
+static void
+chat_window_icon_update (EmpathyChatWindowPriv *priv)
+{
+ GdkPixbuf *icon;
+ EmpathyContact *remote_contact;
+ gboolean avatar_in_icon;
+ guint n_chats;
+
+ n_chats = g_list_length (priv->chats);
+
+ DEBUG ("Update window : Icon");
+
/* Update window icon */
if (priv->chats_new_msg) {
gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
}
}
+}
+
+static void
+chat_window_close_button_update (EmpathyChatWindowPriv *priv,
+ gint num_pages)
+{
+ GtkWidget *chat;
+ GtkWidget *chat_close_button;
+ gint i;
+
+ DEBUG ("Update window : Close Button");
if (num_pages == 1) {
chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
}
}
+static void
+chat_window_update (EmpathyChatWindow *window)
+{
+ EmpathyChatWindowPriv *priv = GET_PRIV (window);
+ gint num_pages;
+
+ num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
+
+ DEBUG ("Update window");
+
+ /* Update Tab menu */
+ chat_window_menu_context_update (priv,
+ num_pages);
+
+ chat_window_conversation_menu_update (priv, window);
+
+ chat_window_contact_menu_update (priv,
+ window);
+
+ chat_window_title_update (priv);
+
+ chat_window_icon_update (priv);
+
+ chat_window_close_button_update (priv,
+ num_pages);
+}
+
static void
append_markup_printf (GString *string,
const char *format,
EmpathyContact *remote_contact;
const gchar *name;
const gchar *id;
- EmpathyAccount *account;
+ 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;
window = chat_window_find_chat (chat);
if (!window) {
remote_contact = empathy_chat_get_remote_contact (chat);
DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
- name, empathy_account_get_unique_name (account), subject, remote_contact);
+ name, tp_proxy_get_object_path (account), subject, remote_contact);
/* Update tab image */
- if (g_list_find (priv->chats_new_msg, chat)) {
+ if (empathy_chat_get_tp_chat (chat) == NULL) {
+ /* No TpChat, we are disconnected */
+ icon_name = NULL;
+ }
+ else if (g_list_find (priv->chats_new_msg, chat)) {
icon_name = EMPATHY_IMAGE_MESSAGE;
}
else if (g_list_find (priv->chats_composing, chat)) {
} else {
icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
}
- widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
- gtk_image_set_from_icon_name (GTK_IMAGE (widget), icon_name, GTK_ICON_SIZE_MENU);
- widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
- gtk_image_set_from_icon_name (GTK_IMAGE (widget), icon_name, GTK_ICON_SIZE_MENU);
+
+ 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 tab tooltip */
tooltip = g_string_new (NULL);
append_markup_printf (tooltip,
"<b>%s</b><small> (%s)</small>",
id,
- empathy_account_get_display_name (account));
+ tp_account_get_display_name (account));
if (!EMP_STR_EMPTY (status)) {
append_markup_printf (tooltip, "\n<i>%s</i>", status);
chat);
}
- g_object_set_data (G_OBJECT (chat), "chat-window-remote-contact",
- remote_contact);
+ 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);
is_room = empathy_chat_is_room (priv->current_chat);
if (is_room) {
const gchar *room;
- EmpathyAccount *account;
+ TpAccount *account;
gboolean found = FALSE;
EmpathyChatroom *chatroom;
{
EmpathyChatWindowPriv *priv = GET_PRIV (window);
gboolean active;
- EmpathyAccount *account;
+ TpAccount *account;
const gchar *room;
EmpathyChatroom *chatroom;
chatroom = empathy_chatroom_new_full (account, room, name, FALSE);
empathy_chatroom_manager_add (priv->chatroom_manager, chatroom);
g_object_unref (chatroom);
- }
+ }
empathy_chatroom_set_favorite (chatroom, active);
}
empathy_chat_set_show_contacts (priv->current_chat, active);
}
-static const gchar *
-chat_get_window_id_for_geometry (EmpathyChat *chat)
+static void
+got_contact_cb (EmpathyTpContactFactory *factory,
+ EmpathyContact *contact,
+ const GError *error,
+ gpointer user_data,
+ GObject *object)
{
- const gchar *res = NULL;
- gboolean separate_windows;
-
- empathy_conf_get_bool (empathy_conf_get (),
- EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS,
- &separate_windows);
+ EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
- if (separate_windows) {
- res = empathy_chat_get_id (chat);
+ if (error != NULL) {
+ DEBUG ("Failed: %s", error->message);
+ return;
+ } else {
+ empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
+ contact, _("Inviting you to this room"));
}
-
- return res ? res : "chat-window";
}
-static gboolean
-chat_window_save_geometry_timeout_cb (EmpathyChatWindow *window)
+static void
+chat_window_invite_participant_activate_cb (GtkAction *action,
+ EmpathyChatWindow *window)
{
EmpathyChatWindowPriv *priv;
- gint x, y, w, h;
+ GtkWidget *dialog;
+ EmpathyTpChat *tp_chat;
+ TpChannel *channel;
+ int response;
+ TpAccount *account;
priv = GET_PRIV (window);
- gtk_window_get_size (GTK_WINDOW (priv->dialog), &w, &h);
- gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
+ g_return_if_fail (priv->current_chat != NULL);
- empathy_geometry_save (chat_get_window_id_for_geometry (priv->current_chat),
- x, y, w, h);
+ tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
+ channel = empathy_tp_chat_get_channel (tp_chat);
+ account = empathy_chat_get_account (priv->current_chat);
- priv->save_geometry_id = 0;
+ dialog = empathy_invite_participant_dialog_new (
+ GTK_WINDOW (priv->dialog), account);
+ gtk_widget_show (dialog);
- return FALSE;
-}
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
-static gboolean
-chat_window_configure_event_cb (GtkWidget *widget,
- GdkEventConfigure *event,
- EmpathyChatWindow *window)
-{
- EmpathyChatWindowPriv *priv;
+ if (response == GTK_RESPONSE_ACCEPT) {
+ TpConnection *connection;
+ EmpathyTpContactFactory *factory;
+ const char *id;
- priv = GET_PRIV (window);
+ id = empathy_contact_selector_dialog_get_selected (
+ EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL);
+ if (EMP_STR_EMPTY (id)) goto out;
- if (priv->save_geometry_id != 0) {
- g_source_remove (priv->save_geometry_id);
- }
+ connection = tp_channel_borrow_connection (channel);
+ factory = empathy_tp_contact_factory_dup_singleton (connection);
- priv->save_geometry_id =
- g_timeout_add_seconds (1,
- (GSourceFunc) chat_window_save_geometry_timeout_cb,
- window);
+ empathy_tp_contact_factory_get_from_id (factory, id,
+ got_contact_cb, tp_chat, NULL, NULL);
- return FALSE;
+ g_object_unref (factory);
+ }
+
+out:
+ gtk_widget_destroy (dialog);
}
static void
empathy_chat_paste (priv->current_chat);
}
+static void
+chat_window_find_activate_cb (GtkAction *action,
+ EmpathyChatWindow *window)
+{
+ EmpathyChatWindowPriv *priv;
+
+ g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
+
+ priv = GET_PRIV (window);
+
+ empathy_chat_find (priv->current_chat);
+}
+
+static void
+chat_window_tabs_next_activate_cb (GtkAction *action,
+ EmpathyChatWindow *window)
+{
+ EmpathyChatWindowPriv *priv;
+ EmpathyChat *chat;
+ gint index_, numPages;
+
+ priv = GET_PRIV (window);
+
+ chat = priv->current_chat;
+ index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
+ numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
+
+ if (index_ == (numPages - 1)) {
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
+ return;
+ }
+
+ gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
+}
+
+static void
+chat_window_tabs_previous_activate_cb (GtkAction *action,
+ EmpathyChatWindow *window)
+{
+ EmpathyChatWindowPriv *priv;
+ EmpathyChat *chat;
+ gint index_, numPages;
+
+ priv = GET_PRIV (window);
+
+ chat = priv->current_chat;
+ index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
+ numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
+
+ if (index_ <= 0) {
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
+ return;
+ }
+
+ gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
+}
+
static void
chat_window_tabs_left_activate_cb (GtkAction *action,
EmpathyChatWindow *window)
{
EmpathyChatWindowPriv *priv;
EmpathyChat *chat;
- gint index;
+ gint index_;
priv = GET_PRIV (window);
chat = priv->current_chat;
- index = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
- if (index <= 0) {
+ index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
+ if (index_ <= 0) {
return;
}
gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
GTK_WIDGET (chat),
- index - 1);
+ index_ - 1);
}
static void
{
EmpathyChatWindowPriv *priv;
EmpathyChat *chat;
- gint index;
+ gint index_;
priv = GET_PRIV (window);
chat = priv->current_chat;
- index = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
+ index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
GTK_WIDGET (chat),
- index + 1);
+ index_ + 1);
}
static void
EmpathyChatWindowPriv *priv = GET_PRIV (window);
gboolean res;
- if (!empathy_notification_is_enabled ()) {
+ if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
return;
} else {
empathy_conf_get_bool (empathy_conf_get (),
G_CALLBACK (chat_window_notification_closed_cb), cb_data);
}
- pixbuf = empathy_misc_get_pixbuf_for_notification (sender, EMPATHY_IMAGE_NEW_MESSAGE);
+ 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 (priv->notification, pixbuf);
}
if (has_focus && 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;
}
EMPATHY_SOUND_MESSAGE_INCOMING);
chat_window_show_or_update_notification (window, message, chat);
}
+
+ /* update the number of unread messages */
+ chat_window_title_update (priv);
}
static GtkNotebook *
priv->current_chat = chat;
priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
+ empathy_chat_messages_read (chat);
chat_window_update_chat_tab (chat);
}
g_signal_connect (chat, "new-message",
G_CALLBACK (chat_window_new_message_cb),
window);
+ 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.
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);
priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
+ empathy_chat_messages_read (chat);
priv->chats_composing = g_list_remove (priv->chats_composing, chat);
if (priv->chats == NULL) {
priv = GET_PRIV (window);
priv->chats_new_msg = g_list_remove (priv->chats_new_msg, priv->current_chat);
+ empathy_chat_messages_read (priv->current_chat);
chat_window_set_urgency_hint (window, FALSE);
return FALSE;
}
+static gboolean
+chat_window_drag_drop (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ guint time_,
+ gpointer user_data)
+{
+ GdkAtom target, uri_target, contact_target;
+
+ target = gtk_drag_dest_find_target (widget, context, NULL);
+ uri_target = gdk_atom_intern_static_string ("text/uri-list");
+ contact_target = gdk_atom_intern_static_string ("text/contact-id");
+
+ if (target == uri_target || target == contact_target) {
+ gtk_drag_get_data (widget, context, target, time_);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+chat_window_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ guint time_,
+ EmpathyChatWindow *window)
+{
+ GdkAtom target;
+ EmpathyChatWindowPriv *priv;
+ GdkAtom dest_target;
+
+ priv = GET_PRIV (window);
+
+ target = gtk_drag_dest_find_target (widget, context, NULL);
+
+ dest_target = gdk_atom_intern_static_string ("text/uri-list");
+ if (target == dest_target) {
+ /* 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;
+ }
+
+ dest_target = gdk_atom_intern_static_string ("text/contact-id");
+ if (target == dest_target) {
+ /* 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
chat_window_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
int y,
GtkSelectionData *selection,
guint info,
- guint time,
+ guint time_,
EmpathyChatWindow *window)
{
if (info == DND_DRAG_TYPE_CONTACT_ID) {
- EmpathyChat *chat;
+ EmpathyChat *chat = NULL;
EmpathyChatWindow *old_window;
- EmpathyAccount *account;
- EmpathyAccountManager *account_manager;
+ TpAccount *account = NULL;
+ TpAccountManager *account_manager;
const gchar *id;
gchar **strv;
const gchar *account_id;
const gchar *contact_id;
id = (const gchar*) gtk_selection_data_get_data (selection);
- account_manager = empathy_account_manager_dup_singleton ();
+
+ /* FIXME: Perhaps should be sure that the account manager is
+ * prepared before calling _ensure_account on it. */
+ account_manager = tp_account_manager_dup ();
DEBUG ("DND contact from roster with id:'%s'", id);
strv = g_strsplit (id, ":", 2);
- account_id = strv[0];
- contact_id = strv[1];
- account = empathy_account_manager_get_account (account_manager, account_id);
- chat = empathy_chat_window_find_chat (account, contact_id);
+ if (g_strv_length (strv) == 2) {
+ account_id = strv[0];
+ contact_id = strv[1];
+ account =
+ tp_account_manager_ensure_account (account_manager, account_id);
+ if (account != NULL)
+ chat = empathy_chat_window_find_chat (account, contact_id);
+ }
+
+ if (account == NULL) {
+ g_strfreev (strv);
+ gtk_drag_finish (context, FALSE, FALSE, time_);
+ return;
+ }
if (!chat) {
TpConnection *connection;
- connection = empathy_account_get_connection (account);
+ connection = tp_account_get_connection (account);
if (connection) {
empathy_dispatcher_chat_with_contact_id (
old_window = chat_window_find_chat (chat);
if (old_window) {
if (old_window == window) {
- gtk_drag_finish (context, TRUE, FALSE, time);
+ gtk_drag_finish (context, TRUE, FALSE, time_);
return;
}
* weird consequences, and we handle that internally
* anyway with add_chat () and remove_chat ().
*/
- gtk_drag_finish (context, TRUE, FALSE, time);
+ gtk_drag_finish (context, TRUE, FALSE, 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;
EmpathyChatWindowPriv *priv;
priv = GET_PRIV (window);
-
- if (old_window == window) {
- DEBUG ("DND tab (within same window)");
- priv->dnd_same_window = TRUE;
- gtk_drag_finish (context, TRUE, FALSE, time);
- return;
- }
-
- priv->dnd_same_window = FALSE;
+ priv->dnd_same_window = (old_window == window);
+ DEBUG ("DND tab (within same window: %s)",
+ priv->dnd_same_window ? "Yes" : "No");
}
-
- /* 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 {
DEBUG ("DND from unknown source");
- gtk_drag_finish (context, FALSE, FALSE, time);
+ gtk_drag_finish (context, FALSE, FALSE, time_);
}
}
g_object_unref (priv->ui_manager);
g_object_unref (priv->chatroom_manager);
- if (priv->save_geometry_id != 0) {
- g_source_remove (priv->save_geometry_id);
- }
+ g_object_unref (priv->notify_mgr);
if (priv->notification != NULL) {
notify_notification_close (priv->notification, NULL);
}
}
+ if (priv->contact_targets) {
+ gtk_target_list_unref (priv->contact_targets);
+ }
+ if (priv->file_targets) {
+ gtk_target_list_unref (priv->file_targets);
+ }
+
chat_windows = g_list_remove (chat_windows, window);
gtk_widget_destroy (priv->dialog);
GClosure *closure;
GtkWidget *menu;
GtkWidget *submenu;
- gint i;
+ guint i;
GtkWidget *chat_vbox;
gchar *filename;
EmpathySmileyManager *smiley_manager;
"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_left", &priv->menu_tabs_left,
g_free (filename);
empathy_builder_connect (gui, window,
- "chat_window", "configure-event", chat_window_configure_event_cb,
"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_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_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,
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,
"delete_event",
G_CALLBACK (chat_window_delete_event_cb),
window);
-
- g_signal_connect_swapped (priv->menu_tabs_prev,
- "activate",
- G_CALLBACK (gtk_notebook_prev_page),
- priv->notebook);
- g_signal_connect_swapped (priv->menu_tabs_next,
- "activate",
- G_CALLBACK (gtk_notebook_next_page),
- priv->notebook);
-
g_signal_connect (priv->dialog,
"focus_in_event",
G_CALLBACK (chat_window_focus_in_event_cb),
/* Set up drag and drop */
gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
- GTK_DEST_DEFAULT_ALL,
+ GTK_DEST_DEFAULT_HIGHLIGHT,
drag_types_dest,
G_N_ELEMENTS (drag_types_dest),
- GDK_ACTION_MOVE);
+ 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);
priv->chats_new_msg = NULL;
priv->chats_composing = NULL;
priv->current_chat = NULL;
+
+ priv->notify_mgr = empathy_notify_manager_dup_singleton ();
}
EmpathyChatWindow *
* be added.
*/
EmpathyChatWindow *
-empathy_chat_window_get_default (void)
+empathy_chat_window_get_default (gboolean room)
{
GList *l;
gboolean separate_windows = TRUE;
}
for (l = chat_windows; l; l = l->next) {
+ EmpathyChatWindowPriv *priv;
EmpathyChatWindow *chat_window;
GtkWidget *dialog;
chat_window = l->data;
+ priv = GET_PRIV (chat_window);
dialog = empathy_chat_window_get_dialog (chat_window);
if (empathy_window_get_is_visible (GTK_WINDOW (dialog))) {
+ guint nb_rooms, nb_private;
+ empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
+
+ /* Skip the window if there aren't any rooms in it */
+ if (room && nb_rooms == 0)
+ continue;
+
+ /* Skip the window if there aren't any 1-1 chats in it */
+ if (!room && nb_private == 0)
+ continue;
+
/* Found a visible window on this desktop */
return chat_window;
}
GtkWidget *label;
GtkWidget *popup_label;
GtkWidget *child;
- gint x, y, w, h;
+ GValue value = { 0, };
g_return_if_fail (window != NULL);
g_return_if_fail (EMPATHY_IS_CHAT (chat));
/* If this window has just been created, position it */
if (priv->chats == NULL) {
- empathy_geometry_load (chat_get_window_id_for_geometry (chat), &x, &y, &w, &h);
+ const gchar *name = "chat-window";
+ gboolean separate_windows;
- if (x >= 0 && y >= 0) {
- /* Let the window manager position it if we don't have
- * good x, y coordinates.
- */
- gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
- }
+ empathy_conf_get_bool (empathy_conf_get (),
+ EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS,
+ &separate_windows);
- if (w > 0 && h > 0) {
- /* Use the defaults from the ui file if we don't have
- * good w, h geometry.
- */
- gtk_window_resize (GTK_WINDOW (priv->dialog), w, h);
+ if (separate_windows) {
+ name = empathy_chat_get_id (chat);
}
+ else if (empathy_chat_is_room (chat)) {
+ name = "room-window";
+ }
+
+ empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
}
child = GTK_WIDGET (chat);
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);
- gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (priv->notebook), child,
- TRUE, TRUE, GTK_PACK_START);
+ 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);
DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
}
}
EmpathyChat *
-empathy_chat_window_find_chat (EmpathyAccount *account,
+empathy_chat_window_find_chat (TpAccount *account,
const gchar *id)
{
GList *l;
/* If the chat has no window, create one */
if (window == NULL) {
- window = empathy_chat_window_get_default ();
+ window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
if (!window) {
window = empathy_chat_window_new ();
}
priv = GET_PRIV (window);
empathy_chat_window_switch_to_chat (window, chat);
- empathy_window_present (GTK_WINDOW (priv->dialog), TRUE);
+ empathy_window_present (GTK_WINDOW (priv->dialog));
gtk_widget_grab_focus (chat->input_text_view);
}
+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;
+}