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