diff options
31 files changed, 595 insertions, 57 deletions
diff --git a/ChangeLog-20000414 b/ChangeLog-20000414 index 48f63618f..9c3ee9e60 100644 --- a/ChangeLog-20000414 +++ b/ChangeLog-20000414 @@ -1,3 +1,53 @@ +2000-04-11 Pavel Cisler <pavel@eazel.com> + + * libnautilus/nautilus-icon-dnd.c: + * libnautilus/nautilus-icon-dnd.h: + (nautilus_icon_dnd_update_drop_target): + Determine if a drop target can accept drop, update highlight for + drop accordingly. + (nautilus_icon_dnd_update_drop_target): + Add drop highlighting for dragged items that can be accepted by a + drop target. + (nautilus_icon_canvas_item_can_accept_item): + Implement the call. + (nautilus_icon_canvas_item_can_accept_items): + Put a cap on the number of items checked as accepted by drop target + to not lock up the machine when dragging lots of items. + (nautilus_icon_dnd_modifier_based_action): + Tweak the modifier keys -- use Alt to force copy. + + * libnautilus/nautilus-icon-container.c: + * libnautilus/nautilus-icon-container.h: + * src/file-manager/fm-directory-view.c: + * src/file-manager/fm-directory-view.h: + * src/file-manager/fm-icon-view.c: + (can_accept_item), (fm_directory_view_can_accept_item): + New signal -- helps figuring out if potential drop target can + accept dropped URIs. For now returns TRUE for any directory that + doesn't match the dragged item. + + * libnautilus/nautilus-icon-container.c: + (item_event_callback): + Support for turning the "highlight for drop" state of an icon on/off -- + during drag&drop don't let the "enter notify" signal reach the canvas item + so that the item won't get prelit. + + * libnautilus/nautilus-icon-canvas-item.c: + (nautilus_icon_canvas_item_event): + Turn off highlight for drop as well as prelight on leave event. + This is a fallback in case the drag&drop for some reason leaves + the target icon highlighted for drop after a drag ends. + + * libnautilus/nautilus-gtk-extensions.c: + * libnautilus/nautilus-gtk-extensions.h: + (nautilus_gtk_marshal_INT__POINTER_POINTER): + Added another binder function. + + * libnautilus/nautilus-file.c: + * libnautilus/nautilus-file.h: + (nautilus_file_matches_uri): Added a utility call used to detect + a drop on self. + 2000-04-10 Andy Hertzfeld <andy@eazel.com> * components/rpmview/nautilus-rpmview.c: diff --git a/libnautilus-extensions/nautilus-file.c b/libnautilus-extensions/nautilus-file.c index 0ed7bf771..d98a2e06e 100644 --- a/libnautilus-extensions/nautilus-file.c +++ b/libnautilus-extensions/nautilus-file.c @@ -360,6 +360,34 @@ nautilus_file_rename (NautilusFile *file, const char *new_name) return result; } +gboolean +nautilus_file_matches_uri (NautilusFile *file, const char *uri_string) +{ + GnomeVFSURI *match_uri; + GnomeVFSURI *file_uri; + gboolean result; + + g_return_val_if_fail (NAUTILUS_IS_FILE (file), FALSE); + g_return_val_if_fail (uri_string != NULL, FALSE); + + match_uri = gnome_vfs_uri_new (uri_string); + if (match_uri == NULL) + return FALSE; + + result = FALSE; + file_uri = gnome_vfs_uri_append_path (file->details->directory->details->uri, + file->details->info->name); + + if (match_uri != NULL) { + result = gnome_vfs_uri_equal (file_uri, match_uri); + } + + gnome_vfs_uri_unref (file_uri); + gnome_vfs_uri_unref (match_uri); + + return result; +} + static int nautilus_file_compare_by_size_with_directories (NautilusFile *file_1, NautilusFile *file_2) { diff --git a/libnautilus-extensions/nautilus-file.h b/libnautilus-extensions/nautilus-file.h index 6cc0c79be..27bb41b76 100644 --- a/libnautilus-extensions/nautilus-file.h +++ b/libnautilus-extensions/nautilus-file.h @@ -122,6 +122,10 @@ void nautilus_file_set_metadata (NautilusFile * char * nautilus_file_get_string_attribute (NautilusFile *file, const char *attribute_name); +/* Matching with another URI*/ +gboolean nautilus_file_matches_uri (NautilusFile *file, + const char *uri); + /* Comparing two file objects for sorting */ int nautilus_file_compare_for_sort (NautilusFile *file_1, NautilusFile *file_2, diff --git a/libnautilus-extensions/nautilus-gtk-extensions.c b/libnautilus-extensions/nautilus-gtk-extensions.c index 99d8d1523..b89404644 100644 --- a/libnautilus-extensions/nautilus-gtk-extensions.c +++ b/libnautilus-extensions/nautilus-gtk-extensions.c @@ -320,6 +320,20 @@ nautilus_gtk_marshal_POINTER__POINTER (GtkObject *object, } void +nautilus_gtk_marshal_INT__POINTER_POINTER (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) +{ + * GTK_RETLOC_INT (args[2]) = + (* (int (*)(GtkObject *, gpointer, gpointer, gpointer)) func) + (object, + GTK_VALUE_POINTER (args[0]), + GTK_VALUE_POINTER (args[1]), + func_data); +} + +void nautilus_gtk_marshal_POINTER__POINTER_POINTER (GtkObject *object, GtkSignalFunc func, gpointer func_data, diff --git a/libnautilus-extensions/nautilus-gtk-extensions.h b/libnautilus-extensions/nautilus-gtk-extensions.h index 88f0684f1..61f1f5187 100644 --- a/libnautilus-extensions/nautilus-gtk-extensions.h +++ b/libnautilus-extensions/nautilus-gtk-extensions.h @@ -38,6 +38,7 @@ #define nautilus_gtk_marshal_STRING__POINTER_STRING nautilus_gtk_marshal_POINTER__POINTER_POINTER #define nautilus_gtk_marshal_STRING__POINTER_POINTER_POINTER nautilus_gtk_marshal_POINTER__POINTER_POINTER_POINTER #define nautilus_gtk_marshal_STRING__POINTER_POINTER_STRING nautilus_gtk_marshal_POINTER__POINTER_POINTER_POINTER +#define nautilus_gtk_marshal_INT__POINTER_STRING nautilus_gtk_marshal_INT__POINTER_POINTER #define NAUTILUS_DEFAULT_POPUP_MENU_DISPLACEMENT 2 @@ -101,6 +102,10 @@ void nautilus_gtk_marshal_POINTER__POINTER GtkSignalFunc func, gpointer func_data, GtkArg *args); +void nautilus_gtk_marshal_INT__POINTER_POINTER (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); void nautilus_gtk_marshal_POINTER__POINTER_POINTER (GtkObject *object, GtkSignalFunc func, gpointer func_data, diff --git a/libnautilus-extensions/nautilus-icon-canvas-item.c b/libnautilus-extensions/nautilus-icon-canvas-item.c index 268a58459..bc5be35e9 100644 --- a/libnautilus-extensions/nautilus-icon-canvas-item.c +++ b/libnautilus-extensions/nautilus-icon-canvas-item.c @@ -1018,8 +1018,14 @@ nautilus_icon_canvas_item_event (GnomeCanvasItem *item, GdkEvent *event) return TRUE; case GDK_LEAVE_NOTIFY: - if (icon_item->details->is_prelit) { + if (icon_item->details->is_prelit + || icon_item->details->is_highlighted_for_drop) { + /* When leaving, turn of the prelight state and the + * higlighted for drop. The latter gets turned on + * by the drag&drop motion callback. + */ icon_item->details->is_prelit = FALSE; + icon_item->details->is_highlighted_for_drop = FALSE; gnome_canvas_item_request_update (item); } return TRUE; diff --git a/libnautilus-extensions/nautilus-icon-container.c b/libnautilus-extensions/nautilus-icon-container.c index a656ebd10..1d656703f 100644 --- a/libnautilus-extensions/nautilus-icon-container.c +++ b/libnautilus-extensions/nautilus-icon-container.c @@ -106,6 +106,7 @@ enum { SELECTION_CHANGED, MOVE_COPY_ITEMS, GET_CONTAINER_URI, + CAN_ACCEPT_ITEM, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; @@ -1868,6 +1869,16 @@ nautilus_icon_container_initialize_class (NautilusIconContainerClass *class) get_container_uri), nautilus_gtk_marshal_STRING__NONE, GTK_TYPE_STRING, 0); + signals[CAN_ACCEPT_ITEM] + = gtk_signal_new ("can_accept_item", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (NautilusIconContainerClass, + can_accept_item), + nautilus_gtk_marshal_INT__POINTER_STRING, + GTK_TYPE_INT, 2, + GTK_TYPE_POINTER, + GTK_TYPE_STRING); @@ -2041,6 +2052,19 @@ item_event_callback (GnomeCanvasItem *item, g_return_val_if_fail (icon != NULL, FALSE); switch (event->type) { + case GDK_ENTER_NOTIFY: + if (details->drag_button != 0) { + /* Drag motion callback will take care of the + * necessary update -- don't let the signal reach + * the canvas item and cause a prelight. + * We let the GDK_LEAVE_NOTIFY fly through here to + * cause the "can_accept_drop" state to get turned off + */ + gtk_signal_emit_stop_by_name (GTK_OBJECT (item), "event"); + return TRUE; + } + return FALSE; + case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: if (handle_icon_button_press (container, icon, &event->button)) { diff --git a/libnautilus-extensions/nautilus-icon-container.h b/libnautilus-extensions/nautilus-icon-container.h index f910052f2..0d3d49d24 100644 --- a/libnautilus-extensions/nautilus-icon-container.h +++ b/libnautilus-extensions/nautilus-icon-container.h @@ -68,6 +68,9 @@ struct NautilusIconContainerClass { double scale_x, double scale_y); char * (* get_container_uri) (NautilusIconContainer *container); + gboolean (* can_accept_item) (NautilusIconContainer *container, + const NautilusIconData *target_uri, + const char *item_uri); /* Connect to these signals to supply information about icons. * They are called as needed after the icons are inserted. diff --git a/libnautilus-extensions/nautilus-icon-dnd.c b/libnautilus-extensions/nautilus-icon-dnd.c index 05a041dd3..f3cd41f97 100644 --- a/libnautilus-extensions/nautilus-icon-dnd.c +++ b/libnautilus-extensions/nautilus-icon-dnd.c @@ -45,10 +45,16 @@ #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); +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); +static void nautilus_icon_dnd_update_drop_target (NautilusIconContainer *container, + GdkDragContext *context, + int x, int y); + typedef struct { char *uri; @@ -510,9 +516,9 @@ drag_motion_callback (GtkWidget *widget, 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); + nautilus_icon_dnd_update_drop_target (NAUTILUS_ICON_CONTAINER (widget), context, x, y); gdk_drag_status (context, context->suggested_action, time); @@ -628,16 +634,18 @@ nautilus_icon_container_selection_items_local (const NautilusIconContainer *cont } static gboolean -nautilus_icon_canvas_item_can_accept_item (const NautilusIcon *drop_target_item, +nautilus_icon_canvas_item_can_accept_item (NautilusIconContainer *container, + const NautilusIcon *drop_target_item, const char *item_uri) { - /* FIXME: - * - * should have NautilusFile answer whether it can handle icon URI - * - * For now: - */ - return TRUE; + gboolean result; + + gtk_signal_emit_by_name (GTK_OBJECT (container), + "can_accept_item", + drop_target_item->data, + item_uri, + &result); + return result; } static gboolean @@ -645,10 +653,17 @@ nautilus_icon_canvas_item_can_accept_items (NautilusIconContainer *container, const NautilusIcon *drop_target_item, const GList *items) { - for (; items != NULL; items = items->next) { - if (!nautilus_icon_canvas_item_can_accept_item (drop_target_item, - ((DndSelectionItem *)items->data)->uri)) + int max; + + /* Iterate through selection checking if item will get accepted by the + * drop target. If more than 100 items selected, return an over-optimisic + * result + */ + for (max = 100; items != NULL && max >=0 ; items = items->next, max--) { + if (!nautilus_icon_canvas_item_can_accept_item (container, drop_target_item, + ((DndSelectionItem *)items->data)->uri)) { return FALSE; + } } return TRUE; @@ -808,6 +823,53 @@ nautilus_icon_container_receive_dropped_icons (NautilusIconContainer *container, } static void +nautilus_icon_dnd_update_drop_target (NautilusIconContainer *container, + GdkDragContext *context, + int x, int y) +{ + NautilusIcon *drop_target_icon; + double world_x, world_y; + + g_assert (NAUTILUS_IS_ICON_CONTAINER (container)); + if (container->details->dnd_info->selection_list == NULL) { + return; + } + + gnome_canvas_window_to_world (GNOME_CANVAS (container), + x, y, &world_x, &world_y); + + + /* Find the item we hit with our drop, if any. */ + drop_target_icon = nautilus_icon_container_item_at (container, world_x, world_y); + + + /* Find if target icon accepts our drop. */ + if (drop_target_icon != NULL + && !nautilus_icon_canvas_item_can_accept_items + (container, drop_target_icon, container->details->dnd_info->selection_list)) { + drop_target_icon = NULL; + } + + /* Check if current drop target changed, update icon drop higlight if needed. */ + if (drop_target_icon != container->details->dnd_info->current_drop_target_icon) { + /* Turn on new target, if any. */ + if (drop_target_icon != NULL) { + gnome_canvas_item_set (GNOME_CANVAS_ITEM (drop_target_icon->item), + "highlighted_for_drop", TRUE, + NULL); + } + /* Turn off old target, if any. */ + if (container->details->dnd_info->current_drop_target_icon != NULL) { + gnome_canvas_item_set (GNOME_CANVAS_ITEM (((NautilusIcon *)container->details-> + dnd_info->current_drop_target_icon)->item), + "highlighted_for_drop", FALSE, NULL); + } + /* Remember the new drop target for the next round. */ + container->details->dnd_info->current_drop_target_icon = drop_target_icon; + } +} + +static void nautilus_icon_container_free_drag_data (NautilusIconContainer *container) { NautilusIconDndInfo *dnd_info; @@ -1013,6 +1075,8 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), info); + container->details->dnd_info->current_drop_target_icon = NULL; + /* create a pixmap and mask to drag with */ pixbuf = nautilus_icon_canvas_item_get_image (container->details->drag_icon->item, NULL); @@ -1095,6 +1159,7 @@ drag_drop_callback (GtkWidget *widget, } NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info = NULL; + NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->current_drop_target_icon = NULL; return FALSE; } @@ -1120,13 +1185,13 @@ nautilus_icon_dnd_modifier_based_action () GdkModifierType modifiers; gdk_window_get_pointer (NULL, NULL, NULL, &modifiers); - if ((modifiers & GDK_CONTROL_MASK) != 0) { + if ((modifiers & GDK_MOD1_MASK) != 0) { return GDK_ACTION_COPY; #if 0 /* FIXME: * don't know how to do links yet */ - } else if ((modifiers & GDK_MOD1_MASK) != 0) { + } else if ((modifiers & GDK_CONTROL_MASK) != 0) { return GDK_ACTION_LINK; #endif } diff --git a/libnautilus-extensions/nautilus-icon-dnd.h b/libnautilus-extensions/nautilus-icon-dnd.h index f75b9aed8..821dd3d9b 100644 --- a/libnautilus-extensions/nautilus-icon-dnd.h +++ b/libnautilus-extensions/nautilus-icon-dnd.h @@ -79,6 +79,9 @@ struct NautilusIconDndInfo { * interface gets added to gtkdnd to do this in a clean way */ gpointer saved_drag_source_info; + + /* last highlighted drop target*/ + gpointer current_drop_target_icon; }; void nautilus_icon_dnd_init (NautilusIconContainer *container, diff --git a/libnautilus-private/nautilus-file.c b/libnautilus-private/nautilus-file.c index 0ed7bf771..d98a2e06e 100644 --- a/libnautilus-private/nautilus-file.c +++ b/libnautilus-private/nautilus-file.c @@ -360,6 +360,34 @@ nautilus_file_rename (NautilusFile *file, const char *new_name) return result; } +gboolean +nautilus_file_matches_uri (NautilusFile *file, const char *uri_string) +{ + GnomeVFSURI *match_uri; + GnomeVFSURI *file_uri; + gboolean result; + + g_return_val_if_fail (NAUTILUS_IS_FILE (file), FALSE); + g_return_val_if_fail (uri_string != NULL, FALSE); + + match_uri = gnome_vfs_uri_new (uri_string); + if (match_uri == NULL) + return FALSE; + + result = FALSE; + file_uri = gnome_vfs_uri_append_path (file->details->directory->details->uri, + file->details->info->name); + + if (match_uri != NULL) { + result = gnome_vfs_uri_equal (file_uri, match_uri); + } + + gnome_vfs_uri_unref (file_uri); + gnome_vfs_uri_unref (match_uri); + + return result; +} + static int nautilus_file_compare_by_size_with_directories (NautilusFile *file_1, NautilusFile *file_2) { diff --git a/libnautilus-private/nautilus-file.h b/libnautilus-private/nautilus-file.h index 6cc0c79be..27bb41b76 100644 --- a/libnautilus-private/nautilus-file.h +++ b/libnautilus-private/nautilus-file.h @@ -122,6 +122,10 @@ void nautilus_file_set_metadata (NautilusFile * char * nautilus_file_get_string_attribute (NautilusFile *file, const char *attribute_name); +/* Matching with another URI*/ +gboolean nautilus_file_matches_uri (NautilusFile *file, + const char *uri); + /* Comparing two file objects for sorting */ int nautilus_file_compare_for_sort (NautilusFile *file_1, NautilusFile *file_2, diff --git a/libnautilus-private/nautilus-gtk-extensions.c b/libnautilus-private/nautilus-gtk-extensions.c index 99d8d1523..b89404644 100644 --- a/libnautilus-private/nautilus-gtk-extensions.c +++ b/libnautilus-private/nautilus-gtk-extensions.c @@ -320,6 +320,20 @@ nautilus_gtk_marshal_POINTER__POINTER (GtkObject *object, } void +nautilus_gtk_marshal_INT__POINTER_POINTER (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) +{ + * GTK_RETLOC_INT (args[2]) = + (* (int (*)(GtkObject *, gpointer, gpointer, gpointer)) func) + (object, + GTK_VALUE_POINTER (args[0]), + GTK_VALUE_POINTER (args[1]), + func_data); +} + +void nautilus_gtk_marshal_POINTER__POINTER_POINTER (GtkObject *object, GtkSignalFunc func, gpointer func_data, diff --git a/libnautilus-private/nautilus-gtk-extensions.h b/libnautilus-private/nautilus-gtk-extensions.h index 88f0684f1..61f1f5187 100644 --- a/libnautilus-private/nautilus-gtk-extensions.h +++ b/libnautilus-private/nautilus-gtk-extensions.h @@ -38,6 +38,7 @@ #define nautilus_gtk_marshal_STRING__POINTER_STRING nautilus_gtk_marshal_POINTER__POINTER_POINTER #define nautilus_gtk_marshal_STRING__POINTER_POINTER_POINTER nautilus_gtk_marshal_POINTER__POINTER_POINTER_POINTER #define nautilus_gtk_marshal_STRING__POINTER_POINTER_STRING nautilus_gtk_marshal_POINTER__POINTER_POINTER_POINTER +#define nautilus_gtk_marshal_INT__POINTER_STRING nautilus_gtk_marshal_INT__POINTER_POINTER #define NAUTILUS_DEFAULT_POPUP_MENU_DISPLACEMENT 2 @@ -101,6 +102,10 @@ void nautilus_gtk_marshal_POINTER__POINTER GtkSignalFunc func, gpointer func_data, GtkArg *args); +void nautilus_gtk_marshal_INT__POINTER_POINTER (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); void nautilus_gtk_marshal_POINTER__POINTER_POINTER (GtkObject *object, GtkSignalFunc func, gpointer func_data, diff --git a/libnautilus-private/nautilus-icon-canvas-item.c b/libnautilus-private/nautilus-icon-canvas-item.c index 268a58459..bc5be35e9 100644 --- a/libnautilus-private/nautilus-icon-canvas-item.c +++ b/libnautilus-private/nautilus-icon-canvas-item.c @@ -1018,8 +1018,14 @@ nautilus_icon_canvas_item_event (GnomeCanvasItem *item, GdkEvent *event) return TRUE; case GDK_LEAVE_NOTIFY: - if (icon_item->details->is_prelit) { + if (icon_item->details->is_prelit + || icon_item->details->is_highlighted_for_drop) { + /* When leaving, turn of the prelight state and the + * higlighted for drop. The latter gets turned on + * by the drag&drop motion callback. + */ icon_item->details->is_prelit = FALSE; + icon_item->details->is_highlighted_for_drop = FALSE; gnome_canvas_item_request_update (item); } return TRUE; diff --git a/libnautilus-private/nautilus-icon-container.c b/libnautilus-private/nautilus-icon-container.c index a656ebd10..1d656703f 100644 --- a/libnautilus-private/nautilus-icon-container.c +++ b/libnautilus-private/nautilus-icon-container.c @@ -106,6 +106,7 @@ enum { SELECTION_CHANGED, MOVE_COPY_ITEMS, GET_CONTAINER_URI, + CAN_ACCEPT_ITEM, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; @@ -1868,6 +1869,16 @@ nautilus_icon_container_initialize_class (NautilusIconContainerClass *class) get_container_uri), nautilus_gtk_marshal_STRING__NONE, GTK_TYPE_STRING, 0); + signals[CAN_ACCEPT_ITEM] + = gtk_signal_new ("can_accept_item", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (NautilusIconContainerClass, + can_accept_item), + nautilus_gtk_marshal_INT__POINTER_STRING, + GTK_TYPE_INT, 2, + GTK_TYPE_POINTER, + GTK_TYPE_STRING); @@ -2041,6 +2052,19 @@ item_event_callback (GnomeCanvasItem *item, g_return_val_if_fail (icon != NULL, FALSE); switch (event->type) { + case GDK_ENTER_NOTIFY: + if (details->drag_button != 0) { + /* Drag motion callback will take care of the + * necessary update -- don't let the signal reach + * the canvas item and cause a prelight. + * We let the GDK_LEAVE_NOTIFY fly through here to + * cause the "can_accept_drop" state to get turned off + */ + gtk_signal_emit_stop_by_name (GTK_OBJECT (item), "event"); + return TRUE; + } + return FALSE; + case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: if (handle_icon_button_press (container, icon, &event->button)) { diff --git a/libnautilus-private/nautilus-icon-container.h b/libnautilus-private/nautilus-icon-container.h index f910052f2..0d3d49d24 100644 --- a/libnautilus-private/nautilus-icon-container.h +++ b/libnautilus-private/nautilus-icon-container.h @@ -68,6 +68,9 @@ struct NautilusIconContainerClass { double scale_x, double scale_y); char * (* get_container_uri) (NautilusIconContainer *container); + gboolean (* can_accept_item) (NautilusIconContainer *container, + const NautilusIconData *target_uri, + const char *item_uri); /* Connect to these signals to supply information about icons. * They are called as needed after the icons are inserted. diff --git a/libnautilus-private/nautilus-icon-dnd.c b/libnautilus-private/nautilus-icon-dnd.c index 05a041dd3..f3cd41f97 100644 --- a/libnautilus-private/nautilus-icon-dnd.c +++ b/libnautilus-private/nautilus-icon-dnd.c @@ -45,10 +45,16 @@ #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); +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); +static void nautilus_icon_dnd_update_drop_target (NautilusIconContainer *container, + GdkDragContext *context, + int x, int y); + typedef struct { char *uri; @@ -510,9 +516,9 @@ drag_motion_callback (GtkWidget *widget, 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); + nautilus_icon_dnd_update_drop_target (NAUTILUS_ICON_CONTAINER (widget), context, x, y); gdk_drag_status (context, context->suggested_action, time); @@ -628,16 +634,18 @@ nautilus_icon_container_selection_items_local (const NautilusIconContainer *cont } static gboolean -nautilus_icon_canvas_item_can_accept_item (const NautilusIcon *drop_target_item, +nautilus_icon_canvas_item_can_accept_item (NautilusIconContainer *container, + const NautilusIcon *drop_target_item, const char *item_uri) { - /* FIXME: - * - * should have NautilusFile answer whether it can handle icon URI - * - * For now: - */ - return TRUE; + gboolean result; + + gtk_signal_emit_by_name (GTK_OBJECT (container), + "can_accept_item", + drop_target_item->data, + item_uri, + &result); + return result; } static gboolean @@ -645,10 +653,17 @@ nautilus_icon_canvas_item_can_accept_items (NautilusIconContainer *container, const NautilusIcon *drop_target_item, const GList *items) { - for (; items != NULL; items = items->next) { - if (!nautilus_icon_canvas_item_can_accept_item (drop_target_item, - ((DndSelectionItem *)items->data)->uri)) + int max; + + /* Iterate through selection checking if item will get accepted by the + * drop target. If more than 100 items selected, return an over-optimisic + * result + */ + for (max = 100; items != NULL && max >=0 ; items = items->next, max--) { + if (!nautilus_icon_canvas_item_can_accept_item (container, drop_target_item, + ((DndSelectionItem *)items->data)->uri)) { return FALSE; + } } return TRUE; @@ -808,6 +823,53 @@ nautilus_icon_container_receive_dropped_icons (NautilusIconContainer *container, } static void +nautilus_icon_dnd_update_drop_target (NautilusIconContainer *container, + GdkDragContext *context, + int x, int y) +{ + NautilusIcon *drop_target_icon; + double world_x, world_y; + + g_assert (NAUTILUS_IS_ICON_CONTAINER (container)); + if (container->details->dnd_info->selection_list == NULL) { + return; + } + + gnome_canvas_window_to_world (GNOME_CANVAS (container), + x, y, &world_x, &world_y); + + + /* Find the item we hit with our drop, if any. */ + drop_target_icon = nautilus_icon_container_item_at (container, world_x, world_y); + + + /* Find if target icon accepts our drop. */ + if (drop_target_icon != NULL + && !nautilus_icon_canvas_item_can_accept_items + (container, drop_target_icon, container->details->dnd_info->selection_list)) { + drop_target_icon = NULL; + } + + /* Check if current drop target changed, update icon drop higlight if needed. */ + if (drop_target_icon != container->details->dnd_info->current_drop_target_icon) { + /* Turn on new target, if any. */ + if (drop_target_icon != NULL) { + gnome_canvas_item_set (GNOME_CANVAS_ITEM (drop_target_icon->item), + "highlighted_for_drop", TRUE, + NULL); + } + /* Turn off old target, if any. */ + if (container->details->dnd_info->current_drop_target_icon != NULL) { + gnome_canvas_item_set (GNOME_CANVAS_ITEM (((NautilusIcon *)container->details-> + dnd_info->current_drop_target_icon)->item), + "highlighted_for_drop", FALSE, NULL); + } + /* Remember the new drop target for the next round. */ + container->details->dnd_info->current_drop_target_icon = drop_target_icon; + } +} + +static void nautilus_icon_container_free_drag_data (NautilusIconContainer *container) { NautilusIconDndInfo *dnd_info; @@ -1013,6 +1075,8 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), info); + container->details->dnd_info->current_drop_target_icon = NULL; + /* create a pixmap and mask to drag with */ pixbuf = nautilus_icon_canvas_item_get_image (container->details->drag_icon->item, NULL); @@ -1095,6 +1159,7 @@ drag_drop_callback (GtkWidget *widget, } NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info = NULL; + NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->current_drop_target_icon = NULL; return FALSE; } @@ -1120,13 +1185,13 @@ nautilus_icon_dnd_modifier_based_action () GdkModifierType modifiers; gdk_window_get_pointer (NULL, NULL, NULL, &modifiers); - if ((modifiers & GDK_CONTROL_MASK) != 0) { + if ((modifiers & GDK_MOD1_MASK) != 0) { return GDK_ACTION_COPY; #if 0 /* FIXME: * don't know how to do links yet */ - } else if ((modifiers & GDK_MOD1_MASK) != 0) { + } else if ((modifiers & GDK_CONTROL_MASK) != 0) { return GDK_ACTION_LINK; #endif } diff --git a/libnautilus-private/nautilus-icon-dnd.h b/libnautilus-private/nautilus-icon-dnd.h index f75b9aed8..821dd3d9b 100644 --- a/libnautilus-private/nautilus-icon-dnd.h +++ b/libnautilus-private/nautilus-icon-dnd.h @@ -79,6 +79,9 @@ struct NautilusIconDndInfo { * interface gets added to gtkdnd to do this in a clean way */ gpointer saved_drag_source_info; + + /* last highlighted drop target*/ + gpointer current_drop_target_icon; }; void nautilus_icon_dnd_init (NautilusIconContainer *container, diff --git a/libnautilus/nautilus-file.c b/libnautilus/nautilus-file.c index 0ed7bf771..d98a2e06e 100644 --- a/libnautilus/nautilus-file.c +++ b/libnautilus/nautilus-file.c @@ -360,6 +360,34 @@ nautilus_file_rename (NautilusFile *file, const char *new_name) return result; } +gboolean +nautilus_file_matches_uri (NautilusFile *file, const char *uri_string) +{ + GnomeVFSURI *match_uri; + GnomeVFSURI *file_uri; + gboolean result; + + g_return_val_if_fail (NAUTILUS_IS_FILE (file), FALSE); + g_return_val_if_fail (uri_string != NULL, FALSE); + + match_uri = gnome_vfs_uri_new (uri_string); + if (match_uri == NULL) + return FALSE; + + result = FALSE; + file_uri = gnome_vfs_uri_append_path (file->details->directory->details->uri, + file->details->info->name); + + if (match_uri != NULL) { + result = gnome_vfs_uri_equal (file_uri, match_uri); + } + + gnome_vfs_uri_unref (file_uri); + gnome_vfs_uri_unref (match_uri); + + return result; +} + static int nautilus_file_compare_by_size_with_directories (NautilusFile *file_1, NautilusFile *file_2) { diff --git a/libnautilus/nautilus-file.h b/libnautilus/nautilus-file.h index 6cc0c79be..27bb41b76 100644 --- a/libnautilus/nautilus-file.h +++ b/libnautilus/nautilus-file.h @@ -122,6 +122,10 @@ void nautilus_file_set_metadata (NautilusFile * char * nautilus_file_get_string_attribute (NautilusFile *file, const char *attribute_name); +/* Matching with another URI*/ +gboolean nautilus_file_matches_uri (NautilusFile *file, + const char *uri); + /* Comparing two file objects for sorting */ int nautilus_file_compare_for_sort (NautilusFile *file_1, NautilusFile *file_2, diff --git a/libnautilus/nautilus-gtk-extensions.c b/libnautilus/nautilus-gtk-extensions.c index 99d8d1523..b89404644 100644 --- a/libnautilus/nautilus-gtk-extensions.c +++ b/libnautilus/nautilus-gtk-extensions.c @@ -320,6 +320,20 @@ nautilus_gtk_marshal_POINTER__POINTER (GtkObject *object, } void +nautilus_gtk_marshal_INT__POINTER_POINTER (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) +{ + * GTK_RETLOC_INT (args[2]) = + (* (int (*)(GtkObject *, gpointer, gpointer, gpointer)) func) + (object, + GTK_VALUE_POINTER (args[0]), + GTK_VALUE_POINTER (args[1]), + func_data); +} + +void nautilus_gtk_marshal_POINTER__POINTER_POINTER (GtkObject *object, GtkSignalFunc func, gpointer func_data, diff --git a/libnautilus/nautilus-gtk-extensions.h b/libnautilus/nautilus-gtk-extensions.h index 88f0684f1..61f1f5187 100644 --- a/libnautilus/nautilus-gtk-extensions.h +++ b/libnautilus/nautilus-gtk-extensions.h @@ -38,6 +38,7 @@ #define nautilus_gtk_marshal_STRING__POINTER_STRING nautilus_gtk_marshal_POINTER__POINTER_POINTER #define nautilus_gtk_marshal_STRING__POINTER_POINTER_POINTER nautilus_gtk_marshal_POINTER__POINTER_POINTER_POINTER #define nautilus_gtk_marshal_STRING__POINTER_POINTER_STRING nautilus_gtk_marshal_POINTER__POINTER_POINTER_POINTER +#define nautilus_gtk_marshal_INT__POINTER_STRING nautilus_gtk_marshal_INT__POINTER_POINTER #define NAUTILUS_DEFAULT_POPUP_MENU_DISPLACEMENT 2 @@ -101,6 +102,10 @@ void nautilus_gtk_marshal_POINTER__POINTER GtkSignalFunc func, gpointer func_data, GtkArg *args); +void nautilus_gtk_marshal_INT__POINTER_POINTER (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); void nautilus_gtk_marshal_POINTER__POINTER_POINTER (GtkObject *object, GtkSignalFunc func, gpointer func_data, diff --git a/libnautilus/nautilus-icon-canvas-item.c b/libnautilus/nautilus-icon-canvas-item.c index 268a58459..bc5be35e9 100644 --- a/libnautilus/nautilus-icon-canvas-item.c +++ b/libnautilus/nautilus-icon-canvas-item.c @@ -1018,8 +1018,14 @@ nautilus_icon_canvas_item_event (GnomeCanvasItem *item, GdkEvent *event) return TRUE; case GDK_LEAVE_NOTIFY: - if (icon_item->details->is_prelit) { + if (icon_item->details->is_prelit + || icon_item->details->is_highlighted_for_drop) { + /* When leaving, turn of the prelight state and the + * higlighted for drop. The latter gets turned on + * by the drag&drop motion callback. + */ icon_item->details->is_prelit = FALSE; + icon_item->details->is_highlighted_for_drop = FALSE; gnome_canvas_item_request_update (item); } return TRUE; diff --git a/libnautilus/nautilus-icon-container.c b/libnautilus/nautilus-icon-container.c index a656ebd10..1d656703f 100644 --- a/libnautilus/nautilus-icon-container.c +++ b/libnautilus/nautilus-icon-container.c @@ -106,6 +106,7 @@ enum { SELECTION_CHANGED, MOVE_COPY_ITEMS, GET_CONTAINER_URI, + CAN_ACCEPT_ITEM, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; @@ -1868,6 +1869,16 @@ nautilus_icon_container_initialize_class (NautilusIconContainerClass *class) get_container_uri), nautilus_gtk_marshal_STRING__NONE, GTK_TYPE_STRING, 0); + signals[CAN_ACCEPT_ITEM] + = gtk_signal_new ("can_accept_item", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (NautilusIconContainerClass, + can_accept_item), + nautilus_gtk_marshal_INT__POINTER_STRING, + GTK_TYPE_INT, 2, + GTK_TYPE_POINTER, + GTK_TYPE_STRING); @@ -2041,6 +2052,19 @@ item_event_callback (GnomeCanvasItem *item, g_return_val_if_fail (icon != NULL, FALSE); switch (event->type) { + case GDK_ENTER_NOTIFY: + if (details->drag_button != 0) { + /* Drag motion callback will take care of the + * necessary update -- don't let the signal reach + * the canvas item and cause a prelight. + * We let the GDK_LEAVE_NOTIFY fly through here to + * cause the "can_accept_drop" state to get turned off + */ + gtk_signal_emit_stop_by_name (GTK_OBJECT (item), "event"); + return TRUE; + } + return FALSE; + case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: if (handle_icon_button_press (container, icon, &event->button)) { diff --git a/libnautilus/nautilus-icon-container.h b/libnautilus/nautilus-icon-container.h index f910052f2..0d3d49d24 100644 --- a/libnautilus/nautilus-icon-container.h +++ b/libnautilus/nautilus-icon-container.h @@ -68,6 +68,9 @@ struct NautilusIconContainerClass { double scale_x, double scale_y); char * (* get_container_uri) (NautilusIconContainer *container); + gboolean (* can_accept_item) (NautilusIconContainer *container, + const NautilusIconData *target_uri, + const char *item_uri); /* Connect to these signals to supply information about icons. * They are called as needed after the icons are inserted. diff --git a/libnautilus/nautilus-icon-dnd.c b/libnautilus/nautilus-icon-dnd.c index 05a041dd3..f3cd41f97 100644 --- a/libnautilus/nautilus-icon-dnd.c +++ b/libnautilus/nautilus-icon-dnd.c @@ -45,10 +45,16 @@ #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); +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); +static void nautilus_icon_dnd_update_drop_target (NautilusIconContainer *container, + GdkDragContext *context, + int x, int y); + typedef struct { char *uri; @@ -510,9 +516,9 @@ drag_motion_callback (GtkWidget *widget, 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); + nautilus_icon_dnd_update_drop_target (NAUTILUS_ICON_CONTAINER (widget), context, x, y); gdk_drag_status (context, context->suggested_action, time); @@ -628,16 +634,18 @@ nautilus_icon_container_selection_items_local (const NautilusIconContainer *cont } static gboolean -nautilus_icon_canvas_item_can_accept_item (const NautilusIcon *drop_target_item, +nautilus_icon_canvas_item_can_accept_item (NautilusIconContainer *container, + const NautilusIcon *drop_target_item, const char *item_uri) { - /* FIXME: - * - * should have NautilusFile answer whether it can handle icon URI - * - * For now: - */ - return TRUE; + gboolean result; + + gtk_signal_emit_by_name (GTK_OBJECT (container), + "can_accept_item", + drop_target_item->data, + item_uri, + &result); + return result; } static gboolean @@ -645,10 +653,17 @@ nautilus_icon_canvas_item_can_accept_items (NautilusIconContainer *container, const NautilusIcon *drop_target_item, const GList *items) { - for (; items != NULL; items = items->next) { - if (!nautilus_icon_canvas_item_can_accept_item (drop_target_item, - ((DndSelectionItem *)items->data)->uri)) + int max; + + /* Iterate through selection checking if item will get accepted by the + * drop target. If more than 100 items selected, return an over-optimisic + * result + */ + for (max = 100; items != NULL && max >=0 ; items = items->next, max--) { + if (!nautilus_icon_canvas_item_can_accept_item (container, drop_target_item, + ((DndSelectionItem *)items->data)->uri)) { return FALSE; + } } return TRUE; @@ -808,6 +823,53 @@ nautilus_icon_container_receive_dropped_icons (NautilusIconContainer *container, } static void +nautilus_icon_dnd_update_drop_target (NautilusIconContainer *container, + GdkDragContext *context, + int x, int y) +{ + NautilusIcon *drop_target_icon; + double world_x, world_y; + + g_assert (NAUTILUS_IS_ICON_CONTAINER (container)); + if (container->details->dnd_info->selection_list == NULL) { + return; + } + + gnome_canvas_window_to_world (GNOME_CANVAS (container), + x, y, &world_x, &world_y); + + + /* Find the item we hit with our drop, if any. */ + drop_target_icon = nautilus_icon_container_item_at (container, world_x, world_y); + + + /* Find if target icon accepts our drop. */ + if (drop_target_icon != NULL + && !nautilus_icon_canvas_item_can_accept_items + (container, drop_target_icon, container->details->dnd_info->selection_list)) { + drop_target_icon = NULL; + } + + /* Check if current drop target changed, update icon drop higlight if needed. */ + if (drop_target_icon != container->details->dnd_info->current_drop_target_icon) { + /* Turn on new target, if any. */ + if (drop_target_icon != NULL) { + gnome_canvas_item_set (GNOME_CANVAS_ITEM (drop_target_icon->item), + "highlighted_for_drop", TRUE, + NULL); + } + /* Turn off old target, if any. */ + if (container->details->dnd_info->current_drop_target_icon != NULL) { + gnome_canvas_item_set (GNOME_CANVAS_ITEM (((NautilusIcon *)container->details-> + dnd_info->current_drop_target_icon)->item), + "highlighted_for_drop", FALSE, NULL); + } + /* Remember the new drop target for the next round. */ + container->details->dnd_info->current_drop_target_icon = drop_target_icon; + } +} + +static void nautilus_icon_container_free_drag_data (NautilusIconContainer *container) { NautilusIconDndInfo *dnd_info; @@ -1013,6 +1075,8 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container, GTK_SIGNAL_FUNC (nautilus_icon_drag_key_callback), info); + container->details->dnd_info->current_drop_target_icon = NULL; + /* create a pixmap and mask to drag with */ pixbuf = nautilus_icon_canvas_item_get_image (container->details->drag_icon->item, NULL); @@ -1095,6 +1159,7 @@ drag_drop_callback (GtkWidget *widget, } NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->saved_drag_source_info = NULL; + NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->current_drop_target_icon = NULL; return FALSE; } @@ -1120,13 +1185,13 @@ nautilus_icon_dnd_modifier_based_action () GdkModifierType modifiers; gdk_window_get_pointer (NULL, NULL, NULL, &modifiers); - if ((modifiers & GDK_CONTROL_MASK) != 0) { + if ((modifiers & GDK_MOD1_MASK) != 0) { return GDK_ACTION_COPY; #if 0 /* FIXME: * don't know how to do links yet */ - } else if ((modifiers & GDK_MOD1_MASK) != 0) { + } else if ((modifiers & GDK_CONTROL_MASK) != 0) { return GDK_ACTION_LINK; #endif } diff --git a/libnautilus/nautilus-icon-dnd.h b/libnautilus/nautilus-icon-dnd.h index f75b9aed8..821dd3d9b 100644 --- a/libnautilus/nautilus-icon-dnd.h +++ b/libnautilus/nautilus-icon-dnd.h @@ -79,6 +79,9 @@ struct NautilusIconDndInfo { * interface gets added to gtkdnd to do this in a clean way */ gpointer saved_drag_source_info; + + /* last highlighted drop target*/ + gpointer current_drop_target_icon; }; void nautilus_icon_dnd_init (NautilusIconContainer *container, diff --git a/src/file-manager/fm-directory-view.c b/src/file-manager/fm-directory-view.c index 5ec74760e..4eb053ec6 100644 --- a/src/file-manager/fm-directory-view.c +++ b/src/file-manager/fm-directory-view.c @@ -2115,6 +2115,30 @@ fm_directory_view_move_copy_items (NautilusIconContainer *container, GTK_WIDGET (view)); } +gboolean +fm_directory_view_can_accept_item (NautilusIconContainer *container, + NautilusFile *target_item, + const char *item_uri, + FMDirectoryView *view) +{ + g_assert (NAUTILUS_IS_ICON_CONTAINER (container)); + g_assert (NAUTILUS_IS_FILE (target_item)); + g_assert (FM_IS_DIRECTORY_VIEW (view)); + + /* FIXME: + * elaborate here some more + * should consider permissions, handle symlinks to directories, etc. + * + * for now just allways return true if dropping into a directory + */ + if (nautilus_file_get_file_type (target_item) != GNOME_VFS_FILE_TYPE_DIRECTORY) { + return FALSE; + } + + /* target is a directory, find out if it matches the item */ + return !nautilus_file_matches_uri (target_item, item_uri); +} + static void add_nautilus_file_to_uri_map (FMDirectoryView *view, diff --git a/src/file-manager/fm-directory-view.h b/src/file-manager/fm-directory-view.h index b15140418..84e34a488 100644 --- a/src/file-manager/fm-directory-view.h +++ b/src/file-manager/fm-directory-view.h @@ -181,6 +181,10 @@ void fm_directory_view_load_uri (FMDi /* Functions callable from the user interface and elsewhere. */ char * fm_directory_view_get_container_uri (NautilusIconContainer *container, FMDirectoryView *view); +gboolean fm_directory_view_can_accept_item (NautilusIconContainer *container, + NautilusFile *target_item, + const char *item_uri, + FMDirectoryView *view); GList * fm_directory_view_get_selection (FMDirectoryView *view); void fm_directory_view_stop (FMDirectoryView *view); gboolean fm_directory_view_can_zoom_in (FMDirectoryView *view); diff --git a/src/file-manager/fm-icon-view.c b/src/file-manager/fm-icon-view.c index 07e69ec41..7dd2c27e2 100644 --- a/src/file-manager/fm-icon-view.c +++ b/src/file-manager/fm-icon-view.c @@ -282,6 +282,10 @@ create_icon_container (FMIconView *icon_view) "get_container_uri", GTK_SIGNAL_FUNC (fm_directory_view_get_container_uri), directory_view); + gtk_signal_connect (GTK_OBJECT (icon_container), + "can_accept_item", + GTK_SIGNAL_FUNC (fm_directory_view_can_accept_item), + directory_view); gtk_container_add (GTK_CONTAINER (icon_view), GTK_WIDGET (icon_container)); |