]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd.c
Clarify what the @event is used for when calling gtk_drag_begin()
[~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       if (time == GDK_CURRENT_TIME)
2366         time = gtk_get_current_event_time ();
2367
2368       pointer = gdk_event_get_device (event);
2369
2370       if (pointer->source == GDK_SOURCE_KEYBOARD)
2371         {
2372           keyboard = pointer;
2373           pointer = gdk_device_get_associated_device (keyboard);
2374         }
2375       else
2376         keyboard = gdk_device_get_associated_device (pointer);
2377     }
2378   else
2379     {
2380       GdkDeviceManager *device_manager;
2381
2382       device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
2383       pointer = gdk_device_manager_get_client_pointer (device_manager);
2384       keyboard = gdk_device_get_associated_device (pointer);
2385     }
2386
2387   if (!pointer)
2388     return NULL;
2389
2390   if (gdk_device_grab (pointer, ipc_widget->window,
2391                        GDK_OWNERSHIP_APPLICATION, FALSE,
2392                        GDK_POINTER_MOTION_MASK |
2393                        GDK_BUTTON_RELEASE_MASK,
2394                        cursor, time) != GDK_GRAB_SUCCESS)
2395     {
2396       gtk_drag_release_ipc_widget (ipc_widget);
2397       return NULL;
2398     }
2399
2400   if (keyboard)
2401     grab_dnd_keys (ipc_widget, keyboard, time);
2402
2403   /* We use a GTK grab here to override any grabs that the widget
2404    * we are dragging from might have held
2405    */
2406   gtk_device_grab_add (ipc_widget, pointer, FALSE);
2407
2408   tmp_list = g_list_last (target_list->list);
2409   while (tmp_list)
2410     {
2411       GtkTargetPair *pair = tmp_list->data;
2412       targets = g_list_prepend (targets, 
2413                                 GINT_TO_POINTER (pair->target));
2414       tmp_list = tmp_list->prev;
2415     }
2416
2417   source_widgets = g_slist_prepend (source_widgets, ipc_widget);
2418
2419   context = gdk_drag_begin (ipc_widget->window, targets);
2420   gdk_drag_context_set_device (context, pointer);
2421   g_list_free (targets);
2422   
2423   info = gtk_drag_get_source_info (context, TRUE);
2424   
2425   info->ipc_widget = ipc_widget;
2426   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), info);
2427
2428   info->widget = g_object_ref (widget);
2429   
2430   info->button = button;
2431   info->cursor = cursor;
2432   info->target_list = target_list;
2433   gtk_target_list_ref (target_list);
2434
2435   info->possible_actions = actions;
2436
2437   info->status = GTK_DRAG_STATUS_DRAG;
2438   info->last_event = NULL;
2439   info->selections = NULL;
2440   info->icon_window = NULL;
2441   info->destroy_icon = FALSE;
2442
2443   /* Set cur_x, cur_y here so if the "drag-begin" signal shows
2444    * the drag icon, it will be in the right place
2445    */
2446   if (event && event->type == GDK_MOTION_NOTIFY)
2447     {
2448       info->cur_screen = gtk_widget_get_screen (widget);
2449       info->cur_x = event->motion.x_root;
2450       info->cur_y = event->motion.y_root;
2451     }
2452   else
2453     {
2454       gdk_display_get_device_state (gtk_widget_get_display (widget), pointer,
2455                                     &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
2456     }
2457
2458   g_signal_emit_by_name (widget, "drag-begin", info->context);
2459
2460   /* Ensure that we have an icon before we start the drag; the
2461    * application may have set one in ::drag_begin, or it may
2462    * not have set one.
2463    */
2464   if (!info->icon_window && !info->icon_pixbuf)
2465     {
2466       if (!site || site->icon_type == GTK_IMAGE_EMPTY)
2467         gtk_drag_set_icon_default (context);
2468       else
2469         switch (site->icon_type)
2470           {
2471           case GTK_IMAGE_PIXMAP:
2472             gtk_drag_set_icon_pixmap (context,
2473                                       site->colormap,
2474                                       site->icon_data.pixmap.pixmap,
2475                                       site->icon_mask,
2476                                       -2, -2);
2477             break;
2478           case GTK_IMAGE_PIXBUF:
2479             gtk_drag_set_icon_pixbuf (context,
2480                                       site->icon_data.pixbuf.pixbuf,
2481                                       -2, -2);
2482             break;
2483           case GTK_IMAGE_STOCK:
2484             gtk_drag_set_icon_stock (context,
2485                                      site->icon_data.stock.stock_id,
2486                                      -2, -2);
2487             break;
2488           case GTK_IMAGE_ICON_NAME:
2489             gtk_drag_set_icon_name (context,
2490                                     site->icon_data.name.icon_name,
2491                                     -2, -2);
2492             break;
2493           case GTK_IMAGE_EMPTY:
2494           default:
2495             g_assert_not_reached();
2496             break;
2497           }
2498     }
2499
2500   /* We need to composite the icon into the cursor, if we are
2501    * not using an icon window.
2502    */
2503   if (info->icon_pixbuf)  
2504     {
2505       cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), 
2506                                     suggested_action,
2507                                     info);
2508   
2509       if (cursor != info->cursor)
2510         {
2511           gdk_device_grab (pointer, widget->window,
2512                            GDK_OWNERSHIP_APPLICATION, FALSE,
2513                            GDK_POINTER_MOTION_MASK |
2514                            GDK_BUTTON_RELEASE_MASK,
2515                            cursor, time);
2516           info->cursor = cursor;
2517         }
2518     }
2519     
2520   if (event && event->type == GDK_MOTION_NOTIFY)
2521     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
2522   else
2523     gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
2524
2525   info->start_x = info->cur_x;
2526   info->start_y = info->cur_y;
2527
2528   g_signal_connect (info->ipc_widget, "grab-broken-event",
2529                     G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
2530   g_signal_connect (info->ipc_widget, "grab-notify",
2531                     G_CALLBACK (gtk_drag_grab_notify_cb), info);
2532   g_signal_connect (info->ipc_widget, "button-release-event",
2533                     G_CALLBACK (gtk_drag_button_release_cb), info);
2534   g_signal_connect (info->ipc_widget, "motion-notify-event",
2535                     G_CALLBACK (gtk_drag_motion_cb), info);
2536   g_signal_connect (info->ipc_widget, "key-press-event",
2537                     G_CALLBACK (gtk_drag_key_cb), info);
2538   g_signal_connect (info->ipc_widget, "key-release-event",
2539                     G_CALLBACK (gtk_drag_key_cb), info);
2540   g_signal_connect (info->ipc_widget, "selection-get",
2541                     G_CALLBACK (gtk_drag_selection_get), info);
2542
2543   info->have_grab = TRUE;
2544   info->grab_time = time;
2545
2546   return info->context;
2547 }
2548
2549 /**
2550  * gtk_drag_begin:
2551  * @widget: the source widget.
2552  * @targets: The targets (data formats) in which the
2553  *    source can provide the data.
2554  * @actions: A bitmask of the allowed drag actions for this drag.
2555  * @button: The button the user clicked to start the drag.
2556  * @event: The event that triggered the start of the drag.
2557  * 
2558  * Initiates a drag on the source side. The function
2559  * only needs to be used when the application is
2560  * starting drags itself, and is not needed when
2561  * gtk_drag_source_set() is used.
2562  *
2563  * The @event is used to retrieve the timestamp that will be used internally to
2564  * grab the pointer.  If @event is #NULL, then GDK_CURRENT_TIME will be used.
2565  * However, you should try to pass a real event in all cases, since that can be
2566  * used by GTK+ to get information about the start position of the drag, for
2567  * example if the @event is a GDK_MOTION_NOTIFY.
2568  *
2569  * Generally there are three cases when you want to start a drag by hand by calling
2570  * this function:
2571  *
2572  * 1. During a button-press-event handler, if you want to start a drag immediately
2573  * when the user presses the mouse button.  Pass the @event that you have in your
2574  * button-press-event handler.
2575  *
2576  * 2. During a motion-notify-event handler, if you want to start a drag when the mouse
2577  * moves past a certain threshold distance after a button-press.  Pass the @event that you
2578  * have in your motion-notify-event handler.
2579  *
2580  * 3. During a timeout handler, if you want to start a drag after the mouse
2581  * button is held down for some time.  Try to save the last event that you got
2582  * from the mouse, using gdk_event_copy(), and pass it to this function
2583  * (remember to free the event with gdk_event_free() when you are done).  If you
2584  * can really not pass a real event, pass #NULL instead.
2585  * 
2586  * Return value: the context for this drag.
2587  **/
2588 GdkDragContext *
2589 gtk_drag_begin (GtkWidget         *widget,
2590                 GtkTargetList     *targets,
2591                 GdkDragAction      actions,
2592                 gint               button,
2593                 GdkEvent          *event)
2594 {
2595   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2596   g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
2597   g_return_val_if_fail (targets != NULL, NULL);
2598
2599   return gtk_drag_begin_internal (widget, NULL, targets,
2600                                   actions, button, event);
2601 }
2602
2603 /**
2604  * gtk_drag_source_set:
2605  * @widget: a #GtkWidget
2606  * @start_button_mask: the bitmask of buttons that can start the drag
2607  * @targets: (allow-none) (array length=n_targets): the table of targets that the drag will support,
2608  *     may be %NULL
2609  * @n_targets: the number of items in @targets
2610  * @actions: the bitmask of possible actions for a drag from this widget
2611  *
2612  * Sets up a widget so that GTK+ will start a drag operation when the user
2613  * clicks and drags on the widget. The widget must have a window.
2614  */
2615 void
2616 gtk_drag_source_set (GtkWidget            *widget,
2617                      GdkModifierType       start_button_mask,
2618                      const GtkTargetEntry *targets,
2619                      gint                  n_targets,
2620                      GdkDragAction         actions)
2621 {
2622   GtkDragSourceSite *site;
2623
2624   g_return_if_fail (GTK_IS_WIDGET (widget));
2625
2626   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2627
2628   gtk_widget_add_events (widget,
2629                          gtk_widget_get_events (widget) |
2630                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2631                          GDK_BUTTON_MOTION_MASK);
2632
2633   if (site)
2634     {
2635       if (site->target_list)
2636         gtk_target_list_unref (site->target_list);
2637     }
2638   else
2639     {
2640       site = g_new0 (GtkDragSourceSite, 1);
2641
2642       site->icon_type = GTK_IMAGE_EMPTY;
2643       
2644       g_signal_connect (widget, "button-press-event",
2645                         G_CALLBACK (gtk_drag_source_event_cb),
2646                         site);
2647       g_signal_connect (widget, "button-release-event",
2648                         G_CALLBACK (gtk_drag_source_event_cb),
2649                         site);
2650       g_signal_connect (widget, "motion-notify-event",
2651                         G_CALLBACK (gtk_drag_source_event_cb),
2652                         site);
2653       
2654       g_object_set_data_full (G_OBJECT (widget),
2655                               I_("gtk-site-data"), 
2656                               site, gtk_drag_source_site_destroy);
2657     }
2658
2659   site->start_button_mask = start_button_mask;
2660
2661   site->target_list = gtk_target_list_new (targets, n_targets);
2662
2663   site->actions = actions;
2664 }
2665
2666 /*************************************************************
2667  * gtk_drag_source_unset
2668  *     Unregister this widget as a drag source.
2669  *   arguments:
2670  *     widget:
2671  *   results:
2672  *************************************************************/
2673
2674 void 
2675 gtk_drag_source_unset (GtkWidget        *widget)
2676 {
2677   GtkDragSourceSite *site;
2678
2679   g_return_if_fail (GTK_IS_WIDGET (widget));
2680
2681   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2682
2683   if (site)
2684     {
2685       g_signal_handlers_disconnect_by_func (widget,
2686                                             gtk_drag_source_event_cb,
2687                                             site);
2688       g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
2689     }
2690 }
2691
2692 /**
2693  * gtk_drag_source_get_target_list:
2694  * @widget: a #GtkWidget
2695  *
2696  * Gets the list of targets this widget can provide for
2697  * drag-and-drop.
2698  *
2699  * Return value: the #GtkTargetList, or %NULL if none
2700  *
2701  * Since: 2.4
2702  **/
2703 GtkTargetList *
2704 gtk_drag_source_get_target_list (GtkWidget *widget)
2705 {
2706   GtkDragSourceSite *site;
2707
2708   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2709
2710   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2711
2712   return site ? site->target_list : NULL;
2713 }
2714
2715 /**
2716  * gtk_drag_source_set_target_list:
2717  * @widget: a #GtkWidget that's a drag source
2718  * @target_list: (allow-none): list of draggable targets, or %NULL for none
2719  *
2720  * Changes the target types that this widget offers for drag-and-drop.
2721  * The widget must first be made into a drag source with
2722  * gtk_drag_source_set().
2723  *
2724  * Since: 2.4
2725  **/
2726 void
2727 gtk_drag_source_set_target_list (GtkWidget     *widget,
2728                                  GtkTargetList *target_list)
2729 {
2730   GtkDragSourceSite *site;
2731
2732   g_return_if_fail (GTK_IS_WIDGET (widget));
2733
2734   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2735   if (site == NULL)
2736     {
2737       g_warning ("gtk_drag_source_set_target_list() requires the widget "
2738                  "to already be a drag source.");
2739       return;
2740     }
2741
2742   if (target_list)
2743     gtk_target_list_ref (target_list);
2744
2745   if (site->target_list)
2746     gtk_target_list_unref (site->target_list);
2747
2748   site->target_list = target_list;
2749 }
2750
2751 /**
2752  * gtk_drag_source_add_text_targets:
2753  * @widget: a #GtkWidget that's is a drag source
2754  *
2755  * Add the text targets supported by #GtkSelection to
2756  * the target list of the drag source.  The targets
2757  * are added with @info = 0. If you need another value, 
2758  * use gtk_target_list_add_text_targets() and
2759  * gtk_drag_source_set_target_list().
2760  * 
2761  * Since: 2.6
2762  **/
2763 void
2764 gtk_drag_source_add_text_targets (GtkWidget *widget)
2765 {
2766   GtkTargetList *target_list;
2767
2768   target_list = gtk_drag_source_get_target_list (widget);
2769   if (target_list)
2770     gtk_target_list_ref (target_list);
2771   else
2772     target_list = gtk_target_list_new (NULL, 0);
2773   gtk_target_list_add_text_targets (target_list, 0);
2774   gtk_drag_source_set_target_list (widget, target_list);
2775   gtk_target_list_unref (target_list);
2776 }
2777
2778 /**
2779  * gtk_drag_source_add_image_targets:
2780  * @widget: a #GtkWidget that's is a drag source
2781  *
2782  * Add the writable image targets supported by #GtkSelection to
2783  * the target list of the drag source. The targets
2784  * are added with @info = 0. If you need another value, 
2785  * use gtk_target_list_add_image_targets() and
2786  * gtk_drag_source_set_target_list().
2787  * 
2788  * Since: 2.6
2789  **/
2790 void
2791 gtk_drag_source_add_image_targets (GtkWidget *widget)
2792 {
2793   GtkTargetList *target_list;
2794
2795   target_list = gtk_drag_source_get_target_list (widget);
2796   if (target_list)
2797     gtk_target_list_ref (target_list);
2798   else
2799     target_list = gtk_target_list_new (NULL, 0);
2800   gtk_target_list_add_image_targets (target_list, 0, TRUE);
2801   gtk_drag_source_set_target_list (widget, target_list);
2802   gtk_target_list_unref (target_list);
2803 }
2804
2805 /**
2806  * gtk_drag_source_add_uri_targets:
2807  * @widget: a #GtkWidget that's is a drag source
2808  *
2809  * Add the URI targets supported by #GtkSelection to
2810  * the target list of the drag source.  The targets
2811  * are added with @info = 0. If you need another value, 
2812  * use gtk_target_list_add_uri_targets() and
2813  * gtk_drag_source_set_target_list().
2814  * 
2815  * Since: 2.6
2816  **/
2817 void
2818 gtk_drag_source_add_uri_targets (GtkWidget *widget)
2819 {
2820   GtkTargetList *target_list;
2821
2822   target_list = gtk_drag_source_get_target_list (widget);
2823   if (target_list)
2824     gtk_target_list_ref (target_list);
2825   else
2826     target_list = gtk_target_list_new (NULL, 0);
2827   gtk_target_list_add_uri_targets (target_list, 0);
2828   gtk_drag_source_set_target_list (widget, target_list);
2829   gtk_target_list_unref (target_list);
2830 }
2831
2832 static void
2833 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2834 {
2835   switch (site->icon_type)
2836     {
2837     case GTK_IMAGE_EMPTY:
2838       break;
2839     case GTK_IMAGE_PIXMAP:
2840       if (site->icon_data.pixmap.pixmap)
2841         g_object_unref (site->icon_data.pixmap.pixmap);
2842       if (site->icon_mask)
2843         g_object_unref (site->icon_mask);
2844       break;
2845     case GTK_IMAGE_PIXBUF:
2846       g_object_unref (site->icon_data.pixbuf.pixbuf);
2847       break;
2848     case GTK_IMAGE_STOCK:
2849       g_free (site->icon_data.stock.stock_id);
2850       break;
2851     case GTK_IMAGE_ICON_NAME:
2852       g_free (site->icon_data.name.icon_name);
2853       break;
2854     default:
2855       g_assert_not_reached();
2856       break;
2857     }
2858   site->icon_type = GTK_IMAGE_EMPTY;
2859   
2860   if (site->colormap)
2861     g_object_unref (site->colormap);
2862   site->colormap = NULL;
2863 }
2864
2865 /**
2866  * gtk_drag_source_set_icon:
2867  * @widget: a #GtkWidget
2868  * @colormap: the colormap of the icon
2869  * @pixmap: the image data for the icon
2870  * @mask: (allow-none): the transparency mask for an image.
2871  *
2872  * Sets the icon that will be used for drags from a particular widget
2873  * from a pixmap/mask. GTK+ retains references for the arguments, and
2874  * will release them when they are no longer needed.
2875  * Use gtk_drag_source_set_icon_pixbuf() instead.
2876  **/
2877 void 
2878 gtk_drag_source_set_icon (GtkWidget     *widget,
2879                           GdkColormap   *colormap,
2880                           GdkPixmap     *pixmap,
2881                           GdkBitmap     *mask)
2882 {
2883   GtkDragSourceSite *site;
2884
2885   g_return_if_fail (GTK_IS_WIDGET (widget));
2886   g_return_if_fail (GDK_IS_COLORMAP (colormap));
2887   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2888   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2889
2890   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2891   g_return_if_fail (site != NULL);
2892   
2893   g_object_ref (colormap);
2894   g_object_ref (pixmap);
2895   if (mask)
2896     g_object_ref (mask);
2897
2898   gtk_drag_source_unset_icon (site);
2899
2900   site->icon_type = GTK_IMAGE_PIXMAP;
2901   
2902   site->icon_data.pixmap.pixmap = pixmap;
2903   site->icon_mask = mask;
2904   site->colormap = colormap;
2905 }
2906
2907 /**
2908  * gtk_drag_source_set_icon_pixbuf:
2909  * @widget: a #GtkWidget
2910  * @pixbuf: the #GdkPixbuf for the drag icon
2911  * 
2912  * Sets the icon that will be used for drags from a particular widget
2913  * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will 
2914  * release it when it is no longer needed.
2915  **/
2916 void 
2917 gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
2918                                  GdkPixbuf   *pixbuf)
2919 {
2920   GtkDragSourceSite *site;
2921
2922   g_return_if_fail (GTK_IS_WIDGET (widget));
2923   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2924
2925   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2926   g_return_if_fail (site != NULL); 
2927   g_object_ref (pixbuf);
2928
2929   gtk_drag_source_unset_icon (site);
2930
2931   site->icon_type = GTK_IMAGE_PIXBUF;
2932   site->icon_data.pixbuf.pixbuf = pixbuf;
2933 }
2934
2935 /**
2936  * gtk_drag_source_set_icon_stock:
2937  * @widget: a #GtkWidget
2938  * @stock_id: the ID of the stock icon to use
2939  *
2940  * Sets the icon that will be used for drags from a particular source
2941  * to a stock icon. 
2942  **/
2943 void 
2944 gtk_drag_source_set_icon_stock (GtkWidget   *widget,
2945                                 const gchar *stock_id)
2946 {
2947   GtkDragSourceSite *site;
2948
2949   g_return_if_fail (GTK_IS_WIDGET (widget));
2950   g_return_if_fail (stock_id != NULL);
2951
2952   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2953   g_return_if_fail (site != NULL);
2954   
2955   gtk_drag_source_unset_icon (site);
2956
2957   site->icon_type = GTK_IMAGE_STOCK;
2958   site->icon_data.stock.stock_id = g_strdup (stock_id);
2959 }
2960
2961 /**
2962  * gtk_drag_source_set_icon_name:
2963  * @widget: a #GtkWidget
2964  * @icon_name: name of icon to use
2965  * 
2966  * Sets the icon that will be used for drags from a particular source
2967  * to a themed icon. See the docs for #GtkIconTheme for more details.
2968  *
2969  * Since: 2.8
2970  **/
2971 void 
2972 gtk_drag_source_set_icon_name (GtkWidget   *widget,
2973                                const gchar *icon_name)
2974 {
2975   GtkDragSourceSite *site;
2976
2977   g_return_if_fail (GTK_IS_WIDGET (widget));
2978   g_return_if_fail (icon_name != NULL);
2979
2980   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2981   g_return_if_fail (site != NULL);
2982
2983   gtk_drag_source_unset_icon (site);
2984
2985   site->icon_type = GTK_IMAGE_ICON_NAME;
2986   site->icon_data.name.icon_name = g_strdup (icon_name);
2987 }
2988
2989 static void
2990 gtk_drag_get_icon (GtkDragSourceInfo *info,
2991                    GtkWidget        **icon_window,
2992                    gint              *hot_x,
2993                    gint              *hot_y)
2994 {
2995   if (get_can_change_screen (info->icon_window))
2996     gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2997                            info->cur_screen);
2998       
2999   if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
3000     {
3001       if (!info->fallback_icon)
3002         {
3003           gint save_hot_x, save_hot_y;
3004           gboolean save_destroy_icon;
3005           GtkWidget *save_icon_window;
3006           
3007           /* HACK to get the appropriate icon
3008            */
3009           save_icon_window = info->icon_window;
3010           save_hot_x = info->hot_x;
3011           save_hot_y = info->hot_x;
3012           save_destroy_icon = info->destroy_icon;
3013
3014           info->icon_window = NULL;
3015           if (!default_icon_pixmap)
3016             set_icon_stock_pixbuf (info->context, 
3017                                    GTK_STOCK_DND, NULL, -2, -2, TRUE);
3018           else
3019             gtk_drag_set_icon_pixmap (info->context, 
3020                                       default_icon_colormap, 
3021                                       default_icon_pixmap, 
3022                                       default_icon_mask,
3023                                       default_icon_hot_x,
3024                                       default_icon_hot_y);
3025           info->fallback_icon = info->icon_window;
3026           
3027           info->icon_window = save_icon_window;
3028           info->hot_x = save_hot_x;
3029           info->hot_y = save_hot_y;
3030           info->destroy_icon = save_destroy_icon;
3031         }
3032       
3033       gtk_widget_hide (info->icon_window);
3034       
3035       *icon_window = info->fallback_icon;
3036       gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
3037       
3038       if (!default_icon_pixmap)
3039         {
3040           *hot_x = -2;
3041           *hot_y = -2;
3042         }
3043       else
3044         {
3045           *hot_x = default_icon_hot_x;
3046           *hot_y = default_icon_hot_y;
3047         }
3048     }
3049   else
3050     {
3051       if (info->fallback_icon)
3052         gtk_widget_hide (info->fallback_icon);
3053       
3054       *icon_window = info->icon_window;
3055       *hot_x = info->hot_x;
3056       *hot_y = info->hot_y;
3057     }
3058 }
3059
3060 static void
3061 gtk_drag_update_icon (GtkDragSourceInfo *info)
3062 {
3063   if (info->icon_window)
3064     {
3065       GtkWidget *icon_window;
3066       gint hot_x, hot_y;
3067   
3068       gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
3069       
3070       gtk_window_move (GTK_WINDOW (icon_window), 
3071                        info->cur_x - hot_x, 
3072                        info->cur_y - hot_y);
3073
3074       if (gtk_widget_get_visible (icon_window))
3075         gdk_window_raise (icon_window->window);
3076       else
3077         gtk_widget_show (icon_window);
3078     }
3079 }
3080
3081 static void 
3082 gtk_drag_set_icon_window (GdkDragContext *context,
3083                           GtkWidget      *widget,
3084                           gint            hot_x,
3085                           gint            hot_y,
3086                           gboolean        destroy_on_release)
3087 {
3088   GtkDragSourceInfo *info;
3089
3090   info = gtk_drag_get_source_info (context, FALSE);
3091   if (info == NULL)
3092     {
3093       if (destroy_on_release)
3094         gtk_widget_destroy (widget);
3095       return;
3096     }
3097
3098   gtk_drag_remove_icon (info);
3099
3100   if (widget)
3101     g_object_ref (widget);  
3102   
3103   info->icon_window = widget;
3104   info->hot_x = hot_x;
3105   info->hot_y = hot_y;
3106   info->destroy_icon = destroy_on_release;
3107
3108   if (widget && info->icon_pixbuf)
3109     {
3110       g_object_unref (info->icon_pixbuf);
3111       info->icon_pixbuf = NULL;
3112     }
3113
3114   gtk_drag_update_cursor (info);
3115   gtk_drag_update_icon (info);
3116 }
3117
3118 /**
3119  * gtk_drag_set_icon_widget:
3120  * @context: the context for a drag. (This must be called 
3121           with a  context for the source side of a drag)
3122  * @widget: a toplevel window to use as an icon.
3123  * @hot_x: the X offset within @widget of the hotspot.
3124  * @hot_y: the Y offset within @widget of the hotspot.
3125  * 
3126  * Changes the icon for a widget to a given widget. GTK+
3127  * will not destroy the icon, so if you don't want
3128  * it to persist, you should connect to the "drag-end" 
3129  * signal and destroy it yourself.
3130  **/
3131 void 
3132 gtk_drag_set_icon_widget (GdkDragContext    *context,
3133                           GtkWidget         *widget,
3134                           gint               hot_x,
3135                           gint               hot_y)
3136 {
3137   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3138   g_return_if_fail (context->is_source);
3139   g_return_if_fail (GTK_IS_WIDGET (widget));
3140
3141   gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
3142 }
3143
3144 static void
3145 icon_window_realize (GtkWidget *window,
3146                      GdkPixbuf *pixbuf)
3147 {
3148   GdkPixmap *pixmap;
3149   GdkPixmap *mask;
3150
3151   gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
3152                                                   gtk_widget_get_colormap (window),
3153                                                   &pixmap, &mask, 128);
3154   
3155   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
3156   g_object_unref (pixmap);
3157   
3158   if (mask)
3159     {
3160       gtk_widget_shape_combine_mask (window, mask, 0, 0);
3161       g_object_unref (mask);
3162     }
3163 }
3164
3165 static void
3166 set_icon_stock_pixbuf (GdkDragContext    *context,
3167                        const gchar       *stock_id,
3168                        GdkPixbuf         *pixbuf,
3169                        gint               hot_x,
3170                        gint               hot_y,
3171                        gboolean           force_window)
3172 {
3173   GtkWidget *window;
3174   gint width, height;
3175   GdkScreen *screen;
3176   GdkDisplay *display;
3177
3178   g_return_if_fail (context != NULL);
3179   g_return_if_fail (pixbuf != NULL || stock_id != NULL);
3180   g_return_if_fail (pixbuf == NULL || stock_id == NULL);
3181
3182   screen = gdk_drawable_get_screen (context->source_window);
3183
3184   /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
3185   gtk_widget_push_colormap (NULL);
3186   window = gtk_window_new (GTK_WINDOW_POPUP);
3187   gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3188   gtk_window_set_screen (GTK_WINDOW (window), screen);
3189   set_can_change_screen (window, TRUE);
3190   gtk_widget_pop_colormap ();
3191
3192   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3193   gtk_widget_set_app_paintable (window, TRUE);
3194
3195   if (stock_id)
3196     {
3197       pixbuf = gtk_widget_render_icon (window, stock_id,
3198                                        GTK_ICON_SIZE_DND, NULL);
3199
3200       if (!pixbuf)
3201         {
3202           g_warning ("Cannot load drag icon from stock_id %s", stock_id);
3203           gtk_widget_destroy (window);
3204           return;
3205         }
3206
3207     }
3208   else
3209     g_object_ref (pixbuf);
3210
3211   display = gdk_drawable_get_display (context->source_window);
3212   width = gdk_pixbuf_get_width (pixbuf);
3213   height = gdk_pixbuf_get_height (pixbuf);
3214
3215   if (!force_window &&
3216       gtk_drag_can_use_rgba_cursor (display, width + 2, height + 2))
3217     {
3218       GtkDragSourceInfo *info;
3219
3220       gtk_widget_destroy (window);
3221
3222       info = gtk_drag_get_source_info (context, FALSE);
3223
3224       if (info->icon_pixbuf)
3225         g_object_unref (info->icon_pixbuf);
3226       info->icon_pixbuf = pixbuf;
3227
3228       gtk_drag_set_icon_window (context, NULL, hot_x, hot_y, TRUE);
3229     }
3230   else
3231     {
3232       gtk_widget_set_size_request (window, width, height);
3233
3234       g_signal_connect_closure (window, "realize",
3235                                 g_cclosure_new (G_CALLBACK (icon_window_realize),
3236                                                 pixbuf,
3237                                                 (GClosureNotify)g_object_unref),
3238                                 FALSE);
3239                     
3240       gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3241    }
3242 }
3243
3244 /**
3245  * gtk_drag_set_icon_pixbuf:
3246  * @context: the context for a drag. (This must be called 
3247  *            with a  context for the source side of a drag)
3248  * @pixbuf: the #GdkPixbuf to use as the drag icon.
3249  * @hot_x: the X offset within @widget of the hotspot.
3250  * @hot_y: the Y offset within @widget of the hotspot.
3251  * 
3252  * Sets @pixbuf as the icon for a given drag.
3253  **/
3254 void 
3255 gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
3256                            GdkPixbuf      *pixbuf,
3257                            gint            hot_x,
3258                            gint            hot_y)
3259 {
3260   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3261   g_return_if_fail (context->is_source);
3262   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
3263
3264   set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3265 }
3266
3267 /**
3268  * gtk_drag_set_icon_stock:
3269  * @context: the context for a drag. (This must be called 
3270  *            with a  context for the source side of a drag)
3271  * @stock_id: the ID of the stock icon to use for the drag.
3272  * @hot_x: the X offset within the icon of the hotspot.
3273  * @hot_y: the Y offset within the icon of the hotspot.
3274  * 
3275  * Sets the icon for a given drag from a stock ID.
3276  **/
3277 void 
3278 gtk_drag_set_icon_stock  (GdkDragContext *context,
3279                           const gchar    *stock_id,
3280                           gint            hot_x,
3281                           gint            hot_y)
3282 {
3283   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3284   g_return_if_fail (context->is_source);
3285   g_return_if_fail (stock_id != NULL);
3286   
3287   set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y, FALSE);
3288 }
3289
3290 /**
3291  * gtk_drag_set_icon_pixmap:
3292  * @context: the context for a drag. (This must be called 
3293  *            with a  context for the source side of a drag)
3294  * @colormap: the colormap of the icon 
3295  * @pixmap: the image data for the icon 
3296  * @mask: (allow-none): the transparency mask for the icon or %NULL for none.
3297  * @hot_x: the X offset within @pixmap of the hotspot.
3298  * @hot_y: the Y offset within @pixmap of the hotspot.
3299  * 
3300  * Sets @pixmap as the icon for a given drag. GTK+ retains
3301  * references for the arguments, and will release them when
3302  * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
3303  * will be more convenient to use.
3304  **/
3305 void 
3306 gtk_drag_set_icon_pixmap (GdkDragContext    *context,
3307                           GdkColormap       *colormap,
3308                           GdkPixmap         *pixmap,
3309                           GdkBitmap         *mask,
3310                           gint               hot_x,
3311                           gint               hot_y)
3312 {
3313   GtkWidget *window;
3314   GdkScreen *screen;
3315   gint width, height;
3316       
3317   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3318   g_return_if_fail (context->is_source);
3319   g_return_if_fail (GDK_IS_COLORMAP (colormap));
3320   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
3321   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
3322
3323   screen = gdk_colormap_get_screen (colormap);
3324   
3325   g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
3326   g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
3327   
3328   gdk_drawable_get_size (pixmap, &width, &height);
3329
3330   gtk_widget_push_colormap (colormap);
3331
3332   window = gtk_window_new (GTK_WINDOW_POPUP);
3333   gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3334   gtk_window_set_screen (GTK_WINDOW (window), screen);
3335   set_can_change_screen (window, FALSE);
3336   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3337   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
3338
3339   gtk_widget_pop_colormap ();
3340
3341   gtk_widget_set_size_request (window, width, height);
3342   gtk_widget_realize (window);
3343
3344   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
3345   
3346   if (mask)
3347     gtk_widget_shape_combine_mask (window, mask, 0, 0);
3348
3349   gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3350 }
3351
3352 /**
3353  * gtk_drag_set_icon_name:
3354  * @context: the context for a drag. (This must be called 
3355  *            with a context for the source side of a drag)
3356  * @icon_name: name of icon to use
3357  * @hot_x: the X offset of the hotspot within the icon
3358  * @hot_y: the Y offset of the hotspot within the icon
3359  * 
3360  * Sets the icon for a given drag from a named themed icon. See
3361  * the docs for #GtkIconTheme for more details. Note that the
3362  * size of the icon depends on the icon theme (the icon is
3363  * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus 
3364  * @hot_x and @hot_y have to be used with care.
3365  *
3366  * Since: 2.8
3367  **/
3368 void 
3369 gtk_drag_set_icon_name (GdkDragContext *context,
3370                         const gchar    *icon_name,
3371                         gint            hot_x,
3372                         gint            hot_y)
3373 {
3374   GdkScreen *screen;
3375   GtkSettings *settings;
3376   GtkIconTheme *icon_theme;
3377   GdkPixbuf *pixbuf;
3378   gint width, height, icon_size;
3379
3380   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3381   g_return_if_fail (context->is_source);
3382   g_return_if_fail (icon_name != NULL);
3383
3384   screen = gdk_drawable_get_screen (context->source_window);
3385   g_return_if_fail (screen != NULL);
3386
3387   settings = gtk_settings_get_for_screen (screen);
3388   if (gtk_icon_size_lookup_for_settings (settings,
3389                                          GTK_ICON_SIZE_DND,
3390                                          &width, &height))
3391     icon_size = MAX (width, height);
3392   else 
3393     icon_size = 32; /* default value for GTK_ICON_SIZE_DND */ 
3394
3395   icon_theme = gtk_icon_theme_get_for_screen (screen);
3396
3397   pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name,
3398                                      icon_size, 0, NULL);
3399   if (pixbuf)
3400     set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3401   else
3402     g_warning ("Cannot load drag icon from icon name %s", icon_name);
3403 }
3404
3405 /**
3406  * gtk_drag_set_icon_default:
3407  * @context: the context for a drag. (This must be called 
3408              with a  context for the source side of a drag)
3409  * 
3410  * Sets the icon for a particular drag to the default
3411  * icon.
3412  **/
3413 void 
3414 gtk_drag_set_icon_default (GdkDragContext *context)
3415 {
3416   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3417   g_return_if_fail (context->is_source);
3418
3419   if (!default_icon_pixmap)
3420     gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
3421   else
3422     gtk_drag_set_icon_pixmap (context, 
3423                               default_icon_colormap, 
3424                               default_icon_pixmap, 
3425                               default_icon_mask,
3426                               default_icon_hot_x,
3427                               default_icon_hot_y);
3428 }
3429
3430 /*************************************************************
3431  * _gtk_drag_source_handle_event:
3432  *     Called from widget event handling code on Drag events
3433  *     for drag sources.
3434  *
3435  *   arguments:
3436  *     toplevel: Toplevel widget that received the event
3437  *     event:
3438  *   results:
3439  *************************************************************/
3440
3441 void
3442 _gtk_drag_source_handle_event (GtkWidget *widget,
3443                                GdkEvent  *event)
3444 {
3445   GtkDragSourceInfo *info;
3446   GdkDragContext *context;
3447
3448   g_return_if_fail (widget != NULL);
3449   g_return_if_fail (event != NULL);
3450
3451   context = event->dnd.context;
3452   info = gtk_drag_get_source_info (context, FALSE);
3453   if (!info)
3454     return;
3455
3456   switch (event->type)
3457     {
3458     case GDK_DRAG_STATUS:
3459       {
3460         GdkCursor *cursor;
3461
3462         if (info->proxy_dest)
3463           {
3464             if (!event->dnd.send_event)
3465               {
3466                 if (info->proxy_dest->proxy_drop_wait)
3467                   {
3468                     gboolean result = context->action != 0;
3469                     
3470                     /* Aha - we can finally pass the MOTIF DROP on... */
3471                     gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
3472                     if (result)
3473                       gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
3474                     else
3475                       gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
3476                   }
3477                 else
3478                   {
3479                     gdk_drag_status (info->proxy_dest->context,
3480                                      event->dnd.context->action,
3481                                      event->dnd.time);
3482                   }
3483               }
3484           }
3485         else if (info->have_grab)
3486           {
3487             cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
3488                                           event->dnd.context->action,
3489                                           info);
3490             if (info->cursor != cursor)
3491               {
3492                 GdkDevice *pointer;
3493
3494                 pointer = gdk_drag_context_get_device (context);
3495                 gdk_device_grab (pointer, widget->window,
3496                                  GDK_OWNERSHIP_APPLICATION, FALSE,
3497                                  GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3498                                  cursor, info->grab_time);
3499                 info->cursor = cursor;
3500               }
3501             
3502             gtk_drag_add_update_idle (info);
3503           }
3504       }
3505       break;
3506       
3507     case GDK_DROP_FINISHED:
3508       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, event->dnd.time);
3509       break;
3510     default:
3511       g_assert_not_reached ();
3512     }
3513 }
3514
3515 /*************************************************************
3516  * gtk_drag_source_check_selection:
3517  *     Check if we've set up handlers/claimed the selection
3518  *     for a given drag. If not, add them.
3519  *   arguments:
3520  *     
3521  *   results:
3522  *************************************************************/
3523
3524 static void
3525 gtk_drag_source_check_selection (GtkDragSourceInfo *info, 
3526                                  GdkAtom            selection,
3527                                  guint32            time)
3528 {
3529   GList *tmp_list;
3530
3531   tmp_list = info->selections;
3532   while (tmp_list)
3533     {
3534       if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
3535         return;
3536       tmp_list = tmp_list->next;
3537     }
3538
3539   gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
3540                                        info->ipc_widget,
3541                                        selection,
3542                                        time);
3543   info->selections = g_list_prepend (info->selections,
3544                                      GUINT_TO_POINTER (selection));
3545
3546   tmp_list = info->target_list->list;
3547   while (tmp_list)
3548     {
3549       GtkTargetPair *pair = tmp_list->data;
3550
3551       gtk_selection_add_target (info->ipc_widget,
3552                                 selection,
3553                                 pair->target,
3554                                 pair->info);
3555       tmp_list = tmp_list->next;
3556     }
3557   
3558   if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
3559     {
3560       gtk_selection_add_target (info->ipc_widget,
3561                                 selection,
3562                                 gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS"),
3563                                 TARGET_MOTIF_SUCCESS);
3564       gtk_selection_add_target (info->ipc_widget,
3565                                 selection,
3566                                 gdk_atom_intern_static_string ("XmTRANSFER_FAILURE"),
3567                                 TARGET_MOTIF_FAILURE);
3568     }
3569
3570   gtk_selection_add_target (info->ipc_widget,
3571                             selection,
3572                             gdk_atom_intern_static_string ("DELETE"),
3573                             TARGET_DELETE);
3574 }
3575
3576 /*************************************************************
3577  * gtk_drag_drop_finished:
3578  *     Clean up from the drag, and display snapback, if necessary.
3579  *   arguments:
3580  *     info:
3581  *     success:
3582  *     time:
3583  *   results:
3584  *************************************************************/
3585
3586 static void
3587 gtk_drag_drop_finished (GtkDragSourceInfo *info,
3588                         GtkDragResult      result,
3589                         guint              time)
3590 {
3591   gboolean success;
3592
3593   success = (result == GTK_DRAG_RESULT_SUCCESS);
3594   gtk_drag_source_release_selections (info, time); 
3595
3596   if (info->proxy_dest)
3597     {
3598       /* The time from the event isn't reliable for Xdnd drags */
3599       gtk_drag_finish (info->proxy_dest->context, success, FALSE, 
3600                        info->proxy_dest->proxy_drop_time);
3601       gtk_drag_source_info_destroy (info);
3602     }
3603   else
3604     {
3605       if (!success)
3606         g_signal_emit_by_name (info->widget, "drag-failed",
3607                                info->context, result, &success);
3608
3609       if (success)
3610         {
3611           gtk_drag_source_info_destroy (info);
3612         }
3613       else
3614         {
3615           GtkDragAnim *anim = g_new (GtkDragAnim, 1);
3616           anim->info = info;
3617           anim->step = 0;
3618           
3619           anim->n_steps = MAX (info->cur_x - info->start_x,
3620                                info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
3621           anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
3622
3623           info->cur_screen = gtk_widget_get_screen (info->widget);
3624
3625           if (!info->icon_window)
3626             set_icon_stock_pixbuf (info->context, NULL, info->icon_pixbuf, 
3627                                    0, 0, TRUE);
3628
3629           gtk_drag_update_icon (info);
3630           
3631           /* Mark the context as dead, so if the destination decides
3632            * to respond really late, we still are OK.
3633            */
3634           gtk_drag_clear_source_info (info->context);
3635           gdk_threads_add_timeout (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
3636         }
3637     }
3638 }
3639
3640 static void
3641 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
3642                                     guint32            time)
3643 {
3644   GdkDisplay *display = gtk_widget_get_display (info->widget);
3645   GList *tmp_list = info->selections;
3646   
3647   while (tmp_list)
3648     {
3649       GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
3650       if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
3651         gtk_selection_owner_set_for_display (display, NULL, selection, time);
3652
3653       tmp_list = tmp_list->next;
3654     }
3655
3656   g_list_free (info->selections);
3657   info->selections = NULL;
3658 }
3659
3660 /*************************************************************
3661  * gtk_drag_drop:
3662  *     Send a drop event.
3663  *   arguments:
3664  *     
3665  *   results:
3666  *************************************************************/
3667
3668 static void
3669 gtk_drag_drop (GtkDragSourceInfo *info, 
3670                guint32            time)
3671 {
3672   if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
3673     {
3674       GtkSelectionData selection_data;
3675       GList *tmp_list;
3676       /* GTK+ traditionally has used application/x-rootwin-drop, but the
3677        * XDND spec specifies x-rootwindow-drop.
3678        */
3679       GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3680       GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3681       
3682       tmp_list = info->target_list->list;
3683       while (tmp_list)
3684         {
3685           GtkTargetPair *pair = tmp_list->data;
3686           
3687           if (pair->target == target1 || pair->target == target2)
3688             {
3689               selection_data.selection = GDK_NONE;
3690               selection_data.target = pair->target;
3691               selection_data.data = NULL;
3692               selection_data.length = -1;
3693               
3694               g_signal_emit_by_name (info->widget, "drag-data-get",
3695                                      info->context, &selection_data,
3696                                      pair->info,
3697                                      time);
3698               
3699               /* FIXME: Should we check for length >= 0 here? */
3700               gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3701               return;
3702             }
3703           tmp_list = tmp_list->next;
3704         }
3705       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3706     }
3707   else
3708     {
3709       if (info->icon_window)
3710         gtk_widget_hide (info->icon_window);
3711         
3712       gdk_drag_drop (info->context, time);
3713       info->drop_timeout = gdk_threads_add_timeout (DROP_ABORT_TIME,
3714                                           gtk_drag_abort_timeout,
3715                                           info);
3716     }
3717 }
3718
3719 /*
3720  * Source side callbacks.
3721  */
3722
3723 static gboolean
3724 gtk_drag_source_event_cb (GtkWidget      *widget,
3725                           GdkEvent       *event,
3726                           gpointer        data)
3727 {
3728   GtkDragSourceSite *site;
3729   gboolean retval = FALSE;
3730   site = (GtkDragSourceSite *)data;
3731
3732   switch (event->type)
3733     {
3734     case GDK_BUTTON_PRESS:
3735       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3736         {
3737           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
3738           site->x = event->button.x;
3739           site->y = event->button.y;
3740         }
3741       break;
3742       
3743     case GDK_BUTTON_RELEASE:
3744       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3745         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
3746       break;
3747       
3748     case GDK_MOTION_NOTIFY:
3749       if (site->state & event->motion.state & site->start_button_mask)
3750         {
3751           /* FIXME: This is really broken and can leave us
3752            * with a stuck grab
3753            */
3754           int i;
3755           for (i=1; i<6; i++)
3756             {
3757               if (site->state & event->motion.state & 
3758                   GDK_BUTTON1_MASK << (i - 1))
3759                 break;
3760             }
3761
3762           if (gtk_drag_check_threshold (widget, site->x, site->y,
3763                                         event->motion.x, event->motion.y))
3764             {
3765               site->state = 0;
3766               gtk_drag_begin_internal (widget, site, site->target_list,
3767                                        site->actions, 
3768                                        i, event);
3769
3770               retval = TRUE;
3771             }
3772         }
3773       break;
3774       
3775     default:                    /* hit for 2/3BUTTON_PRESS */
3776       break;
3777     }
3778   
3779   return retval;
3780 }
3781
3782 static void 
3783 gtk_drag_source_site_destroy (gpointer data)
3784 {
3785   GtkDragSourceSite *site = data;
3786
3787   if (site->target_list)
3788     gtk_target_list_unref (site->target_list);
3789
3790   gtk_drag_source_unset_icon (site);
3791   g_free (site);
3792 }
3793
3794 static void
3795 gtk_drag_selection_get (GtkWidget        *widget, 
3796                         GtkSelectionData *selection_data,
3797                         guint             sel_info,
3798                         guint32           time,
3799                         gpointer          data)
3800 {
3801   GtkDragSourceInfo *info = data;
3802   static GdkAtom null_atom = GDK_NONE;
3803   guint target_info;
3804
3805   if (!null_atom)
3806     null_atom = gdk_atom_intern_static_string ("NULL");
3807
3808   switch (sel_info)
3809     {
3810     case TARGET_DELETE:
3811       g_signal_emit_by_name (info->widget,
3812                              "drag-data-delete", 
3813                              info->context);
3814       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3815       break;
3816     case TARGET_MOTIF_SUCCESS:
3817       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3818       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3819       break;
3820     case TARGET_MOTIF_FAILURE:
3821       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3822       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3823       break;
3824     default:
3825       if (info->proxy_dest)
3826         {
3827           /* This is sort of dangerous and needs to be thought
3828            * through better
3829            */
3830           info->proxy_dest->proxy_data = selection_data;
3831           gtk_drag_get_data (info->widget,
3832                              info->proxy_dest->context,
3833                              selection_data->target,
3834                              time);
3835           gtk_main ();
3836           info->proxy_dest->proxy_data = NULL;
3837         }
3838       else
3839         {
3840           if (gtk_target_list_find (info->target_list, 
3841                                     selection_data->target, 
3842                                     &target_info))
3843             {
3844               g_signal_emit_by_name (info->widget, "drag-data-get",
3845                                      info->context,
3846                                      selection_data,
3847                                      target_info,
3848                                      time);
3849             }
3850         }
3851       break;
3852     }
3853 }
3854
3855 static gboolean
3856 gtk_drag_anim_timeout (gpointer data)
3857 {
3858   GtkDragAnim *anim = data;
3859   gint x, y;
3860   gboolean retval;
3861
3862   if (anim->step == anim->n_steps)
3863     {
3864       gtk_drag_source_info_destroy (anim->info);
3865       g_free (anim);
3866
3867       retval = FALSE;
3868     }
3869   else
3870     {
3871       x = (anim->info->start_x * (anim->step + 1) +
3872            anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3873       y = (anim->info->start_y * (anim->step + 1) +
3874            anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3875       if (anim->info->icon_window)
3876         {
3877           GtkWidget *icon_window;
3878           gint hot_x, hot_y;
3879           
3880           gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);   
3881           gtk_window_move (GTK_WINDOW (icon_window), 
3882                            x - hot_x, 
3883                            y - hot_y);
3884         }
3885   
3886       anim->step++;
3887
3888       retval = TRUE;
3889     }
3890
3891   return retval;
3892 }
3893
3894 static void
3895 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3896 {
3897   if (info->icon_window)
3898     {
3899       gtk_widget_hide (info->icon_window);
3900       if (info->destroy_icon)
3901         gtk_widget_destroy (info->icon_window);
3902
3903       if (info->fallback_icon)
3904         {
3905           gtk_widget_destroy (info->fallback_icon);
3906           info->fallback_icon = NULL;
3907         }
3908
3909       g_object_unref (info->icon_window);
3910       info->icon_window = NULL;
3911     }
3912 }
3913
3914 static void
3915 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3916 {
3917   gint i;
3918
3919   for (i = 0; i < n_drag_cursors; i++)
3920     {
3921       if (info->drag_cursors[i] != NULL)
3922         {
3923           gdk_cursor_unref (info->drag_cursors[i]);
3924           info->drag_cursors[i] = NULL;
3925         }
3926     }
3927
3928   gtk_drag_remove_icon (info);
3929
3930   if (info->icon_pixbuf)
3931     {
3932       g_object_unref (info->icon_pixbuf);
3933       info->icon_pixbuf = NULL;
3934     }
3935
3936   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3937                                         gtk_drag_grab_broken_event_cb,
3938                                         info);
3939   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3940                                         gtk_drag_grab_notify_cb,
3941                                         info);
3942   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3943                                         gtk_drag_button_release_cb,
3944                                         info);
3945   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3946                                         gtk_drag_motion_cb,
3947                                         info);
3948   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3949                                         gtk_drag_key_cb,
3950                                         info);
3951   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3952                                         gtk_drag_selection_get,
3953                                         info);
3954
3955   if (!info->proxy_dest)
3956     g_signal_emit_by_name (info->widget, "drag-end", info->context);
3957
3958   if (info->widget)
3959     g_object_unref (info->widget);
3960
3961   gtk_selection_remove_all (info->ipc_widget);
3962   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), NULL);
3963   source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3964   gtk_drag_release_ipc_widget (info->ipc_widget);
3965
3966   gtk_target_list_unref (info->target_list);
3967
3968   gtk_drag_clear_source_info (info->context);
3969   g_object_unref (info->context);
3970
3971   if (info->drop_timeout)
3972     g_source_remove (info->drop_timeout);
3973
3974   if (info->update_idle)
3975     g_source_remove (info->update_idle);
3976
3977   g_free (info);
3978 }
3979
3980 static gboolean
3981 gtk_drag_update_idle (gpointer data)
3982 {
3983   GtkDragSourceInfo *info = data;
3984   GdkWindow *dest_window;
3985   GdkDragProtocol protocol;
3986   GdkAtom selection;
3987
3988   GdkDragAction action;
3989   GdkDragAction possible_actions;
3990   guint32 time;
3991
3992   info->update_idle = 0;
3993     
3994   if (info->last_event)
3995     {
3996       time = gtk_drag_get_event_time (info->last_event);
3997       gtk_drag_get_event_actions (info->last_event,
3998                                   info->button, 
3999                                   info->possible_actions,
4000                                   &action, &possible_actions);
4001       gtk_drag_update_icon (info);
4002       gdk_drag_find_window_for_screen (info->context,
4003                                        info->icon_window ? info->icon_window->window : NULL,
4004                                        info->cur_screen, info->cur_x, info->cur_y,
4005                                        &dest_window, &protocol);
4006       
4007       if (!gdk_drag_motion (info->context, dest_window, protocol,
4008                             info->cur_x, info->cur_y, action, 
4009                             possible_actions,
4010                             time))
4011         {
4012           gdk_event_free ((GdkEvent *)info->last_event);
4013           info->last_event = NULL;
4014         }
4015   
4016       if (dest_window)
4017         g_object_unref (dest_window);
4018       
4019       selection = gdk_drag_get_selection (info->context);
4020       if (selection)
4021         gtk_drag_source_check_selection (info, selection, time);
4022
4023     }
4024
4025   return FALSE;
4026 }
4027
4028 static void
4029 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
4030 {
4031   /* We use an idle lower than GDK_PRIORITY_REDRAW so that exposes
4032    * from the last move can catch up before we move again.
4033    */
4034   if (!info->update_idle)
4035     info->update_idle = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW + 5,
4036                                          gtk_drag_update_idle,
4037                                          info,
4038                                          NULL);
4039 }
4040
4041 /**
4042  * gtk_drag_update:
4043  * @info: DragSourceInfo for the drag
4044  * @screen: new screen
4045  * @x_root: new X position 
4046  * @y_root: new y position
4047  * @event: event received requiring update
4048  * 
4049  * Updates the status of the drag; called when the
4050  * cursor moves or the modifier changes
4051  **/
4052 static void
4053 gtk_drag_update (GtkDragSourceInfo *info,
4054                  GdkScreen         *screen,
4055                  gint               x_root,
4056                  gint               y_root,
4057                  GdkEvent          *event)
4058 {
4059   info->cur_screen = screen;
4060   info->cur_x = x_root;
4061   info->cur_y = y_root;
4062   if (info->last_event)
4063     {
4064       gdk_event_free ((GdkEvent *)info->last_event);
4065       info->last_event = NULL;
4066     }
4067   if (event)
4068     info->last_event = gdk_event_copy ((GdkEvent *)event);
4069
4070   gtk_drag_add_update_idle (info);
4071 }
4072
4073 /*************************************************************
4074  * gtk_drag_end:
4075  *     Called when the user finishes to drag, either by
4076  *     releasing the mouse, or by pressing Esc.
4077  *   arguments:
4078  *     info: Source info for the drag
4079  *     time: Timestamp for ending the drag
4080  *   results:
4081  *************************************************************/
4082
4083 static void
4084 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
4085 {
4086   GdkEvent *send_event;
4087   GtkWidget *source_widget = info->widget;
4088   GdkDevice *pointer, *keyboard;
4089
4090   pointer = gdk_drag_context_get_device (info->context);
4091   keyboard = gdk_device_get_associated_device (pointer);
4092
4093   /* Prevent ungrab before grab (see bug 623865) */
4094   if (info->grab_time == GDK_CURRENT_TIME)
4095     time = GDK_CURRENT_TIME;
4096
4097   if (info->update_idle)
4098     {
4099       g_source_remove (info->update_idle);
4100       info->update_idle = 0;
4101     }
4102   
4103   if (info->last_event)
4104     {
4105       gdk_event_free (info->last_event);
4106       info->last_event = NULL;
4107     }
4108   
4109   info->have_grab = FALSE;
4110   
4111   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4112                                         gtk_drag_grab_broken_event_cb,
4113                                         info);
4114   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4115                                         gtk_drag_grab_notify_cb,
4116                                         info);
4117   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4118                                         gtk_drag_button_release_cb,
4119                                         info);
4120   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4121                                         gtk_drag_motion_cb,
4122                                         info);
4123   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4124                                         gtk_drag_key_cb,
4125                                         info);
4126
4127   gdk_device_ungrab (pointer, time);
4128   ungrab_dnd_keys (info->ipc_widget, keyboard, time);
4129   gtk_device_grab_remove (info->ipc_widget, pointer);
4130
4131   /* Send on a release pair to the original 
4132    * widget to convince it to release its grab. We need to
4133    * call gtk_propagate_event() here, instead of 
4134    * gtk_widget_event() because widget like GtkList may
4135    * expect propagation.
4136    */
4137
4138   send_event = gdk_event_new (GDK_BUTTON_RELEASE);
4139   send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
4140   send_event->button.send_event = TRUE;
4141   send_event->button.time = time;
4142   send_event->button.x = 0;
4143   send_event->button.y = 0;
4144   send_event->button.axes = NULL;
4145   send_event->button.state = 0;
4146   send_event->button.button = info->button;
4147   send_event->button.device = pointer;
4148   send_event->button.x_root = 0;
4149   send_event->button.y_root = 0;
4150
4151   gtk_propagate_event (source_widget, send_event);
4152   gdk_event_free (send_event);
4153 }
4154
4155 /*************************************************************
4156  * gtk_drag_cancel:
4157  *    Called on cancellation of a drag, either by the user
4158  *    or programmatically.
4159  *   arguments:
4160  *     info: Source info for the drag
4161  *     time: Timestamp for ending the drag
4162  *   results:
4163  *************************************************************/
4164
4165 static void
4166 gtk_drag_cancel (GtkDragSourceInfo *info, GtkDragResult result, guint32 time)
4167 {
4168   gtk_drag_end (info, time);
4169   gdk_drag_abort (info->context, time);
4170   gtk_drag_drop_finished (info, result, time);
4171 }
4172
4173 /*************************************************************
4174  * gtk_drag_motion_cb:
4175  *     "motion-notify-event" callback during drag.
4176  *   arguments:
4177  *     
4178  *   results:
4179  *************************************************************/
4180
4181 static gboolean
4182 gtk_drag_motion_cb (GtkWidget      *widget, 
4183                     GdkEventMotion *event, 
4184                     gpointer        data)
4185 {
4186   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4187   GdkScreen *screen;
4188   gint x_root, y_root;
4189
4190   if (event->is_hint)
4191     {
4192       GdkDisplay *display = gtk_widget_get_display (widget);
4193
4194       gdk_display_get_device_state (display, event->device,
4195                                     &screen, &x_root, &y_root, NULL);
4196       event->x_root = x_root;
4197       event->y_root = y_root;
4198     }
4199   else
4200     screen = gdk_event_get_screen ((GdkEvent *)event);
4201
4202   gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *) event);
4203
4204   return TRUE;
4205 }
4206
4207 /*************************************************************
4208  * gtk_drag_key_cb:
4209  *     "key-press/release-event" callback during drag.
4210  *   arguments:
4211  *     
4212  *   results:
4213  *************************************************************/
4214
4215 #define BIG_STEP 20
4216 #define SMALL_STEP 1
4217
4218 static gboolean
4219 gtk_drag_key_cb (GtkWidget         *widget, 
4220                  GdkEventKey       *event, 
4221                  gpointer           data)
4222 {
4223   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4224   GdkModifierType state;
4225   GdkWindow *root_window;
4226   GdkDevice *pointer;
4227   gint dx, dy;
4228
4229   dx = dy = 0;
4230   state = event->state & gtk_accelerator_get_default_mod_mask ();
4231   pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
4232
4233   if (event->type == GDK_KEY_PRESS)
4234     {
4235       switch (event->keyval)
4236         {
4237         case GDK_Escape:
4238           gtk_drag_cancel (info, GTK_DRAG_RESULT_USER_CANCELLED, event->time);
4239           return TRUE;
4240
4241         case GDK_space:
4242         case GDK_Return:
4243         case GDK_ISO_Enter:
4244         case GDK_KP_Enter:
4245         case GDK_KP_Space:
4246           gtk_drag_end (info, event->time);
4247           gtk_drag_drop (info, event->time);
4248           return TRUE;
4249
4250         case GDK_Up:
4251         case GDK_KP_Up:
4252           dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4253           break;
4254           
4255         case GDK_Down:
4256         case GDK_KP_Down:
4257           dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4258           break;
4259           
4260         case GDK_Left:
4261         case GDK_KP_Left:
4262           dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4263           break;
4264           
4265         case GDK_Right:
4266         case GDK_KP_Right:
4267           dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4268           break;
4269         }
4270       
4271     }
4272
4273   /* Now send a "motion" so that the modifier state is updated */
4274
4275   /* The state is not yet updated in the event, so we need
4276    * to query it here. We could use XGetModifierMapping, but
4277    * that would be overkill.
4278    */
4279   root_window = gtk_widget_get_root_window (widget);
4280   gdk_window_get_device_position (root_window, pointer, NULL, NULL, &state);
4281   event->state = state;
4282
4283   if (dx != 0 || dy != 0)
4284     {
4285       info->cur_x += dx;
4286       info->cur_y += dy;
4287       gdk_display_warp_device (gtk_widget_get_display (widget), pointer,
4288                                gtk_widget_get_screen (widget),
4289                                info->cur_x, info->cur_y);
4290     }
4291
4292   gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
4293
4294   return TRUE;
4295 }
4296
4297 static gboolean
4298 gtk_drag_grab_broken_event_cb (GtkWidget          *widget,
4299                                GdkEventGrabBroken *event,
4300                                gpointer            data)
4301 {
4302   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4303
4304   /* Don't cancel if we break the implicit grab from the initial button_press.
4305    * Also, don't cancel if we re-grab on the widget or on our IPC window, for
4306    * example, when changing the drag cursor.
4307    */
4308   if (event->implicit
4309       || event->grab_window == info->widget->window
4310       || event->grab_window == info->ipc_widget->window)
4311     return FALSE;
4312
4313   gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4314   return TRUE;
4315 }
4316
4317 static void
4318 gtk_drag_grab_notify_cb (GtkWidget        *widget,
4319                          gboolean          was_grabbed,
4320                          gpointer          data)
4321 {
4322   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4323   GdkDevice *pointer;
4324
4325   pointer = gdk_drag_context_get_device (info->context);
4326
4327   if (gtk_widget_device_is_shadowed (widget, pointer))
4328     {
4329       /* We have to block callbacks to avoid recursion here, because
4330          gtk_drag_cancel calls gtk_grab_remove (via gtk_drag_end) */
4331       g_signal_handlers_block_by_func (widget, gtk_drag_grab_notify_cb, data);
4332       gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4333       g_signal_handlers_unblock_by_func (widget, gtk_drag_grab_notify_cb, data);
4334     }
4335 }
4336
4337
4338 /*************************************************************
4339  * gtk_drag_button_release_cb:
4340  *     "button-release-event" callback during drag.
4341  *   arguments:
4342  *     
4343  *   results:
4344  *************************************************************/
4345
4346 static gboolean
4347 gtk_drag_button_release_cb (GtkWidget      *widget, 
4348                             GdkEventButton *event, 
4349                             gpointer        data)
4350 {
4351   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4352
4353   if (event->button != info->button)
4354     return FALSE;
4355
4356   if ((info->context->action != 0) && (info->context->dest_window != NULL))
4357     {
4358       gtk_drag_end (info, event->time);
4359       gtk_drag_drop (info, event->time);
4360     }
4361   else
4362     {
4363       gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
4364     }
4365
4366   return TRUE;
4367 }
4368
4369 static gboolean
4370 gtk_drag_abort_timeout (gpointer data)
4371 {
4372   GtkDragSourceInfo *info = data;
4373   guint32 time = GDK_CURRENT_TIME;
4374
4375   if (info->proxy_dest)
4376     time = info->proxy_dest->proxy_drop_time;
4377
4378   info->drop_timeout = 0;
4379   gtk_drag_drop_finished (info, GTK_DRAG_RESULT_TIMEOUT_EXPIRED, time);
4380   
4381   return FALSE;
4382 }
4383
4384 /**
4385  * gtk_drag_check_threshold:
4386  * @widget: a #GtkWidget
4387  * @start_x: X coordinate of start of drag
4388  * @start_y: Y coordinate of start of drag
4389  * @current_x: current X coordinate
4390  * @current_y: current Y coordinate
4391  * 
4392  * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
4393  * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
4394  * should trigger the beginning of a drag-and-drop operation.
4395  *
4396  * Return Value: %TRUE if the drag threshold has been passed.
4397  **/
4398 gboolean
4399 gtk_drag_check_threshold (GtkWidget *widget,
4400                           gint       start_x,
4401                           gint       start_y,
4402                           gint       current_x,
4403                           gint       current_y)
4404 {
4405   gint drag_threshold;
4406
4407   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
4408
4409   g_object_get (gtk_widget_get_settings (widget),
4410                 "gtk-dnd-drag-threshold", &drag_threshold,
4411                 NULL);
4412   
4413   return (ABS (current_x - start_x) > drag_threshold ||
4414           ABS (current_y - start_y) > drag_threshold);
4415 }