droptarget: Redo

This is a huge reorganization of GtkDropTarget. I did not know how to
split this up, so it's unfortunately all one commit.

Highlights:

- Split GtkDropTarget into GtkDropTarget and GtkDropTargetAsync
  GtkDropTarget is the simple one that only works with GTypes and offers
  a synchronous interface.
  GtkDropTargetAsync retains the full old functionality and allows
  handling mime types.

- Drop events are handled differently
  Instead of picking a single drop target and sending all DND events to
  it, every event is sent to every drop target. The first one to handle
  the event gets to call gdk_drop_status(), further handlers do not
  interact with the GdkDrop.
  Of course, for the ultimate GDK_DROP_STARTING event, only the first
  one to accept the drop gets to handle it.
  This allows stacking DND event controllers that aren't necessarily
  interested in handling the event or that might decide later to drop
  it.

- Port all widgets to either of those
  Both have a somewhat changed API due to the new event handling.
  For the ones who should use the sync version, lots of cleanup was
  involved to operate on a sync API.
This commit is contained in:
Benjamin Otte 2020-02-29 03:47:17 +01:00
parent f4ac74795c
commit a411959c91
35 changed files with 2753 additions and 2407 deletions

View File

@ -152,43 +152,17 @@ prepare_drag (GtkDragSource *source,
return gdk_content_provider_new_typed (GDK_TYPE_TEXTURE, paintable);
}
static void
got_texture (GObject *source,
GAsyncResult *result,
gpointer data)
{
GdkDrop *drop = GDK_DROP (source);
GtkWidget *image = data;
const GValue *value;
GError *error = NULL;
value = gdk_drop_read_value_finish (drop, result, &error);
if (value)
{
GdkTexture *texture = g_value_get_object (value);
gtk_image_set_from_paintable (GTK_IMAGE (image), GDK_PAINTABLE (texture));
}
else
{
g_print ("Failed to get data: %s\n", error->message);
g_error_free (error);
}
}
static gboolean
drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkWidget *widget)
const GValue *value,
double x,
double y,
GtkImage *image)
{
if (gdk_drop_has_value (drop, GDK_TYPE_TEXTURE))
{
gdk_drop_read_value_async (drop, GDK_TYPE_TEXTURE, G_PRIORITY_DEFAULT, NULL, got_texture, widget);
return TRUE;
}
GdkTexture *texture = g_value_get_object (value);
gtk_image_set_from_paintable (GTK_IMAGE (image), GDK_PAINTABLE (texture));
return FALSE;
return TRUE;
}
static void
@ -354,8 +328,8 @@ do_clipboard (GtkWidget *do_widget)
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
/* accept drops on image */
dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_TEXTURE), GDK_ACTION_COPY);
g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), image);
dest = gtk_drop_target_new (GDK_TYPE_TEXTURE, GDK_ACTION_COPY);
g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), image);
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest));
/* context menu on image */
@ -383,8 +357,8 @@ do_clipboard (GtkWidget *do_widget)
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
/* accept drops on image */
dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_TEXTURE), GDK_ACTION_COPY);
g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), image);
dest = gtk_drop_target_new (GDK_TYPE_TEXTURE, GDK_ACTION_COPY);
g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), image);
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest));
/* context menu on image */

View File

@ -6813,14 +6813,16 @@ gtk_drag_source_get_type
<FILE>gtkdroptarget</FILE>
GtkDropTarget
gtk_drop_target_new
gtk_drop_target_set_formats
gtk_drop_target_set_gtypes
gtk_drop_target_get_gtypes
gtk_drop_target_get_formats
gtk_drop_target_set_actions
gtk_drop_target_get_actions
gtk_drop_target_set_preload
gtk_drop_target_get_preload
gtk_drop_target_get_drop
gtk_drop_target_find_mimetype
gtk_drag_highlight
gtk_drag_unhighlight
gtk_drop_target_get_value
gtk_drop_target_reject
<SUBSECTION Standard>
GTK_TYPE_DROP_TARGET
@ -6833,6 +6835,27 @@ GTK_DROP_TARGET_GET_CLASS
gtk_drop_target_get_type
</SECTION>
<SECTION>
<FILE>gtkdroptargetasync</FILE>
GtkDropTargetAsync
gtk_drop_target_async_new
gtk_drop_target_async_set_formats
gtk_drop_target_async_get_formats
gtk_drop_target_async_set_actions
gtk_drop_target_async_get_actions
gtk_drop_target_async_reject_drop
<SUBSECTION Standard>
GTK_TYPE_DROP_TARGET_ASYNC
GTK_DROP_TARGET_ASYNC
GTK_DROP_TARGET_ASYNC_CLASS
GTK_IS_DROP_TARGET_ASYNC
GTK_IS_DROP_TARGET_ASYNC_CLASS
GTK_DROP_TARGET_ASYNC_GET_CLASS
<SUBSECTION Private>
gtk_drop_target_async_get_type
</SECTION>
<SECTION>
<FILE>gtkdropcontrollermotion</FILE>
<TITLE>GtkDropControllerMotion</TITLE>

View File

@ -90,11 +90,12 @@
#include <gtk/gtkcustomlayout.h>
#include <gtk/gtkdebug.h>
#include <gtk/gtkdialog.h>
#include <gtk/gtkdragdest.h>
#include <gtk/gtkdragicon.h>
#include <gtk/gtkdragsource.h>
#include <gtk/gtkdrawingarea.h>
#include <gtk/gtkdropcontrollermotion.h>
#include <gtk/gtkdroptarget.h>
#include <gtk/gtkdroptargetasync.h>
#include <gtk/gtkeditable.h>
#include <gtk/gtkemojichooser.h>
#include <gtk/gtkentry.h>

View File

@ -95,7 +95,7 @@
#endif
#include "gtkcalendar.h"
#include "gtkdragdest.h"
#include "gtkdroptarget.h"
#include "gtkintl.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
@ -285,17 +285,6 @@ static void gtk_calendar_focus_controller_focus (GtkEventController *
GtkWidget *widget);
static void gtk_calendar_state_flags_changed (GtkWidget *widget,
GtkStateFlags previous_state);
static gboolean gtk_calendar_drag_accept (GtkDropTarget *dest,
GdkDrop *drop,
GtkCalendar *calendar);
static void gtk_calendar_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkCalendar *calendar);
static gboolean gtk_calendar_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkCalendar *calendar);
static void calendar_invalidate_day (GtkCalendar *widget,
@ -320,6 +309,57 @@ static char *default_monthname[12];
G_DEFINE_TYPE_WITH_PRIVATE (GtkCalendar, gtk_calendar, GTK_TYPE_WIDGET)
static void
gtk_calendar_drag_notify_value (GtkDropTarget *target,
GParamSpec **pspec,
GtkCalendar *calendar)
{
GDate *date;
const GValue *value;
value = gtk_drop_target_get_value (target);
if (value == NULL)
return;
date = g_date_new ();
g_date_set_parse (date, g_value_get_string (value));
if (!g_date_valid (date))
gtk_drop_target_reject (target);
g_date_free (date);
}
static gboolean
gtk_calendar_drag_drop (GtkDropTarget *dest,
const GValue *value,
double x,
double y,
GtkCalendar *calendar)
{
GDate *date;
GDateTime *datetime;
date = g_date_new ();
g_date_set_parse (date, g_value_get_string (value));
if (!g_date_valid (date))
{
g_warning ("Received invalid date data");
g_date_free (date);
return FALSE;
}
datetime = g_date_time_new_local (g_date_get_year (date),
g_date_get_month (date),
g_date_get_day (date),
0, 0, 0);
g_date_free (date);
gtk_calendar_select_day (calendar, datetime);
g_date_time_unref (datetime);
return TRUE;
}
static void
gtk_calendar_dispose (GObject *object)
{
@ -518,6 +558,7 @@ gtk_calendar_init (GtkCalendar *calendar)
GtkWidget *widget = GTK_WIDGET (calendar);
GtkEventController *controller;
GtkGesture *gesture;
GtkDropTarget *target;
gint i;
#ifdef G_OS_WIN32
wchar_t wbuffer[100];
@ -535,7 +576,6 @@ gtk_calendar_init (GtkCalendar *calendar)
#else
gchar *week_start;
#endif
GtkDropTarget *dest;
int min_year_width;
GDateTime *now;
@ -718,14 +758,11 @@ gtk_calendar_init (GtkCalendar *calendar)
priv->in_drag = 0;
dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (G_TYPE_STRING),
GDK_ACTION_COPY);
g_signal_connect (dest, "accept", G_CALLBACK (gtk_calendar_drag_accept), calendar);
g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_calendar_drag_leave), calendar);
g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_calendar_drag_drop), calendar);
gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (dest));
target = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY);
gtk_drop_target_set_preload (target, TRUE);
g_signal_connect (target, "notify::value", G_CALLBACK (gtk_calendar_drag_notify_value), calendar);
g_signal_connect (target, "drop", G_CALLBACK (gtk_calendar_drag_drop), calendar);
gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (target));
priv->year_before = 0;
@ -1417,139 +1454,6 @@ gtk_calendar_state_flags_changed (GtkWidget *widget,
}
}
/* Get/set whether drag_motion requested the drag data and
* drag_data_received should thus not actually insert the data,
* since the data doesnt result from a drop.
*/
static void
set_status_pending (GdkDrop *drop,
GdkDragAction suggested_action)
{
g_object_set_data (G_OBJECT (drop),
I_("gtk-calendar-status-pending"),
GINT_TO_POINTER (suggested_action));
}
static GdkDragAction
get_status_pending (GdkDrop *drop)
{
return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drop),
"gtk-calendar-status-pending"));
}
static void
gtk_calendar_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkCalendar *calendar)
{
}
static void
got_text (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkDropTarget *dest = GTK_DROP_TARGET (data);
GtkCalendar *calendar = GTK_CALENDAR (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)));
GdkDrop *drop = GDK_DROP (source);
gchar *str;
GDate *date;
GDateTime *datetime;
GdkDragAction suggested_action;
suggested_action = get_status_pending (drop);
set_status_pending (drop, 0);
str = gdk_drop_read_text_finish (drop, result, NULL);
if (suggested_action)
{
if (str)
{
date = g_date_new ();
g_date_set_parse (date, str);
if (!g_date_valid (date))
suggested_action = 0;
g_date_free (date);
g_free (str);
}
else
suggested_action = 0;
gdk_drop_status (drop, suggested_action);
if (suggested_action == 0)
gtk_drop_target_deny_drop (dest, drop);
return;
}
date = g_date_new ();
if (str)
{
g_date_set_parse (date, str);
g_free (str);
}
if (!g_date_valid (date))
{
g_warning ("Received invalid date data");
g_date_free (date);
gdk_drop_finish (drop, 0);
gtk_drop_target_deny_drop (dest, drop);
return;
}
datetime = g_date_time_new_local (g_date_get_year (date),
g_date_get_month (date),
g_date_get_day (date),
0, 0, 0);
g_date_free (date);
gdk_drop_finish (drop, suggested_action);
gtk_calendar_select_day (calendar, datetime);
g_date_time_unref (datetime);
}
static gboolean
gtk_calendar_drag_accept (GtkDropTarget *dest,
GdkDrop *drop,
GtkCalendar *calendar)
{
const char *target;
target = gtk_drop_target_find_mimetype (dest);
if (!target || gdk_drop_get_actions (drop) == 0)
{
gdk_drop_status (drop, 0);
return FALSE;
}
else if (get_status_pending (drop) == 0)
{
set_status_pending (drop, gdk_drop_get_actions (drop));
gdk_drop_read_text_async (drop, NULL, got_text, dest);
}
return TRUE;
}
static gboolean
gtk_calendar_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkCalendar *calendar)
{
const char *target;
target = gtk_drop_target_find_mimetype (dest);
if (target != NULL)
{
set_status_pending (drop, 0);
gdk_drop_read_text_async (drop, NULL, got_text, dest);
return TRUE;
}
return FALSE;
}
/****************************************
* Public API *

View File

@ -36,17 +36,14 @@
#include "gtkcolorchooserprivate.h"
#include "gtkcolorchooserdialog.h"
#include "gtkcolorswatchprivate.h"
#include "gtkdragdest.h"
#include "gtkdragsource.h"
#include "gtkdroptarget.h"
#include "gtkintl.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkprivate.h"
#include "gtksnapshot.h"
#include "gtkstylecontext.h"
#include "gtkdragsource.h"
#include "gtkdragdest.h"
#include "gtkeventcontroller.h"
/**
@ -230,33 +227,16 @@ gtk_color_button_class_init (GtkColorButtonClass *klass)
gtk_widget_class_set_css_name (widget_class, "colorbutton");
}
static void
got_color (GObject *source,
GAsyncResult *result,
gpointer data)
{
GdkDrop *drop = GDK_DROP (source);
const GValue *value;
value = gdk_drop_read_value_finish (drop, result, NULL);
if (value)
{
GdkRGBA *color = g_value_get_boxed (value);
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (data), color);
gdk_drop_finish (drop, GDK_ACTION_COPY);
}
else
gdk_drop_finish (drop, 0);
}
static gboolean
gtk_color_button_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkColorButton *button)
gtk_color_button_drop (GtkDropTarget *dest,
const GValue *value,
double x,
double y,
GtkColorButton *button)
{
gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, button);
GdkRGBA *color = g_value_get_boxed (value);
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (button), color);
return TRUE;
}
@ -320,9 +300,8 @@ gtk_color_button_init (GtkColorButton *button)
priv->rgba.alpha = 1;
priv->use_alpha = FALSE;
dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_RGBA),
GDK_ACTION_COPY);
g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_color_button_drag_drop), button);
dest = gtk_drop_target_new (GDK_TYPE_RGBA, GDK_ACTION_COPY);
g_signal_connect (dest, "drop", G_CALLBACK (gtk_color_button_drop), button);
gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (dest));
source = gtk_drag_source_new ();

View File

@ -22,8 +22,8 @@
#include "gtkbox.h"
#include "gtkcolorchooserprivate.h"
#include "gtkcssnodeprivate.h"
#include "gtkdragdest.h"
#include "gtkdragsource.h"
#include "gtkdroptarget.h"
#include "gtkgesturelongpress.h"
#include "gtkgestureclick.h"
#include "gtkgesturesingle.h"
@ -133,33 +133,16 @@ gtk_color_swatch_drag_begin (GtkDragSource *source,
g_object_unref (paintable);
}
static void
got_color (GObject *source,
GAsyncResult *result,
gpointer data)
{
GdkDrop *drop = GDK_DROP (source);
const GValue *value;
value = gdk_drop_read_value_finish (drop, result, NULL);
if (value)
{
GdkRGBA *color = g_value_get_boxed (value);
gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (data), color);
gdk_drop_finish (drop, GDK_ACTION_COPY);
}
else
gdk_drop_finish (drop, 0);
}
static gboolean
swatch_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
const GValue *value,
double x,
double y,
GtkColorSwatch *swatch)
{
gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, swatch);
gtk_color_swatch_set_rgba (swatch, g_value_get_boxed (value));
return TRUE;
}
@ -670,9 +653,8 @@ gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
if (can_drop && !priv->dest)
{
priv->dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_RGBA),
GDK_ACTION_COPY);
g_signal_connect (priv->dest, "drag-drop", G_CALLBACK (swatch_drag_drop), swatch);
priv->dest = gtk_drop_target_new (GDK_TYPE_RGBA, GDK_ACTION_COPY);
g_signal_connect (priv->dest, "drop", G_CALLBACK (swatch_drag_drop), swatch);
gtk_widget_add_controller (GTK_WIDGET (swatch), GTK_EVENT_CONTROLLER (priv->dest));
}
if (!can_drop && priv->dest)

View File

@ -1,871 +0,0 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
#include "gtkdragdest.h"
#include "gtkdragdestprivate.h"
#include "gtkintl.h"
#include "gtknative.h"
#include "gtktypebuiltins.h"
#include "gtkeventcontrollerprivate.h"
#include "gtkmarshalers.h"
/**
* SECTION:gtkdroptarget
* @Short_description: Event controller to receive DND drops
* @Title: GtkDropTarget
*
* GtkDropTarget is an auxiliary object that is used to receive
* Drag-and-Drop operations.
*
* To use a GtkDropTarget to receive drops on a widget, you create
* a GtkDropTarget object, configure which data formats and actions
* you support, connect to its signals, and then attach
* it to the widget with gtk_widget_add_controller().
*
* During a drag operation, the first signal that a GtkDropTarget
* emits is #GtkDropTarget::accept, which is meant to determine
* whether the target is a possible drop site for the ongoing drag.
* The default handler for the ::accept signal accepts the drag
* if it finds a compatible data format an an action that is supported
* on both sides.
*
* If it is, and the widget becomes the current target, you will
* receive a #GtkDropTarget::drag-enter signal, followed by
* #GtkDropTarget::drag-motion signals as the pointer moves, and
* finally either a #GtkDropTarget::drag-leave signal when the pointer
* move off the widget, or a #GtkDropTarget::drag-drop signal when
* a drop happens.
*
* The ::drag-enter and ::drag-motion handler can call gdk_drop_status()
* to update the status of the ongoing operation. The ::drag-drop handler
* should initiate the data transfer and finish the operation by calling
* gdk_drop_finish().
*
* Between the ::drag-enter and ::drag-leave signals the widget is the
* current drop target, and will receive the %GTK_STATE_FLAG_DROP_ACTIVE
* state, which can be used to style the widget as a drop targett.
*/
struct _GtkDropTarget
{
GtkEventController parent_object;
GdkContentFormats *formats;
GdkDragAction actions;
GtkWidget *widget;
GdkDrop *drop;
gboolean contains;
gboolean contains_pending;
};
struct _GtkDropTargetClass
{
GtkEventControllerClass parent_class;
gboolean (*accept ) (GtkDropTarget *dest,
GdkDrop *drop);
};
enum {
PROP_FORMATS = 1,
PROP_ACTIONS,
PROP_CONTAINS,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
enum {
ACCEPT,
DRAG_ENTER,
DRAG_MOTION,
DRAG_LEAVE,
DRAG_DROP,
NUM_SIGNALS
};
static guint signals[NUM_SIGNALS];
static gboolean gtk_drop_target_accept (GtkDropTarget *dest,
GdkDrop *drop);
static gboolean gtk_drop_target_handle_event (GtkEventController *controller,
GdkEvent *event,
double x,
double y);
static gboolean gtk_drop_target_filter_event (GtkEventController *controller,
GdkEvent *event);
static gboolean gtk_drop_target_get_contains (GtkDropTarget *dest);
static void gtk_drop_target_set_contains (GtkDropTarget *dest,
gboolean contains);
typedef enum {
GTK_DROP_STATUS_NONE,
GTK_DROP_STATUS_ACCEPTED,
GTK_DROP_STATUS_DENIED
} GtkDropStatus;
static GtkDropStatus gtk_drop_target_get_drop_status (GtkDropTarget *dest,
GdkDrop *drop);
static void gtk_drop_target_set_drop_status (GtkDropTarget *dest,
GdkDrop *drop,
GtkDropStatus status);
G_DEFINE_TYPE (GtkDropTarget, gtk_drop_target, GTK_TYPE_EVENT_CONTROLLER);
static void
gtk_drop_target_init (GtkDropTarget *dest)
{
}
static void
gtk_drop_target_finalize (GObject *object)
{
GtkDropTarget *dest = GTK_DROP_TARGET (object);
g_clear_pointer (&dest->formats, gdk_content_formats_unref);
G_OBJECT_CLASS (gtk_drop_target_parent_class)->finalize (object);
}
static void
gtk_drop_target_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkDropTarget *dest = GTK_DROP_TARGET (object);
switch (prop_id)
{
case PROP_FORMATS:
gtk_drop_target_set_formats (dest, g_value_get_boxed (value));
break;
case PROP_ACTIONS:
gtk_drop_target_set_actions (dest, g_value_get_flags (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_drop_target_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkDropTarget *dest = GTK_DROP_TARGET (object);
switch (prop_id)
{
case PROP_FORMATS:
g_value_set_boxed (value, gtk_drop_target_get_formats (dest));
break;
case PROP_ACTIONS:
g_value_set_flags (value, gtk_drop_target_get_actions (dest));
break;
case PROP_CONTAINS:
g_value_set_boolean (value, gtk_drop_target_get_contains (dest));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_drop_target_class_init (GtkDropTargetClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class);
object_class->finalize = gtk_drop_target_finalize;
object_class->set_property = gtk_drop_target_set_property;
object_class->get_property = gtk_drop_target_get_property;
controller_class->handle_event = gtk_drop_target_handle_event;
controller_class->filter_event = gtk_drop_target_filter_event;
class->accept = gtk_drop_target_accept;
/**
* GtkDropTarget:formats:
*
* The #GdkContentFormats that determines the supported data formats
*/
properties[PROP_FORMATS] =
g_param_spec_boxed ("formats", P_("Formats"), P_("Formats"),
GDK_TYPE_CONTENT_FORMATS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDropTarget:actions:
*
* The #GdkDragActions that this drop target supports
*/
properties[PROP_ACTIONS] =
g_param_spec_flags ("actions", P_("Actions"), P_("Actions"),
GDK_TYPE_DRAG_ACTION, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDropTarget:contains:
*
* Whether the drop target is currently the targed of an ongoing drag operation,
* and highlighted.
*/
properties[PROP_CONTAINS] =
g_param_spec_boolean ("contains", P_("Contains an ongoing drag"), P_("Contains the current drag"),
FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
/**
* GtkDropTarget::drag-enter:
* @dest: the #GtkDropTarget
* @drop: the #GdkDrop
*
* The ::drag-enter signal is emitted on the drop site when the cursor
* enters the widget. It can be used to set up custom highlighting.
*/
signals[DRAG_ENTER] =
g_signal_new (I_("drag-enter"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE, 1,
GDK_TYPE_DROP);
/**
* GtkDropTarget::drag-leave:
* @dest: the #GtkDropTarget
* @drop: the #GdkDrop
*
* The ::drag-leave signal is emitted on the drop site when the cursor
* leaves the widget. Its main purpose it to undo things done in
* #GtkDropTarget::drag-enter.
*/
signals[DRAG_LEAVE] =
g_signal_new (I_("drag-leave"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE, 1,
GDK_TYPE_DROP);
/**
* GtkDropTarget::drag-motion:
* @dest: the #GtkDropTarget
* @drop: the #GdkDrop
* @x: the x coordinate of the current cursor position
* @y: the y coordinate of the current cursor position
*
* The ::drag motion signal is emitted while the pointer is moving
* over the drop target.
*/
signals[DRAG_MOTION] =
g_signal_new (I_("drag-motion"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE, 3,
GDK_TYPE_DROP, G_TYPE_INT, G_TYPE_INT);
/**
* GtkWidget::accept:
* @dest: the #GtkDropTarget
* @drop: the #GdkDrop
*
* The ::accept signal is emitted on the drop site when the user
* moves the cursor over the widget during a drag. The signal handler
* must determine whether the cursor position is in a drop zone or not.
* If it is not in a drop zone, it returns %FALSE and no further processing
* is necessary. Otherwise, the handler returns %TRUE. In this case, the
* handler is responsible for providing the necessary information for
* displaying feedback to the user, by calling gdk_drag_status().
*
* The default handler for this signal decides whether to accept the drop
* based on the type of the data.
*
* If the decision whether the drop will be accepted or rejected can't be
* made based solely the data format, handler may inspect the dragged data
* by calling one of the #GdkDrop read functions and return %TRUE to
* tentatively accept the drop. When the data arrives and is found to not be
* acceptable, a call to gtk_drop_target_deny_drop() should be made to reject
* the drop.
*
* Returns: whether the cursor position is in a drop zone
*/
signals[ACCEPT] =
g_signal_new (I_("accept"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetClass, accept),
g_signal_accumulator_first_wins, NULL,
NULL,
G_TYPE_BOOLEAN, 1,
GDK_TYPE_DROP);
/**
* GtkDropTarget::drag-drop:
* @dest: the #GtkDropTarget
* @drop: the #GdkDrop
* @x: the x coordinate of the current cursor position
* @y: the y coordinate of the current cursor position
*
* The ::drag-drop signal is emitted on the drop site when the user drops
* the data onto the widget. The signal handler must determine whether
* the cursor position is in a drop zone or not. If it is not in a drop
* zone, it returns %FALSE and no further processing is necessary.
*
* Otherwise, the handler returns %TRUE. In this case, the handler must
* ensure that gdk_drop_finish() is called to let the source know that
* the drop is done. The call to gtk_drag_finish() can be done either
* directly or after receiving the data.
*
* To receive the data, use one of the read functions provides by #GtkDrop
* and #GtkDragDest: gdk_drop_read_async(), gdk_drop_read_value_async(),
* gdk_drop_read_text_async().
*
* You can use gtk_drop_target_get_drop() to obtain the #GtkDrop object
* for the ongoing operation in your signal handler. If you call one of the
* read functions in your handler, GTK will ensure that the #GtkDrop object
* stays alive until the read is completed. If you delay obtaining the data
* (e.g. to handle %GDK_ACTION_ASK by showing a #GtkPopover), you need to
* hold a reference on the #GtkDrop.
*
* Returns: whether the cursor position is in a drop zone
*/
signals[DRAG_DROP] =
g_signal_new (I_("drag-drop"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_BOOLEAN, 3,
GDK_TYPE_DROP, G_TYPE_INT, G_TYPE_INT);
}
/**
* gtk_drop_target_new:
* @formats: (nullable) (transfer full): the supported data formats
* @actions: the supported actions
*
* Creates a new #GtkDropTarget object.
*
* Returns: the new #GtkDropTarget
*/
GtkDropTarget *
gtk_drop_target_new (GdkContentFormats *formats,
GdkDragAction actions)
{
GtkDropTarget *result;
result = g_object_new (GTK_TYPE_DROP_TARGET,
"formats", formats,
"actions", actions,
NULL);
g_clear_pointer (&formats, gdk_content_formats_unref);
return result;
}
/**
* gtk_drop_target_set_formats:
* @dest: a #GtkDropTarget
* @formats: (nullable): the supported data formats
*
* Sets the data formats that this drop target will accept.
*/
void
gtk_drop_target_set_formats (GtkDropTarget *dest,
GdkContentFormats *formats)
{
g_return_if_fail (GTK_IS_DROP_TARGET (dest));
if (dest->formats == formats)
return;
if (dest->formats)
gdk_content_formats_unref (dest->formats);
dest->formats = formats;
if (dest->formats)
gdk_content_formats_ref (dest->formats);
g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_FORMATS]);
}
/**
* gtk_drop_target_get_formats:
* @dest: a #GtkDropTarget
*
* Gets the data formats that this drop target accepts.
*
* If the result is %NULL, all formats are expected to be supported.
*
* Returns: (nullable): the supported data formats
*/
GdkContentFormats *
gtk_drop_target_get_formats (GtkDropTarget *dest)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), NULL);
return dest->formats;
}
/**
* gtk_drop_target_set_actions:
* @dest: a #GtkDropTarget
* @actions: the supported actions
*
* Sets the actions that this drop target supports.
*/
void
gtk_drop_target_set_actions (GtkDropTarget *dest,
GdkDragAction actions)
{
g_return_if_fail (GTK_IS_DROP_TARGET (dest));
if (dest->actions == actions)
return;
dest->actions = actions;
g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_ACTIONS]);
}
/**
* gtk_drop_target_get_actions:
* @dest: a #GtkDropTarget
*
* Gets the actions that this drop target supports.
*
* Returns: the actions that this drop target supports
*/
GdkDragAction
gtk_drop_target_get_actions (GtkDropTarget *dest)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), 0);
return dest->actions;
}
/**
* gtk_drop_target_get_drop:
* @dest: a #GtkDropTarget
*
* Returns the underlying #GtkDrop object for an ongoing drag.
*
* Returns: (nullable) (transfer none): the #GtkDrop of the current drag operation, or %NULL
*/
GdkDrop *
gtk_drop_target_get_drop (GtkDropTarget *dest)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), NULL);
return dest->drop;
}
static const char *
gtk_drop_target_match (GtkDropTarget *dest,
GdkDrop *drop)
{
GdkContentFormats *formats;
const char *match;
formats = gdk_content_formats_ref (dest->formats);
formats = gdk_content_formats_union_deserialize_mime_types (formats);
match = gdk_content_formats_match_mime_type (formats, gdk_drop_get_formats (drop));
gdk_content_formats_unref (formats);
return match;
}
/**
* gtk_drop_target_find_mimetype:
* @dest: a #GtkDropTarget
*
* Returns a mimetype that is supported both by @dest and the ongoing
* drag. For more detailed control, you can use gdk_drop_get_formats()
* to obtain the content formats that are supported by the source.
*
* Returns: (nullable): a matching mimetype for the ongoing drag, or %NULL
*/
const char *
gtk_drop_target_find_mimetype (GtkDropTarget *dest)
{
if (!dest->drop)
return NULL;
if (dest->formats == NULL)
return NULL;
return gtk_drop_target_match (dest, dest->drop);
}
static gboolean
gtk_drop_target_accept (GtkDropTarget *dest,
GdkDrop *drop)
{
if ((gdk_drop_get_actions (drop) & gtk_drop_target_get_actions (dest)) == 0)
return FALSE;
if (dest->formats == NULL)
return TRUE;
return gdk_content_formats_match (dest->formats, gdk_drop_get_formats (drop));
}
static void
set_drop (GtkDropTarget *dest,
GdkDrop *drop)
{
if (dest->drop == drop)
return;
if (dest->drop)
g_object_remove_weak_pointer (G_OBJECT (dest->drop), (gpointer *)&dest->drop);
dest->drop = drop;
if (dest->drop)
g_object_add_weak_pointer (G_OBJECT (dest->drop), (gpointer *)&dest->drop);
}
static void
gtk_drop_target_emit_drag_enter (GtkDropTarget *dest,
GdkDrop *drop)
{
set_drop (dest, drop);
g_signal_emit (dest, signals[DRAG_ENTER], 0, drop);
}
static void
gtk_drop_target_emit_drag_leave (GtkDropTarget *dest,
GdkDrop *drop)
{
set_drop (dest, drop);
g_signal_emit (dest, signals[DRAG_LEAVE], 0, drop);
set_drop (dest, NULL);
}
static gboolean
gtk_drop_target_emit_accept (GtkDropTarget *dest,
GdkDrop *drop)
{
gboolean result = FALSE;
set_drop (dest, drop);
g_signal_emit (dest, signals[ACCEPT], 0, drop, &result);
return result;
}
static void
gtk_drop_target_emit_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y)
{
set_drop (dest, drop);
g_signal_emit (dest, signals[DRAG_MOTION], 0, drop, x, y);
}
static gboolean
gtk_drop_target_emit_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y)
{
gboolean result = FALSE;
set_drop (dest, drop);
g_signal_emit (dest, signals[DRAG_DROP], 0, drop, x, y, &result);
return result;
}
static void
gtk_drop_target_set_contains (GtkDropTarget *dest,
gboolean contains)
{
if (dest->contains == contains)
return;
dest->contains = contains;
g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_CONTAINS]);
}
static gboolean
gtk_drop_target_get_contains (GtkDropTarget *dest)
{
return dest->contains;
}
static gboolean
gtk_drop_target_filter_event (GtkEventController *controller,
GdkEvent *event)
{
switch ((int)gdk_event_get_event_type (event))
{
case GDK_DRAG_ENTER:
case GDK_DRAG_LEAVE:
case GDK_DRAG_MOTION:
case GDK_DROP_START:
return GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_parent_class)->filter_event (controller, event);
default:;
}
return TRUE;
}
static void
clear_current_dest (gpointer data, GObject *former_object)
{
g_object_set_data (G_OBJECT (data), "current-dest", NULL);
}
static void
unset_current_dest (gpointer data)
{
GtkDropTarget *dest = data;
GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
gtk_drop_target_set_contains (dest, FALSE);
if (widget)
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
}
static GtkDropTarget *
gtk_drop_get_current_dest (GdkDrop *drop)
{
return GTK_DROP_TARGET (g_object_get_data (G_OBJECT (drop), "current-dest"));
}
static void
gtk_drop_set_current_dest (GdkDrop *drop,
GtkDropTarget *dest)
{
GtkDropTarget *old_dest;
GtkWidget *widget;
old_dest = g_object_get_data (G_OBJECT (drop), "current-dest");
if (old_dest == dest)
return;
if (old_dest)
{
gtk_drop_target_set_contains (old_dest, FALSE);
widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (old_dest));
if (widget)
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
gtk_drop_target_emit_drag_leave (old_dest, drop);
g_object_weak_unref (G_OBJECT (old_dest), clear_current_dest, drop);
}
g_object_set_data_full (G_OBJECT (drop), "current-dest", dest, unset_current_dest);
if (dest)
{
g_object_weak_ref (G_OBJECT (dest), clear_current_dest, drop);
gtk_drop_target_emit_drag_enter (dest, drop);
widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
if (widget)
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
gtk_drop_target_set_contains (dest, TRUE);
}
}
static gboolean
gtk_drop_target_handle_event (GtkEventController *controller,
GdkEvent *event,
double x,
double y)
{
GtkDropTarget *dest = GTK_DROP_TARGET (controller);
GdkDrop *drop;
GtkDropStatus status;
gboolean found = FALSE;
drop = gdk_drag_event_get_drop (event);
status = gtk_drop_target_get_drop_status (dest, drop);
if (status == GTK_DROP_STATUS_DENIED)
return FALSE;
switch ((int)gdk_event_get_event_type (event))
{
case GDK_DRAG_MOTION:
if (status != GTK_DROP_STATUS_ACCEPTED)
{
found = gtk_drop_target_emit_accept (dest, drop);
if (found)
gtk_drop_target_set_drop_status (dest, drop, GTK_DROP_STATUS_ACCEPTED);
}
else
found = TRUE;
if (found)
{
gdk_drop_status (drop, gtk_drop_target_get_actions (dest));
gtk_drop_set_current_dest (drop, dest);
gtk_drop_target_emit_drag_motion (dest, drop, x, y);
}
break;
case GDK_DROP_START:
found = gtk_drop_target_emit_drag_drop (dest, drop, x, y);
break;
default:
break;
}
return found;
}
/*
* This function is called if none of the event
* controllers has handled a drag event.
*/
void
gtk_drag_dest_handle_event (GtkWidget *toplevel,
GdkEvent *event)
{
GdkDrop *drop;
GdkEventType event_type;
g_return_if_fail (toplevel != NULL);
g_return_if_fail (event != NULL);
event_type = gdk_event_get_event_type (event);
drop = gdk_drag_event_get_drop (event);
switch ((guint) event_type)
{
case GDK_DRAG_LEAVE:
gtk_drop_set_current_dest (drop, NULL);
break;
case GDK_DRAG_ENTER:
case GDK_DRAG_MOTION:
case GDK_DROP_START:
gtk_drop_set_current_dest (drop, NULL);
gdk_drop_status (drop, 0);
break;
default:
g_assert_not_reached ();
}
}
static GtkDropStatus
gtk_drop_target_get_drop_status (GtkDropTarget *dest,
GdkDrop *drop)
{
GHashTable *denied;
denied = (GHashTable *)g_object_get_data (G_OBJECT (drop), "denied-drags");
if (denied)
return GPOINTER_TO_INT (g_hash_table_lookup (denied, dest));
return GTK_DROP_STATUS_NONE;
}
static void
gtk_drop_target_set_drop_status (GtkDropTarget *dest,
GdkDrop *drop,
GtkDropStatus status)
{
GHashTable *drags;
drags = (GHashTable *)g_object_get_data (G_OBJECT (drop), "denied-drags");
if (!drags)
{
drags = g_hash_table_new (NULL, NULL);
g_object_set_data_full (G_OBJECT (drop), "denied-drags", drags, (GDestroyNotify)g_hash_table_unref);
}
g_hash_table_insert (drags, dest, GINT_TO_POINTER (status));
if (dest == gtk_drop_get_current_dest (drop))
{
gdk_drop_status (drop, 0);
gtk_drop_set_current_dest (drop, NULL);
}
}
/**
* gtk_drop_target_deny_drop:
* @dest: a #GtkDropTarget
* @drop: the #GdkDrop of an ongoing drag operation
*
* Sets the @drop as not accepted on this drag site.
*
* This function should be used when delaying the decision
* on whether to accept a drag or not until after reading
* the data.
*/
void
gtk_drop_target_deny_drop (GtkDropTarget *dest,
GdkDrop *drop)
{
g_return_if_fail (GTK_IS_DROP_TARGET (dest));
g_return_if_fail (GDK_IS_DROP (drop));
gtk_drop_target_set_drop_status (dest, drop, GTK_DROP_STATUS_DENIED);
}

