]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd.c
fix compile warnings
[~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 "config.h"
28
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "gdkconfig.h"
33
34 #include "gdk/gdkkeysyms.h"
35
36 #ifdef GDK_WINDOWING_X11
37 #include <X11/Xlib.h>
38 #include <X11/keysym.h>
39 #include "gdk/x11/gdkx.h"
40 #endif
41
42 #include "gtkdnd.h"
43 #include "gtkiconfactory.h"
44 #include "gtkicontheme.h"
45 #include "gtkimage.h"
46 #include "gtkinvisible.h"
47 #include "gtkmain.h"
48 #include "gtkplug.h"
49 #include "gtkstock.h"
50 #include "gtkwindow.h"
51 #include "gtkintl.h"
52 #include "gtkdndcursors.h"
53 #include "gtkalias.h"
54
55 static GSList *source_widgets = NULL;
56
57 typedef struct _GtkDragSourceSite GtkDragSourceSite;
58 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
59 typedef struct _GtkDragDestSite GtkDragDestSite;
60 typedef struct _GtkDragDestInfo GtkDragDestInfo;
61 typedef struct _GtkDragAnim GtkDragAnim;
62 typedef struct _GtkDragFindData GtkDragFindData;
63
64
65 typedef enum 
66 {
67   GTK_DRAG_STATUS_DRAG,
68   GTK_DRAG_STATUS_WAIT,
69   GTK_DRAG_STATUS_DROP
70 } GtkDragStatus;
71
72 struct _GtkDragSourceSite 
73 {
74   GdkModifierType    start_button_mask;
75   GtkTargetList     *target_list;        /* Targets for drag data */
76   GdkDragAction      actions;            /* Possible actions */
77
78   /* Drag icon */
79   GtkImageType icon_type;
80   union
81   {
82     GtkImagePixmapData pixmap;
83     GtkImagePixbufData pixbuf;
84     GtkImageStockData stock;
85     GtkImageIconNameData name;
86   } icon_data;
87   GdkBitmap *icon_mask;
88
89   GdkColormap       *colormap;           /* Colormap for drag icon */
90
91   /* Stored button press information to detect drag beginning */
92   gint               state;
93   gint               x, y;
94 };
95   
96 struct _GtkDragSourceInfo 
97 {
98   GtkWidget         *widget;
99   GtkTargetList     *target_list; /* Targets for drag data */
100   GdkDragAction      possible_actions; /* Actions allowed by source */
101   GdkDragContext    *context;     /* drag context */
102   GtkWidget         *icon_window; /* Window for drag */
103   GtkWidget         *fallback_icon; /* Window for drag used on other screens */
104   GtkWidget         *ipc_widget;  /* GtkInvisible for grab, message passing */
105   GdkCursor         *cursor;      /* Cursor for drag */
106   gint hot_x, hot_y;              /* Hot spot for drag */
107   gint button;                    /* mouse button starting drag */
108
109   GtkDragStatus      status;      /* drag status */
110   GdkEvent          *last_event;  /* pending event */
111
112   gint               start_x, start_y; /* Initial position */
113   gint               cur_x, cur_y;     /* Current Position */
114   GdkScreen         *cur_screen;       /* Current screen for pointer */
115
116   guint32            grab_time;   /* timestamp for initial grab */
117   GList             *selections;  /* selections we've claimed */
118   
119   GtkDragDestInfo   *proxy_dest;  /* Set if this is a proxy drag */
120
121   guint              update_idle;      /* Idle function to update the drag */
122   guint              drop_timeout;     /* Timeout for aborting drop */
123   guint              destroy_icon : 1; /* If true, destroy icon_window
124                                         */
125   guint              have_grab : 1;    /* Do we still have the pointer grab
126                                         */
127   GdkPixbuf         *icon_pixbuf;
128   GdkCursor         *drag_cursors[6];
129 };
130
131 struct _GtkDragDestSite 
132 {
133   GtkDestDefaults    flags;
134   GtkTargetList     *target_list;
135   GdkDragAction      actions;
136   GdkWindow         *proxy_window;
137   GdkDragProtocol    proxy_protocol;
138   guint              do_proxy : 1;
139   guint              proxy_coords : 1;
140   guint              have_drag : 1;
141   guint              track_motion : 1;
142 };
143   
144 struct _GtkDragDestInfo 
145 {
146   GtkWidget         *widget;       /* Widget in which drag is in */
147   GdkDragContext    *context;      /* Drag context */
148   GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
149   GtkSelectionData  *proxy_data;   /* Set while retrieving proxied data */
150   guint              dropped : 1;     /* Set after we receive a drop */
151   guint32            proxy_drop_time; /* Timestamp for proxied drop */
152   guint              proxy_drop_wait : 1; /* Set if we are waiting for a
153                                            * status reply before sending
154                                            * a proxied drop on.
155                                            */
156   gint               drop_x, drop_y; /* Position of drop */
157 };
158
159 #define DROP_ABORT_TIME 300000
160
161 #define ANIM_STEP_TIME 50
162 #define ANIM_STEP_LENGTH 50
163 #define ANIM_MIN_STEPS 5
164 #define ANIM_MAX_STEPS 10
165
166 struct _GtkDragAnim 
167 {
168   GtkDragSourceInfo *info;
169   gint step;
170   gint n_steps;
171 };
172
173 struct _GtkDragFindData 
174 {
175   gint x;
176   gint y;
177   GdkDragContext *context;
178   GtkDragDestInfo *info;
179   gboolean found;
180   gboolean toplevel;
181   gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
182                         gint x, gint y, guint32 time);
183   guint32 time;
184 };
185
186 /* Enumeration for some targets we handle internally */
187
188 enum {
189   TARGET_MOTIF_SUCCESS = 0x40000000,
190   TARGET_MOTIF_FAILURE,
191   TARGET_DELETE
192 };
193
194 /* Drag icons */
195
196 static GdkPixmap   *default_icon_pixmap = NULL;
197 static GdkPixmap   *default_icon_mask = NULL;
198 static GdkColormap *default_icon_colormap = NULL;
199 static gint         default_icon_hot_x;
200 static gint         default_icon_hot_y;
201
202 /* Forward declarations */
203 static void          gtk_drag_get_event_actions (GdkEvent        *event, 
204                                                  gint             button,
205                                                  GdkDragAction    actions,
206                                                  GdkDragAction   *suggested_action,
207                                                  GdkDragAction   *possible_actions);
208 static GdkCursor *   gtk_drag_get_cursor         (GdkDisplay     *display,
209                                                   GdkDragAction   action,
210                                                   GtkDragSourceInfo *info);
211 static void          gtk_drag_update_cursor      (GtkDragSourceInfo *info);
212 static GtkWidget    *gtk_drag_get_ipc_widget            (GtkWidget *widget);
213 static GtkWidget    *gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen);
214 static void          gtk_drag_release_ipc_widget (GtkWidget      *widget);
215
216 static gboolean      gtk_drag_highlight_expose   (GtkWidget      *widget,
217                                                   GdkEventExpose *event,
218                                                   gpointer        data);
219
220 static void     gtk_drag_selection_received     (GtkWidget        *widget,
221                                                  GtkSelectionData *selection_data,
222                                                  guint             time,
223                                                  gpointer          data);
224 static void     gtk_drag_find_widget            (GtkWidget        *widget,
225                                                  GtkDragFindData  *data);
226 static void     gtk_drag_proxy_begin            (GtkWidget        *widget,
227                                                  GtkDragDestInfo  *dest_info,
228                                                  guint32           time);
229 static void     gtk_drag_dest_realized          (GtkWidget        *widget);
230 static void     gtk_drag_dest_hierarchy_changed (GtkWidget        *widget,
231                                                  GtkWidget        *previous_toplevel);
232 static void     gtk_drag_dest_site_destroy      (gpointer          data);
233 static void     gtk_drag_dest_leave             (GtkWidget        *widget,
234                                                  GdkDragContext   *context,
235                                                  guint             time);
236 static gboolean gtk_drag_dest_motion            (GtkWidget        *widget,
237                                                  GdkDragContext   *context,
238                                                  gint              x,
239                                                  gint              y,
240                                                  guint             time);
241 static gboolean gtk_drag_dest_drop              (GtkWidget        *widget,
242                                                  GdkDragContext   *context,
243                                                  gint              x,
244                                                  gint              y,
245                                                  guint             time);
246
247 static GtkDragDestInfo *  gtk_drag_get_dest_info     (GdkDragContext *context,
248                                                       gboolean        create);
249 static GtkDragSourceInfo *gtk_drag_get_source_info   (GdkDragContext *context,
250                                                       gboolean        create);
251 static void               gtk_drag_clear_source_info (GdkDragContext *context);
252
253 static void gtk_drag_source_check_selection    (GtkDragSourceInfo *info, 
254                                                 GdkAtom            selection,
255                                                 guint32            time);
256 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
257                                                 guint32            time);
258 static void gtk_drag_drop                      (GtkDragSourceInfo *info,
259                                                 guint32            time);
260 static void gtk_drag_drop_finished             (GtkDragSourceInfo *info,
261                                                 GtkDragResult      result,
262                                                 guint              time);
263 static void gtk_drag_cancel                    (GtkDragSourceInfo *info,
264                                                 GtkDragResult      result,
265                                                 guint32            time);
266
267 static gboolean gtk_drag_source_event_cb       (GtkWidget         *widget,
268                                                 GdkEvent          *event,
269                                                 gpointer           data);
270 static void gtk_drag_source_site_destroy       (gpointer           data);
271 static void gtk_drag_selection_get             (GtkWidget         *widget, 
272                                                 GtkSelectionData  *selection_data,
273                                                 guint              sel_info,
274                                                 guint32            time,
275                                                 gpointer           data);
276 static gboolean gtk_drag_anim_timeout          (gpointer           data);
277 static void gtk_drag_remove_icon               (GtkDragSourceInfo *info);
278 static void gtk_drag_source_info_destroy       (GtkDragSourceInfo *info);
279 static void gtk_drag_add_update_idle           (GtkDragSourceInfo *info);
280
281 static void gtk_drag_update                    (GtkDragSourceInfo *info,
282                                                 GdkScreen         *screen,
283                                                 gint               x_root,
284                                                 gint               y_root,
285                                                 GdkEvent          *event);
286 static gboolean gtk_drag_motion_cb             (GtkWidget         *widget, 
287                                                 GdkEventMotion    *event, 
288                                                 gpointer           data);
289 static gboolean gtk_drag_key_cb                (GtkWidget         *widget, 
290                                                 GdkEventKey       *event, 
291                                                 gpointer           data);
292 static gboolean gtk_drag_grab_broken_event_cb  (GtkWidget          *widget,
293                                                 GdkEventGrabBroken *event,
294                                                 gpointer            data);
295 static void     gtk_drag_grab_notify_cb        (GtkWidget         *widget,
296                                                 gboolean           was_grabbed,
297                                                 gpointer           data);
298 static gboolean gtk_drag_button_release_cb     (GtkWidget         *widget, 
299                                                 GdkEventButton    *event, 
300                                                 gpointer           data);
301 static gboolean gtk_drag_abort_timeout         (gpointer           data);
302
303 static void     set_icon_stock_pixbuf          (GdkDragContext    *context,
304                                                 const gchar       *stock_id,
305                                                 GdkPixbuf         *pixbuf,
306                                                 gint               hot_x,
307                                                 gint               hot_y,
308                                                 gboolean           force_window);
309
310 /************************
311  * Cursor and Icon data *
312  ************************/
313
314 static struct {
315   GdkDragAction action;
316   const gchar  *name;
317   const guint8 *data;
318   GdkPixbuf    *pixbuf;
319   GdkCursor    *cursor;
320 } drag_cursors[] = {
321   { GDK_ACTION_DEFAULT, NULL },
322   { GDK_ACTION_ASK,   "dnd-ask",  dnd_cursor_ask,  NULL, NULL },
323   { GDK_ACTION_COPY,  "dnd-copy", dnd_cursor_copy, NULL, NULL },
324   { GDK_ACTION_MOVE,  "dnd-move", dnd_cursor_move, NULL, NULL },
325   { GDK_ACTION_LINK,  "dnd-link", dnd_cursor_link, NULL, NULL },
326   { 0              ,  "dnd-none", dnd_cursor_none, NULL, NULL },
327 };
328
329 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
330
331 /*********************
332  * Utility functions *
333  *********************/
334
335 static void
336 set_can_change_screen (GtkWidget *widget,
337                        gboolean   can_change_screen)
338 {
339   can_change_screen = can_change_screen != FALSE;
340   
341   g_object_set_data (G_OBJECT (widget), I_("gtk-dnd-can-change-screen"),
342                      GUINT_TO_POINTER (can_change_screen));
343 }
344
345 static gboolean
346 get_can_change_screen (GtkWidget *widget)
347 {
348   return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
349
350 }
351
352 static GtkWidget *
353 gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen)
354 {
355   GtkWidget *result;
356   GSList *drag_widgets = g_object_get_data (G_OBJECT (screen), 
357                                             "gtk-dnd-ipc-widgets");
358   
359   if (drag_widgets)
360     {
361       GSList *tmp = drag_widgets;
362       result = drag_widgets->data;
363       drag_widgets = drag_widgets->next;
364       g_object_set_data (G_OBJECT (screen),
365                          I_("gtk-dnd-ipc-widgets"),
366                          drag_widgets);
367       g_slist_free_1 (tmp);
368     }
369   else
370     {
371       result = gtk_window_new (GTK_WINDOW_POPUP);
372       gtk_window_set_screen (GTK_WINDOW (result), screen);
373       gtk_window_resize (GTK_WINDOW (result), 1, 1);
374       gtk_window_move (GTK_WINDOW (result), -100, -100);
375       gtk_widget_show (result);
376     }  
377
378   return result;
379 }
380
381 static GtkWidget *
382 gtk_drag_get_ipc_widget (GtkWidget *widget)
383 {
384   GtkWidget *result;
385   GtkWidget *toplevel;
386
387   result = gtk_drag_get_ipc_widget_for_screen (gtk_widget_get_screen (widget));
388   
389   toplevel = gtk_widget_get_toplevel (widget);
390   
391   if (GTK_IS_WINDOW (toplevel))
392     {
393       if (GTK_WINDOW (toplevel)->group)
394         gtk_window_group_add_window (GTK_WINDOW (toplevel)->group, 
395                                      GTK_WINDOW (result));
396     }
397
398   return result;
399 }
400
401
402 #ifdef GDK_WINDOWING_X11
403
404 /*
405  * We want to handle a handful of keys during DND, e.g. Escape to abort.
406  * Grabbing the keyboard has the unfortunate side-effect of preventing
407  * useful things such as using Alt-Tab to cycle between windows or
408  * switching workspaces. Therefore, we just grab the few keys we are
409  * interested in. Note that we need to put the grabs on the root window
410  * in order for them to still work when the focus is moved to another
411  * app/workspace.
412  *
413  * GDK needs a little help to successfully deliver root key events...
414  */
415
416 static GdkFilterReturn
417 root_key_filter (GdkXEvent *xevent,
418                  GdkEvent  *event,
419                  gpointer   data)
420 {
421   XEvent *ev = (XEvent *)xevent;
422
423   if ((ev->type == KeyPress || ev->type == KeyRelease) &&
424       ev->xkey.root == ev->xkey.window)
425     ev->xkey.window = (Window)data;
426
427   return GDK_FILTER_CONTINUE;
428 }
429
430 typedef struct {
431   gint keysym;
432   gint modifiers;
433 } GrabKey;
434
435 static GrabKey grab_keys[] = {
436   { XK_Escape, 0 },
437   { XK_space, 0 },
438   { XK_KP_Space, 0 },
439   { XK_Return, 0 },
440   { XK_KP_Enter, 0 },
441   { XK_Up, 0 },
442   { XK_Up, Mod1Mask },
443   { XK_Down, 0 },
444   { XK_Down, Mod1Mask },
445   { XK_Left, 0 },
446   { XK_Left, Mod1Mask },
447   { XK_Right, 0 },
448   { XK_Right, Mod1Mask },
449   { XK_KP_Up, 0 },
450   { XK_KP_Up, Mod1Mask },
451   { XK_KP_Down, 0 },
452   { XK_KP_Down, Mod1Mask },
453   { XK_KP_Left, 0 },
454   { XK_KP_Left, Mod1Mask },
455   { XK_KP_Right, 0 },
456   { XK_KP_Right, Mod1Mask }
457 };
458
459 static void
460 grab_dnd_keys (GtkWidget *widget,
461                guint32    time)
462 {
463   guint i;
464   GdkWindow *window, *root;
465   gint keycode;
466
467   window = widget->window;
468   root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
469
470   gdk_error_trap_push ();
471
472   for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
473     {
474       keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
475       XGrabKey (GDK_WINDOW_XDISPLAY (window),
476                 keycode, grab_keys[i].modifiers,
477                 GDK_WINDOW_XID (root),
478                 FALSE,
479                 GrabModeAsync,
480                 GrabModeAsync);
481     }
482
483   gdk_flush ();
484   gdk_error_trap_pop ();
485
486   gdk_window_add_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
487 }
488
489 static void
490 ungrab_dnd_keys (GtkWidget *widget,
491                  guint32    time)
492 {
493   guint i;
494   GdkWindow *window, *root;
495   gint keycode;
496
497   window = widget->window;
498   root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
499
500   gdk_window_remove_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
501
502   gdk_error_trap_push ();
503
504   for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
505     {
506       keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
507       XUngrabKey (GDK_WINDOW_XDISPLAY (window),
508                   keycode, grab_keys[i].modifiers,
509                   GDK_WINDOW_XID (root));
510     }
511
512   gdk_flush ();
513   gdk_error_trap_pop ();
514 }
515
516 #else
517
518 static void
519 grab_dnd_keys (GtkWidget *widget,
520                guint32    time)
521 {
522   gdk_keyboard_grab (widget->window, FALSE, time);
523 }
524
525 static void
526 ungrab_dnd_keys (GtkWidget *widget,
527                  guint32    time)
528 {
529   gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), time);
530 }
531
532 #endif
533
534
535 /***************************************************************
536  * gtk_drag_release_ipc_widget:
537  *     Releases widget retrieved with gtk_drag_get_ipc_widget ()
538  *   arguments:
539  *     widget: the widget to release.
540  *   results:
541  ***************************************************************/
542
543 static void
544 gtk_drag_release_ipc_widget (GtkWidget *widget)
545 {
546   GtkWindow *window = GTK_WINDOW (widget);
547   GdkScreen *screen = gtk_widget_get_screen (widget);
548   GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
549                                             "gtk-dnd-ipc-widgets");
550   ungrab_dnd_keys (widget, GDK_CURRENT_TIME);
551   if (window->group)
552     gtk_window_group_remove_window (window->group, window);
553   drag_widgets = g_slist_prepend (drag_widgets, widget);
554   g_object_set_data (G_OBJECT (screen),
555                      I_("gtk-dnd-ipc-widgets"),
556                      drag_widgets);
557 }
558
559 static guint32
560 gtk_drag_get_event_time (GdkEvent *event)
561 {
562   guint32 tm = GDK_CURRENT_TIME;
563   
564   if (event)
565     switch (event->type)
566       {
567       case GDK_MOTION_NOTIFY:
568         tm = event->motion.time; break;
569       case GDK_BUTTON_PRESS:
570       case GDK_2BUTTON_PRESS:
571       case GDK_3BUTTON_PRESS:
572       case GDK_BUTTON_RELEASE:
573         tm = event->button.time; break;
574       case GDK_KEY_PRESS:
575       case GDK_KEY_RELEASE:
576         tm = event->key.time; break;
577       case GDK_ENTER_NOTIFY:
578       case GDK_LEAVE_NOTIFY:
579         tm = event->crossing.time; break;
580       case GDK_PROPERTY_NOTIFY:
581         tm = event->property.time; break;
582       case GDK_SELECTION_CLEAR:
583       case GDK_SELECTION_REQUEST:
584       case GDK_SELECTION_NOTIFY:
585         tm = event->selection.time; break;
586       case GDK_PROXIMITY_IN:
587       case GDK_PROXIMITY_OUT:
588         tm = event->proximity.time; break;
589       default:                  /* use current time */
590         break;
591       }
592   
593   return tm;
594 }
595
596 static void
597 gtk_drag_get_event_actions (GdkEvent *event, 
598                             gint button, 
599                             GdkDragAction  actions,
600                             GdkDragAction *suggested_action,
601                             GdkDragAction *possible_actions)
602 {
603   *suggested_action = 0;
604   *possible_actions = 0;
605
606   if (event)
607     {
608       GdkModifierType state = 0;
609       
610       switch (event->type)
611         {
612         case GDK_MOTION_NOTIFY:
613           state = event->motion.state;
614           break;
615         case GDK_BUTTON_PRESS:
616         case GDK_2BUTTON_PRESS:
617         case GDK_3BUTTON_PRESS:
618         case GDK_BUTTON_RELEASE:
619           state = event->button.state;
620           break;
621         case GDK_KEY_PRESS:
622         case GDK_KEY_RELEASE:
623           state = event->key.state;
624           break;
625         case GDK_ENTER_NOTIFY:
626         case GDK_LEAVE_NOTIFY:
627           state = event->crossing.state;
628           break;
629         default:
630           break;
631         }
632
633       if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
634         {
635           *suggested_action = GDK_ACTION_ASK;
636           *possible_actions = actions;
637         }
638       else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
639         {
640           if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
641             {
642               if (actions & GDK_ACTION_LINK)
643                 {
644                   *suggested_action = GDK_ACTION_LINK;
645                   *possible_actions = GDK_ACTION_LINK;
646                 }
647             }
648           else if (state & GDK_CONTROL_MASK)
649             {
650               if (actions & GDK_ACTION_COPY)
651                 {
652                   *suggested_action = GDK_ACTION_COPY;
653                   *possible_actions = GDK_ACTION_COPY;
654                 }
655               return;
656             }
657           else
658             {
659               if (actions & GDK_ACTION_MOVE)
660                 {
661                   *suggested_action = GDK_ACTION_MOVE;
662                   *possible_actions = GDK_ACTION_MOVE;
663                 }
664               return;
665             }
666         }
667       else
668         {
669           *possible_actions = actions;
670
671           if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
672             *suggested_action = GDK_ACTION_ASK;
673           else if (actions & GDK_ACTION_COPY)
674             *suggested_action =  GDK_ACTION_COPY;
675           else if (actions & GDK_ACTION_MOVE)
676             *suggested_action = GDK_ACTION_MOVE;
677           else if (actions & GDK_ACTION_LINK)
678             *suggested_action = GDK_ACTION_LINK;
679         }
680     }
681   else
682     {
683       *possible_actions = actions;
684       
685       if (actions & GDK_ACTION_COPY)
686         *suggested_action =  GDK_ACTION_COPY;
687       else if (actions & GDK_ACTION_MOVE)
688         *suggested_action = GDK_ACTION_MOVE;
689       else if (actions & GDK_ACTION_LINK)
690         *suggested_action = GDK_ACTION_LINK;
691     }
692 }
693
694 static gboolean
695 gtk_drag_can_use_rgba_cursor (GdkDisplay *display, 
696                               gint        width,
697                               gint        height)
698 {
699   guint max_width, max_height;
700   
701   if (!gdk_display_supports_cursor_color (display))
702     return FALSE;
703
704   if (!gdk_display_supports_cursor_alpha (display))
705     return FALSE;
706
707   gdk_display_get_maximal_cursor_size (display, 
708                                        &max_width,
709                                        &max_height);
710   if (width > max_width || height > max_height)
711     {
712        /* can't use rgba cursor (too large) */
713       return FALSE;
714     }
715
716   return TRUE;
717 }
718
719 static GdkCursor *
720 gtk_drag_get_cursor (GdkDisplay        *display,
721                      GdkDragAction      action,
722                      GtkDragSourceInfo *info)
723 {
724   gint i;
725
726   /* reconstruct the cursors for each new drag (thus !info),
727    * to catch cursor theme changes 
728    */ 
729   if (!info)
730     {
731       for (i = 0 ; i < n_drag_cursors - 1; i++)
732         if (drag_cursors[i].cursor != NULL)
733           {
734             gdk_cursor_unref (drag_cursors[i].cursor);
735             drag_cursors[i].cursor = NULL;
736           }
737     }
738  
739   for (i = 0 ; i < n_drag_cursors - 1; i++)
740     if (drag_cursors[i].action == action)
741       break;
742
743   if (drag_cursors[i].pixbuf == NULL)
744     drag_cursors[i].pixbuf = 
745       gdk_pixbuf_new_from_inline (-1, drag_cursors[i].data, FALSE, NULL);
746
747   if (drag_cursors[i].cursor != NULL)
748     {
749       if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
750         {
751           gdk_cursor_unref (drag_cursors[i].cursor);
752           drag_cursors[i].cursor = NULL;
753         }
754     }
755   
756   if (drag_cursors[i].cursor == NULL)
757     drag_cursors[i].cursor = gdk_cursor_new_from_name (display, drag_cursors[i].name);
758   
759   if (drag_cursors[i].cursor == NULL)
760     drag_cursors[i].cursor = gdk_cursor_new_from_pixbuf (display, drag_cursors[i].pixbuf, 0, 0);
761
762   if (info && info->icon_pixbuf) 
763     {
764       gint cursor_width, cursor_height;
765       gint icon_width, icon_height;
766       gint width, height;
767       GdkPixbuf *cursor_pixbuf, *pixbuf;
768       gint hot_x, hot_y;
769       gint icon_x, icon_y, ref_x, ref_y;
770
771       if (info->drag_cursors[i] != NULL)
772         {
773           if (display == gdk_cursor_get_display (info->drag_cursors[i]))
774             return info->drag_cursors[i];
775           
776           gdk_cursor_unref (info->drag_cursors[i]);
777           info->drag_cursors[i] = NULL;
778         }
779
780       icon_x = info->hot_x;
781       icon_y = info->hot_y;
782       icon_width = gdk_pixbuf_get_width (info->icon_pixbuf);
783       icon_height = gdk_pixbuf_get_height (info->icon_pixbuf);
784
785       hot_x = hot_y = 0;
786       cursor_pixbuf = gdk_cursor_get_image (drag_cursors[i].cursor);
787       if (!cursor_pixbuf)
788         cursor_pixbuf = g_object_ref (drag_cursors[i].pixbuf);
789       else
790         {
791           if (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"))
792             hot_x = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"));
793           
794           if (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"))
795             hot_y = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"));
796
797 #if 0     
798           /* The code below is an attempt to let cursor themes
799            * determine the attachment of the icon to enable things
800            * like the following:
801            *
802            *    +-----+
803            *    |     |
804            *    |     ||
805            *    +-----+|
806            *        ---+
807            * 
808            * It does not work since Xcursor doesn't allow to attach
809            * any additional information to cursors in a retrievable
810            * way  (there are comments, but no way to get at them
811            * short of searching for the actual cursor file).
812            * If this code ever gets used, the icon_window placement
813            * must be changed to recognize these placement options
814            * as well. Note that this code ignores info->hot_x/y.
815            */ 
816           for (j = 0; j < 10; j++)
817             {
818               const gchar *opt;
819               gchar key[32];
820               gchar **toks;
821               GtkAnchorType icon_anchor;
822
823               g_snprintf (key, 32, "comment%d", j);
824               opt = gdk_pixbuf_get_option (cursor_pixbuf, key);
825               if (opt && g_str_has_prefix ("icon-attach:", opt))
826                 {
827                   toks = g_strsplit (opt + strlen ("icon-attach:"), "'", -1);
828                   if (g_strv_length (toks) != 3)
829                     {
830                       g_strfreev (toks);
831                       break;
832                     }
833                   icon_anchor = atoi (toks[0]);
834                   icon_x = atoi (toks[1]);
835                   icon_y = atoi (toks[2]);
836                   
837                   switch (icon_anchor)
838                     {
839                     case GTK_ANCHOR_NORTH:
840                     case GTK_ANCHOR_CENTER:
841                     case GTK_ANCHOR_SOUTH:
842                       icon_x += icon_width / 2;
843                       break;
844                     case GTK_ANCHOR_NORTH_EAST:
845                     case GTK_ANCHOR_EAST:
846                     case GTK_ANCHOR_SOUTH_EAST:
847                       icon_x += icon_width;
848                       break;
849                     default: ;
850                     }
851                   
852                   switch (icon_anchor)
853                     {
854                     case GTK_ANCHOR_WEST:
855                     case GTK_ANCHOR_CENTER:
856                     case GTK_ANCHOR_EAST:
857                       icon_y += icon_height / 2;
858                       break;
859                     case GTK_ANCHOR_SOUTH_WEST:
860                     case GTK_ANCHOR_SOUTH:
861                     case GTK_ANCHOR_SOUTH_EAST:
862                       icon_x += icon_height;
863                       break;
864                     default: ;
865                     }
866
867                   g_strfreev (toks);
868                   break;
869                 }
870             }
871 #endif
872         }
873
874       cursor_width = gdk_pixbuf_get_width (cursor_pixbuf);
875       cursor_height = gdk_pixbuf_get_height (cursor_pixbuf);
876       
877       ref_x = MAX (hot_x, icon_x);
878       ref_y = MAX (hot_y, icon_y);
879       width = ref_x + MAX (cursor_width - hot_x, icon_width - icon_x);
880       height = ref_y + MAX (cursor_height - hot_y, icon_height - icon_y);
881          
882       if (gtk_drag_can_use_rgba_cursor (display, width, height))
883         {
884           /* Composite cursor and icon so that both hotspots
885            * end up at (ref_x, ref_y)
886            */
887           pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
888                                    width, height); 
889           
890           gdk_pixbuf_fill (pixbuf, 0xff000000);
891           
892           gdk_pixbuf_composite (info->icon_pixbuf, pixbuf,
893                                 ref_x - icon_x, ref_y - icon_y, 
894                                 icon_width, icon_height,
895                                 ref_x - icon_x, ref_y - icon_y, 
896                                 1.0, 1.0, 
897                                 GDK_INTERP_BILINEAR, 255);
898           
899           gdk_pixbuf_composite (cursor_pixbuf, pixbuf,
900                                 ref_x - hot_x, ref_y - hot_y, 
901                                 cursor_width, cursor_height,
902                                 ref_x - hot_x, ref_y - hot_y,
903                                 1.0, 1.0, 
904                                 GDK_INTERP_BILINEAR, 255);
905           
906           info->drag_cursors[i] = 
907             gdk_cursor_new_from_pixbuf (display, pixbuf, ref_x, ref_y);
908           
909           g_object_unref (pixbuf);
910         }
911       
912       g_object_unref (cursor_pixbuf);
913       
914       if (info->drag_cursors[i] != NULL)
915         return info->drag_cursors[i];
916     }
917  
918   return drag_cursors[i].cursor;
919 }
920
921 static void
922 gtk_drag_update_cursor (GtkDragSourceInfo *info)
923 {
924   GdkCursor *cursor;
925   gint i;
926
927   if (!info->have_grab)
928     return;
929
930   for (i = 0 ; i < n_drag_cursors - 1; i++)
931     if (info->cursor == drag_cursors[i].cursor ||
932         info->cursor == info->drag_cursors[i])
933       break;
934   
935   if (i == n_drag_cursors)
936     return;
937
938   cursor = gtk_drag_get_cursor (gdk_cursor_get_display (info->cursor), 
939                                 drag_cursors[i].action, info);
940   
941   if (cursor != info->cursor)
942     {
943       gdk_pointer_grab (info->ipc_widget->window, FALSE,
944                         GDK_POINTER_MOTION_MASK |
945                         GDK_BUTTON_RELEASE_MASK,
946                         NULL,
947                         cursor, info->grab_time);
948       info->cursor = cursor;
949     }
950 }
951
952 /********************
953  * Destination side *
954  ********************/
955
956 /*************************************************************
957  * gtk_drag_get_data:
958  *     Get the data for a drag or drop
959  *   arguments:
960  *     context - drag context
961  *     target  - format to retrieve the data in.
962  *     time    - timestamp of triggering event.
963  *     
964  *   results:
965  *************************************************************/
966
967 void 
968 gtk_drag_get_data (GtkWidget      *widget,
969                    GdkDragContext *context,
970                    GdkAtom         target,
971                    guint32         time)
972 {
973   GtkWidget *selection_widget;
974
975   g_return_if_fail (GTK_IS_WIDGET (widget));
976   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
977   g_return_if_fail (!context->is_source);
978
979   selection_widget = gtk_drag_get_ipc_widget (widget);
980
981   g_object_ref (context);
982   g_object_ref (widget);
983   
984   g_signal_connect (selection_widget, "selection-received",
985                     G_CALLBACK (gtk_drag_selection_received), widget);
986
987   g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
988
989   gtk_selection_convert (selection_widget,
990                          gdk_drag_get_selection (context),
991                          target,
992                          time);
993 }
994
995
996 /*************************************************************
997  * gtk_drag_get_source_widget:
998  *     Get the widget the was the source of this drag, if
999  *     the drag originated from this application.
1000  *   arguments:
1001  *     context: The drag context for this drag
1002  *   results:
1003  *     The source widget, or NULL if the drag originated from
1004  *     a different application.
1005  *************************************************************/
1006
1007 GtkWidget *
1008 gtk_drag_get_source_widget (GdkDragContext *context)
1009 {
1010   GSList *tmp_list;
1011
1012   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
1013   g_return_val_if_fail (!context->is_source, NULL);
1014   
1015   tmp_list = source_widgets;
1016   while (tmp_list)
1017     {
1018       GtkWidget *ipc_widget = tmp_list->data;
1019       
1020       if (ipc_widget->window == context->source_window)
1021         {
1022           GtkDragSourceInfo *info;
1023           info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
1024
1025           return info ? info->widget : NULL;
1026         }
1027
1028       tmp_list = tmp_list->next;
1029     }
1030
1031   return NULL;
1032 }
1033
1034 /*************************************************************
1035  * gtk_drag_finish:
1036  *     Notify the drag source that the transfer of data
1037  *     is complete.
1038  *   arguments:
1039  *     context: The drag context for this drag
1040  *     success: Was the data successfully transferred?
1041  *     time:    The timestamp to use when notifying the destination.
1042  *   results:
1043  *************************************************************/
1044
1045 void 
1046 gtk_drag_finish (GdkDragContext *context,
1047                  gboolean        success,
1048                  gboolean        del,
1049                  guint32         time)
1050 {
1051   GdkAtom target = GDK_NONE;
1052
1053   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1054   g_return_if_fail (!context->is_source);
1055
1056   if (success && del)
1057     {
1058       target = gdk_atom_intern_static_string ("DELETE");
1059     }
1060   else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
1061     {
1062       target = gdk_atom_intern_static_string (success ? 
1063                                               "XmTRANSFER_SUCCESS" : 
1064                                               "XmTRANSFER_FAILURE");
1065     }
1066
1067   if (target != GDK_NONE)
1068     {
1069       GtkWidget *selection_widget = gtk_drag_get_ipc_widget_for_screen (gdk_drawable_get_screen (context->source_window));
1070
1071       g_object_ref (context);
1072       
1073       g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
1074       g_signal_connect (selection_widget, "selection-received",
1075                         G_CALLBACK (gtk_drag_selection_received),
1076                         NULL);
1077       
1078       gtk_selection_convert (selection_widget,
1079                              gdk_drag_get_selection (context),
1080                              target,
1081                              time);
1082     }
1083   
1084   if (!(success && del))
1085     gdk_drop_finish (context, success, time);
1086 }
1087
1088 /*************************************************************
1089  * gtk_drag_highlight_expose:
1090  *     Callback for expose_event for highlighted widgets.
1091  *   arguments:
1092  *     widget:
1093  *     event:
1094  *     data:
1095  *   results:
1096  *************************************************************/
1097
1098 static gboolean
1099 gtk_drag_highlight_expose (GtkWidget      *widget,
1100                            GdkEventExpose *event,
1101                            gpointer        data)
1102 {
1103   gint x, y, width, height;
1104   
1105   if (GTK_WIDGET_DRAWABLE (widget))
1106     {
1107       cairo_t *cr;
1108       
1109       if (GTK_WIDGET_NO_WINDOW (widget))
1110         {
1111           x = widget->allocation.x;
1112           y = widget->allocation.y;
1113           width = widget->allocation.width;
1114           height = widget->allocation.height;
1115         }
1116       else
1117         {
1118           x = 0;
1119           y = 0;
1120           gdk_drawable_get_size (widget->window, &width, &height);
1121         }
1122       
1123       gtk_paint_shadow (widget->style, widget->window,
1124                         GTK_STATE_NORMAL, GTK_SHADOW_OUT,
1125                         &event->area, widget, "dnd",
1126                         x, y, width, height);
1127
1128       cr = gdk_cairo_create (widget->window);
1129       cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
1130       cairo_set_line_width (cr, 1.0);
1131       cairo_rectangle (cr,
1132                        x + 0.5, y + 0.5,
1133                        width - 1, height - 1);
1134       cairo_stroke (cr);
1135       cairo_destroy (cr);
1136     }
1137
1138   return FALSE;
1139 }
1140
1141 /*************************************************************
1142  * gtk_drag_highlight:
1143  *     Highlight the given widget in the default manner.
1144  *   arguments:
1145  *     widget:
1146  *   results:
1147  *************************************************************/
1148
1149 void 
1150 gtk_drag_highlight (GtkWidget  *widget)
1151 {
1152   g_return_if_fail (GTK_IS_WIDGET (widget));
1153
1154   g_signal_connect_after (widget, "expose-event",
1155                           G_CALLBACK (gtk_drag_highlight_expose),
1156                           NULL);
1157
1158   gtk_widget_queue_draw (widget);
1159 }
1160
1161 /*************************************************************
1162  * gtk_drag_unhighlight:
1163  *     Refresh the given widget to remove the highlight.
1164  *   arguments:
1165  *     widget:
1166  *   results:
1167  *************************************************************/
1168
1169 void 
1170 gtk_drag_unhighlight (GtkWidget *widget)
1171 {
1172   g_return_if_fail (GTK_IS_WIDGET (widget));
1173
1174   g_signal_handlers_disconnect_by_func (widget,
1175                                         gtk_drag_highlight_expose,
1176                                         NULL);
1177   
1178   gtk_widget_queue_draw (widget);
1179 }
1180
1181 static void
1182 gtk_drag_dest_set_internal (GtkWidget       *widget,
1183                             GtkDragDestSite *site)
1184 {
1185   GtkDragDestSite *old_site;
1186   
1187   g_return_if_fail (widget != NULL);
1188
1189   /* HACK, do this in the destroy */
1190   old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1191   if (old_site)
1192     {
1193       g_signal_handlers_disconnect_by_func (widget,
1194                                             gtk_drag_dest_realized,
1195                                             old_site);
1196       g_signal_handlers_disconnect_by_func (widget,
1197                                             gtk_drag_dest_hierarchy_changed,
1198                                             old_site);
1199
1200       site->track_motion = old_site->track_motion;
1201     }
1202
1203   if (GTK_WIDGET_REALIZED (widget))
1204     gtk_drag_dest_realized (widget);
1205
1206   g_signal_connect (widget, "realize",
1207                     G_CALLBACK (gtk_drag_dest_realized), site);
1208   g_signal_connect (widget, "hierarchy-changed",
1209                     G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
1210
1211   g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
1212                           site, gtk_drag_dest_site_destroy);
1213 }
1214
1215 /**
1216  * gtk_drag_dest_set:
1217  * @widget: a #GtkWidget
1218  * @flags: which types of default drag behavior to use
1219  * @targets: a pointer to an array of #GtkTargetEntry<!-- -->s indicating
1220  * the drop types that this @widget will accept. Later you can access the list
1221  * with gtk_drag_dest_get_target_list() and gtk_drag_dest_find_target().
1222  * @n_targets: the number of entries in @targets.
1223  * @actions: a bitmask of possible actions for a drop onto this @widget.
1224  *
1225  * Sets a widget as a potential drop destination, and adds default behaviors.
1226  *
1227  * The default behaviors listed in @flags have an effect similar
1228  * to installing default handlers for the widget's drag-and-drop signals
1229  * (#GtkWidget:drag-motion, #GtkWidget:drag-drop, ...). They all exist
1230  * for convenience. When passing #GTK_DEST_DEFAULT_ALL for instance it is
1231  * sufficient to connect to the widget's #GtkWidget::drag-data-received
1232  * signal to get primitive, but consistent drag-and-drop support.
1233  *
1234  * Things become more complicated when you try to preview the dragged data,
1235  * as described in the documentation for #GtkWidget:drag-motion. The default
1236  * behaviors described by @flags make some assumptions, that can conflict
1237  * with your own signal handlers. For instance #GTK_DEST_DEFAULT_DROP causes
1238  * invokations of gdk_drag_status() in the context of #GtkWidget:drag-motion,
1239  * and invokations of gtk_drag_finish() in #GtkWidget:drag-data-received.
1240  * Especially the later is dramatic, when your own #GtkWidget:drag-motion
1241  * handler calls gtk_drag_get_data() to inspect the dragged data.
1242  */
1243 void
1244 gtk_drag_dest_set (GtkWidget            *widget,
1245                    GtkDestDefaults       flags,
1246                    const GtkTargetEntry *targets,
1247                    gint                  n_targets,
1248                    GdkDragAction         actions)
1249 {
1250   GtkDragDestSite *site;
1251   
1252   g_return_if_fail (GTK_IS_WIDGET (widget));
1253
1254   site = g_new (GtkDragDestSite, 1);
1255
1256   site->flags = flags;
1257   site->have_drag = FALSE;
1258   if (targets)
1259     site->target_list = gtk_target_list_new (targets, n_targets);
1260   else
1261     site->target_list = NULL;
1262   site->actions = actions;
1263   site->do_proxy = FALSE;
1264   site->proxy_window = NULL;
1265   site->track_motion = FALSE;
1266
1267   gtk_drag_dest_set_internal (widget, site);
1268 }
1269
1270 /*************************************************************
1271  * gtk_drag_dest_set_proxy:
1272  *     Set up this widget to proxy drags elsewhere.
1273  *   arguments:
1274  *     widget:          
1275  *     proxy_window:    window to which forward drag events
1276  *     protocol:        Drag protocol which the dest widget accepts
1277  *     use_coordinates: If true, send the same coordinates to the
1278  *                      destination, because it is a embedded 
1279  *                      subwindow.
1280  *   results:
1281  *************************************************************/
1282
1283 void 
1284 gtk_drag_dest_set_proxy (GtkWidget      *widget,
1285                          GdkWindow      *proxy_window,
1286                          GdkDragProtocol protocol,
1287                          gboolean        use_coordinates)
1288 {
1289   GtkDragDestSite *site;
1290   
1291   g_return_if_fail (GTK_IS_WIDGET (widget));
1292   g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
1293
1294   site = g_new (GtkDragDestSite, 1);
1295
1296   site->flags = 0;
1297   site->have_drag = FALSE;
1298   site->target_list = NULL;
1299   site->actions = 0;
1300   site->proxy_window = proxy_window;
1301   if (proxy_window)
1302     g_object_ref (proxy_window);
1303   site->do_proxy = TRUE;
1304   site->proxy_protocol = protocol;
1305   site->proxy_coords = use_coordinates;
1306   site->track_motion = FALSE;
1307
1308   gtk_drag_dest_set_internal (widget, site);
1309 }
1310
1311 /*************************************************************
1312  * gtk_drag_dest_unset
1313  *     Unregister this widget as a drag target.
1314  *   arguments:
1315  *     widget:
1316  *   results:
1317  *************************************************************/
1318
1319 void 
1320 gtk_drag_dest_unset (GtkWidget *widget)
1321 {
1322   g_return_if_fail (GTK_IS_WIDGET (widget));
1323
1324   g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
1325 }
1326
1327 /**
1328  * gtk_drag_dest_get_target_list:
1329  * @widget: a #GtkWidget
1330  * 
1331  * Returns the list of targets this widget can accept from
1332  * drag-and-drop.
1333  * 
1334  * Return value: the #GtkTargetList, or %NULL if none
1335  **/
1336 GtkTargetList*
1337 gtk_drag_dest_get_target_list (GtkWidget *widget)
1338 {
1339   GtkDragDestSite *site;
1340
1341   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1342   
1343   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1344
1345   return site ? site->target_list : NULL;  
1346 }
1347
1348 /**
1349  * gtk_drag_dest_set_target_list:
1350  * @widget: a #GtkWidget that's a drag destination
1351  * @target_list: list of droppable targets, or %NULL for none
1352  * 
1353  * Sets the target types that this widget can accept from drag-and-drop.
1354  * The widget must first be made into a drag destination with
1355  * gtk_drag_dest_set().
1356  **/
1357 void
1358 gtk_drag_dest_set_target_list (GtkWidget      *widget,
1359                                GtkTargetList  *target_list)
1360 {
1361   GtkDragDestSite *site;
1362
1363   g_return_if_fail (GTK_IS_WIDGET (widget));
1364   
1365   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1366   
1367   if (!site)
1368     {
1369       g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
1370                  "to make the widget into a drag destination");
1371       return;
1372     }
1373
1374   if (target_list)
1375     gtk_target_list_ref (target_list);
1376   
1377   if (site->target_list)
1378     gtk_target_list_unref (site->target_list);
1379
1380   site->target_list = target_list;
1381 }
1382
1383 /**
1384  * gtk_drag_dest_add_text_targets:
1385  * @widget: a #GtkWidget that's a drag destination
1386  *
1387  * Add the text targets supported by #GtkSelection to
1388  * the target list of the drag destination. The targets
1389  * are added with @info = 0. If you need another value, 
1390  * use gtk_target_list_add_text_targets() and
1391  * gtk_drag_dest_set_target_list().
1392  * 
1393  * Since: 2.6
1394  **/
1395 void
1396 gtk_drag_dest_add_text_targets (GtkWidget *widget)
1397 {
1398   GtkTargetList *target_list;
1399
1400   target_list = gtk_drag_dest_get_target_list (widget);
1401   if (target_list)
1402     gtk_target_list_ref (target_list);
1403   else
1404     target_list = gtk_target_list_new (NULL, 0);
1405   gtk_target_list_add_text_targets (target_list, 0);
1406   gtk_drag_dest_set_target_list (widget, target_list);
1407   gtk_target_list_unref (target_list);
1408 }
1409
1410 /**
1411  * gtk_drag_dest_add_image_targets:
1412  * @widget: a #GtkWidget that's a drag destination
1413  *
1414  * Add the image targets supported by #GtkSelection to
1415  * the target list of the drag destination. The targets
1416  * are added with @info = 0. If you need another value, 
1417  * use gtk_target_list_add_image_targets() and
1418  * gtk_drag_dest_set_target_list().
1419  * 
1420  * Since: 2.6
1421  **/
1422 void
1423 gtk_drag_dest_add_image_targets (GtkWidget *widget)
1424 {
1425   GtkTargetList *target_list;
1426
1427   target_list = gtk_drag_dest_get_target_list (widget);
1428   if (target_list)
1429     gtk_target_list_ref (target_list);
1430   else
1431     target_list = gtk_target_list_new (NULL, 0);
1432   gtk_target_list_add_image_targets (target_list, 0, FALSE);
1433   gtk_drag_dest_set_target_list (widget, target_list);
1434   gtk_target_list_unref (target_list);
1435 }
1436
1437 /**
1438  * gtk_drag_dest_add_uri_targets:
1439  * @widget: a #GtkWidget that's a drag destination
1440  *
1441  * Add the URI targets supported by #GtkSelection to
1442  * the target list of the drag destination. The targets
1443  * are added with @info = 0. If you need another value, 
1444  * use gtk_target_list_add_uri_targets() and
1445  * gtk_drag_dest_set_target_list().
1446  * 
1447  * Since: 2.6
1448  **/
1449 void
1450 gtk_drag_dest_add_uri_targets (GtkWidget *widget)
1451 {
1452   GtkTargetList *target_list;
1453
1454   target_list = gtk_drag_dest_get_target_list (widget);
1455   if (target_list)
1456     gtk_target_list_ref (target_list);
1457   else
1458     target_list = gtk_target_list_new (NULL, 0);
1459   gtk_target_list_add_uri_targets (target_list, 0);
1460   gtk_drag_dest_set_target_list (widget, target_list);
1461   gtk_target_list_unref (target_list);
1462 }
1463
1464 /**
1465  * gtk_drag_dest_set_track_motion:
1466  * @widget: a #GtkWidget that's a drag destination
1467  * @track_motion: whether to accept all targets
1468  * 
1469  * Tells the widget to emit ::drag-motion and ::drag-leave
1470  * events regardless of the targets and the %GTK_DEST_DEFAULT_MOTION
1471  * flag. 
1472  *
1473  * This may be used when a widget wants to do generic
1474  * actions regardless of the targets that the source offers.
1475  *
1476  * Since: 2.10
1477  **/
1478 void
1479 gtk_drag_dest_set_track_motion (GtkWidget *widget,
1480                                 gboolean   track_motion)
1481 {
1482   GtkDragDestSite *site;
1483
1484   g_return_if_fail (GTK_IS_WIDGET (widget));
1485
1486   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1487   
1488   g_return_if_fail (site != NULL);
1489
1490   site->track_motion = track_motion != FALSE;
1491 }
1492
1493 /**
1494  * gtk_drag_dest_get_track_motion:
1495  * @widget: a #GtkWidget that's a drag destination
1496  * 
1497  * Returns whether the widget has been configured to always
1498  * emit ::drag-motion signals.
1499  * 
1500  * Return Value: %TRUE if the widget always emits ::drag-motion events
1501  *
1502  * Since: 2.10
1503  **/
1504 gboolean
1505 gtk_drag_dest_get_track_motion (GtkWidget *widget)
1506 {
1507   GtkDragDestSite *site;
1508
1509   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1510
1511   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1512
1513   if (site)
1514     return site->track_motion;
1515
1516   return FALSE;
1517 }
1518
1519 /*************************************************************
1520  * _gtk_drag_dest_handle_event:
1521  *     Called from widget event handling code on Drag events
1522  *     for destinations.
1523  *
1524  *   arguments:
1525  *     toplevel: Toplevel widget that received the event
1526  *     event:
1527  *   results:
1528  *************************************************************/
1529
1530 void
1531 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1532                             GdkEvent  *event)
1533 {
1534   GtkDragDestInfo *info;
1535   GdkDragContext *context;
1536
1537   g_return_if_fail (toplevel != NULL);
1538   g_return_if_fail (event != NULL);
1539
1540   context = event->dnd.context;
1541
1542   info = gtk_drag_get_dest_info (context, TRUE);
1543
1544   /* Find the widget for the event */
1545   switch (event->type)
1546     {
1547     case GDK_DRAG_ENTER:
1548       break;
1549       
1550     case GDK_DRAG_LEAVE:
1551       if (info->widget)
1552         {
1553           gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1554           info->widget = NULL;
1555         }
1556       break;
1557       
1558     case GDK_DRAG_MOTION:
1559     case GDK_DROP_START:
1560       {
1561         GtkDragFindData data;
1562         gint tx, ty;
1563
1564         if (event->type == GDK_DROP_START)
1565           {
1566             info->dropped = TRUE;
1567             /* We send a leave here so that the widget unhighlights
1568              * properly.
1569              */
1570             if (info->widget)
1571               {
1572                 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1573                 info->widget = NULL;
1574               }
1575           }
1576
1577 #ifdef GDK_WINDOWING_X11
1578         /* Hackaround for: http://bugzilla.gnome.org/show_bug.cgi?id=136112
1579          *
1580          * Currently gdk_window_get_position doesn't provide reliable
1581          * information for embedded windows, so we call the much more
1582          * expensive gdk_window_get_origin().
1583          */
1584         if (GTK_IS_PLUG (toplevel))
1585           gdk_window_get_origin (toplevel->window, &tx, &ty);
1586         else
1587 #endif /* GDK_WINDOWING_X11 */
1588           gdk_window_get_position (toplevel->window, &tx, &ty);
1589
1590         data.x = event->dnd.x_root - tx;
1591         data.y = event->dnd.y_root - ty;
1592         data.context = context;
1593         data.info = info;
1594         data.found = FALSE;
1595         data.toplevel = TRUE;
1596         data.callback = (event->type == GDK_DRAG_MOTION) ?
1597           gtk_drag_dest_motion : gtk_drag_dest_drop;
1598         data.time = event->dnd.time;
1599
1600         gtk_drag_find_widget (toplevel, &data);
1601
1602         if (info->widget && !data.found)
1603           {
1604             gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1605             info->widget = NULL;
1606           }
1607         
1608         /* Send a reply.
1609          */
1610         if (event->type == GDK_DRAG_MOTION)
1611           {
1612             if (!data.found)
1613               gdk_drag_status (context, 0, event->dnd.time);
1614           }
1615         else if (event->type == GDK_DROP_START && !info->proxy_source)
1616           {
1617             gdk_drop_reply (context, data.found, event->dnd.time);
1618             if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1619               gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1620           }
1621       }
1622       break;
1623
1624     default:
1625       g_assert_not_reached ();
1626     }
1627 }
1628
1629 /**
1630  * gtk_drag_dest_find_target:
1631  * @widget: drag destination widget
1632  * @context: drag context
1633  * @target_list: list of droppable targets, or %NULL to use
1634  *    gtk_drag_dest_get_target_list (@widget).
1635  * 
1636  * Looks for a match between @context->targets and the
1637  * @dest_target_list, returning the first matching target, otherwise
1638  * returning %GDK_NONE. @dest_target_list should usually be the return
1639  * value from gtk_drag_dest_get_target_list(), but some widgets may
1640  * have different valid targets for different parts of the widget; in
1641  * that case, they will have to implement a drag_motion handler that
1642  * passes the correct target list to this function.
1643  * 
1644  * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1645  **/
1646 GdkAtom
1647 gtk_drag_dest_find_target (GtkWidget      *widget,
1648                            GdkDragContext *context,
1649                            GtkTargetList  *target_list)
1650 {
1651   GList *tmp_target;
1652   GList *tmp_source = NULL;
1653   GtkWidget *source_widget;
1654
1655   g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1656   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1657   g_return_val_if_fail (!context->is_source, GDK_NONE);
1658
1659
1660   source_widget = gtk_drag_get_source_widget (context);
1661
1662   if (target_list == NULL)
1663     target_list = gtk_drag_dest_get_target_list (widget);
1664   
1665   if (target_list == NULL)
1666     return GDK_NONE;
1667   
1668   tmp_target = target_list->list;
1669   while (tmp_target)
1670     {
1671       GtkTargetPair *pair = tmp_target->data;
1672       tmp_source = context->targets;
1673       while (tmp_source)
1674         {
1675           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1676             {
1677               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1678                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)) &&
1679                   (!(pair->flags & GTK_TARGET_OTHER_APP) || !source_widget) &&
1680                   (!(pair->flags & GTK_TARGET_OTHER_WIDGET) || (source_widget != widget)))
1681                 return pair->target;
1682               else
1683                 break;
1684             }
1685           tmp_source = tmp_source->next;
1686         }
1687       tmp_target = tmp_target->next;
1688     }
1689
1690   return GDK_NONE;
1691 }
1692
1693 static void
1694 gtk_drag_selection_received (GtkWidget        *widget,
1695                              GtkSelectionData *selection_data,
1696                              guint             time,
1697                              gpointer          data)
1698 {
1699   GdkDragContext *context;
1700   GtkDragDestInfo *info;
1701   GtkWidget *drop_widget;
1702
1703   drop_widget = data;
1704
1705   context = g_object_get_data (G_OBJECT (widget), "drag-context");
1706   info = gtk_drag_get_dest_info (context, FALSE);
1707
1708   if (info->proxy_data && 
1709       info->proxy_data->target == selection_data->target)
1710     {
1711       gtk_selection_data_set (info->proxy_data,
1712                               selection_data->type,
1713                               selection_data->format,
1714                               selection_data->data,
1715                               selection_data->length);
1716       gtk_main_quit ();
1717       return;
1718     }
1719
1720   if (selection_data->target == gdk_atom_intern_static_string ("DELETE"))
1721     {
1722       gtk_drag_finish (context, TRUE, FALSE, time);
1723     }
1724   else if ((selection_data->target == gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS")) ||
1725            (selection_data->target == gdk_atom_intern_static_string ("XmTRANSFER_FAILURE")))
1726     {
1727       /* Do nothing */
1728     }
1729   else
1730     {
1731       GtkDragDestSite *site;
1732
1733       site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1734
1735       if (site && site->target_list)
1736         {
1737           guint target_info;
1738
1739           if (gtk_target_list_find (site->target_list, 
1740                                     selection_data->target,
1741                                     &target_info))
1742             {
1743               if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1744                   selection_data->length >= 0)
1745                 g_signal_emit_by_name (drop_widget,
1746                                        "drag-data-received",
1747                                        context, info->drop_x, info->drop_y,
1748                                        selection_data,
1749                                        target_info, time);
1750             }
1751         }
1752       else
1753         {
1754           g_signal_emit_by_name (drop_widget,
1755                                  "drag-data-received",
1756                                  context, info->drop_x, info->drop_y,
1757                                  selection_data,
1758                                  0, time);
1759         }
1760       
1761       if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1762         {
1763
1764           gtk_drag_finish (context, 
1765                            (selection_data->length >= 0),
1766                            (context->action == GDK_ACTION_MOVE),
1767                            time);
1768         }
1769       
1770       g_object_unref (drop_widget);
1771     }
1772
1773   g_signal_handlers_disconnect_by_func (widget,
1774                                         gtk_drag_selection_received,
1775                                         data);
1776   
1777   g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
1778   g_object_unref (context);
1779
1780   gtk_drag_release_ipc_widget (widget);
1781 }
1782
1783 static void
1784 prepend_and_ref_widget (GtkWidget *widget,
1785                         gpointer   data)
1786 {
1787   GSList **slist_p = data;
1788
1789   *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1790 }
1791
1792 /*************************************************************
1793  * gtk_drag_find_widget:
1794  *     Recursive callback used to locate widgets for 
1795  *     DRAG_MOTION and DROP_START events.
1796  *   arguments:
1797  *     
1798  *   results:
1799  *************************************************************/
1800
1801 static void
1802 gtk_drag_find_widget (GtkWidget       *widget,
1803                       GtkDragFindData *data)
1804 {
1805   GtkAllocation new_allocation;
1806   gint allocation_to_window_x = 0;
1807   gint allocation_to_window_y = 0;
1808   gint x_offset = 0;
1809   gint y_offset = 0;
1810
1811   if (data->found || !GTK_WIDGET_MAPPED (widget) || !GTK_WIDGET_SENSITIVE (widget))
1812     return;
1813
1814   /* Note that in the following code, we only count the
1815    * position as being inside a WINDOW widget if it is inside
1816    * widget->window; points that are outside of widget->window
1817    * but within the allocation are not counted. This is consistent
1818    * with the way we highlight drag targets.
1819    *
1820    * data->x,y are relative to widget->parent->window (if
1821    * widget is not a toplevel, widget->window otherwise).
1822    * We compute the allocation of widget in the same coordinates,
1823    * clipping to widget->window, and all intermediate
1824    * windows. If data->x,y is inside that, then we translate
1825    * our coordinates to be relative to widget->window and
1826    * recurse.
1827    */  
1828   new_allocation = widget->allocation;
1829
1830   if (widget->parent)
1831     {
1832       gint tx, ty;
1833       GdkWindow *window = widget->window;
1834
1835       /* Compute the offset from allocation-relative to
1836        * window-relative coordinates.
1837        */
1838       allocation_to_window_x = widget->allocation.x;
1839       allocation_to_window_y = widget->allocation.y;
1840
1841       if (!GTK_WIDGET_NO_WINDOW (widget))
1842         {
1843           /* The allocation is relative to the parent window for
1844            * window widgets, not to widget->window.
1845            */
1846           gdk_window_get_position (window, &tx, &ty);
1847           
1848           allocation_to_window_x -= tx;
1849           allocation_to_window_y -= ty;
1850         }
1851
1852       new_allocation.x = 0 + allocation_to_window_x;
1853       new_allocation.y = 0 + allocation_to_window_y;
1854       
1855       while (window && window != widget->parent->window)
1856         {
1857           GdkRectangle window_rect = { 0, 0, 0, 0 };
1858           
1859           gdk_drawable_get_size (window, &window_rect.width, &window_rect.height);
1860
1861           gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1862
1863           gdk_window_get_position (window, &tx, &ty);
1864           new_allocation.x += tx;
1865           x_offset += tx;
1866           new_allocation.y += ty;
1867           y_offset += ty;
1868           
1869           window = gdk_window_get_parent (window);
1870         }
1871
1872       if (!window)              /* Window and widget heirarchies didn't match. */
1873         return;
1874     }
1875
1876   if (data->toplevel ||
1877       ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1878        (data->x < new_allocation.x + new_allocation.width) && 
1879        (data->y < new_allocation.y + new_allocation.height)))
1880     {
1881       /* First, check if the drag is in a valid drop site in
1882        * one of our children 
1883        */
1884       if (GTK_IS_CONTAINER (widget))
1885         {
1886           GtkDragFindData new_data = *data;
1887           GSList *children = NULL;
1888           GSList *tmp_list;
1889           
1890           new_data.x -= x_offset;
1891           new_data.y -= y_offset;
1892           new_data.found = FALSE;
1893           new_data.toplevel = FALSE;
1894           
1895           /* need to reference children temporarily in case the
1896            * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
1897            */
1898           gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1899           for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1900             {
1901               if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1902                 gtk_drag_find_widget (tmp_list->data, &new_data);
1903               g_object_unref (tmp_list->data);
1904             }
1905           g_slist_free (children);
1906           
1907           data->found = new_data.found;
1908         }
1909
1910       /* If not, and this widget is registered as a drop site, check to
1911        * emit "drag-motion" to check if we are actually in
1912        * a drop site.
1913        */
1914       if (!data->found &&
1915           g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1916         {
1917           data->found = data->callback (widget,
1918                                         data->context,
1919                                         data->x - x_offset - allocation_to_window_x,
1920                                         data->y - y_offset - allocation_to_window_y,
1921                                         data->time);
1922           /* If so, send a "drag-leave" to the last widget */
1923           if (data->found)
1924             {
1925               if (data->info->widget && data->info->widget != widget)
1926                 {
1927                   gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1928                 }
1929               data->info->widget = widget;
1930             }
1931         }
1932     }
1933 }
1934
1935 static void
1936 gtk_drag_proxy_begin (GtkWidget       *widget, 
1937                       GtkDragDestInfo *dest_info,
1938                       guint32          time)
1939 {
1940   GtkDragSourceInfo *source_info;
1941   GList *tmp_list;
1942   GdkDragContext *context;
1943   GtkWidget *ipc_widget;
1944
1945   if (dest_info->proxy_source)
1946     {
1947       gdk_drag_abort (dest_info->proxy_source->context, time);
1948       gtk_drag_source_info_destroy (dest_info->proxy_source);
1949       dest_info->proxy_source = NULL;
1950     }
1951   
1952   ipc_widget = gtk_drag_get_ipc_widget (widget);
1953   context = gdk_drag_begin (ipc_widget->window,
1954                             dest_info->context->targets);
1955
1956   source_info = gtk_drag_get_source_info (context, TRUE);
1957
1958   source_info->ipc_widget = ipc_widget;
1959   source_info->widget = g_object_ref (widget);
1960
1961   source_info->target_list = gtk_target_list_new (NULL, 0);
1962   tmp_list = dest_info->context->targets;
1963   while (tmp_list)
1964     {
1965       gtk_target_list_add (source_info->target_list, 
1966                            GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1967       tmp_list = tmp_list->next;
1968     }
1969
1970   source_info->proxy_dest = dest_info;
1971   
1972   g_signal_connect (ipc_widget,
1973                     "selection-get",
1974                     G_CALLBACK (gtk_drag_selection_get),
1975                     source_info);
1976   
1977   dest_info->proxy_source = source_info;
1978 }
1979
1980 static void
1981 gtk_drag_dest_info_destroy (gpointer data)
1982 {
1983   GtkDragDestInfo *info = data;
1984
1985   g_free (info);
1986 }
1987
1988 static GtkDragDestInfo *
1989 gtk_drag_get_dest_info (GdkDragContext *context,
1990                         gboolean        create)
1991 {
1992   GtkDragDestInfo *info;
1993   static GQuark info_quark = 0;
1994   if (!info_quark)
1995     info_quark = g_quark_from_static_string ("gtk-dest-info");
1996   
1997   info = g_object_get_qdata (G_OBJECT (context), info_quark);
1998   if (!info && create)
1999     {
2000       info = g_new (GtkDragDestInfo, 1);
2001       info->widget = NULL;
2002       info->context = context;
2003       info->proxy_source = NULL;
2004       info->proxy_data = NULL;
2005       info->dropped = FALSE;
2006       info->proxy_drop_wait = FALSE;
2007       g_object_set_qdata_full (G_OBJECT (context), info_quark,
2008                                info, gtk_drag_dest_info_destroy);
2009     }
2010
2011   return info;
2012 }
2013
2014 static GQuark dest_info_quark = 0;
2015
2016 static GtkDragSourceInfo *
2017 gtk_drag_get_source_info (GdkDragContext *context,
2018                           gboolean        create)
2019 {
2020   GtkDragSourceInfo *info;
2021   if (!dest_info_quark)
2022     dest_info_quark = g_quark_from_static_string ("gtk-source-info");
2023   
2024   info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
2025   if (!info && create)
2026     {
2027       info = g_new0 (GtkDragSourceInfo, 1);
2028       info->context = context;
2029       g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
2030     }
2031
2032   return info;
2033 }
2034
2035 static void
2036 gtk_drag_clear_source_info (GdkDragContext *context)
2037 {
2038   g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
2039 }
2040
2041 static void
2042 gtk_drag_dest_realized (GtkWidget *widget)
2043 {
2044   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2045
2046   if (GTK_WIDGET_TOPLEVEL (toplevel))
2047     gdk_window_register_dnd (toplevel->window);
2048 }
2049
2050 static void
2051 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
2052                                  GtkWidget *previous_toplevel)
2053 {
2054   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2055
2056   if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
2057     gdk_window_register_dnd (toplevel->window);
2058 }
2059
2060 static void
2061 gtk_drag_dest_site_destroy (gpointer data)
2062 {
2063   GtkDragDestSite *site = data;
2064
2065   if (site->proxy_window)
2066     g_object_unref (site->proxy_window);
2067     
2068   if (site->target_list)
2069     gtk_target_list_unref (site->target_list);
2070
2071   g_free (site);
2072 }
2073
2074 /*
2075  * Default drag handlers
2076  */
2077 static void  
2078 gtk_drag_dest_leave (GtkWidget      *widget,
2079                      GdkDragContext *context,
2080                      guint           time)
2081 {
2082   GtkDragDestSite *site;
2083
2084   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2085   g_return_if_fail (site != NULL);
2086
2087   if (site->do_proxy)
2088     {
2089       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2090
2091       if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
2092         {
2093           gdk_drag_abort (info->proxy_source->context, time);
2094           gtk_drag_source_info_destroy (info->proxy_source);
2095           info->proxy_source = NULL;
2096         }
2097       
2098       return;
2099     }
2100   else
2101     {
2102       if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
2103         gtk_drag_unhighlight (widget);
2104
2105       if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
2106           site->track_motion)
2107         g_signal_emit_by_name (widget, "drag-leave", context, time);
2108       
2109       site->have_drag = FALSE;
2110     }
2111 }
2112
2113 static gboolean
2114 gtk_drag_dest_motion (GtkWidget      *widget,
2115                       GdkDragContext *context,
2116                       gint            x,
2117                       gint            y,
2118                       guint           time)
2119 {
2120   GtkDragDestSite *site;
2121   GdkDragAction action = 0;
2122   gboolean retval;
2123
2124   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2125   g_return_val_if_fail (site != NULL, FALSE);
2126
2127   if (site->do_proxy)
2128     {
2129       GdkAtom selection;
2130       GdkEvent *current_event;
2131       GdkWindow *dest_window;
2132       GdkDragProtocol proto;
2133         
2134       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2135
2136       if (!info->proxy_source || info->proxy_source->widget != widget)
2137         gtk_drag_proxy_begin (widget, info, time);
2138
2139       current_event = gtk_get_current_event ();
2140
2141       if (site->proxy_window)
2142         {
2143           dest_window = site->proxy_window;
2144           proto = site->proxy_protocol;
2145         }
2146       else
2147         {
2148           gdk_drag_find_window_for_screen (info->proxy_source->context,
2149                                            NULL,
2150                                            gdk_drawable_get_screen (current_event->dnd.window),
2151                                            current_event->dnd.x_root, 
2152                                            current_event->dnd.y_root,
2153                                            &dest_window, &proto);
2154         }
2155       
2156       gdk_drag_motion (info->proxy_source->context, 
2157                        dest_window, proto,
2158                        current_event->dnd.x_root, 
2159                        current_event->dnd.y_root, 
2160                        context->suggested_action, 
2161                        context->actions, time);
2162
2163       if (!site->proxy_window && dest_window)
2164         g_object_unref (dest_window);
2165
2166       selection = gdk_drag_get_selection (info->proxy_source->context);
2167       if (selection && 
2168           selection != gdk_drag_get_selection (info->context))
2169         gtk_drag_source_check_selection (info->proxy_source, selection, time);
2170
2171       gdk_event_free (current_event);
2172       
2173       return TRUE;
2174     }
2175
2176   if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
2177     {
2178       if (context->suggested_action & site->actions)
2179         action = context->suggested_action;
2180       else
2181         {
2182           gint i;
2183           
2184           for (i = 0; i < 8; i++)
2185             {
2186               if ((site->actions & (1 << i)) &&
2187                   (context->actions & (1 << i)))
2188                 {
2189                   action = (1 << i);
2190                   break;
2191                 }
2192             }
2193         }
2194
2195       if (action && gtk_drag_dest_find_target (widget, context, NULL))
2196         {
2197           if (!site->have_drag)
2198             {
2199               site->have_drag = TRUE;
2200               if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
2201                 gtk_drag_highlight (widget);
2202             }
2203
2204           gdk_drag_status (context, action, time);
2205         }
2206       else
2207         {
2208           gdk_drag_status (context, 0, time);
2209           if (!site->track_motion)
2210             return TRUE;
2211         }
2212     }
2213
2214   g_signal_emit_by_name (widget, "drag-motion",
2215                          context, x, y, time, &retval);
2216
2217   return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
2218 }
2219
2220 static gboolean
2221 gtk_drag_dest_drop (GtkWidget        *widget,
2222                     GdkDragContext   *context,
2223                     gint              x,
2224                     gint              y,
2225                     guint             time)
2226 {
2227   GtkDragDestSite *site;
2228   GtkDragDestInfo *info;
2229
2230   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2231   g_return_val_if_fail (site != NULL, FALSE);
2232
2233   info = gtk_drag_get_dest_info (context, FALSE);
2234   g_return_val_if_fail (info != NULL, FALSE);
2235
2236   info->drop_x = x;
2237   info->drop_y = y;
2238
2239   if (site->do_proxy)
2240     {
2241       if (info->proxy_source || 
2242           (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
2243         {
2244           gtk_drag_drop (info->proxy_source, time);
2245         }
2246       else
2247         {
2248           /* We need to synthesize a motion event, wait for a status,
2249            * and, if we get a good one, do a drop.
2250            */
2251           
2252           GdkEvent *current_event;
2253           GdkAtom selection;
2254           GdkWindow *dest_window;
2255           GdkDragProtocol proto;
2256           
2257           gtk_drag_proxy_begin (widget, info, time);
2258           info->proxy_drop_wait = TRUE;
2259           info->proxy_drop_time = time;
2260           
2261           current_event = gtk_get_current_event ();
2262
2263           if (site->proxy_window)
2264             {
2265               dest_window = site->proxy_window;
2266               proto = site->proxy_protocol;
2267             }
2268           else
2269             {
2270               gdk_drag_find_window_for_screen (info->proxy_source->context,
2271                                                NULL,
2272                                                gdk_drawable_get_screen (current_event->dnd.window),
2273                                                current_event->dnd.x_root, 
2274                                                current_event->dnd.y_root,
2275                                                &dest_window, &proto);
2276             }
2277
2278           gdk_drag_motion (info->proxy_source->context, 
2279                            dest_window, proto,
2280                            current_event->dnd.x_root, 
2281                            current_event->dnd.y_root, 
2282                            context->suggested_action, 
2283                            context->actions, time);
2284
2285           if (!site->proxy_window && dest_window)
2286             g_object_unref (dest_window);
2287
2288           selection = gdk_drag_get_selection (info->proxy_source->context);
2289           if (selection && 
2290               selection != gdk_drag_get_selection (info->context))
2291             gtk_drag_source_check_selection (info->proxy_source, selection, time);
2292
2293           gdk_event_free (current_event);
2294         }
2295
2296       return TRUE;
2297     }
2298   else
2299     {
2300       gboolean retval;
2301
2302       if (site->flags & GTK_DEST_DEFAULT_DROP)
2303         {
2304           GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
2305
2306           if (target == GDK_NONE)
2307             {
2308               gtk_drag_finish (context, FALSE, FALSE, time);
2309               return TRUE;
2310             }
2311           else 
2312             gtk_drag_get_data (widget, context, target, time);
2313         }
2314
2315       g_signal_emit_by_name (widget, "drag-drop",
2316                              context, x, y, time, &retval);
2317
2318       return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
2319     }
2320 }
2321
2322 /***************
2323  * Source side *
2324  ***************/
2325
2326 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
2327  * so that we can set the icon from the source site information
2328  */
2329 static GdkDragContext *
2330 gtk_drag_begin_internal (GtkWidget         *widget,
2331                          GtkDragSourceSite *site,
2332                          GtkTargetList     *target_list,
2333                          GdkDragAction      actions,
2334                          gint               button,
2335                          GdkEvent          *event)
2336 {
2337   GtkDragSourceInfo *info;
2338   GList *targets = NULL;
2339   GList *tmp_list;
2340   guint32 time = GDK_CURRENT_TIME;
2341   GdkDragAction possible_actions, suggested_action;
2342   GdkDragContext *context;
2343   GtkWidget *ipc_widget;
2344   GdkCursor *cursor;
2345  
2346   ipc_widget = gtk_drag_get_ipc_widget (widget);
2347   
2348   gtk_drag_get_event_actions (event, button, actions,
2349                               &suggested_action, &possible_actions);
2350   
2351   cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), 
2352                                 suggested_action,
2353                                 NULL);
2354   
2355   if (event)
2356     time = gdk_event_get_time (event);
2357
2358   if (gdk_pointer_grab (ipc_widget->window, FALSE,
2359                         GDK_POINTER_MOTION_MASK |
2360                         GDK_BUTTON_RELEASE_MASK, NULL,
2361                         cursor, time) != GDK_GRAB_SUCCESS)
2362     {
2363       gtk_drag_release_ipc_widget (ipc_widget);
2364       return NULL;
2365     }
2366
2367   grab_dnd_keys (ipc_widget, time);
2368
2369   /* We use a GTK grab here to override any grabs that the widget
2370    * we are dragging from might have held
2371    */
2372   gtk_grab_add (ipc_widget);
2373   
2374   tmp_list = g_list_last (target_list->list);
2375   while (tmp_list)
2376     {
2377       GtkTargetPair *pair = tmp_list->data;
2378       targets = g_list_prepend (targets, 
2379                                 GINT_TO_POINTER (pair->target));
2380       tmp_list = tmp_list->prev;
2381     }
2382
2383   source_widgets = g_slist_prepend (source_widgets, ipc_widget);
2384
2385   context = gdk_drag_begin (ipc_widget->window, targets);
2386   g_list_free (targets);
2387   
2388   info = gtk_drag_get_source_info (context, TRUE);
2389   
2390   info->ipc_widget = ipc_widget;
2391   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), info);
2392
2393   info->widget = g_object_ref (widget);
2394   
2395   info->button = button;
2396   info->cursor = cursor;
2397   info->target_list = target_list;
2398   gtk_target_list_ref (target_list);
2399
2400   info->possible_actions = actions;
2401
2402   info->status = GTK_DRAG_STATUS_DRAG;
2403   info->last_event = NULL;
2404   info->selections = NULL;
2405   info->icon_window = NULL;
2406   info->destroy_icon = FALSE;
2407
2408   /* Set cur_x, cur_y here so if the "drag-begin" signal shows
2409    * the drag icon, it will be in the right place
2410    */
2411   if (event && event->type == GDK_MOTION_NOTIFY)
2412     {
2413       info->cur_screen = gtk_widget_get_screen (widget);
2414       info->cur_x = event->motion.x_root;
2415       info->cur_y = event->motion.y_root;
2416     }
2417   else 
2418     {
2419       gdk_display_get_pointer (gtk_widget_get_display (widget),
2420                                &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
2421     }
2422
2423   g_signal_emit_by_name (widget, "drag-begin", info->context);
2424
2425   /* Ensure that we have an icon before we start the drag; the
2426    * application may have set one in ::drag_begin, or it may
2427    * not have set one.
2428    */
2429   if (!info->icon_window && !info->icon_pixbuf)
2430     {
2431       if (!site || site->icon_type == GTK_IMAGE_EMPTY)
2432         gtk_drag_set_icon_default (context);
2433       else
2434         switch (site->icon_type)
2435           {
2436           case GTK_IMAGE_PIXMAP:
2437             gtk_drag_set_icon_pixmap (context,
2438                                       site->colormap,
2439                                       site->icon_data.pixmap.pixmap,
2440                                       site->icon_mask,
2441                                       -2, -2);
2442             break;
2443           case GTK_IMAGE_PIXBUF:
2444             gtk_drag_set_icon_pixbuf (context,
2445                                       site->icon_data.pixbuf.pixbuf,
2446                                       -2, -2);
2447             break;
2448           case GTK_IMAGE_STOCK:
2449             gtk_drag_set_icon_stock (context,
2450                                      site->icon_data.stock.stock_id,
2451                                      -2, -2);
2452             break;
2453           case GTK_IMAGE_ICON_NAME:
2454             gtk_drag_set_icon_name (context,
2455                                     site->icon_data.name.icon_name,
2456                                     -2, -2);
2457             break;
2458           case GTK_IMAGE_EMPTY:
2459           default:
2460             g_assert_not_reached();
2461             break;
2462           }
2463     }
2464
2465   /* We need to composite the icon into the cursor, if we are
2466    * not using an icon window.
2467    */
2468   if (info->icon_pixbuf)  
2469     {
2470       cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), 
2471                                     suggested_action,
2472                                     info);
2473   
2474       if (cursor != info->cursor)
2475         {
2476           gdk_pointer_grab (widget->window, FALSE,
2477                             GDK_POINTER_MOTION_MASK |
2478                             GDK_BUTTON_RELEASE_MASK,
2479                             NULL,
2480                             cursor, time);
2481           info->cursor = cursor;
2482         }
2483     }
2484     
2485   if (event && event->type == GDK_MOTION_NOTIFY)
2486     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
2487   else
2488     gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
2489
2490   info->start_x = info->cur_x;
2491   info->start_y = info->cur_y;
2492
2493   g_signal_connect (info->ipc_widget, "grab-broken-event",
2494                     G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
2495   g_signal_connect (info->ipc_widget, "grab-notify",
2496                     G_CALLBACK (gtk_drag_grab_notify_cb), info);
2497   g_signal_connect (info->ipc_widget, "button-release-event",
2498                     G_CALLBACK (gtk_drag_button_release_cb), info);
2499   g_signal_connect (info->ipc_widget, "motion-notify-event",
2500                     G_CALLBACK (gtk_drag_motion_cb), info);
2501   g_signal_connect (info->ipc_widget, "key-press-event",
2502                     G_CALLBACK (gtk_drag_key_cb), info);
2503   g_signal_connect (info->ipc_widget, "key-release-event",
2504                     G_CALLBACK (gtk_drag_key_cb), info);
2505   g_signal_connect (info->ipc_widget, "selection-get",
2506                     G_CALLBACK (gtk_drag_selection_get), info);
2507
2508   info->have_grab = TRUE;
2509   info->grab_time = time;
2510
2511   return info->context;
2512 }
2513
2514 /**
2515  * gtk_drag_begin:
2516  * @widget: the source widget.
2517  * @targets: The targets (data formats) in which the
2518  *    source can provide the data.
2519  * @actions: A bitmask of the allowed drag actions for this drag.
2520  * @button: The button the user clicked to start the drag.
2521  * @event: The event that triggered the start of the drag.
2522  * 
2523  * Initiates a drag on the source side. The function
2524  * only needs to be used when the application is
2525  * starting drags itself, and is not needed when
2526  * gtk_drag_source_set() is used.
2527  * 
2528  * Return value: the context for this drag.
2529  **/
2530 GdkDragContext *
2531 gtk_drag_begin (GtkWidget         *widget,
2532                 GtkTargetList     *targets,
2533                 GdkDragAction      actions,
2534                 gint               button,
2535                 GdkEvent          *event)
2536 {
2537   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2538   g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
2539   g_return_val_if_fail (targets != NULL, NULL);
2540
2541   return gtk_drag_begin_internal (widget, NULL, targets,
2542                                   actions, button, event);
2543 }
2544
2545 /*************************************************************
2546  * gtk_drag_source_set:
2547  *     Register a drop site, and possibly add default behaviors.
2548  *   arguments:
2549  *     widget:
2550  *     start_button_mask: Mask of allowed buttons to start drag
2551  *     targets:           Table of targets for this source
2552  *     n_targets:
2553  *     actions:           Actions allowed for this source
2554  *   results:
2555  *************************************************************/
2556
2557 void 
2558 gtk_drag_source_set (GtkWidget            *widget,
2559                      GdkModifierType       start_button_mask,
2560                      const GtkTargetEntry *targets,
2561                      gint                  n_targets,
2562                      GdkDragAction         actions)
2563 {
2564   GtkDragSourceSite *site;
2565
2566   g_return_if_fail (GTK_IS_WIDGET (widget));
2567
2568   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2569
2570   gtk_widget_add_events (widget,
2571                          gtk_widget_get_events (widget) |
2572                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2573                          GDK_BUTTON_MOTION_MASK);
2574
2575   if (site)
2576     {
2577       if (site->target_list)
2578         gtk_target_list_unref (site->target_list);
2579     }
2580   else
2581     {
2582       site = g_new0 (GtkDragSourceSite, 1);
2583
2584       site->icon_type = GTK_IMAGE_EMPTY;
2585       
2586       g_signal_connect (widget, "button-press-event",
2587                         G_CALLBACK (gtk_drag_source_event_cb),
2588                         site);
2589       g_signal_connect (widget, "button-release-event",
2590                         G_CALLBACK (gtk_drag_source_event_cb),
2591                         site);
2592       g_signal_connect (widget, "motion-notify-event",
2593                         G_CALLBACK (gtk_drag_source_event_cb),
2594                         site);
2595       
2596       g_object_set_data_full (G_OBJECT (widget),
2597                               I_("gtk-site-data"), 
2598                               site, gtk_drag_source_site_destroy);
2599     }
2600
2601   site->start_button_mask = start_button_mask;
2602
2603   site->target_list = gtk_target_list_new (targets, n_targets);
2604
2605   site->actions = actions;
2606 }
2607
2608 /*************************************************************
2609  * gtk_drag_source_unset
2610  *     Unregister this widget as a drag source.
2611  *   arguments:
2612  *     widget:
2613  *   results:
2614  *************************************************************/
2615
2616 void 
2617 gtk_drag_source_unset (GtkWidget        *widget)
2618 {
2619   GtkDragSourceSite *site;
2620
2621   g_return_if_fail (GTK_IS_WIDGET (widget));
2622
2623   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2624
2625   if (site)
2626     {
2627       g_signal_handlers_disconnect_by_func (widget,
2628                                             gtk_drag_source_event_cb,
2629                                             site);
2630       g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
2631     }
2632 }
2633
2634 /**
2635  * gtk_drag_source_get_target_list:
2636  * @widget: a #GtkWidget
2637  *
2638  * Gets the list of targets this widget can provide for
2639  * drag-and-drop.
2640  *
2641  * Return value: the #GtkTargetList, or %NULL if none
2642  *
2643  * Since: 2.4
2644  **/
2645 GtkTargetList *
2646 gtk_drag_source_get_target_list (GtkWidget *widget)
2647 {
2648   GtkDragSourceSite *site;
2649
2650   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2651
2652   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2653
2654   return site ? site->target_list : NULL;
2655 }
2656
2657 /**
2658  * gtk_drag_source_set_target_list:
2659  * @widget: a #GtkWidget that's a drag source
2660  * @target_list: list of draggable targets, or %NULL for none
2661  *
2662  * Changes the target types that this widget offers for drag-and-drop.
2663  * The widget must first be made into a drag source with
2664  * gtk_drag_source_set().
2665  *
2666  * Since: 2.4
2667  **/
2668 void
2669 gtk_drag_source_set_target_list (GtkWidget     *widget,
2670                                  GtkTargetList *target_list)
2671 {
2672   GtkDragSourceSite *site;
2673
2674   g_return_if_fail (GTK_IS_WIDGET (widget));
2675
2676   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2677   if (site == NULL)
2678     {
2679       g_warning ("gtk_drag_source_set_target_list() requires the widget "
2680                  "to already be a drag source.");
2681       return;
2682     }
2683
2684   if (target_list)
2685     gtk_target_list_ref (target_list);
2686
2687   if (site->target_list)
2688     gtk_target_list_unref (site->target_list);
2689
2690   site->target_list = target_list;
2691 }
2692
2693 /**
2694  * gtk_drag_source_add_text_targets:
2695  * @widget: a #GtkWidget that's is a drag source
2696  *
2697  * Add the text targets supported by #GtkSelection to
2698  * the target list of the drag source.  The targets
2699  * are added with @info = 0. If you need another value, 
2700  * use gtk_target_list_add_text_targets() and
2701  * gtk_drag_source_set_target_list().
2702  * 
2703  * Since: 2.6
2704  **/
2705 void
2706 gtk_drag_source_add_text_targets (GtkWidget *widget)
2707 {
2708   GtkTargetList *target_list;
2709
2710   target_list = gtk_drag_source_get_target_list (widget);
2711   if (target_list)
2712     gtk_target_list_ref (target_list);
2713   else
2714     target_list = gtk_target_list_new (NULL, 0);
2715   gtk_target_list_add_text_targets (target_list, 0);
2716   gtk_drag_source_set_target_list (widget, target_list);
2717   gtk_target_list_unref (target_list);
2718 }
2719
2720 /**
2721  * gtk_drag_source_add_image_targets:
2722  * @widget: a #GtkWidget that's is a drag source
2723  *
2724  * Add the writable image targets supported by #GtkSelection to
2725  * the target list of the drag source. The targets
2726  * are added with @info = 0. If you need another value, 
2727  * use gtk_target_list_add_image_targets() and
2728  * gtk_drag_source_set_target_list().
2729  * 
2730  * Since: 2.6
2731  **/
2732 void
2733 gtk_drag_source_add_image_targets (GtkWidget *widget)
2734 {
2735   GtkTargetList *target_list;
2736
2737   target_list = gtk_drag_source_get_target_list (widget);
2738   if (target_list)
2739     gtk_target_list_ref (target_list);
2740   else
2741     target_list = gtk_target_list_new (NULL, 0);
2742   gtk_target_list_add_image_targets (target_list, 0, TRUE);
2743   gtk_drag_source_set_target_list (widget, target_list);
2744   gtk_target_list_unref (target_list);
2745 }
2746
2747 /**
2748  * gtk_drag_source_add_uri_targets:
2749  * @widget: a #GtkWidget that's is a drag source
2750  *
2751  * Add the URI targets supported by #GtkSelection to
2752  * the target list of the drag source.  The targets
2753  * are added with @info = 0. If you need another value, 
2754  * use gtk_target_list_add_uri_targets() and
2755  * gtk_drag_source_set_target_list().
2756  * 
2757  * Since: 2.6
2758  **/
2759 void
2760 gtk_drag_source_add_uri_targets (GtkWidget *widget)
2761 {
2762   GtkTargetList *target_list;
2763
2764   target_list = gtk_drag_source_get_target_list (widget);
2765   if (target_list)
2766     gtk_target_list_ref (target_list);
2767   else
2768     target_list = gtk_target_list_new (NULL, 0);
2769   gtk_target_list_add_uri_targets (target_list, 0);
2770   gtk_drag_source_set_target_list (widget, target_list);
2771   gtk_target_list_unref (target_list);
2772 }
2773
2774 static void
2775 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2776 {
2777   switch (site->icon_type)
2778     {
2779     case GTK_IMAGE_EMPTY:
2780       break;
2781     case GTK_IMAGE_PIXMAP:
2782       if (site->icon_data.pixmap.pixmap)
2783         g_object_unref (site->icon_data.pixmap.pixmap);
2784       if (site->icon_mask)
2785         g_object_unref (site->icon_mask);
2786       break;
2787     case GTK_IMAGE_PIXBUF:
2788       g_object_unref (site->icon_data.pixbuf.pixbuf);
2789       break;
2790     case GTK_IMAGE_STOCK:
2791       g_free (site->icon_data.stock.stock_id);
2792       break;
2793     case GTK_IMAGE_ICON_NAME:
2794       g_free (site->icon_data.name.icon_name);
2795       break;
2796     default:
2797       g_assert_not_reached();
2798       break;
2799     }
2800   site->icon_type = GTK_IMAGE_EMPTY;
2801   
2802   if (site->colormap)
2803     g_object_unref (site->colormap);
2804   site->colormap = NULL;
2805 }
2806
2807 /**
2808  * gtk_drag_source_set_icon:
2809  * @widget: a #GtkWidget
2810  * @colormap: the colormap of the icon
2811  * @pixmap: the image data for the icon
2812  * @mask: the transparency mask for an image.
2813  * 
2814  * Sets the icon that will be used for drags from a particular widget
2815  * from a pixmap/mask. GTK+ retains references for the arguments, and 
2816  * will release them when they are no longer needed.
2817  * Use gtk_drag_source_set_icon_pixbuf() instead.
2818  **/
2819 void 
2820 gtk_drag_source_set_icon (GtkWidget     *widget,
2821                           GdkColormap   *colormap,
2822                           GdkPixmap     *pixmap,
2823                           GdkBitmap     *mask)
2824 {
2825   GtkDragSourceSite *site;
2826
2827   g_return_if_fail (GTK_IS_WIDGET (widget));
2828   g_return_if_fail (GDK_IS_COLORMAP (colormap));
2829   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2830   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2831
2832   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2833   g_return_if_fail (site != NULL);
2834   
2835   g_object_ref (colormap);
2836   g_object_ref (pixmap);
2837   if (mask)
2838     g_object_ref (mask);
2839
2840   gtk_drag_source_unset_icon (site);
2841
2842   site->icon_type = GTK_IMAGE_PIXMAP;
2843   
2844   site->icon_data.pixmap.pixmap = pixmap;
2845   site->icon_mask = mask;
2846   site->colormap = colormap;
2847 }
2848
2849 /**
2850  * gtk_drag_source_set_icon_pixbuf:
2851  * @widget: a #GtkWidget
2852  * @pixbuf: the #GdkPixbuf for the drag icon
2853  * 
2854  * Sets the icon that will be used for drags from a particular widget
2855  * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will 
2856  * release it when it is no longer needed.
2857  **/
2858 void 
2859 gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
2860                                  GdkPixbuf   *pixbuf)
2861 {
2862   GtkDragSourceSite *site;
2863
2864   g_return_if_fail (GTK_IS_WIDGET (widget));
2865   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2866
2867   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2868   g_return_if_fail (site != NULL); 
2869   g_object_ref (pixbuf);
2870
2871   gtk_drag_source_unset_icon (site);
2872
2873   site->icon_type = GTK_IMAGE_PIXBUF;
2874   site->icon_data.pixbuf.pixbuf = pixbuf;
2875 }
2876
2877 /**
2878  * gtk_drag_source_set_icon_stock:
2879  * @widget: a #GtkWidget
2880  * @stock_id: the ID of the stock icon to use
2881  *
2882  * Sets the icon that will be used for drags from a particular source
2883  * to a stock icon. 
2884  **/
2885 void 
2886 gtk_drag_source_set_icon_stock (GtkWidget   *widget,
2887                                 const gchar *stock_id)
2888 {
2889   GtkDragSourceSite *site;
2890
2891   g_return_if_fail (GTK_IS_WIDGET (widget));
2892   g_return_if_fail (stock_id != NULL);
2893
2894   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2895   g_return_if_fail (site != NULL);
2896   
2897   gtk_drag_source_unset_icon (site);
2898
2899   site->icon_type = GTK_IMAGE_STOCK;
2900   site->icon_data.stock.stock_id = g_strdup (stock_id);
2901 }
2902
2903 /**
2904  * gtk_drag_source_set_icon_name:
2905  * @widget: a #GtkWidget
2906  * @icon_name: name of icon to use
2907  * 
2908  * Sets the icon that will be used for drags from a particular source
2909  * to a themed icon. See the docs for #GtkIconTheme for more details.
2910  *
2911  * Since: 2.8
2912  **/
2913 void 
2914 gtk_drag_source_set_icon_name (GtkWidget   *widget,
2915                                const gchar *icon_name)
2916 {
2917   GtkDragSourceSite *site;
2918
2919   g_return_if_fail (GTK_IS_WIDGET (widget));
2920   g_return_if_fail (icon_name != NULL);
2921
2922   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2923   g_return_if_fail (site != NULL);
2924
2925   gtk_drag_source_unset_icon (site);
2926
2927   site->icon_type = GTK_IMAGE_ICON_NAME;
2928   site->icon_data.name.icon_name = g_strdup (icon_name);
2929 }
2930
2931 static void
2932 gtk_drag_get_icon (GtkDragSourceInfo *info,
2933                    GtkWidget        **icon_window,
2934                    gint              *hot_x,
2935                    gint              *hot_y)
2936 {
2937   if (get_can_change_screen (info->icon_window))
2938     gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2939                            info->cur_screen);
2940       
2941   if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2942     {
2943       if (!info->fallback_icon)
2944         {
2945           gint save_hot_x, save_hot_y;
2946           gboolean save_destroy_icon;
2947           GtkWidget *save_icon_window;
2948           
2949           /* HACK to get the appropriate icon
2950            */
2951           save_icon_window = info->icon_window;
2952           save_hot_x = info->hot_x;
2953           save_hot_y = info->hot_x;
2954           save_destroy_icon = info->destroy_icon;
2955
2956           info->icon_window = NULL;
2957           if (!default_icon_pixmap)
2958             set_icon_stock_pixbuf (info->context, 
2959                                    GTK_STOCK_DND, NULL, -2, -2, TRUE);
2960           else
2961             gtk_drag_set_icon_pixmap (info->context, 
2962                                       default_icon_colormap, 
2963                                       default_icon_pixmap, 
2964                                       default_icon_mask,
2965                                       default_icon_hot_x,
2966                                       default_icon_hot_y);
2967           info->fallback_icon = info->icon_window;
2968           
2969           info->icon_window = save_icon_window;
2970           info->hot_x = save_hot_x;
2971           info->hot_y = save_hot_y;
2972           info->destroy_icon = save_destroy_icon;
2973         }
2974       
2975       gtk_widget_hide (info->icon_window);
2976       
2977       *icon_window = info->fallback_icon;
2978       gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2979       
2980       if (!default_icon_pixmap)
2981         {
2982           *hot_x = -2;
2983           *hot_y = -2;
2984         }
2985       else
2986         {
2987           *hot_x = default_icon_hot_x;
2988           *hot_y = default_icon_hot_y;
2989         }
2990     }
2991   else
2992     {
2993       if (info->fallback_icon)
2994         gtk_widget_hide (info->fallback_icon);
2995       
2996       *icon_window = info->icon_window;
2997       *hot_x = info->hot_x;
2998       *hot_y = info->hot_y;
2999     }
3000 }
3001
3002 static void
3003 gtk_drag_update_icon (GtkDragSourceInfo *info)
3004 {
3005   if (info->icon_window)
3006     {
3007       GtkWidget *icon_window;
3008       gint hot_x, hot_y;
3009   
3010       gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
3011       
3012       gtk_window_move (GTK_WINDOW (icon_window), 
3013                        info->cur_x - hot_x, 
3014                        info->cur_y - hot_y);
3015
3016       if (GTK_WIDGET_VISIBLE (icon_window))
3017         gdk_window_raise (icon_window->window);
3018       else
3019         gtk_widget_show (icon_window);
3020     }
3021 }
3022
3023 static void 
3024 gtk_drag_set_icon_window (GdkDragContext *context,
3025                           GtkWidget      *widget,
3026                           gint            hot_x,
3027                           gint            hot_y,
3028                           gboolean        destroy_on_release)
3029 {
3030   GtkDragSourceInfo *info;
3031
3032   info = gtk_drag_get_source_info (context, FALSE);
3033   if (info == NULL)
3034     {
3035       if (destroy_on_release)
3036         gtk_widget_destroy (widget);
3037       return;
3038     }
3039
3040   gtk_drag_remove_icon (info);
3041
3042   if (widget)
3043     g_object_ref (widget);  
3044   
3045   info->icon_window = widget;
3046   info->hot_x = hot_x;
3047   info->hot_y = hot_y;
3048   info->destroy_icon = destroy_on_release;
3049
3050   if (widget && info->icon_pixbuf)
3051     {
3052       g_object_unref (info->icon_pixbuf);
3053       info->icon_pixbuf = NULL;
3054     }
3055
3056   gtk_drag_update_cursor (info);
3057   gtk_drag_update_icon (info);
3058 }
3059
3060 /**
3061  * gtk_drag_set_icon_widget:
3062  * @context: the context for a drag. (This must be called 
3063           with a  context for the source side of a drag)
3064  * @widget: a toplevel window to use as an icon.
3065  * @hot_x: the X offset within @widget of the hotspot.
3066  * @hot_y: the Y offset within @widget of the hotspot.
3067  * 
3068  * Changes the icon for a widget to a given widget. GTK+
3069  * will not destroy the icon, so if you don't want
3070  * it to persist, you should connect to the "drag-end" 
3071  * signal and destroy it yourself.
3072  **/
3073 void 
3074 gtk_drag_set_icon_widget (GdkDragContext    *context,
3075                           GtkWidget         *widget,
3076                           gint               hot_x,
3077                           gint               hot_y)
3078 {
3079   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3080   g_return_if_fail (context->is_source);
3081   g_return_if_fail (GTK_IS_WIDGET (widget));
3082
3083   gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
3084 }
3085
3086 static void
3087 icon_window_realize (GtkWidget *window,
3088                      GdkPixbuf *pixbuf)
3089 {
3090   GdkPixmap *pixmap;
3091   GdkPixmap *mask;
3092
3093   gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
3094                                                   gtk_widget_get_colormap (window),
3095                                                   &pixmap, &mask, 128);
3096   
3097   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
3098   g_object_unref (pixmap);
3099   
3100   if (mask)
3101     {
3102       gtk_widget_shape_combine_mask (window, mask, 0, 0);
3103       g_object_unref (mask);
3104     }
3105 }
3106
3107 static void
3108 set_icon_stock_pixbuf (GdkDragContext    *context,
3109                        const gchar       *stock_id,
3110                        GdkPixbuf         *pixbuf,
3111                        gint               hot_x,
3112                        gint               hot_y,
3113                        gboolean           force_window)
3114 {
3115   GtkWidget *window;
3116   gint width, height;
3117   GdkScreen *screen;
3118   GdkDisplay *display;
3119
3120   g_return_if_fail (context != NULL);
3121   g_return_if_fail (pixbuf != NULL || stock_id != NULL);
3122   g_return_if_fail (pixbuf == NULL || stock_id == NULL);
3123
3124   screen = gdk_drawable_get_screen (context->source_window);
3125
3126   /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
3127   gtk_widget_push_colormap (NULL);
3128   window = gtk_window_new (GTK_WINDOW_POPUP);
3129   gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3130   gtk_window_set_screen (GTK_WINDOW (window), screen);
3131   set_can_change_screen (window, TRUE);
3132   gtk_widget_pop_colormap ();
3133
3134   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3135   gtk_widget_set_app_paintable (window, TRUE);
3136
3137   if (stock_id)
3138     {
3139       pixbuf = gtk_widget_render_icon (window, stock_id,
3140                                        GTK_ICON_SIZE_DND, NULL);
3141
3142       if (!pixbuf)
3143         {
3144           g_warning ("Cannot load drag icon from stock_id %s", stock_id);
3145           gtk_widget_destroy (window);
3146           return;
3147         }
3148
3149     }
3150   else
3151     g_object_ref (pixbuf);
3152
3153   display = gdk_drawable_get_display (context->source_window);
3154   width = gdk_pixbuf_get_width (pixbuf);
3155   height = gdk_pixbuf_get_height (pixbuf);
3156
3157   if (!force_window &&
3158       gtk_drag_can_use_rgba_cursor (display, width + 2, height + 2))
3159     {
3160       GtkDragSourceInfo *info;
3161
3162       gtk_widget_destroy (window);
3163
3164       info = gtk_drag_get_source_info (context, FALSE);
3165
3166       if (info->icon_pixbuf)
3167         g_object_unref (info->icon_pixbuf);
3168       info->icon_pixbuf = pixbuf;
3169
3170       gtk_drag_set_icon_window (context, NULL, hot_x, hot_y, TRUE);
3171     }
3172   else
3173     {
3174       gtk_widget_set_size_request (window, width, height);
3175
3176       g_signal_connect_closure (window, "realize",
3177                                 g_cclosure_new (G_CALLBACK (icon_window_realize),
3178                                                 pixbuf,
3179                                                 (GClosureNotify)g_object_unref),
3180                                 FALSE);
3181                     
3182       gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3183    }
3184 }
3185
3186 /**
3187  * gtk_drag_set_icon_pixbuf:
3188  * @context: the context for a drag. (This must be called 
3189  *            with a  context for the source side of a drag)
3190  * @pixbuf: the #GdkPixbuf to use as the drag icon.
3191  * @hot_x: the X offset within @widget of the hotspot.
3192  * @hot_y: the Y offset within @widget of the hotspot.
3193  * 
3194  * Sets @pixbuf as the icon for a given drag.
3195  **/
3196 void 
3197 gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
3198                            GdkPixbuf      *pixbuf,
3199                            gint            hot_x,
3200                            gint            hot_y)
3201 {
3202   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3203   g_return_if_fail (context->is_source);
3204   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
3205
3206   set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3207 }
3208
3209 /**
3210  * gtk_drag_set_icon_stock:
3211  * @context: the context for a drag. (This must be called 
3212  *            with a  context for the source side of a drag)
3213  * @stock_id: the ID of the stock icon to use for the drag.
3214  * @hot_x: the X offset within the icon of the hotspot.
3215  * @hot_y: the Y offset within the icon of the hotspot.
3216  * 
3217  * Sets the icon for a given drag from a stock ID.
3218  **/
3219 void 
3220 gtk_drag_set_icon_stock  (GdkDragContext *context,
3221                           const gchar    *stock_id,
3222                           gint            hot_x,
3223                           gint            hot_y)
3224 {
3225   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3226   g_return_if_fail (context->is_source);
3227   g_return_if_fail (stock_id != NULL);
3228   
3229   set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y, FALSE);
3230 }
3231
3232 /**
3233  * gtk_drag_set_icon_pixmap:
3234  * @context: the context for a drag. (This must be called 
3235  *            with a  context for the source side of a drag)
3236  * @colormap: the colormap of the icon 
3237  * @pixmap: the image data for the icon 
3238  * @mask: the transparency mask for the icon or %NULL for none.
3239  * @hot_x: the X offset within @pixmap of the hotspot.
3240  * @hot_y: the Y offset within @pixmap of the hotspot.
3241  * 
3242  * Sets @pixmap as the icon for a given drag. GTK+ retains
3243  * references for the arguments, and will release them when
3244  * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
3245  * will be more convenient to use.
3246  **/
3247 void 
3248 gtk_drag_set_icon_pixmap (GdkDragContext    *context,
3249                           GdkColormap       *colormap,
3250                           GdkPixmap         *pixmap,
3251                           GdkBitmap         *mask,
3252                           gint               hot_x,
3253                           gint               hot_y)
3254 {
3255   GtkWidget *window;
3256   GdkScreen *screen;
3257   gint width, height;
3258       
3259   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3260   g_return_if_fail (context->is_source);
3261   g_return_if_fail (GDK_IS_COLORMAP (colormap));
3262   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
3263   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
3264
3265   screen = gdk_colormap_get_screen (colormap);
3266   
3267   g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
3268   g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
3269   
3270   gdk_drawable_get_size (pixmap, &width, &height);
3271
3272   gtk_widget_push_colormap (colormap);
3273
3274   window = gtk_window_new (GTK_WINDOW_POPUP);
3275   gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3276   gtk_window_set_screen (GTK_WINDOW (window), screen);
3277   set_can_change_screen (window, FALSE);
3278   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3279   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
3280
3281   gtk_widget_pop_colormap ();
3282
3283   gtk_widget_set_size_request (window, width, height);
3284   gtk_widget_realize (window);
3285
3286   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
3287   
3288   if (mask)
3289     gtk_widget_shape_combine_mask (window, mask, 0, 0);
3290
3291   gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3292 }
3293
3294 /**
3295  * gtk_drag_set_icon_name:
3296  * @context: the context for a drag. (This must be called 
3297  *            with a context for the source side of a drag)
3298  * @icon_name: name of icon to use
3299  * @hot_x: the X offset of the hotspot within the icon
3300  * @hot_y: the Y offset of the hotspot within the icon
3301  * 
3302  * Sets the icon for a given drag from a named themed icon. See
3303  * the docs for #GtkIconTheme for more details. Note that the
3304  * size of the icon depends on the icon theme (the icon is
3305  * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus 
3306  * @hot_x and @hot_y have to be used with care.
3307  *
3308  * Since: 2.8
3309  **/
3310 void 
3311 gtk_drag_set_icon_name (GdkDragContext *context,
3312                         const gchar    *icon_name,
3313                         gint            hot_x,
3314                         gint            hot_y)
3315 {
3316   GdkScreen *screen;
3317   GtkSettings *settings;
3318   GtkIconTheme *icon_theme;
3319   GdkPixbuf *pixbuf;
3320   gint width, height, icon_size;
3321
3322   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3323   g_return_if_fail (context->is_source);
3324   g_return_if_fail (icon_name != NULL);
3325
3326   screen = gdk_drawable_get_screen (context->source_window);
3327   g_return_if_fail (screen != NULL);
3328
3329   settings = gtk_settings_get_for_screen (screen);
3330   if (gtk_icon_size_lookup_for_settings (settings,
3331                                          GTK_ICON_SIZE_DND,
3332                                          &width, &height))
3333     icon_size = MAX (width, height);
3334   else 
3335     icon_size = 32; /* default value for GTK_ICON_SIZE_DND */ 
3336
3337   icon_theme = gtk_icon_theme_get_for_screen (screen);
3338
3339   pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name,
3340                                      icon_size, 0, NULL);
3341   if (pixbuf)
3342     set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3343   else
3344     g_warning ("Cannot load drag icon from icon name %s", icon_name);
3345 }
3346
3347 /**
3348  * gtk_drag_set_icon_default:
3349  * @context: the context for a drag. (This must be called 
3350              with a  context for the source side of a drag)
3351  * 
3352  * Sets the icon for a particular drag to the default
3353  * icon.
3354  **/
3355 void 
3356 gtk_drag_set_icon_default (GdkDragContext *context)
3357 {
3358   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3359   g_return_if_fail (context->is_source);
3360
3361   if (!default_icon_pixmap)
3362     gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
3363   else
3364     gtk_drag_set_icon_pixmap (context, 
3365                               default_icon_colormap, 
3366                               default_icon_pixmap, 
3367                               default_icon_mask,
3368                               default_icon_hot_x,
3369                               default_icon_hot_y);
3370 }
3371
3372 /**
3373  * gtk_drag_set_default_icon:
3374  * @colormap: the colormap of the icon
3375  * @pixmap: the image data for the icon
3376  * @mask: the transparency mask for an image.
3377  * @hot_x: The X offset within @widget of the hotspot.
3378  * @hot_y: The Y offset within @widget of the hotspot.
3379  * 
3380  * Changes the default drag icon. GTK+ retains references for the
3381  * arguments, and will release them when they are no longer needed.
3382  *
3383  * Deprecated: Change the default drag icon via the stock system by 
3384  *     changing the stock pixbuf for #GTK_STOCK_DND instead.
3385  **/
3386 void 
3387 gtk_drag_set_default_icon (GdkColormap   *colormap,
3388                            GdkPixmap     *pixmap,
3389                            GdkBitmap     *mask,
3390                            gint           hot_x,
3391                            gint           hot_y)
3392 {
3393   g_return_if_fail (GDK_IS_COLORMAP (colormap));
3394   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
3395   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
3396   
3397   if (default_icon_colormap)
3398     g_object_unref (default_icon_colormap);
3399   if (default_icon_pixmap)
3400     g_object_unref (default_icon_pixmap);
3401   if (default_icon_mask)
3402     g_object_unref (default_icon_mask);
3403
3404   default_icon_colormap = colormap;
3405   g_object_ref (colormap);
3406   
3407   default_icon_pixmap = pixmap;
3408   g_object_ref (pixmap);
3409
3410   default_icon_mask = mask;
3411   if (mask)
3412     g_object_ref (mask);
3413   
3414   default_icon_hot_x = hot_x;
3415   default_icon_hot_y = hot_y;
3416 }
3417
3418
3419 /*************************************************************
3420  * _gtk_drag_source_handle_event:
3421  *     Called from widget event handling code on Drag events
3422  *     for drag sources.
3423  *
3424  *   arguments:
3425  *     toplevel: Toplevel widget that received the event
3426  *     event:
3427  *   results:
3428  *************************************************************/
3429
3430 void
3431 _gtk_drag_source_handle_event (GtkWidget *widget,
3432                                GdkEvent  *event)
3433 {
3434   GtkDragSourceInfo *info;
3435   GdkDragContext *context;
3436
3437   g_return_if_fail (widget != NULL);
3438   g_return_if_fail (event != NULL);
3439
3440   context = event->dnd.context;
3441   info = gtk_drag_get_source_info (context, FALSE);
3442   if (!info)
3443     return;
3444
3445   switch (event->type)
3446     {
3447     case GDK_DRAG_STATUS:
3448       {
3449         GdkCursor *cursor;
3450
3451         if (info->proxy_dest)
3452           {
3453             if (!event->dnd.send_event)
3454               {
3455                 if (info->proxy_dest->proxy_drop_wait)
3456                   {
3457                     gboolean result = context->action != 0;
3458                     
3459                     /* Aha - we can finally pass the MOTIF DROP on... */
3460                     gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
3461                     if (result)
3462                       gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
3463                     else
3464                       gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
3465                   }
3466                 else
3467                   {
3468                     gdk_drag_status (info->proxy_dest->context,
3469                                      event->dnd.context->action,
3470                                      event->dnd.time);
3471                   }
3472               }
3473           }
3474         else if (info->have_grab)
3475           {
3476             cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
3477                                           event->dnd.context->action,
3478                                           info);
3479             if (info->cursor != cursor)
3480               {
3481                 gdk_pointer_grab (widget->window, FALSE,
3482                                   GDK_POINTER_MOTION_MASK |
3483                                   GDK_BUTTON_RELEASE_MASK,
3484                                   NULL,
3485                                   cursor, info->grab_time);
3486                 info->cursor = cursor;
3487               }
3488             
3489             gtk_drag_add_update_idle (info);
3490           }
3491       }
3492       break;
3493       
3494     case GDK_DROP_FINISHED:
3495       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, event->dnd.time);
3496       break;
3497     default:
3498       g_assert_not_reached ();
3499     }
3500 }
3501
3502 /*************************************************************
3503  * gtk_drag_source_check_selection:
3504  *     Check if we've set up handlers/claimed the selection
3505  *     for a given drag. If not, add them.
3506  *   arguments:
3507  *     
3508  *   results:
3509  *************************************************************/
3510
3511 static void
3512 gtk_drag_source_check_selection (GtkDragSourceInfo *info, 
3513                                  GdkAtom            selection,
3514                                  guint32            time)
3515 {
3516   GList *tmp_list;
3517
3518   tmp_list = info->selections;
3519   while (tmp_list)
3520     {
3521       if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
3522         return;
3523       tmp_list = tmp_list->next;
3524     }
3525
3526   gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
3527                                        info->ipc_widget,
3528                                        selection,
3529                                        time);
3530   info->selections = g_list_prepend (info->selections,
3531                                      GUINT_TO_POINTER (selection));
3532
3533   tmp_list = info->target_list->list;
3534   while (tmp_list)
3535     {
3536       GtkTargetPair *pair = tmp_list->data;
3537
3538       gtk_selection_add_target (info->ipc_widget,
3539                                 selection,
3540                                 pair->target,
3541                                 pair->info);
3542       tmp_list = tmp_list->next;
3543     }
3544   
3545   if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
3546     {
3547       gtk_selection_add_target (info->ipc_widget,
3548                                 selection,
3549                                 gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS"),
3550                                 TARGET_MOTIF_SUCCESS);
3551       gtk_selection_add_target (info->ipc_widget,
3552                                 selection,
3553                                 gdk_atom_intern_static_string ("XmTRANSFER_FAILURE"),
3554                                 TARGET_MOTIF_FAILURE);
3555     }
3556
3557   gtk_selection_add_target (info->ipc_widget,
3558                             selection,
3559                             gdk_atom_intern_static_string ("DELETE"),
3560                             TARGET_DELETE);
3561 }
3562
3563 /*************************************************************
3564  * gtk_drag_drop_finished:
3565  *     Clean up from the drag, and display snapback, if necessary.
3566  *   arguments:
3567  *     info:
3568  *     success:
3569  *     time:
3570  *   results:
3571  *************************************************************/
3572
3573 static void
3574 gtk_drag_drop_finished (GtkDragSourceInfo *info,
3575                         GtkDragResult      result,
3576                         guint              time)
3577 {
3578   gboolean success;
3579
3580   success = (result == GTK_DRAG_RESULT_SUCCESS);
3581   gtk_drag_source_release_selections (info, time); 
3582
3583   if (info->proxy_dest)
3584     {
3585       /* The time from the event isn't reliable for Xdnd drags */
3586       gtk_drag_finish (info->proxy_dest->context, success, FALSE, 
3587                        info->proxy_dest->proxy_drop_time);
3588       gtk_drag_source_info_destroy (info);
3589     }
3590   else
3591     {
3592       if (!success)
3593         g_signal_emit_by_name (info->widget, "drag-failed",
3594                                info->context, result, &success);
3595
3596       if (success)
3597         {
3598           gtk_drag_source_info_destroy (info);
3599         }
3600       else
3601         {
3602           GtkDragAnim *anim = g_new (GtkDragAnim, 1);
3603           anim->info = info;
3604           anim->step = 0;
3605           
3606           anim->n_steps = MAX (info->cur_x - info->start_x,
3607                                info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
3608           anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
3609
3610           info->cur_screen = gtk_widget_get_screen (info->widget);
3611
3612           if (!info->icon_window)
3613             set_icon_stock_pixbuf (info->context, NULL, info->icon_pixbuf, 
3614                                    0, 0, TRUE);
3615
3616           gtk_drag_update_icon (info);
3617           
3618           /* Mark the context as dead, so if the destination decides
3619            * to respond really late, we still are OK.
3620            */
3621           gtk_drag_clear_source_info (info->context);
3622           gdk_threads_add_timeout (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
3623         }
3624     }
3625 }
3626
3627 static void
3628 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
3629                                     guint32            time)
3630 {
3631   GdkDisplay *display = gtk_widget_get_display (info->widget);
3632   GList *tmp_list = info->selections;
3633   
3634   while (tmp_list)
3635     {
3636       GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
3637       if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
3638         gtk_selection_owner_set_for_display (display, NULL, selection, time);
3639
3640       tmp_list = tmp_list->next;
3641     }
3642
3643   g_list_free (info->selections);
3644   info->selections = NULL;
3645 }
3646
3647 /*************************************************************
3648  * gtk_drag_drop:
3649  *     Send a drop event.
3650  *   arguments:
3651  *     
3652  *   results:
3653  *************************************************************/
3654
3655 static void
3656 gtk_drag_drop (GtkDragSourceInfo *info, 
3657                guint32            time)
3658 {
3659   if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
3660     {
3661       GtkSelectionData selection_data;
3662       GList *tmp_list;
3663       /* GTK+ traditionally has used application/x-rootwin-drop, but the
3664        * XDND spec specifies x-rootwindow-drop.
3665        */
3666       GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3667       GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3668       
3669       tmp_list = info->target_list->list;
3670       while (tmp_list)
3671         {
3672           GtkTargetPair *pair = tmp_list->data;
3673           
3674           if (pair->target == target1 || pair->target == target2)
3675             {
3676               selection_data.selection = GDK_NONE;
3677               selection_data.target = pair->target;
3678               selection_data.data = NULL;
3679               selection_data.length = -1;
3680               
3681               g_signal_emit_by_name (info->widget, "drag-data-get",
3682                                      info->context, &selection_data,
3683                                      pair->info,
3684                                      time);
3685               
3686               /* FIXME: Should we check for length >= 0 here? */
3687               gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3688               return;
3689             }
3690           tmp_list = tmp_list->next;
3691         }
3692       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3693     }
3694   else
3695     {
3696       if (info->icon_window)
3697         gtk_widget_hide (info->icon_window);
3698         
3699       gdk_drag_drop (info->context, time);
3700       info->drop_timeout = gdk_threads_add_timeout (DROP_ABORT_TIME,
3701                                           gtk_drag_abort_timeout,
3702                                           info);
3703     }
3704 }
3705
3706 /*
3707  * Source side callbacks.
3708  */
3709
3710 static gboolean
3711 gtk_drag_source_event_cb (GtkWidget      *widget,
3712                           GdkEvent       *event,
3713                           gpointer        data)
3714 {
3715   GtkDragSourceSite *site;
3716   gboolean retval = FALSE;
3717   site = (GtkDragSourceSite *)data;
3718
3719   switch (event->type)
3720     {
3721     case GDK_BUTTON_PRESS:
3722       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3723         {
3724           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
3725           site->x = event->button.x;
3726           site->y = event->button.y;
3727         }
3728       break;
3729       
3730     case GDK_BUTTON_RELEASE:
3731       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3732         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
3733       break;
3734       
3735     case GDK_MOTION_NOTIFY:
3736       if (site->state & event->motion.state & site->start_button_mask)
3737         {
3738           /* FIXME: This is really broken and can leave us
3739            * with a stuck grab
3740            */
3741           int i;
3742           for (i=1; i<6; i++)
3743             {
3744               if (site->state & event->motion.state & 
3745                   GDK_BUTTON1_MASK << (i - 1))
3746                 break;
3747             }
3748
3749           if (gtk_drag_check_threshold (widget, site->x, site->y,
3750                                         event->motion.x, event->motion.y))
3751             {
3752               site->state = 0;
3753               gtk_drag_begin_internal (widget, site, site->target_list,
3754                                        site->actions, 
3755                                        i, event);
3756
3757               retval = TRUE;
3758             }
3759         }
3760       break;
3761       
3762     default:                    /* hit for 2/3BUTTON_PRESS */
3763       break;
3764     }
3765   
3766   return retval;
3767 }
3768
3769 static void 
3770 gtk_drag_source_site_destroy (gpointer data)
3771 {
3772   GtkDragSourceSite *site = data;
3773
3774   if (site->target_list)
3775     gtk_target_list_unref (site->target_list);
3776
3777   gtk_drag_source_unset_icon (site);
3778   g_free (site);
3779 }
3780
3781 static void
3782 gtk_drag_selection_get (GtkWidget        *widget, 
3783                         GtkSelectionData *selection_data,
3784                         guint             sel_info,
3785                         guint32           time,
3786                         gpointer          data)
3787 {
3788   GtkDragSourceInfo *info = data;
3789   static GdkAtom null_atom = GDK_NONE;
3790   guint target_info;
3791
3792   if (!null_atom)
3793     null_atom = gdk_atom_intern_static_string ("NULL");
3794
3795   switch (sel_info)
3796     {
3797     case TARGET_DELETE:
3798       g_signal_emit_by_name (info->widget,
3799                              "drag-data-delete", 
3800                              info->context);
3801       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3802       break;
3803     case TARGET_MOTIF_SUCCESS:
3804       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3805       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3806       break;
3807     case TARGET_MOTIF_FAILURE:
3808       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3809       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3810       break;
3811     default:
3812       if (info->proxy_dest)
3813         {
3814           /* This is sort of dangerous and needs to be thought
3815            * through better
3816            */
3817           info->proxy_dest->proxy_data = selection_data;
3818           gtk_drag_get_data (info->widget,
3819                              info->proxy_dest->context,
3820                              selection_data->target,
3821                              time);
3822           gtk_main ();
3823           info->proxy_dest->proxy_data = NULL;
3824         }
3825       else
3826         {
3827           if (gtk_target_list_find (info->target_list, 
3828                                     selection_data->target, 
3829                                     &target_info))
3830             {
3831               g_signal_emit_by_name (info->widget, "drag-data-get",
3832                                      info->context,
3833                                      selection_data,
3834                                      target_info,
3835                                      time);
3836             }
3837         }
3838       break;
3839     }
3840 }
3841
3842 static gboolean
3843 gtk_drag_anim_timeout (gpointer data)
3844 {
3845   GtkDragAnim *anim = data;
3846   gint x, y;
3847   gboolean retval;
3848
3849   if (anim->step == anim->n_steps)
3850     {
3851       gtk_drag_source_info_destroy (anim->info);
3852       g_free (anim);
3853
3854       retval = FALSE;
3855     }
3856   else
3857     {
3858       x = (anim->info->start_x * (anim->step + 1) +
3859            anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3860       y = (anim->info->start_y * (anim->step + 1) +
3861            anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3862       if (anim->info->icon_window)
3863         {
3864           GtkWidget *icon_window;
3865           gint hot_x, hot_y;
3866           
3867           gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);   
3868           gtk_window_move (GTK_WINDOW (icon_window), 
3869                            x - hot_x, 
3870                            y - hot_y);
3871         }
3872   
3873       anim->step++;
3874
3875       retval = TRUE;
3876     }
3877
3878   return retval;
3879 }
3880
3881 static void
3882 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3883 {
3884   if (info->icon_window)
3885     {
3886       gtk_widget_hide (info->icon_window);
3887       if (info->destroy_icon)
3888         gtk_widget_destroy (info->icon_window);
3889
3890       if (info->fallback_icon)
3891         {
3892           gtk_widget_destroy (info->fallback_icon);
3893           info->fallback_icon = NULL;
3894         }
3895
3896       g_object_unref (info->icon_window);
3897       info->icon_window = NULL;
3898     }
3899 }
3900
3901 static void
3902 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3903 {
3904   gint i;
3905
3906   for (i = 0; i < n_drag_cursors; i++)
3907     {
3908       if (info->drag_cursors[i] != NULL)
3909         {
3910           gdk_cursor_unref (info->drag_cursors[i]);
3911           info->drag_cursors[i] = NULL;
3912         }
3913     }
3914
3915   gtk_drag_remove_icon (info);
3916
3917   if (info->icon_pixbuf)
3918     {
3919       g_object_unref (info->icon_pixbuf);
3920       info->icon_pixbuf = NULL;
3921     }
3922
3923   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3924                                         gtk_drag_grab_broken_event_cb,
3925                                         info);
3926   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3927                                         gtk_drag_grab_notify_cb,
3928                                         info);
3929   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3930                                         gtk_drag_button_release_cb,
3931                                         info);
3932   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3933                                         gtk_drag_motion_cb,
3934                                         info);
3935   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3936                                         gtk_drag_key_cb,
3937                                         info);
3938   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3939                                         gtk_drag_selection_get,
3940                                         info);
3941
3942   if (!info->proxy_dest)
3943     g_signal_emit_by_name (info->widget, "drag-end", info->context);
3944
3945   if (info->widget)
3946     g_object_unref (info->widget);
3947
3948   gtk_selection_remove_all (info->ipc_widget);
3949   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), NULL);
3950   source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3951   gtk_drag_release_ipc_widget (info->ipc_widget);
3952
3953   gtk_target_list_unref (info->target_list);
3954
3955   gtk_drag_clear_source_info (info->context);
3956   g_object_unref (info->context);
3957
3958   if (info->drop_timeout)
3959     g_source_remove (info->drop_timeout);
3960
3961   if (info->update_idle)
3962     g_source_remove (info->update_idle);
3963
3964   g_free (info);
3965 }
3966
3967 static gboolean
3968 gtk_drag_update_idle (gpointer data)
3969 {
3970   GtkDragSourceInfo *info = data;
3971   GdkWindow *dest_window;
3972   GdkDragProtocol protocol;
3973   GdkAtom selection;
3974
3975   GdkDragAction action;
3976   GdkDragAction possible_actions;
3977   guint32 time;
3978
3979   info->update_idle = 0;
3980     
3981   if (info->last_event)
3982     {
3983       time = gtk_drag_get_event_time (info->last_event);
3984       gtk_drag_get_event_actions (info->last_event,
3985                                   info->button, 
3986                                   info->possible_actions,
3987                                   &action, &possible_actions);
3988       gtk_drag_update_icon (info);
3989       gdk_drag_find_window_for_screen (info->context,
3990                                        info->icon_window ? info->icon_window->window : NULL,
3991                                        info->cur_screen, info->cur_x, info->cur_y,
3992                                        &dest_window, &protocol);
3993       
3994       if (!gdk_drag_motion (info->context, dest_window, protocol,
3995                             info->cur_x, info->cur_y, action, 
3996                             possible_actions,
3997                             time))
3998         {
3999           gdk_event_free ((GdkEvent *)info->last_event);
4000           info->last_event = NULL;
4001         }
4002   
4003       if (dest_window)
4004         g_object_unref (dest_window);
4005       
4006       selection = gdk_drag_get_selection (info->context);
4007       if (selection)
4008         gtk_drag_source_check_selection (info, selection, time);
4009
4010     }
4011
4012   return FALSE;
4013 }
4014
4015 static void
4016 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
4017 {
4018   /* We use an idle lower than GDK_PRIORITY_REDRAW so that exposes
4019    * from the last move can catch up before we move again.
4020    */
4021   if (!info->update_idle)
4022     info->update_idle = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW + 5,
4023                                          gtk_drag_update_idle,
4024                                          info,
4025                                          NULL);
4026 }
4027
4028 /**
4029  * gtk_drag_update:
4030  * @info: DragSourceInfo for the drag
4031  * @screen: new screen
4032  * @x_root: new X position 
4033  * @y_root: new y position
4034  * @event: event received requiring update
4035  * 
4036  * Updates the status of the drag; called when the
4037  * cursor moves or the modifier changes
4038  **/
4039 static void
4040 gtk_drag_update (GtkDragSourceInfo *info,
4041                  GdkScreen         *screen,
4042                  gint               x_root,
4043                  gint               y_root,
4044                  GdkEvent          *event)
4045 {
4046   info->cur_screen = screen;
4047   info->cur_x = x_root;
4048   info->cur_y = y_root;
4049   if (info->last_event)
4050     {
4051       gdk_event_free ((GdkEvent *)info->last_event);
4052       info->last_event = NULL;
4053     }
4054   if (event)
4055     info->last_event = gdk_event_copy ((GdkEvent *)event);
4056
4057   gtk_drag_add_update_idle (info);
4058 }
4059
4060 /*************************************************************
4061  * gtk_drag_end:
4062  *     Called when the user finishes to drag, either by
4063  *     releasing the mouse, or by pressing Esc.
4064  *   arguments:
4065  *     info: Source info for the drag
4066  *     time: Timestamp for ending the drag
4067  *   results:
4068  *************************************************************/
4069
4070 static void
4071 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
4072 {
4073   GdkEvent *send_event;
4074   GtkWidget *source_widget = info->widget;
4075   GdkDisplay *display = gtk_widget_get_display (source_widget);
4076
4077   if (info->update_idle)
4078     {
4079       g_source_remove (info->update_idle);
4080       info->update_idle = 0;
4081     }
4082   
4083   if (info->last_event)
4084     {
4085       gdk_event_free (info->last_event);
4086       info->last_event = NULL;
4087     }
4088   
4089   info->have_grab = FALSE;
4090   
4091   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4092                                         gtk_drag_grab_broken_event_cb,
4093                                         info);
4094   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4095                                         gtk_drag_grab_notify_cb,
4096                                         info);
4097   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4098                                         gtk_drag_button_release_cb,
4099                                         info);
4100   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4101                                         gtk_drag_motion_cb,
4102                                         info);
4103   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4104                                         gtk_drag_key_cb,
4105                                         info);
4106
4107   gdk_display_pointer_ungrab (display, time);
4108   ungrab_dnd_keys (info->ipc_widget, time);
4109   gtk_grab_remove (info->ipc_widget);
4110
4111   /* Send on a release pair to the original 
4112    * widget to convince it to release its grab. We need to
4113    * call gtk_propagate_event() here, instead of 
4114    * gtk_widget_event() because widget like GtkList may
4115    * expect propagation.
4116    */
4117
4118   send_event = gdk_event_new (GDK_BUTTON_RELEASE);
4119   send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
4120   send_event->button.send_event = TRUE;
4121   send_event->button.time = time;
4122   send_event->button.x = 0;
4123   send_event->button.y = 0;
4124   send_event->button.axes = NULL;
4125   send_event->button.state = 0;
4126   send_event->button.button = info->button;
4127   send_event->button.device = gdk_display_get_core_pointer (display);
4128   send_event->button.x_root = 0;
4129   send_event->button.y_root = 0;
4130
4131   gtk_propagate_event (source_widget, send_event);
4132   gdk_event_free (send_event);
4133 }
4134
4135 /*************************************************************
4136  * gtk_drag_cancel:
4137  *    Called on cancellation of a drag, either by the user
4138  *    or programmatically.
4139  *   arguments:
4140  *     info: Source info for the drag
4141  *     time: Timestamp for ending the drag
4142  *   results:
4143  *************************************************************/
4144
4145 static void
4146 gtk_drag_cancel (GtkDragSourceInfo *info, GtkDragResult result, guint32 time)
4147 {
4148   gtk_drag_end (info, time);
4149   gdk_drag_abort (info->context, time);
4150   gtk_drag_drop_finished (info, result, time);
4151 }
4152
4153 /*************************************************************
4154  * gtk_drag_motion_cb:
4155  *     "motion-notify-event" callback during drag.
4156  *   arguments:
4157  *     
4158  *   results:
4159  *************************************************************/
4160
4161 static gboolean
4162 gtk_drag_motion_cb (GtkWidget      *widget, 
4163                     GdkEventMotion *event, 
4164                     gpointer        data)
4165 {
4166   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4167   GdkScreen *screen;
4168   gint x_root, y_root;
4169
4170   if (event->is_hint)
4171     {
4172       GdkDisplay *display = gtk_widget_get_display (widget);
4173       
4174       gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
4175       event->x_root = x_root;
4176       event->y_root = y_root;
4177     }
4178   else
4179     screen = gdk_event_get_screen ((GdkEvent *)event);
4180
4181   gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
4182
4183   return TRUE;
4184 }
4185
4186 /*************************************************************
4187  * gtk_drag_key_cb:
4188  *     "key-press/release-event" callback during drag.
4189  *   arguments:
4190  *     
4191  *   results:
4192  *************************************************************/
4193
4194 #define BIG_STEP 20
4195 #define SMALL_STEP 1
4196
4197 static gboolean
4198 gtk_drag_key_cb (GtkWidget         *widget, 
4199                  GdkEventKey       *event, 
4200                  gpointer           data)
4201 {
4202   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4203   GdkModifierType state;
4204   GdkWindow *root_window;
4205   gint dx, dy;
4206
4207   dx = dy = 0;
4208   state = event->state & gtk_accelerator_get_default_mod_mask ();
4209
4210   if (event->type == GDK_KEY_PRESS)
4211     {
4212       switch (event->keyval)
4213         {
4214         case GDK_Escape:
4215           gtk_drag_cancel (info, GTK_DRAG_RESULT_USER_CANCELLED, event->time);
4216           return TRUE;
4217
4218         case GDK_space:
4219         case GDK_Return:
4220         case GDK_ISO_Enter:
4221         case GDK_KP_Enter:
4222         case GDK_KP_Space:
4223           gtk_drag_end (info, event->time);
4224           gtk_drag_drop (info, event->time);
4225           return TRUE;
4226
4227         case GDK_Up:
4228         case GDK_KP_Up:
4229           dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4230           break;
4231           
4232         case GDK_Down:
4233         case GDK_KP_Down:
4234           dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4235           break;
4236           
4237         case GDK_Left:
4238         case GDK_KP_Left:
4239           dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4240           break;
4241           
4242         case GDK_Right:
4243         case GDK_KP_Right:
4244           dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4245           break;
4246         }
4247       
4248     }
4249
4250   /* Now send a "motion" so that the modifier state is updated */
4251
4252   /* The state is not yet updated in the event, so we need
4253    * to query it here. We could use XGetModifierMapping, but
4254    * that would be overkill.
4255    */
4256   root_window = gtk_widget_get_root_window (widget);
4257   gdk_window_get_pointer (root_window, NULL, NULL, &state);
4258   event->state = state;
4259
4260   if (dx != 0 || dy != 0)
4261     {
4262       info->cur_x += dx;
4263       info->cur_y += dy;
4264       gdk_display_warp_pointer (gtk_widget_get_display (widget), 
4265                                 gtk_widget_get_screen (widget), 
4266                                 info->cur_x, info->cur_y);
4267     }
4268
4269   gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
4270
4271   return TRUE;
4272 }
4273
4274 static gboolean
4275 gtk_drag_grab_broken_event_cb (GtkWidget          *widget,
4276                                GdkEventGrabBroken *event,
4277                                gpointer            data)
4278 {
4279   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4280
4281   /* Don't cancel if we break the implicit grab from the initial button_press.
4282    * Also, don't cancel if we re-grab on the widget or on our IPC window, for
4283    * example, when changing the drag cursor.
4284    */
4285   if (event->implicit
4286       || event->grab_window == info->widget->window
4287       || event->grab_window == info->ipc_widget->window)
4288     return FALSE;
4289
4290   gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4291   return TRUE;
4292 }
4293
4294 static void
4295 gtk_drag_grab_notify_cb (GtkWidget        *widget,
4296                          gboolean          was_grabbed,
4297                          gpointer          data)
4298 {
4299   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4300
4301   if (!was_grabbed)
4302     {
4303       /* We have to block callbacks to avoid recursion here, because
4304          gtk_drag_cancel calls gtk_grab_remove (via gtk_drag_end) */
4305       g_signal_handlers_block_by_func (widget, gtk_drag_grab_notify_cb, data);
4306       gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4307       g_signal_handlers_unblock_by_func (widget, gtk_drag_grab_notify_cb, data);
4308     }
4309 }
4310
4311
4312 /*************************************************************
4313  * gtk_drag_button_release_cb:
4314  *     "button-release-event" callback during drag.
4315  *   arguments:
4316  *     
4317  *   results:
4318  *************************************************************/
4319
4320 static gboolean
4321 gtk_drag_button_release_cb (GtkWidget      *widget, 
4322                             GdkEventButton *event, 
4323                             gpointer        data)
4324 {
4325   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4326
4327   if (event->button != info->button)
4328     return FALSE;
4329
4330   if ((info->context->action != 0) && (info->context->dest_window != NULL))
4331     {
4332       gtk_drag_end (info, event->time);
4333       gtk_drag_drop (info, event->time);
4334     }
4335   else
4336     {
4337       gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
4338     }
4339
4340   return TRUE;
4341 }
4342
4343 static gboolean
4344 gtk_drag_abort_timeout (gpointer data)
4345 {
4346   GtkDragSourceInfo *info = data;
4347   guint32 time = GDK_CURRENT_TIME;
4348
4349   if (info->proxy_dest)
4350     time = info->proxy_dest->proxy_drop_time;
4351
4352   info->drop_timeout = 0;
4353   gtk_drag_drop_finished (info, GTK_DRAG_RESULT_TIMEOUT_EXPIRED, time);
4354   
4355   return FALSE;
4356 }
4357
4358 /**
4359  * gtk_drag_check_threshold:
4360  * @widget: a #GtkWidget
4361  * @start_x: X coordinate of start of drag
4362  * @start_y: Y coordinate of start of drag
4363  * @current_x: current X coordinate
4364  * @current_y: current Y coordinate
4365  * 
4366  * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
4367  * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
4368  * should trigger the beginning of a drag-and-drop operation.
4369  *
4370  * Return Value: %TRUE if the drag threshold has been passed.
4371  **/
4372 gboolean
4373 gtk_drag_check_threshold (GtkWidget *widget,
4374                           gint       start_x,
4375                           gint       start_y,
4376                           gint       current_x,
4377                           gint       current_y)
4378 {
4379   gint drag_threshold;
4380
4381   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
4382
4383   g_object_get (gtk_widget_get_settings (widget),
4384                 "gtk-dnd-drag-threshold", &drag_threshold,
4385                 NULL);
4386   
4387   return (ABS (current_x - start_x) > drag_threshold ||
4388           ABS (current_y - start_y) > drag_threshold);
4389 }
4390
4391 #define __GTK_DND_C__
4392 #include "gtkaliasdef.c"