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