View File

@ -1,83 +0,0 @@
/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#ifndef __GTK_DRAG_DEST_H__
#define __GTK_DRAG_DEST_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkwidget.h>
G_BEGIN_DECLS
typedef struct _GtkDropTarget GtkDropTarget;
#define GTK_TYPE_DROP_TARGET (gtk_drop_target_get_type ())
#define GTK_DROP_TARGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DROP_TARGET, GtkDropTarget))
#define GTK_DROP_TARGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_DROP_TARGET, GtkDropTargetClass))
#define GTK_IS_DROP_TARGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DROP_TARGET))
#define GTK_IS_DROP_TARGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_DROP_TARGET))
#define GTK_DROP_TARGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DROP_TARGET, GtkDropTargetClass))
typedef struct _GtkDropTargetClass GtkDropTargetClass;
GDK_AVAILABLE_IN_ALL
GType gtk_drop_target_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkDropTarget *gtk_drop_target_new (GdkContentFormats *formats,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
void gtk_drop_target_set_formats (GtkDropTarget *dest,
GdkContentFormats *formats);
GDK_AVAILABLE_IN_ALL
GdkContentFormats *gtk_drop_target_get_formats (GtkDropTarget *dest);
GDK_AVAILABLE_IN_ALL
void gtk_drop_target_set_actions (GtkDropTarget *dest,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
GdkDragAction gtk_drop_target_get_actions (GtkDropTarget *dest);
GDK_AVAILABLE_IN_ALL
GdkDrop *gtk_drop_target_get_drop (GtkDropTarget *dest);
GDK_AVAILABLE_IN_ALL
const char *gtk_drop_target_find_mimetype (GtkDropTarget *dest);
GDK_AVAILABLE_IN_ALL
void gtk_drop_target_deny_drop (GtkDropTarget *dest,
GdkDrop *drop);
G_END_DECLS
#endif /* __GTK_DRAG_DEST_H__ */

113
gtk/gtkdrop.c Normal file
View File

@ -0,0 +1,113 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkdropprivate.h"
typedef struct _GtkDrop GtkDrop;
struct _GtkDrop
{
/* TRUE if we are waiting for a gdk_drop_status() call */
gboolean waiting;
/* TRUE if begin_event() has been called but end_event() hasn't yet - purely for debugging */
gboolean active;
};
static void
gtk_drop_free (gpointer data)
{
GtkDrop *self = data;
g_slice_free (GtkDrop, self);
}
static GtkDrop *
gtk_drop_lookup (GdkDrop *drop)
{
static GQuark drop_quark = 0;
GtkDrop *result;
if (G_UNLIKELY (drop_quark == 0))
drop_quark = g_quark_from_string ("-gtk-drop-data");
result = g_object_get_qdata (G_OBJECT (drop), drop_quark);
if (result == NULL)
{
result = g_slice_new0 (GtkDrop);
g_object_set_qdata_full (G_OBJECT (drop), drop_quark, result, gtk_drop_free);
}
return result;
}
void
gtk_drop_begin_event (GdkDrop *drop,
GdkEventType event_type)
{
GtkDrop *self;
self = gtk_drop_lookup (drop);
g_assert (self->waiting == FALSE);
g_assert (self->active == FALSE);
self->active = TRUE;
if (event_type == GDK_DRAG_ENTER ||
event_type == GDK_DRAG_MOTION)
self->waiting = TRUE;
}
void
gtk_drop_end_event (GdkDrop *drop)
{
GtkDrop *self;
self = gtk_drop_lookup (drop);
g_assert (self->active == TRUE);
if (self->waiting)
{
gdk_drop_status (drop, 0);
self->waiting = FALSE;
}
self->active = FALSE;
}
gboolean
gtk_drop_status (GdkDrop *drop,
GdkDragAction actions,
GdkDragAction preferred_action)
{
GtkDrop *self;
self = gtk_drop_lookup (drop);
g_assert (self->active == TRUE);
if (!self->waiting)
return FALSE;
gdk_drop_status (drop, actions);
self->waiting = FALSE;
return TRUE;
}

38
gtk/gtkdropprivate.h Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_DROP_PRIVATE_H__
#define __GTK_DROP_PRIVATE_H__
#include <gdk/gdk.h>
G_BEGIN_DECLS
void gtk_drop_begin_event (GdkDrop *drop,
GdkEventType event_type);
void gtk_drop_end_event (GdkDrop *drop);
gboolean gtk_drop_status (GdkDrop *drop,
GdkDragAction actions,
GdkDragAction preferred_action);
G_END_DECLS
#endif

990
gtk/gtkdroptarget.c Normal file
View File

@ -0,0 +1,990 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
#include "gtkdroptarget.h"
#include "gtkdropprivate.h"
#include "gtkeventcontrollerprivate.h"
#include "gtkintl.h"
#include "gtkmarshalers.h"
#include "gtknative.h"
#include "gtkprivate.h"
#include "gtktypebuiltins.h"
/**
* SECTION:gtkdroptarget
* @Short_description: Event controller to receive DND drops
* @Title: GtkDropTarget
* @See_also: #GdkDrop, #GtkDropTargetAsync
*
* GtkDropTarget is an event controller implementing a simple way to
* receive Drag-and-Drop operations.
*
* The most basic way to use a #GtkDropTarget to receive drops on a
* widget, is to create it via gtk_drop_target_new(), passing in the
* #GType of the data you want to receive and connect to the
* GtkDropTarget::drop signal to receive the data.
*
* #GtkDropTarget supports more options, such as:
*
* * rejecting potential drops via the GtkDropTarget::accept signal
* and the gtk_drop_target_reject() function to let other drop
* targets handle the drop
* * tracking an ongoing drag operation before the drop via the
* GtkDropTarget::enter, GtkDropTarget::motion and
* GtkDropTarget::leave signals
* * configuring how to receive data by setting the
* GtkDropTarget:preload property and listening for its availability
* via the GtkDropTarget:value property
*
* However, #GtkDropTarget is ultimately modeled in a synchronous way
* and only supports data transferred via #GType.
* If you want full control over an ongoing drop, the #GdkDropTargetAsync
* object gives you this ability.
*
* While a pointer is dragged over the drop target's widget and the drop
* has not been rejected, that widget will receive the
* %GTK_STATE_FLAG_DROP_ACTIVE state, which can be used to style the widget.
*/
struct _GtkDropTarget
{
GtkEventController parent_object;
GdkContentFormats *formats;
GdkDragAction actions;
guint preload : 1;
guint dropping : 1;
graphene_point_t coords;
GdkDrop *drop;
GCancellable *cancellable; /* NULL unless doing a read of value */
GValue value;
};
struct _GtkDropTargetClass
{
GtkEventControllerClass parent_class;
gboolean (* accept) (GtkDropTarget *self,
GdkDrop *drop);
GdkDragAction (* enter) (GtkDropTarget *self,
double x,
double y);
GdkDragAction (* motion) (GtkDropTarget *self,
double x,
double y);
void (* leave) (GtkDropTarget *self,
GdkDrop *drop);
gboolean (* drop) (GtkDropTarget *self,
const GValue *value,
double x,
double y);
};
enum {
PROP_0,
PROP_ACTIONS,
PROP_DROP,
PROP_FORMATS,
PROP_PRELOAD,
PROP_VALUE,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
enum {
ACCEPT,
ENTER,
MOTION,
LEAVE,
DROP,
NUM_SIGNALS
};
static guint signals[NUM_SIGNALS];
G_DEFINE_TYPE (GtkDropTarget, gtk_drop_target, GTK_TYPE_EVENT_CONTROLLER);
static void
gtk_drop_target_end_drop (GtkDropTarget *self)
{
if (self->drop == NULL)
return;
g_object_freeze_notify (G_OBJECT (self));
if (self->dropping)
{
gdk_drop_finish (self->drop, 0);
self->dropping = FALSE;
}
g_clear_object (&self->drop);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DROP]);
if (G_IS_VALUE (&self->value))
{
g_value_unset (&self->value);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALUE]);
}
if (self->cancellable)
{
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
}
gtk_widget_unset_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)),
GTK_STATE_FLAG_DROP_ACTIVE);
g_object_thaw_notify (G_OBJECT (self));
}
static void
gtk_drop_target_do_drop (GtkDropTarget *self)
{
gboolean success;
g_assert (self->dropping);
g_assert (G_IS_VALUE (&self->value));
g_signal_emit (self, signals[DROP], 0, &self->value, self->coords.x, self->coords.y, &success);
if (success)
gdk_drop_finish (self->drop, gdk_drop_get_actions (self->drop));
else
gdk_drop_finish (self->drop, 0);
self->dropping = FALSE;
gtk_drop_target_end_drop (self);
}
static void
gtk_drop_target_load_done (GObject *source,
GAsyncResult *res,
gpointer data)
{
GtkDropTarget *self = data;
const GValue *value;
GError *error = NULL;
value = gdk_drop_read_value_finish (GDK_DROP (source), res, &error);
if (value == NULL)
{
/* If this happens, data/self is invalid */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_clear_error (&error);
return;
}
g_clear_object (&self->cancellable);
/* XXX: Should this be a warning? */
g_warning ("Failed to receive drop data: %s", error->message);
g_clear_error (&error);
gtk_drop_target_end_drop (self);
return;
}
g_clear_object (&self->cancellable);
g_value_init (&self->value, G_VALUE_TYPE (value));
g_value_copy (value, &self->value);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALUE]);
if (self->dropping)
gtk_drop_target_do_drop (self);
}
static gboolean
gtk_drop_target_load (GtkDropTarget *self)
{
g_assert (self->drop);
if (G_IS_VALUE (&self->value))
return TRUE;
if (self->cancellable)
return FALSE;
self->cancellable = g_cancellable_new ();
gdk_drop_read_value_async (self->drop,
gdk_content_formats_match_gtype (self->formats, gdk_drop_get_formats (self->drop)),
G_PRIORITY_DEFAULT,
self->cancellable,
gtk_drop_target_load_done,
g_object_ref (self));
return FALSE;
}
static void
gtk_drop_target_start_drop (GtkDropTarget *self,
GdkDrop *drop)
{
g_object_freeze_notify (G_OBJECT (self));
gtk_drop_target_end_drop (self);
self->drop = g_object_ref (drop);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DROP]);
if (self->preload)
gtk_drop_target_load (self);
gtk_widget_set_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)),
GTK_STATE_FLAG_DROP_ACTIVE,
FALSE);
g_object_thaw_notify (G_OBJECT (self));
}
static gboolean
gtk_drop_target_accept (GtkDropTarget *self,
GdkDrop *drop)
{
if ((gdk_drop_get_actions (drop) & gtk_drop_target_get_actions (self)) == 0)
return FALSE;
if (self->formats == NULL)
return TRUE;
return gdk_content_formats_match (self->formats, gdk_drop_get_formats (drop));
}
static GdkDragAction
make_action_unique (GdkDragAction actions)
{
if (actions & GDK_ACTION_COPY)
return GDK_ACTION_COPY;
if (actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
if (actions & GDK_ACTION_LINK)
return GDK_ACTION_LINK;
return 0;
}
static GdkDragAction
gtk_drop_target_enter (GtkDropTarget *self,
double x,
double y)
{
return make_action_unique (self->actions & gdk_drop_get_actions (self->drop));
}
static GdkDragAction
gtk_drop_target_motion (GtkDropTarget *self,
double x,
double y)
{
return make_action_unique (self->actions & gdk_drop_get_actions (self->drop));
}
static gboolean
gtk_drop_target_drop (GtkDropTarget *self,
const GValue *value,
double x,
double y)
{
return FALSE;
}
static gboolean
gtk_drop_target_filter_event (GtkEventController *controller,
GdkEvent *event)
{
switch ((int) gdk_event_get_event_type (event))
{
case GDK_DRAG_ENTER:
case GDK_DRAG_LEAVE:
case GDK_DRAG_MOTION:
case GDK_DROP_START:
return GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_parent_class)->filter_event (controller, event);
default:;
}
return TRUE;
}
static gboolean
gtk_drop_target_handle_event (GtkEventController *controller,
GdkEvent *event,
double x,
double y)
{
GtkDropTarget *self = GTK_DROP_TARGET (controller);
/* All drops have been rejected. New drops only arrive via crossing
* events, so we can: */
if (self->drop == NULL)
return FALSE;
switch ((int) gdk_event_get_event_type (event))
{
case GDK_DRAG_MOTION:
{
GtkWidget *widget = gtk_event_controller_get_widget (controller);
GdkDragAction preferred;
/* sanity check */
g_return_val_if_fail (self->drop == gdk_drag_event_get_drop (event), FALSE);
graphene_point_init (&self->coords, x, y);
g_signal_emit (self, signals[MOTION], 0, x, y, &preferred);
if (preferred &&
gtk_drop_status (self->drop, self->actions, preferred))
{
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
}
else
{
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
}
}
return FALSE;
case GDK_DROP_START:
{
/* sanity check */
g_return_val_if_fail (self->drop == gdk_drag_event_get_drop (event), FALSE);
graphene_point_init (&self->coords, x, y);
self->dropping = TRUE;
if (gtk_drop_target_load (self))
gtk_drop_target_do_drop (self);
return TRUE;
}
default:
return FALSE;
}
}
static void
gtk_drop_target_handle_crossing (GtkEventController *controller,
const GtkCrossingData *crossing,
double x,
double y)
{
GtkDropTarget *self = GTK_DROP_TARGET (controller);
GtkWidget *widget = gtk_event_controller_get_widget (controller);
if (crossing->type != GTK_CROSSING_DROP)
return;
/* sanity check */
g_warn_if_fail (self->drop == NULL || self->drop == crossing->drop);
if (crossing->direction == GTK_CROSSING_IN)
{
gboolean accept = FALSE;
GdkDragAction preferred;
if (self->drop != NULL)
return;
/* if we were a target already but self->drop == NULL, the drop
* was rejected already */
if (crossing->old_descendent != NULL ||
crossing->old_target == widget)
return;
g_signal_emit (self, signals[ACCEPT], 0, crossing->drop, &accept);
if (!accept)
return;
graphene_point_init (&self->coords, x, y);
gtk_drop_target_start_drop (self, crossing->drop);
g_signal_emit (self, signals[ENTER], 0, x, y, &preferred);
if (preferred &&
gtk_drop_status (self->drop, self->actions, preferred))
{
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
}
else
{
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
}
}
else
{
if (crossing->new_descendent != NULL ||
crossing->new_target == widget)
return;
g_signal_emit (self, signals[LEAVE], 0, self->drop);
if (!self->dropping)
gtk_drop_target_end_drop (self);
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
}
}
static void
gtk_drop_target_finalize (GObject *object)
{
GtkDropTarget *self = GTK_DROP_TARGET (object);
g_clear_pointer (&self->formats, gdk_content_formats_unref);
G_OBJECT_CLASS (gtk_drop_target_parent_class)->finalize (object);
}
static void
gtk_drop_target_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkDropTarget *self = GTK_DROP_TARGET (object);
switch (prop_id)
{
case PROP_ACTIONS:
gtk_drop_target_set_actions (self, g_value_get_flags (value));
break;
case PROP_PRELOAD:
gtk_drop_target_set_preload (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_drop_target_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkDropTarget *self = GTK_DROP_TARGET (object);
switch (prop_id)
{
case PROP_ACTIONS:
g_value_set_flags (value, self->actions);
break;
case PROP_DROP:
g_value_set_object (value, self->drop);
break;
case PROP_FORMATS:
g_value_set_boxed (value, self->formats);
break;
case PROP_PRELOAD:
g_value_set_boolean (value, self->preload);
break;
case PROP_VALUE:
if (G_IS_VALUE (&self->value))
g_value_set_boxed (value, &self->value);
else
g_value_set_boxed (value, NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_drop_target_class_init (GtkDropTargetClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class);
object_class->finalize = gtk_drop_target_finalize;
object_class->set_property = gtk_drop_target_set_property;
object_class->get_property = gtk_drop_target_get_property;
controller_class->handle_event = gtk_drop_target_handle_event;
controller_class->filter_event = gtk_drop_target_filter_event;
controller_class->handle_crossing = gtk_drop_target_handle_crossing;
class->accept = gtk_drop_target_accept;
class->enter = gtk_drop_target_enter;
class->motion = gtk_drop_target_motion;
class->drop = gtk_drop_target_drop;
/**
* GtkDropTarget:actions:
*
* The #GdkDragActions that this drop target supports
*/
properties[PROP_ACTIONS] =
g_param_spec_flags ("actions",
P_("Actions"),
P_("The actions supported by this drop target"),
GDK_TYPE_DRAG_ACTION, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDropTarget:drop:
*
* The #GdkDrop that is currently being performed
*/
properties[PROP_DROP] =
g_param_spec_object ("drop",
P_("Drop"),
P_("Current drop"),
GDK_TYPE_DROP,
GTK_PARAM_READABLE);
/**
* GtkDropTarget:formats:
*
* The #GdkContentFormats that determine the supported data formats
*/
properties[PROP_FORMATS] =
g_param_spec_boxed ("formats",
P_("Formats"),
P_("The supported formats"),
GDK_TYPE_CONTENT_FORMATS,
GTK_PARAM_READABLE);
/**
* GtkDropTarget:preload:
*
* Whether the drop data should be preloaded when the pointer is only
* hovering over the widget but has not been released.
*
* Setting this property allows finer grained reaction to an ongoing
* drop at the cost of loading more data.
*
* The default value for this property is %FALSE to avoid downloading
* huge amounts of data by accident.
* For example, if somebody drags a full document of gigabytes of text
* from a text editor across a widget with a preloading drop target,
* this data will be downlaoded, even if the data is ultimately dropped
* elsewhere.
*
* For a lot of data formats, the amount of data is very small (like
* %GDK_TYPE_RGBA), so enabling this property does not hurt at all.
*/
properties[PROP_PRELOAD] =
g_param_spec_boolean ("preload",
P_("Preload"),
P_("Whether drop data should be preloaded while hovering"),
FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDropTarget:value:
*
* The value for this drop operation or %NULL if the data has not been
* loaded yet or no drop operation is going on.
*
* Data may be available before the GtkDropTarget::drop signal gets emitted -
* for example when the GtkDropTarget:preload property is set.
* You can use the GObject::notify signal to be notified of available data.
*/
properties[PROP_VALUE] =
g_param_spec_boxed ("value",
P_("Value"),
P_("The value for this drop operation"),
G_TYPE_VALUE,
GTK_PARAM_READABLE);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
/**
* GtkDropTarget::accept:
* @self: the #GtkDropTarget
* @drop: the #GdkDrop
*
* The ::accept signal is emitted on the drop site when a drop operation
* is about to begin.
* If the drop is not accepted, %FALSE will be returned and the drop target
* will ignore the drop. If %TRUE is returned, the drop is accepted for now
* but may be rejected later via a call to gtk_drop_target_reject() or
* ultimately by returning %FALSE from GtkDropTarget::drop
*
* The default handler for this signal decides whether to accept the drop
* based on the formats provided by the @drop.
*
* If the decision whether the drop will be accepted or rejected needs
* inspecting the data, this function should return %TRUE, the
* GtkDropTarget:preload property should be set and the value
* should be inspected via the GObject::notify:value signal and then call
* gtk_drop_target_reject().
*
* Returns: %TRUE if @drop is accepted
*/
signals[ACCEPT] =
g_signal_new (I_("accept"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetClass, accept),
g_signal_accumulator_first_wins, NULL,
NULL,
G_TYPE_BOOLEAN, 1,
GDK_TYPE_DROP);
/**
* GtkDropTarget::enter:
* @self: the #GtkDropTarget
* @x: the x coordinate of the current pointer position
* @y: the y coordinate of the current pointer position
*
* The ::enter signal is emitted on the drop site when the pointer
* enters the widget. It can be used to set up custom highlighting.
*
* Returns: Preferred action for this drag operation or 0 if dropping is not
* supported at the current @x,@y location.
*/
signals[ENTER] =
g_signal_new (I_("enter"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetClass, enter),
g_signal_accumulator_first_wins, NULL,
NULL,
GDK_TYPE_DRAG_ACTION, 2,
G_TYPE_DOUBLE, G_TYPE_DOUBLE);
/**
* GtkDropTarget::motion:
* @self: the #GtkDropTarget
* @x: the x coordinate of the current pointer position
* @y: the y coordinate of the current pointer position
*
* The ::motion signal is emitted while the pointer is moving
* over the drop target.
*
* Returns: Preferred action for this drag operation or 0 if dropping is not
* supported at the current @x,@y location.
*/
signals[MOTION] =
g_signal_new (I_("motion"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetClass, motion),
g_signal_accumulator_first_wins, NULL,
NULL,
GDK_TYPE_DRAG_ACTION, 2,
G_TYPE_DOUBLE, G_TYPE_DOUBLE);
/**
* GtkDropTarget::leave:
* @self: the #GtkDropTarget
* @drop: the #GdkDrop
*
* The ::leave signal is emitted on the drop site when the pointer
* leaves the widget. Its main purpose it to undo things done in
* #GtkDropTarget::enter.
*/
signals[LEAVE] =
g_signal_new (I_("leave"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetClass, leave),
NULL, NULL,
NULL,
G_TYPE_NONE, 0);
/**
* GtkDropTarget::drop:
* @self: the #GtkDropTarget
* @value: the #GValue being dropped
* @x: the x coordinate of the current pointer position
* @y: the y coordinate of the current pointer position
*
* The ::drop signal is emitted on the drop site when the user drops
* the data onto the widget. The signal handler must determine whether
* the pointer position is in a drop zone or not. If it is not in a drop
* zone, it returns %FALSE and no further processing is necessary.
*
* Otherwise, the handler returns %TRUE. In this case, this handler will
* accept the drop. The handler is responsible for rading the given @value
* and performing the drop operation.
*
* Returns: whether the drop was accepted at the given pointer position
*/
signals[DROP] =
g_signal_new (I_("drop"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
g_signal_accumulator_first_wins, NULL,
NULL,
G_TYPE_BOOLEAN, 3,
G_TYPE_VALUE, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
}
static void
gtk_drop_target_init (GtkDropTarget *self)
{
self->formats = gdk_content_formats_new (NULL, 0);
}
/**
* gtk_drop_target_new:
* @type: The supported type or %G_TYPE_INVALID
* @actions: the supported actions
*
* Creates a new #GtkDropTarget object.
*
* If the drop target should support more than 1 type, pass
* %G_TYPE_INVALID for @type and then call
* gtk_drop_target_set_gtypes().
*
* Returns: the new #GtkDropTarget
*/
GtkDropTarget *
gtk_drop_target_new (GType type,
GdkDragAction actions)
{
GtkDropTarget *result;
result = g_object_new (GTK_TYPE_DROP_TARGET,
"actions", actions,
NULL);
if (type != G_TYPE_INVALID)
gtk_drop_target_set_gtypes (result, &type, 1);
return result;
}
/**
* gtk_drop_target_get_formats:
* @self: a #GtkDropTarget
*
* Gets the data formats that this drop target accepts.
*
* If the result is %NULL, all formats are expected to be supported.
*
* Returns: (nullable): the supported data formats
*/
GdkContentFormats *
gtk_drop_target_get_formats (GtkDropTarget *self)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
return self->formats;
}
/**
* gtk_drop_target_set_gtypes:
* @self: a #GtkDropTarget
* @types: (nullable) (transfer none) (array length=n_types):
* all supported #GTypes that can be dropped
* @n_types: number of @types
*
* Sets the supported #GTypes for this drop target.
*
* The GtkDropTarget::drop signal will
**/
void
gtk_drop_target_set_gtypes (GtkDropTarget *self,
GType *types,
gsize n_types)
{
GdkContentFormatsBuilder *builder;
gsize i;
g_return_if_fail (GTK_IS_DROP_TARGET (self));
g_return_if_fail (n_types == 0 || types != NULL);
gdk_content_formats_unref (self->formats);
builder = gdk_content_formats_builder_new ();
for (i = 0; i < n_types; i++)
gdk_content_formats_builder_add_gtype (builder, types[i]);
self->formats = gdk_content_formats_builder_free_to_formats (builder);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FORMATS]);
}
/**
* gtk_drop_target_get_gtypes:
* @self: a #GtkDropTarget
* @n_gtypes: (out) (allow-none): optional pointer to take the
* number of #GTypes contained in the return value
*
* Gets the list of supported #GTypes for @self. If no type have been set,
* %NULL will be returned.
*
* Returns: (transfer none) (nullable): %G_TYPE_INVALID-terminated array of
* types included in @formats or %NULL if none.
**/
const GType *
gtk_drop_target_get_gtypes (GtkDropTarget *self,
gsize *n_types)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
return gdk_content_formats_get_gtypes (self->formats, n_types);
}
/**
* gtk_drop_target_set_actions:
* @self: a #GtkDropTarget
* @actions: the supported actions
*
* Sets the actions that this drop target supports.
*/
void
gtk_drop_target_set_actions (GtkDropTarget *self,
GdkDragAction actions)
{
g_return_if_fail (GTK_IS_DROP_TARGET (self));
if (self->actions == actions)
return;
self->actions = actions;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]);
}
/**
* gtk_drop_target_get_actions:
* @self: a #GtkDropTarget
*
* Gets the actions that this drop target supports.
*
* Returns: the actions that this drop target supports
*/
GdkDragAction
gtk_drop_target_get_actions (GtkDropTarget *self)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET (self), 0);
return self->actions;
}
/**
* gtk_drop_target_set_preload:
* @self: a #GtkDropTarget
* @preload: %TRUE to preload drop data
*
* Sets the GtkDropTarget:preload property.
**/
void
gtk_drop_target_set_preload (GtkDropTarget *self,
gboolean preload)
{
g_return_if_fail (GTK_IS_DROP_TARGET (self));
if (self->preload == preload)
return;
self->preload = preload;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PRELOAD]);
}
/**
* gtk_drop_target_get_preload:
* @self: a #GtkDropTarget
*
* Gets the value of the GtkDropTarget:preload property.
*
* Returns: %TRUE if drop data should be preloaded
*/
gboolean
gtk_drop_target_get_preload (GtkDropTarget *self)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET (self), 0);
return self->preload;
}
/**
* gtk_drop_target_get_drop:
* @self: a #GtkDropTarget
*
* Gets the currently handled drop operation.
*
* If no drop operation is going on, %NULL is returned.
*
* Returns: (nullable) (transfer none): The current drop
**/
GdkDrop *
gtk_drop_target_get_drop (GtkDropTarget *self)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
return self->drop;
}
/**
* gtk_drop_target_get_value:
* @self: a #GtkDropTarget
*
* Gets the value of the GtkDropTarget:value porperty.
*
* Returns: (nullable) (transfer none): The current drop data
**/
const GValue *
gtk_drop_target_get_value (GtkDropTarget *self)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
if (!G_IS_VALUE (&self->value))
return NULL;
return &self->value;
}
/**
* gtk_drop_target_reject:
* @self: a #GtkDropTarget
*
* Rejects the ongoing drop operation.
*
* If no drop operation is ongoing - when GdkDropTarget:drop
* returns %NULL - this function does nothing.
*
* This function should be used when delaying the decision
* on whether to accept a drag or not until after reading
* the data.
*/
void
gtk_drop_target_reject (GtkDropTarget *self)
{
g_return_if_fail (GTK_IS_DROP_TARGET (self));
if (self->drop == NULL)
return;
gtk_drop_target_end_drop (self);
}

