]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd.c
Optimization - when we've already found a widget, skip all checks for
[~andy/gtk] / gtk / gtkdnd.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library 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 #include "gdk/gdkx.h"
21
22 #include "gtkdnd.h"
23 #include "gtkinvisible.h"
24 #include "gtkmain.h"
25 #include "gtksignal.h"
26 #include "gtkwindow.h"
27
28 static GSList *drag_widgets = NULL;
29 static GSList *source_widgets = NULL;
30
31 typedef struct _GtkDragSourceSite GtkDragSourceSite;
32 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
33 typedef struct _GtkDragDestSite GtkDragDestSite;
34 typedef struct _GtkDragDestInfo GtkDragDestInfo;
35 typedef struct _GtkDragAnim GtkDragAnim;
36 typedef struct _GtkDragFindData GtkDragFindData;
37
38
39 typedef enum {
40   GTK_DRAG_STATUS_DRAG,
41   GTK_DRAG_STATUS_WAIT,
42   GTK_DRAG_STATUS_DROP
43 } GtkDragStatus;
44
45 struct _GtkDragSourceSite {
46   GdkModifierType    start_button_mask;
47   GtkTargetList     *target_list;        /* Targets for drag data */
48   GdkDragAction      actions;            /* Possible actions */
49   GdkColormap       *colormap;           /* Colormap for drag icon */
50   GdkPixmap         *pixmap;             /* Icon for drag data */
51   GdkBitmap         *mask;
52
53   /* Stored button press information to detect drag beginning */
54   gint               state;
55   gint               x, y;
56 };
57   
58 struct _GtkDragSourceInfo {
59   GtkWidget         *widget;
60   GtkTargetList     *target_list; /* Targets for drag data */
61   GdkDragContext    *context;     /* drag context */
62   GtkWidget         *icon_window; /* Window for drag */
63   GtkWidget         *ipc_widget;  /* GtkInvisible for grab, message passing */
64   GdkCursor         *cursor;      /* Cursor for drag */
65   gint hot_x, hot_y;              /* Hot spot for drag */
66   gint button;                    /* mouse button starting drag */
67
68   GtkDragStatus      status;      /* drag status */
69   GdkEvent          *last_event;  /* motion event waiting for response */
70
71   gint               start_x, start_y; /* Initial position */
72   gint               cur_x, cur_y;     /* Current Position */
73
74   GList             *selections;  /* selections we've claimed */
75   
76   GtkDragDestInfo   *proxy_dest;  /* Set if this is a proxy drag */
77
78   guint              drop_timeout; /* Timeout for aborting drop */
79 };
80
81 struct _GtkDragDestSite {
82   GtkDestDefaults    flags;
83   GtkTargetList     *target_list;
84   GdkDragAction      actions;
85   GdkWindow         *proxy_window;
86   GdkDragProtocol    proxy_protocol;
87   gboolean           do_proxy : 1;
88   gboolean           proxy_coords : 1;
89   gboolean           have_drag : 1;
90 };
91   
92 struct _GtkDragDestInfo {
93   GtkWidget         *widget;       /* Widget in which drag is in */
94   GdkDragContext    *context;      /* Drag context */
95   GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
96   GtkSelectionData  *proxy_data;   /* Set while retrieving proxied data */
97   gboolean           dropped : 1;     /* Set after we receive a drop */
98   guint32            proxy_drop_time; /* Timestamp for proxied drop */
99   gboolean           proxy_drop_wait : 1; /* Set if we are waiting for a
100                                            * status reply before sending
101                                            * a proxied drop on.
102                                            */
103   gint               drop_x, drop_y; /* Position of drop */
104 };
105
106 #define DROP_ABORT_TIME 300000
107
108 #define ANIM_STEP_TIME 50
109 #define ANIM_STEP_LENGTH 50
110 #define ANIM_MIN_STEPS 5
111 #define ANIM_MAX_STEPS 10
112
113 struct _GtkDragAnim {
114   GtkDragSourceInfo *info;
115   gint step;
116   gint n_steps;
117 };
118
119 struct _GtkDragFindData {
120   gint x;
121   gint y;
122   GdkDragContext *context;
123   GtkDragDestInfo *info;
124   gboolean found;
125   gboolean toplevel;
126   gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
127                         gint x, gint y, guint32 time);
128   guint32 time;
129 };
130
131 /* Enumeration for some targets we handle internally */
132
133 enum {
134   TARGET_MOTIF_SUCCESS = 0x40000000,
135   TARGET_MOTIF_FAILURE,
136   TARGET_DELETE
137 };
138
139 /* Drag icons */
140
141 static GdkPixmap   *default_icon_pixmap = NULL;
142 static GdkPixmap   *default_icon_mask = NULL;
143 static GdkColormap *default_icon_colormap = NULL;
144 static gint         default_icon_hot_x;
145 static gint         default_icon_hot_y;
146
147 /* Forward declarations */
148 static GdkDragAction gtk_drag_get_event_action   (GdkEvent        *event, 
149                                                   gint             button,
150                                                   GdkDragAction    actions);
151 static GdkCursor *   gtk_drag_get_cursor         (GdkDragAction action);
152 static GtkWidget    *gtk_drag_get_ipc_widget     (void);
153 static void          gtk_drag_release_ipc_widget (GtkWidget *widget);
154
155 static GdkAtom   gtk_drag_dest_find_target    (GtkWidget          *widget,
156                                                GtkDragDestSite    *site,
157                                                GdkDragContext     *context);
158 static void      gtk_drag_selection_received  (GtkWidget          *widget,
159                                                GtkSelectionData   *selection_data,
160                                                guint32             time,
161                                                gpointer            data);
162 static void      gtk_drag_find_widget         (GtkWidget          *widget,
163                                                GtkDragFindData    *data);
164 static void      gtk_drag_proxy_begin         (GtkWidget          *widget,
165                                                GtkDragDestInfo    *dest_info);
166 static void      gtk_drag_dest_info_destroy   (gpointer            data);
167 static void      gtk_drag_dest_realized       (GtkWidget          *widget);
168 static void      gtk_drag_dest_site_destroy   (gpointer            data);
169 static void      gtk_drag_dest_leave          (GtkWidget          *widget,
170                                                GdkDragContext     *context,
171                                                guint               time);
172 static gboolean  gtk_drag_dest_motion         (GtkWidget          *widget,
173                                                GdkDragContext     *context,
174                                                gint                x,
175                                                gint                y,
176                                                guint               time);
177 static gboolean  gtk_drag_dest_drop           (GtkWidget          *widget,
178                                                GdkDragContext     *context,
179                                                gint                x,
180                                                gint                y,
181                                                guint               time);
182
183 static void gtk_drag_source_check_selection    (GtkDragSourceInfo *info, 
184                                                 GdkAtom            selection,
185                                                 guint32            time);
186 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
187                                                 guint32            time);
188 static void gtk_drag_drop                      (GtkDragSourceInfo *info,
189                                                 guint32            time);
190 static void gtk_drag_drop_finished             (GtkDragSourceInfo *info,
191                                                 gboolean           success,
192                                                 guint              time);
193
194 static gint gtk_drag_source_event_cb           (GtkWidget         *widget,
195                                                 GdkEvent          *event,
196                                                 gpointer           data);
197 static void gtk_drag_source_site_destroy       (gpointer           data);
198 static void gtk_drag_selection_get             (GtkWidget         *widget, 
199                                                 GtkSelectionData  *selection_data,
200                                                 guint              sel_info,
201                                                 guint32            time,
202                                                 gpointer           data);
203 static gint gtk_drag_anim_timeout              (gpointer           data);
204 static void gtk_drag_remove_icon               (GtkDragSourceInfo *info);
205 static void gtk_drag_source_info_destroy       (gpointer           data);
206 static gint gtk_drag_motion_cb                 (GtkWidget         *widget, 
207                                                 GdkEventMotion    *event, 
208                                                 gpointer           data);
209 static gint gtk_drag_button_release_cb         (GtkWidget         *widget, 
210                                                 GdkEventButton    *event, 
211                                                 gpointer           data);
212 static gint gtk_drag_abort_timeout             (gpointer           data);
213
214 /************************
215  * Cursor and Icon data *
216  ************************/
217
218 #define action_ask_width 16
219 #define action_ask_height 16
220 static const char action_ask_bits[] = {
221   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7, 
222   0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb, 
223   0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
224
225 #define action_ask_mask_width 16
226 #define action_ask_mask_height 16
227 static const char action_ask_mask_bits[] = {
228   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f, 
229   0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07, 
230   0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
231
232 #define action_copy_width 16
233 #define action_copy_height 16
234 static const char action_copy_bits[] = {
235   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb, 
236   0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb, 
237   0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
238
239 #define action_copy_mask_width 16
240 #define action_copy_mask_height 16
241 static const char action_copy_mask_bits[] = {
242   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07, 
243   0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07, 
244   0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
245
246 #define action_move_width 16
247 #define action_move_height 16
248 static const char action_move_bits[] = {
249   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff, 
250   0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb, 
251   0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
252
253 #define action_move_mask_width 16
254 #define action_move_mask_height 16
255 static const char action_move_mask_bits[] = {
256   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00, 
257   0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07, 
258   0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
259
260 #define action_link_width 16
261 #define action_link_height 16
262 static const char action_link_bits[] = {
263   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7, 
264   0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8, 
265   0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
266
267 #define action_link_mask_width 16
268 #define action_link_mask_height 16
269 static const char action_link_mask_bits[] = {
270   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f, 
271   0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77, 
272   0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
273
274 #define action_none_width 16
275 #define action_none_height 16
276 static const char action_none_bits[] = {
277   0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff, 
278   0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 
279   0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
280
281 #define action_none_mask_width 16
282 #define action_none_mask_height 16
283 static const char action_none_mask_bits[] = {
284   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, 
285   0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 
286   0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
287
288 #define CURSOR_WIDTH 16
289 #define CURSOR_HEIGHT 16
290
291 static struct {
292   GdkDragAction action;
293   const char *bits;
294   const char *mask;
295   GdkCursor    *cursor;
296 } drag_cursors[] = {
297   { GDK_ACTION_DEFAULT, 0 },
298   { GDK_ACTION_ASK,   action_ask_bits,  action_ask_mask_bits,  NULL },
299   { GDK_ACTION_COPY,  action_copy_bits, action_copy_mask_bits, NULL },
300   { GDK_ACTION_MOVE,  action_move_bits, action_move_mask_bits, NULL },
301   { GDK_ACTION_LINK,  action_link_bits, action_link_mask_bits, NULL },
302   { 0              ,  action_none_bits, action_none_mask_bits, NULL },
303 };
304
305 static const gint n_drag_cursors = sizeof(drag_cursors) / sizeof(drag_cursors[0]);
306
307 /* XPM */
308 static const char * drag_default_xpm[] = {
309 "32 32 3 1",
310 "       c None",
311 ".      c #000000",
312 "+      c #FFFFFF",
313 "                                ",
314 "                                ",
315 "                ..              ",
316 "              ..+.              ",
317 "             ..++..             ",
318 "           ...++++.             ",
319 "         ...++++++..            ",
320 "       ...+++++++++.            ",
321 "     ...+++++++++++..           ",
322 "    ..+.++++++++++++..          ",
323 "     .++.++++++++++++..         ",
324 "     .+++.++++++++++++..        ",
325 "     .++++.++++++++++++.        ",
326 "     .+++.+++++++++++++..       ",
327 "     .++.+++++++++++++++..      ",
328 "     .+.+++++++++++++++++..     ",
329 "     ..+++++++++++++++++++..    ",
330 "     ..++++++++++++++++++++.    ",
331 "     .++++++++++++++++++++..    ",
332 "     ..+++++++++++++++++..      ",
333 "      .++++++++++++++++..       ",
334 "      ..+++++++++++++...        ",
335 "       .++++++++++++..          ",
336 "       ..+++++++++..            ",
337 "        .++++++++..             ",
338 "        ..++++++..              ",
339 "         .+++++..               ",
340 "          .++..                 ",
341 "           ...                  ",
342 "           ..                   ",
343 "                                ",
344 "                                "};
345
346 /*********************
347  * Utility functions *
348  *********************/
349
350 /*************************************************************
351  * gtk_drag_get_ipc_widget:
352  *     Return a invisible, off-screen, override-redirect
353  *     widget for IPC.
354  *   arguments:
355  *     
356  *   results:
357  *************************************************************/
358
359 static GtkWidget *
360 gtk_drag_get_ipc_widget(void)
361 {
362   GtkWidget *result;
363   
364   if (drag_widgets)
365     {
366       GSList *tmp = drag_widgets;
367       result = drag_widgets->data;
368       drag_widgets = drag_widgets->next;
369       g_slist_free_1 (tmp);
370     }
371   else
372     {
373       result = gtk_invisible_new();
374       gtk_widget_show (result);
375     }
376
377   return result;
378 }
379
380 /*************************************************************
381  * gtk_drag_release_ipc_widget:
382  *     Releases widget retrieved with gtk_drag_get_ipc_widget()
383  *   arguments:
384  *     widget: the widget to release.
385  *   results:
386  *************************************************************/
387
388 static void
389 gtk_drag_release_ipc_widget (GtkWidget *widget)
390 {
391   drag_widgets = g_slist_prepend (drag_widgets, widget);
392 }
393
394 static GdkDragAction
395 gtk_drag_get_event_action (GdkEvent *event, gint button, GdkDragAction actions)
396 {
397   if (event)
398     {
399       GdkModifierType state = 0;
400       
401       switch (event->type)
402         {
403         case GDK_MOTION_NOTIFY:
404           state = event->motion.state;
405           break;
406         case GDK_BUTTON_PRESS:
407         case GDK_2BUTTON_PRESS:
408         case GDK_3BUTTON_PRESS:
409         case GDK_BUTTON_RELEASE:
410           state = event->button.state;
411           break;
412         case GDK_KEY_PRESS:
413         case GDK_KEY_RELEASE:
414           state = event->key.state;
415           break;
416       case GDK_ENTER_NOTIFY:
417       case GDK_LEAVE_NOTIFY:
418           state = event->crossing.state;
419           break;
420       default:
421         break;
422       }
423       
424       if (button == 3)
425         return GDK_ACTION_ASK;
426       
427       if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
428         {
429           if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
430             return (actions & GDK_ACTION_LINK) ? GDK_ACTION_LINK : 0;
431           else if (state & GDK_CONTROL_MASK)
432             return (actions & GDK_ACTION_COPY) ? GDK_ACTION_COPY : 0;
433           else
434             return (actions & GDK_ACTION_MOVE) ? GDK_ACTION_MOVE : 0;
435         }
436       else
437         {
438           if (actions & GDK_ACTION_COPY)
439             return GDK_ACTION_COPY;
440           else if (actions & GDK_ACTION_MOVE)
441             return GDK_ACTION_MOVE;
442           else if (actions & GDK_ACTION_LINK)
443             return GDK_ACTION_LINK;
444         }
445     }
446   
447   return 0;
448 }
449
450 static GdkCursor *
451 gtk_drag_get_cursor (GdkDragAction action)
452 {
453   gint i;
454   
455   for (i = 0 ; i < n_drag_cursors - 1; i++)
456     if (drag_cursors[i].action == action)
457       break;
458
459   if (drag_cursors[i].cursor == NULL)
460     {
461       GdkColor fg, bg;
462
463       GdkPixmap *pixmap = 
464         gdk_bitmap_create_from_data (NULL, 
465                                      drag_cursors[i].bits,
466                                      CURSOR_WIDTH, CURSOR_HEIGHT);
467       GdkPixmap *mask = 
468         gdk_bitmap_create_from_data (NULL, 
469                                      drag_cursors[i].mask,
470                                      CURSOR_WIDTH, CURSOR_HEIGHT);
471
472       gdk_color_white (gdk_colormap_get_system(), &bg);
473       gdk_color_black (gdk_colormap_get_system(), &fg);
474       
475       drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
476
477       gdk_pixmap_unref (pixmap);
478       gdk_pixmap_unref (mask);
479     }
480
481   return drag_cursors[i].cursor;
482 }
483
484 /********************
485  * Destination side *
486  ********************/
487
488 /*************************************************************
489  * gtk_drag_get_data:
490  *     Get the data for a drag or drop
491  *   arguments:
492  *     context - drag context
493  *     target  - format to retrieve the data in.
494  *     time    - timestamp of triggering event.
495  *     
496  *   results:
497  *************************************************************/
498
499 void 
500 gtk_drag_get_data (GtkWidget      *widget,
501                    GdkDragContext *context,
502                    GdkAtom         target,
503                    guint32         time)
504 {
505   GtkWidget *selection_widget;
506
507   g_return_if_fail (widget != NULL);
508   g_return_if_fail (context != NULL);
509
510   selection_widget = gtk_drag_get_ipc_widget();
511
512   gdk_drag_context_ref (context);
513   gtk_widget_ref (widget);
514   
515   gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
516                       GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
517
518   gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
519
520   gtk_selection_convert (selection_widget,
521                          gdk_drag_get_selection(context),
522                          target,
523                          time);
524 }
525
526
527 /*************************************************************
528  * gtk_drag_get_source_widget:
529  *     Get the widget the was the source of this drag, if
530  *     the drag originated from this application.
531  *   arguments:
532  *     context: The drag context for this drag
533  *   results:
534  *     The source widget, or NULL if the drag originated from
535  *     a different application.
536  *************************************************************/
537
538 GtkWidget *
539 gtk_drag_get_source_widget (GdkDragContext *context)
540 {
541   GSList *tmp_list;
542
543   tmp_list = source_widgets;
544   while (tmp_list)
545     {
546       GtkWidget *ipc_widget = tmp_list->data;
547       
548       if (ipc_widget->window == context->source_window)
549         {
550           GtkDragSourceInfo *info;
551           info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
552
553           return info ? info->widget : NULL;
554         }
555
556       tmp_list = tmp_list->next;
557     }
558
559   return NULL;
560 }
561
562 /*************************************************************
563  * gtk_drag_finish:
564  *     Notify the drag source that the transfer of data
565  *     is complete.
566  *   arguments:
567  *     context: The drag context for this drag
568  *     success: Was the data successfully transferred?
569  *     time:    The timestamp to use when notifying the destination.
570  *   results:
571  *************************************************************/
572
573 void 
574 gtk_drag_finish (GdkDragContext *context,
575                  gboolean        success,
576                  gboolean        del,
577                  guint32         time)
578 {
579   GdkAtom target = GDK_NONE;
580
581   g_return_if_fail (context != NULL);
582
583   if (success && del)
584     {
585       target = gdk_atom_intern ("DELETE", FALSE);
586     }
587   else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
588     {
589       target = gdk_atom_intern (success ? 
590                                   "XmTRANSFER_SUCCESS" : 
591                                   "XmTRANSFER_FAILURE",
592                                 FALSE);
593     }
594
595   if (target != GDK_NONE)
596     {
597       GtkWidget *selection_widget = gtk_drag_get_ipc_widget();
598
599       gdk_drag_context_ref (context);
600       
601       gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
602       gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
603                           GTK_SIGNAL_FUNC (gtk_drag_selection_received),
604                           NULL);
605       
606       gtk_selection_convert (selection_widget,
607                              gdk_drag_get_selection(context),
608                              target,
609                              time);
610     }
611   
612   if (!del)
613     gdk_drop_finish (context, success, time);
614 }
615
616 /*************************************************************
617  * gtk_drag_highlight:
618  *     Highlight the given widget in the default manner.
619  *   arguments:
620  *     widget:
621  *   results:
622  *************************************************************/
623
624 void 
625 gtk_drag_highlight (GtkWidget  *widget)
626 {
627   gint x, y;
628
629   g_return_if_fail (widget != NULL);
630
631   if (GTK_WIDGET_NO_WINDOW (widget))
632     {
633       x = widget->allocation.x;
634       y = widget->allocation.y;
635     }
636   else
637     {
638       x = 0;
639       y = 0;
640     }
641         
642   gtk_draw_shadow (widget->style, widget->window,
643                    GTK_STATE_NORMAL, GTK_SHADOW_OUT,
644                    x, y, 
645                    widget->allocation.width,
646                    widget->allocation.height);
647
648   gdk_draw_rectangle (widget->window,
649                       widget->style->black_gc,
650                       FALSE,
651                       x, y,
652                       widget->allocation.width - 1,
653                       widget->allocation.height - 1);
654 }
655
656 /*************************************************************
657  * gtk_drag_unhighlight:
658  *     Refresh the given widget to remove the highlight.
659  *   arguments:
660  *     widget:
661  *   results:
662  *************************************************************/
663
664 void 
665 gtk_drag_unhighlight (GtkWidget  *widget)
666 {
667   gint x, y;
668
669   g_return_if_fail (widget != NULL);
670
671   if (GTK_WIDGET_NO_WINDOW (widget))
672     {
673       x = widget->allocation.x;
674       y = widget->allocation.y;
675     }
676   else
677     {
678       x = 0;
679       y = 0;
680     }
681         
682   gdk_window_clear_area_e (widget->window,
683                            x, y,
684                            widget->allocation.width,
685                            widget->allocation.height);
686 }
687
688 /*************************************************************
689  * gtk_drag_dest_set:
690  *     Register a drop site, and possibly add default behaviors.
691  *   arguments:
692  *     widget:    
693  *     flags:     Which types of default drag behavior to use
694  *     targets:   Table of targets that can be accepted
695  *     n_targets: Number of of entries in targets
696  *     actions:   
697  *   results:
698  *************************************************************/
699
700 void 
701 gtk_drag_dest_set   (GtkWidget       *widget,
702                      GtkDestDefaults  flags,
703                      GtkTargetEntry  *targets,
704                      gint             n_targets,
705                      GdkDragAction    actions)
706 {
707   GtkDragDestSite *site;
708   
709   g_return_if_fail (widget != NULL);
710
711   /* HACK, do this in the destroy */
712   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
713   if (site)
714     gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
715
716   if (GTK_WIDGET_REALIZED (widget))
717     gtk_drag_dest_realized (widget);
718
719   gtk_signal_connect (GTK_OBJECT (widget), "realize",
720                       GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
721
722   site = g_new (GtkDragDestSite, 1);
723
724   site->flags = flags;
725   site->have_drag = FALSE;
726   if (targets)
727     site->target_list = gtk_target_list_new (targets, n_targets);
728   else
729     site->target_list = NULL;
730
731   site->actions = actions;
732   site->do_proxy = FALSE;
733
734   gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
735                             site, gtk_drag_dest_site_destroy);
736 }
737
738 /*************************************************************
739  * gtk_drag_dest_set_proxy:
740  *     Set up this widget to proxy drags elsewhere.
741  *   arguments:
742  *     widget:          
743  *     proxy_window:    window to which forward drag events
744  *     protocol:        Drag protocol which the dest widget accepts
745  *     use_coordinates: If true, send the same coordinates to the
746  *                      destination, because it is a embedded 
747  *                      subwindow.
748  *   results:
749  *************************************************************/
750
751 void 
752 gtk_drag_dest_set_proxy (GtkWidget      *widget,
753                          GdkWindow      *proxy_window,
754                          GdkDragProtocol protocol,
755                          gboolean        use_coordinates)
756 {
757   GtkDragDestSite *site;
758   
759   g_return_if_fail (widget != NULL);
760
761   /* HACK, do this in the destroy */
762   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
763   if (site)
764     gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
765
766   if (GTK_WIDGET_REALIZED (widget))
767     gtk_drag_dest_realized (widget);
768
769   gtk_signal_connect (GTK_OBJECT (widget), "realize",
770                       GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
771
772   site = g_new (GtkDragDestSite, 1);
773
774   site->flags = 0;
775   site->have_drag = FALSE;
776   site->target_list = NULL;
777   site->actions = 0;
778   site->proxy_window = proxy_window;
779   if (proxy_window)
780     gdk_window_ref (proxy_window);
781   site->do_proxy = TRUE;
782   site->proxy_protocol = protocol;
783   site->proxy_coords = use_coordinates;
784
785   gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
786                             site, gtk_drag_dest_site_destroy);
787 }
788
789 /*************************************************************
790  * gtk_drag_dest_unset
791  *     Unregister this widget as a drag target.
792  *   arguments:
793  *     widget:
794  *   results:
795  *************************************************************/
796
797 void 
798 gtk_drag_dest_unset (GtkWidget        *widget)
799 {
800   g_return_if_fail (widget != NULL);
801
802   gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
803 }
804
805 /*************************************************************
806  * gtk_drag_dest_handle_event:
807  *     Called from widget event handling code on Drag events
808  *     for destinations.
809  *
810  *   arguments:
811  *     toplevel: Toplevel widget that received the event
812  *     event:
813  *   results:
814  *************************************************************/
815
816 void
817 gtk_drag_dest_handle_event (GtkWidget *toplevel,
818                             GdkEvent  *event)
819 {
820   GtkDragDestInfo *info;
821   GdkDragContext *context;
822
823   g_return_if_fail (toplevel != NULL);
824   g_return_if_fail (event != NULL);
825
826   context = event->dnd.context;
827
828   info = g_dataset_get_data (context, "gtk-info");
829   if (!info)
830     {
831       info = g_new (GtkDragDestInfo, 1);
832       info->widget = NULL;
833       info->context = event->dnd.context;
834       info->proxy_source = NULL;
835       info->proxy_data = NULL;
836       info->dropped = FALSE;
837       info->proxy_drop_wait = FALSE;
838       g_dataset_set_data_full (context,
839                                "gtk-info",
840                                info,
841                                gtk_drag_dest_info_destroy);
842     }
843
844   /* Find the widget for the event */
845   switch (event->type)
846     {
847     case GDK_DRAG_ENTER:
848       break;
849       
850     case GDK_DRAG_LEAVE:
851       if (info->widget)
852         {
853           gtk_drag_dest_leave (info->widget, context, event->dnd.time);
854           info->widget = NULL;
855         }
856       break;
857       
858     case GDK_DRAG_MOTION:
859     case GDK_DROP_START:
860       {
861         GtkDragFindData data;
862         gint tx, ty;
863
864         if (event->type == GDK_DROP_START)
865           info->dropped = TRUE;
866
867         gdk_window_get_origin (toplevel->window, &tx, &ty);
868
869         data.x = event->dnd.x_root - tx;
870         data.y = event->dnd.y_root - ty;
871         data.context = context;
872         data.info = info;
873         data.found = FALSE;
874         data.toplevel = TRUE;
875         data.callback = (event->type == GDK_DRAG_MOTION) ?
876           gtk_drag_dest_motion : gtk_drag_dest_drop;
877         data.time = event->dnd.time;
878         
879         gtk_drag_find_widget (toplevel, &data);
880
881         /* We send a leave here so that the widget unhighlights
882          * properly
883          */
884         if (info->widget &&
885             ((event->type == GDK_DROP_START) || (!data.found)))
886           {
887             gtk_drag_dest_leave (info->widget, context, event->dnd.time);
888             info->widget = NULL;
889           }
890         
891         /* Send a reply.
892          */
893         if (event->type == GDK_DRAG_MOTION)
894           {
895             if (!data.found)
896               gdk_drag_status (context, 0, event->dnd.time);
897           }
898         else if (event->type == GDK_DROP_START)
899           gdk_drop_reply (context, data.found, event->dnd.time);
900       }
901       break;
902
903     default:
904       g_assert_not_reached();
905     }
906 }
907
908 /*************************************************************
909  * gtk_drag_dest_find_target:
910  *     Decide on a target for the drag.
911  *   arguments:
912  *     site:
913  *     context:
914  *   results:
915  *************************************************************/
916
917 static GdkAtom
918 gtk_drag_dest_find_target (GtkWidget       *widget,
919                            GtkDragDestSite *site,
920                            GdkDragContext  *context)
921 {
922   GList *tmp_target;
923   GList *tmp_source = NULL;
924   GtkWidget *source_widget = gtk_drag_get_source_widget (context);
925
926   tmp_target = site->target_list->list;
927   while (tmp_target)
928     {
929       GtkTargetPair *pair = tmp_target->data;
930       tmp_source = context->targets;
931       while (tmp_source)
932         {
933           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
934             {
935               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
936                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
937                 return pair->target;
938               else
939                 break;
940             }
941           tmp_source = tmp_source->next;
942         }
943       tmp_target = tmp_target->next;
944     }
945
946   return GDK_NONE;
947 }
948
949 static void
950 gtk_drag_selection_received (GtkWidget          *widget,
951                              GtkSelectionData   *selection_data,
952                              guint32             time,
953                              gpointer            data)
954 {
955   GdkDragContext *context;
956   GtkDragDestInfo *info;
957   GtkWidget *drop_widget;
958
959   drop_widget = data;
960
961   context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
962   info = g_dataset_get_data (context, "gtk-info");
963
964   if (info->proxy_data && 
965       info->proxy_data->target == selection_data->target)
966     {
967       gtk_selection_data_set (info->proxy_data,
968                               selection_data->type,
969                               selection_data->format,
970                               selection_data->data,
971                               selection_data->length);
972       gtk_main_quit();
973       return;
974     }
975
976   if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
977     {
978       gtk_drag_finish (context, TRUE, FALSE, time);
979     }
980   else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
981            (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
982     {
983       /* Do nothing */
984     }
985   else
986     {
987       GtkDragDestSite *site;
988
989       site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
990
991       if (site->target_list)
992         {
993           guint target_info;
994
995           if (gtk_target_list_find (site->target_list, 
996                                     selection_data->target,
997                                     &target_info))
998             {
999               if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1000                   selection_data->length >= 0)
1001                 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget), 
1002                                          "drag_data_received",
1003                                          context, info->drop_x, info->drop_y,
1004                                          selection_data, 
1005                                          target_info, time);
1006             }
1007         }
1008       else
1009         {
1010           gtk_signal_emit_by_name (GTK_OBJECT (drop_widget), 
1011                                    "drag_data_received",
1012                                    context, info->drop_x, info->drop_y,
1013                                    selection_data, 0, time);
1014         }
1015       
1016       if (site->flags & GTK_DEST_DEFAULT_DROP)
1017         {
1018
1019           gtk_drag_finish (context, 
1020                            (selection_data->length >= 0),
1021                            (context->action == GDK_ACTION_MOVE),
1022                            time);
1023         }
1024       
1025       gtk_widget_unref (drop_widget);
1026     }
1027
1028   gtk_signal_disconnect_by_func (GTK_OBJECT (widget), 
1029                                  GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1030                                  data);
1031   
1032   gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1033   gdk_drag_context_unref (context);
1034
1035   gtk_drag_release_ipc_widget (widget);
1036 }
1037
1038 /*************************************************************
1039  * gtk_drag_find_widget:
1040  *     Recursive callback used to locate widgets for 
1041  *     DRAG_MOTION and DROP_START events.
1042  *   arguments:
1043  *     
1044  *   results:
1045  *************************************************************/
1046
1047 static void
1048 gtk_drag_find_widget (GtkWidget       *widget,
1049                       GtkDragFindData *data)
1050 {
1051   GtkAllocation new_allocation;
1052   gint x_offset = 0;
1053   gint y_offset = 0;
1054
1055   new_allocation = widget->allocation;
1056
1057   if (data->found || !GTK_WIDGET_MAPPED (widget))
1058     return;
1059
1060   if (!GTK_WIDGET_NO_WINDOW (widget))
1061     {
1062       new_allocation.x = 0;
1063       new_allocation.y = 0;
1064     }
1065   
1066   if (widget->parent)
1067     {
1068       GdkWindow *window = widget->window;
1069       while (window != widget->parent->window)
1070         {
1071           gint tx, ty, twidth, theight;
1072           gdk_window_get_size (window, &twidth, &theight);
1073
1074           if (new_allocation.x < 0)
1075             {
1076               new_allocation.width += new_allocation.x;
1077               new_allocation.x = 0;
1078             }
1079           if (new_allocation.y < 0)
1080             {
1081               new_allocation.height += new_allocation.y;
1082               new_allocation.y = 0;
1083             }
1084           if (new_allocation.x + new_allocation.width > twidth)
1085             new_allocation.width = twidth - new_allocation.x;
1086           if (new_allocation.y + new_allocation.height > theight)
1087             new_allocation.height = theight - new_allocation.y;
1088
1089           gdk_window_get_position (window, &tx, &ty);
1090           new_allocation.x += tx;
1091           x_offset += tx;
1092           new_allocation.y += ty;
1093           y_offset += ty;
1094           
1095           window = gdk_window_get_parent (window);
1096         }
1097     }
1098
1099   if (data->toplevel ||
1100       ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1101        (data->x < new_allocation.x + new_allocation.width) && 
1102        (data->y < new_allocation.y + new_allocation.height)))
1103     {
1104       /* First, check if the drag is in a valid drop site in
1105        * one of our children 
1106        */
1107       if (GTK_IS_CONTAINER (widget))
1108         {
1109           GtkDragFindData new_data = *data;
1110           
1111           new_data.x -= x_offset;
1112           new_data.y -= y_offset;
1113           new_data.found = FALSE;
1114           new_data.toplevel = FALSE;
1115           
1116           gtk_container_forall (GTK_CONTAINER (widget),
1117                                 (GtkCallback)gtk_drag_find_widget,
1118                                 &new_data);
1119           
1120           data->found = new_data.found;
1121         }
1122
1123       /* If not, and this widget is registered as a drop site, check to
1124        * emit "drag_motion" to check if we are actually in
1125        * a drop site.
1126        */
1127       if (!data->found &&
1128           gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1129         {
1130           data->found = data->callback (widget,
1131                                         data->context,
1132                                         data->x - new_allocation.x,
1133                                         data->y - new_allocation.y,
1134                                         data->time);
1135           /* If so, send a "drag_leave" to the last widget */
1136           if (data->found)
1137             {
1138               if (data->info->widget && data->info->widget != widget)
1139                 {
1140                   gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1141                 }
1142               data->info->widget = widget;
1143             }
1144         }
1145     }
1146 }
1147
1148 static void
1149 gtk_drag_proxy_begin (GtkWidget *widget, GtkDragDestInfo *dest_info)
1150 {
1151   GtkDragSourceInfo *source_info;
1152   GList *tmp_list;
1153   
1154   source_info = g_new0 (GtkDragSourceInfo, 1);
1155   source_info->ipc_widget = gtk_drag_get_ipc_widget ();
1156   
1157   source_info->widget = widget;
1158   gtk_widget_ref (source_info->widget);
1159   source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
1160                                          dest_info->context->targets, 
1161                                          dest_info->context->actions);
1162
1163   source_info->target_list = gtk_target_list_new (NULL, 0);
1164   tmp_list = dest_info->context->targets;
1165   while (tmp_list)
1166     {
1167       gtk_target_list_add (source_info->target_list, 
1168                            GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1169       tmp_list = tmp_list->next;
1170     }
1171
1172   source_info->proxy_dest = dest_info;
1173   
1174   g_dataset_set_data (source_info->context, "gtk-info", source_info);
1175   
1176   gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget), 
1177                       "selection_get",
1178                       GTK_SIGNAL_FUNC (gtk_drag_selection_get), 
1179                       source_info);
1180   
1181   dest_info->proxy_source = source_info;
1182 }
1183
1184 static void
1185 gtk_drag_dest_info_destroy (gpointer data)
1186 {
1187   GtkDragDestInfo *info = data;
1188
1189   g_free (info);
1190 }
1191
1192 static void
1193 gtk_drag_dest_realized (GtkWidget *widget)
1194 {
1195   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1196   gdk_window_register_dnd (toplevel->window);
1197 }
1198
1199 static void
1200 gtk_drag_dest_site_destroy (gpointer data)
1201 {
1202   GtkDragDestSite *site = data;
1203
1204   if (site->target_list)
1205     gtk_target_list_unref (site->target_list);
1206
1207   g_free (site);
1208 }
1209
1210 /*
1211  * Default drag handlers
1212  */
1213 static void  
1214 gtk_drag_dest_leave (GtkWidget      *widget,
1215                      GdkDragContext *context,
1216                      guint           time)
1217 {
1218   GtkDragDestSite *site;
1219
1220   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1221   g_return_if_fail (site != NULL);
1222
1223   if (site->do_proxy)
1224     {
1225       GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1226
1227       if (info->proxy_source && !info->dropped)
1228         gdk_drag_abort (info->proxy_source->context, time);
1229       
1230       return;
1231     }
1232   else
1233     {
1234       if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1235         gtk_drag_unhighlight (widget);
1236
1237       if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1238         gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1239                                  context, time);
1240       
1241       site->have_drag = FALSE;
1242     }
1243 }
1244
1245 static gboolean
1246 gtk_drag_dest_motion (GtkWidget      *widget,
1247                       GdkDragContext *context,
1248                       gint            x,
1249                       gint            y,
1250                       guint           time)
1251 {
1252   GtkDragDestSite *site;
1253   GdkDragAction action = 0;
1254   gboolean retval;
1255
1256   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1257   g_return_val_if_fail (site != NULL, FALSE);
1258
1259   if (site->do_proxy)
1260     {
1261       GdkAtom selection;
1262       GdkEvent *current_event;
1263       GdkWindow *dest_window;
1264       GdkDragProtocol proto;
1265         
1266       GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1267
1268       if (!info->proxy_source)
1269         gtk_drag_proxy_begin (widget, info);
1270
1271       current_event = gtk_get_current_event ();
1272
1273       if (site->proxy_window)
1274         {
1275           dest_window = site->proxy_window;
1276           proto = site->proxy_protocol;
1277         }
1278       else
1279         {
1280           gdk_drag_find_window (info->proxy_source->context,
1281                                 NULL,
1282                                 current_event->dnd.x_root, 
1283                                 current_event->dnd.y_root,
1284                                 &dest_window, &proto);
1285         }
1286       
1287       gdk_drag_motion (info->proxy_source->context, 
1288                        dest_window, proto,
1289                        current_event->dnd.x_root, 
1290                        current_event->dnd.y_root, 
1291                        context->suggested_action, time);
1292
1293       selection = gdk_drag_get_selection (info->proxy_source->context);
1294       if (selection && 
1295           selection != gdk_drag_get_selection (info->context))
1296         gtk_drag_source_check_selection (info->proxy_source, selection, time);
1297
1298       gdk_event_free (current_event);
1299       
1300       return TRUE;
1301     }
1302
1303   if (site->flags & GTK_DEST_DEFAULT_MOTION)
1304     {
1305       if (context->suggested_action & site->actions)
1306         action = context->suggested_action;
1307       else
1308         {
1309           gint i;
1310           
1311           for (i=0; i<8; i++)
1312             {
1313               if ((site->actions & (1 << i)) &&
1314                   (context->actions & (1 << i)))
1315                 {
1316                   action = (1 << i);
1317                   break;
1318                 }
1319             }
1320         }
1321       
1322       if (action && gtk_drag_dest_find_target (widget, site, context))
1323         {
1324           if (!site->have_drag)
1325             {
1326               site->have_drag = TRUE;
1327               if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1328                 gtk_drag_highlight (widget);
1329             }
1330           
1331           gdk_drag_status (context, action, time);
1332         }
1333       else
1334         {
1335           gdk_drag_status (context, 0, time);
1336           return TRUE;
1337         }
1338     }
1339
1340   gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1341                            context, x, y, time, &retval);
1342
1343   return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1344 }
1345
1346 static gboolean
1347 gtk_drag_dest_drop (GtkWidget        *widget,
1348                     GdkDragContext   *context,
1349                     gint              x,
1350                     gint              y,
1351                     guint             time)
1352 {
1353   GtkDragDestSite *site;
1354   GtkDragDestInfo *info;
1355
1356   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1357   g_return_val_if_fail (site != NULL, FALSE);
1358
1359   info = g_dataset_get_data (context, "gtk-info");
1360   g_return_val_if_fail (info != NULL, FALSE);
1361
1362   info->drop_x = x;
1363   info->drop_y = y;
1364
1365   if (site->do_proxy)
1366     {
1367       if (info->proxy_source || 
1368           (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1369         {
1370           gtk_drag_drop (info->proxy_source, time);
1371         }
1372       else
1373         {
1374           /* We need to synthesize a motion event, wait for a status,
1375            * and, if we get one a good one, do a drop.
1376            */
1377           
1378           GdkEvent *current_event;
1379           GdkAtom selection;
1380           GdkWindow *dest_window;
1381           GdkDragProtocol proto;
1382           
1383           gtk_drag_proxy_begin (widget, info);
1384           info->proxy_drop_wait = TRUE;
1385           info->proxy_drop_time = time;
1386           
1387           current_event = gtk_get_current_event ();
1388
1389           if (site->proxy_window)
1390             {
1391               dest_window = site->proxy_window;
1392               proto = site->proxy_protocol;
1393             }
1394           else
1395             {
1396               gdk_drag_find_window (info->proxy_source->context,
1397                                     NULL,
1398                                     current_event->dnd.x_root, 
1399                                     current_event->dnd.y_root,
1400                                     &dest_window, &proto);
1401             }
1402             
1403           gdk_drag_motion (info->proxy_source->context, 
1404                            dest_window, proto,
1405                            current_event->dnd.x_root, 
1406                            current_event->dnd.y_root, 
1407                            context->suggested_action, time);
1408
1409           selection = gdk_drag_get_selection (info->proxy_source->context);
1410           if (selection && 
1411               selection != gdk_drag_get_selection (info->context))
1412             gtk_drag_source_check_selection (info->proxy_source, selection, time);
1413
1414           gdk_event_free (current_event);
1415       
1416         }
1417
1418       return TRUE;
1419     }
1420   else
1421     {
1422       gboolean retval;
1423
1424       if (site->flags & GTK_DEST_DEFAULT_MOTION)
1425         {
1426           GdkAtom target = gtk_drag_dest_find_target (widget, site, context);
1427       
1428           if (target == GDK_NONE)
1429             return FALSE;
1430           
1431           gtk_drag_get_data (widget, context, target, time);
1432         }
1433
1434       gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1435                                context, x, y, time, &retval);
1436
1437       return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1438     }
1439 }
1440
1441 /***************
1442  * Source side *
1443  ***************/
1444
1445 /*************************************************************
1446  * gtk_drag_begin: Start a drag operation
1447  *     
1448  *   arguments:
1449  *     widget:   Widget from which drag starts
1450  *     handlers: List of handlers to supply the data for the drag
1451  *     button:   Button user used to start drag
1452  *     time:     Time of event starting drag
1453  *
1454  *   results:
1455  *************************************************************/
1456
1457 GdkDragContext *
1458 gtk_drag_begin (GtkWidget         *widget,
1459                 GtkTargetList     *target_list,
1460                 GdkDragAction      actions,
1461                 gint               button,
1462                 GdkEvent          *event)
1463 {
1464   GtkDragSourceInfo *info;
1465   GList *targets = NULL;
1466   GList *tmp_list;
1467   guint32 time = GDK_CURRENT_TIME;
1468
1469   g_return_val_if_fail (widget != NULL, NULL);
1470   g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1471   g_return_val_if_fail (target_list != NULL, NULL);
1472
1473   if (event)
1474     time = gdk_event_get_time (event);
1475
1476   info = g_new0 (GtkDragSourceInfo, 1);
1477   info->ipc_widget = gtk_drag_get_ipc_widget ();
1478   source_widgets = g_slist_prepend (source_widgets, info->ipc_widget);
1479
1480   gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1481
1482   tmp_list = g_list_last (target_list->list);
1483   while (tmp_list)
1484     {
1485       GtkTargetPair *pair = tmp_list->data;
1486       targets = g_list_prepend (targets, 
1487                                 GINT_TO_POINTER (pair->target));
1488       tmp_list = tmp_list->prev;
1489     }
1490
1491   info->widget = widget;
1492   gtk_widget_ref (info->widget);
1493   
1494   info->context = gdk_drag_begin (info->ipc_widget->window,
1495                                   targets, actions);
1496   g_list_free (targets);
1497   
1498   g_dataset_set_data (info->context, "gtk-info", info);
1499
1500   info->button = button;
1501   info->target_list = target_list;
1502   gtk_target_list_ref (target_list);
1503
1504   info->cursor = NULL;
1505   info->status = GTK_DRAG_STATUS_DRAG;
1506   info->last_event = NULL;
1507   info->selections = NULL;
1508   info->icon_window = NULL;
1509   
1510   if (event)
1511     info->cursor = gtk_drag_get_cursor (
1512             gtk_drag_get_event_action (event, info->button, actions));
1513
1514   gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1515                            info->context);
1516   
1517   /* We use a GTK grab here to override any grabs that the widget
1518    * we are dragging from might have held
1519    */
1520
1521   gtk_grab_add (info->ipc_widget);
1522   gdk_pointer_grab (info->ipc_widget->window, FALSE,
1523                     GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1524                     GDK_BUTTON_RELEASE_MASK, NULL,
1525                     info->cursor, time);
1526
1527   if (event->type == GDK_MOTION_NOTIFY)
1528     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1529   else 
1530     {
1531       gint x, y;
1532       gdk_window_get_pointer (GDK_ROOT_PARENT(), &x, &y, NULL);
1533
1534       info->cur_x = x;
1535       info->cur_y = y;
1536
1537       if (info->icon_window)
1538         {
1539           gdk_window_raise (info->icon_window->window);
1540           gtk_widget_set_uposition (info->icon_window, x - info->hot_x, y - info->hot_y);
1541         }
1542     }
1543
1544   info->start_x = info->cur_x;
1545   info->start_y = info->cur_y;
1546
1547   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1548                       GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1549   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1550                       GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1551   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1552                       GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1553
1554   return info->context;
1555 }
1556
1557 /*************************************************************
1558  * gtk_drag_source_set:
1559  *     Register a drop site, and possibly add default behaviors.
1560  *   arguments:
1561  *     widget:
1562  *     start_button_mask: Mask of allowed buttons to start drag
1563  *     targets:           Table of targets for this source
1564  *     n_targets:
1565  *     actions:           Actions allowed for this source
1566  *   results:
1567  *************************************************************/
1568
1569 void 
1570 gtk_drag_source_set  (GtkWidget         *widget,
1571                       GdkModifierType    start_button_mask,
1572                       GtkTargetEntry    *targets,
1573                       gint               n_targets,
1574                       GdkDragAction      actions)
1575 {
1576   GtkDragSourceSite *site;
1577
1578   g_return_if_fail (widget != NULL);
1579
1580   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1581
1582   gtk_widget_add_events (widget,
1583                          gtk_widget_get_events (widget) |
1584                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1585                          GDK_BUTTON_MOTION_MASK);
1586
1587   if (site)
1588     {
1589       if (site->target_list)
1590         gtk_target_list_unref (site->target_list);
1591     }
1592   else
1593     {
1594       site = g_new0 (GtkDragSourceSite, 1);
1595       
1596       gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1597                           GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1598                           site);
1599       gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1600                           GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1601                           site);
1602       
1603       gtk_object_set_data_full (GTK_OBJECT (widget),
1604                                 "gtk-site-data", 
1605                                 site, gtk_drag_source_site_destroy);
1606     }
1607
1608   site->start_button_mask = start_button_mask;
1609
1610   if (targets)
1611     site->target_list = gtk_target_list_new (targets, n_targets);
1612   else
1613     site->target_list = NULL;
1614
1615   site->actions = actions;
1616
1617 }
1618
1619 /*************************************************************
1620  * gtk_drag_source_unset
1621  *     Unregister this widget as a drag source.
1622  *   arguments:
1623  *     widget:
1624  *   results:
1625  *************************************************************/
1626
1627 void 
1628 gtk_drag_source_unset (GtkWidget        *widget)
1629 {
1630   GtkDragSourceSite *site;
1631
1632   g_return_if_fail (widget != NULL);
1633
1634   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1635
1636   if (site)
1637     {
1638       gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1639       gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1640     }
1641 }
1642
1643 /*************************************************************
1644  * gtk_drag_source_set_icon:
1645  *     Set an icon for drags from this source.
1646  *   arguments:
1647  *     colormap: Colormap for this icon
1648  *     pixmap:
1649  *     mask
1650  *   results:
1651  *************************************************************/
1652
1653 void 
1654 gtk_drag_source_set_icon  (GtkWidget     *widget,
1655                            GdkColormap   *colormap,
1656                            GdkPixmap     *pixmap,
1657                            GdkBitmap     *mask)
1658 {
1659   GtkDragSourceSite *site;
1660
1661   g_return_if_fail (widget != NULL);
1662
1663   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1664   g_return_if_fail (site != NULL);
1665   
1666   if (site->colormap)
1667     gdk_colormap_unref (site->colormap);
1668   if (site->pixmap)
1669     gdk_pixmap_unref (site->pixmap);
1670   if (site->mask)
1671     gdk_pixmap_unref (site->mask);
1672
1673   site->colormap = colormap;
1674   if (colormap)
1675     gdk_colormap_ref (colormap);
1676
1677   site->pixmap = pixmap;
1678   if (pixmap)
1679     gdk_pixmap_ref (pixmap);
1680
1681   site->mask = mask;
1682   if (mask)
1683     gdk_pixmap_ref (mask);
1684 }
1685
1686 /*************************************************************
1687  * gtk_drag_set_icon_widget:
1688  *     Set a widget as the icon for a drag.
1689  *   arguments:
1690  *     context:
1691  *     widget:
1692  *     hot_x:    Hot spot
1693  *     hot_y:
1694  *   results:
1695  *************************************************************/
1696
1697 void 
1698 gtk_drag_set_icon_widget  (GdkDragContext    *context,
1699                            GtkWidget         *widget,
1700                            gint               hot_x,
1701                            gint               hot_y)
1702 {
1703   GtkDragSourceInfo *info;
1704
1705   g_return_if_fail (context != NULL);
1706   g_return_if_fail (widget != NULL);
1707
1708   info = g_dataset_get_data (context, "gtk-info");
1709   gtk_drag_remove_icon (info);
1710   
1711   info->icon_window = widget;
1712   if (widget)
1713     {
1714       gtk_widget_set_uposition (widget, info->cur_x, info->cur_y);      
1715       gtk_widget_ref (widget);
1716       gdk_window_raise (widget->window);
1717       gtk_widget_show (widget);
1718     }
1719
1720   info->hot_x = hot_x;
1721   info->hot_y = hot_y;
1722 }
1723
1724 /*************************************************************
1725  * gtk_drag_set_icon_pixmap:
1726  *     Set a widget as the icon for a drag.
1727  *   arguments:
1728  *     context:
1729  *     colormap: Colormap for the icon window.
1730  *     pixmap:   
1731  *     mask:
1732  *     hot_x:    Hot spot
1733  *     hot_y:
1734  *   results:
1735  *************************************************************/
1736
1737 void 
1738 gtk_drag_set_icon_pixmap  (GdkDragContext    *context,
1739                            GdkColormap       *colormap,
1740                            GdkPixmap         *pixmap,
1741                            GdkBitmap         *mask,
1742                            gint               hot_x,
1743                            gint               hot_y)
1744 {
1745   GtkWidget *window;
1746   gint width, height;
1747       
1748   g_return_if_fail (context != NULL);
1749   g_return_if_fail (colormap != NULL);
1750   g_return_if_fail (pixmap != NULL);
1751
1752   gdk_window_get_size (pixmap, &width, &height);
1753
1754   gtk_widget_push_visual (gdk_colormap_get_visual(colormap));
1755   gtk_widget_push_colormap (colormap);
1756
1757   window = gtk_window_new (GTK_WINDOW_POPUP);
1758   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
1759
1760   gtk_widget_pop_visual ();
1761   gtk_widget_pop_colormap ();
1762
1763   gtk_widget_set_usize (window, width, height);
1764   gtk_widget_realize (window);
1765
1766   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
1767   
1768   if (mask)
1769     gtk_widget_shape_combine_mask (window, mask, 0, 0);
1770
1771   gtk_drag_set_icon_widget (context, window, hot_x, hot_y);
1772 }
1773
1774 /*************************************************************
1775  * gtk_drag_set_icon_default:
1776  *     Set the icon for a drag to the default icon.
1777  *   arguments:
1778  *     context:
1779  *   results:
1780  *************************************************************/
1781
1782 void 
1783 gtk_drag_set_icon_default (GdkDragContext    *context)
1784 {
1785   g_return_if_fail (context != NULL);
1786
1787   if (!default_icon_pixmap)
1788     {
1789       default_icon_colormap = gdk_colormap_get_system ();
1790       default_icon_pixmap = 
1791         gdk_pixmap_colormap_create_from_xpm_d (NULL,
1792                                                default_icon_colormap,
1793                                                &default_icon_mask,
1794                                                NULL, drag_default_xpm);
1795       default_icon_hot_x = -2;
1796       default_icon_hot_y = -2;
1797     }
1798
1799   gtk_drag_set_icon_pixmap (context, 
1800                             default_icon_colormap, 
1801                             default_icon_pixmap, 
1802                             default_icon_mask,
1803                             default_icon_hot_x,
1804                             default_icon_hot_y);
1805 }
1806
1807 /*************************************************************
1808  * gtk_drag_set_default_icon:
1809  *     Set a default icon for all drags as a pixmap.
1810  *   arguments:
1811  *     colormap: Colormap for the icon window.
1812  *     pixmap:   
1813  *     mask:
1814  *     hot_x:    Hot spot
1815  *     hot_y:
1816  *   results:
1817  *************************************************************/
1818
1819 void 
1820 gtk_drag_set_default_icon (GdkColormap   *colormap,
1821                            GdkPixmap     *pixmap,
1822                            GdkBitmap     *mask,
1823                            gint           hot_x,
1824                            gint           hot_y)
1825 {
1826   g_return_if_fail (colormap != NULL);
1827   g_return_if_fail (pixmap != NULL);
1828   
1829   if (default_icon_colormap)
1830     gdk_colormap_unref (default_icon_colormap);
1831   if (default_icon_pixmap)
1832     gdk_pixmap_unref (default_icon_pixmap);
1833   if (default_icon_mask)
1834     gdk_pixmap_unref (default_icon_pixmap);
1835
1836   default_icon_colormap = colormap;
1837   gdk_colormap_ref (colormap);
1838   
1839   default_icon_pixmap = pixmap;
1840   gdk_pixmap_ref (pixmap);
1841
1842   default_icon_mask = mask;
1843   if (mask)
1844     gdk_pixmap_ref (mask);
1845   
1846   default_icon_hot_x = hot_x;
1847   default_icon_hot_y = hot_y;
1848 }
1849
1850
1851 /*************************************************************
1852  * gtk_drag_source_handle_event:
1853  *     Called from widget event handling code on Drag events
1854  *     for drag sources.
1855  *
1856  *   arguments:
1857  *     toplevel: Toplevel widget that received the event
1858  *     event:
1859  *   results:
1860  *************************************************************/
1861
1862 void
1863 gtk_drag_source_handle_event (GtkWidget *widget,
1864                               GdkEvent  *event)
1865 {
1866   GtkDragSourceInfo *info;
1867   GdkDragContext *context;
1868
1869   g_return_if_fail (widget != NULL);
1870   g_return_if_fail (event != NULL);
1871
1872   context = event->dnd.context;
1873   info = g_dataset_get_data (context, "gtk-info");
1874   if (!info)
1875     return;
1876
1877   switch (event->type)
1878     {
1879     case GDK_DRAG_STATUS:
1880       {
1881         GdkCursor *cursor;
1882
1883         if (info->proxy_dest)
1884           {
1885             if (!event->dnd.send_event)
1886               {
1887                 if (info->proxy_dest->proxy_drop_wait)
1888                   {
1889                     /* Aha - we can finally pass the MOTIF DROP on... */
1890                     gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
1891                   }
1892                 else
1893                   {
1894                     gdk_drag_status (info->proxy_dest->context,
1895                                      event->dnd.context->action,
1896                                      event->dnd.time);
1897                   }
1898               }
1899           }
1900         else
1901           {
1902             cursor = gtk_drag_get_cursor (event->dnd.context->action);
1903             if (info->cursor != cursor)
1904               {
1905                 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window), 
1906                                           PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
1907                                           ((GdkCursorPrivate *)cursor)->xcursor,
1908                                           event->dnd.time);
1909                 info->cursor = cursor;
1910               }
1911             
1912             if (info->last_event)
1913               {
1914                 gtk_drag_motion_cb (info->widget, 
1915                                     (GdkEventMotion *)info->last_event, 
1916                                     info);
1917                 info->last_event = NULL;
1918               }
1919           }
1920       }
1921       break;
1922       
1923     case GDK_DROP_FINISHED:
1924       gtk_drag_drop_finished (info, TRUE, event->dnd.time);
1925       break;
1926     default:
1927       g_assert_not_reached();
1928     }
1929 }
1930
1931 /*************************************************************
1932  * gtk_drag_source_check_selection:
1933  *     Check if we've set up handlers/claimed the selection
1934  *     for a given drag. If not, add them.
1935  *   arguments:
1936  *     
1937  *   results:
1938  *************************************************************/
1939
1940 static void
1941 gtk_drag_source_check_selection (GtkDragSourceInfo *info, 
1942                                  GdkAtom            selection,
1943                                  guint32            time)
1944 {
1945   GList *tmp_list;
1946
1947   tmp_list = info->selections;
1948   while (tmp_list)
1949     {
1950       if (GPOINTER_TO_UINT (tmp_list->data) == selection)
1951         return;
1952       tmp_list = tmp_list->next;
1953     }
1954
1955   gtk_selection_owner_set (info->ipc_widget, selection, time);
1956   info->selections = g_list_prepend (info->selections,
1957                                      GUINT_TO_POINTER (selection));
1958
1959   tmp_list = info->target_list->list;
1960   while (tmp_list)
1961     {
1962       GtkTargetPair *pair = tmp_list->data;
1963
1964       gtk_selection_add_target (info->ipc_widget,
1965                                 selection,
1966                                 pair->target,
1967                                 pair->info);
1968       tmp_list = tmp_list->next;
1969     }
1970   
1971   if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
1972     {
1973       gtk_selection_add_target (info->ipc_widget,
1974                                 selection,
1975                                 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
1976                                 TARGET_MOTIF_SUCCESS);
1977       gtk_selection_add_target (info->ipc_widget,
1978                                 selection,
1979                                 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
1980                                 TARGET_MOTIF_FAILURE);
1981     }
1982
1983   gtk_selection_add_target (info->ipc_widget,
1984                             selection,
1985                             gdk_atom_intern ("DELETE", FALSE),
1986                             TARGET_DELETE);
1987 }
1988
1989 /*************************************************************
1990  * gtk_drag_drop_finished:
1991  *     Clean up from the drag, and display snapback, if necessary.
1992  *   arguments:
1993  *     info:
1994  *     success:
1995  *     time:
1996  *   results:
1997  *************************************************************/
1998
1999 static void
2000 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2001                         gboolean           success,
2002                         guint              time)
2003 {
2004   if (info->proxy_dest)
2005     {
2006       /* The time from the event isn't reliable for Xdnd drags */
2007       gtk_drag_finish (info->proxy_dest->context, success, FALSE, 
2008                        info->proxy_dest->proxy_drop_time);
2009     }
2010   else
2011     {
2012       if (success)
2013         {
2014           gtk_drag_source_info_destroy (info);
2015         }
2016       else
2017         {
2018           GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2019           anim->info = info;
2020           anim->step = 0;
2021           
2022           anim->n_steps = MAX (info->cur_x - info->start_x,
2023                                info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2024           anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2025           if (info->icon_window)
2026             {
2027               gtk_widget_show(info->icon_window);
2028               gdk_window_raise (info->icon_window->window);
2029             }
2030           
2031           /* Mark the context as dead, so if the destination decides
2032            * to respond really late, we still are OK.
2033            */
2034           g_dataset_set_data (info->context, "gtk-info", NULL);
2035           gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2036         }
2037     }
2038
2039   gtk_drag_source_release_selections (info, GDK_CURRENT_TIME); /* fixme */
2040 }
2041
2042 static void
2043 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2044                                     guint32            time)
2045 {
2046   GList *tmp_list = info->selections;
2047   while (tmp_list)
2048     {
2049       GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2050       if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2051         gtk_selection_owner_set (NULL, selection, time);
2052       tmp_list = tmp_list->next;
2053     }
2054
2055   g_list_free (info->selections);
2056   info->selections = NULL;
2057 }
2058
2059 /*************************************************************
2060  * gtk_drag_drop:
2061  *     Send a drop event.
2062  *   arguments:
2063  *     
2064  *   results:
2065  *************************************************************/
2066
2067 static void
2068 gtk_drag_drop (GtkDragSourceInfo *info, guint32 time)
2069 {
2070   if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2071     {
2072       GtkSelectionData selection_data;
2073       GList *tmp_list;
2074       GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2075       
2076       tmp_list = info->target_list->list;
2077       while (tmp_list)
2078         {
2079           GtkTargetPair *pair = tmp_list->data;
2080           
2081           if (pair->target == target)
2082             {
2083               selection_data.selection = GDK_NONE;
2084               selection_data.target = target;
2085               selection_data.data = NULL;
2086               selection_data.length = -1;
2087               
2088               gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2089                                        info->context, &selection_data,
2090                                        pair->info, 
2091                                        time);
2092               
2093               /* FIXME: Should we check for length >= 0 here? */
2094               gtk_drag_drop_finished (info, TRUE, time);
2095               return;
2096             }
2097           tmp_list = tmp_list->next;
2098         }
2099       gtk_drag_drop_finished (info, FALSE, time);
2100     }
2101   else
2102     {
2103       if (info->icon_window)
2104         gtk_widget_hide (info->icon_window);
2105         
2106       gdk_drag_drop (info->context, time);
2107       info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2108                                             gtk_drag_abort_timeout,
2109                                             info);
2110     }
2111 }
2112
2113 /*
2114  * Source side callbacks.
2115  */
2116
2117 static gint
2118 gtk_drag_source_event_cb (GtkWidget      *widget,
2119                           GdkEvent       *event,
2120                           gpointer        data)
2121 {
2122   GtkDragSourceSite *site;
2123   site = (GtkDragSourceSite *)data;
2124
2125   switch (event->type)
2126     {
2127     case GDK_BUTTON_PRESS:
2128       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2129         {
2130           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2131           site->x = event->button.x;
2132           site->y = event->button.y;
2133         }
2134       break;
2135       
2136     case GDK_BUTTON_RELEASE:
2137       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2138         {
2139           site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2140         }
2141       break;
2142       
2143     case GDK_MOTION_NOTIFY:
2144       if (site->state & event->motion.state & site->start_button_mask)
2145         {
2146           /* FIXME: This is really broken and can leave us
2147            * with a stuck grab
2148            */
2149           int i;
2150           for (i=1; i<6; i++)
2151             {
2152               if (site->state & event->motion.state & 
2153                   GDK_BUTTON1_MASK << (i - 1))
2154                 break;
2155             }
2156           
2157           if (MAX (abs(site->x - event->motion.x),
2158                    abs(site->y - event->motion.y)) > 3)
2159             {
2160               GtkDragSourceInfo *info;
2161               GdkDragContext *context;
2162               
2163               site->state = 0;
2164               context = gtk_drag_begin (widget, site->target_list,
2165                                         site->actions, 
2166                                         i, event);
2167
2168               
2169               info = g_dataset_get_data (context, "gtk-info");
2170
2171               if (!info->icon_window)
2172                 {
2173                   if (site->pixmap)
2174                     gtk_drag_set_icon_pixmap (context,
2175                                               site->colormap,
2176                                               site->pixmap,
2177                                               site->mask, -2, -2);
2178                   else
2179                     gtk_drag_set_icon_default (context);
2180                 }
2181
2182               return TRUE;
2183             }
2184         }
2185       break;
2186       
2187     default:                    /* hit for 2/3BUTTON_PRESS */
2188       break;
2189     }
2190   return FALSE;
2191 }
2192
2193 static void 
2194 gtk_drag_source_site_destroy (gpointer data)
2195 {
2196   GtkDragSourceSite *site = data;
2197
2198   if (site->target_list)
2199     gtk_target_list_unref (site->target_list);
2200
2201   if (site->pixmap)
2202     gdk_pixmap_unref (site->pixmap);
2203   
2204   if (site->mask)
2205     gdk_pixmap_unref (site->mask);
2206   
2207   g_free (site);
2208 }
2209
2210 static void
2211 gtk_drag_selection_get (GtkWidget        *widget, 
2212                         GtkSelectionData *selection_data,
2213                         guint             sel_info,
2214                         guint32           time,
2215                         gpointer          data)
2216 {
2217   GtkDragSourceInfo *info = data;
2218   static GdkAtom null_atom = GDK_NONE;
2219   guint target_info;
2220
2221   if (!null_atom)
2222     null_atom = gdk_atom_intern ("NULL", FALSE);
2223
2224   switch (sel_info)
2225     {
2226     case TARGET_DELETE:
2227       gtk_signal_emit_by_name (GTK_OBJECT (info->widget), 
2228                                "drag_data_delete", 
2229                                info->context);
2230       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2231       break;
2232     case TARGET_MOTIF_SUCCESS:
2233       gtk_drag_drop_finished (info, TRUE, time);
2234       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2235       break;
2236     case TARGET_MOTIF_FAILURE:
2237       gtk_drag_drop_finished (info, FALSE, time);
2238       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2239       break;
2240     default:
2241       if (info->proxy_dest)
2242         {
2243           /* This is sort of dangerous and needs to be thought
2244            * through better
2245            */
2246           info->proxy_dest->proxy_data = selection_data;
2247           gtk_drag_get_data (info->widget,
2248                              info->proxy_dest->context,
2249                              selection_data->target,
2250                              time);
2251           gtk_main();
2252           info->proxy_dest->proxy_data = NULL;
2253         }
2254       else
2255         {
2256           if (gtk_target_list_find (info->target_list, 
2257                                     selection_data->target, 
2258                                     &target_info))
2259             {
2260               gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2261                                        info->context, 
2262                                        selection_data, 
2263                                        target_info, 
2264                                        time);
2265             }
2266         }
2267       break;
2268     }
2269 }
2270
2271 static gint
2272 gtk_drag_anim_timeout (gpointer data)
2273 {
2274   GtkDragAnim *anim = data;
2275   gint x, y;
2276   gboolean retval;
2277
2278   GDK_THREADS_ENTER ();
2279
2280   if (anim->step == anim->n_steps)
2281     {
2282       gtk_drag_source_info_destroy (anim->info);
2283       g_free (anim);
2284
2285       retval = FALSE;
2286     }
2287   else
2288     {
2289       x = (anim->info->start_x * (anim->step + 1) +
2290            anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2291       y = (anim->info->start_y * (anim->step + 1) +
2292            anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2293       if (anim->info->icon_window)
2294         gtk_widget_set_uposition (anim->info->icon_window, x, y);
2295   
2296       anim->step++;
2297
2298       retval = TRUE;
2299     }
2300
2301   GDK_THREADS_LEAVE ();
2302
2303   return retval;
2304 }
2305
2306 static void
2307 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2308 {
2309   if (info->icon_window)
2310     {
2311       gtk_widget_hide (info->icon_window);
2312       gtk_widget_unref (info->icon_window);
2313
2314       info->icon_window = NULL;
2315     }
2316 }
2317
2318 static void
2319 gtk_drag_source_info_destroy (gpointer data)
2320 {
2321   GtkDragSourceInfo *info = data;
2322
2323   gtk_drag_remove_icon (data);
2324
2325   if (!info->proxy_dest)
2326     gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end", 
2327                              info->context);
2328
2329   if (info->widget)
2330     gtk_widget_unref (info->widget);
2331   
2332   gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2333   gtk_selection_remove_all (info->ipc_widget);
2334   gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2335   source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2336   gtk_drag_release_ipc_widget (info->ipc_widget);
2337
2338   gtk_target_list_unref (info->target_list);
2339
2340   g_dataset_set_data (info->context, "gtk-info", NULL);
2341   gdk_drag_context_unref (info->context);
2342
2343   if (info->drop_timeout)
2344     gtk_timeout_remove (info->drop_timeout);
2345
2346   g_free (info);
2347 }
2348
2349 /*************************************************************
2350  * gtk_drag_motion_cb:
2351  *     "motion_notify_event" callback during drag.
2352  *   arguments:
2353  *     
2354  *   results:
2355  *************************************************************/
2356
2357 static gint
2358 gtk_drag_motion_cb (GtkWidget      *widget, 
2359                     GdkEventMotion *event, 
2360                     gpointer        data)
2361 {
2362   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2363   GdkAtom selection;
2364   GdkDragAction action;
2365   GdkWindow *window = NULL;
2366   GdkWindow *dest_window;
2367   GdkDragProtocol protocol;
2368   gint x_root, y_root;
2369
2370   if (event->is_hint)
2371     {
2372       gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2373       event->x_root = x_root;
2374       event->y_root = y_root;
2375     }
2376
2377   action = gtk_drag_get_event_action ((GdkEvent *)event, 
2378                                       info->button, 
2379                                       info->context->actions);
2380   
2381   info->cur_x = event->x_root - info->hot_x;
2382   info->cur_y = event->y_root - info->hot_y;
2383
2384   if (info->icon_window)
2385     {
2386       gdk_window_raise (info->icon_window->window);
2387       gtk_widget_set_uposition (info->icon_window, info->cur_x, info->cur_y);
2388       window = info->icon_window->window;
2389     }
2390   
2391   gdk_drag_find_window (info->context,
2392                         window, event->x_root, event->y_root,
2393                         &dest_window, &protocol);
2394
2395   if (gdk_drag_motion (info->context, dest_window, protocol,
2396                        event->x_root, event->y_root, action,
2397                        event->time))
2398     {
2399       if (info->last_event)
2400         gdk_event_free ((GdkEvent *)info->last_event);
2401       
2402       info->last_event = gdk_event_copy ((GdkEvent *)event);
2403     }
2404
2405   if (dest_window)
2406     gdk_window_unref (dest_window);
2407
2408   selection = gdk_drag_get_selection (info->context);
2409   if (selection)
2410     gtk_drag_source_check_selection (info, selection, event->time);
2411
2412 #if 0
2413   /* We ignore the response, so we can respond precisely to the drop
2414    */
2415   if (event->is_hint)
2416     gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
2417 #endif  
2418
2419   return TRUE;
2420 }
2421
2422 /*************************************************************
2423  * gtk_drag_motion_cb:
2424  *     "button_release_event" callback during drag.
2425  *   arguments:
2426  *     
2427  *   results:
2428  *************************************************************/
2429
2430 static gint
2431 gtk_drag_button_release_cb (GtkWidget      *widget, 
2432                             GdkEventButton *event, 
2433                             gpointer        data)
2434 {
2435   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2436   GtkWidget *source_widget = info->widget;
2437   GdkEvent send_event;
2438
2439   gtk_widget_ref (source_widget);
2440
2441   if (event->button != info->button)
2442     return FALSE;
2443
2444   gdk_pointer_ungrab (event->time);
2445
2446   if ((info->context->action != 0) && (info->context->dest_window != NULL))
2447     {
2448       gtk_drag_drop (info, event->time);
2449     }
2450   else
2451     {
2452       gdk_drag_abort (info->context, event->time);
2453       gtk_drag_drop_finished (info, FALSE, event->time);
2454     }
2455
2456   gtk_grab_remove (widget);
2457
2458   send_event.button.type = GDK_BUTTON_RELEASE;
2459   send_event.button.window = source_widget->window;
2460   send_event.button.x = 0;
2461   send_event.button.y = 0;
2462   send_event.button.state = event->state;
2463   send_event.button.button = event->button;
2464   
2465   send_event.button.time = event->time;
2466
2467   /* Send on the button release to the original widget to
2468    * convince it to release its grab
2469    */
2470   gtk_widget_event (source_widget, &send_event);
2471   gtk_widget_unref (source_widget);
2472   
2473   return TRUE;
2474 }
2475
2476 static gint
2477 gtk_drag_abort_timeout (gpointer data)
2478 {
2479   GtkDragSourceInfo *info = data;
2480   guint32 time = GDK_CURRENT_TIME;
2481
2482   if (info->proxy_dest)
2483     time = info->proxy_dest->proxy_drop_time;
2484
2485   info->drop_timeout = 0;
2486   gtk_drag_drop_finished (info, FALSE, time);
2487   
2488   return FALSE;
2489 }