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