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