1/* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25#include "config.h"
26
27#include "gtkdragsourceprivate.h"
28
29#include "gtkgesturedrag.h"
30#include "gtkgesturesingleprivate.h"
31#include "gtkimagedefinitionprivate.h"
32#include "gtknative.h"
33#include "gtkwidgetprivate.h"
34#include "gtkintl.h"
35#include "gtkimageprivate.h"
36#include "gtkdragicon.h"
37#include "gtkprivate.h"
38#include "gtkmarshalers.h"
39#include "gtkicontheme.h"
40#include "gtkpicture.h"
41#include "gtksettingsprivate.h"
42#include "gtkgesturesingle.h"
43
44/**
45 * GtkDragSource:
46 *
47 * `GtkDragSource` is an event controller to initiate Drag-And-Drop operations.
48 *
49 * `GtkDragSource` can be set up with the necessary
50 * ingredients for a DND operation ahead of time. This includes
51 * the source for the data that is being transferred, in the form
52 * of a [class@Gdk.ContentProvider], the desired action, and the icon to
53 * use during the drag operation. After setting it up, the drag
54 * source must be added to a widget as an event controller, using
55 * [method@Gtk.Widget.add_controller].
56 *
57 * ```c
58 * static void
59 * my_widget_init (MyWidget *self)
60 * {
61 * GtkDragSource *drag_source = gtk_drag_source_new ();
62 *
63 * g_signal_connect (drag_source, "prepare", G_CALLBACK (on_drag_prepare), self);
64 * g_signal_connect (drag_source, "drag-begin", G_CALLBACK (on_drag_begin), self);
65 *
66 * gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drag_source));
67 * }
68 * ```
69 *
70 * Setting up the content provider and icon ahead of time only makes
71 * sense when the data does not change. More commonly, you will want
72 * to set them up just in time. To do so, `GtkDragSource` has
73 * [signal@Gtk.DragSource::prepare] and [signal@Gtk.DragSource::drag-begin]
74 * signals.
75 *
76 * The ::prepare signal is emitted before a drag is started, and
77 * can be used to set the content provider and actions that the
78 * drag should be started with.
79 *
80 * ```c
81 * static GdkContentProvider *
82 * on_drag_prepare (GtkDragSource *source,
83 * double x,
84 * double y,
85 * MyWidget *self)
86 * {
87 * // This widget supports two types of content: GFile objects
88 * // and GdkPixbuf objects; GTK will handle the serialization
89 * // of these types automatically
90 * GFile *file = my_widget_get_file (self);
91 * GdkPixbuf *pixbuf = my_widget_get_pixbuf (self);
92 *
93 * return gdk_content_provider_new_union ((GdkContentProvider *[2]) {
94 * gdk_content_provider_new_typed (G_TYPE_FILE, file),
95 * gdk_content_provider_new_typed (GDK_TYPE_PIXBUF, pixbuf),
96 * }, 2);
97 * }
98 * ```
99 *
100 * The ::drag-begin signal is emitted after the `GdkDrag` object has
101 * been created, and can be used to set up the drag icon.
102 *
103 * ```c
104 * static void
105 * on_drag_begin (GtkDragSource *source,
106 * GdkDrag *drag,
107 * MyWidget *self)
108 * {
109 * // Set the widget as the drag icon
110 * GdkPaintable *paintable = gtk_widget_paintable_new (GTK_WIDGET (self));
111 * gtk_drag_source_set_icon (source, paintable, 0, 0);
112 * g_object_unref (paintable);
113 * }
114 * ```
115 *
116 * During the DND operation, `GtkDragSource` emits signals that
117 * can be used to obtain updates about the status of the operation,
118 * but it is not normally necessary to connect to any signals,
119 * except for one case: when the supported actions include
120 * %GDK_ACTION_MOVE, you need to listen for the
121 * [signal@Gtk.DragSource::drag-end] signal and delete the
122 * data after it has been transferred.
123 */
124
125struct _GtkDragSource
126{
127 GtkGestureSingle parent_instance;
128
129 GdkContentProvider *content;
130 GdkDragAction actions;
131
132 GdkPaintable *paintable;
133 int hot_x;
134 int hot_y;
135
136 double start_x;
137 double start_y;
138
139 GdkDrag *drag;
140};
141
142struct _GtkDragSourceClass
143{
144 GtkGestureSingleClass parent_class;
145
146 GdkContentProvider *(* prepare) (GtkDragSource *source,
147 double x,
148 double y);
149};
150
151enum {
152 PROP_CONTENT = 1,
153 PROP_ACTIONS,
154 NUM_PROPERTIES
155};
156
157static GParamSpec *properties[NUM_PROPERTIES];
158
159enum {
160 PREPARE,
161 DRAG_BEGIN,
162 DRAG_END,
163 DRAG_CANCEL,
164 NUM_SIGNALS
165};
166
167static guint signals[NUM_SIGNALS];
168
169static void gtk_drag_source_dnd_finished_cb (GdkDrag *drag,
170 GtkDragSource *source);
171static void gtk_drag_source_cancel_cb (GdkDrag *drag,
172 GdkDragCancelReason reason,
173 GtkDragSource *source);
174
175static GdkContentProvider *gtk_drag_source_prepare (GtkDragSource *source,
176 double x,
177 double y);
178
179static void gtk_drag_source_drag_begin (GtkDragSource *source);
180
181G_DEFINE_TYPE (GtkDragSource, gtk_drag_source, GTK_TYPE_GESTURE_SINGLE);
182
183static void
184gtk_drag_source_init (GtkDragSource *source)
185{
186 source->actions = GDK_ACTION_COPY;
187}
188
189static void
190gtk_drag_source_finalize (GObject *object)
191{
192 GtkDragSource *source = GTK_DRAG_SOURCE (object);
193
194 g_clear_object (&source->content);
195 g_clear_object (&source->paintable);
196
197 G_OBJECT_CLASS (gtk_drag_source_parent_class)->finalize (object);
198}
199
200static void
201gtk_drag_source_set_property (GObject *object,
202 guint prop_id,
203 const GValue *value,
204 GParamSpec *pspec)
205{
206 GtkDragSource *source = GTK_DRAG_SOURCE (object);
207
208 switch (prop_id)
209 {
210 case PROP_CONTENT:
211 gtk_drag_source_set_content (source, content: g_value_get_object (value));
212 break;
213
214 case PROP_ACTIONS:
215 gtk_drag_source_set_actions (source, actions: g_value_get_flags (value));
216 break;
217
218 default:
219 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
220 }
221}
222
223static void
224gtk_drag_source_get_property (GObject *object,
225 guint prop_id,
226 GValue *value,
227 GParamSpec *pspec)
228{
229 GtkDragSource *source = GTK_DRAG_SOURCE (object);
230
231 switch (prop_id)
232 {
233 case PROP_CONTENT:
234 g_value_set_object (value, v_object: gtk_drag_source_get_content (source));
235 break;
236
237 case PROP_ACTIONS:
238 g_value_set_flags (value, v_flags: gtk_drag_source_get_actions (source));
239 break;
240
241 default:
242 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
243 }
244}
245
246static gboolean
247gtk_drag_source_filter_event (GtkEventController *controller,
248 GdkEvent *event)
249{
250 /* Let touchpad swipe events go through, only if they match n-points */
251 if (gdk_event_get_event_type (event) == GDK_TOUCHPAD_SWIPE)
252 {
253 guint n_points;
254 guint n_fingers;
255
256 g_object_get (G_OBJECT (controller), first_property_name: "n-points", &n_points, NULL);
257 n_fingers = gdk_touchpad_event_get_n_fingers (event);
258
259 if (n_fingers == n_points)
260 return FALSE;
261 else
262 return TRUE;
263 }
264
265 return GTK_EVENT_CONTROLLER_CLASS (gtk_drag_source_parent_class)->filter_event (controller, event);
266}
267
268static void
269gtk_drag_source_begin (GtkGesture *gesture,
270 GdkEventSequence *sequence)
271{
272 GtkDragSource *source = GTK_DRAG_SOURCE (gesture);
273 GdkEventSequence *current;
274
275 current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
276
277 gtk_gesture_get_point (gesture, sequence: current, x: &source->start_x, y: &source->start_y);
278}
279
280static void
281gtk_drag_source_update (GtkGesture *gesture,
282 GdkEventSequence *sequence)
283{
284 GtkDragSource *source = GTK_DRAG_SOURCE (gesture);
285 GtkWidget *widget;
286 double x, y;
287
288 if (!gtk_gesture_is_recognized (gesture))
289 return;
290
291 gtk_gesture_get_point (gesture, sequence, x: &x, y: &y);
292
293 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
294
295 if (gtk_drag_check_threshold_double (widget, start_x: source->start_x, start_y: source->start_y, current_x: x, current_y: y))
296 {
297 gtk_drag_source_drag_begin (source);
298 }
299}
300
301static void
302gtk_drag_source_class_init (GtkDragSourceClass *class)
303{
304 GObjectClass *object_class = G_OBJECT_CLASS (class);
305 GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class);
306 GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (class);
307
308 object_class->finalize = gtk_drag_source_finalize;
309 object_class->set_property = gtk_drag_source_set_property;
310 object_class->get_property = gtk_drag_source_get_property;
311
312 controller_class->filter_event = gtk_drag_source_filter_event;
313
314 gesture_class->begin = gtk_drag_source_begin;
315 gesture_class->update = gtk_drag_source_update;
316 gesture_class->end = NULL;
317
318 class->prepare = gtk_drag_source_prepare;
319
320 /**
321 * GtkDragSource:content: (attributes org.gtk.Property.get=gtk_drag_source_get_content org.gtk.Propery.set=gtk_drag_source_set_content)
322 *
323 * The data that is offered by drag operations from this source.
324 */
325 properties[PROP_CONTENT] =
326 g_param_spec_object (name: "content",
327 P_("Content"),
328 P_("The content provider for the dragged data"),
329 GDK_TYPE_CONTENT_PROVIDER,
330 flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
331
332 /**
333 * GtkDragSource:actions: (attributes org.gtk.Property.get=gtk_drag_source_get_actions org.gtk.Property.set=gtk_drag_source_set_actions)
334 *
335 * The actions that are supported by drag operations from the source.
336 *
337 * Note that you must handle the [signal@Gtk.DragSource::drag-end] signal
338 * if the actions include %GDK_ACTION_MOVE.
339 */
340 properties[PROP_ACTIONS] =
341 g_param_spec_flags (name: "actions",
342 P_("Actions"),
343 P_("Supported actions"),
344 flags_type: GDK_TYPE_DRAG_ACTION, default_value: GDK_ACTION_COPY,
345 flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
346
347 g_object_class_install_properties (oclass: object_class, n_pspecs: NUM_PROPERTIES, pspecs: properties);
348
349 /**
350 * GtkDragSource::prepare:
351 * @source: the `GtkDragSource`
352 * @x: the X coordinate of the drag starting point
353 * @y: the Y coordinate fo the drag starting point
354 *
355 * Emitted when a drag is about to be initiated.
356 *
357 * It returns the `GdkContentProvider` to use for the drag that is about
358 * to start. The default handler for this signal returns the value of
359 * the [property@Gtk.DragSource:content] property, so if you set up that
360 * property ahead of time, you don't need to connect to this signal.
361 *
362 * Returns: (transfer full) (nullable): a `GdkContentProvider`
363 */
364 signals[PREPARE] =
365 g_signal_new (I_("prepare"),
366 G_TYPE_FROM_CLASS (class),
367 signal_flags: G_SIGNAL_RUN_LAST,
368 G_STRUCT_OFFSET (GtkDragSourceClass, prepare),
369 accumulator: g_signal_accumulator_first_wins, NULL,
370 NULL,
371 GDK_TYPE_CONTENT_PROVIDER, n_params: 2,
372 G_TYPE_DOUBLE, G_TYPE_DOUBLE);
373
374 /**
375 * GtkDragSource::drag-begin:
376 * @source: the `GtkDragSource`
377 * @drag: the `GdkDrag` object
378 *
379 * Emitted on the drag source when a drag is started.
380 *
381 * It can be used to e.g. set a custom drag icon with
382 * [method@Gtk.DragSource.set_icon].
383 */
384 signals[DRAG_BEGIN] =
385 g_signal_new (I_("drag-begin"),
386 G_TYPE_FROM_CLASS (class),
387 signal_flags: G_SIGNAL_RUN_LAST,
388 class_offset: 0,
389 NULL, NULL,
390 NULL,
391 G_TYPE_NONE, n_params: 1,
392 GDK_TYPE_DRAG);
393
394 /**
395 * GtkDragSource::drag-end:
396 * @source: the `GtkDragSource`
397 * @drag: the `GdkDrag` object
398 * @delete_data: %TRUE if the drag was performing %GDK_ACTION_MOVE,
399 * and the data should be deleted
400 *
401 * Emitted on the drag source when a drag is finished.
402 *
403 * A typical reason to connect to this signal is to undo
404 * things done in [signal@Gtk.DragSource::prepare] or
405 * [signal@Gtk.DragSource::drag-begin] handlers.
406 */
407 signals[DRAG_END] =
408 g_signal_new (I_("drag-end"),
409 G_TYPE_FROM_CLASS (class),
410 signal_flags: G_SIGNAL_RUN_LAST,
411 class_offset: 0,
412 NULL, NULL,
413 NULL,
414 G_TYPE_NONE, n_params: 2,
415 GDK_TYPE_DRAG,
416 G_TYPE_BOOLEAN);
417
418 /**
419 * GtkDragSource::drag-cancel:
420 * @source: the `GtkDragSource`
421 * @drag: the `GdkDrag` object
422 * @reason: information on why the drag failed
423 *
424 * Emitted on the drag source when a drag has failed.
425 *
426 * The signal handler may handle a failed drag operation based on
427 * the type of error. It should return %TRUE if the failure has been handled
428 * and the default "drag operation failed" animation should not be shown.
429 *
430 * Returns: %TRUE if the failed drag operation has been already handled
431 */
432 signals[DRAG_CANCEL] =
433 g_signal_new (I_("drag-cancel"),
434 G_TYPE_FROM_CLASS (class),
435 signal_flags: G_SIGNAL_RUN_LAST,
436 class_offset: 0,
437 accumulator: _gtk_boolean_handled_accumulator, NULL,
438 c_marshaller: _gtk_marshal_BOOLEAN__OBJECT_ENUM,
439 G_TYPE_BOOLEAN, n_params: 2,
440 GDK_TYPE_DRAG,
441 GDK_TYPE_DRAG_CANCEL_REASON);
442}
443
444static GdkContentProvider *
445gtk_drag_source_prepare (GtkDragSource *source,
446 double x,
447 double y)
448{
449 if (source->actions == 0)
450 return NULL;
451
452 if (source->content == NULL)
453 return NULL;
454
455 return g_object_ref (source->content);
456}
457
458static void
459drag_end (GtkDragSource *source,
460 gboolean success)
461{
462 gboolean delete_data;
463
464 g_signal_handlers_disconnect_by_func (source->drag, gtk_drag_source_dnd_finished_cb, source);
465 g_signal_handlers_disconnect_by_func (source->drag, gtk_drag_source_cancel_cb, source);
466
467 delete_data = success && gdk_drag_get_selected_action (drag: source->drag) == GDK_ACTION_MOVE;
468
469 g_signal_emit (instance: source, signal_id: signals[DRAG_END], detail: 0, source->drag, delete_data);
470
471 gdk_drag_drop_done (drag: source->drag, success);
472 g_clear_object (&source->drag);
473 g_object_unref (object: source);
474}
475
476static void
477gtk_drag_source_dnd_finished_cb (GdkDrag *drag,
478 GtkDragSource *source)
479{
480 drag_end (source, TRUE);
481}
482
483static void
484gtk_drag_source_cancel_cb (GdkDrag *drag,
485 GdkDragCancelReason reason,
486 GtkDragSource *source)
487{
488 gboolean success = FALSE;
489
490 g_signal_emit (instance: source, signal_id: signals[DRAG_CANCEL], detail: 0, source->drag, reason, &success);
491 drag_end (source, success);
492}
493
494static void
495gtk_drag_source_ensure_icon (GtkDragSource *self,
496 GdkDrag *drag)
497{
498 GdkContentProvider *provider;
499 GtkWidget *icon, *child;
500 GdkContentFormats *formats;
501 const GType *types;
502 gsize i, n_types;
503
504 icon = gtk_drag_icon_get_for_drag (drag);
505 /* If an icon has been set already, we don't need to set one. */
506 if (gtk_drag_icon_get_child (self: GTK_DRAG_ICON (ptr: icon)))
507 return;
508
509 if (self->paintable)
510 {
511 gtk_drag_icon_set_from_paintable (drag,
512 paintable: self->paintable,
513 hot_x: self->hot_x,
514 hot_y: self->hot_y);
515 return;
516 }
517
518 gdk_drag_set_hotspot (drag, hot_x: -2, hot_y: -2);
519
520 provider = gdk_drag_get_content (drag);
521 formats = gdk_content_provider_ref_formats (provider);
522 types = gdk_content_formats_get_gtypes (formats, n_gtypes: &n_types);
523 for (i = 0; i < n_types; i++)
524 {
525 GValue value = G_VALUE_INIT;
526
527 g_value_init (value: &value, g_type: types[i]);
528 if (gdk_content_provider_get_value (provider, value: &value, NULL))
529 {
530 child = gtk_drag_icon_create_widget_for_value (value: &value);
531
532 if (child)
533 {
534 gtk_drag_icon_set_child (self: GTK_DRAG_ICON (ptr: icon), child);
535 g_value_unset (value: &value);
536 gdk_content_formats_unref (formats);
537 return;
538 }
539 }
540 g_value_unset (value: &value);
541 }
542
543 gdk_content_formats_unref (formats);
544 child = gtk_image_new_from_icon_name (icon_name: "text-x-generic");
545 gtk_image_set_icon_size (GTK_IMAGE (child), icon_size: GTK_ICON_SIZE_LARGE);
546 gtk_drag_icon_set_child (self: GTK_DRAG_ICON (ptr: icon), child);
547}
548
549static void
550gtk_drag_source_drag_begin (GtkDragSource *source)
551{
552 GtkWidget *widget;
553 GdkDevice *device, *pointer;
554 GdkSeat *seat;
555 double x, y;
556 GtkNative *native;
557 GdkSurface *surface;
558 double px, py;
559 int dx, dy;
560 GdkContentProvider *content = NULL;
561
562 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source));
563 device = gtk_gesture_get_device (GTK_GESTURE (source));
564 seat = gdk_device_get_seat (device);
565
566 if (device == gdk_seat_get_keyboard (seat))
567 pointer = gdk_seat_get_pointer (seat);
568 else
569 pointer = device;
570
571 native = gtk_widget_get_native (widget);
572 surface = gtk_native_get_surface (self: native);
573
574 gtk_widget_translate_coordinates (src_widget: widget, GTK_WIDGET (native), src_x: source->start_x, src_y: source->start_y, dest_x: &x, dest_y: &y);
575 gdk_surface_get_device_position (surface, device: pointer, x: &px, y: &py, NULL);
576
577 dx = round (x: px - x);
578 dy = round (x: py - y);
579
580 g_signal_emit (instance: source, signal_id: signals[PREPARE], detail: 0, source->start_x, source->start_y, &content);
581 if (!content)
582 return;
583
584 source->drag = gdk_drag_begin (surface, device: pointer, content, actions: source->actions, dx, dy);
585
586 g_object_unref (object: content);
587
588 if (source->drag == NULL)
589 return;
590
591 gtk_widget_reset_controllers (widget);
592
593 g_signal_emit (instance: source, signal_id: signals[DRAG_BEGIN], detail: 0, source->drag);
594
595 gtk_drag_source_ensure_icon (self: source, drag: source->drag);
596
597 /* Keep the source alive until the drag is done */
598 g_object_ref (source);
599
600 g_signal_connect (source->drag, "dnd-finished",
601 G_CALLBACK (gtk_drag_source_dnd_finished_cb), source);
602 g_signal_connect (source->drag, "cancel",
603 G_CALLBACK (gtk_drag_source_cancel_cb), source);
604}
605
606/**
607 * gtk_drag_source_new:
608 *
609 * Creates a new `GtkDragSource` object.
610 *
611 * Returns: the new `GtkDragSource`
612 */
613GtkDragSource *
614gtk_drag_source_new (void)
615{
616 return g_object_new (GTK_TYPE_DRAG_SOURCE, NULL);
617}
618
619/**
620 * gtk_drag_source_get_content: (attributes org.gtk.Method.get_property=content)
621 * @source: a `GtkDragSource`
622 *
623 * Gets the current content provider of a `GtkDragSource`.
624 *
625 * Returns: (nullable) (transfer none): the `GdkContentProvider` of @source
626 */
627GdkContentProvider *
628gtk_drag_source_get_content (GtkDragSource *source)
629{
630 g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), NULL);
631
632 return source->content;
633}
634
635/**
636 * gtk_drag_source_set_content: (attributes org.gtk.Method.set_property=content)
637 * @source: a `GtkDragSource`
638 * @content: (nullable): a `GdkContentProvider`
639 *
640 * Sets a content provider on a `GtkDragSource`.
641 *
642 * When the data is requested in the cause of a DND operation,
643 * it will be obtained from the content provider.
644 *
645 * This function can be called before a drag is started,
646 * or in a handler for the [signal@Gtk.DragSource::prepare] signal.
647 *
648 * You may consider setting the content provider back to
649 * %NULL in a [signal@Gtk.DragSource::drag-end] signal handler.
650 */
651void
652gtk_drag_source_set_content (GtkDragSource *source,
653 GdkContentProvider *content)
654{
655 g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
656
657 if (!g_set_object (&source->content, content))
658 return;
659
660 g_object_notify_by_pspec (G_OBJECT (source), pspec: properties[PROP_CONTENT]);
661}
662
663/**
664 * gtk_drag_source_get_actions: (attributes org.gtk.Method.get_property=actions)
665 * @source: a `GtkDragSource`
666 *
667 * Gets the actions that are currently set on the `GtkDragSource`.
668 *
669 * Returns: the actions set on @source
670 */
671GdkDragAction
672gtk_drag_source_get_actions (GtkDragSource *source)
673{
674 g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), 0);
675
676 return source->actions;
677}
678
679/**
680 * gtk_drag_source_set_actions: (attributes org.gtk.Method.set_property=actions)
681 * @source: a `GtkDragSource`
682 * @actions: the actions to offer
683 *
684 * Sets the actions on the `GtkDragSource`.
685 *
686 * During a DND operation, the actions are offered to potential
687 * drop targets. If @actions include %GDK_ACTION_MOVE, you need
688 * to listen to the [signal@Gtk.DragSource::drag-end] signal and
689 * handle @delete_data being %TRUE.
690 *
691 * This function can be called before a drag is started,
692 * or in a handler for the [signal@Gtk.DragSource::prepare] signal.
693 */
694void
695gtk_drag_source_set_actions (GtkDragSource *source,
696 GdkDragAction actions)
697{
698 g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
699
700 if (source->actions == actions)
701 return;
702
703 source->actions = actions;
704
705 g_object_notify_by_pspec (G_OBJECT (source), pspec: properties[PROP_ACTIONS]);
706}
707
708/**
709 * gtk_drag_source_set_icon:
710 * @source: a `GtkDragSource`
711 * @paintable: (nullable): the `GdkPaintable` to use as icon
712 * @hot_x: the hotspot X coordinate on the icon
713 * @hot_y: the hotspot Y coordinate on the icon
714 *
715 * Sets a paintable to use as icon during DND operations.
716 *
717 * The hotspot coordinates determine the point on the icon
718 * that gets aligned with the hotspot of the cursor.
719 *
720 * If @paintable is %NULL, a default icon is used.
721 *
722 * This function can be called before a drag is started, or in
723 * a [signal@Gtk.DragSource::prepare] or
724 * [signal@Gtk.DragSource::drag-begin] signal handler.
725 */
726void
727gtk_drag_source_set_icon (GtkDragSource *source,
728 GdkPaintable *paintable,
729 int hot_x,
730 int hot_y)
731{
732 g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
733
734 g_set_object (&source->paintable, paintable);
735
736 source->hot_x = hot_x;
737 source->hot_y = hot_y;
738}
739
740/**
741 * gtk_drag_source_get_drag:
742 * @source: a `GtkDragSource`
743 *
744 * Returns the underlying `GdkDrag` object for an ongoing drag.
745 *
746 * Returns: (nullable) (transfer none): the `GdkDrag` of the current
747 * drag operation
748 */
749GdkDrag *
750gtk_drag_source_get_drag (GtkDragSource *source)
751{
752 g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), NULL);
753
754 return source->drag;
755}
756
757/**
758 * gtk_drag_source_drag_cancel:
759 * @source: a `GtkDragSource`
760 *
761 * Cancels a currently ongoing drag operation.
762 */
763void
764gtk_drag_source_drag_cancel (GtkDragSource *source)
765{
766 g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
767
768 if (source->drag)
769 {
770 gboolean success = FALSE;
771
772 g_signal_emit (instance: source, signal_id: signals[DRAG_CANCEL], detail: 0, source->drag, GDK_DRAG_CANCEL_ERROR, &success);
773 drag_end (source, FALSE);
774 }
775}
776
777/**
778 * gtk_drag_check_threshold: (method)
779 * @widget: a `GtkWidget`
780 * @start_x: X coordinate of start of drag
781 * @start_y: Y coordinate of start of drag
782 * @current_x: current X coordinate
783 * @current_y: current Y coordinate
784 *
785 * Checks to see if a drag movement has passed the GTK drag threshold.
786 *
787 * Returns: %TRUE if the drag threshold has been passed.
788 */
789gboolean
790gtk_drag_check_threshold (GtkWidget *widget,
791 int start_x,
792 int start_y,
793 int current_x,
794 int current_y)
795{
796 int drag_threshold;
797
798 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
799
800 drag_threshold = gtk_settings_get_dnd_drag_threshold (settings: gtk_widget_get_settings (widget));
801
802 return (ABS (current_x - start_x) > drag_threshold ||
803 ABS (current_y - start_y) > drag_threshold);
804}
805
806gboolean
807gtk_drag_check_threshold_double (GtkWidget *widget,
808 double start_x,
809 double start_y,
810 double current_x,
811 double current_y)
812{
813 int drag_threshold;
814
815 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
816
817 drag_threshold = gtk_settings_get_dnd_drag_threshold (settings: gtk_widget_get_settings (widget));
818
819 return (ABS (current_x - start_x) > drag_threshold ||
820 ABS (current_y - start_y) > drag_threshold);
821}
822

source code of gtk/gtk/gtkdragsource.c