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 "gtkdroptargetasync.h" |
28 | |
29 | #include "gtkdropprivate.h" |
30 | #include "gtkeventcontrollerprivate.h" |
31 | #include "gtkintl.h" |
32 | #include "gtkmarshalers.h" |
33 | #include "gtknative.h" |
34 | #include "gtktypebuiltins.h" |
35 | |
36 | |
37 | /** |
38 | * GtkDropTargetAsync: |
39 | * |
40 | * `GtkDropTargetAsync` is an event controller to receive Drag-and-Drop |
41 | * operations, asynchronously. |
42 | * |
43 | * It is the more complete but also more complex method of handling drop |
44 | * operations compared to [class@Gtk.DropTarget], and you should only use |
45 | * it if `GtkDropTarget` doesn't provide all the features you need. |
46 | * |
47 | * To use a `GtkDropTargetAsync` to receive drops on a widget, you create |
48 | * a `GtkDropTargetAsync` object, configure which data formats and actions |
49 | * you support, connect to its signals, and then attach it to the widget |
50 | * with [method@Gtk.Widget.add_controller]. |
51 | * |
52 | * During a drag operation, the first signal that a `GtkDropTargetAsync` |
53 | * emits is [signal@Gtk.DropTargetAsync::accept], which is meant to determine |
54 | * whether the target is a possible drop site for the ongoing drop. The |
55 | * default handler for the ::accept signal accepts the drop if it finds |
56 | * a compatible data format and an action that is supported on both sides. |
57 | * |
58 | * If it is, and the widget becomes a target, you will receive a |
59 | * [signal@Gtk.DropTargetAsync::drag-enter] signal, followed by |
60 | * [signal@Gtk.DropTargetAsync::drag-motion] signals as the pointer moves, |
61 | * optionally a [signal@Gtk.DropTargetAsync::drop] signal when a drop happens, |
62 | * and finally a [signal@Gtk.DropTargetAsync::drag-leave] signal when the |
63 | * pointer moves off the widget. |
64 | * |
65 | * The ::drag-enter and ::drag-motion handler return a `GdkDragAction` |
66 | * to update the status of the ongoing operation. The ::drop handler |
67 | * should decide if it ultimately accepts the drop and if it does, it |
68 | * should initiate the data transfer and finish the operation by calling |
69 | * [method@Gdk.Drop.finish]. |
70 | * |
71 | * Between the ::drag-enter and ::drag-leave signals the widget is a |
72 | * current drop target, and will receive the %GTK_STATE_FLAG_DROP_ACTIVE |
73 | * state, which can be used by themes to style the widget as a drop target. |
74 | */ |
75 | |
76 | struct _GtkDropTargetAsync |
77 | { |
78 | GtkEventController parent_object; |
79 | |
80 | GdkContentFormats *formats; |
81 | GdkDragAction actions; |
82 | |
83 | GdkDrop *drop; |
84 | gboolean rejected; |
85 | }; |
86 | |
87 | struct _GtkDropTargetAsyncClass |
88 | { |
89 | GtkEventControllerClass parent_class; |
90 | |
91 | gboolean (* accept) (GtkDropTargetAsync *self, |
92 | GdkDrop *drop); |
93 | GdkDragAction (* drag_enter) (GtkDropTargetAsync *self, |
94 | GdkDrop *drop, |
95 | double x, |
96 | double y); |
97 | GdkDragAction (* drag_motion) (GtkDropTargetAsync *self, |
98 | GdkDrop *drop, |
99 | double x, |
100 | double y); |
101 | void (* drag_leave) (GtkDropTargetAsync *self, |
102 | GdkDrop *drop); |
103 | gboolean (* drop) (GtkDropTargetAsync *self, |
104 | GdkDrop *drop, |
105 | double x, |
106 | double y); |
107 | }; |
108 | |
109 | enum { |
110 | PROP_0, |
111 | PROP_ACTIONS, |
112 | PROP_FORMATS, |
113 | NUM_PROPERTIES |
114 | }; |
115 | |
116 | static GParamSpec *properties[NUM_PROPERTIES]; |
117 | |
118 | enum { |
119 | ACCEPT, |
120 | DRAG_ENTER, |
121 | DRAG_MOTION, |
122 | DRAG_LEAVE, |
123 | DROP, |
124 | NUM_SIGNALS |
125 | }; |
126 | |
127 | static guint signals[NUM_SIGNALS]; |
128 | |
129 | G_DEFINE_TYPE (GtkDropTargetAsync, gtk_drop_target_async, GTK_TYPE_EVENT_CONTROLLER); |
130 | |
131 | static gboolean |
132 | gtk_drop_target_async_accept (GtkDropTargetAsync *self, |
133 | GdkDrop *drop) |
134 | { |
135 | if ((gdk_drop_get_actions (self: drop) & self->actions) == 0) |
136 | return FALSE; |
137 | |
138 | if (self->formats == NULL) |
139 | return TRUE; |
140 | |
141 | return gdk_content_formats_match (first: self->formats, second: gdk_drop_get_formats (self: drop)); |
142 | } |
143 | |
144 | static GdkDragAction |
145 | make_action_unique (GdkDragAction actions) |
146 | { |
147 | if (actions & GDK_ACTION_COPY) |
148 | return GDK_ACTION_COPY; |
149 | |
150 | if (actions & GDK_ACTION_MOVE) |
151 | return GDK_ACTION_MOVE; |
152 | |
153 | if (actions & GDK_ACTION_LINK) |
154 | return GDK_ACTION_LINK; |
155 | |
156 | return 0; |
157 | } |
158 | |
159 | static GdkDragAction |
160 | gtk_drop_target_async_drag_enter (GtkDropTargetAsync *self, |
161 | GdkDrop *drop, |
162 | double x, |
163 | double y) |
164 | { |
165 | return make_action_unique (actions: self->actions & gdk_drop_get_actions (self: drop)); |
166 | } |
167 | |
168 | static GdkDragAction |
169 | gtk_drop_target_async_drag_motion (GtkDropTargetAsync *self, |
170 | GdkDrop *drop, |
171 | double x, |
172 | double y) |
173 | { |
174 | return make_action_unique (actions: self->actions & gdk_drop_get_actions (self: drop)); |
175 | } |
176 | |
177 | static gboolean |
178 | gtk_drop_target_async_drop (GtkDropTargetAsync *self, |
179 | GdkDrop *drop, |
180 | double x, |
181 | double y) |
182 | { |
183 | return FALSE; |
184 | } |
185 | |
186 | static gboolean |
187 | gtk_drop_target_async_filter_event (GtkEventController *controller, |
188 | GdkEvent *event) |
189 | { |
190 | switch ((int)gdk_event_get_event_type (event)) |
191 | { |
192 | case GDK_DRAG_ENTER: |
193 | case GDK_DRAG_LEAVE: |
194 | case GDK_DRAG_MOTION: |
195 | case GDK_DROP_START: |
196 | return GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_async_parent_class)->filter_event (controller, event); |
197 | |
198 | default:; |
199 | } |
200 | |
201 | return TRUE; |
202 | } |
203 | |
204 | static gboolean |
205 | gtk_drop_target_async_handle_event (GtkEventController *controller, |
206 | GdkEvent *event, |
207 | double x, |
208 | double y) |
209 | { |
210 | GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (controller); |
211 | GdkDrop *drop; |
212 | |
213 | switch ((int) gdk_event_get_event_type (event)) |
214 | { |
215 | case GDK_DRAG_MOTION: |
216 | { |
217 | GtkWidget *widget = gtk_event_controller_get_widget (controller); |
218 | GdkDragAction preferred_action; |
219 | |
220 | drop = gdk_dnd_event_get_drop (event); |
221 | /* sanity check */ |
222 | g_return_val_if_fail (self->drop == drop, FALSE); |
223 | if (self->rejected) |
224 | return FALSE; |
225 | |
226 | g_signal_emit (instance: self, signal_id: signals[DRAG_MOTION], detail: 0, drop, x, y, &preferred_action); |
227 | if (preferred_action && |
228 | gtk_drop_status (drop: self->drop, actions: self->actions, preferred_action)) |
229 | { |
230 | gtk_widget_set_state_flags (widget, flags: GTK_STATE_FLAG_DROP_ACTIVE, FALSE); |
231 | } |
232 | else |
233 | { |
234 | gtk_widget_unset_state_flags (widget, flags: GTK_STATE_FLAG_DROP_ACTIVE); |
235 | } |
236 | } |
237 | return FALSE; |
238 | |
239 | case GDK_DROP_START: |
240 | { |
241 | gboolean handled; |
242 | |
243 | drop = gdk_dnd_event_get_drop (event); |
244 | /* sanity check */ |
245 | g_return_val_if_fail (self->drop == drop, FALSE); |
246 | if (self->rejected) |
247 | return FALSE; |
248 | |
249 | g_signal_emit (instance: self, signal_id: signals[DROP], detail: 0, self->drop, x, y, &handled); |
250 | return handled; |
251 | } |
252 | |
253 | default: |
254 | return FALSE; |
255 | } |
256 | } |
257 | |
258 | static void |
259 | gtk_drop_target_async_handle_crossing (GtkEventController *controller, |
260 | const GtkCrossingData *crossing, |
261 | double x, |
262 | double y) |
263 | { |
264 | GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (controller); |
265 | GtkWidget *widget = gtk_event_controller_get_widget (controller); |
266 | |
267 | if (crossing->type != GTK_CROSSING_DROP) |
268 | return; |
269 | |
270 | /* sanity check */ |
271 | g_warn_if_fail (self->drop == NULL || self->drop == crossing->drop); |
272 | |
273 | if (crossing->direction == GTK_CROSSING_IN) |
274 | { |
275 | gboolean accept = FALSE; |
276 | GdkDragAction preferred_action; |
277 | |
278 | if (self->drop != NULL) |
279 | return; |
280 | |
281 | self->drop = g_object_ref (crossing->drop); |
282 | |
283 | g_signal_emit (instance: self, signal_id: signals[ACCEPT], detail: 0, self->drop, &accept); |
284 | self->rejected = !accept; |
285 | if (self->rejected) |
286 | return; |
287 | |
288 | g_signal_emit (instance: self, signal_id: signals[DRAG_ENTER], detail: 0, self->drop, x, y, &preferred_action); |
289 | if (preferred_action && |
290 | gtk_drop_status (drop: self->drop, actions: self->actions, preferred_action)) |
291 | { |
292 | gtk_widget_set_state_flags (widget, flags: GTK_STATE_FLAG_DROP_ACTIVE, FALSE); |
293 | } |
294 | } |
295 | else |
296 | { |
297 | if (crossing->new_descendent != NULL || |
298 | crossing->new_target == widget) |
299 | return; |
300 | |
301 | g_signal_emit (instance: self, signal_id: signals[DRAG_LEAVE], detail: 0, self->drop); |
302 | g_clear_object (&self->drop); |
303 | gtk_widget_unset_state_flags (widget, flags: GTK_STATE_FLAG_DROP_ACTIVE); |
304 | } |
305 | } |
306 | |
307 | static void |
308 | gtk_drop_target_async_finalize (GObject *object) |
309 | { |
310 | GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object); |
311 | |
312 | g_clear_pointer (&self->formats, gdk_content_formats_unref); |
313 | |
314 | G_OBJECT_CLASS (gtk_drop_target_async_parent_class)->finalize (object); |
315 | } |
316 | |
317 | static void |
318 | gtk_drop_target_async_set_property (GObject *object, |
319 | guint prop_id, |
320 | const GValue *value, |
321 | GParamSpec *pspec) |
322 | { |
323 | GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object); |
324 | |
325 | switch (prop_id) |
326 | { |
327 | case PROP_ACTIONS: |
328 | gtk_drop_target_async_set_actions (self, actions: g_value_get_flags (value)); |
329 | break; |
330 | |
331 | case PROP_FORMATS: |
332 | gtk_drop_target_async_set_formats (self, formats: g_value_get_boxed (value)); |
333 | break; |
334 | |
335 | default: |
336 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
337 | } |
338 | } |
339 | |
340 | static void |
341 | gtk_drop_target_async_get_property (GObject *object, |
342 | guint prop_id, |
343 | GValue *value, |
344 | GParamSpec *pspec) |
345 | { |
346 | GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object); |
347 | |
348 | switch (prop_id) |
349 | { |
350 | case PROP_ACTIONS: |
351 | g_value_set_flags (value, v_flags: gtk_drop_target_async_get_actions (self)); |
352 | break; |
353 | |
354 | case PROP_FORMATS: |
355 | g_value_set_boxed (value, v_boxed: gtk_drop_target_async_get_formats (self)); |
356 | break; |
357 | |
358 | default: |
359 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
360 | } |
361 | } |
362 | |
363 | static void |
364 | gtk_drop_target_async_class_init (GtkDropTargetAsyncClass *class) |
365 | { |
366 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
367 | GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class); |
368 | |
369 | object_class->finalize = gtk_drop_target_async_finalize; |
370 | object_class->set_property = gtk_drop_target_async_set_property; |
371 | object_class->get_property = gtk_drop_target_async_get_property; |
372 | |
373 | controller_class->handle_event = gtk_drop_target_async_handle_event; |
374 | controller_class->filter_event = gtk_drop_target_async_filter_event; |
375 | controller_class->handle_crossing = gtk_drop_target_async_handle_crossing; |
376 | |
377 | class->accept = gtk_drop_target_async_accept; |
378 | class->drag_enter = gtk_drop_target_async_drag_enter; |
379 | class->drag_motion = gtk_drop_target_async_drag_motion; |
380 | class->drop = gtk_drop_target_async_drop; |
381 | |
382 | /** |
383 | * GtkDropTargetAsync:actions: (attributes org.gtk.Property.get=gtk_drop_target_async_get_actions org.gtk.Property.set=gtk_drop_target_async_set_actions) |
384 | * |
385 | * The `GdkDragActions` that this drop target supports. |
386 | */ |
387 | properties[PROP_ACTIONS] = |
388 | g_param_spec_flags (name: "actions" , P_("Actions" ), P_("Actions" ), |
389 | flags_type: GDK_TYPE_DRAG_ACTION, default_value: 0, |
390 | flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); |
391 | |
392 | /** |
393 | * GtkDropTargetAsync:formats: (attributes org.gtk.Property.get=gtk_drop_target_async_get_formats org.gtk.Property.set=gtk_drop_target_async_set_formats) |
394 | * |
395 | * The `GdkContentFormats` that determines the supported data formats. |
396 | */ |
397 | properties[PROP_FORMATS] = |
398 | g_param_spec_boxed (name: "formats" , P_("Formats" ), P_("Formats" ), |
399 | GDK_TYPE_CONTENT_FORMATS, |
400 | flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); |
401 | |
402 | g_object_class_install_properties (oclass: object_class, n_pspecs: NUM_PROPERTIES, pspecs: properties); |
403 | |
404 | /** |
405 | * GtkDropTargetAsync::accept: |
406 | * @self: the `GtkDropTargetAsync` |
407 | * @drop: the `GdkDrop` |
408 | * |
409 | * Emitted on the drop site when a drop operation is about to begin. |
410 | * |
411 | * If the drop is not accepted, %FALSE will be returned and the drop target |
412 | * will ignore the drop. If %TRUE is returned, the drop is accepted for now |
413 | * but may be rejected later via a call to [method@Gtk.DropTargetAsync.reject_drop] |
414 | * or ultimately by returning %FALSE from a [signal@Gtk.DropTargetAsync::drop] |
415 | * handler. |
416 | * |
417 | * The default handler for this signal decides whether to accept the drop |
418 | * based on the formats provided by the @drop. |
419 | * |
420 | * If the decision whether the drop will be accepted or rejected needs |
421 | * further processing, such as inspecting the data, this function should |
422 | * return %TRUE and proceed as is @drop was accepted and if it decides to |
423 | * reject the drop later, it should call [method@Gtk.DropTargetAsync.reject_drop]. |
424 | * |
425 | * Returns: %TRUE if @drop is accepted |
426 | */ |
427 | signals[ACCEPT] = |
428 | g_signal_new (I_("accept" ), |
429 | G_TYPE_FROM_CLASS (class), |
430 | signal_flags: G_SIGNAL_RUN_LAST, |
431 | G_STRUCT_OFFSET (GtkDropTargetAsyncClass, accept), |
432 | accumulator: g_signal_accumulator_first_wins, NULL, |
433 | NULL, |
434 | G_TYPE_BOOLEAN, n_params: 1, |
435 | GDK_TYPE_DROP); |
436 | |
437 | /** |
438 | * GtkDropTargetAsync::drag-enter: |
439 | * @self: the `GtkDropTargetAsync` |
440 | * @drop: the `GdkDrop` |
441 | * @x: the x coordinate of the current pointer position |
442 | * @y: the y coordinate of the current pointer position |
443 | * |
444 | * Emitted on the drop site when the pointer enters the widget. |
445 | * |
446 | * It can be used to set up custom highlighting. |
447 | * |
448 | * Returns: Preferred action for this drag operation. |
449 | */ |
450 | signals[DRAG_ENTER] = |
451 | g_signal_new (I_("drag-enter" ), |
452 | G_TYPE_FROM_CLASS (class), |
453 | signal_flags: G_SIGNAL_RUN_LAST, |
454 | G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_enter), |
455 | accumulator: g_signal_accumulator_first_wins, NULL, |
456 | NULL, |
457 | return_type: GDK_TYPE_DRAG_ACTION, n_params: 3, |
458 | GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE); |
459 | |
460 | /** |
461 | * GtkDropTargetAsync::drag-motion: |
462 | * @self: the `GtkDropTargetAsync` |
463 | * @drop: the `GdkDrop` |
464 | * @x: the x coordinate of the current pointer position |
465 | * @y: the y coordinate of the current pointer position |
466 | * |
467 | * Emitted while the pointer is moving over the drop target. |
468 | * |
469 | * Returns: Preferred action for this drag operation. |
470 | */ |
471 | signals[DRAG_MOTION] = |
472 | g_signal_new (I_("drag-motion" ), |
473 | G_TYPE_FROM_CLASS (class), |
474 | signal_flags: G_SIGNAL_RUN_LAST, |
475 | G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_motion), |
476 | accumulator: g_signal_accumulator_first_wins, NULL, |
477 | NULL, |
478 | return_type: GDK_TYPE_DRAG_ACTION, n_params: 3, |
479 | GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE); |
480 | |
481 | /** |
482 | * GtkDropTargetAsync::drag-leave: |
483 | * @self: the `GtkDropTargetAsync` |
484 | * @drop: the `GdkDrop` |
485 | * |
486 | * Emitted on the drop site when the pointer leaves the widget. |
487 | * |
488 | * Its main purpose it to undo things done in |
489 | * `GtkDropTargetAsync`::drag-enter. |
490 | */ |
491 | signals[DRAG_LEAVE] = |
492 | g_signal_new (I_("drag-leave" ), |
493 | G_TYPE_FROM_CLASS (class), |
494 | signal_flags: G_SIGNAL_RUN_LAST, |
495 | G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_leave), |
496 | NULL, NULL, |
497 | NULL, |
498 | G_TYPE_NONE, n_params: 1, |
499 | GDK_TYPE_DROP); |
500 | |
501 | /** |
502 | * GtkDropTargetAsync::drop: |
503 | * @self: the `GtkDropTargetAsync` |
504 | * @drop: the `GdkDrop` |
505 | * @x: the x coordinate of the current pointer position |
506 | * @y: the y coordinate of the current pointer position |
507 | * |
508 | * Emitted on the drop site when the user drops the data onto the widget. |
509 | * |
510 | * The signal handler must determine whether the pointer position is in a |
511 | * drop zone or not. If it is not in a drop zone, it returns %FALSE and no |
512 | * further processing is necessary. |
513 | * |
514 | * Otherwise, the handler returns %TRUE. In this case, this handler will |
515 | * accept the drop. The handler must ensure that [method@Gdk.Drop.finish] |
516 | * is called to let the source know that the drop is done. The call to |
517 | * [method@Gdk.Drop.finish] must only be done when all data has been received. |
518 | * |
519 | * To receive the data, use one of the read functions provided by |
520 | * [class@Gdk.Drop] such as [method@Gdk.Drop.read_async] or |
521 | * [method@Gdk.Drop.read_value_async]. |
522 | * |
523 | * Returns: whether the drop is accepted at the given pointer position |
524 | */ |
525 | signals[DROP] = |
526 | g_signal_new (I_("drop" ), |
527 | G_TYPE_FROM_CLASS (class), |
528 | signal_flags: G_SIGNAL_RUN_LAST, |
529 | class_offset: 0, |
530 | accumulator: g_signal_accumulator_first_wins, NULL, |
531 | NULL, |
532 | G_TYPE_BOOLEAN, n_params: 3, |
533 | GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE); |
534 | } |
535 | |
536 | static void |
537 | gtk_drop_target_async_init (GtkDropTargetAsync *self) |
538 | { |
539 | } |
540 | |
541 | /** |
542 | * gtk_drop_target_async_new: |
543 | * @formats: (nullable) (transfer full): the supported data formats |
544 | * @actions: the supported actions |
545 | * |
546 | * Creates a new `GtkDropTargetAsync` object. |
547 | * |
548 | * Returns: the new `GtkDropTargetAsync` |
549 | */ |
550 | GtkDropTargetAsync * |
551 | gtk_drop_target_async_new (GdkContentFormats *formats, |
552 | GdkDragAction actions) |
553 | { |
554 | GtkDropTargetAsync *result; |
555 | |
556 | result = g_object_new (GTK_TYPE_DROP_TARGET_ASYNC, |
557 | first_property_name: "formats" , formats, |
558 | "actions" , actions, |
559 | NULL); |
560 | |
561 | g_clear_pointer (&formats, gdk_content_formats_unref); |
562 | |
563 | return result; |
564 | } |
565 | |
566 | /** |
567 | * gtk_drop_target_async_set_formats: (attributes org.gtk.Method.set_property=formats) |
568 | * @self: a `GtkDropTargetAsync` |
569 | * @formats: (nullable): the supported data formats or %NULL for any format |
570 | * |
571 | * Sets the data formats that this drop target will accept. |
572 | */ |
573 | void |
574 | gtk_drop_target_async_set_formats (GtkDropTargetAsync *self, |
575 | GdkContentFormats *formats) |
576 | { |
577 | g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self)); |
578 | |
579 | if (self->formats == formats) |
580 | return; |
581 | |
582 | if (self->formats) |
583 | gdk_content_formats_unref (formats: self->formats); |
584 | |
585 | self->formats = formats; |
586 | |
587 | if (self->formats) |
588 | gdk_content_formats_ref (formats: self->formats); |
589 | |
590 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_FORMATS]); |
591 | } |
592 | |
593 | /** |
594 | * gtk_drop_target_async_get_formats: (attributes org.gtk.Method.get_property=formats) |
595 | * @self: a `GtkDropTargetAsync` |
596 | * |
597 | * Gets the data formats that this drop target accepts. |
598 | * |
599 | * If the result is %NULL, all formats are expected to be supported. |
600 | * |
601 | * Returns: (nullable): the supported data formats |
602 | */ |
603 | GdkContentFormats * |
604 | gtk_drop_target_async_get_formats (GtkDropTargetAsync *self) |
605 | { |
606 | g_return_val_if_fail (GTK_IS_DROP_TARGET_ASYNC (self), NULL); |
607 | |
608 | return self->formats; |
609 | } |
610 | |
611 | /** |
612 | * gtk_drop_target_async_set_actions: (attributes org.gtk.Method.set_property=actions) |
613 | * @self: a `GtkDropTargetAsync` |
614 | * @actions: the supported actions |
615 | * |
616 | * Sets the actions that this drop target supports. |
617 | */ |
618 | void |
619 | gtk_drop_target_async_set_actions (GtkDropTargetAsync *self, |
620 | GdkDragAction actions) |
621 | { |
622 | g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self)); |
623 | |
624 | if (self->actions == actions) |
625 | return; |
626 | |
627 | self->actions = actions; |
628 | |
629 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_ACTIONS]); |
630 | } |
631 | |
632 | /** |
633 | * gtk_drop_target_async_get_actions: (attributes org.gtk.Method.get_property=actions) |
634 | * @self: a `GtkDropTargetAsync` |
635 | * |
636 | * Gets the actions that this drop target supports. |
637 | * |
638 | * Returns: the actions that this drop target supports |
639 | */ |
640 | GdkDragAction |
641 | gtk_drop_target_async_get_actions (GtkDropTargetAsync *self) |
642 | { |
643 | g_return_val_if_fail (GTK_IS_DROP_TARGET_ASYNC (self), 0); |
644 | |
645 | return self->actions; |
646 | } |
647 | |
648 | /** |
649 | * gtk_drop_target_async_reject_drop: |
650 | * @self: a `GtkDropTargetAsync` |
651 | * @drop: the `GdkDrop` of an ongoing drag operation |
652 | * |
653 | * Sets the @drop as not accepted on this drag site. |
654 | * |
655 | * This function should be used when delaying the decision |
656 | * on whether to accept a drag or not until after reading |
657 | * the data. |
658 | */ |
659 | void |
660 | gtk_drop_target_async_reject_drop (GtkDropTargetAsync *self, |
661 | GdkDrop *drop) |
662 | { |
663 | g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self)); |
664 | g_return_if_fail (GDK_IS_DROP (drop)); |
665 | g_return_if_fail (self->drop == drop); |
666 | |
667 | if (self->rejected) |
668 | return; |
669 | |
670 | self->rejected = TRUE; |
671 | gtk_widget_unset_state_flags (widget: gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)), |
672 | flags: GTK_STATE_FLAG_DROP_ACTIVE); |
673 | } |
674 | |