]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd.c
Move main thread lock back to GDK - we need it there for locking when
[~andy/gtk] / gtk / gtkdnd.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "gdk/gdkx.h"
21
22 #include "gtkdnd.h"
23 #include "gtkinvisible.h"
24 #include "gtkmain.h"
25 #include "gtksignal.h"
26 #include "gtkwindow.h"
27
28 static GSList *drag_widgets = NULL;
29 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_window_new (GTK_WINDOW_POPUP);
1758   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
1759
1760   gtk_widget_pop_visual ();
1761   gtk_widget_pop_colormap ();
1762
1763   gtk_widget_set_usize (window, width, height);
1764   gtk_widget_realize (window);
1765
1766   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
1767   
1768   if (mask)
1769     gtk_widget_shape_combine_mask (window, mask, 0, 0);
1770
1771   gtk_drag_set_icon_widget (context, window, hot_x, hot_y);
1772 }
1773
1774 /*************************************************************
1775  * gtk_drag_set_icon_default:
1776  *     Set the icon for a drag to the default icon.
1777  *   arguments:
1778  *     context:
1779  *   results:
1780  *************************************************************/
1781
1782 void 
1783 gtk_drag_set_icon_default (GdkDragContext    *context)
1784 {
1785   g_return_if_fail (context != NULL);
1786
1787   if (!default_icon_pixmap)
1788     {
1789       default_icon_colormap = gdk_colormap_get_system ();
1790       default_icon_pixmap = 
1791         gdk_pixmap_colormap_create_from_xpm_d (NULL,
1792                                                default_icon_colormap,
1793                                                &default_icon_mask,
1794                                                NULL, drag_default_xpm);
1795       default_icon_hot_x = -2;
1796       default_icon_hot_y = -2;
1797     }
1798
1799   gtk_drag_set_icon_pixmap (context, 
1800                             default_icon_colormap, 
1801                             default_icon_pixmap, 
1802                             default_icon_mask,
1803                             default_icon_hot_x,
1804                             default_icon_hot_y);
1805 }
1806
1807 /*************************************************************
1808  * gtk_drag_set_default_icon:
1809  *     Set a default icon for all drags as a pixmap.
1810  *   arguments:
1811  *     colormap: Colormap for the icon window.
1812  *     pixmap:   
1813  *     mask:
1814  *     hot_x:    Hot spot
1815  *     hot_y:
1816  *   results:
1817  *************************************************************/
1818
1819 void 
1820 gtk_drag_set_default_icon (GdkColormap   *colormap,
1821                            GdkPixmap     *pixmap,
1822                            GdkBitmap     *mask,
1823                            gint           hot_x,
1824                            gint           hot_y)
1825 {
1826   g_return_if_fail (colormap != NULL);
1827   g_return_if_fail (pixmap != NULL);
1828   
1829   if (default_icon_colormap)
1830     gdk_colormap_unref (default_icon_colormap);
1831   if (default_icon_pixmap)
1832     gdk_pixmap_unref (default_icon_pixmap);
1833   if (default_icon_mask)
1834     gdk_pixmap_unref (default_icon_pixmap);
1835
1836   default_icon_colormap = colormap;
1837   gdk_colormap_ref (colormap);
1838   
1839   default_icon_pixmap = pixmap;
1840   gdk_pixmap_ref (pixmap);
1841
1842   default_icon_mask = mask;
1843   if (mask)
1844     gdk_pixmap_ref (mask);
1845   
1846   default_icon_hot_x = hot_x;
1847   default_icon_hot_y = hot_y;
1848 }
1849
1850
1851 /*************************************************************
1852  * gtk_drag_source_handle_event:
1853  *     Called from widget event handling code on Drag events
1854  *     for drag sources.
1855  *
1856  *   arguments:
1857  *     toplevel: Toplevel widget that received the event
1858  *     event:
1859  *   results:
1860  *************************************************************/
1861
1862 void
1863 gtk_drag_source_handle_event (GtkWidget *widget,
1864                               GdkEvent  *event)
1865 {
1866   GtkDragSourceInfo *info;
1867   GdkDragContext *context;
1868
1869   g_return_if_fail (widget != NULL);
1870   g_return_if_fail (event != NULL);
1871
1872   context = event->dnd.context;
1873   info = g_dataset_get_data (context, "gtk-info");
1874   if (!info)
1875     return;
1876
1877   switch (event->type)
1878     {
1879     case GDK_DRAG_STATUS:
1880       {
1881         GdkCursor *cursor;
1882
1883         if (info->proxy_dest)
1884           {
1885             if (!event->dnd.send_event)
1886               {
1887                 if (info->proxy_dest->proxy_drop_wait)
1888                   {
1889                     /* Aha - we can finally pass the MOTIF DROP on... */
1890                     gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
1891                   }
1892                 else
1893                   {
1894                     gdk_drag_status (info->proxy_dest->context,
1895                                      event->dnd.context->action,
1896                                      event->dnd.time);
1897                   }
1898               }
1899           }
1900         else
1901           {
1902             cursor = gtk_drag_get_cursor (event->dnd.context->action);
1903             if (info->cursor != cursor)
1904               {
1905                 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window), 
1906                                           PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
1907                                           ((GdkCursorPrivate *)cursor)->xcursor,
1908                                           event->dnd.time);
1909                 info->cursor = cursor;
1910               }
1911             
1912             if (info->last_event)
1913               {
1914                 gtk_drag_motion_cb (info->widget, 
1915                                     (GdkEventMotion *)info->last_event, 
1916                                     info);
1917                 info->last_event = NULL;
1918               }
1919           }
1920       }
1921       break;
1922       
1923     case GDK_DROP_FINISHED:
1924       gtk_drag_drop_finished (info, TRUE, event->dnd.time);
1925       break;
1926     default:
1927       g_assert_not_reached();
1928     }
1929 }
1930
1931 /*************************************************************
1932  * gtk_drag_source_check_selection:
1933  *     Check if we've set up handlers/claimed the selection
1934  *     for a given drag. If not, add them.
1935  *   arguments:
1936  *     
1937  *   results:
1938  *************************************************************/
1939
1940 static void
1941 gtk_drag_source_check_selection (GtkDragSourceInfo *info, 
1942                                  GdkAtom            selection,
1943                                  guint32            time)
1944 {
1945   GList *tmp_list;
1946
1947   tmp_list = info->selections;
1948   while (tmp_list)
1949     {
1950       if (GPOINTER_TO_UINT (tmp_list->data) == selection)
1951         return;
1952       tmp_list = tmp_list->next;
1953     }
1954
1955   gtk_selection_owner_set (info->ipc_widget, selection, time);
1956   info->selections = g_list_prepend (info->selections,
1957                                      GUINT_TO_POINTER (selection));
1958
1959   tmp_list = info->target_list->list;
1960   while (tmp_list)
1961     {
1962       GtkTargetPair *pair = tmp_list->data;
1963
1964       gtk_selection_add_target (info->ipc_widget,
1965                                 selection,
1966                                 pair->target,
1967                                 pair->info);
1968       tmp_list = tmp_list->next;
1969     }
1970   
1971   if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
1972     {
1973       gtk_selection_add_target (info->ipc_widget,
1974                                 selection,
1975                                 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
1976                                 TARGET_MOTIF_SUCCESS);
1977       gtk_selection_add_target (info->ipc_widget,
1978                                 selection,
1979                                 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
1980                                 TARGET_MOTIF_FAILURE);
1981     }
1982
1983   gtk_selection_add_target (info->ipc_widget,
1984                             selection,
1985                             gdk_atom_intern ("DELETE", FALSE),
1986                             TARGET_DELETE);
1987 }
1988
1989 /*************************************************************
1990  * gtk_drag_drop_finished:
1991  *     Clean up from the drag, and display snapback, if necessary.
1992  *   arguments:
1993  *     info:
1994  *     success:
1995  *     time:
1996  *   results:
1997  *************************************************************/
1998
1999 static void
2000 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2001                         gboolean           success,
2002                         guint              time)
2003 {
2004   if (info->proxy_dest)
2005     {
2006       /* The time from the event isn't reliable for Xdnd drags */
2007       gtk_drag_finish (info->proxy_dest->context, success, FALSE, 
2008                        info->proxy_dest->proxy_drop_time);
2009     }
2010   else
2011     {
2012       if (success)
2013         {
2014           gtk_drag_source_info_destroy (info);
2015         }
2016       else
2017         {
2018           GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2019           anim->info = info;
2020           anim->step = 0;
2021           
2022           anim->n_steps = MAX (info->cur_x - info->start_x,
2023                                info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2024           anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2025           if (info->icon_window)
2026             {
2027               gtk_widget_show(info->icon_window);
2028               gdk_window_raise (info->icon_window->window);
2029             }
2030           
2031           gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2032         }
2033     }
2034
2035   gtk_drag_source_release_selections (info, GDK_CURRENT_TIME); /* fixme */
2036 }
2037
2038 static void
2039 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2040                                     guint32            time)
2041 {
2042   GList *tmp_list = info->selections;
2043   while (tmp_list)
2044     {
2045       GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2046       if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2047         gtk_selection_owner_set (NULL, selection, time);
2048       tmp_list = tmp_list->next;
2049     }
2050
2051   g_list_free (info->selections);
2052   info->selections = NULL;
2053 }
2054
2055 /*************************************************************
2056  * gtk_drag_drop:
2057  *     Send a drop event.
2058  *   arguments:
2059  *     
2060  *   results:
2061  *************************************************************/
2062
2063 static void
2064 gtk_drag_drop (GtkDragSourceInfo *info, guint32 time)
2065 {
2066   if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2067     {
2068       GtkSelectionData selection_data;
2069       GList *tmp_list;
2070       GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2071       
2072       tmp_list = info->target_list->list;
2073       while (tmp_list)
2074         {
2075           GtkTargetPair *pair = tmp_list->data;
2076           
2077           if (pair->target == target)
2078             {
2079               selection_data.selection = GDK_NONE;
2080               selection_data.target = target;
2081               selection_data.data = NULL;
2082               selection_data.length = -1;
2083               
2084               gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2085                                        info->context, &selection_data,
2086                                        pair->info, 
2087                                        time);
2088               
2089               /* FIXME: Should we check for length >= 0 here? */
2090               gtk_drag_drop_finished (info, TRUE, time);
2091               return;
2092             }
2093           tmp_list = tmp_list->next;
2094         }
2095       gtk_drag_drop_finished (info, FALSE, time);
2096     }
2097   else
2098     {
2099       if (info->icon_window)
2100         gtk_widget_hide (info->icon_window);
2101         
2102       gdk_drag_drop (info->context, time);
2103       info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2104                                             gtk_drag_abort_timeout,
2105                                             info);
2106     }
2107 }
2108
2109 /*
2110  * Source side callbacks.
2111  */
2112
2113 static gint
2114 gtk_drag_source_event_cb (GtkWidget      *widget,
2115                           GdkEvent       *event,
2116                           gpointer        data)
2117 {
2118   GtkDragSourceSite *site;
2119   site = (GtkDragSourceSite *)data;
2120
2121   switch (event->type)
2122     {
2123     case GDK_BUTTON_PRESS:
2124       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2125         {
2126           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2127           site->x = event->button.x;
2128           site->y = event->button.y;
2129         }
2130       break;
2131       
2132     case GDK_BUTTON_RELEASE:
2133       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2134         {
2135           site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2136         }
2137       break;
2138       
2139     case GDK_MOTION_NOTIFY:
2140       if (site->state & event->motion.state & site->start_button_mask)
2141         {
2142           /* FIXME: This is really broken and can leave us
2143            * with a stuck grab
2144            */
2145           int i;
2146           for (i=1; i<6; i++)
2147             {
2148               if (site->state & event->motion.state & 
2149                   GDK_BUTTON1_MASK << (i - 1))
2150                 break;
2151             }
2152           
2153           if (MAX (abs(site->x - event->motion.x),
2154                    abs(site->y - event->motion.y)) > 3)
2155             {
2156               GtkDragSourceInfo *info;
2157               GdkDragContext *context;
2158               
2159               site->state = 0;
2160               context = gtk_drag_begin (widget, site->target_list,
2161                                         site->actions, 
2162                                         i, event);
2163
2164               
2165               info = g_dataset_get_data (context, "gtk-info");
2166
2167               if (!info->icon_window)
2168                 {
2169                   if (site->pixmap)
2170                     gtk_drag_set_icon_pixmap (context,
2171                                               site->colormap,
2172                                               site->pixmap,
2173                                               site->mask, -2, -2);
2174                   else
2175                     gtk_drag_set_icon_default (context);
2176                 }
2177
2178               return TRUE;
2179             }
2180         }
2181       break;
2182       
2183     default:                    /* hit for 2/3BUTTON_PRESS */
2184       break;
2185     }
2186   return FALSE;
2187 }
2188
2189 static void 
2190 gtk_drag_source_site_destroy (gpointer data)
2191 {
2192   GtkDragSourceSite *site = data;
2193
2194   if (site->target_list)
2195     gtk_target_list_unref (site->target_list);
2196
2197   if (site->pixmap)
2198     gdk_pixmap_unref (site->pixmap);
2199   
2200   if (site->mask)
2201     gdk_pixmap_unref (site->mask);
2202   
2203   g_free (site);
2204 }
2205
2206 static void
2207 gtk_drag_selection_get (GtkWidget        *widget, 
2208                         GtkSelectionData *selection_data,
2209                         guint             sel_info,
2210                         guint32           time,
2211                         gpointer          data)
2212 {
2213   GtkDragSourceInfo *info = data;
2214   static GdkAtom null_atom = GDK_NONE;
2215   guint target_info;
2216
2217   if (!null_atom)
2218     null_atom = gdk_atom_intern ("NULL", FALSE);
2219
2220   switch (sel_info)
2221     {
2222     case TARGET_DELETE:
2223       gtk_signal_emit_by_name (GTK_OBJECT (info->widget), 
2224                                "drag_data_delete", 
2225                                info->context);
2226       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2227       break;
2228     case TARGET_MOTIF_SUCCESS:
2229       gtk_drag_drop_finished (info, TRUE, time);
2230       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2231       break;
2232     case TARGET_MOTIF_FAILURE:
2233       gtk_drag_drop_finished (info, FALSE, time);
2234       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2235       break;
2236     default:
2237       if (info->proxy_dest)
2238         {
2239           /* This is sort of dangerous and needs to be thought
2240            * through better
2241            */
2242           info->proxy_dest->proxy_data = selection_data;
2243           gtk_drag_get_data (info->widget,
2244                              info->proxy_dest->context,
2245                              selection_data->target,
2246                              time);
2247           gtk_main();
2248           info->proxy_dest->proxy_data = NULL;
2249         }
2250       else
2251         {
2252           if (gtk_target_list_find (info->target_list, 
2253                                     selection_data->target, 
2254                                     &target_info))
2255             {
2256               gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2257                                        info->context, 
2258                                        selection_data, 
2259                                        target_info, 
2260                                        time);
2261             }
2262         }
2263       break;
2264     }
2265 }
2266
2267 static gint
2268 gtk_drag_anim_timeout (gpointer data)
2269 {
2270   GtkDragAnim *anim = data;
2271   gint x, y;
2272   gboolean retval;
2273
2274   GDK_THREADS_ENTER ();
2275
2276   if (anim->step == anim->n_steps)
2277     {
2278       gtk_drag_source_info_destroy (anim->info);
2279       g_free (anim);
2280
2281       retval = FALSE;
2282     }
2283   else
2284     {
2285       x = (anim->info->start_x * (anim->step + 1) +
2286            anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2287       y = (anim->info->start_y * (anim->step + 1) +
2288            anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2289       if (anim->info->icon_window)
2290         gtk_widget_set_uposition (anim->info->icon_window, x, y);
2291   
2292       anim->step++;
2293
2294       retval = TRUE;
2295     }
2296
2297   GDK_THREADS_LEAVE ();
2298
2299   return retval;
2300 }
2301
2302 static void
2303 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2304 {
2305   if (info->icon_window)
2306     {
2307       gtk_widget_hide (info->icon_window);
2308       gtk_widget_unref (info->icon_window);
2309
2310       info->icon_window = NULL;
2311     }
2312 }
2313
2314 static void
2315 gtk_drag_source_info_destroy (gpointer data)
2316 {
2317   GtkDragSourceInfo *info = data;
2318
2319   gtk_drag_remove_icon (data);
2320
2321   if (!info->proxy_dest)
2322     gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end", 
2323                              info->context);
2324
2325   if (info->widget)
2326     gtk_widget_unref (info->widget);
2327   
2328   gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2329   gtk_selection_remove_all (info->ipc_widget);
2330   gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2331   source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2332   gtk_drag_release_ipc_widget (info->ipc_widget);
2333
2334   gtk_target_list_unref (info->target_list);
2335
2336   g_dataset_set_data (info->context, "gtk-info", NULL);
2337   gdk_drag_context_unref (info->context);
2338
2339   if (info->drop_timeout)
2340     gtk_timeout_remove (info->drop_timeout);
2341
2342   g_free (info);
2343 }
2344
2345 /*************************************************************
2346  * gtk_drag_motion_cb:
2347  *     "motion_notify_event" callback during drag.
2348  *   arguments:
2349  *     
2350  *   results:
2351  *************************************************************/
2352
2353 static gint
2354 gtk_drag_motion_cb (GtkWidget      *widget, 
2355                     GdkEventMotion *event, 
2356                     gpointer        data)
2357 {
2358   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2359   GdkAtom selection;
2360   GdkDragAction action;
2361   GdkWindow *window = NULL;
2362   GdkWindow *dest_window;
2363   GdkDragProtocol protocol;
2364   gint x_root, y_root;
2365
2366   if (event->is_hint)
2367     {
2368       gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2369       event->x_root = x_root;
2370       event->y_root = y_root;
2371     }
2372
2373   action = gtk_drag_get_event_action ((GdkEvent *)event, 
2374                                       info->button, 
2375                                       info->context->actions);
2376   
2377   info->cur_x = event->x_root - info->hot_x;
2378   info->cur_y = event->y_root - info->hot_y;
2379
2380   if (info->icon_window)
2381     {
2382       gdk_window_raise (info->icon_window->window);
2383       gtk_widget_set_uposition (info->icon_window, info->cur_x, info->cur_y);
2384       window = info->icon_window->window;
2385     }
2386   
2387   gdk_drag_find_window (info->context,
2388                         window, event->x_root, event->y_root,
2389                         &dest_window, &protocol);
2390
2391   if (gdk_drag_motion (info->context, dest_window, protocol,
2392                        event->x_root, event->y_root, action,
2393                        event->time))
2394     {
2395       if (info->last_event)
2396         gdk_event_free ((GdkEvent *)info->last_event);
2397       
2398       info->last_event = gdk_event_copy ((GdkEvent *)event);
2399     }
2400
2401   if (dest_window)
2402     gdk_window_unref (dest_window);
2403
2404   selection = gdk_drag_get_selection (info->context);
2405   if (selection)
2406     gtk_drag_source_check_selection (info, selection, event->time);
2407
2408 #if 0
2409   /* We ignore the response, so we can respond precisely to the drop
2410    */
2411   if (event->is_hint)
2412     gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
2413 #endif  
2414
2415   return TRUE;
2416 }
2417
2418 /*************************************************************
2419  * gtk_drag_motion_cb:
2420  *     "button_release_event" callback during drag.
2421  *   arguments:
2422  *     
2423  *   results:
2424  *************************************************************/
2425
2426 static gint
2427 gtk_drag_button_release_cb (GtkWidget      *widget, 
2428                             GdkEventButton *event, 
2429                             gpointer        data)
2430 {
2431   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2432   GtkWidget *source_widget = info->widget;
2433   GdkEvent send_event;
2434
2435   gtk_widget_ref (source_widget);
2436
2437   if (event->button != info->button)
2438     return FALSE;
2439
2440   gdk_pointer_ungrab (event->time);
2441
2442   if ((info->context->action != 0) && (info->context->dest_window != NULL))
2443     {
2444       gtk_drag_drop (info, event->time);
2445     }
2446   else
2447     {
2448       gdk_drag_abort (info->context, event->time);
2449       gtk_drag_drop_finished (info, FALSE, event->time);
2450     }
2451
2452   gtk_grab_remove (widget);
2453
2454   send_event.button.type = GDK_BUTTON_RELEASE;
2455   send_event.button.window = source_widget->window;
2456   send_event.button.x = 0;
2457   send_event.button.y = 0;
2458   send_event.button.state = event->state;
2459   send_event.button.button = event->button;
2460   
2461   send_event.button.time = event->time;
2462
2463   /* Send on the button release to the original widget to
2464    * convince it to release its grab
2465    */
2466   gtk_widget_event (source_widget, &send_event);
2467   gtk_widget_unref (source_widget);
2468   
2469   return TRUE;
2470 }
2471
2472 static gint
2473 gtk_drag_abort_timeout (gpointer data)
2474 {
2475   GtkDragSourceInfo *info = data;
2476   guint32 time = GDK_CURRENT_TIME;
2477
2478   if (info->proxy_dest)
2479     time = info->proxy_dest->proxy_drop_time;
2480
2481   info->drop_timeout = 0;
2482   gtk_drag_drop_finished (info, FALSE, time);
2483   
2484   return FALSE;
2485 }