86
gtk/gtkdroptarget.h Normal file
View File

@ -0,0 +1,86 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_DROP_TARGET_H__
#define __GTK_DROP_TARGET_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtktypes.h>
G_BEGIN_DECLS
typedef struct _GtkDropTarget GtkDropTarget;
#define GTK_TYPE_DROP_TARGET (gtk_drop_target_get_type ())
#define GTK_DROP_TARGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DROP_TARGET, GtkDropTarget))
#define GTK_DROP_TARGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_DROP_TARGET, GtkDropTargetClass))
#define GTK_IS_DROP_TARGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DROP_TARGET))
#define GTK_IS_DROP_TARGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_DROP_TARGET))
#define GTK_DROP_TARGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DROP_TARGET, GtkDropTargetClass))
typedef struct _GtkDropTargetClass GtkDropTargetClass;
GDK_AVAILABLE_IN_ALL
GType gtk_drop_target_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkDropTarget * gtk_drop_target_new (GType type,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
void gtk_drop_target_set_gtypes (GtkDropTarget *self,
GType *types,
gsize n_types);
GDK_AVAILABLE_IN_ALL
const GType * gtk_drop_target_get_gtypes (GtkDropTarget *self,
gsize *n_types);
GDK_AVAILABLE_IN_ALL
GdkContentFormats * gtk_drop_target_get_formats (GtkDropTarget *self);
GDK_AVAILABLE_IN_ALL
void gtk_drop_target_set_actions (GtkDropTarget *self,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
GdkDragAction gtk_drop_target_get_actions (GtkDropTarget *self);
GDK_AVAILABLE_IN_ALL
void gtk_drop_target_set_preload (GtkDropTarget *self,
gboolean preload);
GDK_AVAILABLE_IN_ALL
gboolean gtk_drop_target_get_preload (GtkDropTarget *self);
GDK_AVAILABLE_IN_ALL
GdkDrop * gtk_drop_target_get_drop (GtkDropTarget *self);
GDK_AVAILABLE_IN_ALL
const GValue * gtk_drop_target_get_value (GtkDropTarget *self);
GDK_AVAILABLE_IN_ALL
void gtk_drop_target_reject (GtkDropTarget *self);
G_END_DECLS
#endif /* __GTK_DROP_TARGET_H__ */

673
gtk/gtkdroptargetasync.c Normal file
View File

@ -0,0 +1,673 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
#include "gtkdroptargetasync.h"
#include "gtkdropprivate.h"
#include "gtkeventcontrollerprivate.h"
#include "gtkintl.h"
#include "gtkmarshalers.h"
#include "gtknative.h"
#include "gtktypebuiltins.h"
/**
* SECTION:gtkdroptargetasync
* @Short_description: Event controller to receive DND drops
* @Title: GtkDropTargetAsync
* @See_also: #GtkDropTarget
*
* GtkDropTargetAsync is an auxiliary object that can be used to receive
* Drag-and-Drop operations.
* It is the more complete but also more complex method of handling drop
* operations compared to #GtkDropTarget and you should only use it if
* #GtkDropTarget doesn't provide all the features you need.
*
* To use a #GtkDropTargetAsync to receive drops on a widget, you create
* a #GtkDropTargetAsync object, configure which data formats and actions
* you support, connect to its signals, and then attach
* it to the widget with gtk_widget_add_controller().
*
* During a drag operation, the first signal that a GtkDropTargetAsync
* emits is #GtkDropTargetAsync::accept, which is meant to determine
* whether the target is a possible drop site for the ongoing drop.
* The default handler for the ::accept signal accepts the drop
* if it finds a compatible data format and an action that is supported
* on both sides.
*
* If it is, and the widget becomes a target, you will receive a
* #GtkDropTargetAsync::drag-enter signal, followed by
* #GtkDropTargetAsync::drag-motion signals as the pointer moves,
* optionally a #GtkDropTargetAsync::drop signal when a drop happens,
* and finally a #GtkDropTargetAsync::drag-leave signal when the pointer
* moves off the widget.
*
* The ::drag-enter and ::drag-motion handler return a #GdkDragAction
* to update the status of the ongoing operation. The ::drop handler
* should decide if it ultimately accepts the drop and if it does, it
* should initiate the data transfer and finish the operation by calling
* gdk_drop_finish().
*
* Between the ::drag-enter and ::drag-leave signals the widget is a
* current drop target, and will receive the %GTK_STATE_FLAG_DROP_ACTIVE
* state, which can be used by themes to style the widget as a drop target.
*/
struct _GtkDropTargetAsync
{
GtkEventController parent_object;
GdkContentFormats *formats;
GdkDragAction actions;
GdkDrop *drop;
gboolean rejected;
};
struct _GtkDropTargetAsyncClass
{
GtkEventControllerClass parent_class;
gboolean (* accept) (GtkDropTargetAsync *self,
GdkDrop *drop);
GdkDragAction (* drag_enter) (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y);
GdkDragAction (* drag_motion) (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y);
void (* drag_leave) (GtkDropTargetAsync *self,
GdkDrop *drop);
gboolean (* drop) (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y);
};
enum {
PROP_0,
PROP_ACTIONS,
PROP_FORMATS,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
enum {
ACCEPT,
DRAG_ENTER,
DRAG_MOTION,
DRAG_LEAVE,
DROP,
NUM_SIGNALS
};
static guint signals[NUM_SIGNALS];
G_DEFINE_TYPE (GtkDropTargetAsync, gtk_drop_target_async, GTK_TYPE_EVENT_CONTROLLER);
static gboolean
gtk_drop_target_async_accept (GtkDropTargetAsync *self,
GdkDrop *drop)
{
if ((gdk_drop_get_actions (drop) & self->actions) == 0)
return FALSE;
if (self->formats == NULL)
return TRUE;
return gdk_content_formats_match (self->formats, gdk_drop_get_formats (drop));
}
static GdkDragAction
make_action_unique (GdkDragAction actions)
{
if (actions & GDK_ACTION_COPY)
return GDK_ACTION_COPY;
if (actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
if (actions & GDK_ACTION_LINK)
return GDK_ACTION_LINK;
return 0;
}
static GdkDragAction
gtk_drop_target_async_drag_enter (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y)
{
return make_action_unique (self->actions & gdk_drop_get_actions (drop));
}
static GdkDragAction
gtk_drop_target_async_drag_motion (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y)
{
return make_action_unique (self->actions & gdk_drop_get_actions (drop));
}
static gboolean
gtk_drop_target_async_drop (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y)
{
return FALSE;
}
static gboolean
gtk_drop_target_async_filter_event (GtkEventController *controller,
GdkEvent *event)
{
switch ((int)gdk_event_get_event_type (event))
{
case GDK_DRAG_ENTER:
case GDK_DRAG_LEAVE:
case GDK_DRAG_MOTION:
case GDK_DROP_START:
return GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_async_parent_class)->filter_event (controller, event);
default:;
}
return TRUE;
}
static gboolean
gtk_drop_target_async_handle_event (GtkEventController *controller,
GdkEvent *event,
double x,
double y)
{
GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (controller);
GdkDrop *drop;
switch ((int) gdk_event_get_event_type (event))
{
case GDK_DRAG_MOTION:
{
GtkWidget *widget = gtk_event_controller_get_widget (controller);
GdkDragAction preferred_action;
drop = gdk_drag_event_get_drop (event);
/* sanity check */
g_return_val_if_fail (self->drop == drop, FALSE);
if (self->rejected)
return FALSE;
g_signal_emit (self, signals[DRAG_MOTION], 0, drop, x, y, &preferred_action);
if (preferred_action &&
gtk_drop_status (self->drop, self->actions, preferred_action))
{
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
}
else
{
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
}
}
return FALSE;
case GDK_DROP_START:
{
gboolean handled;
drop = gdk_drag_event_get_drop (event);
/* sanity check */
g_return_val_if_fail (self->drop == drop, FALSE);
if (self->rejected)
return FALSE;
g_signal_emit (self, signals[DROP], 0, self->drop, x, y, &handled);
return handled;
}
default:
return FALSE;
}
}
static void
gtk_drop_target_async_handle_crossing (GtkEventController *controller,
const GtkCrossingData *crossing,
double x,
double y)
{
GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (controller);
GtkWidget *widget = gtk_event_controller_get_widget (controller);
if (crossing->type != GTK_CROSSING_DROP)
return;
/* sanity check */
g_warn_if_fail (self->drop == NULL || self->drop == crossing->drop);
if (crossing->direction == GTK_CROSSING_IN)
{
gboolean accept = FALSE;
GdkDragAction preferred_action;
if (self->drop != NULL)
return;
self->drop = g_object_ref (crossing->drop);
g_signal_emit (self, signals[ACCEPT], 0, self->drop, &accept);
self->rejected = !accept;
if (self->rejected)
return;
g_signal_emit (self, signals[DRAG_ENTER], 0, self->drop, x, y, &preferred_action);
if (preferred_action &&
gtk_drop_status (self->drop, self->actions, preferred_action))
{
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
}
}
else
{
if (crossing->new_descendent != NULL ||
crossing->new_target == widget)
return;
g_signal_emit (self, signals[DRAG_LEAVE], 0, self->drop);
g_clear_object (&self->drop);
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
}
}
static void
gtk_drop_target_async_finalize (GObject *object)
{
GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object);
g_clear_pointer (&self->formats, gdk_content_formats_unref);
G_OBJECT_CLASS (gtk_drop_target_async_parent_class)->finalize (object);
}
static void
gtk_drop_target_async_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object);
switch (prop_id)
{
case PROP_ACTIONS:
gtk_drop_target_async_set_actions (self, g_value_get_flags (value));
break;
case PROP_FORMATS:
gtk_drop_target_async_set_formats (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_drop_target_async_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object);
switch (prop_id)
{
case PROP_ACTIONS:
g_value_set_flags (value, gtk_drop_target_async_get_actions (self));
break;
case PROP_FORMATS:
g_value_set_boxed (value, gtk_drop_target_async_get_formats (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_drop_target_async_class_init (GtkDropTargetAsyncClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class);
object_class->finalize = gtk_drop_target_async_finalize;
object_class->set_property = gtk_drop_target_async_set_property;
object_class->get_property = gtk_drop_target_async_get_property;
controller_class->handle_event = gtk_drop_target_async_handle_event;
controller_class->filter_event = gtk_drop_target_async_filter_event;
controller_class->handle_crossing = gtk_drop_target_async_handle_crossing;
class->accept = gtk_drop_target_async_accept;
class->drag_enter = gtk_drop_target_async_drag_enter;
class->drag_motion = gtk_drop_target_async_drag_motion;
class->drop = gtk_drop_target_async_drop;
/**
* GtkDropTargetAsync:actions:
*
* The #GdkDragActions that this drop target supports
*/
properties[PROP_ACTIONS] =
g_param_spec_flags ("actions", P_("Actions"), P_("Actions"),
GDK_TYPE_DRAG_ACTION, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDropTargetAsync:formats:
*
* The #GdkContentFormats that determines the supported data formats
*/
properties[PROP_FORMATS] =
g_param_spec_boxed ("formats", P_("Formats"), P_("Formats"),
GDK_TYPE_CONTENT_FORMATS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
/**
* GtkWidget::accept:
* @self: the #GtkDropTargetAsync
* @drop: the #GdkDrop
*
* The ::accept signal is emitted on the drop site when a drop operation
* is about to begin.
* If the drop is not accepted, %FALSE will be returned and the drop target
* will ignore the drop. If %TRUE is returned, the drop is accepted for now
* but may be rejected later via a call to gtk_drop_target_reject() or
* ultimately by returning %FALSE from GtkDropTarget::drop
*
* The default handler for this signal decides whether to accept the drop
* based on the formats provided by the @drop.
*
* If the decision whether the drop will be accepted or rejected needs
* further procesing, such as inspecting the data, this function should
* return %TRUE and proceed as is @drop was accepted and if it decides to
* reject the drop later, it should call gtk_drop_target_reject_drop().
*
* Returns: %TRUE if @drop is accepted
*/
signals[ACCEPT] =
g_signal_new (I_("accept"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetAsyncClass, accept),
g_signal_accumulator_first_wins, NULL,
NULL,
G_TYPE_BOOLEAN, 1,
GDK_TYPE_DROP);
/**
* GtkDropTargetAsync::drag-enter:
* @self: the #GtkDropTargetAsync
* @drop: the #GdkDrop
* @x: the x coordinate of the current pointer position
* @y: the y coordinate of the current pointer position
*
* The ::drag-enter signal is emitted on the drop site when the pointer
* enters the widget. It can be used to set up custom highlighting.
*
* Returns: Preferred action for this drag operation.
*/
signals[DRAG_ENTER] =
g_signal_new (I_("drag-enter"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_enter),
g_signal_accumulator_first_wins, NULL,
NULL,
GDK_TYPE_DRAG_ACTION, 3,
GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
/**
* GtkDropTargetAsync::drag-motion:
* @self: the #GtkDropTargetAsync
* @drop: the #GdkDrop
* @x: the x coordinate of the current pointer position
* @y: the y coordinate of the current pointer position
*
* The ::drag-motion signal is emitted while the pointer is moving
* over the drop target.
*
* Returns: Preferred action for this drag operation.
*/
signals[DRAG_MOTION] =
g_signal_new (I_("drag-motion"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_motion),
g_signal_accumulator_first_wins, NULL,
NULL,
GDK_TYPE_DRAG_ACTION, 3,
GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
/**
* GtkDropTargetAsync::drag-leave:
* @self: the #GtkDropTargetAsync
* @drop: the #GdkDrop
*
* The ::drag-leave signal is emitted on the drop site when the pointer
* leaves the widget. Its main purpose it to undo things done in
* #GtkDropTargetAsync::drag-enter.
*/
signals[DRAG_LEAVE] =
g_signal_new (I_("drag-leave"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_leave),
NULL, NULL,
NULL,
G_TYPE_NONE, 1,
GDK_TYPE_DROP);
/**
* GtkDropTargetAsync::drop:
* @self: the #GtkDropTargetAsync
* @drop: the #GdkDrop
* @x: the x coordinate of the current pointer position
* @y: the y coordinate of the current pointer position
*
* The ::drop signal is emitted on the drop site when the user drops
* the data onto the widget. The signal handler must determine whether
* the pointer position is in a drop zone or not. If it is not in a drop
* zone, it returns %FALSE and no further processing is necessary.
*
* Otherwise, the handler returns %TRUE. In this case, this handler will
* accept the drop. The handler must ensure that gdk_drop_finish() is
* called to let the source know that the drop is done. The call to
* gtk_drag_finish() must only be done when all data has been received.
*
* To receive the data, use one of the read functions provides by #GdkDrop
* such as gdk_drop_read_async() or gdk_drop_read_value_async().
*
* Returns: whether the drop is accepted at the given pointer position
*/
signals[DROP] =
g_signal_new (I_("drop"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
g_signal_accumulator_first_wins, NULL,
NULL,
G_TYPE_BOOLEAN, 3,
GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
}
static void
gtk_drop_target_async_init (GtkDropTargetAsync *self)
{
}
/**
* gtk_drop_target_async_new:
* @formats: (nullable) (transfer full): the supported data formats
* @actions: the supported actions
*
* Creates a new #GtkDropTargetAsync object.
*
* Returns: the new #GtkDropTargetAsync
*/
GtkDropTargetAsync *
gtk_drop_target_async_new (GdkContentFormats *formats,
GdkDragAction actions)
{
GtkDropTargetAsync *result;
result = g_object_new (GTK_TYPE_DROP_TARGET_ASYNC,
"formats", formats,
"actions", actions,
NULL);
g_clear_pointer (&formats, gdk_content_formats_unref);
return result;
}
/**
* gtk_drop_target_async_set_formats:
* @self: a #GtkDropTargetAsync
* @formats: (nullable): the supported data formats or %NULL for
* any format.
*
* Sets the data formats that this drop target will accept.
*/
void
gtk_drop_target_async_set_formats (GtkDropTargetAsync *self,
GdkContentFormats *formats)
{
g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self));
if (self->formats == formats)
return;
if (self->formats)
gdk_content_formats_unref (self->formats);
self->formats = formats;
if (self->formats)
gdk_content_formats_ref (self->formats);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FORMATS]);
}
/**
* gtk_drop_target_async_get_formats:
* @self: a #GtkDropTargetAsync
*
* Gets the data formats that this drop target accepts.
*
* If the result is %NULL, all formats are expected to be supported.
*
* Returns: (nullable): the supported data formats
*/
GdkContentFormats *
gtk_drop_target_async_get_formats (GtkDropTargetAsync *self)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET_ASYNC (self), NULL);
return self->formats;
}
/**
* gtk_drop_target_async_set_actions:
* @self: a #GtkDropTargetAsync
* @actions: the supported actions
*
* Sets the actions that this drop target supports.
*/
void
gtk_drop_target_async_set_actions (GtkDropTargetAsync *self,
GdkDragAction actions)
{
g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self));
if (self->actions == actions)
return;
self->actions = actions;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]);
}
/**
* gtk_drop_target_async_get_actions:
* @self: a #GtkDropTargetAsync
*
* Gets the actions that this drop target supports.
*
* Returns: the actions that this drop target supports
*/
GdkDragAction
gtk_drop_target_async_get_actions (GtkDropTargetAsync *self)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET_ASYNC (self), 0);
return self->actions;
}
/**
* gtk_drop_target_async_reject_drop:
* @self: a #GtkDropTargetAsync
* @drop: the #GdkDrop of an ongoing drag operation
*
* Sets the @drop as not accepted on this drag site.
*
* This function should be used when delaying the decision
* on whether to accept a drag or not until after reading
* the data.
*/
void
gtk_drop_target_async_reject_drop (GtkDropTargetAsync *self,
GdkDrop *drop)
{
g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self));
g_return_if_fail (GDK_IS_DROP (drop));
g_return_if_fail (self->drop == drop);
if (self->rejected)
return;
self->rejected = TRUE;
gtk_widget_unset_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)),
GTK_STATE_FLAG_DROP_ACTIVE);
}

