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