Dragging from the list view



I just checked in the list view drag source. Dragging from the tree
sidebar will be coming shortly, and will be mostly the same as the list
view.

The major problem with it so far is that you can only drag rows that are
already selected, making dragging a two-part process (select the row,
drag it).  Hopefully I'll be able to fix this soonish.

Please bang on it and file bugs in bugzilla, and review the attached
patch for errors.

-dave
? nautilus-list-view-drag-source.patch
? components/adapter/Nautilus_ComponentAdapterFactory_std.server.in
? components/history/Nautilus_View_history.server.in
? components/loser/content/Nautilus_View_content-loser.server.in
? components/loser/sidebar/Nautilus_View_sidebar-loser.server.in
? components/music/Nautilus_View_music.server.in
? components/notes/Nautilus_View_notes.server.in
? components/sample/Nautilus_View_sample.server.in
? components/text/Nautilus_View_text.server.in
? components/throbber/Nautilus_Control_throbber.server.in
? components/tree/Nautilus_View_tree.server.in
? po/missing
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/nautilus/ChangeLog,v
retrieving revision 1.5460
diff -u -r1.5460 ChangeLog
--- ChangeLog	26 Jul 2002 00:45:58 -0000	1.5460
+++ ChangeLog	26 Jul 2002 22:55:30 -0000
@@ -1,3 +1,24 @@
+2002-07-26  Dave Camp  <dave ximian com>
+
+	* libnautilus-private/Makefile.am: Added eggtreemultidnd.[ch].
+	* libnautilus-private/eggtreemultidnd.c:
+	* libnautilus-private/eggtreemultidnd.h: New files.
+	* src/file-manager/fm-list-model.c:
+	(fm_list_model_multi_row_draggable),
+	(fm_list_model_file_for_path),
+	(each_path_get_data_binder), (fm_list_model_multi_drag_data_get),
+	(fm_list_model_multi_drag_data_delete),
+	(fm_list_model_set_drag_view), (fm_list_model_get_drag_types),
+	(fm_list_model_multi_drag_source_init), (fm_list_model_get_type):
+	Implemented the multi drag source.
+	* src/file-manager/fm-list-model.h:
+	* src/file-manager/fm-list-view.c: (event_after_callback): Moved 
+	the context menu out of here...
+	(button_release_callback): to here.
+	(button_press_callback): Pass the current view and the position
+	to the model.
+	(create_and_set_up_tree_view): Enable drag source on the view.
+
 2002-07-25  Michael Meeks  <michael ximian com>
 
 	* libnautilus-private/nautilus-icon-factory.c
Index: libnautilus-private/Makefile.am
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/Makefile.am,v
retrieving revision 1.218
diff -u -r1.218 Makefile.am
--- libnautilus-private/Makefile.am	25 Jul 2002 20:59:14 -0000	1.218
+++ libnautilus-private/Makefile.am	26 Jul 2002 22:55:32 -0000
@@ -36,6 +36,8 @@
 
 libnautilus_private_la_SOURCES = \
 	$(nautilus_metafile_server_idl_sources)	\
+	eggtreemultidnd.c \
+	eggtreemultidnd.h \
 	nautilus-audio-player.c \
 	nautilus-audio-player.h \
 	nautilus-authn-manager.c \
Index: po/POTFILES.in
===================================================================
RCS file: /cvs/gnome/nautilus/po/POTFILES.in,v
retrieving revision 1.116
diff -u -r1.116 POTFILES.in
--- po/POTFILES.in	16 Jul 2002 22:17:57 -0000	1.116
+++ po/POTFILES.in	26 Jul 2002 22:55:33 -0000
@@ -49,6 +49,7 @@
 icons/tahoe/tahoe.xml
 libbackground/preview-file-selection.c
 libnautilus-private/filesystem-attributes.xml
+libnautilus-private/eggtreemultidnd.c
 libnautilus-private/nautilus-authn-manager.c
 libnautilus-private/nautilus-customization-data.c
 libnautilus-private/nautilus-dnd.c
@@ -68,6 +69,7 @@
 libnautilus-private/nautilus-theme.c
 libnautilus-private/nautilus-trash-directory.c
 libnautilus-private/nautilus-trash-file.c
+libnautilus-private/nautilus-tree-view-drag-dest.c
 libnautilus-private/nautilus-undo-signal-handlers.c
 libnautilus-private/nautilus-view-identifier.c
 libnautilus-private/nautilus-volume-monitor.c
Index: src/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 -r1.16 fm-list-model.c
--- src/file-manager/fm-list-model.c	17 May 2002 14:11:25 -0000	1.16
+++ src/file-manager/fm-list-model.c	26 Jul 2002 22:55:34 -0000
@@ -24,12 +24,14 @@
 
 #include <config.h>
 #include "fm-list-model.h"
+#include <libnautilus-private/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)
 
@@ -46,13 +48,30 @@
 	GtkSortType order;
 
 	gboolean sort_directories_first;
