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