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