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