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