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