From c2157d4b53c52a01c292c1caa8cb04caff93354d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Thu, 30 Jun 2022 01:49:37 +0100 Subject: [PATCH 01/12] dnd: Introduce helper to compute preferred drop action Based on the logic from nautilus_drag_default_drop_action_for_icons(), but tailored for GTK4 paradigm. Also add some cases that weren't handled well previously. Heavily inspired by more conservative patch by Corey Berla. --- src/nautilus-dnd.c | 193 +++++++++++++++------------------------------ src/nautilus-dnd.h | 7 +- 2 files changed, 65 insertions(+), 135 deletions(-) diff --git a/src/nautilus-dnd.c b/src/nautilus-dnd.c index c35698652c..e57be8a27c 100644 --- a/src/nautilus-dnd.c +++ b/src/nautilus-dnd.c @@ -24,12 +24,13 @@ #include #include "nautilus-dnd.h" +#include + #if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION #include "nautilus-program-choosing.h" #include #include -#include #include #include #include "nautilus-file-utilities.h" @@ -348,6 +349,7 @@ nautilus_drag_default_drop_action_for_netscape_url (GdkDragContext *context) return gdk_drag_context_get_suggested_action (context); } +#endif static gboolean check_same_fs (NautilusFile *file1, @@ -394,6 +396,8 @@ source_is_deletable (GFile *file) return ret; } +#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION + NautilusDragInfo * nautilus_drag_get_source_data (GdkDragContext *context) { @@ -435,136 +439,9 @@ nautilus_drag_get_source_data (GdkDragContext *context) return source_data; } +#endif -void -nautilus_drag_default_drop_action_for_icons (GdkDragContext *context, - const char *target_uri_string, - const GList *items, - guint32 source_actions, - int *action) -{ - gboolean same_fs; - gboolean target_is_source_parent; - gboolean source_deletable; - const char *dropped_uri; - GFile *target, *dropped, *dropped_directory; - GdkDragAction actions; - NautilusFile *dropped_file, *target_file; - - if (target_uri_string == NULL) - { - *action = 0; - return; - } - - /* this is needed because of how dnd works. The actions at the time drag-begin - * is done are not set, because they are first set on drag-motion. However, - * for our use case, which is validation with the sidebar for dnd feedback - * when the dnd doesn't have as a destination the sidebar itself, we need - * a way to know the actions at drag-begin time. Either canvas view or - * list view know them when starting the drag, but asking for them here - * would be breaking the current model too much. So instead we rely on the - * caller, which will ask if appropiate to those objects about the actions - * available, instead of relying solely on the context here. */ - if (source_actions) - { - actions = source_actions & (GDK_ACTION_MOVE | GDK_ACTION_COPY); - } - else - { - actions = gdk_drag_context_get_actions (context) & (GDK_ACTION_MOVE | GDK_ACTION_COPY); - } - if (actions == 0) - { - /* We can't use copy or move, just go with the suggested action. */ - *action = gdk_drag_context_get_suggested_action (context); - return; - } - - if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_ASK) - { - /* Don't override ask */ - *action = gdk_drag_context_get_suggested_action (context); - return; - } - - dropped_uri = ((NautilusDragSelectionItem *) items->data)->uri; - dropped_file = ((NautilusDragSelectionItem *) items->data)->file; - target_file = nautilus_file_get_by_uri (target_uri_string); - - /* - * Check for trash URI. We do a find_directory for any Trash directory. - * Passing 0 permissions as gnome-vfs would override the permissions - * passed with 700 while creating .Trash directory - */ - if (eel_uri_is_trash (target_uri_string)) - { - /* Only move to Trash */ - if (actions & GDK_ACTION_MOVE) - { - *action = GDK_ACTION_MOVE; - } - nautilus_file_unref (target_file); - return; - } - else if (target_file != NULL && nautilus_file_is_archive (target_file)) - { - *action = GDK_ACTION_COPY; - - nautilus_file_unref (target_file); - return; - } - else - { - target = g_file_new_for_uri (target_uri_string); - } - - same_fs = check_same_fs (target_file, dropped_file); - - nautilus_file_unref (target_file); - - /* Compare the first dropped uri with the target uri for same fs match. */ - dropped = g_file_new_for_uri (dropped_uri); - dropped_directory = g_file_get_parent (dropped); - target_is_source_parent = FALSE; - if (dropped_directory != NULL) - { - /* If the dropped file is already in the same directory but - * is in another filesystem we still want to move, not copy - * as this is then just a move of a mountpoint to another - * position in the dir */ - target_is_source_parent = g_file_equal (dropped_directory, target); - g_object_unref (dropped_directory); - } - source_deletable = source_is_deletable (dropped); - - if ((same_fs && source_deletable) || target_is_source_parent || - g_file_has_uri_scheme (dropped, "trash")) - { - if (actions & GDK_ACTION_MOVE) - { - *action = GDK_ACTION_MOVE; - } - else - { - *action = gdk_drag_context_get_suggested_action (context); - } - } - else - { - if (actions & GDK_ACTION_COPY) - { - *action = GDK_ACTION_COPY; - } - else - { - *action = gdk_drag_context_get_suggested_action (context); - } - } - - g_object_unref (target); - g_object_unref (dropped); -} +#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION GdkDragAction nautilus_drag_default_drop_action_for_uri_list (GdkDragContext *context, @@ -991,6 +868,62 @@ nautilus_drag_autoscroll_stop (NautilusDragInfo *drag_info) #endif +GdkDragAction +nautilus_dnd_get_prefered_action (NautilusFile *target_file, + GFile *dropped) +{ + g_return_val_if_fail (NAUTILUS_IS_FILE (target_file), 0); + g_return_val_if_fail (dropped == NULL || G_IS_FILE (dropped), 0); + + /* First check target imperatives */ + + if (nautilus_file_is_archive (target_file)) + { + return GDK_ACTION_COPY; + } + else if (!nautilus_file_is_directory (target_file)) + { + /* No other file type other than archives and directories currently + * accepts drops */ + return 0; + } + + if (nautilus_file_is_in_trash (target_file)) + { + return GDK_ACTION_MOVE; + } + + if (dropped != NULL) + { + g_autoptr (GFile) target_location = NULL; + g_autoptr (NautilusFile) dropped_file = NULL; + gboolean same_fs; + gboolean source_deletable; + + if (g_file_has_uri_scheme (dropped, "trash")) + { + return GDK_ACTION_MOVE; + } + + target_location = nautilus_file_get_location (target_file); + if (g_file_equal (target_location, dropped) || + g_file_has_parent (dropped, target_location)) + { + return 0; + } + + dropped_file = nautilus_file_get (dropped); + same_fs = check_same_fs (target_file, dropped_file); + source_deletable = source_is_deletable (dropped); + if (same_fs && source_deletable) + { + return GDK_ACTION_MOVE; + } + } + + return GDK_ACTION_COPY; +} + #define MAX_DRAWN_DRAG_ICONS 10 #define NAUTILUS_DRAG_SURFACE_ICON_SIZE 64 diff --git a/src/nautilus-dnd.h b/src/nautilus-dnd.h index a8fc416ff8..069256cfb1 100644 --- a/src/nautilus-dnd.h +++ b/src/nautilus-dnd.h @@ -108,11 +108,6 @@ gboolean nautilus_drag_items_local (const char *target_uri, const GList *selection_list); gboolean nautilus_drag_uris_local (const char *target_uri, const GList *source_uri_list); -void nautilus_drag_default_drop_action_for_icons (GdkDragContext *context, - const char *target_uri, - const GList *items, - guint32 source_actions, - int *action); GdkDragAction nautilus_drag_default_drop_action_for_netscape_url (GdkDragContext *context); GdkDragAction nautilus_drag_default_drop_action_for_uri_list (GdkDragContext *context, const char *target_uri_string); @@ -144,5 +139,7 @@ NautilusDragInfo * nautilus_drag_get_source_data (GdkDr GList * nautilus_drag_file_list_from_selection_list (const GList *selection_list); #endif +GdkDragAction nautilus_dnd_get_prefered_action (NautilusFile *target_file, + GFile *dropped); GdkPaintable * get_paintable_for_drag_selection (GList *selection, int scale); -- GitLab From c7cf21e1aa38cff617bdbd101a8211ba1b7d6ff7 Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Thu, 16 Jun 2022 08:46:50 -0700 Subject: [PATCH 02/12] files-view-dnd: Reenable for GTK4 Reuse existing functions where possible. --- src/nautilus-files-view-dnd.c | 9 ++++++--- src/nautilus-files-view-dnd.h | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/nautilus-files-view-dnd.c b/src/nautilus-files-view-dnd.c index fbb0e2ee79..0f305c832a 100644 --- a/src/nautilus-files-view-dnd.c +++ b/src/nautilus-files-view-dnd.c @@ -23,7 +23,6 @@ * Pavel Cisler */ -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION #include #include "nautilus-files-view-dnd.h" @@ -134,10 +133,12 @@ nautilus_files_view_handle_uri_list_drop (NautilusFilesView *view, if (action == GDK_ACTION_ASK) { +#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION action = nautilus_drag_drop_action_ask (GTK_WIDGET (view), GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK); if (action == 0) +#endif { g_free (container_uri); return; @@ -359,18 +360,21 @@ nautilus_files_view_drop_proxy_received_uris (NautilusFilesView *view, if (action == GDK_ACTION_ASK) { +#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION action = nautilus_drag_drop_action_ask (GTK_WIDGET (view), GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK); if (action == 0) +#endif { return; } } +#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION nautilus_clipboard_clear_if_colliding_uris (GTK_WIDGET (view), source_uri_list); - +#endif nautilus_files_view_move_copy_items (view, source_uri_list, target_uri != NULL ? target_uri : container_uri, action); @@ -408,4 +412,3 @@ nautilus_files_view_handle_hover (NautilusFilesView *view, g_object_unref (location); nautilus_file_unref (target_file); } -#endif diff --git a/src/nautilus-files-view-dnd.h b/src/nautilus-files-view-dnd.h index 7cfdb53b07..73b9263a76 100644 --- a/src/nautilus-files-view-dnd.h +++ b/src/nautilus-files-view-dnd.h @@ -25,7 +25,7 @@ */ #pragma once -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION + #include "nautilus-files-view.h" void nautilus_files_view_handle_uri_list_drop (NautilusFilesView *view, @@ -53,4 +53,3 @@ void nautilus_files_view_drop_proxy_received_uris (NautilusFilesView *view, const GList *uris, const char *target_location, GdkDragAction action); -#endif -- GitLab From 938d4f8d353a878f47383e94ec335f1fc374d70d Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Thu, 16 Jun 2022 08:11:56 -0700 Subject: [PATCH 03/12] window-slot-dnd: Reenable pathbar and tabs as drop targets Keep existing code with modifications for GtkDropTarget. Eventually there should be further cleanup but this works for now. --- src/nautilus-dnd.h | 4 +- src/nautilus-notebook.c | 4 +- src/nautilus-pathbar.c | 2 - src/nautilus-window-slot-dnd.c | 303 +++++---------------------------- src/nautilus-window-slot-dnd.h | 2 - 5 files changed, 50 insertions(+), 265 deletions(-) diff --git a/src/nautilus-dnd.h b/src/nautilus-dnd.h index 069256cfb1..2c45a0ec18 100644 --- a/src/nautilus-dnd.h +++ b/src/nautilus-dnd.h @@ -27,10 +27,10 @@ #include #include "nautilus-file.h" -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - #define HOVER_TIMEOUT 500 +#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION + /* Drag & Drop target names. */ #define NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE "x-special/gnome-icon-list" #define NAUTILUS_ICON_DND_URI_LIST_TYPE "text/uri-list" diff --git a/src/nautilus-notebook.c b/src/nautilus-notebook.c index 504ea5caab..9713b545bc 100644 --- a/src/nautilus-notebook.c +++ b/src/nautilus-notebook.c @@ -289,9 +289,7 @@ build_tab_label (GtkNotebook *notebook, gtk_widget_show (close_button); g_object_set_data (G_OBJECT (tab_label), "nautilus-notebook-tab", GINT_TO_POINTER (1)); -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - nautilus_drag_slot_proxy_init (box, NULL, slot); -#endif + nautilus_drag_slot_proxy_init (tab_label, NULL, slot); g_object_set_data (G_OBJECT (tab_label), "label", label); g_object_set_data (G_OBJECT (tab_label), "spinner", spinner); diff --git a/src/nautilus-pathbar.c b/src/nautilus-pathbar.c index 1b5671b090..f43ccaf5ff 100644 --- a/src/nautilus-pathbar.c +++ b/src/nautilus-pathbar.c @@ -1118,9 +1118,7 @@ make_button_data (NautilusPathBar *self, g_signal_connect (controller, "pressed", G_CALLBACK (on_click_gesture_pressed), button_data); -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION nautilus_drag_slot_proxy_init (button_data->button, button_data->file, NULL); -#endif g_object_unref (path); diff --git a/src/nautilus-window-slot-dnd.c b/src/nautilus-window-slot-dnd.c index ccec73b608..3d929a3f3c 100644 --- a/src/nautilus-window-slot-dnd.c +++ b/src/nautilus-window-slot-dnd.c @@ -23,7 +23,6 @@ * Ettore Perazzoli */ -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION #include #include "nautilus-notebook.h" @@ -33,19 +32,6 @@ typedef struct { - gboolean have_data; - gboolean have_valid_data; - - gboolean drop_occurred; - - unsigned int info; - union - { - GList *selection_list; - GList *uri_list; - GtkSelectionData *selection_data; - } data; - NautilusFile *target_file; NautilusWindowSlot *target_slot; GtkWidget *widget; @@ -120,8 +106,7 @@ slot_proxy_switch_location_timer (gpointer user_data) } static void -slot_proxy_check_switch_location_timer (NautilusDragSlotProxyInfo *drag_info, - GtkWidget *widget) +slot_proxy_check_switch_location_timer (NautilusDragSlotProxyInfo *drag_info) { if (drag_info->switch_location_timer) { @@ -143,50 +128,33 @@ slot_proxy_remove_switch_location_timer (NautilusDragSlotProxyInfo *drag_info) } } -static gboolean -slot_proxy_drag_motion (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - unsigned int time, - gpointer user_data) +static GdkDragAction +slot_proxy_drag_motion (GtkDropTarget *target, + gdouble x, + gdouble y, + gpointer user_data) { NautilusDragSlotProxyInfo *drag_info; NautilusWindowSlot *target_slot; GtkRoot *window; - GdkAtom target; - int action; + GdkDragAction action; char *target_uri; GFile *location; - gboolean valid_text_drag; - gboolean valid_xds_drag; + const GValue *value; drag_info = user_data; action = 0; - valid_text_drag = FALSE; - valid_xds_drag = FALSE; - if (gtk_drag_get_source_widget (context) == widget) + value = gtk_drop_target_get_value (target); + if (value == NULL) { - goto out; + return 0; } - window = gtk_widget_get_root (widget); + window = gtk_widget_get_root (drag_info->widget); g_assert (NAUTILUS_IS_WINDOW (window)); - if (!drag_info->have_data) - { - target = gtk_drag_dest_find_target (widget, context, NULL); - - if (target == GDK_NONE) - { - goto out; - } - - gtk_drag_get_data (widget, context, target, time); - } - target_uri = NULL; if (drag_info->target_file != NULL) { @@ -227,48 +195,18 @@ slot_proxy_drag_motion (GtkWidget *widget, } } - if (drag_info->have_data && - drag_info->have_valid_data) + if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) { - if (drag_info->info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) - { - nautilus_drag_default_drop_action_for_icons (context, target_uri, - drag_info->data.selection_list, - 0, - &action); - } - else if (drag_info->info == NAUTILUS_ICON_DND_URI_LIST) - { - action = nautilus_drag_default_drop_action_for_uri_list (context, target_uri); - } - else if (drag_info->info == NAUTILUS_ICON_DND_TEXT) - { - valid_text_drag = TRUE; - } - else if (drag_info->info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE || - drag_info->info == NAUTILUS_ICON_DND_RAW) - { - valid_xds_drag = TRUE; - } + GSList *items = g_value_get_boxed (value); + action = nautilus_dnd_get_prefered_action (drag_info->target_file, items->data); } g_free (target_uri); out: - if (action != 0 || valid_text_drag || valid_xds_drag) - { - gtk_drag_highlight (widget); - slot_proxy_check_switch_location_timer (drag_info, widget); - } - else - { - gtk_drag_unhighlight (widget); - slot_proxy_remove_switch_location_timer (drag_info); - } - - gdk_drag_status (context, action, time); + slot_proxy_check_switch_location_timer (drag_info); - return TRUE; + return action; } static void @@ -286,96 +224,37 @@ static void drag_info_clear (NautilusDragSlotProxyInfo *drag_info) { slot_proxy_remove_switch_location_timer (drag_info); - - if (!drag_info->have_data) - { - goto out; - } - - if (drag_info->info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) - { - nautilus_drag_destroy_selection_list (drag_info->data.selection_list); - } - else if (drag_info->info == NAUTILUS_ICON_DND_URI_LIST) - { - g_list_free (drag_info->data.uri_list); - } - else if (drag_info->info == NAUTILUS_ICON_DND_TEXT || - drag_info->info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE || - drag_info->info == NAUTILUS_ICON_DND_RAW) - { - if (drag_info->data.selection_data != NULL) - { - gtk_selection_data_free (drag_info->data.selection_data); - } - } - -out: - drag_info->have_data = FALSE; - drag_info->have_valid_data = FALSE; - - drag_info->drop_occurred = FALSE; } static void -slot_proxy_drag_leave (GtkWidget *widget, - GdkDragContext *context, - unsigned int time, - gpointer user_data) +slot_proxy_drag_leave (GtkDropTarget *target, + gpointer user_data) { NautilusDragSlotProxyInfo *drag_info; drag_info = user_data; - gtk_drag_unhighlight (widget); drag_info_clear (drag_info); } -static gboolean -slot_proxy_drag_drop (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - unsigned int time, - gpointer user_data) -{ - GdkAtom target; - NautilusDragSlotProxyInfo *drag_info; - - drag_info = user_data; - g_assert (!drag_info->have_data); - - drag_info->drop_occurred = TRUE; - - target = gtk_drag_dest_find_target (widget, context, NULL); - gtk_drag_get_data (widget, context, target, time); - - return TRUE; -} - - static void -slot_proxy_handle_drop (GtkWidget *widget, - GdkDragContext *context, - unsigned int time, - NautilusDragSlotProxyInfo *drag_info) +slot_proxy_handle_drop (GtkDropTarget *target, + const GValue *value, + gdouble x, + gdouble y, + gpointer user_data) { GtkRoot *window; NautilusWindowSlot *target_slot; NautilusFilesView *target_view; char *target_uri; - GList *uri_list; + GList *uri_list = NULL; GFile *location; + NautilusDragSlotProxyInfo *drag_info; - if (!drag_info->have_data || - !drag_info->have_valid_data) - { - gtk_drag_finish (context, FALSE, FALSE, time); - drag_info_clear (drag_info); - return; - } + drag_info = user_data; - window = gtk_widget_get_root (widget); + window = gtk_widget_get_root (drag_info->widget); g_assert (NAUTILUS_IS_WINDOW (window)); if (drag_info->target_slot != NULL) @@ -413,105 +292,33 @@ slot_proxy_handle_drop (GtkWidget *widget, if (target_slot != NULL && target_view != NULL) { - if (drag_info->info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) + if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) { - uri_list = nautilus_drag_uri_list_from_selection_list (drag_info->data.selection_list); - g_assert (uri_list != NULL); + GSList *items = g_value_get_boxed (value); + for (GSList *l = items; l != NULL; l = l->next) + { + uri_list = g_list_prepend (uri_list, g_file_get_uri (l->data)); + } nautilus_files_view_drop_proxy_received_uris (target_view, uri_list, target_uri, - gdk_drag_context_get_selected_action (context)); + gdk_drop_get_actions (gtk_drop_target_get_current_drop (target))); g_list_free_full (uri_list, g_free); } - else if (drag_info->info == NAUTILUS_ICON_DND_URI_LIST) - { - nautilus_files_view_drop_proxy_received_uris (target_view, - drag_info->data.uri_list, - target_uri, - gdk_drag_context_get_selected_action (context)); - } - - gtk_drag_finish (context, TRUE, FALSE, time); - } - else - { - gtk_drag_finish (context, FALSE, FALSE, time); } - g_free (target_uri); drag_info_clear (drag_info); } -static void -slot_proxy_drag_data_received (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - GtkSelectionData *data, - unsigned int info, - unsigned int time, - gpointer user_data) -{ - NautilusDragSlotProxyInfo *drag_info; - char **uris; - - drag_info = user_data; - - g_assert (!drag_info->have_data); - - drag_info->have_data = TRUE; - drag_info->info = info; - - if (gtk_selection_data_get_length (data) < 0) - { - drag_info->have_valid_data = FALSE; - return; - } - - if (info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) - { - drag_info->data.selection_list = nautilus_drag_build_selection_list (data); - - drag_info->have_valid_data = drag_info->data.selection_list != NULL; - } - else if (info == NAUTILUS_ICON_DND_URI_LIST) - { - uris = gtk_selection_data_get_uris (data); - drag_info->data.uri_list = nautilus_drag_uri_list_from_array ((const char **) uris); - g_strfreev (uris); - - drag_info->have_valid_data = drag_info->data.uri_list != NULL; - } - else if (info == NAUTILUS_ICON_DND_TEXT || - info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE || - info == NAUTILUS_ICON_DND_RAW) - { - drag_info->data.selection_data = gtk_selection_data_copy (data); - drag_info->have_valid_data = drag_info->data.selection_data != NULL; - } - - if (drag_info->drop_occurred) - { - slot_proxy_handle_drop (widget, context, time, drag_info); - } -} - void nautilus_drag_slot_proxy_init (GtkWidget *widget, NautilusFile *target_file, NautilusWindowSlot *target_slot) { NautilusDragSlotProxyInfo *drag_info; - - const GtkTargetEntry targets[] = - { - { NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST }, - { NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, 0, NAUTILUS_ICON_DND_XDNDDIRECTSAVE }, /* XDS Protocol Type */ - { NAUTILUS_ICON_DND_RAW_TYPE, 0, NAUTILUS_ICON_DND_RAW } - }; - GtkTargetList *target_list; + GtkDropTarget *target; g_assert (GTK_IS_WIDGET (widget)); @@ -533,31 +340,15 @@ nautilus_drag_slot_proxy_init (GtkWidget *widget, } drag_info->widget = widget; - - gtk_drag_dest_set (widget, 0, - NULL, 0, - GDK_ACTION_MOVE | - GDK_ACTION_COPY | - GDK_ACTION_LINK | - GDK_ACTION_ASK); - - target_list = gtk_target_list_new (targets, G_N_ELEMENTS (targets)); - gtk_target_list_add_uri_targets (target_list, NAUTILUS_ICON_DND_URI_LIST); - gtk_target_list_add_text_targets (target_list, NAUTILUS_ICON_DND_TEXT); - gtk_drag_dest_set_target_list (widget, target_list); - gtk_target_list_unref (target_list); - - g_signal_connect (widget, "drag-motion", - G_CALLBACK (slot_proxy_drag_motion), - drag_info); - g_signal_connect (widget, "drag-drop", - G_CALLBACK (slot_proxy_drag_drop), - drag_info); - g_signal_connect (widget, "drag-data-received", - G_CALLBACK (slot_proxy_drag_data_received), - drag_info); - g_signal_connect (widget, "drag-leave", - G_CALLBACK (slot_proxy_drag_leave), - drag_info); + /* TODO: Implement GDK_ACTION_ASK */ + target = gtk_drop_target_new (G_TYPE_INVALID, GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK); + + gtk_drop_target_set_preload (target, TRUE); + /* TODO: Implement GDK_TYPE_STRING */ + gtk_drop_target_set_gtypes (target, (GType[1]) { GDK_TYPE_FILE_LIST }, 1); + g_signal_connect (target, "enter", G_CALLBACK (slot_proxy_drag_motion), drag_info); + g_signal_connect (target, "motion", G_CALLBACK (slot_proxy_drag_motion), drag_info); + g_signal_connect (target, "drop", G_CALLBACK (slot_proxy_handle_drop), drag_info); + g_signal_connect (target, "leave", G_CALLBACK (slot_proxy_drag_leave), drag_info); + gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (target)); } -#endif diff --git a/src/nautilus-window-slot-dnd.h b/src/nautilus-window-slot-dnd.h index 0988dffb29..bd77969a90 100644 --- a/src/nautilus-window-slot-dnd.h +++ b/src/nautilus-window-slot-dnd.h @@ -25,7 +25,6 @@ #pragma once -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION #include #include @@ -36,4 +35,3 @@ void nautilus_drag_slot_proxy_init (GtkWidget *widget, NautilusFile *target_file, NautilusWindowSlot *target_slot); -#endif -- GitLab From ef1eb2aaf95fb471eb4a35e2f73602a96a3884ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Thu, 30 Jun 2022 01:28:55 +0100 Subject: [PATCH 04/12] dnd: Major cleanup GTK4 does the heavy lifting now, with GtkDropTarget and company. So, remove code that's no longer going to be useful. Also slim down file headers. --- src/nautilus-dnd.c | 729 +-------------------------------------------- src/nautilus-dnd.h | 135 +-------- 2 files changed, 21 insertions(+), 843 deletions(-) diff --git a/src/nautilus-dnd.c b/src/nautilus-dnd.c index e57be8a27c..eb25b5d4ed 100644 --- a/src/nautilus-dnd.c +++ b/src/nautilus-dnd.c @@ -1,356 +1,15 @@ -/* nautilus-dnd.c - Common Drag & drop handling code shared by the icon container - * and the list view. +/* nautilus-dnd.h - Common Drag & drop handling code * - * Copyright (C) 2000, 2001 Eazel, Inc. + * Authors: Pavel Cisler , + * Ettore Perazzoli + * Copyright (C) 2000, 2001 Eazel, Inc. + * Copyright (C) 2022 The GNOME project contributors * - * The Gnome 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. - * - * The Gnome 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 the Gnome Library; see the file COPYING.LIB. If not, - * see . - * - * Authors: Pavel Cisler , - * Ettore Perazzoli + * SPDX-License-Identifier: GPL-3.0-or-later */ -#include #include "nautilus-dnd.h" -#include - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - -#include "nautilus-program-choosing.h" -#include -#include -#include -#include -#include "nautilus-file-utilities.h" -#include -#include - -/* a set of defines stolen from the eel-icon-dnd.c file. - * These are in microseconds. - */ -#define AUTOSCROLL_TIMEOUT_INTERVAL 100 -#define AUTOSCROLL_INITIAL_DELAY 100000 - -/* drag this close to the view edge to start auto scroll*/ -#define AUTO_SCROLL_MARGIN 30 - -/* the smallest amount of auto scroll used when we just enter the autoscroll - * margin - */ -#define MIN_AUTOSCROLL_DELTA 5 - -/* the largest amount of auto scroll used when we are right over the view - * edge - */ -#define MAX_AUTOSCROLL_DELTA 50 - -void -nautilus_drag_init (NautilusDragInfo *drag_info, - const GtkTargetEntry *drag_types, - int drag_type_count, - gboolean add_text_targets) -{ - drag_info->target_list = gtk_target_list_new (drag_types, - drag_type_count); - - if (add_text_targets) - { - gtk_target_list_add_text_targets (drag_info->target_list, - NAUTILUS_ICON_DND_TEXT); - } - - drag_info->drop_occurred = FALSE; - drag_info->need_to_destroy = FALSE; -} - -void -nautilus_drag_finalize (NautilusDragInfo *drag_info) -{ - gtk_target_list_unref (drag_info->target_list); - nautilus_drag_destroy_selection_list (drag_info->selection_list); - nautilus_drag_destroy_selection_list (drag_info->selection_cache); - - g_free (drag_info); -} - - -/* Functions to deal with NautilusDragSelectionItems. */ - -NautilusDragSelectionItem * -nautilus_drag_selection_item_new (void) -{ - return g_new0 (NautilusDragSelectionItem, 1); -} - -static void -drag_selection_item_destroy (NautilusDragSelectionItem *item) -{ - g_clear_object (&item->file); - g_free (item->uri); - g_free (item); -} - -void -nautilus_drag_destroy_selection_list (GList *list) -{ - GList *p; - - if (list == NULL) - { - return; - } - - for (p = list; p != NULL; p = p->next) - { - drag_selection_item_destroy (p->data); - } - - g_list_free (list); -} - -GList * -nautilus_drag_uri_list_from_selection_list (const GList *selection_list) -{ - NautilusDragSelectionItem *selection_item; - GList *uri_list; - const GList *l; - - uri_list = NULL; - for (l = selection_list; l != NULL; l = l->next) - { - selection_item = (NautilusDragSelectionItem *) l->data; - if (selection_item->uri != NULL) - { - uri_list = g_list_prepend (uri_list, g_strdup (selection_item->uri)); - } - } - - return g_list_reverse (uri_list); -} - -/* - * Transfer: Full. Free with g_list_free_full (list, g_object_unref); - */ -GList * -nautilus_drag_file_list_from_selection_list (const GList *selection_list) -{ - NautilusDragSelectionItem *selection_item; - GList *file_list; - const GList *l; - - file_list = NULL; - for (l = selection_list; l != NULL; l = l->next) - { - selection_item = (NautilusDragSelectionItem *) l->data; - if (selection_item->file != NULL) - { - file_list = g_list_prepend (file_list, g_object_ref (selection_item->file)); - } - } - - return g_list_reverse (file_list); -} - -GList * -nautilus_drag_uri_list_from_array (const char **uris) -{ - GList *uri_list; - int i; - - if (uris == NULL) - { - return NULL; - } - - uri_list = NULL; - - for (i = 0; uris[i] != NULL; i++) - { - uri_list = g_list_prepend (uri_list, g_strdup (uris[i])); - } - - return g_list_reverse (uri_list); -} - -GList * -nautilus_drag_build_selection_list (GtkSelectionData *data) -{ - GList *result; - const guchar *p, *oldp; - int size; - - result = NULL; - oldp = gtk_selection_data_get_data (data); - size = gtk_selection_data_get_length (data); - - while (size > 0) - { - NautilusDragSelectionItem *item; - guint len; - - /* The list is in the form: - * - * name\rx:y:width:height\r\n - * - * The geometry information after the first \r is optional. */ - - /* 1: Decode name. */ - - p = memchr (oldp, '\r', size); - if (p == NULL) - { - break; - } - - item = nautilus_drag_selection_item_new (); - - len = p - oldp; - - item->uri = g_malloc (len + 1); - memcpy (item->uri, oldp, len); - item->uri[len] = 0; - item->file = nautilus_file_get_by_uri (item->uri); - - p++; - if (*p == '\n' || *p == '\0') - { - result = g_list_prepend (result, item); - if (p == 0) - { - g_warning ("Invalid x-special/gnome-icon-list data received: " - "missing newline character."); - break; - } - else - { - oldp = p + 1; - continue; - } - } - - size -= p - oldp; - oldp = p; - - /* 2: Decode geometry information. */ - - item->got_icon_position = sscanf ((const gchar *) p, "%d:%d:%d:%d%*s", - &item->icon_x, &item->icon_y, - &item->icon_width, &item->icon_height) == 4; - if (!item->got_icon_position) - { - g_warning ("Invalid x-special/gnome-icon-list data received: " - "invalid icon position specification."); - } - - result = g_list_prepend (result, item); - - p = memchr (p, '\r', size); - if (p == NULL || p[1] != '\n') - { - g_warning ("Invalid x-special/gnome-icon-list data received: " - "missing newline character."); - if (p == NULL) - { - break; - } - } - else - { - p += 2; - } - - size -= p - oldp; - oldp = p; - } - - return g_list_reverse (result); -} - -static gboolean -nautilus_drag_file_local_internal (const char *target_uri_string, - const char *first_source_uri) -{ - /* check if the first item on the list has target_uri_string as a parent - * FIXME: - * we should really test each item but that would be slow for large selections - * and currently dropped items can only be from the same container - */ - GFile *target, *item, *parent; - gboolean result; - - result = FALSE; - - target = g_file_new_for_uri (target_uri_string); - - /* get the parent URI of the first item in the selection */ - item = g_file_new_for_uri (first_source_uri); - parent = g_file_get_parent (item); - g_object_unref (item); - - if (parent != NULL) - { - result = g_file_equal (parent, target); - g_object_unref (parent); - } - - g_object_unref (target); - - return result; -} - -gboolean -nautilus_drag_uris_local (const char *target_uri, - const GList *source_uri_list) -{ - /* must have at least one item */ - g_assert (source_uri_list); - - return nautilus_drag_file_local_internal (target_uri, source_uri_list->data); -} - -gboolean -nautilus_drag_items_local (const char *target_uri_string, - const GList *selection_list) -{ - /* must have at least one item */ - g_assert (selection_list); - - return nautilus_drag_file_local_internal (target_uri_string, - ((NautilusDragSelectionItem *) selection_list->data)->uri); -} - -GdkDragAction -nautilus_drag_default_drop_action_for_netscape_url (GdkDragContext *context) -{ - /* Mozilla defaults to copy, but unless thats the - * only allowed thing (enforced by ctrl) we want to LINK */ - if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY && - gdk_drag_context_get_actions (context) != GDK_ACTION_COPY) - { - return GDK_ACTION_LINK; - } - else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE) - { - /* Don't support move */ - return GDK_ACTION_COPY; - } - - return gdk_drag_context_get_suggested_action (context); -} -#endif - static gboolean check_same_fs (NautilusFile *file1, NautilusFile *file2) @@ -397,234 +56,6 @@ source_is_deletable (GFile *file) } #if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - -NautilusDragInfo * -nautilus_drag_get_source_data (GdkDragContext *context) -{ - GtkWidget *source_widget; - NautilusDragInfo *source_data; - - source_widget = gtk_drag_get_source_widget (context); - if (source_widget == NULL) - { - return NULL; - } - - if (GTK_IS_TREE_VIEW (source_widget)) - { - NautilusWindow *window; - NautilusWindowSlot *active_slot; - NautilusView *view; - - window = NAUTILUS_WINDOW (gtk_widget_get_root (source_widget)); - active_slot = nautilus_window_get_active_slot (window); - view = nautilus_window_slot_get_current_view (active_slot); - if (NAUTILUS_IS_LIST_VIEW (view)) - { - source_data = nautilus_list_view_dnd_get_drag_source_data (NAUTILUS_LIST_VIEW (view), - context); - } - else - { - g_warning ("Got a drag context with a tree view source widget, but current view is not list view"); - source_data = NULL; - } - } - else - { - /* it's a slot or something else */ - g_warning ("Requested drag source data from a widget that doesn't support it"); - source_data = NULL; - } - - return source_data; -} -#endif - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - -GdkDragAction -nautilus_drag_default_drop_action_for_uri_list (GdkDragContext *context, - const char *target_uri_string) -{ - if (eel_uri_is_trash (target_uri_string) && (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE)) - { - /* Only move to Trash */ - return GDK_ACTION_MOVE; - } - else - { - return gdk_drag_context_get_suggested_action (context); - } -} - -/* Encode a "x-special/gnome-icon-list" selection. - * Along with the URIs of the dragged files, this encodes - * the location and size of each icon relative to the cursor. - */ -static void -add_one_gnome_icon (const char *uri, - int x, - int y, - int w, - int h, - gpointer data) -{ - GString *result; - - result = (GString *) data; - - g_string_append_printf (result, "%s\r%d:%d:%hu:%hu\r\n", - uri, x, y, w, h); -} - -static void -add_one_uri (const char *uri, - int x, - int y, - int w, - int h, - gpointer data) -{ - GString *result; - - result = (GString *) data; - - g_string_append (result, uri); - g_string_append (result, "\r\n"); -} - -static void -cache_one_item (const char *uri, - int x, - int y, - int w, - int h, - gpointer data) -{ - GList **cache = data; - NautilusDragSelectionItem *item; - - item = nautilus_drag_selection_item_new (); - item->uri = nautilus_uri_to_native_uri (uri); - - if (item->uri == NULL) - { - item->uri = g_strdup (uri); - } - - item->file = nautilus_file_get_by_uri (uri); - item->icon_x = x; - item->icon_y = y; - item->icon_width = w; - item->icon_height = h; - *cache = g_list_prepend (*cache, item); -} - -GList * -nautilus_drag_create_selection_cache (gpointer container_context, - NautilusDragEachSelectedItemIterator each_selected_item_iterator) -{ - GList *cache = NULL; - - (*each_selected_item_iterator)(cache_one_item, container_context, &cache); - cache = g_list_reverse (cache); - - return cache; -} - -/* Common function for drag_data_get_callback calls. - * Returns FALSE if it doesn't handle drag data */ -gboolean -nautilus_drag_drag_data_get_from_cache (GList *cache, - GdkDragContext *context, - GtkSelectionData *selection_data, - guint info, - guint32 time) -{ - GList *l; - GString *result; - NautilusDragEachSelectedItemDataGet func; - - if (cache == NULL) - { - return FALSE; - } - - switch (info) - { - case NAUTILUS_ICON_DND_GNOME_ICON_LIST: - { - func = add_one_gnome_icon; - } - break; - - case NAUTILUS_ICON_DND_URI_LIST: - case NAUTILUS_ICON_DND_TEXT: - { - func = add_one_uri; - } - break; - - default: - { - return FALSE; - } - } - - result = g_string_new (NULL); - - for (l = cache; l != NULL; l = l->next) - { - NautilusDragSelectionItem *item = l->data; - (*func)(item->uri, item->icon_x, item->icon_y, item->icon_width, item->icon_height, result); - } - - gtk_selection_data_set (selection_data, - gtk_selection_data_get_target (selection_data), - 8, (guchar *) result->str, result->len); - g_string_free (result, TRUE); - - return TRUE; -} - -typedef struct -{ - GMainLoop *loop; - GdkDragAction chosen; -} DropActionMenuData; - -static void -menu_deactivate_callback (GtkWidget *menu, - gpointer data) -{ - DropActionMenuData *damd; - - damd = data; - - if (g_main_loop_is_running (damd->loop)) - { - g_main_loop_quit (damd->loop); - } -} - -static void -drop_action_activated_callback (GtkWidget *menu_item, - gpointer data) -{ - DropActionMenuData *damd; - - damd = data; - - damd->chosen = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item), - "action")); - - if (g_main_loop_is_running (damd->loop)) - { - g_main_loop_quit (damd->loop); - } -} - static void append_drop_action_menu_item (GtkWidget *menu, const char *text, @@ -650,12 +81,13 @@ append_drop_action_menu_item (GtkWidget *menu, gtk_widget_show (menu_item); } - +#endif /* Pops up a menu of actions to perform on dropped files */ GdkDragAction nautilus_drag_drop_action_ask (GtkWidget *widget, GdkDragAction actions) { +#if 0 GtkWidget *popover; GtkWidget *menu; GtkWidget *menu_item; @@ -723,150 +155,9 @@ nautilus_drag_drop_action_ask (GtkWidget *widget, g_object_unref (popover); return damd.chosen; -} - -gboolean -nautilus_drag_autoscroll_in_scroll_region (GtkWidget *widget) -{ - float x_scroll_delta, y_scroll_delta; - - nautilus_drag_autoscroll_calculate_delta (widget, &x_scroll_delta, &y_scroll_delta); - - return x_scroll_delta != 0 || y_scroll_delta != 0; -} - - -void -nautilus_drag_autoscroll_calculate_delta (GtkWidget *widget, - float *x_scroll_delta, - float *y_scroll_delta) -{ - GtkAllocation allocation; - GdkDisplay *display; - GdkSeat *seat; - GdkDevice *pointer; - int x, y; - - g_assert (GTK_IS_WIDGET (widget)); - - display = gtk_widget_get_display (widget); - seat = gdk_display_get_default_seat (display); - pointer = gdk_seat_get_pointer (seat); - gdk_window_get_device_position (gtk_widget_get_window (widget), pointer, - &x, &y, NULL); - - /* Find out if we are anywhere close to the tree view edges - * to see if we need to autoscroll. - */ - *x_scroll_delta = 0; - *y_scroll_delta = 0; - - if (x < AUTO_SCROLL_MARGIN) - { - *x_scroll_delta = (float) (x - AUTO_SCROLL_MARGIN); - } - - gtk_widget_get_allocation (widget, &allocation); - if (x > allocation.width - AUTO_SCROLL_MARGIN) - { - if (*x_scroll_delta != 0) - { - /* Already trying to scroll because of being too close to - * the top edge -- must be the window is really short, - * don't autoscroll. - */ - return; - } - *x_scroll_delta = (float) (x - (allocation.width - AUTO_SCROLL_MARGIN)); - } - - if (y < AUTO_SCROLL_MARGIN) - { - *y_scroll_delta = (float) (y - AUTO_SCROLL_MARGIN); - } - - if (y > allocation.height - AUTO_SCROLL_MARGIN) - { - if (*y_scroll_delta != 0) - { - /* Already trying to scroll because of being too close to - * the top edge -- must be the window is really narrow, - * don't autoscroll. - */ - return; - } - *y_scroll_delta = (float) (y - (allocation.height - AUTO_SCROLL_MARGIN)); - } - - if (*x_scroll_delta == 0 && *y_scroll_delta == 0) - { - /* no work */ - return; - } - - /* Adjust the scroll delta to the proper acceleration values depending on how far - * into the sroll margins we are. - * FIXME bugzilla.eazel.com 2486: - * we could use an exponential acceleration factor here for better feel - */ - if (*x_scroll_delta != 0) - { - *x_scroll_delta /= AUTO_SCROLL_MARGIN; - *x_scroll_delta *= (MAX_AUTOSCROLL_DELTA - MIN_AUTOSCROLL_DELTA); - *x_scroll_delta += MIN_AUTOSCROLL_DELTA; - } - - if (*y_scroll_delta != 0) - { - *y_scroll_delta /= AUTO_SCROLL_MARGIN; - *y_scroll_delta *= (MAX_AUTOSCROLL_DELTA - MIN_AUTOSCROLL_DELTA); - *y_scroll_delta += MIN_AUTOSCROLL_DELTA; - } -} - - - -void -nautilus_drag_autoscroll_start (NautilusDragInfo *drag_info, - GtkWidget *widget, - GSourceFunc callback, - gpointer user_data) -{ - if (nautilus_drag_autoscroll_in_scroll_region (widget)) - { - if (drag_info->auto_scroll_timeout_id == 0) - { - drag_info->waiting_to_autoscroll = TRUE; - drag_info->start_auto_scroll_in = g_get_monotonic_time () - + AUTOSCROLL_INITIAL_DELAY; - - drag_info->auto_scroll_timeout_id = g_timeout_add - (AUTOSCROLL_TIMEOUT_INTERVAL, - callback, - user_data); - } - } - else - { - if (drag_info->auto_scroll_timeout_id != 0) - { - g_source_remove (drag_info->auto_scroll_timeout_id); - drag_info->auto_scroll_timeout_id = 0; - } - } -} - -void -nautilus_drag_autoscroll_stop (NautilusDragInfo *drag_info) -{ - if (drag_info->auto_scroll_timeout_id != 0) - { - g_source_remove (drag_info->auto_scroll_timeout_id); - drag_info->auto_scroll_timeout_id = 0; - } -} - #endif + return 0; +} GdkDragAction nautilus_dnd_get_prefered_action (NautilusFile *target_file, diff --git a/src/nautilus-dnd.h b/src/nautilus-dnd.h index 2c45a0ec18..5fec2858b0 100644 --- a/src/nautilus-dnd.h +++ b/src/nautilus-dnd.h @@ -1,26 +1,12 @@ - -/* nautilus-dnd.h - Common Drag & drop handling code shared by the icon container - and the list view. - - Copyright (C) 2000 Eazel, Inc. - - The Gnome 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. - - The Gnome 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 the Gnome Library; see the file COPYING.LIB. If not, - see . - - Authors: Pavel Cisler , - Ettore Perazzoli -*/ +/* nautilus-dnd.h - Common Drag & drop handling code + * + * Authors: Pavel Cisler , + * Ettore Perazzoli + * Copyright (C) 2000 Eazel, Inc. + * Copyright (C) 2022 The GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ #pragma once @@ -29,8 +15,6 @@ #define HOVER_TIMEOUT 500 -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - /* Drag & Drop target names. */ #define NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE "x-special/gnome-icon-list" #define NAUTILUS_ICON_DND_URI_LIST_TYPE "text/uri-list" @@ -39,105 +23,8 @@ #define NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE "XdndDirectSave0" /* XDS Protocol Type */ #define NAUTILUS_ICON_DND_RAW_TYPE "application/octet-stream" -/* drag&drop-related information. */ -typedef struct { - GtkTargetList *target_list; - - /* Stuff saved at "receive data" time needed later in the drag. */ - gboolean got_drop_data_type; - NautilusIconDndTargetType data_type; - GtkSelectionData *selection_data; - char *direct_save_uri; - - /* Start of the drag, in window coordinates. */ - int start_x, start_y; - - /* List of NautilusDragSelectionItems, representing items being dragged, or NULL - * if data about them has not been received from the source yet. - */ - GList *selection_list; - - /* cache of selected URIs, representing items being dragged */ - GList *selection_cache; - - /* File selection list information request handler, for the call for - * information (mostly the file system info, in order to know if we want - * co copy or move the files) about the files being dragged, that can - * come from another nautilus process, like the desktop. */ - NautilusFileListHandle *file_list_info_handler; - - /* has the drop occurred ? */ - gboolean drop_occurred; - - /* whether or not need to clean up the previous dnd data */ - gboolean need_to_destroy; - - /* autoscrolling during dragging */ - int auto_scroll_timeout_id; - gboolean waiting_to_autoscroll; - gint64 start_auto_scroll_in; - - /* source context actions. Used for peek the actions using a GdkDragContext - * source at drag-begin time when they are not available yet (they become - * available at drag-motion time) */ - guint32 source_actions; - -} NautilusDragInfo; - -typedef void (* NautilusDragEachSelectedItemDataGet) (const char *url, - int x, int y, int w, int h, - gpointer data); -typedef void (* NautilusDragEachSelectedItemIterator) (NautilusDragEachSelectedItemDataGet iteratee, - gpointer iterator_context, - gpointer data); - -void nautilus_drag_init (NautilusDragInfo *drag_info, - const GtkTargetEntry *drag_types, - int drag_type_count, - gboolean add_text_targets); -void nautilus_drag_finalize (NautilusDragInfo *drag_info); -NautilusDragSelectionItem *nautilus_drag_selection_item_new (void); -void nautilus_drag_destroy_selection_list (GList *selection_list); -GList *nautilus_drag_build_selection_list (GtkSelectionData *data); - -GList * nautilus_drag_uri_list_from_selection_list (const GList *selection_list); - -GList * nautilus_drag_uri_list_from_array (const char **uris); - -gboolean nautilus_drag_items_local (const char *target_uri, - const GList *selection_list); -gboolean nautilus_drag_uris_local (const char *target_uri, - const GList *source_uri_list); -GdkDragAction nautilus_drag_default_drop_action_for_netscape_url (GdkDragContext *context); -GdkDragAction nautilus_drag_default_drop_action_for_uri_list (GdkDragContext *context, - const char *target_uri_string); -GList *nautilus_drag_create_selection_cache (gpointer container_context, - NautilusDragEachSelectedItemIterator each_selected_item_iterator); -gboolean nautilus_drag_drag_data_get_from_cache (GList *cache, - GdkDragContext *context, - GtkSelectionData *selection_data, - guint info, - guint32 time); -int nautilus_drag_modifier_based_action (int default_action, - int non_default_action); - -GdkDragAction nautilus_drag_drop_action_ask (GtkWidget *widget, - GdkDragAction possible_actions); - -gboolean nautilus_drag_autoscroll_in_scroll_region (GtkWidget *widget); -void nautilus_drag_autoscroll_calculate_delta (GtkWidget *widget, - float *x_scroll_delta, - float *y_scroll_delta); -void nautilus_drag_autoscroll_start (NautilusDragInfo *drag_info, - GtkWidget *widget, - GSourceFunc callback, - gpointer user_data); -void nautilus_drag_autoscroll_stop (NautilusDragInfo *drag_info); - -NautilusDragInfo * nautilus_drag_get_source_data (GdkDragContext *context); - -GList * nautilus_drag_file_list_from_selection_list (const GList *selection_list); -#endif +GdkDragAction nautilus_drag_drop_action_ask (GtkWidget *widget, + GdkDragAction possible_actions); GdkDragAction nautilus_dnd_get_prefered_action (NautilusFile *target_file, GFile *dropped); -- GitLab From e4652cf398359d9cd1235c0ac242bd7a64ba2677 Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Thu, 16 Jun 2022 08:07:49 -0700 Subject: [PATCH 05/12] properties: Restore icon drop target --- src/nautilus-properties-window.c | 71 +++++++++++--------------------- 1 file changed, 25 insertions(+), 46 deletions(-) diff --git a/src/nautilus-properties-window.c b/src/nautilus-properties-window.c index b3bfa1991d..b1572ce9d3 100644 --- a/src/nautilus-properties-window.c +++ b/src/nautilus-properties-window.c @@ -254,22 +254,6 @@ typedef struct gboolean cancelled; } StartupData; -/* drag and drop definitions */ - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION -enum -{ - TARGET_URI_LIST, - TARGET_GNOME_URI_LIST, -}; - -static const GtkTargetEntry target_table[] = -{ - { "text/uri-list", 0, TARGET_URI_LIST }, - { "x-special/gnome-icon-list", 0, TARGET_GNOME_URI_LIST }, -}; -#endif - #define DIRECTORY_CONTENTS_UPDATE_INTERVAL 200 /* milliseconds */ #define FILES_UPDATE_INTERVAL 200 /* milliseconds */ @@ -474,7 +458,6 @@ update_properties_window_icon (NautilusPropertiesWindow *self) gtk_image_set_pixel_size (GTK_IMAGE (self->icon_button_image), pixel_size); } -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION /* utility to test if a uri refers to a local image */ static gboolean uri_is_local_image (const char *uri) @@ -497,7 +480,6 @@ uri_is_local_image (const char *uri) return TRUE; } -#endif static void reset_icon (NautilusPropertiesWindow *self) @@ -516,27 +498,28 @@ reset_icon (NautilusPropertiesWindow *self) } } -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION static void -nautilus_properties_window_drag_data_received (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - GtkSelectionData *selection_data, - guint info, - guint time) -{ - g_auto (GStrv) uris = NULL; +nautilus_properties_window_drag_drop_cb (GtkDropTarget *target, + const GValue *value, + gdouble x, + gdouble y, + gpointer user_data) +{ + GSList *file_list; gboolean exactly_one; GtkImage *image; GtkWindow *window; - image = GTK_IMAGE (widget); + image = GTK_IMAGE (user_data); window = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (image))); - uris = g_strsplit ((const gchar *) gtk_selection_data_get_data (selection_data), "\r\n", 0); - exactly_one = uris[0] != NULL && (uris[1] == NULL || uris[1][0] == '\0'); + if (!G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) + { + return; + } + file_list = g_value_get_boxed (value); + exactly_one = file_list != NULL && g_slist_next (file_list) == NULL; if (!exactly_one) { @@ -547,16 +530,15 @@ nautilus_properties_window_drag_data_received (GtkWidget *widget, } else { - if (uri_is_local_image (uris[0])) + g_autofree gchar *uri = g_file_get_uri (file_list->data); + + if (uri_is_local_image (uri)) { - set_icon (uris[0], NAUTILUS_PROPERTIES_WINDOW (window)); + set_icon (uri, NAUTILUS_PROPERTIES_WINDOW (window)); } else { - g_autoptr (GFile) f = NULL; - - f = g_file_new_for_uri (uris[0]); - if (!g_file_is_native (f)) + if (!g_file_is_native (file_list->data)) { show_dialog (_("The file that you dropped is not local."), _("You can only use local images as custom icons."), @@ -573,7 +555,6 @@ nautilus_properties_window_drag_data_received (GtkWidget *widget, } } } -#endif static void setup_image_widget (NautilusPropertiesWindow *self, @@ -583,16 +564,14 @@ setup_image_widget (NautilusPropertiesWindow *self, if (is_customizable) { -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION + GtkDropTarget *target; + /* prepare the image to receive dropped objects to assign custom images */ - gtk_drag_dest_set (self->icon_button_image, - GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, - target_table, G_N_ELEMENTS (target_table), - GDK_ACTION_COPY | GDK_ACTION_MOVE); + target = gtk_drop_target_new (GDK_TYPE_FILE_LIST, GDK_ACTION_COPY); + gtk_widget_add_controller (self->icon_button, GTK_EVENT_CONTROLLER (target)); + g_signal_connect (target, "drop", + G_CALLBACK (nautilus_properties_window_drag_drop_cb), self->icon_button_image); - g_signal_connect (self->icon_button_image, "drag-data-received", - G_CALLBACK (nautilus_properties_window_drag_data_received), NULL); -#endif g_signal_connect (self->icon_button, "clicked", G_CALLBACK (select_image_button_callback), self); gtk_stack_set_visible_child (self->icon_stack, self->icon_button); -- GitLab From a611f33227fa5a47c662c0885758210e739bda2d Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Thu, 16 Jun 2022 08:31:28 -0700 Subject: [PATCH 06/12] view-item: Add drag-accept property for DnD Set drag-accept to TRUE when a drag has entered an item. Set flag NAUTILUS_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT when the DnD has entered an item so that the icon will be update (i.e. the folder will show the "open folder" icon) --- src/nautilus-grid-cell.c | 15 +++++++++++++++ src/nautilus-name-cell.c | 15 +++++++++++++++ src/nautilus-view-item.c | 27 +++++++++++++++++++++++++++ src/nautilus-view-item.h | 2 ++ 4 files changed, 59 insertions(+) diff --git a/src/nautilus-grid-cell.c b/src/nautilus-grid-cell.c index 426a2c2a84..28fc43622b 100644 --- a/src/nautilus-grid-cell.c +++ b/src/nautilus-grid-cell.c @@ -31,6 +31,7 @@ update_icon (NautilusGridCell *self) { NautilusViewItem *item; NautilusFileIconFlags flags; + gboolean drag_accept; g_autoptr (GdkPaintable) icon_paintable = NULL; GtkStyleContext *style_context; NautilusFile *file; @@ -46,6 +47,12 @@ update_icon (NautilusGridCell *self) NAUTILUS_FILE_ICON_FLAGS_USE_EMBLEMS | NAUTILUS_FILE_ICON_FLAGS_USE_ONE_EMBLEM; + g_object_get (item, "drag-accept", &drag_accept, NULL); + if (drag_accept) + { + flags |= NAUTILUS_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT; + } + icon_paintable = nautilus_file_get_icon_paintable (file, icon_size, 1, flags); gtk_picture_set_paintable (GTK_PICTURE (self->icon), icon_paintable); @@ -127,6 +134,12 @@ on_item_size_changed (NautilusGridCell *self) update_captions (self); } +static void +on_item_drag_accept_changed (NautilusGridCell *self) +{ + update_icon (self); +} + static void on_item_is_cut_changed (NautilusGridCell *self) { @@ -181,6 +194,8 @@ nautilus_grid_cell_init (NautilusGridCell *self) self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM); g_signal_group_connect_swapped (self->item_signal_group, "notify::icon-size", (GCallback) on_item_size_changed, self); + g_signal_group_connect_swapped (self->item_signal_group, "notify::drag-accept", + (GCallback) on_item_drag_accept_changed, self); g_signal_group_connect_swapped (self->item_signal_group, "notify::is-cut", (GCallback) on_item_is_cut_changed, self); g_signal_group_connect_swapped (self->item_signal_group, "file-changed", diff --git a/src/nautilus-name-cell.c b/src/nautilus-name-cell.c index daa6308a7d..9a269ac68a 100644 --- a/src/nautilus-name-cell.c +++ b/src/nautilus-name-cell.c @@ -133,6 +133,7 @@ static void update_icon (NautilusNameCell *self) { NautilusFileIconFlags flags; + gboolean drag_accept; g_autoptr (GdkPaintable) icon_paintable = NULL; GtkStyleContext *style_context; NautilusViewItem *item; @@ -152,6 +153,12 @@ update_icon (NautilusNameCell *self) NAUTILUS_FILE_ICON_FLAGS_USE_EMBLEMS | NAUTILUS_FILE_ICON_FLAGS_USE_ONE_EMBLEM; + g_object_get (item, "drag-accept", &drag_accept, NULL); + if (drag_accept) + { + flags |= NAUTILUS_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT; + } + icon_paintable = nautilus_file_get_icon_paintable (file, icon_size, 1, flags); gtk_picture_set_paintable (GTK_PICTURE (self->icon), icon_paintable); @@ -198,6 +205,12 @@ on_item_size_changed (NautilusNameCell *self) update_icon (self); } +static void +on_item_drag_accept_changed (NautilusNameCell *self) +{ + update_icon (self); +} + static void on_item_is_cut_changed (NautilusNameCell *self) { @@ -225,6 +238,8 @@ nautilus_name_cell_init (NautilusNameCell *self) self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM); g_signal_group_connect_swapped (self->item_signal_group, "notify::icon-size", (GCallback) on_item_size_changed, self); + g_signal_group_connect_swapped (self->item_signal_group, "notify::drag-accept", + (GCallback) on_item_drag_accept_changed, self); g_signal_group_connect_swapped (self->item_signal_group, "notify::is-cut", (GCallback) on_item_is_cut_changed, self); g_signal_group_connect_swapped (self->item_signal_group, "file-changed", diff --git a/src/nautilus-view-item.c b/src/nautilus-view-item.c index 560cfec42f..84423fa2d6 100644 --- a/src/nautilus-view-item.c +++ b/src/nautilus-view-item.c @@ -11,6 +11,7 @@ struct _NautilusViewItem GObject parent_instance; guint icon_size; gboolean is_cut; + gboolean drag_accept; NautilusFile *file; GtkWidget *item_ui; }; @@ -23,6 +24,7 @@ enum PROP_FILE, PROP_ICON_SIZE, PROP_IS_CUT, + PROP_DRAG_ACCEPT, PROP_ITEM_UI, N_PROPS }; @@ -85,6 +87,12 @@ nautilus_view_item_get_property (GObject *object, } break; + case PROP_DRAG_ACCEPT: + { + g_value_set_boolean (value, self->drag_accept); + } + break; + case PROP_ITEM_UI: { g_value_set_object (value, self->item_ui); @@ -126,6 +134,12 @@ nautilus_view_item_set_property (GObject *object, } break; + case PROP_DRAG_ACCEPT: + { + self->drag_accept = g_value_get_boolean (value); + } + break; + case PROP_ITEM_UI: { g_set_object (&self->item_ui, g_value_get_object (value)); @@ -164,6 +178,10 @@ nautilus_view_item_class_init (NautilusViewItemClass *klass) "", "", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + properties[PROP_DRAG_ACCEPT] = g_param_spec_boolean ("drag-accept", + "", "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_FILE] = g_param_spec_object ("file", "", "", NAUTILUS_TYPE_FILE, @@ -219,6 +237,15 @@ nautilus_view_item_set_cut (NautilusViewItem *self, g_object_set (self, "is-cut", is_cut, NULL); } +void +nautilus_view_item_set_drag_accept (NautilusViewItem *self, + gboolean drag_accept) +{ + g_return_if_fail (NAUTILUS_IS_VIEW_ITEM (self)); + + g_object_set (self, "drag-accept", drag_accept, NULL); +} + NautilusFile * nautilus_view_item_get_file (NautilusViewItem *self) { diff --git a/src/nautilus-view-item.h b/src/nautilus-view-item.h index 5e620520aa..9bdaff158b 100644 --- a/src/nautilus-view-item.h +++ b/src/nautilus-view-item.h @@ -26,6 +26,8 @@ void nautilus_view_item_set_icon_size (NautilusViewItem *self, guint nautilus_view_item_get_icon_size (NautilusViewItem *self); void nautilus_view_item_set_cut (NautilusViewItem *self, gboolean is_cut); +void nautilus_view_item_set_drag_accept (NautilusViewItem *self, + gboolean drag_accept); NautilusFile * nautilus_view_item_get_file (NautilusViewItem *self); -- GitLab From 3bddcf5192ff2e26158f14a501565eb639210a8d Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Thu, 16 Jun 2022 08:36:25 -0700 Subject: [PATCH 07/12] list-base: Reimplement drop targets in view Fixing a major regression of the GTK4 porting. --- src/nautilus-list-base.c | 231 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index e400196295..ebb8a59479 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -12,7 +12,9 @@ #include "nautilus-view-item.h" #include "nautilus-view-model.h" #include "nautilus-files-view.h" +#include "nautilus-files-view-dnd.h" #include "nautilus-file.h" +#include "nautilus-file-operations.h" #include "nautilus-metadata.h" #include "nautilus-global-preferences.h" #include "nautilus-thumbnails.h" @@ -44,6 +46,9 @@ struct _NautilusListBasePrivate gboolean single_click_mode; gboolean activate_on_release; gboolean deny_background_click; + + GdkDragAction drag_item_action; + GdkDragAction drag_view_action; }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusListBase, nautilus_list_base, NAUTILUS_TYPE_FILES_VIEW) @@ -529,11 +534,210 @@ on_item_drag_prepare (GtkDragSource *source, return gdk_content_provider_new_typed (GDK_TYPE_FILE_LIST, file_list); } +static void +real_perform_drop (NautilusListBase *self, + const GValue *value, + GdkDragAction action, + GFile *target_location) +{ + if (!gdk_drag_action_is_unique (action)) + { + /* TODO: Implement */ + } + else if (G_VALUE_HOLDS (value, G_TYPE_STRING)) + { + /* TODO: Implement */ + } + else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) + { + GSList *source_file_list = g_value_get_boxed (value); + GList *source_uri_list = NULL; + g_autofree gchar *target_uri = NULL; + + for (GSList *l = source_file_list; l != NULL; l = l->next) + { + source_uri_list = g_list_prepend (source_uri_list, g_file_get_uri (l->data)); + } + source_uri_list = g_list_reverse (source_uri_list); + + target_uri = g_file_get_uri (target_location); + nautilus_files_view_drop_proxy_received_uris (NAUTILUS_FILES_VIEW (self), + source_uri_list, + target_uri, + action); + g_list_free_full (source_uri_list, g_free); + } +} + +static GdkDragAction +on_item_drag_enter (GtkDropTarget *target, + double x, + double y, + gpointer user_data) +{ + NautilusViewCell *cell = user_data; + NautilusListBase *self = nautilus_view_cell_get_view (cell); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + NautilusViewItem *item; + const GValue *value; + g_autoptr (NautilusFile) dest_file = NULL; + + /* Reset action cache. */ + priv->drag_item_action = 0; + + item = nautilus_view_cell_get_item (cell); + if (item == NULL) + { + gtk_drop_target_reject (target); + return 0; + } + + dest_file = nautilus_file_ref (nautilus_view_item_get_file (item)); + + if (!nautilus_file_is_archive (dest_file) && !nautilus_file_is_directory (dest_file)) + { + gtk_drop_target_reject (target); + return 0; + } + + value = gtk_drop_target_get_value (target); + if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) + { + GSList *source_file_list; + + source_file_list = g_value_get_boxed (value); + priv->drag_item_action = nautilus_dnd_get_prefered_action (dest_file, + source_file_list->data); + } + + if (priv->drag_item_action == 0) + { + gtk_drop_target_reject (target); + return 0; + } + + nautilus_view_item_set_drag_accept (item, TRUE); + return priv->drag_item_action; +} + +static GdkDragAction +on_item_drag_motion (GtkDropTarget *target, + double x, + double y, + gpointer user_data) +{ + NautilusViewCell *cell = user_data; + NautilusListBase *self = nautilus_view_cell_get_view (cell); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + /* There's a bug in GtkDropTarget where motion overrides enter + * so until we fix that let's just return the action that we already + * received from enter*/ + + return priv->drag_item_action; +} + +static void +on_item_drag_leave (GtkDropTarget *dest, + gpointer user_data) +{ + NautilusViewCell *cell = user_data; + NautilusViewItem *item = nautilus_view_cell_get_item (cell); + + nautilus_view_item_set_drag_accept (item, FALSE); +} + +static gboolean +on_item_drop (GtkDropTarget *target, + const GValue *value, + double x, + double y, + gpointer user_data) +{ + NautilusViewCell *cell = user_data; + NautilusListBase *self = nautilus_view_cell_get_view (cell); + NautilusViewItem *item = nautilus_view_cell_get_item (cell); + GdkDragAction actions; + GFile *target_location; + + actions = gdk_drop_get_actions (gtk_drop_target_get_current_drop (target)); + target_location = nautilus_file_get_location (nautilus_view_item_get_file (item)); + + real_perform_drop (self, value, actions, target_location); + + return TRUE; +} + +static GdkDragAction +on_view_drag_enter (GtkDropTarget *target, + double x, + double y, + gpointer user_data) +{ + NautilusListBase *self = user_data; + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + g_autoptr (NautilusFile) dest_file = NULL; + const GValue *value; + GList *source_file_list; + + /* Reset action cache */ + priv->drag_view_action = 0; + + value = gtk_drop_target_get_value (target); + if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) + { + source_file_list = g_value_get_boxed (value); + dest_file = nautilus_file_get (nautilus_view_get_location (NAUTILUS_VIEW (self))); + priv->drag_view_action = nautilus_dnd_get_prefered_action (dest_file, + source_file_list->data); + } + + if (priv->drag_view_action == 0) + { + gtk_drop_target_reject (target); + return 0; + } + + return priv->drag_view_action; +} + +static GdkDragAction +on_view_drag_motion (GtkDropTarget *target, + double x, + double y, + gpointer user_data) +{ + NautilusListBase *self = user_data; + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + return priv->drag_view_action; +} + +static gboolean +on_view_drop (GtkDropTarget *target, + const GValue *value, + double x, + double y, + gpointer user_data) +{ + NautilusListBase *self = user_data; + GdkDragAction actions; + GFile *target_location; + + actions = gdk_drop_get_actions (gtk_drop_target_get_current_drop (target)); + target_location = nautilus_view_get_location (NAUTILUS_VIEW (self)); + + real_perform_drop (self, value, actions, target_location); + + return TRUE; +} + void setup_cell_common (GtkListItem *listitem, NautilusViewCell *cell) { GtkEventController *controller; + GtkDropTarget *drop_target; g_object_bind_property (listitem, "item", cell, "item", @@ -558,6 +762,17 @@ setup_cell_common (GtkListItem *listitem, gtk_widget_add_controller (GTK_WIDGET (cell), controller); gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); g_signal_connect (controller, "prepare", G_CALLBACK (on_item_drag_prepare), cell); + + /* TODO: Implement GDK_ACTION_ASK */ + drop_target = gtk_drop_target_new (G_TYPE_INVALID, GDK_ACTION_COPY | GDK_ACTION_MOVE); + gtk_drop_target_set_preload (drop_target, TRUE); + /* TODO: Implement GDK_TYPE_STRING */ + gtk_drop_target_set_gtypes (drop_target, (GType[1]) { GDK_TYPE_FILE_LIST }, 1); + g_signal_connect (drop_target, "enter", G_CALLBACK (on_item_drag_enter), cell); + g_signal_connect (drop_target, "leave", G_CALLBACK (on_item_drag_leave), cell); + g_signal_connect (drop_target, "motion", G_CALLBACK (on_item_drag_motion), cell); + g_signal_connect (drop_target, "drop", G_CALLBACK (on_item_drop), cell); + gtk_widget_add_controller (GTK_WIDGET (cell), GTK_EVENT_CONTROLLER (drop_target)); } static void @@ -610,6 +825,11 @@ real_begin_loading (NautilusFilesView *files_view) /* When double clicking on an item this deny_background_click can persist * because the new view interrupts the gesture sequence, so lets reset it.*/ priv->deny_background_click = FALSE; + + /* When DnD is used to navigate between directories, the normal callbacks + * are ignored. Update DnD variables here upon navigating to a directory*/ + priv->drag_view_action = priv->drag_item_action; + priv->drag_item_action = 0; } static void @@ -1376,6 +1596,7 @@ nautilus_list_base_setup_gestures (NautilusListBase *self) { GtkWidget *view_ui = nautilus_list_base_get_view_ui (self); GtkEventController *controller; + GtkDropTarget *drop_target; controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ()); gtk_widget_add_controller (view_ui, controller); @@ -1388,4 +1609,14 @@ nautilus_list_base_setup_gestures (NautilusListBase *self) gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE); g_signal_connect (controller, "pressed", G_CALLBACK (on_view_longpress_pressed), self); + + /* TODO: Implement GDK_ACTION_ASK */ + drop_target = gtk_drop_target_new (G_TYPE_INVALID, GDK_ACTION_COPY | GDK_ACTION_MOVE); + gtk_drop_target_set_preload (drop_target, TRUE); + /* TODO: Implement GDK_TYPE_STRING */ + gtk_drop_target_set_gtypes (drop_target, (GType[1]) { GDK_TYPE_FILE_LIST }, 1); + g_signal_connect (drop_target, "enter", G_CALLBACK (on_view_drag_enter), self); + g_signal_connect (drop_target, "motion", G_CALLBACK (on_view_drag_motion), self); + g_signal_connect (drop_target, "drop", G_CALLBACK (on_view_drop), self); + gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drop_target)); } -- GitLab From 6ee09df4f15b9399f0ee4c87b8cb1abccaaa29c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Thu, 30 Jun 2022 00:37:08 +0100 Subject: [PATCH 08/12] css: Disable undersired drop feedback on views While a view is accepting a drop hovering it, there is a blue ring, as provided by libadwaita's stylesheet with a wildcard selector. But this ring e partially hidden by the view's children and also clipped by the window rounded corner. There is a similar problem in list view cells, which get drop feedback blue ring on each cell, instead of the whole row as we would prefer. In all these cases, disable the blue ring with a specific selector. --- src/resources/css/Adwaita.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/resources/css/Adwaita.css b/src/resources/css/Adwaita.css index 47110f38e2..ee95de54fe 100644 --- a/src/resources/css/Adwaita.css +++ b/src/resources/css/Adwaita.css @@ -204,6 +204,11 @@ padding: 6px; } +/* We want drop feedback on the whole row. Disable per-cell feedback */ +.nautilus-list-view #NautilusViewCell:drop(active) { + box-shadow: none; +} + .nautilus-list-view.compact #NautilusViewCell { padding-top: 3px; padding-bottom: 3px; @@ -214,6 +219,11 @@ } /* Both views */ +.nautilus-list-view:drop(active), +.nautilus-grid-view:drop(active) { + box-shadow: none; +} + .view .thumbnail { background: url('/org/gnome/nautilus/Checkerboard.png') repeat; box-shadow: 0px 1px 2px 0px @shade_color, -- GitLab From 2c98abb55bae07d9617dba06e7eeef323a58cce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Thu, 30 Jun 2022 00:43:45 +0100 Subject: [PATCH 09/12] grid-view: Improve drop feedback on grid view The view cell widget doesn't fill the whole parent area, because there is a padding. Disable the parent's padding, add the padding back into the cell itself, and set matching rounded corners. --- src/resources/css/Adwaita.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/resources/css/Adwaita.css b/src/resources/css/Adwaita.css index ee95de54fe..af7e33ad77 100644 --- a/src/resources/css/Adwaita.css +++ b/src/resources/css/Adwaita.css @@ -155,7 +155,11 @@ } .nautilus-grid-view gridview > child { + padding: 0px; +} +.nautilus-grid-view #NautilusViewCell { padding: 6px; + border-radius: 6px; } /* Column view */ -- GitLab From 964192f0244fd296b2ae73ddf273263e1460a108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Thu, 30 Jun 2022 16:52:36 +0100 Subject: [PATCH 10/12] name-cell: Improve drop feedback on list view Since the GtkDropTarget is set on each cell, the default active state (blue rectangle) is set only around a portion of the row in list view. We disabled the GTK_STATE_FLAG_DROP_ACTIVE for view-cell anyways, but we should have a better indication of drop state in list view since the icons are small. Heavily based on a different approach by Corey Berla --- src/nautilus-name-cell.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/nautilus-name-cell.c b/src/nautilus-name-cell.c index 9a269ac68a..55574cb0a7 100644 --- a/src/nautilus-name-cell.c +++ b/src/nautilus-name-cell.c @@ -208,6 +208,21 @@ on_item_size_changed (NautilusNameCell *self) static void on_item_drag_accept_changed (NautilusNameCell *self) { + gboolean drag_accept; + NautilusViewItem *item; + GtkWidget *list_row = gtk_widget_get_parent (gtk_widget_get_parent (GTK_WIDGET (self))); + + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + g_object_get (item, "drag-accept", &drag_accept, NULL); + if (drag_accept) + { + gtk_widget_set_state_flags (list_row, GTK_STATE_FLAG_DROP_ACTIVE, FALSE); + } + else + { + gtk_widget_unset_state_flags (list_row, GTK_STATE_FLAG_DROP_ACTIVE); + } + update_icon (self); } -- GitLab From 3457f7fe5002481cca5102f85bd3a6eec7e1e94a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Wed, 29 Jun 2022 21:34:51 +0100 Subject: [PATCH 11/12] list-base: Open directories on hover during DnD Restore behavior from before GTK4 switch. --- src/nautilus-list-base.c | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index ebb8a59479..ce4f276412 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -49,6 +49,7 @@ struct _NautilusListBasePrivate GdkDragAction drag_item_action; GdkDragAction drag_view_action; + guint hover_timer_id; }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusListBase, nautilus_list_base, NAUTILUS_TYPE_FILES_VIEW) @@ -534,6 +535,50 @@ on_item_drag_prepare (GtkDragSource *source, return gdk_content_provider_new_typed (GDK_TYPE_FILE_LIST, file_list); } +static gboolean +hover_timer (gpointer user_data) +{ + NautilusViewCell *cell = user_data; + NautilusListBase *self = nautilus_view_cell_get_view (cell); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + NautilusViewItem *item = nautilus_view_cell_get_item (cell); + g_autofree gchar *uri = NULL; + + priv->hover_timer_id = 0; + + uri = nautilus_file_get_uri (nautilus_view_item_get_file (item)); + nautilus_files_view_handle_hover (NAUTILUS_FILES_VIEW (self), uri); + + return G_SOURCE_REMOVE; +} + +static void +on_item_drag_hover_enter (GtkDropControllerMotion *controller, + gdouble x, + gdouble y, + gpointer user_data) +{ + NautilusViewCell *cell = user_data; + NautilusListBase *self = nautilus_view_cell_get_view (cell); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + if (priv->hover_timer_id == 0) + { + priv->hover_timer_id = g_timeout_add (HOVER_TIMEOUT, hover_timer, cell); + } +} + +static void +on_item_drag_hover_leave (GtkDropControllerMotion *controller, + gpointer user_data) +{ + NautilusViewCell *cell = user_data; + NautilusListBase *self = nautilus_view_cell_get_view (cell); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + g_clear_handle_id (&priv->hover_timer_id, g_source_remove); +} + static void real_perform_drop (NautilusListBase *self, const GValue *value, @@ -773,6 +818,11 @@ setup_cell_common (GtkListItem *listitem, g_signal_connect (drop_target, "motion", G_CALLBACK (on_item_drag_motion), cell); g_signal_connect (drop_target, "drop", G_CALLBACK (on_item_drop), cell); gtk_widget_add_controller (GTK_WIDGET (cell), GTK_EVENT_CONTROLLER (drop_target)); + + controller = gtk_drop_controller_motion_new (); + gtk_widget_add_controller (GTK_WIDGET (cell), controller); + g_signal_connect (controller, "enter", G_CALLBACK (on_item_drag_hover_enter), cell); + g_signal_connect (controller, "leave", G_CALLBACK (on_item_drag_hover_leave), cell); } static void @@ -1406,6 +1456,7 @@ nautilus_list_base_dispose (GObject *object) g_clear_handle_id (&priv->scroll_to_file_handle_id, g_source_remove); g_clear_handle_id (&priv->prioritize_thumbnailing_handle_id, g_source_remove); + g_clear_handle_id (&priv->hover_timer_id, g_source_remove); G_OBJECT_CLASS (nautilus_list_base_parent_class)->dispose (object); } -- GitLab From c21904c53fc7ce53d3ecc3b9cb6a500f506ca6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Thu, 30 Jun 2022 18:06:09 +0100 Subject: [PATCH 12/12] list-base: Support non-local drops We reject drops on GtkDropTarget::enter if no drop action is supported. But this assumes the drop value is already available, which holds only for local (in-process) dnd opertaions. When dragging from other apps, we need to preload the value first, which may happen at any point in the future after ::enter. So, as per GTK documentation's advice, listen to changes to the GtkDropTarget:value property and update the cached preferred action when a new non-NULL value is loaded. --- src/nautilus-list-base.c | 92 ++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 22 deletions(-) diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index ce4f276412..c6809c6a8a 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -579,6 +579,25 @@ on_item_drag_hover_leave (GtkDropControllerMotion *controller, g_clear_handle_id (&priv->hover_timer_id, g_source_remove); } +static GdkDragAction +get_preferred_action (NautilusFile *target_file, + const GValue *value) +{ + GdkDragAction action = 0; + + if (value == NULL) + { + action = nautilus_dnd_get_prefered_action (target_file, NULL); + } + if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) + { + GSList *source_file_list = g_value_get_boxed (value); + action = nautilus_dnd_get_prefered_action (target_file, source_file_list->data); + } + + return action; +} + static void real_perform_drop (NautilusListBase *self, const GValue *value, @@ -646,15 +665,7 @@ on_item_drag_enter (GtkDropTarget *target, } value = gtk_drop_target_get_value (target); - if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) - { - GSList *source_file_list; - - source_file_list = g_value_get_boxed (value); - priv->drag_item_action = nautilus_dnd_get_prefered_action (dest_file, - source_file_list->data); - } - + priv->drag_item_action = get_preferred_action (dest_file, value); if (priv->drag_item_action == 0) { gtk_drop_target_reject (target); @@ -665,6 +676,30 @@ on_item_drag_enter (GtkDropTarget *target, return priv->drag_item_action; } +static void +on_item_drag_value_notify (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + GtkDropTarget *target = GTK_DROP_TARGET (object); + NautilusViewCell *cell = user_data; + NautilusListBase *self = nautilus_view_cell_get_view (cell); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + const GValue *value; + NautilusViewItem *item; + + value = gtk_drop_target_get_value (target); + if (value == NULL) + { + return; + } + + item = nautilus_view_cell_get_item (cell); + g_return_if_fail (NAUTILUS_IS_VIEW_ITEM (item)); + + priv->drag_item_action = get_preferred_action (nautilus_view_item_get_file (item), value); +} + static GdkDragAction on_item_drag_motion (GtkDropTarget *target, double x, @@ -721,22 +756,12 @@ on_view_drag_enter (GtkDropTarget *target, { NautilusListBase *self = user_data; NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - g_autoptr (NautilusFile) dest_file = NULL; + NautilusFile *dest_file; const GValue *value; - GList *source_file_list; - - /* Reset action cache */ - priv->drag_view_action = 0; + dest_file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)); value = gtk_drop_target_get_value (target); - if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) - { - source_file_list = g_value_get_boxed (value); - dest_file = nautilus_file_get (nautilus_view_get_location (NAUTILUS_VIEW (self))); - priv->drag_view_action = nautilus_dnd_get_prefered_action (dest_file, - source_file_list->data); - } - + priv->drag_view_action = get_preferred_action (dest_file, value); if (priv->drag_view_action == 0) { gtk_drop_target_reject (target); @@ -746,6 +771,27 @@ on_view_drag_enter (GtkDropTarget *target, return priv->drag_view_action; } +static void +on_view_drag_value_notify (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + GtkDropTarget *target = GTK_DROP_TARGET (object); + NautilusListBase *self = user_data; + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + const GValue *value; + NautilusFile *dest_file; + + value = gtk_drop_target_get_value (target); + if (value == NULL) + { + return; + } + + dest_file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)); + priv->drag_item_action = get_preferred_action (dest_file, value); +} + static GdkDragAction on_view_drag_motion (GtkDropTarget *target, double x, @@ -814,6 +860,7 @@ setup_cell_common (GtkListItem *listitem, /* TODO: Implement GDK_TYPE_STRING */ gtk_drop_target_set_gtypes (drop_target, (GType[1]) { GDK_TYPE_FILE_LIST }, 1); g_signal_connect (drop_target, "enter", G_CALLBACK (on_item_drag_enter), cell); + g_signal_connect (drop_target, "notify::value", G_CALLBACK (on_item_drag_value_notify), cell); g_signal_connect (drop_target, "leave", G_CALLBACK (on_item_drag_leave), cell); g_signal_connect (drop_target, "motion", G_CALLBACK (on_item_drag_motion), cell); g_signal_connect (drop_target, "drop", G_CALLBACK (on_item_drop), cell); @@ -1667,6 +1714,7 @@ nautilus_list_base_setup_gestures (NautilusListBase *self) /* TODO: Implement GDK_TYPE_STRING */ gtk_drop_target_set_gtypes (drop_target, (GType[1]) { GDK_TYPE_FILE_LIST }, 1); g_signal_connect (drop_target, "enter", G_CALLBACK (on_view_drag_enter), self); + g_signal_connect (drop_target, "notify::value", G_CALLBACK (on_view_drag_value_notify), self); g_signal_connect (drop_target, "motion", G_CALLBACK (on_view_drag_motion), self); g_signal_connect (drop_target, "drop", G_CALLBACK (on_view_drop), self); gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drop_target)); -- GitLab