/*
* 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));
}