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