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