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