Commit 34733e86 authored by Dave Camp's avatar Dave Camp Committed by Dave Camp

Added eggtreemultidnd.[ch].

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.
parent b5988f6a
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
......
......@@ -36,6 +36,8 @@ marshal_sources = \
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 \
......
/* 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 */
}
/* 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__ */
......@@ -49,6 +49,7 @@ icons/sierra/sierra.xml
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-search-uri.c
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
......
......@@ -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 @@ struct FMListModelDetails {
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 @@ static const AttributeEntry attributes[] = {
{ "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 @@ fm_list_model_has_default_sort_func (GtkTreeSortable *sortable)
}
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 @@ fm_list_model_get_column_id_from_zoom_level (NautilusZoomLevel zoom_level)
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);
}