77
gtk/gtkdroptargetasync.h Normal file
View File

@ -0,0 +1,77 @@
/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#ifndef __GTK_DROP_TARGET_ASYNC_H__
#define __GTK_DROP_TARGET_ASYNC_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkwidget.h>
G_BEGIN_DECLS
typedef struct _GtkDropTargetAsync GtkDropTargetAsync;
typedef struct _GtkDropTargetAsyncClass GtkDropTargetAsyncClass;
#define GTK_TYPE_DROP_TARGET_ASYNC (gtk_drop_target_async_get_type ())
#define GTK_DROP_TARGET_ASYNC(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DROP_TARGET_ASYNC, GtkDropTargetAsync))
#define GTK_DROP_TARGET_ASYNC_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_DROP_TARGET_ASYNC, GtkDropTargetAsyncClass))
#define GTK_IS_DROP_TARGET_ASYNC(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DROP_TARGET_ASYNC))
#define GTK_IS_DROP_TARGET_ASYNC_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_DROP_TARGET_ASYNC))
#define GTK_DROP_TARGET_ASYNC_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DROP_TARGET_ASYNC, GtkDropTargetAsyncClass))
GDK_AVAILABLE_IN_ALL
GType gtk_drop_target_async_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkDropTargetAsync * gtk_drop_target_async_new (GdkContentFormats *formats,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
void gtk_drop_target_async_set_formats (GtkDropTargetAsync *self,
GdkContentFormats *formats);
GDK_AVAILABLE_IN_ALL
GdkContentFormats * gtk_drop_target_async_get_formats (GtkDropTargetAsync *self);
GDK_AVAILABLE_IN_ALL
void gtk_drop_target_async_set_actions (GtkDropTargetAsync *self,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
GdkDragAction gtk_drop_target_async_get_actions (GtkDropTargetAsync *self);
GDK_AVAILABLE_IN_ALL
void gtk_drop_target_async_reject_drop (GtkDropTargetAsync *self,
GdkDrop *drop);
G_END_DECLS
#endif /* __GTK_DROP_TARGET_ASYNC_H__ */

View File

@ -37,7 +37,7 @@
#include "gtkcellrendererpixbuf.h"
#include "gtkcombobox.h"
#include "gtkcssiconthemevalueprivate.h"
#include "gtkdragdest.h"
#include "gtkdroptarget.h"
#include "gtkicontheme.h"
#include "gtkimage.h"
#include "gtklabel.h"
@ -268,11 +268,6 @@ static void gtk_file_chooser_button_finalize (GObject *ob
/* GtkWidget Functions */
static void gtk_file_chooser_button_destroy (GtkWidget *widget);
static gboolean gtk_file_chooser_button_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkWidget *widget);
static void gtk_file_chooser_button_show (GtkWidget *widget);
static void gtk_file_chooser_button_hide (GtkWidget *widget);
static void gtk_file_chooser_button_root (GtkWidget *widget);
@ -350,6 +345,115 @@ G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE
G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
gtk_file_chooser_button_file_chooser_iface_init))
struct DndSelectFolderData
{
GtkFileSystem *file_system;
GtkFileChooserButton *button;
GtkFileChooserAction action;
GFile *file;
gchar **uris;
guint i;
gboolean selected;
};
static void
dnd_select_folder_get_info_cb (GCancellable *cancellable,
GFileInfo *info,
const GError *error,
gpointer user_data)
{
struct DndSelectFolderData *data = user_data;
GtkFileChooserButton *button = data->button;
GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
gboolean cancelled = g_cancellable_is_cancelled (cancellable);
if (cancellable != priv->dnd_select_folder_cancellable)
{
g_object_unref (data->button);
g_object_unref (data->file);
g_strfreev (data->uris);
g_free (data);
g_object_unref (cancellable);
return;
}
priv->dnd_select_folder_cancellable = NULL;
if (!cancelled && !error && info != NULL)
{
gboolean is_folder;
is_folder = _gtk_file_info_consider_as_directory (info);
data->selected =
(((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && is_folder) ||
(data->action == GTK_FILE_CHOOSER_ACTION_OPEN && !is_folder)) &&
gtk_file_chooser_select_file (GTK_FILE_CHOOSER (data->button), data->file, NULL));
}
else
data->selected = FALSE;
if (data->selected || data->uris[++data->i] == NULL)
{
g_signal_emit (data->button, file_chooser_button_signals[FILE_SET], 0);
g_object_unref (data->button);
g_object_unref (data->file);
g_strfreev (data->uris);
g_free (data);
g_object_unref (cancellable);
return;
}
if (data->file)
g_object_unref (data->file);
data->file = g_file_new_for_uri (data->uris[data->i]);
priv->dnd_select_folder_cancellable =
_gtk_file_system_get_info (data->file_system, data->file,
"standard::type",
dnd_select_folder_get_info_cb, user_data);
g_object_unref (cancellable);
}
static gboolean
gtk_file_chooser_button_drop (GtkDropTarget *target,
const GValue *value,
double x,
double y,
GtkFileChooserButton *button)
{
GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
struct DndSelectFolderData *info;
GFile *file;
file = g_value_get_object (value);
info = g_new0 (struct DndSelectFolderData, 1);
info->button = g_object_ref (button);
info->i = 0;
info->uris = g_new0 (char *, 2);
info->selected = FALSE;
info->file_system = priv->fs;
g_object_get (priv->chooser, "action", &info->action, NULL);
info->file = g_object_ref (file);
if (priv->dnd_select_folder_cancellable)
g_cancellable_cancel (priv->dnd_select_folder_cancellable);
priv->dnd_select_folder_cancellable =
_gtk_file_system_get_info (priv->fs, info->file,
"standard::type",
dnd_select_folder_get_info_cb, info);
return TRUE;
}
static void
gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
{
@ -441,8 +545,7 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button)
GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
GtkWidget *box;
GtkWidget *icon;
GdkContentFormatsBuilder *builder;
GtkDropTarget *dest;
GtkDropTarget *target;
priv->button = gtk_button_new ();
g_signal_connect (priv->button, "clicked", G_CALLBACK (button_clicked_cb), button);
@ -494,13 +597,9 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button)
NULL, NULL);
/* DnD */
builder = gdk_content_formats_builder_new ();
gdk_content_formats_builder_add_gtype (builder, G_TYPE_STRING);
gdk_content_formats_builder_add_gtype (builder, GDK_TYPE_FILE_LIST);
dest = gtk_drop_target_new (gdk_content_formats_builder_free_to_formats (builder),
GDK_ACTION_COPY);
g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_file_chooser_button_drag_drop), button);
gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (dest));
target = gtk_drop_target_new (G_TYPE_FILE, GDK_ACTION_COPY);
g_signal_connect (target, "drop", G_CALLBACK (gtk_file_chooser_button_drop), button);
gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (target));
}
@ -1045,169 +1144,6 @@ gtk_file_chooser_button_destroy (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->destroy (widget);
}
struct DndSelectFolderData
{
GtkFileSystem *file_system;
GtkFileChooserButton *button;
GtkFileChooserAction action;
GFile *file;
gchar **uris;
guint i;
gboolean selected;
};
static void
dnd_select_folder_get_info_cb (GCancellable *cancellable,
GFileInfo *info,
const GError *error,
gpointer user_data)
{
struct DndSelectFolderData *data = user_data;
GtkFileChooserButton *button = data->button;
GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
gboolean cancelled = g_cancellable_is_cancelled (cancellable);
if (cancellable != priv->dnd_select_folder_cancellable)
{
g_object_unref (data->button);
g_object_unref (data->file);
g_strfreev (data->uris);
g_free (data);
g_object_unref (cancellable);
return;
}
priv->dnd_select_folder_cancellable = NULL;
if (!cancelled && !error && info != NULL)
{
gboolean is_folder;
is_folder = _gtk_file_info_consider_as_directory (info);
data->selected =
(((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && is_folder) ||
(data->action == GTK_FILE_CHOOSER_ACTION_OPEN && !is_folder)) &&
gtk_file_chooser_select_file (GTK_FILE_CHOOSER (data->button), data->file, NULL));
}
else
data->selected = FALSE;
if (data->selected || data->uris[++data->i] == NULL)
{
g_signal_emit (data->button, file_chooser_button_signals[FILE_SET], 0);
g_object_unref (data->button);
g_object_unref (data->file);
g_strfreev (data->uris);
g_free (data);
g_object_unref (cancellable);
return;
}
if (data->file)
g_object_unref (data->file);
data->file = g_file_new_for_uri (data->uris[data->i]);
priv->dnd_select_folder_cancellable =
_gtk_file_system_get_info (data->file_system, data->file,
"standard::type",
dnd_select_folder_get_info_cb, user_data);
g_object_unref (cancellable);
}
static void
dnd_select_file (GtkFileChooserButton *button,
GFile *file)
{
GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
struct DndSelectFolderData *info;
info = g_new0 (struct DndSelectFolderData, 1);
info->button = g_object_ref (button);
info->i = 0;
info->uris = g_new0 (char *, 2);
info->selected = FALSE;
info->file_system = priv->fs;
g_object_get (priv->chooser, "action", &info->action, NULL);
info->file = g_object_ref (file);
if (priv->dnd_select_folder_cancellable)
g_cancellable_cancel (priv->dnd_select_folder_cancellable);
priv->dnd_select_folder_cancellable =
_gtk_file_system_get_info (priv->fs, info->file,
"standard::type",
dnd_select_folder_get_info_cb, info);
}
static void
got_file (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (data);
GdkDrop *drop = GDK_DROP (source);
const GValue *value;
value = gdk_drop_read_value_finish (drop, result, NULL);
if (value)
{
GFile *file;
file = g_value_get_object (value);
dnd_select_file (button, file);
}
}
static void
got_text (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (data);
GdkDrop *drop = GDK_DROP (source);
char *str;
str = gdk_drop_read_text_finish (drop, result, NULL);
if (str)
{
GFile *file;
file = g_file_new_for_uri (str);
dnd_select_file (button, file);
g_object_unref (file);
}
}
static gboolean
gtk_file_chooser_button_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkWidget *button)
{
if (gdk_drop_has_value (drop, G_TYPE_FILE))
{
gdk_drop_read_value_async (drop, G_TYPE_FILE, G_PRIORITY_DEFAULT, NULL, got_file, button);
return TRUE;
}
else
{
gdk_drop_read_text_async (drop, NULL, got_text, button);
return TRUE;
}
return FALSE;
}
static void
gtk_file_chooser_button_show (GtkWidget *widget)
{

View File

@ -31,7 +31,7 @@
#include "gtkcomboboxtext.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkdragsource.h"
#include "gtkdragdest.h"
#include "gtkdroptarget.h"
#include "gtkentry.h"
#include "gtkfilechooserprivate.h"
#include "gtkfilechooserdialog.h"
@ -1839,25 +1839,17 @@ out:
g_object_unref (cancellable);
}
static void
file_list_drag_data_received_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
static gboolean
file_list_drag_drop_cb (GtkDropTarget *dest,
const GValue *value,
double x,
double y,
GtkFileChooserWidget *impl)
{
GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (user_data);
GtkFileChooserWidgetPrivate *priv = gtk_file_chooser_widget_get_instance_private (impl);
GdkDrop *drop = GDK_DROP (source);
GSList *files;
const GValue *value;
FileListDragData *data;
value = gdk_drop_read_value_finish (drop, result, NULL);
if (value == NULL)
{
gdk_drop_finish (drop, 0);
return;
}
files = g_value_get_boxed (value);
data = g_new0 (FileListDragData, 1);
@ -1873,30 +1865,6 @@ file_list_drag_data_received_cb (GObject *source,
file_list_drag_data_received_get_info_cb,
data);
gdk_drop_finish (drop, gdk_drop_get_actions (drop));
}
/* Don't do anything with the drag_drop signal */
static gboolean
file_list_drag_drop_cb (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkFileChooserWidget *impl)
{
gdk_drop_read_value_async (drop, GDK_TYPE_FILE_LIST, G_PRIORITY_DEFAULT, NULL, file_list_drag_data_received_cb, impl);
return TRUE;
}
/* Disable the normal tree drag motion handler, it makes it look like you're
dropping the dragged item onto a tree item */
static gboolean
file_list_drag_accept_cb (GtkDropTarget *dest,
GdkDrop *drop,
GtkFileChooserWidget *impl)
{
g_signal_stop_emission_by_name (dest, "accept");
return TRUE;
}
@ -7954,7 +7922,7 @@ post_process_ui (GtkFileChooserWidget *impl)
GtkCellRenderer *cell;
GList *cells;
GFile *file;
GtkDropTarget *dest;
GtkDropTarget *target;
/* Setup file list treeview */
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
@ -7966,11 +7934,9 @@ post_process_ui (GtkFileChooserWidget *impl)
gdk_content_formats_new_for_gtype (GDK_TYPE_FILE_LIST),
GDK_ACTION_COPY | GDK_ACTION_MOVE);
dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_FILE_LIST),
GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (dest, "accept", G_CALLBACK (file_list_drag_accept_cb), impl);
g_signal_connect (dest, "drag-drop", G_CALLBACK (file_list_drag_drop_cb), impl);
gtk_widget_add_controller (priv->browse_files_tree_view, GTK_EVENT_CONTROLLER (dest));
target = gtk_drop_target_new (GDK_TYPE_FILE_LIST, GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (target, "drop", G_CALLBACK (file_list_drag_drop_cb), impl);
gtk_widget_add_controller (priv->browse_files_tree_view, GTK_EVENT_CONTROLLER (target));
/* File browser treemodel columns are shared between GtkFileChooser implementations,
* so we don't set cell renderer attributes in GtkBuilder, but rather keep that

View File

@ -30,7 +30,6 @@
#include "gtkcellrenderertext.h"
#include "gtkcombobox.h"
#include "gtkcssnodeprivate.h"
#include "gtkdragdest.h"
#include "gtkdragsource.h"
#include "gtkentry.h"
#include "gtkintl.h"
@ -48,7 +47,6 @@
#include "gtkwindow.h"
#include "gtkeventcontrollerkey.h"
#include "gtkdragsource.h"
#include "gtkdragdest.h"
#include "gtkdragicon.h"
#include "gtknative.h"
@ -287,19 +285,19 @@ static GdkContentProvider * gtk_icon_view_drag_data_get (GtkIco
GtkTreePath *source_row);
/* Target side drag signals */
static void gtk_icon_view_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkIconView *icon_view);
static void gtk_icon_view_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkIconView *icon_view);
static gboolean gtk_icon_view_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkIconView *icon_view);
static void gtk_icon_view_drag_leave (GtkDropTargetAsync *dest,
GdkDrop *drop,
GtkIconView *icon_view);
static GdkDragAction gtk_icon_view_drag_motion (GtkDropTargetAsync *dest,
GdkDrop *drop,
double x,
double y,
GtkIconView *icon_view);
static gboolean gtk_icon_view_drag_drop (GtkDropTargetAsync *dest,
GdkDrop *drop,
double x,
double y,
GtkIconView *icon_view);
static void gtk_icon_view_drag_data_received (GObject *source,
GAsyncResult *result,
gpointer data);
@ -5828,12 +5826,12 @@ drag_scroll_timeout (gpointer data)
}
static gboolean
set_destination (GtkIconView *icon_view,
GtkDropTarget *dest,
gint x,
gint y,
GdkDragAction *suggested_action,
GType *target)
set_destination (GtkIconView *icon_view,
GtkDropTargetAsync *dest,
gint x,
gint y,
GdkDragAction *suggested_action,
GType *target)
{
GtkWidget *widget;
GtkTreePath *path = NULL;
@ -5863,7 +5861,7 @@ set_destination (GtkIconView *icon_view,
return FALSE; /* no longer a drop site */
}
formats = gtk_drop_target_get_formats (dest);
formats = gtk_drop_target_async_get_formats (dest);
*target = gdk_content_formats_match_gtype (formats, formats);
if (*target == G_TYPE_INVALID)
return FALSE;
@ -6129,9 +6127,9 @@ gtk_icon_view_dnd_finished_cb (GdkDrag *drag,
/* Target side drag signals */
static void
gtk_icon_view_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkIconView *icon_view)
gtk_icon_view_drag_leave (GtkDropTargetAsync *dest,
GdkDrop *drop,
GtkIconView *icon_view)
{
/* unset any highlight row */
gtk_icon_view_set_drag_dest_item (icon_view,
@ -6141,24 +6139,22 @@ gtk_icon_view_drag_leave (GtkDropTarget *dest,
remove_scroll_timeout (icon_view);
}
static void
gtk_icon_view_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkIconView *icon_view)
static GdkDragAction
gtk_icon_view_drag_motion (GtkDropTargetAsync *dest,
GdkDrop *drop,
double x,
double y,
GtkIconView *icon_view)
{
GtkTreePath *path = NULL;
GtkIconViewDropPosition pos;
GdkDragAction suggested_action = 0;
GType target;
gboolean empty;
GdkDragAction result;
if (!set_destination (icon_view, dest, x, y, &suggested_action, &target))
{
gdk_drop_status (drop, 0);
return;
}
return 0;
gtk_icon_view_get_drag_dest_item (icon_view, &path, &pos);
@ -6168,7 +6164,7 @@ gtk_icon_view_drag_motion (GtkDropTarget *dest,
if (path == NULL && !empty)
{
/* Can't drop here. */
gdk_drop_status (drop, 0);
result = 0;
}
else
{
@ -6189,20 +6185,22 @@ gtk_icon_view_drag_motion (GtkDropTarget *dest,
else
{
set_status_pending (drop, 0);
gdk_drop_status (drop, suggested_action);
}
result = suggested_action;
}
if (path)
gtk_tree_path_free (path);
return result;
}
static gboolean
gtk_icon_view_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkIconView *icon_view)
gtk_icon_view_drag_drop (GtkDropTargetAsync *dest,
GdkDrop *drop,
double x,
double y,
GtkIconView *icon_view)
{
GtkTreePath *path;
GdkDragAction suggested_action = 0;
@ -6217,7 +6215,7 @@ gtk_icon_view_drag_drop (GtkDropTarget *dest,
if (!icon_view->priv->dest_set)
return FALSE;
if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag-drop"))
if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drop"))
return FALSE;
if (!set_destination (icon_view, dest, x, y, &suggested_action, &target))
@ -6324,8 +6322,6 @@ gtk_icon_view_drag_data_received (GObject *source,
suggested_action = 0;
}
gdk_drop_status (drop, suggested_action);
if (path)
gtk_tree_path_free (path);
@ -6407,10 +6403,11 @@ gtk_icon_view_enable_model_drag_dest (GtkIconView *icon_view,
g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
icon_view->priv->dest = gtk_drop_target_new (gdk_content_formats_ref (formats), actions);
icon_view->priv->dest = gtk_drop_target_async_new (gdk_content_formats_ref (formats), actions);
g_signal_connect (icon_view->priv->dest, "drag-leave", G_CALLBACK (gtk_icon_view_drag_leave), icon_view);
g_signal_connect (icon_view->priv->dest, "drag-enter", G_CALLBACK (gtk_icon_view_drag_motion), icon_view);
g_signal_connect (icon_view->priv->dest, "drag-motion", G_CALLBACK (gtk_icon_view_drag_motion), icon_view);
g_signal_connect (icon_view->priv->dest, "drag-drop", G_CALLBACK (gtk_icon_view_drag_drop), icon_view);
g_signal_connect (icon_view->priv->dest, "drop", G_CALLBACK (gtk_icon_view_drag_drop), icon_view);
gtk_widget_add_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest));
icon_view->priv->dest_actions = actions;

View File

@ -17,10 +17,10 @@
#include "gtk/gtkiconview.h"
#include "gtk/gtkcssnodeprivate.h"
#include "gtk/gtkdragdest.h"
#include "gtk/gtkgestureclick.h"
#include "gtk/gtkeventcontrollermotion.h"
#include "gtk/gtkdragsource.h"
#include "gtk/gtkdroptargetasync.h"
#include "gtk/gtkgestureclick.h"
#ifndef __GTK_ICON_VIEW_PRIVATE_H__
#define __GTK_ICON_VIEW_PRIVATE_H__
@ -135,7 +135,7 @@ struct _GtkIconViewPrivate
gint press_start_y;
GdkContentFormats *source_formats;
GtkDropTarget *dest;
GtkDropTargetAsync *dest;
GtkCssNode *dndnode;
GdkDrag *drag;

View File

@ -25,7 +25,6 @@
#include "gtkbuildable.h"
#include "gtkcontainerprivate.h"
#include "gtkcssnodeprivate.h"
#include "gtkdragdest.h"
#include "gtkgestureclick.h"
#include "gtkintl.h"
#include "gtkmain.h"

View File

@ -117,7 +117,7 @@
#include "gtkaccelmapprivate.h"
#include "gtkbox.h"
#include "gtkdebug.h"
#include "gtkdragdestprivate.h"
#include "gtkdropprivate.h"
#include "gtkmain.h"
#include "gtkmediafileprivate.h"
#include "gtkmodulesprivate.h"
@ -1550,9 +1550,14 @@ handle_pointing_event (GdkEvent *event)
event, gdk_crossing_event_get_mode (event), NULL);
break;
case GDK_DRAG_LEAVE:
old_target = update_pointer_focus_state (toplevel, event, NULL);
gtk_synthesize_crossing_events (GTK_ROOT (toplevel), GTK_CROSSING_DROP, old_target, NULL,
event, GDK_CROSSING_NORMAL, gdk_drag_event_get_drop (event));
{
GdkDrop *drop = gdk_drag_event_get_drop (event);
old_target = update_pointer_focus_state (toplevel, event, NULL);
gtk_drop_begin_event (drop, GDK_DRAG_LEAVE);
gtk_synthesize_crossing_events (GTK_ROOT (toplevel), GTK_CROSSING_DROP, old_target, NULL,
event, GDK_CROSSING_NORMAL, drop);
gtk_drop_end_event (drop);
}
break;
case GDK_ENTER_NOTIFY:
if (gdk_crossing_event_get_mode (event) == GDK_CROSSING_GRAB ||
@ -1586,10 +1591,14 @@ handle_pointing_event (GdkEvent *event)
gtk_window_maybe_update_cursor (toplevel, NULL, device);
}
else if (type == GDK_DRAG_ENTER || type == GDK_DRAG_MOTION || type == GDK_DROP_START)
else if ((old_target != target) &&
(type == GDK_DRAG_ENTER || type == GDK_DRAG_MOTION || type == GDK_DROP_START))
{
GdkDrop *drop = gdk_drag_event_get_drop (event);
gtk_drop_begin_event (drop, type);
gtk_synthesize_crossing_events (GTK_ROOT (toplevel), GTK_CROSSING_DROP, old_target, target,
event, GDK_CROSSING_NORMAL, gdk_drag_event_get_drop (event));
gtk_drop_end_event (drop);
}
else if (type == GDK_TOUCH_BEGIN)
gtk_window_set_pointer_focus_grab (toplevel, device, sequence, target);
@ -1786,19 +1795,19 @@ gtk_main_do_event (GdkEvent *event)
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
case GDK_DRAG_ENTER:
case GDK_DRAG_LEAVE:
/* Crossing event propagation happens during picking */
break;
case GDK_DRAG_MOTION:
case GDK_DROP_START:
if (gtk_propagate_event (target_widget, event))
break;
G_GNUC_FALLTHROUGH;
case GDK_DRAG_ENTER:
case GDK_DRAG_LEAVE:
/* Crossing event propagation happens during picking */
gtk_drag_dest_handle_event (target_widget, event);
{
GdkDrop *drop = gdk_drag_event_get_drop (event);
gtk_drop_begin_event (drop, gdk_event_get_event_type (event));
gtk_propagate_event (target_widget, event);
gtk_drop_end_event (drop);
}
break;
case GDK_EVENT_LAST:

View File

@ -33,7 +33,7 @@
#include "gtkbuildable.h"
#include "gtkbutton.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtkdragdest.h"
#include "gtkdroptarget.h"
#include "gtkdragicon.h"
#include "gtkdropcontrollermotion.h"
#include "gtkeventcontrollermotion.h"
@ -793,14 +793,15 @@ static void gtk_notebook_dnd_finished_cb (GdkDrag *drag,
static void gtk_notebook_drag_cancel_cb (GdkDrag *drag,
GdkDragCancelReason reason,
GtkWidget *widget);
static void gtk_notebook_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y);
static GdkDragAction gtk_notebook_drag_motion(GtkDropTarget *dest,
double x,
double y,
GtkNotebook *notebook);
static gboolean gtk_notebook_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y);
const GValue *value,
double x,
double y,
GtkNotebook *notebook);
/*** GtkContainer Methods ***/
static void gtk_notebook_add (GtkContainer *container,
@ -1430,9 +1431,10 @@ gtk_notebook_init (GtkNotebook *notebook)
gtk_widget_set_vexpand (priv->stack_widget, TRUE);
gtk_widget_set_parent (priv->stack_widget, GTK_WIDGET (notebook));
dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_NOTEBOOK_PAGE), GDK_ACTION_MOVE);
g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_notebook_drag_motion), NULL);
g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_notebook_drag_drop), NULL);
dest = gtk_drop_target_new (GTK_TYPE_NOTEBOOK_PAGE, GDK_ACTION_MOVE);
gtk_drop_target_set_preload (dest, TRUE);
g_signal_connect (dest, "motion", G_CALLBACK (gtk_notebook_drag_motion), notebook);
g_signal_connect (dest, "drop", G_CALLBACK (gtk_notebook_drag_drop), notebook);
gtk_widget_add_controller (GTK_WIDGET (priv->tabs_widget), GTK_EVENT_CONTROLLER (dest));
gesture = gtk_gesture_click_new ();
@ -3294,112 +3296,78 @@ gtk_notebook_switch_page_timeout (gpointer data)
return FALSE;
}
static void
gtk_notebook_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y)
static gboolean
gtk_notebook_can_drag_from (GtkNotebook *self,
GtkNotebook *other,
GtkNotebookPage *page)
{
/* always allow dragging inside self */
if (self == other)
return TRUE;
/* if the groups don't match, fail */
if (self->priv->group == 0 ||
self->priv->group != other->priv->group)
return FALSE;
/* Check that the dragged page is not a parent of the notebook
* being dragged into */
if (GTK_WIDGET (self) == page->child ||
gtk_widget_is_ancestor (GTK_WIDGET (self), GTK_WIDGET (page->child)) ||
GTK_WIDGET (self) == page->tab_label ||
gtk_widget_is_ancestor (GTK_WIDGET (self), GTK_WIDGET (page->tab_label)))
return FALSE;
return TRUE;
}
static GdkDragAction
gtk_notebook_drag_motion (GtkDropTarget *dest,
double x,
double y,
GtkNotebook *notebook)
{
GtkWidget *tabs = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
GtkWidget *widget = gtk_widget_get_ancestor (tabs, GTK_TYPE_NOTEBOOK);
GtkNotebook *notebook = GTK_NOTEBOOK (widget);
GtkNotebookPrivate *priv = notebook->priv;
GdkContentFormats *formats;
GdkDrag *drag = gdk_drop_get_drag (gtk_drop_target_get_drop (dest));
GtkNotebook *source;
priv->mouse_x = x;
priv->mouse_y = y;
formats = gtk_drop_target_get_formats (dest);
if (gdk_content_formats_contain_gtype (formats, GTK_TYPE_NOTEBOOK_PAGE))
{
GQuark group, source_group;
GtkWidget *source_child;
GdkDrag *drag = gdk_drop_get_drag (drop);
if (!drag)
return 0;
if (!drag)
{
gdk_drop_status (drop, 0);
}
else
{
GtkNotebook *source = GTK_NOTEBOOK (g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin"));
source = GTK_NOTEBOOK (g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin"));
g_assert (source->priv->cur_page != NULL);
g_assert (source->priv->cur_page != NULL);
source_child = source->priv->cur_page->child;
if (!gtk_notebook_can_drag_from (notebook, source, source->priv->cur_page))
return 0;
group = notebook->priv->group;
source_group = source->priv->group;
if (group != 0 && group == source_group &&
!(widget == source_child ||
gtk_widget_is_ancestor (widget, source_child)))
{
gdk_drop_status (drop, GDK_ACTION_MOVE);
}
else
{
/* it's a tab, but doesn't share
* ID with this notebook */
gdk_drop_status (drop, 0);
}
}
}
}
static void
got_page (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkNotebook *notebook = GTK_NOTEBOOK (data);
GdkDrop *drop = GDK_DROP (source);
GdkDrag *drag = gdk_drop_get_drag (drop);
GtkWidget *source_widget;
const GValue *value;
source_widget = GTK_WIDGET (drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL);
value = gdk_drop_read_value_finish (drop, result, NULL);
if (value)
{
GtkNotebookPage *page = g_value_get_object (value);
do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, page->child);
gdk_drop_finish (drop, GDK_ACTION_MOVE);
}
else
gdk_drop_finish (drop, 0);
return GDK_ACTION_MOVE;
}
static gboolean
gtk_notebook_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y)
const GValue *value,
double x,
double y,
GtkNotebook *self)
{
GtkWidget *tabs = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
GtkWidget *widget = gtk_widget_get_ancestor (tabs, GTK_TYPE_NOTEBOOK);
GtkNotebook *notebook = GTK_NOTEBOOK (widget);
GdkDrag *drag = gdk_drop_get_drag (drop);
GtkWidget *source_widget;
GdkDrag *drag = gdk_drop_get_drag (gtk_drop_target_get_drop (dest));
GtkNotebook *source;
GtkNotebookPage *page = g_value_get_object (value);
source_widget = GTK_WIDGET (drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL);
source = drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL;
if (GTK_IS_NOTEBOOK (source_widget) &&
(gdk_drop_get_actions (drop) & GDK_ACTION_MOVE))
{
notebook->priv->mouse_x = x;
notebook->priv->mouse_y = y;
if (!gtk_notebook_can_drag_from (self, source, source->priv->cur_page))
return FALSE;
gdk_drop_read_value_async (drop, GTK_TYPE_NOTEBOOK_PAGE, G_PRIORITY_DEFAULT, NULL, got_page, notebook);
self->priv->mouse_x = x;
self->priv->mouse_y = y;
return TRUE;
}
do_detach_tab (source, self, page->child);
gdk_drop_finish (drop, 0);
return FALSE;
return TRUE;
}
/**

View File

@ -50,7 +50,7 @@
#include "gtklabel.h"
#include "gtkbutton.h"
#include "gtklistbox.h"
#include "gtkdragdest.h"
#include "gtkdroptarget.h"
#include "gtkseparator.h"
#include "gtkentry.h"
#include "gtkgesturelongpress.h"
@ -150,8 +150,6 @@ struct _GtkPlacesSidebar {
GtkWidget *trash_row;
/* DND */
GSList *drag_list; /* list of GFile */
gint drag_data_info;
gboolean dragging_over;
GtkWidget *drag_row;
gint drag_row_height;
@ -175,8 +173,6 @@ struct _GtkPlacesSidebar {
GtkPlacesOpenFlags open_flags;
guint mounting : 1;
guint drag_data_received : 1;
guint drop_occurred : 1;
guint show_recent_set : 1;
guint show_recent : 1;
guint show_desktop_set : 1;
@ -300,13 +296,6 @@ static GMountOperation * get_mount_operation (GtkPlacesSidebar *sidebar);
static GMountOperation * get_unmount_operation (GtkPlacesSidebar *sidebar);
/* Identifiers for target types */
enum {
DND_UNKNOWN,
DND_GTK_SIDEBAR_ROW,
DND_TEXT_URI_LIST
};
G_DEFINE_TYPE (GtkPlacesSidebar, gtk_places_sidebar, GTK_TYPE_WIDGET);
static void
@ -380,18 +369,6 @@ emit_drag_action_requested (GtkPlacesSidebar *sidebar,
return ret_action;
}
static GdkDragAction
emit_drag_action_ask (GtkPlacesSidebar *sidebar,
GdkDragAction actions)
{
GdkDragAction ret_action = 0;
g_signal_emit (sidebar, places_sidebar_signals[DRAG_ACTION_ASK], 0,
actions, &ret_action);
return ret_action;
}
static void
emit_drag_perform_drop (GtkPlacesSidebar *sidebar,
GFile *dest_file,
@ -1513,7 +1490,8 @@ update_places (GtkPlacesSidebar *sidebar)
static gboolean
check_valid_drop_target (GtkPlacesSidebar *sidebar,
GtkSidebarRow *row)
GtkSidebarRow *row,
const GValue *value)
{
GtkPlacesSidebarPlaceType place_type;
GtkPlacesSidebarSectionType section_type;
@ -1522,6 +1500,8 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar,
GFile *dest_file;
gint drag_action;
g_return_val_if_fail (value != NULL, TRUE);
if (row == NULL)
return FALSE;
@ -1560,19 +1540,18 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar,
}
/* Dragging a bookmark? */
if (sidebar->drag_data_received &&
sidebar->drag_data_info == DND_GTK_SIDEBAR_ROW)
if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW))
{
/* Don't allow reordering bookmarks into non-bookmark areas */
valid = section_type == SECTION_BOOKMARKS;
}
else
else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
{
/* Dragging a file */
if (uri != NULL)
{
dest_file = g_file_new_for_uri (uri);
drag_action = emit_drag_action_requested (sidebar, dest_file, sidebar->drag_list);
drag_action = emit_drag_action_requested (sidebar, dest_file, g_value_get_boxed (value));
valid = drag_action > 0;
g_object_unref (dest_file);
@ -1582,6 +1561,11 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar,
valid = FALSE;
}
}
else
{
g_assert_not_reached ();
valid = TRUE;
}
g_free (uri);
return valid;
@ -1589,7 +1573,7 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar,
static void
update_possible_drop_targets (GtkPlacesSidebar *sidebar,
gboolean dragging)
const GValue *value)
{
GList *rows;
GList *l;
@ -1599,55 +1583,18 @@ update_possible_drop_targets (GtkPlacesSidebar *sidebar,
for (l = rows; l != NULL; l = l->next)
{
sensitive = !dragging || check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (l->data));
sensitive = value == NULL || check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (l->data), value);
gtk_widget_set_sensitive (GTK_WIDGET (l->data), sensitive);
}
g_list_free (rows);
}
static void drag_data_received_callback (GObject *source,
GAsyncResult *result,
gpointer user_data);
static gboolean
get_drag_data (GtkPlacesSidebar *self,
GtkDropTarget *dest,
GdkDrop *drop,
GtkListBoxRow *row)
{
GdkContentFormats *formats = gdk_drop_get_formats (drop);
if (row)
g_object_set_data_full (G_OBJECT (drop), "places-sidebar-row", g_object_ref (row), g_object_unref);
gdk_drop_read_value_async (drop,
gdk_content_formats_match_gtype (formats, formats),
G_PRIORITY_DEFAULT,
NULL,
drag_data_received_callback,
self->list_box);
return TRUE;
}
static void
free_drag_data (GtkPlacesSidebar *sidebar)
start_drop_feedback (GtkPlacesSidebar *sidebar,
const GValue *value)
{
sidebar->drag_data_received = FALSE;
if (sidebar->drag_list)
{
g_slist_free_full (sidebar->drag_list, g_object_unref);
sidebar->drag_list = NULL;
}
}
static void
start_drop_feedback (GtkPlacesSidebar *sidebar)
{
if (sidebar->drag_data_received &&
sidebar->drag_data_info != DND_GTK_SIDEBAR_ROW)
if (value && !G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW))
{
gtk_sidebar_row_reveal (GTK_SIDEBAR_ROW (sidebar->new_bookmark_row));
/* If the state is permanent, don't change it. The application controls it. */
@ -1655,15 +1602,13 @@ start_drop_feedback (GtkPlacesSidebar *sidebar)
sidebar->drop_state = DROP_STATE_NEW_BOOKMARK_ARMED;
}
update_possible_drop_targets (sidebar, TRUE);
update_possible_drop_targets (sidebar, value);
}
static void
stop_drop_feedback (GtkPlacesSidebar *sidebar)
{
update_possible_drop_targets (sidebar, FALSE);
free_drag_data (sidebar);
update_possible_drop_targets (sidebar, NULL);
if (sidebar->drop_state != DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT &&
sidebar->new_bookmark_row != NULL)
@ -1686,7 +1631,6 @@ stop_drop_feedback (GtkPlacesSidebar *sidebar)
}
sidebar->dragging_over = FALSE;
sidebar->drag_data_info = DND_UNKNOWN;
}
static GtkWidget *
@ -1695,20 +1639,19 @@ create_placeholder_row (GtkPlacesSidebar *sidebar)
return g_object_new (GTK_TYPE_SIDEBAR_ROW, "placeholder", TRUE, NULL);
}
static void
drag_motion_callback (GtkDropTarget *dest,
GdkDrop *drop,
gint x,
gint y,
gpointer user_data)
static GdkDragAction
drag_motion_callback (GtkDropTarget *target,
double x,
double y,
GtkPlacesSidebar *sidebar)
{
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
gint action;
GdkDragAction action;
GtkListBoxRow *row;
GtkPlacesSidebarPlaceType place_type;
gchar *drop_target_uri = NULL;
gint row_index;
gint row_placeholder_index;
const GValue *value;
sidebar->dragging_over = TRUE;
action = 0;
@ -1716,17 +1659,16 @@ drag_motion_callback (GtkDropTarget *dest,
gtk_list_box_drag_unhighlight_row (GTK_LIST_BOX (sidebar->list_box));
/* Nothing to do if no drag data */
if (!sidebar->drag_data_received &&
!get_drag_data (sidebar, dest, drop, row))
/* Nothing to do if no value yet */
value = gtk_drop_target_get_value (target);
if (value == NULL)
goto out;
/* Nothing to do if the target is not valid drop destination */
if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (row)))
if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (row), value))
goto out;
if (sidebar->drag_data_received &&
sidebar->drag_data_info == DND_GTK_SIDEBAR_ROW)
if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW))
{
/* Dragging bookmarks always moves them to another position in the bookmarks list */
action = GDK_ACTION_MOVE;
@ -1783,7 +1725,7 @@ drag_motion_callback (GtkDropTarget *dest,
gtk_list_box_prepend (GTK_LIST_BOX (sidebar->list_box),
sidebar->row_placeholder);
}
else
else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
{
gtk_list_box_drag_highlight_row (GTK_LIST_BOX (sidebar->list_box), row);
@ -1795,32 +1737,33 @@ drag_motion_callback (GtkDropTarget *dest,
* file move/copy operation itself, or if we should only try to
* create bookmarks out of the dragged URIs.
*/
if (sidebar->drag_list != NULL)
if (place_type == PLACES_DROP_FEEDBACK)
{
if (place_type == PLACES_DROP_FEEDBACK)
action = GDK_ACTION_COPY;
}
else
{
/* uri may be NULL for unmounted volumes, for example, so we don't allow drops there */
if (drop_target_uri != NULL)
{
action = GDK_ACTION_COPY;
}
else
{
/* uri may be NULL for unmounted volumes, for example, so we don't allow drops there */
if (drop_target_uri != NULL)
{
GFile *dest_file = g_file_new_for_uri (drop_target_uri);
GFile *dest_file = g_file_new_for_uri (drop_target_uri);
action = emit_drag_action_requested (sidebar, dest_file, sidebar->drag_list);
action = emit_drag_action_requested (sidebar, dest_file, g_value_get_boxed (value));
g_object_unref (dest_file);
}
g_object_unref (dest_file);
}
}
g_free (drop_target_uri);
}
else
{
g_assert_not_reached ();
}
out:
start_drop_feedback (sidebar);
gdk_drop_status (drop, action);
start_drop_feedback (sidebar, value);
return action;
}
/* Reorders the bookmark to the specified position */
@ -1867,51 +1810,26 @@ drop_files_as_bookmarks (GtkPlacesSidebar *sidebar,
}
}
static void
drag_data_received_callback (GObject *source,
GAsyncResult *result,
gpointer user_data)
static gboolean
drag_drop_callback (GtkDropTarget *target,
const GValue *value,
double x,
double y,
GtkPlacesSidebar *sidebar)
{
GdkDrop *drop = GDK_DROP (source);
GtkWidget *list_box = user_data;
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (gtk_widget_get_ancestor (list_box, GTK_TYPE_PLACES_SIDEBAR));
gint target_order_index;
GtkPlacesSidebarPlaceType target_place_type;
GtkPlacesSidebarSectionType target_section_type;
gchar *target_uri;
GtkListBoxRow *target_row;
GdkDragAction real_action;
const GValue *value;
gboolean result;
value = gdk_drop_read_value_finish (drop, result, NULL);
if (!sidebar->drag_data_received)
{
if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
{
/* Free spurious drag data from previous drags if present */
if (sidebar->drag_list != NULL)
g_slist_free_full (sidebar->drag_list, g_object_unref);
sidebar->drag_list = g_slist_copy_deep (g_value_get_boxed (value), (GCopyFunc) g_object_ref, NULL);
sidebar->drag_data_info = DND_TEXT_URI_LIST;
}
else if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW))
{
sidebar->drag_list = NULL;
sidebar->drag_data_info = DND_GTK_SIDEBAR_ROW;
}
sidebar->drag_data_received = TRUE;
}
if (!sidebar->drop_occurred)
return;
target_row = g_object_get_data (G_OBJECT (drop), "places-sidebar-row");
target_row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (sidebar->list_box), y);
if (target_row == NULL)
return;
return FALSE;
if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (target_row)))
return;
if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (target_row), value))
return FALSE;
g_object_get (target_row,
"place-type", &target_place_type,
@ -1919,9 +1837,9 @@ drag_data_received_callback (GObject *source,
"order-index", &target_order_index,
"uri", &target_uri,
NULL);
real_action = 0;
result = FALSE;
if (sidebar->drag_data_info == DND_GTK_SIDEBAR_ROW)
if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW))
{
GtkWidget **source_row;
/* A bookmark got reordered */
@ -1934,41 +1852,37 @@ drag_data_received_callback (GObject *source,
g_object_get (sidebar->row_placeholder, "order-index", &target_order_index, NULL);
reorder_bookmarks (sidebar, GTK_SIDEBAR_ROW (*source_row), target_order_index);
real_action = GDK_ACTION_MOVE;
result = TRUE;
}
else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
{
/* Dropping URIs! */
if (target_place_type == PLACES_DROP_FEEDBACK)
{
drop_files_as_bookmarks (sidebar, g_value_get_boxed (value), target_order_index);
}
else
{
GFile *dest_file = g_file_new_for_uri (target_uri);
emit_drag_perform_drop (sidebar,
dest_file,
g_value_get_boxed (value),
gdk_drop_get_actions (gtk_drop_target_get_drop (target)));
g_object_unref (dest_file);
}
result = TRUE;
}
else
{
/* Dropping URIs! */
/* file transfer requested */
real_action = gdk_drop_get_actions (drop);
if (!gdk_drag_action_is_unique (real_action))
real_action = emit_drag_action_ask (sidebar, real_action);
if (real_action > 0)
{
if (target_place_type == PLACES_DROP_FEEDBACK)
{
drop_files_as_bookmarks (sidebar, sidebar->drag_list, target_order_index);
}
else
{
GFile *dest_file = g_file_new_for_uri (target_uri);
emit_drag_perform_drop (sidebar, dest_file, sidebar->drag_list, real_action);
g_object_unref (dest_file);
}
}
g_assert_not_reached ();
}
out:
sidebar->drop_occurred = FALSE;
g_object_set_data (G_OBJECT (drop), "places-sidebar-row", NULL);
gdk_drop_finish (drop, real_action);
stop_drop_feedback (sidebar);
g_free (target_uri);
return result;
}
static void
@ -2010,28 +1924,7 @@ drag_leave_callback (GtkDropTarget *dest,
sidebar->drop_state = DROP_STATE_NORMAL;
}
sidebar->drag_data_received = FALSE;
sidebar->dragging_over = FALSE;
sidebar->drag_data_info = DND_UNKNOWN;
}
static gboolean
drag_drop_callback (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
gpointer user_data)
{
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
gboolean retval = FALSE;
GtkListBoxRow *row;
row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (sidebar->list_box), y);
sidebar->drop_occurred = TRUE;
retval = get_drag_data (sidebar, dest, drop, row);
return retval;
}
static void
@ -3842,11 +3735,10 @@ shell_shows_desktop_changed (GtkSettings *settings,
static void
gtk_places_sidebar_init (GtkPlacesSidebar *sidebar)
{
GtkDropTarget *dest;
GtkDropTarget *target;
gboolean show_desktop;
GtkEventController *controller;
GtkGesture *gesture;
GdkContentFormatsBuilder *builder;
sidebar->cancellable = g_cancellable_new ();
@ -3901,20 +3793,18 @@ gtk_places_sidebar_init (GtkPlacesSidebar *sidebar)
gtk_widget_add_controller (GTK_WIDGET (sidebar), GTK_EVENT_CONTROLLER (gesture));
/* DND support */
builder = gdk_content_formats_builder_new ();
gdk_content_formats_builder_add_gtype (builder, GTK_TYPE_SIDEBAR_ROW);
gdk_content_formats_builder_add_gtype (builder, GDK_TYPE_FILE_LIST);
dest = gtk_drop_target_new (gdk_content_formats_builder_free_to_formats (builder),
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
g_signal_connect (dest, "drag-motion", G_CALLBACK (drag_motion_callback), sidebar);
g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop_callback), sidebar);
g_signal_connect (dest, "drag-leave", G_CALLBACK (drag_leave_callback), sidebar);
gtk_widget_add_controller (sidebar->list_box, GTK_EVENT_CONTROLLER (dest));
target = gtk_drop_target_new (G_TYPE_INVALID, GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
gtk_drop_target_set_preload (target, TRUE);
gtk_drop_target_set_gtypes (target, (GType[2]) { GTK_TYPE_SIDEBAR_ROW, GDK_TYPE_FILE_LIST }, 2);
g_signal_connect (target, "enter", G_CALLBACK (drag_motion_callback), sidebar);
g_signal_connect (target, "motion", G_CALLBACK (drag_motion_callback), sidebar);
g_signal_connect (target, "drop", G_CALLBACK (drag_drop_callback), sidebar);
g_signal_connect (target, "leave", G_CALLBACK (drag_leave_callback), sidebar);
gtk_widget_add_controller (sidebar->list_box, GTK_EVENT_CONTROLLER (target));
sidebar->drag_row = NULL;
sidebar->row_placeholder = NULL;
sidebar->dragging_over = FALSE;
sidebar->drag_data_info = DND_UNKNOWN;
gtk_container_add (GTK_CONTAINER (sidebar->swin), sidebar->list_box);
@ -4071,8 +3961,6 @@ gtk_places_sidebar_dispose (GObject *object)
sidebar->cancellable = NULL;
}
free_drag_data (sidebar);
if (sidebar->bookmarks_manager != NULL)
{
_gtk_bookmarks_manager_free (sidebar->bookmarks_manager);
@ -5068,7 +4956,7 @@ gtk_places_sidebar_set_drop_targets_visible (GtkPlacesSidebar *sidebar,
if (visible)
{
sidebar->drop_state = DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT;
start_drop_feedback (sidebar);
start_drop_feedback (sidebar, NULL);
}
else
{

View File

@ -30,6 +30,9 @@
#include "gtkbutton.h"
#include "gtkcssnodeprivate.h"
#include "gtkdebug.h"
#include "gtkdragicon.h"
#include "gtkdragsource.h"
#include "gtkdroptarget.h"
#include "gtkeditable.h"
#include "gtkemojichooser.h"
#include "gtkemojicompletion.h"
@ -65,9 +68,6 @@
#include "gtkwindow.h"
#include "gtknative.h"
#include "gtkactionmuxerprivate.h"
#include "gtkdragsource.h"
#include "gtkdragdest.h"
#include "gtkdragicon.h"
#include "a11y/gtktextaccessible.h"
@ -190,7 +190,6 @@ struct _GtkTextPrivate
int dnd_position; /* In chars, -1 == no DND cursor */
int drag_start_x;
int drag_start_y;
int drop_position; /* where the drop should happen */
int insert_pos;
int selection_bound;
int scroll_offset;
@ -336,20 +335,18 @@ static void gtk_text_state_flags_changed (GtkWidget *widget,
static void gtk_text_root (GtkWidget *widget);
static gboolean gtk_text_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
const GValue *value,
double x,
double y,
GtkText *text);
static gboolean gtk_text_drag_accept (GtkDropTarget *dest,
GdkDrop *drop,
GtkText *self);
static void gtk_text_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
static GdkDragAction gtk_text_drag_motion (GtkDropTarget *dest,
double x,
double y,
GtkText *text);
static void gtk_text_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkText *text);
@ -1712,7 +1709,7 @@ gtk_text_init (GtkText *self)
GtkGesture *gesture;
GtkEventController *controller;
int i;
GtkDropTarget *dest;
GtkDropTarget *target;
gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
@ -1734,13 +1731,13 @@ gtk_text_init (GtkText *self)
priv->selection_content = g_object_new (GTK_TYPE_TEXT_CONTENT, NULL);
GTK_TEXT_CONTENT (priv->selection_content)->self = self;
dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (G_TYPE_STRING),
GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (dest, "accept", G_CALLBACK (gtk_text_drag_accept), self);
g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_text_drag_motion), self);
g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_text_drag_leave), self);
g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_text_drag_drop), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (dest));
target = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (target, "accept", G_CALLBACK (gtk_text_drag_accept), self);
g_signal_connect (target, "enter", G_CALLBACK (gtk_text_drag_motion), self);
g_signal_connect (target, "motion", G_CALLBACK (gtk_text_drag_motion), self);
g_signal_connect (target, "leave", G_CALLBACK (gtk_text_drag_leave), self);
g_signal_connect (target, "drop", G_CALLBACK (gtk_text_drag_drop), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (target));
/* This object is completely private. No external entity can gain a reference
* to it; so we create it here and destroy it in finalize().
@ -6120,8 +6117,7 @@ gtk_text_selection_bubble_popup_set (GtkText *self)
static void
gtk_text_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkText *self)
GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
GtkWidget *widget = GTK_WIDGET (self);
@ -6130,94 +6126,45 @@ gtk_text_drag_leave (GtkDropTarget *dest,
gtk_widget_queue_draw (widget);
}
static GdkDragAction
gtk_text_get_action (GtkText *self,
GdkDrop *drop)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
GdkDrag *drag = gdk_drop_get_drag (drop);
GdkDragAction actions;
actions = gdk_drop_get_actions (drop);
if (drag == priv->drag &&
actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
if (actions & GDK_ACTION_COPY)
return GDK_ACTION_COPY;
if (actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
return 0;
}
static void
got_text (GObject *source,
GAsyncResult *result,
gpointer data)
{
GdkDrop *drop = GDK_DROP (source);
GtkText *self = GTK_TEXT (data);
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
char *str;
GdkDragAction action;
str = gdk_drop_read_text_finish (drop, result, NULL);
action = gtk_text_get_action (self, drop);
if (action && str)
{
int length = -1;
int pos;
if (priv->truncate_multiline)
length = truncate_multiline (str);
if (priv->selection_bound == priv->current_pos ||
priv->drop_position < priv->selection_bound ||
priv->drop_position > priv->current_pos)
{
gtk_text_insert_text (self, str, length, &priv->drop_position);
}
else
{
/* Replacing selection */
begin_change (self);
gtk_text_delete_selection (self);
pos = MIN (priv->selection_bound, priv->current_pos);
gtk_text_insert_text (self, str, length, &pos);
end_change (self);
}
gdk_drop_finish (drop, action);
}
else
{
/* Drag and drop didn't happen! */
gdk_drop_finish (drop, 0);
}
g_free (str);
}
static gboolean
gtk_text_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
const GValue *value,
double x,
double y,
GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
int drop_position;
int length;
const char *str;
if (priv->editable && gdk_drop_has_value (drop, G_TYPE_STRING))
if (!priv->editable)
return FALSE;
drop_position = gtk_text_find_position (self, x + priv->scroll_offset);
str = g_value_get_string (value);
if (priv->truncate_multiline)
length = truncate_multiline (str);
else
length = -1;
if (priv->selection_bound == priv->current_pos ||
drop_position < priv->selection_bound ||
drop_position > priv->current_pos)
{
priv->drop_position = gtk_text_find_position (self, x + priv->scroll_offset);
gdk_drop_read_text_async (drop, NULL, got_text, self);
gtk_text_insert_text (self, str, length, &drop_position);
}
else
gdk_drop_finish (drop, 0);
{
int pos;
/* Replacing selection */
begin_change (self);
gtk_text_delete_selection (self);
pos = MIN (priv->selection_bound, priv->current_pos);
gtk_text_insert_text (self, str, length, &pos);
end_change (self);
}
return TRUE;
}
@ -6238,40 +6185,42 @@ gtk_text_drag_accept (GtkDropTarget *dest,
return gdk_content_formats_match (gtk_drop_target_get_formats (dest), gdk_drop_get_formats (drop));
}
static void
gtk_text_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkText *self)
static GdkDragAction
gtk_text_drag_motion (GtkDropTarget *target,
double x,
double y,
GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
int new_position, old_position;
if (!priv->editable)
{
gtk_drop_target_reject (target);
return 0;
}
old_position = priv->dnd_position;
new_position = gtk_text_find_position (self, x + priv->scroll_offset);
if (priv->editable)
if (priv->selection_bound == priv->current_pos ||
new_position < priv->selection_bound ||
new_position > priv->current_pos)
{
if (priv->selection_bound == priv->current_pos ||
new_position < priv->selection_bound ||
new_position > priv->current_pos)
{
priv->dnd_position = new_position;
}
else
{
priv->dnd_position = -1;
}
priv->dnd_position = new_position;
}
else
{
/* Entry not editable, or no text */
priv->dnd_position = -1;
}
if (priv->dnd_position != old_position)
gtk_widget_queue_draw (GTK_WIDGET (self));
if (priv->drag)
return GDK_ACTION_MOVE;
else
return GDK_ACTION_COPY;
}
/* We display the cursor when

View File

@ -423,17 +423,16 @@ static GtkTextBuffer* gtk_text_view_create_buffer (GtkTextView *text_view);
/* Target side drag signals */
static void gtk_text_view_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkTextView *text_view);
static gboolean gtk_text_view_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
static GdkDragAction
gtk_text_view_drag_motion (GtkDropTarget *dest,
double x,
double y,
GtkTextView *text_view);
static gboolean gtk_text_view_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
const GValue *value,
double x,
double y,
GtkTextView *text_view);
static gboolean gtk_text_view_popup_menu (GtkWidget *widget);
@ -1752,10 +1751,11 @@ gtk_text_view_init (GtkTextView *text_view)
priv->scroll_after_paste = FALSE;
dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_TEXT_BUFFER), GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_text_view_drag_leave), text_view);
g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_text_view_drag_motion), text_view);
g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_text_view_drag_drop), text_view);
dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (dest, "enter", G_CALLBACK (gtk_text_view_drag_motion), text_view);
g_signal_connect (dest, "motion", G_CALLBACK (gtk_text_view_drag_motion), text_view);
g_signal_connect (dest, "leave", G_CALLBACK (gtk_text_view_drag_leave), text_view);
g_signal_connect (dest, "drop", G_CALLBACK (gtk_text_view_drag_drop), text_view);
gtk_widget_add_controller (GTK_WIDGET (text_view), GTK_EVENT_CONTROLLER (dest));
controller = gtk_drop_controller_motion_new ();
@ -7788,7 +7788,6 @@ gtk_text_view_start_selection_dnd (GtkTextView *text_view,
static void
gtk_text_view_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
@ -7796,11 +7795,10 @@ gtk_text_view_drag_leave (GtkDropTarget *dest,
gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
}
static gboolean
static GdkDragAction
gtk_text_view_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
double x,
double y,
GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
@ -7834,117 +7832,50 @@ gtk_text_view_drag_motion (GtkDropTarget *dest,
if (can_accept)
{
gtk_text_mark_set_visible (priv->dnd_mark, cursor_visible (text_view));
gdk_drop_status (drop, GDK_ACTION_COPY | GDK_ACTION_MOVE);
if (text_view->priv->drag)
return GDK_ACTION_MOVE;
else
return GDK_ACTION_COPY;
}
else
{
gdk_drop_status (drop, 0);
gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
return 0;
}
/* TRUE return means don't propagate the drag motion to parent
* widgets that may also be drop sites.
*/
return TRUE;
}
static GdkDragAction
gtk_text_view_get_action (GtkTextView *textview,
GdkDrop *drop)
static gboolean
gtk_text_view_drag_drop (GtkDropTarget *dest,
const GValue *value,
double x,
double y,
GtkTextView *text_view)
{
GdkDrag *drag = gdk_drop_get_drag (drop);
GdkDragAction actions;
actions = gdk_drop_get_actions (drop);
if (drag == textview->priv->drag &&
actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
if (actions & GDK_ACTION_COPY)
return GDK_ACTION_COPY;
if (actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
return 0;
}
static void
got_text (GObject *source,
GAsyncResult *result,
gpointer data)
{
GdkDrop *drop = GDK_DROP (source);
GtkTextView *text_view = GTK_TEXT_VIEW (data);
GtkTextViewPrivate *priv = text_view->priv;
GtkTextBuffer *buffer;
char *str;
GtkTextIter drop_point;
GdkDragAction action;
str = gdk_drop_read_text_finish (drop, result, NULL);
if (!str)
{
gdk_drop_finish (drop, 0);
return;
}
buffer = get_buffer (text_view);
gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
action = gtk_text_view_get_action (text_view, drop);
if (!gtk_text_iter_can_insert (&drop_point, priv->editable))
return FALSE;
gtk_text_buffer_begin_user_action (buffer);
if (!gtk_text_buffer_insert_interactive (buffer,
&drop_point, (gchar *) str, -1,
&drop_point, (gchar *) g_value_get_string (value), -1,
text_view->priv->editable))
gtk_widget_error_bell (GTK_WIDGET (text_view));
g_free (str);
gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
gtk_text_buffer_place_cursor (buffer, &drop_point);
gtk_text_buffer_end_user_action (buffer);
gdk_drop_finish (drop, action);
}
static gboolean
gtk_text_view_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
GtkTextIter drop_point;
GtkTextBuffer *buffer = NULL;
gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
buffer = get_buffer (text_view);
gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
if (!gtk_text_iter_can_insert (&drop_point, priv->editable))
goto done;
if (gtk_text_view_get_action (text_view, drop) == 0)
goto done;
if (gdk_drop_has_value (drop, G_TYPE_STRING))
{
gdk_drop_read_text_async (drop, NULL, got_text, text_view);
return TRUE;
}
done:
gdk_drop_finish (drop, 0);
return TRUE;
}
static void
gtk_text_view_set_hadjustment (GtkTextView *text_view,
GtkAdjustment *adjustment)

View File

@ -31,9 +31,9 @@
#include "gtkcssnumbervalueprivate.h"
#include "gtkcsscolorvalueprivate.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtkdragdest.h"
#include "gtkdragsource.h"
#include "gtkdragicon.h"
#include "gtkdroptargetasync.h"
#include "gtkentryprivate.h"
#include "gtksearchentryprivate.h"
#include "gtkeventcontrollerkey.h"
@ -311,7 +311,7 @@ struct _TreeViewDragInfo
GtkTreeRowReference *source_item;
GtkCssNode *cssnode;
GtkDropTarget *dest;
GtkDropTargetAsync *dest;
GdkModifierType start_button_mask;
guint source_set : 1;
@ -667,8 +667,8 @@ static void gtk_tree_view_key_controller_key_released (GtkEventControllerKey
guint keycode,
GdkModifierType state,
GtkTreeView *tree_view);
static void gtk_tree_view_focus_controller_focus_out (GtkEventController *focus,
GtkTreeView *tree_view);
static void gtk_tree_view_focus_controller_focus_out (GtkEventController *focus,
GtkTreeView *tree_view);
static gint gtk_tree_view_focus (GtkWidget *widget,
GtkDirectionType direction);
@ -690,22 +690,22 @@ static GdkContentProvider * gtk_tree_view_drag_data_get (GtkTreeView
GtkTreePath *source_row);
/* Target side drag signals */
static void gtk_tree_view_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkTreeView *tree_view);
static void gtk_tree_view_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkTreeView *tree_view);
static gboolean gtk_tree_view_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkTreeView *tree_view);
static void gtk_tree_view_drag_data_received (GObject *source,
GAsyncResult *result,
gpointer data);
static void gtk_tree_view_drag_leave (GtkDropTargetAsync *dest,
GdkDrop *drop,
GtkTreeView *tree_view);
static GdkDragAction gtk_tree_view_drag_motion (GtkDropTargetAsync *dest,
GdkDrop *drop,
double x,
double y,
GtkTreeView *tree_view);
static gboolean gtk_tree_view_drag_drop (GtkDropTargetAsync *dest,
GdkDrop *drop,
double x,
double y,
GtkTreeView *tree_view);
static void gtk_tree_view_drag_data_received (GObject *source,
GAsyncResult *result,
gpointer data);
/* tree_model signals */
static gboolean gtk_tree_view_real_move_cursor (GtkTreeView *tree_view,
@ -6859,13 +6859,13 @@ scroll_row_timeout (gpointer data)
/* Returns TRUE if event should not be propagated to parent widgets */
static gboolean
set_destination_row (GtkTreeView *tree_view,
GtkDropTarget *dest,
set_destination_row (GtkTreeView *tree_view,
GtkDropTargetAsync *dest,
/* coordinates relative to the widget */
gint x,
gint y,
GdkDragAction *suggested_action,
GType *target)
gint x,
gint y,
GdkDragAction *suggested_action,
GType *target)
{
GtkTreePath *path = NULL;
GtkTreeViewDropPosition pos;
@ -6899,7 +6899,7 @@ set_destination_row (GtkTreeView *tree_view,
return FALSE; /* no longer a drop site */
}
formats = gtk_drop_target_get_formats (dest);
formats = gtk_drop_target_async_get_formats (dest);
*target = gdk_content_formats_match_gtype (formats, formats);
if (*target == G_TYPE_INVALID)
return FALSE;
@ -7195,9 +7195,9 @@ gtk_tree_view_drag_data_get (GtkTreeView *tree_view,
}
static void
gtk_tree_view_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkTreeView *tree_view)
gtk_tree_view_drag_leave (GtkDropTargetAsync *dest,
GdkDrop *drop,
GtkTreeView *tree_view)
{
/* unset any highlight row */
gtk_tree_view_set_drag_dest_row (tree_view,
@ -7212,12 +7212,12 @@ gtk_tree_view_drag_leave (GtkDropTarget *dest,
}
static void
gtk_tree_view_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkTreeView *tree_view)
static GdkDragAction
gtk_tree_view_drag_motion (GtkDropTargetAsync *dest,
GdkDrop *drop,
double x,
double y,
GtkTreeView *tree_view)
{
gboolean empty;
GtkTreePath *path = NULL;
@ -7226,10 +7226,7 @@ gtk_tree_view_drag_motion (GtkDropTarget *dest,
GType target;
if (!set_destination_row (tree_view, dest, x, y, &suggested_action, &target))
{
gdk_drop_status (drop, 0);
return;
}
return 0;
tree_view->event_last_x = x;
tree_view->event_last_y = y;
@ -7241,8 +7238,7 @@ gtk_tree_view_drag_motion (GtkDropTarget *dest,
if (path == NULL && !empty)
{
/* Can't drop here. */
gdk_drop_status (drop, 0);
suggested_action = 0;
}
else
{
@ -7270,21 +7266,22 @@ gtk_tree_view_drag_motion (GtkDropTarget *dest,
else
{
set_status_pending (drop, 0);
gdk_drop_status (drop, suggested_action);
}
}
if (path)
gtk_tree_path_free (path);
return suggested_action;
}
static gboolean
gtk_tree_view_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkTreeView *tree_view)
gtk_tree_view_drag_drop (GtkDropTargetAsync *dest,
GdkDrop *drop,
double x,
double y,
GtkTreeView *tree_view)
{
GtkTreePath *path;
GdkDragAction suggested_action = 0;
@ -7436,8 +7433,6 @@ gtk_tree_view_drag_data_received (GObject *source,
}
}
gdk_drop_status (drop, suggested_action);
if (path)
gtk_tree_path_free (path);
@ -12869,10 +12864,11 @@ gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view,
di = ensure_info (tree_view);
di->dest_set = TRUE;
di->dest = gtk_drop_target_new (gdk_content_formats_ref (formats), actions);
di->dest = gtk_drop_target_async_new (gdk_content_formats_ref (formats), actions);
g_signal_connect (di->dest, "drag-leave", G_CALLBACK (gtk_tree_view_drag_leave), tree_view);
g_signal_connect (di->dest, "drag-enter", G_CALLBACK (gtk_tree_view_drag_motion), tree_view);
g_signal_connect (di->dest, "drag-motion", G_CALLBACK (gtk_tree_view_drag_motion), tree_view);
g_signal_connect (di->dest, "drag-drop", G_CALLBACK (gtk_tree_view_drag_drop), tree_view);
g_signal_connect (di->dest, "drop", G_CALLBACK (gtk_tree_view_drag_drop), tree_view);
gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest));
g_object_ref (di->dest);

View File

@ -5489,7 +5489,7 @@ gtk_widget_has_grab (GtkWidget *widget)
{
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
if (!GTK_IS_WIDGET (widget)) return FALSE;
return priv->has_grab;
}

View File

@ -40,7 +40,7 @@
#include "gtkcsscolorvalueprivate.h"
#include "gtkcssshadowvalueprivate.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtkdragdest.h"
#include "gtkdroptargetasync.h"
#include "gtkeventcontrollerfocus.h"
#include "gtkeventcontrollerkey.h"
#include "gtkeventcontrollermotion.h"
@ -280,12 +280,6 @@ typedef struct
GtkConstraintSolver *constraint_solver;
} GtkWindowPrivate;
#ifdef GDK_WINDOWING_X11
static const char *dnd_dest_targets [] = {
"application/x-rootwindow-drop"
};
#endif
enum {
SET_FOCUS,
ACTIVATE_FOCUS,
@ -1767,6 +1761,18 @@ gtk_window_activate_default_activate (GtkWidget *widget,
gtk_window_real_activate_default (GTK_WINDOW (widget));
}
static gboolean
gtk_window_accept_rootwindow_drop (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y,
gpointer unused)
{
gdk_drop_finish (drop, GDK_ACTION_MOVE);
return TRUE;
}
static void
gtk_window_init (GtkWindow *window)
{
@ -1776,9 +1782,7 @@ gtk_window_init (GtkWindow *window)
GdkSeat *seat;
GtkEventController *motion_controller;
GtkEventController *controller;
#ifdef GDK_WINDOWING_X11
GtkDropTarget *dest;
#endif
GtkDropTargetAsync *target;
widget = GTK_WIDGET (window);
@ -1829,10 +1833,10 @@ gtk_window_init (GtkWindow *window)
priv->scale = gtk_widget_get_scale_factor (widget);
#ifdef GDK_WINDOWING_X11
dest = gtk_drop_target_new (gdk_content_formats_new (dnd_dest_targets, G_N_ELEMENTS (dnd_dest_targets)), GDK_ACTION_MOVE);
gtk_widget_add_controller (GTK_WIDGET (window), GTK_EVENT_CONTROLLER (dest));
#endif
target = gtk_drop_target_async_new (gdk_content_formats_new ((const char*[1]) { "application/x-rootwindow-drop" }, 1),
GDK_ACTION_MOVE);
g_signal_connect (target, "drop", G_CALLBACK (gtk_window_accept_rootwindow_drop), NULL);
gtk_widget_add_controller (GTK_WIDGET (window), GTK_EVENT_CONTROLLER (target));
seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
g_signal_connect (seat, "device-removed",

View File

@ -98,6 +98,7 @@ gtk_private_sources = files([
'gtkcssvalue.c',
'gtkcsswidgetnode.c',
'gtkcustomlayout.c',
'gtkdrop.c',
'gtkfilechooserembed.c',
'gtkfilechooserentry.c',
'gtkfilechoosererrorstack.c',
@ -207,11 +208,12 @@ gtk_public_sources = files([
'gtkcontainer.c',
'gtkcssprovider.c',
'gtkdialog.c',
'gtkdragdest.c',
'gtkdragicon.c',
'gtkdragsource.c',
'gtkdrawingarea.c',
'gtkdropcontrollermotion.c',
'gtkdroptarget.c',
'gtkdroptargetasync.c',
'gtkeditable.c',
'gtkemojichooser.c',
'gtkemojicompletion.c',
@ -457,11 +459,12 @@ gtk_public_headers = files([
'gtkcustomlayout.h',
'gtkdebug.h',
'gtkdialog.h',
'gtkdragdest.h',
'gtkdragicon.h',
'gtkdragsource.h',
'gtkdrawingarea.h',
'gtkdropcontrollermotion.h',
'gtkdroptarget.h',
'gtkdroptargetasync.h',
'gtkeditable.h',
'gtkemojichooser.h',
'gtkentry.h',

View File

@ -287,93 +287,6 @@ static const char * trashcan_open_xpm[] = {
GdkPixbuf *trashcan_open;
GdkPixbuf *trashcan_closed;
gboolean have_drag;
static const char *target_table[] = {
"STRING",
"text/plain",
"application/x-rootwindow-drop"
};
static guint n_targets = sizeof(target_table) / sizeof(target_table[0]);
void
target_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkWidget *widget)
{
g_print("leave\n");
have_drag = FALSE;
gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_closed);
}
gboolean
target_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkWidget *widget)
{
char *s;
if (!have_drag)
{
have_drag = TRUE;
gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_open);
}
s = gdk_content_formats_to_string (gdk_drop_get_formats (drop));
g_print ("%s\n", s);
gdk_drop_status (drop, GDK_ACTION_ALL);
return TRUE;
}
static void
got_text_in_target (GObject *object,
GAsyncResult *result,
gpointer data)
{
char *str;
str = gdk_drop_read_text_finish (GDK_DROP (object), result, NULL);
if (str)
{
g_print ("Received \"%s\" in target\n", str);
g_free (str);
}
gdk_drop_finish (GDK_DROP (object), GDK_ACTION_MOVE);
}
gboolean
target_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkWidget *widget)
{
GdkContentFormats *formats;
const char *format;
g_print("drop\n");
have_drag = FALSE;
gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_closed);
formats = gdk_drop_get_formats (drop);
format = gdk_content_formats_match_mime_type (formats, formats);
if (format)
{
gdk_drop_read_text_async (drop, NULL, got_text_in_target, widget);
return TRUE;
}
gdk_drop_status (drop, 0);
return FALSE;
}
static GdkDragAction
action_make_unique (GdkDragAction action)
@ -394,31 +307,67 @@ action_make_unique (GdkDragAction action)
return 0;
}
static void
got_text (GObject *object,
GAsyncResult *result,
gpointer data)
GdkDragAction
trash_drag_enter (GtkDropTarget *dest,
GdkDrop *drop,
double x,
double y,
GtkWidget *widget)
{
char *str;
char *s;
str = gdk_drop_read_text_finish (GDK_DROP (object), result, NULL);
if (str)
{
g_print ("Received \"%s\" in label\n", str);
g_free (str);
}
gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_open);
s = gdk_content_formats_to_string (gdk_drop_get_formats (drop));
g_print ("trash enter: %s\n", s);
g_free (s);
return action_make_unique (gdk_drop_get_actions (drop));;
}
void
label_drag_drop (GtkDropTarget *dest,
GdkDragAction
trash_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkWidget *widget)
{
char *s;
gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_closed);
s = gdk_content_formats_to_string (gdk_drop_get_formats (drop));
g_print ("trash leave: %s\n", s);
g_free (s);
return action_make_unique (gdk_drop_get_actions (drop));
}
gboolean
trash_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
double x,
double y,
GtkWidget *widget)
{
char *s;
s = gdk_content_formats_to_string (gdk_drop_get_formats (drop));
g_print ("trash drop: %s\n", s);
g_free (s);
gdk_drop_finish (drop, action_make_unique (gdk_drop_get_actions (drop)));
return TRUE;
}
gboolean
label_drag_drop (GtkDropTarget *dest,
const GValue *value,
int x,
int y,
GtkWidget *widget)
{
gdk_drop_read_text_async (drop, NULL, got_text, widget);
GdkDragAction action = action_make_unique (gdk_drop_get_actions (drop));
gdk_drop_finish (drop, action);
g_print ("Received \"%s\" in label\n", g_value_get_string (value));
return TRUE;
}
/* The following is a rather elaborate example demonstrating/testing
@ -443,18 +392,10 @@ popdown_cb (gpointer data)
return FALSE;
}
gboolean
popup_motion (GtkDropTarget *dest,
GdkDrop *drop)
{
gdk_drop_status (drop, GDK_ACTION_COPY);
return TRUE;
}
void
popup_enter (GtkDropTarget *dest)
{
g_print ("popup enter\n");
g_print ("popup enter\n");
if (!in_popup)
{
in_popup = TRUE;
@ -483,10 +424,8 @@ g_print ("popup leave\n");
}
static gboolean
popup_drop (GtkDropTarget *dest,
GdkDrop *drop)
popup_drop (GtkDropTarget *dest)
{
gdk_drop_finish (drop, GDK_ACTION_COPY);
popdown_cb (NULL);
return TRUE;
}
@ -501,12 +440,10 @@ popup_cb (gpointer data)
GtkWidget *button;
GtkWidget *grid;
int i, j;
GdkContentFormats *targets;
popup_window = gtk_window_new ();
grid = gtk_grid_new ();
targets = gdk_content_formats_new_for_gtype (G_TYPE_STRING);
for (i=0; i<3; i++)
for (j=0; j<3; j++)
@ -520,16 +457,13 @@ popup_cb (gpointer data)
gtk_widget_set_vexpand (button, TRUE);
gtk_grid_attach (GTK_GRID (grid), button, i, j, 1, 1);
dest = gtk_drop_target_new (targets, GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (dest, "accept", G_CALLBACK (popup_motion), NULL);
g_signal_connect (dest, "drag-enter", G_CALLBACK (popup_enter), NULL);
g_signal_connect (dest, "drag-leave", G_CALLBACK (popup_leave), NULL);
g_signal_connect (dest, "drag-drop", G_CALLBACK (popup_drop), NULL);
dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (dest, "enter", G_CALLBACK (popup_enter), NULL);
g_signal_connect (dest, "leave", G_CALLBACK (popup_leave), NULL);
g_signal_connect (dest, "drop", G_CALLBACK (popup_drop), NULL);
gtk_widget_add_controller (button, GTK_EVENT_CONTROLLER (dest));
}
gtk_container_add (GTK_CONTAINER (popup_window), grid);
gdk_content_formats_unref (targets);
}
gtk_widget_show (popup_window);
popped_up = TRUE;
@ -550,18 +484,17 @@ popsite_motion (GtkDropTarget *dest,
}
void
popsite_enter (GtkDropTarget *dest)
popsite_enter (GtkDropControllerMotion *motion)
{
g_print ("popsite enter\n");
g_print ("popsite enter\n");
if (!popup_timer)
popup_timer = g_timeout_add (500, popup_cb, NULL);
}
void
popsite_leave (GtkDropTarget *dest)
popsite_leave (GtkDropControllerMotion *motion)
{
g_print ("popsite leave\n");
g_print ("popsite leave\n");
if (popup_timer)
{
g_source_remove (popup_timer);
@ -606,8 +539,9 @@ main (int argc, char **argv)
GdkTexture *texture;
GdkContentProvider *content;
GtkDragSource *source;
GdkContentFormats *targets;
GtkDropTarget *dest;
GtkDropTargetAsync *async;
GtkEventController *controller;
gboolean done = FALSE;
test_init ();
@ -630,9 +564,8 @@ main (int argc, char **argv)
label = gtk_label_new ("Drop Here\n");
targets = gdk_content_formats_new (target_table, n_targets - 1); /* no rootwin */
dest = gtk_drop_target_new (gdk_content_formats_ref (targets), GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (dest, "drag-drop", G_CALLBACK (label_drag_drop), NULL);
dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (dest, "drop", G_CALLBACK (label_drag_drop), NULL);
gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest));
gtk_widget_set_hexpand (label, TRUE);
@ -641,22 +574,21 @@ main (int argc, char **argv)
label = gtk_label_new ("Popup\n");
dest = gtk_drop_target_new (targets, GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (dest, "accept", G_CALLBACK (popsite_motion), NULL);
g_signal_connect (dest, "drag-enter", G_CALLBACK (popsite_enter), NULL);
g_signal_connect (dest, "drag-leave", G_CALLBACK (popsite_leave), NULL);
gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest));
controller = gtk_drop_controller_motion_new ();
g_signal_connect (controller, "enter", G_CALLBACK (popsite_enter), NULL);
g_signal_connect (controller, "leave", G_CALLBACK (popsite_leave), NULL);
gtk_widget_add_controller (label, controller);
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_set_vexpand (label, TRUE);
gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 1, 1);
pixmap = gtk_image_new_from_pixbuf (trashcan_closed);
dest = gtk_drop_target_new (NULL, 0);
g_signal_connect (dest, "drag-leave", G_CALLBACK (target_drag_leave), pixmap);
g_signal_connect (dest, "accept", G_CALLBACK (target_drag_motion), pixmap);
g_signal_connect (dest, "drag-drop", G_CALLBACK (target_drag_drop), pixmap);
gtk_widget_add_controller (pixmap, GTK_EVENT_CONTROLLER (dest));
async = gtk_drop_target_async_new (NULL, 0);
g_signal_connect (async, "drag-enter", G_CALLBACK (trash_drag_enter), pixmap);
g_signal_connect (async, "drag-leave", G_CALLBACK (trash_drag_leave), pixmap);
g_signal_connect (async, "drop", G_CALLBACK (trash_drag_drop), pixmap);
gtk_widget_add_controller (pixmap, GTK_EVENT_CONTROLLER (async));
gtk_widget_set_hexpand (pixmap, TRUE);
gtk_widget_set_vexpand (pixmap, TRUE);

