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