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