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