/* 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 . */ /* * 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" #include "gtkselectionprivate.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, connect to its signals, and then attach * it to the widget with gtk_widget_add_controller(). */ struct _GtkDropTarget { GtkEventController parent_object; GdkContentFormats *formats; GdkDragAction actions; GtkWidget *widget; GdkDrop *drop; gboolean contains; gboolean contains_pending; }; struct _GtkDropTargetClass { GtkEventControllerClass parent_class; gboolean (*drag_motion) (GtkDropTarget *dest, int x, int y); }; enum { PROP_FORMATS = 1, PROP_ACTIONS, PROP_CONTAINS, NUM_PROPERTIES }; static GParamSpec *properties[NUM_PROPERTIES]; enum { DRAG_LEAVE, DRAG_MOTION, DRAG_DROP, NUM_SIGNALS }; static guint signals[NUM_SIGNALS]; static gboolean gtk_drop_target_drag_motion (GtkDropTarget *dest, int x, int y); static gboolean gtk_drop_target_handle_event (GtkEventController *controller, const GdkEvent *event); static gboolean gtk_drop_target_filter_event (GtkEventController *controller, const GdkEvent *event); static void gtk_drop_target_set_widget (GtkEventController *controller, GtkWidget *widget); static void gtk_drop_target_unset_widget (GtkEventController *controller); static gboolean gtk_drop_target_get_contains (GtkDropTarget *dest); static void gtk_drop_target_set_contains (GtkDropTarget *dest, gboolean contains); 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; controller_class->set_widget = gtk_drop_target_set_widget; controller_class->unset_widget = gtk_drop_target_unset_widget; class->drag_motion = gtk_drop_target_drag_motion; /** * 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-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(). * * 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 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 the data. * * 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, G_STRUCT_OFFSET (GtkDropTargetClass, drag_motion), g_signal_accumulator_first_wins, 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. * * 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(), gtk_drop_target_read_selection(). * * 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, 2, G_TYPE_INT, G_TYPE_INT); } /** * gtk_drop_target_new: * @formats: (nullable): 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) { return g_object_new (GTK_TYPE_DROP_TARGET, "formats", formats, "actions", actions, NULL); } /** * 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. * * 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; } static void gtk_drag_dest_realized (GtkWidget *widget) { GtkNative *native = gtk_widget_get_native (widget); gdk_surface_register_dnd (gtk_native_get_surface (native)); } static void gtk_drag_dest_hierarchy_changed (GtkWidget *widget, GParamSpec *pspec, gpointer data) { GtkNative *native = gtk_widget_get_native (widget); if (native && gtk_widget_get_realized (GTK_WIDGET (native))) gdk_surface_register_dnd (gtk_native_get_surface (native)); } /** * 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; } 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. * * 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 gboolean gtk_drop_target_drag_motion (GtkDropTarget *dest, int x, int y) { GdkDrop *drop = dest->drop; GdkDragAction dest_actions; GdkDragAction actions; GdkAtom target; dest_actions = gtk_drop_target_get_actions (dest); actions = dest_actions & gdk_drop_get_actions (drop); target = gtk_drop_target_match (dest, drop); if (actions && target) { gdk_drop_status (drop, dest_actions); gtk_drop_target_set_contains (dest, TRUE); } else { gdk_drop_status (drop, 0); gtk_drop_target_set_contains (dest, FALSE); } return TRUE; } 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_leave (GtkDropTarget *dest, GdkDrop *drop) { set_drop (dest, drop); g_signal_emit (dest, signals[DRAG_LEAVE], 0, time); set_drop (dest, NULL); gtk_drop_target_set_contains (dest, FALSE); } static gboolean gtk_drop_target_emit_drag_motion (GtkDropTarget *dest, GdkDrop *drop, int x, int y) { gboolean result = FALSE; dest->contains_pending = TRUE; set_drop (dest, drop); g_signal_emit (dest, signals[DRAG_MOTION], 0, x, y, &result); if (dest->contains_pending) gtk_drop_target_set_contains (dest, result); return result; } 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, x, y, &result); return result; } static void gtk_drop_target_set_contains (GtkDropTarget *dest, gboolean contains) { GtkWidget *widget; widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)); dest->contains_pending = FALSE; if (dest->contains == contains) return; dest->contains = contains; if (contains) gtk_drag_highlight (widget); else gtk_drag_unhighlight (widget); 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, const 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 gtk_drop_set_current_dest (GdkDrop *drop, GtkDropTarget *dest) { GtkDropTarget *old_dest; old_dest = g_object_get_data (G_OBJECT (drop), "current-dest"); if (old_dest) g_object_weak_unref (G_OBJECT (old_dest), clear_current_dest, drop); g_object_set_data (G_OBJECT (drop), "current-dest", dest); if (dest) g_object_weak_ref (G_OBJECT (dest), clear_current_dest, drop); } static GtkDropTarget * gtk_drop_get_current_dest (GdkDrop *drop) { return g_object_get_data (G_OBJECT (drop), "current-dest"); } static gboolean gtk_drop_target_handle_event (GtkEventController *controller, const GdkEvent *event) { GtkDropTarget *dest = GTK_DROP_TARGET (controller); GdkDrop *drop; GtkDropTarget *old_dest; double x, y; gboolean found = FALSE; gdk_event_get_coords (event, &x, &y); drop = gdk_event_get_drop (event); old_dest = gtk_drop_get_current_dest (drop); switch ((int)gdk_event_get_event_type (event)) { case GDK_DRAG_MOTION: found = gtk_drop_target_emit_drag_motion (dest, drop, x, y); break; case GDK_DROP_START: /* We send a leave before the drop so that the widget unhighlights * properly. */ if (old_dest) { gtk_drop_target_emit_drag_leave (old_dest, drop); gtk_drop_set_current_dest (drop, NULL); } found = gtk_drop_target_emit_drag_drop (dest, drop, x, y); break; default: break; } if (found) { if (old_dest && old_dest != dest) { gtk_drop_target_emit_drag_leave (old_dest, drop); gtk_drop_set_current_dest (drop, NULL); } gtk_drop_set_current_dest (drop, dest); } 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) { GtkDropTarget *dest; 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_event_get_drop (event); switch ((guint) event_type) { case GDK_DRAG_ENTER: break; case GDK_DRAG_LEAVE: dest = gtk_drop_get_current_dest (drop); if (dest) { gtk_drop_target_emit_drag_leave (dest, drop); gtk_drop_set_current_dest (drop, NULL); } break; case GDK_DRAG_MOTION: case GDK_DROP_START: gdk_drop_status (drop, 0); break; default: g_assert_not_reached (); } } static void gtk_drop_target_set_widget (GtkEventController *controller, GtkWidget *widget) { GtkDropTarget *dest = GTK_DROP_TARGET (controller); GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_parent_class)->set_widget (controller, widget); if (gtk_widget_get_realized (widget)) gtk_drag_dest_realized (widget); g_signal_connect (widget, "realize", G_CALLBACK (gtk_drag_dest_realized), dest); g_signal_connect (widget, "notify::root", G_CALLBACK (gtk_drag_dest_hierarchy_changed), dest); } static void gtk_drop_target_unset_widget (GtkEventController *controller) { GtkWidget *widget; widget = gtk_event_controller_get_widget (controller); g_signal_handlers_disconnect_by_func (widget, gtk_drag_dest_realized, controller); g_signal_handlers_disconnect_by_func (widget, gtk_drag_dest_hierarchy_changed, controller); GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_parent_class)->unset_widget (controller); } /** * gtk_drag_highlight: (method) * @widget: a widget to highlight * * Highlights a widget as a currently hovered drop target. * To end the highlight, call gtk_drag_unhighlight(). * * GTK calls this automatically if %GTK_DEST_DEFAULT_HIGHLIGHT is set. */ void gtk_drag_highlight (GtkWidget *widget) { g_return_if_fail (GTK_IS_WIDGET (widget)); gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE); } /** * gtk_drag_unhighlight: (method) * @widget: a widget to remove the highlight from * * Removes a highlight set by gtk_drag_highlight() from a widget. */ void gtk_drag_unhighlight (GtkWidget *widget) { g_return_if_fail (GTK_IS_WIDGET (widget)); gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE); } static void gtk_drag_get_data_got_data (GObject *source, GAsyncResult *result, gpointer data) { GTask *task = data; gssize written; GError *error = NULL; guchar *bytes; gsize size; GtkSelectionData *sdata; GtkDropTarget *dest; GdkDrop *drop; GdkDisplay *display; written = g_output_stream_splice_finish (G_OUTPUT_STREAM (source), result, &error); if (written < 0) { g_task_return_error (task, error); g_object_unref (task); return; } bytes = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (source)); size = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (source)); dest = GTK_DROP_TARGET (g_task_get_source_object (task)); drop = GDK_DROP (g_object_get_data (G_OBJECT (task), "drop")); display = GDK_DISPLAY (g_object_get_data (G_OBJECT (task), "display")); sdata = g_slice_new0 (GtkSelectionData); sdata->target = g_task_get_task_data (task); sdata->type = g_task_get_task_data (task); sdata->format = 8; sdata->length = size; sdata->data = bytes ? bytes : (guchar *)g_strdup (""); sdata->display = display; set_drop (dest, drop); g_task_return_pointer (task, sdata, NULL); set_drop (dest, NULL); g_object_unref (task); } static void gtk_drag_get_data_got_stream (GObject *source, GAsyncResult *result, gpointer data) { GTask *task = data; GInputStream *input_stream; GOutputStream *output_stream; GError *error = NULL; const char *mime_type; input_stream = gdk_drop_read_finish (GDK_DROP (source), result, &mime_type, &error); if (input_stream == NULL) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_set_task_data (task, (gpointer)g_intern_string (mime_type), NULL); output_stream = g_memory_output_stream_new_resizable (); g_output_stream_splice_async (output_stream, input_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, NULL, gtk_drag_get_data_got_data, task); g_object_unref (output_stream); g_object_unref (input_stream); } /** * gtk_drop_target_read_selection: * @dest: a #GtkDropTarget * @target: the data format to read * @cancellable: (nullable): a cancellable * @callback: callback to call on completion * @user_data: data to pass to @callback * * Asynchronously reads the dropped data from an ongoing * drag on a #GtkDropTarget, and returns the data in a * #GtkSelectionData object. * * This function is meant for cases where a #GtkSelectionData * object is needed, such as when using the #GtkTreeModel DND * support. In most other cases, the #GdkDrop async read * APIs that return in input stream or #GValue are more * convenient and should be preferred. */ void gtk_drop_target_read_selection (GtkDropTarget *dest, GdkAtom target, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GtkWidget *widget; g_return_if_fail (GTK_IS_DROP_TARGET (dest)); task = g_task_new (dest, NULL, callback, user_data); g_object_set_data_full (G_OBJECT (task), "drop", g_object_ref (dest->drop), g_object_unref); widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)); if (widget) g_object_set_data (G_OBJECT (task), "display", gtk_widget_get_display (widget)); gdk_drop_read_async (dest->drop, (const char *[2]) { target, NULL }, G_PRIORITY_DEFAULT, NULL, gtk_drag_get_data_got_stream, task); } /** * gtk_drop_target_read_selection_finish: * @dest: a #GtkDropTarget * @result: a #GAsyncResult * @error: (allow-none): location to store error information on failure, or %NULL * * Finishes an async drop read operation, see gtk_drop_target_read_selection(). * * Returns: (nullable) (transfer full): the #GtkSelectionData, or %NULL */ GtkSelectionData * gtk_drop_target_read_selection_finish (GtkDropTarget *dest, GAsyncResult *result, GError **error) { g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), NULL); return g_task_propagate_pointer (G_TASK (result), error); }