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