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