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