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