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