[nautilus] Cache drag selections on drag begin



commit 1b4eec6e523543c413d7c0da95436f92358ba80a
Author: William Jon McCann <jmccann redhat com>
Date:   Mon Jan 21 17:54:44 2013 -0500

    Cache drag selections on drag begin
    
    Since the contents of the view may change.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=692234

 libnautilus-private/nautilus-canvas-dnd.c |   13 ++-
 libnautilus-private/nautilus-dnd.c        |   73 +++++++++++++++
 libnautilus-private/nautilus-dnd.h        |   10 ++
 src/nautilus-list-model.c                 |  127 ++++----------------------
 src/nautilus-list-model.h                 |    3 +
 src/nautilus-list-view.c                  |  144 ++++++++++++++++-------------
 src/nautilus-places-sidebar.c             |    3 +-
 7 files changed, 195 insertions(+), 178 deletions(-)
---
diff --git a/libnautilus-private/nautilus-canvas-dnd.c b/libnautilus-private/nautilus-canvas-dnd.c
index efa71d8..8e7501b 100644
--- a/libnautilus-private/nautilus-canvas-dnd.c
+++ b/libnautilus-private/nautilus-canvas-dnd.c
@@ -311,6 +311,8 @@ drag_data_get_callback (GtkWidget *widget,
 			guint32 time,
 			gpointer data)
 {
+	NautilusDragInfo *drag_info;
+
 	g_assert (widget != NULL);
 	g_assert (NAUTILUS_IS_CANVAS_CONTAINER (widget));
 	g_return_if_fail (context != NULL);
@@ -319,8 +321,8 @@ drag_data_get_callback (GtkWidget *widget,
 	 * the selection data in the right format. Pass it means to
 	 * iterate all the selected icons.
 	 */
-	nautilus_drag_drag_data_get (widget, context, selection_data,
-		info, time, widget, each_icon_get_data_binder);
+	drag_info = &(NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info->drag_info);
+	nautilus_drag_drag_data_get_from_cache (drag_info->selection_cache, context, selection_data, info, time);
 }
 
 
@@ -1307,6 +1309,7 @@ drag_begin_callback (GtkWidget      *widget,
 		     gpointer        data)
 {
 	NautilusCanvasContainer *container;
+	NautilusDragInfo *drag_info;
 	cairo_surface_t *surface;
 	double x1, y1, x2, y2, winx, winy;
 	int x_offset, y_offset;
@@ -1333,6 +1336,12 @@ drag_begin_callback (GtkWidget      *widget,
         cairo_surface_set_device_offset (surface, -x_offset, -y_offset);
         gtk_drag_set_icon_surface (context, surface);
         cairo_surface_destroy (surface);
+
+	/* cache the data at the beginning since the view may change */
+	drag_info = &(container->details->dnd_info->drag_info);
+	drag_info->selection_cache = nautilus_drag_create_selection_cache (widget,
+									   each_icon_get_data_binder);
+
 }
 
 void
diff --git a/libnautilus-private/nautilus-dnd.c b/libnautilus-private/nautilus-dnd.c
index afb3c54..cba7b05 100644
--- a/libnautilus-private/nautilus-dnd.c
+++ b/libnautilus-private/nautilus-dnd.c
@@ -81,6 +81,7 @@ nautilus_drag_finalize (NautilusDragInfo *drag_info)
 {
 	gtk_target_list_unref (drag_info->target_list);
 	nautilus_drag_destroy_selection_list (drag_info->selection_list);
+	nautilus_drag_destroy_selection_list (drag_info->selection_cache);
 
 	g_free (drag_info);
 }
@@ -585,9 +586,81 @@ add_one_uri (const char *uri, int x, int y, int w, int h, gpointer data)
 	g_string_append (result, "\r\n");
 }
 
+static void
+cache_one_item (const char *uri,
+		int x, int y,
+		int w, int h,
+		gpointer data)
+{
+	GList **cache = data;
+	NautilusDragSelectionItem *item;
+
+	item = nautilus_drag_selection_item_new ();
+	item->uri = g_strdup (uri);
+	item->icon_x = x;
+	item->icon_y = y;
+	item->icon_width = w;
+	item->icon_height = h;
+	*cache = g_list_prepend (*cache, item);
+}
+
+GList *
+nautilus_drag_create_selection_cache (gpointer container_context,
+				      NautilusDragEachSelectedItemIterator each_selected_item_iterator)
+{
+	GList *cache = NULL;
+
+	(* each_selected_item_iterator) (cache_one_item, container_context, &cache);
+	cache = g_list_reverse (cache);
+
+	return cache;
+}
+
 /* Common function for drag_data_get_callback calls.
  * Returns FALSE if it doesn't handle drag data */
 gboolean
+nautilus_drag_drag_data_get_from_cache (GList *cache,
+					GdkDragContext *context,
+					GtkSelectionData *selection_data,
+					guint info,
+					guint32 time)
+{
+	GList *l;
+	GString *result;
+	NautilusDragEachSelectedItemDataGet func;
+
+	if (cache == NULL) {
+		return FALSE;
+	}
+
+	switch (info) {
+	case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
+		func = add_one_gnome_icon;
+		break;
+	case NAUTILUS_ICON_DND_URI_LIST:
+	case NAUTILUS_ICON_DND_TEXT:
+		func = add_one_uri;
+		break;
+	default:
+		return FALSE;
+	}
+
+	result = g_string_new (NULL);
+
+	for (l = cache; l != NULL; l = l->next) {
+		NautilusDragSelectionItem *item = l->data;
+		(*func) (item->uri, item->icon_x, item->icon_y, item->icon_width, item->icon_height, result);
+	}
+
+	gtk_selection_data_set (selection_data,
+				gtk_selection_data_get_target (selection_data),
+				8, (guchar *) result->str, result->len);
+	g_string_free (result, TRUE);
+
+	return TRUE;
+}
+
+gboolean
 nautilus_drag_drag_data_get (GtkWidget *widget,
 			GdkDragContext *context,
 			GtkSelectionData *selection_data,
diff --git a/libnautilus-private/nautilus-dnd.h b/libnautilus-private/nautilus-dnd.h
index 0c45b88..6f42609 100644
--- a/libnautilus-private/nautilus-dnd.h
+++ b/libnautilus-private/nautilus-dnd.h
@@ -82,6 +82,9 @@ typedef struct {
 	 */
 	GList *selection_list;
 
+	/* cache of selected URIs, representing items being dragged */
+	GList *selection_cache;
+
 	/* has the drop occured ? */
 	gboolean drop_occured;
 
@@ -137,6 +140,13 @@ gboolean		    nautilus_drag_drag_data_get			(GtkWidget			      *widget,
 									 guint32			       time,
 									 gpointer			       container_context,
 									 NautilusDragEachSelectedItemIterator  each_selected_item_iterator);
+GList			   *nautilus_drag_create_selection_cache	(gpointer			       container_context,
+									 NautilusDragEachSelectedItemIterator  each_selected_item_iterator);
+gboolean		    nautilus_drag_drag_data_get_from_cache	(GList				      *cache,
+									 GdkDragContext			      *context,
+									 GtkSelectionData		      *selection_data,
+									 guint				       info,
+									 guint32			       time);
 int			    nautilus_drag_modifier_based_action		(int				       default_action,
 									 int				       non_default_action);
 
diff --git a/src/nautilus-list-model.c b/src/nautilus-list-model.c
index fb87484..ba8cb59 100644
--- a/src/nautilus-list-model.c
+++ b/src/nautilus-list-model.c
@@ -33,7 +33,6 @@
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
-#include <libegg/eggtreemultidnd.h>
 #include <eel/eel-graphic-effects.h>
 #include <libnautilus-private/nautilus-dnd.h>
 
@@ -56,7 +55,6 @@ static int nautilus_list_model_file_entry_compare_func (gconstpointer a,
 							gpointer      user_data);
 static void nautilus_list_model_tree_model_init (GtkTreeModelIface *iface);
 static void nautilus_list_model_sortable_init (GtkTreeSortableIface *iface);
-static void nautilus_list_model_multi_drag_source_init (EggTreeMultiDragSourceIface *iface);
 
 struct NautilusListModelDetails {
 	GSequence *files;
@@ -101,17 +99,13 @@ G_DEFINE_TYPE_WITH_CODE (NautilusListModel, nautilus_list_model, G_TYPE_OBJECT,
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
 						nautilus_list_model_tree_model_init)
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
-						nautilus_list_model_sortable_init)
-			 G_IMPLEMENT_INTERFACE (EGG_TYPE_TREE_MULTI_DRAG_SOURCE,
-						nautilus_list_model_multi_drag_source_init));
+						nautilus_list_model_sortable_init));
 
 static const GtkTargetEntry drag_types [] = {
 	{ NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
 	{ NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
 };
 
-static GtkTargetList *drag_target_list = NULL;
-
 static void
 file_entry_free (FileEntry *file_entry)
 {
@@ -765,101 +759,6 @@ nautilus_list_model_has_default_sort_func (GtkTreeSortable *sortable)
 	return FALSE;
 }
 
-static gboolean
-nautilus_list_model_multi_row_draggable (EggTreeMultiDragSource *drag_source, GList *path_list)
-{
-	return TRUE;
-}
-
-static void
-each_path_get_data_binder (NautilusDragEachSelectedItemDataGet data_get,
-			   gpointer context,
-			   gpointer data)
-{
-	DragDataGetInfo *info;
-	GList *l;
-	NautilusFile *file;
-	GtkTreeRowReference *row;
-	GtkTreePath *path;
-	char *uri;
-	GdkRectangle cell_area;
-	GtkTreeViewColumn *column;
-
-	info = context;
-
-	g_return_if_fail (info->model->details->drag_view);
-
-	column = gtk_tree_view_get_column (info->model->details->drag_view, 0);
-
-	for (l = info->path_list; l != NULL; l = l->next) {
-		row = l->data;
-
-		path = gtk_tree_row_reference_get_path (row);
-		file = nautilus_list_model_file_for_path (info->model, path);
-		if (file) {
-			gtk_tree_view_get_cell_area
-				(info->model->details->drag_view,
-				 path, 
-				 column,
-				 &cell_area);
-				
-			uri = nautilus_file_get_uri (file);
-				
-			(*data_get) (uri, 
-				     0,
-				     cell_area.y - info->model->details->drag_begin_y,
-				     cell_area.width, cell_area.height, 
-				     data);
-				
-			g_free (uri);
-			
-			nautilus_file_unref (file);
-		}
-		
-		gtk_tree_path_free (path);
-	}
-}
-
-static gboolean
-nautilus_list_model_multi_drag_data_get (EggTreeMultiDragSource *drag_source, 
-					 GList *path_list, 
-					 GtkSelectionData *selection_data)
-{
-	NautilusListModel *model;
-	DragDataGetInfo context;
-	guint target_info;
-	
-	model = NAUTILUS_LIST_MODEL (drag_source);
-
-	context.model = model;
-	context.path_list = path_list;
-
-	if (!drag_target_list) {
-		drag_target_list = nautilus_list_model_get_drag_target_list ();
-	}
-
-	if (gtk_target_list_find (drag_target_list,
-				  gtk_selection_data_get_target (selection_data),
-				  &target_info)) {
-		nautilus_drag_drag_data_get (NULL,
-					     NULL,
-					     selection_data,
-					     target_info,
-					     GDK_CURRENT_TIME,
-					     &context,
-					     each_path_get_data_binder);
-		return TRUE;
-	} else {
-		return FALSE;
-	}
-}
-
-static gboolean
-nautilus_list_model_multi_drag_data_delete (EggTreeMultiDragSource *drag_source, GList *path_list)
-{
-	return TRUE;
-}
-
 static void
 add_dummy_row (NautilusListModel *model, FileEntry *parent_entry)
 {
@@ -1392,6 +1291,22 @@ nautilus_list_model_set_drag_view (NautilusListModel *model,
 	model->details->drag_begin_y = drag_begin_y;
 }
 
+GtkTreeView *
+nautilus_list_model_get_drag_view (NautilusListModel *model,
+				   int *drag_begin_x,
+				   int *drag_begin_y)
+{
+	if (drag_begin_x != NULL) {
+		*drag_begin_x = model->details->drag_begin_x;
+	}
+
+	if (drag_begin_y != NULL) {
+		*drag_begin_y = model->details->drag_begin_y;
+	}
+
+	return model->details->drag_view;
+}
+
 GtkTargetList *
 nautilus_list_model_get_drag_target_list ()
 {
@@ -1548,14 +1463,6 @@ nautilus_list_model_sortable_init (GtkTreeSortableIface *iface)
 	iface->has_default_sort_func = nautilus_list_model_has_default_sort_func;
 }
 
-static void
-nautilus_list_model_multi_drag_source_init (EggTreeMultiDragSourceIface *iface)
-{
-	iface->row_draggable = nautilus_list_model_multi_row_draggable;
-	iface->drag_data_get = nautilus_list_model_multi_drag_data_get;
-	iface->drag_data_delete = nautilus_list_model_multi_drag_data_delete;
-}
-
 void
 nautilus_list_model_subdirectory_done_loading (NautilusListModel *model, NautilusDirectory *directory)
 {
diff --git a/src/nautilus-list-model.h b/src/nautilus-list-model.h
index 0b07e8c..57ecd64 100644
--- a/src/nautilus-list-model.h
+++ b/src/nautilus-list-model.h
@@ -114,6 +114,9 @@ void              nautilus_list_model_set_drag_view (NautilusListModel *model,
 						     GtkTreeView *view,
 						     int begin_x, 
 						     int begin_y);
+GtkTreeView *     nautilus_list_model_get_drag_view (NautilusListModel *model,
+						     int *drag_begin_x,
+						     int *drag_begin_y);
 
 GtkTargetList *   nautilus_list_model_get_drag_target_list (void);
 
diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c
index e7b7b5e..90f3c50 100644
--- a/src/nautilus-list-view.c
+++ b/src/nautilus-list-view.c
@@ -40,7 +40,6 @@
 #include <gdk/gdk.h>
 #include <gdk/gdkkeysyms.h>
 #include <gtk/gtk.h>
-#include <libegg/eggtreemultidnd.h>
 #include <glib/gi18n.h>
 #include <glib-object.h>
 #include <libnautilus-extension/nautilus-column-provider.h>
@@ -354,7 +353,7 @@ drag_data_get_callback (GtkWidget *widget,
 {
 	GtkTreeView *tree_view;
 	GtkTreeModel *model;
-	GList *ref_list;
+	GList *selection_cache;
 
 	tree_view = GTK_TREE_VIEW (widget);
   
@@ -364,67 +363,12 @@ drag_data_get_callback (GtkWidget *widget,
 		return;
 	}
 
-	ref_list = g_object_get_data (G_OBJECT (context), "drag-info");
-
-	if (ref_list == NULL) {
+	selection_cache = g_object_get_data (G_OBJECT (context), "drag-info");
+	if (selection_cache == NULL) {
 		return;
 	}
 
-	if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model)) {
-		egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model),
-							  ref_list,
-							  selection_data);
-	}
-}
-
-static void
-filtered_selection_foreach (GtkTreeModel *model,
-			    GtkTreePath *path,
-			    GtkTreeIter *iter,
-			    gpointer data)
-{
-	struct SelectionForeachData *selection_data;
-	GtkTreeIter parent;
-	GtkTreeIter child;
-	
-	selection_data = data;
-
-	/* If the parent folder is also selected, don't include this file in the
-	 * file operation, since that would copy it to the toplevel target instead
-	 * of keeping it as a child of the copied folder
-	 */
-	child = *iter;
-	while (gtk_tree_model_iter_parent (model, &parent, &child)) {
-		if (gtk_tree_selection_iter_is_selected (selection_data->selection,
-							 &parent)) {
-			return;
-		}
-		child = parent;
-	}
-	
-	selection_data->list = g_list_prepend (selection_data->list, 
-					       gtk_tree_row_reference_new (model, path));
-}
-
-static GList *
-get_filtered_selection_refs (GtkTreeView *tree_view)
-{
-	struct SelectionForeachData selection_data;
-
-	selection_data.list = NULL;
-	selection_data.selection = gtk_tree_view_get_selection (tree_view);
-	
-	gtk_tree_selection_selected_foreach (selection_data.selection, 
-					     filtered_selection_foreach, 
-					     &selection_data);
-	return g_list_reverse (selection_data.list);
-}
-
-static void
-ref_list_free (GList *ref_list)
-{
-	g_list_foreach (ref_list, (GFunc) gtk_tree_row_reference_free, NULL);
-	g_list_free (ref_list);
+	nautilus_drag_drag_data_get_from_cache (selection_cache, context, selection_data, info, time);
 }
 
 static void
@@ -466,12 +410,80 @@ get_drag_pixbuf (NautilusListView *view)
 	return ret;
 }
 
+/* iteration glue struct */
+typedef struct {
+	NautilusListView *view;
+	NautilusDragEachSelectedItemDataGet iteratee;
+	gpointer iteratee_data;
+} ListGetDataBinderContext;
+
+static void
+item_get_data_binder (GtkTreeModel *model,
+		      GtkTreePath *path,
+		      GtkTreeIter *iter,
+		      gpointer data)
+{
+	ListGetDataBinderContext *context = data;
+	NautilusFile *file;
+	GtkTreeView *treeview;
+	GtkTreeViewColumn *column;
+	GdkRectangle cell_area;
+	int drag_begin_y  = 0;
+	char *uri;
+
+	treeview = nautilus_list_model_get_drag_view (context->view->details->model,
+						      NULL,
+						      &drag_begin_y);
+	column = gtk_tree_view_get_column (treeview, 0);
+
+	file = nautilus_list_model_file_for_path (NAUTILUS_LIST_MODEL (model), path);
+	if (file == NULL) {
+		return;
+	}
+
+	gtk_tree_view_get_cell_area (treeview,
+				     path,
+				     column,
+				     &cell_area);
+
+	uri = nautilus_file_get_uri (file);
+	nautilus_file_unref (file);
+
+	/* pass the uri, mouse-relative x/y and icon width/height */
+	context->iteratee (uri,
+			   0,
+			   cell_area.y - drag_begin_y,
+			   cell_area.width,
+			   cell_area.height,
+			   context->iteratee_data);
+
+	g_free (uri);
+}
+
+static void
+each_item_get_data_binder (NautilusDragEachSelectedItemDataGet iteratee,
+			   gpointer iterator_context,
+			   gpointer data)
+{
+	NautilusListView *view = NAUTILUS_LIST_VIEW (iterator_context);
+	ListGetDataBinderContext context;
+	GtkTreeSelection *selection;
+
+	context.view = view;
+	context.iteratee = iteratee;
+	context.iteratee_data = data;
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->details->tree_view));
+	gtk_tree_selection_selected_foreach (selection, item_get_data_binder, &context);
+}
+
+
 static void
 drag_begin_callback (GtkWidget *widget,
 		     GdkDragContext *context,
 		     NautilusListView *view)
 {
-	GList *ref_list;
+	GList *selection_cache;
 	GdkPixbuf *pixbuf;
 
 	pixbuf = get_drag_pixbuf (view);
@@ -486,12 +498,14 @@ drag_begin_callback (GtkWidget *widget,
 
 	stop_drag_check (view);
 	view->details->drag_started = TRUE;
-	
-	ref_list = get_filtered_selection_refs (GTK_TREE_VIEW (widget));
+
+	selection_cache = nautilus_drag_create_selection_cache (view,
+								each_item_get_data_binder);
+
 	g_object_set_data_full (G_OBJECT (context),
 				"drag-info",
-				ref_list,
-				(GDestroyNotify)ref_list_free);
+				selection_cache,
+				(GDestroyNotify)nautilus_drag_destroy_selection_list);
 }
 
 static gboolean
diff --git a/src/nautilus-places-sidebar.c b/src/nautilus-places-sidebar.c
index afb7203..b337a1c 100644
--- a/src/nautilus-places-sidebar.c
+++ b/src/nautilus-places-sidebar.c
@@ -1309,7 +1309,6 @@ build_selection_list (const char *data)
 	int i;
 
 	uris = g_uri_list_extract_uris (data);
-
 	result = NULL;
 	for (i = 0; uris[i]; i++) {
 		uri = uris[i];
@@ -1467,7 +1466,9 @@ drag_data_received_callback (GtkWidget *widget,
 			switch (info) {
 			case TEXT_URI_LIST:
 				selection_list = build_selection_list ((const gchar *) gtk_selection_data_get_data (selection_data));
+				g_assert (selection_list);
 				uris = uri_list_from_selection (selection_list);
+				g_assert (uris);
 				nautilus_file_operations_copy_move (uris, NULL, drop_uri,
 								    real_action, GTK_WIDGET (tree_view),
 								    NULL, NULL);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]