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