]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd.c
Doh! Mixed up x and y.
[~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 (GTK_OBJECT (widget), "expose_event",
814                       GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
815                       NULL);
816
817   gtk_widget_queue_draw (widget);
818 }
819
820 /*************************************************************
821  * gtk_drag_unhighlight:
822  *     Refresh the given widget to remove the highlight.
823  *   arguments:
824  *     widget:
825  *   results:
826  *************************************************************/
827
828 void 
829 gtk_drag_unhighlight (GtkWidget *widget)
830 {
831   g_return_if_fail (widget != NULL);
832
833   gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
834                                  GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
835                                  NULL);
836   gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
837                                  GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
838                                  NULL);
839   
840   gtk_widget_queue_clear (widget);
841 }
842
843 static void
844 gtk_drag_dest_set_internal (GtkWidget       *widget,
845                             GtkDragDestSite *site)
846 {
847   GtkDragDestSite *old_site;
848   
849   g_return_if_fail (widget != NULL);
850
851   /* HACK, do this in the destroy */
852   old_site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
853   if (old_site)
854     gtk_signal_disconnect_by_data (GTK_OBJECT (widget), old_site);
855
856   if (GTK_WIDGET_REALIZED (widget))
857     gtk_drag_dest_realized (widget);
858
859   gtk_signal_connect (GTK_OBJECT (widget), "realize",
860                       GTK_SIGNAL_FUNC (gtk_drag_dest_realized), site);
861
862   gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
863                             site, gtk_drag_dest_site_destroy);
864 }
865                             
866
867 /*************************************************************
868  * gtk_drag_dest_set:
869  *     Register a drop site, and possibly add default behaviors.
870  *   arguments:
871  *     widget:    
872  *     flags:     Which types of default drag behavior to use
873  *     targets:   Table of targets that can be accepted
874  *     n_targets: Number of of entries in targets
875  *     actions:   
876  *   results:
877  *************************************************************/
878
879 void 
880 gtk_drag_dest_set   (GtkWidget            *widget,
881                      GtkDestDefaults       flags,
882                      const GtkTargetEntry *targets,
883                      gint                  n_targets,
884                      GdkDragAction         actions)
885 {
886   GtkDragDestSite *site;
887   
888   g_return_if_fail (widget != NULL);
889
890   site = g_new (GtkDragDestSite, 1);
891
892   site->flags = flags;
893   site->have_drag = FALSE;
894   if (targets)
895     site->target_list = gtk_target_list_new (targets, n_targets);
896   else
897     site->target_list = NULL;
898
899   site->actions = actions;
900   site->do_proxy = FALSE;
901
902   gtk_drag_dest_set_internal (widget, site);
903 }
904
905 /*************************************************************
906  * gtk_drag_dest_set_proxy:
907  *     Set up this widget to proxy drags elsewhere.
908  *   arguments:
909  *     widget:          
910  *     proxy_window:    window to which forward drag events
911  *     protocol:        Drag protocol which the dest widget accepts
912  *     use_coordinates: If true, send the same coordinates to the
913  *                      destination, because it is a embedded 
914  *                      subwindow.
915  *   results:
916  *************************************************************/
917
918 void 
919 gtk_drag_dest_set_proxy (GtkWidget      *widget,
920                          GdkWindow      *proxy_window,
921                          GdkDragProtocol protocol,
922                          gboolean        use_coordinates)
923 {
924   GtkDragDestSite *site;
925   
926   g_return_if_fail (widget != NULL);
927
928   site = g_new (GtkDragDestSite, 1);
929
930   site->flags = 0;
931   site->have_drag = FALSE;
932   site->target_list = NULL;
933   site->actions = 0;
934   site->proxy_window = proxy_window;
935   if (proxy_window)
936     gdk_window_ref (proxy_window);
937   site->do_proxy = TRUE;
938   site->proxy_protocol = protocol;
939   site->proxy_coords = use_coordinates;
940
941   gtk_drag_dest_set_internal (widget, site);
942 }
943
944 /*************************************************************
945  * gtk_drag_dest_unset
946  *     Unregister this widget as a drag target.
947  *   arguments:
948  *     widget:
949  *   results:
950  *************************************************************/
951
952 void 
953 gtk_drag_dest_unset (GtkWidget *widget)
954 {
955   g_return_if_fail (widget != NULL);
956
957   gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
958 }
959
960 /*************************************************************
961  * gtk_drag_dest_handle_event:
962  *     Called from widget event handling code on Drag events
963  *     for destinations.
964  *
965  *   arguments:
966  *     toplevel: Toplevel widget that received the event
967  *     event:
968  *   results:
969  *************************************************************/
970
971 void
972 gtk_drag_dest_handle_event (GtkWidget *toplevel,
973                             GdkEvent  *event)
974 {
975   GtkDragDestInfo *info;
976   GdkDragContext *context;
977
978   g_return_if_fail (toplevel != NULL);
979   g_return_if_fail (event != NULL);
980
981   context = event->dnd.context;
982
983   info = g_dataset_get_data (context, "gtk-info");
984   if (!info)
985     {
986       info = g_new (GtkDragDestInfo, 1);
987       info->widget = NULL;
988       info->context = event->dnd.context;
989       info->proxy_source = NULL;
990       info->proxy_data = NULL;
991       info->dropped = FALSE;
992       info->proxy_drop_wait = FALSE;
993       g_object_set_qdata_full (G_OBJECT (context),
994                                g_quark_from_static_string ("gtk-info"),
995                                info,
996                                gtk_drag_dest_info_destroy);
997     }
998
999   /* Find the widget for the event */
1000   switch (event->type)
1001     {
1002     case GDK_DRAG_ENTER:
1003       break;
1004       
1005     case GDK_DRAG_LEAVE:
1006       if (info->widget)
1007         {
1008           gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1009           info->widget = NULL;
1010         }
1011       break;
1012       
1013     case GDK_DRAG_MOTION:
1014     case GDK_DROP_START:
1015       {
1016         GtkDragFindData data;
1017         gint tx, ty;
1018
1019         if (event->type == GDK_DROP_START)
1020           {
1021             info->dropped = TRUE;
1022             /* We send a leave here so that the widget unhighlights
1023              * properly.
1024              */
1025             if (info->widget)
1026               {
1027                 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1028                 info->widget = NULL;
1029               }
1030           }
1031
1032         gdk_window_get_origin (toplevel->window, &tx, &ty);
1033
1034         data.x = event->dnd.x_root - tx;
1035         data.y = event->dnd.y_root - ty;
1036         data.context = context;
1037         data.info = info;
1038         data.found = FALSE;
1039         data.toplevel = TRUE;
1040         data.callback = (event->type == GDK_DRAG_MOTION) ?
1041           gtk_drag_dest_motion : gtk_drag_dest_drop;
1042         data.time = event->dnd.time;
1043         
1044         gtk_drag_find_widget (toplevel, &data);
1045
1046         if (info->widget && !data.found)
1047           {
1048             gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1049             info->widget = NULL;
1050           }
1051         
1052         /* Send a reply.
1053          */
1054         if (event->type == GDK_DRAG_MOTION)
1055           {
1056             if (!data.found)
1057               gdk_drag_status (context, 0, event->dnd.time);
1058           }
1059         else if (event->type == GDK_DROP_START && !info->proxy_source)
1060           {
1061             gdk_drop_reply (context, data.found, event->dnd.time);
1062             if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1063               gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1064           }
1065       }
1066       break;
1067
1068     default:
1069       g_assert_not_reached ();
1070     }
1071 }
1072
1073 /*************************************************************
1074  * gtk_drag_dest_find_target:
1075  *     Decide on a target for the drag.
1076  *   arguments:
1077  *     site:
1078  *     context:
1079  *   results:
1080  *************************************************************/
1081
1082 static GdkAtom
1083 gtk_drag_dest_find_target (GtkWidget       *widget,
1084                            GtkDragDestSite *site,
1085                            GdkDragContext  *context)
1086 {
1087   GList *tmp_target;
1088   GList *tmp_source = NULL;
1089   GtkWidget *source_widget = gtk_drag_get_source_widget (context);
1090
1091   tmp_target = site->target_list->list;
1092   while (tmp_target)
1093     {
1094       GtkTargetPair *pair = tmp_target->data;
1095       tmp_source = context->targets;
1096       while (tmp_source)
1097         {
1098           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1099             {
1100               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1101                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1102                 return pair->target;
1103               else
1104                 break;
1105             }
1106           tmp_source = tmp_source->next;
1107         }
1108       tmp_target = tmp_target->next;
1109     }
1110
1111   return GDK_NONE;
1112 }
1113
1114 static void
1115 gtk_drag_selection_received (GtkWidget        *widget,
1116                              GtkSelectionData *selection_data,
1117                              guint32           time,
1118                              gpointer          data)
1119 {
1120   GdkDragContext *context;
1121   GtkDragDestInfo *info;
1122   GtkWidget *drop_widget;
1123
1124   drop_widget = data;
1125
1126   context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1127   info = g_object_get_qdata (G_OBJECT (context), g_quark_from_static_string ("gtk-info"));
1128
1129   if (info->proxy_data && 
1130       info->proxy_data->target == selection_data->target)
1131     {
1132       gtk_selection_data_set (info->proxy_data,
1133                               selection_data->type,
1134                               selection_data->format,
1135                               selection_data->data,
1136                               selection_data->length);
1137       gtk_main_quit ();
1138       return;
1139     }
1140
1141   if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1142     {
1143       gtk_drag_finish (context, TRUE, FALSE, time);
1144     }
1145   else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1146            (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1147     {
1148       /* Do nothing */
1149     }
1150   else
1151     {
1152       GtkDragDestSite *site;
1153
1154       site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1155
1156       if (site && site->target_list)
1157         {
1158           guint target_info;
1159
1160           if (gtk_target_list_find (site->target_list, 
1161                                     selection_data->target,
1162                                     &target_info))
1163             {
1164               if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1165                   selection_data->length >= 0)
1166                 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget), 
1167                                          "drag_data_received",
1168                                          context, info->drop_x, info->drop_y,
1169                                          selection_data, 
1170                                          target_info, time);
1171             }
1172         }
1173       else
1174         {
1175           gtk_signal_emit_by_name (GTK_OBJECT (drop_widget), 
1176                                    "drag_data_received",
1177                                    context, info->drop_x, info->drop_y,
1178                                    selection_data, 0, time);
1179         }
1180       
1181       if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1182         {
1183
1184           gtk_drag_finish (context, 
1185                            (selection_data->length >= 0),
1186                            (context->action == GDK_ACTION_MOVE),
1187                            time);
1188         }
1189       
1190       gtk_widget_unref (drop_widget);
1191     }
1192
1193   gtk_signal_disconnect_by_func (GTK_OBJECT (widget), 
1194                                  GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1195                                  data);
1196   
1197   gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1198   gdk_drag_context_unref (context);
1199
1200   gtk_drag_release_ipc_widget (widget);
1201 }
1202
1203 /*************************************************************
1204  * gtk_drag_find_widget:
1205  *     Recursive callback used to locate widgets for 
1206  *     DRAG_MOTION and DROP_START events.
1207  *   arguments:
1208  *     
1209  *   results:
1210  *************************************************************/
1211
1212 static void
1213 gtk_drag_find_widget (GtkWidget       *widget,
1214                       GtkDragFindData *data)
1215 {
1216   GtkAllocation new_allocation;
1217   gint x_offset = 0;
1218   gint y_offset = 0;
1219
1220   new_allocation = widget->allocation;
1221
1222   if (data->found || !GTK_WIDGET_MAPPED (widget))
1223     return;
1224
1225   /* Note that in the following code, we only count the
1226    * position as being inside a WINDOW widget if it is inside
1227    * widget->window; points that are outside of widget->window
1228    * but within the allocation are not counted. This is consistent
1229    * with the way we highlight drag targets.
1230    */
1231   if (!GTK_WIDGET_NO_WINDOW (widget))
1232     {
1233       new_allocation.x = 0;
1234       new_allocation.y = 0;
1235     }
1236   
1237   if (widget->parent)
1238     {
1239       GdkWindow *window = widget->window;
1240       while (window != widget->parent->window)
1241         {
1242           gint tx, ty, twidth, theight;
1243           gdk_window_get_size (window, &twidth, &theight);
1244
1245           if (new_allocation.x < 0)
1246             {
1247               new_allocation.width += new_allocation.x;
1248               new_allocation.x = 0;
1249             }
1250           if (new_allocation.y < 0)
1251             {
1252               new_allocation.height += new_allocation.y;
1253               new_allocation.y = 0;
1254             }
1255           if (new_allocation.x + new_allocation.width > twidth)
1256             new_allocation.width = twidth - new_allocation.x;
1257           if (new_allocation.y + new_allocation.height > theight)
1258             new_allocation.height = theight - new_allocation.y;
1259
1260           gdk_window_get_position (window, &tx, &ty);
1261           new_allocation.x += tx;
1262           x_offset += tx;
1263           new_allocation.y += ty;
1264           y_offset += ty;
1265           
1266           window = gdk_window_get_parent (window);
1267         }
1268     }
1269
1270   if (data->toplevel ||
1271       ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1272        (data->x < new_allocation.x + new_allocation.width) && 
1273        (data->y < new_allocation.y + new_allocation.height)))
1274     {
1275       /* First, check if the drag is in a valid drop site in
1276        * one of our children 
1277        */
1278       if (GTK_IS_CONTAINER (widget))
1279         {
1280           GtkDragFindData new_data = *data;
1281           
1282           new_data.x -= x_offset;
1283           new_data.y -= y_offset;
1284           new_data.found = FALSE;
1285           new_data.toplevel = FALSE;
1286           
1287           gtk_container_forall (GTK_CONTAINER (widget),
1288                                 (GtkCallback)gtk_drag_find_widget,
1289                                 &new_data);
1290           
1291           data->found = new_data.found;
1292         }
1293
1294       /* If not, and this widget is registered as a drop site, check to
1295        * emit "drag_motion" to check if we are actually in
1296        * a drop site.
1297        */
1298       if (!data->found &&
1299           gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1300         {
1301           data->found = data->callback (widget,
1302                                         data->context,
1303                                         data->x - new_allocation.x,
1304                                         data->y - new_allocation.y,
1305                                         data->time);
1306           /* If so, send a "drag_leave" to the last widget */
1307           if (data->found)
1308             {
1309               if (data->info->widget && data->info->widget != widget)
1310                 {
1311                   gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1312                 }
1313               data->info->widget = widget;
1314             }
1315         }
1316     }
1317 }
1318
1319 static void
1320 gtk_drag_proxy_begin (GtkWidget       *widget, 
1321                       GtkDragDestInfo *dest_info)
1322 {
1323   GtkDragSourceInfo *source_info;
1324   GList *tmp_list;
1325   
1326   source_info = g_new0 (GtkDragSourceInfo, 1);
1327   source_info->ipc_widget = gtk_drag_get_ipc_widget ();
1328   
1329   source_info->widget = widget;
1330   gtk_widget_ref (source_info->widget);
1331   source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
1332                                          dest_info->context->targets);
1333
1334   source_info->target_list = gtk_target_list_new (NULL, 0);
1335   tmp_list = dest_info->context->targets;
1336   while (tmp_list)
1337     {
1338       gtk_target_list_add (source_info->target_list, 
1339                            GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1340       tmp_list = tmp_list->next;
1341     }
1342
1343   source_info->proxy_dest = dest_info;
1344   
1345   g_object_set_qdata (G_OBJECT (source_info->context),
1346                       g_quark_from_static_string ("gtk-info"),
1347                       source_info);
1348   
1349   gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget), 
1350                       "selection_get",
1351                       GTK_SIGNAL_FUNC (gtk_drag_selection_get), 
1352                       source_info);
1353   
1354   dest_info->proxy_source = source_info;
1355 }
1356
1357 static void
1358 gtk_drag_dest_info_destroy (gpointer data)
1359 {
1360   GtkDragDestInfo *info = data;
1361
1362   g_free (info);
1363 }
1364
1365 static void
1366 gtk_drag_dest_realized (GtkWidget *widget)
1367 {
1368   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1369   gdk_window_register_dnd (toplevel->window);
1370 }
1371
1372 static void
1373 gtk_drag_dest_site_destroy (gpointer data)
1374 {
1375   GtkDragDestSite *site = data;
1376
1377   if (site->target_list)
1378     gtk_target_list_unref (site->target_list);
1379
1380   g_free (site);
1381 }
1382
1383 /*
1384  * Default drag handlers
1385  */
1386 static void  
1387 gtk_drag_dest_leave (GtkWidget      *widget,
1388                      GdkDragContext *context,
1389                      guint           time)
1390 {
1391   GtkDragDestSite *site;
1392
1393   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1394   g_return_if_fail (site != NULL);
1395
1396   if (site->do_proxy)
1397     {
1398       GtkDragDestInfo *info = g_object_get_qdata (G_OBJECT (context),
1399                                                   g_quark_from_static_string ("gtk-info"));
1400
1401       if (info->proxy_source && !info->dropped)
1402         gdk_drag_abort (info->proxy_source->context, time);
1403       
1404       return;
1405     }
1406   else
1407     {
1408       if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1409         gtk_drag_unhighlight (widget);
1410
1411       if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1412         gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1413                                  context, time);
1414       
1415       site->have_drag = FALSE;
1416     }
1417 }
1418
1419 static gboolean
1420 gtk_drag_dest_motion (GtkWidget      *widget,
1421                       GdkDragContext *context,
1422                       gint            x,
1423                       gint            y,
1424                       guint           time)
1425 {
1426   GtkDragDestSite *site;
1427   GdkDragAction action = 0;
1428   gboolean retval;
1429
1430   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1431   g_return_val_if_fail (site != NULL, FALSE);
1432
1433   if (site->do_proxy)
1434     {
1435       GdkAtom selection;
1436       GdkEvent *current_event;
1437       GdkWindow *dest_window;
1438       GdkDragProtocol proto;
1439         
1440       GtkDragDestInfo *info = g_object_get_qdata (G_OBJECT (context),
1441                                                  g_quark_from_static_string ("gtk-info"));
1442
1443       if (!info->proxy_source)
1444         gtk_drag_proxy_begin (widget, info);
1445
1446       current_event = gtk_get_current_event ();
1447
1448       if (site->proxy_window)
1449         {
1450           dest_window = site->proxy_window;
1451           proto = site->proxy_protocol;
1452         }
1453       else
1454         {
1455           gdk_drag_find_window (info->proxy_source->context,
1456                                 NULL,
1457                                 current_event->dnd.x_root, 
1458                                 current_event->dnd.y_root,
1459                                 &dest_window, &proto);
1460         }
1461       
1462       gdk_drag_motion (info->proxy_source->context, 
1463                        dest_window, proto,
1464                        current_event->dnd.x_root, 
1465                        current_event->dnd.y_root, 
1466                        context->suggested_action, 
1467                        context->actions, time);
1468
1469       if (!site->proxy_window && dest_window)
1470         gdk_window_unref (dest_window);
1471
1472       selection = gdk_drag_get_selection (info->proxy_source->context);
1473       if (selection && 
1474           selection != gdk_drag_get_selection (info->context))
1475         gtk_drag_source_check_selection (info->proxy_source, selection, time);
1476
1477       gdk_event_free (current_event);
1478       
1479       return TRUE;
1480     }
1481
1482   if (site->flags & GTK_DEST_DEFAULT_MOTION)
1483     {
1484       if (context->suggested_action & site->actions)
1485         action = context->suggested_action;
1486       else
1487         {
1488           gint i;
1489           
1490           for (i=0; i<8; i++)
1491             {
1492               if ((site->actions & (1 << i)) &&
1493                   (context->actions & (1 << i)))
1494                 {
1495                   action = (1 << i);
1496                   break;
1497                 }
1498             }
1499         }
1500       
1501       if (action && gtk_drag_dest_find_target (widget, site, context))
1502         {
1503           if (!site->have_drag)
1504             {
1505               site->have_drag = TRUE;
1506               if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1507                 gtk_drag_highlight (widget);
1508             }
1509           
1510           gdk_drag_status (context, action, time);
1511         }
1512       else
1513         {
1514           gdk_drag_status (context, 0, time);
1515           return TRUE;
1516         }
1517     }
1518
1519   gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1520                            context, x, y, time, &retval);
1521
1522   return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1523 }
1524
1525 static gboolean
1526 gtk_drag_dest_drop (GtkWidget        *widget,
1527                     GdkDragContext   *context,
1528                     gint              x,
1529                     gint              y,
1530                     guint             time)
1531 {
1532   GtkDragDestSite *site;
1533   GtkDragDestInfo *info;
1534
1535   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1536   g_return_val_if_fail (site != NULL, FALSE);
1537
1538   info = g_object_get_qdata (G_OBJECT (context),
1539                              g_quark_from_static_string ("gtk-info"));
1540   g_return_val_if_fail (info != NULL, FALSE);
1541
1542   info->drop_x = x;
1543   info->drop_y = y;
1544
1545   if (site->do_proxy)
1546     {
1547       if (info->proxy_source || 
1548           (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1549         {
1550           gtk_drag_drop (info->proxy_source, time);
1551         }
1552       else
1553         {
1554           /* We need to synthesize a motion event, wait for a status,
1555            * and, if we get a good one, do a drop.
1556            */
1557           
1558           GdkEvent *current_event;
1559           GdkAtom selection;
1560           GdkWindow *dest_window;
1561           GdkDragProtocol proto;
1562           
1563           gtk_drag_proxy_begin (widget, info);
1564           info->proxy_drop_wait = TRUE;
1565           info->proxy_drop_time = time;
1566           
1567           current_event = gtk_get_current_event ();
1568
1569           if (site->proxy_window)
1570             {
1571               dest_window = site->proxy_window;
1572               proto = site->proxy_protocol;
1573             }
1574           else
1575             {
1576               gdk_drag_find_window (info->proxy_source->context,
1577                                     NULL,
1578                                     current_event->dnd.x_root, 
1579                                     current_event->dnd.y_root,
1580                                     &dest_window, &proto);
1581             }
1582
1583           gdk_drag_motion (info->proxy_source->context, 
1584                            dest_window, proto,
1585                            current_event->dnd.x_root, 
1586                            current_event->dnd.y_root, 
1587                            context->suggested_action, 
1588                            context->actions, time);
1589
1590           if (!site->proxy_window && dest_window)
1591             gdk_window_unref (dest_window);
1592
1593           selection = gdk_drag_get_selection (info->proxy_source->context);
1594           if (selection && 
1595               selection != gdk_drag_get_selection (info->context))
1596             gtk_drag_source_check_selection (info->proxy_source, selection, time);
1597
1598           gdk_event_free (current_event);
1599       
1600         }
1601
1602       return TRUE;
1603     }
1604   else
1605     {
1606       gboolean retval;
1607
1608       if (site->flags & GTK_DEST_DEFAULT_DROP)
1609         {
1610           GdkAtom target = gtk_drag_dest_find_target (widget, site, context);
1611       
1612           if (target == GDK_NONE)
1613             return FALSE;
1614           
1615           gtk_drag_get_data (widget, context, target, time);
1616         }
1617
1618       gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1619                                context, x, y, time, &retval);
1620
1621       return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1622     }
1623 }
1624
1625 /***************
1626  * Source side *
1627  ***************/
1628
1629 /*************************************************************
1630  * gtk_drag_begin: Start a drag operation
1631  *     
1632  *   arguments:
1633  *     widget:   Widget from which drag starts
1634  *     handlers: List of handlers to supply the data for the drag
1635  *     button:   Button user used to start drag
1636  *     time:     Time of event starting drag
1637  *
1638  *   results:
1639  *************************************************************/
1640
1641 GdkDragContext *
1642 gtk_drag_begin (GtkWidget         *widget,
1643                 GtkTargetList     *target_list,
1644                 GdkDragAction      actions,
1645                 gint               button,
1646                 GdkEvent          *event)
1647 {
1648   GtkDragSourceInfo *info;
1649   GList *targets = NULL;
1650   GList *tmp_list;
1651   guint32 time = GDK_CURRENT_TIME;
1652   GdkDragAction possible_actions, suggested_action;
1653
1654   g_return_val_if_fail (widget != NULL, NULL);
1655   g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1656   g_return_val_if_fail (target_list != NULL, NULL);
1657
1658   if (event)
1659     time = gdk_event_get_time (event);
1660
1661   info = g_new0 (GtkDragSourceInfo, 1);
1662   info->ipc_widget = gtk_drag_get_ipc_widget ();
1663   source_widgets = g_slist_prepend (source_widgets, info->ipc_widget);
1664
1665   gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1666
1667   tmp_list = g_list_last (target_list->list);
1668   while (tmp_list)
1669     {
1670       GtkTargetPair *pair = tmp_list->data;
1671       targets = g_list_prepend (targets, 
1672                                 GINT_TO_POINTER (pair->target));
1673       tmp_list = tmp_list->prev;
1674     }
1675
1676   info->widget = widget;
1677   gtk_widget_ref (info->widget);
1678   
1679   info->context = gdk_drag_begin (info->ipc_widget->window, targets);
1680   g_list_free (targets);
1681   
1682   g_object_set_qdata (G_OBJECT (info->context),
1683                      g_quark_from_static_string ("gtk-info"), info);
1684
1685   info->button = button;
1686   info->target_list = target_list;
1687   gtk_target_list_ref (target_list);
1688
1689   info->possible_actions = actions;
1690
1691   info->cursor = NULL;
1692   info->status = GTK_DRAG_STATUS_DRAG;
1693   info->last_event = NULL;
1694   info->selections = NULL;
1695   info->icon_window = NULL;
1696   info->destroy_icon = FALSE;
1697
1698   gtk_drag_get_event_actions (event, info->button, actions,
1699                               &suggested_action, &possible_actions);
1700   
1701   info->cursor = gtk_drag_get_cursor (suggested_action);
1702
1703   /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1704    * the drag icon, it will be in the right place
1705    */
1706   if (event && event->type == GDK_MOTION_NOTIFY)
1707     {
1708       info->cur_x = event->motion.x_root;
1709       info->cur_y = event->motion.y_root;
1710     }
1711   else 
1712     {
1713       gint x, y;
1714       gdk_window_get_pointer (GDK_ROOT_PARENT (), &x, &y, NULL);
1715
1716       info->cur_x = x;
1717       info->cur_y = y;
1718     }
1719
1720   gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1721                            info->context);
1722   
1723   if (event && event->type == GDK_MOTION_NOTIFY)
1724     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1725
1726   info->start_x = info->cur_x;
1727   info->start_y = info->cur_y;
1728
1729   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1730                       GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1731   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1732                       GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1733   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_press_event",
1734                       GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1735   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_release_event",
1736                       GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1737   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1738                       GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1739
1740   /* We use a GTK grab here to override any grabs that the widget
1741    * we are dragging from might have held
1742    */
1743   gtk_grab_add (info->ipc_widget);
1744   if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
1745                         GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1746                         GDK_BUTTON_RELEASE_MASK, NULL,
1747                         info->cursor, time) == 0)
1748     {
1749       if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
1750         {
1751           /* FIXME: This should be cleaned up... */
1752           GdkEventButton ev;
1753
1754           ev.time = time;
1755           ev.type = GDK_BUTTON_RELEASE;
1756           ev.button = info->button;
1757
1758           gtk_drag_button_release_cb (widget, &ev, info);
1759
1760           return NULL;
1761         }
1762     }
1763
1764   return info->context;
1765 }
1766
1767 /*************************************************************
1768  * gtk_drag_source_set:
1769  *     Register a drop site, and possibly add default behaviors.
1770  *   arguments:
1771  *     widget:
1772  *     start_button_mask: Mask of allowed buttons to start drag
1773  *     targets:           Table of targets for this source
1774  *     n_targets:
1775  *     actions:           Actions allowed for this source
1776  *   results:
1777  *************************************************************/
1778
1779 void 
1780 gtk_drag_source_set (GtkWidget            *widget,
1781                      GdkModifierType       start_button_mask,
1782                      const GtkTargetEntry *targets,
1783                      gint                  n_targets,
1784                      GdkDragAction         actions)
1785 {
1786   GtkDragSourceSite *site;
1787
1788   g_return_if_fail (widget != NULL);
1789
1790   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1791
1792   gtk_widget_add_events (widget,
1793                          gtk_widget_get_events (widget) |
1794                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1795                          GDK_BUTTON_MOTION_MASK);
1796
1797   if (site)
1798     {
1799       if (site->target_list)
1800         gtk_target_list_unref (site->target_list);
1801     }
1802   else
1803     {
1804       site = g_new0 (GtkDragSourceSite, 1);
1805       
1806       gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1807                           GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1808                           site);
1809       gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1810                           GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1811                           site);
1812       
1813       gtk_object_set_data_full (GTK_OBJECT (widget),
1814                                 "gtk-site-data", 
1815                                 site, gtk_drag_source_site_destroy);
1816     }
1817
1818   site->start_button_mask = start_button_mask;
1819
1820   if (targets)
1821     site->target_list = gtk_target_list_new (targets, n_targets);
1822   else
1823     site->target_list = NULL;
1824
1825   site->actions = actions;
1826
1827 }
1828
1829 /*************************************************************
1830  * gtk_drag_source_unset
1831  *     Unregister this widget as a drag source.
1832  *   arguments:
1833  *     widget:
1834  *   results:
1835  *************************************************************/
1836
1837 void 
1838 gtk_drag_source_unset (GtkWidget        *widget)
1839 {
1840   GtkDragSourceSite *site;
1841
1842   g_return_if_fail (widget != NULL);
1843
1844   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1845
1846   if (site)
1847     {
1848       gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1849       gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1850     }
1851 }
1852
1853 /*************************************************************
1854  * gtk_drag_source_set_icon:
1855  *     Set an icon for drags from this source.
1856  *   arguments:
1857  *     colormap: Colormap for this icon
1858  *     pixmap:
1859  *     mask
1860  *   results:
1861  *************************************************************/
1862
1863 void 
1864 gtk_drag_source_set_icon (GtkWidget     *widget,
1865                           GdkColormap   *colormap,
1866                           GdkPixmap     *pixmap,
1867                           GdkBitmap     *mask)
1868 {
1869   GtkDragSourceSite *site;
1870
1871   g_return_if_fail (widget != NULL);
1872
1873   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1874   g_return_if_fail (site != NULL);
1875   
1876   if (site->colormap)
1877     gdk_colormap_unref (site->colormap);
1878   if (site->pixmap)
1879     gdk_pixmap_unref (site->pixmap);
1880   if (site->mask)
1881     gdk_pixmap_unref (site->mask);
1882
1883   site->colormap = colormap;
1884   if (colormap)
1885     gdk_colormap_ref (colormap);
1886
1887   site->pixmap = pixmap;
1888   if (pixmap)
1889     gdk_pixmap_ref (pixmap);
1890
1891   site->mask = mask;
1892   if (mask)
1893     gdk_pixmap_ref (mask);
1894 }
1895
1896 /*************************************************************
1897  * gtk_drag_set_icon_window:
1898  *     Set a widget as the icon for a drag.
1899  *   arguments:
1900  *     context:
1901  *     widget:
1902  *     hot_x:    Hot spot
1903  *     hot_y:
1904  *   results:
1905  *************************************************************/
1906
1907 static void 
1908 gtk_drag_set_icon_window (GdkDragContext *context,
1909                           GtkWidget      *widget,
1910                           gint            hot_x,
1911                           gint            hot_y,
1912                           gboolean        destroy_on_release)
1913 {
1914   GtkDragSourceInfo *info;
1915
1916   g_return_if_fail (context != NULL);
1917   g_return_if_fail (widget != NULL);
1918
1919   info = g_object_get_qdata (G_OBJECT (context),
1920                             g_quark_from_static_string ("gtk-info"));
1921   gtk_drag_remove_icon (info);
1922
1923   info->icon_window = widget;
1924   info->hot_x = hot_x;
1925   info->hot_y = hot_y;
1926
1927   if (widget)
1928     {
1929       gtk_widget_set_uposition (widget, 
1930                                 info->cur_x - info->hot_x, 
1931                                 info->cur_y - info->hot_y);
1932       gtk_widget_ref (widget);
1933       gdk_window_raise (widget->window);
1934       gtk_widget_show (widget);
1935     }
1936
1937   info->destroy_icon = destroy_on_release;
1938 }
1939
1940 /*************************************************************
1941  * gtk_drag_set_icon_widget:
1942  *     Set a widget as the icon for a drag.
1943  *   arguments:
1944  *     context:
1945  *     widget:
1946  *     hot_x:    Hot spot
1947  *     hot_y:
1948  *   results:
1949  *************************************************************/
1950
1951 void 
1952 gtk_drag_set_icon_widget (GdkDragContext    *context,
1953                           GtkWidget         *widget,
1954                           gint               hot_x,
1955                           gint               hot_y)
1956 {
1957   g_return_if_fail (context != NULL);
1958   g_return_if_fail (widget != NULL);
1959
1960   gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
1961 }
1962
1963 /*************************************************************
1964  * gtk_drag_set_icon_pixmap:
1965  *     Set a widget as the icon for a drag.
1966  *   arguments:
1967  *     context:
1968  *     colormap: Colormap for the icon window.
1969  *     pixmap:   
1970  *     mask:
1971  *     hot_x:    Hot spot
1972  *     hot_y:
1973  *   results:
1974  *************************************************************/
1975
1976 void 
1977 gtk_drag_set_icon_pixmap (GdkDragContext    *context,
1978                           GdkColormap       *colormap,
1979                           GdkPixmap         *pixmap,
1980                           GdkBitmap         *mask,
1981                           gint               hot_x,
1982                           gint               hot_y)
1983 {
1984   GtkWidget *window;
1985   gint width, height;
1986       
1987   g_return_if_fail (context != NULL);
1988   g_return_if_fail (colormap != NULL);
1989   g_return_if_fail (pixmap != NULL);
1990
1991   gdk_window_get_size (pixmap, &width, &height);
1992
1993   gtk_widget_push_colormap (colormap);
1994
1995   window = gtk_window_new (GTK_WINDOW_POPUP);
1996   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
1997   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
1998
1999   gtk_widget_pop_colormap ();
2000
2001   gtk_widget_set_usize (window, width, height);
2002   gtk_widget_realize (window);
2003
2004   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2005   
2006   if (mask)
2007     gtk_widget_shape_combine_mask (window, mask, 0, 0);
2008
2009   gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2010 }
2011
2012 /*************************************************************
2013  * gtk_drag_set_icon_default:
2014  *     Set the icon for a drag to the default icon.
2015  *   arguments:
2016  *     context:
2017  *   results:
2018  *************************************************************/
2019
2020 void 
2021 gtk_drag_set_icon_default (GdkDragContext    *context)
2022 {
2023   g_return_if_fail (context != NULL);
2024
2025   if (!default_icon_pixmap)
2026     {
2027       default_icon_colormap = gdk_colormap_get_system ();
2028       default_icon_pixmap = 
2029         gdk_pixmap_colormap_create_from_xpm_d (NULL,
2030                                                default_icon_colormap,
2031                                                &default_icon_mask,
2032                                                NULL, (gchar **)drag_default_xpm);
2033       default_icon_hot_x = -2;
2034       default_icon_hot_y = -2;
2035     }
2036
2037   gtk_drag_set_icon_pixmap (context, 
2038                             default_icon_colormap, 
2039                             default_icon_pixmap, 
2040                             default_icon_mask,
2041                             default_icon_hot_x,
2042                             default_icon_hot_y);
2043 }
2044
2045 /*************************************************************
2046  * gtk_drag_set_default_icon:
2047  *     Set a default icon for all drags as a pixmap.
2048  *   arguments:
2049  *     colormap: Colormap for the icon window.
2050  *     pixmap:   
2051  *     mask:
2052  *     hot_x:    Hot spot
2053  *     hot_y:
2054  *   results:
2055  *************************************************************/
2056
2057 void 
2058 gtk_drag_set_default_icon (GdkColormap   *colormap,
2059                            GdkPixmap     *pixmap,
2060                            GdkBitmap     *mask,
2061                            gint           hot_x,
2062                            gint           hot_y)
2063 {
2064   g_return_if_fail (colormap != NULL);
2065   g_return_if_fail (pixmap != NULL);
2066   
2067   if (default_icon_colormap)
2068     gdk_colormap_unref (default_icon_colormap);
2069   if (default_icon_pixmap)
2070     gdk_pixmap_unref (default_icon_pixmap);
2071   if (default_icon_mask)
2072     gdk_pixmap_unref (default_icon_mask);
2073
2074   default_icon_colormap = colormap;
2075   gdk_colormap_ref (colormap);
2076   
2077   default_icon_pixmap = pixmap;
2078   gdk_pixmap_ref (pixmap);
2079
2080   default_icon_mask = mask;
2081   if (mask)
2082     gdk_pixmap_ref (mask);
2083   
2084   default_icon_hot_x = hot_x;
2085   default_icon_hot_y = hot_y;
2086 }
2087
2088
2089 /*************************************************************
2090  * gtk_drag_source_handle_event:
2091  *     Called from widget event handling code on Drag events
2092  *     for drag sources.
2093  *
2094  *   arguments:
2095  *     toplevel: Toplevel widget that received the event
2096  *     event:
2097  *   results:
2098  *************************************************************/
2099
2100 void
2101 gtk_drag_source_handle_event (GtkWidget *widget,
2102                               GdkEvent  *event)
2103 {
2104   GtkDragSourceInfo *info;
2105   GdkDragContext *context;
2106
2107   g_return_if_fail (widget != NULL);
2108   g_return_if_fail (event != NULL);
2109
2110   context = event->dnd.context;
2111   info = g_object_get_qdata (G_OBJECT (context),
2112                             g_quark_from_static_string ("gtk-info"));
2113   if (!info)
2114     return;
2115
2116   switch (event->type)
2117     {
2118     case GDK_DRAG_STATUS:
2119       {
2120         GdkCursor *cursor;
2121
2122         if (info->proxy_dest)
2123           {
2124             if (!event->dnd.send_event)
2125               {
2126                 if (info->proxy_dest->proxy_drop_wait)
2127                   {
2128                     gboolean result = context->action != 0;
2129                     
2130                     /* Aha - we can finally pass the MOTIF DROP on... */
2131                     gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2132                     if (result)
2133                       gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2134                     else
2135                       gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2136                   }
2137                 else
2138                   {
2139                     gdk_drag_status (info->proxy_dest->context,
2140                                      event->dnd.context->action,
2141                                      event->dnd.time);
2142                   }
2143               }
2144           }
2145         else
2146           {
2147             cursor = gtk_drag_get_cursor (event->dnd.context->action);
2148             if (info->cursor != cursor)
2149               {
2150 #ifdef GDK_WINDOWING_X11
2151                 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window), 
2152                                           PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
2153                                           ((GdkCursorPrivate *)cursor)->xcursor,
2154                                           event->dnd.time);
2155 #elif defined (GDK_WINDOWING_WIN32)
2156                 gdk_pointer_grab (widget->window, FALSE,
2157                                   GDK_POINTER_MOTION_MASK |
2158                                   GDK_POINTER_MOTION_HINT_MASK |
2159                                   GDK_BUTTON_RELEASE_MASK,
2160                                   NULL,
2161                                   info->cursor, event->dnd.time);
2162 #endif
2163                 info->cursor = cursor;
2164               }
2165             
2166             if (info->last_event)
2167               {
2168                 gtk_drag_update (info,
2169                                  info->cur_x, info->cur_y,
2170                                  info->last_event);
2171                 info->last_event = NULL;
2172               }
2173           }
2174       }
2175       break;
2176       
2177     case GDK_DROP_FINISHED:
2178       gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2179       break;
2180     default:
2181       g_assert_not_reached ();
2182     }
2183 }
2184
2185 /*************************************************************
2186  * gtk_drag_source_check_selection:
2187  *     Check if we've set up handlers/claimed the selection
2188  *     for a given drag. If not, add them.
2189  *   arguments:
2190  *     
2191  *   results:
2192  *************************************************************/
2193
2194 static void
2195 gtk_drag_source_check_selection (GtkDragSourceInfo *info, 
2196                                  GdkAtom            selection,
2197                                  guint32            time)
2198 {
2199   GList *tmp_list;
2200
2201   tmp_list = info->selections;
2202   while (tmp_list)
2203     {
2204       if (GPOINTER_TO_UINT (tmp_list->data) == selection)
2205         return;
2206       tmp_list = tmp_list->next;
2207     }
2208
2209   gtk_selection_owner_set (info->ipc_widget, selection, time);
2210   info->selections = g_list_prepend (info->selections,
2211                                      GUINT_TO_POINTER (selection));
2212
2213   tmp_list = info->target_list->list;
2214   while (tmp_list)
2215     {
2216       GtkTargetPair *pair = tmp_list->data;
2217
2218       gtk_selection_add_target (info->ipc_widget,
2219                                 selection,
2220                                 pair->target,
2221                                 pair->info);
2222       tmp_list = tmp_list->next;
2223     }
2224   
2225   if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2226     {
2227       gtk_selection_add_target (info->ipc_widget,
2228                                 selection,
2229                                 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2230                                 TARGET_MOTIF_SUCCESS);
2231       gtk_selection_add_target (info->ipc_widget,
2232                                 selection,
2233                                 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2234                                 TARGET_MOTIF_FAILURE);
2235     }
2236
2237   gtk_selection_add_target (info->ipc_widget,
2238                             selection,
2239                             gdk_atom_intern ("DELETE", FALSE),
2240                             TARGET_DELETE);
2241 }
2242
2243 /*************************************************************
2244  * gtk_drag_drop_finished:
2245  *     Clean up from the drag, and display snapback, if necessary.
2246  *   arguments:
2247  *     info:
2248  *     success:
2249  *     time:
2250  *   results:
2251  *************************************************************/
2252
2253 static void
2254 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2255                         gboolean           success,
2256                         guint              time)
2257 {
2258   gtk_drag_source_release_selections (info, time); 
2259
2260   if (info->proxy_dest)
2261     {
2262       /* The time from the event isn't reliable for Xdnd drags */
2263       gtk_drag_finish (info->proxy_dest->context, success, FALSE, 
2264                        info->proxy_dest->proxy_drop_time);
2265       gtk_drag_source_info_destroy (info);
2266     }
2267   else
2268     {
2269       if (success)
2270         {
2271           gtk_drag_source_info_destroy (info);
2272         }
2273       else
2274         {
2275           GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2276           anim->info = info;
2277           anim->step = 0;
2278           
2279           anim->n_steps = MAX (info->cur_x - info->start_x,
2280                                info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2281           anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2282           if (info->icon_window)
2283             {
2284               gtk_widget_show (info->icon_window);
2285               gdk_window_raise (info->icon_window->window);
2286             }
2287           
2288           /* Mark the context as dead, so if the destination decides
2289            * to respond really late, we still are OK.
2290            */
2291           g_object_set_qdata (G_OBJECT (info->context),
2292                              g_quark_from_static_string ("gtk-info"), NULL);
2293           gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2294         }
2295     }
2296 }
2297
2298 static void
2299 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2300                                     guint32            time)
2301 {
2302   GList *tmp_list = info->selections;
2303   while (tmp_list)
2304     {
2305       GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2306       if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2307         gtk_selection_owner_set (NULL, selection, time);
2308       tmp_list = tmp_list->next;
2309     }
2310
2311   g_list_free (info->selections);
2312   info->selections = NULL;
2313 }
2314
2315 /*************************************************************
2316  * gtk_drag_drop:
2317  *     Send a drop event.
2318  *   arguments:
2319  *     
2320  *   results:
2321  *************************************************************/
2322
2323 static void
2324 gtk_drag_drop (GtkDragSourceInfo *info, 
2325                guint32            time)
2326 {
2327   if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2328     {
2329       GtkSelectionData selection_data;
2330       GList *tmp_list;
2331       GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2332       
2333       tmp_list = info->target_list->list;
2334       while (tmp_list)
2335         {
2336           GtkTargetPair *pair = tmp_list->data;
2337           
2338           if (pair->target == target)
2339             {
2340               selection_data.selection = GDK_NONE;
2341               selection_data.target = target;
2342               selection_data.data = NULL;
2343               selection_data.length = -1;
2344               
2345               gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2346                                        info->context, &selection_data,
2347                                        pair->info, 
2348                                        time);
2349               
2350               /* FIXME: Should we check for length >= 0 here? */
2351               gtk_drag_drop_finished (info, TRUE, time);
2352               return;
2353             }
2354           tmp_list = tmp_list->next;
2355         }
2356       gtk_drag_drop_finished (info, FALSE, time);
2357     }
2358   else
2359     {
2360       if (info->icon_window)
2361         gtk_widget_hide (info->icon_window);
2362         
2363       gdk_drag_drop (info->context, time);
2364       info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2365                                             gtk_drag_abort_timeout,
2366                                             info);
2367     }
2368 }
2369
2370 /*
2371  * Source side callbacks.
2372  */
2373
2374 static gint
2375 gtk_drag_source_event_cb (GtkWidget      *widget,
2376                           GdkEvent       *event,
2377                           gpointer        data)
2378 {
2379   GtkDragSourceSite *site;
2380   site = (GtkDragSourceSite *)data;
2381
2382   switch (event->type)
2383     {
2384     case GDK_BUTTON_PRESS:
2385       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2386         {
2387           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2388           site->x = event->button.x;
2389           site->y = event->button.y;
2390         }
2391       break;
2392       
2393     case GDK_BUTTON_RELEASE:
2394       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2395         {
2396           site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2397         }
2398       break;
2399       
2400     case GDK_MOTION_NOTIFY:
2401       if (site->state & event->motion.state & site->start_button_mask)
2402         {
2403           /* FIXME: This is really broken and can leave us
2404            * with a stuck grab
2405            */
2406           int i;
2407           for (i=1; i<6; i++)
2408             {
2409               if (site->state & event->motion.state & 
2410                   GDK_BUTTON1_MASK << (i - 1))
2411                 break;
2412             }
2413           
2414           if (MAX (ABS (site->x - event->motion.x),
2415                    ABS (site->y - event->motion.y)) > 3)
2416             {
2417               GtkDragSourceInfo *info;
2418               GdkDragContext *context;
2419               
2420               site->state = 0;
2421               context = gtk_drag_begin (widget, site->target_list,
2422                                         site->actions, 
2423                                         i, event);
2424
2425               
2426               info = g_object_get_qdata (G_OBJECT (context),
2427                                          g_quark_from_static_string ("gtk-info"));
2428
2429               if (!info->icon_window)
2430                 {
2431                   if (site->pixmap)
2432                     gtk_drag_set_icon_pixmap (context,
2433                                               site->colormap,
2434                                               site->pixmap,
2435                                               site->mask, -2, -2);
2436                   else
2437                     gtk_drag_set_icon_default (context);
2438                 }
2439
2440               return TRUE;
2441             }
2442         }
2443       break;
2444       
2445     default:                    /* hit for 2/3BUTTON_PRESS */
2446       break;
2447     }
2448   return FALSE;
2449 }
2450
2451 static void 
2452 gtk_drag_source_site_destroy (gpointer data)
2453 {
2454   GtkDragSourceSite *site = data;
2455
2456   if (site->target_list)
2457     gtk_target_list_unref (site->target_list);
2458
2459   if (site->pixmap)
2460     gdk_pixmap_unref (site->pixmap);
2461   
2462   if (site->mask)
2463     gdk_pixmap_unref (site->mask);
2464   
2465   g_free (site);
2466 }
2467
2468 static void
2469 gtk_drag_selection_get (GtkWidget        *widget, 
2470                         GtkSelectionData *selection_data,
2471                         guint             sel_info,
2472                         guint32           time,
2473                         gpointer          data)
2474 {
2475   GtkDragSourceInfo *info = data;
2476   static GdkAtom null_atom = GDK_NONE;
2477   guint target_info;
2478
2479   if (!null_atom)
2480     null_atom = gdk_atom_intern ("NULL", FALSE);
2481
2482   switch (sel_info)
2483     {
2484     case TARGET_DELETE:
2485       gtk_signal_emit_by_name (GTK_OBJECT (info->widget), 
2486                                "drag_data_delete", 
2487                                info->context);
2488       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2489       break;
2490     case TARGET_MOTIF_SUCCESS:
2491       gtk_drag_drop_finished (info, TRUE, time);
2492       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2493       break;
2494     case TARGET_MOTIF_FAILURE:
2495       gtk_drag_drop_finished (info, FALSE, time);
2496       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2497       break;
2498     default:
2499       if (info->proxy_dest)
2500         {
2501           /* This is sort of dangerous and needs to be thought
2502            * through better
2503            */
2504           info->proxy_dest->proxy_data = selection_data;
2505           gtk_drag_get_data (info->widget,
2506                              info->proxy_dest->context,
2507                              selection_data->target,
2508                              time);
2509           gtk_main ();
2510           info->proxy_dest->proxy_data = NULL;
2511         }
2512       else
2513         {
2514           if (gtk_target_list_find (info->target_list, 
2515                                     selection_data->target, 
2516                                     &target_info))
2517             {
2518               gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2519                                        info->context, 
2520                                        selection_data, 
2521                                        target_info, 
2522                                        time);
2523             }
2524         }
2525       break;
2526     }
2527 }
2528
2529 static gint
2530 gtk_drag_anim_timeout (gpointer data)
2531 {
2532   GtkDragAnim *anim = data;
2533   gint x, y;
2534   gboolean retval;
2535
2536   GDK_THREADS_ENTER ();
2537
2538   if (anim->step == anim->n_steps)
2539     {
2540       gtk_drag_source_info_destroy (anim->info);
2541       g_free (anim);
2542
2543       retval = FALSE;
2544     }
2545   else
2546     {
2547       x = (anim->info->start_x * (anim->step + 1) +
2548            anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2549       y = (anim->info->start_y * (anim->step + 1) +
2550            anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2551       if (anim->info->icon_window)
2552         gtk_widget_set_uposition (anim->info->icon_window, 
2553                                   x - anim->info->hot_x, 
2554                                   y - anim->info->hot_y);
2555   
2556       anim->step++;
2557
2558       retval = TRUE;
2559     }
2560
2561   GDK_THREADS_LEAVE ();
2562
2563   return retval;
2564 }
2565
2566 static void
2567 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2568 {
2569   if (info->icon_window)
2570     {
2571       gtk_widget_hide (info->icon_window);
2572       if (info->destroy_icon)
2573         gtk_widget_destroy (info->icon_window);
2574
2575       gtk_widget_unref (info->icon_window);
2576       info->icon_window = NULL;
2577     }
2578 }
2579
2580 static void
2581 gtk_drag_source_info_destroy (gpointer data)
2582 {
2583   GtkDragSourceInfo *info = data;
2584
2585   gtk_drag_remove_icon (data);
2586
2587   if (!info->proxy_dest)
2588     gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end", 
2589                              info->context);
2590
2591   if (info->widget)
2592     gtk_widget_unref (info->widget);
2593
2594   gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2595   gtk_selection_remove_all (info->ipc_widget);
2596   gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2597   source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2598   gtk_drag_release_ipc_widget (info->ipc_widget);
2599
2600   gtk_target_list_unref (info->target_list);
2601
2602   g_object_set_qdata (G_OBJECT (info->context), g_quark_from_static_string ("gtk-info"), NULL);
2603   gdk_drag_context_unref (info->context);
2604
2605   if (info->drop_timeout)
2606     gtk_timeout_remove (info->drop_timeout);
2607
2608   g_free (info);
2609 }
2610
2611 /*************************************************************
2612  * gtk_drag_update:
2613  *     Function to update the status of the drag when the
2614  *     cursor moves or the modifier changes
2615  *   arguments:
2616  *     info: DragSourceInfo for the drag
2617  *     x_root, y_root: position of darg
2618  *     event: The event that triggered this call
2619  *   results:
2620  *************************************************************/
2621
2622 static void
2623 gtk_drag_update (GtkDragSourceInfo *info,
2624                  gint               x_root,
2625                  gint               y_root,
2626                  GdkEvent          *event)
2627 {
2628   GdkDragAction action;
2629   GdkDragAction possible_actions;
2630   GdkWindow *window = NULL;
2631   GdkWindow *dest_window;
2632   GdkDragProtocol protocol;
2633   GdkAtom selection;
2634   guint32 time = gtk_drag_get_event_time (event);
2635
2636   gtk_drag_get_event_actions (event,
2637                               info->button, 
2638                               info->possible_actions,
2639                               &action, &possible_actions);
2640   info->cur_x = x_root;
2641   info->cur_y = y_root;
2642
2643   if (info->icon_window)
2644     {
2645       gdk_window_raise (info->icon_window->window);
2646       gtk_widget_set_uposition (info->icon_window, 
2647                                 info->cur_x - info->hot_x, 
2648                                 info->cur_y - info->hot_y);
2649       window = info->icon_window->window;
2650     }
2651   
2652   gdk_drag_find_window (info->context,
2653                         window, x_root, y_root,
2654                         &dest_window, &protocol);
2655
2656   if (gdk_drag_motion (info->context, dest_window, protocol,
2657                        x_root, y_root, action, 
2658                        possible_actions,
2659                        time))
2660     {
2661       if (info->last_event != event) /* Paranoia, should not happen */
2662         {
2663           if (info->last_event)
2664             gdk_event_free ((GdkEvent *)info->last_event);
2665           info->last_event = gdk_event_copy ((GdkEvent *)event);
2666         }
2667     }
2668   else
2669     {
2670       if (info->last_event)
2671         {
2672           gdk_event_free ((GdkEvent *)info->last_event);
2673           info->last_event = NULL;
2674         }
2675     }
2676   
2677   if (dest_window)
2678     gdk_window_unref (dest_window);
2679
2680   selection = gdk_drag_get_selection (info->context);
2681   if (selection)
2682     gtk_drag_source_check_selection (info, selection, time);
2683 }
2684
2685 /*************************************************************
2686  * gtk_drag_end:
2687  *     Called when the user finishes to drag, either by
2688  *     releasing the mouse, or by pressing Esc.
2689  *   arguments:
2690  *     widget: GtkInvisible widget for this drag
2691  *     info: 
2692  *   results:
2693  *************************************************************/
2694
2695 static void
2696 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
2697 {
2698   GdkEvent send_event;
2699   GtkWidget *source_widget = info->widget;
2700
2701   gdk_pointer_ungrab (time);
2702   gdk_keyboard_ungrab (time);
2703
2704   gtk_grab_remove (info->ipc_widget);
2705
2706   gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget), 
2707                                  GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
2708                                  info);
2709   gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget), 
2710                                  GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
2711                                  info);
2712
2713   /* Send on a release pair to the the original 
2714    * widget to convince it to release its grab. We need to
2715    * call gtk_propagate_event() here, instead of 
2716    * gtk_widget_event() because widget like GtkList may
2717    * expect propagation.
2718    */
2719
2720   send_event.button.type = GDK_BUTTON_RELEASE;
2721   send_event.button.window = GDK_ROOT_PARENT ();
2722   send_event.button.send_event = TRUE;
2723   send_event.button.time = time;
2724   send_event.button.x = 0;
2725   send_event.button.y = 0;
2726   send_event.button.axes = NULL;
2727   send_event.button.state = 0;
2728   send_event.button.button = info->button;
2729   send_event.button.device = gdk_core_pointer;
2730   send_event.button.x_root = 0;
2731   send_event.button.y_root = 0;
2732
2733   gtk_propagate_event (source_widget, &send_event);
2734 }
2735
2736 /*************************************************************
2737  * gtk_drag_motion_cb:
2738  *     "motion_notify_event" callback during drag.
2739  *   arguments:
2740  *     
2741  *   results:
2742  *************************************************************/
2743
2744 static gint
2745 gtk_drag_motion_cb (GtkWidget      *widget, 
2746                     GdkEventMotion *event, 
2747                     gpointer        data)
2748 {
2749   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2750   gint x_root, y_root;
2751
2752   if (event->is_hint)
2753     {
2754       gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2755       event->x_root = x_root;
2756       event->y_root = y_root;
2757     }
2758
2759   gtk_drag_update (info, event->x_root, event->y_root, (GdkEvent *)event);
2760
2761   return TRUE;
2762 }
2763
2764 /*************************************************************
2765  * gtk_drag_key_cb:
2766  *     "key_press/release_event" callback during drag.
2767  *   arguments:
2768  *     
2769  *   results:
2770  *************************************************************/
2771
2772 static gint 
2773 gtk_drag_key_cb (GtkWidget         *widget, 
2774                  GdkEventKey       *event, 
2775                  gpointer           data)
2776 {
2777   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2778   GdkModifierType state;
2779   
2780   if (event->type == GDK_KEY_PRESS)
2781     {
2782       if (event->keyval == GDK_Escape)
2783         {
2784           gtk_drag_end (info, event->time);
2785           gdk_drag_abort (info->context, event->time);
2786           gtk_drag_drop_finished (info, FALSE, event->time);
2787
2788           return TRUE;
2789         }
2790     }
2791
2792   /* Now send a "motion" so that the modifier state is updated */
2793
2794   /* The state is not yet updated in the event, so we need
2795    * to query it here. We could use XGetModifierMapping, but
2796    * that would be overkill.
2797    */
2798   gdk_window_get_pointer (GDK_ROOT_PARENT(), NULL, NULL, &state);
2799
2800   event->state = state;
2801   gtk_drag_update (info, info->cur_x, info->cur_y, (GdkEvent *)event);
2802
2803   return TRUE;
2804 }
2805
2806 /*************************************************************
2807  * gtk_drag_button_release_cb:
2808  *     "button_release_event" callback during drag.
2809  *   arguments:
2810  *     
2811  *   results:
2812  *************************************************************/
2813
2814 static gint
2815 gtk_drag_button_release_cb (GtkWidget      *widget, 
2816                             GdkEventButton *event, 
2817                             gpointer        data)
2818 {
2819   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2820
2821   if (event->button != info->button)
2822     return FALSE;
2823
2824   gtk_drag_end (info, event->time);
2825
2826   if ((info->context->action != 0) && (info->context->dest_window != NULL))
2827     {
2828       gtk_drag_drop (info, event->time);
2829     }
2830   else
2831     {
2832       gdk_drag_abort (info->context, event->time);
2833       gtk_drag_drop_finished (info, FALSE, event->time);
2834     }
2835
2836   return TRUE;
2837 }
2838
2839 static gint
2840 gtk_drag_abort_timeout (gpointer data)
2841 {
2842   GtkDragSourceInfo *info = data;
2843   guint32 time = GDK_CURRENT_TIME;
2844
2845   GDK_THREADS_ENTER ();
2846
2847   if (info->proxy_dest)
2848     time = info->proxy_dest->proxy_drop_time;
2849
2850   info->drop_timeout = 0;
2851   gtk_drag_drop_finished (info, FALSE, time);
2852   
2853   GDK_THREADS_LEAVE ();
2854   
2855   return FALSE;
2856 }