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