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