View File

@ -128,7 +128,7 @@ static void
perform_drop (GdkDrop *drop,
GtkWidget *image)
{
if (gdk_drop_has_value (drop, GDK_TYPE_TEXTURE))
if (gdk_content_formats_contain_gtype (gdk_drop_get_formats (drop), GDK_TYPE_TEXTURE))
gdk_drop_read_value_async (drop, GDK_TYPE_TEXTURE, G_PRIORITY_DEFAULT, NULL, got_texture, image);
else
{
@ -192,39 +192,37 @@ ask_actions (GdkDrop *drop,
static gboolean
delayed_deny (gpointer data)
{
GtkDropTarget *dest = data;
GtkDropTargetAsync *dest = data;
GtkWidget *image = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
GdkDrop *drop = GDK_DROP (g_object_get_data (G_OBJECT (image), "drop"));
if (drop)
{
g_print ("denying drop, late\n");
gtk_drop_target_deny_drop (dest, drop);
gtk_drop_target_async_reject_drop (dest, drop);
}
return G_SOURCE_REMOVE;
}
static gboolean
image_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
gpointer data)
image_drag_accept (GtkDropTargetAsync *dest,
GdkDrop *drop,
gpointer data)
{
GtkWidget *image = data;
g_object_set_data_full (G_OBJECT (image), "drop", g_object_ref (drop), g_object_unref);
g_timeout_add (1000, delayed_deny, dest);
gdk_drop_status (drop, gtk_drop_target_get_actions (dest));
return TRUE;
}
static gboolean
image_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
double x,
double y,
gpointer data)
{
GtkWidget *image = data;
@ -330,7 +328,7 @@ make_image (const gchar *icon_name, int hotspot)
{
GtkWidget *image;
GtkDragSource *source;
GtkDropTarget *dest;
GtkDropTargetAsync *dest;
GdkContentFormats *formats;
GdkContentFormatsBuilder *builder;
@ -352,9 +350,9 @@ make_image (const gchar *icon_name, int hotspot)
g_signal_connect (source, "drag-cancel", G_CALLBACK (drag_cancel), NULL);
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
dest = gtk_drop_target_new (formats, GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_ASK);
g_signal_connect (dest, "accept", G_CALLBACK (image_drag_motion), image);
g_signal_connect (dest, "drag-drop", G_CALLBACK (image_drag_drop), image);
dest = gtk_drop_target_async_new (formats, GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_ASK);
g_signal_connect (dest, "accept", G_CALLBACK (image_drag_accept), image);
g_signal_connect (dest, "drop", G_CALLBACK (image_drag_drop), image);
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest));
return image;

View File

@ -50,12 +50,6 @@ drag_cancel (GtkDragSource *source,
drag_end (source, drag);
}
typedef struct {
GtkWidget *canvas;
double x;
double y;
} DropData;
typedef struct {
double x, y;
double angle;
@ -76,32 +70,22 @@ apply_transform (GtkWidget *item)
gsk_transform_unref (transform);
}
static void
got_data (GObject *source,
GAsyncResult *result,
gpointer user_data)
static gboolean
drag_drop (GtkDropTarget *target,
const GValue *value,
double x,
double y)
{
GdkDrop *drop = GDK_DROP (source);
DropData *data = user_data;
GtkWidget *item;
const GValue *value;
TransformData *transform_data;
GtkWidget *canvas;
GtkWidget *last_child;
value = gdk_drop_read_value_finish (drop, result, NULL);
if (value == NULL)
{
gdk_drop_finish (drop, 0);
return;
}
item = g_value_get_object (value);
transform_data = g_object_get_data (G_OBJECT (item), "transform-data");
transform_data->x = data->x;
transform_data->y = data->y;
transform_data->x = x;
transform_data->y = y;
canvas = gtk_widget_get_parent (item);
last_child = gtk_widget_get_last_child (canvas);
@ -110,26 +94,6 @@ got_data (GObject *source,
apply_transform (item);
gdk_drop_finish (drop, GDK_ACTION_MOVE);
g_free (data);
}
static gboolean
drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y)
{
DropData *data;
data = g_new (DropData, 1);
data->canvas = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
data->x = x;
data->y = y;
gdk_drop_read_value_async (drop, GTK_TYPE_WIDGET, G_PRIORITY_DEFAULT, NULL, got_data, data);
return TRUE;
}
@ -153,8 +117,8 @@ canvas_new (void)
g_signal_connect (source, "drag-cancel", G_CALLBACK (drag_cancel), NULL);
gtk_widget_add_controller (canvas, GTK_EVENT_CONTROLLER (source));
dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_WIDGET), GDK_ACTION_MOVE);
g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), NULL);
dest = gtk_drop_target_new (GTK_TYPE_WIDGET, GDK_ACTION_MOVE);
g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), NULL);
gtk_widget_add_controller (canvas, GTK_EVENT_CONTROLLER (dest));
return canvas;
@ -186,31 +150,16 @@ set_color (GtkWidget *item,
g_free (css);
}
static void
got_color (GObject *source,
GAsyncResult *result,
gpointer data)
{
GdkDrop *drop = GDK_DROP (source);
GtkDropTarget *dest = data;
GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
const GValue *value;
GdkRGBA *color;
value = gdk_drop_read_value_finish (drop, result, NULL);
color = g_value_get_boxed (value);
set_color (item, color);
gdk_drop_finish (drop, GDK_ACTION_COPY);
}
static gboolean
item_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y)
const GValue *value,
double x,
double y)
{
gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, dest);
GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
set_color (item, g_value_get_boxed (value));
return TRUE;
}
@ -261,7 +210,6 @@ canvas_item_new (int i,
TransformData *transform_data;
GdkRGBA rgba;
GtkDropTarget *dest;
GdkContentFormats *formats;
GtkGesture *gesture;
label = g_strdup_printf ("Item %d", i);
@ -283,11 +231,9 @@ canvas_item_new (int i,
g_free (label);
g_free (id);
formats = gdk_content_formats_new_for_gtype (GDK_TYPE_RGBA);
dest = gtk_drop_target_new (formats, GDK_ACTION_COPY);
g_signal_connect (dest, "drag-drop", G_CALLBACK (item_drag_drop), NULL);
dest = gtk_drop_target_new (GDK_TYPE_RGBA, GDK_ACTION_COPY);
g_signal_connect (dest, "drop", G_CALLBACK (item_drag_drop), NULL);
gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (dest));
gdk_content_formats_unref (formats);
gesture = gtk_gesture_rotate_new ();
g_signal_connect (gesture, "angle-changed", G_CALLBACK (angle_changed), NULL);

View File

@ -29,47 +29,30 @@ drag_begin (GtkDragSource *source,
g_object_unref (paintable);
}
static void
got_row (GObject *src,
GAsyncResult *result,
gpointer data)
static gboolean
drag_drop (GtkDropTarget *dest,
const GValue *value,
double x,
double y,
gpointer data)
{
GdkDrop *drop = GDK_DROP (src);
GtkWidget *target = data;
GtkWidget *source;
int pos;
source = g_value_get_object (gdk_drop_read_value_finish (drop, result, NULL));
source = g_value_get_object (value);
if (source == NULL)
{
gdk_drop_finish (drop, 0);
return;
}
return FALSE;
pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (target));
if (source == target)
{
gdk_drop_finish (drop, 0);
return;
}
return FALSE;
g_object_ref (source);
gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (source)), source);
gtk_list_box_insert (GTK_LIST_BOX (gtk_widget_get_parent (target)), source, pos);
g_object_unref (source);
gdk_drop_finish (drop, GDK_ACTION_MOVE);
}
static gboolean
drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
gpointer data)
{
gdk_drop_read_value_async (drop, GTK_TYPE_LIST_BOX_ROW, G_PRIORITY_DEFAULT, NULL, got_row, data);
return TRUE;
}
@ -96,8 +79,8 @@ create_row (const gchar *text)
g_signal_connect (source, "prepare", G_CALLBACK (prepare), row);
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_LIST_BOX_ROW), GDK_ACTION_MOVE);
g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), row);
dest = gtk_drop_target_new (GTK_TYPE_LIST_BOX_ROW, GDK_ACTION_MOVE);
g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), row);
gtk_widget_add_controller (GTK_WIDGET (row), GTK_EVENT_CONTROLLER (dest));
return row;

