diff options
-rw-r--r-- | ChangeLog-20000414 | 26 | ||||
-rw-r--r-- | libnautilus-extensions/nautilus-icon-container.c | 21 | ||||
-rw-r--r-- | libnautilus-extensions/nautilus-icon-dnd.c | 396 | ||||
-rw-r--r-- | libnautilus-extensions/nautilus-icon-dnd.h | 10 | ||||
-rw-r--r-- | libnautilus-private/nautilus-icon-container.c | 21 | ||||
-rw-r--r-- | libnautilus-private/nautilus-icon-dnd.c | 396 | ||||
-rw-r--r-- | libnautilus-private/nautilus-icon-dnd.h | 10 | ||||
-rw-r--r-- | libnautilus/nautilus-icon-container.c | 21 | ||||
-rw-r--r-- | libnautilus/nautilus-icon-dnd.c | 396 | ||||
-rw-r--r-- | libnautilus/nautilus-icon-dnd.h | 10 |
10 files changed, 1154 insertions, 153 deletions
diff --git a/ChangeLog-20000414 b/ChangeLog-20000414 index 3013b601c..22920ef31 100644 --- a/ChangeLog-20000414 +++ b/ChangeLog-20000414 @@ -1,3 +1,29 @@ +2000-04-10 Pavel Cisler <pavel@eazel.com> + Nasty hack to allow drag&drop actions to respond to modifier keys + and the drop context properly. This will one day get fixed by + adding proper hooks to Gtk+ and using them. For now we have to + do this. + + * libnautilus/nautilus-icon-dnd.h: + * libnautilus/nautilus-icon-dnd.c: + (nautilus_icon_dnd_update_drop_action), + (nautilus_icon_dnd_modifier_based_action), + (nautilus_icon_dnd_get_event_actions), + (nautilus_icon_dnd_get_event_time), + (nautilus_icon_dnd_source_check_selection), + (nautilus_icon_dnd_update), + (nautilus_icon_drag_key_callback), + (nautilus_icon_dnd_begin_drag), (drag_drop_callback): + Pull in a bunch of code from gtkdnd.c. Add new call to + allow overriding drop actions based on modifiers and the drop + context. Connect the "key_press_event" and "key_release_event" + to the copied code. All this to be able to call + nautilus_icon_dnd_get_event_actions instead of the broken + and private gtk_drag_get_event_actions. + I still have to add code to change the default action based + on the drop target between copy and move based whether the + source and drop containers are the same, etc. + 2000-04-10 Andy Hertzfeld <andy@eazel.com> * components/rpmview/nautilus-rpmview.c: diff --git a/libnautilus-extensions/nautilus-icon-container.c b/libnautilus-extensions/nautilus-icon-container.c index 0ccc8ad8e..a656ebd10 100644 --- a/libnautilus-extensions/nautilus-icon-container.c +++ b/libnautilus-extensions/nautilus-icon-container.c @@ -1667,20 +1667,12 @@ motion_notify_event (GtkWidget *widget, */ motion->x = details->drag_x; motion->y = details->drag_y; - + + /* drag action passed in here gets updated in + * nautilus_icon_dnd_update_drop_action + */ nautilus_icon_dnd_begin_drag (container, - GDK_ACTION_MOVE -#if 0 - /* - * FIXME: - * - * disable copy operations for now -- the default drop action is - * wrong (should be move, not copy) and the copy engine doesn't - * handle conflicts correctly yet - */ - | GDK_ACTION_COPY -#endif - , + GDK_ACTION_MOVE, details->drag_button, motion); } @@ -1700,6 +1692,9 @@ key_press_event (GtkWidget *widget, { NautilusIconContainer *container; + /* allow the drag state update the drag action if modifiers changed */ + nautilus_icon_dnd_update_drop_action (widget); + if (NAUTILUS_CALL_PARENT_CLASS (GTK_WIDGET_CLASS, key_press_event, (widget, event))) { return TRUE; } diff --git a/libnautilus-extensions/nautilus-icon-dnd.c b/libnautilus-extensions/nautilus-icon-dnd.c index 503ce3dae..05a041dd3 100644 --- a/libnautilus-extensions/nautilus-icon-dnd.c +++ b/libnautilus-extensions/nautilus-icon-dnd.c @@ -31,10 +31,13 @@ #include <math.h> #include <string.h> #include <stdio.h> +#include <gdk/gdkx.h> +#include <gdk/gdkkeysyms.h> #include <gtk/gtksignal.h> #include <libgnomevfs/gnome-vfs-uri.h> #include "nautilus-glib-extensions.h" #include "nautilus-gtk-extensions.h" +#include "nautilus-gtk-macros.h" #include "nautilus-gnome-extensions.h" #include "nautilus-background.h" #include "nautilus-graphic-effects.h" @@ -42,6 +45,11 @@ #include <libgnomeui/gnome-canvas-rect-ellipse.h> #include "nautilus-icon-private.h" +static int nautilus_icon_drag_key_callback (GtkWidget *widget, GdkEventKey *event, + gpointer data); +static gboolean drag_drop_callback (GtkWidget *widget, GdkDragContext *context, + int x, int y, guint32 time, gpointer data); + typedef struct { char *uri; gboolean got_icon_position; @@ -501,6 +509,8 @@ drag_motion_callback (GtkWidget *widget, int x, int y, guint32 time) { + nautilus_icon_dnd_update_drop_action (widget); + nautilus_icon_container_ensure_drag_data (NAUTILUS_ICON_CONTAINER (widget), context, time); nautilus_icon_container_position_shadow (NAUTILUS_ICON_CONTAINER (widget), x, y); @@ -817,43 +827,6 @@ nautilus_icon_container_free_drag_data (NautilusIconContainer *container) } } -static gboolean -drag_drop_callback (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - guint32 time, - gpointer data) -{ - NautilusIconDndInfo *dnd_info; - - dnd_info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info; - - nautilus_icon_container_ensure_drag_data (NAUTILUS_ICON_CONTAINER (widget), context, time); - - g_assert (dnd_info->got_data_type); - switch (dnd_info->data_type) { - case NAUTILUS_ICON_DND_GNOME_ICON_LIST: - nautilus_icon_container_receive_dropped_icons - (NAUTILUS_ICON_CONTAINER (widget), - context, x, y); - gtk_drag_finish (context, TRUE, FALSE, time); - break; - case NAUTILUS_ICON_DND_COLOR: - nautilus_background_receive_dropped_color - (nautilus_get_widget_background (widget), - widget, x, y, dnd_info->selection_data); - gtk_drag_finish (context, TRUE, FALSE, time); - break; - default: - gtk_drag_finish (context, FALSE, FALSE, time); - } - - nautilus_icon_container_free_drag_data (NAUTILUS_ICON_CONTAINER (widget)); - - return FALSE; -} - static void drag_leave_callback (GtkWidget *widget, GdkDragContext *context, @@ -910,6 +883,8 @@ nautilus_icon_dnd_init (NautilusIconContainer *container, GTK_SIGNAL_FUNC (drag_leave_callback), NULL); container->details->dnd_info = dnd_info; + container->details->dnd_info->saved_drag_source_info = NULL; + } void @@ -935,6 +910,59 @@ nautilus_icon_dnd_fini (NautilusIconContainer *container) } +/* During drag&drop keep a saved pointer to the private drag context. + * We also replace the severely broken gtk_drag_get_event_actions. + * To do this we copy a lot of code from gtkdnd.c. + * + * This is a hack-workaround to deal with the inability to override + * drag action feedback in gtk and will be removed once the appropriate + * interface gets added to gtkdnd to do this in a clean way. + * For now we need to mirror the code here + * to allow us to control the drop action based on the modifier + * key state, drop target and other drag&drop state. + * + * FIXME: + */ + +typedef struct GtkDragDestInfo GtkDragDestInfo; +typedef struct GtkDragStatus GtkDragStatus; + +typedef struct GtkDragSourceInfo +{ + GtkWidget *widget; + GtkTargetList *target_list; /* Targets for drag data */ + GdkDragAction possible_actions; /* Actions allowed by source */ + GdkDragContext *context; /* drag context */ + GtkWidget *icon_window; /* Window for drag */ + GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */ + GdkCursor *cursor; /* Cursor for drag */ + gint hot_x, hot_y; /* Hot spot for drag */ + gint button; /* mouse button starting drag */ + + gint status; /* drag status !!! GtkDragStatus in real life*/ + GdkEvent *last_event; /* motion event waiting for response */ + + gint start_x, start_y; /* Initial position */ + gint cur_x, cur_y; /* Current Position */ + + GList *selections; /* selections we've claimed */ + + GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */ + + guint drop_timeout; /* Timeout for aborting drop */ + guint destroy_icon : 1; /* If true, destroy icon_window + */ +} GtkDragSourceInfo; + +static GtkDragSourceInfo * +nautilus_icon_dnd_get_drag_source_info (GtkWidget *widget, GdkDragContext *context) +{ + if (context == NULL) + return NULL; + + return g_dataset_get_data (context, "gtk-info"); +} + void nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, GdkDragAction actions, @@ -950,6 +978,7 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, int x_offset, y_offset; ArtDRect world_rect; ArtIRect window_rect; + GtkDragSourceInfo *info; g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container)); g_return_if_fail (event != NULL); @@ -971,7 +1000,19 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, actions, button, (GdkEvent *) event); - + + /* set up state for overriding the broken gtk_drag_get_event_actions call */ + info = nautilus_icon_dnd_get_drag_source_info (GTK_WIDGET (container), context); + container->details->dnd_info->saved_drag_source_info = info; + + g_assert (info != NULL); + + gtk_signal_connect (GTK_OBJECT (info ? info->ipc_widget : NULL), "key_press_event", + GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), info); + gtk_signal_connect (GTK_OBJECT (info ? info->ipc_widget : NULL), "key_release_event", + GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), info); + + /* create a pixmap and mask to drag with */ pixbuf = nautilus_icon_canvas_item_get_image (container->details->drag_icon->item, NULL); @@ -1009,6 +1050,55 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, x_offset, y_offset); } +static gboolean +drag_drop_callback (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + guint32 time, + gpointer data) +{ + NautilusIconDndInfo *dnd_info; + GtkDragSourceInfo *info; + + dnd_info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info; + + nautilus_icon_container_ensure_drag_data (NAUTILUS_ICON_CONTAINER (widget), context, time); + + g_assert (dnd_info->got_data_type); + switch (dnd_info->data_type) { + case NAUTILUS_ICON_DND_GNOME_ICON_LIST: + nautilus_icon_container_receive_dropped_icons + (NAUTILUS_ICON_CONTAINER (widget), + context, x, y); + gtk_drag_finish (context, TRUE, FALSE, time); + break; + case NAUTILUS_ICON_DND_COLOR: + nautilus_background_receive_dropped_color + (nautilus_get_widget_background (widget), + widget, x, y, dnd_info->selection_data); + gtk_drag_finish (context, TRUE, FALSE, time); + break; + default: + gtk_drag_finish (context, FALSE, FALSE, time); + } + + nautilus_icon_container_free_drag_data (NAUTILUS_ICON_CONTAINER (widget)); + + info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info; + g_assert (info != NULL); + + if (info != NULL) { + gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget), + GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), + info); + } + + NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info = NULL; + + return FALSE; +} + void nautilus_icon_dnd_end_drag (NautilusIconContainer *container) { @@ -1023,3 +1113,233 @@ nautilus_icon_dnd_end_drag (NautilusIconContainer *container) * Can that possibly be right? */ } + +static int +nautilus_icon_dnd_modifier_based_action () +{ + GdkModifierType modifiers; + gdk_window_get_pointer (NULL, NULL, NULL, &modifiers); + + if ((modifiers & GDK_CONTROL_MASK) != 0) { + return GDK_ACTION_COPY; +#if 0 + /* FIXME: + * don't know how to do links yet + */ + } else if ((modifiers & GDK_MOD1_MASK) != 0) { + return GDK_ACTION_LINK; +#endif + } + + return GDK_ACTION_MOVE; +} + +void +nautilus_icon_dnd_update_drop_action (GtkWidget *widget) +{ + GtkDragSourceInfo *info; + + info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info; + if (info == NULL) + return; + + info->possible_actions = nautilus_icon_dnd_modifier_based_action (); +} + +/* Replacement for broken gtk_drag_get_event_actions */ + +static void +nautilus_icon_dnd_get_event_actions (GdkEvent *event, + gint button, + GdkDragAction actions, + GdkDragAction *suggested_action, + GdkDragAction *possible_actions) +{ + *suggested_action = nautilus_icon_dnd_modifier_based_action (); + *possible_actions = *suggested_action; +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +static guint32 +nautilus_icon_dnd_get_event_time (GdkEvent *event) +{ + guint32 tm = GDK_CURRENT_TIME; + + if (event) + switch (event->type) + { + case GDK_MOTION_NOTIFY: + tm = event->motion.time; break; + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + tm = event->button.time; break; + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + tm = event->key.time; break; + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + tm = event->crossing.time; break; + case GDK_PROPERTY_NOTIFY: + tm = event->property.time; break; + case GDK_SELECTION_CLEAR: + case GDK_SELECTION_REQUEST: + case GDK_SELECTION_NOTIFY: + tm = event->selection.time; break; + case GDK_PROXIMITY_IN: + case GDK_PROXIMITY_OUT: + tm = event->proximity.time; break; + default: /* use current time */ + break; + } + + return tm; +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +enum { + TARGET_MOTIF_SUCCESS = 0x40000000, + TARGET_MOTIF_FAILURE, + TARGET_DELETE +}; + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +static void +nautilus_icon_dnd_source_check_selection (GtkDragSourceInfo *info, + GdkAtom selection, + guint32 time) +{ + GList *tmp_list; + + tmp_list = info->selections; + while (tmp_list) + { + if (GPOINTER_TO_UINT (tmp_list->data) == selection) + return; + tmp_list = tmp_list->next; + } + + gtk_selection_owner_set (info->ipc_widget, selection, time); + info->selections = g_list_prepend (info->selections, + GUINT_TO_POINTER (selection)); + + tmp_list = info->target_list->list; + while (tmp_list) + { + GtkTargetPair *pair = tmp_list->data; + + gtk_selection_add_target (info->ipc_widget, + selection, + pair->target, + pair->info); + tmp_list = tmp_list->next; + } + + if (info->context->protocol == GDK_DRAG_PROTO_MOTIF) + { + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE), + TARGET_MOTIF_SUCCESS); + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE), + TARGET_MOTIF_FAILURE); + } + + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("DELETE", FALSE), + TARGET_DELETE); +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +static void +nautilus_icon_dnd_update (GtkDragSourceInfo *info, + gint x_root, + gint y_root, + GdkEvent *event) +{ + GdkDragAction action; + GdkDragAction possible_actions; + GdkWindow *window = NULL; + GdkWindow *dest_window; + GdkDragProtocol protocol; + GdkAtom selection; + guint32 time = nautilus_icon_dnd_get_event_time (event); + + nautilus_icon_dnd_get_event_actions (event, + info->button, + info->possible_actions, + &action, &possible_actions); + info->cur_x = x_root; + info->cur_y = y_root; + + if (info->icon_window) + { + gdk_window_raise (info->icon_window->window); + gtk_widget_set_uposition (info->icon_window, + info->cur_x - info->hot_x, + info->cur_y - info->hot_y); + window = info->icon_window->window; + } + + gdk_drag_find_window (info->context, + window, x_root, y_root, + &dest_window, &protocol); + + if (gdk_drag_motion (info->context, dest_window, protocol, + x_root, y_root, action, + possible_actions, + time)) + { + if (info->last_event) + gdk_event_free ((GdkEvent *)info->last_event); + + info->last_event = gdk_event_copy ((GdkEvent *)event); + } + + if (dest_window) + gdk_window_unref (dest_window); + + selection = gdk_drag_get_selection (info->context); + + if (selection) + nautilus_icon_dnd_source_check_selection (info, selection, time); +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ +static int +nautilus_icon_drag_key_callback (GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + GtkDragSourceInfo *info = (GtkDragSourceInfo *)data; + GdkModifierType state; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE); + g_return_val_if_fail (event != NULL, TRUE); + g_return_val_if_fail (data != NULL, TRUE); + + if (event->type == GDK_KEY_PRESS + && event->keyval == GDK_Escape) { + return FALSE; + } + + /* Now send a "motion" so that the modifier state is updated */ + + /* The state is not yet updated in the event, so we need + * to query it here. We could use XGetModifierMapping, but + * that would be overkill. + */ + gdk_window_get_pointer (GDK_ROOT_PARENT(), NULL, NULL, &state); + + event->state = state; + nautilus_icon_dnd_update (info, info->cur_x, info->cur_y, (GdkEvent *)event); + + return TRUE; +} diff --git a/libnautilus-extensions/nautilus-icon-dnd.h b/libnautilus-extensions/nautilus-icon-dnd.h index 0da67ce12..f75b9aed8 100644 --- a/libnautilus-extensions/nautilus-icon-dnd.h +++ b/libnautilus-extensions/nautilus-icon-dnd.h @@ -71,6 +71,14 @@ struct NautilusIconDndInfo { /* Shadow for the icons being dragged. */ GnomeCanvasItem *shadow; + + + /* During drag&drop keep a saved pointer to the private drag context. + * This is a hack-workaround to deal with the inability to override + * drag action feedback in gtk and will be removed once the appropriate + * interface gets added to gtkdnd to do this in a clean way + */ + gpointer saved_drag_source_info; }; void nautilus_icon_dnd_init (NautilusIconContainer *container, @@ -82,4 +90,6 @@ void nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, GdkEventMotion *event); void nautilus_icon_dnd_end_drag (NautilusIconContainer *container); +void nautilus_icon_dnd_update_drop_action (GtkWidget *widget); + #endif /* NAUTILUS_ICON_DND_H */ diff --git a/libnautilus-private/nautilus-icon-container.c b/libnautilus-private/nautilus-icon-container.c index 0ccc8ad8e..a656ebd10 100644 --- a/libnautilus-private/nautilus-icon-container.c +++ b/libnautilus-private/nautilus-icon-container.c @@ -1667,20 +1667,12 @@ motion_notify_event (GtkWidget *widget, */ motion->x = details->drag_x; motion->y = details->drag_y; - + + /* drag action passed in here gets updated in + * nautilus_icon_dnd_update_drop_action + */ nautilus_icon_dnd_begin_drag (container, - GDK_ACTION_MOVE -#if 0 - /* - * FIXME: - * - * disable copy operations for now -- the default drop action is - * wrong (should be move, not copy) and the copy engine doesn't - * handle conflicts correctly yet - */ - | GDK_ACTION_COPY -#endif - , + GDK_ACTION_MOVE, details->drag_button, motion); } @@ -1700,6 +1692,9 @@ key_press_event (GtkWidget *widget, { NautilusIconContainer *container; + /* allow the drag state update the drag action if modifiers changed */ + nautilus_icon_dnd_update_drop_action (widget); + if (NAUTILUS_CALL_PARENT_CLASS (GTK_WIDGET_CLASS, key_press_event, (widget, event))) { return TRUE; } diff --git a/libnautilus-private/nautilus-icon-dnd.c b/libnautilus-private/nautilus-icon-dnd.c index 503ce3dae..05a041dd3 100644 --- a/libnautilus-private/nautilus-icon-dnd.c +++ b/libnautilus-private/nautilus-icon-dnd.c @@ -31,10 +31,13 @@ #include <math.h> #include <string.h> #include <stdio.h> +#include <gdk/gdkx.h> +#include <gdk/gdkkeysyms.h> #include <gtk/gtksignal.h> #include <libgnomevfs/gnome-vfs-uri.h> #include "nautilus-glib-extensions.h" #include "nautilus-gtk-extensions.h" +#include "nautilus-gtk-macros.h" #include "nautilus-gnome-extensions.h" #include "nautilus-background.h" #include "nautilus-graphic-effects.h" @@ -42,6 +45,11 @@ #include <libgnomeui/gnome-canvas-rect-ellipse.h> #include "nautilus-icon-private.h" +static int nautilus_icon_drag_key_callback (GtkWidget *widget, GdkEventKey *event, + gpointer data); +static gboolean drag_drop_callback (GtkWidget *widget, GdkDragContext *context, + int x, int y, guint32 time, gpointer data); + typedef struct { char *uri; gboolean got_icon_position; @@ -501,6 +509,8 @@ drag_motion_callback (GtkWidget *widget, int x, int y, guint32 time) { + nautilus_icon_dnd_update_drop_action (widget); + nautilus_icon_container_ensure_drag_data (NAUTILUS_ICON_CONTAINER (widget), context, time); nautilus_icon_container_position_shadow (NAUTILUS_ICON_CONTAINER (widget), x, y); @@ -817,43 +827,6 @@ nautilus_icon_container_free_drag_data (NautilusIconContainer *container) } } -static gboolean -drag_drop_callback (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - guint32 time, - gpointer data) -{ - NautilusIconDndInfo *dnd_info; - - dnd_info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info; - - nautilus_icon_container_ensure_drag_data (NAUTILUS_ICON_CONTAINER (widget), context, time); - - g_assert (dnd_info->got_data_type); - switch (dnd_info->data_type) { - case NAUTILUS_ICON_DND_GNOME_ICON_LIST: - nautilus_icon_container_receive_dropped_icons - (NAUTILUS_ICON_CONTAINER (widget), - context, x, y); - gtk_drag_finish (context, TRUE, FALSE, time); - break; - case NAUTILUS_ICON_DND_COLOR: - nautilus_background_receive_dropped_color - (nautilus_get_widget_background (widget), - widget, x, y, dnd_info->selection_data); - gtk_drag_finish (context, TRUE, FALSE, time); - break; - default: - gtk_drag_finish (context, FALSE, FALSE, time); - } - - nautilus_icon_container_free_drag_data (NAUTILUS_ICON_CONTAINER (widget)); - - return FALSE; -} - static void drag_leave_callback (GtkWidget *widget, GdkDragContext *context, @@ -910,6 +883,8 @@ nautilus_icon_dnd_init (NautilusIconContainer *container, GTK_SIGNAL_FUNC (drag_leave_callback), NULL); container->details->dnd_info = dnd_info; + container->details->dnd_info->saved_drag_source_info = NULL; + } void @@ -935,6 +910,59 @@ nautilus_icon_dnd_fini (NautilusIconContainer *container) } +/* During drag&drop keep a saved pointer to the private drag context. + * We also replace the severely broken gtk_drag_get_event_actions. + * To do this we copy a lot of code from gtkdnd.c. + * + * This is a hack-workaround to deal with the inability to override + * drag action feedback in gtk and will be removed once the appropriate + * interface gets added to gtkdnd to do this in a clean way. + * For now we need to mirror the code here + * to allow us to control the drop action based on the modifier + * key state, drop target and other drag&drop state. + * + * FIXME: + */ + +typedef struct GtkDragDestInfo GtkDragDestInfo; +typedef struct GtkDragStatus GtkDragStatus; + +typedef struct GtkDragSourceInfo +{ + GtkWidget *widget; + GtkTargetList *target_list; /* Targets for drag data */ + GdkDragAction possible_actions; /* Actions allowed by source */ + GdkDragContext *context; /* drag context */ + GtkWidget *icon_window; /* Window for drag */ + GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */ + GdkCursor *cursor; /* Cursor for drag */ + gint hot_x, hot_y; /* Hot spot for drag */ + gint button; /* mouse button starting drag */ + + gint status; /* drag status !!! GtkDragStatus in real life*/ + GdkEvent *last_event; /* motion event waiting for response */ + + gint start_x, start_y; /* Initial position */ + gint cur_x, cur_y; /* Current Position */ + + GList *selections; /* selections we've claimed */ + + GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */ + + guint drop_timeout; /* Timeout for aborting drop */ + guint destroy_icon : 1; /* If true, destroy icon_window + */ +} GtkDragSourceInfo; + +static GtkDragSourceInfo * +nautilus_icon_dnd_get_drag_source_info (GtkWidget *widget, GdkDragContext *context) +{ + if (context == NULL) + return NULL; + + return g_dataset_get_data (context, "gtk-info"); +} + void nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, GdkDragAction actions, @@ -950,6 +978,7 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, int x_offset, y_offset; ArtDRect world_rect; ArtIRect window_rect; + GtkDragSourceInfo *info; g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container)); g_return_if_fail (event != NULL); @@ -971,7 +1000,19 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, actions, button, (GdkEvent *) event); - + + /* set up state for overriding the broken gtk_drag_get_event_actions call */ + info = nautilus_icon_dnd_get_drag_source_info (GTK_WIDGET (container), context); + container->details->dnd_info->saved_drag_source_info = info; + + g_assert (info != NULL); + + gtk_signal_connect (GTK_OBJECT (info ? info->ipc_widget : NULL), "key_press_event", + GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), info); + gtk_signal_connect (GTK_OBJECT (info ? info->ipc_widget : NULL), "key_release_event", + GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), info); + + /* create a pixmap and mask to drag with */ pixbuf = nautilus_icon_canvas_item_get_image (container->details->drag_icon->item, NULL); @@ -1009,6 +1050,55 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, x_offset, y_offset); } +static gboolean +drag_drop_callback (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + guint32 time, + gpointer data) +{ + NautilusIconDndInfo *dnd_info; + GtkDragSourceInfo *info; + + dnd_info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info; + + nautilus_icon_container_ensure_drag_data (NAUTILUS_ICON_CONTAINER (widget), context, time); + + g_assert (dnd_info->got_data_type); + switch (dnd_info->data_type) { + case NAUTILUS_ICON_DND_GNOME_ICON_LIST: + nautilus_icon_container_receive_dropped_icons + (NAUTILUS_ICON_CONTAINER (widget), + context, x, y); + gtk_drag_finish (context, TRUE, FALSE, time); + break; + case NAUTILUS_ICON_DND_COLOR: + nautilus_background_receive_dropped_color + (nautilus_get_widget_background (widget), + widget, x, y, dnd_info->selection_data); + gtk_drag_finish (context, TRUE, FALSE, time); + break; + default: + gtk_drag_finish (context, FALSE, FALSE, time); + } + + nautilus_icon_container_free_drag_data (NAUTILUS_ICON_CONTAINER (widget)); + + info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info; + g_assert (info != NULL); + + if (info != NULL) { + gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget), + GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), + info); + } + + NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info = NULL; + + return FALSE; +} + void nautilus_icon_dnd_end_drag (NautilusIconContainer *container) { @@ -1023,3 +1113,233 @@ nautilus_icon_dnd_end_drag (NautilusIconContainer *container) * Can that possibly be right? */ } + +static int +nautilus_icon_dnd_modifier_based_action () +{ + GdkModifierType modifiers; + gdk_window_get_pointer (NULL, NULL, NULL, &modifiers); + + if ((modifiers & GDK_CONTROL_MASK) != 0) { + return GDK_ACTION_COPY; +#if 0 + /* FIXME: + * don't know how to do links yet + */ + } else if ((modifiers & GDK_MOD1_MASK) != 0) { + return GDK_ACTION_LINK; +#endif + } + + return GDK_ACTION_MOVE; +} + +void +nautilus_icon_dnd_update_drop_action (GtkWidget *widget) +{ + GtkDragSourceInfo *info; + + info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info; + if (info == NULL) + return; + + info->possible_actions = nautilus_icon_dnd_modifier_based_action (); +} + +/* Replacement for broken gtk_drag_get_event_actions */ + +static void +nautilus_icon_dnd_get_event_actions (GdkEvent *event, + gint button, + GdkDragAction actions, + GdkDragAction *suggested_action, + GdkDragAction *possible_actions) +{ + *suggested_action = nautilus_icon_dnd_modifier_based_action (); + *possible_actions = *suggested_action; +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +static guint32 +nautilus_icon_dnd_get_event_time (GdkEvent *event) +{ + guint32 tm = GDK_CURRENT_TIME; + + if (event) + switch (event->type) + { + case GDK_MOTION_NOTIFY: + tm = event->motion.time; break; + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + tm = event->button.time; break; + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + tm = event->key.time; break; + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + tm = event->crossing.time; break; + case GDK_PROPERTY_NOTIFY: + tm = event->property.time; break; + case GDK_SELECTION_CLEAR: + case GDK_SELECTION_REQUEST: + case GDK_SELECTION_NOTIFY: + tm = event->selection.time; break; + case GDK_PROXIMITY_IN: + case GDK_PROXIMITY_OUT: + tm = event->proximity.time; break; + default: /* use current time */ + break; + } + + return tm; +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +enum { + TARGET_MOTIF_SUCCESS = 0x40000000, + TARGET_MOTIF_FAILURE, + TARGET_DELETE +}; + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +static void +nautilus_icon_dnd_source_check_selection (GtkDragSourceInfo *info, + GdkAtom selection, + guint32 time) +{ + GList *tmp_list; + + tmp_list = info->selections; + while (tmp_list) + { + if (GPOINTER_TO_UINT (tmp_list->data) == selection) + return; + tmp_list = tmp_list->next; + } + + gtk_selection_owner_set (info->ipc_widget, selection, time); + info->selections = g_list_prepend (info->selections, + GUINT_TO_POINTER (selection)); + + tmp_list = info->target_list->list; + while (tmp_list) + { + GtkTargetPair *pair = tmp_list->data; + + gtk_selection_add_target (info->ipc_widget, + selection, + pair->target, + pair->info); + tmp_list = tmp_list->next; + } + + if (info->context->protocol == GDK_DRAG_PROTO_MOTIF) + { + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE), + TARGET_MOTIF_SUCCESS); + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE), + TARGET_MOTIF_FAILURE); + } + + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("DELETE", FALSE), + TARGET_DELETE); +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +static void +nautilus_icon_dnd_update (GtkDragSourceInfo *info, + gint x_root, + gint y_root, + GdkEvent *event) +{ + GdkDragAction action; + GdkDragAction possible_actions; + GdkWindow *window = NULL; + GdkWindow *dest_window; + GdkDragProtocol protocol; + GdkAtom selection; + guint32 time = nautilus_icon_dnd_get_event_time (event); + + nautilus_icon_dnd_get_event_actions (event, + info->button, + info->possible_actions, + &action, &possible_actions); + info->cur_x = x_root; + info->cur_y = y_root; + + if (info->icon_window) + { + gdk_window_raise (info->icon_window->window); + gtk_widget_set_uposition (info->icon_window, + info->cur_x - info->hot_x, + info->cur_y - info->hot_y); + window = info->icon_window->window; + } + + gdk_drag_find_window (info->context, + window, x_root, y_root, + &dest_window, &protocol); + + if (gdk_drag_motion (info->context, dest_window, protocol, + x_root, y_root, action, + possible_actions, + time)) + { + if (info->last_event) + gdk_event_free ((GdkEvent *)info->last_event); + + info->last_event = gdk_event_copy ((GdkEvent *)event); + } + + if (dest_window) + gdk_window_unref (dest_window); + + selection = gdk_drag_get_selection (info->context); + + if (selection) + nautilus_icon_dnd_source_check_selection (info, selection, time); +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ +static int +nautilus_icon_drag_key_callback (GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + GtkDragSourceInfo *info = (GtkDragSourceInfo *)data; + GdkModifierType state; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE); + g_return_val_if_fail (event != NULL, TRUE); + g_return_val_if_fail (data != NULL, TRUE); + + if (event->type == GDK_KEY_PRESS + && event->keyval == GDK_Escape) { + return FALSE; + } + + /* Now send a "motion" so that the modifier state is updated */ + + /* The state is not yet updated in the event, so we need + * to query it here. We could use XGetModifierMapping, but + * that would be overkill. + */ + gdk_window_get_pointer (GDK_ROOT_PARENT(), NULL, NULL, &state); + + event->state = state; + nautilus_icon_dnd_update (info, info->cur_x, info->cur_y, (GdkEvent *)event); + + return TRUE; +} diff --git a/libnautilus-private/nautilus-icon-dnd.h b/libnautilus-private/nautilus-icon-dnd.h index 0da67ce12..f75b9aed8 100644 --- a/libnautilus-private/nautilus-icon-dnd.h +++ b/libnautilus-private/nautilus-icon-dnd.h @@ -71,6 +71,14 @@ struct NautilusIconDndInfo { /* Shadow for the icons being dragged. */ GnomeCanvasItem *shadow; + + + /* During drag&drop keep a saved pointer to the private drag context. + * This is a hack-workaround to deal with the inability to override + * drag action feedback in gtk and will be removed once the appropriate + * interface gets added to gtkdnd to do this in a clean way + */ + gpointer saved_drag_source_info; }; void nautilus_icon_dnd_init (NautilusIconContainer *container, @@ -82,4 +90,6 @@ void nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, GdkEventMotion *event); void nautilus_icon_dnd_end_drag (NautilusIconContainer *container); +void nautilus_icon_dnd_update_drop_action (GtkWidget *widget); + #endif /* NAUTILUS_ICON_DND_H */ diff --git a/libnautilus/nautilus-icon-container.c b/libnautilus/nautilus-icon-container.c index 0ccc8ad8e..a656ebd10 100644 --- a/libnautilus/nautilus-icon-container.c +++ b/libnautilus/nautilus-icon-container.c @@ -1667,20 +1667,12 @@ motion_notify_event (GtkWidget *widget, */ motion->x = details->drag_x; motion->y = details->drag_y; - + + /* drag action passed in here gets updated in + * nautilus_icon_dnd_update_drop_action + */ nautilus_icon_dnd_begin_drag (container, - GDK_ACTION_MOVE -#if 0 - /* - * FIXME: - * - * disable copy operations for now -- the default drop action is - * wrong (should be move, not copy) and the copy engine doesn't - * handle conflicts correctly yet - */ - | GDK_ACTION_COPY -#endif - , + GDK_ACTION_MOVE, details->drag_button, motion); } @@ -1700,6 +1692,9 @@ key_press_event (GtkWidget *widget, { NautilusIconContainer *container; + /* allow the drag state update the drag action if modifiers changed */ + nautilus_icon_dnd_update_drop_action (widget); + if (NAUTILUS_CALL_PARENT_CLASS (GTK_WIDGET_CLASS, key_press_event, (widget, event))) { return TRUE; } diff --git a/libnautilus/nautilus-icon-dnd.c b/libnautilus/nautilus-icon-dnd.c index 503ce3dae..05a041dd3 100644 --- a/libnautilus/nautilus-icon-dnd.c +++ b/libnautilus/nautilus-icon-dnd.c @@ -31,10 +31,13 @@ #include <math.h> #include <string.h> #include <stdio.h> +#include <gdk/gdkx.h> +#include <gdk/gdkkeysyms.h> #include <gtk/gtksignal.h> #include <libgnomevfs/gnome-vfs-uri.h> #include "nautilus-glib-extensions.h" #include "nautilus-gtk-extensions.h" +#include "nautilus-gtk-macros.h" #include "nautilus-gnome-extensions.h" #include "nautilus-background.h" #include "nautilus-graphic-effects.h" @@ -42,6 +45,11 @@ #include <libgnomeui/gnome-canvas-rect-ellipse.h> #include "nautilus-icon-private.h" +static int nautilus_icon_drag_key_callback (GtkWidget *widget, GdkEventKey *event, + gpointer data); +static gboolean drag_drop_callback (GtkWidget *widget, GdkDragContext *context, + int x, int y, guint32 time, gpointer data); + typedef struct { char *uri; gboolean got_icon_position; @@ -501,6 +509,8 @@ drag_motion_callback (GtkWidget *widget, int x, int y, guint32 time) { + nautilus_icon_dnd_update_drop_action (widget); + nautilus_icon_container_ensure_drag_data (NAUTILUS_ICON_CONTAINER (widget), context, time); nautilus_icon_container_position_shadow (NAUTILUS_ICON_CONTAINER (widget), x, y); @@ -817,43 +827,6 @@ nautilus_icon_container_free_drag_data (NautilusIconContainer *container) } } -static gboolean -drag_drop_callback (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - guint32 time, - gpointer data) -{ - NautilusIconDndInfo *dnd_info; - - dnd_info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info; - - nautilus_icon_container_ensure_drag_data (NAUTILUS_ICON_CONTAINER (widget), context, time); - - g_assert (dnd_info->got_data_type); - switch (dnd_info->data_type) { - case NAUTILUS_ICON_DND_GNOME_ICON_LIST: - nautilus_icon_container_receive_dropped_icons - (NAUTILUS_ICON_CONTAINER (widget), - context, x, y); - gtk_drag_finish (context, TRUE, FALSE, time); - break; - case NAUTILUS_ICON_DND_COLOR: - nautilus_background_receive_dropped_color - (nautilus_get_widget_background (widget), - widget, x, y, dnd_info->selection_data); - gtk_drag_finish (context, TRUE, FALSE, time); - break; - default: - gtk_drag_finish (context, FALSE, FALSE, time); - } - - nautilus_icon_container_free_drag_data (NAUTILUS_ICON_CONTAINER (widget)); - - return FALSE; -} - static void drag_leave_callback (GtkWidget *widget, GdkDragContext *context, @@ -910,6 +883,8 @@ nautilus_icon_dnd_init (NautilusIconContainer *container, GTK_SIGNAL_FUNC (drag_leave_callback), NULL); container->details->dnd_info = dnd_info; + container->details->dnd_info->saved_drag_source_info = NULL; + } void @@ -935,6 +910,59 @@ nautilus_icon_dnd_fini (NautilusIconContainer *container) } +/* During drag&drop keep a saved pointer to the private drag context. + * We also replace the severely broken gtk_drag_get_event_actions. + * To do this we copy a lot of code from gtkdnd.c. + * + * This is a hack-workaround to deal with the inability to override + * drag action feedback in gtk and will be removed once the appropriate + * interface gets added to gtkdnd to do this in a clean way. + * For now we need to mirror the code here + * to allow us to control the drop action based on the modifier + * key state, drop target and other drag&drop state. + * + * FIXME: + */ + +typedef struct GtkDragDestInfo GtkDragDestInfo; +typedef struct GtkDragStatus GtkDragStatus; + +typedef struct GtkDragSourceInfo +{ + GtkWidget *widget; + GtkTargetList *target_list; /* Targets for drag data */ + GdkDragAction possible_actions; /* Actions allowed by source */ + GdkDragContext *context; /* drag context */ + GtkWidget *icon_window; /* Window for drag */ + GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */ + GdkCursor *cursor; /* Cursor for drag */ + gint hot_x, hot_y; /* Hot spot for drag */ + gint button; /* mouse button starting drag */ + + gint status; /* drag status !!! GtkDragStatus in real life*/ + GdkEvent *last_event; /* motion event waiting for response */ + + gint start_x, start_y; /* Initial position */ + gint cur_x, cur_y; /* Current Position */ + + GList *selections; /* selections we've claimed */ + + GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */ + + guint drop_timeout; /* Timeout for aborting drop */ + guint destroy_icon : 1; /* If true, destroy icon_window + */ +} GtkDragSourceInfo; + +static GtkDragSourceInfo * +nautilus_icon_dnd_get_drag_source_info (GtkWidget *widget, GdkDragContext *context) +{ + if (context == NULL) + return NULL; + + return g_dataset_get_data (context, "gtk-info"); +} + void nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, GdkDragAction actions, @@ -950,6 +978,7 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, int x_offset, y_offset; ArtDRect world_rect; ArtIRect window_rect; + GtkDragSourceInfo *info; g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container)); g_return_if_fail (event != NULL); @@ -971,7 +1000,19 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, actions, button, (GdkEvent *) event); - + + /* set up state for overriding the broken gtk_drag_get_event_actions call */ + info = nautilus_icon_dnd_get_drag_source_info (GTK_WIDGET (container), context); + container->details->dnd_info->saved_drag_source_info = info; + + g_assert (info != NULL); + + gtk_signal_connect (GTK_OBJECT (info ? info->ipc_widget : NULL), "key_press_event", + GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), info); + gtk_signal_connect (GTK_OBJECT (info ? info->ipc_widget : NULL), "key_release_event", + GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), info); + + /* create a pixmap and mask to drag with */ pixbuf = nautilus_icon_canvas_item_get_image (container->details->drag_icon->item, NULL); @@ -1009,6 +1050,55 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, x_offset, y_offset); } +static gboolean +drag_drop_callback (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + guint32 time, + gpointer data) +{ + NautilusIconDndInfo *dnd_info; + GtkDragSourceInfo *info; + + dnd_info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info; + + nautilus_icon_container_ensure_drag_data (NAUTILUS_ICON_CONTAINER (widget), context, time); + + g_assert (dnd_info->got_data_type); + switch (dnd_info->data_type) { + case NAUTILUS_ICON_DND_GNOME_ICON_LIST: + nautilus_icon_container_receive_dropped_icons + (NAUTILUS_ICON_CONTAINER (widget), + context, x, y); + gtk_drag_finish (context, TRUE, FALSE, time); + break; + case NAUTILUS_ICON_DND_COLOR: + nautilus_background_receive_dropped_color + (nautilus_get_widget_background (widget), + widget, x, y, dnd_info->selection_data); + gtk_drag_finish (context, TRUE, FALSE, time); + break; + default: + gtk_drag_finish (context, FALSE, FALSE, time); + } + + nautilus_icon_container_free_drag_data (NAUTILUS_ICON_CONTAINER (widget)); + + info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info; + g_assert (info != NULL); + + if (info != NULL) { + gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget), + GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), + info); + } + + NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info = NULL; + + return FALSE; +} + void nautilus_icon_dnd_end_drag (NautilusIconContainer *container) { @@ -1023,3 +1113,233 @@ nautilus_icon_dnd_end_drag (NautilusIconContainer *container) * Can that possibly be right? */ } + +static int +nautilus_icon_dnd_modifier_based_action () +{ + GdkModifierType modifiers; + gdk_window_get_pointer (NULL, NULL, NULL, &modifiers); + + if ((modifiers & GDK_CONTROL_MASK) != 0) { + return GDK_ACTION_COPY; +#if 0 + /* FIXME: + * don't know how to do links yet + */ + } else if ((modifiers & GDK_MOD1_MASK) != 0) { + return GDK_ACTION_LINK; +#endif + } + + return GDK_ACTION_MOVE; +} + +void +nautilus_icon_dnd_update_drop_action (GtkWidget *widget) +{ + GtkDragSourceInfo *info; + + info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info; + if (info == NULL) + return; + + info->possible_actions = nautilus_icon_dnd_modifier_based_action (); +} + +/* Replacement for broken gtk_drag_get_event_actions */ + +static void +nautilus_icon_dnd_get_event_actions (GdkEvent *event, + gint button, + GdkDragAction actions, + GdkDragAction *suggested_action, + GdkDragAction *possible_actions) +{ + *suggested_action = nautilus_icon_dnd_modifier_based_action (); + *possible_actions = *suggested_action; +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +static guint32 +nautilus_icon_dnd_get_event_time (GdkEvent *event) +{ + guint32 tm = GDK_CURRENT_TIME; + + if (event) + switch (event->type) + { + case GDK_MOTION_NOTIFY: + tm = event->motion.time; break; + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + tm = event->button.time; break; + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + tm = event->key.time; break; + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + tm = event->crossing.time; break; + case GDK_PROPERTY_NOTIFY: + tm = event->property.time; break; + case GDK_SELECTION_CLEAR: + case GDK_SELECTION_REQUEST: + case GDK_SELECTION_NOTIFY: + tm = event->selection.time; break; + case GDK_PROXIMITY_IN: + case GDK_PROXIMITY_OUT: + tm = event->proximity.time; break; + default: /* use current time */ + break; + } + + return tm; +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +enum { + TARGET_MOTIF_SUCCESS = 0x40000000, + TARGET_MOTIF_FAILURE, + TARGET_DELETE +}; + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +static void +nautilus_icon_dnd_source_check_selection (GtkDragSourceInfo *info, + GdkAtom selection, + guint32 time) +{ + GList *tmp_list; + + tmp_list = info->selections; + while (tmp_list) + { + if (GPOINTER_TO_UINT (tmp_list->data) == selection) + return; + tmp_list = tmp_list->next; + } + + gtk_selection_owner_set (info->ipc_widget, selection, time); + info->selections = g_list_prepend (info->selections, + GUINT_TO_POINTER (selection)); + + tmp_list = info->target_list->list; + while (tmp_list) + { + GtkTargetPair *pair = tmp_list->data; + + gtk_selection_add_target (info->ipc_widget, + selection, + pair->target, + pair->info); + tmp_list = tmp_list->next; + } + + if (info->context->protocol == GDK_DRAG_PROTO_MOTIF) + { + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE), + TARGET_MOTIF_SUCCESS); + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE), + TARGET_MOTIF_FAILURE); + } + + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("DELETE", FALSE), + TARGET_DELETE); +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ + +static void +nautilus_icon_dnd_update (GtkDragSourceInfo *info, + gint x_root, + gint y_root, + GdkEvent *event) +{ + GdkDragAction action; + GdkDragAction possible_actions; + GdkWindow *window = NULL; + GdkWindow *dest_window; + GdkDragProtocol protocol; + GdkAtom selection; + guint32 time = nautilus_icon_dnd_get_event_time (event); + + nautilus_icon_dnd_get_event_actions (event, + info->button, + info->possible_actions, + &action, &possible_actions); + info->cur_x = x_root; + info->cur_y = y_root; + + if (info->icon_window) + { + gdk_window_raise (info->icon_window->window); + gtk_widget_set_uposition (info->icon_window, + info->cur_x - info->hot_x, + info->cur_y - info->hot_y); + window = info->icon_window->window; + } + + gdk_drag_find_window (info->context, + window, x_root, y_root, + &dest_window, &protocol); + + if (gdk_drag_motion (info->context, dest_window, protocol, + x_root, y_root, action, + possible_actions, + time)) + { + if (info->last_event) + gdk_event_free ((GdkEvent *)info->last_event); + + info->last_event = gdk_event_copy ((GdkEvent *)event); + } + + if (dest_window) + gdk_window_unref (dest_window); + + selection = gdk_drag_get_selection (info->context); + + if (selection) + nautilus_icon_dnd_source_check_selection (info, selection, time); +} + +/* Copied from gtkdnd.c to work around broken gtk_drag_get_event_actions */ +static int +nautilus_icon_drag_key_callback (GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + GtkDragSourceInfo *info = (GtkDragSourceInfo *)data; + GdkModifierType state; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE); + g_return_val_if_fail (event != NULL, TRUE); + g_return_val_if_fail (data != NULL, TRUE); + + if (event->type == GDK_KEY_PRESS + && event->keyval == GDK_Escape) { + return FALSE; + } + + /* Now send a "motion" so that the modifier state is updated */ + + /* The state is not yet updated in the event, so we need + * to query it here. We could use XGetModifierMapping, but + * that would be overkill. + */ + gdk_window_get_pointer (GDK_ROOT_PARENT(), NULL, NULL, &state); + + event->state = state; + nautilus_icon_dnd_update (info, info->cur_x, info->cur_y, (GdkEvent *)event); + + return TRUE; +} diff --git a/libnautilus/nautilus-icon-dnd.h b/libnautilus/nautilus-icon-dnd.h index 0da67ce12..f75b9aed8 100644 --- a/libnautilus/nautilus-icon-dnd.h +++ b/libnautilus/nautilus-icon-dnd.h @@ -71,6 +71,14 @@ struct NautilusIconDndInfo { /* Shadow for the icons being dragged. */ GnomeCanvasItem *shadow; + + + /* During drag&drop keep a saved pointer to the private drag context. + * This is a hack-workaround to deal with the inability to override + * drag action feedback in gtk and will be removed once the appropriate + * interface gets added to gtkdnd to do this in a clean way + */ + gpointer saved_drag_source_info; }; void nautilus_icon_dnd_init (NautilusIconContainer *container, @@ -82,4 +90,6 @@ void nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, GdkEventMotion *event); void nautilus_icon_dnd_end_drag (NautilusIconContainer *container); +void nautilus_icon_dnd_update_drop_action (GtkWidget *widget); + #endif /* NAUTILUS_ICON_DND_H */ |