]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd.c
Add gtk_drag_source_set/get_target_list(). (#127499, based on patch from
[~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 "gdkconfig.h"
28
29 #include "gdk/gdkkeysyms.h"
30
31 #include "gtkdnd.h"
32 #include "gtkimage.h"
33 #include "gtkinvisible.h"
34 #include "gtkmain.h"
35 #include "gtkstock.h"
36 #include "gtkwindow.h"
37
38 static GSList *source_widgets = NULL;
39
40 typedef struct _GtkDragSourceSite GtkDragSourceSite;
41 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
42 typedef struct _GtkDragDestSite GtkDragDestSite;
43 typedef struct _GtkDragDestInfo GtkDragDestInfo;
44 typedef struct _GtkDragAnim GtkDragAnim;
45 typedef struct _GtkDragFindData GtkDragFindData;
46
47
48 typedef enum 
49 {
50   GTK_DRAG_STATUS_DRAG,
51   GTK_DRAG_STATUS_WAIT,
52   GTK_DRAG_STATUS_DROP
53 } GtkDragStatus;
54
55 struct _GtkDragSourceSite 
56 {
57   GdkModifierType    start_button_mask;
58   GtkTargetList     *target_list;        /* Targets for drag data */
59   GdkDragAction      actions;            /* Possible actions */
60
61   /* Drag icon */
62   GtkImageType icon_type;
63   union
64   {
65     GtkImagePixmapData pixmap;
66     GtkImagePixbufData pixbuf;
67     GtkImageStockData stock;
68   } icon_data;
69   GdkBitmap *icon_mask;
70
71   GdkColormap       *colormap;           /* Colormap for drag icon */
72
73   /* Stored button press information to detect drag beginning */
74   gint               state;
75   gint               x, y;
76 };
77   
78 struct _GtkDragSourceInfo 
79 {
80   GtkWidget         *widget;
81   GtkTargetList     *target_list; /* Targets for drag data */
82   GdkDragAction      possible_actions; /* Actions allowed by source */
83   GdkDragContext    *context;     /* drag context */
84   GtkWidget         *icon_window; /* Window for drag */
85   GtkWidget         *fallback_icon; /* Window for drag used on other screens */
86   GtkWidget         *ipc_widget;  /* GtkInvisible for grab, message passing */
87   GdkCursor         *cursor;      /* Cursor for drag */
88   gint hot_x, hot_y;              /* Hot spot for drag */
89   gint button;                    /* mouse button starting drag */
90
91   GtkDragStatus      status;      /* drag status */
92   GdkEvent          *last_event;  /* pending event */
93
94   gint               start_x, start_y; /* Initial position */
95   gint               cur_x, cur_y;     /* Current Position */
96   GdkScreen         *cur_screen;       /* Current screen for pointer */
97
98   guint32            grab_time;   /* timestamp for initial grab */
99   GList             *selections;  /* selections we've claimed */
100   
101   GtkDragDestInfo   *proxy_dest;  /* Set if this is a proxy drag */
102
103   guint              update_idle;      /* Idle function to update the drag */
104   guint              drop_timeout;     /* Timeout for aborting drop */
105   guint              destroy_icon : 1; /* If true, destroy icon_window
106                                         */
107   guint              have_grab : 1;    /* Do we still have the pointer grab
108                                          */
109 };
110
111 struct _GtkDragDestSite 
112 {
113   GtkDestDefaults    flags;
114   GtkTargetList     *target_list;
115   GdkDragAction      actions;
116   GdkWindow         *proxy_window;
117   GdkDragProtocol    proxy_protocol;
118   guint              do_proxy : 1;
119   guint              proxy_coords : 1;
120   guint              have_drag : 1;
121 };
122   
123 struct _GtkDragDestInfo 
124 {
125   GtkWidget         *widget;       /* Widget in which drag is in */
126   GdkDragContext    *context;      /* Drag context */
127   GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
128   GtkSelectionData  *proxy_data;   /* Set while retrieving proxied data */
129   guint              dropped : 1;     /* Set after we receive a drop */
130   guint32            proxy_drop_time; /* Timestamp for proxied drop */
131   guint              proxy_drop_wait : 1; /* Set if we are waiting for a
132                                            * status reply before sending
133                                            * a proxied drop on.
134                                            */
135   gint               drop_x, drop_y; /* Position of drop */
136 };
137
138 #define DROP_ABORT_TIME 300000
139
140 #define ANIM_STEP_TIME 50
141 #define ANIM_STEP_LENGTH 50
142 #define ANIM_MIN_STEPS 5
143 #define ANIM_MAX_STEPS 10
144
145 struct _GtkDragAnim 
146 {
147   GtkDragSourceInfo *info;
148   gint step;
149   gint n_steps;
150 };
151
152 struct _GtkDragFindData 
153 {
154   gint x;
155   gint y;
156   GdkDragContext *context;
157   GtkDragDestInfo *info;
158   gboolean found;
159   gboolean toplevel;
160   gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
161                         gint x, gint y, guint32 time);
162   guint32 time;
163 };
164
165 /* Enumeration for some targets we handle internally */
166
167 enum {
168   TARGET_MOTIF_SUCCESS = 0x40000000,
169   TARGET_MOTIF_FAILURE,
170   TARGET_DELETE
171 };
172
173 /* Drag icons */
174
175 static GdkPixmap   *default_icon_pixmap = NULL;
176 static GdkPixmap   *default_icon_mask = NULL;
177 static GdkColormap *default_icon_colormap = NULL;
178 static gint         default_icon_hot_x;
179 static gint         default_icon_hot_y;
180
181 /* Forward declarations */
182 static void          gtk_drag_get_event_actions (GdkEvent        *event, 
183                                                  gint             button,
184                                                  GdkDragAction    actions,
185                                                  GdkDragAction   *suggested_action,
186                                                  GdkDragAction   *possible_actions);
187 static GdkCursor *   gtk_drag_get_cursor         (GdkDisplay     *display,
188                                                   GdkDragAction   action);
189 static GtkWidget    *gtk_drag_get_ipc_widget     (GdkScreen      *screen);
190 static void          gtk_drag_release_ipc_widget (GtkWidget      *widget);
191
192 static gboolean      gtk_drag_highlight_expose   (GtkWidget      *widget,
193                                                   GdkEventExpose *event,
194                                                   gpointer        data);
195
196 static void     gtk_drag_selection_received     (GtkWidget        *widget,
197                                                  GtkSelectionData *selection_data,
198                                                  guint32           time,
199                                                  gpointer          data);
200 static void     gtk_drag_find_widget            (GtkWidget        *widget,
201                                                  GtkDragFindData  *data);
202 static void     gtk_drag_proxy_begin            (GtkWidget        *widget,
203                                                  GtkDragDestInfo  *dest_info,
204                                                  guint32           time);
205 static void     gtk_drag_dest_realized          (GtkWidget        *widget);
206 static void     gtk_drag_dest_hierarchy_changed (GtkWidget        *widget,
207                                                  GtkWidget        *previous_toplevel);
208 static void     gtk_drag_dest_site_destroy      (gpointer          data);
209 static void     gtk_drag_dest_leave             (GtkWidget        *widget,
210                                                  GdkDragContext   *context,
211                                                  guint             time);
212 static gboolean gtk_drag_dest_motion            (GtkWidget        *widget,
213                                                  GdkDragContext   *context,
214                                                  gint              x,
215                                                  gint              y,
216                                                  guint             time);
217 static gboolean gtk_drag_dest_drop              (GtkWidget        *widget,
218                                                  GdkDragContext   *context,
219                                                  gint              x,
220                                                  gint              y,
221                                                  guint             time);
222
223 static GtkDragDestInfo *  gtk_drag_get_dest_info     (GdkDragContext *context,
224                                                       gboolean        create);
225 static GtkDragSourceInfo *gtk_drag_get_source_info   (GdkDragContext *context,
226                                                       gboolean        create);
227 static void               gtk_drag_clear_source_info (GdkDragContext *context);
228
229 static void gtk_drag_source_check_selection    (GtkDragSourceInfo *info, 
230                                                 GdkAtom            selection,
231                                                 guint32            time);
232 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
233                                                 guint32            time);
234 static void gtk_drag_drop                      (GtkDragSourceInfo *info,
235                                                 guint32            time);
236 static void gtk_drag_drop_finished             (GtkDragSourceInfo *info,
237                                                 gboolean           success,
238                                                 guint              time);
239 static void gtk_drag_cancel                    (GtkDragSourceInfo *info,
240                                                 guint32            time);
241
242 static gint gtk_drag_source_event_cb           (GtkWidget         *widget,
243                                                 GdkEvent          *event,
244                                                 gpointer           data);
245 static void gtk_drag_source_site_destroy       (gpointer           data);
246 static void gtk_drag_selection_get             (GtkWidget         *widget, 
247                                                 GtkSelectionData  *selection_data,
248                                                 guint              sel_info,
249                                                 guint32            time,
250                                                 gpointer           data);
251 static gint gtk_drag_anim_timeout              (gpointer           data);
252 static void gtk_drag_remove_icon               (GtkDragSourceInfo *info);
253 static void gtk_drag_source_info_destroy       (GtkDragSourceInfo *info);
254 static void gtk_drag_add_update_idle           (GtkDragSourceInfo *info);
255
256 static void gtk_drag_update                    (GtkDragSourceInfo *info,
257                                                 GdkScreen         *screen,
258                                                 gint               x_root,
259                                                 gint               y_root,
260                                                 GdkEvent          *event);
261 static gint gtk_drag_motion_cb                 (GtkWidget         *widget, 
262                                                 GdkEventMotion    *event, 
263                                                 gpointer           data);
264 static gint gtk_drag_key_cb                    (GtkWidget         *widget, 
265                                                 GdkEventKey       *event, 
266                                                 gpointer           data);
267 static gint gtk_drag_button_release_cb         (GtkWidget         *widget, 
268                                                 GdkEventButton    *event, 
269                                                 gpointer           data);
270 static gint gtk_drag_abort_timeout             (gpointer           data);
271
272 /************************
273  * Cursor and Icon data *
274  ************************/
275
276 #define action_ask_width 16
277 #define action_ask_height 16
278 static const guchar action_ask_bits[] = {
279   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7, 
280   0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb, 
281   0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
282
283 #define action_ask_mask_width 16
284 #define action_ask_mask_height 16
285 static const guchar action_ask_mask_bits[] = {
286   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f, 
287   0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07, 
288   0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
289
290 #define action_copy_width 16
291 #define action_copy_height 16
292 static const guchar action_copy_bits[] = {
293   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb, 
294   0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb, 
295   0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
296
297 #define action_copy_mask_width 16
298 #define action_copy_mask_height 16
299 static const guchar action_copy_mask_bits[] = {
300   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07, 
301   0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07, 
302   0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
303
304 #define action_move_width 16
305 #define action_move_height 16
306 static const guchar action_move_bits[] = {
307   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff, 
308   0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb, 
309   0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
310
311 #define action_move_mask_width 16
312 #define action_move_mask_height 16
313 static const guchar action_move_mask_bits[] = {
314   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00, 
315   0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07, 
316   0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
317
318 #define action_link_width 16
319 #define action_link_height 16
320 static const guchar action_link_bits[] = {
321   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7, 
322   0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8, 
323   0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
324
325 #define action_link_mask_width 16
326 #define action_link_mask_height 16
327 static const guchar action_link_mask_bits[] = {
328   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f, 
329   0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77, 
330   0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
331
332 #define action_none_width 16
333 #define action_none_height 16
334 static const guchar action_none_bits[] = {
335   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff, 
336   0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 
337   0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
338
339 #define action_none_mask_width 16
340 #define action_none_mask_height 16
341 static const guchar action_none_mask_bits[] = {
342   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, 
343   0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 
344   0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
345
346 #define CURSOR_WIDTH 16
347 #define CURSOR_HEIGHT 16
348
349 static struct {
350   GdkDragAction action;
351   const guchar *bits;
352   const guchar *mask;
353   GdkCursor    *cursor;
354 } drag_cursors[] = {
355   { GDK_ACTION_DEFAULT, 0 },
356   { GDK_ACTION_ASK,   action_ask_bits,  action_ask_mask_bits,  NULL },
357   { GDK_ACTION_COPY,  action_copy_bits, action_copy_mask_bits, NULL },
358   { GDK_ACTION_MOVE,  action_move_bits, action_move_mask_bits, NULL },
359   { GDK_ACTION_LINK,  action_link_bits, action_link_mask_bits, NULL },
360   { 0              ,  action_none_bits, action_none_mask_bits, NULL },
361 };
362
363 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
364
365 /*********************
366  * Utility functions *
367  *********************/
368
369 static void
370 set_can_change_screen (GtkWidget *widget,
371                        gboolean   can_change_screen)
372 {
373   can_change_screen = can_change_screen != FALSE;
374   
375   g_object_set_data (G_OBJECT (widget), "gtk-dnd-can-change-screen",
376                      GUINT_TO_POINTER (can_change_screen));
377 }
378
379 static gboolean
380 get_can_change_screen (GtkWidget *widget)
381 {
382   return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
383
384 }
385
386 /*************************************************************
387  * gtk_drag_get_ipc_widget:
388  *     Return a invisible, off-screen, override-redirect
389  *     widget for IPC.
390  *   arguments:
391  *     
392  *   results:
393  *************************************************************/
394
395 static GtkWidget *
396 gtk_drag_get_ipc_widget (GdkScreen *screen)
397 {
398   GtkWidget *result;
399   GSList *drag_widgets = g_object_get_data (G_OBJECT (screen), 
400                                             "gtk-dnd-ipc-widgets");
401   
402   if (drag_widgets)
403     {
404       GSList *tmp = drag_widgets;
405       result = drag_widgets->data;
406       drag_widgets = drag_widgets->next;
407       g_object_set_data (G_OBJECT (screen),
408                          "gtk-dnd-ipc-widgets",
409                          drag_widgets);
410       g_slist_free_1 (tmp);
411     }
412   else
413     {
414       result = gtk_invisible_new_for_screen (screen);
415       gtk_widget_show (result);
416     }
417
418   return result;
419 }
420
421 /***************************************************************
422  * gtk_drag_release_ipc_widget:
423  *     Releases widget retrieved with gtk_drag_get_ipc_widget ()
424  *   arguments:
425  *     widget: the widget to release.
426  *   results:
427  ***************************************************************/
428
429 static void
430 gtk_drag_release_ipc_widget (GtkWidget *widget)
431 {
432   GdkScreen *screen = gtk_widget_get_screen (widget);
433   GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
434                                             "gtk-dnd-ipc-widgets");
435   drag_widgets = g_slist_prepend (drag_widgets, widget);
436   g_object_set_data (G_OBJECT (screen),
437                      "gtk-dnd-ipc-widgets",
438                      drag_widgets);
439 }
440
441 static guint32
442 gtk_drag_get_event_time (GdkEvent *event)
443 {
444   guint32 tm = GDK_CURRENT_TIME;
445   
446   if (event)
447     switch (event->type)
448       {
449       case GDK_MOTION_NOTIFY:
450         tm = event->motion.time; break;
451       case GDK_BUTTON_PRESS:
452       case GDK_2BUTTON_PRESS:
453       case GDK_3BUTTON_PRESS:
454       case GDK_BUTTON_RELEASE:
455         tm = event->button.time; break;
456       case GDK_KEY_PRESS:
457       case GDK_KEY_RELEASE:
458         tm = event->key.time; break;
459       case GDK_ENTER_NOTIFY:
460       case GDK_LEAVE_NOTIFY:
461         tm = event->crossing.time; break;
462       case GDK_PROPERTY_NOTIFY:
463         tm = event->property.time; break;
464       case GDK_SELECTION_CLEAR:
465       case GDK_SELECTION_REQUEST:
466       case GDK_SELECTION_NOTIFY:
467         tm = event->selection.time; break;
468       case GDK_PROXIMITY_IN:
469       case GDK_PROXIMITY_OUT:
470         tm = event->proximity.time; break;
471       default:                  /* use current time */
472         break;
473       }
474   
475   return tm;
476 }
477
478 static void
479 gtk_drag_get_event_actions (GdkEvent *event, 
480                             gint button, 
481                             GdkDragAction  actions,
482                             GdkDragAction *suggested_action,
483                             GdkDragAction *possible_actions)
484 {
485   *suggested_action = 0;
486   *possible_actions = 0;
487
488   if (event)
489     {
490       GdkModifierType state = 0;
491       
492       switch (event->type)
493         {
494         case GDK_MOTION_NOTIFY:
495           state = event->motion.state;
496           break;
497         case GDK_BUTTON_PRESS:
498         case GDK_2BUTTON_PRESS:
499         case GDK_3BUTTON_PRESS:
500         case GDK_BUTTON_RELEASE:
501           state = event->button.state;
502           break;
503         case GDK_KEY_PRESS:
504         case GDK_KEY_RELEASE:
505           state = event->key.state;
506           break;
507         case GDK_ENTER_NOTIFY:
508         case GDK_LEAVE_NOTIFY:
509           state = event->crossing.state;
510           break;
511         default:
512           break;
513         }
514
515       if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
516         {
517           *suggested_action = GDK_ACTION_ASK;
518           *possible_actions = actions;
519         }
520       else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
521         {
522           if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
523             {
524               if (actions & GDK_ACTION_LINK)
525                 {
526                   *suggested_action = GDK_ACTION_LINK;
527                   *possible_actions = GDK_ACTION_LINK;
528                 }
529             }
530           else if (state & GDK_CONTROL_MASK)
531             {
532               if (actions & GDK_ACTION_COPY)
533                 {
534                   *suggested_action = GDK_ACTION_COPY;
535                   *possible_actions = GDK_ACTION_COPY;
536                 }
537               return;
538             }
539           else
540             {
541               if (actions & GDK_ACTION_MOVE)
542                 {
543                   *suggested_action = GDK_ACTION_MOVE;
544                   *possible_actions = GDK_ACTION_MOVE;
545                 }
546               return;
547             }
548         }
549       else
550         {
551           *possible_actions = actions;
552
553           if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
554             *suggested_action = GDK_ACTION_ASK;
555           else if (actions & GDK_ACTION_COPY)
556             *suggested_action =  GDK_ACTION_COPY;
557           else if (actions & GDK_ACTION_MOVE)
558             *suggested_action = GDK_ACTION_MOVE;
559           else if (actions & GDK_ACTION_LINK)
560             *suggested_action = GDK_ACTION_LINK;
561         }
562     }
563   else
564     {
565       *possible_actions = actions;
566       
567       if (actions & GDK_ACTION_COPY)
568         *suggested_action =  GDK_ACTION_COPY;
569       else if (actions & GDK_ACTION_MOVE)
570         *suggested_action = GDK_ACTION_MOVE;
571       else if (actions & GDK_ACTION_LINK)
572         *suggested_action = GDK_ACTION_LINK;
573     }
574   
575   return;
576 }
577
578 static GdkCursor *
579 gtk_drag_get_cursor (GdkDisplay   *display,
580                      GdkDragAction action)
581 {
582   gint i;
583   
584   for (i = 0 ; i < n_drag_cursors - 1; i++)
585     if (drag_cursors[i].action == action)
586       break;
587   if (drag_cursors[i].cursor != NULL)
588     {
589       if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
590         {
591           gdk_cursor_unref (drag_cursors[i].cursor);
592           drag_cursors[i].cursor = NULL;
593         }
594     }
595
596   if (drag_cursors[i].cursor == NULL)
597     {
598       GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
599       GdkColor fg = { 0, 0x0000, 0x0000, 0x0000 };
600       GdkScreen *screen = gdk_display_get_default_screen (display);
601       GdkWindow *window = gdk_screen_get_root_window (screen);
602
603       GdkPixmap *pixmap = 
604         gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].bits, CURSOR_WIDTH, CURSOR_HEIGHT);
605
606       GdkPixmap *mask = 
607         gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].mask, CURSOR_WIDTH, CURSOR_HEIGHT);
608
609       drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
610
611       g_object_unref (pixmap);
612       g_object_unref (mask);
613     }
614
615   return drag_cursors[i].cursor;
616 }
617
618 /********************
619  * Destination side *
620  ********************/
621
622 /*************************************************************
623  * gtk_drag_get_data:
624  *     Get the data for a drag or drop
625  *   arguments:
626  *     context - drag context
627  *     target  - format to retrieve the data in.
628  *     time    - timestamp of triggering event.
629  *     
630  *   results:
631  *************************************************************/
632
633 void 
634 gtk_drag_get_data (GtkWidget      *widget,
635                    GdkDragContext *context,
636                    GdkAtom         target,
637                    guint32         time)
638 {
639   GtkWidget *selection_widget;
640
641   g_return_if_fail (GTK_IS_WIDGET (widget));
642   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
643   g_return_if_fail (!context->is_source);
644
645   selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
646
647   g_object_ref (context);
648   g_object_ref (widget);
649   
650   g_signal_connect (selection_widget, "selection_received",
651                     G_CALLBACK (gtk_drag_selection_received), widget);
652
653   g_object_set_data (G_OBJECT (selection_widget), "drag-context", context);
654
655   gtk_selection_convert (selection_widget,
656                          gdk_drag_get_selection (context),
657                          target,
658                          time);
659 }
660
661
662 /*************************************************************
663  * gtk_drag_get_source_widget:
664  *     Get the widget the was the source of this drag, if
665  *     the drag originated from this application.
666  *   arguments:
667  *     context: The drag context for this drag
668  *   results:
669  *     The source widget, or NULL if the drag originated from
670  *     a different application.
671  *************************************************************/
672
673 GtkWidget *
674 gtk_drag_get_source_widget (GdkDragContext *context)
675 {
676   GSList *tmp_list;
677
678   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
679   g_return_val_if_fail (!context->is_source, NULL);
680   
681   tmp_list = source_widgets;
682   while (tmp_list)
683     {
684       GtkWidget *ipc_widget = tmp_list->data;
685       
686       if (ipc_widget->window == context->source_window)
687         {
688           GtkDragSourceInfo *info;
689           info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
690
691           return info ? info->widget : NULL;
692         }
693
694       tmp_list = tmp_list->next;
695     }
696
697   return NULL;
698 }
699
700 /*************************************************************
701  * gtk_drag_finish:
702  *     Notify the drag source that the transfer of data
703  *     is complete.
704  *   arguments:
705  *     context: The drag context for this drag
706  *     success: Was the data successfully transferred?
707  *     time:    The timestamp to use when notifying the destination.
708  *   results:
709  *************************************************************/
710
711 void 
712 gtk_drag_finish (GdkDragContext *context,
713                  gboolean        success,
714                  gboolean        del,
715                  guint32         time)
716 {
717   GdkAtom target = GDK_NONE;
718
719   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
720   g_return_if_fail (!context->is_source);
721
722   if (success && del)
723     {
724       target = gdk_atom_intern ("DELETE", FALSE);
725     }
726   else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
727     {
728       target = gdk_atom_intern (success ? 
729                                   "XmTRANSFER_SUCCESS" : 
730                                   "XmTRANSFER_FAILURE",
731                                 FALSE);
732     }
733
734   if (target != GDK_NONE)
735     {
736       GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
737
738       g_object_ref (context);
739       
740       g_object_set_data (G_OBJECT (selection_widget), "drag-context", context);
741       g_signal_connect (selection_widget, "selection_received",
742                         G_CALLBACK (gtk_drag_selection_received),
743                         NULL);
744       
745       gtk_selection_convert (selection_widget,
746                              gdk_drag_get_selection (context),
747                              target,
748                              time);
749     }
750   
751   if (!(success && del))
752     gdk_drop_finish (context, success, time);
753 }
754
755 /*************************************************************
756  * gtk_drag_highlight_expose:
757  *     Callback for expose_event for highlighted widgets.
758  *   arguments:
759  *     widget:
760  *     event:
761  *     data:
762  *   results:
763  *************************************************************/
764
765 static gboolean
766 gtk_drag_highlight_expose (GtkWidget      *widget,
767                            GdkEventExpose *event,
768                            gpointer        data)
769 {
770   gint x, y, width, height;
771   
772   if (GTK_WIDGET_DRAWABLE (widget))
773     {
774       if (GTK_WIDGET_NO_WINDOW (widget))
775         {
776           x = widget->allocation.x;
777           y = widget->allocation.y;
778           width = widget->allocation.width;
779           height = widget->allocation.height;
780         }
781       else
782         {
783           x = 0;
784           y = 0;
785           gdk_drawable_get_size (widget->window, &width, &height);
786         }
787       
788       gtk_paint_shadow (widget->style, widget->window,
789                         GTK_STATE_NORMAL, GTK_SHADOW_OUT,
790                         NULL, widget, "dnd",
791                         x, y, width, height);
792       
793       gdk_draw_rectangle (widget->window,
794                           widget->style->black_gc,
795                           FALSE,
796                           x, y, width - 1, height - 1);
797     }
798
799   return FALSE;
800 }
801
802 /*************************************************************
803  * gtk_drag_highlight:
804  *     Highlight the given widget in the default manner.
805  *   arguments:
806  *     widget:
807  *   results:
808  *************************************************************/
809
810 void 
811 gtk_drag_highlight (GtkWidget  *widget)
812 {
813   g_return_if_fail (GTK_IS_WIDGET (widget));
814
815   g_signal_connect_after (widget, "expose_event",
816                           G_CALLBACK (gtk_drag_highlight_expose),
817                           NULL);
818
819   gtk_widget_queue_draw (widget);
820 }
821
822 /*************************************************************
823  * gtk_drag_unhighlight:
824  *     Refresh the given widget to remove the highlight.
825  *   arguments:
826  *     widget:
827  *   results:
828  *************************************************************/
829
830 void 
831 gtk_drag_unhighlight (GtkWidget *widget)
832 {
833   g_return_if_fail (GTK_IS_WIDGET (widget));
834
835   g_signal_handlers_disconnect_by_func (widget,
836                                         gtk_drag_highlight_expose,
837                                         NULL);
838   
839   gtk_widget_queue_draw (widget);
840 }
841
842 static void
843 gtk_drag_dest_set_internal (GtkWidget       *widget,
844                             GtkDragDestSite *site)
845 {
846   GtkDragDestSite *old_site;
847   
848   g_return_if_fail (widget != NULL);
849
850   /* HACK, do this in the destroy */
851   old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
852   if (old_site)
853     {
854       g_signal_handlers_disconnect_by_func (widget,
855                                             gtk_drag_dest_realized,
856                                             old_site);
857       g_signal_handlers_disconnect_by_func (widget,
858                                             gtk_drag_dest_hierarchy_changed,
859                                             old_site);
860     }
861
862   if (GTK_WIDGET_REALIZED (widget))
863     gtk_drag_dest_realized (widget);
864
865   g_signal_connect (widget, "realize",
866                     G_CALLBACK (gtk_drag_dest_realized), site);
867   g_signal_connect (widget, "hierarchy_changed",
868                     G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
869
870   g_object_set_data_full (G_OBJECT (widget), "gtk-drag-dest",
871                           site, gtk_drag_dest_site_destroy);
872 }
873                             
874
875 /*************************************************************
876  * gtk_drag_dest_set:
877  *     Register a drop site, and possibly add default behaviors.
878  *   arguments:
879  *     widget:    
880  *     flags:     Which types of default drag behavior to use
881  *     targets:   Table of targets that can be accepted
882  *     n_targets: Number of of entries in targets
883  *     actions:   
884  *   results:
885  *************************************************************/
886
887 void 
888 gtk_drag_dest_set   (GtkWidget            *widget,
889                      GtkDestDefaults       flags,
890                      const GtkTargetEntry *targets,
891                      gint                  n_targets,
892                      GdkDragAction         actions)
893 {
894   GtkDragDestSite *site;
895   
896   g_return_if_fail (GTK_IS_WIDGET (widget));
897
898   site = g_new (GtkDragDestSite, 1);
899
900   site->flags = flags;
901   site->have_drag = FALSE;
902   if (targets)
903     site->target_list = gtk_target_list_new (targets, n_targets);
904   else
905     site->target_list = NULL;
906   site->actions = actions;
907   site->do_proxy = FALSE;
908   site->proxy_window = NULL;
909
910   gtk_drag_dest_set_internal (widget, site);
911 }
912
913 /*************************************************************
914  * gtk_drag_dest_set_proxy:
915  *     Set up this widget to proxy drags elsewhere.
916  *   arguments:
917  *     widget:          
918  *     proxy_window:    window to which forward drag events
919  *     protocol:        Drag protocol which the dest widget accepts
920  *     use_coordinates: If true, send the same coordinates to the
921  *                      destination, because it is a embedded 
922  *                      subwindow.
923  *   results:
924  *************************************************************/
925
926 void 
927 gtk_drag_dest_set_proxy (GtkWidget      *widget,
928                          GdkWindow      *proxy_window,
929                          GdkDragProtocol protocol,
930                          gboolean        use_coordinates)
931 {
932   GtkDragDestSite *site;
933   
934   g_return_if_fail (GTK_IS_WIDGET (widget));
935   g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
936
937   site = g_new (GtkDragDestSite, 1);
938
939   site->flags = 0;
940   site->have_drag = FALSE;
941   site->target_list = NULL;
942   site->actions = 0;
943   site->proxy_window = proxy_window;
944   if (proxy_window)
945     g_object_ref (proxy_window);
946   site->do_proxy = TRUE;
947   site->proxy_protocol = protocol;
948   site->proxy_coords = use_coordinates;
949
950   gtk_drag_dest_set_internal (widget, site);
951 }
952
953 /*************************************************************
954  * gtk_drag_dest_unset
955  *     Unregister this widget as a drag target.
956  *   arguments:
957  *     widget:
958  *   results:
959  *************************************************************/
960
961 void 
962 gtk_drag_dest_unset (GtkWidget *widget)
963 {
964   g_return_if_fail (GTK_IS_WIDGET (widget));
965
966   g_object_set_data (G_OBJECT (widget), "gtk-drag-dest", NULL);
967 }
968
969 /**
970  * gtk_drag_dest_get_target_list:
971  * @widget: a #GtkWidget
972  * 
973  * Returns the list of targets this widget can accept from
974  * drag-and-drop.
975  * 
976  * Return value: the #GtkTargetList, or %NULL if none
977  **/
978 GtkTargetList*
979 gtk_drag_dest_get_target_list (GtkWidget *widget)
980 {
981   GtkDragDestSite *site;
982
983   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
984   
985   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
986
987   return site ? site->target_list : NULL;  
988 }
989
990 /**
991  * gtk_drag_dest_set_target_list:
992  * @widget: a #GtkWidget that's a drag destination
993  * @target_list: list of droppable targets, or %NULL for none
994  * 
995  * Sets the target types that this widget can accept from drag-and-drop.
996  * The widget must first be made into a drag destination with
997  * gtk_drag_dest_set().
998  **/
999 void
1000 gtk_drag_dest_set_target_list (GtkWidget      *widget,
1001                                GtkTargetList  *target_list)
1002 {
1003   GtkDragDestSite *site;
1004
1005   g_return_if_fail (GTK_IS_WIDGET (widget));
1006   
1007   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1008   
1009   if (!site)
1010     {
1011       g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
1012                  "to make the widget into a drag destination");
1013       return;
1014     }
1015
1016   if (target_list)
1017     gtk_target_list_ref (target_list);
1018   
1019   if (site->target_list)
1020     gtk_target_list_unref (site->target_list);
1021
1022   site->target_list = target_list;
1023 }
1024
1025
1026 /*************************************************************
1027  * _gtk_drag_dest_handle_event:
1028  *     Called from widget event handling code on Drag events
1029  *     for destinations.
1030  *
1031  *   arguments:
1032  *     toplevel: Toplevel widget that received the event
1033  *     event:
1034  *   results:
1035  *************************************************************/
1036
1037 void
1038 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1039                             GdkEvent  *event)
1040 {
1041   GtkDragDestInfo *info;
1042   GdkDragContext *context;
1043
1044   g_return_if_fail (toplevel != NULL);
1045   g_return_if_fail (event != NULL);
1046
1047   context = event->dnd.context;
1048
1049   info = gtk_drag_get_dest_info (context, TRUE);
1050
1051   /* Find the widget for the event */
1052   switch (event->type)
1053     {
1054     case GDK_DRAG_ENTER:
1055       break;
1056       
1057     case GDK_DRAG_LEAVE:
1058       if (info->widget)
1059         {
1060           gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1061           info->widget = NULL;
1062         }
1063       break;
1064       
1065     case GDK_DRAG_MOTION:
1066     case GDK_DROP_START:
1067       {
1068         GtkDragFindData data;
1069         gint tx, ty;
1070
1071         if (event->type == GDK_DROP_START)
1072           {
1073             info->dropped = TRUE;
1074             /* We send a leave here so that the widget unhighlights
1075              * properly.
1076              */
1077             if (info->widget)
1078               {
1079                 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1080                 info->widget = NULL;
1081               }
1082           }
1083
1084         gdk_window_get_position (toplevel->window, &tx, &ty);
1085
1086         data.x = event->dnd.x_root - tx;
1087         data.y = event->dnd.y_root - ty;
1088         data.context = context;
1089         data.info = info;
1090         data.found = FALSE;
1091         data.toplevel = TRUE;
1092         data.callback = (event->type == GDK_DRAG_MOTION) ?
1093           gtk_drag_dest_motion : gtk_drag_dest_drop;
1094         data.time = event->dnd.time;
1095         
1096         gtk_drag_find_widget (toplevel, &data);
1097
1098         if (info->widget && !data.found)
1099           {
1100             gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1101             info->widget = NULL;
1102           }
1103         
1104         /* Send a reply.
1105          */
1106         if (event->type == GDK_DRAG_MOTION)
1107           {
1108             if (!data.found)
1109               gdk_drag_status (context, 0, event->dnd.time);
1110           }
1111         else if (event->type == GDK_DROP_START && !info->proxy_source)
1112           {
1113             gdk_drop_reply (context, data.found, event->dnd.time);
1114             if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1115               gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1116           }
1117       }
1118       break;
1119
1120     default:
1121       g_assert_not_reached ();
1122     }
1123 }
1124
1125 /**
1126  * gtk_drag_dest_find_target:
1127  * @widget: drag destination widget
1128  * @context: drag context
1129  * @target_list: list of droppable targets, or %NULL to use
1130  *    gtk_drag_dest_get_target_list (@widget).
1131  * 
1132  * Looks for a match between @context->targets and the
1133  * @dest_target_list, returning the first matching target, otherwise
1134  * returning %GDK_NONE. @dest_target_list should usually be the return
1135  * value from gtk_drag_dest_get_target_list(), but some widgets may
1136  * have different valid targets for different parts of the widget; in
1137  * that case, they will have to implement a drag_motion handler that
1138  * passes the correct target list to this function.
1139  * 
1140  * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1141  **/
1142 GdkAtom
1143 gtk_drag_dest_find_target (GtkWidget      *widget,
1144                            GdkDragContext *context,
1145                            GtkTargetList  *target_list)
1146 {
1147   GList *tmp_target;
1148   GList *tmp_source = NULL;
1149   GtkWidget *source_widget;
1150
1151   g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1152   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1153   g_return_val_if_fail (!context->is_source, GDK_NONE);
1154
1155
1156   source_widget = gtk_drag_get_source_widget (context);
1157
1158   if (target_list == NULL)
1159     target_list = gtk_drag_dest_get_target_list (widget);
1160   
1161   if (target_list == NULL)
1162     return GDK_NONE;
1163   
1164   tmp_target = target_list->list;
1165   while (tmp_target)
1166     {
1167       GtkTargetPair *pair = tmp_target->data;
1168       tmp_source = context->targets;
1169       while (tmp_source)
1170         {
1171           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1172             {
1173               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1174                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1175                 return pair->target;
1176               else
1177                 break;
1178             }
1179           tmp_source = tmp_source->next;
1180         }
1181       tmp_target = tmp_target->next;
1182     }
1183
1184   return GDK_NONE;
1185 }
1186
1187 static void
1188 gtk_drag_selection_received (GtkWidget        *widget,
1189                              GtkSelectionData *selection_data,
1190                              guint32           time,
1191                              gpointer          data)
1192 {
1193   GdkDragContext *context;
1194   GtkDragDestInfo *info;
1195   GtkWidget *drop_widget;
1196
1197   drop_widget = data;
1198
1199   context = g_object_get_data (G_OBJECT (widget), "drag-context");
1200   info = gtk_drag_get_dest_info (context, FALSE);
1201
1202   if (info->proxy_data && 
1203       info->proxy_data->target == selection_data->target)
1204     {
1205       gtk_selection_data_set (info->proxy_data,
1206                               selection_data->type,
1207                               selection_data->format,
1208                               selection_data->data,
1209                               selection_data->length);
1210       gtk_main_quit ();
1211       return;
1212     }
1213
1214   if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1215     {
1216       gtk_drag_finish (context, TRUE, FALSE, time);
1217     }
1218   else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1219            (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1220     {
1221       /* Do nothing */
1222     }
1223   else
1224     {
1225       GtkDragDestSite *site;
1226
1227       site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1228
1229       if (site && site->target_list)
1230         {
1231           guint target_info;
1232
1233           if (gtk_target_list_find (site->target_list, 
1234                                     selection_data->target,
1235                                     &target_info))
1236             {
1237               if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1238                   selection_data->length >= 0)
1239                 g_signal_emit_by_name (drop_widget,
1240                                        "drag_data_received",
1241                                        context, info->drop_x, info->drop_y,
1242                                        selection_data,
1243                                        target_info, time);
1244             }
1245         }
1246       else
1247         {
1248           g_signal_emit_by_name (drop_widget,
1249                                  "drag_data_received",
1250                                  context, info->drop_x, info->drop_y,
1251                                  selection_data,
1252                                  0, time);
1253         }
1254       
1255       if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1256         {
1257
1258           gtk_drag_finish (context, 
1259                            (selection_data->length >= 0),
1260                            (context->action == GDK_ACTION_MOVE),
1261                            time);
1262         }
1263       
1264       g_object_unref (drop_widget);
1265     }
1266
1267   g_signal_handlers_disconnect_by_func (widget,
1268                                         gtk_drag_selection_received,
1269                                         data);
1270   
1271   g_object_set_data (G_OBJECT (widget), "drag-context", NULL);
1272   g_object_unref (context);
1273
1274   gtk_drag_release_ipc_widget (widget);
1275 }
1276
1277 static void
1278 prepend_and_ref_widget (GtkWidget *widget,
1279                         gpointer   data)
1280 {
1281   GSList **slist_p = data;
1282
1283   *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1284 }
1285
1286 /*************************************************************
1287  * gtk_drag_find_widget:
1288  *     Recursive callback used to locate widgets for 
1289  *     DRAG_MOTION and DROP_START events.
1290  *   arguments:
1291  *     
1292  *   results:
1293  *************************************************************/
1294
1295 static void
1296 gtk_drag_find_widget (GtkWidget       *widget,
1297                       GtkDragFindData *data)
1298 {
1299   GtkAllocation new_allocation;
1300   gint allocation_to_window_x = 0;
1301   gint allocation_to_window_y = 0;
1302   gint x_offset = 0;
1303   gint y_offset = 0;
1304
1305   if (data->found || !GTK_WIDGET_MAPPED (widget) || !GTK_WIDGET_SENSITIVE (widget))
1306     return;
1307
1308   /* Note that in the following code, we only count the
1309    * position as being inside a WINDOW widget if it is inside
1310    * widget->window; points that are outside of widget->window
1311    * but within the allocation are not counted. This is consistent
1312    * with the way we highlight drag targets.
1313    *
1314    * data->x,y are relative to widget->parent->window (if
1315    * widget is not a toplevel, widget->window otherwise).
1316    * We compute the allocation of widget in the same coordinates,
1317    * clipping to widget->window, and all intermediate
1318    * windows. If data->x,y is inside that, then we translate
1319    * our coordinates to be relative to widget->window and
1320    * recurse.
1321    */  
1322   new_allocation = widget->allocation;
1323
1324   if (widget->parent)
1325     {
1326       gint tx, ty;
1327       GdkWindow *window = widget->window;
1328
1329       /* Compute the offset from allocation-relative to
1330        * window-relative coordinates.
1331        */
1332       allocation_to_window_x = widget->allocation.x;
1333       allocation_to_window_y = widget->allocation.y;
1334
1335       if (!GTK_WIDGET_NO_WINDOW (widget))
1336         {
1337           /* The allocation is relative to the parent window for
1338            * window widgets, not to widget->window.
1339            */
1340           gdk_window_get_position (window, &tx, &ty);
1341           
1342           allocation_to_window_x -= tx;
1343           allocation_to_window_y -= ty;
1344         }
1345
1346       new_allocation.x = 0 + allocation_to_window_x;
1347       new_allocation.y = 0 + allocation_to_window_y;
1348       
1349       while (window && window != widget->parent->window)
1350         {
1351           GdkRectangle window_rect = { 0, 0, 0, 0 };
1352           
1353           gdk_drawable_get_size (window, &window_rect.width, &window_rect.height);
1354
1355           gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1356
1357           gdk_window_get_position (window, &tx, &ty);
1358           new_allocation.x += tx;
1359           x_offset += tx;
1360           new_allocation.y += ty;
1361           y_offset += ty;
1362           
1363           window = gdk_window_get_parent (window);
1364         }
1365
1366       if (!window)              /* Window and widget heirarchies didn't match. */
1367         return;
1368     }
1369
1370   if (data->toplevel ||
1371       ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1372        (data->x < new_allocation.x + new_allocation.width) && 
1373        (data->y < new_allocation.y + new_allocation.height)))
1374     {
1375       /* First, check if the drag is in a valid drop site in
1376        * one of our children 
1377        */
1378       if (GTK_IS_CONTAINER (widget))
1379         {
1380           GtkDragFindData new_data = *data;
1381           GSList *children = NULL;
1382           GSList *tmp_list;
1383           
1384           new_data.x -= x_offset;
1385           new_data.y -= y_offset;
1386           new_data.found = FALSE;
1387           new_data.toplevel = FALSE;
1388           
1389           /* need to reference children temporarily in case the
1390            * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1391            */
1392           gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1393           for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1394             {
1395               if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1396                 gtk_drag_find_widget (tmp_list->data, &new_data);
1397               g_object_unref (tmp_list->data);
1398             }
1399           g_slist_free (children);
1400           
1401           data->found = new_data.found;
1402         }
1403
1404       /* If not, and this widget is registered as a drop site, check to
1405        * emit "drag_motion" to check if we are actually in
1406        * a drop site.
1407        */
1408       if (!data->found &&
1409           g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1410         {
1411           data->found = data->callback (widget,
1412                                         data->context,
1413                                         data->x - x_offset - allocation_to_window_x,
1414                                         data->y - y_offset - allocation_to_window_y,
1415                                         data->time);
1416           /* If so, send a "drag_leave" to the last widget */
1417           if (data->found)
1418             {
1419               if (data->info->widget && data->info->widget != widget)
1420                 {
1421                   gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1422                 }
1423               data->info->widget = widget;
1424             }
1425         }
1426     }
1427 }
1428
1429 static void
1430 gtk_drag_proxy_begin (GtkWidget       *widget, 
1431                       GtkDragDestInfo *dest_info,
1432                       guint32          time)
1433 {
1434   GtkDragSourceInfo *source_info;
1435   GList *tmp_list;
1436   GdkDragContext *context;
1437   GtkWidget *ipc_widget;
1438
1439   if (dest_info->proxy_source)
1440     {
1441       gdk_drag_abort (dest_info->proxy_source->context, time);
1442       gtk_drag_source_info_destroy (dest_info->proxy_source);
1443       dest_info->proxy_source = NULL;
1444     }
1445   
1446   ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1447   context = gdk_drag_begin (ipc_widget->window,
1448                             dest_info->context->targets);
1449
1450   source_info = gtk_drag_get_source_info (context, TRUE);
1451
1452   source_info->ipc_widget = ipc_widget;
1453   source_info->widget = gtk_widget_ref (widget);
1454
1455   source_info->target_list = gtk_target_list_new (NULL, 0);
1456   tmp_list = dest_info->context->targets;
1457   while (tmp_list)
1458     {
1459       gtk_target_list_add (source_info->target_list, 
1460                            GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1461       tmp_list = tmp_list->next;
1462     }
1463
1464   source_info->proxy_dest = dest_info;
1465   
1466   g_signal_connect (ipc_widget,
1467                     "selection_get",
1468                     G_CALLBACK (gtk_drag_selection_get),
1469                     source_info);
1470   
1471   dest_info->proxy_source = source_info;
1472 }
1473
1474 static void
1475 gtk_drag_dest_info_destroy (gpointer data)
1476 {
1477   GtkDragDestInfo *info = data;
1478
1479   g_free (info);
1480 }
1481
1482 static GtkDragDestInfo *
1483 gtk_drag_get_dest_info (GdkDragContext *context,
1484                         gboolean        create)
1485 {
1486   GtkDragDestInfo *info;
1487   static GQuark info_quark = 0;
1488   if (!info_quark)
1489     info_quark = g_quark_from_static_string ("gtk-dest-info");
1490   
1491   info = g_object_get_qdata (G_OBJECT (context), info_quark);
1492   if (!info && create)
1493     {
1494       info = g_new (GtkDragDestInfo, 1);
1495       info->widget = NULL;
1496       info->context = context;
1497       info->proxy_source = NULL;
1498       info->proxy_data = NULL;
1499       info->dropped = FALSE;
1500       info->proxy_drop_wait = FALSE;
1501       g_object_set_qdata_full (G_OBJECT (context), info_quark,
1502                                info, gtk_drag_dest_info_destroy);
1503     }
1504
1505   return info;
1506 }
1507
1508 static GQuark dest_info_quark = 0;
1509
1510 static GtkDragSourceInfo *
1511 gtk_drag_get_source_info (GdkDragContext *context,
1512                           gboolean        create)
1513 {
1514   GtkDragSourceInfo *info;
1515   if (!dest_info_quark)
1516     dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1517   
1518   info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1519   if (!info && create)
1520     {
1521       info = g_new0 (GtkDragSourceInfo, 1);
1522       info->context = context;
1523       g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1524     }
1525
1526   return info;
1527 }
1528
1529 static void
1530 gtk_drag_clear_source_info (GdkDragContext *context)
1531 {
1532   g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1533 }
1534
1535 static void
1536 gtk_drag_dest_realized (GtkWidget *widget)
1537 {
1538   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1539
1540   if (GTK_WIDGET_TOPLEVEL (toplevel))
1541     gdk_window_register_dnd (toplevel->window);
1542 }
1543
1544 static void
1545 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1546                                  GtkWidget *previous_toplevel)
1547 {
1548   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1549
1550   if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1551     gdk_window_register_dnd (toplevel->window);
1552 }
1553
1554 static void
1555 gtk_drag_dest_site_destroy (gpointer data)
1556 {
1557   GtkDragDestSite *site = data;
1558
1559   if (site->proxy_window)
1560     g_object_unref (site->proxy_window);
1561     
1562   if (site->target_list)
1563     gtk_target_list_unref (site->target_list);
1564
1565   g_free (site);
1566 }
1567
1568 /*
1569  * Default drag handlers
1570  */
1571 static void  
1572 gtk_drag_dest_leave (GtkWidget      *widget,
1573                      GdkDragContext *context,
1574                      guint           time)
1575 {
1576   GtkDragDestSite *site;
1577
1578   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1579   g_return_if_fail (site != NULL);
1580
1581   if (site->do_proxy)
1582     {
1583       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1584
1585       if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1586         {
1587           gdk_drag_abort (info->proxy_source->context, time);
1588           gtk_drag_source_info_destroy (info->proxy_source);
1589           info->proxy_source = NULL;
1590         }
1591       
1592       return;
1593     }
1594   else
1595     {
1596       if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1597         gtk_drag_unhighlight (widget);
1598
1599       if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1600         g_signal_emit_by_name (widget, "drag_leave",
1601                                context, time);
1602       
1603       site->have_drag = FALSE;
1604     }
1605 }
1606
1607 static gboolean
1608 gtk_drag_dest_motion (GtkWidget      *widget,
1609                       GdkDragContext *context,
1610                       gint            x,
1611                       gint            y,
1612                       guint           time)
1613 {
1614   GtkDragDestSite *site;
1615   GdkDragAction action = 0;
1616   gboolean retval;
1617
1618   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1619   g_return_val_if_fail (site != NULL, FALSE);
1620
1621   if (site->do_proxy)
1622     {
1623       GdkAtom selection;
1624       GdkEvent *current_event;
1625       GdkWindow *dest_window;
1626       GdkDragProtocol proto;
1627         
1628       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1629
1630       if (!info->proxy_source || info->proxy_source->widget != widget)
1631         gtk_drag_proxy_begin (widget, info, time);
1632
1633       current_event = gtk_get_current_event ();
1634
1635       if (site->proxy_window)
1636         {
1637           dest_window = site->proxy_window;
1638           proto = site->proxy_protocol;
1639         }
1640       else
1641         {
1642           gdk_drag_find_window_for_screen (info->proxy_source->context,
1643                                            NULL,
1644                                            gdk_drawable_get_screen (current_event->dnd.window),
1645                                            current_event->dnd.x_root, 
1646                                            current_event->dnd.y_root,
1647                                            &dest_window, &proto);
1648         }
1649       
1650       gdk_drag_motion (info->proxy_source->context, 
1651                        dest_window, proto,
1652                        current_event->dnd.x_root, 
1653                        current_event->dnd.y_root, 
1654                        context->suggested_action, 
1655                        context->actions, time);
1656
1657       if (!site->proxy_window && dest_window)
1658         g_object_unref (dest_window);
1659
1660       selection = gdk_drag_get_selection (info->proxy_source->context);
1661       if (selection && 
1662           selection != gdk_drag_get_selection (info->context))
1663         gtk_drag_source_check_selection (info->proxy_source, selection, time);
1664
1665       gdk_event_free (current_event);
1666       
1667       return TRUE;
1668     }
1669
1670   if (site->flags & GTK_DEST_DEFAULT_MOTION)
1671     {
1672       if (context->suggested_action & site->actions)
1673         action = context->suggested_action;
1674       else
1675         {
1676           gint i;
1677           
1678           for (i=0; i<8; i++)
1679             {
1680               if ((site->actions & (1 << i)) &&
1681                   (context->actions & (1 << i)))
1682                 {
1683                   action = (1 << i);
1684                   break;
1685                 }
1686             }
1687         }
1688       
1689       if (action && gtk_drag_dest_find_target (widget, context, NULL))
1690         {
1691           if (!site->have_drag)
1692             {
1693               site->have_drag = TRUE;
1694               if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1695                 gtk_drag_highlight (widget);
1696             }
1697           
1698           gdk_drag_status (context, action, time);
1699         }
1700       else
1701         {
1702           gdk_drag_status (context, 0, time);
1703           return TRUE;
1704         }
1705     }
1706
1707   g_signal_emit_by_name (widget, "drag_motion",
1708                          context, x, y, time, &retval);
1709
1710   return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1711 }
1712
1713 static gboolean
1714 gtk_drag_dest_drop (GtkWidget        *widget,
1715                     GdkDragContext   *context,
1716                     gint              x,
1717                     gint              y,
1718                     guint             time)
1719 {
1720   GtkDragDestSite *site;
1721   GtkDragDestInfo *info;
1722
1723   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1724   g_return_val_if_fail (site != NULL, FALSE);
1725
1726   info = gtk_drag_get_dest_info (context, FALSE);
1727   g_return_val_if_fail (info != NULL, FALSE);
1728
1729   info->drop_x = x;
1730   info->drop_y = y;
1731
1732   if (site->do_proxy)
1733     {
1734       if (info->proxy_source || 
1735           (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1736         {
1737           gtk_drag_drop (info->proxy_source, time);
1738         }
1739       else
1740         {
1741           /* We need to synthesize a motion event, wait for a status,
1742            * and, if we get a good one, do a drop.
1743            */
1744           
1745           GdkEvent *current_event;
1746           GdkAtom selection;
1747           GdkWindow *dest_window;
1748           GdkDragProtocol proto;
1749           
1750           gtk_drag_proxy_begin (widget, info, time);
1751           info->proxy_drop_wait = TRUE;
1752           info->proxy_drop_time = time;
1753           
1754           current_event = gtk_get_current_event ();
1755
1756           if (site->proxy_window)
1757             {
1758               dest_window = site->proxy_window;
1759               proto = site->proxy_protocol;
1760             }
1761           else
1762             {
1763               gdk_drag_find_window_for_screen (info->proxy_source->context,
1764                                                NULL,
1765                                                gdk_drawable_get_screen (current_event->dnd.window),
1766                                                current_event->dnd.x_root, 
1767                                                current_event->dnd.y_root,
1768                                                &dest_window, &proto);
1769             }
1770
1771           gdk_drag_motion (info->proxy_source->context, 
1772                            dest_window, proto,
1773                            current_event->dnd.x_root, 
1774                            current_event->dnd.y_root, 
1775                            context->suggested_action, 
1776                            context->actions, time);
1777
1778           if (!site->proxy_window && dest_window)
1779             g_object_unref (dest_window);
1780
1781           selection = gdk_drag_get_selection (info->proxy_source->context);
1782           if (selection && 
1783               selection != gdk_drag_get_selection (info->context))
1784             gtk_drag_source_check_selection (info->proxy_source, selection, time);
1785
1786           gdk_event_free (current_event);
1787         }
1788
1789       return TRUE;
1790     }
1791   else
1792     {
1793       gboolean retval;
1794
1795       if (site->flags & GTK_DEST_DEFAULT_DROP)
1796         {
1797           GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1798       
1799           if (target == GDK_NONE)
1800             {
1801               gtk_drag_finish (context, FALSE, FALSE, time);
1802               return TRUE;
1803             }
1804           else
1805             gtk_drag_get_data (widget, context, target, time);
1806         }
1807
1808       g_signal_emit_by_name (widget, "drag_drop",
1809                              context, x, y, time, &retval);
1810
1811       return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1812     }
1813 }
1814
1815 /***************
1816  * Source side *
1817  ***************/
1818
1819 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
1820  * so that we can set the icon from the source site information
1821  */
1822 static GdkDragContext *
1823 gtk_drag_begin_internal (GtkWidget         *widget,
1824                          GtkDragSourceSite *site,
1825                          GtkTargetList     *target_list,
1826                          GdkDragAction      actions,
1827                          gint               button,
1828                          GdkEvent          *event)
1829 {
1830   GtkDragSourceInfo *info;
1831   GList *targets = NULL;
1832   GList *tmp_list;
1833   guint32 time = GDK_CURRENT_TIME;
1834   GdkDragAction possible_actions, suggested_action;
1835   GdkDragContext *context;
1836   GtkWidget *ipc_widget;
1837   GdkCursor *cursor;
1838  
1839   ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1840   
1841   gtk_drag_get_event_actions (event, button, actions,
1842                               &suggested_action, &possible_actions);
1843   
1844   cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), suggested_action);
1845   
1846   if (event)
1847     time = gdk_event_get_time (event);
1848
1849   if (gdk_pointer_grab (ipc_widget->window, FALSE,
1850                         GDK_POINTER_MOTION_MASK |
1851                         GDK_BUTTON_RELEASE_MASK, NULL,
1852                         cursor, time) != 0)
1853     {
1854       gtk_drag_release_ipc_widget (ipc_widget);
1855       return NULL;
1856     }
1857
1858   if (gdk_keyboard_grab (ipc_widget->window, FALSE, time) != 0)
1859     {
1860       gtk_drag_release_ipc_widget (ipc_widget);
1861       return NULL;
1862     }
1863
1864   /* We use a GTK grab here to override any grabs that the widget
1865    * we are dragging from might have held
1866    */
1867   gtk_grab_add (ipc_widget);
1868   
1869   tmp_list = g_list_last (target_list->list);
1870   while (tmp_list)
1871     {
1872       GtkTargetPair *pair = tmp_list->data;
1873       targets = g_list_prepend (targets, 
1874                                 GINT_TO_POINTER (pair->target));
1875       tmp_list = tmp_list->prev;
1876     }
1877
1878   source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1879
1880   context = gdk_drag_begin (ipc_widget->window, targets);
1881   g_list_free (targets);
1882   
1883   info = gtk_drag_get_source_info (context, TRUE);
1884   
1885   info->ipc_widget = ipc_widget;
1886   g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", info);
1887
1888   info->widget = gtk_widget_ref (widget);
1889   
1890   info->button = button;
1891   info->cursor = cursor;
1892   info->target_list = target_list;
1893   gtk_target_list_ref (target_list);
1894
1895   info->possible_actions = actions;
1896
1897   info->status = GTK_DRAG_STATUS_DRAG;
1898   info->last_event = NULL;
1899   info->selections = NULL;
1900   info->icon_window = NULL;
1901   info->destroy_icon = FALSE;
1902
1903   /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1904    * the drag icon, it will be in the right place
1905    */
1906   if (event && event->type == GDK_MOTION_NOTIFY)
1907     {
1908       info->cur_screen = gtk_widget_get_screen (widget);
1909       info->cur_x = event->motion.x_root;
1910       info->cur_y = event->motion.y_root;
1911     }
1912   else 
1913     {
1914       gdk_display_get_pointer (gtk_widget_get_display (widget),
1915                                &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
1916     }
1917
1918   g_signal_emit_by_name (widget, "drag_begin",
1919                          info->context);
1920
1921   /* Ensure that we have an icon before we start the drag; the
1922    * application may have set one in ::drag_begin, or it may
1923    * not have set one.
1924    */
1925   if (!info->icon_window)
1926     {
1927       if (!site || site->icon_type == GTK_IMAGE_EMPTY)
1928         gtk_drag_set_icon_default (context);
1929       else
1930         switch (site->icon_type)
1931           {
1932           case GTK_IMAGE_PIXMAP:
1933             gtk_drag_set_icon_pixmap (context,
1934                                       site->colormap,
1935                                       site->icon_data.pixmap.pixmap,
1936                                       site->icon_mask,
1937                                       -2, -2);
1938             break;
1939           case GTK_IMAGE_PIXBUF:
1940             gtk_drag_set_icon_pixbuf (context,
1941                                       site->icon_data.pixbuf.pixbuf,
1942                                       -2, -2);
1943             break;
1944           case GTK_IMAGE_STOCK:
1945             gtk_drag_set_icon_stock (context,
1946                                      site->icon_data.stock.stock_id,
1947                                      -2, -2);
1948             break;
1949           case GTK_IMAGE_EMPTY:
1950           default:
1951             g_assert_not_reached();
1952             break;
1953           }
1954     }
1955               
1956   if (event && event->type == GDK_MOTION_NOTIFY)
1957     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1958
1959   info->start_x = info->cur_x;
1960   info->start_y = info->cur_y;
1961
1962   g_signal_connect (info->ipc_widget, "button_release_event",
1963                     G_CALLBACK (gtk_drag_button_release_cb), info);
1964   g_signal_connect (info->ipc_widget, "motion_notify_event",
1965                     G_CALLBACK (gtk_drag_motion_cb), info);
1966   g_signal_connect (info->ipc_widget, "key_press_event",
1967                     G_CALLBACK (gtk_drag_key_cb), info);
1968   g_signal_connect (info->ipc_widget, "key_release_event",
1969                     G_CALLBACK (gtk_drag_key_cb), info);
1970   g_signal_connect (info->ipc_widget, "selection_get",
1971                     G_CALLBACK (gtk_drag_selection_get), info);
1972
1973   info->have_grab = TRUE;
1974   info->grab_time = time;
1975
1976   return info->context;
1977 }
1978
1979 /**
1980  * gtk_drag_begin:
1981  * @widget: the source widget.
1982  * @targets: The targets (data formats) in which the
1983  *    source can provide the data.
1984  * @actions: A bitmask of the allowed drag actions for this drag.
1985  * @button: The button the user clicked to start the drag.
1986  * @event: The event that triggered the start of the drag.
1987  * 
1988  * Initiates a drag on the source side. The function
1989  * only needs to be used when the application is
1990  * starting drags itself, and is not needed when
1991  * gtk_drag_source_set() is used.
1992  * 
1993  * Return value: the context for this drag.
1994  **/
1995 GdkDragContext *
1996 gtk_drag_begin (GtkWidget         *widget,
1997                 GtkTargetList     *targets,
1998                 GdkDragAction      actions,
1999                 gint               button,
2000                 GdkEvent          *event)
2001 {
2002   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2003   g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
2004   g_return_val_if_fail (targets != NULL, NULL);
2005
2006   return gtk_drag_begin_internal (widget, NULL, targets,
2007                                   actions, button, event);
2008 }
2009
2010 /*************************************************************
2011  * gtk_drag_source_set:
2012  *     Register a drop site, and possibly add default behaviors.
2013  *   arguments:
2014  *     widget:
2015  *     start_button_mask: Mask of allowed buttons to start drag
2016  *     targets:           Table of targets for this source
2017  *     n_targets:
2018  *     actions:           Actions allowed for this source
2019  *   results:
2020  *************************************************************/
2021
2022 void 
2023 gtk_drag_source_set (GtkWidget            *widget,
2024                      GdkModifierType       start_button_mask,
2025                      const GtkTargetEntry *targets,
2026                      gint                  n_targets,
2027                      GdkDragAction         actions)
2028 {
2029   GtkDragSourceSite *site;
2030
2031   g_return_if_fail (GTK_IS_WIDGET (widget));
2032
2033   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2034
2035   gtk_widget_add_events (widget,
2036                          gtk_widget_get_events (widget) |
2037                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2038                          GDK_BUTTON_MOTION_MASK);
2039
2040   if (site)
2041     {
2042       if (site->target_list)
2043         gtk_target_list_unref (site->target_list);
2044     }
2045   else
2046     {
2047       site = g_new0 (GtkDragSourceSite, 1);
2048
2049       site->icon_type = GTK_IMAGE_EMPTY;
2050       
2051       g_signal_connect (widget, "button_press_event",
2052                         G_CALLBACK (gtk_drag_source_event_cb),
2053                         site);
2054       g_signal_connect (widget, "motion_notify_event",
2055                         G_CALLBACK (gtk_drag_source_event_cb),
2056                         site);
2057       
2058       g_object_set_data_full (G_OBJECT (widget),
2059                               "gtk-site-data", 
2060                               site, gtk_drag_source_site_destroy);
2061     }
2062
2063   site->start_button_mask = start_button_mask;
2064
2065   if (targets)
2066     site->target_list = gtk_target_list_new (targets, n_targets);
2067   else
2068     site->target_list = NULL;
2069
2070   site->actions = actions;
2071
2072 }
2073
2074 /*************************************************************
2075  * gtk_drag_source_unset
2076  *     Unregister this widget as a drag source.
2077  *   arguments:
2078  *     widget:
2079  *   results:
2080  *************************************************************/
2081
2082 void 
2083 gtk_drag_source_unset (GtkWidget        *widget)
2084 {
2085   GtkDragSourceSite *site;
2086
2087   g_return_if_fail (GTK_IS_WIDGET (widget));
2088
2089   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2090
2091   if (site)
2092     {
2093       g_signal_handlers_disconnect_by_func (widget,
2094                                             gtk_drag_source_event_cb,
2095                                             site);
2096       g_signal_handlers_disconnect_by_func (widget,
2097                                             gtk_drag_source_event_cb,
2098                                             site);
2099       g_object_set_data (G_OBJECT (widget), "gtk-site-data", NULL);
2100     }
2101 }
2102
2103 /**
2104  * gtk_drag_source_get_target_list:
2105  * @widget: a #GtkWidget
2106  *
2107  * Gets the list of targets this widget can provide for
2108  * drag-and-drop.
2109  *
2110  * Return value: the #GtkTargetList, or %NULL if none
2111  **/
2112 GtkTargetList *
2113 gtk_drag_source_get_target_list (GtkWidget *widget)
2114 {
2115   GtkDragSourceSite *site;
2116
2117   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2118
2119   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2120
2121   return site ? site->target_list : NULL;
2122 }
2123
2124 /**
2125  * gtk_drag_source_set_target_list:
2126  * @widget: a #GtkWidget that's a drag source
2127  * @target_list: list of draggable targets, or %NULL for none
2128  *
2129  * Changes the target types that this widget offers for drag-and-drop.
2130  * The widget must first be made into a drag source with
2131  * gtk_drag_source_set().
2132  **/
2133 void
2134 gtk_drag_source_set_target_list (GtkWidget     *widget,
2135                                  GtkTargetList *target_list)
2136 {
2137   GtkDragSourceSite *site;
2138
2139   g_return_if_fail (GTK_IS_WIDGET (widget));
2140
2141   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2142   if (site == NULL)
2143     {
2144       g_warning ("gtk_drag_source_set_target_list() requires the widget "
2145                  "to already be a drag source.");
2146       return;
2147     }
2148
2149   if (target_list)
2150     gtk_target_list_ref (target_list);
2151
2152   if (site->target_list)
2153     gtk_target_list_unref (site->target_list);
2154
2155   site->target_list = target_list;
2156 }
2157
2158 static void
2159 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2160 {
2161   switch (site->icon_type)
2162     {
2163     case GTK_IMAGE_EMPTY:
2164       break;
2165     case GTK_IMAGE_PIXMAP:
2166       if (site->icon_data.pixmap.pixmap)
2167         g_object_unref (site->icon_data.pixmap.pixmap);
2168       if (site->icon_mask)
2169         g_object_unref (site->icon_mask);
2170       break;
2171     case GTK_IMAGE_PIXBUF:
2172       g_object_unref (site->icon_data.pixbuf.pixbuf);
2173       break;
2174     case GTK_IMAGE_STOCK:
2175       g_free (site->icon_data.stock.stock_id);
2176       break;
2177     default:
2178       g_assert_not_reached();
2179       break;
2180     }
2181   site->icon_type = GTK_IMAGE_EMPTY;
2182   
2183   if (site->colormap)
2184     g_object_unref (site->colormap);
2185   site->colormap = NULL;
2186 }
2187
2188 /**
2189  * gtk_drag_source_set_icon:
2190  * @widget: a #GtkWidget
2191  * @colormap: the colormap of the icon
2192  * @pixmap: the image data for the icon
2193  * @mask: the transparency mask for an image.
2194  * 
2195  * Sets the icon that will be used for drags from a particular widget
2196  * from a pixmap/mask. GTK+ retains references for the arguments, and 
2197  * will release them when they are no longer needed.
2198  * Use gtk_drag_source_set_icon_pixbuf() instead.
2199  **/
2200 void 
2201 gtk_drag_source_set_icon (GtkWidget     *widget,
2202                           GdkColormap   *colormap,
2203                           GdkPixmap     *pixmap,
2204                           GdkBitmap     *mask)
2205 {
2206   GtkDragSourceSite *site;
2207
2208   g_return_if_fail (GTK_IS_WIDGET (widget));
2209   g_return_if_fail (GDK_IS_COLORMAP (colormap));
2210   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2211   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2212
2213   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2214   g_return_if_fail (site != NULL);
2215   
2216   g_object_ref (colormap);
2217   g_object_ref (pixmap);
2218   if (mask)
2219     g_object_ref (mask);
2220
2221   gtk_drag_source_unset_icon (site);
2222
2223   site->icon_type = GTK_IMAGE_PIXMAP;
2224   
2225   site->icon_data.pixmap.pixmap = pixmap;
2226   site->icon_mask = mask;
2227   site->colormap = colormap;
2228 }
2229
2230 /**
2231  * gtk_drag_source_set_icon_pixbuf:
2232  * @widget: a #GtkWidget
2233  * @pixbuf: the #GdkPixbuf for the drag icon
2234  * 
2235  * Sets the icon that will be used for drags from a particular widget
2236  * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will 
2237  * release it when it is no longer needed.
2238  **/
2239 void 
2240 gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
2241                                  GdkPixbuf   *pixbuf)
2242 {
2243   GtkDragSourceSite *site;
2244
2245   g_return_if_fail (GTK_IS_WIDGET (widget));
2246   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2247
2248   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2249   g_return_if_fail (site != NULL); 
2250   g_object_ref (pixbuf);
2251
2252   gtk_drag_source_unset_icon (site);
2253
2254   site->icon_type = GTK_IMAGE_PIXBUF;
2255   site->icon_data.pixbuf.pixbuf = pixbuf;
2256 }
2257
2258 /**
2259  * gtk_drag_source_set_icon_stock:
2260  * @widget: a #GtkWidget
2261  * @stock_id: the ID of the stock icon to use
2262  *
2263  * Sets the icon that will be used for drags from a particular source
2264  * to a stock icon. 
2265  **/
2266 void 
2267 gtk_drag_source_set_icon_stock (GtkWidget   *widget,
2268                                 const gchar *stock_id)
2269 {
2270   GtkDragSourceSite *site;
2271
2272   g_return_if_fail (GTK_IS_WIDGET (widget));
2273   g_return_if_fail (stock_id != NULL);
2274
2275   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2276   g_return_if_fail (site != NULL);
2277   
2278   gtk_drag_source_unset_icon (site);
2279
2280   site->icon_type = GTK_IMAGE_STOCK;
2281   site->icon_data.stock.stock_id = g_strdup (stock_id);
2282 }
2283
2284 static void
2285 gtk_drag_get_icon (GtkDragSourceInfo *info,
2286                    GtkWidget        **icon_window,
2287                    gint              *hot_x,
2288                    gint              *hot_y)
2289 {
2290   if (get_can_change_screen (info->icon_window))
2291     gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2292                            info->cur_screen);
2293       
2294   if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2295     {
2296       if (!info->fallback_icon)
2297         {
2298           gint save_hot_x, save_hot_y;
2299           gboolean save_destroy_icon;
2300           GtkWidget *save_icon_window;
2301           
2302           /* HACK to get the appropriate icon
2303            */
2304           save_icon_window = info->icon_window;
2305           save_hot_x = info->hot_x;
2306           save_hot_y = info->hot_x;
2307           save_destroy_icon = info->destroy_icon;
2308
2309           info->icon_window = NULL;
2310           gtk_drag_set_icon_default (info->context);
2311           info->fallback_icon = info->icon_window;
2312           
2313           info->icon_window = save_icon_window;
2314           info->hot_x = save_hot_x;
2315           info->hot_y = save_hot_y;
2316           info->destroy_icon = save_destroy_icon;
2317         }
2318       
2319       gtk_widget_hide (info->icon_window);
2320       
2321       *icon_window = info->fallback_icon;
2322       gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2323       
2324       if (!default_icon_pixmap)
2325         {
2326           *hot_x = -2;
2327           *hot_y = -2;
2328         }
2329       else
2330         {
2331           *hot_x = default_icon_hot_x;
2332           *hot_y = default_icon_hot_y;
2333         }
2334     }
2335   else
2336     {
2337       if (info->fallback_icon)
2338         gtk_widget_hide (info->fallback_icon);
2339       
2340       *icon_window = info->icon_window;
2341       *hot_x = info->hot_x;
2342       *hot_y = info->hot_y;
2343     }
2344 }
2345
2346 static void
2347 gtk_drag_update_icon (GtkDragSourceInfo *info)
2348 {
2349   if (info->icon_window)
2350     {
2351       GtkWidget *icon_window;
2352       gint hot_x, hot_y;
2353   
2354       gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
2355       
2356       gtk_window_move (GTK_WINDOW (icon_window), 
2357                        info->cur_x - hot_x, 
2358                        info->cur_y - hot_y);
2359       
2360       if (GTK_WIDGET_VISIBLE (icon_window))
2361         gdk_window_raise (icon_window->window);
2362       else
2363         gtk_widget_show (icon_window);
2364     }
2365 }
2366
2367 static void 
2368 gtk_drag_set_icon_window (GdkDragContext *context,
2369                           GtkWidget      *widget,
2370                           gint            hot_x,
2371                           gint            hot_y,
2372                           gboolean        destroy_on_release)
2373 {
2374   GtkDragSourceInfo *info;
2375
2376   info = gtk_drag_get_source_info (context, FALSE);
2377   gtk_drag_remove_icon (info);
2378
2379   if (widget)
2380     gtk_widget_ref (widget);
2381   
2382   info->icon_window = widget;
2383   info->hot_x = hot_x;
2384   info->hot_y = hot_y;
2385   info->destroy_icon = destroy_on_release;
2386  
2387   gtk_drag_update_icon (info);
2388 }
2389
2390 /**
2391  * gtk_drag_set_icon_widget:
2392  * @context: the context for a drag. (This must be called 
2393           with a  context for the source side of a drag)
2394  * @widget: a toplevel window to use as an icon.
2395  * @hot_x: the X offset within @widget of the hotspot.
2396  * @hot_y: the Y offset within @widget of the hotspot.
2397  * 
2398  * Changes the icon for a widget to a given widget. GTK+
2399  * will not destroy the icon, so if you don't want
2400  * it to persist, you should connect to the "drag_end" 
2401  * signal and destroy it yourself.
2402  **/
2403 void 
2404 gtk_drag_set_icon_widget (GdkDragContext    *context,
2405                           GtkWidget         *widget,
2406                           gint               hot_x,
2407                           gint               hot_y)
2408 {
2409   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2410   g_return_if_fail (context->is_source);
2411   g_return_if_fail (GTK_IS_WIDGET (widget));
2412
2413   gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2414 }
2415
2416 static void
2417 icon_window_realize (GtkWidget *window,
2418                      GdkPixbuf *pixbuf)
2419 {
2420   GdkPixmap *pixmap;
2421   GdkPixmap *mask;
2422   
2423   gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
2424                                                   gtk_widget_get_colormap (window),
2425                                                   &pixmap, &mask, 128);
2426   
2427   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2428   
2429   if (mask)
2430     gtk_widget_shape_combine_mask (window, mask, 0, 0);
2431
2432   g_object_unref (pixmap);
2433
2434   if (mask)
2435     g_object_unref (mask);
2436 }
2437
2438 static void
2439 set_icon_stock_pixbuf (GdkDragContext    *context,
2440                        const gchar       *stock_id,
2441                        GdkPixbuf         *pixbuf,
2442                        gint               hot_x,
2443                        gint               hot_y)
2444 {
2445   GtkWidget *window;
2446   gint width, height;
2447   GdkScreen *screen;
2448
2449   g_return_if_fail (context != NULL);
2450   g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2451   g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2452   
2453   screen = gdk_drawable_get_screen (context->source_window);
2454
2455   /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
2456   gtk_widget_push_colormap (NULL);
2457   window = gtk_window_new (GTK_WINDOW_POPUP);
2458   gtk_window_set_screen (GTK_WINDOW (window), screen);
2459   set_can_change_screen (window, TRUE);
2460   gtk_widget_pop_colormap ();
2461
2462   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2463   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2464   
2465   if (stock_id)
2466     {
2467       pixbuf = gtk_widget_render_icon (window, stock_id,
2468                                        GTK_ICON_SIZE_DND, NULL);
2469
2470       if (!pixbuf)
2471         {
2472           g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2473           gtk_widget_destroy (window);
2474           return;
2475         }
2476
2477     }
2478   else
2479     g_object_ref (pixbuf);
2480   
2481   width = gdk_pixbuf_get_width (pixbuf);
2482   height = gdk_pixbuf_get_width (pixbuf);
2483
2484   gtk_widget_set_size_request (window,
2485                                gdk_pixbuf_get_width (pixbuf),
2486                                gdk_pixbuf_get_height (pixbuf));
2487
2488   g_signal_connect_closure (window, "realize",
2489                             g_cclosure_new (G_CALLBACK (icon_window_realize),
2490                                             pixbuf,
2491                                             (GClosureNotify)g_object_unref),
2492                             FALSE);
2493                     
2494   gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2495 }
2496
2497 /**
2498  * gtk_drag_set_icon_pixbuf:
2499  * @context: the context for a drag. (This must be called 
2500  *            with a  context for the source side of a drag)
2501  * @pixbuf: the #GdkPixbuf to use as the drag icon.
2502  * @hot_x: the X offset within @widget of the hotspot.
2503  * @hot_y: the Y offset within @widget of the hotspot.
2504  * 
2505  * Sets @pixbuf as the icon for a given drag.
2506  **/
2507 void 
2508 gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
2509                            GdkPixbuf      *pixbuf,
2510                            gint            hot_x,
2511                            gint            hot_y)
2512 {
2513   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2514   g_return_if_fail (context->is_source);
2515   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2516   
2517   set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2518 }
2519
2520 /**
2521  * gtk_drag_set_icon_stock:
2522  * @context: the context for a drag. (This must be called 
2523  *            with a  context for the source side of a drag)
2524  * @stock_id: the ID of the stock icon to use for the drag.
2525  * @hot_x: the X offset within the icon of the hotspot.
2526  * @hot_y: the Y offset within the icon of the hotspot.
2527  * 
2528  * Sets the the icon for a given drag from a stock ID.
2529  **/
2530 void 
2531 gtk_drag_set_icon_stock  (GdkDragContext *context,
2532                           const gchar    *stock_id,
2533                           gint            hot_x,
2534                           gint            hot_y)
2535 {
2536   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2537   g_return_if_fail (context->is_source);
2538   g_return_if_fail (stock_id != NULL);
2539   
2540   set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2541 }
2542
2543 /**
2544  * gtk_drag_set_icon_pixmap:
2545  * @context: the context for a drag. (This must be called 
2546  *            with a  context for the source side of a drag)
2547  * @colormap: the colormap of the icon 
2548  * @pixmap: the image data for the icon 
2549  * @mask: the transparency mask for the icon
2550  * @hot_x: the X offset within @pixmap of the hotspot.
2551  * @hot_y: the Y offset within @pixmap of the hotspot.
2552  * 
2553  * Sets @pixmap as the icon for a given drag. GTK+ retains
2554  * references for the arguments, and will release them when
2555  * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2556  * will be more convenient to use.
2557  **/
2558 void 
2559 gtk_drag_set_icon_pixmap (GdkDragContext    *context,
2560                           GdkColormap       *colormap,
2561                           GdkPixmap         *pixmap,
2562                           GdkBitmap         *mask,
2563                           gint               hot_x,
2564                           gint               hot_y)
2565 {
2566   GtkWidget *window;
2567   GdkScreen *screen;
2568   gint width, height;
2569       
2570   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2571   g_return_if_fail (context->is_source);
2572   g_return_if_fail (GDK_IS_COLORMAP (colormap));
2573   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2574   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2575
2576   screen = gdk_colormap_get_screen (colormap);
2577   
2578   g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
2579   g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
2580   
2581   gdk_drawable_get_size (pixmap, &width, &height);
2582
2583   gtk_widget_push_colormap (colormap);
2584
2585   window = gtk_window_new (GTK_WINDOW_POPUP);
2586   gtk_window_set_screen (GTK_WINDOW (window), screen);
2587   set_can_change_screen (window, FALSE);
2588   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2589   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2590
2591   gtk_widget_pop_colormap ();
2592
2593   gtk_widget_set_size_request (window, width, height);
2594   gtk_widget_realize (window);
2595
2596   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2597   
2598   if (mask)
2599     gtk_widget_shape_combine_mask (window, mask, 0, 0);
2600
2601   gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2602 }
2603
2604 /**
2605  * gtk_drag_set_icon_default:
2606  * @context: the context for a drag. (This must be called 
2607              with a  context for the source side of a drag)
2608  * 
2609  * Sets the icon for a particular drag to the default
2610  * icon.
2611  **/
2612 void 
2613 gtk_drag_set_icon_default (GdkDragContext    *context)
2614 {
2615   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2616   g_return_if_fail (context->is_source);
2617
2618   if (!default_icon_pixmap)
2619     gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2620   else
2621     gtk_drag_set_icon_pixmap (context, 
2622                               default_icon_colormap, 
2623                               default_icon_pixmap, 
2624                               default_icon_mask,
2625                               default_icon_hot_x,
2626                               default_icon_hot_y);
2627 }
2628
2629 /**
2630  * gtk_drag_set_default_icon:
2631  * @colormap: the colormap of the icon
2632  * @pixmap: the image data for the icon
2633  * @mask: the transparency mask for an image.
2634  * @hot_x: The X offset within @widget of the hotspot.
2635  * @hot_y: The Y offset within @widget of the hotspot.
2636  * 
2637  * Changes the default drag icon. GTK+ retains references for the
2638  * arguments, and will release them when they are no longer needed.
2639  * This function is obsolete. The default icon should now be changed
2640  * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2641  **/
2642 void 
2643 gtk_drag_set_default_icon (GdkColormap   *colormap,
2644                            GdkPixmap     *pixmap,
2645                            GdkBitmap     *mask,
2646                            gint           hot_x,
2647                            gint           hot_y)
2648 {
2649   g_return_if_fail (GDK_IS_COLORMAP (colormap));
2650   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2651   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2652   
2653   if (default_icon_colormap)
2654     g_object_unref (default_icon_colormap);
2655   if (default_icon_pixmap)
2656     g_object_unref (default_icon_pixmap);
2657   if (default_icon_mask)
2658     g_object_unref (default_icon_mask);
2659
2660   default_icon_colormap = colormap;
2661   g_object_ref (colormap);
2662   
2663   default_icon_pixmap = pixmap;
2664   g_object_ref (pixmap);
2665
2666   default_icon_mask = mask;
2667   if (mask)
2668     g_object_ref (mask);
2669   
2670   default_icon_hot_x = hot_x;
2671   default_icon_hot_y = hot_y;
2672 }
2673
2674
2675 /*************************************************************
2676  * _gtk_drag_source_handle_event:
2677  *     Called from widget event handling code on Drag events
2678  *     for drag sources.
2679  *
2680  *   arguments:
2681  *     toplevel: Toplevel widget that received the event
2682  *     event:
2683  *   results:
2684  *************************************************************/
2685
2686 void
2687 _gtk_drag_source_handle_event (GtkWidget *widget,
2688                                GdkEvent  *event)
2689 {
2690   GtkDragSourceInfo *info;
2691   GdkDragContext *context;
2692
2693   g_return_if_fail (widget != NULL);
2694   g_return_if_fail (event != NULL);
2695
2696   context = event->dnd.context;
2697   info = gtk_drag_get_source_info (context, FALSE);
2698   if (!info)
2699     return;
2700
2701   switch (event->type)
2702     {
2703     case GDK_DRAG_STATUS:
2704       {
2705         GdkCursor *cursor;
2706
2707         if (info->proxy_dest)
2708           {
2709             if (!event->dnd.send_event)
2710               {
2711                 if (info->proxy_dest->proxy_drop_wait)
2712                   {
2713                     gboolean result = context->action != 0;
2714                     
2715                     /* Aha - we can finally pass the MOTIF DROP on... */
2716                     gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2717                     if (result)
2718                       gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2719                     else
2720                       gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2721                   }
2722                 else
2723                   {
2724                     gdk_drag_status (info->proxy_dest->context,
2725                                      event->dnd.context->action,
2726                                      event->dnd.time);
2727                   }
2728               }
2729           }
2730         else if (info->have_grab)
2731           {
2732             cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2733                                           event->dnd.context->action);
2734             if (info->cursor != cursor)
2735               {
2736                 gdk_pointer_grab (widget->window, FALSE,
2737                                   GDK_POINTER_MOTION_MASK |
2738                                   GDK_BUTTON_RELEASE_MASK,
2739                                   NULL,
2740                                   cursor, info->grab_time);
2741                 info->cursor = cursor;
2742               }
2743             
2744             if (info->last_event)
2745               gtk_drag_add_update_idle (info);
2746           }
2747       }
2748       break;
2749       
2750     case GDK_DROP_FINISHED:
2751       gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2752       break;
2753     default:
2754       g_assert_not_reached ();
2755     }
2756 }
2757
2758 /*************************************************************
2759  * gtk_drag_source_check_selection:
2760  *     Check if we've set up handlers/claimed the selection
2761  *     for a given drag. If not, add them.
2762  *   arguments:
2763  *     
2764  *   results:
2765  *************************************************************/
2766
2767 static void
2768 gtk_drag_source_check_selection (GtkDragSourceInfo *info, 
2769                                  GdkAtom            selection,
2770                                  guint32            time)
2771 {
2772   GList *tmp_list;
2773
2774   tmp_list = info->selections;
2775   while (tmp_list)
2776     {
2777       if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2778         return;
2779       tmp_list = tmp_list->next;
2780     }
2781
2782   gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2783                                        info->ipc_widget,
2784                                        selection,
2785                                        time);
2786   info->selections = g_list_prepend (info->selections,
2787                                      GUINT_TO_POINTER (selection));
2788
2789   tmp_list = info->target_list->list;
2790   while (tmp_list)
2791     {
2792       GtkTargetPair *pair = tmp_list->data;
2793
2794       gtk_selection_add_target (info->ipc_widget,
2795                                 selection,
2796                                 pair->target,
2797                                 pair->info);
2798       tmp_list = tmp_list->next;
2799     }
2800   
2801   if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2802     {
2803       gtk_selection_add_target (info->ipc_widget,
2804                                 selection,
2805                                 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2806                                 TARGET_MOTIF_SUCCESS);
2807       gtk_selection_add_target (info->ipc_widget,
2808                                 selection,
2809                                 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2810                                 TARGET_MOTIF_FAILURE);
2811     }
2812
2813   gtk_selection_add_target (info->ipc_widget,
2814                             selection,
2815                             gdk_atom_intern ("DELETE", FALSE),
2816                             TARGET_DELETE);
2817 }
2818
2819 /*************************************************************
2820  * gtk_drag_drop_finished:
2821  *     Clean up from the drag, and display snapback, if necessary.
2822  *   arguments:
2823  *     info:
2824  *     success:
2825  *     time:
2826  *   results:
2827  *************************************************************/
2828
2829 static void
2830 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2831                         gboolean           success,
2832                         guint              time)
2833 {
2834   gtk_drag_source_release_selections (info, time); 
2835
2836   if (info->proxy_dest)
2837     {
2838       /* The time from the event isn't reliable for Xdnd drags */
2839       gtk_drag_finish (info->proxy_dest->context, success, FALSE, 
2840                        info->proxy_dest->proxy_drop_time);
2841       gtk_drag_source_info_destroy (info);
2842     }
2843   else
2844     {
2845       if (success)
2846         {
2847           gtk_drag_source_info_destroy (info);
2848         }
2849       else
2850         {
2851           GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2852           anim->info = info;
2853           anim->step = 0;
2854           
2855           anim->n_steps = MAX (info->cur_x - info->start_x,
2856                                info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2857           anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2858
2859           info->cur_screen = gtk_widget_get_screen (info->widget);
2860           gtk_drag_update_icon (info);
2861           
2862           /* Mark the context as dead, so if the destination decides
2863            * to respond really late, we still are OK.
2864            */
2865           gtk_drag_clear_source_info (info->context);
2866           g_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2867         }
2868     }
2869 }
2870
2871 static void
2872 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2873                                     guint32            time)
2874 {
2875   GdkDisplay *display = gtk_widget_get_display (info->widget);
2876   GList *tmp_list = info->selections;
2877   
2878   while (tmp_list)
2879     {
2880       GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2881       if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2882         gtk_selection_owner_set_for_display (display, NULL, selection, time);
2883
2884       tmp_list = tmp_list->next;
2885     }
2886
2887   g_list_free (info->selections);
2888   info->selections = NULL;
2889 }
2890
2891 /*************************************************************
2892  * gtk_drag_drop:
2893  *     Send a drop event.
2894  *   arguments:
2895  *     
2896  *   results:
2897  *************************************************************/
2898
2899 static void
2900 gtk_drag_drop (GtkDragSourceInfo *info, 
2901                guint32            time)
2902 {
2903   if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2904     {
2905       GtkSelectionData selection_data;
2906       GList *tmp_list;
2907       /* GTK+ traditionally has used application/x-rootwin-drop, but the
2908        * XDND spec specifies x-rootwindow-drop.
2909        */
2910       GdkAtom target1 = gdk_atom_intern ("application/x-rootwindow-drop", FALSE);
2911       GdkAtom target2 = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2912       
2913       tmp_list = info->target_list->list;
2914       while (tmp_list)
2915         {
2916           GtkTargetPair *pair = tmp_list->data;
2917           
2918           if (pair->target == target1 || pair->target == target2)
2919             {
2920               selection_data.selection = GDK_NONE;
2921               selection_data.target = pair->target;
2922               selection_data.data = NULL;
2923               selection_data.length = -1;
2924               
2925               g_signal_emit_by_name (info->widget, "drag_data_get",
2926                                      info->context, &selection_data,
2927                                      pair->info,
2928                                      time);
2929               
2930               /* FIXME: Should we check for length >= 0 here? */
2931               gtk_drag_drop_finished (info, TRUE, time);
2932               return;
2933             }
2934           tmp_list = tmp_list->next;
2935         }
2936       gtk_drag_drop_finished (info, FALSE, time);
2937     }
2938   else
2939     {
2940       if (info->icon_window)
2941         gtk_widget_hide (info->icon_window);
2942         
2943       gdk_drag_drop (info->context, time);
2944       info->drop_timeout = g_timeout_add (DROP_ABORT_TIME,
2945                                           gtk_drag_abort_timeout,
2946                                           info);
2947     }
2948 }
2949
2950 /*
2951  * Source side callbacks.
2952  */
2953
2954 static gint
2955 gtk_drag_source_event_cb (GtkWidget      *widget,
2956                           GdkEvent       *event,
2957                           gpointer        data)
2958 {
2959   GtkDragSourceSite *site;
2960   gboolean retval = FALSE;
2961   site = (GtkDragSourceSite *)data;
2962
2963   switch (event->type)
2964     {
2965     case GDK_BUTTON_PRESS:
2966       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2967         {
2968           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2969           site->x = event->button.x;
2970           site->y = event->button.y;
2971         }
2972       break;
2973       
2974     case GDK_BUTTON_RELEASE:
2975       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2976         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2977       break;
2978       
2979     case GDK_MOTION_NOTIFY:
2980       if (site->state & event->motion.state & site->start_button_mask)
2981         {
2982           /* FIXME: This is really broken and can leave us
2983            * with a stuck grab
2984            */
2985           int i;
2986           for (i=1; i<6; i++)
2987             {
2988               if (site->state & event->motion.state & 
2989                   GDK_BUTTON1_MASK << (i - 1))
2990                 break;
2991             }
2992
2993           if (gtk_drag_check_threshold (widget, site->x, site->y,
2994                                         event->motion.x, event->motion.y))
2995             {
2996               GdkDragContext *context;
2997               
2998               site->state = 0;
2999               context = gtk_drag_begin_internal (widget, site, site->target_list,
3000                                                  site->actions, 
3001                                                  i, event);
3002
3003               retval = TRUE;
3004             }
3005         }
3006       break;
3007       
3008     default:                    /* hit for 2/3BUTTON_PRESS */
3009       break;
3010     }
3011   
3012   return retval;
3013 }
3014
3015 static void 
3016 gtk_drag_source_site_destroy (gpointer data)
3017 {
3018   GtkDragSourceSite *site = data;
3019
3020   if (site->target_list)
3021     gtk_target_list_unref (site->target_list);
3022
3023   gtk_drag_source_unset_icon (site);
3024   g_free (site);
3025 }
3026
3027 static void
3028 gtk_drag_selection_get (GtkWidget        *widget, 
3029                         GtkSelectionData *selection_data,
3030                         guint             sel_info,
3031                         guint32           time,
3032                         gpointer          data)
3033 {
3034   GtkDragSourceInfo *info = data;
3035   static GdkAtom null_atom = GDK_NONE;
3036   guint target_info;
3037
3038   if (!null_atom)
3039     null_atom = gdk_atom_intern ("NULL", FALSE);
3040
3041   switch (sel_info)
3042     {
3043     case TARGET_DELETE:
3044       g_signal_emit_by_name (info->widget,
3045                              "drag_data_delete", 
3046                              info->context);
3047       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3048       break;
3049     case TARGET_MOTIF_SUCCESS:
3050       gtk_drag_drop_finished (info, TRUE, time);
3051       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3052       break;
3053     case TARGET_MOTIF_FAILURE:
3054       gtk_drag_drop_finished (info, FALSE, time);
3055       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3056       break;
3057     default:
3058       if (info->proxy_dest)
3059         {
3060           /* This is sort of dangerous and needs to be thought
3061            * through better
3062            */
3063           info->proxy_dest->proxy_data = selection_data;
3064           gtk_drag_get_data (info->widget,
3065                              info->proxy_dest->context,
3066                              selection_data->target,
3067                              time);
3068           gtk_main ();
3069           info->proxy_dest->proxy_data = NULL;
3070         }
3071       else
3072         {
3073           if (gtk_target_list_find (info->target_list, 
3074                                     selection_data->target, 
3075                                     &target_info))
3076             {
3077               g_signal_emit_by_name (info->widget, "drag_data_get",
3078                                      info->context,
3079                                      selection_data,
3080                                      target_info,
3081                                      time);
3082             }
3083         }
3084       break;
3085     }
3086 }
3087
3088 static gint
3089 gtk_drag_anim_timeout (gpointer data)
3090 {
3091   GtkDragAnim *anim = data;
3092   gint x, y;
3093   gboolean retval;
3094
3095   GDK_THREADS_ENTER ();
3096
3097   if (anim->step == anim->n_steps)
3098     {
3099       gtk_drag_source_info_destroy (anim->info);
3100       g_free (anim);
3101
3102       retval = FALSE;
3103     }
3104   else
3105     {
3106       x = (anim->info->start_x * (anim->step + 1) +
3107            anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3108       y = (anim->info->start_y * (anim->step + 1) +
3109            anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3110       if (anim->info->icon_window)
3111         {
3112           GtkWidget *icon_window;
3113           gint hot_x, hot_y;
3114           
3115           gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);
3116           
3117           gtk_window_move (GTK_WINDOW (icon_window), 
3118                            x - hot_x, 
3119                            y - hot_y);
3120         }
3121   
3122       anim->step++;
3123
3124       retval = TRUE;
3125     }
3126
3127   GDK_THREADS_LEAVE ();
3128
3129   return retval;
3130 }
3131
3132 static void
3133 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3134 {
3135   if (info->icon_window)
3136     {
3137       gtk_widget_hide (info->icon_window);
3138       if (info->destroy_icon)
3139         gtk_widget_destroy (info->icon_window);
3140
3141       if (info->fallback_icon)
3142         {
3143           gtk_widget_destroy (info->fallback_icon);
3144           info->fallback_icon = NULL;
3145         }
3146
3147       g_object_unref (info->icon_window);
3148       info->icon_window = NULL;
3149     }
3150 }
3151
3152 static void
3153 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3154 {
3155   gtk_drag_remove_icon (info);
3156
3157   if (!info->proxy_dest)
3158     g_signal_emit_by_name (info->widget, "drag_end", 
3159                            info->context);
3160
3161   if (info->widget)
3162     g_object_unref (info->widget);
3163
3164
3165   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3166                                         gtk_drag_button_release_cb,
3167                                         info);
3168   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3169                                         gtk_drag_motion_cb,
3170                                         info);
3171   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3172                                         gtk_drag_key_cb,
3173                                         info);
3174   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3175                                         gtk_drag_selection_get,
3176                                         info);
3177
3178   gtk_selection_remove_all (info->ipc_widget);
3179   g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", NULL);
3180   source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3181   gtk_drag_release_ipc_widget (info->ipc_widget);
3182
3183   gtk_target_list_unref (info->target_list);
3184
3185   gtk_drag_clear_source_info (info->context);
3186   g_object_unref (info->context);
3187
3188   if (info->drop_timeout)
3189     g_source_remove (info->drop_timeout);
3190
3191   g_free (info);
3192 }
3193
3194 static gboolean
3195 gtk_drag_update_idle (gpointer data)
3196 {
3197   GtkDragSourceInfo *info = data;
3198   GdkWindow *dest_window;
3199   GdkDragProtocol protocol;
3200   GdkAtom selection;
3201
3202   GdkDragAction action;
3203   GdkDragAction possible_actions;
3204   guint32 time;
3205
3206   GDK_THREADS_ENTER ();
3207
3208   info->update_idle = 0;
3209     
3210   time = gtk_drag_get_event_time (info->last_event);
3211   gtk_drag_get_event_actions (info->last_event,
3212                               info->button, 
3213                               info->possible_actions,
3214                               &action, &possible_actions);
3215   gtk_drag_update_icon (info);
3216   gdk_drag_find_window_for_screen (info->context,
3217                                    info->icon_window ? info->icon_window->window : NULL,
3218                                    info->cur_screen, info->cur_x, info->cur_y,
3219                                    &dest_window, &protocol);
3220   
3221   if (!gdk_drag_motion (info->context, dest_window, protocol,
3222                         info->cur_x, info->cur_y, action, 
3223                         possible_actions,
3224                         time))
3225     {
3226       gdk_event_free ((GdkEvent *)info->last_event);
3227       info->last_event = NULL;
3228     }
3229   
3230   if (dest_window)
3231     g_object_unref (dest_window);
3232
3233   selection = gdk_drag_get_selection (info->context);
3234   if (selection)
3235     gtk_drag_source_check_selection (info, selection, time);
3236
3237   GDK_THREADS_LEAVE ();
3238
3239   return FALSE;
3240 }
3241
3242 static void
3243 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
3244 {
3245   /* We use an idle lowerthan GDK_PRIORITY_REDRAW so that exposes
3246    * from the last move can catch up before we move again.
3247    */
3248   if (!info->update_idle)
3249     info->update_idle = g_idle_add_full (GDK_PRIORITY_REDRAW + 5,
3250                                          gtk_drag_update_idle,
3251                                          info,
3252                                          NULL);
3253 }
3254
3255 /**
3256  * gtk_drag_update:
3257  * @info: DragSourceInfo for the drag
3258  * @screen: new screen
3259  * @x_root: new X position 
3260  * @y_root: new y position
3261  * @event: event received requiring update
3262  * 
3263  * Updates the status of the drag; called when the
3264  * cursor moves or the modifier changes
3265  **/
3266 static void
3267 gtk_drag_update (GtkDragSourceInfo *info,
3268                  GdkScreen         *screen,
3269                  gint               x_root,
3270                  gint               y_root,
3271                  GdkEvent          *event)
3272 {
3273   info->cur_screen = screen;
3274   info->cur_x = x_root;
3275   info->cur_y = y_root;
3276   if (info->last_event)
3277     gdk_event_free ((GdkEvent *)info->last_event);
3278   info->last_event = gdk_event_copy ((GdkEvent *)event);
3279
3280   gtk_drag_add_update_idle (info);
3281 }
3282
3283 /*************************************************************
3284  * gtk_drag_end:
3285  *     Called when the user finishes to drag, either by
3286  *     releasing the mouse, or by pressing Esc.
3287  *   arguments:
3288  *     info: Source info for the drag
3289  *     time: Timestamp for ending the drag
3290  *   results:
3291  *************************************************************/
3292
3293 static void
3294 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3295 {
3296   GdkEvent *send_event;
3297   GtkWidget *source_widget = info->widget;
3298   GdkDisplay *display = gtk_widget_get_display (source_widget);
3299
3300   if (info->update_idle)
3301     {
3302       g_source_remove (info->update_idle);
3303       info->update_idle = 0;
3304     }
3305   
3306   if (info->last_event)
3307     {
3308       gdk_event_free (info->last_event);
3309       info->last_event = NULL;
3310     }
3311   
3312   info->have_grab = FALSE;
3313   
3314   gdk_display_pointer_ungrab (display, time);
3315   gdk_display_keyboard_ungrab (display, time);
3316   gtk_grab_remove (info->ipc_widget);
3317
3318   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3319                                         gtk_drag_button_release_cb,
3320                                         info);
3321   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3322                                         gtk_drag_motion_cb,
3323                                         info);
3324   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3325                                         gtk_drag_key_cb,
3326                                         info);
3327
3328   /* Send on a release pair to the the original 
3329    * widget to convince it to release its grab. We need to
3330    * call gtk_propagate_event() here, instead of 
3331    * gtk_widget_event() because widget like GtkList may
3332    * expect propagation.
3333    */
3334
3335   send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3336   send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3337   send_event->button.send_event = TRUE;
3338   send_event->button.time = time;
3339   send_event->button.x = 0;
3340   send_event->button.y = 0;
3341   send_event->button.axes = NULL;
3342   send_event->button.state = 0;
3343   send_event->button.button = info->button;
3344   send_event->button.device = gdk_display_get_core_pointer (display);
3345   send_event->button.x_root = 0;
3346   send_event->button.y_root = 0;
3347
3348   gtk_propagate_event (source_widget, send_event);
3349   gdk_event_free (send_event);
3350 }
3351
3352 /*************************************************************
3353  * gtk_drag_cancel:
3354  *    Called on cancellation of a drag, either by the user
3355  *    or programmatically.
3356  *   arguments:
3357  *     info: Source info for the drag
3358  *     time: Timestamp for ending the drag
3359  *   results:
3360  *************************************************************/
3361
3362 static void
3363 gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
3364 {
3365   gtk_drag_end (info, time);
3366   gdk_drag_abort (info->context, time);
3367   gtk_drag_drop_finished (info, FALSE, time);
3368 }
3369
3370 /*************************************************************
3371  * gtk_drag_motion_cb:
3372  *     "motion_notify_event" callback during drag.
3373  *   arguments:
3374  *     
3375  *   results:
3376  *************************************************************/
3377
3378 static gint
3379 gtk_drag_motion_cb (GtkWidget      *widget, 
3380                     GdkEventMotion *event, 
3381                     gpointer        data)
3382 {
3383   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3384   GdkScreen *screen;
3385   gint x_root, y_root;
3386
3387   if (event->is_hint)
3388     {
3389       GdkDisplay *display = gtk_widget_get_display (widget);
3390       
3391       gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
3392       event->x_root = x_root;
3393       event->y_root = y_root;
3394     }
3395   else
3396     screen = gdk_event_get_screen ((GdkEvent *)event);
3397
3398   gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
3399
3400   return TRUE;
3401 }
3402
3403 /*************************************************************
3404  * gtk_drag_key_cb:
3405  *     "key_press/release_event" callback during drag.
3406  *   arguments:
3407  *     
3408  *   results:
3409  *************************************************************/
3410
3411 static gint 
3412 gtk_drag_key_cb (GtkWidget         *widget, 
3413                  GdkEventKey       *event, 
3414                  gpointer           data)
3415 {
3416   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3417   GdkModifierType state;
3418   GdkWindow *root_window;
3419   
3420   if (event->type == GDK_KEY_PRESS)
3421     {
3422       if (event->keyval == GDK_Escape)
3423         {
3424           gtk_drag_cancel (info, event->time);
3425
3426           return TRUE;
3427         }
3428     }
3429
3430   /* Now send a "motion" so that the modifier state is updated */
3431
3432   /* The state is not yet updated in the event, so we need
3433    * to query it here. We could use XGetModifierMapping, but
3434    * that would be overkill.
3435    */
3436   root_window = gtk_widget_get_root_window (widget);
3437   gdk_window_get_pointer (root_window, NULL, NULL, &state);
3438
3439   event->state = state;
3440   gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
3441
3442   return TRUE;
3443 }
3444
3445 /*************************************************************
3446  * gtk_drag_button_release_cb:
3447  *     "button_release_event" callback during drag.
3448  *   arguments:
3449  *     
3450  *   results:
3451  *************************************************************/
3452
3453 static gint
3454 gtk_drag_button_release_cb (GtkWidget      *widget, 
3455                             GdkEventButton *event, 
3456                             gpointer        data)
3457 {
3458   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3459
3460   if (event->button != info->button)
3461     return FALSE;
3462
3463   if ((info->context->action != 0) && (info->context->dest_window != NULL))
3464     {
3465       gtk_drag_end (info, event->time);
3466       gtk_drag_drop (info, event->time);
3467     }
3468   else
3469     {
3470       gtk_drag_cancel (info, event->time);
3471     }
3472
3473   return TRUE;
3474 }
3475
3476 static gint
3477 gtk_drag_abort_timeout (gpointer data)
3478 {
3479   GtkDragSourceInfo *info = data;
3480   guint32 time = GDK_CURRENT_TIME;
3481
3482   GDK_THREADS_ENTER ();
3483
3484   if (info->proxy_dest)
3485     time = info->proxy_dest->proxy_drop_time;
3486
3487   info->drop_timeout = 0;
3488   gtk_drag_drop_finished (info, FALSE, time);
3489   
3490   GDK_THREADS_LEAVE ();
3491   
3492   return FALSE;
3493 }
3494
3495 /**
3496  * gtk_drag_check_threshold:
3497  * @widget: a #GtkWidget
3498  * @start_x: X coordinate of start of drag
3499  * @start_y: Y coordinate of start of drag
3500  * @current_x: current X coordinate
3501  * @current_y: current Y coordinate
3502  * 
3503  * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3504  * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
3505  * should trigger the beginning of a drag-and-drop operation.
3506  *
3507  * Return Value: %TRUE if the drag threshold has been passed.
3508  **/
3509 gboolean
3510 gtk_drag_check_threshold (GtkWidget *widget,
3511                           gint       start_x,
3512                           gint       start_y,
3513                           gint       current_x,
3514                           gint       current_y)
3515 {
3516   gint drag_threshold;
3517
3518   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
3519
3520   g_object_get (gtk_widget_get_settings (widget),
3521                 "gtk-dnd-drag-threshold", &drag_threshold,
3522                 NULL);
3523   
3524   return (ABS (current_x - start_x) > drag_threshold ||
3525           ABS (current_y - start_y) > drag_threshold);
3526 }