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