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