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