+
+	GtkTreeView *drag_view;
+	int drag_begin_x;
+	int drag_begin_y;
 };
 
 typedef struct {
+	FMListModel *model;
+	
+	GList *path_list;
+} DragDataGetInfo;
+
+typedef struct {
 	const char *attribute_name;
 	int sort_column_id;
 } AttributeEntry;
 
+static 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 },
+	{ NAUTILUS_ICON_DND_URL_TYPE, 0, NAUTILUS_ICON_DND_URL },
+	{ NAUTILUS_ICON_DND_TEXT_TYPE, 0, NAUTILUS_ICON_DND_TEXT }
+};
+
 /*
  * Do not change the order of the type and size attributes, they 
  * have to be in this order so that the column_id to attribute mapping
@@ -71,6 +90,8 @@
 	{ "date_modified", FM_LIST_MODEL_DATE_MODIFIED_COLUMN },
 };
 
+static GtkTargetList *drag_target_list = NULL;
+
 static guint
 fm_list_model_get_flags (GtkTreeModel *tree_model)
 {
@@ -485,18 +506,108 @@
 }
 
 static gboolean
-fm_list_model_row_draggable (GtkTreeDragSource *drag_source, GtkTreePath *path)
+fm_list_model_multi_row_draggable (EggTreeMultiDragSource *drag_source, GList *path_list)
 {
-	/* We always return FALSE here until we can fix the dnd support */
-	return FALSE;
+	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;
+	GtkTreeIter iter;
+
+	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);
+
+		if (gtk_tree_model_get_iter (GTK_TREE_MODEL (info->model), 
+					     &iter, path)) {
+			gtk_tree_model_get (GTK_TREE_MODEL (info->model), 
+					    &iter, 
+					    FM_LIST_MODEL_FILE_COLUMN, &file,
+					    -1);
+
+			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, 
+					     cell_area.x - info->model->details->drag_begin_x,
+					     cell_area.y - info->model->details->drag_begin_y,
+					     cell_area.width, cell_area.height, 
+					     data);
+				
+				g_free (uri);
+			}
+		}
+		
+		gtk_tree_path_free (path);
+	}
 }
 
 static gboolean
-fm_list_model_drag_data_get (GtkTreeDragSource *drag_source, GtkTreePath *path, GtkSelectionData *selection_data)
+fm_list_model_multi_drag_data_get (EggTreeMultiDragSource *drag_source, 
+				   GList *path_list, 
+				   GtkSelectionData *selection_data)
 {
-	return FALSE;
+	FMListModel *model;
+	DragDataGetInfo context;
+	guint target_info;
+	
+	model = FM_LIST_MODEL (drag_source);
+
+	context.model = model;
+	context.path_list = path_list;
+
+	if (!drag_target_list) {
+		drag_target_list = gtk_target_list_new 
+			(drag_types, G_N_ELEMENTS (drag_types));
+
+	}
+
+	if (gtk_target_list_find (drag_target_list,
+				  selection_data->target,
+				  &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
+fm_list_model_multi_drag_data_delete (EggTreeMultiDragSource *drag_source, GList *path_list)
+{
+	return TRUE;
+}
 
 void
 fm_list_model_add_file (FMListModel *model, NautilusFile *file)
@@ -809,6 +920,29 @@
 	g_return_val_if_reached (FM_LIST_MODEL_STANDARD_ICON_COLUMN);
 }
 
+void
+fm_list_model_set_drag_view (FMListModel *model,
+			     GtkTreeView *view,
+			     int drag_begin_x,
+			     int drag_begin_y)
+{
+	g_return_if_fail (model != NULL);
+	g_return_if_fail (FM_IS_LIST_MODEL (model));
+	g_return_if_fail (!view || GTK_IS_TREE_VIEW (view));
+	
+	model->details->drag_view = view;
+	model->details->drag_begin_x = drag_begin_x;
+	model->details->drag_begin_y = drag_begin_y;
+}
+
+void
+fm_list_model_get_drag_types (GtkTargetEntry **entries,
+			      int *num_entries)
+{
+	*entries = drag_types;
+	*num_entries = G_N_ELEMENTS (drag_types);
+}
+
 static void
 fm_list_model_finalize (GObject *object)
 {
@@ -866,10 +1000,11 @@
 }
 
 static void
-fm_list_model_drag_source_init (GtkTreeDragSourceIface *iface)
+fm_list_model_multi_drag_source_init (EggTreeMultiDragSourceIface *iface)
 {
-	iface->row_draggable = fm_list_model_row_draggable;
-	iface->drag_data_get = fm_list_model_drag_data_get;
+	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;
 }
 
 GType
@@ -902,8 +1037,8 @@
 			NULL
 		};
 
-		static const GInterfaceInfo drag_source_info = {
-			(GInterfaceInitFunc) fm_list_model_drag_source_init,
+		static const GInterfaceInfo multi_drag_source_info = {
+			(GInterfaceInitFunc) fm_list_model_multi_drag_source_init,
 			NULL,
 			NULL
 		};
@@ -916,8 +1051,8 @@
 					     GTK_TYPE_TREE_SORTABLE,
 					     &sortable_info);
 		g_type_add_interface_static (object_type,
-					     GTK_TYPE_TREE_DRAG_SOURCE,
-					     &drag_source_info);
+					     EGG_TYPE_TREE_MULTI_DRAG_SOURCE,
+					     &multi_drag_source_info);
 	}
 	
 	return object_type;
