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