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