Index: file-manager/Makefile.am
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/Makefile.am,v
retrieving revision 1.68
diff -u -p -r1.68 Makefile.am
--- file-manager/Makefile.am	9 Apr 2002 13:51:09 -0000	1.68
+++ file-manager/Makefile.am	9 Jun 2002 16:18:03 -0000
@@ -17,6 +17,8 @@ INCLUDES = 					\
 
 
 libnautilus_file_manager_la_SOURCES=	\
+	eggtreemultidnd.c		\
+	eggtreemultidnd.h		\
 	fm-desktop-icon-view.c		\
 	fm-directory-view.c		\
 	fm-error-reporting.c		\
Index: file-manager/eggtreemultidnd.c
===================================================================
RCS file: file-manager/eggtreemultidnd.c
diff -N file-manager/eggtreemultidnd.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ file-manager/eggtreemultidnd.c	9 Jun 2002 16:18:03 -0000
@@ -0,0 +1,391 @@
+/* eggtreemultidnd.c
+ * Copyright (C) 2001  Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtkmain.h>
+#include "eggtreemultidnd.h"
+
+#define EGG_TREE_MULTI_DND_STRING "EggTreeMultiDndString"
+
+typedef struct
+{
+  guint pressed_button;
+  gint x;
+  gint y;
+  guint motion_notify_handler;
+  guint button_release_handler;
+  guint drag_data_get_handler;
+  GdkEvent *press_event;
+} EggTreeMultiDndData;
+
+/* CUT-N-PASTE from gtktreeview.c */
+typedef struct _TreeViewDragInfo TreeViewDragInfo;
+struct _TreeViewDragInfo
+{
+  GdkModifierType start_button_mask;
+  GtkTargetList *source_target_list;
+  GdkDragAction source_actions;
+
+  GtkTargetList *dest_target_list;
+
+  guint source_set : 1;
+  guint dest_set : 1;
+};
+
+
+GType
+egg_tree_multi_drag_source_get_type (void)
+{
+  static GType our_type = 0;
+
+  if (!our_type)
+    {
+      static const GTypeInfo our_info =
+      {
+        sizeof (EggTreeMultiDragSourceIface), /* class_size */
+	NULL,		/* base_init */
+	NULL,		/* base_finalize */
+	NULL,
+	NULL,		/* class_finalize */
+	NULL,		/* class_data */
+	0,
+	0,              /* n_preallocs */
+	NULL
+      };
+
+      our_type = g_type_register_static (G_TYPE_INTERFACE, "EggTreeMultiDragSource", &our_info, 0);
+    }
+  
+  return our_type;
+}
+
+
+/**
+ * egg_tree_multi_drag_source_row_draggable:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row on which user is initiating a drag
+ * 
+ * Asks the #EggTreeMultiDragSource whether a particular row can be used as
+ * the source of a DND operation. If the source doesn't implement
+ * this interface, the row is assumed draggable.
+ *
+ * Return value: %TRUE if the row can be dragged
+ **/
+gboolean
+egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source,
+					  GList                  *path_list)
+{
+  EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+  g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+  g_return_val_if_fail (iface->row_draggable != NULL, FALSE);
+  g_return_val_if_fail (path_list != NULL, FALSE);
+
+  if (iface->row_draggable)
+    return (* iface->row_draggable) (drag_source, path_list);
+  else
+    return TRUE;
+}
+
+
+/**
+ * egg_tree_multi_drag_source_drag_data_delete:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row that was being dragged
+ * 
+ * Asks the #EggTreeMultiDragSource to delete the row at @path, because
+ * it was moved somewhere else via drag-and-drop. Returns %FALSE
+ * if the deletion fails because @path no longer exists, or for
+ * some model-specific reason. Should robustly handle a @path no
+ * longer found in the model!
+ * 
+ * Return value: %TRUE if the row was successfully deleted
+ **/
+gboolean
+egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
+					     GList                  *path_list)
+{
+  EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+  g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+  g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE);
+  g_return_val_if_fail (path_list != NULL, FALSE);
+
+  return (* iface->drag_data_delete) (drag_source, path_list);
+}
+
+/**
+ * egg_tree_multi_drag_source_drag_data_get:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row that was dragged
+ * @selection_data: a #EggSelectionData to fill with data from the dragged row
+ * 
+ * Asks the #EggTreeMultiDragSource to fill in @selection_data with a
+ * representation of the row at @path. @selection_data->target gives
+ * the required type of the data.  Should robustly handle a @path no
+ * longer found in the model!
+ * 
+ * Return value: %TRUE if data of the required type was provided 
+ **/
+gboolean
+egg_tree_multi_drag_source_drag_data_get    (EggTreeMultiDragSource *drag_source,
+					     GList                  *path_list,
+					     GtkSelectionData  *selection_data)
+{
+  EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+  g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+  g_return_val_if_fail (iface->drag_data_get != NULL, FALSE);
+  g_return_val_if_fail (path_list != NULL, FALSE);
+  g_return_val_if_fail (selection_data != NULL, FALSE);
+
+  return (* iface->drag_data_get) (drag_source, path_list, selection_data);
+}
+
+static void
+stop_drag_check (GtkWidget *widget)
+{
+  EggTreeMultiDndData *priv_data;
+
+  priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+  gdk_event_free (priv_data->press_event);
+  priv_data->press_event = NULL;
+  g_signal_handler_disconnect (widget, priv_data->motion_notify_handler);
+  g_signal_handler_disconnect (widget, priv_data->button_release_handler);
+  g_signal_handler_disconnect (widget, priv_data->drag_data_get_handler);
+}
+
+static gboolean
+egg_tree_multi_drag_button_release_event (GtkWidget      *widget,
+					  GdkEventButton *event,
+					  gpointer        data)
+{
+  EggTreeMultiDndData *priv_data;
+
+  priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+  gtk_propagate_event (widget, priv_data->press_event);
+  
+  stop_drag_check (widget);
+
+  return FALSE;
+}
+
+static void
+selection_foreach (GtkTreeModel *model,
+		   GtkTreePath  *path,
+		   GtkTreeIter  *iter,
+		   gpointer      data)
+{
+  GList **list_ptr;
+
+  list_ptr = (GList **) data;
+
+  *list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path));
+}
+
+static void
+path_list_free (GList *path_list)
+{
+  g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL);
+  g_list_free (path_list);
+}
+
+static void
+set_context_data (GdkDragContext *context,
+		  GList          *path_list)
+{
+  g_object_set_data_full (G_OBJECT (context),
+                          "egg-tree-view-multi-source-row",
+                          path_list,
+                          (GDestroyNotify) path_list_free);
+}
+
+static GList *
+get_context_data (GdkDragContext *context)
+{
+  return g_object_get_data (G_OBJECT (context),
+			    "egg-tree-view-multi-source-row");
+}
+
+/* CUT-N-PASTE from gtktreeview.c */
+static TreeViewDragInfo*
+get_info (GtkTreeView *tree_view)
+{
+  return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
+}
+
+
+static void
+egg_tree_multi_drag_drag_data_get (GtkWidget        *widget,
+				   GdkDragContext   *context,
+				   GtkSelectionData *selection_data,
+				   guint             info,
+				   guint             time)
+{
+  GtkTreeView *tree_view;
+  GtkTreeModel *model;
+  TreeViewDragInfo *di;
+  GList *path_list;
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  model = gtk_tree_view_get_model (tree_view);
+
+  if (model == NULL)
+    return;
+
+  di = get_info (GTK_TREE_VIEW (widget));
+
+  if (di == NULL)
+    return;
+
+  path_list = get_context_data (context);
+
+  if (path_list == NULL)
+    return;
+
+  /* We can implement the GTK_TREE_MODEL_ROW target generically for
+   * any model; for DragSource models there are some other targets
+   * we also support.
+   */
+
+  if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model))
+    {
+      egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model),
+						path_list,
+						selection_data);
+    }
+}
+
+static gboolean
+egg_tree_multi_drag_motion_event (GtkWidget      *widget,
+				  GdkEventMotion *event,
+				  gpointer        data)
+{
+  EggTreeMultiDndData *priv_data;
+
+  priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+  if (gtk_drag_check_threshold (widget,
+				priv_data->x,
+				priv_data->y,
+				event->x,
+				event->y))
+    {
+      GList *path_list = NULL;
+      GtkTreeSelection *selection;
+      GtkTreeModel *model;
+      GdkDragContext *context;
+      TreeViewDragInfo *di;
+
+      di = get_info (GTK_TREE_VIEW (widget));
+
+      if (di == NULL)
+	return FALSE;
+      
+      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+      stop_drag_check (widget);
+      gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list);
+      path_list = g_list_reverse (path_list);
+      model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+      if (egg_tree_multi_drag_source_row_draggable (EGG_TREE_MULTI_DRAG_SOURCE (model), path_list))
+	{
+
+	  context = gtk_drag_begin (widget,
+				    di->source_target_list,
+				    di->source_actions,
+				    priv_data->pressed_button,
+				    (GdkEvent*)event);
+	  set_context_data (context, path_list);
+	  gtk_drag_set_icon_default (context);
+
+	}
+      else
+	{
+	  path_list_free (path_list);
+	}
+    }
+
+  return TRUE;
+}
+
+static gboolean
+egg_tree_multi_drag_button_press_event (GtkWidget      *widget,
+					GdkEventButton *event,
+					gpointer        data)
+{
+  GtkTreeView *tree_view;
+  GtkTreePath *path = NULL;
+  GtkTreeViewColumn *column = NULL;
+  gint cell_x, cell_y;
+  GtkTreeSelection *selection;
+  EggTreeMultiDndData *priv_data;
+
+  if (event->type == GDK_2BUTTON_PRESS)
+    return FALSE;
+
+  tree_view = GTK_TREE_VIEW (widget);
+  priv_data = g_object_get_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING);
+  if (priv_data == NULL)
+    {
+      priv_data = g_new (EggTreeMultiDndData, 1);
+      g_object_set_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING, priv_data);
+    }
+
+  if ((GdkEvent *) event == priv_data->press_event)
+    return FALSE;
+
+  gtk_tree_view_get_path_at_pos (tree_view,
+				 event->x, event->y,
+				 &path, &column,
+				 &cell_x, &cell_y);
+
+  selection = gtk_tree_view_get_selection (tree_view);
+  if (gtk_tree_selection_path_is_selected (selection, path))
+    {
+      priv_data->pressed_button = event->button;
+      priv_data->x = event->x;
+      priv_data->y = event->y;
+      priv_data->press_event = gdk_event_copy ((GdkEvent *)event);
+      priv_data->motion_notify_handler =
+	g_signal_connect (G_OBJECT (tree_view), "motion_notify_event", G_CALLBACK (egg_tree_multi_drag_motion_event), NULL);
+      priv_data->button_release_handler =
+	g_signal_connect (G_OBJECT (tree_view), "button_release_event", G_CALLBACK (egg_tree_multi_drag_button_release_event), NULL);
+      priv_data->drag_data_get_handler =
+	g_signal_connect (G_OBJECT (tree_view), "drag_data_get", G_CALLBACK (egg_tree_multi_drag_drag_data_get), NULL);
+      return TRUE;
+    }
+
+  gtk_tree_path_free (path);
+
+  return FALSE;
+}
+
+void
+egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view)
+{
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+  g_signal_connect (G_OBJECT (tree_view), "button_press_event", G_CALLBACK (egg_tree_multi_drag_button_press_event), NULL);
+}
+
Index: file-manager/eggtreemultidnd.h
===================================================================
RCS file: file-manager/eggtreemultidnd.h
diff -N file-manager/eggtreemultidnd.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ file-manager/eggtreemultidnd.h	9 Jun 2002 16:18:03 -0000
@@ -0,0 +1,76 @@
+/* eggtreednd.h
+ * Copyright (C) 2001  Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_TREE_MULTI_DND_H__
+#define __EGG_TREE_MULTI_DND_H__
+
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtkdnd.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_TREE_MULTI_DRAG_SOURCE            (egg_tree_multi_drag_source_get_type ())
+#define EGG_TREE_MULTI_DRAG_SOURCE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSource))
+#define EGG_IS_TREE_MULTI_DRAG_SOURCE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE))
+#define EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSourceIface))
+
+typedef struct _EggTreeMultiDragSource      EggTreeMultiDragSource; /* Dummy typedef */
+typedef struct _EggTreeMultiDragSourceIface EggTreeMultiDragSourceIface;
+
+struct _EggTreeMultiDragSourceIface
+{
+  GTypeInterface g_iface;
+
+  /* VTable - not signals */
+  gboolean     (* row_draggable)        (EggTreeMultiDragSource   *drag_source,
+                                         GList                    *path_list);
+
+  gboolean     (* drag_data_get)        (EggTreeMultiDragSource   *drag_source,
+                                         GList                    *path_list,
+                                         GtkSelectionData         *selection_data);
+
+  gboolean     (* drag_data_delete)     (EggTreeMultiDragSource *drag_source,
+                                         GList                  *path_list);
+};
+
+GType    egg_tree_multi_drag_source_get_type         (void) G_GNUC_CONST;
+
+/* Returns whether the given row can be dragged */
+gboolean egg_tree_multi_drag_source_row_draggable    (EggTreeMultiDragSource *drag_source,
+						      GList                  *path_list);
+
+/* Deletes the given row, or returns FALSE if it can't */
+gboolean egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
+						      GList                  *path_list);
+
+
+/* Fills in selection_data with type selection_data->target based on the row
+ * denoted by path, returns TRUE if it does anything
+ */
+gboolean egg_tree_multi_drag_source_drag_data_get    (EggTreeMultiDragSource *drag_source,
+						      GList                  *path_list,
+						      GtkSelectionData       *selection_data);
+void     egg_tree_multi_drag_add_drag_support        (GtkTreeView            *tree_view);
+
+
+
+G_END_DECLS
+
+#endif /* __EGG_TREE_MULTI_DND_H__ */
Index: file-manager/fm-list-model.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-list-model.c,v
retrieving revision 1.16
diff -u -p -r1.16 fm-list-model.c
--- file-manager/fm-list-model.c	17 May 2002 14:11:25 -0000	1.16
+++ file-manager/fm-list-model.c	9 Jun 2002 16:18:04 -0000
@@ -24,12 +24,14 @@
 
 #include <config.h>
 #include "fm-list-model.h"
