1/* GDK - The GIMP Drawing Kit
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/**
26 * GdkDrag:
27 *
28 * The `GdkDrag` object represents the source of an ongoing DND operation.
29 *
30 * A `GdkDrag` is created when a drag is started, and stays alive for duration of
31 * the DND operation. After a drag has been started with [func@Gdk.Drag.begin],
32 * the caller gets informed about the status of the ongoing drag operation
33 * with signals on the `GdkDrag` object.
34 *
35 * GTK provides a higher level abstraction based on top of these functions,
36 * and so they are not normally needed in GTK applications. See the
37 * "Drag and Drop" section of the GTK documentation for more information.
38 */
39
40#include "config.h"
41
42#include "gdkdragprivate.h"
43#include "gdkdisplay.h"
44#include "gdksurface.h"
45#include "gdkintl.h"
46#include "gdkcontentformats.h"
47#include "gdkcontentprovider.h"
48#include "gdkcontentserializer.h"
49#include "gdkcursor.h"
50#include "gdkenumtypes.h"
51#include "gdkeventsprivate.h"
52
53static struct {
54 GdkDragAction action;
55 const char *name;
56 GdkCursor *cursor;
57} drag_cursors[] = {
58 { GDK_ACTION_ASK, "dnd-ask", NULL },
59 { GDK_ACTION_COPY, "dnd-copy", NULL },
60 { GDK_ACTION_MOVE, "dnd-move", NULL },
61 { GDK_ACTION_LINK, "dnd-link", NULL },
62 { 0, "dnd-none", NULL },
63};
64
65enum {
66 PROP_0,
67 PROP_CONTENT,
68 PROP_DEVICE,
69 PROP_DISPLAY,
70 PROP_FORMATS,
71 PROP_SELECTED_ACTION,
72 PROP_ACTIONS,
73 PROP_SURFACE,
74 N_PROPERTIES
75};
76
77enum {
78 CANCEL,
79 DROP_PERFORMED,
80 DND_FINISHED,
81 N_SIGNALS
82};
83
84typedef struct _GdkDragPrivate GdkDragPrivate;
85
86struct _GdkDragPrivate {
87 GdkSurface *surface;
88
89 GdkDisplay *display;
90 GdkDevice *device;
91 GdkContentFormats *formats;
92 GdkContentProvider *content;
93
94 GdkDragAction actions;
95 GdkDragAction selected_action;
96
97 guint drop_done : 1; /* Whether gdk_drag_drop_done() was performed */
98};
99
100static GParamSpec *properties[N_PROPERTIES] = { NULL, };
101static guint signals[N_SIGNALS] = { 0 };
102static GList *drags = NULL;
103
104G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkDrag, gdk_drag, G_TYPE_OBJECT)
105
106/**
107 * gdk_drag_get_display: (attributes org.gtk.Method.get_property=display)
108 * @drag: a `GdkDrag`
109 *
110 * Gets the `GdkDisplay` that the drag object was created for.
111 *
112 * Returns: (transfer none): a `GdkDisplay`
113 */
114GdkDisplay *
115gdk_drag_get_display (GdkDrag *drag)
116{
117 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
118
119 g_return_val_if_fail (GDK_IS_DRAG (drag), NULL);
120
121 return priv->display;
122}
123
124/**
125 * gdk_drag_get_formats: (attributes org.gtk.Method.get_property=formats)
126 * @drag: a `GdkDrag`
127 *
128 * Retrieves the formats supported by this `GdkDrag` object.
129 *
130 * Returns: (transfer none): a `GdkContentFormats`
131 */
132GdkContentFormats *
133gdk_drag_get_formats (GdkDrag *drag)
134{
135 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
136
137 g_return_val_if_fail (GDK_IS_DRAG (drag), NULL);
138
139 return priv->formats;
140}
141
142/**
143 * gdk_drag_get_actions: (attributes org.gtk.Method.get_property=actions)
144 * @drag: a `GdkDrag`
145 *
146 * Determines the bitmask of possible actions proposed by the source.
147 *
148 * Returns: the `GdkDragAction` flags
149 */
150GdkDragAction
151gdk_drag_get_actions (GdkDrag *drag)
152{
153 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
154
155 g_return_val_if_fail (GDK_IS_DRAG (drag), 0);
156
157 return priv->actions;
158}
159
160/**
161 * gdk_drag_get_selected_action: (attributes org.gtk.Method.get_property=selected-action)
162 * @drag: a `GdkDrag`
163 *
164 * Determines the action chosen by the drag destination.
165 *
166 * Returns: a `GdkDragAction` value
167 */
168GdkDragAction
169gdk_drag_get_selected_action (GdkDrag *drag)
170{
171 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
172
173 g_return_val_if_fail (GDK_IS_DRAG (drag), 0);
174
175 return priv->selected_action;
176}
177
178/**
179 * gdk_drag_get_device: (attributes org.gtk.Method.get_property=device)
180 * @drag: a `GdkDrag`
181 *
182 * Returns the `GdkDevice` associated to the `GdkDrag` object.
183 *
184 * Returns: (transfer none): The `GdkDevice` associated to @drag.
185 */
186GdkDevice *
187gdk_drag_get_device (GdkDrag *drag)
188{
189 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
190
191 g_return_val_if_fail (GDK_IS_DRAG (drag), NULL);
192
193 return priv->device;
194}
195
196/**
197 * gdk_drag_get_content: (attributes org.gtk.Method.get_property=content)
198 * @drag: a `GdkDrag`
199 *
200 * Returns the `GdkContentProvider` associated to the `GdkDrag` object.
201 *
202 * Returns: (transfer none): The `GdkContentProvider` associated to @drag.
203 */
204GdkContentProvider *
205gdk_drag_get_content (GdkDrag *drag)
206{
207 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
208
209 g_return_val_if_fail (GDK_IS_DRAG (drag), NULL);
210
211 return priv->content;
212}
213
214/**
215 * gdk_drag_get_surface: (attributes org.gtk.Method.get_property=surface)
216 * @drag: a `GdkDrag`
217 *
218 * Returns the `GdkSurface` where the drag originates.
219 *
220 * Returns: (transfer none): The `GdkSurface` where the drag originates
221 */
222GdkSurface *
223gdk_drag_get_surface (GdkDrag *drag)
224{
225 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
226
227 g_return_val_if_fail (GDK_IS_DRAG (drag), NULL);
228
229 return priv->surface;
230}
231
232static void
233gdk_drag_init (GdkDrag *drag)
234{
235 drags = g_list_prepend (list: drags, data: drag);
236}
237
238static void
239gdk_drag_set_property (GObject *gobject,
240 guint prop_id,
241 const GValue *value,
242 GParamSpec *pspec)
243{
244 GdkDrag *drag = GDK_DRAG (gobject);
245 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
246
247 switch (prop_id)
248 {
249 case PROP_CONTENT:
250 priv->content = g_value_dup_object (value);
251 if (priv->content)
252 {
253 g_assert (priv->formats == NULL);
254 priv->formats = gdk_content_provider_ref_formats (provider: priv->content);
255 }
256 break;
257
258 case PROP_DEVICE:
259 priv->device = g_value_dup_object (value);
260 g_assert (priv->device != NULL);
261 priv->display = gdk_device_get_display (device: priv->device);
262 break;
263
264 case PROP_FORMATS:
265 if (priv->formats)
266 {
267 GdkContentFormats *override = g_value_dup_boxed (value);
268 if (override)
269 {
270 gdk_content_formats_unref (formats: priv->formats);
271 priv->formats = override;
272 }
273 }
274 else
275 {
276 priv->formats = g_value_dup_boxed (value);
277 g_assert (priv->formats != NULL);
278 }
279 break;
280
281 case PROP_SELECTED_ACTION:
282 {
283 GdkDragAction action = g_value_get_flags (value);
284 gdk_drag_set_selected_action (drag, action);
285 }
286 break;
287
288 case PROP_ACTIONS:
289 {
290 GdkDragAction actions = g_value_get_flags (value);
291 gdk_drag_set_actions (drag, actions);
292 }
293 break;
294
295 case PROP_SURFACE:
296 priv->surface = g_value_dup_object (value);
297 g_assert (priv->surface != NULL);
298 break;
299
300 default:
301 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
302 break;
303 }
304}
305
306static void
307gdk_drag_get_property (GObject *gobject,
308 guint prop_id,
309 GValue *value,
310 GParamSpec *pspec)
311{
312 GdkDrag *drag = GDK_DRAG (gobject);
313 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
314
315 switch (prop_id)
316 {
317 case PROP_CONTENT:
318 g_value_set_object (value, v_object: priv->content);
319 break;
320
321 case PROP_DEVICE:
322 g_value_set_object (value, v_object: priv->device);
323 break;
324
325 case PROP_DISPLAY:
326 g_value_set_object (value, v_object: priv->display);
327 break;
328
329 case PROP_FORMATS:
330 g_value_set_boxed (value, v_boxed: priv->formats);
331 break;
332
333 case PROP_SELECTED_ACTION:
334 g_value_set_flags (value, v_flags: priv->selected_action);
335 break;
336
337 case PROP_ACTIONS:
338 g_value_set_flags (value, v_flags: priv->actions);
339 break;
340
341 case PROP_SURFACE:
342 g_value_set_object (value, v_object: priv->surface);
343 break;
344
345 default:
346 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
347 break;
348 }
349}
350
351static void
352gdk_drag_finalize (GObject *object)
353{
354 GdkDrag *drag = GDK_DRAG (object);
355 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
356
357 drags = g_list_remove (list: drags, data: drag);
358
359 g_clear_object (&priv->content);
360 g_clear_pointer (&priv->formats, gdk_content_formats_unref);
361
362 g_clear_object (&priv->surface);
363
364 G_OBJECT_CLASS (gdk_drag_parent_class)->finalize (object);
365}
366
367static void
368gdk_drag_class_init (GdkDragClass *klass)
369{
370 GObjectClass *object_class = G_OBJECT_CLASS (klass);
371
372 object_class->get_property = gdk_drag_get_property;
373 object_class->set_property = gdk_drag_set_property;
374 object_class->finalize = gdk_drag_finalize;
375
376 /**
377 * GdkDrag:content: (attributes org.gtk.Property.get=gdk_drag_get_content)
378 *
379 * The `GdkContentProvider`.
380 */
381 properties[PROP_CONTENT] =
382 g_param_spec_object (name: "content",
383 nick: "Content",
384 blurb: "The content being dragged",
385 GDK_TYPE_CONTENT_PROVIDER,
386 flags: G_PARAM_READWRITE |
387 G_PARAM_CONSTRUCT_ONLY |
388 G_PARAM_STATIC_STRINGS |
389 G_PARAM_EXPLICIT_NOTIFY);
390
391 /**
392 * GdkDrag:device: (attributes org.gtk.Property.get=gdk_drag_get_device)
393 *
394 * The `GdkDevice` that is performing the drag.
395 */
396 properties[PROP_DEVICE] =
397 g_param_spec_object (name: "device",
398 nick: "Device",
399 blurb: "The device performing the drag",
400 GDK_TYPE_DEVICE,
401 flags: G_PARAM_READWRITE |
402 G_PARAM_CONSTRUCT_ONLY |
403 G_PARAM_STATIC_STRINGS |
404 G_PARAM_EXPLICIT_NOTIFY);
405
406 /**
407 * GdkDrag:display: (attributes org.gtk.Property.get=gdk_drag_get_display)
408 *
409 * The `GdkDisplay` that the drag belongs to.
410 */
411 properties[PROP_DISPLAY] =
412 g_param_spec_object (name: "display",
413 nick: "Display",
414 blurb: "Display this drag belongs to",
415 GDK_TYPE_DISPLAY,
416 flags: G_PARAM_READABLE |
417 G_PARAM_STATIC_STRINGS |
418 G_PARAM_EXPLICIT_NOTIFY);
419
420 /**
421 * GdkDrag:formats: (attributes org.gtk.Property.get=gdk_drag_get_formats)
422 *
423 * The possible formats that the drag can provide its data in.
424 */
425 properties[PROP_FORMATS] =
426 g_param_spec_boxed (name: "formats",
427 nick: "Formats",
428 blurb: "The possible formats for data",
429 GDK_TYPE_CONTENT_FORMATS,
430 flags: G_PARAM_READWRITE |
431 G_PARAM_CONSTRUCT_ONLY |
432 G_PARAM_STATIC_STRINGS |
433 G_PARAM_EXPLICIT_NOTIFY);
434
435 /**
436 * GdkDrag:selected-action: (attributes org.gtk.Property.get=gdk_drag_get_selected_action)
437 *
438 * The currently selected action of the drag.
439 */
440 properties[PROP_SELECTED_ACTION] =
441 g_param_spec_flags (name: "selected-action",
442 nick: "Selected action",
443 blurb: "The currently selected action",
444 flags_type: GDK_TYPE_DRAG_ACTION,
445 default_value: 0,
446 flags: G_PARAM_READWRITE |
447 G_PARAM_STATIC_STRINGS |
448 G_PARAM_EXPLICIT_NOTIFY);
449
450 /**
451 * GdkDrag:actions: (attributes org.gtk.Property.get=gdk_drag_get_actions)
452 *
453 * The possible actions of this drag.
454 */
455 properties[PROP_ACTIONS] =
456 g_param_spec_flags (name: "actions",
457 nick: "Actions",
458 blurb: "The possible actions",
459 flags_type: GDK_TYPE_DRAG_ACTION,
460 default_value: 0,
461 flags: G_PARAM_READWRITE |
462 G_PARAM_STATIC_STRINGS |
463 G_PARAM_EXPLICIT_NOTIFY);
464
465 /**
466 * GdkDrag:surface: (attributes org.gtk.Property.get=gdk_drag_get_surface)
467 *
468 * The surface where the drag originates.
469 */
470 properties[PROP_SURFACE] =
471 g_param_spec_object (name: "surface",
472 nick: "Surface",
473 blurb: "The surface where the drag originates",
474 GDK_TYPE_SURFACE,
475 flags: G_PARAM_READWRITE |
476 G_PARAM_CONSTRUCT_ONLY |
477 G_PARAM_STATIC_STRINGS |
478 G_PARAM_EXPLICIT_NOTIFY);
479
480 /**
481 * GdkDrag::cancel:
482 * @drag: The object on which the signal is emitted
483 * @reason: The reason the drag was cancelled
484 *
485 * Emitted when the drag operation is cancelled.
486 */
487 signals[CANCEL] =
488 g_signal_new (signal_name: g_intern_static_string (string: "cancel"),
489 G_TYPE_FROM_CLASS (object_class),
490 signal_flags: G_SIGNAL_RUN_LAST,
491 G_STRUCT_OFFSET (GdkDragClass, cancel),
492 NULL, NULL,
493 NULL,
494 G_TYPE_NONE, n_params: 1, GDK_TYPE_DRAG_CANCEL_REASON);
495
496 /**
497 * GdkDrag::drop-performed:
498 * @drag: The object on which the signal is emitted
499 *
500 * Emitted when the drop operation is performed on an accepting client.
501 */
502 signals[DROP_PERFORMED] =
503 g_signal_new (signal_name: g_intern_static_string (string: "drop-performed"),
504 G_TYPE_FROM_CLASS (object_class),
505 signal_flags: G_SIGNAL_RUN_LAST,
506 G_STRUCT_OFFSET (GdkDragClass, drop_performed),
507 NULL, NULL,
508 NULL,
509 G_TYPE_NONE, n_params: 0);
510
511 /**
512 * GdkDrag::dnd-finished:
513 * @drag: The object on which the signal is emitted
514 *
515 * Emitted when the destination side has finished reading all data.
516 *
517 * The drag object can now free all miscellaneous data.
518 */
519 signals[DND_FINISHED] =
520 g_signal_new (signal_name: g_intern_static_string (string: "dnd-finished"),
521 G_TYPE_FROM_CLASS (object_class),
522 signal_flags: G_SIGNAL_RUN_LAST,
523 G_STRUCT_OFFSET (GdkDragClass, dnd_finished),
524 NULL, NULL,
525 NULL,
526 G_TYPE_NONE, n_params: 0);
527
528 g_object_class_install_properties (oclass: object_class, n_pspecs: N_PROPERTIES, pspecs: properties);
529}
530
531static void
532gdk_drag_write_done (GObject *content,
533 GAsyncResult *result,
534 gpointer task)
535{
536 GError *error = NULL;
537
538 if (gdk_content_provider_write_mime_type_finish (GDK_CONTENT_PROVIDER (content), result, error: &error))
539 g_task_return_boolean (task, TRUE);
540 else
541 g_task_return_error (task, error);
542
543 g_object_unref (object: task);
544}
545
546static void
547gdk_drag_write_serialize_done (GObject *content,
548 GAsyncResult *result,
549 gpointer task)
550{
551 GError *error = NULL;
552
553 if (gdk_content_serialize_finish (result, error: &error))
554 g_task_return_boolean (task, TRUE);
555 else
556 g_task_return_error (task, error);
557
558 g_object_unref (object: task);
559}
560
561void
562gdk_drag_write_async (GdkDrag *drag,
563 const char *mime_type,
564 GOutputStream *stream,
565 int io_priority,
566 GCancellable *cancellable,
567 GAsyncReadyCallback callback,
568 gpointer user_data)
569{
570 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
571 GdkContentFormats *formats, *mime_formats;
572 GTask *task;
573 GType gtype;
574
575 g_return_if_fail (GDK_IS_DRAG (drag));
576 g_return_if_fail (priv->content);
577 g_return_if_fail (mime_type != NULL);
578 g_return_if_fail (mime_type == g_intern_string (mime_type));
579 g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
580 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
581 g_return_if_fail (callback != NULL);
582
583 task = g_task_new (source_object: drag, cancellable, callback, callback_data: user_data);
584 g_task_set_priority (task, priority: io_priority);
585 g_task_set_source_tag (task, gdk_drag_write_async);
586
587 formats = gdk_content_provider_ref_formats (provider: priv->content);
588 if (gdk_content_formats_contain_mime_type (formats, mime_type))
589 {
590 gdk_content_provider_write_mime_type_async (provider: priv->content,
591 mime_type,
592 stream,
593 io_priority,
594 cancellable,
595 callback: gdk_drag_write_done,
596 user_data: task);
597 gdk_content_formats_unref (formats);
598 return;
599 }
600
601 mime_formats = gdk_content_formats_new (mime_types: (const char *[2]) { mime_type, NULL }, n_mime_types: 1);
602 mime_formats = gdk_content_formats_union_serialize_gtypes (formats: mime_formats);
603 gtype = gdk_content_formats_match_gtype (first: formats, second: mime_formats);
604 if (gtype != G_TYPE_INVALID)
605 {
606 GValue value = G_VALUE_INIT;
607 GError *error = NULL;
608
609 g_assert (gtype != G_TYPE_INVALID);
610
611 g_value_init (value: &value, g_type: gtype);
612 if (gdk_content_provider_get_value (provider: priv->content, value: &value, error: &error))
613 {
614 gdk_content_serialize_async (stream,
615 mime_type,
616 value: &value,
617 io_priority,
618 cancellable,
619 callback: gdk_drag_write_serialize_done,
620 g_object_ref (task));
621 }
622 else
623 {
624 g_task_return_error (task, error);
625 }
626
627 g_value_unset (value: &value);
628 }
629 else
630 {
631 g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED,
632 _("No compatible formats to transfer clipboard contents."));
633 }
634
635 gdk_content_formats_unref (formats: mime_formats);
636 gdk_content_formats_unref (formats);
637 g_object_unref (object: task);
638}
639
640gboolean
641gdk_drag_write_finish (GdkDrag *drag,
642 GAsyncResult *result,
643 GError **error)
644{
645 g_return_val_if_fail (g_task_is_valid (result, drag), FALSE);
646 g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_drag_write_async, FALSE);
647
648 return g_task_propagate_boolean (G_TASK (result), error);
649}
650
651void
652gdk_drag_set_actions (GdkDrag *drag,
653 GdkDragAction actions)
654{
655 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
656
657 if (priv->actions == actions)
658 return;
659
660 priv->actions = actions;
661
662 g_object_notify_by_pspec (G_OBJECT (drag), pspec: properties[PROP_ACTIONS]);
663}
664
665void
666gdk_drag_set_selected_action (GdkDrag *drag,
667 GdkDragAction action)
668{
669 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
670 GdkCursor *cursor;
671
672 if (priv->selected_action == action)
673 return;
674
675 priv->selected_action = action;
676
677 cursor = gdk_drag_get_cursor (drag, action);
678 gdk_drag_set_cursor (drag, cursor);
679
680 g_object_notify_by_pspec (G_OBJECT (drag), pspec: properties[PROP_SELECTED_ACTION]);
681}
682
683/**
684 * gdk_drag_get_drag_surface:
685 * @drag: a `GdkDrag`
686 *
687 * Returns the surface on which the drag icon should be rendered
688 * during the drag operation.
689 *
690 * Note that the surface may not be available until the drag operation
691 * has begun. GDK will move the surface in accordance with the ongoing
692 * drag operation. The surface is owned by @drag and will be destroyed
693 * when the drag operation is over.
694 *
695 * Returns: (nullable) (transfer none): the drag surface
696 */
697GdkSurface *
698gdk_drag_get_drag_surface (GdkDrag *drag)
699{
700 g_return_val_if_fail (GDK_IS_DRAG (drag), NULL);
701
702 if (GDK_DRAG_GET_CLASS (drag)->get_drag_surface)
703 return GDK_DRAG_GET_CLASS (drag)->get_drag_surface (drag);
704
705 return NULL;
706}
707
708/**
709 * gdk_drag_set_hotspot:
710 * @drag: a `GdkDrag`
711 * @hot_x: x coordinate of the drag surface hotspot
712 * @hot_y: y coordinate of the drag surface hotspot
713 *
714 * Sets the position of the drag surface that will be kept
715 * under the cursor hotspot.
716 *
717 * Initially, the hotspot is at the top left corner of the drag surface.
718 */
719void
720gdk_drag_set_hotspot (GdkDrag *drag,
721 int hot_x,
722 int hot_y)
723{
724 g_return_if_fail (GDK_IS_DRAG (drag));
725
726 if (GDK_DRAG_GET_CLASS (drag)->set_hotspot)
727 GDK_DRAG_GET_CLASS (drag)->set_hotspot (drag, hot_x, hot_y);
728}
729
730/**
731 * gdk_drag_drop_done:
732 * @drag: a `GdkDrag`
733 * @success: whether the drag was ultimatively successful
734 *
735 * Informs GDK that the drop ended.
736 *
737 * Passing %FALSE for @success may trigger a drag cancellation
738 * animation.
739 *
740 * This function is called by the drag source, and should be the
741 * last call before dropping the reference to the @drag.
742 *
743 * The `GdkDrag` will only take the first [method@Gdk.Drag.drop_done]
744 * call as effective, if this function is called multiple times,
745 * all subsequent calls will be ignored.
746 */
747void
748gdk_drag_drop_done (GdkDrag *drag,
749 gboolean success)
750{
751 GdkDragPrivate *priv = gdk_drag_get_instance_private (self: drag);
752
753 g_return_if_fail (GDK_IS_DRAG (drag));
754
755 if (priv->drop_done)
756 return;
757
758 priv->drop_done = TRUE;
759
760 if (GDK_DRAG_GET_CLASS (drag)->drop_done)
761 GDK_DRAG_GET_CLASS (drag)->drop_done (drag, success);
762}
763
764void
765gdk_drag_set_cursor (GdkDrag *drag,
766 GdkCursor *cursor)
767{
768 g_return_if_fail (GDK_IS_DRAG (drag));
769
770 if (GDK_DRAG_GET_CLASS (drag)->set_cursor)
771 GDK_DRAG_GET_CLASS (drag)->set_cursor (drag, cursor);
772}
773
774void
775gdk_drag_cancel (GdkDrag *drag,
776 GdkDragCancelReason reason)
777{
778 g_return_if_fail (GDK_IS_DRAG (drag));
779
780 g_signal_emit (instance: drag, signal_id: signals[CANCEL], detail: 0, reason);
781}
782
783gboolean
784gdk_drag_handle_source_event (GdkEvent *event)
785{
786 GdkDrag *drag;
787 GList *l;
788
789 for (l = drags; l; l = l->next)
790 {
791 drag = l->data;
792
793 if (!GDK_DRAG_GET_CLASS (drag)->handle_event)
794 continue;
795
796 if (GDK_DRAG_GET_CLASS (drag)->handle_event (drag, event))
797 return TRUE;
798 }
799
800 return FALSE;
801}
802
803GdkCursor *
804gdk_drag_get_cursor (GdkDrag *drag,
805 GdkDragAction action)
806{
807 int i;
808
809 for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
810 if (drag_cursors[i].action == action)
811 break;
812
813 if (drag_cursors[i].cursor == NULL)
814 drag_cursors[i].cursor = gdk_cursor_new_from_name (name: drag_cursors[i].name, NULL);
815
816 return drag_cursors[i].cursor;
817}
818
819/**
820 * gdk_drag_action_is_unique:
821 * @action: a `GdkDragAction`
822 *
823 * Checks if @action represents a single action or includes
824 * multiple actions.
825 *
826 * When @action is 0 - ie no action was given, %TRUE
827 * is returned.
828 *
829 * Returns: %TRUE if exactly one action was given
830 */
831gboolean
832gdk_drag_action_is_unique (GdkDragAction action)
833{
834 return (action & (action - 1)) == 0;
835}
836

source code of gtk/gdk/gdkdrag.c