/* * nautilus-window-slot-dnd.c - Handle DnD for widgets acting as * NautilusWindowSlot proxies * * Copyright (C) 2000, 2001 Eazel, Inc. * Copyright (C) 2010, Red Hat, 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 */ #include #include "nautilus-application.h" #include "nautilus-files-view-dnd.h" #include "nautilus-window-slot-dnd.h" #ifdef GDK_WINDOWING_X11 #include #endif typedef struct { NautilusFile *target_file; NautilusWindowSlot *target_slot; GtkWidget *widget; graphene_point_t hover_start_point; guint switch_location_timer; } NautilusDragSlotProxyInfo; static void switch_location (NautilusDragSlotProxyInfo *drag_info) { GFile *location; GtkRoot *window; if (drag_info->target_file == NULL) { return; } window = gtk_widget_get_root (drag_info->widget); g_assert (NAUTILUS_IS_WINDOW (window)); location = nautilus_file_get_location (drag_info->target_file); nautilus_application_open_location_full (NAUTILUS_APPLICATION (g_application_get_default ()), location, NAUTILUS_OPEN_FLAG_DONT_MAKE_ACTIVE, NULL, NAUTILUS_WINDOW (window), NULL); g_object_unref (location); } static gboolean slot_proxy_switch_location_timer (gpointer user_data) { NautilusDragSlotProxyInfo *drag_info = user_data; drag_info->switch_location_timer = 0; switch_location (drag_info); return FALSE; } static void slot_proxy_check_switch_location_timer (NautilusDragSlotProxyInfo *drag_info) { if (drag_info->switch_location_timer) { return; } drag_info->switch_location_timer = g_timeout_add (HOVER_TIMEOUT, slot_proxy_switch_location_timer, drag_info); } static void slot_proxy_remove_switch_location_timer (NautilusDragSlotProxyInfo *drag_info) { if (drag_info->switch_location_timer != 0) { g_source_remove (drag_info->switch_location_timer); drag_info->switch_location_timer = 0; } } static GdkDragAction slot_proxy_drag_motion (GtkDropTarget *target, gdouble x, gdouble y, gpointer user_data) { NautilusDragSlotProxyInfo *drag_info; NautilusWindowSlot *target_slot; GtkRoot *window; GdkDragAction action; char *target_uri; GFile *location; const GValue *value; graphene_point_t start; drag_info = user_data; action = 0; value = gtk_drop_target_get_value (target); if (value == NULL) { return 0; } window = gtk_widget_get_root (drag_info->widget); g_assert (NAUTILUS_IS_WINDOW (window)); target_uri = NULL; if (drag_info->target_file != NULL) { target_uri = nautilus_file_get_uri (drag_info->target_file); } else { if (drag_info->target_slot != NULL) { target_slot = drag_info->target_slot; } else { target_slot = nautilus_window_get_active_slot (NAUTILUS_WINDOW (window)); } if (target_slot != NULL) { location = nautilus_window_slot_get_location (target_slot); target_uri = g_file_get_uri (location); } } if (target_uri != NULL) { NautilusFile *file; NautilusDirectory *directory; gboolean can; file = nautilus_file_get_existing_by_uri (target_uri); directory = nautilus_directory_get_for_file (file); can = nautilus_file_can_write (file) && nautilus_directory_is_editable (directory); nautilus_directory_unref (directory); g_object_unref (file); if (!can) { action = 0; goto out; } if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) { GSList *items = g_value_get_boxed (value); action = nautilus_dnd_get_preferred_action (file, items->data); } } g_free (target_uri); out: start = drag_info->hover_start_point; if (gtk_drag_check_threshold (drag_info->widget, start.x, start.y, x, y)) { slot_proxy_remove_switch_location_timer (drag_info); slot_proxy_check_switch_location_timer (drag_info); drag_info->hover_start_point.x = x; drag_info->hover_start_point.y = y; } return action; } static void drag_info_free (gpointer user_data) { NautilusDragSlotProxyInfo *drag_info = user_data; g_clear_object (&drag_info->target_file); g_clear_object (&drag_info->target_slot); g_slice_free (NautilusDragSlotProxyInfo, drag_info); } static void drag_info_clear (NautilusDragSlotProxyInfo *drag_info) { slot_proxy_remove_switch_location_timer (drag_info); } static void slot_proxy_drag_leave (GtkDropTarget *target, gpointer user_data) { NautilusDragSlotProxyInfo *drag_info; drag_info = user_data; drag_info_clear (drag_info); } static void 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 = NULL; GFile *location; NautilusDragSlotProxyInfo *drag_info; drag_info = user_data; window = gtk_widget_get_root (drag_info->widget); g_assert (NAUTILUS_IS_WINDOW (window)); if (drag_info->target_slot != NULL) { target_slot = drag_info->target_slot; } else { target_slot = nautilus_window_get_active_slot (NAUTILUS_WINDOW (window)); } target_uri = NULL; if (drag_info->target_file != NULL) { target_uri = nautilus_file_get_uri (drag_info->target_file); } else if (target_slot != NULL) { location = nautilus_window_slot_get_location (target_slot); target_uri = g_file_get_uri (location); } target_view = NULL; if (target_slot != NULL) { NautilusView *view; view = nautilus_window_slot_get_current_view (target_slot); if (view && NAUTILUS_IS_FILES_VIEW (view)) { target_view = NAUTILUS_FILES_VIEW (view); } } if (target_slot != NULL && target_view != NULL) { if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) { GSList *items = g_value_get_boxed (value); GdkDragAction actions; for (GSList *l = items; l != NULL; l = l->next) { uri_list = g_list_prepend (uri_list, g_file_get_uri (l->data)); } actions = gdk_drop_get_actions (gtk_drop_target_get_current_drop (target)); #ifdef GDK_WINDOWING_X11 if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) { /* Temporary workaround until the below GTK MR (or equivalend fix) * is merged. Without this fix, the preferred action isn't set correctly. * https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4982 */ GdkDrag *drag = gdk_drop_get_drag (gtk_drop_target_get_current_drop (target)); actions = gdk_drag_get_selected_action (drag); } #endif nautilus_files_view_drop_proxy_received_uris (target_view, uri_list, target_uri, actions); g_list_free_full (uri_list, g_free); } } g_free (target_uri); drag_info_clear (drag_info); } void nautilus_drag_slot_proxy_init (GtkWidget *widget, NautilusFile *target_file, NautilusWindowSlot *target_slot) { NautilusDragSlotProxyInfo *drag_info; GtkDropTarget *target; g_assert (GTK_IS_WIDGET (widget)); drag_info = g_slice_new0 (NautilusDragSlotProxyInfo); g_object_set_data_full (G_OBJECT (widget), "drag-slot-proxy-data", drag_info, drag_info_free); if (target_file != NULL) { drag_info->target_file = nautilus_file_ref (target_file); } if (target_slot != NULL) { drag_info->target_slot = g_object_ref (target_slot); } drag_info->widget = widget; /* TODO: Implement GDK_ACTION_ASK */ target = gtk_drop_target_new (G_TYPE_INVALID, GDK_ACTION_ALL); 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)); }