+#include "eggtreemultidnd.h"
 
 #include <string.h>
 #include <eel/eel-gtk-macros.h>
 #include <gtk/gtktreednd.h>
 #include <gtk/gtktreesortable.h>
 #include <libnautilus-private/nautilus-icon-factory.h>
+#include <libnautilus-private/nautilus-dnd.h>
 
 #define G_SLIST(x) ((GSList *) x)
 
@@ -71,6 +73,9 @@ static const AttributeEntry attributes[]
 	{ "date_modified", FM_LIST_MODEL_DATE_MODIFIED_COLUMN },
 };
 
+
+
+
 static guint
 fm_list_model_get_flags (GtkTreeModel *tree_model)
 {
@@ -485,6 +490,28 @@ fm_list_model_has_default_sort_func (Gtk
 }
 
 static gboolean
+fm_list_model_multi_row_draggable (EggTreeMultiDragSource *drag_source, GList *path_list)
+{
+	g_print ("Foo");
+	return TRUE;
+}
+
+static gboolean
+fm_list_model_multi_drag_data_get (EggTreeMultiDragSource *drag_source, GList *path_list, GtkSelectionData *selection_data)
+{
+	g_print ("Bar\n");
+	return FALSE;
+}
+
+static gboolean
+fm_list_model_multi_drag_data_delete (EggTreeMultiDragSource *drag_source, GList *path_list)
+{
+	g_print ("Baz\n");
+	return FALSE;
+}
+
+
+static gboolean
 fm_list_model_row_draggable (GtkTreeDragSource *drag_source, GtkTreePath *path)
 {
 	/* We always return FALSE here until we can fix the dnd support */
@@ -866,6 +893,14 @@ fm_list_model_sortable_init (GtkTreeSort
 }
 
 static void
+fm_list_model_multi_drag_source_init (EggTreeMultiDragSourceIface *iface)
+{
+	iface->row_draggable = fm_list_model_multi_row_draggable;
+	iface->drag_data_get = fm_list_model_multi_drag_data_get;
+	iface->drag_data_delete = fm_list_model_multi_drag_data_delete;
+}
+
+static void
 fm_list_model_drag_source_init (GtkTreeDragSourceIface *iface)
 {
 	iface->row_draggable = fm_list_model_row_draggable;
@@ -902,6 +937,12 @@ fm_list_model_get_type (void)
 			NULL
 		};
 
+		static const GInterfaceInfo multi_drag_source_info = {
+			(GInterfaceInitFunc) fm_list_model_multi_drag_source_init,
+			NULL,
+			NULL
+		};
+
 		static const GInterfaceInfo drag_source_info = {
 			(GInterfaceInitFunc) fm_list_model_drag_source_init,
 			NULL,
@@ -915,6 +956,9 @@ fm_list_model_get_type (void)
 		g_type_add_interface_static (object_type,
 					     GTK_TYPE_TREE_SORTABLE,
 					     &sortable_info);
+		g_type_add_interface_static (object_type,
+					     EGG_TYPE_TREE_MULTI_DRAG_SOURCE,
+					     &multi_drag_source_info);
 		g_type_add_interface_static (object_type,
 					     GTK_TYPE_TREE_DRAG_SOURCE,
 					     &drag_source_info);
Index: file-manager/fm-list-view.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-list-view.c,v
retrieving revision 1.175
diff -u -p -r1.175 fm-list-view.c
--- file-manager/fm-list-view.c	3 Jun 2002 21:16:17 -0000	1.175
+++ file-manager/fm-list-view.c	9 Jun 2002 16:18:04 -0000
@@ -28,6 +28,7 @@
 #include <config.h>
 #include "fm-list-view.h"
 
+#include "eggtreemultidnd.h"
 #include "fm-error-reporting.h"
 #include "fm-list-model.h"
 #include <eel/eel-cell-renderer-pixbuf-list.h>
@@ -291,6 +292,12 @@ create_and_set_up_tree_view (FMListView 
 	GtkTreeViewColumn *column;
 	
 	view->details->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
+	egg_tree_multi_drag_add_drag_support (view->details->tree_view);
+	gtk_tree_view_enable_model_drag_source (view->details->tree_view,
+						GDK_BUTTON1_MASK| GDK_BUTTON2_MASK,
+						drag_types,
+						G_N_ELEMENTS (drag_types),
+						(GDK_ACTION_MOVE | GDK_ACTION_COPY));
 
 	g_signal_connect_object (gtk_tree_view_get_selection (view->details->tree_view),
 				 "changed",
