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