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