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