View File

@ -55,10 +55,6 @@ gchar *tabs4 [] = {
NULL
};
static const char *button_targets[] = {
"GTK_NOTEBOOK_TAB"
};
static GtkNotebook*
window_creation_function (GtkNotebook *source_notebook,
GtkWidget *child,
@ -93,7 +89,8 @@ on_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint page_num, gpoi
static gboolean
remove_in_idle (gpointer data)
{
GtkWidget *child = data;
GtkNotebookPage *page = data;
GtkWidget *child = gtk_notebook_page_get_child (page);
GtkWidget *parent = gtk_widget_get_ancestor (child, GTK_TYPE_NOTEBOOK);
GtkWidget *tab_label;
@ -104,44 +101,17 @@ remove_in_idle (gpointer data)
return G_SOURCE_REMOVE;
}
static void
got_page (GObject *source,
GAsyncResult *result,
gpointer data)
{
GdkDrop *drop = GDK_DROP (source);
GInputStream *stream;
const char *mime_type;
stream = gdk_drop_read_finish (drop, result, &mime_type, NULL);
if (stream)
{
GBytes *bytes;
GtkWidget **child;
bytes = g_input_stream_read_bytes (stream, sizeof (gpointer), NULL, NULL);
child = (gpointer)g_bytes_get_data (bytes, NULL);
g_idle_add (remove_in_idle, *child);
gdk_drop_finish (drop, GDK_ACTION_MOVE);
g_bytes_unref (bytes);
g_object_unref (stream);
}
else
gdk_drop_finish (drop, 0);
}
static gboolean
on_button_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
const GValue *value,
double x,
double y,
gpointer user_data)
{
gdk_drop_read_async (drop, (const char *[]) { "GTK_NOTEBOOK_TAB", NULL }, G_PRIORITY_DEFAULT, NULL, got_page, NULL);
GtkNotebookPage *page;
gdk_drop_finish (drop, GDK_ACTION_MOVE);
page = g_value_get_object (value);
g_idle_add (remove_in_idle, page);
return TRUE;
}
@ -298,8 +268,8 @@ create_trash_button (void)
button = gtk_button_new_with_mnemonic ("_Delete");
dest = gtk_drop_target_new (gdk_content_formats_new (button_targets, G_N_ELEMENTS (button_targets)), GDK_ACTION_MOVE);
g_signal_connect (dest, "drag-drop", G_CALLBACK (on_button_drag_drop), NULL);
dest = gtk_drop_target_new (GTK_TYPE_NOTEBOOK_PAGE, GDK_ACTION_MOVE);
g_signal_connect (dest, "drop", G_CALLBACK (on_button_drag_drop), NULL);
gtk_widget_add_controller (button, GTK_EVENT_CONTROLLER (dest));
return button;

