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