]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd.c
filechooserbutton: New test for opening the Other item in the combo box and then...
[~andy/gtk] / gtk / gtkdnd.c
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 <math.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "gdk/gdk.h"
32
33 #ifdef GDK_WINDOWING_X11
34 #include <X11/Xlib.h>
35 #include <X11/keysym.h>
36 #include "gdk/x11/gdkx.h"
37 #ifdef XINPUT_2
38 #include <X11/extensions/XInput2.h>
39 #endif
40 #endif
41
42 #include "gtkdnd.h"
43 #include "gtkiconfactory.h"
44 #include "gtkiconhelperprivate.h"
45 #include "gtkicontheme.h"
46 #include "gtkinvisible.h"
47 #include "gtkmain.h"
48 #include "gtkplug.h"
49 #include "gtkstock.h"
50 #include "gtktooltip.h"
51 #include "gtkwindow.h"
52 #include "gtkintl.h"
53 #include "gtkselectionprivate.h"
54
55
56 /**
57  * SECTION:gtkdnd
58  * @Short_description: Functions for controlling drag and drop handling
59  * @Title: Drag and Drop
60  *
61  * GTK+ has a rich set of functions for doing inter-process
62  * communication via the drag-and-drop metaphor. GTK+
63  * can do drag-and-drop (DND) via multiple protocols.
64  * The currently supported protocols are the Xdnd and
65  * Motif protocols.
66  *
67  * As well as the functions listed here, applications
68  * may need to use some facilities provided for
69  * <link linkend="gtk-Selections">Selections</link>.
70  * Also, the Drag and Drop API makes use of signals
71  * in the #GtkWidget class.
72  */
73
74
75 static GSList *source_widgets = NULL;
76
77 typedef struct _GtkDragSourceSite GtkDragSourceSite;
78 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
79 typedef struct _GtkDragDestSite GtkDragDestSite;
80 typedef struct _GtkDragDestInfo GtkDragDestInfo;
81 typedef struct _GtkDragAnim GtkDragAnim;
82
83
84 typedef enum 
85 {
86   GTK_DRAG_STATUS_DRAG,
87   GTK_DRAG_STATUS_WAIT,
88   GTK_DRAG_STATUS_DROP
89 } GtkDragStatus;
90
91 struct _GtkDragSourceSite 
92 {
93   GdkModifierType    start_button_mask;
94   GtkTargetList     *target_list;        /* Targets for drag data */
95   GdkDragAction      actions;            /* Possible actions */
96
97   GtkIconHelper     *icon_helper;
98
99   /* Stored button press information to detect drag beginning */
100   gint               state;
101   gint               x, y;
102 };
103   
104 struct _GtkDragSourceInfo 
105 {
106   GtkWidget         *widget;
107   GtkTargetList     *target_list; /* Targets for drag data */
108   GdkDragAction      possible_actions; /* Actions allowed by source */
109   GdkDragContext    *context;     /* drag context */
110   GtkWidget         *icon_window; /* Window for drag */
111   GtkWidget         *fallback_icon; /* Window for drag used on other screens */
112   GtkWidget         *ipc_widget;  /* GtkInvisible for grab, message passing */
113   GdkCursor         *cursor;      /* Cursor for drag */
114   gint hot_x, hot_y;              /* Hot spot for drag */
115   gint button;                    /* mouse button starting drag */
116
117   GtkDragStatus      status;      /* drag status */
118   GdkEvent          *last_event;  /* pending event */
119
120   gint               start_x, start_y; /* Initial position */
121   gint               cur_x, cur_y;     /* Current Position */
122   GdkScreen         *cur_screen;       /* Current screen for pointer */
123
124   guint32            grab_time;   /* timestamp for initial grab */
125   GList             *selections;  /* selections we've claimed */
126   
127   GtkDragDestInfo   *proxy_dest;  /* Set if this is a proxy drag */
128
129   guint              update_idle;      /* Idle function to update the drag */
130   guint              drop_timeout;     /* Timeout for aborting drop */
131   guint              destroy_icon : 1; /* If true, destroy icon_window */
132   guint              have_grab : 1;    /* Do we still have the pointer grab */
133   GtkIconHelper     *icon_helper;
134   GdkCursor         *drag_cursors[6];
135 };
136
137 struct _GtkDragDestSite
138 {
139   GtkDestDefaults    flags;
140   GtkTargetList     *target_list;
141   GdkDragAction      actions;
142   GdkWindow         *proxy_window;
143   GdkDragProtocol    proxy_protocol;
144   guint              do_proxy     : 1;
145   guint              proxy_coords : 1;
146   guint              have_drag    : 1;
147   guint              track_motion : 1;
148 };
149
150 struct _GtkDragDestInfo
151 {
152   GtkWidget         *widget;              /* Widget in which drag is in */
153   GdkDragContext    *context;             /* Drag context */
154   GtkDragSourceInfo *proxy_source;        /* Set if this is a proxy drag */
155   GtkSelectionData  *proxy_data;          /* Set while retrieving proxied data */
156   guint32            proxy_drop_time;     /* Timestamp for proxied drop */
157   guint              proxy_drop_wait : 1; /* Set if we are waiting for a
158                                            * status reply before sending
159                                            * a proxied drop on.
160                                            */
161   guint              dropped : 1;         /* Set after we receive a drop */
162   gint               drop_x, drop_y;      /* Position of drop */
163 };
164
165 #define DROP_ABORT_TIME 300000
166
167 #define ANIM_STEP_TIME 50
168 #define ANIM_STEP_LENGTH 50
169 #define ANIM_MIN_STEPS 5
170 #define ANIM_MAX_STEPS 10
171
172 struct _GtkDragAnim 
173 {
174   GtkDragSourceInfo *info;
175   gint step;
176   gint n_steps;
177 };
178
179 typedef gboolean (* GtkDragDestCallback) (GtkWidget      *widget,
180                                           GdkDragContext *context,
181                                           gint            x,
182                                           gint            y,
183                                           guint32         time);
184
185 /* Enumeration for some targets we handle internally */
186
187 enum {
188   TARGET_MOTIF_SUCCESS = 0x40000000,
189   TARGET_MOTIF_FAILURE,
190   TARGET_DELETE
191 };
192
193 /* Forward declarations */
194 static void          gtk_drag_get_event_actions (GdkEvent        *event, 
195                                                  gint             button,
196                                                  GdkDragAction    actions,
197                                                  GdkDragAction   *suggested_action,
198                                                  GdkDragAction   *possible_actions);
199 static GdkCursor *   gtk_drag_get_cursor         (GtkWidget      *widget,
200                                                   GdkDisplay     *display,
201                                                   GdkDragAction   action,
202                                                   GtkDragSourceInfo *info);
203 static void          gtk_drag_update_cursor      (GtkDragSourceInfo *info);
204 static GtkWidget    *gtk_drag_get_ipc_widget            (GtkWidget *widget);
205 static GtkWidget    *gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen);
206 static void          gtk_drag_release_ipc_widget (GtkWidget      *widget);
207
208 static void     gtk_drag_selection_received     (GtkWidget        *widget,
209                                                  GtkSelectionData *selection_data,
210                                                  guint             time,
211                                                  gpointer          data);
212 static gboolean gtk_drag_find_widget            (GtkWidget        *widget,
213                                                  GdkDragContext   *context,
214                                                  GtkDragDestInfo  *info,
215                                                  gint              x,
216                                                  gint              y,
217                                                  guint32           time,
218                                                  GtkDragDestCallback callback);
219 static void     gtk_drag_proxy_begin            (GtkWidget        *widget,
220                                                  GtkDragDestInfo  *dest_info,
221                                                  guint32           time);
222 static void     gtk_drag_dest_realized          (GtkWidget        *widget);
223 static void     gtk_drag_dest_hierarchy_changed (GtkWidget        *widget,
224                                                  GtkWidget        *previous_toplevel);
225 static void     gtk_drag_dest_site_destroy      (gpointer          data);
226 static void     gtk_drag_dest_leave             (GtkWidget        *widget,
227                                                  GdkDragContext   *context,
228                                                  guint             time);
229 static gboolean gtk_drag_dest_motion            (GtkWidget        *widget,
230                                                  GdkDragContext   *context,
231                                                  gint              x,
232                                                  gint              y,
233                                                  guint             time);
234 static gboolean gtk_drag_dest_drop              (GtkWidget        *widget,
235                                                  GdkDragContext   *context,
236                                                  gint              x,
237                                                  gint              y,
238                                                  guint             time);
239
240 static GtkDragDestInfo *  gtk_drag_get_dest_info     (GdkDragContext *context,
241                                                       gboolean        create);
242 static GtkDragSourceInfo *gtk_drag_get_source_info   (GdkDragContext *context,
243                                                       gboolean        create);
244 static void               gtk_drag_clear_source_info (GdkDragContext *context);
245
246 static void gtk_drag_source_check_selection    (GtkDragSourceInfo *info, 
247                                                 GdkAtom            selection,
248                                                 guint32            time);
249 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
250                                                 guint32            time);
251 static void gtk_drag_drop                      (GtkDragSourceInfo *info,
252                                                 guint32            time);
253 static void gtk_drag_drop_finished             (GtkDragSourceInfo *info,
254                                                 GtkDragResult      result,
255                                                 guint              time);
256 static void gtk_drag_cancel                    (GtkDragSourceInfo *info,
257                                                 GtkDragResult      result,
258                                                 guint32            time);
259
260 static gboolean gtk_drag_source_event_cb       (GtkWidget         *widget,
261                                                 GdkEvent          *event,
262                                                 gpointer           data);
263 static void gtk_drag_source_site_destroy       (gpointer           data);
264 static void gtk_drag_selection_get             (GtkWidget         *widget, 
265                                                 GtkSelectionData  *selection_data,
266                                                 guint              sel_info,
267                                                 guint32            time,
268                                                 gpointer           data);
269 static gboolean gtk_drag_anim_timeout          (gpointer           data);
270 static void gtk_drag_remove_icon               (GtkDragSourceInfo *info);
271 static void gtk_drag_source_info_destroy       (GtkDragSourceInfo *info);
272 static void gtk_drag_add_update_idle           (GtkDragSourceInfo *info);
273
274 static void gtk_drag_update                    (GtkDragSourceInfo *info,
275                                                 GdkScreen         *screen,
276                                                 gint               x_root,
277                                                 gint               y_root,
278                                                 GdkEvent          *event);
279 static gboolean gtk_drag_motion_cb             (GtkWidget         *widget, 
280                                                 GdkEventMotion    *event, 
281                                                 gpointer           data);
282 static gboolean gtk_drag_key_cb                (GtkWidget         *widget, 
283                                                 GdkEventKey       *event, 
284                                                 gpointer           data);
285 static gboolean gtk_drag_grab_broken_event_cb  (GtkWidget          *widget,
286                                                 GdkEventGrabBroken *event,
287                                                 gpointer            data);
288 static void     gtk_drag_grab_notify_cb        (GtkWidget         *widget,
289                                                 gboolean           was_grabbed,
290                                                 gpointer           data);
291 static gboolean gtk_drag_button_release_cb     (GtkWidget         *widget, 
292                                                 GdkEventButton    *event, 
293                                                 gpointer           data);
294 static gboolean gtk_drag_abort_timeout         (gpointer           data);
295
296 static void     set_icon_helper (GdkDragContext    *context,
297                                  GtkIconHelper     *helper,
298                                  gint               hot_x,
299                                  gint               hot_y,
300                                  gboolean           force_window);
301
302 /************************
303  * Cursor and Icon data *
304  ************************/
305
306 static struct {
307   GdkDragAction action;
308   const gchar  *name;
309   GdkPixbuf    *pixbuf;
310   GdkCursor    *cursor;
311 } drag_cursors[] = {
312   { GDK_ACTION_DEFAULT, NULL },
313   { GDK_ACTION_ASK,   "dnd-ask",  NULL, NULL },
314   { GDK_ACTION_COPY,  "dnd-copy", NULL, NULL },
315   { GDK_ACTION_MOVE,  "dnd-move", NULL, NULL },
316   { GDK_ACTION_LINK,  "dnd-link", NULL, NULL },
317   { 0              ,  "dnd-none", NULL, NULL },
318 };
319
320 /*********************
321  * Utility functions *
322  *********************/
323
324 static void
325 set_can_change_screen (GtkWidget *widget,
326                        gboolean   can_change_screen)
327 {
328   can_change_screen = can_change_screen != FALSE;
329   
330   g_object_set_data (G_OBJECT (widget), I_("gtk-dnd-can-change-screen"),
331                      GUINT_TO_POINTER (can_change_screen));
332 }
333
334 static gboolean
335 get_can_change_screen (GtkWidget *widget)
336 {
337   return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
338
339 }
340
341 static GtkWidget *
342 gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen)
343 {
344   GtkWidget *result;
345   GSList *drag_widgets = g_object_get_data (G_OBJECT (screen), 
346                                             "gtk-dnd-ipc-widgets");
347   
348   if (drag_widgets)
349     {
350       GSList *tmp = drag_widgets;
351       result = drag_widgets->data;
352       drag_widgets = drag_widgets->next;
353       g_object_set_data (G_OBJECT (screen),
354                          I_("gtk-dnd-ipc-widgets"),
355                          drag_widgets);
356       g_slist_free_1 (tmp);
357     }
358   else
359     {
360       result = gtk_window_new (GTK_WINDOW_POPUP);
361       gtk_window_set_screen (GTK_WINDOW (result), screen);
362       gtk_window_resize (GTK_WINDOW (result), 1, 1);
363       gtk_window_move (GTK_WINDOW (result), -99, -99);
364       gtk_widget_show (result);
365     }  
366
367   return result;
368 }
369
370 static GtkWidget *
371 gtk_drag_get_ipc_widget (GtkWidget *widget)
372 {
373   GtkWidget *result;
374   GtkWidget *toplevel;
375
376   result = gtk_drag_get_ipc_widget_for_screen (gtk_widget_get_screen (widget));
377   
378   toplevel = gtk_widget_get_toplevel (widget);
379   
380   if (GTK_IS_WINDOW (toplevel))
381     {
382       if (gtk_window_has_group (GTK_WINDOW (toplevel)))
383         gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
384                                      GTK_WINDOW (result));
385     }
386
387   return result;
388 }
389
390 #if defined (GDK_WINDOWING_X11)
391
392 /*
393  * We want to handle a handful of keys during DND, e.g. Escape to abort.
394  * Grabbing the keyboard has the unfortunate side-effect of preventing
395  * useful things such as using Alt-Tab to cycle between windows or
396  * switching workspaces. Therefore, we just grab the few keys we are
397  * interested in. Note that we need to put the grabs on the root window
398  * in order for them to still work when the focus is moved to another
399  * app/workspace.
400  *
401  * GDK needs a little help to successfully deliver root key events...
402  */
403
404 static GdkFilterReturn
405 root_key_filter (GdkXEvent *xevent,
406                  GdkEvent  *event,
407                  gpointer   data)
408 {
409   XEvent *ev = (XEvent *) xevent;
410
411   if ((ev->type == KeyPress || ev->type == KeyRelease) &&
412       ev->xkey.root == ev->xkey.window)
413     ev->xkey.window = (Window)data;
414   else if (ev->type == GenericEvent)
415     {
416       XGenericEventCookie *cookie;
417       XIDeviceEvent *dev;
418
419       cookie = &ev->xcookie;
420       dev = (XIDeviceEvent *) cookie->data;
421
422       if (dev->evtype == XI_KeyPress ||
423           dev->evtype == XI_KeyRelease)
424         dev->event = (Window)data;
425     }
426
427   return GDK_FILTER_CONTINUE;
428 }
429
430 typedef struct {
431   gint keysym;
432   gint modifiers;
433 } GrabKey;
434
435 static GrabKey grab_keys[] = {
436   { XK_Escape, 0 },
437   { XK_space, 0 },
438   { XK_KP_Space, 0 },
439   { XK_Return, 0 },
440   { XK_KP_Enter, 0 },
441   { XK_Up, 0 },
442   { XK_Up, Mod1Mask },
443   { XK_Down, 0 },
444   { XK_Down, Mod1Mask },
445   { XK_Left, 0 },
446   { XK_Left, Mod1Mask },
447   { XK_Right, 0 },
448   { XK_Right, Mod1Mask },
449   { XK_KP_Up, 0 },
450   { XK_KP_Up, Mod1Mask },
451   { XK_KP_Down, 0 },
452   { XK_KP_Down, Mod1Mask },
453   { XK_KP_Left, 0 },
454   { XK_KP_Left, Mod1Mask },
455   { XK_KP_Right, 0 },
456   { XK_KP_Right, Mod1Mask }
457 };
458
459 static void
460 grab_dnd_keys (GtkWidget *widget,
461                GdkDevice *device,
462                guint32    time)
463 {
464   guint i;
465   GdkWindow *window, *root;
466   gint keycode;
467 #ifdef XINPUT_2
468   gint deviceid;
469   XIGrabModifiers mods;
470   gint num_mods;
471   XIEventMask evmask;
472   unsigned char mask[(XI_LASTEVENT + 7)/8];
473   gboolean using_xi2;
474
475   deviceid = gdk_x11_device_get_id (device);
476
477   if (GDK_IS_X11_DEVICE_MANAGER_XI2 (gdk_display_get_device_manager (gtk_widget_get_display (widget))))
478     using_xi2 = TRUE;
479   else
480     using_xi2 = FALSE;
481 #endif
482
483   window = gtk_widget_get_window (widget);
484   if (!GDK_IS_X11_WINDOW (window))
485     {
486       gdk_device_grab (device,
487                        gtk_widget_get_window (widget),
488                        GDK_OWNERSHIP_APPLICATION, FALSE,
489                        GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
490                        NULL, time);
491       return;
492     }
493
494
495   root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
496
497   gdk_error_trap_push ();
498
499   for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
500     {
501       keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
502       if (keycode == NoSymbol)
503         continue;
504
505 #ifdef XINPUT_2
506       if (using_xi2)
507         {
508           memset (mask, 0, sizeof (mask));
509           XISetMask (mask, XI_KeyPress);
510           XISetMask (mask, XI_KeyRelease);
511
512           evmask.deviceid = deviceid;
513           evmask.mask_len = sizeof (mask);
514           evmask.mask = mask;
515
516           num_mods = 1;
517           mods.modifiers = grab_keys[i].modifiers;
518
519           XIGrabKeycode (GDK_WINDOW_XDISPLAY (window),
520                          deviceid,
521                          keycode,
522                          GDK_WINDOW_XID (root),
523                          GrabModeAsync,
524                          GrabModeAsync,
525                          False,
526                          &evmask,
527                          num_mods,
528                          &mods);
529         }
530       else
531 #endif
532         XGrabKey (GDK_WINDOW_XDISPLAY (window),
533                   keycode, grab_keys[i].modifiers,
534                   GDK_WINDOW_XID (root),
535                   FALSE,
536                   GrabModeAsync,
537                   GrabModeAsync);
538     }
539
540   gdk_flush ();
541   gdk_error_trap_pop_ignored ();
542
543   gdk_window_add_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
544 }
545
546 static void
547 ungrab_dnd_keys (GtkWidget *widget,
548                  GdkDevice *device,
549                  guint32    time)
550 {
551   guint i;
552   GdkWindow *window, *root;
553   gint keycode;
554 #ifdef XINPUT_2
555   XIGrabModifiers mods;
556   gint num_mods;
557   gint deviceid;
558   gboolean using_xi2;
559
560   deviceid = gdk_x11_device_get_id (device);
561   if (GDK_IS_X11_DEVICE_MANAGER_XI2 (gdk_display_get_device_manager (gtk_widget_get_display (widget))))
562     using_xi2 = TRUE;
563   else
564     using_xi2 = FALSE;
565 #endif
566
567   window = gtk_widget_get_window (widget);
568   if (!GDK_IS_X11_WINDOW (window))
569     {
570       gdk_device_ungrab (device, time);
571       return;
572     }
573
574   root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
575
576   gdk_window_remove_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
577
578   gdk_error_trap_push ();
579
580   for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
581     {
582       keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
583       if (keycode == NoSymbol)
584         continue;
585
586 #ifdef XINPUT_2
587       if (using_xi2)
588         {
589           num_mods = 1;
590           mods.modifiers = grab_keys[i].modifiers;
591
592           XIUngrabKeycode (GDK_WINDOW_XDISPLAY (window),
593                            deviceid,
594                            keycode,
595                            GDK_WINDOW_XID (root),
596                            num_mods,
597                            &mods);
598         }
599       else
600 #endif
601         XUngrabKey (GDK_WINDOW_XDISPLAY (window),
602                     keycode, grab_keys[i].modifiers,
603                     GDK_WINDOW_XID (root));
604     }
605
606   gdk_flush ();
607   gdk_error_trap_pop_ignored ();
608 }
609
610 #else /* !GDK_WINDOWING_X11 */
611
612 static void
613 grab_dnd_keys (GtkWidget *widget,
614                GdkDevice *device,
615                guint32    time)
616 {
617   gdk_device_grab (device,
618                    gtk_widget_get_window (widget),
619                    GDK_OWNERSHIP_APPLICATION, FALSE,
620                    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
621                    NULL, time);
622 }
623
624 static void
625 ungrab_dnd_keys (GtkWidget *widget,
626                  GdkDevice *device,
627                  guint32    time)
628 {
629   gdk_device_ungrab (device, time);
630 }
631
632 #endif /* GDK_WINDOWING_X11 */
633
634
635 /***************************************************************
636  * gtk_drag_release_ipc_widget:
637  *     Releases widget retrieved with gtk_drag_get_ipc_widget ()
638  *   arguments:
639  *     widget: the widget to release.
640  *   results:
641  ***************************************************************/
642
643 static void
644 gtk_drag_release_ipc_widget (GtkWidget *widget)
645 {
646   GtkWindow *window = GTK_WINDOW (widget);
647   GdkScreen *screen = gtk_widget_get_screen (widget);
648   GdkDragContext *context = g_object_get_data (G_OBJECT (widget), "drag-context");
649   GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
650                                             "gtk-dnd-ipc-widgets");
651   GdkDevice *pointer, *keyboard;
652
653   if (context)
654     {
655       pointer = gdk_drag_context_get_device (context);
656       keyboard = gdk_device_get_associated_device (pointer);
657
658       if (keyboard)
659         ungrab_dnd_keys (widget, keyboard, GDK_CURRENT_TIME);
660     }
661
662   if (gtk_window_has_group (window))
663     gtk_window_group_remove_window (gtk_window_get_group (window),
664                                     window);
665   drag_widgets = g_slist_prepend (drag_widgets, widget);
666   g_object_set_data (G_OBJECT (screen),
667                      I_("gtk-dnd-ipc-widgets"),
668                      drag_widgets);
669 }
670
671 static guint32
672 gtk_drag_get_event_time (GdkEvent *event)
673 {
674   guint32 tm = GDK_CURRENT_TIME;
675   
676   if (event)
677     switch (event->type)
678       {
679       case GDK_MOTION_NOTIFY:
680         tm = event->motion.time; break;
681       case GDK_BUTTON_PRESS:
682       case GDK_2BUTTON_PRESS:
683       case GDK_3BUTTON_PRESS:
684       case GDK_BUTTON_RELEASE:
685         tm = event->button.time; break;
686       case GDK_KEY_PRESS:
687       case GDK_KEY_RELEASE:
688         tm = event->key.time; break;
689       case GDK_ENTER_NOTIFY:
690       case GDK_LEAVE_NOTIFY:
691         tm = event->crossing.time; break;
692       case GDK_PROPERTY_NOTIFY:
693         tm = event->property.time; break;
694       case GDK_SELECTION_CLEAR:
695       case GDK_SELECTION_REQUEST:
696       case GDK_SELECTION_NOTIFY:
697         tm = event->selection.time; break;
698       case GDK_PROXIMITY_IN:
699       case GDK_PROXIMITY_OUT:
700         tm = event->proximity.time; break;
701       default:                  /* use current time */
702         break;
703       }
704   
705   return tm;
706 }
707
708 static void
709 gtk_drag_get_event_actions (GdkEvent *event, 
710                             gint button, 
711                             GdkDragAction  actions,
712                             GdkDragAction *suggested_action,
713                             GdkDragAction *possible_actions)
714 {
715   *suggested_action = 0;
716   *possible_actions = 0;
717
718   if (event)
719     {
720       GdkModifierType state = 0;
721       
722       switch (event->type)
723         {
724         case GDK_MOTION_NOTIFY:
725           state = event->motion.state;
726           break;
727         case GDK_BUTTON_PRESS:
728         case GDK_2BUTTON_PRESS:
729         case GDK_3BUTTON_PRESS:
730         case GDK_BUTTON_RELEASE:
731           state = event->button.state;
732           break;
733         case GDK_KEY_PRESS:
734         case GDK_KEY_RELEASE:
735           state = event->key.state;
736           break;
737         case GDK_ENTER_NOTIFY:
738         case GDK_LEAVE_NOTIFY:
739           state = event->crossing.state;
740           break;
741         default:
742           break;
743         }
744
745       if ((button == GDK_BUTTON_MIDDLE || button == GDK_BUTTON_SECONDARY) && (actions & GDK_ACTION_ASK))
746         {
747           *suggested_action = GDK_ACTION_ASK;
748           *possible_actions = actions;
749         }
750       else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
751         {
752           if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
753             {
754               if (actions & GDK_ACTION_LINK)
755                 {
756                   *suggested_action = GDK_ACTION_LINK;
757                   *possible_actions = GDK_ACTION_LINK;
758                 }
759             }
760           else if (state & GDK_CONTROL_MASK)
761             {
762               if (actions & GDK_ACTION_COPY)
763                 {
764                   *suggested_action = GDK_ACTION_COPY;
765                   *possible_actions = GDK_ACTION_COPY;
766                 }
767               return;
768             }
769           else
770             {
771               if (actions & GDK_ACTION_MOVE)
772                 {
773                   *suggested_action = GDK_ACTION_MOVE;
774                   *possible_actions = GDK_ACTION_MOVE;
775                 }
776               return;
777             }
778         }
779       else
780         {
781           *possible_actions = actions;
782
783           if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
784             *suggested_action = GDK_ACTION_ASK;
785           else if (actions & GDK_ACTION_COPY)
786             *suggested_action =  GDK_ACTION_COPY;
787           else if (actions & GDK_ACTION_MOVE)
788             *suggested_action = GDK_ACTION_MOVE;
789           else if (actions & GDK_ACTION_LINK)
790             *suggested_action = GDK_ACTION_LINK;
791         }
792     }
793   else
794     {
795       *possible_actions = actions;
796       
797       if (actions & GDK_ACTION_COPY)
798         *suggested_action =  GDK_ACTION_COPY;
799       else if (actions & GDK_ACTION_MOVE)
800         *suggested_action = GDK_ACTION_MOVE;
801       else if (actions & GDK_ACTION_LINK)
802         *suggested_action = GDK_ACTION_LINK;
803     }
804 }
805
806 static gboolean
807 gtk_drag_can_use_rgba_cursor (GdkDisplay *display, 
808                               gint        width,
809                               gint        height)
810 {
811   guint max_width, max_height;
812   
813   if (!gdk_display_supports_cursor_color (display))
814     return FALSE;
815
816   if (!gdk_display_supports_cursor_alpha (display))
817     return FALSE;
818
819   gdk_display_get_maximal_cursor_size (display, 
820                                        &max_width,
821                                        &max_height);
822   if (width > max_width || height > max_height)
823     {
824        /* can't use rgba cursor (too large) */
825       return FALSE;
826     }
827
828   return TRUE;
829 }
830
831 static void
832 ensure_drag_cursor_pixbuf (int i)
833 {
834   if (drag_cursors[i].pixbuf == NULL)
835     {
836       char *path = g_strconcat ("/org/gtk/libgtk/cursor/",  drag_cursors[i].name, ".png", NULL);
837       GInputStream *stream = g_resources_open_stream (path, 0, NULL);
838       if (stream != NULL)
839         {
840           drag_cursors[i].pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
841           g_object_unref (stream);
842         }
843       g_free (path);
844     }
845 }
846
847 static GdkCursor *
848 gtk_drag_get_cursor (GtkWidget         *widget,
849                      GdkDisplay        *display,
850                      GdkDragAction      action,
851                      GtkDragSourceInfo *info)
852 {
853   gint i;
854
855   /* reconstruct the cursors for each new drag (thus !info),
856    * to catch cursor theme changes 
857    */ 
858   if (!info)
859     {
860       for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
861         if (drag_cursors[i].cursor != NULL)
862           {
863             g_object_unref (drag_cursors[i].cursor);
864             drag_cursors[i].cursor = NULL;
865           }
866     }
867  
868   for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
869     if (drag_cursors[i].action == action)
870       break;
871
872   if (drag_cursors[i].cursor != NULL)
873     {
874       if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
875         {
876           g_object_unref (drag_cursors[i].cursor);
877           drag_cursors[i].cursor = NULL;
878         }
879     }
880   
881   if (drag_cursors[i].cursor == NULL)
882     drag_cursors[i].cursor = gdk_cursor_new_from_name (display, drag_cursors[i].name);
883   
884   if (drag_cursors[i].cursor == NULL)
885     {
886       ensure_drag_cursor_pixbuf (i);
887       drag_cursors[i].cursor = gdk_cursor_new_from_pixbuf (display, drag_cursors[i].pixbuf, 0, 0);
888     }
889
890   if (info && info->icon_helper) 
891     {
892       gint cursor_width, cursor_height;
893       gint icon_width, icon_height;
894       gint width, height;
895       GdkPixbuf *cursor_pixbuf, *pixbuf, *icon_pixbuf;
896       gint hot_x, hot_y;
897       gint icon_x, icon_y, ref_x, ref_y;
898
899       if (info->drag_cursors[i] != NULL)
900         {
901           if (display == gdk_cursor_get_display (info->drag_cursors[i]))
902             return info->drag_cursors[i];
903           
904           g_object_unref (info->drag_cursors[i]);
905           info->drag_cursors[i] = NULL;
906         }
907
908       icon_pixbuf = _gtk_icon_helper_ensure_pixbuf (info->icon_helper,
909                                                     gtk_widget_get_style_context (widget));
910
911       icon_x = info->hot_x;
912       icon_y = info->hot_y;
913       icon_width = gdk_pixbuf_get_width (icon_pixbuf);
914       icon_height = gdk_pixbuf_get_height (icon_pixbuf);
915
916       hot_x = hot_y = 0;
917       cursor_pixbuf = gdk_cursor_get_image (drag_cursors[i].cursor);
918       if (!cursor_pixbuf)
919         {
920           ensure_drag_cursor_pixbuf (i);
921           cursor_pixbuf = g_object_ref (drag_cursors[i].pixbuf);
922         }
923       else
924         {
925           if (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"))
926             hot_x = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"));
927           
928           if (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"))
929             hot_y = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"));
930
931 #if 0     
932           /* The code below is an attempt to let cursor themes
933            * determine the attachment of the icon to enable things
934            * like the following:
935            *
936            *    +-----+
937            *    |     |
938            *    |     ||
939            *    +-----+|
940            *        ---+
941            * 
942            * It does not work since Xcursor doesn't allow to attach
943            * any additional information to cursors in a retrievable
944            * way  (there are comments, but no way to get at them
945            * short of searching for the actual cursor file).
946            * If this code ever gets used, the icon_window placement
947            * must be changed to recognize these placement options
948            * as well. Note that this code ignores info->hot_x/y.
949            */ 
950           for (j = 0; j < 10; j++)
951             {
952               const gchar *opt;
953               gchar key[32];
954               gchar **toks;
955               GtkAnchorType icon_anchor;
956
957               g_snprintf (key, 32, "comment%d", j);
958               opt = gdk_pixbuf_get_option (cursor_pixbuf, key);
959               if (opt && g_str_has_prefix ("icon-attach:", opt))
960                 {
961                   toks = g_strsplit (opt + strlen ("icon-attach:"), "'", -1);
962                   if (g_strv_length (toks) != 3)
963                     {
964                       g_strfreev (toks);
965                       break;
966                     }
967                   icon_anchor = atoi (toks[0]);
968                   icon_x = atoi (toks[1]);
969                   icon_y = atoi (toks[2]);
970                   
971                   switch (icon_anchor)
972                     {
973                     case GTK_ANCHOR_NORTH:
974                     case GTK_ANCHOR_CENTER:
975                     case GTK_ANCHOR_SOUTH:
976                       icon_x += icon_width / 2;
977                       break;
978                     case GTK_ANCHOR_NORTH_EAST:
979                     case GTK_ANCHOR_EAST:
980                     case GTK_ANCHOR_SOUTH_EAST:
981                       icon_x += icon_width;
982                       break;
983                     default: ;
984                     }
985                   
986                   switch (icon_anchor)
987                     {
988                     case GTK_ANCHOR_WEST:
989                     case GTK_ANCHOR_CENTER:
990                     case GTK_ANCHOR_EAST:
991                       icon_y += icon_height / 2;
992                       break;
993                     case GTK_ANCHOR_SOUTH_WEST:
994                     case GTK_ANCHOR_SOUTH:
995                     case GTK_ANCHOR_SOUTH_EAST:
996                       icon_x += icon_height;
997                       break;
998                     default: ;
999                     }
1000
1001                   g_strfreev (toks);
1002                   break;
1003                 }
1004             }
1005 #endif
1006         }
1007
1008       cursor_width = gdk_pixbuf_get_width (cursor_pixbuf);
1009       cursor_height = gdk_pixbuf_get_height (cursor_pixbuf);
1010       
1011       ref_x = MAX (hot_x, icon_x);
1012       ref_y = MAX (hot_y, icon_y);
1013       width = ref_x + MAX (cursor_width - hot_x, icon_width - icon_x);
1014       height = ref_y + MAX (cursor_height - hot_y, icon_height - icon_y);
1015          
1016       if (gtk_drag_can_use_rgba_cursor (display, width, height))
1017         {
1018           /* Composite cursor and icon so that both hotspots
1019            * end up at (ref_x, ref_y)
1020            */
1021           pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
1022                                    width, height); 
1023           
1024           gdk_pixbuf_fill (pixbuf, 0xff000000);
1025           
1026           gdk_pixbuf_composite (icon_pixbuf, pixbuf,
1027                                 ref_x - icon_x, ref_y - icon_y, 
1028                                 icon_width, icon_height,
1029                                 ref_x - icon_x, ref_y - icon_y, 
1030                                 1.0, 1.0, 
1031                                 GDK_INTERP_BILINEAR, 255);
1032           
1033           gdk_pixbuf_composite (cursor_pixbuf, pixbuf,
1034                                 ref_x - hot_x, ref_y - hot_y, 
1035                                 cursor_width, cursor_height,
1036                                 ref_x - hot_x, ref_y - hot_y,
1037                                 1.0, 1.0, 
1038                                 GDK_INTERP_BILINEAR, 255);
1039           
1040           info->drag_cursors[i] = 
1041             gdk_cursor_new_from_pixbuf (display, pixbuf, ref_x, ref_y);
1042           
1043           g_object_unref (pixbuf);
1044         }
1045       
1046       g_object_unref (cursor_pixbuf);
1047       g_object_unref (icon_pixbuf);
1048       
1049       if (info->drag_cursors[i] != NULL)
1050         return info->drag_cursors[i];
1051     }
1052  
1053   return drag_cursors[i].cursor;
1054 }
1055
1056 static void
1057 gtk_drag_update_cursor (GtkDragSourceInfo *info)
1058 {
1059   GdkCursor *cursor;
1060   gint i;
1061
1062   if (!info->have_grab)
1063     return;
1064
1065   for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
1066     if (info->cursor == drag_cursors[i].cursor ||
1067         info->cursor == info->drag_cursors[i])
1068       break;
1069   
1070   if (i == G_N_ELEMENTS (drag_cursors))
1071     return;
1072
1073   cursor = gtk_drag_get_cursor (info->widget,
1074                                 gdk_cursor_get_display (info->cursor), 
1075                                 drag_cursors[i].action, info);
1076   
1077   if (cursor != info->cursor)
1078     {
1079       GdkDevice *pointer;
1080
1081       pointer = gdk_drag_context_get_device (info->context);
1082       gdk_device_grab (pointer,
1083                        gtk_widget_get_window (info->ipc_widget),
1084                        GDK_OWNERSHIP_APPLICATION, FALSE,
1085                        GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
1086                        cursor, info->grab_time);
1087       info->cursor = cursor;
1088     }
1089 }
1090
1091 /********************
1092  * Destination side *
1093  ********************/
1094
1095 /**
1096  * gtk_drag_get_data: (method)
1097  * @widget: the widget that will receive the
1098  *   #GtkWidget::drag-data-received signal.
1099  * @context: the drag context
1100  * @target: the target (form of the data) to retrieve.
1101  * @time_: a timestamp for retrieving the data. This will
1102  *   generally be the time received in a #GtkWidget::drag-motion"
1103  *   or #GtkWidget::drag-drop" signal.
1104  *
1105  * Gets the data associated with a drag. When the data
1106  * is received or the retrieval fails, GTK+ will emit a
1107  * #GtkWidget::drag-data-received signal. Failure of the retrieval
1108  * is indicated by the length field of the @selection_data
1109  * signal parameter being negative. However, when gtk_drag_get_data()
1110  * is called implicitely because the %GTK_DEST_DEFAULT_DROP was set,
1111  * then the widget will not receive notification of failed
1112  * drops.
1113  */
1114 void
1115 gtk_drag_get_data (GtkWidget      *widget,
1116                    GdkDragContext *context,
1117                    GdkAtom         target,
1118                    guint32         time_)
1119 {
1120   GtkWidget *selection_widget;
1121
1122   g_return_if_fail (GTK_IS_WIDGET (widget));
1123   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1124
1125   selection_widget = gtk_drag_get_ipc_widget (widget);
1126
1127   g_object_ref (context);
1128   g_object_ref (widget);
1129
1130   g_signal_connect (selection_widget, "selection-received",
1131                     G_CALLBACK (gtk_drag_selection_received), widget);
1132
1133   g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
1134
1135   gtk_selection_convert (selection_widget,
1136                          gdk_drag_get_selection (context),
1137                          target,
1138                          time_);
1139 }
1140
1141
1142 /**
1143  * gtk_drag_get_source_widget: (method)
1144  * @context: a (destination side) drag context
1145  *
1146  * Determines the source widget for a drag.
1147  *
1148  * Return value: (transfer none): if the drag is occurring
1149  *     within a single application, a pointer to the source widget.
1150  *     Otherwise, %NULL.
1151  */
1152 GtkWidget *
1153 gtk_drag_get_source_widget (GdkDragContext *context)
1154 {
1155   GSList *tmp_list;
1156
1157   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
1158   
1159   tmp_list = source_widgets;
1160   while (tmp_list)
1161     {
1162       GtkWidget *ipc_widget = tmp_list->data;
1163
1164       if (gtk_widget_get_window (ipc_widget) == gdk_drag_context_get_source_window (context))
1165         {
1166           GtkDragSourceInfo *info;
1167           info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
1168
1169           return info ? info->widget : NULL;
1170         }
1171
1172       tmp_list = tmp_list->next;
1173     }
1174
1175   return NULL;
1176 }
1177
1178 /**
1179  * gtk_drag_finish: (method)
1180  * @context: the drag context.
1181  * @success: a flag indicating whether the drop was successful
1182  * @del: a flag indicating whether the source should delete the
1183  *   original data. (This should be %TRUE for a move)
1184  * @time_: the timestamp from the #GtkWidget::drag-drop signal.
1185  *
1186  * Informs the drag source that the drop is finished, and
1187  * that the data of the drag will no longer be required.
1188  */
1189 void 
1190 gtk_drag_finish (GdkDragContext *context,
1191                  gboolean        success,
1192                  gboolean        del,
1193                  guint32         time)
1194 {
1195   GdkAtom target = GDK_NONE;
1196
1197   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1198
1199   if (success && del)
1200     {
1201       target = gdk_atom_intern_static_string ("DELETE");
1202     }
1203   else if (gdk_drag_context_get_protocol (context) == GDK_DRAG_PROTO_MOTIF)
1204     {
1205       target = gdk_atom_intern_static_string (success ? 
1206                                               "XmTRANSFER_SUCCESS" : 
1207                                               "XmTRANSFER_FAILURE");
1208     }
1209
1210   if (target != GDK_NONE)
1211     {
1212       GtkWidget *selection_widget = gtk_drag_get_ipc_widget_for_screen (gdk_window_get_screen (gdk_drag_context_get_source_window (context)));
1213
1214       g_object_ref (context);
1215       
1216       g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
1217       g_signal_connect (selection_widget, "selection-received",
1218                         G_CALLBACK (gtk_drag_selection_received),
1219                         NULL);
1220       
1221       gtk_selection_convert (selection_widget,
1222                              gdk_drag_get_selection (context),
1223                              target,
1224                              time);
1225     }
1226   
1227   if (!(success && del))
1228     gdk_drop_finish (context, success, time);
1229 }
1230
1231 /*************************************************************
1232  * gtk_drag_highlight_draw:
1233  *     Callback for expose_event for highlighted widgets.
1234  *   arguments:
1235  *     widget:
1236  *     event:
1237  *     data:
1238  *   results:
1239  *************************************************************/
1240
1241 static gboolean
1242 gtk_drag_highlight_draw (GtkWidget *widget,
1243                          cairo_t   *cr,
1244                          gpointer   data)
1245 {
1246   int width = gtk_widget_get_allocated_width (widget);
1247   int height = gtk_widget_get_allocated_height (widget);
1248   GtkStyleContext *context;
1249
1250   context = gtk_widget_get_style_context (widget);
1251
1252   gtk_style_context_save (context);
1253   gtk_style_context_add_class (context, GTK_STYLE_CLASS_DND);
1254
1255   gtk_render_frame (context, cr, 0, 0, width, height);
1256
1257   gtk_style_context_restore (context);
1258
1259   cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
1260   cairo_set_line_width (cr, 1.0);
1261   cairo_rectangle (cr,
1262                    0.5, 0.5,
1263                    width - 1, height - 1);
1264   cairo_stroke (cr);
1265
1266   return FALSE;
1267 }
1268
1269 /**
1270  * gtk_drag_highlight: (method)
1271  * @widget: a widget to highlight
1272  *
1273  * Draws a highlight around a widget. This will attach
1274  * handlers to #GtkWidget::draw, so the highlight
1275  * will continue to be displayed until gtk_drag_unhighlight()
1276  * is called.
1277  */
1278 void 
1279 gtk_drag_highlight (GtkWidget  *widget)
1280 {
1281   g_return_if_fail (GTK_IS_WIDGET (widget));
1282
1283   g_signal_connect_after (widget, "draw",
1284                           G_CALLBACK (gtk_drag_highlight_draw),
1285                           NULL);
1286
1287   gtk_widget_queue_draw (widget);
1288 }
1289
1290 /**
1291  * gtk_drag_unhighlight: (method)
1292  * @widget: a widget to remove the highlight from.
1293  *
1294  * Removes a highlight set by gtk_drag_highlight() from
1295  * a widget.
1296  */
1297 void 
1298 gtk_drag_unhighlight (GtkWidget *widget)
1299 {
1300   g_return_if_fail (GTK_IS_WIDGET (widget));
1301
1302   g_signal_handlers_disconnect_by_func (widget,
1303                                         gtk_drag_highlight_draw,
1304                                         NULL);
1305   
1306   gtk_widget_queue_draw (widget);
1307 }
1308
1309 static void
1310 gtk_drag_dest_set_internal (GtkWidget       *widget,
1311                             GtkDragDestSite *site)
1312 {
1313   GtkDragDestSite *old_site;
1314   
1315   g_return_if_fail (widget != NULL);
1316
1317   /* HACK, do this in the destroy */
1318   old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1319   if (old_site)
1320     {
1321       g_signal_handlers_disconnect_by_func (widget,
1322                                             gtk_drag_dest_realized,
1323                                             old_site);
1324       g_signal_handlers_disconnect_by_func (widget,
1325                                             gtk_drag_dest_hierarchy_changed,
1326                                             old_site);
1327
1328       site->track_motion = old_site->track_motion;
1329     }
1330
1331   if (gtk_widget_get_realized (widget))
1332     gtk_drag_dest_realized (widget);
1333
1334   g_signal_connect (widget, "realize",
1335                     G_CALLBACK (gtk_drag_dest_realized), site);
1336   g_signal_connect (widget, "hierarchy-changed",
1337                     G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
1338
1339   g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
1340                           site, gtk_drag_dest_site_destroy);
1341 }
1342
1343 /**
1344  * gtk_drag_dest_set: (method)
1345  * @widget: a #GtkWidget
1346  * @flags: which types of default drag behavior to use
1347  * @targets: (allow-none) (array length=n_targets): a pointer to an array of #GtkTargetEntry<!-- -->s
1348  *     indicating the drop types that this @widget will accept, or %NULL.
1349  *     Later you can access the list with gtk_drag_dest_get_target_list()
1350  *     and gtk_drag_dest_find_target().
1351  * @n_targets: the number of entries in @targets
1352  * @actions: a bitmask of possible actions for a drop onto this @widget.
1353  *
1354  * Sets a widget as a potential drop destination, and adds default behaviors.
1355  *
1356  * The default behaviors listed in @flags have an effect similar
1357  * to installing default handlers for the widget's drag-and-drop signals
1358  * (#GtkWidget::drag-motion, #GtkWidget::drag-drop, ...). They all exist
1359  * for convenience. When passing #GTK_DEST_DEFAULT_ALL for instance it is
1360  * sufficient to connect to the widget's #GtkWidget::drag-data-received
1361  * signal to get primitive, but consistent drag-and-drop support.
1362  *
1363  * Things become more complicated when you try to preview the dragged data,
1364  * as described in the documentation for #GtkWidget::drag-motion. The default
1365  * behaviors described by @flags make some assumptions, that can conflict
1366  * with your own signal handlers. For instance #GTK_DEST_DEFAULT_DROP causes
1367  * invokations of gdk_drag_status() in the context of #GtkWidget::drag-motion,
1368  * and invokations of gtk_drag_finish() in #GtkWidget::drag-data-received.
1369  * Especially the later is dramatic, when your own #GtkWidget::drag-motion
1370  * handler calls gtk_drag_get_data() to inspect the dragged data.
1371  *
1372  * There's no way to set a default action here, you can use the
1373  * #GtkWidget::drag-motion callback for that. Here's an example which selects
1374  * the action to use depending on whether the control key is pressed or not:
1375  * |[
1376  * static void
1377  * drag_motion (GtkWidget *widget,
1378  *              GdkDragContext *context,
1379  *              gint x,
1380  *              gint y,
1381  *              guint time)
1382  * {
1383  *   GdkModifierType mask;
1384  *
1385  *   gdk_window_get_pointer (gtk_widget_get_window (widget),
1386  *                           NULL, NULL, &mask);
1387  *   if (mask & GDK_CONTROL_MASK)
1388  *     gdk_drag_status (context, GDK_ACTION_COPY, time);
1389  *   else
1390  *     gdk_drag_status (context, GDK_ACTION_MOVE, time);
1391  * }
1392  * ]|
1393  */
1394 void
1395 gtk_drag_dest_set (GtkWidget            *widget,
1396                    GtkDestDefaults       flags,
1397                    const GtkTargetEntry *targets,
1398                    gint                  n_targets,
1399                    GdkDragAction         actions)
1400 {
1401   GtkDragDestSite *site;
1402   
1403   g_return_if_fail (GTK_IS_WIDGET (widget));
1404
1405   site = g_slice_new0 (GtkDragDestSite);
1406
1407   site->flags = flags;
1408   site->have_drag = FALSE;
1409   if (targets)
1410     site->target_list = gtk_target_list_new (targets, n_targets);
1411   else
1412     site->target_list = NULL;
1413   site->actions = actions;
1414   site->do_proxy = FALSE;
1415   site->proxy_window = NULL;
1416   site->track_motion = FALSE;
1417
1418   gtk_drag_dest_set_internal (widget, site);
1419 }
1420
1421 /**
1422  * gtk_drag_dest_set_proxy: (method)
1423  * @widget: a #GtkWidget
1424  * @proxy_window: the window to which to forward drag events
1425  * @protocol: the drag protocol which the @proxy_window accepts
1426  *   (You can use gdk_drag_get_protocol() to determine this)
1427  * @use_coordinates: If %TRUE, send the same coordinates to the
1428  *   destination, because it is an embedded
1429  *   subwindow.
1430  *
1431  * Sets this widget as a proxy for drops to another window.
1432  */
1433 void 
1434 gtk_drag_dest_set_proxy (GtkWidget      *widget,
1435                          GdkWindow      *proxy_window,
1436                          GdkDragProtocol protocol,
1437                          gboolean        use_coordinates)
1438 {
1439   GtkDragDestSite *site;
1440   
1441   g_return_if_fail (GTK_IS_WIDGET (widget));
1442   g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
1443
1444   site = g_slice_new (GtkDragDestSite);
1445
1446   site->flags = 0;
1447   site->have_drag = FALSE;
1448   site->target_list = NULL;
1449   site->actions = 0;
1450   site->proxy_window = proxy_window;
1451   if (proxy_window)
1452     g_object_ref (proxy_window);
1453   site->do_proxy = TRUE;
1454   site->proxy_protocol = protocol;
1455   site->proxy_coords = use_coordinates;
1456   site->track_motion = FALSE;
1457
1458   gtk_drag_dest_set_internal (widget, site);
1459 }
1460
1461 /**
1462  * gtk_drag_dest_unset: (method)
1463  * @widget: a #GtkWidget
1464  *
1465  * Clears information about a drop destination set with
1466  * gtk_drag_dest_set(). The widget will no longer receive
1467  * notification of drags.
1468  */
1469 void 
1470 gtk_drag_dest_unset (GtkWidget *widget)
1471 {
1472   GtkDragDestSite *old_site;
1473
1474   g_return_if_fail (GTK_IS_WIDGET (widget));
1475
1476   old_site = g_object_get_data (G_OBJECT (widget),
1477                                 "gtk-drag-dest");
1478   if (old_site)
1479     {
1480       g_signal_handlers_disconnect_by_func (widget,
1481                                             gtk_drag_dest_realized,
1482                                             old_site);
1483       g_signal_handlers_disconnect_by_func (widget,
1484                                             gtk_drag_dest_hierarchy_changed,
1485                                             old_site);
1486     }
1487
1488   g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
1489 }
1490
1491 /**
1492  * gtk_drag_dest_get_target_list: (method)
1493  * @widget: a #GtkWidget
1494  * 
1495  * Returns the list of targets this widget can accept from
1496  * drag-and-drop.
1497  * 
1498  * Return value: (transfer none): the #GtkTargetList, or %NULL if none
1499  **/
1500 GtkTargetList*
1501 gtk_drag_dest_get_target_list (GtkWidget *widget)
1502 {
1503   GtkDragDestSite *site;
1504
1505   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1506   
1507   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1508
1509   return site ? site->target_list : NULL;  
1510 }
1511
1512 /**
1513  * gtk_drag_dest_set_target_list: (method)
1514  * @widget: a #GtkWidget that's a drag destination
1515  * @target_list: (allow-none): list of droppable targets, or %NULL for none
1516  * 
1517  * Sets the target types that this widget can accept from drag-and-drop.
1518  * The widget must first be made into a drag destination with
1519  * gtk_drag_dest_set().
1520  **/
1521 void
1522 gtk_drag_dest_set_target_list (GtkWidget      *widget,
1523                                GtkTargetList  *target_list)
1524 {
1525   GtkDragDestSite *site;
1526
1527   g_return_if_fail (GTK_IS_WIDGET (widget));
1528   
1529   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1530   
1531   if (!site)
1532     {
1533       g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
1534                  "to make the widget into a drag destination");
1535       return;
1536     }
1537
1538   if (target_list)
1539     gtk_target_list_ref (target_list);
1540   
1541   if (site->target_list)
1542     gtk_target_list_unref (site->target_list);
1543
1544   site->target_list = target_list;
1545 }
1546
1547 /**
1548  * gtk_drag_dest_add_text_targets: (method)
1549  * @widget: a #GtkWidget that's a drag destination
1550  *
1551  * Add the text targets supported by #GtkSelection to
1552  * the target list of the drag destination. The targets
1553  * are added with @info = 0. If you need another value, 
1554  * use gtk_target_list_add_text_targets() and
1555  * gtk_drag_dest_set_target_list().
1556  * 
1557  * Since: 2.6
1558  **/
1559 void
1560 gtk_drag_dest_add_text_targets (GtkWidget *widget)
1561 {
1562   GtkTargetList *target_list;
1563
1564   target_list = gtk_drag_dest_get_target_list (widget);
1565   if (target_list)
1566     gtk_target_list_ref (target_list);
1567   else
1568     target_list = gtk_target_list_new (NULL, 0);
1569   gtk_target_list_add_text_targets (target_list, 0);
1570   gtk_drag_dest_set_target_list (widget, target_list);
1571   gtk_target_list_unref (target_list);
1572 }
1573
1574 /**
1575  * gtk_drag_dest_add_image_targets: (method)
1576  * @widget: a #GtkWidget that's a drag destination
1577  *
1578  * Add the image targets supported by #GtkSelection to
1579  * the target list of the drag destination. The targets
1580  * are added with @info = 0. If you need another value, 
1581  * use gtk_target_list_add_image_targets() and
1582  * gtk_drag_dest_set_target_list().
1583  * 
1584  * Since: 2.6
1585  **/
1586 void
1587 gtk_drag_dest_add_image_targets (GtkWidget *widget)
1588 {
1589   GtkTargetList *target_list;
1590
1591   target_list = gtk_drag_dest_get_target_list (widget);
1592   if (target_list)
1593     gtk_target_list_ref (target_list);
1594   else
1595     target_list = gtk_target_list_new (NULL, 0);
1596   gtk_target_list_add_image_targets (target_list, 0, FALSE);
1597   gtk_drag_dest_set_target_list (widget, target_list);
1598   gtk_target_list_unref (target_list);
1599 }
1600
1601 /**
1602  * gtk_drag_dest_add_uri_targets: (method)
1603  * @widget: a #GtkWidget that's a drag destination
1604  *
1605  * Add the URI targets supported by #GtkSelection to
1606  * the target list of the drag destination. The targets
1607  * are added with @info = 0. If you need another value, 
1608  * use gtk_target_list_add_uri_targets() and
1609  * gtk_drag_dest_set_target_list().
1610  * 
1611  * Since: 2.6
1612  **/
1613 void
1614 gtk_drag_dest_add_uri_targets (GtkWidget *widget)
1615 {
1616   GtkTargetList *target_list;
1617
1618   target_list = gtk_drag_dest_get_target_list (widget);
1619   if (target_list)
1620     gtk_target_list_ref (target_list);
1621   else
1622     target_list = gtk_target_list_new (NULL, 0);
1623   gtk_target_list_add_uri_targets (target_list, 0);
1624   gtk_drag_dest_set_target_list (widget, target_list);
1625   gtk_target_list_unref (target_list);
1626 }
1627
1628 /**
1629  * gtk_drag_dest_set_track_motion: (method)
1630  * @widget: a #GtkWidget that's a drag destination
1631  * @track_motion: whether to accept all targets
1632  *
1633  * Tells the widget to emit #GtkWidget::drag-motion and
1634  * #GtkWidget::drag-leave events regardless of the targets and the
1635  * %GTK_DEST_DEFAULT_MOTION flag.
1636  *
1637  * This may be used when a widget wants to do generic
1638  * actions regardless of the targets that the source offers.
1639  *
1640  * Since: 2.10
1641  **/
1642 void
1643 gtk_drag_dest_set_track_motion (GtkWidget *widget,
1644                                 gboolean   track_motion)
1645 {
1646   GtkDragDestSite *site;
1647
1648   g_return_if_fail (GTK_IS_WIDGET (widget));
1649
1650   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1651   
1652   g_return_if_fail (site != NULL);
1653
1654   site->track_motion = track_motion != FALSE;
1655 }
1656
1657 /**
1658  * gtk_drag_dest_get_track_motion: (method)
1659  * @widget: a #GtkWidget that's a drag destination
1660  *
1661  * Returns whether the widget has been configured to always
1662  * emit #GtkWidget::drag-motion signals.
1663  *
1664  * Return Value: %TRUE if the widget always emits
1665  *   #GtkWidget::drag-motion events
1666  *
1667  * Since: 2.10
1668  **/
1669 gboolean
1670 gtk_drag_dest_get_track_motion (GtkWidget *widget)
1671 {
1672   GtkDragDestSite *site;
1673
1674   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1675
1676   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1677
1678   if (site)
1679     return site->track_motion;
1680
1681   return FALSE;
1682 }
1683
1684 /*************************************************************
1685  * _gtk_drag_dest_handle_event:
1686  *     Called from widget event handling code on Drag events
1687  *     for destinations.
1688  *
1689  *   arguments:
1690  *     toplevel: Toplevel widget that received the event
1691  *     event:
1692  *   results:
1693  *************************************************************/
1694
1695 void
1696 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1697                             GdkEvent  *event)
1698 {
1699   GtkDragDestInfo *info;
1700   GdkDragContext *context;
1701
1702   g_return_if_fail (toplevel != NULL);
1703   g_return_if_fail (event != NULL);
1704
1705   context = event->dnd.context;
1706
1707   info = gtk_drag_get_dest_info (context, TRUE);
1708
1709   /* Find the widget for the event */
1710   switch (event->type)
1711     {
1712     case GDK_DRAG_ENTER:
1713       break;
1714       
1715     case GDK_DRAG_LEAVE:
1716       if (info->widget)
1717         {
1718           gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1719           info->widget = NULL;
1720         }
1721       break;
1722       
1723     case GDK_DRAG_MOTION:
1724     case GDK_DROP_START:
1725       {
1726         GdkWindow *window;
1727         gint tx, ty;
1728         gboolean found;
1729
1730         if (event->type == GDK_DROP_START)
1731           {
1732             info->dropped = TRUE;
1733             /* We send a leave here so that the widget unhighlights
1734              * properly.
1735              */
1736             if (info->widget)
1737               {
1738                 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1739                 info->widget = NULL;
1740               }
1741           }
1742
1743         window = gtk_widget_get_window (toplevel);
1744
1745 #ifdef GDK_WINDOWING_X11
1746         /* Hackaround for: http://bugzilla.gnome.org/show_bug.cgi?id=136112
1747          *
1748          * Currently gdk_window_get_position doesn't provide reliable
1749          * information for embedded windows, so we call the much more
1750          * expensive gdk_window_get_origin().
1751          */
1752         if (GTK_IS_PLUG (toplevel))
1753           gdk_window_get_origin (window, &tx, &ty);
1754         else
1755 #endif /* GDK_WINDOWING_X11 */
1756           gdk_window_get_position (window, &tx, &ty);
1757
1758         found = gtk_drag_find_widget (toplevel,
1759                                       context,
1760                                       info,
1761                                       event->dnd.x_root - tx,
1762                                       event->dnd.y_root - ty,
1763                                       event->dnd.time,
1764                                       (event->type == GDK_DRAG_MOTION) ?
1765                                       gtk_drag_dest_motion :
1766                                       gtk_drag_dest_drop);
1767
1768         if (info->widget && !found)
1769           {
1770             gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1771             info->widget = NULL;
1772           }
1773         
1774         /* Send a reply.
1775          */
1776         if (event->type == GDK_DRAG_MOTION)
1777           {
1778             if (!found)
1779               gdk_drag_status (context, 0, event->dnd.time);
1780           }
1781         else if (event->type == GDK_DROP_START && !info->proxy_source)
1782           {
1783             gdk_drop_reply (context, found, event->dnd.time);
1784             if ((gdk_drag_context_get_protocol (context) == GDK_DRAG_PROTO_MOTIF) && !found)
1785               gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1786           }
1787       }
1788       break;
1789
1790     default:
1791       g_assert_not_reached ();
1792     }
1793 }
1794
1795 /**
1796  * gtk_drag_dest_find_target: (method)
1797  * @widget: drag destination widget
1798  * @context: drag context
1799  * @target_list: (allow-none): list of droppable targets, or %NULL to use
1800  *    gtk_drag_dest_get_target_list (@widget).
1801  *
1802  * Looks for a match between the supported targets of @context and the
1803  * @dest_target_list, returning the first matching target, otherwise
1804  * returning %GDK_NONE. @dest_target_list should usually be the return
1805  * value from gtk_drag_dest_get_target_list(), but some widgets may
1806  * have different valid targets for different parts of the widget; in
1807  * that case, they will have to implement a drag_motion handler that
1808  * passes the correct target list to this function.
1809  *
1810  * Return value: (transfer none): first target that the source offers
1811  *     and the dest can accept, or %GDK_NONE
1812  **/
1813 GdkAtom
1814 gtk_drag_dest_find_target (GtkWidget      *widget,
1815                            GdkDragContext *context,
1816                            GtkTargetList  *target_list)
1817 {
1818   GList *tmp_target;
1819   GList *tmp_source = NULL;
1820   GtkWidget *source_widget;
1821
1822   g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1823   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1824
1825
1826   source_widget = gtk_drag_get_source_widget (context);
1827
1828   if (target_list == NULL)
1829     target_list = gtk_drag_dest_get_target_list (widget);
1830   
1831   if (target_list == NULL)
1832     return GDK_NONE;
1833   
1834   tmp_target = target_list->list;
1835   while (tmp_target)
1836     {
1837       GtkTargetPair *pair = tmp_target->data;
1838       tmp_source = gdk_drag_context_list_targets (context);
1839       while (tmp_source)
1840         {
1841           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1842             {
1843               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1844                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)) &&
1845                   (!(pair->flags & GTK_TARGET_OTHER_APP) || !source_widget) &&
1846                   (!(pair->flags & GTK_TARGET_OTHER_WIDGET) || (source_widget != widget)))
1847                 return pair->target;
1848               else
1849                 break;
1850             }
1851           tmp_source = tmp_source->next;
1852         }
1853       tmp_target = tmp_target->next;
1854     }
1855
1856   return GDK_NONE;
1857 }
1858
1859 static void
1860 gtk_drag_selection_received (GtkWidget        *widget,
1861                              GtkSelectionData *selection_data,
1862                              guint             time,
1863                              gpointer          data)
1864 {
1865   GdkDragContext *context;
1866   GtkDragDestInfo *info;
1867   GtkWidget *drop_widget;
1868   GdkAtom target;
1869
1870   drop_widget = data;
1871
1872   context = g_object_get_data (G_OBJECT (widget), "drag-context");
1873   info = gtk_drag_get_dest_info (context, FALSE);
1874
1875   if (info->proxy_data && 
1876       gtk_selection_data_get_target (info->proxy_data) == gtk_selection_data_get_target (selection_data))
1877     {
1878       gtk_selection_data_set (info->proxy_data,
1879                               gtk_selection_data_get_data_type (selection_data),
1880                               gtk_selection_data_get_format (selection_data),
1881                               gtk_selection_data_get_data (selection_data),
1882                               gtk_selection_data_get_length (selection_data));
1883       gtk_main_quit ();
1884       return;
1885     }
1886
1887   target = gtk_selection_data_get_target (selection_data);
1888   if (target == gdk_atom_intern_static_string ("DELETE"))
1889     {
1890       gtk_drag_finish (context, TRUE, FALSE, time);
1891     }
1892   else if ((target == gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS")) ||
1893            (target == gdk_atom_intern_static_string ("XmTRANSFER_FAILURE")))
1894     {
1895       /* Do nothing */
1896     }
1897   else
1898     {
1899       GtkDragDestSite *site;
1900
1901       site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1902
1903       if (site && site->target_list)
1904         {
1905           guint target_info;
1906
1907           if (gtk_target_list_find (site->target_list, 
1908                                     target,
1909                                     &target_info))
1910             {
1911               if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1912                   gtk_selection_data_get_length (selection_data) >= 0)
1913                 g_signal_emit_by_name (drop_widget,
1914                                        "drag-data-received",
1915                                        context, info->drop_x, info->drop_y,
1916                                        selection_data,
1917                                        target_info, time);
1918             }
1919         }
1920       else
1921         {
1922           g_signal_emit_by_name (drop_widget,
1923                                  "drag-data-received",
1924                                  context, info->drop_x, info->drop_y,
1925                                  selection_data,
1926                                  0, time);
1927         }
1928       
1929       if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1930         {
1931
1932           gtk_drag_finish (context, 
1933                            (gtk_selection_data_get_length (selection_data) >= 0),
1934                            (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
1935                            time);
1936         }
1937       
1938       g_object_unref (drop_widget);
1939     }
1940
1941   g_signal_handlers_disconnect_by_func (widget,
1942                                         gtk_drag_selection_received,
1943                                         data);
1944   
1945   g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
1946   g_object_unref (context);
1947
1948   gtk_drag_release_ipc_widget (widget);
1949 }
1950
1951 /*************************************************************
1952  * gtk_drag_find_widget:
1953  *     Function used to locate widgets for
1954  *     DRAG_MOTION and DROP_START events.
1955  *************************************************************/
1956
1957 static gboolean
1958 gtk_drag_find_widget (GtkWidget           *widget,
1959                       GdkDragContext      *context,
1960                       GtkDragDestInfo     *info,
1961                       gint                 x,
1962                       gint                 y,
1963                       guint32              time,
1964                       GtkDragDestCallback  callback)
1965 {
1966   if (!gtk_widget_get_mapped (widget) ||
1967       !gtk_widget_get_sensitive (widget))
1968     return FALSE;
1969
1970   /* Get the widget at the pointer coordinates and travel up
1971    * the widget hierarchy from there.
1972    */
1973   widget = _gtk_widget_find_at_coords (gtk_widget_get_window (widget),
1974                                        x, y, &x, &y);
1975   if (!widget)
1976     return FALSE;
1977
1978   while (widget)
1979     {
1980       GtkWidget *parent;
1981       GList *hierarchy = NULL;
1982       gboolean found = FALSE;
1983
1984       if (!gtk_widget_get_mapped (widget) ||
1985           !gtk_widget_get_sensitive (widget))
1986         return FALSE;
1987
1988       /* need to reference the entire hierarchy temporarily in case the
1989        * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
1990        */
1991       for (parent = widget;
1992            parent;
1993            parent = gtk_widget_get_parent (parent))
1994         {
1995           hierarchy = g_list_prepend (hierarchy, g_object_ref (parent));
1996         }
1997
1998       /* If the current widget is registered as a drop site, check to
1999        * emit "drag-motion" to check if we are actually in a drop
2000        * site.
2001        */
2002       if (g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
2003         {
2004           found = callback (widget, context, x, y, time);
2005
2006           /* If so, send a "drag-leave" to the last widget */
2007           if (found)
2008             {
2009               if (info->widget && info->widget != widget)
2010                 {
2011                   gtk_drag_dest_leave (info->widget, context, time);
2012                 }
2013
2014               info->widget = widget;
2015             }
2016         }
2017
2018       if (!found)
2019         {
2020           /* Get the parent before unreffing the hierarchy because
2021            * invoking the callback might have destroyed the widget
2022            */
2023           parent = gtk_widget_get_parent (widget);
2024
2025           /* The parent might be going away when unreffing the
2026            * hierarchy, so also protect againt that
2027            */
2028           if (parent)
2029             g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
2030         }
2031
2032       g_list_free_full (hierarchy, g_object_unref);
2033
2034       if (found)
2035         return TRUE;
2036
2037       if (parent)
2038         g_object_remove_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
2039       else
2040         return FALSE;
2041
2042       if (!gtk_widget_translate_coordinates (widget, parent, x, y, &x, &y))
2043         return FALSE;
2044
2045       widget = parent;
2046     }
2047
2048   return FALSE;
2049 }
2050
2051 static void
2052 gtk_drag_proxy_begin (GtkWidget       *widget,
2053                       GtkDragDestInfo *dest_info,
2054                       guint32          time)
2055 {
2056   GtkDragSourceInfo *source_info;
2057   GList *tmp_list;
2058   GdkDragContext *context;
2059   GtkWidget *ipc_widget;
2060
2061   if (dest_info->proxy_source)
2062     {
2063       gdk_drag_abort (dest_info->proxy_source->context, time);
2064       gtk_drag_source_info_destroy (dest_info->proxy_source);
2065       dest_info->proxy_source = NULL;
2066     }
2067   
2068   ipc_widget = gtk_drag_get_ipc_widget (widget);
2069   context = gdk_drag_begin (gtk_widget_get_window (ipc_widget),
2070                             gdk_drag_context_list_targets (dest_info->context));
2071
2072   source_info = gtk_drag_get_source_info (context, TRUE);
2073
2074   source_info->ipc_widget = ipc_widget;
2075   source_info->widget = g_object_ref (widget);
2076
2077   source_info->target_list = gtk_target_list_new (NULL, 0);
2078   tmp_list = gdk_drag_context_list_targets (dest_info->context);
2079   while (tmp_list)
2080     {
2081       gtk_target_list_add (source_info->target_list,
2082                            GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
2083       tmp_list = tmp_list->next;
2084     }
2085
2086   source_info->proxy_dest = dest_info;
2087   
2088   g_signal_connect (ipc_widget,
2089                     "selection-get",
2090                     G_CALLBACK (gtk_drag_selection_get),
2091                     source_info);
2092   
2093   dest_info->proxy_source = source_info;
2094 }
2095
2096 static void
2097 gtk_drag_dest_info_destroy (gpointer data)
2098 {
2099   g_slice_free (GtkDragDestInfo, data);
2100 }
2101
2102 static GtkDragDestInfo *
2103 gtk_drag_get_dest_info (GdkDragContext *context,
2104                         gboolean        create)
2105 {
2106   GtkDragDestInfo *info;
2107   static GQuark info_quark = 0;
2108   if (!info_quark)
2109     info_quark = g_quark_from_static_string ("gtk-dest-info");
2110   
2111   info = g_object_get_qdata (G_OBJECT (context), info_quark);
2112   if (!info && create)
2113     {
2114       info = g_slice_new0 (GtkDragDestInfo);
2115       info->context = context;
2116       g_object_set_qdata_full (G_OBJECT (context), info_quark,
2117                                info, gtk_drag_dest_info_destroy);
2118     }
2119
2120   return info;
2121 }
2122
2123 static GQuark dest_info_quark = 0;
2124
2125 static GtkDragSourceInfo *
2126 gtk_drag_get_source_info (GdkDragContext *context,
2127                           gboolean        create)
2128 {
2129   GtkDragSourceInfo *info;
2130   if (!dest_info_quark)
2131     dest_info_quark = g_quark_from_static_string ("gtk-source-info");
2132   
2133   info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
2134   if (!info && create)
2135     {
2136       info = g_new0 (GtkDragSourceInfo, 1);
2137       info->context = context;
2138       g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
2139     }
2140
2141   return info;
2142 }
2143
2144 static void
2145 gtk_drag_clear_source_info (GdkDragContext *context)
2146 {
2147   g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
2148 }
2149
2150 static void
2151 gtk_drag_dest_realized (GtkWidget *widget)
2152 {
2153   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2154
2155   if (gtk_widget_is_toplevel (toplevel))
2156     gdk_window_register_dnd (gtk_widget_get_window (toplevel));
2157 }
2158
2159 static void
2160 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
2161                                  GtkWidget *previous_toplevel)
2162 {
2163   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2164
2165   if (gtk_widget_is_toplevel (toplevel) && gtk_widget_get_realized (toplevel))
2166     gdk_window_register_dnd (gtk_widget_get_window (toplevel));
2167 }
2168
2169 static void
2170 gtk_drag_dest_site_destroy (gpointer data)
2171 {
2172   GtkDragDestSite *site = data;
2173
2174   if (site->proxy_window)
2175     g_object_unref (site->proxy_window);
2176     
2177   if (site->target_list)
2178     gtk_target_list_unref (site->target_list);
2179
2180   g_slice_free (GtkDragDestSite, site);
2181 }
2182
2183 /*
2184  * Default drag handlers
2185  */
2186 static void  
2187 gtk_drag_dest_leave (GtkWidget      *widget,
2188                      GdkDragContext *context,
2189                      guint           time)
2190 {
2191   GtkDragDestSite *site;
2192
2193   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2194   g_return_if_fail (site != NULL);
2195
2196   if (site->do_proxy)
2197     {
2198       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2199
2200       if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
2201         {
2202           gdk_drag_abort (info->proxy_source->context, time);
2203           gtk_drag_source_info_destroy (info->proxy_source);
2204           info->proxy_source = NULL;
2205         }
2206       
2207       return;
2208     }
2209   else
2210     {
2211       if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
2212         gtk_drag_unhighlight (widget);
2213
2214       if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
2215           site->track_motion)
2216         g_signal_emit_by_name (widget, "drag-leave", context, time);
2217       
2218       site->have_drag = FALSE;
2219     }
2220 }
2221
2222 static gboolean
2223 gtk_drag_dest_motion (GtkWidget      *widget,
2224                       GdkDragContext *context,
2225                       gint            x,
2226                       gint            y,
2227                       guint           time)
2228 {
2229   GtkDragDestSite *site;
2230   GdkDragAction action = 0;
2231   gboolean retval;
2232
2233   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2234   g_return_val_if_fail (site != NULL, FALSE);
2235
2236   if (site->do_proxy)
2237     {
2238       GdkAtom selection;
2239       GdkEvent *current_event;
2240       GdkWindow *dest_window;
2241       GdkDragProtocol proto;
2242         
2243       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2244
2245       if (!info->proxy_source || info->proxy_source->widget != widget)
2246         gtk_drag_proxy_begin (widget, info, time);
2247
2248       current_event = gtk_get_current_event ();
2249
2250       if (site->proxy_window)
2251         {
2252           dest_window = site->proxy_window;
2253           proto = site->proxy_protocol;
2254         }
2255       else
2256         {
2257           gdk_drag_find_window_for_screen (info->proxy_source->context,
2258                                            NULL,
2259                                            gdk_window_get_screen (current_event->dnd.window),
2260                                            current_event->dnd.x_root, 
2261                                            current_event->dnd.y_root,
2262                                            &dest_window, &proto);
2263         }
2264       
2265       gdk_drag_motion (info->proxy_source->context, 
2266                        dest_window, proto,
2267                        current_event->dnd.x_root, 
2268                        current_event->dnd.y_root, 
2269                        gdk_drag_context_get_suggested_action (context),
2270                        gdk_drag_context_get_actions (context),
2271                        time);
2272
2273       if (!site->proxy_window && dest_window)
2274         g_object_unref (dest_window);
2275
2276       selection = gdk_drag_get_selection (info->proxy_source->context);
2277       if (selection && 
2278           selection != gdk_drag_get_selection (info->context))
2279         gtk_drag_source_check_selection (info->proxy_source, selection, time);
2280
2281       gdk_event_free (current_event);
2282       
2283       return TRUE;
2284     }
2285
2286   if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
2287     {
2288       if (gdk_drag_context_get_suggested_action (context) & site->actions)
2289         action = gdk_drag_context_get_suggested_action (context);
2290       else
2291         {
2292           gint i;
2293           
2294           for (i = 0; i < 8; i++)
2295             {
2296               if ((site->actions & (1 << i)) &&
2297                   (gdk_drag_context_get_actions (context) & (1 << i)))
2298                 {
2299                   action = (1 << i);
2300                   break;
2301                 }
2302             }
2303         }
2304
2305       if (action && gtk_drag_dest_find_target (widget, context, NULL))
2306         {
2307           if (!site->have_drag)
2308             {
2309               site->have_drag = TRUE;
2310               if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
2311                 gtk_drag_highlight (widget);
2312             }
2313
2314           gdk_drag_status (context, action, time);
2315         }
2316       else
2317         {
2318           gdk_drag_status (context, 0, time);
2319           if (!site->track_motion)
2320             return TRUE;
2321         }
2322     }
2323
2324   g_signal_emit_by_name (widget, "drag-motion",
2325                          context, x, y, time, &retval);
2326
2327   return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
2328 }
2329
2330 static gboolean
2331 gtk_drag_dest_drop (GtkWidget        *widget,
2332                     GdkDragContext   *context,
2333                     gint              x,
2334                     gint              y,
2335                     guint             time)
2336 {
2337   GtkDragDestSite *site;
2338   GtkDragDestInfo *info;
2339
2340   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2341   g_return_val_if_fail (site != NULL, FALSE);
2342
2343   info = gtk_drag_get_dest_info (context, FALSE);
2344   g_return_val_if_fail (info != NULL, FALSE);
2345
2346   info->drop_x = x;
2347   info->drop_y = y;
2348
2349   if (site->do_proxy)
2350     {
2351       if (info->proxy_source || 
2352           (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_ROOTWIN))
2353         {
2354           gtk_drag_drop (info->proxy_source, time);
2355         }
2356       else
2357         {
2358           /* We need to synthesize a motion event, wait for a status,
2359            * and, if we get a good one, do a drop.
2360            */
2361           
2362           GdkEvent *current_event;
2363           GdkAtom selection;
2364           GdkWindow *dest_window;
2365           GdkDragProtocol proto;
2366           
2367           gtk_drag_proxy_begin (widget, info, time);
2368           info->proxy_drop_wait = TRUE;
2369           info->proxy_drop_time = time;
2370           
2371           current_event = gtk_get_current_event ();
2372
2373           if (site->proxy_window)
2374             {
2375               dest_window = site->proxy_window;
2376               proto = site->proxy_protocol;
2377             }
2378           else
2379             {
2380               gdk_drag_find_window_for_screen (info->proxy_source->context,
2381                                                NULL,
2382                                                gdk_window_get_screen (current_event->dnd.window),
2383                                                current_event->dnd.x_root, 
2384                                                current_event->dnd.y_root,
2385                                                &dest_window, &proto);
2386             }
2387
2388           gdk_drag_motion (info->proxy_source->context, 
2389                            dest_window, proto,
2390                            current_event->dnd.x_root, 
2391                            current_event->dnd.y_root, 
2392                            gdk_drag_context_get_suggested_action (context),
2393                            gdk_drag_context_get_actions (context),
2394                            time);
2395
2396           if (!site->proxy_window && dest_window)
2397             g_object_unref (dest_window);
2398
2399           selection = gdk_drag_get_selection (info->proxy_source->context);
2400           if (selection && 
2401               selection != gdk_drag_get_selection (info->context))
2402             gtk_drag_source_check_selection (info->proxy_source, selection, time);
2403
2404           gdk_event_free (current_event);
2405         }
2406
2407       return TRUE;
2408     }
2409   else
2410     {
2411       gboolean retval;
2412
2413       if (site->flags & GTK_DEST_DEFAULT_DROP)
2414         {
2415           GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
2416
2417           if (target == GDK_NONE)
2418             {
2419               gtk_drag_finish (context, FALSE, FALSE, time);
2420               return TRUE;
2421             }
2422           else 
2423             gtk_drag_get_data (widget, context, target, time);
2424         }
2425
2426       g_signal_emit_by_name (widget, "drag-drop",
2427                              context, x, y, time, &retval);
2428
2429       return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
2430     }
2431 }
2432
2433 /***************
2434  * Source side *
2435  ***************/
2436
2437 static GtkIconHelper *
2438 gtk_drag_source_site_get_icon_helper (GtkDragSourceSite *site)
2439 {
2440   GtkIconHelper *helper;
2441
2442   if (site)
2443     helper = g_object_ref (site->icon_helper);
2444   else
2445     helper = _gtk_icon_helper_new ();
2446
2447   if (_gtk_icon_helper_get_is_empty (helper))
2448     _gtk_icon_helper_set_stock_id (helper, GTK_STOCK_DND, GTK_ICON_SIZE_DND);
2449
2450   return helper;
2451 }
2452
2453 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
2454  * so that we can set the icon from the source site information
2455  */
2456 static GdkDragContext *
2457 gtk_drag_begin_internal (GtkWidget         *widget,
2458                          GtkDragSourceSite *site,
2459                          GtkTargetList     *target_list,
2460                          GdkDragAction      actions,
2461                          gint               button,
2462                          GdkEvent          *event)
2463 {
2464   GtkDragSourceInfo *info;
2465   GList *targets = NULL;
2466   GList *tmp_list;
2467   guint32 time = GDK_CURRENT_TIME;
2468   GdkDragAction possible_actions, suggested_action;
2469   GdkDragContext *context;
2470   GtkWidget *ipc_widget;
2471   GdkCursor *cursor;
2472   GdkDevice *pointer, *keyboard;
2473   GdkWindow *ipc_window;
2474
2475   pointer = keyboard = NULL;
2476   ipc_widget = gtk_drag_get_ipc_widget (widget);
2477   
2478   gtk_drag_get_event_actions (event, button, actions,
2479                               &suggested_action, &possible_actions);
2480   
2481   cursor = gtk_drag_get_cursor (widget,
2482                                 gtk_widget_get_display (widget), 
2483                                 suggested_action,
2484                                 NULL);
2485   
2486   if (event)
2487     {
2488       time = gdk_event_get_time (event);
2489       if (time == GDK_CURRENT_TIME)
2490         time = gtk_get_current_event_time ();
2491
2492       pointer = gdk_event_get_device (event);
2493
2494       if (gdk_device_get_source (pointer) == GDK_SOURCE_KEYBOARD)
2495         {
2496           keyboard = pointer;
2497           pointer = gdk_device_get_associated_device (keyboard);
2498         }
2499       else
2500         keyboard = gdk_device_get_associated_device (pointer);
2501     }
2502   else
2503     {
2504       GdkDeviceManager *device_manager;
2505
2506       device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
2507       pointer = gdk_device_manager_get_client_pointer (device_manager);
2508       keyboard = gdk_device_get_associated_device (pointer);
2509     }
2510
2511   if (!pointer)
2512     return NULL;
2513
2514   ipc_window = gtk_widget_get_window (ipc_widget);
2515
2516   if (gdk_device_grab (pointer, ipc_window,
2517                        GDK_OWNERSHIP_APPLICATION, FALSE,
2518                        GDK_POINTER_MOTION_MASK |
2519                        GDK_BUTTON_RELEASE_MASK,
2520                        cursor, time) != GDK_GRAB_SUCCESS)
2521     {
2522       gtk_drag_release_ipc_widget (ipc_widget);
2523       return NULL;
2524     }
2525
2526   if (keyboard)
2527     grab_dnd_keys (ipc_widget, keyboard, time);
2528
2529   /* We use a GTK grab here to override any grabs that the widget
2530    * we are dragging from might have held
2531    */
2532   gtk_device_grab_add (ipc_widget, pointer, FALSE);
2533
2534   tmp_list = g_list_last (target_list->list);
2535   while (tmp_list)
2536     {
2537       GtkTargetPair *pair = tmp_list->data;
2538       targets = g_list_prepend (targets, 
2539                                 GINT_TO_POINTER (pair->target));
2540       tmp_list = tmp_list->prev;
2541     }
2542
2543   source_widgets = g_slist_prepend (source_widgets, ipc_widget);
2544
2545   context = gdk_drag_begin (ipc_window, targets);
2546   gdk_drag_context_set_device (context, pointer);
2547   g_list_free (targets);
2548   
2549   info = gtk_drag_get_source_info (context, TRUE);
2550   
2551   info->ipc_widget = ipc_widget;
2552   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), info);
2553
2554   info->widget = g_object_ref (widget);
2555   
2556   info->button = button;
2557   info->cursor = cursor;
2558   info->target_list = target_list;
2559   gtk_target_list_ref (target_list);
2560
2561   info->possible_actions = actions;
2562
2563   info->status = GTK_DRAG_STATUS_DRAG;
2564   info->last_event = NULL;
2565   info->selections = NULL;
2566   info->icon_window = NULL;
2567   info->destroy_icon = FALSE;
2568
2569   /* Set cur_x, cur_y here so if the "drag-begin" signal shows
2570    * the drag icon, it will be in the right place
2571    */
2572   if (event && event->type == GDK_MOTION_NOTIFY)
2573     {
2574       info->cur_screen = gtk_widget_get_screen (widget);
2575       info->cur_x = event->motion.x_root;
2576       info->cur_y = event->motion.y_root;
2577     }
2578   else
2579     {
2580       gdk_device_get_position (pointer, &info->cur_screen, &info->cur_x, &info->cur_y);
2581     }
2582
2583   g_signal_emit_by_name (widget, "drag-begin", info->context);
2584
2585   /* Ensure that we have an icon before we start the drag; the
2586    * application may have set one in ::drag_begin, or it may
2587    * not have set one.
2588    */
2589   if (!info->icon_window && !info->icon_helper)
2590     info->icon_helper = gtk_drag_source_site_get_icon_helper (site);
2591
2592   /* We need to composite the icon into the cursor, if we are
2593    * not using an icon window.
2594    */
2595   if (info->icon_helper)
2596     {
2597       cursor = gtk_drag_get_cursor (widget,
2598                                     gtk_widget_get_display (widget), 
2599                                     suggested_action,
2600                                     info);
2601   
2602       if (cursor != info->cursor)
2603         {
2604           gdk_device_grab (pointer, gtk_widget_get_window (widget),
2605                            GDK_OWNERSHIP_APPLICATION, FALSE,
2606                            GDK_POINTER_MOTION_MASK |
2607                            GDK_BUTTON_RELEASE_MASK,
2608                            cursor, time);
2609           info->cursor = cursor;
2610         }
2611     }
2612     
2613   if (event && event->type == GDK_MOTION_NOTIFY)
2614     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
2615   else
2616     gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
2617
2618   info->start_x = info->cur_x;
2619   info->start_y = info->cur_y;
2620
2621   g_signal_connect (info->ipc_widget, "grab-broken-event",
2622                     G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
2623   g_signal_connect (info->ipc_widget, "grab-notify",
2624                     G_CALLBACK (gtk_drag_grab_notify_cb), info);
2625   g_signal_connect (info->ipc_widget, "button-release-event",
2626                     G_CALLBACK (gtk_drag_button_release_cb), info);
2627   g_signal_connect (info->ipc_widget, "motion-notify-event",
2628                     G_CALLBACK (gtk_drag_motion_cb), info);
2629   g_signal_connect (info->ipc_widget, "key-press-event",
2630                     G_CALLBACK (gtk_drag_key_cb), info);
2631   g_signal_connect (info->ipc_widget, "key-release-event",
2632                     G_CALLBACK (gtk_drag_key_cb), info);
2633   g_signal_connect (info->ipc_widget, "selection-get",
2634                     G_CALLBACK (gtk_drag_selection_get), info);
2635
2636   info->have_grab = TRUE;
2637   info->grab_time = time;
2638
2639   return info->context;
2640 }
2641
2642 /**
2643  * gtk_drag_begin: (method)
2644  * @widget: the source widget.
2645  * @targets: The targets (data formats) in which the
2646  *    source can provide the data.
2647  * @actions: A bitmask of the allowed drag actions for this drag.
2648  * @button: The button the user clicked to start the drag.
2649  * @event: The event that triggered the start of the drag.
2650  *
2651  * Initiates a drag on the source side. The function
2652  * only needs to be used when the application is
2653  * starting drags itself, and is not needed when
2654  * gtk_drag_source_set() is used.
2655  *
2656  * The @event is used to retrieve the timestamp that will be used internally to
2657  * grab the pointer.  If @event is #NULL, then GDK_CURRENT_TIME will be used.
2658  * However, you should try to pass a real event in all cases, since that can be
2659  * used by GTK+ to get information about the start position of the drag, for
2660  * example if the @event is a %GDK_MOTION_NOTIFY.
2661  *
2662  * Generally there are three cases when you want to start a drag by hand by
2663  * calling this function:
2664  *
2665  * 1. During a #GtkWidget::button-press-event handler, if you want to start a drag
2666  * immediately when the user presses the mouse button.  Pass the @event
2667  * that you have in your #GtkWidget::button-press-event handler.
2668  *
2669  * 2. During a #GtkWidget::motion-notify-event handler, if you want to start a drag
2670  * when the mouse moves past a certain threshold distance after a button-press.
2671  * Pass the @event that you have in your #GtkWidget::motion-notify-event handler.
2672  *
2673  * 3. During a timeout handler, if you want to start a drag after the mouse
2674  * button is held down for some time.  Try to save the last event that you got
2675  * from the mouse, using gdk_event_copy(), and pass it to this function
2676  * (remember to free the event with gdk_event_free() when you are done).
2677  * If you can really not pass a real event, pass #NULL instead.
2678  *
2679  * Return value: (transfer none): the context for this drag.
2680  **/
2681 GdkDragContext *
2682 gtk_drag_begin (GtkWidget         *widget,
2683                 GtkTargetList     *targets,
2684                 GdkDragAction      actions,
2685                 gint               button,
2686                 GdkEvent          *event)
2687 {
2688   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2689   g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
2690   g_return_val_if_fail (targets != NULL, NULL);
2691
2692   return gtk_drag_begin_internal (widget, NULL, targets,
2693                                   actions, button, event);
2694 }
2695
2696 /**
2697  * gtk_drag_source_set: (method)
2698  * @widget: a #GtkWidget
2699  * @start_button_mask: the bitmask of buttons that can start the drag
2700  * @targets: (allow-none) (array length=n_targets): the table of targets that the drag will support,
2701  *     may be %NULL
2702  * @n_targets: the number of items in @targets
2703  * @actions: the bitmask of possible actions for a drag from this widget
2704  *
2705  * Sets up a widget so that GTK+ will start a drag operation when the user
2706  * clicks and drags on the widget. The widget must have a window.
2707  */
2708 void
2709 gtk_drag_source_set (GtkWidget            *widget,
2710                      GdkModifierType       start_button_mask,
2711                      const GtkTargetEntry *targets,
2712                      gint                  n_targets,
2713                      GdkDragAction         actions)
2714 {
2715   GtkDragSourceSite *site;
2716
2717   g_return_if_fail (GTK_IS_WIDGET (widget));
2718
2719   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2720
2721   gtk_widget_add_events (widget,
2722                          gtk_widget_get_events (widget) |
2723                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2724                          GDK_BUTTON_MOTION_MASK);
2725
2726   if (site)
2727     {
2728       if (site->target_list)
2729         gtk_target_list_unref (site->target_list);
2730     }
2731   else
2732     {
2733       site = g_slice_new0 (GtkDragSourceSite);
2734       site->icon_helper = _gtk_icon_helper_new ();
2735       
2736       g_signal_connect (widget, "button-press-event",
2737                         G_CALLBACK (gtk_drag_source_event_cb),
2738                         site);
2739       g_signal_connect (widget, "button-release-event",
2740                         G_CALLBACK (gtk_drag_source_event_cb),
2741                         site);
2742       g_signal_connect (widget, "motion-notify-event",
2743                         G_CALLBACK (gtk_drag_source_event_cb),
2744                         site);
2745       
2746       g_object_set_data_full (G_OBJECT (widget),
2747                               I_("gtk-site-data"), 
2748                               site, gtk_drag_source_site_destroy);
2749     }
2750
2751   site->start_button_mask = start_button_mask;
2752
2753   site->target_list = gtk_target_list_new (targets, n_targets);
2754
2755   site->actions = actions;
2756 }
2757
2758 /**
2759  * gtk_drag_source_unset: (method)
2760  * @widget: a #GtkWidget
2761  *
2762  * Undoes the effects of gtk_drag_source_set().
2763  */
2764 void 
2765 gtk_drag_source_unset (GtkWidget *widget)
2766 {
2767   GtkDragSourceSite *site;
2768
2769   g_return_if_fail (GTK_IS_WIDGET (widget));
2770
2771   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2772
2773   if (site)
2774     {
2775       g_signal_handlers_disconnect_by_func (widget,
2776                                             gtk_drag_source_event_cb,
2777                                             site);
2778       g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
2779     }
2780 }
2781
2782 /**
2783  * gtk_drag_source_get_target_list: (method)
2784  * @widget: a #GtkWidget
2785  *
2786  * Gets the list of targets this widget can provide for
2787  * drag-and-drop.
2788  *
2789  * Return value: (transfer none): the #GtkTargetList, or %NULL if none
2790  *
2791  * Since: 2.4
2792  **/
2793 GtkTargetList *
2794 gtk_drag_source_get_target_list (GtkWidget *widget)
2795 {
2796   GtkDragSourceSite *site;
2797
2798   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2799
2800   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2801
2802   return site ? site->target_list : NULL;
2803 }
2804
2805 /**
2806  * gtk_drag_source_set_target_list: (method)
2807  * @widget: a #GtkWidget that's a drag source
2808  * @target_list: (allow-none): list of draggable targets, or %NULL for none
2809  *
2810  * Changes the target types that this widget offers for drag-and-drop.
2811  * The widget must first be made into a drag source with
2812  * gtk_drag_source_set().
2813  *
2814  * Since: 2.4
2815  **/
2816 void
2817 gtk_drag_source_set_target_list (GtkWidget     *widget,
2818                                  GtkTargetList *target_list)
2819 {
2820   GtkDragSourceSite *site;
2821
2822   g_return_if_fail (GTK_IS_WIDGET (widget));
2823
2824   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2825   if (site == NULL)
2826     {
2827       g_warning ("gtk_drag_source_set_target_list() requires the widget "
2828                  "to already be a drag source.");
2829       return;
2830     }
2831
2832   if (target_list)
2833     gtk_target_list_ref (target_list);
2834
2835   if (site->target_list)
2836     gtk_target_list_unref (site->target_list);
2837
2838   site->target_list = target_list;
2839 }
2840
2841 /**
2842  * gtk_drag_source_add_text_targets: (method)
2843  * @widget: a #GtkWidget that's is a drag source
2844  *
2845  * Add the text targets supported by #GtkSelection to
2846  * the target list of the drag source.  The targets
2847  * are added with @info = 0. If you need another value, 
2848  * use gtk_target_list_add_text_targets() and
2849  * gtk_drag_source_set_target_list().
2850  * 
2851  * Since: 2.6
2852  **/
2853 void
2854 gtk_drag_source_add_text_targets (GtkWidget *widget)
2855 {
2856   GtkTargetList *target_list;
2857
2858   target_list = gtk_drag_source_get_target_list (widget);
2859   if (target_list)
2860     gtk_target_list_ref (target_list);
2861   else
2862     target_list = gtk_target_list_new (NULL, 0);
2863   gtk_target_list_add_text_targets (target_list, 0);
2864   gtk_drag_source_set_target_list (widget, target_list);
2865   gtk_target_list_unref (target_list);
2866 }
2867
2868 /**
2869  * gtk_drag_source_add_image_targets: (method)
2870  * @widget: a #GtkWidget that's is a drag source
2871  *
2872  * Add the writable image targets supported by #GtkSelection to
2873  * the target list of the drag source. The targets
2874  * are added with @info = 0. If you need another value, 
2875  * use gtk_target_list_add_image_targets() and
2876  * gtk_drag_source_set_target_list().
2877  * 
2878  * Since: 2.6
2879  **/
2880 void
2881 gtk_drag_source_add_image_targets (GtkWidget *widget)
2882 {
2883   GtkTargetList *target_list;
2884
2885   target_list = gtk_drag_source_get_target_list (widget);
2886   if (target_list)
2887     gtk_target_list_ref (target_list);
2888   else
2889     target_list = gtk_target_list_new (NULL, 0);
2890   gtk_target_list_add_image_targets (target_list, 0, TRUE);
2891   gtk_drag_source_set_target_list (widget, target_list);
2892   gtk_target_list_unref (target_list);
2893 }
2894
2895 /**
2896  * gtk_drag_source_add_uri_targets: (method)
2897  * @widget: a #GtkWidget that's is a drag source
2898  *
2899  * Add the URI targets supported by #GtkSelection to
2900  * the target list of the drag source.  The targets
2901  * are added with @info = 0. If you need another value, 
2902  * use gtk_target_list_add_uri_targets() and
2903  * gtk_drag_source_set_target_list().
2904  * 
2905  * Since: 2.6
2906  **/
2907 void
2908 gtk_drag_source_add_uri_targets (GtkWidget *widget)
2909 {
2910   GtkTargetList *target_list;
2911
2912   target_list = gtk_drag_source_get_target_list (widget);
2913   if (target_list)
2914     gtk_target_list_ref (target_list);
2915   else
2916     target_list = gtk_target_list_new (NULL, 0);
2917   gtk_target_list_add_uri_targets (target_list, 0);
2918   gtk_drag_source_set_target_list (widget, target_list);
2919   gtk_target_list_unref (target_list);
2920 }
2921
2922 /**
2923  * gtk_drag_source_set_icon_pixbuf: (method)
2924  * @widget: a #GtkWidget
2925  * @pixbuf: the #GdkPixbuf for the drag icon
2926  * 
2927  * Sets the icon that will be used for drags from a particular widget
2928  * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will 
2929  * release it when it is no longer needed.
2930  **/
2931 void 
2932 gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
2933                                  GdkPixbuf   *pixbuf)
2934 {
2935   GtkDragSourceSite *site;
2936
2937   g_return_if_fail (GTK_IS_WIDGET (widget));
2938   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2939
2940   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2941   g_return_if_fail (site != NULL); 
2942   g_object_ref (pixbuf);
2943
2944   _gtk_icon_helper_set_pixbuf (site->icon_helper, pixbuf);
2945 }
2946
2947 /**
2948  * gtk_drag_source_set_icon_stock: (method)
2949  * @widget: a #GtkWidget
2950  * @stock_id: the ID of the stock icon to use
2951  *
2952  * Sets the icon that will be used for drags from a particular source
2953  * to a stock icon. 
2954  **/
2955 void 
2956 gtk_drag_source_set_icon_stock (GtkWidget   *widget,
2957                                 const gchar *stock_id)
2958 {
2959   GtkDragSourceSite *site;
2960
2961   g_return_if_fail (GTK_IS_WIDGET (widget));
2962   g_return_if_fail (stock_id != NULL);
2963
2964   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2965   g_return_if_fail (site != NULL);
2966
2967   _gtk_icon_helper_set_stock_id (site->icon_helper, stock_id, GTK_ICON_SIZE_DND);
2968 }
2969
2970 /**
2971  * gtk_drag_source_set_icon_name: (method)
2972  * @widget: a #GtkWidget
2973  * @icon_name: name of icon to use
2974  * 
2975  * Sets the icon that will be used for drags from a particular source
2976  * to a themed icon. See the docs for #GtkIconTheme for more details.
2977  *
2978  * Since: 2.8
2979  **/
2980 void 
2981 gtk_drag_source_set_icon_name (GtkWidget   *widget,
2982                                const gchar *icon_name)
2983 {
2984   GtkDragSourceSite *site;
2985
2986   g_return_if_fail (GTK_IS_WIDGET (widget));
2987   g_return_if_fail (icon_name != NULL);
2988
2989   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2990   g_return_if_fail (site != NULL);
2991
2992   _gtk_icon_helper_set_icon_name (site->icon_helper, icon_name, GTK_ICON_SIZE_DND);
2993 }
2994
2995 /**
2996  * gtk_drag_source_set_icon_gicon: (method)
2997  * @widget: a #GtkWidget
2998  * @icon: A #GIcon
2999  * 
3000  * Sets the icon that will be used for drags from a particular source
3001  * to @icon. See the docs for #GtkIconTheme for more details.
3002  *
3003  * Since: 3.2
3004  **/
3005 void
3006 gtk_drag_source_set_icon_gicon (GtkWidget       *widget,
3007                                 GIcon           *icon)
3008 {
3009   GtkDragSourceSite *site;
3010
3011   g_return_if_fail (GTK_IS_WIDGET (widget));
3012   g_return_if_fail (icon != NULL);
3013   
3014   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
3015   g_return_if_fail (site != NULL);
3016
3017   _gtk_icon_helper_set_gicon (site->icon_helper, icon, GTK_ICON_SIZE_DND);
3018 }
3019
3020 static void
3021 gtk_drag_get_icon (GtkDragSourceInfo *info,
3022                    GtkWidget        **icon_window,
3023                    gint              *hot_x,
3024                    gint              *hot_y)
3025 {
3026   if (get_can_change_screen (info->icon_window))
3027     gtk_window_set_screen (GTK_WINDOW (info->icon_window),
3028                            info->cur_screen);
3029       
3030   if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
3031     {
3032       if (!info->fallback_icon)
3033         {
3034           gint save_hot_x, save_hot_y;
3035           gboolean save_destroy_icon;
3036           GtkWidget *save_icon_window;
3037           GtkIconHelper *helper;
3038           
3039           /* HACK to get the appropriate icon
3040            */
3041           save_icon_window = info->icon_window;
3042           save_hot_x = info->hot_x;
3043           save_hot_y = info->hot_x;
3044           save_destroy_icon = info->destroy_icon;
3045
3046           info->icon_window = NULL;
3047
3048           helper = _gtk_icon_helper_new ();
3049           _gtk_icon_helper_set_stock_id (helper, GTK_STOCK_DND, GTK_ICON_SIZE_DND);
3050           set_icon_helper (info->context, helper, -2, -2, TRUE);
3051           info->fallback_icon = info->icon_window;
3052           
3053           info->icon_window = save_icon_window;
3054           info->hot_x = save_hot_x;
3055           info->hot_y = save_hot_y;
3056           info->destroy_icon = save_destroy_icon;
3057
3058           g_object_unref (helper);
3059         }
3060       
3061       gtk_widget_hide (info->icon_window);
3062       
3063       *icon_window = info->fallback_icon;
3064       gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
3065       
3066       *hot_x = -2;
3067       *hot_y = -2;
3068     }
3069   else
3070     {
3071       if (info->fallback_icon)
3072         gtk_widget_hide (info->fallback_icon);
3073       
3074       *icon_window = info->icon_window;
3075       *hot_x = info->hot_x;
3076       *hot_y = info->hot_y;
3077     }
3078 }
3079
3080 static void
3081 gtk_drag_update_icon (GtkDragSourceInfo *info)
3082 {
3083   if (info->icon_window)
3084     {
3085       GtkWidget *icon_window;
3086       gint hot_x, hot_y;
3087   
3088       gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
3089       
3090       gtk_window_move (GTK_WINDOW (icon_window), 
3091                        info->cur_x - hot_x, 
3092                        info->cur_y - hot_y);
3093
3094       if (gtk_widget_get_visible (icon_window))
3095         gdk_window_raise (gtk_widget_get_window (icon_window));
3096       else
3097         gtk_widget_show (icon_window);
3098     }
3099 }
3100
3101 static void 
3102 gtk_drag_set_icon_window (GdkDragContext *context,
3103                           GtkWidget      *widget,
3104                           gint            hot_x,
3105                           gint            hot_y,
3106                           gboolean        destroy_on_release)
3107 {
3108   GtkDragSourceInfo *info;
3109
3110   info = gtk_drag_get_source_info (context, FALSE);
3111   if (info == NULL)
3112     {
3113       if (destroy_on_release)
3114         gtk_widget_destroy (widget);
3115       return;
3116     }
3117
3118   gtk_drag_remove_icon (info);
3119
3120   if (widget)
3121     g_object_ref (widget);  
3122   
3123   info->icon_window = widget;
3124   info->hot_x = hot_x;
3125   info->hot_y = hot_y;
3126   info->destroy_icon = destroy_on_release;
3127
3128   if (widget && info->icon_helper)
3129     g_clear_object (&info->icon_helper);
3130
3131   gtk_drag_update_cursor (info);
3132   gtk_drag_update_icon (info);
3133 }
3134
3135 /**
3136  * gtk_drag_set_icon_widget: (method)
3137  * @context: the context for a drag. (This must be called 
3138           with a  context for the source side of a drag)
3139  * @widget: a toplevel window to use as an icon.
3140  * @hot_x: the X offset within @widget of the hotspot.
3141  * @hot_y: the Y offset within @widget of the hotspot.
3142  * 
3143  * Changes the icon for a widget to a given widget. GTK+
3144  * will not destroy the icon, so if you don't want
3145  * it to persist, you should connect to the "drag-end" 
3146  * signal and destroy it yourself.
3147  **/
3148 void 
3149 gtk_drag_set_icon_widget (GdkDragContext    *context,
3150                           GtkWidget         *widget,
3151                           gint               hot_x,
3152                           gint               hot_y)
3153 {
3154   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3155   g_return_if_fail (GTK_IS_WIDGET (widget));
3156
3157   gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
3158 }
3159
3160 static void
3161 icon_window_realize (GtkWidget *window,
3162                      GtkIconHelper *helper)
3163 {
3164   cairo_surface_t *surface;
3165   cairo_pattern_t *pattern;
3166   cairo_t *cr;
3167   GdkPixbuf *pixbuf;
3168
3169   pixbuf = _gtk_icon_helper_ensure_pixbuf (helper, gtk_widget_get_style_context (window));
3170   surface = gdk_window_create_similar_surface (gtk_widget_get_window (window),
3171                                                CAIRO_CONTENT_COLOR,
3172                                                gdk_pixbuf_get_width (pixbuf),
3173                                                gdk_pixbuf_get_height (pixbuf));
3174
3175   cr = cairo_create (surface);
3176   cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
3177   gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
3178   cairo_paint (cr);
3179   cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE);
3180   cairo_paint (cr);
3181   cairo_pop_group_to_source (cr);
3182   cairo_paint (cr);
3183   cairo_destroy (cr);
3184
3185   pattern = cairo_pattern_create_for_surface (surface);
3186   gdk_window_set_background_pattern (gtk_widget_get_window (window), pattern);
3187   cairo_pattern_destroy (pattern);
3188
3189   cairo_surface_destroy (surface);
3190
3191   if (gdk_pixbuf_get_has_alpha (pixbuf))
3192     {
3193       cairo_region_t *region;
3194
3195       surface = cairo_image_surface_create (CAIRO_FORMAT_A1,
3196                                             gdk_pixbuf_get_width (pixbuf),
3197                                             gdk_pixbuf_get_height (pixbuf));
3198       
3199       cr = cairo_create (surface);
3200       gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
3201       cairo_paint (cr);
3202       cairo_destroy (cr);
3203
3204       region = gdk_cairo_region_create_from_surface (surface);
3205       gtk_widget_shape_combine_region (window, region);
3206       cairo_region_destroy (region);
3207
3208       cairo_surface_destroy (surface);
3209     }
3210
3211   g_object_unref (pixbuf);
3212 }
3213
3214 static void
3215 set_icon_helper (GdkDragContext    *context,
3216                  GtkIconHelper     *helper,
3217                  gint               hot_x,
3218                  gint               hot_y,
3219                  gboolean           force_window)
3220 {
3221   GtkWidget *window;
3222   gint width, height;
3223   GdkScreen *screen;
3224   GdkDisplay *display;
3225
3226   g_return_if_fail (context != NULL);
3227   g_return_if_fail (helper != NULL);
3228
3229   screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
3230
3231   window = gtk_window_new (GTK_WINDOW_POPUP);
3232   gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3233   gtk_window_set_screen (GTK_WINDOW (window), screen);
3234   set_can_change_screen (window, TRUE);
3235
3236   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3237   gtk_widget_set_app_paintable (window, TRUE);
3238
3239   display = gdk_window_get_display (gdk_drag_context_get_source_window (context));
3240   _gtk_icon_helper_get_size (helper, 
3241                              gtk_widget_get_style_context (window),
3242                              &width, &height);
3243
3244   if (!force_window &&
3245       gtk_drag_can_use_rgba_cursor (display, width + 2, height + 2))
3246     {
3247       GtkDragSourceInfo *info;
3248
3249       gtk_widget_destroy (window);
3250
3251       info = gtk_drag_get_source_info (context, FALSE);
3252
3253       if (info->icon_helper)
3254         g_object_unref (info->icon_helper);
3255       info->icon_helper = g_object_ref (helper);
3256
3257       gtk_drag_set_icon_window (context, NULL, hot_x, hot_y, TRUE);
3258     }
3259   else
3260     {
3261       gtk_widget_set_size_request (window, width, height);
3262
3263       g_signal_connect_closure (window, "realize",
3264                                 g_cclosure_new (G_CALLBACK (icon_window_realize),
3265                                                 g_object_ref (helper),
3266                                                 (GClosureNotify)g_object_unref),
3267                                 FALSE);
3268                     
3269       gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3270    }
3271 }
3272
3273 /**
3274  * gtk_drag_set_icon_pixbuf: (method)
3275  * @context: the context for a drag. (This must be called 
3276  *            with a  context for the source side of a drag)
3277  * @pixbuf: the #GdkPixbuf to use as the drag icon.
3278  * @hot_x: the X offset within @widget of the hotspot.
3279  * @hot_y: the Y offset within @widget of the hotspot.
3280  * 
3281  * Sets @pixbuf as the icon for a given drag.
3282  **/
3283 void 
3284 gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
3285                            GdkPixbuf      *pixbuf,
3286                            gint            hot_x,
3287                            gint            hot_y)
3288 {
3289   GtkIconHelper *icon;
3290
3291   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3292   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
3293
3294   icon = _gtk_icon_helper_new ();
3295   _gtk_icon_helper_set_pixbuf (icon, pixbuf);
3296   set_icon_helper (context, icon, hot_x, hot_y, FALSE);
3297
3298   g_object_unref (icon);
3299 }
3300
3301 /**
3302  * gtk_drag_set_icon_stock: (method)
3303  * @context: the context for a drag. (This must be called 
3304  *            with a  context for the source side of a drag)
3305  * @stock_id: the ID of the stock icon to use for the drag.
3306  * @hot_x: the X offset within the icon of the hotspot.
3307  * @hot_y: the Y offset within the icon of the hotspot.
3308  * 
3309  * Sets the icon for a given drag from a stock ID.
3310  **/
3311 void 
3312 gtk_drag_set_icon_stock  (GdkDragContext *context,
3313                           const gchar    *stock_id,
3314                           gint            hot_x,
3315                           gint            hot_y)
3316 {
3317   GtkIconHelper *icon;
3318
3319   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3320   g_return_if_fail (stock_id != NULL);
3321
3322   icon = _gtk_icon_helper_new ();
3323   _gtk_icon_helper_set_stock_id (icon, stock_id, GTK_ICON_SIZE_DND);
3324   set_icon_helper (context, icon, hot_x, hot_y, FALSE);
3325
3326   g_object_unref (icon);
3327 }
3328
3329 /* XXX: This function is in gdk, too. Should it be in Cairo? */
3330 static gboolean
3331 _gtk_cairo_surface_extents (cairo_surface_t *surface,
3332                             GdkRectangle *extents)
3333 {
3334   double x1, x2, y1, y2;
3335   cairo_t *cr;
3336
3337   g_return_val_if_fail (surface != NULL, FALSE);
3338   g_return_val_if_fail (extents != NULL, FALSE);
3339
3340   cr = cairo_create (surface);
3341   cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
3342
3343   x1 = floor (x1);
3344   y1 = floor (y1);
3345   x2 = ceil (x2);
3346   y2 = ceil (y2);
3347   x2 -= x1;
3348   y2 -= y1;
3349   
3350   if (x1 < G_MININT || x1 > G_MAXINT ||
3351       y1 < G_MININT || y1 > G_MAXINT ||
3352       x2 > G_MAXINT || y2 > G_MAXINT)
3353     {
3354       extents->x = extents->y = extents->width = extents->height = 0;
3355       return FALSE;
3356     }
3357
3358   extents->x = x1;
3359   extents->y = y1;
3360   extents->width = x2;
3361   extents->height = y2;
3362
3363   return TRUE;
3364 }
3365
3366 /**
3367  * gtk_drag_set_icon_surface: (method)
3368  * @context: the context for a drag. (This must be called
3369  *            with a context for the source side of a drag)
3370  * @surface: the surface to use as icon
3371  *
3372  * Sets @surface as the icon for a given drag. GTK+ retains
3373  * references for the arguments, and will release them when
3374  * they are no longer needed.
3375  *
3376  * To position the surface relative to the mouse, use
3377  * cairo_surface_set_device_offset() on @surface. The mouse
3378  * cursor will be positioned at the (0,0) coordinate of the
3379  * surface.
3380  **/
3381 void 
3382 gtk_drag_set_icon_surface (GdkDragContext    *context,
3383                            cairo_surface_t   *surface)
3384 {
3385   GtkWidget *window;
3386   GdkScreen *screen;
3387   GdkRectangle extents;
3388   cairo_pattern_t *pattern;
3389       
3390   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3391   g_return_if_fail (surface != NULL);
3392
3393   _gtk_cairo_surface_extents (surface, &extents);
3394
3395
3396   screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
3397
3398   window = gtk_window_new (GTK_WINDOW_POPUP);
3399   gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3400   gtk_window_set_screen (GTK_WINDOW (window), screen);
3401   set_can_change_screen (window, TRUE);
3402
3403   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3404   gtk_widget_set_app_paintable (window, TRUE);
3405
3406   gtk_widget_set_size_request (window, extents.width, extents.height);
3407   gtk_widget_realize (window);
3408
3409   if (cairo_surface_get_content (surface) != CAIRO_CONTENT_COLOR)
3410     {
3411       cairo_surface_t *saturated;
3412       cairo_region_t *region;
3413       cairo_t *cr;
3414
3415       region = gdk_cairo_region_create_from_surface (surface);
3416       cairo_region_translate (region, -extents.x, -extents.y);
3417
3418       gtk_widget_shape_combine_region (window, region);
3419       cairo_region_destroy (region);
3420
3421       /* Need to saturate the colors, so it doesn't look like semi-transparent
3422        * pixels were painted on black. */
3423       saturated = gdk_window_create_similar_surface (gtk_widget_get_window (window),
3424                                                      CAIRO_CONTENT_COLOR,
3425                                                      extents.width,
3426                                                      extents.height);
3427       
3428       cr = cairo_create (saturated);
3429       cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
3430       cairo_set_source_surface (cr, surface, -extents.x, -extents.y);
3431       cairo_paint (cr);
3432       cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE);
3433       cairo_paint (cr);
3434       cairo_pop_group_to_source (cr);
3435       cairo_paint (cr);
3436       cairo_destroy (cr);
3437     
3438       pattern = cairo_pattern_create_for_surface (saturated);
3439
3440       cairo_surface_destroy (saturated);
3441     }
3442   else
3443     {
3444       cairo_matrix_t matrix;
3445
3446       pattern = cairo_pattern_create_for_surface (surface);
3447       cairo_matrix_init_translate (&matrix, extents.x, extents.y);
3448       cairo_pattern_set_matrix (pattern, &matrix);
3449     }
3450
3451   gdk_window_set_background_pattern (gtk_widget_get_window (window), pattern);
3452
3453   gtk_drag_set_icon_window (context, window, extents.x, extents.y, TRUE);
3454 }
3455
3456 /**
3457  * gtk_drag_set_icon_name: (method)
3458  * @context: the context for a drag. (This must be called 
3459  *            with a context for the source side of a drag)
3460  * @icon_name: name of icon to use
3461  * @hot_x: the X offset of the hotspot within the icon
3462  * @hot_y: the Y offset of the hotspot within the icon
3463  * 
3464  * Sets the icon for a given drag from a named themed icon. See
3465  * the docs for #GtkIconTheme for more details. Note that the
3466  * size of the icon depends on the icon theme (the icon is
3467  * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus 
3468  * @hot_x and @hot_y have to be used with care.
3469  *
3470  * Since: 2.8
3471  **/
3472 void 
3473 gtk_drag_set_icon_name (GdkDragContext *context,
3474                         const gchar    *icon_name,
3475                         gint            hot_x,
3476                         gint            hot_y)
3477 {
3478   GtkIconHelper *icon;
3479
3480   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3481   g_return_if_fail (icon_name != NULL);
3482
3483   icon = _gtk_icon_helper_new ();
3484   _gtk_icon_helper_set_icon_name (icon, icon_name, GTK_ICON_SIZE_DND);
3485   set_icon_helper (context, icon, hot_x, hot_y, FALSE);
3486
3487   g_object_unref (icon);
3488 }
3489
3490 /**
3491  * gtk_drag_set_icon_gicon: (method)
3492  * @context: the context for a drag. (This must be called 
3493  *            with a context for the source side of a drag)
3494  * @icon: a #GIcon
3495  * @hot_x: the X offset of the hotspot within the icon
3496  * @hot_y: the Y offset of the hotspot within the icon
3497  * 
3498  * Sets the icon for a given drag from the given @icon.  See the
3499  * documentation for gtk_drag_set_icon_name() for more details about
3500  * using icons in drag and drop.
3501  *
3502  * Since: 3.2
3503  **/
3504 void 
3505 gtk_drag_set_icon_gicon (GdkDragContext *context,
3506                          GIcon          *icon,
3507                          gint            hot_x,
3508                          gint            hot_y)
3509 {
3510   GtkIconHelper *helper;
3511
3512   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3513   g_return_if_fail (icon != NULL);
3514
3515   helper = _gtk_icon_helper_new ();
3516   _gtk_icon_helper_set_gicon (helper, icon, GTK_ICON_SIZE_DND);
3517   set_icon_helper (context, helper, hot_x, hot_y, FALSE);
3518
3519   g_object_unref (helper);
3520 }
3521
3522 /**
3523  * gtk_drag_set_icon_default: (method)
3524  * @context: the context for a drag. (This must be called 
3525              with a  context for the source side of a drag)
3526  * 
3527  * Sets the icon for a particular drag to the default
3528  * icon.
3529  **/
3530 void 
3531 gtk_drag_set_icon_default (GdkDragContext *context)
3532 {
3533   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3534
3535   gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
3536 }
3537
3538 /*************************************************************
3539  * _gtk_drag_source_handle_event:
3540  *     Called from widget event handling code on Drag events
3541  *     for drag sources.
3542  *
3543  *   arguments:
3544  *     toplevel: Toplevel widget that received the event
3545  *     event:
3546  *   results:
3547  *************************************************************/
3548
3549 void
3550 _gtk_drag_source_handle_event (GtkWidget *widget,
3551                                GdkEvent  *event)
3552 {
3553   GtkDragSourceInfo *info;
3554   GdkDragContext *context;
3555
3556   g_return_if_fail (widget != NULL);
3557   g_return_if_fail (event != NULL);
3558
3559   context = event->dnd.context;
3560   info = gtk_drag_get_source_info (context, FALSE);
3561   if (!info)
3562     return;
3563
3564   switch (event->type)
3565     {
3566     case GDK_DRAG_STATUS:
3567       {
3568         GdkCursor *cursor;
3569
3570         if (info->proxy_dest)
3571           {
3572             if (!event->dnd.send_event)
3573               {
3574                 if (info->proxy_dest->proxy_drop_wait)
3575                   {
3576                     gboolean result = gdk_drag_context_get_selected_action (context) != 0;
3577                     
3578                     /* Aha - we can finally pass the MOTIF DROP on... */
3579                     gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
3580                     if (result)
3581                       gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
3582                     else
3583                       gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
3584                   }
3585                 else
3586                   {
3587                     gdk_drag_status (info->proxy_dest->context,
3588                                      gdk_drag_context_get_selected_action (event->dnd.context),
3589                                      event->dnd.time);
3590                   }
3591               }
3592           }
3593         else if (info->have_grab)
3594           {
3595             cursor = gtk_drag_get_cursor (widget, 
3596                                           gtk_widget_get_display (widget),
3597                                           gdk_drag_context_get_selected_action (event->dnd.context),
3598                                           info);
3599             if (info->cursor != cursor)
3600               {
3601                 GdkDevice *pointer;
3602
3603                 pointer = gdk_drag_context_get_device (context);
3604                 gdk_device_grab (pointer, gtk_widget_get_window (widget),
3605                                  GDK_OWNERSHIP_APPLICATION, FALSE,
3606                                  GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3607                                  cursor, info->grab_time);
3608                 info->cursor = cursor;
3609               }
3610             
3611             gtk_drag_add_update_idle (info);
3612           }
3613       }
3614       break;
3615       
3616     case GDK_DROP_FINISHED:
3617       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, event->dnd.time);
3618       break;
3619     default:
3620       g_assert_not_reached ();
3621     }
3622 }
3623
3624 /*************************************************************
3625  * gtk_drag_source_check_selection:
3626  *     Check if we've set up handlers/claimed the selection
3627  *     for a given drag. If not, add them.
3628  *   arguments:
3629  *     
3630  *   results:
3631  *************************************************************/
3632
3633 static void
3634 gtk_drag_source_check_selection (GtkDragSourceInfo *info, 
3635                                  GdkAtom            selection,
3636                                  guint32            time)
3637 {
3638   GList *tmp_list;
3639
3640   tmp_list = info->selections;
3641   while (tmp_list)
3642     {
3643       if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
3644         return;
3645       tmp_list = tmp_list->next;
3646     }
3647
3648   gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
3649                                        info->ipc_widget,
3650                                        selection,
3651                                        time);
3652   info->selections = g_list_prepend (info->selections,
3653                                      GUINT_TO_POINTER (selection));
3654
3655   tmp_list = info->target_list->list;
3656   while (tmp_list)
3657     {
3658       GtkTargetPair *pair = tmp_list->data;
3659
3660       gtk_selection_add_target (info->ipc_widget,
3661                                 selection,
3662                                 pair->target,
3663                                 pair->info);
3664       tmp_list = tmp_list->next;
3665     }
3666   
3667   if (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_MOTIF)
3668     {
3669       gtk_selection_add_target (info->ipc_widget,
3670                                 selection,
3671                                 gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS"),
3672                                 TARGET_MOTIF_SUCCESS);
3673       gtk_selection_add_target (info->ipc_widget,
3674                                 selection,
3675                                 gdk_atom_intern_static_string ("XmTRANSFER_FAILURE"),
3676                                 TARGET_MOTIF_FAILURE);
3677     }
3678
3679   gtk_selection_add_target (info->ipc_widget,
3680                             selection,
3681                             gdk_atom_intern_static_string ("DELETE"),
3682                             TARGET_DELETE);
3683 }
3684
3685 /*************************************************************
3686  * gtk_drag_drop_finished:
3687  *     Clean up from the drag, and display snapback, if necessary.
3688  *   arguments:
3689  *     info:
3690  *     success:
3691  *     time:
3692  *   results:
3693  *************************************************************/
3694
3695 static void
3696 gtk_drag_drop_finished (GtkDragSourceInfo *info,
3697                         GtkDragResult      result,
3698                         guint              time)
3699 {
3700   gboolean success;
3701
3702   success = (result == GTK_DRAG_RESULT_SUCCESS);
3703   gtk_drag_source_release_selections (info, time); 
3704
3705   if (info->proxy_dest)
3706     {
3707       /* The time from the event isn't reliable for Xdnd drags */
3708       gtk_drag_finish (info->proxy_dest->context, success, FALSE, 
3709                        info->proxy_dest->proxy_drop_time);
3710       gtk_drag_source_info_destroy (info);
3711     }
3712   else
3713     {
3714       if (!success)
3715         g_signal_emit_by_name (info->widget, "drag-failed",
3716                                info->context, result, &success);
3717
3718       if (success)
3719         {
3720           gtk_drag_source_info_destroy (info);
3721         }
3722       else
3723         {
3724           GtkDragAnim *anim = g_slice_new0 (GtkDragAnim);
3725           anim->info = info;
3726           anim->step = 0;
3727
3728           anim->n_steps = MAX (info->cur_x - info->start_x,
3729                info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
3730           anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
3731
3732           info->cur_screen = gtk_widget_get_screen (info->widget);
3733
3734           if (!info->icon_window)
3735             set_icon_helper (info->context, info->icon_helper, 
3736                              0, 0, TRUE);
3737
3738           gtk_drag_update_icon (info);
3739
3740           /* Mark the context as dead, so if the destination decides
3741            * to respond really late, we still are OK.
3742            */
3743           gtk_drag_clear_source_info (info->context);
3744           gdk_threads_add_timeout (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
3745         }
3746     }
3747 }
3748
3749 static void
3750 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
3751                                     guint32            time)
3752 {
3753   GdkDisplay *display = gtk_widget_get_display (info->widget);
3754   GList *tmp_list = info->selections;
3755   
3756   while (tmp_list)
3757     {
3758       GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
3759       if (gdk_selection_owner_get_for_display (display, selection) == gtk_widget_get_window (info->ipc_widget))
3760         gtk_selection_owner_set_for_display (display, NULL, selection, time);
3761
3762       tmp_list = tmp_list->next;
3763     }
3764
3765   g_list_free (info->selections);
3766   info->selections = NULL;
3767 }
3768
3769 /*************************************************************
3770  * gtk_drag_drop:
3771  *     Send a drop event.
3772  *   arguments:
3773  *     
3774  *   results:
3775  *************************************************************/
3776
3777 static void
3778 gtk_drag_drop (GtkDragSourceInfo *info, 
3779                guint32            time)
3780 {
3781   if (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_ROOTWIN)
3782     {
3783       GtkSelectionData selection_data;
3784       GList *tmp_list;
3785       /* GTK+ traditionally has used application/x-rootwin-drop, but the
3786        * XDND spec specifies x-rootwindow-drop.
3787        */
3788       GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3789       GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3790       
3791       tmp_list = info->target_list->list;
3792       while (tmp_list)
3793         {
3794           GtkTargetPair *pair = tmp_list->data;
3795           
3796           if (pair->target == target1 || pair->target == target2)
3797             {
3798               selection_data.selection = GDK_NONE;
3799               selection_data.target = pair->target;
3800               selection_data.data = NULL;
3801               selection_data.length = -1;
3802               
3803               g_signal_emit_by_name (info->widget, "drag-data-get",
3804                                      info->context, &selection_data,
3805                                      pair->info,
3806                                      time);
3807               
3808               /* FIXME: Should we check for length >= 0 here? */
3809               gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3810               return;
3811             }
3812           tmp_list = tmp_list->next;
3813         }
3814       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3815     }
3816   else
3817     {
3818       if (info->icon_window)
3819         gtk_widget_hide (info->icon_window);
3820         
3821       gdk_drag_drop (info->context, time);
3822       info->drop_timeout = gdk_threads_add_timeout (DROP_ABORT_TIME,
3823                                           gtk_drag_abort_timeout,
3824                                           info);
3825     }
3826 }
3827
3828 /*
3829  * Source side callbacks.
3830  */
3831
3832 static gboolean
3833 gtk_drag_source_event_cb (GtkWidget      *widget,
3834                           GdkEvent       *event,
3835                           gpointer        data)
3836 {
3837   GtkDragSourceSite *site;
3838   gboolean retval = FALSE;
3839   site = (GtkDragSourceSite *)data;
3840
3841   switch (event->type)
3842     {
3843     case GDK_BUTTON_PRESS:
3844       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3845         {
3846           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
3847           site->x = event->button.x;
3848           site->y = event->button.y;
3849         }
3850       break;
3851       
3852     case GDK_BUTTON_RELEASE:
3853       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3854         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
3855       break;
3856       
3857     case GDK_MOTION_NOTIFY:
3858       if (site->state & event->motion.state & site->start_button_mask)
3859         {
3860           /* FIXME: This is really broken and can leave us
3861            * with a stuck grab
3862            */
3863           int i;
3864           for (i=1; i<6; i++)
3865             {
3866               if (site->state & event->motion.state & 
3867                   GDK_BUTTON1_MASK << (i - 1))
3868                 break;
3869             }
3870
3871           if (gtk_drag_check_threshold (widget, site->x, site->y,
3872                                         event->motion.x, event->motion.y))
3873             {
3874               site->state = 0;
3875               gtk_drag_begin_internal (widget, site, site->target_list,
3876                                        site->actions, 
3877                                        i, event);
3878
3879               retval = TRUE;
3880             }
3881         }
3882       break;
3883       
3884     default:                    /* hit for 2/3BUTTON_PRESS */
3885       break;
3886     }
3887   
3888   return retval;
3889 }
3890
3891 static void 
3892 gtk_drag_source_site_destroy (gpointer data)
3893 {
3894   GtkDragSourceSite *site = data;
3895
3896   if (site->target_list)
3897     gtk_target_list_unref (site->target_list);
3898
3899   g_clear_object (&site->icon_helper);
3900   g_slice_free (GtkDragSourceSite, site);
3901 }
3902
3903 static void
3904 gtk_drag_selection_get (GtkWidget        *widget, 
3905                         GtkSelectionData *selection_data,
3906                         guint             sel_info,
3907                         guint32           time,
3908                         gpointer          data)
3909 {
3910   GtkDragSourceInfo *info = data;
3911   static GdkAtom null_atom = GDK_NONE;
3912   guint target_info;
3913
3914   if (!null_atom)
3915     null_atom = gdk_atom_intern_static_string ("NULL");
3916
3917   switch (sel_info)
3918     {
3919     case TARGET_DELETE:
3920       g_signal_emit_by_name (info->widget,
3921                              "drag-data-delete", 
3922                              info->context);
3923       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3924       break;
3925     case TARGET_MOTIF_SUCCESS:
3926       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3927       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3928       break;
3929     case TARGET_MOTIF_FAILURE:
3930       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3931       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3932       break;
3933     default:
3934       if (info->proxy_dest)
3935         {
3936           /* This is sort of dangerous and needs to be thought
3937            * through better
3938            */
3939           info->proxy_dest->proxy_data = selection_data;
3940           gtk_drag_get_data (info->widget,
3941                              info->proxy_dest->context,
3942                              gtk_selection_data_get_target (selection_data),
3943                              time);
3944           gtk_main ();
3945           info->proxy_dest->proxy_data = NULL;
3946         }
3947       else
3948         {
3949           if (gtk_target_list_find (info->target_list, 
3950                                     gtk_selection_data_get_target (selection_data),
3951                                     &target_info))
3952             {
3953               g_signal_emit_by_name (info->widget, "drag-data-get",
3954                                      info->context,
3955                                      selection_data,
3956                                      target_info,
3957                                      time);
3958             }
3959         }
3960       break;
3961     }
3962 }
3963
3964 static gboolean
3965 gtk_drag_anim_timeout (gpointer data)
3966 {
3967   GtkDragAnim *anim;
3968   GtkDragSourceInfo *info;
3969   gint x, y;
3970   gboolean retval;
3971
3972   anim = data;
3973   info = anim->info;
3974
3975   if (anim->step == anim->n_steps)
3976     {
3977       gtk_drag_source_info_destroy (anim->info);
3978       g_slice_free (GtkDragAnim, anim);
3979
3980       retval = FALSE;
3981     }
3982   else
3983     {
3984       x = (info->start_x * (anim->step + 1) +
3985            info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3986       y = (info->start_y * (anim->step + 1) +
3987            info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3988       if (info->icon_window)
3989         {
3990           GtkWidget *icon_window;
3991           gint hot_x, hot_y;
3992
3993           gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
3994           gtk_window_move (GTK_WINDOW (icon_window),
3995                            x - hot_x,
3996                            y - hot_y);
3997         }
3998
3999       anim->step++;
4000
4001       retval = TRUE;
4002     }
4003
4004   return retval;
4005 }
4006
4007 static void
4008 gtk_drag_remove_icon (GtkDragSourceInfo *info)
4009 {
4010   if (info->icon_window)
4011     {
4012       gtk_widget_hide (info->icon_window);
4013       if (info->destroy_icon)
4014         gtk_widget_destroy (info->icon_window);
4015
4016       if (info->fallback_icon)
4017         {
4018           gtk_widget_destroy (info->fallback_icon);
4019           info->fallback_icon = NULL;
4020         }
4021
4022       g_object_unref (info->icon_window);
4023       info->icon_window = NULL;
4024     }
4025 }
4026
4027 static void
4028 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
4029 {
4030   gint i;
4031
4032   for (i = 0; i < G_N_ELEMENTS (drag_cursors); i++)
4033     {
4034       if (info->drag_cursors[i] != NULL)
4035         {
4036           g_object_unref (info->drag_cursors[i]);
4037           info->drag_cursors[i] = NULL;
4038         }
4039     }
4040
4041   gtk_drag_remove_icon (info);
4042   g_clear_object (&info->icon_helper);
4043
4044   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4045                                         gtk_drag_grab_broken_event_cb,
4046                                         info);
4047   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4048                                         gtk_drag_grab_notify_cb,
4049                                         info);
4050   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4051                                         gtk_drag_button_release_cb,
4052                                         info);
4053   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4054                                         gtk_drag_motion_cb,
4055                                         info);
4056   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4057                                         gtk_drag_key_cb,
4058                                         info);
4059   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4060                                         gtk_drag_selection_get,
4061                                         info);
4062
4063   if (!info->proxy_dest)
4064     g_signal_emit_by_name (info->widget, "drag-end", info->context);
4065
4066   if (info->widget)
4067     g_object_unref (info->widget);
4068
4069   gtk_selection_remove_all (info->ipc_widget);
4070   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), NULL);
4071   source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
4072   gtk_drag_release_ipc_widget (info->ipc_widget);
4073
4074   gtk_target_list_unref (info->target_list);
4075
4076   gtk_drag_clear_source_info (info->context);
4077   g_object_unref (info->context);
4078
4079   if (info->drop_timeout)
4080     g_source_remove (info->drop_timeout);
4081
4082   if (info->update_idle)
4083     g_source_remove (info->update_idle);
4084
4085   g_free (info);
4086 }
4087
4088 static gboolean
4089 gtk_drag_update_idle (gpointer data)
4090 {
4091   GtkDragSourceInfo *info = data;
4092   GdkWindow *dest_window;
4093   GdkDragProtocol protocol;
4094   GdkAtom selection;
4095
4096   GdkDragAction action;
4097   GdkDragAction possible_actions;
4098   guint32 time;
4099
4100   info->update_idle = 0;
4101     
4102   if (info->last_event)
4103     {
4104       time = gtk_drag_get_event_time (info->last_event);
4105       gtk_drag_get_event_actions (info->last_event,
4106                                   info->button, 
4107                                   info->possible_actions,
4108                                   &action, &possible_actions);
4109       gtk_drag_update_icon (info);
4110       gdk_drag_find_window_for_screen (info->context,
4111                                        info->icon_window ? gtk_widget_get_window (info->icon_window) : NULL,
4112                                        info->cur_screen, info->cur_x, info->cur_y,
4113                                        &dest_window, &protocol);
4114       
4115       if (!gdk_drag_motion (info->context, dest_window, protocol,
4116                             info->cur_x, info->cur_y, action, 
4117                             possible_actions,
4118                             time))
4119         {
4120           gdk_event_free ((GdkEvent *)info->last_event);
4121           info->last_event = NULL;
4122         }
4123   
4124       if (dest_window)
4125         g_object_unref (dest_window);
4126       
4127       selection = gdk_drag_get_selection (info->context);
4128       if (selection)
4129         gtk_drag_source_check_selection (info, selection, time);
4130
4131     }
4132
4133   return FALSE;
4134 }
4135
4136 static void
4137 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
4138 {
4139   /* We use an idle lower than GDK_PRIORITY_REDRAW so that exposes
4140    * from the last move can catch up before we move again.
4141    */
4142   if (!info->update_idle)
4143     info->update_idle = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW + 5,
4144                                          gtk_drag_update_idle,
4145                                          info,
4146                                          NULL);
4147 }
4148
4149 /**
4150  * gtk_drag_update:
4151  * @info: DragSourceInfo for the drag
4152  * @screen: new screen
4153  * @x_root: new X position 
4154  * @y_root: new y position
4155  * @event: event received requiring update
4156  * 
4157  * Updates the status of the drag; called when the
4158  * cursor moves or the modifier changes
4159  **/
4160 static void
4161 gtk_drag_update (GtkDragSourceInfo *info,
4162                  GdkScreen         *screen,
4163                  gint               x_root,
4164                  gint               y_root,
4165                  GdkEvent          *event)
4166 {
4167   info->cur_screen = screen;
4168   info->cur_x = x_root;
4169   info->cur_y = y_root;
4170   if (info->last_event)
4171     {
4172       gdk_event_free ((GdkEvent *)info->last_event);
4173       info->last_event = NULL;
4174     }
4175   if (event)
4176     info->last_event = gdk_event_copy ((GdkEvent *)event);
4177
4178   gtk_drag_add_update_idle (info);
4179 }
4180
4181 /*************************************************************
4182  * gtk_drag_end:
4183  *     Called when the user finishes to drag, either by
4184  *     releasing the mouse, or by pressing Esc.
4185  *   arguments:
4186  *     info: Source info for the drag
4187  *     time: Timestamp for ending the drag
4188  *   results:
4189  *************************************************************/
4190
4191 static void
4192 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
4193 {
4194   GtkWidget *source_widget = info->widget;
4195   GdkDevice *pointer, *keyboard;
4196
4197   pointer = gdk_drag_context_get_device (info->context);
4198   keyboard = gdk_device_get_associated_device (pointer);
4199
4200   /* Prevent ungrab before grab (see bug 623865) */
4201   if (info->grab_time == GDK_CURRENT_TIME)
4202     time = GDK_CURRENT_TIME;
4203
4204   if (info->update_idle)
4205     {
4206       g_source_remove (info->update_idle);
4207       info->update_idle = 0;
4208     }
4209   
4210   if (info->last_event)
4211     {
4212       gdk_event_free (info->last_event);
4213       info->last_event = NULL;
4214     }
4215   
4216   info->have_grab = FALSE;
4217   
4218   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4219                                         gtk_drag_grab_broken_event_cb,
4220                                         info);
4221   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4222                                         gtk_drag_grab_notify_cb,
4223                                         info);
4224   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4225                                         gtk_drag_button_release_cb,
4226                                         info);
4227   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4228                                         gtk_drag_motion_cb,
4229                                         info);
4230   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4231                                         gtk_drag_key_cb,
4232                                         info);
4233
4234   gdk_device_ungrab (pointer, time);
4235   ungrab_dnd_keys (info->ipc_widget, keyboard, time);
4236   gtk_device_grab_remove (info->ipc_widget, pointer);
4237
4238   if (gtk_widget_get_realized (source_widget))
4239     {
4240       GdkEvent *send_event;
4241
4242       /* Send on a release pair to the original widget to convince it
4243        * to release its grab. We need to call gtk_propagate_event()
4244        * here, instead of gtk_widget_event() because widget like
4245        * GtkList may expect propagation.
4246        */
4247
4248       send_event = gdk_event_new (GDK_BUTTON_RELEASE);
4249       send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
4250       send_event->button.send_event = TRUE;
4251       send_event->button.time = time;
4252       send_event->button.x = 0;
4253       send_event->button.y = 0;
4254       send_event->button.axes = NULL;
4255       send_event->button.state = 0;
4256       send_event->button.button = info->button;
4257       send_event->button.device = pointer;
4258       send_event->button.x_root = 0;
4259       send_event->button.y_root = 0;
4260
4261       gtk_propagate_event (source_widget, send_event);
4262       gdk_event_free (send_event);
4263     }
4264 }
4265
4266 /*************************************************************
4267  * gtk_drag_cancel:
4268  *    Called on cancellation of a drag, either by the user
4269  *    or programmatically.
4270  *   arguments:
4271  *     info: Source info for the drag
4272  *     time: Timestamp for ending the drag
4273  *   results:
4274  *************************************************************/
4275
4276 static void
4277 gtk_drag_cancel (GtkDragSourceInfo *info, GtkDragResult result, guint32 time)
4278 {
4279   gtk_drag_end (info, time);
4280   gdk_drag_abort (info->context, time);
4281   gtk_drag_drop_finished (info, result, time);
4282 }
4283
4284 /*************************************************************
4285  * gtk_drag_motion_cb:
4286  *     "motion-notify-event" callback during drag.
4287  *   arguments:
4288  *     
4289  *   results:
4290  *************************************************************/
4291
4292 static gboolean
4293 gtk_drag_motion_cb (GtkWidget      *widget,
4294                     GdkEventMotion *event,
4295                     gpointer        data)
4296 {
4297   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4298   GdkScreen *screen;
4299   gint x_root, y_root;
4300
4301   if (event->is_hint)
4302     {
4303       gdk_device_get_position (event->device, &screen, &x_root, &y_root);
4304       event->x_root = x_root;
4305       event->y_root = y_root;
4306     }
4307   else
4308     screen = gdk_event_get_screen ((GdkEvent *)event);
4309
4310   x_root = (gint)(event->x_root + 0.5);
4311   y_root = (gint)(event->y_root + 0.5);
4312   gtk_drag_update (info, screen, x_root, y_root, (GdkEvent *) event);
4313
4314   return TRUE;
4315 }
4316
4317 /*************************************************************
4318  * gtk_drag_key_cb:
4319  *     "key-press/release-event" callback during drag.
4320  *   arguments:
4321  *     
4322  *   results:
4323  *************************************************************/
4324
4325 #define BIG_STEP 20
4326 #define SMALL_STEP 1
4327
4328 static gboolean
4329 gtk_drag_key_cb (GtkWidget         *widget, 
4330                  GdkEventKey       *event, 
4331                  gpointer           data)
4332 {
4333   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4334   GdkModifierType state;
4335   GdkWindow *root_window;
4336   GdkDevice *pointer;
4337   gint dx, dy;
4338
4339   dx = dy = 0;
4340   state = event->state & gtk_accelerator_get_default_mod_mask ();
4341   pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
4342
4343   if (event->type == GDK_KEY_PRESS)
4344     {
4345       switch (event->keyval)
4346         {
4347         case GDK_KEY_Escape:
4348           gtk_drag_cancel (info, GTK_DRAG_RESULT_USER_CANCELLED, event->time);
4349           return TRUE;
4350
4351         case GDK_KEY_space:
4352         case GDK_KEY_Return:
4353         case GDK_KEY_ISO_Enter:
4354         case GDK_KEY_KP_Enter:
4355         case GDK_KEY_KP_Space:
4356           if ((gdk_drag_context_get_selected_action (info->context) != 0) &&
4357               (gdk_drag_context_get_dest_window (info->context) != NULL))
4358             {
4359               gtk_drag_end (info, event->time);
4360               gtk_drag_drop (info, event->time);
4361             }
4362           else
4363             {
4364               gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
4365             }
4366
4367           return TRUE;
4368
4369         case GDK_KEY_Up:
4370         case GDK_KEY_KP_Up:
4371           dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4372           break;
4373
4374         case GDK_KEY_Down:
4375         case GDK_KEY_KP_Down:
4376           dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4377           break;
4378
4379         case GDK_KEY_Left:
4380         case GDK_KEY_KP_Left:
4381           dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4382           break;
4383
4384         case GDK_KEY_Right:
4385         case GDK_KEY_KP_Right:
4386           dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4387           break;
4388         }
4389     }
4390
4391   /* Now send a "motion" so that the modifier state is updated */
4392
4393   /* The state is not yet updated in the event, so we need
4394    * to query it here. We could use XGetModifierMapping, but
4395    * that would be overkill.
4396    */
4397   root_window = gtk_widget_get_root_window (widget);
4398   gdk_window_get_device_position (root_window, pointer, NULL, NULL, &state);
4399   event->state = state;
4400
4401   if (dx != 0 || dy != 0)
4402     {
4403       info->cur_x += dx;
4404       info->cur_y += dy;
4405       gdk_device_warp (pointer,
4406                        gtk_widget_get_screen (widget),
4407                        info->cur_x, info->cur_y);
4408     }
4409
4410   gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
4411
4412   return TRUE;
4413 }
4414
4415 static gboolean
4416 gtk_drag_grab_broken_event_cb (GtkWidget          *widget,
4417                                GdkEventGrabBroken *event,
4418                                gpointer            data)
4419 {
4420   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4421
4422   /* Don't cancel if we break the implicit grab from the initial button_press.
4423    * Also, don't cancel if we re-grab on the widget or on our IPC window, for
4424    * example, when changing the drag cursor.
4425    */
4426   if (event->implicit
4427       || event->grab_window == gtk_widget_get_window (info->widget)
4428       || event->grab_window == gtk_widget_get_window (info->ipc_widget))
4429     return FALSE;
4430
4431   gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4432   return TRUE;
4433 }
4434
4435 static void
4436 gtk_drag_grab_notify_cb (GtkWidget        *widget,
4437                          gboolean          was_grabbed,
4438                          gpointer          data)
4439 {
4440   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4441   GdkDevice *pointer;
4442
4443   pointer = gdk_drag_context_get_device (info->context);
4444
4445   if (gtk_widget_device_is_shadowed (widget, pointer))
4446     {
4447       /* We have to block callbacks to avoid recursion here, because
4448          gtk_drag_cancel calls gtk_grab_remove (via gtk_drag_end) */
4449       g_signal_handlers_block_by_func (widget, gtk_drag_grab_notify_cb, data);
4450       gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4451       g_signal_handlers_unblock_by_func (widget, gtk_drag_grab_notify_cb, data);
4452     }
4453 }
4454
4455
4456 /*************************************************************
4457  * gtk_drag_button_release_cb:
4458  *     "button-release-event" callback during drag.
4459  *   arguments:
4460  *     
4461  *   results:
4462  *************************************************************/
4463
4464 static gboolean
4465 gtk_drag_button_release_cb (GtkWidget      *widget, 
4466                             GdkEventButton *event, 
4467                             gpointer        data)
4468 {
4469   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4470
4471   if (event->button != info->button)
4472     return FALSE;
4473
4474   if ((gdk_drag_context_get_selected_action (info->context) != 0) &&
4475       (gdk_drag_context_get_dest_window (info->context) != NULL))
4476     {
4477       gtk_drag_end (info, event->time);
4478       gtk_drag_drop (info, event->time);
4479     }
4480   else
4481     {
4482       gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
4483     }
4484
4485   return TRUE;
4486 }
4487
4488 static gboolean
4489 gtk_drag_abort_timeout (gpointer data)
4490 {
4491   GtkDragSourceInfo *info = data;
4492   guint32 time = GDK_CURRENT_TIME;
4493
4494   if (info->proxy_dest)
4495     time = info->proxy_dest->proxy_drop_time;
4496
4497   info->drop_timeout = 0;
4498   gtk_drag_drop_finished (info, GTK_DRAG_RESULT_TIMEOUT_EXPIRED, time);
4499   
4500   return FALSE;
4501 }
4502
4503 /**
4504  * gtk_drag_check_threshold: (method)
4505  * @widget: a #GtkWidget
4506  * @start_x: X coordinate of start of drag
4507  * @start_y: Y coordinate of start of drag
4508  * @current_x: current X coordinate
4509  * @current_y: current Y coordinate
4510  * 
4511  * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
4512  * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
4513  * should trigger the beginning of a drag-and-drop operation.
4514  *
4515  * Return Value: %TRUE if the drag threshold has been passed.
4516  **/
4517 gboolean
4518 gtk_drag_check_threshold (GtkWidget *widget,
4519                           gint       start_x,
4520                           gint       start_y,
4521                           gint       current_x,
4522                           gint       current_y)
4523 {
4524   gint drag_threshold;
4525
4526   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
4527
4528   g_object_get (gtk_widget_get_settings (widget),
4529                 "gtk-dnd-drag-threshold", &drag_threshold,
4530                 NULL);
4531   
4532   return (ABS (current_x - start_x) > drag_threshold ||
4533           ABS (current_y - start_y) > drag_threshold);
4534 }