From 6dc8fc3a4dd72540994bf6e448adbdd1ed577393 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 31 Dec 2019 01:54:43 -0500 Subject: [PATCH] Add a GtkDropTarget object Add an explicit GtkDropTarget object, and move the destination-side DND signals here. The object is used by connecting to its signals and attaching it to a widget with gtk_drop_target_attach(). --- gtk/gtkdnd.c | 89 +++-- gtk/gtkdndprivate.h | 4 +- gtk/gtkdragdest.c | 699 +++++++++++++++++++++++++++++++++++++-- gtk/gtkdragdest.h | 69 +++- gtk/gtkdragdestprivate.h | 46 +++ gtk/gtkdragsource.c | 10 +- 6 files changed, 838 insertions(+), 79 deletions(-) create mode 100644 gtk/gtkdragdestprivate.h diff --git a/gtk/gtkdnd.c b/gtk/gtkdnd.c index 20bcb774e1..dc6e5d8cfe 100644 --- a/gtk/gtkdnd.c +++ b/gtk/gtkdnd.c @@ -26,7 +26,7 @@ #include "gtkdndprivate.h" -#include "gtkdragdest.h" +#include "gtkdragdestprivate.h" #include "gtkimageprivate.h" #include "gtkintl.h" #include "gtkmain.h" @@ -111,6 +111,7 @@ static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDrop *drop, * Destination side * ********************/ + typedef struct { GdkDrop *drop; GtkWidget *widget; @@ -124,6 +125,9 @@ gtk_drag_get_data_finish (GtkDragGetData *data, { GtkDragDestSite *site; GtkSelectionData sdata; + GdkContentFormats *target_list = NULL; + GdkDragAction actions = 0; + GtkDestDefaults flags = 0; site = g_object_get_data (G_OBJECT (data->widget), "gtk-drag-dest"); @@ -133,30 +137,30 @@ gtk_drag_get_data_finish (GtkDragGetData *data, sdata.length = size; sdata.data = bytes ? bytes : (guchar *)g_strdup (""); sdata.display = gtk_widget_get_display (data->widget); - - if (site && site->target_list) + + if (site) { - if (gdk_content_formats_contain_mime_type (site->target_list, data->mime_type)) + target_list = gtk_drop_target_get_formats (site->dest); + actions = gtk_drop_target_get_actions (site->dest); + flags = gtk_drop_target_get_defaults (site->dest); + } + + if (target_list) + { + if (gdk_content_formats_contain_mime_type (target_list, data->mime_type)) { - if (!(site->flags & GTK_DEST_DEFAULT_DROP) || - size >= 0) - g_signal_emit_by_name (data->widget, - "drag-data-received", - data->drop, - &sdata); + if (!(flags & GTK_DEST_DEFAULT_DROP) || size >= 0) + gtk_drop_target_emit_drag_data_received (site->dest, data->drop, &sdata); } } else { - g_signal_emit_by_name (data->widget, - "drag-data-received", - data->drop, - &sdata); + gtk_drop_target_emit_drag_data_received (site->dest, data->drop, &sdata); } - if (site && site->flags & GTK_DEST_DEFAULT_DROP) + if (flags & GTK_DEST_DEFAULT_DROP) { - GdkDragAction action = site->actions & gdk_drop_get_actions (data->drop); + GdkDragAction action = actions & gdk_drop_get_actions (data->drop); if (size == 0) action = 0; @@ -535,16 +539,20 @@ gtk_drag_dest_leave (GtkWidget *widget, guint time) { GtkDragDestSite *site; + GtkDestDefaults flags; + gboolean track_motion; site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"); g_return_if_fail (site != NULL); - if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag) + flags = gtk_drop_target_get_defaults (site->dest); + track_motion = gtk_drop_target_get_track_motion (site->dest); + + if ((flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag) gtk_drag_unhighlight (widget); - if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag || - site->track_motion) - g_signal_emit_by_name (widget, "drag-leave", drop, time); + if (!(flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag || track_motion) + gtk_drop_target_emit_drag_leave (site->dest, drop, time); site->have_drag = FALSE; } @@ -557,43 +565,52 @@ gtk_drag_dest_motion (GtkWidget *widget, guint time) { GtkDragDestSite *site; + GdkDragAction dest_actions; + GtkDestDefaults flags; + gboolean track_motion; gboolean retval; site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"); g_return_val_if_fail (site != NULL, FALSE); - if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION) + dest_actions = gtk_drop_target_get_actions (site->dest); + flags = gtk_drop_target_get_defaults (site->dest); + track_motion = gtk_drop_target_get_track_motion (site->dest); + + if (track_motion || flags & GTK_DEST_DEFAULT_MOTION) { GdkDragAction actions; + GdkAtom target; actions = gdk_drop_get_actions (drop); - if ((actions & site->actions) == 0) + if ((dest_actions & actions) == 0) actions = 0; - if (actions && gtk_drag_dest_find_target (widget, drop, NULL)) + target = gtk_drop_target_match (site->dest, drop); + + if (actions && target) { if (!site->have_drag) { site->have_drag = TRUE; - if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) + if (flags & GTK_DEST_DEFAULT_HIGHLIGHT) gtk_drag_highlight (widget); } - gdk_drop_status (drop, site->actions); + gdk_drop_status (drop, dest_actions); } else { gdk_drop_status (drop, 0); - if (!site->track_motion) + if (!track_motion) return TRUE; } } - g_signal_emit_by_name (widget, "drag-motion", - drop, x, y, &retval); + retval = gtk_drop_target_emit_drag_motion (site->dest, drop, x, y); - return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval; + return (flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval; } static gboolean @@ -605,17 +622,22 @@ gtk_drag_dest_drop (GtkWidget *widget, { GtkDragDestSite *site; GtkDragDestInfo *info; + GtkDestDefaults flags; gboolean retval; site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"); g_return_val_if_fail (site != NULL, FALSE); + flags = gtk_drop_target_get_defaults (site->dest); + info = gtk_drag_get_dest_info (drop, FALSE); g_return_val_if_fail (info != NULL, FALSE); - if (site->flags & GTK_DEST_DEFAULT_DROP) + if (flags & GTK_DEST_DEFAULT_DROP) { - GdkAtom target = gtk_drag_dest_find_target (widget, drop, NULL); + GdkAtom target; + + target = gtk_drop_target_match (site->dest, drop); if (target == NULL) { @@ -626,8 +648,7 @@ gtk_drag_dest_drop (GtkWidget *widget, gtk_drag_get_data (widget, drop, target); } - g_signal_emit_by_name (widget, "drag-drop", - drop, x, y, &retval); + retval = gtk_drop_target_emit_drag_drop (site->dest, drop, x, y); - return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval; + return (flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval; } diff --git a/gtk/gtkdndprivate.h b/gtk/gtkdndprivate.h index 45448a17e2..65dce27d7e 100644 --- a/gtk/gtkdndprivate.h +++ b/gtk/gtkdndprivate.h @@ -29,13 +29,11 @@ typedef struct _GtkDragDestSite GtkDragDestSite; struct _GtkDragDestSite { + GtkDropTarget *dest; GtkDestDefaults flags; - GdkContentFormats *target_list; - GdkDragAction actions; guint do_proxy : 1; guint proxy_coords : 1; guint have_drag : 1; - guint track_motion : 1; }; G_BEGIN_DECLS diff --git a/gtk/gtkdragdest.c b/gtk/gtkdragdest.c index da2205e09d..f158b3acf2 100644 --- a/gtk/gtkdragdest.c +++ b/gtk/gtkdragdest.c @@ -25,11 +25,15 @@ #include "config.h" #include "gtkdragdest.h" +#include "gtkdragdestprivate.h" #include "gtkdnd.h" #include "gtkdndprivate.h" #include "gtkintl.h" #include "gtknative.h" +#include "gtktypebuiltins.h" +#include "gtkeventcontroller.h" +#include "gtkmarshalers.h" static void @@ -56,8 +60,7 @@ gtk_drag_dest_site_destroy (gpointer data) { GtkDragDestSite *site = data; - if (site->target_list) - gdk_content_formats_unref (site->target_list); + g_clear_object (&site->dest); g_slice_free (GtkDragDestSite, site); } @@ -71,23 +74,16 @@ gtk_drag_dest_set_internal (GtkWidget *widget, old_site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest")); if (old_site) { - g_signal_handlers_disconnect_by_func (widget, - gtk_drag_dest_realized, - old_site); - g_signal_handlers_disconnect_by_func (widget, - gtk_drag_dest_hierarchy_changed, - old_site); - - site->track_motion = old_site->track_motion; + g_signal_handlers_disconnect_by_func (widget, gtk_drag_dest_realized, old_site); + g_signal_handlers_disconnect_by_func (widget, gtk_drag_dest_hierarchy_changed, old_site); + gtk_drop_target_set_track_motion (site->dest, gtk_drop_target_get_track_motion (old_site->dest)); } if (gtk_widget_get_realized (widget)) gtk_drag_dest_realized (widget); - g_signal_connect (widget, "realize", - G_CALLBACK (gtk_drag_dest_realized), site); - g_signal_connect (widget, "notify::root", - G_CALLBACK (gtk_drag_dest_hierarchy_changed), site); + g_signal_connect (widget, "realize", G_CALLBACK (gtk_drag_dest_realized), site); + g_signal_connect (widget, "notify::root", G_CALLBACK (gtk_drag_dest_hierarchy_changed), site); g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"), site, gtk_drag_dest_site_destroy); @@ -142,7 +138,7 @@ gtk_drag_dest_set_internal (GtkWidget *widget, * } * ]| */ -void +GtkDropTarget * gtk_drag_dest_set (GtkWidget *widget, GtkDestDefaults flags, GdkContentFormats *targets, @@ -150,20 +146,16 @@ gtk_drag_dest_set (GtkWidget *widget, { GtkDragDestSite *site; - g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); site = g_slice_new0 (GtkDragDestSite); - site->flags = flags; + site->dest = gtk_drop_target_new (flags, targets, actions); site->have_drag = FALSE; - if (targets) - site->target_list = gdk_content_formats_ref (targets); - else - site->target_list = NULL; - site->actions = actions; - site->track_motion = FALSE; gtk_drag_dest_set_internal (widget, site); + + return site->dest; } /** @@ -213,7 +205,7 @@ gtk_drag_dest_get_target_list (GtkWidget *widget) site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest")); - return site ? site->target_list : NULL; + return site ? gtk_drop_target_get_formats (site->dest) : NULL; } /** @@ -242,13 +234,7 @@ gtk_drag_dest_set_target_list (GtkWidget *widget, return; } - if (target_list) - gdk_content_formats_ref (target_list); - - if (site->target_list) - gdk_content_formats_unref (site->target_list); - - site->target_list = target_list; + gtk_drop_target_set_formats (site->dest, target_list); } /** @@ -350,7 +336,7 @@ gtk_drag_dest_set_track_motion (GtkWidget *widget, g_return_if_fail (site != NULL); - site->track_motion = track_motion != FALSE; + gtk_drop_target_set_track_motion (site->dest, track_motion); } /** @@ -373,7 +359,7 @@ gtk_drag_dest_get_track_motion (GtkWidget *widget) site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest")); if (site) - return site->track_motion; + return gtk_drop_target_get_track_motion (site->dest); return FALSE; } @@ -414,3 +400,650 @@ gtk_drag_dest_find_target (GtkWidget *widget, gdk_drop_get_formats (drop)); } + +/** + * SECTION:gtkdragdest + * @Short_description: An object 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, connect to its signals, and then attach + * it to the widgtet with gtk_drop_target_attach(). + */ + +struct _GtkDropTarget +{ + GObject parent_instance; + + GdkContentFormats *formats; + GdkDragAction actions; + GtkDestDefaults defaults; + gboolean track_motion; + + GtkWidget *widget; + GdkDrop *drop; +}; + +struct _GtkDropTargetClass +{ + GObjectClass parent_class; +}; + +enum { + PROP_FORMATS = 1, + PROP_ACTIONS, + PROP_DEFAULTS, + PROP_TRACK_MOTION, + NUM_PROPERTIES +}; + +static GParamSpec *properties[NUM_PROPERTIES]; + +enum { + DRAG_LEAVE, + DRAG_MOTION, + DRAG_DROP, + DRAG_DATA_RECEIVED, + NUM_SIGNALS +}; + +static guint signals[NUM_SIGNALS]; + +G_DEFINE_TYPE (GtkDropTarget, gtk_drop_target, G_TYPE_OBJECT); + +static void +gtk_drop_target_init (GtkDropTarget *dest) +{ + dest->defaults = GTK_DEST_DEFAULT_ALL; +} + +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; + + case PROP_DEFAULTS: + gtk_drop_target_set_defaults (dest, g_value_get_flags (value)); + break; + + case PROP_TRACK_MOTION: + gtk_drop_target_set_track_motion (dest, 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 *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_DEFAULTS: + g_value_set_flags (value, gtk_drop_target_get_defaults (dest)); + break; + + case PROP_TRACK_MOTION: + g_value_set_boolean (value, gtk_drop_target_get_track_motion (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); + + 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; + + /** + * 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 are supported + */ + 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); + + /** + * GtkDropTargets:defaults: + * + * Flags that determine the default behavior + */ + properties[PROP_DEFAULTS] = + g_param_spec_flags ("defaults", P_("Defaults"), P_("Defaults"), + GTK_TYPE_DEST_DEFAULTS, GTK_DEST_DEFAULT_ALL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkDropTarget:track-motion: + * + * Whether the drop target should emit #GtkDropTarget::drag-motion signals + * unconditionally + */ + properties[PROP_TRACK_MOTION] = + g_param_spec_boolean ("track-motion", P_("Track motion"), P_("Track motion"), + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); + + /** + * GtkDropTarget::drag-leave: + * @dest: the #GtkDropTarget + * + * The ::drag-leave signal is emitted on the drop site when the cursor + * leaves the widget. A typical reason to connect to this signal is to + * undo things done in #GtkDropTarget::drag-motion, e.g. undo highlighting. + * + * Likewise, the #GtkWidget::drag-leave signal is also emitted before the + * #GtkDropTarget::drag-drop signal, for instance to allow cleaning up of + * a preview item created in the #GtkDropTarget::drag-motion signal handler. + */ + 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, 0); + + /** + * GtkWidget::drag-motion: + * @dest: the #GtkDropTarget + * @x: the x coordinate of the current cursor position + * @y: the y coordinate of the current cursor position + * + * The ::drag-motion 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(). + * + * If the decision whether the drop will be accepted or rejected can't be + * made based solely on the cursor position and the type of the data, the + * handler may inspect the dragged data by calling one of the #GdkDrop + * read functions, and defer the gdk_drag_status() call to when it has + * received th data. + * + * Note that you must pass #GTK_DEST_DEFAULT_DROP, + * #GTK_DEST_DEFAULT_MOTION or #GTK_DEST_DEFAULT_ALL to gtk_drag_dest_set() + * when using the ::drag-motion signal that way. + * + * Also note that there is no drag-enter signal. The drag receiver has to + * keep track of whether he has received any drag-motion signals since the + * last #GtkWidget::drag-leave and if not, treat the drag-motion signal as + * an "enter" signal. Upon an "enter", the handler will typically highlight + * the drop site with gtk_drag_highlight(). + * + * Returns: whether the cursor position is in a drop zone + */ + signals[DRAG_MOTION] = + g_signal_new (I_("drag-motion"), + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_BOOLEAN, 2, + G_TYPE_INT, G_TYPE_INT); + + /** + * GtkDropTarget::drag-drop: + * @dest: the #GtkDropTarget + * @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. + * + * 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, 2, + G_TYPE_INT, G_TYPE_INT); + + signals[DRAG_DATA_RECEIVED] = + g_signal_new (I_("drag-data-received"), + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + _gtk_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (signals[DRAG_DATA_RECEIVED], + G_TYPE_FROM_CLASS (class), + _gtk_marshal_VOID__BOXEDv); +} + +/** + * gtk_drop_target_new: + * @defaults: flags determining the default behaviour + * @formats: (nullable): the supported data formats + * @actions: the supported actions + * + * Creates a new #GtkDropTarget object + * + * Returns: the new #GtkDropTarget + */ +GtkDropTarget * +gtk_drop_target_new (GtkDestDefaults defaults, + GdkContentFormats *formats, + GdkDragAction actions) +{ + return g_object_new (GTK_TYPE_DROP_TARGET, + "defaults", defaults, + "formats", formats, + "actions", actions, + NULL); +} + +/** + * gtk_drop_target_set_formats: + * @dest: a #GtkDropTarget + * @formats: (nullable): the supported data formats + * + * Sets th 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. + * + * Returns: 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: + * @dst: 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_set_defaults: + * @dest: a #GtkDropTarget + * @defaults: flags determining the default behaviour + * + * Sets the flags determining the behavior of the drop target. + */ +void +gtk_drop_target_set_defaults (GtkDropTarget *dest, + GtkDestDefaults defaults) +{ + g_return_if_fail (GTK_IS_DROP_TARGET (dest)); + + if (dest->defaults == defaults) + return; + + dest->defaults = defaults; + + g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_DEFAULTS]); +} + +/** + * gtk_drop_target_get_defaults: + * @dest: a #GtkDropTarget + * + * Gets the flags determining the behavior of the drop target. + * + * Returns: flags determining the behaviour of the drop target + */ +GtkDestDefaults +gtk_drop_target_get_defaults (GtkDropTarget *dest) +{ + g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), GTK_DEST_DEFAULT_ALL); + + return dest->defaults; +} + +/** + * gtk_drop_target_set_track_motion: + * @dest: a #GtkDropTarget + * @track_motion: whether to accept all targets + * + * Tells the drop target to emit #GtkDropTarget::drag-motion and + * #GtkDropTarget::drag-leave events regardless of the targets and + * the %GTK_DEST_DEFAULT_MOTION flag. + * + * This may be used when a drop target wants to do generic + * actions regardless of the targets that the source offers. + */ +void +gtk_drop_target_set_track_motion (GtkDropTarget *dest, + gboolean track_motion) +{ + g_return_if_fail (GTK_IS_DROP_TARGET (dest)); + + if (dest->track_motion == track_motion) + return; + + dest->track_motion = track_motion; + + g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_TRACK_MOTION]); +} + +/** + * gtk_drop_target_get_track_motion: + * @dest: a #GtkDropTarget + * + * Gets the value of the #GtkDropTarget::track-motion property. + * + * Returns: whether to accept all targets + */ +gboolean +gtk_drop_target_get_track_motion (GtkDropTarget *dest) +{ + g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), FALSE); + + return dest->track_motion; +} + +/** + * gtk_drop_target_attach: + * @dest: (transfer full): a #GtkDropTarget + * @widget the widget to attach @dest to + * + * Attaches the @dest to widget and makes it accept drops + * on the widgets. + * + * To undo the effect of this call, use gtk_drop_target_detach(). + */ +void +gtk_drop_target_attach (GtkDropTarget *dest, + GtkWidget *widget) +{ + GtkDragDestSite *site; + + g_return_if_fail (GTK_IS_DROP_TARGET (dest)); + g_return_if_fail (dest->widget == NULL); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + dest->widget = widget; + + site = g_slice_new0 (GtkDragDestSite); + + site->dest = dest; + site->have_drag = FALSE; + + gtk_drag_dest_set_internal (widget, site); +} + +/** + * gtk_drop_target_detach: + * @dest: a #GtkDropTarget + * + * Undoes the effect of a prior gtk_drop_target_attach() call. + */ +void +gtk_drop_target_detach (GtkDropTarget *dest) +{ + g_return_if_fail (GTK_IS_DROP_TARGET (dest)); + + if (dest->widget) + { + gtk_drag_dest_unset (dest->widget); + dest->widget = NULL; + } +} + +/** + * gtk_drop_target_get_target: + * @dest: a #GtkDropTarget + * + * Gts the widget that the drop target is attached to. + * + * Returns: (nullable): get the widget that @dest is attached to + */ +GtkWidget * +gtk_drop_target_get_target (GtkDropTarget *dest) +{ + g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), NULL); + + return dest->widget; +} + +/** + * gtk_drop_target_get_drop: + * @dest: a #GtkDropTarget + * + * Returns the underlying #GtkDrop object for an ongoing drag. + * + * Returns: (nullable): 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; +} + +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. + * + * 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; + + return gtk_drop_target_match (dest, dest->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); +} + +void +gtk_drop_target_emit_drag_leave (GtkDropTarget *dest, + GdkDrop *drop, + guint time) +{ + set_drop (dest, drop); + g_signal_emit (dest, signals[DRAG_LEAVE], 0, time); + set_drop (dest, NULL); +} + +gboolean +gtk_drop_target_emit_drag_motion (GtkDropTarget *dest, + GdkDrop *drop, + int x, + int y) +{ + gboolean result = FALSE; + + set_drop (dest, drop); + g_signal_emit (dest, signals[DRAG_MOTION], 0, x, y, &result); + + return result; +} + +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, x, y, &result); + + return result; +} + +void +gtk_drop_target_emit_drag_data_received (GtkDropTarget *dest, + GdkDrop *drop, + GtkSelectionData *sdata) +{ + set_drop (dest, drop); + g_signal_emit (dest, signals[DRAG_DATA_RECEIVED], 0, sdata); +} diff --git a/gtk/gtkdragdest.h b/gtk/gtkdragdest.h index f241f514a7..e126298bf9 100644 --- a/gtk/gtkdragdest.h +++ b/gtk/gtkdragdest.h @@ -37,6 +37,8 @@ G_BEGIN_DECLS +typedef struct _GtkDropTarget GtkDropTarget; + /** * GtkDestDefaults: * @GTK_DEST_DEFAULT_MOTION: If set for a widget, GTK+, during a drag over this @@ -46,7 +48,7 @@ G_BEGIN_DECLS * @GTK_DEST_DEFAULT_HIGHLIGHT: If set for a widget, GTK+ will draw a highlight on * this widget as long as a drag is over this widget and the widget drag format * and action are acceptable. - * @GTK_DEST_DEFAULT_DROP: If set for a widget, when a drop occurs, GTK+ will + * @GTK_DEST_DEFAULT_OP: If set for a widget, when a drop occurs, GTK+ will * will check if the drag matches this widget’s list of possible formats and * actions. If so, GTK+ will call gtk_drag_get_data() on behalf of the widget. * Whether or not the drop is successful, GTK+ will call gdk_drag_finish(). If @@ -67,10 +69,10 @@ typedef enum { } GtkDestDefaults; GDK_AVAILABLE_IN_ALL -void gtk_drag_dest_set (GtkWidget *widget, - GtkDestDefaults flags, - GdkContentFormats *targets, - GdkDragAction actions); +GtkDropTarget *gtk_drag_dest_set (GtkWidget *widget, + GtkDestDefaults flags, + GdkContentFormats *targets, + GdkDragAction actions); GDK_AVAILABLE_IN_ALL void gtk_drag_dest_unset (GtkWidget *widget); @@ -98,6 +100,63 @@ GDK_AVAILABLE_IN_ALL gboolean gtk_drag_dest_get_track_motion (GtkWidget *widget); +#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 (GtkDestDefaults defaults, + 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 +void gtk_drop_target_set_defaults (GtkDropTarget *dest, + GtkDestDefaults defaults); +GDK_AVAILABLE_IN_ALL +GtkDestDefaults gtk_drop_target_get_defaults (GtkDropTarget *dest); + +GDK_AVAILABLE_IN_ALL +void gtk_drop_target_set_track_motion (GtkDropTarget *dest, + gboolean track_motion); +GDK_AVAILABLE_IN_ALL +gboolean gtk_drop_target_get_track_motion (GtkDropTarget *dest); + +GDK_AVAILABLE_IN_ALL +void gtk_drop_target_attach (GtkDropTarget *dest, + GtkWidget *widget); +GDK_AVAILABLE_IN_ALL +void gtk_drop_target_detach (GtkDropTarget *dest); + +GDK_AVAILABLE_IN_ALL +GdkDrop *gtk_drop_target_get_drop (GtkDropTarget *dest); + +GDK_AVAILABLE_IN_ALL +GtkWidget *gtk_drop_target_get_target (GtkDropTarget *dest); + +GDK_AVAILABLE_IN_ALL +const char *gtk_drop_target_find_mimetype (GtkDropTarget *dest); + + G_END_DECLS #endif /* __GTK_DRAG_DEST_H__ */ diff --git a/gtk/gtkdragdestprivate.h b/gtk/gtkdragdestprivate.h new file mode 100644 index 0000000000..deb772763f --- /dev/null +++ b/gtk/gtkdragdestprivate.h @@ -0,0 +1,46 @@ + +/* GTK - The GIMP Toolkit + * Copyright (C) 2020 Matthias Clasen + * + * 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 . + */ + +#ifndef __GTK_DRAG_DEST_PRIVATE_H__ +#define __GTK_DRAG_DEST_PRIVATE_H__ + +#include "gtkdragdest.h" + +G_BEGIN_DECLS + +void gtk_drop_target_emit_drag_data_received (GtkDropTarget *dest, + GdkDrop *drop, + GtkSelectionData *sdata); +void gtk_drop_target_emit_drag_leave (GtkDropTarget *dest, + GdkDrop *drop, + guint time); +gboolean gtk_drop_target_emit_drag_motion (GtkDropTarget *dest, + GdkDrop *drop, + int x, + int y); +gboolean gtk_drop_target_emit_drag_drop (GtkDropTarget *dest, + GdkDrop *drop, + int x, + int y); + +const char * gtk_drop_target_match (GtkDropTarget *dest, + GdkDrop *drop); + +G_END_DECLS + +#endif diff --git a/gtk/gtkdragsource.c b/gtk/gtkdragsource.c index 1f4930b3a9..653f112b68 100644 --- a/gtk/gtkdragsource.c +++ b/gtk/gtkdragsource.c @@ -310,7 +310,8 @@ static void gtk_drag_source_cancel_cb (GdkDrag *drag, GtkDragSource *source); static void -drag_end (GtkDragSource *source) +drag_end (GtkDragSource *source, + gboolean success) { g_signal_handlers_disconnect_by_func (source->drag, gtk_drag_source_drop_performed_cb, source); g_signal_handlers_disconnect_by_func (source->drag, gtk_drag_source_dnd_finished_cb, source); @@ -318,6 +319,8 @@ drag_end (GtkDragSource *source) g_signal_emit (source, signals[DRAG_END], 0); + gdk_drag_drop_done (source->drag, success); + g_object_set_data (G_OBJECT (source->drag), I_("gtk-drag-source"), NULL); g_clear_object (&source->drag); source->widget = NULL; @@ -330,8 +333,7 @@ gtk_drag_source_dnd_finished_cb (GdkDrag *drag, { if (gdk_drag_get_selected_action (drag) == GDK_ACTION_MOVE) g_signal_emit (source, signals[DRAG_DATA_DELETE], 0); - - drag_end (source); + drag_end (source, TRUE); } static void @@ -342,7 +344,7 @@ gtk_drag_source_cancel_cb (GdkDrag *drag, gboolean success = FALSE; g_signal_emit (source, signals[DRAG_FAILED], 0, reason, &success); - drag_end (source); + drag_end (source, FALSE); } static void