]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd.c
Make gdkx.h the only installed header from gdk/x11. All structures in
[~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 (!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
1065  * 
1066  * Looks for a match between @context->targets and the
1067  * @dest_target_list, returning the first matching target, otherwise
1068  * returning %GDK_NONE. @dest_target_list should usually be the return
1069  * value from gtk_drag_dest_get_target_list(), but some widgets may
1070  * have different valid targets for different parts of the widget; in
1071  * that case, they will have to implement a drag_motion handler that
1072  * passes the correct target list to this function.
1073  * 
1074  * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1075  **/
1076 GdkAtom
1077 gtk_drag_dest_find_target (GtkWidget      *widget,
1078                            GdkDragContext *context,
1079                            GtkTargetList  *target_list)
1080 {
1081   GList *tmp_target;
1082   GList *tmp_source = NULL;
1083   GtkWidget *source_widget = gtk_drag_get_source_widget (context);
1084
1085   if (target_list == NULL)
1086     return GDK_NONE;
1087   
1088   tmp_target = target_list->list;
1089   while (tmp_target)
1090     {
1091       GtkTargetPair *pair = tmp_target->data;
1092       tmp_source = context->targets;
1093       while (tmp_source)
1094         {
1095           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1096             {
1097               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1098                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1099                 return pair->target;
1100               else
1101                 break;
1102             }
1103           tmp_source = tmp_source->next;
1104         }
1105       tmp_target = tmp_target->next;
1106     }
1107
1108   return GDK_NONE;
1109 }
1110
1111 static void
1112 gtk_drag_selection_received (GtkWidget        *widget,
1113                              GtkSelectionData *selection_data,
1114                              guint32           time,
1115                              gpointer          data)
1116 {
1117   GdkDragContext *context;
1118   GtkDragDestInfo *info;
1119   GtkWidget *drop_widget;
1120
1121   drop_widget = data;
1122
1123   context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1124   info = gtk_drag_get_dest_info (context, FALSE);
1125
1126   if (info->proxy_data && 
1127       info->proxy_data->target == selection_data->target)
1128     {
1129       gtk_selection_data_set (info->proxy_data,
1130                               selection_data->type,
1131                               selection_data->format,
1132                               selection_data->data,
1133                               selection_data->length);
1134       gtk_main_quit ();
1135       return;
1136     }
1137
1138   if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1139     {
1140       gtk_drag_finish (context, TRUE, FALSE, time);
1141     }
1142   else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1143            (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1144     {
1145       /* Do nothing */
1146     }
1147   else
1148     {
1149       GtkDragDestSite *site;
1150
1151       site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1152
1153       if (site && site->target_list)
1154         {
1155           guint target_info;
1156
1157           if (gtk_target_list_find (site->target_list, 
1158                                     selection_data->target,
1159                                     &target_info))
1160             {
1161               if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1162                   selection_data->length >= 0)
1163                 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget), 
1164                                          "drag_data_received",
1165                                          context, info->drop_x, info->drop_y,
1166                                          selection_data, 
1167                                          target_info, time);
1168             }
1169         }
1170       else
1171         {
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, 0, time);
1176         }
1177       
1178       if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1179         {
1180
1181           gtk_drag_finish (context, 
1182                            (selection_data->length >= 0),
1183                            (context->action == GDK_ACTION_MOVE),
1184                            time);
1185         }
1186       
1187       gtk_widget_unref (drop_widget);
1188     }
1189
1190   gtk_signal_disconnect_by_func (GTK_OBJECT (widget), 
1191                                  GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1192                                  data);
1193   
1194   gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1195   gdk_drag_context_unref (context);
1196
1197   gtk_drag_release_ipc_widget (widget);
1198 }
1199
1200 /*************************************************************
1201  * gtk_drag_find_widget:
1202  *     Recursive callback used to locate widgets for 
1203  *     DRAG_MOTION and DROP_START events.
1204  *   arguments:
1205  *     
1206  *   results:
1207  *************************************************************/
1208
1209 static void
1210 gtk_drag_find_widget (GtkWidget       *widget,
1211                       GtkDragFindData *data)
1212 {
1213   GtkAllocation new_allocation;
1214   gint x_offset = 0;
1215   gint y_offset = 0;
1216
1217   new_allocation = widget->allocation;
1218
1219   if (data->found || !GTK_WIDGET_MAPPED (widget))
1220     return;
1221
1222   /* Note that in the following code, we only count the
1223    * position as being inside a WINDOW widget if it is inside
1224    * widget->window; points that are outside of widget->window
1225    * but within the allocation are not counted. This is consistent
1226    * with the way we highlight drag targets.
1227    */
1228   if (!GTK_WIDGET_NO_WINDOW (widget))
1229     {
1230       new_allocation.x = 0;
1231       new_allocation.y = 0;
1232     }
1233   
1234   if (widget->parent)
1235     {
1236       GdkWindow *window = widget->window;
1237       while (window != widget->parent->window)
1238         {
1239           gint tx, ty, twidth, theight;
1240           gdk_window_get_size (window, &twidth, &theight);
1241
1242           if (new_allocation.x < 0)
1243             {
1244               new_allocation.width += new_allocation.x;
1245               new_allocation.x = 0;
1246             }
1247           if (new_allocation.y < 0)
1248             {
1249               new_allocation.height += new_allocation.y;
1250               new_allocation.y = 0;
1251             }
1252           if (new_allocation.x + new_allocation.width > twidth)
1253             new_allocation.width = twidth - new_allocation.x;
1254           if (new_allocation.y + new_allocation.height > theight)
1255             new_allocation.height = theight - new_allocation.y;
1256
1257           gdk_window_get_position (window, &tx, &ty);
1258           new_allocation.x += tx;
1259           x_offset += tx;
1260           new_allocation.y += ty;
1261           y_offset += ty;
1262           
1263           window = gdk_window_get_parent (window);
1264         }
1265     }
1266
1267   if (data->toplevel ||
1268       ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1269        (data->x < new_allocation.x + new_allocation.width) && 
1270        (data->y < new_allocation.y + new_allocation.height)))
1271     {
1272       /* First, check if the drag is in a valid drop site in
1273        * one of our children 
1274        */
1275       if (GTK_IS_CONTAINER (widget))
1276         {
1277           GtkDragFindData new_data = *data;
1278           
1279           new_data.x -= x_offset;
1280           new_data.y -= y_offset;
1281           new_data.found = FALSE;
1282           new_data.toplevel = FALSE;
1283           
1284           gtk_container_forall (GTK_CONTAINER (widget),
1285                                 (GtkCallback)gtk_drag_find_widget,
1286                                 &new_data);
1287           
1288           data->found = new_data.found;
1289         }
1290
1291       /* If not, and this widget is registered as a drop site, check to
1292        * emit "drag_motion" to check if we are actually in
1293        * a drop site.
1294        */
1295       if (!data->found &&
1296           gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1297         {
1298           data->found = data->callback (widget,
1299                                         data->context,
1300                                         data->x - new_allocation.x,
1301                                         data->y - new_allocation.y,
1302                                         data->time);
1303           /* If so, send a "drag_leave" to the last widget */
1304           if (data->found)
1305             {
1306               if (data->info->widget && data->info->widget != widget)
1307                 {
1308                   gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1309                 }
1310               data->info->widget = widget;
1311             }
1312         }
1313     }
1314 }
1315
1316 static void
1317 gtk_drag_proxy_begin (GtkWidget       *widget, 
1318                       GtkDragDestInfo *dest_info,
1319                       guint32          time)
1320 {
1321   GtkDragSourceInfo *source_info;
1322   GList *tmp_list;
1323   GdkDragContext *context;
1324   GtkWidget *ipc_widget;
1325
1326   if (dest_info->proxy_source)
1327     {
1328       gdk_drag_abort (dest_info->proxy_source->context, time);
1329       gtk_drag_source_info_destroy (dest_info->proxy_source);
1330       dest_info->proxy_source = NULL;
1331     }
1332   
1333   ipc_widget = gtk_drag_get_ipc_widget ();
1334   context = gdk_drag_begin (ipc_widget->window,
1335                             dest_info->context->targets);
1336
1337   source_info = gtk_drag_get_source_info (context, TRUE);
1338
1339   source_info->ipc_widget = ipc_widget;
1340   source_info->widget = gtk_widget_ref (widget);
1341
1342   source_info->target_list = gtk_target_list_new (NULL, 0);
1343   tmp_list = dest_info->context->targets;
1344   while (tmp_list)
1345     {
1346       gtk_target_list_add (source_info->target_list, 
1347                            GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1348       tmp_list = tmp_list->next;
1349     }
1350
1351   source_info->proxy_dest = dest_info;
1352   
1353   gtk_signal_connect (GTK_OBJECT (ipc_widget), 
1354                       "selection_get",
1355                       GTK_SIGNAL_FUNC (gtk_drag_selection_get), 
1356                       source_info);
1357   
1358   dest_info->proxy_source = source_info;
1359 }
1360
1361 static void
1362 gtk_drag_dest_info_destroy (gpointer data)
1363 {
1364   GtkDragDestInfo *info = data;
1365
1366   g_free (info);
1367 }
1368
1369 static GtkDragDestInfo *
1370 gtk_drag_get_dest_info (GdkDragContext *context,
1371                         gboolean        create)
1372 {
1373   GtkDragDestInfo *info;
1374   static GQuark info_quark = 0;
1375   if (!info_quark)
1376     info_quark = g_quark_from_static_string ("gtk-dest-info");
1377   
1378   info = g_object_get_qdata (G_OBJECT (context), info_quark);
1379   if (!info && create)
1380     {
1381       info = g_new (GtkDragDestInfo, 1);
1382       info->widget = NULL;
1383       info->context = context;
1384       info->proxy_source = NULL;
1385       info->proxy_data = NULL;
1386       info->dropped = FALSE;
1387       info->proxy_drop_wait = FALSE;
1388       g_object_set_qdata_full (G_OBJECT (context), info_quark,
1389                                info, gtk_drag_dest_info_destroy);
1390     }
1391
1392   return info;
1393 }
1394
1395 static GQuark dest_info_quark = 0;
1396
1397 static GtkDragSourceInfo *
1398 gtk_drag_get_source_info (GdkDragContext *context,
1399                           gboolean        create)
1400 {
1401   GtkDragSourceInfo *info;
1402   if (!dest_info_quark)
1403     dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1404   
1405   info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1406   if (!info && create)
1407     {
1408       info = g_new0 (GtkDragSourceInfo, 1);
1409       info->context = context;
1410       g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1411     }
1412
1413   return info;
1414 }
1415
1416 static void
1417 gtk_drag_clear_source_info (GdkDragContext *context)
1418 {
1419   g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1420 }
1421
1422 static void
1423 gtk_drag_dest_realized (GtkWidget *widget)
1424 {
1425   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1426
1427   if (GTK_WIDGET_TOPLEVEL (toplevel))
1428     gdk_window_register_dnd (toplevel->window);
1429 }
1430
1431 static void
1432 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1433                                  GtkWidget *previous_toplevel)
1434 {
1435   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1436
1437   if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1438     gdk_window_register_dnd (toplevel->window);
1439 }
1440
1441 static void
1442 gtk_drag_dest_site_destroy (gpointer data)
1443 {
1444   GtkDragDestSite *site = data;
1445
1446   if (site->target_list)
1447     gtk_target_list_unref (site->target_list);
1448
1449   g_free (site);
1450 }
1451
1452 /*
1453  * Default drag handlers
1454  */
1455 static void  
1456 gtk_drag_dest_leave (GtkWidget      *widget,
1457                      GdkDragContext *context,
1458                      guint           time)
1459 {
1460   GtkDragDestSite *site;
1461
1462   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1463   g_return_if_fail (site != NULL);
1464
1465   if (site->do_proxy)
1466     {
1467       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1468
1469       if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1470         {
1471           gdk_drag_abort (info->proxy_source->context, time);
1472           gtk_drag_source_info_destroy (info->proxy_source);
1473           info->proxy_source = NULL;
1474         }
1475       
1476       return;
1477     }
1478   else
1479     {
1480       if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1481         gtk_drag_unhighlight (widget);
1482
1483       if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1484         gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1485                                  context, time);
1486       
1487       site->have_drag = FALSE;
1488     }
1489 }
1490
1491 static gboolean
1492 gtk_drag_dest_motion (GtkWidget      *widget,
1493                       GdkDragContext *context,
1494                       gint            x,
1495                       gint            y,
1496                       guint           time)
1497 {
1498   GtkDragDestSite *site;
1499   GdkDragAction action = 0;
1500   gboolean retval;
1501
1502   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1503   g_return_val_if_fail (site != NULL, FALSE);
1504
1505   if (site->do_proxy)
1506     {
1507       GdkAtom selection;
1508       GdkEvent *current_event;
1509       GdkWindow *dest_window;
1510       GdkDragProtocol proto;
1511         
1512       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1513
1514       if (!info->proxy_source || info->proxy_source->widget != widget)
1515         gtk_drag_proxy_begin (widget, info, time);
1516
1517       current_event = gtk_get_current_event ();
1518
1519       if (site->proxy_window)
1520         {
1521           dest_window = site->proxy_window;
1522           proto = site->proxy_protocol;
1523         }
1524       else
1525         {
1526           gdk_drag_find_window (info->proxy_source->context,
1527                                 NULL,
1528                                 current_event->dnd.x_root, 
1529                                 current_event->dnd.y_root,
1530                                 &dest_window, &proto);
1531         }
1532       
1533       gdk_drag_motion (info->proxy_source->context, 
1534                        dest_window, proto,
1535                        current_event->dnd.x_root, 
1536                        current_event->dnd.y_root, 
1537                        context->suggested_action, 
1538                        context->actions, time);
1539
1540       if (!site->proxy_window && dest_window)
1541         gdk_window_unref (dest_window);
1542
1543       selection = gdk_drag_get_selection (info->proxy_source->context);
1544       if (selection && 
1545           selection != gdk_drag_get_selection (info->context))
1546         gtk_drag_source_check_selection (info->proxy_source, selection, time);
1547
1548       gdk_event_free (current_event);
1549       
1550       return TRUE;
1551     }
1552
1553   if (site->flags & GTK_DEST_DEFAULT_MOTION)
1554     {
1555       if (context->suggested_action & site->actions)
1556         action = context->suggested_action;
1557       else
1558         {
1559           gint i;
1560           
1561           for (i=0; i<8; i++)
1562             {
1563               if ((site->actions & (1 << i)) &&
1564                   (context->actions & (1 << i)))
1565                 {
1566                   action = (1 << i);
1567                   break;
1568                 }
1569             }
1570         }
1571       
1572       if (action && gtk_drag_dest_find_target (widget, context, site->target_list))
1573         {
1574           if (!site->have_drag)
1575             {
1576               site->have_drag = TRUE;
1577               if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1578                 gtk_drag_highlight (widget);
1579             }
1580           
1581           gdk_drag_status (context, action, time);
1582         }
1583       else
1584         {
1585           gdk_drag_status (context, 0, time);
1586           return TRUE;
1587         }
1588     }
1589
1590   gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1591                            context, x, y, time, &retval);
1592
1593   return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1594 }
1595
1596 static gboolean
1597 gtk_drag_dest_drop (GtkWidget        *widget,
1598                     GdkDragContext   *context,
1599                     gint              x,
1600                     gint              y,
1601                     guint             time)
1602 {
1603   GtkDragDestSite *site;
1604   GtkDragDestInfo *info;
1605
1606   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1607   g_return_val_if_fail (site != NULL, FALSE);
1608
1609   info = gtk_drag_get_dest_info (context, FALSE);
1610   g_return_val_if_fail (info != NULL, FALSE);
1611
1612   info->drop_x = x;
1613   info->drop_y = y;
1614
1615   if (site->do_proxy)
1616     {
1617       if (info->proxy_source || 
1618           (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1619         {
1620           gtk_drag_drop (info->proxy_source, time);
1621         }
1622       else
1623         {
1624           /* We need to synthesize a motion event, wait for a status,
1625            * and, if we get a good one, do a drop.
1626            */
1627           
1628           GdkEvent *current_event;
1629           GdkAtom selection;
1630           GdkWindow *dest_window;
1631           GdkDragProtocol proto;
1632           
1633           gtk_drag_proxy_begin (widget, info, time);
1634           info->proxy_drop_wait = TRUE;
1635           info->proxy_drop_time = time;
1636           
1637           current_event = gtk_get_current_event ();
1638
1639           if (site->proxy_window)
1640             {
1641               dest_window = site->proxy_window;
1642               proto = site->proxy_protocol;
1643             }
1644           else
1645             {
1646               gdk_drag_find_window (info->proxy_source->context,
1647                                     NULL,
1648                                     current_event->dnd.x_root, 
1649                                     current_event->dnd.y_root,
1650                                     &dest_window, &proto);
1651             }
1652
1653           gdk_drag_motion (info->proxy_source->context, 
1654                            dest_window, proto,
1655                            current_event->dnd.x_root, 
1656                            current_event->dnd.y_root, 
1657                            context->suggested_action, 
1658                            context->actions, time);
1659
1660           if (!site->proxy_window && dest_window)
1661             gdk_window_unref (dest_window);
1662
1663           selection = gdk_drag_get_selection (info->proxy_source->context);
1664           if (selection && 
1665               selection != gdk_drag_get_selection (info->context))
1666             gtk_drag_source_check_selection (info->proxy_source, selection, time);
1667
1668           gdk_event_free (current_event);
1669         }
1670
1671       return TRUE;
1672     }
1673   else
1674     {
1675       gboolean retval;
1676
1677       if (site->flags & GTK_DEST_DEFAULT_DROP)
1678         {
1679           GdkAtom target = gtk_drag_dest_find_target (widget, context, site->target_list);
1680       
1681           if (target == GDK_NONE)
1682             return FALSE;
1683           
1684           gtk_drag_get_data (widget, context, target, time);
1685         }
1686
1687       gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1688                                context, x, y, time, &retval);
1689
1690       return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1691     }
1692 }
1693
1694 /***************
1695  * Source side *
1696  ***************/
1697
1698 /*************************************************************
1699  * gtk_drag_begin: Start a drag operation
1700  *     
1701  *   arguments:
1702  *     widget:   Widget from which drag starts
1703  *     handlers: List of handlers to supply the data for the drag
1704  *     button:   Button user used to start drag
1705  *     time:     Time of event starting drag
1706  *
1707  *   results:
1708  *************************************************************/
1709
1710 GdkDragContext *
1711 gtk_drag_begin (GtkWidget         *widget,
1712                 GtkTargetList     *target_list,
1713                 GdkDragAction      actions,
1714                 gint               button,
1715                 GdkEvent          *event)
1716 {
1717   GtkDragSourceInfo *info;
1718   GList *targets = NULL;
1719   GList *tmp_list;
1720   guint32 time = GDK_CURRENT_TIME;
1721   GdkDragAction possible_actions, suggested_action;
1722   GdkDragContext *context;
1723   GtkWidget *ipc_widget;
1724
1725   g_return_val_if_fail (widget != NULL, NULL);
1726   g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1727   g_return_val_if_fail (target_list != NULL, NULL);
1728
1729   if (event)
1730     time = gdk_event_get_time (event);
1731
1732   tmp_list = g_list_last (target_list->list);
1733   while (tmp_list)
1734     {
1735       GtkTargetPair *pair = tmp_list->data;
1736       targets = g_list_prepend (targets, 
1737                                 GINT_TO_POINTER (pair->target));
1738       tmp_list = tmp_list->prev;
1739     }
1740
1741   ipc_widget = gtk_drag_get_ipc_widget ();
1742   source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1743
1744   context = gdk_drag_begin (ipc_widget->window, targets);
1745   g_list_free (targets);
1746   
1747   info = gtk_drag_get_source_info (context, TRUE);
1748   
1749   info->ipc_widget = ipc_widget;
1750   gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1751
1752   info->widget = gtk_widget_ref (widget);
1753
1754   
1755   info->button = button;
1756   info->target_list = target_list;
1757   gtk_target_list_ref (target_list);
1758
1759   info->possible_actions = actions;
1760
1761   info->cursor = NULL;
1762   info->status = GTK_DRAG_STATUS_DRAG;
1763   info->last_event = NULL;
1764   info->selections = NULL;
1765   info->icon_window = NULL;
1766   info->destroy_icon = FALSE;
1767
1768   gtk_drag_get_event_actions (event, info->button, actions,
1769                               &suggested_action, &possible_actions);
1770
1771   info->cursor = gtk_drag_get_cursor (suggested_action);
1772
1773   /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1774    * the drag icon, it will be in the right place
1775    */
1776   if (event && event->type == GDK_MOTION_NOTIFY)
1777     {
1778       info->cur_x = event->motion.x_root;
1779       info->cur_y = event->motion.y_root;
1780     }
1781   else 
1782     {
1783       gint x, y;
1784       gdk_window_get_pointer (GDK_ROOT_PARENT (), &x, &y, NULL);
1785
1786       info->cur_x = x;
1787       info->cur_y = y;
1788     }
1789
1790   gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1791                            info->context);
1792   
1793   if (event && event->type == GDK_MOTION_NOTIFY)
1794     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1795
1796   info->start_x = info->cur_x;
1797   info->start_y = info->cur_y;
1798
1799   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1800                       GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1801   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1802                       GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1803   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_press_event",
1804                       GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1805   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_release_event",
1806                       GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1807   gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1808                       GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1809
1810   /* We use a GTK grab here to override any grabs that the widget
1811    * we are dragging from might have held
1812    */
1813   gtk_grab_add (info->ipc_widget);
1814   if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
1815                         GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1816                         GDK_BUTTON_RELEASE_MASK, NULL,
1817                         info->cursor, time) == 0)
1818     {
1819       if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
1820         {
1821           /* FIXME: This should be cleaned up... */
1822           GdkEventButton ev;
1823
1824           ev.time = time;
1825           ev.type = GDK_BUTTON_RELEASE;
1826           ev.button = info->button;
1827
1828           gtk_drag_button_release_cb (widget, &ev, info);
1829
1830           return NULL;
1831         }
1832     }
1833
1834   return info->context;
1835 }
1836
1837 /*************************************************************
1838  * gtk_drag_source_set:
1839  *     Register a drop site, and possibly add default behaviors.
1840  *   arguments:
1841  *     widget:
1842  *     start_button_mask: Mask of allowed buttons to start drag
1843  *     targets:           Table of targets for this source
1844  *     n_targets:
1845  *     actions:           Actions allowed for this source
1846  *   results:
1847  *************************************************************/
1848
1849 void 
1850 gtk_drag_source_set (GtkWidget            *widget,
1851                      GdkModifierType       start_button_mask,
1852                      const GtkTargetEntry *targets,
1853                      gint                  n_targets,
1854                      GdkDragAction         actions)
1855 {
1856   GtkDragSourceSite *site;
1857
1858   g_return_if_fail (widget != NULL);
1859
1860   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1861
1862   gtk_widget_add_events (widget,
1863                          gtk_widget_get_events (widget) |
1864                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1865                          GDK_BUTTON_MOTION_MASK);
1866
1867   if (site)
1868     {
1869       if (site->target_list)
1870         gtk_target_list_unref (site->target_list);
1871     }
1872   else
1873     {
1874       site = g_new0 (GtkDragSourceSite, 1);
1875
1876       site->icon_type = GTK_IMAGE_EMPTY;
1877       
1878       gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1879                           GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1880                           site);
1881       gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1882                           GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1883                           site);
1884       
1885       gtk_object_set_data_full (GTK_OBJECT (widget),
1886                                 "gtk-site-data", 
1887                                 site, gtk_drag_source_site_destroy);
1888     }
1889
1890   site->start_button_mask = start_button_mask;
1891
1892   if (targets)
1893     site->target_list = gtk_target_list_new (targets, n_targets);
1894   else
1895     site->target_list = NULL;
1896
1897   site->actions = actions;
1898
1899 }
1900
1901 /*************************************************************
1902  * gtk_drag_source_unset
1903  *     Unregister this widget as a drag source.
1904  *   arguments:
1905  *     widget:
1906  *   results:
1907  *************************************************************/
1908
1909 void 
1910 gtk_drag_source_unset (GtkWidget        *widget)
1911 {
1912   GtkDragSourceSite *site;
1913
1914   g_return_if_fail (widget != NULL);
1915
1916   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1917
1918   if (site)
1919     {
1920       gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1921       gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1922     }
1923 }
1924
1925 static void
1926 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
1927 {
1928   switch (site->icon_type)
1929     {
1930     case GTK_IMAGE_EMPTY:
1931       break;
1932     case GTK_IMAGE_PIXMAP:
1933       if (site->icon_data.pixmap.pixmap)
1934         gdk_pixmap_unref (site->icon_data.pixmap.pixmap);
1935       if (site->icon_mask)
1936         gdk_pixmap_unref (site->icon_mask);
1937       break;
1938     case GTK_IMAGE_PIXBUF:
1939       g_object_unref (G_OBJECT (site->icon_data.pixbuf.pixbuf));
1940       break;
1941     case GTK_IMAGE_STOCK:
1942       g_free (G_OBJECT (site->icon_data.stock.stock_id));
1943       break;
1944     default:
1945       g_assert_not_reached();
1946       break;
1947     }
1948   site->icon_type = GTK_IMAGE_EMPTY;
1949   
1950   if (site->colormap)
1951     gdk_colormap_unref (site->colormap);
1952   site->colormap = NULL;
1953 }
1954
1955 /**
1956  * gtk_drag_source_set_icon:
1957  * @widget: a #GtkWidget
1958  * @colormap: the colormap of the icon
1959  * @pixmap: the image data for the icon
1960  * @mask: the transparency mask for an image.
1961  * 
1962  * Sets the icon that will be used for drags from a particular widget
1963  * from a pixmap/mask. GTK+ retains a reference count for the
1964  * arguments, and will release them when they are no longer needed.
1965  * Use gtk_drag_source_set_icon_pixbuf() instead.
1966  **/
1967 void 
1968 gtk_drag_source_set_icon (GtkWidget     *widget,
1969                           GdkColormap   *colormap,
1970                           GdkPixmap     *pixmap,
1971                           GdkBitmap     *mask)
1972 {
1973   GtkDragSourceSite *site;
1974
1975   g_return_if_fail (widget != NULL);
1976   g_return_if_fail (GDK_IS_COLORMAP (colormap));
1977   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
1978   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
1979
1980   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1981   g_return_if_fail (site != NULL);
1982   
1983   gdk_colormap_ref (colormap);
1984   gdk_pixmap_ref (pixmap);
1985   if (mask)
1986     gdk_pixmap_ref (mask);
1987
1988   gtk_drag_source_unset_icon (site);
1989
1990   site->icon_type = GTK_IMAGE_PIXMAP;
1991   
1992   site->icon_data.pixmap.pixmap = pixmap;
1993   site->icon_mask = mask;
1994   site->colormap = colormap;
1995 }
1996
1997 /**
1998  * gtk_drag_source_set_icon_pixbuf:
1999  * @widget: a #GtkWidget
2000  * @pixbuf: the #GdkPixbuf for the drag icon
2001  * 
2002  * Sets the icon that will be used for drags from a particular widget
2003  * from a #GdkPixbuf. GTK+ retains a reference count @pixbuf.
2004  * and will release it when it is no longer needed.
2005  **/
2006 void 
2007 gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
2008                                  GdkPixbuf   *pixbuf)
2009 {
2010   GtkDragSourceSite *site;
2011
2012   g_return_if_fail (widget != NULL);
2013   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2014
2015   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2016   g_return_if_fail (site != NULL);
2017   
2018   gdk_pixbuf_ref (pixbuf);
2019
2020   gtk_drag_source_unset_icon (site);
2021
2022   site->icon_type = GTK_IMAGE_PIXBUF;
2023   site->icon_data.pixbuf.pixbuf = pixbuf;
2024 }
2025
2026 /**
2027  * gtk_drag_source_set_icon_stock:
2028  * @widget: a #GtkWidget
2029  * @stock: the ID of the stock icon to use..
2030  * @size: size at which to render the stock icon
2031  *
2032  * Sets the icon that will be used for drags from a particular to
2033  * a stock icon. 
2034  **/
2035 void 
2036 gtk_drag_source_set_icon_stock (GtkWidget   *widget,
2037                                 const gchar *stock_id)
2038 {
2039   GtkDragSourceSite *site;
2040
2041   g_return_if_fail (widget != NULL);
2042   g_return_if_fail (stock_id != NULL);
2043
2044   site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2045   g_return_if_fail (site != NULL);
2046   
2047   gtk_drag_source_unset_icon (site);
2048
2049   site->icon_data.stock.stock_id = g_strdup (stock_id);
2050 }
2051
2052 static void 
2053 gtk_drag_set_icon_window (GdkDragContext *context,
2054                           GtkWidget      *widget,
2055                           gint            hot_x,
2056                           gint            hot_y,
2057                           gboolean        destroy_on_release)
2058 {
2059   GtkDragSourceInfo *info;
2060
2061   g_return_if_fail (context != NULL);
2062   g_return_if_fail (widget != NULL);
2063
2064   info = gtk_drag_get_source_info (context, FALSE);
2065   gtk_drag_remove_icon (info);
2066
2067   info->icon_window = widget;
2068   info->hot_x = hot_x;
2069   info->hot_y = hot_y;
2070
2071   if (widget)
2072     {
2073       gtk_widget_set_uposition (widget, 
2074                                 info->cur_x - info->hot_x, 
2075                                 info->cur_y - info->hot_y);
2076       gtk_widget_ref (widget);
2077       gdk_window_raise (widget->window);
2078       gtk_widget_show (widget);
2079     }
2080
2081   info->destroy_icon = destroy_on_release;
2082 }
2083
2084 /**
2085  * gtk_drag_set_icon_widget:
2086  * @context: the context for a drag. (This must be called 
2087           with a  context for the source side of a drag)
2088  * @widget: a toplevel window to use as an icon.
2089  * @hot_x: the X offset within @widget of the hotspot.
2090  * @hot_y: the Y offset within @widget of the hotspot.
2091  * 
2092  * Changes the icon for a widget to a given widget. GTK+
2093  * will not destroy the icon, so if you don't want
2094  * it to persist, you should connect to the "drag_end" 
2095  * signal and destroy it yourself.
2096  **/
2097 void 
2098 gtk_drag_set_icon_widget (GdkDragContext    *context,
2099                           GtkWidget         *widget,
2100                           gint               hot_x,
2101                           gint               hot_y)
2102 {
2103   g_return_if_fail (context != NULL);
2104   g_return_if_fail (widget != NULL);
2105
2106   gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2107 }
2108
2109 static void
2110 set_icon_stock_pixbuf (GdkDragContext    *context,
2111                        const gchar       *stock_id,
2112                        GdkPixbuf         *pixbuf,
2113                        gint               hot_x,
2114                        gint               hot_y)
2115 {
2116   GtkWidget *window;
2117   gint width, height;
2118   GdkPixmap *pixmap;
2119   GdkPixmap *mask;
2120   
2121   g_return_if_fail (context != NULL);
2122   g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2123   g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2124
2125   gtk_widget_push_colormap (gdk_rgb_get_colormap ());
2126   window = gtk_window_new (GTK_WINDOW_POPUP);
2127   gtk_widget_pop_colormap ();
2128
2129   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2130   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2131   
2132   if (stock_id)
2133     {
2134       pixbuf = gtk_widget_render_icon (window, stock_id,
2135                                        GTK_ICON_SIZE_DND, NULL);
2136
2137       if (!pixbuf)
2138         {
2139           g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2140           gtk_widget_destroy (window);
2141           return;
2142         }
2143
2144     }
2145   
2146   width = gdk_pixbuf_get_width (pixbuf);
2147   height = gdk_pixbuf_get_width (pixbuf);
2148
2149   gtk_widget_set_usize (window,
2150                         gdk_pixbuf_get_width (pixbuf),
2151                         gdk_pixbuf_get_height (pixbuf));
2152   gtk_widget_realize (window);
2153
2154   gdk_pixbuf_render_pixmap_and_mask (pixbuf, &pixmap, &mask, 128);
2155   
2156   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2157   
2158   if (mask)
2159     gtk_widget_shape_combine_mask (window, mask, 0, 0);
2160
2161   g_object_unref (G_OBJECT (pixmap));
2162   g_object_unref (G_OBJECT (mask));
2163
2164   gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2165 }
2166
2167 /**
2168  * gtk_drag_set_icon_pixbuf:
2169  * @context: the context for a drag. (This must be called 
2170  *            with a  context for the source side of a drag)
2171  * @pixbuf: the #GdkPixbuf to use as the drag icon.
2172  * @hot_x: the X offset within @widget of the hotspot.
2173  * @hot_y: the Y offset within @widget of the hotspot.
2174  * 
2175  * Sets @pixbuf as the icon for a given drag.
2176  **/
2177 void 
2178 gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
2179                            GdkPixbuf      *pixbuf,
2180                            gint            hot_x,
2181                            gint            hot_y)
2182 {
2183   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2184   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2185   
2186   set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2187 }
2188
2189 /**
2190  * gtk_drag_set_icon_pixbuf:
2191  * @context: the context for a drag. (This must be called 
2192  *            with a  context for the source side of a drag)
2193  * @stock: the ID of the stock icon to use for the drag.
2194  * @hot_x: the X offset within the icon of the hotspot.
2195  * @hot_y: the Y offset within the icon of the hotspot.
2196  * 
2197  * Sets the the icon for a given drag from a stock ID.
2198  **/
2199 void 
2200 gtk_drag_set_icon_stock  (GdkDragContext *context,
2201                           const gchar    *stock_id,
2202                           gint            hot_x,
2203                           gint            hot_y)
2204 {
2205   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2206   g_return_if_fail (stock_id != NULL);
2207   
2208   set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2209 }
2210
2211 /**
2212  * gtk_drag_set_icon_pixmap:
2213  * @context: the context for a drag. (This must be called 
2214  *            with a  context for the source side of a drag)
2215  * @colormap: the colormap of the icon 
2216  * @pixmap: the image data for the icon 
2217  * @mask: the transparency mask for the icon
2218  * @hot_x: the X offset within @pixmap of the hotspot.
2219  * @hot_y: the Y offset within @pixmap of the hotspot.
2220  * 
2221  * Sets @pixmap as the icon for a given drag. GTK+ retains a
2222  * reference count for the arguments, and will release them when
2223  * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2224  * will be more convenient to use.
2225  **/
2226 void 
2227 gtk_drag_set_icon_pixmap (GdkDragContext    *context,
2228                           GdkColormap       *colormap,
2229                           GdkPixmap         *pixmap,
2230                           GdkBitmap         *mask,
2231                           gint               hot_x,
2232                           gint               hot_y)
2233 {
2234   GtkWidget *window;
2235   gint width, height;
2236       
2237   g_return_if_fail (context != NULL);
2238   g_return_if_fail (colormap != NULL);
2239   g_return_if_fail (pixmap != NULL);
2240
2241   gdk_window_get_size (pixmap, &width, &height);
2242
2243   gtk_widget_push_colormap (colormap);
2244
2245   window = gtk_window_new (GTK_WINDOW_POPUP);
2246   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2247   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2248
2249   gtk_widget_pop_colormap ();
2250
2251   gtk_widget_set_usize (window, width, height);
2252   gtk_widget_realize (window);
2253
2254   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2255   
2256   if (mask)
2257     gtk_widget_shape_combine_mask (window, mask, 0, 0);
2258
2259   gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2260 }
2261
2262 /**
2263  * gtk_drag_set_icon_default:
2264  * @context: the context for a drag. (This must be called 
2265              with a  context for the source side of a drag)
2266  * 
2267  * Sets the icon for a particular drag to the default
2268  * icon.
2269  **/
2270 void 
2271 gtk_drag_set_icon_default (GdkDragContext    *context)
2272 {
2273   g_return_if_fail (context != NULL);
2274
2275   if (!default_icon_pixmap)
2276     gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2277   else
2278     gtk_drag_set_icon_pixmap (context, 
2279                               default_icon_colormap, 
2280                               default_icon_pixmap, 
2281                               default_icon_mask,
2282                               default_icon_hot_x,
2283                               default_icon_hot_y);
2284 }
2285
2286 /**
2287  * gtk_drag_set_default_icon:
2288  * @colormap: the colormap of the icon
2289  * @pixmap: the image data for the icon
2290  * @mask: the transparency mask for an image.
2291  * @hot_x: The X offset within @widget of the hotspot.
2292  * @hot_y: The Y offset within @widget of the hotspot.
2293  * 
2294  * Changes the default drag icon. GTK+ retains a reference count for the
2295  * arguments, and will release them when they are no longer needed.
2296  * This function is obsolete. The default icon should now be changed
2297  * via the stock system by changing the stock pixbuf for GTK_STOCK_DND.
2298  **/
2299 void 
2300 gtk_drag_set_default_icon (GdkColormap   *colormap,
2301                            GdkPixmap     *pixmap,
2302                            GdkBitmap     *mask,
2303                            gint           hot_x,
2304                            gint           hot_y)
2305 {
2306   g_return_if_fail (colormap != NULL);
2307   g_return_if_fail (pixmap != NULL);
2308   
2309   if (default_icon_colormap)
2310     gdk_colormap_unref (default_icon_colormap);
2311   if (default_icon_pixmap)
2312     gdk_pixmap_unref (default_icon_pixmap);
2313   if (default_icon_mask)
2314     gdk_pixmap_unref (default_icon_mask);
2315
2316   default_icon_colormap = colormap;
2317   gdk_colormap_ref (colormap);
2318   
2319   default_icon_pixmap = pixmap;
2320   gdk_pixmap_ref (pixmap);
2321
2322   default_icon_mask = mask;
2323   if (mask)
2324     gdk_pixmap_ref (mask);
2325   
2326   default_icon_hot_x = hot_x;
2327   default_icon_hot_y = hot_y;
2328 }
2329
2330
2331 /*************************************************************
2332  * _gtk_drag_source_handle_event:
2333  *     Called from widget event handling code on Drag events
2334  *     for drag sources.
2335  *
2336  *   arguments:
2337  *     toplevel: Toplevel widget that received the event
2338  *     event:
2339  *   results:
2340  *************************************************************/
2341
2342 void
2343 _gtk_drag_source_handle_event (GtkWidget *widget,
2344                                GdkEvent  *event)
2345 {
2346   GtkDragSourceInfo *info;
2347   GdkDragContext *context;
2348
2349   g_return_if_fail (widget != NULL);
2350   g_return_if_fail (event != NULL);
2351
2352   context = event->dnd.context;
2353   info = gtk_drag_get_source_info (context, FALSE);
2354   if (!info)
2355     return;
2356
2357   switch (event->type)
2358     {
2359     case GDK_DRAG_STATUS:
2360       {
2361         GdkCursor *cursor;
2362
2363         if (info->proxy_dest)
2364           {
2365             if (!event->dnd.send_event)
2366               {
2367                 if (info->proxy_dest->proxy_drop_wait)
2368                   {
2369                     gboolean result = context->action != 0;
2370                     
2371                     /* Aha - we can finally pass the MOTIF DROP on... */
2372                     gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2373                     if (result)
2374                       gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2375                     else
2376                       gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2377                   }
2378                 else
2379                   {
2380                     gdk_drag_status (info->proxy_dest->context,
2381                                      event->dnd.context->action,
2382                                      event->dnd.time);
2383                   }
2384               }
2385           }
2386         else
2387           {
2388             cursor = gtk_drag_get_cursor (event->dnd.context->action);
2389             if (info->cursor != cursor)
2390               {
2391                 gdk_pointer_grab (widget->window, FALSE,
2392                                   GDK_POINTER_MOTION_MASK |
2393                                   GDK_POINTER_MOTION_HINT_MASK |
2394                                   GDK_BUTTON_RELEASE_MASK,
2395                                   NULL,
2396                                   cursor, event->dnd.time);
2397                 info->cursor = cursor;
2398               }
2399             
2400             if (info->last_event)
2401               {
2402                 gtk_drag_update (info,
2403                                  info->cur_x, info->cur_y,
2404                                  info->last_event);
2405                 info->last_event = NULL;
2406               }
2407           }
2408       }
2409       break;
2410       
2411     case GDK_DROP_FINISHED:
2412       gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2413       break;
2414     default:
2415       g_assert_not_reached ();
2416     }
2417 }
2418
2419 /*************************************************************
2420  * gtk_drag_source_check_selection:
2421  *     Check if we've set up handlers/claimed the selection
2422  *     for a given drag. If not, add them.
2423  *   arguments:
2424  *     
2425  *   results:
2426  *************************************************************/
2427
2428 static void
2429 gtk_drag_source_check_selection (GtkDragSourceInfo *info, 
2430                                  GdkAtom            selection,
2431                                  guint32            time)
2432 {
2433   GList *tmp_list;
2434
2435   tmp_list = info->selections;
2436   while (tmp_list)
2437     {
2438       if (GPOINTER_TO_UINT (tmp_list->data) == selection)
2439         return;
2440       tmp_list = tmp_list->next;
2441     }
2442
2443   gtk_selection_owner_set (info->ipc_widget, selection, time);
2444   info->selections = g_list_prepend (info->selections,
2445                                      GUINT_TO_POINTER (selection));
2446
2447   tmp_list = info->target_list->list;
2448   while (tmp_list)
2449     {
2450       GtkTargetPair *pair = tmp_list->data;
2451
2452       gtk_selection_add_target (info->ipc_widget,
2453                                 selection,
2454                                 pair->target,
2455                                 pair->info);
2456       tmp_list = tmp_list->next;
2457     }
2458   
2459   if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2460     {
2461       gtk_selection_add_target (info->ipc_widget,
2462                                 selection,
2463                                 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2464                                 TARGET_MOTIF_SUCCESS);
2465       gtk_selection_add_target (info->ipc_widget,
2466                                 selection,
2467                                 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2468                                 TARGET_MOTIF_FAILURE);
2469     }
2470
2471   gtk_selection_add_target (info->ipc_widget,
2472                             selection,
2473                             gdk_atom_intern ("DELETE", FALSE),
2474                             TARGET_DELETE);
2475 }
2476
2477 /*************************************************************
2478  * gtk_drag_drop_finished:
2479  *     Clean up from the drag, and display snapback, if necessary.
2480  *   arguments:
2481  *     info:
2482  *     success:
2483  *     time:
2484  *   results:
2485  *************************************************************/
2486
2487 static void
2488 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2489                         gboolean           success,
2490                         guint              time)
2491 {
2492   gtk_drag_source_release_selections (info, time); 
2493
2494   if (info->proxy_dest)
2495     {
2496       /* The time from the event isn't reliable for Xdnd drags */
2497       gtk_drag_finish (info->proxy_dest->context, success, FALSE, 
2498                        info->proxy_dest->proxy_drop_time);
2499       gtk_drag_source_info_destroy (info);
2500     }
2501   else
2502     {
2503       if (success)
2504         {
2505           gtk_drag_source_info_destroy (info);
2506         }
2507       else
2508         {
2509           GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2510           anim->info = info;
2511           anim->step = 0;
2512           
2513           anim->n_steps = MAX (info->cur_x - info->start_x,
2514                                info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2515           anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2516           if (info->icon_window)
2517             {
2518               gtk_widget_show (info->icon_window);
2519               gdk_window_raise (info->icon_window->window);
2520             }
2521           
2522           /* Mark the context as dead, so if the destination decides
2523            * to respond really late, we still are OK.
2524            */
2525           gtk_drag_clear_source_info (info->context);
2526           gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2527         }
2528     }
2529 }
2530
2531 static void
2532 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2533                                     guint32            time)
2534 {
2535   GList *tmp_list = info->selections;
2536   while (tmp_list)
2537     {
2538       GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2539       if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2540         gtk_selection_owner_set (NULL, selection, time);
2541       tmp_list = tmp_list->next;
2542     }
2543
2544   g_list_free (info->selections);
2545   info->selections = NULL;
2546 }
2547
2548 /*************************************************************
2549  * gtk_drag_drop:
2550  *     Send a drop event.
2551  *   arguments:
2552  *     
2553  *   results:
2554  *************************************************************/
2555
2556 static void
2557 gtk_drag_drop (GtkDragSourceInfo *info, 
2558                guint32            time)
2559 {
2560   if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2561     {
2562       GtkSelectionData selection_data;
2563       GList *tmp_list;
2564       GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2565       
2566       tmp_list = info->target_list->list;
2567       while (tmp_list)
2568         {
2569           GtkTargetPair *pair = tmp_list->data;
2570           
2571           if (pair->target == target)
2572             {
2573               selection_data.selection = GDK_NONE;
2574               selection_data.target = target;
2575               selection_data.data = NULL;
2576               selection_data.length = -1;
2577               
2578               gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2579                                        info->context, &selection_data,
2580                                        pair->info, 
2581                                        time);
2582               
2583               /* FIXME: Should we check for length >= 0 here? */
2584               gtk_drag_drop_finished (info, TRUE, time);
2585               return;
2586             }
2587           tmp_list = tmp_list->next;
2588         }
2589       gtk_drag_drop_finished (info, FALSE, time);
2590     }
2591   else
2592     {
2593       if (info->icon_window)
2594         gtk_widget_hide (info->icon_window);
2595         
2596       gdk_drag_drop (info->context, time);
2597       info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2598                                             gtk_drag_abort_timeout,
2599                                             info);
2600     }
2601 }
2602
2603 /*
2604  * Source side callbacks.
2605  */
2606
2607 static gint
2608 gtk_drag_source_event_cb (GtkWidget      *widget,
2609                           GdkEvent       *event,
2610                           gpointer        data)
2611 {
2612   GtkDragSourceSite *site;
2613   gboolean retval = FALSE;
2614   site = (GtkDragSourceSite *)data;
2615
2616   switch (event->type)
2617     {
2618     case GDK_BUTTON_PRESS:
2619       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2620         {
2621           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2622           site->x = event->button.x;
2623           site->y = event->button.y;
2624         }
2625       break;
2626       
2627     case GDK_BUTTON_RELEASE:
2628       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2629         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2630       break;
2631       
2632     case GDK_MOTION_NOTIFY:
2633       if (site->state & event->motion.state & site->start_button_mask)
2634         {
2635           /* FIXME: This is really broken and can leave us
2636            * with a stuck grab
2637            */
2638           int i;
2639           for (i=1; i<6; i++)
2640             {
2641               if (site->state & event->motion.state & 
2642                   GDK_BUTTON1_MASK << (i - 1))
2643                 break;
2644             }
2645
2646           if (gtk_drag_check_threshold (widget, site->x, site->y,
2647                                         event->motion.x, event->motion.y))
2648             {
2649               GtkDragSourceInfo *info;
2650               GdkDragContext *context;
2651               
2652               site->state = 0;
2653               context = gtk_drag_begin (widget, site->target_list,
2654                                         site->actions, 
2655                                         i, event);
2656
2657               info = gtk_drag_get_source_info (context, FALSE);
2658
2659               if (!info->icon_window)
2660                 {
2661                   switch (site->icon_type)
2662                     {
2663                     case GTK_IMAGE_EMPTY:
2664                       gtk_drag_set_icon_default (context);
2665                       break;
2666                     case GTK_IMAGE_PIXMAP:
2667                       gtk_drag_set_icon_pixmap (context,
2668                                                 site->colormap,
2669                                                 site->icon_data.pixmap.pixmap,
2670                                                 site->icon_mask,
2671                                                 -2, -2);
2672                       break;
2673                     case GTK_IMAGE_PIXBUF:
2674                       gtk_drag_set_icon_pixbuf (context,
2675                                                 site->icon_data.pixbuf.pixbuf,
2676                                                 -2, -2);
2677                       break;
2678                     case GTK_IMAGE_STOCK:
2679                       gtk_drag_set_icon_stock (context,
2680                                                site->icon_data.stock.stock_id,
2681                                                -2, -2);
2682                       break;
2683                     default:
2684                       g_assert_not_reached();
2685                       break;
2686                     }
2687                 }
2688
2689               retval = TRUE;
2690             }
2691         }
2692       break;
2693       
2694     default:                    /* hit for 2/3BUTTON_PRESS */
2695       break;
2696     }
2697   
2698   return retval;
2699 }
2700
2701 static void 
2702 gtk_drag_source_site_destroy (gpointer data)
2703 {
2704   GtkDragSourceSite *site = data;
2705
2706   if (site->target_list)
2707     gtk_target_list_unref (site->target_list);
2708
2709   gtk_drag_source_unset_icon (site);
2710   g_free (site);
2711 }
2712
2713 static void
2714 gtk_drag_selection_get (GtkWidget        *widget, 
2715                         GtkSelectionData *selection_data,
2716                         guint             sel_info,
2717                         guint32           time,
2718                         gpointer          data)
2719 {
2720   GtkDragSourceInfo *info = data;
2721   static GdkAtom null_atom = GDK_NONE;
2722   guint target_info;
2723
2724   if (!null_atom)
2725     null_atom = gdk_atom_intern ("NULL", FALSE);
2726
2727   switch (sel_info)
2728     {
2729     case TARGET_DELETE:
2730       gtk_signal_emit_by_name (GTK_OBJECT (info->widget), 
2731                                "drag_data_delete", 
2732                                info->context);
2733       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2734       break;
2735     case TARGET_MOTIF_SUCCESS:
2736       gtk_drag_drop_finished (info, TRUE, time);
2737       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2738       break;
2739     case TARGET_MOTIF_FAILURE:
2740       gtk_drag_drop_finished (info, FALSE, time);
2741       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2742       break;
2743     default:
2744       if (info->proxy_dest)
2745         {
2746           /* This is sort of dangerous and needs to be thought
2747            * through better
2748            */
2749           info->proxy_dest->proxy_data = selection_data;
2750           gtk_drag_get_data (info->widget,
2751                              info->proxy_dest->context,
2752                              selection_data->target,
2753                              time);
2754           gtk_main ();
2755           info->proxy_dest->proxy_data = NULL;
2756         }
2757       else
2758         {
2759           if (gtk_target_list_find (info->target_list, 
2760                                     selection_data->target, 
2761                                     &target_info))
2762             {
2763               gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2764                                        info->context, 
2765                                        selection_data, 
2766                                        target_info, 
2767                                        time);
2768             }
2769         }
2770       break;
2771     }
2772 }
2773
2774 static gint
2775 gtk_drag_anim_timeout (gpointer data)
2776 {
2777   GtkDragAnim *anim = data;
2778   gint x, y;
2779   gboolean retval;
2780
2781   GDK_THREADS_ENTER ();
2782
2783   if (anim->step == anim->n_steps)
2784     {
2785       gtk_drag_source_info_destroy (anim->info);
2786       g_free (anim);
2787
2788       retval = FALSE;
2789     }
2790   else
2791     {
2792       x = (anim->info->start_x * (anim->step + 1) +
2793            anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2794       y = (anim->info->start_y * (anim->step + 1) +
2795            anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2796       if (anim->info->icon_window)
2797         gtk_widget_set_uposition (anim->info->icon_window, 
2798                                   x - anim->info->hot_x, 
2799                                   y - anim->info->hot_y);
2800   
2801       anim->step++;
2802
2803       retval = TRUE;
2804     }
2805
2806   GDK_THREADS_LEAVE ();
2807
2808   return retval;
2809 }
2810
2811 static void
2812 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2813 {
2814   if (info->icon_window)
2815     {
2816       gtk_widget_hide (info->icon_window);
2817       if (info->destroy_icon)
2818         gtk_widget_destroy (info->icon_window);
2819
2820       gtk_widget_unref (info->icon_window);
2821       info->icon_window = NULL;
2822     }
2823 }
2824
2825 static void
2826 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
2827 {
2828   gtk_drag_remove_icon (info);
2829
2830   if (!info->proxy_dest)
2831     gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end", 
2832                              info->context);
2833
2834   if (info->widget)
2835     gtk_widget_unref (info->widget);
2836
2837   gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2838   gtk_selection_remove_all (info->ipc_widget);
2839   gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2840   source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2841   gtk_drag_release_ipc_widget (info->ipc_widget);
2842
2843   gtk_target_list_unref (info->target_list);
2844
2845   gtk_drag_clear_source_info (info->context);
2846   gdk_drag_context_unref (info->context);
2847
2848   if (info->drop_timeout)
2849     gtk_timeout_remove (info->drop_timeout);
2850
2851   g_free (info);
2852 }
2853
2854 /*************************************************************
2855  * gtk_drag_update:
2856  *     Function to update the status of the drag when the
2857  *     cursor moves or the modifier changes
2858  *   arguments:
2859  *     info: DragSourceInfo for the drag
2860  *     x_root, y_root: position of darg
2861  *     event: The event that triggered this call
2862  *   results:
2863  *************************************************************/
2864
2865 static void
2866 gtk_drag_update (GtkDragSourceInfo *info,
2867                  gint               x_root,
2868                  gint               y_root,
2869                  GdkEvent          *event)
2870 {
2871   GdkDragAction action;
2872   GdkDragAction possible_actions;
2873   GdkWindow *window = NULL;
2874   GdkWindow *dest_window;
2875   GdkDragProtocol protocol;
2876   GdkAtom selection;
2877   guint32 time = gtk_drag_get_event_time (event);
2878
2879   gtk_drag_get_event_actions (event,
2880                               info->button, 
2881                               info->possible_actions,
2882                               &action, &possible_actions);
2883   info->cur_x = x_root;
2884   info->cur_y = y_root;
2885
2886   if (info->icon_window)
2887     {
2888       gdk_window_raise (info->icon_window->window);
2889       gtk_widget_set_uposition (info->icon_window, 
2890                                 info->cur_x - info->hot_x, 
2891                                 info->cur_y - info->hot_y);
2892       window = info->icon_window->window;
2893     }
2894   
2895   gdk_drag_find_window (info->context,
2896                         window, x_root, y_root,
2897                         &dest_window, &protocol);
2898   
2899   if (gdk_drag_motion (info->context, dest_window, protocol,
2900                        x_root, y_root, action, 
2901                        possible_actions,
2902                        time))
2903     {
2904       if (info->last_event != event) /* Paranoia, should not happen */
2905         {
2906           if (info->last_event)
2907             gdk_event_free ((GdkEvent *)info->last_event);
2908           info->last_event = gdk_event_copy ((GdkEvent *)event);
2909         }
2910     }
2911   else
2912     {
2913       if (info->last_event)
2914         {
2915           gdk_event_free ((GdkEvent *)info->last_event);
2916           info->last_event = NULL;
2917         }
2918     }
2919   
2920   if (dest_window)
2921     gdk_window_unref (dest_window);
2922
2923   selection = gdk_drag_get_selection (info->context);
2924   if (selection)
2925     gtk_drag_source_check_selection (info, selection, time);
2926 }
2927
2928 /*************************************************************
2929  * gtk_drag_end:
2930  *     Called when the user finishes to drag, either by
2931  *     releasing the mouse, or by pressing Esc.
2932  *   arguments:
2933  *     widget: GtkInvisible widget for this drag
2934  *     info: 
2935  *   results:
2936  *************************************************************/
2937
2938 static void
2939 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
2940 {
2941   GdkEvent send_event;
2942   GtkWidget *source_widget = info->widget;
2943
2944   gdk_pointer_ungrab (time);
2945   gdk_keyboard_ungrab (time);
2946
2947   gtk_grab_remove (info->ipc_widget);
2948
2949   gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget), 
2950                                  GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
2951                                  info);
2952   gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget), 
2953                                  GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
2954                                  info);
2955
2956   /* Send on a release pair to the the original 
2957    * widget to convince it to release its grab. We need to
2958    * call gtk_propagate_event() here, instead of 
2959    * gtk_widget_event() because widget like GtkList may
2960    * expect propagation.
2961    */
2962
2963   send_event.button.type = GDK_BUTTON_RELEASE;
2964   send_event.button.window = GDK_ROOT_PARENT ();
2965   send_event.button.send_event = TRUE;
2966   send_event.button.time = time;
2967   send_event.button.x = 0;
2968   send_event.button.y = 0;
2969   send_event.button.axes = NULL;
2970   send_event.button.state = 0;
2971   send_event.button.button = info->button;
2972   send_event.button.device = gdk_device_get_core_pointer ();
2973   send_event.button.x_root = 0;
2974   send_event.button.y_root = 0;
2975
2976   gtk_propagate_event (source_widget, &send_event);
2977 }
2978
2979 /*************************************************************
2980  * gtk_drag_motion_cb:
2981  *     "motion_notify_event" callback during drag.
2982  *   arguments:
2983  *     
2984  *   results:
2985  *************************************************************/
2986
2987 static gint
2988 gtk_drag_motion_cb (GtkWidget      *widget, 
2989                     GdkEventMotion *event, 
2990                     gpointer        data)
2991 {
2992   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2993   gint x_root, y_root;
2994
2995   if (event->is_hint)
2996     {
2997       gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2998       event->x_root = x_root;
2999       event->y_root = y_root;
3000     }
3001
3002   gtk_drag_update (info, event->x_root, event->y_root, (GdkEvent *)event);
3003
3004   return TRUE;
3005 }
3006
3007 /*************************************************************
3008  * gtk_drag_key_cb:
3009  *     "key_press/release_event" callback during drag.
3010  *   arguments:
3011  *     
3012  *   results:
3013  *************************************************************/
3014
3015 static gint 
3016 gtk_drag_key_cb (GtkWidget         *widget, 
3017                  GdkEventKey       *event, 
3018                  gpointer           data)
3019 {
3020   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3021   GdkModifierType state;
3022   
3023   if (event->type == GDK_KEY_PRESS)
3024     {
3025       if (event->keyval == GDK_Escape)
3026         {
3027           gtk_drag_end (info, event->time);
3028           gdk_drag_abort (info->context, event->time);
3029           gtk_drag_drop_finished (info, FALSE, event->time);
3030
3031           return TRUE;
3032         }
3033     }
3034
3035   /* Now send a "motion" so that the modifier state is updated */
3036
3037   /* The state is not yet updated in the event, so we need
3038    * to query it here. We could use XGetModifierMapping, but
3039    * that would be overkill.
3040    */
3041   gdk_window_get_pointer (GDK_ROOT_PARENT(), NULL, NULL, &state);
3042
3043   event->state = state;
3044   gtk_drag_update (info, info->cur_x, info->cur_y, (GdkEvent *)event);
3045
3046   return TRUE;
3047 }
3048
3049 /*************************************************************
3050  * gtk_drag_button_release_cb:
3051  *     "button_release_event" callback during drag.
3052  *   arguments:
3053  *     
3054  *   results:
3055  *************************************************************/
3056
3057 static gint
3058 gtk_drag_button_release_cb (GtkWidget      *widget, 
3059                             GdkEventButton *event, 
3060                             gpointer        data)
3061 {
3062   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3063
3064   if (event->button != info->button)
3065     return FALSE;
3066
3067   gtk_drag_end (info, event->time);
3068
3069   if ((info->context->action != 0) && (info->context->dest_window != NULL))
3070     {
3071       gtk_drag_drop (info, event->time);
3072     }
3073   else
3074     {
3075       gdk_drag_abort (info->context, event->time);
3076       gtk_drag_drop_finished (info, FALSE, event->time);
3077     }
3078
3079   return TRUE;
3080 }
3081
3082 static gint
3083 gtk_drag_abort_timeout (gpointer data)
3084 {
3085   GtkDragSourceInfo *info = data;
3086   guint32 time = GDK_CURRENT_TIME;
3087
3088   GDK_THREADS_ENTER ();
3089
3090   if (info->proxy_dest)
3091     time = info->proxy_dest->proxy_drop_time;
3092
3093   info->drop_timeout = 0;
3094   gtk_drag_drop_finished (info, FALSE, time);
3095   
3096   GDK_THREADS_LEAVE ();
3097   
3098   return FALSE;
3099 }
3100
3101 /**
3102  * gtk_drag_check_threshold:
3103  * @widget: a #GtkWidget
3104  * @start_x: X coordinate of start of drag
3105  * @start_y: Y coordinate of start of drag
3106  * @current_x: current X coordinate
3107  * @current_y: current Y coordinate
3108  * 
3109  * Checks to see if a mouse drag starting at (start_x, start_y) and ending
3110  * at (current_x, current_y) has passed the GTK drag threshhold, and thus
3111  * should trigger the beginning of a drag-and-drop operation.
3112  *
3113  * Return Value: If the drag threshold has been passed.
3114  **/
3115 gboolean
3116 gtk_drag_check_threshold (GtkWidget *widget,
3117                           gint       start_x,
3118                           gint       start_y,
3119                           gint       current_x,
3120                           gint       current_y)
3121 {
3122 #define DRAG_THRESHOLD 8
3123
3124   return (ABS (current_x - start_x) > DRAG_THRESHOLD ||
3125           ABS (current_y - start_y) > DRAG_THRESHOLD);
3126 }