dnd: use GtkPlacesSidebar drop targets hints

Currently the dnd on the sidebar is only triggered when hovering
above the sidebar itself. However we would like to give some
feedback all along on the dnd operation.
For that GtkPlacesSidebar has set_drop_targets_visible public API,
which was implemented a few months ago and the GtkFileChooser is
already using.

I just forgot to implement the support for it on Nautilus... even if
the original work was done for Nautilus, since users will probably
use dnd more on nautilus than on the file chooser.

I'm not entirely happy with the implementation, since it uses custom
functions to access the drag data, given that we need them at drag-begin
time and in random places on the code, since nautilus is doing all the dnd
work manually and on different places.
The final result is that drag and drop is still managed mostly on its own
widgets, in this case list-view and canvas-view, and nautilus-dnd manages
a central accessor for dnd in nautilus, in this case requesting depending
on the widget that is the owner of the data, its data through the custom
functions of that particula widget. All other ways I tried to do it entirely
with only gtk_drag_* or gdk_drag_* functions were in vanish if no a complete
refactoring is done, and probably it doesn't worth the effort. Also I
actually separated the list view dnd code as well, so now at least the
pattern to handling dnd on nautilus is more or less consistent.
parent 02af0763
......@@ -32,6 +32,8 @@
#include <config.h>
#include <math.h>
#include <src/nautilus-window.h>
#include "nautilus-canvas-dnd.h"
#include "nautilus-file-dnd.h"
......@@ -528,12 +530,18 @@ drag_end_callback (GtkWidget *widget,
{
NautilusCanvasContainer *container;
NautilusCanvasDndInfo *dnd_info;
NautilusWindow *window;
container = NAUTILUS_CANVAS_CONTAINER (widget);
window = NAUTILUS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (container)));
dnd_info = container->details->dnd_info;
nautilus_drag_destroy_selection_list (dnd_info->drag_info.selection_list);
nautilus_drag_destroy_selection_list (container->details->dnd_source_info->selection_cache);
dnd_info->drag_info.selection_list = NULL;
container->details->dnd_source_info->selection_cache = NULL;
nautilus_window_end_dnd (window, context);
}
static NautilusCanvasIcon *
......@@ -1074,6 +1082,13 @@ nautilus_canvas_container_receive_dropped_icons (NautilusCanvasContainer *contai
container->details->dnd_info->drag_info.selection_list = NULL;
}
NautilusDragInfo *
nautilus_canvas_dnd_get_drag_source_data (NautilusCanvasContainer *container,
GdkDragContext *context)
{
return container->details->dnd_source_info;
}
static void
nautilus_canvas_container_get_drop_action (NautilusCanvasContainer *container,
GdkDragContext *context,
......@@ -1104,8 +1119,9 @@ nautilus_canvas_container_get_drop_action (NautilusCanvasContainer *container,
switch (container->details->dnd_info->drag_info.data_type) {
case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
if (container->details->dnd_info->drag_info.selection_list != NULL) {
nautilus_drag_default_drop_action_for_icons (context, drop_target,
container->details->dnd_info->drag_info.selection_list,
nautilus_drag_default_drop_action_for_icons (context, drop_target,
container->details->dnd_info->drag_info.selection_list,
0,
action);
}
break;
......@@ -1258,12 +1274,14 @@ drag_begin_callback (GtkWidget *widget,
{
NautilusCanvasContainer *container;
NautilusDragInfo *drag_info;
NautilusWindow *window;
cairo_surface_t *surface;
double x1, y1, x2, y2, winx, winy;
int x_offset, y_offset;
int start_x, start_y;
container = NAUTILUS_CANVAS_CONTAINER (widget);
window = NAUTILUS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (container)));
start_x = container->details->dnd_info->drag_info.start_x +
gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
......@@ -1290,6 +1308,10 @@ drag_begin_callback (GtkWidget *widget,
drag_info->selection_cache = nautilus_drag_create_selection_cache (widget,
each_icon_get_data_binder);
container->details->dnd_source_info->selection_cache = nautilus_drag_create_selection_cache (widget,
each_icon_get_data_binder);
nautilus_window_start_dnd (window, context);
}
void
......@@ -1301,11 +1323,14 @@ nautilus_canvas_dnd_begin_drag (NautilusCanvasContainer *container,
int start_y)
{
NautilusCanvasDndInfo *dnd_info;
NautilusDragInfo *dnd_source_info;
g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
g_return_if_fail (event != NULL);
dnd_info = container->details->dnd_info;
container->details->dnd_source_info = g_new0 (NautilusDragInfo, 1);
dnd_source_info = container->details->dnd_source_info;
g_return_if_fail (dnd_info != NULL);
/* Notice that the event is in bin_window coordinates, because of
......@@ -1316,6 +1341,7 @@ nautilus_canvas_dnd_begin_drag (NautilusCanvasContainer *container,
dnd_info->drag_info.start_y = start_y -
gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));
dnd_source_info->source_actions = actions;
/* start the drag */
gtk_drag_begin_with_coordinates (GTK_WIDGET (container),
dnd_info->drag_info.target_list,
......
......@@ -54,4 +54,7 @@ void nautilus_canvas_dnd_begin_drag (NautilusCanvasContainer *conta
int start_y);
void nautilus_canvas_dnd_end_drag (NautilusCanvasContainer *container);
NautilusDragInfo* nautilus_canvas_dnd_get_drag_source_data (NautilusCanvasContainer *container,
GdkDragContext *context);
#endif /* NAUTILUS_CANVAS_DND_H */
......@@ -182,6 +182,7 @@ struct NautilusCanvasContainerDetails {
/* DnD info. */
NautilusCanvasDndInfo *dnd_info;
NautilusDragInfo *dnd_source_info;
/* zoom level */
int zoom_level;
......
......@@ -35,6 +35,8 @@
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <libnautilus-private/nautilus-file-utilities.h>
#include <libnautilus-private/nautilus-canvas-dnd.h>
#include <src/nautilus-list-view-dnd.h>
#include <stdio.h>
#include <string.h>
......@@ -381,10 +383,49 @@ source_is_deletable (GFile *file)
return ret;
}
NautilusDragInfo *
nautilus_drag_get_source_data (GdkDragContext *context)
{
GtkWidget *source_widget;
NautilusDragInfo *source_data;
source_widget = gtk_drag_get_source_widget (context);
if (source_widget == NULL)
return NULL;
if (NAUTILUS_IS_CANVAS_CONTAINER (source_widget)) {
source_data = nautilus_canvas_dnd_get_drag_source_data (NAUTILUS_CANVAS_CONTAINER (source_widget),
context);
} else if (GTK_IS_TREE_VIEW (source_widget)) {
NautilusWindow *window;
NautilusWindowSlot *active_slot;
NautilusView *view;
window = NAUTILUS_WINDOW (gtk_widget_get_toplevel (source_widget));
active_slot = nautilus_window_get_active_slot (window);
view = nautilus_window_slot_get_current_view (active_slot);
if (NAUTILUS_IS_LIST_VIEW (view)) {
source_data = nautilus_list_view_dnd_get_drag_source_data (NAUTILUS_LIST_VIEW (view),
context);
} else {
g_warning ("Got a drag context with a tree view source widget, but current view is not list view");
source_data = NULL;
}
} else {
/* it's a slot or something else */
g_warning ("Requested drag source data from a widget that doesn't support it");
source_data = NULL;
}
return source_data;
}
void
nautilus_drag_default_drop_action_for_icons (GdkDragContext *context,
const char *target_uri_string, const GList *items,
int *action)
const char *target_uri_string,
const GList *items,
guint32 source_actions,
int *action)
{
gboolean same_fs;
gboolean target_is_source_parent;
......@@ -399,7 +440,19 @@ nautilus_drag_default_drop_action_for_icons (GdkDragContext *context,
return;
}
actions = gdk_drag_context_get_actions (context) & (GDK_ACTION_MOVE | GDK_ACTION_COPY);
/* this is needed because of how dnd works. The actions at the time drag-begin
* is done are not set, because they are first set on drag-motion. However,
* for our use case, which is validation with the sidebar for dnd feedback
* when the dnd doesn't have as a destination the sidebar itself, we need
* a way to know the actions at drag-begin time. Either canvas view or
* list view know them when starting the drag, but asking for them here
* would be breaking the current model too much. So instead we rely on the
* caller, which will ask if appropiate to those objects about the actions
* available, instead of relying solely on the context here. */
if (source_actions)
actions = source_actions & (GDK_ACTION_MOVE | GDK_ACTION_COPY);
else
actions = gdk_drag_context_get_actions (context) & (GDK_ACTION_MOVE | GDK_ACTION_COPY);
if (actions == 0) {
/* We can't use copy or move, just go with the suggested action. */
*action = gdk_drag_context_get_suggested_action (context);
......
......@@ -90,6 +90,11 @@ typedef struct {
gboolean waiting_to_autoscroll;
gint64 start_auto_scroll_in;
/* source context actions. Used for peek the actions using a GdkDragContext
* source at drag-begin time when they are not available yet (they become
* available at drag-motion time) */
guint32 source_actions;
} NautilusDragInfo;
typedef void (* NautilusDragEachSelectedItemDataGet) (const char *url,
......@@ -120,6 +125,7 @@ gboolean nautilus_drag_items_on_desktop (const GList *selection_li
void nautilus_drag_default_drop_action_for_icons (GdkDragContext *context,
const char *target_uri,
const GList *items,
guint32 source_actions,
int *action);
GdkDragAction nautilus_drag_default_drop_action_for_netscape_url (GdkDragContext *context);
GdkDragAction nautilus_drag_default_drop_action_for_uri_list (GdkDragContext *context,
......@@ -149,4 +155,6 @@ void nautilus_drag_autoscroll_stop (NautilusDragInfo *drag_info);
gboolean nautilus_drag_selection_includes_special_link (GList *selection_list);
NautilusDragInfo * nautilus_drag_get_source_data (GdkDragContext *context);
#endif
......@@ -513,6 +513,7 @@ get_drop_action (NautilusTreeViewDragDest *dest,
(context,
drop_target,
dest->details->drag_list,
0,
&action);
break;
case NAUTILUS_ICON_DND_NETSCAPE_URL:
......
......@@ -21,33 +21,37 @@
#include "nautilus-list-view-dnd.h"
#include "nautilus-list-view-private.h"
#include "nautilus-dnd.h"
static GtkTargetList * source_target_list = NULL;
static void
drag_info_data_free (NautilusListView *list_view);
static void
drag_data_get_callback (GtkWidget *widget,
GdkDragContext *context,
GtkSelectionData *selection_data,
guint info,
guint time)
guint time,
gpointer user_data)
{
GtkTreeView *tree_view;
GtkTreeModel *model;
GList *selection_cache;
NautilusListView *list_view;
tree_view = GTK_TREE_VIEW (widget);
list_view = NAUTILUS_LIST_VIEW (user_data);
model = gtk_tree_view_get_model (tree_view);
if (model == NULL)
return;
selection_cache = g_object_get_data (G_OBJECT (context), "drag-info");
if (selection_cache == NULL)
if (list_view->details->drag_source_info == NULL ||
list_view->details->drag_source_info->selection_cache == NULL)
return;
nautilus_drag_drag_data_get_from_cache (selection_cache, context, selection_data, info, time);
nautilus_drag_drag_data_get_from_cache (list_view->details->drag_source_info->selection_cache,
context, selection_data, info, time);
}
static cairo_surface_t *
......@@ -159,9 +163,10 @@ drag_begin_callback (GtkWidget *widget,
GdkDragContext *context,
NautilusListView *view)
{
GList *selection_cache;
cairo_surface_t *surface;
NautilusWindow *window;
window = nautilus_files_view_get_window (NAUTILUS_FILES_VIEW (view));
surface = get_drag_surface (view);
if (surface)
{
......@@ -176,20 +181,73 @@ drag_begin_callback (GtkWidget *widget,
view->details->drag_button = 0;
view->details->drag_started = TRUE;
selection_cache = nautilus_drag_create_selection_cache (view,
each_item_get_data_binder);
view->details->drag_source_info->selection_cache = nautilus_drag_create_selection_cache (view,
each_item_get_data_binder);
nautilus_window_start_dnd (window, context);
}
static void
drag_end_callback (GtkWidget *widget,
GdkDragContext *context,
NautilusListView *list_view)
{
NautilusWindow *window;
window = nautilus_files_view_get_window (NAUTILUS_FILES_VIEW (list_view));
nautilus_window_end_dnd (window, context);
drag_info_data_free (list_view);
}
static void
drag_info_data_free (NautilusListView *list_view)
{
nautilus_drag_destroy_selection_list (list_view->details->drag_source_info->selection_cache);
list_view->details->drag_source_info->selection_cache = NULL;
g_free (list_view->details->drag_source_info);
list_view->details->drag_source_info = NULL;
g_object_set_data_full (G_OBJECT (context),
"drag-info",
selection_cache,
(GDestroyNotify)nautilus_drag_destroy_selection_list);
g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_begin_callback, list_view);
g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_data_get_callback, list_view);
g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_end_callback, list_view);
}
NautilusDragInfo *
nautilus_list_view_dnd_get_drag_source_data (NautilusListView *list_view,
GdkDragContext *context)
{
GtkTreeView *tree_view;
GtkTreeModel *model;
tree_view = GTK_TREE_VIEW (list_view->details->tree_view);
model = gtk_tree_view_get_model (tree_view);
if (model == NULL)
return NULL;
if (list_view->details->drag_source_info == NULL ||
list_view->details->drag_source_info->selection_cache == NULL)
return NULL;
return list_view->details->drag_source_info;
}
void
nautilus_list_view_dnd_init (NautilusListView *list_view)
{
if (list_view->details->drag_source_info != NULL)
return;
list_view->details->drag_source_info = g_new0 (NautilusDragInfo, 1);
g_signal_connect_object (list_view->details->tree_view, "drag-begin",
G_CALLBACK (drag_begin_callback), list_view, 0);
g_signal_connect_object (list_view->details->tree_view, "drag-end",
G_CALLBACK (drag_end_callback), list_view, 0);
g_signal_connect_object (list_view->details->tree_view, "drag-data-get",
G_CALLBACK (drag_data_get_callback), list_view, 0);
}
......@@ -209,9 +267,13 @@ nautilus_list_view_dnd_drag_begin (NautilusListView *list_view,
event->x,
event->y))
{
guint32 actions;
actions = GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK;
list_view->details->drag_source_info->source_actions = actions;
gtk_drag_begin_with_coordinates (GTK_WIDGET (list_view->details->tree_view),
source_target_list,
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK,
actions,
list_view->details->drag_button,
(GdkEvent*)event,
-1,
......
......@@ -22,8 +22,13 @@
#include "nautilus-list-view.h"
#include "nautilus-dnd.h"
void nautilus_list_view_dnd_init (NautilusListView *list_view);
gboolean nautilus_list_view_dnd_drag_begin (NautilusListView *list_view,
GdkEventMotion *event);
NautilusDragInfo *
nautilus_list_view_dnd_get_drag_source_data (NautilusListView *list_view,
GdkDragContext *context);
#endif /* NAUTILUS_LIST_VIEW_DND_H */
......@@ -20,6 +20,7 @@
#include "nautilus-list-model.h"
#include "nautilus-tree-view-drag-dest.h"
#include "nautilus-dnd.h"
struct NautilusListViewDetails {
GtkTreeView *tree_view;
......@@ -53,6 +54,7 @@ struct NautilusListViewDetails {
gboolean ignore_button_release;
gboolean row_selected_on_button_down;
gboolean active;
NautilusDragInfo *drag_source_info;
GHashTable *columns;
GtkWidget *column_editor;
......
......@@ -333,6 +333,7 @@ motion_notify_callback (GtkWidget *widget,
}
}
nautilus_list_view_dnd_init (view);
handled = nautilus_list_view_dnd_drag_begin (view, event);
return handled;
......@@ -1621,7 +1622,6 @@ create_and_set_up_tree_view (NautilusListView *view)
"changed",
G_CALLBACK (list_selection_changed_callback), view, 0);
nautilus_list_view_dnd_init (view);
g_signal_connect_object (view->details->tree_view, "motion-notify-event",
G_CALLBACK (motion_notify_callback), view, 0);
g_signal_connect_object (view->details->tree_view, "enter-notify-event",
......
......@@ -214,6 +214,7 @@ slot_proxy_drag_motion (GtkWidget *widget,
if (drag_info->info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) {
nautilus_drag_default_drop_action_for_icons (context, target_uri,
drag_info->data.selection_list,
0,
&action);
} else if (drag_info->info == NAUTILUS_ICON_DND_URI_LIST) {
action = nautilus_drag_default_drop_action_for_uri_list (context, target_uri);
......
......@@ -977,6 +977,24 @@ build_selection_list_from_gfile_list (GList *gfile_list)
return g_list_reverse (result);
}
void
nautilus_window_start_dnd (NautilusWindow *window,
GdkDragContext *context)
{
gtk_places_sidebar_set_drop_targets_visible (GTK_PLACES_SIDEBAR (window->priv->places_sidebar),
TRUE,
context);
}
void
nautilus_window_end_dnd (NautilusWindow *window,
GdkDragContext *context)
{
gtk_places_sidebar_set_drop_targets_visible (GTK_PLACES_SIDEBAR (window->priv->places_sidebar),
FALSE,
context);
}
/* Callback used when the places sidebar needs to know the drag action to suggest */
static GdkDragAction
places_sidebar_drag_action_requested_cb (GtkPlacesSidebar *sidebar,
......@@ -988,17 +1006,28 @@ places_sidebar_drag_action_requested_cb (GtkPlacesSidebar *sidebar,
GList *items;
char *uri;
int action = 0;
items = build_selection_list_from_gfile_list (source_file_list);
NautilusDragInfo *info;
guint32 source_actions;
info = nautilus_drag_get_source_data (context);
if (info != NULL) {
items = info->selection_cache;
source_actions = info->source_actions;
} else {
items = build_selection_list_from_gfile_list (source_file_list);
source_actions = 0;
}
uri = g_file_get_uri (dest_file);
if (g_list_length (items) < 1)
goto out;
nautilus_drag_default_drop_action_for_icons (context, uri, items, &action);
nautilus_drag_default_drop_action_for_icons (context, uri, items, source_actions, &action);
out:
nautilus_drag_destroy_selection_list (items);
if (info == NULL)
nautilus_drag_destroy_selection_list (items);
g_free (uri);
return action;
......
......@@ -145,4 +145,8 @@ void nautilus_window_sync_title (NautilusWindow *window,
void nautilus_window_show_operation_notification (NautilusWindow *window,
gchar *main_label,
GFile *folder_to_open);
void nautilus_window_start_dnd (NautilusWindow *window,
GdkDragContext *context);
void nautilus_window_end_dnd (NautilusWindow *window,
GdkDragContext *context);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment