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