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 "gtkdroptarget.h"
28
29#include "gtkdropprivate.h"
30#include "gtkeventcontrollerprivate.h"
31#include "gtkintl.h"
32#include "gtkmarshalers.h"
33#include "gtknative.h"
34#include "gtkprivate.h"
35#include "gtktypebuiltins.h"
36
37
38/**
39 * GtkDropTarget:
40 *
41 * `GtkDropTarget` is an event controller to receive Drag-and-Drop operations.
42 *
43 * The most basic way to use a `GtkDropTarget` to receive drops on a
44 * widget is to create it via [ctor@Gtk.DropTarget.new], passing in the
45 * `GType` of the data you want to receive and connect to the
46 * [signal@Gtk.DropTarget::drop] signal to receive the data:
47 *
48 * ```c
49 * static gboolean
50 * on_drop (GtkDropTarget *target,
51 * const GValue *value,
52 * double x,
53 * double y,
54 * gpointer data)
55 * {
56 * MyWidget *self = data;
57 *
58 * // Call the appropriate setter depending on the type of data
59 * // that we received
60 * if (G_VALUE_HOLDS (value, G_TYPE_FILE))
61 * my_widget_set_file (self, g_value_get_object (value));
62 * else if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
63 * my_widget_set_pixbuf (self, g_value_get_object (value));
64 * else
65 * return FALSE;
66 *
67 * return TRUE;
68 * }
69 *
70 * static void
71 * my_widget_init (MyWidget *self)
72 * {
73 * GtkDropTarget *target =
74 * gtk_drop_target_new (G_TYPE_INVALID, GDK_ACTION_COPY);
75 *
76 * // This widget accepts two types of drop types: GFile objects
77 * // and GdkPixbuf objects
78 * gtk_drop_target_set_gtypes (target, (GTypes [2]) {
79 * G_TYPE_FILE,
80 * GDK_TYPE_PIXBUF,
81 * }, 2);
82 *
83 * g_signal_connect (target, "drop", G_CALLBACK (on_drop), self);
84 * gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (target));
85 * }
86 * ```
87 *
88 * `GtkDropTarget` supports more options, such as:
89 *
90 * * rejecting potential drops via the [signal@Gtk.DropTarget::accept] signal
91 * and the [method@Gtk.DropTarget.reject] function to let other drop
92 * targets handle the drop
93 * * tracking an ongoing drag operation before the drop via the
94 * [signal@Gtk.DropTarget::enter], [signal@Gtk.DropTarget::motion] and
95 * [signal@Gtk.DropTarget::leave] signals
96 * * configuring how to receive data by setting the
97 * [property@Gtk.DropTarget:preload] property and listening for its
98 * availability via the [property@Gtk.DropTarget:value] property
99 *
100 * However, `GtkDropTarget` is ultimately modeled in a synchronous way
101 * and only supports data transferred via `GType`. If you want full control
102 * over an ongoing drop, the [class@Gtk.DropTargetAsync] object gives you
103 * this ability.
104 *
105 * While a pointer is dragged over the drop target's widget and the drop
106 * has not been rejected, that widget will receive the
107 * %GTK_STATE_FLAG_DROP_ACTIVE state, which can be used to style the widget.
108 *
109 * If you are not interested in receiving the drop, but just want to update
110 * UI state during a Drag-and-Drop operation (e.g. switching tabs), you can
111 * use [class@Gtk.DropControllerMotion].
112 */
113
114struct _GtkDropTarget
115{
116 GtkEventController parent_object;
117
118 GdkContentFormats *formats;
119 GdkDragAction actions;
120 guint preload : 1;
121
122 guint dropping : 1;
123 graphene_point_t coords;
124 GdkDrop *drop;
125 GCancellable *cancellable; /* NULL unless doing a read of value */
126 GValue value;
127};
128
129struct _GtkDropTargetClass
130{
131 GtkEventControllerClass parent_class;
132
133 gboolean (* accept) (GtkDropTarget *self,
134 GdkDrop *drop);
135 GdkDragAction (* enter) (GtkDropTarget *self,
136 double x,
137 double y);
138 GdkDragAction (* motion) (GtkDropTarget *self,
139 double x,
140 double y);
141 void (* leave) (GtkDropTarget *self,
142 GdkDrop *drop);
143 gboolean (* drop) (GtkDropTarget *self,
144 const GValue *value,
145 double x,
146 double y);
147};
148
149enum {
150 PROP_0,
151 PROP_ACTIONS,
152 PROP_CURRENT_DROP,
153 PROP_DROP,
154 PROP_FORMATS,
155 PROP_PRELOAD,
156 PROP_VALUE,
157 NUM_PROPERTIES
158};
159
160static GParamSpec *properties[NUM_PROPERTIES];
161
162enum {
163 ACCEPT,
164 ENTER,
165 MOTION,
166 LEAVE,
167 DROP,
168 NUM_SIGNALS
169};
170
171static guint signals[NUM_SIGNALS];
172
173G_DEFINE_TYPE (GtkDropTarget, gtk_drop_target, GTK_TYPE_EVENT_CONTROLLER);
174
175static void
176gtk_drop_target_end_drop (GtkDropTarget *self)
177{
178 if (self->drop == NULL)
179 return;
180
181 g_object_freeze_notify (G_OBJECT (self));
182
183 if (self->dropping)
184 {
185 gdk_drop_finish (self: self->drop, action: 0);
186 self->dropping = FALSE;
187 }
188
189 g_clear_object (&self->drop);
190 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_DROP]);
191 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_CURRENT_DROP]);
192
193 if (G_IS_VALUE (&self->value))
194 {
195 g_value_unset (value: &self->value);
196 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_VALUE]);
197 }
198
199 if (self->cancellable)
200 {
201 g_cancellable_cancel (cancellable: self->cancellable);
202 g_clear_object (&self->cancellable);
203 }
204
205 gtk_widget_unset_state_flags (widget: gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)),
206 flags: GTK_STATE_FLAG_DROP_ACTIVE);
207
208 g_object_thaw_notify (G_OBJECT (self));
209}
210
211static GdkDragAction
212make_action_unique (GdkDragAction actions)
213{
214 if (actions & GDK_ACTION_COPY)
215 return GDK_ACTION_COPY;
216
217 if (actions & GDK_ACTION_MOVE)
218 return GDK_ACTION_MOVE;
219
220 if (actions & GDK_ACTION_LINK)
221 return GDK_ACTION_LINK;
222
223 return 0;
224}
225
226static void
227gtk_drop_target_do_drop (GtkDropTarget *self)
228{
229 gboolean success;
230
231 g_assert (self->dropping);
232 g_assert (G_IS_VALUE (&self->value));
233
234 g_signal_emit (instance: self, signal_id: signals[DROP], detail: 0, &self->value, self->coords.x, self->coords.y, &success);
235
236 if (success)
237 gdk_drop_finish (self: self->drop, action: make_action_unique (actions: self->actions & gdk_drop_get_actions (self: self->drop)));
238 else
239 gdk_drop_finish (self: self->drop, action: 0);
240
241 self->dropping = FALSE;
242
243 gtk_drop_target_end_drop (self);
244}
245
246static void
247gtk_drop_target_load_done (GObject *source,
248 GAsyncResult *res,
249 gpointer data)
250{
251 GtkDropTarget *self = data;
252 const GValue *value;
253 GError *error = NULL;
254
255 value = gdk_drop_read_value_finish (GDK_DROP (source), result: res, error: &error);
256 if (value == NULL)
257 {
258 /* If this happens, data/self is invalid */
259 if (g_error_matches (error, G_IO_ERROR, code: G_IO_ERROR_CANCELLED))
260 {
261 g_clear_error (err: &error);
262 return;
263 }
264
265 g_clear_object (&self->cancellable);
266 /* XXX: Should this be a warning? */
267 g_warning ("Failed to receive drop data: %s", error->message);
268 g_clear_error (err: &error);
269 gtk_drop_target_end_drop (self);
270 return;
271 }
272
273 g_clear_object (&self->cancellable);
274 g_value_init (value: &self->value, G_VALUE_TYPE (value));
275 g_value_copy (src_value: value, dest_value: &self->value);
276 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_VALUE]);
277
278 if (self->dropping)
279 gtk_drop_target_do_drop (self);
280}
281
282static gboolean
283gtk_drop_target_load_local (GtkDropTarget *self,
284 GType type)
285{
286 GdkDrag *drag;
287
288 drag = gdk_drop_get_drag (self: self->drop);
289 if (drag == NULL)
290 return FALSE;
291
292 g_value_init (value: &self->value, g_type: type);
293 if (gdk_content_provider_get_value (provider: gdk_drag_get_content (drag),
294 value: &self->value,
295 NULL))
296 return TRUE;
297
298 g_value_unset (value: &self->value);
299 return FALSE;
300}
301
302static gboolean
303gtk_drop_target_load (GtkDropTarget *self)
304{
305 GType type;
306
307 g_assert (self->drop);
308
309 if (G_IS_VALUE (&self->value))
310 return TRUE;
311
312 if (self->cancellable)
313 return FALSE;
314
315 type = gdk_content_formats_match_gtype (first: self->formats, second: gdk_drop_get_formats (self: self->drop));
316
317 if (gtk_drop_target_load_local (self, type))
318 return TRUE;
319
320 self->cancellable = g_cancellable_new ();
321
322 gdk_drop_read_value_async (self: self->drop,
323 type,
324 G_PRIORITY_DEFAULT,
325 cancellable: self->cancellable,
326 callback: gtk_drop_target_load_done,
327 g_object_ref (self));
328 return FALSE;
329}
330
331static void
332gtk_drop_target_start_drop (GtkDropTarget *self,
333 GdkDrop *drop)
334{
335 g_object_freeze_notify (G_OBJECT (self));
336
337 gtk_drop_target_end_drop (self);
338
339 self->drop = g_object_ref (drop);
340 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_DROP]);
341 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_CURRENT_DROP]);
342
343 if (self->preload)
344 gtk_drop_target_load (self);
345
346 gtk_widget_set_state_flags (widget: gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)),
347 flags: GTK_STATE_FLAG_DROP_ACTIVE,
348 FALSE);
349
350 g_object_thaw_notify (G_OBJECT (self));
351}
352
353static gboolean
354gtk_drop_target_accept (GtkDropTarget *self,
355 GdkDrop *drop)
356{
357 if ((gdk_drop_get_actions (self: drop) & gtk_drop_target_get_actions (self)) == 0)
358 return FALSE;
359
360 if (self->formats == NULL)
361 return TRUE;
362
363 return gdk_content_formats_match_gtype (first: self->formats, second: gdk_drop_get_formats (self: drop)) != G_TYPE_INVALID;
364}
365
366static GdkDragAction
367gtk_drop_target_enter (GtkDropTarget *self,
368 double x,
369 double y)
370{
371 return make_action_unique (actions: self->actions & gdk_drop_get_actions (self: self->drop));
372}
373
374static GdkDragAction
375gtk_drop_target_motion (GtkDropTarget *self,
376 double x,
377 double y)
378{
379 return make_action_unique (actions: self->actions & gdk_drop_get_actions (self: self->drop));
380}
381
382static gboolean
383gtk_drop_target_drop (GtkDropTarget *self,
384 const GValue *value,
385 double x,
386 double y)
387{
388 return FALSE;
389}
390
391static gboolean
392gtk_drop_target_filter_event (GtkEventController *controller,
393 GdkEvent *event)
394{
395 switch ((int) gdk_event_get_event_type (event))
396 {
397 case GDK_DRAG_ENTER:
398 case GDK_DRAG_LEAVE:
399 case GDK_DRAG_MOTION:
400 case GDK_DROP_START:
401 return GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_parent_class)->filter_event (controller, event);
402
403 default:;
404 }
405
406 return TRUE;
407}
408
409static gboolean
410gtk_drop_target_handle_event (GtkEventController *controller,
411 GdkEvent *event,
412 double x,
413 double y)
414{
415 GtkDropTarget *self = GTK_DROP_TARGET (controller);
416
417 /* All drops have been rejected. New drops only arrive via crossing
418 * events, so we can: */
419 if (self->drop == NULL)
420 return FALSE;
421
422 switch ((int) gdk_event_get_event_type (event))
423 {
424 case GDK_DRAG_MOTION:
425 {
426 GtkWidget *widget = gtk_event_controller_get_widget (controller);
427 GdkDragAction preferred;
428
429 /* sanity check */
430 g_return_val_if_fail (self->drop == gdk_dnd_event_get_drop (event), FALSE);
431
432 graphene_point_init (p: &self->coords, x, y);
433 g_signal_emit (instance: self, signal_id: signals[MOTION], detail: 0, x, y, &preferred);
434 if (preferred &&
435 gtk_drop_status (drop: self->drop, actions: self->actions, preferred_action: preferred))
436 {
437 gtk_widget_set_state_flags (widget, flags: GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
438 }
439 else
440 {
441 gtk_widget_unset_state_flags (widget, flags: GTK_STATE_FLAG_DROP_ACTIVE);
442 }
443 }
444 return FALSE;
445
446 case GDK_DROP_START:
447 {
448 /* sanity check */
449 g_return_val_if_fail (self->drop == gdk_dnd_event_get_drop (event), FALSE);
450
451 graphene_point_init (p: &self->coords, x, y);
452 self->dropping = TRUE;
453 if (gtk_drop_target_load (self))
454 gtk_drop_target_do_drop (self);
455
456 return TRUE;
457 }
458
459 default:
460 return FALSE;
461 }
462}
463
464static void
465gtk_drop_target_handle_crossing (GtkEventController *controller,
466 const GtkCrossingData *crossing,
467 double x,
468 double y)
469{
470 GtkDropTarget *self = GTK_DROP_TARGET (controller);
471 GtkWidget *widget = gtk_event_controller_get_widget (controller);
472
473 if (crossing->type != GTK_CROSSING_DROP)
474 return;
475
476 /* sanity check */
477 g_warn_if_fail (self->drop == NULL || self->drop == crossing->drop);
478
479 if (crossing->direction == GTK_CROSSING_IN)
480 {
481 gboolean accept = FALSE;
482 GdkDragAction preferred;
483
484 if (self->drop != NULL)
485 return;
486
487 /* if we were a target already but self->drop == NULL, the drop
488 * was rejected already */
489 if (crossing->old_descendent != NULL ||
490 crossing->old_target == widget)
491 return;
492
493 g_signal_emit (instance: self, signal_id: signals[ACCEPT], detail: 0, crossing->drop, &accept);
494 if (!accept)
495 return;
496
497 graphene_point_init (p: &self->coords, x, y);
498 gtk_drop_target_start_drop (self, drop: crossing->drop);
499
500 g_signal_emit (instance: self, signal_id: signals[ENTER], detail: 0, x, y, &preferred);
501 if (preferred &&
502 gtk_drop_status (drop: self->drop, actions: self->actions, preferred_action: preferred))
503 {
504 gtk_widget_set_state_flags (widget, flags: GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
505 }
506 else
507 {
508 gtk_widget_unset_state_flags (widget, flags: GTK_STATE_FLAG_DROP_ACTIVE);
509 }
510 }
511 else
512 {
513 if (crossing->new_descendent != NULL ||
514 crossing->new_target == widget)
515 return;
516
517 g_signal_emit (instance: self, signal_id: signals[LEAVE], detail: 0);
518 if (!self->dropping)
519 gtk_drop_target_end_drop (self);
520 gtk_widget_unset_state_flags (widget, flags: GTK_STATE_FLAG_DROP_ACTIVE);
521 }
522}
523
524static void
525gtk_drop_target_finalize (GObject *object)
526{
527 GtkDropTarget *self = GTK_DROP_TARGET (object);
528
529 g_clear_pointer (&self->formats, gdk_content_formats_unref);
530
531 G_OBJECT_CLASS (gtk_drop_target_parent_class)->finalize (object);
532}
533
534static void
535gtk_drop_target_set_property (GObject *object,
536 guint prop_id,
537 const GValue *value,
538 GParamSpec *pspec)
539{
540 GtkDropTarget *self = GTK_DROP_TARGET (object);
541
542 switch (prop_id)
543 {
544 case PROP_ACTIONS:
545 gtk_drop_target_set_actions (self, actions: g_value_get_flags (value));
546 break;
547
548 case PROP_FORMATS:
549 self->formats = g_value_dup_boxed (value);
550 if (self->formats == NULL)
551 self->formats = gdk_content_formats_new (NULL, n_mime_types: 0);
552 break;
553
554 case PROP_PRELOAD:
555 gtk_drop_target_set_preload (self, preload: g_value_get_boolean (value));
556 break;
557
558 default:
559 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
560 }
561}
562
563static void
564gtk_drop_target_get_property (GObject *object,
565 guint prop_id,
566 GValue *value,
567 GParamSpec *pspec)
568{
569 GtkDropTarget *self = GTK_DROP_TARGET (object);
570
571 switch (prop_id)
572 {
573 case PROP_ACTIONS:
574 g_value_set_flags (value, v_flags: self->actions);
575 break;
576
577 case PROP_DROP:
578 case PROP_CURRENT_DROP:
579 g_value_set_object (value, v_object: self->drop);
580 break;
581
582 case PROP_FORMATS:
583 g_value_set_boxed (value, v_boxed: self->formats);
584 break;
585
586 case PROP_PRELOAD:
587 g_value_set_boolean (value, v_boolean: self->preload);
588 break;
589
590 case PROP_VALUE:
591 if (G_IS_VALUE (&self->value))
592 g_value_set_boxed (value, v_boxed: &self->value);
593 else
594 g_value_set_boxed (value, NULL);
595 break;
596
597 default:
598 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
599 }
600}
601
602static void
603gtk_drop_target_class_init (GtkDropTargetClass *class)
604{
605 GObjectClass *object_class = G_OBJECT_CLASS (class);
606 GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class);
607
608 object_class->finalize = gtk_drop_target_finalize;
609 object_class->set_property = gtk_drop_target_set_property;
610 object_class->get_property = gtk_drop_target_get_property;
611
612 controller_class->handle_event = gtk_drop_target_handle_event;
613 controller_class->filter_event = gtk_drop_target_filter_event;
614 controller_class->handle_crossing = gtk_drop_target_handle_crossing;
615
616 class->accept = gtk_drop_target_accept;
617 class->enter = gtk_drop_target_enter;
618 class->motion = gtk_drop_target_motion;
619 class->drop = gtk_drop_target_drop;
620
621 /**
622 * GtkDropTarget:actions: (attributes org.gtk.Property.get=gtk_drop_target_get_actions org.gtk.Property.set=gtk_drop_target_set_actions)
623 *
624 * The `GdkDragActions` that this drop target supports.
625 */
626 properties[PROP_ACTIONS] =
627 g_param_spec_flags (name: "actions",
628 P_("Actions"),
629 P_("The actions supported by this drop target"),
630 flags_type: GDK_TYPE_DRAG_ACTION, default_value: 0,
631 flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
632
633 /**
634 * GtkDropTarget:drop: (attributes org.gtk.Property.get=gtk_drop_target_get_drop) (skip)
635 *
636 * The `GdkDrop` that is currently being performed.
637 *
638 * Deprecated: 4.4: Use [property@Gtk.DropTarget:current-drop] instead
639 */
640 properties[PROP_DROP] =
641 g_param_spec_object (name: "drop",
642 P_("Drop"),
643 P_("Current drop"),
644 GDK_TYPE_DROP,
645 GTK_PARAM_READABLE | G_PARAM_DEPRECATED);
646
647 /**
648 * GtkDropTarget:current-drop: (attributes org.gtk.Property.get=gtk_drop_target_get_current_drop)
649 *
650 * The `GdkDrop` that is currently being performed.
651 *
652 * Since: 4.4
653 */
654 properties[PROP_CURRENT_DROP] =
655 g_param_spec_object (name: "current-drop",
656 P_("Current drop"),
657 P_("Current drop"),
658 GDK_TYPE_DROP,
659 GTK_PARAM_READABLE);
660
661 /**
662 * GtkDropTarget:formats: (attributes org.gtk.Property.get=gtk_drop_target_get_formats)
663 *
664 * The `GdkContentFormats` that determine the supported data formats.
665 */
666 properties[PROP_FORMATS] =
667 g_param_spec_boxed (name: "formats",
668 P_("Formats"),
669 P_("The supported formats"),
670 GDK_TYPE_CONTENT_FORMATS,
671 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
672
673 /**
674 * GtkDropTarget:preload: (attributes org.gtk.Property.get=gtk_drop_target_get_preload org.gtk.Property.set=gtk_drop_target_set_preload)
675 *
676 * Whether the drop data should be preloaded when the pointer is only
677 * hovering over the widget but has not been released.
678 *
679 * Setting this property allows finer grained reaction to an ongoing
680 * drop at the cost of loading more data.
681 *
682 * The default value for this property is %FALSE to avoid downloading
683 * huge amounts of data by accident.
684 *
685 * For example, if somebody drags a full document of gigabytes of text
686 * from a text editor across a widget with a preloading drop target,
687 * this data will be downloaded, even if the data is ultimately dropped
688 * elsewhere.
689 *
690 * For a lot of data formats, the amount of data is very small (like
691 * %GDK_TYPE_RGBA), so enabling this property does not hurt at all.
692 * And for local-only Drag-and-Drop operations, no data transfer is done,
693 * so enabling it there is free.
694 */
695 properties[PROP_PRELOAD] =
696 g_param_spec_boolean (name: "preload",
697 P_("Preload"),
698 P_("Whether drop data should be preloaded while hovering"),
699 FALSE,
700 flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
701
702 /**
703 * GtkDropTarget:value: (attributes org.gtk.Property.get=gtk_drop_target_get_value)
704 *
705 * The value for this drop operation.
706 *
707 * This is %NULL if the data has not been loaded yet or no drop
708 * operation is going on.
709 *
710 * Data may be available before the [signal@Gtk.DropTarget::drop]
711 * signal gets emitted - for example when the [property@Gtk.DropTarget:preload]
712 * property is set. You can use the ::notify signal to be notified
713 * of available data.
714 */
715 properties[PROP_VALUE] =
716 g_param_spec_boxed (name: "value",
717 P_("Value"),
718 P_("The value for this drop operation"),
719 G_TYPE_VALUE,
720 GTK_PARAM_READABLE);
721
722 g_object_class_install_properties (oclass: object_class, n_pspecs: NUM_PROPERTIES, pspecs: properties);
723
724 /**
725 * GtkDropTarget::accept:
726 * @self: the `GtkDropTarget`
727 * @drop: the `GdkDrop`
728 *
729 * Emitted on the drop site when a drop operation is about to begin.
730 *
731 * If the drop is not accepted, %FALSE will be returned and the drop target
732 * will ignore the drop. If %TRUE is returned, the drop is accepted for now
733 * but may be rejected later via a call to [method@Gtk.DropTarget.reject]
734 * or ultimately by returning %FALSE from a [signal@Gtk.DropTarget::drop]
735 * handler.
736 *
737 * The default handler for this signal decides whether to accept the drop
738 * based on the formats provided by the @drop.
739 *
740 * If the decision whether the drop will be accepted or rejected depends
741 * on the data, this function should return %TRUE, the
742 * [property@Gtk.DropTarget:preload] property should be set and the value
743 * should be inspected via the ::notify:value signal, calling
744 * [method@Gtk.DropTarget.reject] if required.
745 *
746 * Returns: %TRUE if @drop is accepted
747 */
748 signals[ACCEPT] =
749 g_signal_new (I_("accept"),
750 G_TYPE_FROM_CLASS (class),
751 signal_flags: G_SIGNAL_RUN_LAST,
752 G_STRUCT_OFFSET (GtkDropTargetClass, accept),
753 accumulator: g_signal_accumulator_first_wins, NULL,
754 NULL,
755 G_TYPE_BOOLEAN, n_params: 1,
756 GDK_TYPE_DROP);
757
758 /**
759 * GtkDropTarget::enter:
760 * @self: the `GtkDropTarget`
761 * @x: the x coordinate of the current pointer position
762 * @y: the y coordinate of the current pointer position
763 *
764 * Emitted on the drop site when the pointer enters the widget.
765 *
766 * It can be used to set up custom highlighting.
767 *
768 * Returns: Preferred action for this drag operation or 0 if
769 * dropping is not supported at the current @x,@y location.
770 */
771 signals[ENTER] =
772 g_signal_new (I_("enter"),
773 G_TYPE_FROM_CLASS (class),
774 signal_flags: G_SIGNAL_RUN_LAST,
775 G_STRUCT_OFFSET (GtkDropTargetClass, enter),
776 accumulator: g_signal_accumulator_first_wins, NULL,
777 NULL,
778 return_type: GDK_TYPE_DRAG_ACTION, n_params: 2,
779 G_TYPE_DOUBLE, G_TYPE_DOUBLE);
780
781 /**
782 * GtkDropTarget::motion:
783 * @self: the `GtkDropTarget`
784 * @x: the x coordinate of the current pointer position
785 * @y: the y coordinate of the current pointer position
786 *
787 * Emitted while the pointer is moving over the drop target.
788 *
789 * Returns: Preferred action for this drag operation or 0 if
790 * dropping is not supported at the current @x,@y location.
791 */
792 signals[MOTION] =
793 g_signal_new (I_("motion"),
794 G_TYPE_FROM_CLASS (class),
795 signal_flags: G_SIGNAL_RUN_LAST,
796 G_STRUCT_OFFSET (GtkDropTargetClass, motion),
797 accumulator: g_signal_accumulator_first_wins, NULL,
798 NULL,
799 return_type: GDK_TYPE_DRAG_ACTION, n_params: 2,
800 G_TYPE_DOUBLE, G_TYPE_DOUBLE);
801
802 /**
803 * GtkDropTarget::leave:
804 * @self: the `GtkDropTarget`
805 *
806 * Emitted on the drop site when the pointer leaves the widget.
807 *
808 * Its main purpose it to undo things done in
809 * [signal@Gtk.DropTarget::enter].
810 */
811 signals[LEAVE] =
812 g_signal_new (I_("leave"),
813 G_TYPE_FROM_CLASS (class),
814 signal_flags: G_SIGNAL_RUN_LAST,
815 G_STRUCT_OFFSET (GtkDropTargetClass, leave),
816 NULL, NULL,
817 NULL,
818 G_TYPE_NONE, n_params: 0);
819
820 /**
821 * GtkDropTarget::drop:
822 * @self: the `GtkDropTarget`
823 * @value: the `GValue` being dropped
824 * @x: the x coordinate of the current pointer position
825 * @y: the y coordinate of the current pointer position
826 *
827 * Emitted on the drop site when the user drops the data onto the widget.
828 *
829 * The signal handler must determine whether the pointer position is in
830 * a drop zone or not. If it is not in a drop zone, it returns %FALSE
831 * and no further processing is necessary.
832 *
833 * Otherwise, the handler returns %TRUE. In this case, this handler will
834 * accept the drop. The handler is responsible for using the given @value
835 * and performing the drop operation.
836 *
837 * Returns: whether the drop was accepted at the given pointer position
838 */
839 signals[DROP] =
840 g_signal_new (I_("drop"),
841 G_TYPE_FROM_CLASS (class),
842 signal_flags: G_SIGNAL_RUN_LAST,
843 class_offset: 0,
844 accumulator: g_signal_accumulator_first_wins, NULL,
845 NULL,
846 G_TYPE_BOOLEAN, n_params: 3,
847 G_TYPE_VALUE, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
848}
849
850static void
851gtk_drop_target_init (GtkDropTarget *self)
852{
853}
854
855/**
856 * gtk_drop_target_new:
857 * @type: The supported type or %G_TYPE_INVALID
858 * @actions: the supported actions
859 *
860 * Creates a new `GtkDropTarget` object.
861 *
862 * If the drop target should support more than 1 type, pass
863 * %G_TYPE_INVALID for @type and then call
864 * [method@Gtk.DropTarget.set_gtypes].
865 *
866 * Returns: the new `GtkDropTarget`
867 */
868GtkDropTarget *
869gtk_drop_target_new (GType type,
870 GdkDragAction actions)
871{
872 GtkDropTarget *result;
873 GdkContentFormats *formats;
874
875 if (type != G_TYPE_INVALID)
876 formats = gdk_content_formats_new_for_gtype (type);
877 else
878 formats = NULL;
879
880 result = g_object_new (GTK_TYPE_DROP_TARGET,
881 first_property_name: "formats", formats,
882 "actions", actions,
883 NULL);
884
885 g_clear_pointer (&formats, gdk_content_formats_unref);
886
887 return result;
888}
889
890/**
891 * gtk_drop_target_get_formats: (attributes org.gtk.Method.get_property=formats)
892 * @self: a `GtkDropTarget`
893 *
894 * Gets the data formats that this drop target accepts.
895 *
896 * If the result is %NULL, all formats are expected to be supported.
897 *
898 * Returns: (nullable) (transfer none): the supported data formats
899 */
900GdkContentFormats *
901gtk_drop_target_get_formats (GtkDropTarget *self)
902{
903 g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
904
905 return self->formats;
906}
907
908/**
909 * gtk_drop_target_set_gtypes:
910 * @self: a `GtkDropTarget`
911 * @types: (nullable) (transfer none) (array length=n_types): all supported `GType`s
912 * that can be dropped on the target
913 * @n_types: number of @types
914 *
915 * Sets the supported `GTypes` for this drop target.
916 */
917void
918gtk_drop_target_set_gtypes (GtkDropTarget *self,
919 GType *types,
920 gsize n_types)
921{
922 GdkContentFormatsBuilder *builder;
923 gsize i;
924
925 g_return_if_fail (GTK_IS_DROP_TARGET (self));
926 g_return_if_fail (n_types == 0 || types != NULL);
927
928 gdk_content_formats_unref (formats: self->formats);
929
930 builder = gdk_content_formats_builder_new ();
931 for (i = 0; i < n_types; i++)
932 gdk_content_formats_builder_add_gtype (builder, type: types[i]);
933
934 self->formats = gdk_content_formats_builder_free_to_formats (builder);
935
936 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_FORMATS]);
937}
938
939/**
940 * gtk_drop_target_get_gtypes:
941 * @self: a `GtkDropTarget`
942 * @n_types: (out) (optional): the number of `GType`s contained in the
943 * return value
944 *
945 * Gets the list of supported `GType`s that can be dropped on the target.
946 *
947 * If no types have been set, `NULL` will be returned.
948 *
949 * Returns: (transfer none) (nullable) (array length=n_types):
950 * the `G_TYPE_INVALID`-terminated array of types included in
951 * formats
952 */
953const GType *
954gtk_drop_target_get_gtypes (GtkDropTarget *self,
955 gsize *n_types)
956{
957 g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
958
959 return gdk_content_formats_get_gtypes (formats: self->formats, n_gtypes: n_types);
960}
961
962/**
963 * gtk_drop_target_set_actions: (attributes org.gtk.Method.set_property=actions)
964 * @self: a `GtkDropTarget`
965 * @actions: the supported actions
966 *
967 * Sets the actions that this drop target supports.
968 */
969void
970gtk_drop_target_set_actions (GtkDropTarget *self,
971 GdkDragAction actions)
972{
973 g_return_if_fail (GTK_IS_DROP_TARGET (self));
974
975 if (self->actions == actions)
976 return;
977
978 self->actions = actions;
979
980 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_ACTIONS]);
981}
982
983/**
984 * gtk_drop_target_get_actions: (attributes org.gtk.Method.get_property=actions)
985 * @self: a `GtkDropTarget`
986 *
987 * Gets the actions that this drop target supports.
988 *
989 * Returns: the actions that this drop target supports
990 */
991GdkDragAction
992gtk_drop_target_get_actions (GtkDropTarget *self)
993{
994 g_return_val_if_fail (GTK_IS_DROP_TARGET (self), 0);
995
996 return self->actions;
997}
998
999/**
1000 * gtk_drop_target_set_preload: (attributes org.gtk.Method.set_property=preload)
1001 * @self: a `GtkDropTarget`
1002 * @preload: %TRUE to preload drop data
1003 *
1004 * Sets whether data should be preloaded on hover.
1005 */
1006void
1007gtk_drop_target_set_preload (GtkDropTarget *self,
1008 gboolean preload)
1009{
1010 g_return_if_fail (GTK_IS_DROP_TARGET (self));
1011
1012 if (self->preload == preload)
1013 return;
1014
1015 self->preload = preload;
1016
1017 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_PRELOAD]);
1018}
1019
1020/**
1021 * gtk_drop_target_get_preload: (attributes org.gtk.Method.get_property=preload)
1022 * @self: a `GtkDropTarget`
1023 *
1024 * Gets whether data should be preloaded on hover.
1025 *
1026 * Returns: %TRUE if drop data should be preloaded
1027 */
1028gboolean
1029gtk_drop_target_get_preload (GtkDropTarget *self)
1030{
1031 g_return_val_if_fail (GTK_IS_DROP_TARGET (self), 0);
1032
1033 return self->preload;
1034}
1035
1036/**
1037 * gtk_drop_target_get_drop: (attributes org.gtk.Method.get_property=drop)
1038 * @self: a `GtkDropTarget`
1039 *
1040 * Gets the currently handled drop operation.
1041 *
1042 * If no drop operation is going on, %NULL is returned.
1043 *
1044 * Returns: (nullable) (transfer none): The current drop
1045 *
1046 * Deprecated: 4.4: Use [method@Gtk.DropTarget.get_current_drop] instead
1047 */
1048GdkDrop *
1049gtk_drop_target_get_drop (GtkDropTarget *self)
1050{
1051 g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
1052
1053 return self->drop;
1054}
1055
1056/**
1057 * gtk_drop_target_get_current_drop: (attributes org.gtk.Method.get_property=current-drop)
1058 * @self: a `GtkDropTarget`
1059 *
1060 * Gets the currently handled drop operation.
1061 *
1062 * If no drop operation is going on, %NULL is returned.
1063 *
1064 * Returns: (nullable) (transfer none): The current drop
1065 *
1066 * Since: 4.4
1067 */
1068GdkDrop *
1069gtk_drop_target_get_current_drop (GtkDropTarget *self)
1070{
1071 g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
1072
1073 return self->drop;
1074}
1075
1076/**
1077 * gtk_drop_target_get_value: (attributes org.gtk.Method.get_property=value)
1078 * @self: a `GtkDropTarget`
1079 *
1080 * Gets the current drop data, as a `GValue`.
1081 *
1082 * Returns: (nullable) (transfer none): The current drop data
1083 */
1084const GValue *
1085gtk_drop_target_get_value (GtkDropTarget *self)
1086{
1087 g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
1088
1089 if (!G_IS_VALUE (&self->value))
1090 return NULL;
1091
1092 return &self->value;
1093}
1094
1095/**
1096 * gtk_drop_target_reject:
1097 * @self: a `GtkDropTarget`
1098 *
1099 * Rejects the ongoing drop operation.
1100 *
1101 * If no drop operation is ongoing, i.e when [property@Gtk.DropTarget:current-drop]
1102 * is %NULL, this function does nothing.
1103 *
1104 * This function should be used when delaying the decision
1105 * on whether to accept a drag or not until after reading
1106 * the data.
1107 */
1108void
1109gtk_drop_target_reject (GtkDropTarget *self)
1110{
1111 g_return_if_fail (GTK_IS_DROP_TARGET (self));
1112
1113 if (self->drop == NULL)
1114 return;
1115
1116 gtk_drop_target_end_drop (self);
1117}
1118
1119

source code of gtk/gtk/gtkdroptarget.c