#include <glib/gi18n.h>
#include <libnotify/notification.h>
-#include <telepathy-glib/account-manager.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-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 "empathy-chat-window.h"
#include "empathy-about-dialog.h"
+#include "empathy-invite-participant-dialog.h"
#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
#include <libempathy/empathy-debug.h>
GtkAction *menu_edit_cut;
GtkAction *menu_edit_copy;
GtkAction *menu_edit_paste;
+ GtkAction *menu_edit_find;
GtkAction *menu_tabs_next;
GtkAction *menu_tabs_prev;
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)
}
}
+static guint
+get_all_unread_messages (EmpathyChatWindowPriv *priv)
+{
+ GList *l;
+ guint nb = 0;
+
+ 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)
{
- const gchar *name;
-
- name = empathy_chat_get_name (priv->current_chat);
+ 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_menu_context_update (priv,
num_pages);
+ chat_window_conversation_menu_update (priv, window);
+
chat_window_contact_menu_update (priv,
window);
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);
empathy_chat_set_show_contacts (priv->current_chat, active);
}
+static void
+got_contact_cb (EmpathyTpContactFactory *factory,
+ EmpathyContact *contact,
+ const GError *error,
+ gpointer user_data,
+ GObject *object)
+{
+ EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
+
+ 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"));
+ }
+}
+
+static void
+chat_window_invite_participant_activate_cb (GtkAction *action,
+ EmpathyChatWindow *window)
+{
+ EmpathyChatWindowPriv *priv;
+ GtkWidget *dialog;
+ EmpathyTpChat *tp_chat;
+ TpChannel *channel;
+ int response;
+ TpAccount *account;
+
+ priv = GET_PRIV (window);
+
+ g_return_if_fail (priv->current_chat != NULL);
+
+ 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);
+
+ dialog = empathy_invite_participant_dialog_new (
+ GTK_WINDOW (priv->dialog), account);
+ gtk_widget_show (dialog);
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (response == GTK_RESPONSE_ACCEPT) {
+ TpConnection *connection;
+ EmpathyTpContactFactory *factory;
+ const char *id;
+
+ id = empathy_contact_selector_dialog_get_selected (
+ EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL);
+ if (EMP_STR_EMPTY (id)) goto out;
+
+ connection = tp_channel_borrow_connection (channel);
+ factory = empathy_tp_contact_factory_dup_singleton (connection);
+
+ empathy_tp_contact_factory_get_from_id (factory, id,
+ got_contact_cb, tp_chat, NULL, NULL);
+
+ g_object_unref (factory);
+ }
+
+out:
+ gtk_widget_destroy (dialog);
+}
+
static void
chat_window_close_activate_cb (GtkAction *action,
EmpathyChatWindow *window)
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)
}
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);
}
/* 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,
+ guint time_,
EmpathyChatWindow *window)
{
GdkAtom target;
priv = GET_PRIV (window);
contact = empathy_chat_get_remote_contact (priv->current_chat);
- if (!empathy_contact_is_online (contact)) {
- gdk_drag_status (context, 0, time);
+ /* 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);
+ gdk_drag_status (context, 0, time_);
return FALSE;
}
- gdk_drag_status (context, GDK_ACTION_COPY, time);
+ gdk_drag_status (context, GDK_ACTION_COPY, time_);
return TRUE;
}
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);
+ gdk_drag_status (context, GDK_ACTION_COPY, time_);
return TRUE;
}
- /* Otherwise, it must be a notebook tab drag. Set to MOVE. */
- gdk_drag_status (context, GDK_ACTION_MOVE, time);
- return TRUE;
+ return FALSE;
}
static void
priv = GET_PRIV (window);
contact = empathy_chat_get_remote_contact (priv->current_chat);
- if (!EMPATHY_IS_CONTACT (contact)) {
- gtk_drag_finish (context, TRUE, FALSE, time);
+ /* 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);
+ 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_);
"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,
"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,
/* 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_COPY);
"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);
* 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;
+ GValue value = { 0, };
g_return_if_fail (window != NULL);
g_return_if_fail (EMPATHY_IS_CHAT (chat));
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);
}
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);
}
/* 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;
+}