]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd.c
Mention impact of GtkDestDefaults on "drag-motion" handlers. Clearify
[~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.
1081  * @n_targets: the number of entries in @targets.
1082  * @actions: a bitmask of possible actions for a drop onto this @widget.
1083  *
1084  * Sets a widget as a potential drop destination, and adds default behaviors.
1085  *
1086  * The default behaviors listed in @flags have an effect similar
1087  * to installing default handlers for the widget's drag-and-drop signals
1088  * (#GtkWidget:drag-motion, #GtkWidget:drag-drop, ...). They are exist for
1089  * convenience, but have to be selected carefully as some of those default
1090  * behaviors make assumptions, that can conflict with your own signal handlers.
1091  * For instance the default behaviors implied by #GTK_DEST_DEFAULT_DROP,
1092  * #GTK_DEST_DEFAULT_MOTION and #GTK_DEST_DEFAULT_ALL, use #gdk_drag_status
1093  * and gtk_drag_finish() in a way that conflicts with #GtkWidget:drag-motion
1094  * handlers calling gtk_drag_get_data() to inspect the dragged data.
1095  *
1096  * The list of @targets can be retrieved with gtk_drag_dest_get_target_list(),
1097  * or gtk_drag_dest_find_target().
1098  */
1099 void
1100 gtk_drag_dest_set (GtkWidget            *widget,
1101                    GtkDestDefaults       flags,
1102                    const GtkTargetEntry *targets,
1103                    gint                  n_targets,
1104                    GdkDragAction         actions)
1105 {
1106   GtkDragDestSite *site;
1107   
1108   g_return_if_fail (GTK_IS_WIDGET (widget));
1109
1110   site = g_new (GtkDragDestSite, 1);
1111
1112   site->flags = flags;
1113   site->have_drag = FALSE;
1114   if (targets)
1115     site->target_list = gtk_target_list_new (targets, n_targets);
1116   else
1117     site->target_list = NULL;
1118   site->actions = actions;
1119   site->do_proxy = FALSE;
1120   site->proxy_window = NULL;
1121   site->track_motion = FALSE;
1122
1123   gtk_drag_dest_set_internal (widget, site);
1124 }
1125
1126 /*************************************************************
1127  * gtk_drag_dest_set_proxy:
1128  *     Set up this widget to proxy drags elsewhere.
1129  *   arguments:
1130  *     widget:          
1131  *     proxy_window:    window to which forward drag events
1132  *     protocol:        Drag protocol which the dest widget accepts
1133  *     use_coordinates: If true, send the same coordinates to the
1134  *                      destination, because it is a embedded 
1135  *                      subwindow.
1136  *   results:
1137  *************************************************************/
1138
1139 void 
1140 gtk_drag_dest_set_proxy (GtkWidget      *widget,
1141                          GdkWindow      *proxy_window,
1142                          GdkDragProtocol protocol,
1143                          gboolean        use_coordinates)
1144 {
1145   GtkDragDestSite *site;
1146   
1147   g_return_if_fail (GTK_IS_WIDGET (widget));
1148   g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
1149
1150   site = g_new (GtkDragDestSite, 1);
1151
1152   site->flags = 0;
1153   site->have_drag = FALSE;
1154   site->target_list = NULL;
1155   site->actions = 0;
1156   site->proxy_window = proxy_window;
1157   if (proxy_window)
1158     g_object_ref (proxy_window);
1159   site->do_proxy = TRUE;
1160   site->proxy_protocol = protocol;
1161   site->proxy_coords = use_coordinates;
1162   site->track_motion = FALSE;
1163
1164   gtk_drag_dest_set_internal (widget, site);
1165 }
1166
1167 /*************************************************************
1168  * gtk_drag_dest_unset
1169  *     Unregister this widget as a drag target.
1170  *   arguments:
1171  *     widget:
1172  *   results:
1173  *************************************************************/
1174
1175 void 
1176 gtk_drag_dest_unset (GtkWidget *widget)
1177 {
1178   g_return_if_fail (GTK_IS_WIDGET (widget));
1179
1180   g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
1181 }
1182
1183 /**
1184  * gtk_drag_dest_get_target_list:
1185  * @widget: a #GtkWidget
1186  * 
1187  * Returns the list of targets this widget can accept from
1188  * drag-and-drop.
1189  * 
1190  * Return value: the #GtkTargetList, or %NULL if none
1191  **/
1192 GtkTargetList*
1193 gtk_drag_dest_get_target_list (GtkWidget *widget)
1194 {
1195   GtkDragDestSite *site;
1196
1197   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1198   
1199   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1200
1201   return site ? site->target_list : NULL;  
1202 }
1203
1204 /**
1205  * gtk_drag_dest_set_target_list:
1206  * @widget: a #GtkWidget that's a drag destination
1207  * @target_list: list of droppable targets, or %NULL for none
1208  * 
1209  * Sets the target types that this widget can accept from drag-and-drop.
1210  * The widget must first be made into a drag destination with
1211  * gtk_drag_dest_set().
1212  **/
1213 void
1214 gtk_drag_dest_set_target_list (GtkWidget      *widget,
1215                                GtkTargetList  *target_list)
1216 {
1217   GtkDragDestSite *site;
1218
1219   g_return_if_fail (GTK_IS_WIDGET (widget));
1220   
1221   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1222   
1223   if (!site)
1224     {
1225       g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
1226                  "to make the widget into a drag destination");
1227       return;
1228     }
1229
1230   if (target_list)
1231     gtk_target_list_ref (target_list);
1232   
1233   if (site->target_list)
1234     gtk_target_list_unref (site->target_list);
1235
1236   site->target_list = target_list;
1237 }
1238
1239 /**
1240  * gtk_drag_dest_add_text_targets:
1241  * @widget: a #GtkWidget that's a drag destination
1242  *
1243  * Add the text targets supported by #GtkSelection to
1244  * the target list of the drag destination. The targets
1245  * are added with @info = 0. If you need another value, 
1246  * use gtk_target_list_add_text_targets() and
1247  * gtk_drag_dest_set_target_list().
1248  * 
1249  * Since: 2.6
1250  **/
1251 void
1252 gtk_drag_dest_add_text_targets (GtkWidget *widget)
1253 {
1254   GtkTargetList *target_list;
1255
1256   target_list = gtk_drag_dest_get_target_list (widget);
1257   if (target_list)
1258     gtk_target_list_ref (target_list);
1259   else
1260     target_list = gtk_target_list_new (NULL, 0);
1261   gtk_target_list_add_text_targets (target_list, 0);
1262   gtk_drag_dest_set_target_list (widget, target_list);
1263   gtk_target_list_unref (target_list);
1264 }
1265
1266 /**
1267  * gtk_drag_dest_add_image_targets:
1268  * @widget: a #GtkWidget that's a drag destination
1269  *
1270  * Add the image targets supported by #GtkSelection to
1271  * the target list of the drag destination. The targets
1272  * are added with @info = 0. If you need another value, 
1273  * use gtk_target_list_add_image_targets() and
1274  * gtk_drag_dest_set_target_list().
1275  * 
1276  * Since: 2.6
1277  **/
1278 void
1279 gtk_drag_dest_add_image_targets (GtkWidget *widget)
1280 {
1281   GtkTargetList *target_list;
1282
1283   target_list = gtk_drag_dest_get_target_list (widget);
1284   if (target_list)
1285     gtk_target_list_ref (target_list);
1286   else
1287     target_list = gtk_target_list_new (NULL, 0);
1288   gtk_target_list_add_image_targets (target_list, 0, FALSE);
1289   gtk_drag_dest_set_target_list (widget, target_list);
1290   gtk_target_list_unref (target_list);
1291 }
1292
1293 /**
1294  * gtk_drag_dest_add_uri_targets:
1295  * @widget: a #GtkWidget that's a drag destination
1296  *
1297  * Add the URI targets supported by #GtkSelection to
1298  * the target list of the drag destination. The targets
1299  * are added with @info = 0. If you need another value, 
1300  * use gtk_target_list_add_uri_targets() and
1301  * gtk_drag_dest_set_target_list().
1302  * 
1303  * Since: 2.6
1304  **/
1305 void
1306 gtk_drag_dest_add_uri_targets (GtkWidget *widget)
1307 {
1308   GtkTargetList *target_list;
1309
1310   target_list = gtk_drag_dest_get_target_list (widget);
1311   if (target_list)
1312     gtk_target_list_ref (target_list);
1313   else
1314     target_list = gtk_target_list_new (NULL, 0);
1315   gtk_target_list_add_uri_targets (target_list, 0);
1316   gtk_drag_dest_set_target_list (widget, target_list);
1317   gtk_target_list_unref (target_list);
1318 }
1319
1320 /**
1321  * gtk_drag_dest_set_track_motion:
1322  * @widget: a #GtkWidget that's a drag destination
1323  * @track_motion: whether to accept all targets
1324  * 
1325  * Tells the widget to emit ::drag-motion and ::drag-leave
1326  * events regardless of the targets and the %GTK_DEST_DEFAULT_MOTION
1327  * flag. 
1328  *
1329  * This may be used when a widget wants to do generic
1330  * actions regardless of the targets that the source offers.
1331  *
1332  * Since: 2.10
1333  **/
1334 void
1335 gtk_drag_dest_set_track_motion (GtkWidget *widget,
1336                                 gboolean   track_motion)
1337 {
1338   GtkDragDestSite *site;
1339
1340   g_return_if_fail (GTK_IS_WIDGET (widget));
1341
1342   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1343   
1344   g_return_if_fail (site != NULL);
1345
1346   site->track_motion = track_motion != FALSE;
1347 }
1348
1349 /**
1350  * gtk_drag_dest_get_track_motion:
1351  * @widget: a #GtkWidget that's a drag destination
1352  * 
1353  * Returns whether the widget has been configured to always
1354  * emit ::drag-motion signals.
1355  * 
1356  * Return Value: %TRUE if the widget always emits ::drag-motion events
1357  *
1358  * Since: 2.10
1359  **/
1360 gboolean
1361 gtk_drag_dest_get_track_motion (GtkWidget *widget)
1362 {
1363   GtkDragDestSite *site;
1364
1365   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1366
1367   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1368
1369   if (site)
1370     return site->track_motion;
1371
1372   return FALSE;
1373 }
1374
1375 /*************************************************************
1376  * _gtk_drag_dest_handle_event:
1377  *     Called from widget event handling code on Drag events
1378  *     for destinations.
1379  *
1380  *   arguments:
1381  *     toplevel: Toplevel widget that received the event
1382  *     event:
1383  *   results:
1384  *************************************************************/
1385
1386 void
1387 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1388                             GdkEvent  *event)
1389 {
1390   GtkDragDestInfo *info;
1391   GdkDragContext *context;
1392
1393   g_return_if_fail (toplevel != NULL);
1394   g_return_if_fail (event != NULL);
1395
1396   context = event->dnd.context;
1397
1398   info = gtk_drag_get_dest_info (context, TRUE);
1399
1400   /* Find the widget for the event */
1401   switch (event->type)
1402     {
1403     case GDK_DRAG_ENTER:
1404       break;
1405       
1406     case GDK_DRAG_LEAVE:
1407       if (info->widget)
1408         {
1409           gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1410           info->widget = NULL;
1411         }
1412       break;
1413       
1414     case GDK_DRAG_MOTION:
1415     case GDK_DROP_START:
1416       {
1417         GtkDragFindData data;
1418         gint tx, ty;
1419
1420         if (event->type == GDK_DROP_START)
1421           {
1422             info->dropped = TRUE;
1423             /* We send a leave here so that the widget unhighlights
1424              * properly.
1425              */
1426             if (info->widget)
1427               {
1428                 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1429                 info->widget = NULL;
1430               }
1431           }
1432
1433 #ifdef GDK_WINDOWING_X11
1434         /* Hackaround for: http://bugzilla.gnome.org/show_bug.cgi?id=136112
1435          *
1436          * Currently gdk_window_get_position doesn't provide reliable
1437          * information for embedded windows, so we call the much more
1438          * expensive gdk_window_get_origin().
1439          */
1440         if (GTK_IS_PLUG (toplevel))
1441           gdk_window_get_origin (toplevel->window, &tx, &ty);
1442         else
1443 #endif /* GDK_WINDOWING_X11 */
1444           gdk_window_get_position (toplevel->window, &tx, &ty);
1445
1446         data.x = event->dnd.x_root - tx;
1447         data.y = event->dnd.y_root - ty;
1448         data.context = context;
1449         data.info = info;
1450         data.found = FALSE;
1451         data.toplevel = TRUE;
1452         data.callback = (event->type == GDK_DRAG_MOTION) ?
1453           gtk_drag_dest_motion : gtk_drag_dest_drop;
1454         data.time = event->dnd.time;
1455
1456         gtk_drag_find_widget (toplevel, &data);
1457
1458         if (info->widget && !data.found)
1459           {
1460             gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1461             info->widget = NULL;
1462           }
1463         
1464         /* Send a reply.
1465          */
1466         if (event->type == GDK_DRAG_MOTION)
1467           {
1468             if (!data.found)
1469               gdk_drag_status (context, 0, event->dnd.time);
1470           }
1471         else if (event->type == GDK_DROP_START && !info->proxy_source)
1472           {
1473             gdk_drop_reply (context, data.found, event->dnd.time);
1474             if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1475               gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1476           }
1477       }
1478       break;
1479
1480     default:
1481       g_assert_not_reached ();
1482     }
1483 }
1484
1485 /**
1486  * gtk_drag_dest_find_target:
1487  * @widget: drag destination widget
1488  * @context: drag context
1489  * @target_list: list of droppable targets, or %NULL to use
1490  *    gtk_drag_dest_get_target_list (@widget).
1491  * 
1492  * Looks for a match between @context->targets and the
1493  * @dest_target_list, returning the first matching target, otherwise
1494  * returning %GDK_NONE. @dest_target_list should usually be the return
1495  * value from gtk_drag_dest_get_target_list(), but some widgets may
1496  * have different valid targets for different parts of the widget; in
1497  * that case, they will have to implement a drag_motion handler that
1498  * passes the correct target list to this function.
1499  * 
1500  * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1501  **/
1502 GdkAtom
1503 gtk_drag_dest_find_target (GtkWidget      *widget,
1504                            GdkDragContext *context,
1505                            GtkTargetList  *target_list)
1506 {
1507   GList *tmp_target;
1508   GList *tmp_source = NULL;
1509   GtkWidget *source_widget;
1510
1511   g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1512   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1513   g_return_val_if_fail (!context->is_source, GDK_NONE);
1514
1515
1516   source_widget = gtk_drag_get_source_widget (context);
1517
1518   if (target_list == NULL)
1519     target_list = gtk_drag_dest_get_target_list (widget);
1520   
1521   if (target_list == NULL)
1522     return GDK_NONE;
1523   
1524   tmp_target = target_list->list;
1525   while (tmp_target)
1526     {
1527       GtkTargetPair *pair = tmp_target->data;
1528       tmp_source = context->targets;
1529       while (tmp_source)
1530         {
1531           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1532             {
1533               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1534                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)) &&
1535                   (!(pair->flags & GTK_TARGET_OTHER_APP) || !source_widget) &&
1536                   (!(pair->flags & GTK_TARGET_OTHER_WIDGET) || (source_widget != widget)))
1537                 return pair->target;
1538               else
1539                 break;
1540             }
1541           tmp_source = tmp_source->next;
1542         }
1543       tmp_target = tmp_target->next;
1544     }
1545
1546   return GDK_NONE;
1547 }
1548
1549 static void
1550 gtk_drag_selection_received (GtkWidget        *widget,
1551                              GtkSelectionData *selection_data,
1552                              guint             time,
1553                              gpointer          data)
1554 {
1555   GdkDragContext *context;
1556   GtkDragDestInfo *info;
1557   GtkWidget *drop_widget;
1558
1559   drop_widget = data;
1560
1561   context = g_object_get_data (G_OBJECT (widget), "drag-context");
1562   info = gtk_drag_get_dest_info (context, FALSE);
1563
1564   if (info->proxy_data && 
1565       info->proxy_data->target == selection_data->target)
1566     {
1567       gtk_selection_data_set (info->proxy_data,
1568                               selection_data->type,
1569                               selection_data->format,
1570                               selection_data->data,
1571                               selection_data->length);
1572       gtk_main_quit ();
1573       return;
1574     }
1575
1576   if (selection_data->target == gdk_atom_intern_static_string ("DELETE"))
1577     {
1578       gtk_drag_finish (context, TRUE, FALSE, time);
1579     }
1580   else if ((selection_data->target == gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS")) ||
1581            (selection_data->target == gdk_atom_intern_static_string ("XmTRANSFER_FAILURE")))
1582     {
1583       /* Do nothing */
1584     }
1585   else
1586     {
1587       GtkDragDestSite *site;
1588
1589       site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1590
1591       if (site && site->target_list)
1592         {
1593           guint target_info;
1594
1595           if (gtk_target_list_find (site->target_list, 
1596                                     selection_data->target,
1597                                     &target_info))
1598             {
1599               if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1600                   selection_data->length >= 0)
1601                 g_signal_emit_by_name (drop_widget,
1602                                        "drag_data_received",
1603                                        context, info->drop_x, info->drop_y,
1604                                        selection_data,
1605                                        target_info, time);
1606             }
1607         }
1608       else
1609         {
1610           g_signal_emit_by_name (drop_widget,
1611                                  "drag_data_received",
1612                                  context, info->drop_x, info->drop_y,
1613                                  selection_data,
1614                                  0, time);
1615         }
1616       
1617       if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1618         {
1619
1620           gtk_drag_finish (context, 
1621                            (selection_data->length >= 0),
1622                            (context->action == GDK_ACTION_MOVE),
1623                            time);
1624         }
1625       
1626       g_object_unref (drop_widget);
1627     }
1628
1629   g_signal_handlers_disconnect_by_func (widget,
1630                                         gtk_drag_selection_received,
1631                                         data);
1632   
1633   g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
1634   g_object_unref (context);
1635
1636   gtk_drag_release_ipc_widget (widget);
1637 }
1638
1639 static void
1640 prepend_and_ref_widget (GtkWidget *widget,
1641                         gpointer   data)
1642 {
1643   GSList **slist_p = data;
1644
1645   *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1646 }
1647
1648 /*************************************************************
1649  * gtk_drag_find_widget:
1650  *     Recursive callback used to locate widgets for 
1651  *     DRAG_MOTION and DROP_START events.
1652  *   arguments:
1653  *     
1654  *   results:
1655  *************************************************************/
1656
1657 static void
1658 gtk_drag_find_widget (GtkWidget       *widget,
1659                       GtkDragFindData *data)
1660 {
1661   GtkAllocation new_allocation;
1662   gint allocation_to_window_x = 0;
1663   gint allocation_to_window_y = 0;
1664   gint x_offset = 0;
1665   gint y_offset = 0;
1666
1667   if (data->found || !GTK_WIDGET_MAPPED (widget) || !GTK_WIDGET_SENSITIVE (widget))
1668     return;
1669
1670   /* Note that in the following code, we only count the
1671    * position as being inside a WINDOW widget if it is inside
1672    * widget->window; points that are outside of widget->window
1673    * but within the allocation are not counted. This is consistent
1674    * with the way we highlight drag targets.
1675    *
1676    * data->x,y are relative to widget->parent->window (if
1677    * widget is not a toplevel, widget->window otherwise).
1678    * We compute the allocation of widget in the same coordinates,
1679    * clipping to widget->window, and all intermediate
1680    * windows. If data->x,y is inside that, then we translate
1681    * our coordinates to be relative to widget->window and
1682    * recurse.
1683    */  
1684   new_allocation = widget->allocation;
1685
1686   if (widget->parent)
1687     {
1688       gint tx, ty;
1689       GdkWindow *window = widget->window;
1690
1691       /* Compute the offset from allocation-relative to
1692        * window-relative coordinates.
1693        */
1694       allocation_to_window_x = widget->allocation.x;
1695       allocation_to_window_y = widget->allocation.y;
1696
1697       if (!GTK_WIDGET_NO_WINDOW (widget))
1698         {
1699           /* The allocation is relative to the parent window for
1700            * window widgets, not to widget->window.
1701            */
1702           gdk_window_get_position (window, &tx, &ty);
1703           
1704           allocation_to_window_x -= tx;
1705           allocation_to_window_y -= ty;
1706         }
1707
1708       new_allocation.x = 0 + allocation_to_window_x;
1709       new_allocation.y = 0 + allocation_to_window_y;
1710       
1711       while (window && window != widget->parent->window)
1712         {
1713           GdkRectangle window_rect = { 0, 0, 0, 0 };
1714           
1715           gdk_drawable_get_size (window, &window_rect.width, &window_rect.height);
1716
1717           gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1718
1719           gdk_window_get_position (window, &tx, &ty);
1720           new_allocation.x += tx;
1721           x_offset += tx;
1722           new_allocation.y += ty;
1723           y_offset += ty;
1724           
1725           window = gdk_window_get_parent (window);
1726         }
1727
1728       if (!window)              /* Window and widget heirarchies didn't match. */
1729         return;
1730     }
1731
1732   if (data->toplevel ||
1733       ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1734        (data->x < new_allocation.x + new_allocation.width) && 
1735        (data->y < new_allocation.y + new_allocation.height)))
1736     {
1737       /* First, check if the drag is in a valid drop site in
1738        * one of our children 
1739        */
1740       if (GTK_IS_CONTAINER (widget))
1741         {
1742           GtkDragFindData new_data = *data;
1743           GSList *children = NULL;
1744           GSList *tmp_list;
1745           
1746           new_data.x -= x_offset;
1747           new_data.y -= y_offset;
1748           new_data.found = FALSE;
1749           new_data.toplevel = FALSE;
1750           
1751           /* need to reference children temporarily in case the
1752            * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1753            */
1754           gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1755           for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1756             {
1757               if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1758                 gtk_drag_find_widget (tmp_list->data, &new_data);
1759               g_object_unref (tmp_list->data);
1760             }
1761           g_slist_free (children);
1762           
1763           data->found = new_data.found;
1764         }
1765
1766       /* If not, and this widget is registered as a drop site, check to
1767        * emit "drag_motion" to check if we are actually in
1768        * a drop site.
1769        */
1770       if (!data->found &&
1771           g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1772         {
1773           data->found = data->callback (widget,
1774                                         data->context,
1775                                         data->x - x_offset - allocation_to_window_x,
1776                                         data->y - y_offset - allocation_to_window_y,
1777                                         data->time);
1778           /* If so, send a "drag_leave" to the last widget */
1779           if (data->found)
1780             {
1781               if (data->info->widget && data->info->widget != widget)
1782                 {
1783                   gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1784                 }
1785               data->info->widget = widget;
1786             }
1787         }
1788     }
1789 }
1790
1791 static void
1792 gtk_drag_proxy_begin (GtkWidget       *widget, 
1793                       GtkDragDestInfo *dest_info,
1794                       guint32          time)
1795 {
1796   GtkDragSourceInfo *source_info;
1797   GList *tmp_list;
1798   GdkDragContext *context;
1799   GtkWidget *ipc_widget;
1800
1801   if (dest_info->proxy_source)
1802     {
1803       gdk_drag_abort (dest_info->proxy_source->context, time);
1804       gtk_drag_source_info_destroy (dest_info->proxy_source);
1805       dest_info->proxy_source = NULL;
1806     }
1807   
1808   ipc_widget = gtk_drag_get_ipc_widget (widget);
1809   context = gdk_drag_begin (ipc_widget->window,
1810                             dest_info->context->targets);
1811
1812   source_info = gtk_drag_get_source_info (context, TRUE);
1813
1814   source_info->ipc_widget = ipc_widget;
1815   source_info->widget = g_object_ref (widget);
1816
1817   source_info->target_list = gtk_target_list_new (NULL, 0);
1818   tmp_list = dest_info->context->targets;
1819   while (tmp_list)
1820     {
1821       gtk_target_list_add (source_info->target_list, 
1822                            GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1823       tmp_list = tmp_list->next;
1824     }
1825
1826   source_info->proxy_dest = dest_info;
1827   
1828   g_signal_connect (ipc_widget,
1829                     "selection_get",
1830                     G_CALLBACK (gtk_drag_selection_get),
1831                     source_info);
1832   
1833   dest_info->proxy_source = source_info;
1834 }
1835
1836 static void
1837 gtk_drag_dest_info_destroy (gpointer data)
1838 {
1839   GtkDragDestInfo *info = data;
1840
1841   g_free (info);
1842 }
1843
1844 static GtkDragDestInfo *
1845 gtk_drag_get_dest_info (GdkDragContext *context,
1846                         gboolean        create)
1847 {
1848   GtkDragDestInfo *info;
1849   static GQuark info_quark = 0;
1850   if (!info_quark)
1851     info_quark = g_quark_from_static_string ("gtk-dest-info");
1852   
1853   info = g_object_get_qdata (G_OBJECT (context), info_quark);
1854   if (!info && create)
1855     {
1856       info = g_new (GtkDragDestInfo, 1);
1857       info->widget = NULL;
1858       info->context = context;
1859       info->proxy_source = NULL;
1860       info->proxy_data = NULL;
1861       info->dropped = FALSE;
1862       info->proxy_drop_wait = FALSE;
1863       g_object_set_qdata_full (G_OBJECT (context), info_quark,
1864                                info, gtk_drag_dest_info_destroy);
1865     }
1866
1867   return info;
1868 }
1869
1870 static GQuark dest_info_quark = 0;
1871
1872 static GtkDragSourceInfo *
1873 gtk_drag_get_source_info (GdkDragContext *context,
1874                           gboolean        create)
1875 {
1876   GtkDragSourceInfo *info;
1877   if (!dest_info_quark)
1878     dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1879   
1880   info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1881   if (!info && create)
1882     {
1883       info = g_new0 (GtkDragSourceInfo, 1);
1884       info->context = context;
1885       g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1886     }
1887
1888   return info;
1889 }
1890
1891 static void
1892 gtk_drag_clear_source_info (GdkDragContext *context)
1893 {
1894   g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1895 }
1896
1897 static void
1898 gtk_drag_dest_realized (GtkWidget *widget)
1899 {
1900   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1901
1902   if (GTK_WIDGET_TOPLEVEL (toplevel))
1903     gdk_window_register_dnd (toplevel->window);
1904 }
1905
1906 static void
1907 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1908                                  GtkWidget *previous_toplevel)
1909 {
1910   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1911
1912   if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1913     gdk_window_register_dnd (toplevel->window);
1914 }
1915
1916 static void
1917 gtk_drag_dest_site_destroy (gpointer data)
1918 {
1919   GtkDragDestSite *site = data;
1920
1921   if (site->proxy_window)
1922     g_object_unref (site->proxy_window);
1923     
1924   if (site->target_list)
1925     gtk_target_list_unref (site->target_list);
1926
1927   g_free (site);
1928 }
1929
1930 /*
1931  * Default drag handlers
1932  */
1933 static void  
1934 gtk_drag_dest_leave (GtkWidget      *widget,
1935                      GdkDragContext *context,
1936                      guint           time)
1937 {
1938   GtkDragDestSite *site;
1939
1940   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1941   g_return_if_fail (site != NULL);
1942
1943   if (site->do_proxy)
1944     {
1945       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1946
1947       if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1948         {
1949           gdk_drag_abort (info->proxy_source->context, time);
1950           gtk_drag_source_info_destroy (info->proxy_source);
1951           info->proxy_source = NULL;
1952         }
1953       
1954       return;
1955     }
1956   else
1957     {
1958       if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1959         gtk_drag_unhighlight (widget);
1960
1961       if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
1962           site->track_motion)
1963         g_signal_emit_by_name (widget, "drag_leave", context, time);
1964       
1965       site->have_drag = FALSE;
1966     }
1967 }
1968
1969 static gboolean
1970 gtk_drag_dest_motion (GtkWidget      *widget,
1971                       GdkDragContext *context,
1972                       gint            x,
1973                       gint            y,
1974                       guint           time)
1975 {
1976   GtkDragDestSite *site;
1977   GdkDragAction action = 0;
1978   gboolean retval;
1979
1980   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1981   g_return_val_if_fail (site != NULL, FALSE);
1982
1983   if (site->do_proxy)
1984     {
1985       GdkAtom selection;
1986       GdkEvent *current_event;
1987       GdkWindow *dest_window;
1988       GdkDragProtocol proto;
1989         
1990       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1991
1992       if (!info->proxy_source || info->proxy_source->widget != widget)
1993         gtk_drag_proxy_begin (widget, info, time);
1994
1995       current_event = gtk_get_current_event ();
1996
1997       if (site->proxy_window)
1998         {
1999           dest_window = site->proxy_window;
2000           proto = site->proxy_protocol;
2001         }
2002       else
2003         {
2004           gdk_drag_find_window_for_screen (info->proxy_source->context,
2005                                            NULL,
2006                                            gdk_drawable_get_screen (current_event->dnd.window),
2007                                            current_event->dnd.x_root, 
2008                                            current_event->dnd.y_root,
2009                                            &dest_window, &proto);
2010         }
2011       
2012       gdk_drag_motion (info->proxy_source->context, 
2013                        dest_window, proto,
2014                        current_event->dnd.x_root, 
2015                        current_event->dnd.y_root, 
2016                        context->suggested_action, 
2017                        context->actions, time);
2018
2019       if (!site->proxy_window && dest_window)
2020         g_object_unref (dest_window);
2021
2022       selection = gdk_drag_get_selection (info->proxy_source->context);
2023       if (selection && 
2024           selection != gdk_drag_get_selection (info->context))
2025         gtk_drag_source_check_selection (info->proxy_source, selection, time);
2026
2027       gdk_event_free (current_event);
2028       
2029       return TRUE;
2030     }
2031
2032   if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
2033     {
2034       if (context->suggested_action & site->actions)
2035         action = context->suggested_action;
2036       else
2037         {
2038           gint i;
2039           
2040           for (i = 0; i < 8; i++)
2041             {
2042               if ((site->actions & (1 << i)) &&
2043                   (context->actions & (1 << i)))
2044                 {
2045                   action = (1 << i);
2046                   break;
2047                 }
2048             }
2049         }
2050
2051       if (action && gtk_drag_dest_find_target (widget, context, NULL))
2052         {
2053           if (!site->have_drag)
2054             {
2055               site->have_drag = TRUE;
2056               if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
2057                 gtk_drag_highlight (widget);
2058             }
2059
2060           gdk_drag_status (context, action, time);
2061         }
2062       else
2063         {
2064           gdk_drag_status (context, 0, time);
2065           if (!site->track_motion)
2066             return TRUE;
2067         }
2068     }
2069
2070   g_signal_emit_by_name (widget, "drag_motion",
2071                          context, x, y, time, &retval);
2072
2073   return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
2074 }
2075
2076 static gboolean
2077 gtk_drag_dest_drop (GtkWidget        *widget,
2078                     GdkDragContext   *context,
2079                     gint              x,
2080                     gint              y,
2081                     guint             time)
2082 {
2083   GtkDragDestSite *site;
2084   GtkDragDestInfo *info;
2085
2086   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2087   g_return_val_if_fail (site != NULL, FALSE);
2088
2089   info = gtk_drag_get_dest_info (context, FALSE);
2090   g_return_val_if_fail (info != NULL, FALSE);
2091
2092   info->drop_x = x;
2093   info->drop_y = y;
2094
2095   if (site->do_proxy)
2096     {
2097       if (info->proxy_source || 
2098           (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
2099         {
2100           gtk_drag_drop (info->proxy_source, time);
2101         }
2102       else
2103         {
2104           /* We need to synthesize a motion event, wait for a status,
2105            * and, if we get a good one, do a drop.
2106            */
2107           
2108           GdkEvent *current_event;
2109           GdkAtom selection;
2110           GdkWindow *dest_window;
2111           GdkDragProtocol proto;
2112           
2113           gtk_drag_proxy_begin (widget, info, time);
2114           info->proxy_drop_wait = TRUE;
2115           info->proxy_drop_time = time;
2116           
2117           current_event = gtk_get_current_event ();
2118
2119           if (site->proxy_window)
2120             {
2121               dest_window = site->proxy_window;
2122               proto = site->proxy_protocol;
2123             }
2124           else
2125             {
2126               gdk_drag_find_window_for_screen (info->proxy_source->context,
2127                                                NULL,
2128                                                gdk_drawable_get_screen (current_event->dnd.window),
2129                                                current_event->dnd.x_root, 
2130                                                current_event->dnd.y_root,
2131                                                &dest_window, &proto);
2132             }
2133
2134           gdk_drag_motion (info->proxy_source->context, 
2135                            dest_window, proto,
2136                            current_event->dnd.x_root, 
2137                            current_event->dnd.y_root, 
2138                            context->suggested_action, 
2139                            context->actions, time);
2140
2141           if (!site->proxy_window && dest_window)
2142             g_object_unref (dest_window);
2143
2144           selection = gdk_drag_get_selection (info->proxy_source->context);
2145           if (selection && 
2146               selection != gdk_drag_get_selection (info->context))
2147             gtk_drag_source_check_selection (info->proxy_source, selection, time);
2148
2149           gdk_event_free (current_event);
2150         }
2151
2152       return TRUE;
2153     }
2154   else
2155     {
2156       gboolean retval;
2157
2158       if (site->flags & GTK_DEST_DEFAULT_DROP)
2159         {
2160           GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
2161
2162           if (target == GDK_NONE)
2163             {
2164               gtk_drag_finish (context, FALSE, FALSE, time);
2165               return TRUE;
2166             }
2167           else 
2168             gtk_drag_get_data (widget, context, target, time);
2169         }
2170
2171       g_signal_emit_by_name (widget, "drag_drop",
2172                              context, x, y, time, &retval);
2173
2174       return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
2175     }
2176 }
2177
2178 /***************
2179  * Source side *
2180  ***************/
2181
2182 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
2183  * so that we can set the icon from the source site information
2184  */
2185 static GdkDragContext *
2186 gtk_drag_begin_internal (GtkWidget         *widget,
2187                          GtkDragSourceSite *site,
2188                          GtkTargetList     *target_list,
2189                          GdkDragAction      actions,
2190                          gint               button,
2191                          GdkEvent          *event)
2192 {
2193   GtkDragSourceInfo *info;
2194   GList *targets = NULL;
2195   GList *tmp_list;
2196   guint32 time = GDK_CURRENT_TIME;
2197   GdkDragAction possible_actions, suggested_action;
2198   GdkDragContext *context;
2199   GtkWidget *ipc_widget;
2200   GdkCursor *cursor;
2201  
2202   ipc_widget = gtk_drag_get_ipc_widget (widget);
2203   
2204   gtk_drag_get_event_actions (event, button, actions,
2205                               &suggested_action, &possible_actions);
2206   
2207   cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), 
2208                                 suggested_action,
2209                                 NULL);
2210   
2211   if (event)
2212     time = gdk_event_get_time (event);
2213
2214   if (gdk_pointer_grab (ipc_widget->window, FALSE,
2215                         GDK_POINTER_MOTION_MASK |
2216                         GDK_BUTTON_RELEASE_MASK, NULL,
2217                         cursor, time) != GDK_GRAB_SUCCESS)
2218     {
2219       gtk_drag_release_ipc_widget (ipc_widget);
2220       return NULL;
2221     }
2222
2223   gdk_keyboard_grab (ipc_widget->window, FALSE, time);
2224     
2225   /* We use a GTK grab here to override any grabs that the widget
2226    * we are dragging from might have held
2227    */
2228   gtk_grab_add (ipc_widget);
2229   
2230   tmp_list = g_list_last (target_list->list);
2231   while (tmp_list)
2232     {
2233       GtkTargetPair *pair = tmp_list->data;
2234       targets = g_list_prepend (targets, 
2235                                 GINT_TO_POINTER (pair->target));
2236       tmp_list = tmp_list->prev;
2237     }
2238
2239   source_widgets = g_slist_prepend (source_widgets, ipc_widget);
2240
2241   context = gdk_drag_begin (ipc_widget->window, targets);
2242   g_list_free (targets);
2243   
2244   info = gtk_drag_get_source_info (context, TRUE);
2245   
2246   info->ipc_widget = ipc_widget;
2247   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), info);
2248
2249   info->widget = g_object_ref (widget);
2250   
2251   info->button = button;
2252   info->cursor = cursor;
2253   info->target_list = target_list;
2254   gtk_target_list_ref (target_list);
2255
2256   info->possible_actions = actions;
2257
2258   info->status = GTK_DRAG_STATUS_DRAG;
2259   info->last_event = NULL;
2260   info->selections = NULL;
2261   info->icon_window = NULL;
2262   info->destroy_icon = FALSE;
2263
2264   /* Set cur_x, cur_y here so if the "drag_begin" signal shows
2265    * the drag icon, it will be in the right place
2266    */
2267   if (event && event->type == GDK_MOTION_NOTIFY)
2268     {
2269       info->cur_screen = gtk_widget_get_screen (widget);
2270       info->cur_x = event->motion.x_root;
2271       info->cur_y = event->motion.y_root;
2272     }
2273   else 
2274     {
2275       gdk_display_get_pointer (gtk_widget_get_display (widget),
2276                                &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
2277     }
2278
2279   g_signal_emit_by_name (widget, "drag_begin", info->context);
2280
2281   /* Ensure that we have an icon before we start the drag; the
2282    * application may have set one in ::drag_begin, or it may
2283    * not have set one.
2284    */
2285   if (!info->icon_window && !info->icon_pixbuf)
2286     {
2287       if (!site || site->icon_type == GTK_IMAGE_EMPTY)
2288         gtk_drag_set_icon_default (context);
2289       else
2290         switch (site->icon_type)
2291           {
2292           case GTK_IMAGE_PIXMAP:
2293             gtk_drag_set_icon_pixmap (context,
2294                                       site->colormap,
2295                                       site->icon_data.pixmap.pixmap,
2296                                       site->icon_mask,
2297                                       -2, -2);
2298             break;
2299           case GTK_IMAGE_PIXBUF:
2300             gtk_drag_set_icon_pixbuf (context,
2301                                       site->icon_data.pixbuf.pixbuf,
2302                                       -2, -2);
2303             break;
2304           case GTK_IMAGE_STOCK:
2305             gtk_drag_set_icon_stock (context,
2306                                      site->icon_data.stock.stock_id,
2307                                      -2, -2);
2308             break;
2309           case GTK_IMAGE_ICON_NAME:
2310             gtk_drag_set_icon_name (context,
2311                                     site->icon_data.name.icon_name,
2312                                     -2, -2);
2313             break;
2314           case GTK_IMAGE_EMPTY:
2315           default:
2316             g_assert_not_reached();
2317             break;
2318           }
2319     }
2320
2321   /* We need to composite the icon into the cursor, if we are
2322    * not using an icon window.
2323    */
2324   if (info->icon_pixbuf)  
2325     {
2326       cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), 
2327                                     suggested_action,
2328                                     info);
2329   
2330       if (cursor != info->cursor)
2331         {
2332           gdk_pointer_grab (widget->window, FALSE,
2333                             GDK_POINTER_MOTION_MASK |
2334                             GDK_BUTTON_RELEASE_MASK,
2335                             NULL,
2336                             cursor, time);
2337           info->cursor = cursor;
2338         }
2339     }
2340     
2341   if (event && event->type == GDK_MOTION_NOTIFY)
2342     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
2343   else
2344     gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
2345
2346   info->start_x = info->cur_x;
2347   info->start_y = info->cur_y;
2348
2349   g_signal_connect (info->ipc_widget, "grab_broken_event",
2350                     G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
2351   g_signal_connect (info->ipc_widget, "grab_notify",
2352                     G_CALLBACK (gtk_drag_grab_notify_cb), info);
2353   g_signal_connect (info->ipc_widget, "button_release_event",
2354                     G_CALLBACK (gtk_drag_button_release_cb), info);
2355   g_signal_connect (info->ipc_widget, "motion_notify_event",
2356                     G_CALLBACK (gtk_drag_motion_cb), info);
2357   g_signal_connect (info->ipc_widget, "key_press_event",
2358                     G_CALLBACK (gtk_drag_key_cb), info);
2359   g_signal_connect (info->ipc_widget, "key_release_event",
2360                     G_CALLBACK (gtk_drag_key_cb), info);
2361   g_signal_connect (info->ipc_widget, "selection_get",
2362                     G_CALLBACK (gtk_drag_selection_get), info);
2363
2364   info->have_grab = TRUE;
2365   info->grab_time = time;
2366
2367   return info->context;
2368 }
2369
2370 /**
2371  * gtk_drag_begin:
2372  * @widget: the source widget.
2373  * @targets: The targets (data formats) in which the
2374  *    source can provide the data.
2375  * @actions: A bitmask of the allowed drag actions for this drag.
2376  * @button: The button the user clicked to start the drag.
2377  * @event: The event that triggered the start of the drag.
2378  * 
2379  * Initiates a drag on the source side. The function
2380  * only needs to be used when the application is
2381  * starting drags itself, and is not needed when
2382  * gtk_drag_source_set() is used.
2383  * 
2384  * Return value: the context for this drag.
2385  **/
2386 GdkDragContext *
2387 gtk_drag_begin (GtkWidget         *widget,
2388                 GtkTargetList     *targets,
2389                 GdkDragAction      actions,
2390                 gint               button,
2391                 GdkEvent          *event)
2392 {
2393   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2394   g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
2395   g_return_val_if_fail (targets != NULL, NULL);
2396
2397   return gtk_drag_begin_internal (widget, NULL, targets,
2398                                   actions, button, event);
2399 }
2400
2401 /*************************************************************
2402  * gtk_drag_source_set:
2403  *     Register a drop site, and possibly add default behaviors.
2404  *   arguments:
2405  *     widget:
2406  *     start_button_mask: Mask of allowed buttons to start drag
2407  *     targets:           Table of targets for this source
2408  *     n_targets:
2409  *     actions:           Actions allowed for this source
2410  *   results:
2411  *************************************************************/
2412
2413 void 
2414 gtk_drag_source_set (GtkWidget            *widget,
2415                      GdkModifierType       start_button_mask,
2416                      const GtkTargetEntry *targets,
2417                      gint                  n_targets,
2418                      GdkDragAction         actions)
2419 {
2420   GtkDragSourceSite *site;
2421
2422   g_return_if_fail (GTK_IS_WIDGET (widget));
2423
2424   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2425
2426   gtk_widget_add_events (widget,
2427                          gtk_widget_get_events (widget) |
2428                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2429                          GDK_BUTTON_MOTION_MASK);
2430
2431   if (site)
2432     {
2433       if (site->target_list)
2434         gtk_target_list_unref (site->target_list);
2435     }
2436   else
2437     {
2438       site = g_new0 (GtkDragSourceSite, 1);
2439
2440       site->icon_type = GTK_IMAGE_EMPTY;
2441       
2442       g_signal_connect (widget, "button_press_event",
2443                         G_CALLBACK (gtk_drag_source_event_cb),
2444                         site);
2445       g_signal_connect (widget, "button_release_event",
2446                         G_CALLBACK (gtk_drag_source_event_cb),
2447                         site);
2448       g_signal_connect (widget, "motion_notify_event",
2449                         G_CALLBACK (gtk_drag_source_event_cb),
2450                         site);
2451       
2452       g_object_set_data_full (G_OBJECT (widget),
2453                               I_("gtk-site-data"), 
2454                               site, gtk_drag_source_site_destroy);
2455     }
2456
2457   site->start_button_mask = start_button_mask;
2458
2459   site->target_list = gtk_target_list_new (targets, n_targets);
2460
2461   site->actions = actions;
2462 }
2463
2464 /*************************************************************
2465  * gtk_drag_source_unset
2466  *     Unregister this widget as a drag source.
2467  *   arguments:
2468  *     widget:
2469  *   results:
2470  *************************************************************/
2471
2472 void 
2473 gtk_drag_source_unset (GtkWidget        *widget)
2474 {
2475   GtkDragSourceSite *site;
2476
2477   g_return_if_fail (GTK_IS_WIDGET (widget));
2478
2479   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2480
2481   if (site)
2482     {
2483       g_signal_handlers_disconnect_by_func (widget,
2484                                             gtk_drag_source_event_cb,
2485                                             site);
2486       g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
2487     }
2488 }
2489
2490 /**
2491  * gtk_drag_source_get_target_list:
2492  * @widget: a #GtkWidget
2493  *
2494  * Gets the list of targets this widget can provide for
2495  * drag-and-drop.
2496  *
2497  * Return value: the #GtkTargetList, or %NULL if none
2498  *
2499  * Since: 2.4
2500  **/
2501 GtkTargetList *
2502 gtk_drag_source_get_target_list (GtkWidget *widget)
2503 {
2504   GtkDragSourceSite *site;
2505
2506   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2507
2508   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2509
2510   return site ? site->target_list : NULL;
2511 }
2512
2513 /**
2514  * gtk_drag_source_set_target_list:
2515  * @widget: a #GtkWidget that's a drag source
2516  * @target_list: list of draggable targets, or %NULL for none
2517  *
2518  * Changes the target types that this widget offers for drag-and-drop.
2519  * The widget must first be made into a drag source with
2520  * gtk_drag_source_set().
2521  *
2522  * Since: 2.4
2523  **/
2524 void
2525 gtk_drag_source_set_target_list (GtkWidget     *widget,
2526                                  GtkTargetList *target_list)
2527 {
2528   GtkDragSourceSite *site;
2529
2530   g_return_if_fail (GTK_IS_WIDGET (widget));
2531
2532   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2533   if (site == NULL)
2534     {
2535       g_warning ("gtk_drag_source_set_target_list() requires the widget "
2536                  "to already be a drag source.");
2537       return;
2538     }
2539
2540   if (target_list)
2541     gtk_target_list_ref (target_list);
2542
2543   if (site->target_list)
2544     gtk_target_list_unref (site->target_list);
2545
2546   site->target_list = target_list;
2547 }
2548
2549 /**
2550  * gtk_drag_source_add_text_targets:
2551  * @widget: a #GtkWidget that's is a drag source
2552  *
2553  * Add the text targets supported by #GtkSelection to
2554  * the target list of the drag source.  The targets
2555  * are added with @info = 0. If you need another value, 
2556  * use gtk_target_list_add_text_targets() and
2557  * gtk_drag_source_set_target_list().
2558  * 
2559  * Since: 2.6
2560  **/
2561 void
2562 gtk_drag_source_add_text_targets (GtkWidget *widget)
2563 {
2564   GtkTargetList *target_list;
2565
2566   target_list = gtk_drag_source_get_target_list (widget);
2567   if (target_list)
2568     gtk_target_list_ref (target_list);
2569   else
2570     target_list = gtk_target_list_new (NULL, 0);
2571   gtk_target_list_add_text_targets (target_list, 0);
2572   gtk_drag_source_set_target_list (widget, target_list);
2573   gtk_target_list_unref (target_list);
2574 }
2575
2576 /**
2577  * gtk_drag_source_add_image_targets:
2578  * @widget: a #GtkWidget that's is a drag source
2579  *
2580  * Add the writable image targets supported by #GtkSelection to
2581  * the target list of the drag source. The targets
2582  * are added with @info = 0. If you need another value, 
2583  * use gtk_target_list_add_image_targets() and
2584  * gtk_drag_source_set_target_list().
2585  * 
2586  * Since: 2.6
2587  **/
2588 void
2589 gtk_drag_source_add_image_targets (GtkWidget *widget)
2590 {
2591   GtkTargetList *target_list;
2592
2593   target_list = gtk_drag_source_get_target_list (widget);
2594   if (target_list)
2595     gtk_target_list_ref (target_list);
2596   else
2597     target_list = gtk_target_list_new (NULL, 0);
2598   gtk_target_list_add_image_targets (target_list, 0, TRUE);
2599   gtk_drag_source_set_target_list (widget, target_list);
2600   gtk_target_list_unref (target_list);
2601 }
2602
2603 /**
2604  * gtk_drag_source_add_uri_targets:
2605  * @widget: a #GtkWidget that's is a drag source
2606  *
2607  * Add the URI targets supported by #GtkSelection to
2608  * the target list of the drag source.  The targets
2609  * are added with @info = 0. If you need another value, 
2610  * use gtk_target_list_add_uri_targets() and
2611  * gtk_drag_source_set_target_list().
2612  * 
2613  * Since: 2.6
2614  **/
2615 void
2616 gtk_drag_source_add_uri_targets (GtkWidget *widget)
2617 {
2618   GtkTargetList *target_list;
2619
2620   target_list = gtk_drag_source_get_target_list (widget);
2621   if (target_list)
2622     gtk_target_list_ref (target_list);
2623   else
2624     target_list = gtk_target_list_new (NULL, 0);
2625   gtk_target_list_add_uri_targets (target_list, 0);
2626   gtk_drag_source_set_target_list (widget, target_list);
2627   gtk_target_list_unref (target_list);
2628 }
2629
2630 static void
2631 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2632 {
2633   switch (site->icon_type)
2634     {
2635     case GTK_IMAGE_EMPTY:
2636       break;
2637     case GTK_IMAGE_PIXMAP:
2638       if (site->icon_data.pixmap.pixmap)
2639         g_object_unref (site->icon_data.pixmap.pixmap);
2640       if (site->icon_mask)
2641         g_object_unref (site->icon_mask);
2642       break;
2643     case GTK_IMAGE_PIXBUF:
2644       g_object_unref (site->icon_data.pixbuf.pixbuf);
2645       break;
2646     case GTK_IMAGE_STOCK:
2647       g_free (site->icon_data.stock.stock_id);
2648       break;
2649     case GTK_IMAGE_ICON_NAME:
2650       g_free (site->icon_data.name.icon_name);
2651       break;
2652     default:
2653       g_assert_not_reached();
2654       break;
2655     }
2656   site->icon_type = GTK_IMAGE_EMPTY;
2657   
2658   if (site->colormap)
2659     g_object_unref (site->colormap);
2660   site->colormap = NULL;
2661 }
2662
2663 /**
2664  * gtk_drag_source_set_icon:
2665  * @widget: a #GtkWidget
2666  * @colormap: the colormap of the icon
2667  * @pixmap: the image data for the icon
2668  * @mask: the transparency mask for an image.
2669  * 
2670  * Sets the icon that will be used for drags from a particular widget
2671  * from a pixmap/mask. GTK+ retains references for the arguments, and 
2672  * will release them when they are no longer needed.
2673  * Use gtk_drag_source_set_icon_pixbuf() instead.
2674  **/
2675 void 
2676 gtk_drag_source_set_icon (GtkWidget     *widget,
2677                           GdkColormap   *colormap,
2678                           GdkPixmap     *pixmap,
2679                           GdkBitmap     *mask)
2680 {
2681   GtkDragSourceSite *site;
2682
2683   g_return_if_fail (GTK_IS_WIDGET (widget));
2684   g_return_if_fail (GDK_IS_COLORMAP (colormap));
2685   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2686   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2687
2688   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2689   g_return_if_fail (site != NULL);
2690   
2691   g_object_ref (colormap);
2692   g_object_ref (pixmap);
2693   if (mask)
2694     g_object_ref (mask);
2695
2696   gtk_drag_source_unset_icon (site);
2697
2698   site->icon_type = GTK_IMAGE_PIXMAP;
2699   
2700   site->icon_data.pixmap.pixmap = pixmap;
2701   site->icon_mask = mask;
2702   site->colormap = colormap;
2703 }
2704
2705 /**
2706  * gtk_drag_source_set_icon_pixbuf:
2707  * @widget: a #GtkWidget
2708  * @pixbuf: the #GdkPixbuf for the drag icon
2709  * 
2710  * Sets the icon that will be used for drags from a particular widget
2711  * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will 
2712  * release it when it is no longer needed.
2713  **/
2714 void 
2715 gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
2716                                  GdkPixbuf   *pixbuf)
2717 {
2718   GtkDragSourceSite *site;
2719
2720   g_return_if_fail (GTK_IS_WIDGET (widget));
2721   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2722
2723   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2724   g_return_if_fail (site != NULL); 
2725   g_object_ref (pixbuf);
2726
2727   gtk_drag_source_unset_icon (site);
2728
2729   site->icon_type = GTK_IMAGE_PIXBUF;
2730   site->icon_data.pixbuf.pixbuf = pixbuf;
2731 }
2732
2733 /**
2734  * gtk_drag_source_set_icon_stock:
2735  * @widget: a #GtkWidget
2736  * @stock_id: the ID of the stock icon to use
2737  *
2738  * Sets the icon that will be used for drags from a particular source
2739  * to a stock icon. 
2740  **/
2741 void 
2742 gtk_drag_source_set_icon_stock (GtkWidget   *widget,
2743                                 const gchar *stock_id)
2744 {
2745   GtkDragSourceSite *site;
2746
2747   g_return_if_fail (GTK_IS_WIDGET (widget));
2748   g_return_if_fail (stock_id != NULL);
2749
2750   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2751   g_return_if_fail (site != NULL);
2752   
2753   gtk_drag_source_unset_icon (site);
2754
2755   site->icon_type = GTK_IMAGE_STOCK;
2756   site->icon_data.stock.stock_id = g_strdup (stock_id);
2757 }
2758
2759 /**
2760  * gtk_drag_source_set_icon_name:
2761  * @widget: a #GtkWidget
2762  * @icon_name: name of icon to use
2763  * 
2764  * Sets the icon that will be used for drags from a particular source
2765  * to a themed icon. See the docs for #GtkIconTheme for more details.
2766  *
2767  * Since: 2.8
2768  **/
2769 void 
2770 gtk_drag_source_set_icon_name (GtkWidget   *widget,
2771                                const gchar *icon_name)
2772 {
2773   GtkDragSourceSite *site;
2774
2775   g_return_if_fail (GTK_IS_WIDGET (widget));
2776   g_return_if_fail (icon_name != NULL);
2777
2778   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2779   g_return_if_fail (site != NULL);
2780
2781   gtk_drag_source_unset_icon (site);
2782
2783   site->icon_type = GTK_IMAGE_ICON_NAME;
2784   site->icon_data.name.icon_name = g_strdup (icon_name);
2785 }
2786
2787 static void
2788 gtk_drag_get_icon (GtkDragSourceInfo *info,
2789                    GtkWidget        **icon_window,
2790                    gint              *hot_x,
2791                    gint              *hot_y)
2792 {
2793   if (get_can_change_screen (info->icon_window))
2794     gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2795                            info->cur_screen);
2796       
2797   if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2798     {
2799       if (!info->fallback_icon)
2800         {
2801           gint save_hot_x, save_hot_y;
2802           gboolean save_destroy_icon;
2803           GtkWidget *save_icon_window;
2804           
2805           /* HACK to get the appropriate icon
2806            */
2807           save_icon_window = info->icon_window;
2808           save_hot_x = info->hot_x;
2809           save_hot_y = info->hot_x;
2810           save_destroy_icon = info->destroy_icon;
2811
2812           info->icon_window = NULL;
2813           if (!default_icon_pixmap)
2814             set_icon_stock_pixbuf (info->context, 
2815                                    GTK_STOCK_DND, NULL, -2, -2, TRUE);
2816           else
2817             gtk_drag_set_icon_pixmap (info->context, 
2818                                       default_icon_colormap, 
2819                                       default_icon_pixmap, 
2820                                       default_icon_mask,
2821                                       default_icon_hot_x,
2822                                       default_icon_hot_y);
2823           info->fallback_icon = info->icon_window;
2824           
2825           info->icon_window = save_icon_window;
2826           info->hot_x = save_hot_x;
2827           info->hot_y = save_hot_y;
2828           info->destroy_icon = save_destroy_icon;
2829         }
2830       
2831       gtk_widget_hide (info->icon_window);
2832       
2833       *icon_window = info->fallback_icon;
2834       gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2835       
2836       if (!default_icon_pixmap)
2837         {
2838           *hot_x = -2;
2839           *hot_y = -2;
2840         }
2841       else
2842         {
2843           *hot_x = default_icon_hot_x;
2844           *hot_y = default_icon_hot_y;
2845         }
2846     }
2847   else
2848     {
2849       if (info->fallback_icon)
2850         gtk_widget_hide (info->fallback_icon);
2851       
2852       *icon_window = info->icon_window;
2853       *hot_x = info->hot_x;
2854       *hot_y = info->hot_y;
2855     }
2856 }
2857
2858 static void
2859 gtk_drag_update_icon (GtkDragSourceInfo *info)
2860 {
2861   if (info->icon_window)
2862     {
2863       GtkWidget *icon_window;
2864       gint hot_x, hot_y;
2865   
2866       gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
2867       
2868       gtk_window_move (GTK_WINDOW (icon_window), 
2869                        info->cur_x - hot_x, 
2870                        info->cur_y - hot_y);
2871
2872       if (GTK_WIDGET_VISIBLE (icon_window))
2873         gdk_window_raise (icon_window->window);
2874       else
2875         gtk_widget_show (icon_window);
2876     }
2877 }
2878
2879 static void 
2880 gtk_drag_set_icon_window (GdkDragContext *context,
2881                           GtkWidget      *widget,
2882                           gint            hot_x,
2883                           gint            hot_y,
2884                           gboolean        destroy_on_release)
2885 {
2886   GtkDragSourceInfo *info;
2887
2888   info = gtk_drag_get_source_info (context, FALSE);
2889   if (info == NULL)
2890     {
2891       if (destroy_on_release)
2892         gtk_widget_destroy (widget);
2893       return;
2894     }
2895
2896   gtk_drag_remove_icon (info);
2897
2898   if (widget)
2899     g_object_ref (widget);  
2900   
2901   info->icon_window = widget;
2902   info->hot_x = hot_x;
2903   info->hot_y = hot_y;
2904   info->destroy_icon = destroy_on_release;
2905
2906   if (widget && info->icon_pixbuf)
2907     {
2908       g_object_unref (info->icon_pixbuf);
2909       info->icon_pixbuf = NULL;
2910     }
2911
2912   gtk_drag_update_cursor (info);
2913   gtk_drag_update_icon (info);
2914 }
2915
2916 /**
2917  * gtk_drag_set_icon_widget:
2918  * @context: the context for a drag. (This must be called 
2919           with a  context for the source side of a drag)
2920  * @widget: a toplevel window to use as an icon.
2921  * @hot_x: the X offset within @widget of the hotspot.
2922  * @hot_y: the Y offset within @widget of the hotspot.
2923  * 
2924  * Changes the icon for a widget to a given widget. GTK+
2925  * will not destroy the icon, so if you don't want
2926  * it to persist, you should connect to the "drag_end" 
2927  * signal and destroy it yourself.
2928  **/
2929 void 
2930 gtk_drag_set_icon_widget (GdkDragContext    *context,
2931                           GtkWidget         *widget,
2932                           gint               hot_x,
2933                           gint               hot_y)
2934 {
2935   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2936   g_return_if_fail (context->is_source);
2937   g_return_if_fail (GTK_IS_WIDGET (widget));
2938
2939   gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2940 }
2941
2942 static void
2943 icon_window_realize (GtkWidget *window,
2944                      GdkPixbuf *pixbuf)
2945 {
2946   GdkPixmap *pixmap;
2947   GdkPixmap *mask;
2948
2949   gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
2950                                                   gtk_widget_get_colormap (window),
2951                                                   &pixmap, &mask, 128);
2952   
2953   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2954   g_object_unref (pixmap);
2955   
2956   if (mask)
2957     {
2958       gtk_widget_shape_combine_mask (window, mask, 0, 0);
2959       g_object_unref (mask);
2960     }
2961 }
2962
2963 static void
2964 set_icon_stock_pixbuf (GdkDragContext    *context,
2965                        const gchar       *stock_id,
2966                        GdkPixbuf         *pixbuf,
2967                        gint               hot_x,
2968                        gint               hot_y,
2969                        gboolean           force_window)
2970 {
2971   GtkWidget *window;
2972   gint width, height;
2973   GdkScreen *screen;
2974   GdkDisplay *display;
2975
2976   g_return_if_fail (context != NULL);
2977   g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2978   g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2979
2980   screen = gdk_drawable_get_screen (context->source_window);
2981
2982   /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
2983   gtk_widget_push_colormap (NULL);
2984   window = gtk_window_new (GTK_WINDOW_POPUP);
2985   gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
2986   gtk_window_set_screen (GTK_WINDOW (window), screen);
2987   set_can_change_screen (window, TRUE);
2988   gtk_widget_pop_colormap ();
2989
2990   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2991   gtk_widget_set_app_paintable (window, TRUE);
2992
2993   if (stock_id)
2994     {
2995       pixbuf = gtk_widget_render_icon (window, stock_id,
2996                                        GTK_ICON_SIZE_DND, NULL);
2997
2998       if (!pixbuf)
2999         {
3000           g_warning ("Cannot load drag icon from stock_id %s", stock_id);
3001           gtk_widget_destroy (window);
3002           return;
3003         }
3004
3005     }
3006   else
3007     g_object_ref (pixbuf);
3008
3009   display = gdk_drawable_get_display (context->source_window);
3010   width = gdk_pixbuf_get_width (pixbuf);
3011   height = gdk_pixbuf_get_height (pixbuf);
3012
3013   if (!force_window &&
3014       gtk_drag_can_use_rgba_cursor (display, width + 2, height + 2))
3015     {
3016       GtkDragSourceInfo *info;
3017
3018       gtk_widget_destroy (window);
3019
3020       info = gtk_drag_get_source_info (context, FALSE);
3021
3022       if (info->icon_pixbuf)
3023         g_object_unref (info->icon_pixbuf);
3024       info->icon_pixbuf = pixbuf;
3025
3026       gtk_drag_set_icon_window (context, NULL, hot_x, hot_y, TRUE);
3027     }
3028   else
3029     {
3030       gtk_widget_set_size_request (window, width, height);
3031
3032       g_signal_connect_closure (window, "realize",
3033                                 g_cclosure_new (G_CALLBACK (icon_window_realize),
3034                                                 pixbuf,
3035                                                 (GClosureNotify)g_object_unref),
3036                                 FALSE);
3037                     
3038       gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3039    }
3040 }
3041
3042 /**
3043  * gtk_drag_set_icon_pixbuf:
3044  * @context: the context for a drag. (This must be called 
3045  *            with a  context for the source side of a drag)
3046  * @pixbuf: the #GdkPixbuf to use as the drag icon.
3047  * @hot_x: the X offset within @widget of the hotspot.
3048  * @hot_y: the Y offset within @widget of the hotspot.
3049  * 
3050  * Sets @pixbuf as the icon for a given drag.
3051  **/
3052 void 
3053 gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
3054                            GdkPixbuf      *pixbuf,
3055                            gint            hot_x,
3056                            gint            hot_y)
3057 {
3058   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3059   g_return_if_fail (context->is_source);
3060   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
3061
3062   set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3063 }
3064
3065 /**
3066  * gtk_drag_set_icon_stock:
3067  * @context: the context for a drag. (This must be called 
3068  *            with a  context for the source side of a drag)
3069  * @stock_id: the ID of the stock icon to use for the drag.
3070  * @hot_x: the X offset within the icon of the hotspot.
3071  * @hot_y: the Y offset within the icon of the hotspot.
3072  * 
3073  * Sets the icon for a given drag from a stock ID.
3074  **/
3075 void 
3076 gtk_drag_set_icon_stock  (GdkDragContext *context,
3077                           const gchar    *stock_id,
3078                           gint            hot_x,
3079                           gint            hot_y)
3080 {
3081   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3082   g_return_if_fail (context->is_source);
3083   g_return_if_fail (stock_id != NULL);
3084   
3085   set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y, FALSE);
3086 }
3087
3088 /**
3089  * gtk_drag_set_icon_pixmap:
3090  * @context: the context for a drag. (This must be called 
3091  *            with a  context for the source side of a drag)
3092  * @colormap: the colormap of the icon 
3093  * @pixmap: the image data for the icon 
3094  * @mask: the transparency mask for the icon
3095  * @hot_x: the X offset within @pixmap of the hotspot.
3096  * @hot_y: the Y offset within @pixmap of the hotspot.
3097  * 
3098  * Sets @pixmap as the icon for a given drag. GTK+ retains
3099  * references for the arguments, and will release them when
3100  * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
3101  * will be more convenient to use.
3102  **/
3103 void 
3104 gtk_drag_set_icon_pixmap (GdkDragContext    *context,
3105                           GdkColormap       *colormap,
3106                           GdkPixmap         *pixmap,
3107                           GdkBitmap         *mask,
3108                           gint               hot_x,
3109                           gint               hot_y)
3110 {
3111   GtkWidget *window;
3112   GdkScreen *screen;
3113   gint width, height;
3114       
3115   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3116   g_return_if_fail (context->is_source);
3117   g_return_if_fail (GDK_IS_COLORMAP (colormap));
3118   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
3119   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
3120
3121   screen = gdk_colormap_get_screen (colormap);
3122   
3123   g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
3124   g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
3125   
3126   gdk_drawable_get_size (pixmap, &width, &height);
3127
3128   gtk_widget_push_colormap (colormap);
3129
3130   window = gtk_window_new (GTK_WINDOW_POPUP);
3131   gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3132   gtk_window_set_screen (GTK_WINDOW (window), screen);
3133   set_can_change_screen (window, FALSE);
3134   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3135   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
3136
3137   gtk_widget_pop_colormap ();
3138
3139   gtk_widget_set_size_request (window, width, height);
3140   gtk_widget_realize (window);
3141
3142   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
3143   
3144   if (mask)
3145     gtk_widget_shape_combine_mask (window, mask, 0, 0);
3146
3147   gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3148 }
3149
3150 /**
3151  * gtk_drag_set_icon_name:
3152  * @context: the context for a drag. (This must be called 
3153  *            with a context for the source side of a drag)
3154  * @icon_name: name of icon to use
3155  * @hot_x: the X offset of the hotspot within the icon
3156  * @hot_y: the Y offset of the hotspot within the icon
3157  * 
3158  * Sets the icon for a given drag from a named themed icon. See
3159  * the docs for #GtkIconTheme for more details. Note that the
3160  * size of the icon depends on the icon theme (the icon is
3161  * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus 
3162  * @hot_x and @hot_y have to be used with care.
3163  *
3164  * Since: 2.8
3165  **/
3166 void 
3167 gtk_drag_set_icon_name (GdkDragContext *context,
3168                         const gchar    *icon_name,
3169                         gint            hot_x,
3170                         gint            hot_y)
3171 {
3172   GdkScreen *screen;
3173   GtkSettings *settings;
3174   GtkIconTheme *icon_theme;
3175   GdkPixbuf *pixbuf;
3176   gint width, height, icon_size;
3177
3178   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3179   g_return_if_fail (context->is_source);
3180   g_return_if_fail (icon_name != NULL);
3181
3182   screen = gdk_drawable_get_screen (context->source_window);
3183   g_return_if_fail (screen != NULL);
3184
3185   settings = gtk_settings_get_for_screen (screen);
3186   if (gtk_icon_size_lookup_for_settings (settings,
3187                                          GTK_ICON_SIZE_DND,
3188                                          &width, &height))
3189     icon_size = MAX (width, height);
3190   else 
3191     icon_size = 32; /* default value for GTK_ICON_SIZE_DND */ 
3192
3193   icon_theme = gtk_icon_theme_get_for_screen (screen);
3194
3195   pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name,
3196                                      icon_size, 0, NULL);
3197   if (pixbuf)
3198     set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3199   else
3200     g_warning ("Cannot load drag icon from icon name %s", icon_name);
3201 }
3202
3203 /**
3204  * gtk_drag_set_icon_default:
3205  * @context: the context for a drag. (This must be called 
3206              with a  context for the source side of a drag)
3207  * 
3208  * Sets the icon for a particular drag to the default
3209  * icon.
3210  **/
3211 void 
3212 gtk_drag_set_icon_default (GdkDragContext *context)
3213 {
3214   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3215   g_return_if_fail (context->is_source);
3216
3217   if (!default_icon_pixmap)
3218     gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
3219   else
3220     gtk_drag_set_icon_pixmap (context, 
3221                               default_icon_colormap, 
3222                               default_icon_pixmap, 
3223                               default_icon_mask,
3224                               default_icon_hot_x,
3225                               default_icon_hot_y);
3226 }
3227
3228 /**
3229  * gtk_drag_set_default_icon:
3230  * @colormap: the colormap of the icon
3231  * @pixmap: the image data for the icon
3232  * @mask: the transparency mask for an image.
3233  * @hot_x: The X offset within @widget of the hotspot.
3234  * @hot_y: The Y offset within @widget of the hotspot.
3235  * 
3236  * Changes the default drag icon. GTK+ retains references for the
3237  * arguments, and will release them when they are no longer needed.
3238  * This function is obsolete. The default icon should now be changed
3239  * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
3240  **/
3241 void 
3242 gtk_drag_set_default_icon (GdkColormap   *colormap,
3243                            GdkPixmap     *pixmap,
3244                            GdkBitmap     *mask,
3245                            gint           hot_x,
3246                            gint           hot_y)
3247 {
3248   g_return_if_fail (GDK_IS_COLORMAP (colormap));
3249   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
3250   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
3251   
3252   if (default_icon_colormap)
3253     g_object_unref (default_icon_colormap);
3254   if (default_icon_pixmap)
3255     g_object_unref (default_icon_pixmap);
3256   if (default_icon_mask)
3257     g_object_unref (default_icon_mask);
3258
3259   default_icon_colormap = colormap;
3260   g_object_ref (colormap);
3261   
3262   default_icon_pixmap = pixmap;
3263   g_object_ref (pixmap);
3264
3265   default_icon_mask = mask;
3266   if (mask)
3267     g_object_ref (mask);
3268   
3269   default_icon_hot_x = hot_x;
3270   default_icon_hot_y = hot_y;
3271 }
3272
3273
3274 /*************************************************************
3275  * _gtk_drag_source_handle_event:
3276  *     Called from widget event handling code on Drag events
3277  *     for drag sources.
3278  *
3279  *   arguments:
3280  *     toplevel: Toplevel widget that received the event
3281  *     event:
3282  *   results:
3283  *************************************************************/
3284
3285 void
3286 _gtk_drag_source_handle_event (GtkWidget *widget,
3287                                GdkEvent  *event)
3288 {
3289   GtkDragSourceInfo *info;
3290   GdkDragContext *context;
3291
3292   g_return_if_fail (widget != NULL);
3293   g_return_if_fail (event != NULL);
3294
3295   context = event->dnd.context;
3296   info = gtk_drag_get_source_info (context, FALSE);
3297   if (!info)
3298     return;
3299
3300   switch (event->type)
3301     {
3302     case GDK_DRAG_STATUS:
3303       {
3304         GdkCursor *cursor;
3305
3306         if (info->proxy_dest)
3307           {
3308             if (!event->dnd.send_event)
3309               {
3310                 if (info->proxy_dest->proxy_drop_wait)
3311                   {
3312                     gboolean result = context->action != 0;
3313                     
3314                     /* Aha - we can finally pass the MOTIF DROP on... */
3315                     gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
3316                     if (result)
3317                       gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
3318                     else
3319                       gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
3320                   }
3321                 else
3322                   {
3323                     gdk_drag_status (info->proxy_dest->context,
3324                                      event->dnd.context->action,
3325                                      event->dnd.time);
3326                   }
3327               }
3328           }
3329         else if (info->have_grab)
3330           {
3331             cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
3332                                           event->dnd.context->action,
3333                                           info);
3334             if (info->cursor != cursor)
3335               {
3336                 gdk_pointer_grab (widget->window, FALSE,
3337                                   GDK_POINTER_MOTION_MASK |
3338                                   GDK_BUTTON_RELEASE_MASK,
3339                                   NULL,
3340                                   cursor, info->grab_time);
3341                 info->cursor = cursor;
3342               }
3343             
3344             gtk_drag_add_update_idle (info);
3345           }
3346       }
3347       break;
3348       
3349     case GDK_DROP_FINISHED:
3350       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, event->dnd.time);
3351       break;
3352     default:
3353       g_assert_not_reached ();
3354     }
3355 }
3356
3357 /*************************************************************
3358  * gtk_drag_source_check_selection:
3359  *     Check if we've set up handlers/claimed the selection
3360  *     for a given drag. If not, add them.
3361  *   arguments:
3362  *     
3363  *   results:
3364  *************************************************************/
3365
3366 static void
3367 gtk_drag_source_check_selection (GtkDragSourceInfo *info, 
3368                                  GdkAtom            selection,
3369                                  guint32            time)
3370 {
3371   GList *tmp_list;
3372
3373   tmp_list = info->selections;
3374   while (tmp_list)
3375     {
3376       if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
3377         return;
3378       tmp_list = tmp_list->next;
3379     }
3380
3381   gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
3382                                        info->ipc_widget,
3383                                        selection,
3384                                        time);
3385   info->selections = g_list_prepend (info->selections,
3386                                      GUINT_TO_POINTER (selection));
3387
3388   tmp_list = info->target_list->list;
3389   while (tmp_list)
3390     {
3391       GtkTargetPair *pair = tmp_list->data;
3392
3393       gtk_selection_add_target (info->ipc_widget,
3394                                 selection,
3395                                 pair->target,
3396                                 pair->info);
3397       tmp_list = tmp_list->next;
3398     }
3399   
3400   if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
3401     {
3402       gtk_selection_add_target (info->ipc_widget,
3403                                 selection,
3404                                 gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS"),
3405                                 TARGET_MOTIF_SUCCESS);
3406       gtk_selection_add_target (info->ipc_widget,
3407                                 selection,
3408                                 gdk_atom_intern_static_string ("XmTRANSFER_FAILURE"),
3409                                 TARGET_MOTIF_FAILURE);
3410     }
3411
3412   gtk_selection_add_target (info->ipc_widget,
3413                             selection,
3414                             gdk_atom_intern_static_string ("DELETE"),
3415                             TARGET_DELETE);
3416 }
3417
3418 /*************************************************************
3419  * gtk_drag_drop_finished:
3420  *     Clean up from the drag, and display snapback, if necessary.
3421  *   arguments:
3422  *     info:
3423  *     success:
3424  *     time:
3425  *   results:
3426  *************************************************************/
3427
3428 static void
3429 gtk_drag_drop_finished (GtkDragSourceInfo *info,
3430                         GtkDragResult      result,
3431                         guint              time)
3432 {
3433   gboolean success;
3434
3435   success = (result == GTK_DRAG_RESULT_SUCCESS);
3436   gtk_drag_source_release_selections (info, time); 
3437
3438   if (info->proxy_dest)
3439     {
3440       /* The time from the event isn't reliable for Xdnd drags */
3441       gtk_drag_finish (info->proxy_dest->context, success, FALSE, 
3442                        info->proxy_dest->proxy_drop_time);
3443       gtk_drag_source_info_destroy (info);
3444     }
3445   else
3446     {
3447       if (!success)
3448         g_signal_emit_by_name (info->widget, "drag_failed",
3449                                info->context, result, &success);
3450
3451       if (success)
3452         {
3453           gtk_drag_source_info_destroy (info);
3454         }
3455       else
3456         {
3457           GtkDragAnim *anim = g_new (GtkDragAnim, 1);
3458           anim->info = info;
3459           anim->step = 0;
3460           
3461           anim->n_steps = MAX (info->cur_x - info->start_x,
3462                                info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
3463           anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
3464
3465           info->cur_screen = gtk_widget_get_screen (info->widget);
3466
3467           if (!info->icon_window)
3468             set_icon_stock_pixbuf (info->context, NULL, info->icon_pixbuf, 
3469                                    0, 0, TRUE);
3470
3471           gtk_drag_update_icon (info);
3472           
3473           /* Mark the context as dead, so if the destination decides
3474            * to respond really late, we still are OK.
3475            */
3476           gtk_drag_clear_source_info (info->context);
3477           gdk_threads_add_timeout (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
3478         }
3479     }
3480 }
3481
3482 static void
3483 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
3484                                     guint32            time)
3485 {
3486   GdkDisplay *display = gtk_widget_get_display (info->widget);
3487   GList *tmp_list = info->selections;
3488   
3489   while (tmp_list)
3490     {
3491       GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
3492       if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
3493         gtk_selection_owner_set_for_display (display, NULL, selection, time);
3494
3495       tmp_list = tmp_list->next;
3496     }
3497
3498   g_list_free (info->selections);
3499   info->selections = NULL;
3500 }
3501
3502 /*************************************************************
3503  * gtk_drag_drop:
3504  *     Send a drop event.
3505  *   arguments:
3506  *     
3507  *   results:
3508  *************************************************************/
3509
3510 static void
3511 gtk_drag_drop (GtkDragSourceInfo *info, 
3512                guint32            time)
3513 {
3514   if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
3515     {
3516       GtkSelectionData selection_data;
3517       GList *tmp_list;
3518       /* GTK+ traditionally has used application/x-rootwin-drop, but the
3519        * XDND spec specifies x-rootwindow-drop.
3520        */
3521       GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3522       GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3523       
3524       tmp_list = info->target_list->list;
3525       while (tmp_list)
3526         {
3527           GtkTargetPair *pair = tmp_list->data;
3528           
3529           if (pair->target == target1 || pair->target == target2)
3530             {
3531               selection_data.selection = GDK_NONE;
3532               selection_data.target = pair->target;
3533               selection_data.data = NULL;
3534               selection_data.length = -1;
3535               
3536               g_signal_emit_by_name (info->widget, "drag_data_get",
3537                                      info->context, &selection_data,
3538                                      pair->info,
3539                                      time);
3540               
3541               /* FIXME: Should we check for length >= 0 here? */
3542               gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3543               return;
3544             }
3545           tmp_list = tmp_list->next;
3546         }
3547       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3548     }
3549   else
3550     {
3551       if (info->icon_window)
3552         gtk_widget_hide (info->icon_window);
3553         
3554       gdk_drag_drop (info->context, time);
3555       info->drop_timeout = gdk_threads_add_timeout (DROP_ABORT_TIME,
3556                                           gtk_drag_abort_timeout,
3557                                           info);
3558     }
3559 }
3560
3561 /*
3562  * Source side callbacks.
3563  */
3564
3565 static gboolean
3566 gtk_drag_source_event_cb (GtkWidget      *widget,
3567                           GdkEvent       *event,
3568                           gpointer        data)
3569 {
3570   GtkDragSourceSite *site;
3571   gboolean retval = FALSE;
3572   site = (GtkDragSourceSite *)data;
3573
3574   switch (event->type)
3575     {
3576     case GDK_BUTTON_PRESS:
3577       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3578         {
3579           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
3580           site->x = event->button.x;
3581           site->y = event->button.y;
3582         }
3583       break;
3584       
3585     case GDK_BUTTON_RELEASE:
3586       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3587         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
3588       break;
3589       
3590     case GDK_MOTION_NOTIFY:
3591       if (site->state & event->motion.state & site->start_button_mask)
3592         {
3593           /* FIXME: This is really broken and can leave us
3594            * with a stuck grab
3595            */
3596           int i;
3597           for (i=1; i<6; i++)
3598             {
3599               if (site->state & event->motion.state & 
3600                   GDK_BUTTON1_MASK << (i - 1))
3601                 break;
3602             }
3603
3604           if (gtk_drag_check_threshold (widget, site->x, site->y,
3605                                         event->motion.x, event->motion.y))
3606             {
3607               site->state = 0;
3608               gtk_drag_begin_internal (widget, site, site->target_list,
3609                                        site->actions, 
3610                                        i, event);
3611
3612               retval = TRUE;
3613             }
3614         }
3615       break;
3616       
3617     default:                    /* hit for 2/3BUTTON_PRESS */
3618       break;
3619     }
3620   
3621   return retval;
3622 }
3623
3624 static void 
3625 gtk_drag_source_site_destroy (gpointer data)
3626 {
3627   GtkDragSourceSite *site = data;
3628
3629   if (site->target_list)
3630     gtk_target_list_unref (site->target_list);
3631
3632   gtk_drag_source_unset_icon (site);
3633   g_free (site);
3634 }
3635
3636 static void
3637 gtk_drag_selection_get (GtkWidget        *widget, 
3638                         GtkSelectionData *selection_data,
3639                         guint             sel_info,
3640                         guint32           time,
3641                         gpointer          data)
3642 {
3643   GtkDragSourceInfo *info = data;
3644   static GdkAtom null_atom = GDK_NONE;
3645   guint target_info;
3646
3647   if (!null_atom)
3648     null_atom = gdk_atom_intern_static_string ("NULL");
3649
3650   switch (sel_info)
3651     {
3652     case TARGET_DELETE:
3653       g_signal_emit_by_name (info->widget,
3654                              "drag_data_delete", 
3655                              info->context);
3656       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3657       break;
3658     case TARGET_MOTIF_SUCCESS:
3659       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3660       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3661       break;
3662     case TARGET_MOTIF_FAILURE:
3663       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3664       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3665       break;
3666     default:
3667       if (info->proxy_dest)
3668         {
3669           /* This is sort of dangerous and needs to be thought
3670            * through better
3671            */
3672           info->proxy_dest->proxy_data = selection_data;
3673           gtk_drag_get_data (info->widget,
3674                              info->proxy_dest->context,
3675                              selection_data->target,
3676                              time);
3677           gtk_main ();
3678           info->proxy_dest->proxy_data = NULL;
3679         }
3680       else
3681         {
3682           if (gtk_target_list_find (info->target_list, 
3683                                     selection_data->target, 
3684                                     &target_info))
3685             {
3686               g_signal_emit_by_name (info->widget, "drag_data_get",
3687                                      info->context,
3688                                      selection_data,
3689                                      target_info,
3690                                      time);
3691             }
3692         }
3693       break;
3694     }
3695 }
3696
3697 static gboolean
3698 gtk_drag_anim_timeout (gpointer data)
3699 {
3700   GtkDragAnim *anim = data;
3701   gint x, y;
3702   gboolean retval;
3703
3704   if (anim->step == anim->n_steps)
3705     {
3706       gtk_drag_source_info_destroy (anim->info);
3707       g_free (anim);
3708
3709       retval = FALSE;
3710     }
3711   else
3712     {
3713       x = (anim->info->start_x * (anim->step + 1) +
3714            anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3715       y = (anim->info->start_y * (anim->step + 1) +
3716            anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3717       if (anim->info->icon_window)
3718         {
3719           GtkWidget *icon_window;
3720           gint hot_x, hot_y;
3721           
3722           gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);   
3723           gtk_window_move (GTK_WINDOW (icon_window), 
3724                            x - hot_x, 
3725                            y - hot_y);
3726         }
3727   
3728       anim->step++;
3729
3730       retval = TRUE;
3731     }
3732
3733   return retval;
3734 }
3735
3736 static void
3737 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3738 {
3739   if (info->icon_window)
3740     {
3741       gtk_widget_hide (info->icon_window);
3742       if (info->destroy_icon)
3743         gtk_widget_destroy (info->icon_window);
3744
3745       if (info->fallback_icon)
3746         {
3747           gtk_widget_destroy (info->fallback_icon);
3748           info->fallback_icon = NULL;
3749         }
3750
3751       g_object_unref (info->icon_window);
3752       info->icon_window = NULL;
3753     }
3754 }
3755
3756 static void
3757 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3758 {
3759   gint i;
3760
3761   for (i = 0; i < n_drag_cursors; i++)
3762     {
3763       if (info->drag_cursors[i] != NULL)
3764         {
3765           gdk_cursor_unref (info->drag_cursors[i]);
3766           info->drag_cursors[i] = NULL;
3767         }
3768     }
3769
3770   gtk_drag_remove_icon (info);
3771
3772   if (info->icon_pixbuf)
3773     {
3774       g_object_unref (info->icon_pixbuf);
3775       info->icon_pixbuf = NULL;
3776     }
3777
3778   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3779                                         gtk_drag_grab_broken_event_cb,
3780                                         info);
3781   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3782                                         gtk_drag_grab_notify_cb,
3783                                         info);
3784   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3785                                         gtk_drag_button_release_cb,
3786                                         info);
3787   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3788                                         gtk_drag_motion_cb,
3789                                         info);
3790   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3791                                         gtk_drag_key_cb,
3792                                         info);
3793   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3794                                         gtk_drag_selection_get,
3795                                         info);
3796
3797   if (!info->proxy_dest)
3798     g_signal_emit_by_name (info->widget, "drag_end", 
3799                            info->context);
3800
3801   if (info->widget)
3802     g_object_unref (info->widget);
3803
3804   gtk_selection_remove_all (info->ipc_widget);
3805   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), NULL);
3806   source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3807   gtk_drag_release_ipc_widget (info->ipc_widget);
3808
3809   gtk_target_list_unref (info->target_list);
3810
3811   gtk_drag_clear_source_info (info->context);
3812   g_object_unref (info->context);
3813
3814   if (info->drop_timeout)
3815     g_source_remove (info->drop_timeout);
3816
3817   if (info->update_idle)
3818     g_source_remove (info->update_idle);
3819
3820   g_free (info);
3821 }
3822
3823 static gboolean
3824 gtk_drag_update_idle (gpointer data)
3825 {
3826   GtkDragSourceInfo *info = data;
3827   GdkWindow *dest_window;
3828   GdkDragProtocol protocol;
3829   GdkAtom selection;
3830
3831   GdkDragAction action;
3832   GdkDragAction possible_actions;
3833   guint32 time;
3834
3835   info->update_idle = 0;
3836     
3837   if (info->last_event)
3838     {
3839       time = gtk_drag_get_event_time (info->last_event);
3840       gtk_drag_get_event_actions (info->last_event,
3841                                   info->button, 
3842                                   info->possible_actions,
3843                                   &action, &possible_actions);
3844       gtk_drag_update_icon (info);
3845       gdk_drag_find_window_for_screen (info->context,
3846                                        info->icon_window ? info->icon_window->window : NULL,
3847                                        info->cur_screen, info->cur_x, info->cur_y,
3848                                        &dest_window, &protocol);
3849       
3850       if (!gdk_drag_motion (info->context, dest_window, protocol,
3851                             info->cur_x, info->cur_y, action, 
3852                             possible_actions,
3853                             time))
3854         {
3855           gdk_event_free ((GdkEvent *)info->last_event);
3856           info->last_event = NULL;
3857         }
3858   
3859       if (dest_window)
3860         g_object_unref (dest_window);
3861       
3862       selection = gdk_drag_get_selection (info->context);
3863       if (selection)
3864         gtk_drag_source_check_selection (info, selection, time);
3865
3866     }
3867
3868   return FALSE;
3869 }
3870
3871 static void
3872 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
3873 {
3874   /* We use an idle lower than GDK_PRIORITY_REDRAW so that exposes
3875    * from the last move can catch up before we move again.
3876    */
3877   if (!info->update_idle)
3878     info->update_idle = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW + 5,
3879                                          gtk_drag_update_idle,
3880                                          info,
3881                                          NULL);
3882 }
3883
3884 /**
3885  * gtk_drag_update:
3886  * @info: DragSourceInfo for the drag
3887  * @screen: new screen
3888  * @x_root: new X position 
3889  * @y_root: new y position
3890  * @event: event received requiring update
3891  * 
3892  * Updates the status of the drag; called when the
3893  * cursor moves or the modifier changes
3894  **/
3895 static void
3896 gtk_drag_update (GtkDragSourceInfo *info,
3897                  GdkScreen         *screen,
3898                  gint               x_root,
3899                  gint               y_root,
3900                  GdkEvent          *event)
3901 {
3902   info->cur_screen = screen;
3903   info->cur_x = x_root;
3904   info->cur_y = y_root;
3905   if (info->last_event)
3906     {
3907       gdk_event_free ((GdkEvent *)info->last_event);
3908       info->last_event = NULL;
3909     }
3910   if (event)
3911     info->last_event = gdk_event_copy ((GdkEvent *)event);
3912
3913   gtk_drag_add_update_idle (info);
3914 }
3915
3916 /*************************************************************
3917  * gtk_drag_end:
3918  *     Called when the user finishes to drag, either by
3919  *     releasing the mouse, or by pressing Esc.
3920  *   arguments:
3921  *     info: Source info for the drag
3922  *     time: Timestamp for ending the drag
3923  *   results:
3924  *************************************************************/
3925
3926 static void
3927 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3928 {
3929   GdkEvent *send_event;
3930   GtkWidget *source_widget = info->widget;
3931   GdkDisplay *display = gtk_widget_get_display (source_widget);
3932
3933   if (info->update_idle)
3934     {
3935       g_source_remove (info->update_idle);
3936       info->update_idle = 0;
3937     }
3938   
3939   if (info->last_event)
3940     {
3941       gdk_event_free (info->last_event);
3942       info->last_event = NULL;
3943     }
3944   
3945   info->have_grab = FALSE;
3946   
3947   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3948                                         gtk_drag_grab_broken_event_cb,
3949                                         info);
3950   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3951                                         gtk_drag_grab_notify_cb,
3952                                         info);
3953   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3954                                         gtk_drag_button_release_cb,
3955                                         info);
3956   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3957                                         gtk_drag_motion_cb,
3958                                         info);
3959   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3960                                         gtk_drag_key_cb,
3961                                         info);
3962
3963   gdk_display_pointer_ungrab (display, time);
3964   gdk_display_keyboard_ungrab (display, time);
3965   gtk_grab_remove (info->ipc_widget);
3966
3967   /* Send on a release pair to the original 
3968    * widget to convince it to release its grab. We need to
3969    * call gtk_propagate_event() here, instead of 
3970    * gtk_widget_event() because widget like GtkList may
3971    * expect propagation.
3972    */
3973
3974   send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3975   send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3976   send_event->button.send_event = TRUE;
3977   send_event->button.time = time;
3978   send_event->button.x = 0;
3979   send_event->button.y = 0;
3980   send_event->button.axes = NULL;
3981   send_event->button.state = 0;
3982   send_event->button.button = info->button;
3983   send_event->button.device = gdk_display_get_core_pointer (display);
3984   send_event->button.x_root = 0;
3985   send_event->button.y_root = 0;
3986
3987   gtk_propagate_event (source_widget, send_event);
3988   gdk_event_free (send_event);
3989 }
3990
3991 /*************************************************************
3992  * gtk_drag_cancel:
3993  *    Called on cancellation of a drag, either by the user
3994  *    or programmatically.
3995  *   arguments:
3996  *     info: Source info for the drag
3997  *     time: Timestamp for ending the drag
3998  *   results:
3999  *************************************************************/
4000
4001 static void
4002 gtk_drag_cancel (GtkDragSourceInfo *info, GtkDragResult result, guint32 time)
4003 {
4004   gtk_drag_end (info, time);
4005   gdk_drag_abort (info->context, time);
4006   gtk_drag_drop_finished (info, result, time);
4007 }
4008
4009 /*************************************************************
4010  * gtk_drag_motion_cb:
4011  *     "motion_notify_event" callback during drag.
4012  *   arguments:
4013  *     
4014  *   results:
4015  *************************************************************/
4016
4017 static gboolean
4018 gtk_drag_motion_cb (GtkWidget      *widget, 
4019                     GdkEventMotion *event, 
4020                     gpointer        data)
4021 {
4022   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4023   GdkScreen *screen;
4024   gint x_root, y_root;
4025
4026   if (event->is_hint)
4027     {
4028       GdkDisplay *display = gtk_widget_get_display (widget);
4029       
4030       gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
4031       event->x_root = x_root;
4032       event->y_root = y_root;
4033     }
4034   else
4035     screen = gdk_event_get_screen ((GdkEvent *)event);
4036
4037   gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
4038
4039   return TRUE;
4040 }
4041
4042 /*************************************************************
4043  * gtk_drag_key_cb:
4044  *     "key_press/release_event" callback during drag.
4045  *   arguments:
4046  *     
4047  *   results:
4048  *************************************************************/
4049
4050 #define BIG_STEP 20
4051 #define SMALL_STEP 1
4052
4053 static gboolean
4054 gtk_drag_key_cb (GtkWidget         *widget, 
4055                  GdkEventKey       *event, 
4056                  gpointer           data)
4057 {
4058   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4059   GdkModifierType state;
4060   GdkWindow *root_window;
4061   gint dx, dy;
4062
4063   dx = dy = 0;
4064   state = event->state & gtk_accelerator_get_default_mod_mask ();
4065
4066   if (event->type == GDK_KEY_PRESS)
4067     {
4068       switch (event->keyval)
4069         {
4070         case GDK_Escape:
4071           gtk_drag_cancel (info, GTK_DRAG_RESULT_USER_CANCELLED, event->time);
4072           return TRUE;
4073
4074         case GDK_space:
4075         case GDK_Return:
4076         case GDK_KP_Enter:
4077         case GDK_KP_Space:
4078           gtk_drag_end (info, event->time);
4079           gtk_drag_drop (info, event->time);
4080           return TRUE;
4081
4082         case GDK_Up:
4083         case GDK_KP_Up:
4084           dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4085           break;
4086           
4087         case GDK_Down:
4088         case GDK_KP_Down:
4089           dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4090           break;
4091           
4092         case GDK_Left:
4093         case GDK_KP_Left:
4094           dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4095           break;
4096           
4097         case GDK_Right:
4098         case GDK_KP_Right:
4099           dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4100           break;
4101         }
4102       
4103     }
4104
4105   /* Now send a "motion" so that the modifier state is updated */
4106
4107   /* The state is not yet updated in the event, so we need
4108    * to query it here. We could use XGetModifierMapping, but
4109    * that would be overkill.
4110    */
4111   root_window = gtk_widget_get_root_window (widget);
4112   gdk_window_get_pointer (root_window, NULL, NULL, &state);
4113   event->state = state;
4114
4115   if (dx != 0 || dy != 0)
4116     {
4117       info->cur_x += dx;
4118       info->cur_y += dy;
4119       gdk_display_warp_pointer (gtk_widget_get_display (widget), 
4120                                 gtk_widget_get_screen (widget), 
4121                                 info->cur_x, info->cur_y);
4122     }
4123
4124   gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
4125
4126   return TRUE;
4127 }
4128
4129 static gboolean
4130 gtk_drag_grab_broken_event_cb (GtkWidget          *widget,
4131                                GdkEventGrabBroken *event,
4132                                gpointer            data)
4133 {
4134   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4135
4136   /* Don't cancel if we break the implicit grab from the initial button_press.
4137    * Also, don't cancel if we re-grab on the widget or on our IPC window, for
4138    * example, when changing the drag cursor.
4139    */
4140   if (event->implicit
4141       || event->grab_window == info->widget->window
4142       || event->grab_window == info->ipc_widget->window)
4143     return FALSE;
4144
4145   gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4146   return TRUE;
4147 }
4148
4149 static void
4150 gtk_drag_grab_notify_cb (GtkWidget        *widget,
4151                          gboolean          was_grabbed,
4152                          gpointer          data)
4153 {
4154   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4155
4156   if (!was_grabbed)
4157     {
4158       /* We have to block callbacks to avoid recursion here, because
4159          gtk_drag_cancel calls gtk_grab_remove (via gtk_drag_end) */
4160       g_signal_handlers_block_by_func (widget, gtk_drag_grab_notify_cb, data);
4161       gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4162       g_signal_handlers_unblock_by_func (widget, gtk_drag_grab_notify_cb, data);
4163     }
4164 }
4165
4166
4167 /*************************************************************
4168  * gtk_drag_button_release_cb:
4169  *     "button_release_event" callback during drag.
4170  *   arguments:
4171  *     
4172  *   results:
4173  *************************************************************/
4174
4175 static gboolean
4176 gtk_drag_button_release_cb (GtkWidget      *widget, 
4177                             GdkEventButton *event, 
4178                             gpointer        data)
4179 {
4180   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4181
4182   if (event->button != info->button)
4183     return FALSE;
4184
4185   if ((info->context->action != 0) && (info->context->dest_window != NULL))
4186     {
4187       gtk_drag_end (info, event->time);
4188       gtk_drag_drop (info, event->time);
4189     }
4190   else
4191     {
4192       gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
4193     }
4194
4195   return TRUE;
4196 }
4197
4198 static gboolean
4199 gtk_drag_abort_timeout (gpointer data)
4200 {
4201   GtkDragSourceInfo *info = data;
4202   guint32 time = GDK_CURRENT_TIME;
4203
4204   if (info->proxy_dest)
4205     time = info->proxy_dest->proxy_drop_time;
4206
4207   info->drop_timeout = 0;
4208   gtk_drag_drop_finished (info, GTK_DRAG_RESULT_TIMEOUT_EXPIRED, time);
4209   
4210   return FALSE;
4211 }
4212
4213 /**
4214  * gtk_drag_check_threshold:
4215  * @widget: a #GtkWidget
4216  * @start_x: X coordinate of start of drag
4217  * @start_y: Y coordinate of start of drag
4218  * @current_x: current X coordinate
4219  * @current_y: current Y coordinate
4220  * 
4221  * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
4222  * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
4223  * should trigger the beginning of a drag-and-drop operation.
4224  *
4225  * Return Value: %TRUE if the drag threshold has been passed.
4226  **/
4227 gboolean
4228 gtk_drag_check_threshold (GtkWidget *widget,
4229                           gint       start_x,
4230                           gint       start_y,
4231                           gint       current_x,
4232                           gint       current_y)
4233 {
4234   gint drag_threshold;
4235
4236   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
4237
4238   g_object_get (gtk_widget_get_settings (widget),
4239                 "gtk-dnd-drag-threshold", &drag_threshold,
4240                 NULL);
4241   
4242   return (ABS (current_x - start_x) > drag_threshold ||
4243           ABS (current_y - start_y) > drag_threshold);
4244 }
4245
4246 #define __GTK_DND_C__
4247 #include "gtkaliasdef.c"