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