From 2e2fdd45ae2d7fac58c9cc40b48c84ffc6b3f48e Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Mon, 14 Sep 2009 21:15:06 -0500 Subject: Implementing drag and drop file sending on chat windows --- libempathy-gtk/empathy-chat.c | 1 + libempathy-gtk/empathy-ui-utils.c | 36 ++++++++++++++++++++------------ libempathy-gtk/empathy-ui-utils.h | 2 ++ src/empathy-chat-window.c | 44 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 13 deletions(-) diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c index 372e90cf0..c83649a04 100644 --- a/libempathy-gtk/empathy-chat.c +++ b/libempathy-gtk/empathy-chat.c @@ -2007,6 +2007,7 @@ chat_create_ui (EmpathyChat *chat) /* Add message view. */ chat->view = empathy_theme_manager_create_view (empathy_theme_manager_get ()); + gtk_drag_dest_unset (GTK_WIDGET (chat->view)); g_signal_connect (chat->view, "focus_in_event", G_CALLBACK (chat_text_view_focus_in_event_cb), chat); diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c index ce5ec419e..411a76640 100644 --- a/libempathy-gtk/empathy-ui-utils.c +++ b/libempathy-gtk/empathy-ui-utils.c @@ -1451,30 +1451,40 @@ empathy_toggle_button_set_state_quietly (GtkWidget *widget, g_signal_handlers_unblock_by_func (widget, callback, user_data); } +void +empathy_send_file (EmpathyContact *contact, GFile *file) +{ + EmpathyFTFactory *factory; + GtkRecentManager *manager; + gchar *uri; + + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + g_return_if_fail (G_IS_FILE (file)); + + factory = empathy_ft_factory_dup_singleton (); + + empathy_ft_factory_new_transfer_outgoing (factory, contact, file); + + uri = g_file_get_uri (file); + manager = gtk_recent_manager_get_default (); + gtk_recent_manager_add_item (manager, uri); + g_free (uri); + + g_object_unref (factory); +} + static void file_manager_send_file_response_cb (GtkDialog *widget, gint response_id, EmpathyContact *contact) { - EmpathyFTFactory *factory; GFile *file; - gchar *uri; - GtkRecentManager *manager; if (response_id == GTK_RESPONSE_OK) { file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (widget)); - uri = g_file_get_uri (file); - - factory = empathy_ft_factory_dup_singleton (); - empathy_ft_factory_new_transfer_outgoing (factory, contact, - file); + empathy_send_file (contact, file); - manager = gtk_recent_manager_get_default (); - gtk_recent_manager_add_item (manager, uri); - - g_free (uri); - g_object_unref (factory); g_object_unref (file); } diff --git a/libempathy-gtk/empathy-ui-utils.h b/libempathy-gtk/empathy-ui-utils.h index 7bec0884e..0f453ddbc 100644 --- a/libempathy-gtk/empathy-ui-utils.h +++ b/libempathy-gtk/empathy-ui-utils.h @@ -110,6 +110,8 @@ void empathy_toggle_button_set_state_quietly (GtkWidget *widge GtkWidget * empathy_link_button_new (const gchar *url, const gchar *title); +void empathy_send_file (EmpathyContact *contact, + GFile *file); void empathy_send_file_with_file_chooser (EmpathyContact *contact); void empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler); diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index 278c8f044..15ab92ce6 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -106,6 +106,7 @@ static const guint tab_accel_keys[] = { typedef enum { DND_DRAG_TYPE_CONTACT_ID, + DND_DRAG_TYPE_URI_LIST, DND_DRAG_TYPE_TAB } DndDragType; @@ -114,6 +115,10 @@ static const GtkTargetEntry drag_types_dest[] = { { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB }, }; +static const GtkTargetEntry drag_types_uri_dest[] = { + { "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); @@ -1403,6 +1408,40 @@ chat_window_drag_data_received (GtkWidget *widget, */ gtk_drag_finish (context, TRUE, FALSE, time_); } + else if (info == DND_DRAG_TYPE_URI_LIST) { + EmpathyChatWindowPriv *priv; + EmpathyContact *contact; + const gchar *data; + GFile *file; + gchar *nl; + gchar *uri; + + /* Only handle a single file for new. It would be wicked cool to be + able to do multiple files, offering to zip them or whatever like + nautilus-sendto does. Note that text/uri-list is defined to have + each line terminated by \r\n, but we can be tolerant of applications + that only use \n or don't terminate single-line entries. + */ + data = (const gchar*) gtk_selection_data_get_data (selection); + nl = strstr (data, "\r\n"); + if (!nl) { + nl = strchr (data, '\n'); + } + if (nl) { + uri = g_strndup (data, nl - data); + file = g_file_new_for_uri (uri); + g_free (uri); + } + else { + file = g_file_new_for_uri (data); + } + + priv = GET_PRIV (window); + contact = empathy_chat_get_remote_contact (priv->current_chat); + empathy_send_file (contact, file); + + g_object_unref (file); + } else if (info == DND_DRAG_TYPE_TAB) { EmpathyChat **chat; EmpathyChatWindow *old_window = NULL; @@ -1617,6 +1656,11 @@ empathy_chat_window_init (EmpathyChatWindow *window) drag_types_dest, G_N_ELEMENTS (drag_types_dest), GDK_ACTION_MOVE); + gtk_drag_dest_set (GTK_WIDGET (priv->notebook), + GTK_DEST_DEFAULT_ALL, + drag_types_uri_dest, + G_N_ELEMENTS (drag_types_uri_dest), + GDK_ACTION_COPY); g_signal_connect (priv->notebook, "drag-data-received", -- cgit v1.2.3 From df2e42c11963a62c729ca592c3fb291a82ccedb9 Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Mon, 14 Sep 2009 21:31:58 -0500 Subject: Call gtk_drag_finish when we get a file transfer drag on a chat window --- src/empathy-chat-window.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index 15ab92ce6..be29b3a8a 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -1441,6 +1441,7 @@ chat_window_drag_data_received (GtkWidget *widget, empathy_send_file (contact, file); g_object_unref (file); + gtk_drag_finish (context, TRUE, FALSE, time); } else if (info == DND_DRAG_TYPE_TAB) { EmpathyChat **chat; -- cgit v1.2.3 From 810fc88802d72b880ff9472be7374f72a34df748 Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Tue, 15 Sep 2009 12:47:09 -0500 Subject: Don't try to offer file transfers to MUCs --- src/empathy-chat-window.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index be29b3a8a..5b3a3ac0f 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -1416,6 +1416,14 @@ chat_window_drag_data_received (GtkWidget *widget, gchar *nl; gchar *uri; + 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); + return; + } + /* Only handle a single file for new. It would be wicked cool to be able to do multiple files, offering to zip them or whatever like nautilus-sendto does. Note that text/uri-list is defined to have @@ -1436,8 +1444,6 @@ chat_window_drag_data_received (GtkWidget *widget, file = g_file_new_for_uri (data); } - priv = GET_PRIV (window); - contact = empathy_chat_get_remote_contact (priv->current_chat); empathy_send_file (contact, file); g_object_unref (file); -- cgit v1.2.3 From 42185f1d38082579ca113d612000c670eac76869 Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Tue, 15 Sep 2009 15:10:24 -0500 Subject: Always prefer GDK_ACTION_COPY for text/uri-list drags --- src/empathy-chat-window.c | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index 5b3a3ac0f..90d4f8f6d 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -113,10 +113,12 @@ typedef enum { 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_uri_dest[] = { - { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST }, +static const GtkTargetEntry drag_types_dest_noft[] = { + { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID }, + { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB }, }; static void chat_window_update (EmpathyChatWindow *window); @@ -1326,6 +1328,28 @@ chat_window_focus_in_event_cb (GtkWidget *widget, return FALSE; } +static void +chat_window_drag_motion (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + guint time, + EmpathyChatWindow *window) +{ + static GtkTargetList *list = NULL; + GdkAtom target; + + if (list == NULL) { + list = gtk_target_list_new (drag_types_dest_noft, + G_N_ELEMENTS (drag_types_dest_noft)); + } + + target = gtk_drag_dest_find_target (widget, context, list); + if (target == GDK_NONE) { + gdk_drag_status (context, GDK_ACTION_COPY, time); + } +} + static void chat_window_drag_data_received (GtkWidget *widget, GdkDragContext *context, @@ -1662,13 +1686,12 @@ empathy_chat_window_init (EmpathyChatWindow *window) GTK_DEST_DEFAULT_ALL, drag_types_dest, G_N_ELEMENTS (drag_types_dest), - GDK_ACTION_MOVE); - gtk_drag_dest_set (GTK_WIDGET (priv->notebook), - GTK_DEST_DEFAULT_ALL, - drag_types_uri_dest, - G_N_ELEMENTS (drag_types_uri_dest), - GDK_ACTION_COPY); + GDK_ACTION_MOVE | GDK_ACTION_COPY); + g_signal_connect (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), -- cgit v1.2.3 From d068663d7d73c7dc54b560b3ed9846c218dd8f35 Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Tue, 15 Sep 2009 15:57:51 -0500 Subject: Handle MOVE and COPY drags better, change contact-id drags to COPY --- src/empathy-chat-window.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index 90d4f8f6d..36f31b0b1 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -116,8 +116,7 @@ static const GtkTargetEntry drag_types_dest[] = { { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST }, }; -static const GtkTargetEntry drag_types_dest_noft[] = { - { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID }, +static const GtkTargetEntry drag_types_dest_move[] = { { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB }, }; @@ -1340,14 +1339,17 @@ chat_window_drag_motion (GtkWidget *widget, GdkAtom target; if (list == NULL) { - list = gtk_target_list_new (drag_types_dest_noft, - G_N_ELEMENTS (drag_types_dest_noft)); + list = gtk_target_list_new (drag_types_dest_move, + G_N_ELEMENTS (drag_types_dest_move)); } target = gtk_drag_dest_find_target (widget, context, list); if (target == GDK_NONE) { gdk_drag_status (context, GDK_ACTION_COPY, time); } + else { + gdk_drag_status (context, GDK_ACTION_MOVE, time); + } } static void -- cgit v1.2.3 From 3cbb3a10e138e1f777427311bf33f30cb6d4839e Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Thu, 17 Sep 2009 16:17:49 -0500 Subject: Implemented file drags to contact list, along with row highlights --- libempathy-gtk/empathy-contact-list-view.c | 252 ++++++++++++++++++++--------- src/empathy-chat-window.c | 4 +- 2 files changed, 175 insertions(+), 81 deletions(-) diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c index c19ad32df..04a448817 100644 --- a/libempathy-gtk/empathy-contact-list-view.c +++ b/libempathy-gtk/empathy-contact-list-view.c @@ -88,17 +88,21 @@ enum { enum DndDragType { DND_DRAG_TYPE_CONTACT_ID, - DND_DRAG_TYPE_URL, + DND_DRAG_TYPE_URI_LIST, DND_DRAG_TYPE_STRING, }; static const GtkTargetEntry drag_types_dest[] = { + { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST }, { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID }, - { "text/uri-list", 0, DND_DRAG_TYPE_URL }, { "text/plain", 0, DND_DRAG_TYPE_STRING }, { "STRING", 0, DND_DRAG_TYPE_STRING }, }; +static const GtkTargetEntry drag_types_dest_file[] = { + { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST }, +}; + static const GtkTargetEntry drag_types_source[] = { { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID }, }; @@ -244,102 +248,143 @@ contact_list_view_drag_data_received (GtkWidget *view, guint time_) { EmpathyContactListViewPriv *priv; - TpAccountManager *account_manager; - EmpathyTpContactFactory *factory = NULL; - TpAccount *account = NULL; GtkTreeModel *model; - GtkTreeViewDropPosition position; - GtkTreePath *path; - const gchar *id; + gboolean success = TRUE; + const gchar *sel_data; gchar **strv = NULL; - const gchar *account_id = NULL; - const gchar *contact_id = NULL; - gchar *new_group = NULL; - gchar *old_group = NULL; - DndGetContactData *data; gboolean is_row; - gboolean success = TRUE; + GtkTreeViewDropPosition position; + GtkTreePath *path; priv = GET_PRIV (view); model = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); + sel_data = (const gchar*) gtk_selection_data_get_data (selection); + DEBUG ("Received %s%s drag & drop contact from roster with id:'%s'", + context->action == GDK_ACTION_MOVE ? "move" : "", + context->action == GDK_ACTION_COPY ? "copy" : "", + sel_data); + /* Get destination group information. */ is_row = gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (view), x, y, &path, &position); + if (!is_row) { + success = FALSE; + goto OUT; + } - if (is_row) { + if (info == DND_DRAG_TYPE_CONTACT_ID || info == DND_DRAG_TYPE_STRING) { + TpAccountManager *account_manager; + EmpathyTpContactFactory *factory = NULL; + TpAccount *account = NULL; + const gchar *account_id = NULL; + const gchar *contact_id = NULL; + gchar *new_group = NULL; + gchar *old_group = NULL; + DndGetContactData *data; new_group = empathy_contact_list_store_get_parent_group (model, - path, NULL); + path, NULL); gtk_tree_path_free (path); - } + path = NULL; + + /* Get source group information. */ + if (priv->drag_row) { + path = gtk_tree_row_reference_get_path (priv->drag_row); + if (path) { + old_group = empathy_contact_list_store_get_parent_group ( + model, path, NULL); + gtk_tree_path_free (path); + path = NULL; + } + } - /* Get source group information. */ - if (priv->drag_row) { - path = gtk_tree_row_reference_get_path (priv->drag_row); - if (path) { - old_group = empathy_contact_list_store_get_parent_group ( - model, path, NULL); - gtk_tree_path_free (path); + if (!tp_strdiff (old_group, new_group)) { + g_free (new_group); + g_free (old_group); + goto OUT; } - } - if (!tp_strdiff (old_group, new_group)) { - g_free (new_group); - g_free (old_group); - goto OUT; - } + /* FIXME: should probably make sure the account manager is prepared + * before calling _ensure_account on it. See bug 600115. */ + account_manager = tp_account_manager_dup (); + strv = g_strsplit (sel_data, ":", 2); + 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) { + TpConnection *connection; + + connection = tp_account_get_connection (account); + if (connection) { + factory = empathy_tp_contact_factory_dup_singleton (connection); + } + g_object_unref (account_manager); + + if (!factory) { + DEBUG ("Failed to get factory for account '%s'", account_id); + success = FALSE; + g_free (new_group); + g_free (old_group); + goto OUT; + } + } - id = (const gchar*) gtk_selection_data_get_data (selection); - DEBUG ("Received %s%s drag & drop contact from roster with id:'%s'", - context->action == GDK_ACTION_MOVE ? "move" : "", - context->action == GDK_ACTION_COPY ? "copy" : "", - id); - - /* FIXME: should probably make sure the account manager is prepared - * before calling _ensure_account on it. See bug 600115. */ - account_manager = tp_account_manager_dup (); - strv = g_strsplit (id, ":", 2); - 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) { - TpConnection *connection; + data = g_slice_new0 (DndGetContactData); + data->new_group = new_group; + data->old_group = old_group; + data->action = context->action; - connection = tp_account_get_connection (account); - if (connection) { - factory = empathy_tp_contact_factory_dup_singleton (connection); - } - } - g_object_unref (account_manager); + /* FIXME: We should probably wait for the cb before calling + * gtk_drag_finish */ + empathy_tp_contact_factory_get_from_id (factory, contact_id, + contact_list_view_drag_got_contact, + data, (GDestroyNotify) contact_list_view_dnd_get_contact_free, + G_OBJECT (view)); - if (!factory) { - DEBUG ("Failed to get factory for account '%s'", account_id); - success = FALSE; - g_free (new_group); - g_free (old_group); - goto OUT; + g_object_unref (factory); } + else if (info == DND_DRAG_TYPE_URI_LIST) { + GtkTreeIter iter; + const gchar *nl; + gchar *uri; + GFile *file; + EmpathyContact *contact; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, + -1); + if (!contact) { + success = FALSE; + goto OUT; + } - data = g_slice_new0 (DndGetContactData); - data->new_group = new_group; - data->old_group = old_group; - data->action = context->action; + nl = strstr (sel_data, "\r\n"); + if (!nl) { + nl = strchr (sel_data, '\n'); + } + if (nl) { + uri = g_strndup (sel_data, nl - sel_data); + file = g_file_new_for_uri (uri); + g_free (uri); + } + else { + file = g_file_new_for_uri (sel_data); + } - /* FIXME: We should probably wait for the cb before calling - * gtk_drag_finish */ - empathy_tp_contact_factory_get_from_id (factory, contact_id, - contact_list_view_drag_got_contact, - data, (GDestroyNotify) contact_list_view_dnd_get_contact_free, - G_OBJECT (view)); + empathy_send_file (contact, file); - g_object_unref (factory); + g_object_unref (file); + gtk_drag_finish (context, TRUE, FALSE, time_); + } OUT: + gtk_tree_path_free (path); g_strfreev (strv); gtk_drag_finish (context, success, FALSE, GDK_CURRENT_TIME); } @@ -363,12 +408,25 @@ contact_list_view_drag_motion (GtkWidget *widget, gint y, guint time_) { + EmpathyContactListViewPriv *priv; + GtkTreeModel *model; + static GtkTargetList *file_targets = NULL; + GdkAtom target; + GtkTreeIter iter; static DragMotionData *dm = NULL; GtkTreePath *path; gboolean is_row; gboolean is_different = FALSE; gboolean cleanup = TRUE; - int action = 0; + gboolean retval = TRUE; + + priv = GET_PRIV (EMPATHY_CONTACT_LIST_VIEW (widget)); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + + if (file_targets == NULL) { + file_targets = gtk_target_list_new (drag_types_dest_file, + G_N_ELEMENTS (drag_types_dest_file)); + } is_row = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), x, @@ -387,15 +445,51 @@ contact_list_view_drag_motion (GtkWidget *widget, cleanup &= FALSE; } - if (context->actions == GDK_ACTION_COPY) { - action = context->suggested_action; - } else if (context->actions & GDK_ACTION_MOVE) { - action = GDK_ACTION_MOVE; + if (path == NULL) { + gdk_drag_status (context, 0, time_); + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0); + return FALSE; + } + target = gtk_drag_dest_find_target (widget, context, file_targets); + gtk_tree_model_get_iter (model, &iter, path); + + if (target == GDK_NONE) { + gboolean is_group; + gtk_tree_model_get (model, &iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + -1); + if (is_group) { + gdk_drag_status (context, GDK_ACTION_MOVE, time_); + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + path, + GTK_TREE_VIEW_DROP_INTO_OR_BEFORE); + } + else { + gdk_drag_status (context, 0, time_); + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0); + retval = FALSE; + } + } + else { + EmpathyContact *contact; + gtk_tree_model_get (model, &iter, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, + -1); + if (contact) { + gdk_drag_status (context, GDK_ACTION_COPY, time_); + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + path, + GTK_TREE_VIEW_DROP_INTO_OR_BEFORE); + } + else { + gdk_drag_status (context, 0, time_); + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0); + retval = FALSE; + } } - gdk_drag_status (context, action, time_); if (!is_different && !cleanup) { - return TRUE; + return retval; } if (dm) { @@ -420,7 +514,7 @@ contact_list_view_drag_motion (GtkWidget *widget, dm); } - return TRUE; + return retval; } static void @@ -973,7 +1067,7 @@ contact_list_view_setup (EmpathyContactListView *view) /* Setup view */ g_object_set (view, "headers-visible", FALSE, - "reorderable", TRUE, + "reorderable", FALSE, "show-expanders", FALSE, NULL); diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index 36f31b0b1..a58d4f65c 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -1438,9 +1438,9 @@ chat_window_drag_data_received (GtkWidget *widget, EmpathyChatWindowPriv *priv; EmpathyContact *contact; const gchar *data; - GFile *file; - gchar *nl; + const gchar *nl; gchar *uri; + GFile *file; priv = GET_PRIV (window); contact = empathy_chat_get_remote_contact (priv->current_chat); -- cgit v1.2.3 From 18be92ecfb97887044867643e130ee0ee7c6b3f8 Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Thu, 17 Sep 2009 17:11:11 -0500 Subject: Allow contacts to be dragged anywhere in a group, or dragged to the non-group --- libempathy-gtk/empathy-contact-list-view.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c index 04a448817..78e1f9020 100644 --- a/libempathy-gtk/empathy-contact-list-view.c +++ b/libempathy-gtk/empathy-contact-list-view.c @@ -454,20 +454,35 @@ contact_list_view_drag_motion (GtkWidget *widget, gtk_tree_model_get_iter (model, &iter, path); if (target == GDK_NONE) { - gboolean is_group; + GtkTreeIter group_iter; + gboolean is_group; + GtkTreePath *group_path; gtk_tree_model_get (model, &iter, EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, -1); + if (is_group) { + group_iter = iter; + } + else { + if (gtk_tree_model_iter_parent (model, &group_iter, &iter)) + gtk_tree_model_get (model, &group_iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + -1); + } if (is_group) { gdk_drag_status (context, GDK_ACTION_MOVE, time_); + group_path = gtk_tree_model_get_path (model, &group_iter); gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), - path, + group_path, GTK_TREE_VIEW_DROP_INTO_OR_BEFORE); + gtk_tree_path_free (group_path); } else { - gdk_drag_status (context, 0, time_); - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0); - retval = FALSE; + group_path = gtk_tree_path_new_first (); + gdk_drag_status (context, GDK_ACTION_MOVE, time_); + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + group_path, + GTK_TREE_VIEW_DROP_BEFORE); } } else { -- cgit v1.2.3 From 85e7ae3c96ccc9734f5f09371cd44817ff751ce6 Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Fri, 18 Sep 2009 14:11:44 -0500 Subject: Setting reorderable on the contact list view to get row previews as drag icons This is a hack. There's a comment explaining the hack. Read it. --- libempathy-gtk/empathy-contact-list-view.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c index 78e1f9020..eb18acf0b 100644 --- a/libempathy-gtk/empathy-contact-list-view.c +++ b/libempathy-gtk/empathy-contact-list-view.c @@ -1080,9 +1080,14 @@ contact_list_view_setup (EmpathyContactListView *view) GTK_TREE_MODEL (priv->store)); /* Setup view */ + /* Setting reorderable is a hack that gets us row previews as drag icons + for free. We override all the drag handlers. It's tricky to get the + position of the drag icon right in drag_begin. GtkTreeView has special + voodoo for it, so we let it do the voodoo that he do. + */ g_object_set (view, "headers-visible", FALSE, - "reorderable", FALSE, + "reorderable", TRUE, "show-expanders", FALSE, NULL); -- cgit v1.2.3 From cfb4283d66005aa2f12b15ce0d5aefde965d815a Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Mon, 5 Oct 2009 13:37:23 -0500 Subject: Split contact_list_view_drag_data_received into smaller functions --- libempathy-gtk/empathy-contact-list-view.c | 259 ++++++++++++++++------------- 1 file changed, 141 insertions(+), 118 deletions(-) diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c index eb18acf0b..24793b457 100644 --- a/libempathy-gtk/empathy-contact-list-view.c +++ b/libempathy-gtk/empathy-contact-list-view.c @@ -238,6 +238,135 @@ contact_list_view_drag_got_contact (EmpathyTpContactFactory *factory, } } +static gboolean +contact_list_view_contact_drag_received (GtkWidget *view, + GdkDragContext *context, + GtkTreeModel *model, + GtkTreePath *path, + GtkSelectionData *selection) +{ + EmpathyContactListViewPriv *priv; + TpAccountManager *account_manager; + EmpathyTpContactFactory *factory = NULL; + TpAccount *account; + DndGetContactData *data; + GtkTreePath *source_path; + const gchar *sel_data; + gchar **strv = NULL; + const gchar *account_id = NULL; + const gchar *contact_id = NULL; + gchar *new_group = NULL; + gchar *old_group = NULL; + gboolean success = TRUE; + + priv = GET_PRIV (view); + + sel_data = (const gchar *) gtk_selection_data_get_data (selection); + new_group = empathy_contact_list_store_get_parent_group (model, + path, NULL); + + /* Get source group information. */ + if (priv->drag_row) { + source_path = gtk_tree_row_reference_get_path (priv->drag_row); + if (source_path) { + old_group = empathy_contact_list_store_get_parent_group ( + model, source_path, NULL); + gtk_tree_path_free (source_path); + } + } + + if (!tp_strdiff (old_group, new_group)) { + g_free (new_group); + g_free (old_group); + return FALSE; + } + + account_manager = tp_account_manager_dup (); + strv = g_strsplit (sel_data, ":", 2); + 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) { + TpConnection *connection; + + connection = tp_account_get_connection (account); + if (connection) { + factory = empathy_tp_contact_factory_dup_singleton (connection); + } + } + g_object_unref (account_manager); + + if (!factory) { + DEBUG ("Failed to get factory for account '%s'", account_id); + success = FALSE; + g_free (new_group); + g_free (old_group); + return FALSE; + } + + data = g_slice_new0 (DndGetContactData); + data->new_group = new_group; + data->old_group = old_group; + data->action = context->action; + + /* FIXME: We should probably wait for the cb before calling + * gtk_drag_finish */ + empathy_tp_contact_factory_get_from_id (factory, contact_id, + contact_list_view_drag_got_contact, + data, (GDestroyNotify) contact_list_view_dnd_get_contact_free, + G_OBJECT (view)); + g_strfreev (strv); + g_object_unref (factory); + + return TRUE; +} + +static gboolean +contact_list_view_file_drag_received (GtkWidget *view, + GdkDragContext *context, + GtkTreeModel *model, + GtkTreePath *path, + GtkSelectionData *selection) +{ + GtkTreeIter iter; + const gchar *sel_data; + const gchar *nl; + gchar *uri; + GFile *file; + EmpathyContact *contact; + + sel_data = (const gchar *) gtk_selection_data_get_data (selection); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, + -1); + if (!contact) { + return FALSE; + } + + nl = strstr (sel_data, "\r\n"); + if (!nl) { + nl = strchr (sel_data, '\n'); + } + if (nl) { + uri = g_strndup (sel_data, nl - sel_data); + file = g_file_new_for_uri (uri); + g_free (uri); + } + else { + file = g_file_new_for_uri (sel_data); + } + + empathy_send_file (contact, file); + + g_object_unref (file); + + return TRUE; +} + static void contact_list_view_drag_data_received (GtkWidget *view, GdkDragContext *context, @@ -247,24 +376,14 @@ contact_list_view_drag_data_received (GtkWidget *view, guint info, guint time_) { - EmpathyContactListViewPriv *priv; GtkTreeModel *model; - gboolean success = TRUE; - const gchar *sel_data; - gchar **strv = NULL; gboolean is_row; GtkTreeViewDropPosition position; GtkTreePath *path; + gboolean success = TRUE; - priv = GET_PRIV (view); model = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); - sel_data = (const gchar*) gtk_selection_data_get_data (selection); - DEBUG ("Received %s%s drag & drop contact from roster with id:'%s'", - context->action == GDK_ACTION_MOVE ? "move" : "", - context->action == GDK_ACTION_COPY ? "copy" : "", - sel_data); - /* Get destination group information. */ is_row = gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (view), x, @@ -273,119 +392,23 @@ contact_list_view_drag_data_received (GtkWidget *view, &position); if (!is_row) { success = FALSE; - goto OUT; } - - if (info == DND_DRAG_TYPE_CONTACT_ID || info == DND_DRAG_TYPE_STRING) { - TpAccountManager *account_manager; - EmpathyTpContactFactory *factory = NULL; - TpAccount *account = NULL; - const gchar *account_id = NULL; - const gchar *contact_id = NULL; - gchar *new_group = NULL; - gchar *old_group = NULL; - DndGetContactData *data; - new_group = empathy_contact_list_store_get_parent_group (model, - path, NULL); - gtk_tree_path_free (path); - path = NULL; - - /* Get source group information. */ - if (priv->drag_row) { - path = gtk_tree_row_reference_get_path (priv->drag_row); - if (path) { - old_group = empathy_contact_list_store_get_parent_group ( - model, path, NULL); - gtk_tree_path_free (path); - path = NULL; - } - } - - if (!tp_strdiff (old_group, new_group)) { - g_free (new_group); - g_free (old_group); - goto OUT; - } - - /* FIXME: should probably make sure the account manager is prepared - * before calling _ensure_account on it. See bug 600115. */ - account_manager = tp_account_manager_dup (); - strv = g_strsplit (sel_data, ":", 2); - 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) { - TpConnection *connection; - - connection = tp_account_get_connection (account); - if (connection) { - factory = empathy_tp_contact_factory_dup_singleton (connection); - } - g_object_unref (account_manager); - - if (!factory) { - DEBUG ("Failed to get factory for account '%s'", account_id); - success = FALSE; - g_free (new_group); - g_free (old_group); - goto OUT; - } - } - - data = g_slice_new0 (DndGetContactData); - data->new_group = new_group; - data->old_group = old_group; - data->action = context->action; - - /* FIXME: We should probably wait for the cb before calling - * gtk_drag_finish */ - empathy_tp_contact_factory_get_from_id (factory, contact_id, - contact_list_view_drag_got_contact, - data, (GDestroyNotify) contact_list_view_dnd_get_contact_free, - G_OBJECT (view)); - - g_object_unref (factory); + else if (info == DND_DRAG_TYPE_CONTACT_ID || info == DND_DRAG_TYPE_STRING) { + success = contact_list_view_contact_drag_received (view, + context, + model, + path, + selection); } else if (info == DND_DRAG_TYPE_URI_LIST) { - GtkTreeIter iter; - const gchar *nl; - gchar *uri; - GFile *file; - EmpathyContact *contact; - - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_model_get (model, &iter, - EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, - -1); - if (!contact) { - success = FALSE; - goto OUT; - } - - nl = strstr (sel_data, "\r\n"); - if (!nl) { - nl = strchr (sel_data, '\n'); - } - if (nl) { - uri = g_strndup (sel_data, nl - sel_data); - file = g_file_new_for_uri (uri); - g_free (uri); - } - else { - file = g_file_new_for_uri (sel_data); - } - - empathy_send_file (contact, file); - - g_object_unref (file); - gtk_drag_finish (context, TRUE, FALSE, time_); + success = contact_list_view_file_drag_received (view, + context, + model, + path, + selection); } -OUT: gtk_tree_path_free (path); - g_strfreev (strv); gtk_drag_finish (context, success, FALSE, GDK_CURRENT_TIME); } -- cgit v1.2.3 From f167ec2d92a526d19f1faf8320d9ff975ae1e502 Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Mon, 5 Oct 2009 14:22:32 -0500 Subject: Utilitiy function to send files from a URI list, for dnd implementations --- libempathy-gtk/empathy-chat.c | 5 +++++ libempathy-gtk/empathy-contact-list-view.c | 20 ++------------------ libempathy-gtk/empathy-ui-utils.c | 30 ++++++++++++++++++++++++++++++ libempathy-gtk/empathy-ui-utils.h | 2 ++ src/empathy-chat-window.c | 27 ++------------------------- 5 files changed, 41 insertions(+), 43 deletions(-) diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c index c83649a04..52806e07c 100644 --- a/libempathy-gtk/empathy-chat.c +++ b/libempathy-gtk/empathy-chat.c @@ -2007,6 +2007,11 @@ chat_create_ui (EmpathyChat *chat) /* Add message view. */ chat->view = empathy_theme_manager_create_view (empathy_theme_manager_get ()); + /* If this is a GtkTextView, it's set as a drag destination for text/plain + and other types, even though it's non-editable and doesn't accept any + drags. This steals drag motion for anything inside the scrollbars, + making drag destinations on chat windows far less useful. + */ gtk_drag_dest_unset (GTK_WIDGET (chat->view)); g_signal_connect (chat->view, "focus_in_event", G_CALLBACK (chat_text_view_focus_in_event_cb), diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c index 24793b457..3e1464654 100644 --- a/libempathy-gtk/empathy-contact-list-view.c +++ b/libempathy-gtk/empathy-contact-list-view.c @@ -332,9 +332,6 @@ contact_list_view_file_drag_received (GtkWidget *view, { GtkTreeIter iter; const gchar *sel_data; - const gchar *nl; - gchar *uri; - GFile *file; EmpathyContact *contact; sel_data = (const gchar *) gtk_selection_data_get_data (selection); @@ -347,22 +344,9 @@ contact_list_view_file_drag_received (GtkWidget *view, return FALSE; } - nl = strstr (sel_data, "\r\n"); - if (!nl) { - nl = strchr (sel_data, '\n'); - } - if (nl) { - uri = g_strndup (sel_data, nl - sel_data); - file = g_file_new_for_uri (uri); - g_free (uri); - } - else { - file = g_file_new_for_uri (sel_data); - } - - empathy_send_file (contact, file); + empathy_send_file_from_uri_list (contact, sel_data); - g_object_unref (file); + g_object_unref (contact); return TRUE; } diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c index 411a76640..9f4182ce1 100644 --- a/libempathy-gtk/empathy-ui-utils.c +++ b/libempathy-gtk/empathy-ui-utils.c @@ -1473,6 +1473,36 @@ empathy_send_file (EmpathyContact *contact, GFile *file) g_object_unref (factory); } +void +empathy_send_file_from_uri_list (EmpathyContact *contact, const gchar *uri_list) +{ + const gchar *nl; + GFile *file; + + /* Only handle a single file for now. It would be wicked cool to be + able to do multiple files, offering to zip them or whatever like + nautilus-sendto does. Note that text/uri-list is defined to have + each line terminated by \r\n, but we can be tolerant of applications + that only use \n or don't terminate single-line entries. + */ + nl = strstr (uri_list, "\r\n"); + if (!nl) { + nl = strchr (uri_list, '\n'); + } + if (nl) { + gchar *uri = g_strndup (uri_list, nl - uri_list); + file = g_file_new_for_uri (uri); + g_free (uri); + } + else { + file = g_file_new_for_uri (uri_list); + } + + empathy_send_file (contact, file); + + g_object_unref (file); +} + static void file_manager_send_file_response_cb (GtkDialog *widget, gint response_id, diff --git a/libempathy-gtk/empathy-ui-utils.h b/libempathy-gtk/empathy-ui-utils.h index 0f453ddbc..e0c0904b0 100644 --- a/libempathy-gtk/empathy-ui-utils.h +++ b/libempathy-gtk/empathy-ui-utils.h @@ -112,6 +112,8 @@ GtkWidget * empathy_link_button_new (const gchar *url, void empathy_send_file (EmpathyContact *contact, GFile *file); +void empathy_send_file_from_uri_list (EmpathyContact *contact, + const gchar *uri_list); void empathy_send_file_with_file_chooser (EmpathyContact *contact); void empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler); diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index a58d4f65c..2c6acf75e 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -1438,9 +1438,6 @@ chat_window_drag_data_received (GtkWidget *widget, EmpathyChatWindowPriv *priv; EmpathyContact *contact; const gchar *data; - const gchar *nl; - gchar *uri; - GFile *file; priv = GET_PRIV (window); contact = empathy_chat_get_remote_contact (priv->current_chat); @@ -1450,29 +1447,9 @@ chat_window_drag_data_received (GtkWidget *widget, return; } - /* Only handle a single file for new. It would be wicked cool to be - able to do multiple files, offering to zip them or whatever like - nautilus-sendto does. Note that text/uri-list is defined to have - each line terminated by \r\n, but we can be tolerant of applications - that only use \n or don't terminate single-line entries. - */ - data = (const gchar*) gtk_selection_data_get_data (selection); - nl = strstr (data, "\r\n"); - if (!nl) { - nl = strchr (data, '\n'); - } - if (nl) { - uri = g_strndup (data, nl - data); - file = g_file_new_for_uri (uri); - g_free (uri); - } - else { - file = g_file_new_for_uri (data); - } - - empathy_send_file (contact, file); + data = (const gchar *) gtk_selection_data_get_data (selection); + empathy_send_file_from_uri_list (contact, data); - g_object_unref (file); gtk_drag_finish (context, TRUE, FALSE, time); } else if (info == DND_DRAG_TYPE_TAB) { -- cgit v1.2.3 From 43390cb381e1dd4b68771c3c285bc1f5326a5b74 Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Mon, 5 Oct 2009 14:48:24 -0500 Subject: Fixed leak in drag motion and added comments for bug #595226 --- libempathy-gtk/empathy-contact-list-view.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c index 3e1464654..0dc5cba14 100644 --- a/libempathy-gtk/empathy-contact-list-view.c +++ b/libempathy-gtk/empathy-contact-list-view.c @@ -453,7 +453,10 @@ contact_list_view_drag_motion (GtkWidget *widget, } if (path == NULL) { - gdk_drag_status (context, 0, time_); + /* Coordinates don't point to an actual row, so make sure the pointer + and highlighting don't indicate that a drag is possible. + */ + gdk_drag_status (context, GDK_ACTION_DEFAULT, time_); gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0); return FALSE; } @@ -461,6 +464,13 @@ contact_list_view_drag_motion (GtkWidget *widget, gtk_tree_model_get_iter (model, &iter, path); if (target == GDK_NONE) { + /* If target == GDK_NONE, then we don't have a target that can be + dropped on a contact. This means a contact drag. If we're + pointing to a group, highlight it. Otherwise, if the contact + we're pointing to is in a group, highlight that. Otherwise, + set the drag position to before the first row for a drag into + the "non-group" at the top. + */ GtkTreeIter group_iter; gboolean is_group; GtkTreePath *group_path; @@ -493,6 +503,9 @@ contact_list_view_drag_motion (GtkWidget *widget, } } else { + /* This is a file drag, and it can only be dropped on contacts, + not groups. + */ EmpathyContact *contact; gtk_tree_model_get (model, &iter, EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, @@ -502,6 +515,7 @@ contact_list_view_drag_motion (GtkWidget *widget, gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), path, GTK_TREE_VIEW_DROP_INTO_OR_BEFORE); + g_object_unref (contact); } else { gdk_drag_status (context, 0, time_); -- cgit v1.2.3 From bbe8c4eadaf0aadd45b3f70cb55dfad44bd4ba49 Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Mon, 5 Oct 2009 14:52:46 -0500 Subject: [empathy-chat-window] Documented MOVE/COPY selection in drag_motion --- src/empathy-chat-window.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index 2c6acf75e..a6bd5f259 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -1344,6 +1344,10 @@ chat_window_drag_motion (GtkWidget *widget, } target = gtk_drag_dest_find_target (widget, context, list); + /* If target != GDK_NONE, this target type is a type we should move + instead of copy. That's a notebook tab. Other drag types, such + as files or contacts, use copy. + */ if (target == GDK_NONE) { gdk_drag_status (context, GDK_ACTION_COPY, time); } -- cgit v1.2.3 From adade63c475294db24a422c3a0e5f6b3879641b6 Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Mon, 5 Oct 2009 15:09:46 -0500 Subject: Move GtkTargetLists into priv so they're no longer static --- libempathy-gtk/empathy-contact-list-view.c | 16 +++++++++------- src/empathy-chat-window.c | 19 +++++++++++++------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c index 0dc5cba14..0a9b260f8 100644 --- a/libempathy-gtk/empathy-contact-list-view.c +++ b/libempathy-gtk/empathy-contact-list-view.c @@ -65,6 +65,7 @@ typedef struct { EmpathyContactListFeatureFlags list_features; EmpathyContactFeatureFlags contact_features; GtkWidget *tooltip_widget; + GtkTargetList *file_targets; } EmpathyContactListViewPriv; typedef struct { @@ -417,7 +418,6 @@ contact_list_view_drag_motion (GtkWidget *widget, { EmpathyContactListViewPriv *priv; GtkTreeModel *model; - static GtkTargetList *file_targets = NULL; GdkAtom target; GtkTreeIter iter; static DragMotionData *dm = NULL; @@ -430,11 +430,6 @@ contact_list_view_drag_motion (GtkWidget *widget, priv = GET_PRIV (EMPATHY_CONTACT_LIST_VIEW (widget)); model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); - if (file_targets == NULL) { - file_targets = gtk_target_list_new (drag_types_dest_file, - G_N_ELEMENTS (drag_types_dest_file)); - } - is_row = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), x, y, @@ -460,7 +455,7 @@ contact_list_view_drag_motion (GtkWidget *widget, gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0); return FALSE; } - target = gtk_drag_dest_find_target (widget, context, file_targets); + target = gtk_drag_dest_find_target (widget, context, priv->file_targets); gtk_tree_model_get_iter (model, &iter, path); if (target == GDK_NONE) { @@ -1266,6 +1261,9 @@ contact_list_view_finalize (GObject *object) if (priv->tooltip_widget) { gtk_widget_destroy (priv->tooltip_widget); } + if (priv->file_targets) { + gtk_target_list_unref (priv->file_targets); + } G_OBJECT_CLASS (empathy_contact_list_view_parent_class)->finalize (object); } @@ -1396,6 +1394,10 @@ empathy_contact_list_view_init (EmpathyContactListView *view) empathy_contact_list_store_row_separator_func, NULL, NULL); + /* Set up drag target lists. */ + priv->file_targets = gtk_target_list_new (drag_types_dest_file, + G_N_ELEMENTS (drag_types_dest_file)); + /* Connect to tree view signals rather than override. */ g_signal_connect (view, "button-press-event", G_CALLBACK (contact_list_view_button_press_event_cb), diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index a6bd5f259..cd2aeaffe 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -80,6 +80,8 @@ typedef struct { NotifyNotification *notification; NotificationData *notification_data; + GtkTargetList *move_targets; + /* Menu items. */ GtkUIManager *ui_manager; GtkAction *menu_conv_insert_smiley; @@ -1335,15 +1337,12 @@ chat_window_drag_motion (GtkWidget *widget, guint time, EmpathyChatWindow *window) { - static GtkTargetList *list = NULL; GdkAtom target; + EmpathyChatWindowPriv *priv; - if (list == NULL) { - list = gtk_target_list_new (drag_types_dest_move, - G_N_ELEMENTS (drag_types_dest_move)); - } + priv = GET_PRIV (window); - target = gtk_drag_dest_find_target (widget, context, list); + target = gtk_drag_dest_find_target (widget, context, priv->move_targets); /* If target != GDK_NONE, this target type is a type we should move instead of copy. That's a notebook tab. Other drag types, such as files or contacts, use copy. @@ -1518,6 +1517,10 @@ chat_window_finalize (GObject *object) } } + if (priv->move_targets) { + gtk_target_list_unref (priv->move_targets); + } + chat_windows = g_list_remove (chat_windows, window); gtk_widget_destroy (priv->dialog); @@ -1629,6 +1632,10 @@ empathy_chat_window_init (EmpathyChatWindow *window) g_object_unref (accel_group); + /* Set up drag target lists */ + priv->move_targets = gtk_target_list_new (drag_types_dest_move, + G_N_ELEMENTS (drag_types_dest_move)); + /* Set up smiley menu */ smiley_manager = empathy_smiley_manager_dup_singleton (); submenu = empathy_smiley_menu_new (smiley_manager, -- cgit v1.2.3 From bcc824a40f40c86e87cfdd84c00652ada111fdaa Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Tue, 6 Oct 2009 17:56:47 -0500 Subject: [empathy-chat-window] Cleanup DND, don't accept drags to offline contacts --- src/empathy-chat-window.c | 82 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index cd2aeaffe..0aea2a7c7 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -80,7 +80,8 @@ typedef struct { NotifyNotification *notification; NotificationData *notification_data; - GtkTargetList *move_targets; + GtkTargetList *contact_targets; + GtkTargetList *file_targets; /* Menu items. */ GtkUIManager *ui_manager; @@ -118,8 +119,12 @@ static const GtkTargetEntry drag_types_dest[] = { { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST }, }; -static const GtkTargetEntry drag_types_dest_move[] = { - { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB }, +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); @@ -1329,7 +1334,7 @@ chat_window_focus_in_event_cb (GtkWidget *widget, return FALSE; } -static void +static gboolean chat_window_drag_motion (GtkWidget *widget, GdkDragContext *context, int x, @@ -1339,20 +1344,51 @@ chat_window_drag_motion (GtkWidget *widget, { GdkAtom target; EmpathyChatWindowPriv *priv; + GdkAtom dest_target; priv = GET_PRIV (window); - target = gtk_drag_dest_find_target (widget, context, priv->move_targets); - /* If target != GDK_NONE, this target type is a type we should move - instead of copy. That's a notebook tab. Other drag types, such - as files or contacts, use copy. - */ - if (target == GDK_NONE) { + 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); + if (!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; } - else { - gdk_drag_status (context, GDK_ACTION_MOVE, time); + + 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; } + + /* Otherwise, it must be a notebook tab drag. Set to MOVE. */ + gdk_drag_status (context, GDK_ACTION_MOVE, time); + return TRUE; } static void @@ -1517,8 +1553,11 @@ chat_window_finalize (GObject *object) } } - if (priv->move_targets) { - gtk_target_list_unref (priv->move_targets); + 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); @@ -1633,8 +1672,10 @@ empathy_chat_window_init (EmpathyChatWindow *window) g_object_unref (accel_group); /* Set up drag target lists */ - priv->move_targets = gtk_target_list_new (drag_types_dest_move, - G_N_ELEMENTS (drag_types_dest_move)); + 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 (); @@ -1678,10 +1719,11 @@ empathy_chat_window_init (EmpathyChatWindow *window) G_N_ELEMENTS (drag_types_dest), GDK_ACTION_MOVE | GDK_ACTION_COPY); - g_signal_connect (priv->notebook, - "drag-motion", - G_CALLBACK (chat_window_drag_motion), - window); + /* 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), -- cgit v1.2.3 From 89bc747b15ff519b81b8b153bcf94c8cd19245b1 Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Wed, 7 Oct 2009 10:47:41 -0500 Subject: [empathy-contact-list-view] Don't accept file drags to offline/non-FT contacts --- libempathy-gtk/empathy-contact-list-view.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c index 0a9b260f8..6b25cc10f 100644 --- a/libempathy-gtk/empathy-contact-list-view.c +++ b/libempathy-gtk/empathy-contact-list-view.c @@ -505,7 +505,9 @@ contact_list_view_drag_motion (GtkWidget *widget, gtk_tree_model_get (model, &iter, EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, -1); - if (contact) { + if (contact != NULL && + empathy_contact_is_online (contact) && + (empathy_contact_get_capabilities (contact) & EMPATHY_CAPABILITIES_FT)) { gdk_drag_status (context, GDK_ACTION_COPY, time_); gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), path, -- cgit v1.2.3 From 9ec54fdc072173c6676ec2d8ea0b8eabe6936cef Mon Sep 17 00:00:00 2001 From: Shaun McCance Date: Tue, 24 Nov 2009 11:26:45 -0600 Subject: Some improvements to file drag-and-drop based on comments on #595226 --- src/empathy-chat-window.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c index 0aea2a7c7..fb04e7c3e 100644 --- a/src/empathy-chat-window.c +++ b/src/empathy-chat-window.c @@ -1339,7 +1339,7 @@ chat_window_drag_motion (GtkWidget *widget, GdkDragContext *context, int x, int y, - guint time, + guint time_, EmpathyChatWindow *window) { GdkAtom target; @@ -1362,16 +1362,21 @@ chat_window_drag_motion (GtkWidget *widget, 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; } @@ -1382,12 +1387,12 @@ chat_window_drag_motion (GtkWidget *widget, 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); + gdk_drag_status (context, GDK_ACTION_MOVE, time_); return TRUE; } @@ -1481,15 +1486,18 @@ chat_window_drag_data_received (GtkWidget *widget, 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; -- cgit v1.2.3