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