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