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