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