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