Index: src/file-manager/fm-list-model.h
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-list-model.h,v
retrieving revision 1.6
diff -u -r1.6 fm-list-model.h
--- src/file-manager/fm-list-model.h	13 May 2002 22:12:47 -0000	1.6
+++ src/file-manager/fm-list-model.h	26 Jul 2002 22:55:34 -0000
@@ -23,6 +23,8 @@
 */
 
 #include <gtk/gtktreemodel.h>
+#include <gtk/gtktreeview.h>
+#include <gdk/gdkdnd.h>
 #include <libnautilus-private/nautilus-file.h>
 #include <libnautilus-private/nautilus-icon-factory.h>
 
@@ -86,5 +88,17 @@
 
 NautilusZoomLevel fm_list_model_get_zoom_level_from_column_id (int               column);
 int               fm_list_model_get_column_id_from_zoom_level (NautilusZoomLevel zoom_level);
+
+NautilusFile *    fm_list_model_file_for_path (FMListModel *model, GtkTreePath *path);
+
+
+
+void              fm_list_model_set_drag_view (FMListModel *model,
+					       GtkTreeView *view,
+					       int begin_x, 
+					       int begin_y);
+
+void              fm_list_model_get_drag_types (GtkTargetEntry **entries,
+						int *num_entries);
 
 #endif /* FM_LIST_MODEL_H */
Index: src/file-manager/fm-list-view.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-list-view.c,v
retrieving revision 1.179
diff -u -r1.179 fm-list-view.c
--- src/file-manager/fm-list-view.c	25 Jul 2002 20:59:17 -0000	1.179
+++ src/file-manager/fm-list-view.c	26 Jul 2002 22:55:35 -0000
@@ -39,6 +39,7 @@
 #include <libgnome/gnome-i18n.h>
 #include <libgnome/gnome-macros.h>
 #include <libgnomevfs/gnome-vfs-utils.h>
+#include <libnautilus-private/eggtreemultidnd.h>
 #include <libnautilus-private/nautilus-directory-background.h>
 #include <libnautilus-private/nautilus-dnd.h>
 #include <libnautilus-private/nautilus-file-dnd.h>
@@ -140,32 +141,28 @@
 		fm_directory_view_activate_files (view, file_list);
 		nautilus_file_list_free (file_list);
 	}
-
-	if (event->type == GDK_BUTTON_PRESS
-	    && event->window == gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget))
-	    && (((GdkEventButton *) event)->button == 3)) {
-		/* Put up the right kind of menu if we right click in the tree view. */
-		if (tree_view_has_selection (GTK_TREE_VIEW (widget))) {
-			fm_directory_view_pop_up_selection_context_menu (view, (GdkEventButton *) event);
-		} else {
-			fm_directory_view_pop_up_background_context_menu (view, (GdkEventButton *) event);
-		}
-	}
 }
 
 static gboolean
 button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callback_data)
 {
+	FMListView *view;
 	GtkTreeView *tree_view;
 	GtkTreePath *path;
 	gboolean result;
 
+	view = FM_LIST_VIEW (callback_data);
 	tree_view = GTK_TREE_VIEW (widget);
 
 	if (event->window != gtk_tree_view_get_bin_window (tree_view)) {
 		return FALSE;
 	}
 
+	fm_list_model_set_drag_view
+		(FM_LIST_MODEL (gtk_tree_view_get_model (tree_view)),
+		 tree_view,
+		 event->x, event->y);
+
 	result = FALSE;
 
 	if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y,
@@ -206,6 +203,16 @@
 		nautilus_file_list_free (file_list);
 	}
 
+	if (event->window == gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget))
+	    && (event->button == 3)) {
+		/* Put up the right kind of menu if we right click in the tree view. */
+		if (tree_view_has_selection (GTK_TREE_VIEW (widget))) {
+			fm_directory_view_pop_up_selection_context_menu (FM_DIRECTORY_VIEW (view), (GdkEventButton *) event);
+		} else {
+			fm_directory_view_pop_up_background_context_menu (FM_DIRECTORY_VIEW (view), (GdkEventButton *) event);
+		}
+	}
+
 	return FALSE;
 }
 
@@ -346,8 +353,22 @@
 {
 	GtkCellRenderer *cell;
 	GtkTreeViewColumn *column;
-	
+	GtkTargetEntry *drag_types;
+	int num_drag_types;	
+
 	view->details->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
+
+	fm_list_model_get_drag_types (&drag_types, &num_drag_types);
+
+	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_BUTTON3_MASK,
+		 drag_types,
+		 num_drag_types,
+		 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK);
+
 	view->details->drag_dest = 
 		nautilus_tree_view_drag_dest_new (view->details->tree_view);
 
/* 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__ */
/* 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);
  /* FIXME */
/*  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_new0 (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 (path && 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);

      if (priv_data->drag_data_get_handler == 0) 
	{
	  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;
    }

  if (path) 
    {
      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);
  /* FIXME */

}



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