View File

@ -84,32 +84,16 @@ get_dragsource (void)
return GTK_WIDGET (tv);
}
static void
got_text (GObject *source,
GAsyncResult *result,
gpointer data)
{
GdkDrop *drop = GDK_DROP (source);
GtkWidget *widget = data;
const GValue *value;
value = gdk_drop_read_value_finish (drop, result, NULL);
if (value == NULL)
return;
gtk_label_set_label (GTK_LABEL (widget), g_value_get_string (value));
}
static void
drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
const GValue *value,
int x,
int y,
gpointer dada)
{
GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
gdk_drop_read_value_async (drop, G_TYPE_STRING, G_PRIORITY_DEFAULT, NULL, got_text, widget);
gtk_label_set_label (GTK_LABEL (widget), g_value_get_string (value));
}
static GtkWidget *
@ -119,8 +103,8 @@ get_droptarget (void)
GtkDropTarget *dest;
label = gtk_label_new ("Drop here");
dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (G_TYPE_STRING), GDK_ACTION_COPY);
g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), NULL);
dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY);
g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), NULL);
gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest));
return label;

View File

@ -150,7 +150,8 @@ test_type (gconstpointer data)
/* These are set in init() */
if ((g_type_is_a (type, GDK_TYPE_CLIPBOARD) ||
g_type_is_a (type, GDK_TYPE_CONTENT_PROVIDER)) &&
g_type_is_a (type, GDK_TYPE_CONTENT_PROVIDER) ||
g_type_is_a (type, GTK_TYPE_DROP_TARGET)) &&
strcmp (pspec->name, "formats") == 0)
continue;