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 | |
53 | static 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 | |
65 | enum { |
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 | |
77 | enum { |
78 | CANCEL, |
79 | DROP_PERFORMED, |
80 | DND_FINISHED, |
81 | N_SIGNALS |
82 | }; |
83 | |
84 | typedef struct _GdkDragPrivate GdkDragPrivate; |
85 | |
86 | struct _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 | |
100 | static GParamSpec *properties[N_PROPERTIES] = { NULL, }; |
101 | static guint signals[N_SIGNALS] = { 0 }; |
102 | static GList *drags = NULL; |
103 | |
104 | G_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 | */ |
114 | GdkDisplay * |
115 | gdk_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 | */ |
132 | GdkContentFormats * |
133 | gdk_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 | */ |
150 | GdkDragAction |
151 | gdk_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 | */ |
168 | GdkDragAction |
169 | gdk_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 | */ |
186 | GdkDevice * |
187 | gdk_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 | */ |
204 | GdkContentProvider * |
205 | gdk_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 | */ |
222 | GdkSurface * |
223 | gdk_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 | |
232 | static void |
233 | gdk_drag_init (GdkDrag *drag) |
234 | { |
235 | drags = g_list_prepend (list: drags, data: drag); |
236 | } |
237 | |
238 | static void |
239 | gdk_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 | |
306 | static void |
307 | gdk_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 | |
351 | static void |
352 | gdk_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 | |
367 | static void |
368 | gdk_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 | |
531 | static void |
532 | gdk_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 | |
546 | static void |
547 | gdk_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 | |
561 | void |
562 | gdk_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 | |
640 | gboolean |
641 | gdk_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 | |
651 | void |
652 | gdk_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 | |
665 | void |
666 | gdk_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 | */ |
697 | GdkSurface * |
698 | gdk_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 | */ |
719 | void |
720 | gdk_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 | */ |
747 | void |
748 | gdk_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 | |
764 | void |
765 | gdk_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 | |
774 | void |
775 | gdk_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 | |
783 | gboolean |
784 | gdk_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 | |
803 | GdkCursor * |
804 | gdk_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 | */ |
831 | gboolean |
832 | gdk_drag_action_is_unique (GdkDragAction action) |
833 | { |
834 | return (action & (action - 1)) == 0; |
835 | } |
836 | |