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