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