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