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