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