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