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