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