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 "gtkdnd.h"
28 #include "gtkdndprivate.h"
29 #include "gtksettingsprivate.h"
30
31 #include <math.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "gdk/gdk.h"
36
37 #ifdef GDK_WINDOWING_X11
38 #include <X11/Xlib.h>
39 #include <X11/keysym.h>
40 #include "gdk/x11/gdkx.h"
41 #ifdef XINPUT_2
42 #include <X11/extensions/XInput2.h>
43 #endif
44 #endif
45
46 #ifdef GDK_WINDOWING_WIN32
47 #include <gdk/win32/gdkwin32.h>
48 #endif
49
50 #ifdef GDK_WINDOWING_WAYLAND
51 #include <gdk/wayland/gdkwayland.h>
52 #endif
53
54 #include "gtkdragdest.h"
55 #include "gtkgesturedrag.h"
56 #include "gtkgesturesingle.h"
57 #include "gtkicontheme.h"
58 #include "gtkimageprivate.h"
59 #include "gtkintl.h"
60 #include "gtkmain.h"
61 #include "gtkplug.h"
62 #include "gtktooltipprivate.h"
63 #include "gtkwindow.h"
64 #include "gtkrender.h"
65 #include "gtkselectionprivate.h"
66 #include "gtkwindowgroup.h"
67 #include "gtkwindowprivate.h"
68 #include "gtkwidgetprivate.h"
69
70
71 /**
72 * SECTION:gtkdnd
73 * @Short_description: Functions for controlling drag and drop handling
74 * @Title: Drag and Drop
75 *
76 * GTK+ has a rich set of functions for doing inter-process communication
77 * via the drag-and-drop metaphor.
78 *
79 * As well as the functions listed here, applications may need to use some
80 * facilities provided for [Selections][gtk3-Selections]. Also, the Drag and
81 * Drop API makes use of signals in the #GtkWidget class.
82 */
83
84
85 static GSList *source_widgets = NULL;
86
87 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
88 typedef struct _GtkDragDestInfo GtkDragDestInfo;
89
90
91 typedef enum
92 {
93 GTK_DRAG_STATUS_DRAG,
94 GTK_DRAG_STATUS_WAIT,
95 GTK_DRAG_STATUS_DROP
96 } GtkDragStatus;
97
98 struct _GtkDragSourceInfo
99 {
100 GtkWidget *widget;
101 GtkTargetList *target_list; /* Targets for drag data */
102 GdkDragAction possible_actions; /* Actions allowed by source */
103 GdkDragContext *context; /* drag context */
104 GtkWidget *icon_window; /* Window for drag */
105 GtkWidget *icon_widget; /* Widget for drag */
106 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
107 GdkCursor *cursor; /* Cursor for drag */
108 gint hot_x, hot_y; /* Hot spot for drag */
109 gint button; /* mouse button starting drag */
110
111 GtkDragStatus status; /* drag status */
112 GdkEvent *last_event; /* pending event */
113
114 gint start_x, start_y; /* Initial position */
115 gint cur_x, cur_y; /* Current Position */
116 GdkScreen *cur_screen; /* Current screen for pointer */
117
118 guint32 grab_time; /* timestamp for initial grab */
119 GList *selections; /* selections we've claimed */
120
121 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
122
123 guint update_idle; /* Idle function to update the drag */
124 guint drop_timeout; /* Timeout for aborting drop */
125 guint destroy_icon : 1; /* If true, destroy icon_widget */
126 guint have_grab : 1; /* Do we still have the pointer grab */
127 };
128
129 struct _GtkDragDestInfo
130 {
131 GtkWidget *widget; /* Widget in which drag is in */
132 GdkDragContext *context; /* Drag context */
133 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
134 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
135 guint32 proxy_drop_time; /* Timestamp for proxied drop */
136 guint proxy_drop_wait : 1; /* Set if we are waiting for a
137 * status reply before sending
138 * a proxied drop on.
139 */
140 guint dropped : 1; /* Set after we receive a drop */
141 gint drop_x, drop_y; /* Position of drop */
142 };
143
144 #define DROP_ABORT_TIME 300000
145
146 typedef gboolean (* GtkDragDestCallback) (GtkWidget *widget,
147 GdkDragContext *context,
148 gint x,
149 gint y,
150 guint32 time);
151
152 /* Enumeration for some targets we handle internally */
153
154 enum {
155 TARGET_DELETE = 0x40000002
156 };
157
158 /* Forward declarations */
159 static void gtk_drag_get_event_actions (const GdkEvent *event,
160 gint button,
161 GdkDragAction actions,
162 GdkDragAction *suggested_action,
163 GdkDragAction *possible_actions);
164 static GdkCursor * gtk_drag_get_cursor (GtkWidget *widget,
165 GdkDisplay *display,
166 GdkDragAction action,
167 GtkDragSourceInfo *info);
168 static void gtk_drag_update_cursor (GtkDragSourceInfo *info);
169 static GtkWidget *gtk_drag_get_ipc_widget (GtkWidget *widget);
170 static GtkWidget *gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen);
171 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
172
173 static void gtk_drag_selection_received (GtkWidget *widget,
174 GtkSelectionData *selection_data,
175 guint time,
176 gpointer data);
177 static gboolean gtk_drag_find_widget (GtkWidget *widget,
178 GdkDragContext *context,
179 GtkDragDestInfo *info,
180 gint x,
181 gint y,
182 guint32 time,
183 GtkDragDestCallback callback);
184 static void gtk_drag_proxy_begin (GtkWidget *widget,
185 GtkDragDestInfo *dest_info,
186 guint32 time);
187 static void gtk_drag_dest_leave (GtkWidget *widget,
188 GdkDragContext *context,
189 guint time);
190 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
191 GdkDragContext *context,
192 gint x,
193 gint y,
194 guint time);
195 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
196 GdkDragContext *context,
197 gint x,
198 gint y,
199 guint time);
200 static void gtk_drag_dest_set_widget (GtkDragDestInfo *info,
201 GtkWidget *widget);
202
203 static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDragContext *context,
204 gboolean create);
205 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
206 gboolean create);
207 static void gtk_drag_clear_source_info (GdkDragContext *context);
208
209 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
210 GdkAtom selection,
211 guint32 time);
212 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
213 guint32 time);
214 static void gtk_drag_drop (GtkDragSourceInfo *info,
215 guint32 time);
216 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
217 GtkDragResult result,
218 guint time);
219 static void gtk_drag_cancel_internal (GtkDragSourceInfo *info,
220 GtkDragResult result,
221 guint32 time);
222
223 static void gtk_drag_selection_get (GtkWidget *widget,
224 GtkSelectionData *selection_data,
225 guint sel_info,
226 guint32 time,
227 gpointer data);
228 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
229 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
230
231 static void gtk_drag_context_drop_performed_cb (GdkDragContext *context,
232 guint time,
233 GtkDragSourceInfo *info);
234 static void gtk_drag_context_cancel_cb (GdkDragContext *context,
235 GdkDragCancelReason reason,
236 GtkDragSourceInfo *info);
237 static void gtk_drag_context_action_cb (GdkDragContext *context,
238 GdkDragAction action,
239 GtkDragSourceInfo *info);
240 static void gtk_drag_context_dnd_finished_cb (GdkDragContext *context,
241 GtkDragSourceInfo *info);
242 static void gtk_drag_add_update_idle (GtkDragSourceInfo *info);
243
244 static void gtk_drag_update (GtkDragSourceInfo *info,
245 GdkScreen *screen,
246 gint x_root,
247 gint y_root,
248 const GdkEvent *event);
249 static gboolean gtk_drag_motion_cb (GtkWidget *widget,
250 GdkEventMotion *event,
251 gpointer data);
252 static gboolean gtk_drag_key_cb (GtkWidget *widget,
253 GdkEventKey *event,
254 gpointer data);
255 static gboolean gtk_drag_grab_broken_event_cb (GtkWidget *widget,
256 GdkEventGrabBroken *event,
257 gpointer data);
258 static void gtk_drag_grab_notify_cb (GtkWidget *widget,
259 gboolean was_grabbed,
260 gpointer data);
261 static gboolean gtk_drag_button_release_cb (GtkWidget *widget,
262 GdkEventButton *event,
263 gpointer data);
264 static gboolean gtk_drag_abort_timeout (gpointer data);
265
266 static void set_icon_helper (GdkDragContext *context,
267 GtkImageDefinition*def,
268 gint hot_x,
269 gint hot_y);
270
271 /************************
272 * Cursor and Icon data *
273 ************************/
274
275 static struct {
276 GdkDragAction action;
277 const gchar *name;
278 GdkPixbuf *pixbuf;
279 GdkCursor *cursor;
280 } drag_cursors[] = {
281 { GDK_ACTION_DEFAULT, NULL },
282 { GDK_ACTION_ASK, "dnd-ask", NULL, NULL },
283 { GDK_ACTION_COPY, "copy", NULL, NULL },
284 { GDK_ACTION_MOVE, "move", NULL, NULL },
285 { GDK_ACTION_LINK, "alias", NULL, NULL },
286 { 0 , "no-drop", NULL, NULL },
287 };
288
289 /*********************
290 * Utility functions *
291 *********************/
292
293 static GtkWidget *
gtk_drag_get_ipc_widget_for_screen(GdkScreen * screen)294 gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen)
295 {
296 GtkWidget *result;
297 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
298 "gtk-dnd-ipc-widgets");
299
300 if (drag_widgets)
301 {
302 GSList *tmp = drag_widgets;
303 result = drag_widgets->data;
304 drag_widgets = drag_widgets->next;
305 g_object_set_data (G_OBJECT (screen),
306 I_("gtk-dnd-ipc-widgets"),
307 drag_widgets);
308 g_slist_free_1 (tmp);
309 }
310 else
311 {
312 result = gtk_window_new (GTK_WINDOW_POPUP);
313 gtk_window_set_screen (GTK_WINDOW (result), screen);
314 gtk_window_resize (GTK_WINDOW (result), 1, 1);
315 gtk_window_move (GTK_WINDOW (result), -99, -99);
316 gtk_widget_show (result);
317 }
318
319 return result;
320 }
321
322 static GtkWidget *
gtk_drag_get_ipc_widget(GtkWidget * widget)323 gtk_drag_get_ipc_widget (GtkWidget *widget)
324 {
325 GtkWidget *result;
326 GtkWidget *toplevel;
327
328 result = gtk_drag_get_ipc_widget_for_screen (gtk_widget_get_screen (widget));
329
330 toplevel = gtk_widget_get_toplevel (widget);
331
332 if (GTK_IS_WINDOW (toplevel))
333 {
334 if (gtk_window_has_group (GTK_WINDOW (toplevel)))
335 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
336 GTK_WINDOW (result));
337 }
338
339 return result;
340 }
341
342 #if defined (GDK_WINDOWING_X11)
343
344 /*
345 * We want to handle a handful of keys during DND, e.g. Escape to abort.
346 * Grabbing the keyboard has the unfortunate side-effect of preventing
347 * useful things such as using Alt-Tab to cycle between windows or
348 * switching workspaces. Therefore, we just grab the few keys we are
349 * interested in. Note that we need to put the grabs on the root window
350 * in order for them to still work when the focus is moved to another
351 * app/workspace.
352 *
353 * GDK needs a little help to successfully deliver root key events...
354 */
355
356 static GdkFilterReturn
root_key_filter(GdkXEvent * xevent,GdkEvent * event,gpointer data)357 root_key_filter (GdkXEvent *xevent,
358 GdkEvent *event,
359 gpointer data)
360 {
361 XEvent *ev = (XEvent *) xevent;
362
363 if ((ev->type == KeyPress || ev->type == KeyRelease) &&
364 ev->xkey.root == ev->xkey.window)
365 ev->xkey.window = (Window)data;
366 else if (ev->type == GenericEvent)
367 {
368 XGenericEventCookie *cookie;
369 XIDeviceEvent *dev;
370
371 cookie = &ev->xcookie;
372 dev = (XIDeviceEvent *) cookie->data;
373
374 if (dev->evtype == XI_KeyPress ||
375 dev->evtype == XI_KeyRelease)
376 dev->event = (Window)data;
377 }
378
379 return GDK_FILTER_CONTINUE;
380 }
381
382 typedef struct {
383 gint keysym;
384 gint modifiers;
385 } GrabKey;
386
387 static GrabKey grab_keys[] = {
388 { XK_Escape, 0 },
389 { XK_space, 0 },
390 { XK_KP_Space, 0 },
391 { XK_Return, 0 },
392 { XK_KP_Enter, 0 },
393 { XK_Up, 0 },
394 { XK_Up, Mod1Mask },
395 { XK_Down, 0 },
396 { XK_Down, Mod1Mask },
397 { XK_Left, 0 },
398 { XK_Left, Mod1Mask },
399 { XK_Right, 0 },
400 { XK_Right, Mod1Mask },
401 { XK_KP_Up, 0 },
402 { XK_KP_Up, Mod1Mask },
403 { XK_KP_Down, 0 },
404 { XK_KP_Down, Mod1Mask },
405 { XK_KP_Left, 0 },
406 { XK_KP_Left, Mod1Mask },
407 { XK_KP_Right, 0 },
408 { XK_KP_Right, Mod1Mask }
409 };
410
411 static void
grab_dnd_keys(GtkWidget * widget,GdkDevice * device,guint32 time)412 grab_dnd_keys (GtkWidget *widget,
413 GdkDevice *device,
414 guint32 time)
415 {
416 guint i;
417 GdkDisplay *display;
418 GdkWindow *window, *root;
419 gint keycode;
420 #ifdef XINPUT_2
421 gint deviceid;
422 XIGrabModifiers mods;
423 gint num_mods;
424 XIEventMask evmask;
425 unsigned char mask[(XI_LASTEVENT + 7)/8];
426 gboolean using_xi2;
427
428 window = gtk_widget_get_window (widget);
429 if (!GDK_IS_X11_WINDOW (window))
430 {
431 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
432 gdk_device_grab (device,
433 gtk_widget_get_window (widget),
434 GDK_OWNERSHIP_APPLICATION, FALSE,
435 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
436 NULL, time);
437 G_GNUC_END_IGNORE_DEPRECATIONS;
438 return;
439 }
440
441 deviceid = gdk_x11_device_get_id (device);
442
443 if (GDK_IS_X11_DEVICE_XI2 (device))
444 using_xi2 = TRUE;
445 else
446 using_xi2 = FALSE;
447 #endif
448
449 display = gtk_widget_get_display (widget);
450 root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
451
452 gdk_x11_display_error_trap_push (display);
453
454 for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
455 {
456 keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
457 if (keycode == NoSymbol)
458 continue;
459
460 #ifdef XINPUT_2
461 if (using_xi2)
462 {
463 memset (mask, 0, sizeof (mask));
464 XISetMask (mask, XI_KeyPress);
465 XISetMask (mask, XI_KeyRelease);
466
467 evmask.deviceid = deviceid;
468 evmask.mask_len = sizeof (mask);
469 evmask.mask = mask;
470
471 num_mods = 1;
472 mods.modifiers = grab_keys[i].modifiers;
473
474 XIGrabKeycode (GDK_WINDOW_XDISPLAY (window),
475 deviceid,
476 keycode,
477 GDK_WINDOW_XID (root),
478 GrabModeAsync,
479 GrabModeAsync,
480 False,
481 &evmask,
482 num_mods,
483 &mods);
484 }
485 else
486 #endif
487 XGrabKey (GDK_WINDOW_XDISPLAY (window),
488 keycode, grab_keys[i].modifiers,
489 GDK_WINDOW_XID (root),
490 FALSE,
491 GrabModeAsync,
492 GrabModeAsync);
493 }
494
495 gdk_display_flush (display);
496 gdk_x11_display_error_trap_pop_ignored (display);
497
498 gdk_window_add_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
499 }
500
501 static void
ungrab_dnd_keys(GtkWidget * widget,GdkDevice * device,guint32 time)502 ungrab_dnd_keys (GtkWidget *widget,
503 GdkDevice *device,
504 guint32 time)
505 {
506 guint i;
507 GdkWindow *window, *root;
508 GdkDisplay *display;
509 gint keycode;
510 #ifdef XINPUT_2
511 XIGrabModifiers mods;
512 gint num_mods;
513 gint deviceid;
514 gboolean using_xi2;
515
516 window = gtk_widget_get_window (widget);
517 if (!GDK_IS_X11_WINDOW (window))
518 {
519 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
520 gdk_device_ungrab (device, time);
521 G_GNUC_END_IGNORE_DEPRECATIONS;
522 return;
523 }
524
525 deviceid = gdk_x11_device_get_id (device);
526
527 if (GDK_IS_X11_DEVICE_XI2 (device))
528 using_xi2 = TRUE;
529 else
530 using_xi2 = FALSE;
531 #endif
532
533 display = gtk_widget_get_display (widget);
534 root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
535
536 gdk_window_remove_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
537
538 gdk_x11_display_error_trap_push (display);
539
540 for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
541 {
542 keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
543 if (keycode == NoSymbol)
544 continue;
545
546 #ifdef XINPUT_2
547 if (using_xi2)
548 {
549 num_mods = 1;
550 mods.modifiers = grab_keys[i].modifiers;
551
552 XIUngrabKeycode (GDK_WINDOW_XDISPLAY (window),
553 deviceid,
554 keycode,
555 GDK_WINDOW_XID (root),
556 num_mods,
557 &mods);
558 }
559 else
560 #endif
561 XUngrabKey (GDK_WINDOW_XDISPLAY (window),
562 keycode, grab_keys[i].modifiers,
563 GDK_WINDOW_XID (root));
564 }
565
566 gdk_display_flush (display);
567 gdk_x11_display_error_trap_pop_ignored (display);
568 }
569
570 #else /* !GDK_WINDOWING_X11 */
571
572 static void
grab_dnd_keys(GtkWidget * widget,GdkDevice * device,guint32 time)573 grab_dnd_keys (GtkWidget *widget,
574 GdkDevice *device,
575 guint32 time)
576 {
577 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
578 gdk_device_grab (device,
579 gtk_widget_get_window (widget),
580 GDK_OWNERSHIP_APPLICATION, FALSE,
581 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
582 NULL, time);
583 G_GNUC_END_IGNORE_DEPRECATIONS;
584 }
585
586 static void
ungrab_dnd_keys(GtkWidget * widget,GdkDevice * device,guint32 time)587 ungrab_dnd_keys (GtkWidget *widget,
588 GdkDevice *device,
589 guint32 time)
590 {
591 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
592 gdk_device_ungrab (device, time);
593 G_GNUC_END_IGNORE_DEPRECATIONS;
594 }
595
596 #endif /* GDK_WINDOWING_X11 */
597
598 /*
599 * gtk_drag_release_ipc_widget:
600 * @widget: the widget to release
601 *
602 * Releases widget retrieved with gtk_drag_get_ipc_widget().
603 */
604 static void
gtk_drag_release_ipc_widget(GtkWidget * widget)605 gtk_drag_release_ipc_widget (GtkWidget *widget)
606 {
607 GtkWindow *window = GTK_WINDOW (widget);
608 GdkScreen *screen = gtk_widget_get_screen (widget);
609 GdkDragContext *context = g_object_get_data (G_OBJECT (widget), "drag-context");
610 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
611 "gtk-dnd-ipc-widgets");
612 GdkDevice *pointer, *keyboard;
613
614 if (context)
615 {
616 pointer = gdk_drag_context_get_device (context);
617 keyboard = gdk_device_get_associated_device (pointer);
618
619 if (keyboard)
620 ungrab_dnd_keys (widget, keyboard, GDK_CURRENT_TIME);
621 }
622
623 if (gtk_window_has_group (window))
624 gtk_window_group_remove_window (gtk_window_get_group (window),
625 window);
626 drag_widgets = g_slist_prepend (drag_widgets, widget);
627 g_object_set_data (G_OBJECT (screen),
628 I_("gtk-dnd-ipc-widgets"),
629 drag_widgets);
630 }
631
632 static guint32
gtk_drag_get_event_time(GdkEvent * event)633 gtk_drag_get_event_time (GdkEvent *event)
634 {
635 guint32 tm = GDK_CURRENT_TIME;
636
637 if (event)
638 switch (event->type)
639 {
640 case GDK_MOTION_NOTIFY:
641 tm = event->motion.time; break;
642 case GDK_BUTTON_PRESS:
643 case GDK_2BUTTON_PRESS:
644 case GDK_3BUTTON_PRESS:
645 case GDK_BUTTON_RELEASE:
646 tm = event->button.time; break;
647 case GDK_KEY_PRESS:
648 case GDK_KEY_RELEASE:
649 tm = event->key.time; break;
650 case GDK_ENTER_NOTIFY:
651 case GDK_LEAVE_NOTIFY:
652 tm = event->crossing.time; break;
653 case GDK_PROPERTY_NOTIFY:
654 tm = event->property.time; break;
655 case GDK_SELECTION_CLEAR:
656 case GDK_SELECTION_REQUEST:
657 case GDK_SELECTION_NOTIFY:
658 tm = event->selection.time; break;
659 case GDK_PROXIMITY_IN:
660 case GDK_PROXIMITY_OUT:
661 tm = event->proximity.time; break;
662 default: /* use current time */
663 break;
664 }
665
666 return tm;
667 }
668
669 static void
gtk_drag_get_event_actions(const GdkEvent * event,gint button,GdkDragAction actions,GdkDragAction * suggested_action,GdkDragAction * possible_actions)670 gtk_drag_get_event_actions (const GdkEvent *event,
671 gint button,
672 GdkDragAction actions,
673 GdkDragAction *suggested_action,
674 GdkDragAction *possible_actions)
675 {
676 *suggested_action = 0;
677 *possible_actions = 0;
678
679 if (event)
680 {
681 GdkModifierType state = 0;
682
683 switch (event->type)
684 {
685 case GDK_MOTION_NOTIFY:
686 state = event->motion.state;
687 break;
688 case GDK_BUTTON_PRESS:
689 case GDK_2BUTTON_PRESS:
690 case GDK_3BUTTON_PRESS:
691 case GDK_BUTTON_RELEASE:
692 state = event->button.state;
693 break;
694 case GDK_KEY_PRESS:
695 case GDK_KEY_RELEASE:
696 state = event->key.state;
697 break;
698 case GDK_ENTER_NOTIFY:
699 case GDK_LEAVE_NOTIFY:
700 state = event->crossing.state;
701 break;
702 default:
703 break;
704 }
705
706 if ((button == GDK_BUTTON_MIDDLE || button == GDK_BUTTON_SECONDARY) && (actions & GDK_ACTION_ASK))
707 {
708 *suggested_action = GDK_ACTION_ASK;
709 *possible_actions = actions;
710 }
711 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
712 {
713 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
714 {
715 if (actions & GDK_ACTION_LINK)
716 {
717 *suggested_action = GDK_ACTION_LINK;
718 *possible_actions = GDK_ACTION_LINK;
719 }
720 }
721 else if (state & GDK_CONTROL_MASK)
722 {
723 if (actions & GDK_ACTION_COPY)
724 {
725 *suggested_action = GDK_ACTION_COPY;
726 *possible_actions = GDK_ACTION_COPY;
727 }
728 }
729 else
730 {
731 if (actions & GDK_ACTION_MOVE)
732 {
733 *suggested_action = GDK_ACTION_MOVE;
734 *possible_actions = GDK_ACTION_MOVE;
735 }
736 }
737 }
738 else
739 {
740 *possible_actions = actions;
741
742 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
743 *suggested_action = GDK_ACTION_ASK;
744 else if (actions & GDK_ACTION_COPY)
745 *suggested_action = GDK_ACTION_COPY;
746 else if (actions & GDK_ACTION_MOVE)
747 *suggested_action = GDK_ACTION_MOVE;
748 else if (actions & GDK_ACTION_LINK)
749 *suggested_action = GDK_ACTION_LINK;
750 }
751 }
752 else
753 {
754 *possible_actions = actions;
755
756 if (actions & GDK_ACTION_COPY)
757 *suggested_action = GDK_ACTION_COPY;
758 else if (actions & GDK_ACTION_MOVE)
759 *suggested_action = GDK_ACTION_MOVE;
760 else if (actions & GDK_ACTION_LINK)
761 *suggested_action = GDK_ACTION_LINK;
762 }
763 }
764
765 static void
ensure_drag_cursor_pixbuf(int i)766 ensure_drag_cursor_pixbuf (int i)
767 {
768 if (drag_cursors[i].pixbuf == NULL)
769 {
770 char *path = g_strconcat ("/org/gtk/libgtk/cursor/", drag_cursors[i].name, ".png", NULL);
771 GInputStream *stream = g_resources_open_stream (path, 0, NULL);
772 if (stream != NULL)
773 {
774 drag_cursors[i].pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
775 g_object_unref (stream);
776 }
777 g_free (path);
778 }
779 }
780
781 static GdkCursor *
gtk_drag_get_cursor(GtkWidget * widget,GdkDisplay * display,GdkDragAction action,GtkDragSourceInfo * info)782 gtk_drag_get_cursor (GtkWidget *widget,
783 GdkDisplay *display,
784 GdkDragAction action,
785 GtkDragSourceInfo *info)
786 {
787 gint i;
788
789 /* reconstruct the cursors for each new drag (thus !info),
790 * to catch cursor theme changes
791 */
792 if (!info)
793 {
794 for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
795 g_clear_object (&drag_cursors[i].cursor);
796 }
797
798 for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
799 if (drag_cursors[i].action == action)
800 break;
801
802 if (drag_cursors[i].cursor != NULL)
803 {
804 if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
805 g_clear_object (&drag_cursors[i].cursor);
806 }
807
808 if (drag_cursors[i].cursor == NULL)
809 drag_cursors[i].cursor = gdk_cursor_new_from_name (display, drag_cursors[i].name);
810
811 if (drag_cursors[i].cursor == NULL)
812 {
813 ensure_drag_cursor_pixbuf (i);
814 drag_cursors[i].cursor = gdk_cursor_new_from_pixbuf (display, drag_cursors[i].pixbuf, 0, 0);
815 }
816
817 return drag_cursors[i].cursor;
818 }
819
820 static void
gtk_drag_update_cursor(GtkDragSourceInfo * info)821 gtk_drag_update_cursor (GtkDragSourceInfo *info)
822 {
823 GdkCursor *cursor;
824 gint i;
825
826 if (!info->have_grab)
827 return;
828
829 for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
830 if (info->cursor == drag_cursors[i].cursor)
831 break;
832
833 if (i == G_N_ELEMENTS (drag_cursors))
834 return;
835
836 cursor = gtk_drag_get_cursor (info->widget,
837 gdk_cursor_get_display (info->cursor),
838 drag_cursors[i].action, info);
839
840 if (cursor != info->cursor)
841 {
842 GdkDevice *pointer;
843
844 pointer = gdk_drag_context_get_device (info->context);
845 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
846 gdk_device_grab (pointer,
847 gtk_widget_get_window (info->ipc_widget),
848 GDK_OWNERSHIP_APPLICATION, FALSE,
849 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
850 cursor, info->grab_time);
851 G_GNUC_END_IGNORE_DEPRECATIONS;
852 info->cursor = cursor;
853 }
854 }
855
856 /********************
857 * Destination side *
858 ********************/
859
860 /**
861 * gtk_drag_get_data: (method)
862 * @widget: the widget that will receive the
863 * #GtkWidget::drag-data-received signal
864 * @context: the drag context
865 * @target: the target (form of the data) to retrieve
866 * @time_: a timestamp for retrieving the data. This will
867 * generally be the time received in a #GtkWidget::drag-motion
868 * or #GtkWidget::drag-drop signal
869 *
870 * Gets the data associated with a drag. When the data
871 * is received or the retrieval fails, GTK+ will emit a
872 * #GtkWidget::drag-data-received signal. Failure of the retrieval
873 * is indicated by the length field of the @selection_data
874 * signal parameter being negative. However, when gtk_drag_get_data()
875 * is called implicitely because the %GTK_DEST_DEFAULT_DROP was set,
876 * then the widget will not receive notification of failed
877 * drops.
878 */
879 void
gtk_drag_get_data(GtkWidget * widget,GdkDragContext * context,GdkAtom target,guint32 time_)880 gtk_drag_get_data (GtkWidget *widget,
881 GdkDragContext *context,
882 GdkAtom target,
883 guint32 time_)
884 {
885 GtkWidget *selection_widget;
886
887 g_return_if_fail (GTK_IS_WIDGET (widget));
888 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
889
890 selection_widget = gtk_drag_get_ipc_widget (widget);
891
892 g_object_ref (context);
893 g_object_ref (widget);
894
895 g_signal_connect (selection_widget, "selection-received",
896 G_CALLBACK (gtk_drag_selection_received), widget);
897
898 g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
899
900 gtk_selection_convert (selection_widget,
901 gdk_drag_get_selection (context),
902 target,
903 time_);
904 }
905
906 /**
907 * gtk_drag_get_source_widget:
908 * @context: a (destination side) drag context
909 *
910 * Determines the source widget for a drag.
911 *
912 * Returns: (nullable) (transfer none): if the drag is occurring
913 * within a single application, a pointer to the source widget.
914 * Otherwise, %NULL.
915 */
916 GtkWidget *
gtk_drag_get_source_widget(GdkDragContext * context)917 gtk_drag_get_source_widget (GdkDragContext *context)
918 {
919 GSList *tmp_list;
920
921 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
922
923 tmp_list = source_widgets;
924 while (tmp_list)
925 {
926 GtkWidget *ipc_widget = tmp_list->data;
927
928 if (gtk_widget_get_window (ipc_widget) == gdk_drag_context_get_source_window (context))
929 {
930 GtkDragSourceInfo *info;
931 info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
932
933 return info ? info->widget : NULL;
934 }
935
936 tmp_list = tmp_list->next;
937 }
938
939 return NULL;
940 }
941
942 /**
943 * gtk_drag_finish:
944 * @context: the drag context
945 * @success: a flag indicating whether the drop was successful
946 * @del: a flag indicating whether the source should delete the
947 * original data. (This should be %TRUE for a move)
948 * @time_: the timestamp from the #GtkWidget::drag-drop signal
949 *
950 * Informs the drag source that the drop is finished, and
951 * that the data of the drag will no longer be required.
952 */
953 void
gtk_drag_finish(GdkDragContext * context,gboolean success,gboolean del,guint32 time)954 gtk_drag_finish (GdkDragContext *context,
955 gboolean success,
956 gboolean del,
957 guint32 time)
958 {
959 GdkAtom target = GDK_NONE;
960
961 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
962
963 if (success && del)
964 {
965 target = gdk_atom_intern_static_string ("DELETE");
966 }
967
968 if (target != GDK_NONE)
969 {
970 GtkWidget *selection_widget = gtk_drag_get_ipc_widget_for_screen (gdk_window_get_screen (gdk_drag_context_get_source_window (context)));
971
972 g_object_ref (context);
973
974 g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
975 g_signal_connect (selection_widget, "selection-received",
976 G_CALLBACK (gtk_drag_selection_received),
977 NULL);
978
979 gtk_selection_convert (selection_widget,
980 gdk_drag_get_selection (context),
981 target,
982 time);
983 }
984
985 if (!(success && del))
986 gdk_drop_finish (context, success, time);
987 }
988
989 /**
990 * gtk_drag_highlight: (method)
991 * @widget: a widget to highlight
992 *
993 * Highlights a widget as a currently hovered drop target.
994 * To end the highlight, call gtk_drag_unhighlight().
995 * GTK+ calls this automatically if %GTK_DEST_DEFAULT_HIGHLIGHT is set.
996 */
997 void
gtk_drag_highlight(GtkWidget * widget)998 gtk_drag_highlight (GtkWidget *widget)
999 {
1000 g_return_if_fail (GTK_IS_WIDGET (widget));
1001
1002 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
1003 }
1004
1005 /**
1006 * gtk_drag_unhighlight: (method)
1007 * @widget: a widget to remove the highlight from
1008 *
1009 * Removes a highlight set by gtk_drag_highlight() from
1010 * a widget.
1011 */
1012 void
gtk_drag_unhighlight(GtkWidget * widget)1013 gtk_drag_unhighlight (GtkWidget *widget)
1014 {
1015 g_return_if_fail (GTK_IS_WIDGET (widget));
1016
1017 gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
1018 }
1019
1020 /*
1021 * _gtk_drag_dest_handle_event:
1022 * @toplevel: Toplevel widget that received the event
1023 * @event: the event to handle
1024 *
1025 * Called from widget event handling code on Drag events
1026 * for destinations.
1027 */
1028 void
_gtk_drag_dest_handle_event(GtkWidget * toplevel,GdkEvent * event)1029 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1030 GdkEvent *event)
1031 {
1032 GtkDragDestInfo *info;
1033 GdkDragContext *context;
1034
1035 g_return_if_fail (toplevel != NULL);
1036 g_return_if_fail (event != NULL);
1037
1038 context = event->dnd.context;
1039
1040 info = gtk_drag_get_dest_info (context, TRUE);
1041
1042 /* Find the widget for the event */
1043 switch (event->type)
1044 {
1045 case GDK_DRAG_ENTER:
1046 break;
1047
1048 case GDK_DRAG_LEAVE:
1049 if (info->widget)
1050 {
1051 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1052 gtk_drag_dest_set_widget (info, NULL);
1053 }
1054 break;
1055
1056 case GDK_DRAG_MOTION:
1057 case GDK_DROP_START:
1058 {
1059 GdkWindow *window;
1060 gint tx, ty;
1061 gboolean found;
1062
1063 if (event->type == GDK_DROP_START)
1064 {
1065 info->dropped = TRUE;
1066 /* We send a leave here so that the widget unhighlights
1067 * properly.
1068 */
1069 if (info->widget)
1070 {
1071 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1072 gtk_drag_dest_set_widget (info, NULL);
1073 }
1074 }
1075
1076 window = gtk_widget_get_window (toplevel);
1077
1078 #ifdef GDK_WINDOWING_X11
1079 /* Hackaround for: http://bugzilla.gnome.org/show_bug.cgi?id=136112
1080 *
1081 * Currently gdk_window_get_position doesn't provide reliable
1082 * information for embedded windows, so we call the much more
1083 * expensive gdk_window_get_origin().
1084 */
1085 if (GTK_IS_PLUG (toplevel))
1086 gdk_window_get_origin (window, &tx, &ty);
1087 else
1088 #endif /* GDK_WINDOWING_X11 */
1089 gdk_window_get_position (window, &tx, &ty);
1090
1091 found = gtk_drag_find_widget (toplevel,
1092 context,
1093 info,
1094 event->dnd.x_root - tx,
1095 event->dnd.y_root - ty,
1096 event->dnd.time,
1097 (event->type == GDK_DRAG_MOTION) ?
1098 gtk_drag_dest_motion :
1099 gtk_drag_dest_drop);
1100
1101 if (info->widget && !found)
1102 {
1103 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1104 gtk_drag_dest_set_widget (info, NULL);
1105 }
1106
1107 /* Send a reply.
1108 */
1109 if (event->type == GDK_DRAG_MOTION)
1110 {
1111 if (!found)
1112 gdk_drag_status (context, 0, event->dnd.time);
1113 }
1114 else if (event->type == GDK_DROP_START && !info->proxy_source)
1115 {
1116 gdk_drop_reply (context, found, event->dnd.time);
1117 }
1118 }
1119 break;
1120
1121 default:
1122 g_assert_not_reached ();
1123 }
1124 }
1125
1126 static void
gtk_drag_selection_received(GtkWidget * widget,GtkSelectionData * selection_data,guint time,gpointer data)1127 gtk_drag_selection_received (GtkWidget *widget,
1128 GtkSelectionData *selection_data,
1129 guint time,
1130 gpointer data)
1131 {
1132 GdkDragContext *context;
1133 GtkDragDestInfo *info;
1134 GtkWidget *drop_widget;
1135 GdkAtom target;
1136
1137 drop_widget = data;
1138
1139 context = g_object_get_data (G_OBJECT (widget), "drag-context");
1140 info = gtk_drag_get_dest_info (context, FALSE);
1141
1142 if (info->proxy_data &&
1143 gtk_selection_data_get_target (info->proxy_data) == gtk_selection_data_get_target (selection_data))
1144 {
1145 gtk_selection_data_set (info->proxy_data,
1146 gtk_selection_data_get_data_type (selection_data),
1147 gtk_selection_data_get_format (selection_data),
1148 gtk_selection_data_get_data (selection_data),
1149 gtk_selection_data_get_length (selection_data));
1150 gtk_main_quit ();
1151 return;
1152 }
1153
1154 target = gtk_selection_data_get_target (selection_data);
1155 if (target == gdk_atom_intern_static_string ("DELETE"))
1156 {
1157 gtk_drag_finish (context, TRUE, FALSE, time);
1158 }
1159 else if ((target == gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS")) ||
1160 (target == gdk_atom_intern_static_string ("XmTRANSFER_FAILURE")))
1161 {
1162 /* Do nothing */
1163 }
1164 else
1165 {
1166 GtkDragDestSite *site;
1167
1168 site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1169
1170 if (site && site->target_list)
1171 {
1172 guint target_info;
1173
1174 if (gtk_target_list_find (site->target_list,
1175 target,
1176 &target_info))
1177 {
1178 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1179 gtk_selection_data_get_length (selection_data) >= 0)
1180 g_signal_emit_by_name (drop_widget,
1181 "drag-data-received",
1182 context, info->drop_x, info->drop_y,
1183 selection_data,
1184 target_info, time);
1185 }
1186 }
1187 else
1188 {
1189 g_signal_emit_by_name (drop_widget,
1190 "drag-data-received",
1191 context, info->drop_x, info->drop_y,
1192 selection_data,
1193 0, time);
1194 }
1195
1196 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1197 {
1198
1199 gtk_drag_finish (context,
1200 (gtk_selection_data_get_length (selection_data) >= 0),
1201 (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
1202 time);
1203 }
1204
1205 g_object_unref (drop_widget);
1206 }
1207
1208 g_signal_handlers_disconnect_by_func (widget,
1209 gtk_drag_selection_received,
1210 data);
1211
1212 g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
1213 g_object_unref (context);
1214
1215 gtk_drag_release_ipc_widget (widget);
1216 }
1217
1218 static gboolean
gtk_drag_find_widget(GtkWidget * widget,GdkDragContext * context,GtkDragDestInfo * info,gint x,gint y,guint32 time,GtkDragDestCallback callback)1219 gtk_drag_find_widget (GtkWidget *widget,
1220 GdkDragContext *context,
1221 GtkDragDestInfo *info,
1222 gint x,
1223 gint y,
1224 guint32 time,
1225 GtkDragDestCallback callback)
1226 {
1227 if (!gtk_widget_get_mapped (widget) ||
1228 !gtk_widget_get_sensitive (widget))
1229 return FALSE;
1230
1231 /* Get the widget at the pointer coordinates and travel up
1232 * the widget hierarchy from there.
1233 */
1234 widget = _gtk_widget_find_at_coords (gtk_widget_get_window (widget),
1235 x, y, &x, &y);
1236 if (!widget)
1237 return FALSE;
1238
1239 while (widget)
1240 {
1241 GtkWidget *parent;
1242 GList *hierarchy = NULL;
1243 gboolean found = FALSE;
1244
1245 if (!gtk_widget_get_mapped (widget))
1246 return FALSE;
1247
1248 if (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_INSENSITIVE)
1249 {
1250 widget = gtk_widget_get_parent (widget);
1251 continue;
1252 }
1253
1254 /* need to reference the entire hierarchy temporarily in case the
1255 * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
1256 */
1257 for (parent = widget;
1258 parent;
1259 parent = gtk_widget_get_parent (parent))
1260 {
1261 hierarchy = g_list_prepend (hierarchy, g_object_ref (parent));
1262 }
1263
1264 /* If the current widget is registered as a drop site, check to
1265 * emit "drag-motion" to check if we are actually in a drop
1266 * site.
1267 */
1268 if (g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1269 {
1270 found = callback (widget, context, x, y, time);
1271
1272 /* If so, send a "drag-leave" to the last widget */
1273 if (found && info->widget != widget)
1274 {
1275 if (info->widget)
1276 gtk_drag_dest_leave (info->widget, context, time);
1277
1278 gtk_drag_dest_set_widget (info, widget);
1279 }
1280 }
1281
1282 if (!found)
1283 {
1284 /* Get the parent before unreffing the hierarchy because
1285 * invoking the callback might have destroyed the widget
1286 */
1287 parent = gtk_widget_get_parent (widget);
1288
1289 /* The parent might be going away when unreffing the
1290 * hierarchy, so also protect againt that
1291 */
1292 if (parent)
1293 g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
1294 }
1295
1296 g_list_free_full (hierarchy, g_object_unref);
1297
1298 if (found)
1299 return TRUE;
1300
1301 if (parent)
1302 g_object_remove_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
1303 else
1304 return FALSE;
1305
1306 if (!gtk_widget_translate_coordinates (widget, parent, x, y, &x, &y))
1307 return FALSE;
1308
1309 widget = parent;
1310 }
1311
1312 return FALSE;
1313 }
1314
1315 static void
gtk_drag_proxy_begin(GtkWidget * widget,GtkDragDestInfo * dest_info,guint32 time)1316 gtk_drag_proxy_begin (GtkWidget *widget,
1317 GtkDragDestInfo *dest_info,
1318 guint32 time)
1319 {
1320 GtkDragSourceInfo *source_info;
1321 GList *tmp_list;
1322 GdkDragContext *context;
1323 GtkWidget *ipc_widget;
1324
1325 if (dest_info->proxy_source)
1326 {
1327 gdk_drag_abort (dest_info->proxy_source->context, time);
1328 gtk_drag_source_info_destroy (dest_info->proxy_source);
1329 dest_info->proxy_source = NULL;
1330 }
1331
1332 ipc_widget = gtk_drag_get_ipc_widget (widget);
1333 context = gdk_drag_begin (gtk_widget_get_window (ipc_widget),
1334 gdk_drag_context_list_targets (dest_info->context));
1335
1336 source_info = gtk_drag_get_source_info (context, TRUE);
1337
1338 source_info->ipc_widget = ipc_widget;
1339 source_info->widget = g_object_ref (widget);
1340
1341 source_info->target_list = gtk_target_list_new (NULL, 0);
1342 tmp_list = gdk_drag_context_list_targets (dest_info->context);
1343 while (tmp_list)
1344 {
1345 gtk_target_list_add (source_info->target_list,
1346 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1347 tmp_list = tmp_list->next;
1348 }
1349
1350 source_info->proxy_dest = dest_info;
1351
1352 g_signal_connect (ipc_widget,
1353 "selection-get",
1354 G_CALLBACK (gtk_drag_selection_get),
1355 source_info);
1356
1357 dest_info->proxy_source = source_info;
1358 }
1359
1360 static void
gtk_drag_dest_set_widget(GtkDragDestInfo * info,GtkWidget * widget)1361 gtk_drag_dest_set_widget (GtkDragDestInfo *info,
1362 GtkWidget *widget)
1363 {
1364 if (info->widget)
1365 g_object_remove_weak_pointer (G_OBJECT (info->widget), (gpointer *) &info->widget);
1366
1367 info->widget = widget;
1368
1369 if (info->widget)
1370 g_object_add_weak_pointer (G_OBJECT (info->widget), (gpointer *) &info->widget);
1371 }
1372
1373 static void
gtk_drag_dest_info_destroy(gpointer data)1374 gtk_drag_dest_info_destroy (gpointer data)
1375 {
1376 GtkDragDestInfo *info = (GtkDragDestInfo *)data;
1377
1378 gtk_drag_dest_set_widget (info, NULL);
1379
1380 g_slice_free (GtkDragDestInfo, data);
1381 }
1382
1383 static GtkDragDestInfo *
gtk_drag_get_dest_info(GdkDragContext * context,gboolean create)1384 gtk_drag_get_dest_info (GdkDragContext *context,
1385 gboolean create)
1386 {
1387 GtkDragDestInfo *info;
1388 static GQuark info_quark = 0;
1389 if (!info_quark)
1390 info_quark = g_quark_from_static_string ("gtk-dest-info");
1391
1392 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1393 if (!info && create)
1394 {
1395 info = g_slice_new0 (GtkDragDestInfo);
1396 info->context = context;
1397 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1398 info, gtk_drag_dest_info_destroy);
1399 }
1400
1401 return info;
1402 }
1403
1404 static GQuark dest_info_quark = 0;
1405
1406 static GtkDragSourceInfo *
gtk_drag_get_source_info(GdkDragContext * context,gboolean create)1407 gtk_drag_get_source_info (GdkDragContext *context,
1408 gboolean create)
1409 {
1410 GtkDragSourceInfo *info;
1411 if (!dest_info_quark)
1412 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1413
1414 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1415 if (!info && create)
1416 {
1417 info = g_new0 (GtkDragSourceInfo, 1);
1418 info->context = context;
1419 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1420 }
1421
1422 return info;
1423 }
1424
1425 static void
gtk_drag_clear_source_info(GdkDragContext * context)1426 gtk_drag_clear_source_info (GdkDragContext *context)
1427 {
1428 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1429 }
1430
1431 /*
1432 * Default drag handlers
1433 */
1434 static void
gtk_drag_dest_leave(GtkWidget * widget,GdkDragContext * context,guint time)1435 gtk_drag_dest_leave (GtkWidget *widget,
1436 GdkDragContext *context,
1437 guint time)
1438 {
1439 GtkDragDestSite *site;
1440
1441 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1442 g_return_if_fail (site != NULL);
1443
1444 if (site->do_proxy)
1445 {
1446 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1447
1448 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1449 {
1450 gdk_drag_abort (info->proxy_source->context, time);
1451 gtk_drag_source_info_destroy (info->proxy_source);
1452 info->proxy_source = NULL;
1453 }
1454
1455 return;
1456 }
1457 else
1458 {
1459 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1460 gtk_drag_unhighlight (widget);
1461
1462 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
1463 site->track_motion)
1464 g_signal_emit_by_name (widget, "drag-leave", context, time);
1465
1466 site->have_drag = FALSE;
1467 }
1468 }
1469
1470 static gboolean
gtk_drag_dest_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)1471 gtk_drag_dest_motion (GtkWidget *widget,
1472 GdkDragContext *context,
1473 gint x,
1474 gint y,
1475 guint time)
1476 {
1477 GtkDragDestSite *site;
1478 GdkDragAction action = 0;
1479 gboolean retval;
1480
1481 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1482 g_return_val_if_fail (site != NULL, FALSE);
1483
1484 if (site->do_proxy)
1485 {
1486 GdkAtom selection;
1487 GdkEvent *current_event;
1488 GdkWindow *dest_window;
1489 GdkDragProtocol proto;
1490
1491 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1492
1493 if (!info->proxy_source || info->proxy_source->widget != widget)
1494 gtk_drag_proxy_begin (widget, info, time);
1495
1496 current_event = gtk_get_current_event ();
1497
1498 if (site->proxy_window)
1499 {
1500 dest_window = site->proxy_window;
1501 proto = site->proxy_protocol;
1502 }
1503 else
1504 {
1505 gdk_drag_find_window_for_screen (info->proxy_source->context,
1506 NULL,
1507 gdk_window_get_screen (current_event->dnd.window),
1508 current_event->dnd.x_root,
1509 current_event->dnd.y_root,
1510 &dest_window, &proto);
1511 }
1512
1513 gdk_drag_motion (info->proxy_source->context,
1514 dest_window, proto,
1515 current_event->dnd.x_root,
1516 current_event->dnd.y_root,
1517 gdk_drag_context_get_suggested_action (context),
1518 gdk_drag_context_get_actions (context),
1519 time);
1520
1521 if (!site->proxy_window && dest_window)
1522 g_object_unref (dest_window);
1523
1524 selection = gdk_drag_get_selection (info->proxy_source->context);
1525 if (selection &&
1526 selection != gdk_drag_get_selection (info->context))
1527 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1528
1529 gdk_event_free (current_event);
1530
1531 return TRUE;
1532 }
1533
1534 if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
1535 {
1536 if (gdk_drag_context_get_suggested_action (context) & site->actions)
1537 action = gdk_drag_context_get_suggested_action (context);
1538 else
1539 {
1540 gint i;
1541
1542 for (i = 0; i < 8; i++)
1543 {
1544 if ((site->actions & (1 << i)) &&
1545 (gdk_drag_context_get_actions (context) & (1 << i)))
1546 {
1547 action = (1 << i);
1548 break;
1549 }
1550 }
1551 }
1552
1553 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1554 {
1555 if (!site->have_drag)
1556 {
1557 site->have_drag = TRUE;
1558 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1559 gtk_drag_highlight (widget);
1560 }
1561
1562 gdk_drag_status (context, action, time);
1563 }
1564 else
1565 {
1566 gdk_drag_status (context, 0, time);
1567 if (!site->track_motion)
1568 return TRUE;
1569 }
1570 }
1571
1572 g_signal_emit_by_name (widget, "drag-motion",
1573 context, x, y, time, &retval);
1574
1575 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1576 }
1577
1578 static gboolean
gtk_drag_dest_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)1579 gtk_drag_dest_drop (GtkWidget *widget,
1580 GdkDragContext *context,
1581 gint x,
1582 gint y,
1583 guint time)
1584 {
1585 GtkDragDestSite *site;
1586 GtkDragDestInfo *info;
1587
1588 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1589 g_return_val_if_fail (site != NULL, FALSE);
1590
1591 info = gtk_drag_get_dest_info (context, FALSE);
1592 g_return_val_if_fail (info != NULL, FALSE);
1593
1594 info->drop_x = x;
1595 info->drop_y = y;
1596
1597 if (site->do_proxy)
1598 {
1599 if (info->proxy_source ||
1600 (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_ROOTWIN))
1601 {
1602 gtk_drag_drop (info->proxy_source, time);
1603 }
1604 else
1605 {
1606 /* We need to synthesize a motion event, wait for a status,
1607 * and, if we get a good one, do a drop.
1608 */
1609
1610 GdkEvent *current_event;
1611 GdkAtom selection;
1612 GdkWindow *dest_window;
1613 GdkDragProtocol proto;
1614
1615 gtk_drag_proxy_begin (widget, info, time);
1616 info->proxy_drop_wait = TRUE;
1617 info->proxy_drop_time = time;
1618
1619 current_event = gtk_get_current_event ();
1620
1621 if (site->proxy_window)
1622 {
1623 dest_window = site->proxy_window;
1624 proto = site->proxy_protocol;
1625 }
1626 else
1627 {
1628 gdk_drag_find_window_for_screen (info->proxy_source->context,
1629 NULL,
1630 gdk_window_get_screen (current_event->dnd.window),
1631 current_event->dnd.x_root,
1632 current_event->dnd.y_root,
1633 &dest_window, &proto);
1634 }
1635
1636 gdk_drag_motion (info->proxy_source->context,
1637 dest_window, proto,
1638 current_event->dnd.x_root,
1639 current_event->dnd.y_root,
1640 gdk_drag_context_get_suggested_action (context),
1641 gdk_drag_context_get_actions (context),
1642 time);
1643
1644 if (!site->proxy_window && dest_window)
1645 g_object_unref (dest_window);
1646
1647 selection = gdk_drag_get_selection (info->proxy_source->context);
1648 if (selection &&
1649 selection != gdk_drag_get_selection (info->context))
1650 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1651
1652 gdk_event_free (current_event);
1653 }
1654
1655 return TRUE;
1656 }
1657 else
1658 {
1659 gboolean retval;
1660
1661 if (site->flags & GTK_DEST_DEFAULT_DROP)
1662 {
1663 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1664
1665 if (target == GDK_NONE)
1666 {
1667 gtk_drag_finish (context, FALSE, FALSE, time);
1668 return TRUE;
1669 }
1670 else
1671 gtk_drag_get_data (widget, context, target, time);
1672 }
1673
1674 g_signal_emit_by_name (widget, "drag-drop",
1675 context, x, y, time, &retval);
1676
1677 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1678 }
1679 }
1680
1681 /***************
1682 * Source side *
1683 ***************/
1684
1685
1686 static gboolean
gtk_drag_is_managed(GtkWidget * source_widget)1687 gtk_drag_is_managed (GtkWidget *source_widget)
1688 {
1689 return
1690 #ifdef GDK_WINDOWING_X11
1691 GDK_IS_X11_DISPLAY (gtk_widget_get_display (source_widget)) ||
1692 #endif
1693 #ifdef GDK_WINDOWING_WAYLAND
1694 GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (source_widget)) ||
1695 #endif
1696 #ifdef GDK_WINDOWING_WIN32
1697 GDK_IS_WIN32_DISPLAY (gtk_widget_get_display (source_widget)) ||
1698 #endif
1699 FALSE;
1700 }
1701
1702 /* Like gtk_drag_begin(), but also communicates the need to
1703 * create an icon for the drag operation back to the caller.
1704 * If the caller passes out_needs_icon == NULL, it means that
1705 * the caller does not care.
1706 */
1707 GdkDragContext *
gtk_drag_begin_internal(GtkWidget * widget,gboolean * out_needs_icon,GtkTargetList * target_list,GdkDragAction actions,gint button,const GdkEvent * event,int x,int y)1708 gtk_drag_begin_internal (GtkWidget *widget,
1709 gboolean *out_needs_icon,
1710 GtkTargetList *target_list,
1711 GdkDragAction actions,
1712 gint button,
1713 const GdkEvent *event,
1714 int x,
1715 int y)
1716 {
1717 GtkDragSourceInfo *info;
1718 GList *targets = NULL;
1719 GList *tmp_list;
1720 guint32 time = GDK_CURRENT_TIME;
1721 GdkDragAction possible_actions, suggested_action;
1722 GdkDragContext *context;
1723 GtkWidget *ipc_widget;
1724 GdkCursor *cursor;
1725 GdkDevice *pointer, *keyboard;
1726 GdkWindow *ipc_window;
1727 gint start_x, start_y;
1728 GdkAtom selection;
1729 gboolean managed;
1730
1731 managed = gtk_drag_is_managed (widget);
1732
1733 pointer = keyboard = NULL;
1734 ipc_widget = gtk_drag_get_ipc_widget (widget);
1735
1736 gtk_drag_get_event_actions (event, button, actions,
1737 &suggested_action, &possible_actions);
1738
1739 cursor = gtk_drag_get_cursor (widget,
1740 gtk_widget_get_display (widget),
1741 suggested_action,
1742 NULL);
1743
1744 if (event)
1745 {
1746 time = gdk_event_get_time (event);
1747 if (time == GDK_CURRENT_TIME)
1748 time = gtk_get_current_event_time ();
1749
1750 pointer = gdk_event_get_device (event);
1751
1752 if (gdk_device_get_source (pointer) == GDK_SOURCE_KEYBOARD)
1753 {
1754 keyboard = pointer;
1755 pointer = gdk_device_get_associated_device (keyboard);
1756 }
1757 else
1758 keyboard = gdk_device_get_associated_device (pointer);
1759 }
1760 else
1761 {
1762 GdkSeat *seat;
1763
1764 seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
1765 pointer = gdk_seat_get_pointer (seat);
1766 keyboard = gdk_seat_get_keyboard (seat);
1767 }
1768
1769 if (!pointer)
1770 return NULL;
1771
1772 ipc_window = gtk_widget_get_window (ipc_widget);
1773
1774 if (!managed)
1775 {
1776 gboolean grabbed;
1777
1778 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1779 grabbed = gdk_device_grab (pointer, ipc_window,
1780 GDK_OWNERSHIP_APPLICATION, FALSE,
1781 GDK_POINTER_MOTION_MASK |
1782 GDK_BUTTON_RELEASE_MASK,
1783 cursor, time) == GDK_GRAB_SUCCESS;
1784 G_GNUC_END_IGNORE_DEPRECATIONS;
1785
1786 if (!grabbed)
1787 {
1788 gtk_drag_release_ipc_widget (ipc_widget);
1789 return NULL;
1790 }
1791
1792 if (keyboard)
1793 grab_dnd_keys (ipc_widget, keyboard, time);
1794
1795 /* We use a GTK grab here to override any grabs that the widget
1796 * we are dragging from might have held
1797 */
1798 gtk_device_grab_add (ipc_widget, pointer, FALSE);
1799 }
1800
1801 tmp_list = g_list_last (target_list->list);
1802 while (tmp_list)
1803 {
1804 GtkTargetPair *pair = tmp_list->data;
1805 targets = g_list_prepend (targets,
1806 GINT_TO_POINTER (pair->target));
1807 tmp_list = tmp_list->prev;
1808 }
1809
1810 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1811
1812 if (x != -1 && y != -1)
1813 {
1814 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1815 gtk_widget_translate_coordinates (widget, toplevel,
1816 x, y, &x, &y);
1817 gdk_window_get_root_coords (gtk_widget_get_window (toplevel),
1818 x, y, &start_x, &start_y);
1819 }
1820 else if (event && event->type == GDK_MOTION_NOTIFY)
1821 {
1822 start_x = event->motion.x_root;
1823 start_y = event->motion.y_root;
1824 }
1825 else
1826 gdk_device_get_position (pointer, NULL, &start_x, &start_y);
1827
1828 context = gdk_drag_begin_from_point (ipc_window, pointer, targets, start_x, start_y);
1829
1830 gdk_drag_context_set_device (context, pointer);
1831 g_list_free (targets);
1832
1833 if (managed &&
1834 !gdk_drag_context_manage_dnd (context, ipc_window, actions))
1835 {
1836 gtk_drag_release_ipc_widget (ipc_widget);
1837 g_object_unref (context);
1838 return NULL;
1839 }
1840
1841 info = gtk_drag_get_source_info (context, TRUE);
1842
1843 info->ipc_widget = ipc_widget;
1844 g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), info);
1845
1846 info->widget = g_object_ref (widget);
1847
1848 info->button = button;
1849 info->cursor = cursor;
1850 info->target_list = target_list;
1851 gtk_target_list_ref (target_list);
1852
1853 info->possible_actions = actions;
1854
1855 info->status = GTK_DRAG_STATUS_DRAG;
1856 info->last_event = NULL;
1857 info->selections = NULL;
1858 info->icon_window = NULL;
1859 info->icon_widget = NULL;
1860 info->destroy_icon = FALSE;
1861
1862 if (event)
1863 info->cur_screen = gdk_event_get_screen (event);
1864 else
1865 gdk_device_get_position (pointer, &info->cur_screen, NULL, NULL);
1866
1867 info->start_x = start_x;
1868 info->start_y = start_y;
1869
1870 gtk_widget_reset_controllers (widget);
1871
1872 g_signal_emit_by_name (widget, "drag-begin", info->context);
1873
1874 /* Ensure that we have an icon before we start the drag; the
1875 * application may have set one in ::drag_begin, or it may
1876 * not have set one.
1877 */
1878 if (!info->icon_widget && out_needs_icon == NULL)
1879 {
1880 GtkImageDefinition *icon = gtk_image_definition_new_icon_name ("text-x-generic");
1881 set_icon_helper (info->context, icon, 0, 0);
1882 gtk_image_definition_unref (icon);
1883 }
1884
1885 if (out_needs_icon != NULL)
1886 *out_needs_icon = (info->icon_widget == NULL);
1887
1888 if (managed)
1889 {
1890 g_signal_connect (context, "drop-performed",
1891 G_CALLBACK (gtk_drag_context_drop_performed_cb), info);
1892 g_signal_connect (context, "dnd-finished",
1893 G_CALLBACK (gtk_drag_context_dnd_finished_cb), info);
1894 g_signal_connect (context, "cancel",
1895 G_CALLBACK (gtk_drag_context_cancel_cb), info);
1896 g_signal_connect (context, "action-changed",
1897 G_CALLBACK (gtk_drag_context_action_cb), info);
1898
1899 selection = gdk_drag_get_selection (context);
1900 if (selection)
1901 gtk_drag_source_check_selection (info, selection, time);
1902 }
1903 else
1904 {
1905 info->cur_x = info->start_x;
1906 info->cur_y = info->start_y;
1907
1908 if (event && event->type == GDK_MOTION_NOTIFY)
1909 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1910 else
1911 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
1912
1913 g_signal_connect (info->ipc_widget, "grab-broken-event",
1914 G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
1915 g_signal_connect (info->ipc_widget, "grab-notify",
1916 G_CALLBACK (gtk_drag_grab_notify_cb), info);
1917 g_signal_connect (info->ipc_widget, "button-release-event",
1918 G_CALLBACK (gtk_drag_button_release_cb), info);
1919 g_signal_connect (info->ipc_widget, "motion-notify-event",
1920 G_CALLBACK (gtk_drag_motion_cb), info);
1921 g_signal_connect (info->ipc_widget, "key-press-event",
1922 G_CALLBACK (gtk_drag_key_cb), info);
1923 g_signal_connect (info->ipc_widget, "key-release-event",
1924 G_CALLBACK (gtk_drag_key_cb), info);
1925 }
1926
1927 g_signal_connect (info->ipc_widget, "selection-get",
1928 G_CALLBACK (gtk_drag_selection_get), info);
1929
1930 info->have_grab = TRUE;
1931 info->grab_time = time;
1932
1933 return info->context;
1934 }
1935
1936 /**
1937 * gtk_drag_begin_with_coordinates: (method)
1938 * @widget: the source widget
1939 * @targets: The targets (data formats) in which the
1940 * source can provide the data
1941 * @actions: A bitmask of the allowed drag actions for this drag
1942 * @button: The button the user clicked to start the drag
1943 * @event: (nullable): The event that triggered the start of the drag,
1944 * or %NULL if none can be obtained.
1945 * @x: The initial x coordinate to start dragging from, in the coordinate space
1946 * of @widget. If -1 is passed, the coordinates are retrieved from @event or
1947 * the current pointer position
1948 * @y: The initial y coordinate to start dragging from, in the coordinate space
1949 * of @widget. If -1 is passed, the coordinates are retrieved from @event or
1950 * the current pointer position
1951 *
1952 * Initiates a drag on the source side. The function only needs to be used
1953 * when the application is starting drags itself, and is not needed when
1954 * gtk_drag_source_set() is used.
1955 *
1956 * The @event is used to retrieve the timestamp that will be used internally to
1957 * grab the pointer. If @event is %NULL, then %GDK_CURRENT_TIME will be used.
1958 * However, you should try to pass a real event in all cases, since that can be
1959 * used to get information about the drag.
1960 *
1961 * Generally there are three cases when you want to start a drag by hand by
1962 * calling this function:
1963 *
1964 * 1. During a #GtkWidget::button-press-event handler, if you want to start a drag
1965 * immediately when the user presses the mouse button. Pass the @event
1966 * that you have in your #GtkWidget::button-press-event handler.
1967 *
1968 * 2. During a #GtkWidget::motion-notify-event handler, if you want to start a drag
1969 * when the mouse moves past a certain threshold distance after a button-press.
1970 * Pass the @event that you have in your #GtkWidget::motion-notify-event handler.
1971 *
1972 * 3. During a timeout handler, if you want to start a drag after the mouse
1973 * button is held down for some time. Try to save the last event that you got
1974 * from the mouse, using gdk_event_copy(), and pass it to this function
1975 * (remember to free the event with gdk_event_free() when you are done).
1976 * If you really cannot pass a real event, pass %NULL instead.
1977 *
1978 * Returns: (transfer none): the context for this drag
1979 *
1980 * Since: 3.10
1981 */
1982 GdkDragContext *
gtk_drag_begin_with_coordinates(GtkWidget * widget,GtkTargetList * targets,GdkDragAction actions,gint button,GdkEvent * event,gint x,gint y)1983 gtk_drag_begin_with_coordinates (GtkWidget *widget,
1984 GtkTargetList *targets,
1985 GdkDragAction actions,
1986 gint button,
1987 GdkEvent *event,
1988 gint x,
1989 gint y)
1990 {
1991 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1992 g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
1993 g_return_val_if_fail (targets != NULL, NULL);
1994
1995 return gtk_drag_begin_internal (widget, NULL, targets,
1996 actions, button, event, x, y);
1997 }
1998
1999 /**
2000 * gtk_drag_begin: (method)
2001 * @widget: the source widget
2002 * @targets: The targets (data formats) in which the
2003 * source can provide the data
2004 * @actions: A bitmask of the allowed drag actions for this drag
2005 * @button: The button the user clicked to start the drag
2006 * @event: (nullable): The event that triggered the start of the drag,
2007 * or %NULL if none can be obtained.
2008 *
2009 * This function is equivalent to gtk_drag_begin_with_coordinates(),
2010 * passing -1, -1 as coordinates.
2011 *
2012 * Returns: (transfer none): the context for this drag
2013 *
2014 * Deprecated: 3.10: Use gtk_drag_begin_with_coordinates() instead
2015 */
2016 GdkDragContext *
gtk_drag_begin(GtkWidget * widget,GtkTargetList * targets,GdkDragAction actions,gint button,GdkEvent * event)2017 gtk_drag_begin (GtkWidget *widget,
2018 GtkTargetList *targets,
2019 GdkDragAction actions,
2020 gint button,
2021 GdkEvent *event)
2022 {
2023 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2024 g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
2025 g_return_val_if_fail (targets != NULL, NULL);
2026
2027 return gtk_drag_begin_internal (widget, NULL, targets,
2028 actions, button, event, -1, -1);
2029 }
2030
2031 static void
icon_widget_destroyed(GtkWidget * widget,GtkDragSourceInfo * info)2032 icon_widget_destroyed (GtkWidget *widget,
2033 GtkDragSourceInfo *info)
2034 {
2035 g_clear_object (&info->icon_widget);
2036 }
2037
2038 static void
gtk_drag_update_icon_window(GtkDragSourceInfo * info)2039 gtk_drag_update_icon_window (GtkDragSourceInfo *info)
2040 {
2041 if (!gtk_drag_is_managed (info->widget) && info->icon_window)
2042 {
2043 gtk_window_move (GTK_WINDOW (info->icon_window),
2044 info->cur_x - info->hot_x,
2045 info->cur_y - info->hot_y);
2046
2047 if (gtk_widget_get_visible (info->icon_window))
2048 gdk_window_raise (gtk_widget_get_window (info->icon_window));
2049 else
2050 gtk_widget_show (info->icon_window);
2051 }
2052 }
2053
2054 static void
gtk_drag_set_icon_widget_internal(GdkDragContext * context,GtkWidget * widget,gint hot_x,gint hot_y,gboolean destroy_on_release)2055 gtk_drag_set_icon_widget_internal (GdkDragContext *context,
2056 GtkWidget *widget,
2057 gint hot_x,
2058 gint hot_y,
2059 gboolean destroy_on_release)
2060 {
2061 GtkDragSourceInfo *info;
2062
2063 info = gtk_drag_get_source_info (context, FALSE);
2064 if (info == NULL)
2065 {
2066 if (destroy_on_release)
2067 gtk_widget_destroy (widget);
2068 return;
2069 }
2070
2071 gtk_drag_remove_icon (info);
2072
2073 if (widget)
2074 g_object_ref (widget);
2075
2076 info->icon_widget = widget;
2077 info->hot_x = hot_x;
2078 info->hot_y = hot_y;
2079 info->destroy_icon = destroy_on_release;
2080
2081 if (!widget)
2082 goto out;
2083
2084 g_signal_connect (widget, "destroy", G_CALLBACK (icon_widget_destroyed), info);
2085
2086 gdk_drag_context_set_hotspot (context, hot_x, hot_y);
2087
2088 if (!info->icon_window)
2089 {
2090 GdkScreen *screen;
2091 GdkVisual *visual;
2092 gboolean has_rgba;
2093
2094 screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
2095 visual = gdk_screen_get_rgba_visual (screen);
2096 has_rgba = visual != NULL && gdk_screen_is_composited (screen);
2097
2098 info->icon_window = gtk_window_new (GTK_WINDOW_POPUP);
2099 gtk_window_set_type_hint (GTK_WINDOW (info->icon_window), GDK_WINDOW_TYPE_HINT_DND);
2100 gtk_window_set_screen (GTK_WINDOW (info->icon_window), screen);
2101 gtk_widget_set_size_request (info->icon_window, 24, 24);
2102 if (visual)
2103 gtk_widget_set_visual (info->icon_window, visual);
2104 gtk_widget_set_events (info->icon_window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2105
2106 if (has_rgba)
2107 gtk_widget_set_app_paintable (info->icon_window, TRUE);
2108
2109 gtk_window_set_hardcoded_window (GTK_WINDOW (info->icon_window),
2110 gdk_drag_context_get_drag_window (context));
2111 gtk_widget_show (info->icon_window);
2112 }
2113
2114 if (GTK_IS_WINDOW (widget))
2115 {
2116 gtk_widget_hide (widget);
2117 gtk_widget_unrealize (widget);
2118 gtk_widget_set_parent_window (widget, gtk_widget_get_window (info->icon_window));
2119 gtk_widget_show (widget);
2120 }
2121
2122 if (gtk_bin_get_child (GTK_BIN (info->icon_window)))
2123 gtk_container_remove (GTK_CONTAINER (info->icon_window), gtk_bin_get_child (GTK_BIN (info->icon_window)));
2124 gtk_container_add (GTK_CONTAINER (info->icon_window), widget);
2125
2126 out:
2127 gtk_drag_update_cursor (info);
2128 gtk_drag_update_icon_window (info);
2129 }
2130
2131 /**
2132 * gtk_drag_set_icon_widget:
2133 * @context: the context for a drag. (This must be called
2134 with a context for the source side of a drag)
2135 * @widget: a widget to use as an icon
2136 * @hot_x: the X offset within @widget of the hotspot
2137 * @hot_y: the Y offset within @widget of the hotspot
2138 *
2139 * Changes the icon for drag operation to a given widget.
2140 * GTK+ will not destroy the widget, so if you don’t want
2141 * it to persist, you should connect to the “drag-end”
2142 * signal and destroy it yourself.
2143 */
2144 void
gtk_drag_set_icon_widget(GdkDragContext * context,GtkWidget * widget,gint hot_x,gint hot_y)2145 gtk_drag_set_icon_widget (GdkDragContext *context,
2146 GtkWidget *widget,
2147 gint hot_x,
2148 gint hot_y)
2149 {
2150 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2151 g_return_if_fail (GTK_IS_WIDGET (widget));
2152
2153 gtk_drag_set_icon_widget_internal (context, widget, hot_x, hot_y, FALSE);
2154 }
2155
2156 static void
gtk_drag_draw_icon_pattern(GtkWidget * window,cairo_t * cr,gpointer pattern)2157 gtk_drag_draw_icon_pattern (GtkWidget *window,
2158 cairo_t *cr,
2159 gpointer pattern)
2160 {
2161 cairo_set_source (cr, pattern);
2162 cairo_paint (cr);
2163 }
2164
2165 static void
gtk_drag_draw_icon_pattern_and_background(GtkWidget * window,cairo_t * cr,gpointer pattern)2166 gtk_drag_draw_icon_pattern_and_background (GtkWidget *window,
2167 cairo_t *cr,
2168 gpointer pattern)
2169 {
2170 GtkStyleContext *context;
2171 int width, height;
2172
2173 context = gtk_widget_get_style_context (window);
2174 width = gtk_widget_get_allocated_width (window);
2175 height = gtk_widget_get_allocated_height (window);
2176
2177 gtk_render_background (context, cr, 0, 0, width, height);
2178 gtk_render_frame (context, cr, 0, 0, width, height);
2179
2180 cairo_set_source (cr, pattern);
2181 cairo_paint (cr);
2182 }
2183
2184 static void
set_icon_helper(GdkDragContext * context,GtkImageDefinition * def,gint hot_x,gint hot_y)2185 set_icon_helper (GdkDragContext *context,
2186 GtkImageDefinition *def,
2187 gint hot_x,
2188 gint hot_y)
2189 {
2190 GtkWidget *widget;
2191
2192 widget = gtk_image_new ();
2193 gtk_widget_show (widget);
2194
2195 gtk_image_set_from_definition (GTK_IMAGE (widget), def, GTK_ICON_SIZE_DND);
2196
2197 gtk_drag_set_icon_widget_internal (context, widget, hot_x, hot_y, TRUE);
2198 }
2199
2200 void
gtk_drag_set_icon_definition(GdkDragContext * context,GtkImageDefinition * def,gint hot_x,gint hot_y)2201 gtk_drag_set_icon_definition (GdkDragContext *context,
2202 GtkImageDefinition *def,
2203 gint hot_x,
2204 gint hot_y)
2205 {
2206 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2207 g_return_if_fail (def != NULL);
2208
2209 set_icon_helper (context, def, hot_x, hot_y);
2210 }
2211
2212 /**
2213 * gtk_drag_set_icon_pixbuf:
2214 * @context: the context for a drag (This must be called
2215 * with a context for the source side of a drag)
2216 * @pixbuf: the #GdkPixbuf to use as the drag icon
2217 * @hot_x: the X offset within @widget of the hotspot
2218 * @hot_y: the Y offset within @widget of the hotspot
2219 *
2220 * Sets @pixbuf as the icon for a given drag.
2221 */
2222 void
gtk_drag_set_icon_pixbuf(GdkDragContext * context,GdkPixbuf * pixbuf,gint hot_x,gint hot_y)2223 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2224 GdkPixbuf *pixbuf,
2225 gint hot_x,
2226 gint hot_y)
2227 {
2228 GtkImageDefinition *def;
2229
2230 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2231 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2232
2233 def = gtk_image_definition_new_pixbuf (pixbuf, 1);
2234 set_icon_helper (context, def, hot_x, hot_y);
2235
2236 gtk_image_definition_unref (def);
2237 }
2238
2239 /**
2240 * gtk_drag_set_icon_stock:
2241 * @context: the context for a drag (This must be called
2242 * with a context for the source side of a drag)
2243 * @stock_id: the ID of the stock icon to use for the drag
2244 * @hot_x: the X offset within the icon of the hotspot
2245 * @hot_y: the Y offset within the icon of the hotspot
2246 *
2247 * Sets the icon for a given drag from a stock ID.
2248 *
2249 * Deprecated: 3.10: Use gtk_drag_set_icon_name() instead.
2250 */
2251 void
gtk_drag_set_icon_stock(GdkDragContext * context,const gchar * stock_id,gint hot_x,gint hot_y)2252 gtk_drag_set_icon_stock (GdkDragContext *context,
2253 const gchar *stock_id,
2254 gint hot_x,
2255 gint hot_y)
2256 {
2257 GtkImageDefinition *def;
2258
2259 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2260 g_return_if_fail (stock_id != NULL);
2261
2262 def = gtk_image_definition_new_stock (stock_id);
2263 set_icon_helper (context, def, hot_x, hot_y);
2264
2265 gtk_image_definition_unref (def);
2266 }
2267
2268 /* XXX: This function is in gdk, too. Should it be in Cairo? */
2269 static gboolean
_gtk_cairo_surface_extents(cairo_surface_t * surface,GdkRectangle * extents)2270 _gtk_cairo_surface_extents (cairo_surface_t *surface,
2271 GdkRectangle *extents)
2272 {
2273 double x1, x2, y1, y2;
2274 cairo_t *cr;
2275
2276 g_return_val_if_fail (surface != NULL, FALSE);
2277 g_return_val_if_fail (extents != NULL, FALSE);
2278
2279 cr = cairo_create (surface);
2280 cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
2281 cairo_destroy (cr);
2282
2283 x1 = floor (x1);
2284 y1 = floor (y1);
2285 x2 = ceil (x2);
2286 y2 = ceil (y2);
2287 x2 -= x1;
2288 y2 -= y1;
2289
2290 if (x1 < G_MININT || x1 > G_MAXINT ||
2291 y1 < G_MININT || y1 > G_MAXINT ||
2292 x2 > G_MAXINT || y2 > G_MAXINT)
2293 {
2294 extents->x = extents->y = extents->width = extents->height = 0;
2295 return FALSE;
2296 }
2297
2298 extents->x = x1;
2299 extents->y = y1;
2300 extents->width = x2;
2301 extents->height = y2;
2302
2303 return TRUE;
2304 }
2305
2306 /**
2307 * gtk_drag_set_icon_surface:
2308 * @context: the context for a drag (This must be called
2309 * with a context for the source side of a drag)
2310 * @surface: the surface to use as icon
2311 *
2312 * Sets @surface as the icon for a given drag. GTK+ retains
2313 * references for the arguments, and will release them when
2314 * they are no longer needed.
2315 *
2316 * To position the surface relative to the mouse, use
2317 * cairo_surface_set_device_offset() on @surface. The mouse
2318 * cursor will be positioned at the (0,0) coordinate of the
2319 * surface.
2320 */
2321 void
gtk_drag_set_icon_surface(GdkDragContext * context,cairo_surface_t * surface)2322 gtk_drag_set_icon_surface (GdkDragContext *context,
2323 cairo_surface_t *surface)
2324 {
2325 GtkWidget *window;
2326 GdkScreen *screen;
2327 GdkRectangle extents;
2328 cairo_pattern_t *pattern;
2329 GdkVisual *rgba_visual;
2330 gboolean has_rgba;
2331 cairo_matrix_t matrix;
2332
2333 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2334 g_return_if_fail (surface != NULL);
2335
2336 _gtk_cairo_surface_extents (surface, &extents);
2337
2338 screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
2339 rgba_visual = gdk_screen_get_rgba_visual (screen);
2340
2341 window = gtk_window_new (GTK_WINDOW_POPUP);
2342 has_rgba = rgba_visual != NULL && gdk_screen_is_composited (screen);
2343
2344 gtk_window_set_screen (GTK_WINDOW (window), screen);
2345
2346 if (has_rgba)
2347 gtk_widget_set_visual (GTK_WIDGET (window), rgba_visual);
2348
2349 gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
2350
2351 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2352 gtk_widget_set_app_paintable (window, TRUE);
2353
2354 gtk_widget_set_size_request (window, extents.width, extents.height);
2355 gtk_widget_realize (window);
2356
2357 pattern = cairo_pattern_create_for_surface (surface);
2358 cairo_matrix_init_translate (&matrix, extents.x, extents.y);
2359 cairo_pattern_set_matrix (pattern, &matrix);
2360
2361 g_signal_connect_data (window,
2362 "draw",
2363 has_rgba ? G_CALLBACK (gtk_drag_draw_icon_pattern)
2364 : G_CALLBACK (gtk_drag_draw_icon_pattern_and_background),
2365 pattern,
2366 (GClosureNotify) cairo_pattern_destroy,
2367 G_CONNECT_AFTER);
2368
2369 gtk_drag_set_icon_widget_internal (context, window, extents.x, extents.y, TRUE);
2370 }
2371
2372 /**
2373 * gtk_drag_set_icon_name:
2374 * @context: the context for a drag (This must be called
2375 * with a context for the source side of a drag)
2376 * @icon_name: name of icon to use
2377 * @hot_x: the X offset of the hotspot within the icon
2378 * @hot_y: the Y offset of the hotspot within the icon
2379 *
2380 * Sets the icon for a given drag from a named themed icon. See
2381 * the docs for #GtkIconTheme for more details. Note that the
2382 * size of the icon depends on the icon theme (the icon is
2383 * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus
2384 * @hot_x and @hot_y have to be used with care.
2385 *
2386 * Since: 2.8
2387 */
2388 void
gtk_drag_set_icon_name(GdkDragContext * context,const gchar * icon_name,gint hot_x,gint hot_y)2389 gtk_drag_set_icon_name (GdkDragContext *context,
2390 const gchar *icon_name,
2391 gint hot_x,
2392 gint hot_y)
2393 {
2394 GtkImageDefinition *def;
2395
2396 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2397 g_return_if_fail (icon_name != NULL && icon_name[0] != '\0');
2398
2399 def = gtk_image_definition_new_icon_name (icon_name);
2400 set_icon_helper (context, def, hot_x, hot_y);
2401
2402 gtk_image_definition_unref (def);
2403 }
2404
2405 /**
2406 * gtk_drag_set_icon_gicon:
2407 * @context: the context for a drag (This must be called
2408 * with a context for the source side of a drag)
2409 * @icon: a #GIcon
2410 * @hot_x: the X offset of the hotspot within the icon
2411 * @hot_y: the Y offset of the hotspot within the icon
2412 *
2413 * Sets the icon for a given drag from the given @icon.
2414 * See the documentation for gtk_drag_set_icon_name()
2415 * for more details about using icons in drag and drop.
2416 *
2417 * Since: 3.2
2418 */
2419 void
gtk_drag_set_icon_gicon(GdkDragContext * context,GIcon * icon,gint hot_x,gint hot_y)2420 gtk_drag_set_icon_gicon (GdkDragContext *context,
2421 GIcon *icon,
2422 gint hot_x,
2423 gint hot_y)
2424 {
2425 GtkImageDefinition *def;
2426
2427 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2428 g_return_if_fail (icon != NULL);
2429
2430 def = gtk_image_definition_new_gicon (icon);
2431 set_icon_helper (context, def, hot_x, hot_y);
2432
2433 gtk_image_definition_unref (def);
2434 }
2435
2436 /**
2437 * gtk_drag_set_icon_default:
2438 * @context: the context for a drag (This must be called
2439 * with a context for the source side of a drag)
2440 *
2441 * Sets the icon for a particular drag to the default
2442 * icon.
2443 */
2444 void
gtk_drag_set_icon_default(GdkDragContext * context)2445 gtk_drag_set_icon_default (GdkDragContext *context)
2446 {
2447 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2448
2449 gtk_drag_set_icon_name (context, "text-x-generic", -2, -2);
2450 }
2451
2452 /*
2453 * _gtk_drag_source_handle_event:
2454 * @toplevel: Toplevel widget that received the event
2455 * @event: the event to handle
2456 *
2457 * Called from widget event handling code on Drag events
2458 * for drag sources.
2459 */
2460 void
_gtk_drag_source_handle_event(GtkWidget * widget,GdkEvent * event)2461 _gtk_drag_source_handle_event (GtkWidget *widget,
2462 GdkEvent *event)
2463 {
2464 GtkDragSourceInfo *info;
2465 GdkDragContext *context;
2466
2467 g_return_if_fail (widget != NULL);
2468 g_return_if_fail (event != NULL);
2469
2470 context = event->dnd.context;
2471 info = gtk_drag_get_source_info (context, FALSE);
2472 if (!info)
2473 return;
2474
2475 switch (event->type)
2476 {
2477 case GDK_DRAG_STATUS:
2478 {
2479 GdkCursor *cursor;
2480 if (info->proxy_dest)
2481 {
2482 if (!event->dnd.send_event)
2483 {
2484 if (info->proxy_dest->proxy_drop_wait)
2485 {
2486 gboolean result = gdk_drag_context_get_selected_action (context) != 0;
2487
2488 /* Aha - we can finally pass the DROP on... */
2489 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2490 if (result)
2491 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2492 else
2493 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2494 }
2495 else
2496 {
2497 gdk_drag_status (info->proxy_dest->context,
2498 gdk_drag_context_get_selected_action (event->dnd.context),
2499 event->dnd.time);
2500 }
2501 }
2502 }
2503 else if (info->have_grab)
2504 {
2505 cursor = gtk_drag_get_cursor (widget,
2506 gtk_widget_get_display (widget),
2507 gdk_drag_context_get_selected_action (event->dnd.context),
2508 info);
2509 if (info->cursor != cursor)
2510 {
2511 GdkDevice *pointer;
2512
2513 pointer = gdk_drag_context_get_device (context);
2514 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2515 gdk_device_grab (pointer, gtk_widget_get_window (widget),
2516 GDK_OWNERSHIP_APPLICATION, FALSE,
2517 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
2518 cursor, info->grab_time);
2519 G_GNUC_END_IGNORE_DEPRECATIONS;
2520 info->cursor = cursor;
2521 }
2522
2523 gtk_drag_add_update_idle (info);
2524 }
2525 }
2526 break;
2527
2528 case GDK_DROP_FINISHED:
2529 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, event->dnd.time);
2530 break;
2531 default:
2532 g_assert_not_reached ();
2533 }
2534 }
2535
2536 static void
gtk_drag_source_check_selection(GtkDragSourceInfo * info,GdkAtom selection,guint32 time)2537 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2538 GdkAtom selection,
2539 guint32 time)
2540 {
2541 GList *tmp_list;
2542
2543 tmp_list = info->selections;
2544 while (tmp_list)
2545 {
2546 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2547 return;
2548 tmp_list = tmp_list->next;
2549 }
2550
2551 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2552 info->ipc_widget,
2553 selection,
2554 time);
2555 info->selections = g_list_prepend (info->selections,
2556 GUINT_TO_POINTER (selection));
2557
2558 tmp_list = info->target_list->list;
2559 while (tmp_list)
2560 {
2561 GtkTargetPair *pair = tmp_list->data;
2562
2563 gtk_selection_add_target (info->ipc_widget,
2564 selection,
2565 pair->target,
2566 pair->info);
2567 tmp_list = tmp_list->next;
2568 }
2569
2570 gtk_selection_add_target (info->ipc_widget,
2571 selection,
2572 gdk_atom_intern_static_string ("DELETE"),
2573 TARGET_DELETE);
2574 }
2575
2576
2577 /* Clean up from the drag, and display snapback, if necessary. */
2578 static void
gtk_drag_drop_finished(GtkDragSourceInfo * info,GtkDragResult result,guint time)2579 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2580 GtkDragResult result,
2581 guint time)
2582 {
2583 gboolean success;
2584
2585 success = (result == GTK_DRAG_RESULT_SUCCESS);
2586 gtk_drag_source_release_selections (info, time);
2587
2588 if (info->proxy_dest)
2589 {
2590 /* The time from the event isn't reliable for Xdnd drags */
2591 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2592 info->proxy_dest->proxy_drop_time);
2593 gtk_drag_source_info_destroy (info);
2594 }
2595 else
2596 {
2597 if (!success)
2598 g_signal_emit_by_name (info->widget, "drag-failed",
2599 info->context, result, &success);
2600
2601 gdk_drag_drop_done (info->context, success);
2602 gtk_drag_source_info_destroy (info);
2603 }
2604 }
2605
2606 static void
gtk_drag_source_release_selections(GtkDragSourceInfo * info,guint32 time)2607 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2608 guint32 time)
2609 {
2610 GdkDisplay *display = gtk_widget_get_display (info->widget);
2611 GList *tmp_list = info->selections;
2612
2613 while (tmp_list)
2614 {
2615 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2616 if (gdk_selection_owner_get_for_display (display, selection) == gtk_widget_get_window (info->ipc_widget))
2617 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2618
2619 tmp_list = tmp_list->next;
2620 }
2621
2622 g_list_free (info->selections);
2623 info->selections = NULL;
2624 }
2625
2626 static void
gtk_drag_drop(GtkDragSourceInfo * info,guint32 time)2627 gtk_drag_drop (GtkDragSourceInfo *info,
2628 guint32 time)
2629 {
2630 if (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_ROOTWIN)
2631 {
2632 GtkSelectionData selection_data;
2633 GList *tmp_list;
2634 /* GTK+ traditionally has used application/x-rootwin-drop, but the
2635 * XDND spec specifies x-rootwindow-drop.
2636 */
2637 GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
2638 GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
2639
2640 tmp_list = info->target_list->list;
2641 while (tmp_list)
2642 {
2643 GtkTargetPair *pair = tmp_list->data;
2644
2645 if (pair->target == target1 || pair->target == target2)
2646 {
2647 selection_data.selection = GDK_NONE;
2648 selection_data.target = pair->target;
2649 selection_data.data = NULL;
2650 selection_data.length = -1;
2651
2652 g_signal_emit_by_name (info->widget, "drag-data-get",
2653 info->context, &selection_data,
2654 pair->info,
2655 time);
2656
2657 /* FIXME: Should we check for length >= 0 here? */
2658 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
2659 return;
2660 }
2661 tmp_list = tmp_list->next;
2662 }
2663 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
2664 }
2665 else
2666 {
2667 if (info->icon_window)
2668 gtk_widget_hide (info->icon_window);
2669
2670 gdk_drag_drop (info->context, time);
2671 info->drop_timeout = gdk_threads_add_timeout (DROP_ABORT_TIME,
2672 gtk_drag_abort_timeout,
2673 info);
2674 g_source_set_name_by_id (info->drop_timeout, "[gtk+] gtk_drag_abort_timeout");
2675 }
2676 }
2677
2678 /*
2679 * Source side callbacks.
2680 */
2681 static void
gtk_drag_selection_get(GtkWidget * widget,GtkSelectionData * selection_data,guint sel_info,guint32 time,gpointer data)2682 gtk_drag_selection_get (GtkWidget *widget,
2683 GtkSelectionData *selection_data,
2684 guint sel_info,
2685 guint32 time,
2686 gpointer data)
2687 {
2688 GtkDragSourceInfo *info = data;
2689 static GdkAtom null_atom = GDK_NONE;
2690 guint target_info;
2691
2692 if (!null_atom)
2693 null_atom = gdk_atom_intern_static_string ("NULL");
2694
2695 switch (sel_info)
2696 {
2697 case TARGET_DELETE:
2698 g_signal_emit_by_name (info->widget,
2699 "drag-data-delete",
2700 info->context);
2701 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2702 break;
2703 default:
2704 if (info->proxy_dest)
2705 {
2706 /* This is sort of dangerous and needs to be thought
2707 * through better
2708 */
2709 info->proxy_dest->proxy_data = selection_data;
2710 gtk_drag_get_data (info->widget,
2711 info->proxy_dest->context,
2712 gtk_selection_data_get_target (selection_data),
2713 time);
2714 gtk_main ();
2715 info->proxy_dest->proxy_data = NULL;
2716 }
2717 else
2718 {
2719 if (gtk_target_list_find (info->target_list,
2720 gtk_selection_data_get_target (selection_data),
2721 &target_info))
2722 {
2723 g_signal_emit_by_name (info->widget, "drag-data-get",
2724 info->context,
2725 selection_data,
2726 target_info,
2727 time);
2728 }
2729 }
2730 break;
2731 }
2732 }
2733
2734 static void
gtk_drag_remove_icon(GtkDragSourceInfo * info)2735 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2736 {
2737 if (info->icon_widget)
2738 {
2739 GtkWidget *widget;
2740
2741 widget = info->icon_widget;
2742 info->icon_widget = NULL;
2743
2744 g_signal_handlers_disconnect_by_func (widget, icon_widget_destroyed, info);
2745
2746 gtk_widget_hide (widget);
2747 gtk_widget_set_opacity (widget, 1.0);
2748
2749 if (info->destroy_icon)
2750 gtk_widget_destroy (widget);
2751 else
2752 gtk_container_remove (GTK_CONTAINER (info->icon_window), widget);
2753
2754 g_object_unref (widget);
2755 }
2756 }
2757
2758 static void
gtk_drag_source_info_free(GtkDragSourceInfo * info)2759 gtk_drag_source_info_free (GtkDragSourceInfo *info)
2760 {
2761 gtk_drag_remove_icon (info);
2762 gtk_widget_destroy (info->icon_window);
2763 g_free (info);
2764 }
2765
2766 static void
gtk_drag_source_info_destroy(GtkDragSourceInfo * info)2767 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
2768 {
2769 GdkDragContext *context;
2770 GdkEvent *last_event;
2771
2772 g_signal_handlers_disconnect_by_func (info->context,
2773 gtk_drag_context_drop_performed_cb,
2774 info);
2775 g_signal_handlers_disconnect_by_func (info->context,
2776 gtk_drag_context_dnd_finished_cb,
2777 info);
2778 g_signal_handlers_disconnect_by_func (info->context,
2779 gtk_drag_context_cancel_cb,
2780 info);
2781 g_signal_handlers_disconnect_by_func (info->context,
2782 gtk_drag_context_action_cb,
2783 info);
2784
2785 g_signal_handlers_disconnect_by_func (info->ipc_widget,
2786 gtk_drag_grab_broken_event_cb,
2787 info);
2788 g_signal_handlers_disconnect_by_func (info->ipc_widget,
2789 gtk_drag_grab_notify_cb,
2790 info);
2791 g_signal_handlers_disconnect_by_func (info->ipc_widget,
2792 gtk_drag_button_release_cb,
2793 info);
2794 g_signal_handlers_disconnect_by_func (info->ipc_widget,
2795 gtk_drag_motion_cb,
2796 info);
2797 g_signal_handlers_disconnect_by_func (info->ipc_widget,
2798 gtk_drag_key_cb,
2799 info);
2800 g_signal_handlers_disconnect_by_func (info->ipc_widget,
2801 gtk_drag_selection_get,
2802 info);
2803
2804 if (!info->proxy_dest)
2805 g_signal_emit_by_name (info->widget, "drag-end", info->context);
2806
2807 g_clear_object (&info->widget);
2808
2809 gtk_selection_remove_all (info->ipc_widget);
2810 g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), NULL);
2811 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2812 gtk_drag_release_ipc_widget (info->ipc_widget);
2813
2814 gtk_target_list_unref (info->target_list);
2815
2816 if (info->drop_timeout)
2817 g_source_remove (info->drop_timeout);
2818
2819 if (info->update_idle)
2820 g_source_remove (info->update_idle);
2821
2822 /* keep the icon_window alive until the (possible) drag cancel animation is done */
2823 g_object_set_data_full (G_OBJECT (info->context), "former-gtk-source-info", info, (GDestroyNotify)gtk_drag_source_info_free);
2824 context = info->context;
2825 last_event = info->last_event;
2826
2827 gtk_drag_clear_source_info (context);
2828
2829 if (last_event)
2830 gdk_event_free (last_event);
2831
2832 g_object_unref (context);
2833 }
2834
2835 static gboolean
gtk_drag_update_idle(gpointer data)2836 gtk_drag_update_idle (gpointer data)
2837 {
2838 GtkDragSourceInfo *info = data;
2839 GdkWindow *dest_window;
2840 GdkDragProtocol protocol;
2841 GdkAtom selection;
2842
2843 GdkDragAction action;
2844 GdkDragAction possible_actions;
2845 guint32 time;
2846
2847 info->update_idle = 0;
2848
2849 if (info->last_event)
2850 {
2851 time = gtk_drag_get_event_time (info->last_event);
2852 gtk_drag_get_event_actions (info->last_event,
2853 info->button,
2854 info->possible_actions,
2855 &action, &possible_actions);
2856
2857 gtk_drag_update_icon_window (info);
2858 gdk_drag_find_window_for_screen (info->context,
2859 info->icon_window ? gtk_widget_get_window (info->icon_window) : NULL,
2860 info->cur_screen, info->cur_x, info->cur_y,
2861 &dest_window, &protocol);
2862
2863 if (!gdk_drag_motion (info->context, dest_window, protocol,
2864 info->cur_x, info->cur_y, action,
2865 possible_actions,
2866 time))
2867 {
2868 gdk_event_free ((GdkEvent *)info->last_event);
2869 info->last_event = NULL;
2870 }
2871
2872 if (dest_window)
2873 g_object_unref (dest_window);
2874
2875 selection = gdk_drag_get_selection (info->context);
2876 if (selection)
2877 gtk_drag_source_check_selection (info, selection, time);
2878
2879 }
2880
2881 return FALSE;
2882 }
2883
2884 static void
gtk_drag_add_update_idle(GtkDragSourceInfo * info)2885 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
2886 {
2887 /* We use an idle lower than GDK_PRIORITY_REDRAW so that exposes
2888 * from the last move can catch up before we move again.
2889 */
2890 if (!info->update_idle)
2891 {
2892 info->update_idle = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW + 5,
2893 gtk_drag_update_idle,
2894 info,
2895 NULL);
2896 g_source_set_name_by_id (info->update_idle, "[gtk+] gtk_drag_update_idle");
2897 }
2898 }
2899
2900 /*
2901 * gtk_drag_update:
2902 * @info: DragSourceInfo for the drag
2903 * @screen: new screen
2904 * @x_root: new X position
2905 * @y_root: new y position
2906 * @event: event received requiring update
2907 *
2908 * Updates the status of the drag; called when the
2909 * cursor moves or the modifier changes
2910 */
2911 static void
gtk_drag_update(GtkDragSourceInfo * info,GdkScreen * screen,gint x_root,gint y_root,const GdkEvent * event)2912 gtk_drag_update (GtkDragSourceInfo *info,
2913 GdkScreen *screen,
2914 gint x_root,
2915 gint y_root,
2916 const GdkEvent *event)
2917 {
2918 info->cur_screen = screen;
2919 info->cur_x = x_root;
2920 info->cur_y = y_root;
2921 if (info->last_event)
2922 {
2923 gdk_event_free ((GdkEvent *)info->last_event);
2924 info->last_event = NULL;
2925 }
2926 if (event)
2927 info->last_event = gdk_event_copy ((GdkEvent *)event);
2928
2929 gtk_drag_add_update_idle (info);
2930 }
2931
2932 /* Called when the user finishes to drag, either by
2933 * releasing the mouse, or by pressing Esc.
2934 */
2935 static void
gtk_drag_end(GtkDragSourceInfo * info,guint32 time)2936 gtk_drag_end (GtkDragSourceInfo *info,
2937 guint32 time)
2938 {
2939 GdkDevice *pointer, *keyboard;
2940
2941 pointer = gdk_drag_context_get_device (info->context);
2942 keyboard = gdk_device_get_associated_device (pointer);
2943
2944 /* Prevent ungrab before grab (see bug 623865) */
2945 if (info->grab_time == GDK_CURRENT_TIME)
2946 time = GDK_CURRENT_TIME;
2947
2948 if (info->update_idle)
2949 {
2950 g_source_remove (info->update_idle);
2951 info->update_idle = 0;
2952 }
2953
2954 if (info->last_event)
2955 {
2956 gdk_event_free (info->last_event);
2957 info->last_event = NULL;
2958 }
2959
2960 info->have_grab = FALSE;
2961
2962 g_signal_handlers_disconnect_by_func (info->ipc_widget,
2963 gtk_drag_grab_broken_event_cb,
2964 info);
2965 g_signal_handlers_disconnect_by_func (info->ipc_widget,
2966 gtk_drag_grab_notify_cb,
2967 info);
2968 g_signal_handlers_disconnect_by_func (info->ipc_widget,
2969 gtk_drag_button_release_cb,
2970 info);
2971 g_signal_handlers_disconnect_by_func (info->ipc_widget,
2972 gtk_drag_motion_cb,
2973 info);
2974 g_signal_handlers_disconnect_by_func (info->ipc_widget,
2975 gtk_drag_key_cb,
2976 info);
2977
2978 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2979 gdk_device_ungrab (pointer, time);
2980 G_GNUC_END_IGNORE_DEPRECATIONS;
2981
2982 ungrab_dnd_keys (info->ipc_widget, keyboard, time);
2983 gtk_device_grab_remove (info->ipc_widget, pointer);
2984 }
2985
2986 /* Called on cancellation of a drag, either by the user
2987 * or programmatically.
2988 */
2989 static void
gtk_drag_cancel_internal(GtkDragSourceInfo * info,GtkDragResult result,guint32 time)2990 gtk_drag_cancel_internal (GtkDragSourceInfo *info,
2991 GtkDragResult result,
2992 guint32 time)
2993 {
2994 gtk_drag_end (info, time);
2995 gdk_drag_abort (info->context, time);
2996 gtk_drag_drop_finished (info, result, time);
2997 }
2998
2999 static void
gtk_drag_context_drop_performed_cb(GdkDragContext * context,guint32 time_,GtkDragSourceInfo * info)3000 gtk_drag_context_drop_performed_cb (GdkDragContext *context,
3001 guint32 time_,
3002 GtkDragSourceInfo *info)
3003 {
3004 gtk_drag_end (info, time_);
3005 gtk_drag_drop (info, time_);
3006 }
3007
3008 static void
gtk_drag_context_cancel_cb(GdkDragContext * context,GdkDragCancelReason reason,GtkDragSourceInfo * info)3009 gtk_drag_context_cancel_cb (GdkDragContext *context,
3010 GdkDragCancelReason reason,
3011 GtkDragSourceInfo *info)
3012 {
3013 GtkDragResult result;
3014
3015 switch (reason)
3016 {
3017 case GDK_DRAG_CANCEL_NO_TARGET:
3018 result = GTK_DRAG_RESULT_NO_TARGET;
3019 break;
3020 case GDK_DRAG_CANCEL_USER_CANCELLED:
3021 result = GTK_DRAG_RESULT_USER_CANCELLED;
3022 break;
3023 case GDK_DRAG_CANCEL_ERROR:
3024 default:
3025 result = GTK_DRAG_RESULT_ERROR;
3026 break;
3027 }
3028 gtk_drag_cancel_internal (info, result, GDK_CURRENT_TIME);
3029 }
3030
3031 static void
gtk_drag_context_action_cb(GdkDragContext * context,GdkDragAction action,GtkDragSourceInfo * info)3032 gtk_drag_context_action_cb (GdkDragContext *context,
3033 GdkDragAction action,
3034 GtkDragSourceInfo *info)
3035 {
3036 if (info->proxy_dest)
3037 {
3038 if (info->proxy_dest->proxy_drop_wait)
3039 {
3040 gboolean result = gdk_drag_context_get_selected_action (context) != 0;
3041
3042 /* Aha - we can finally pass the DROP on... */
3043 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
3044 if (result)
3045 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
3046 else
3047 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
3048 }
3049 else
3050 {
3051 gdk_drag_status (info->proxy_dest->context,
3052 gdk_drag_context_get_selected_action (context),
3053 GDK_CURRENT_TIME);
3054 }
3055
3056 g_signal_stop_emission_by_name (context, "action");
3057 }
3058 }
3059
3060 static void
gtk_drag_context_dnd_finished_cb(GdkDragContext * context,GtkDragSourceInfo * info)3061 gtk_drag_context_dnd_finished_cb (GdkDragContext *context,
3062 GtkDragSourceInfo *info)
3063 {
3064 gtk_drag_source_release_selections (info, GDK_CURRENT_TIME);
3065
3066 if (info->proxy_dest)
3067 {
3068 /* The time from the event isn't reliable for Xdnd drags */
3069 gtk_drag_finish (info->proxy_dest->context, TRUE, FALSE,
3070 info->proxy_dest->proxy_drop_time);
3071 }
3072
3073 gtk_drag_source_info_destroy (info);
3074 }
3075
3076 /* “motion-notify-event” callback during drag. */
3077 static gboolean
gtk_drag_motion_cb(GtkWidget * widget,GdkEventMotion * event,gpointer data)3078 gtk_drag_motion_cb (GtkWidget *widget,
3079 GdkEventMotion *event,
3080 gpointer data)
3081 {
3082 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3083 GdkScreen *screen;
3084 gint x_root, y_root;
3085
3086 if (event->is_hint)
3087 {
3088 gdk_device_get_position (event->device, &screen, &x_root, &y_root);
3089 event->x_root = x_root;
3090 event->y_root = y_root;
3091 }
3092 else
3093 screen = gdk_event_get_screen ((GdkEvent *)event);
3094
3095 x_root = (gint)(event->x_root + 0.5);
3096 y_root = (gint)(event->y_root + 0.5);
3097 gtk_drag_update (info, screen, x_root, y_root, (GdkEvent *) event);
3098
3099 return TRUE;
3100 }
3101
3102 #define BIG_STEP 20
3103 #define SMALL_STEP 1
3104
3105 /* “key-press/release-event” callback during drag */
3106 static gboolean
gtk_drag_key_cb(GtkWidget * widget,GdkEventKey * event,gpointer data)3107 gtk_drag_key_cb (GtkWidget *widget,
3108 GdkEventKey *event,
3109 gpointer data)
3110 {
3111 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3112 GdkModifierType state;
3113 GdkWindow *root_window;
3114 GdkDevice *pointer;
3115 gint dx, dy;
3116
3117 dx = dy = 0;
3118 state = event->state & gtk_accelerator_get_default_mod_mask ();
3119 pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
3120
3121 if (event->type == GDK_KEY_PRESS)
3122 {
3123 switch (event->keyval)
3124 {
3125 case GDK_KEY_Escape:
3126 gtk_drag_cancel_internal (info, GTK_DRAG_RESULT_USER_CANCELLED, event->time);
3127 return TRUE;
3128
3129 case GDK_KEY_space:
3130 case GDK_KEY_Return:
3131 case GDK_KEY_ISO_Enter:
3132 case GDK_KEY_KP_Enter:
3133 case GDK_KEY_KP_Space:
3134 if ((gdk_drag_context_get_selected_action (info->context) != 0) &&
3135 (gdk_drag_context_get_dest_window (info->context) != NULL))
3136 {
3137 gtk_drag_end (info, event->time);
3138 gtk_drag_drop (info, event->time);
3139 }
3140 else
3141 {
3142 gtk_drag_cancel_internal (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
3143 }
3144
3145 return TRUE;
3146
3147 case GDK_KEY_Up:
3148 case GDK_KEY_KP_Up:
3149 dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
3150 break;
3151
3152 case GDK_KEY_Down:
3153 case GDK_KEY_KP_Down:
3154 dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
3155 break;
3156
3157 case GDK_KEY_Left:
3158 case GDK_KEY_KP_Left:
3159 dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
3160 break;
3161
3162 case GDK_KEY_Right:
3163 case GDK_KEY_KP_Right:
3164 dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
3165 break;
3166 }
3167 }
3168
3169 /* Now send a "motion" so that the modifier state is updated */
3170
3171 /* The state is not yet updated in the event, so we need
3172 * to query it here. We could use XGetModifierMapping, but
3173 * that would be overkill.
3174 */
3175 root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
3176 gdk_window_get_device_position (root_window, pointer, NULL, NULL, &state);
3177 event->state = state;
3178
3179 if (dx != 0 || dy != 0)
3180 {
3181 info->cur_x += dx;
3182 info->cur_y += dy;
3183 gdk_device_warp (pointer,
3184 gtk_widget_get_screen (widget),
3185 info->cur_x, info->cur_y);
3186 }
3187
3188 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
3189
3190 return TRUE;
3191 }
3192
3193 static gboolean
gtk_drag_grab_broken_event_cb(GtkWidget * widget,GdkEventGrabBroken * event,gpointer data)3194 gtk_drag_grab_broken_event_cb (GtkWidget *widget,
3195 GdkEventGrabBroken *event,
3196 gpointer data)
3197 {
3198 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3199
3200 /* Don't cancel if we break the implicit grab from the initial button_press.
3201 * Also, don't cancel if we re-grab on the widget or on our IPC window, for
3202 * example, when changing the drag cursor.
3203 */
3204 if (event->implicit
3205 || event->grab_window == gtk_widget_get_window (info->widget)
3206 || event->grab_window == gtk_widget_get_window (info->ipc_widget))
3207 return FALSE;
3208
3209 gtk_drag_cancel_internal (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
3210 return TRUE;
3211 }
3212
3213 static void
gtk_drag_grab_notify_cb(GtkWidget * widget,gboolean was_grabbed,gpointer data)3214 gtk_drag_grab_notify_cb (GtkWidget *widget,
3215 gboolean was_grabbed,
3216 gpointer data)
3217 {
3218 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3219 GdkDevice *pointer;
3220
3221 pointer = gdk_drag_context_get_device (info->context);
3222
3223 if (gtk_widget_device_is_shadowed (widget, pointer))
3224 {
3225 /* We have to block callbacks to avoid recursion here, because
3226 * gtk_drag_cancel_internal calls gtk_grab_remove (via gtk_drag_end)
3227 */
3228 g_signal_handlers_block_by_func (widget, gtk_drag_grab_notify_cb, data);
3229 gtk_drag_cancel_internal (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
3230 g_signal_handlers_unblock_by_func (widget, gtk_drag_grab_notify_cb, data);
3231 }
3232 }
3233
3234 /* “button-release-event” callback during drag */
3235 static gboolean
gtk_drag_button_release_cb(GtkWidget * widget,GdkEventButton * event,gpointer data)3236 gtk_drag_button_release_cb (GtkWidget *widget,
3237 GdkEventButton *event,
3238 gpointer data)
3239 {
3240 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3241
3242 if (event->button != info->button)
3243 return FALSE;
3244
3245 if ((gdk_drag_context_get_selected_action (info->context) != 0) &&
3246 (gdk_drag_context_get_dest_window (info->context) != NULL))
3247 {
3248 gtk_drag_end (info, event->time);
3249 gtk_drag_drop (info, event->time);
3250 }
3251 else
3252 {
3253 gtk_drag_cancel_internal (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
3254 }
3255
3256 return TRUE;
3257 }
3258
3259 static gboolean
gtk_drag_abort_timeout(gpointer data)3260 gtk_drag_abort_timeout (gpointer data)
3261 {
3262 GtkDragSourceInfo *info = data;
3263 guint32 time = GDK_CURRENT_TIME;
3264
3265 if (info->proxy_dest)
3266 time = info->proxy_dest->proxy_drop_time;
3267
3268 info->drop_timeout = 0;
3269 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_TIMEOUT_EXPIRED, time);
3270
3271 return FALSE;
3272 }
3273
3274 /**
3275 * gtk_drag_check_threshold: (method)
3276 * @widget: a #GtkWidget
3277 * @start_x: X coordinate of start of drag
3278 * @start_y: Y coordinate of start of drag
3279 * @current_x: current X coordinate
3280 * @current_y: current Y coordinate
3281 *
3282 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3283 * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
3284 * should trigger the beginning of a drag-and-drop operation.
3285 *
3286 * Returns: %TRUE if the drag threshold has been passed.
3287 */
3288 gboolean
gtk_drag_check_threshold(GtkWidget * widget,gint start_x,gint start_y,gint current_x,gint current_y)3289 gtk_drag_check_threshold (GtkWidget *widget,
3290 gint start_x,
3291 gint start_y,
3292 gint current_x,
3293 gint current_y)
3294 {
3295 gint drag_threshold;
3296
3297 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
3298
3299 drag_threshold = gtk_settings_get_dnd_drag_threshold (gtk_widget_get_settings (widget));
3300
3301 return (ABS (current_x - start_x) > drag_threshold ||
3302 ABS (current_y - start_y) > drag_threshold);
3303 }
3304
3305 /**
3306 * gtk_drag_cancel: (method)
3307 * @context: a #GdkDragContext, as e.g. returned by gtk_drag_begin_with_coordinates()
3308 *
3309 * Cancels an ongoing drag operation on the source side.
3310 *
3311 * If you want to be able to cancel a drag operation in this way,
3312 * you need to keep a pointer to the drag context, either from an
3313 * explicit call to gtk_drag_begin_with_coordinates(), or by
3314 * connecting to #GtkWidget::drag-begin.
3315 *
3316 * If @context does not refer to an ongoing drag operation, this
3317 * function does nothing.
3318 *
3319 * If a drag is cancelled in this way, the @result argument of
3320 * #GtkWidget::drag-failed is set to @GTK_DRAG_RESULT_ERROR.
3321 *
3322 * Since: 3.16
3323 */
3324 void
gtk_drag_cancel(GdkDragContext * context)3325 gtk_drag_cancel (GdkDragContext *context)
3326 {
3327 GtkDragSourceInfo *info;
3328
3329 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3330
3331 info = gtk_drag_get_source_info (context, FALSE);
3332 if (info != NULL)
3333 gtk_drag_cancel_internal (info, GTK_DRAG_RESULT_ERROR, gtk_get_current_event_time ());
3334 }
3335