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