]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd.c
Remove pointless cast
[~andy/gtk] / gtk / gtkdnd.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <config.h>
28
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "gdkconfig.h"
33
34 #include "gdk/gdkkeysyms.h"
35
36 #include "gtkdnd.h"
37 #include "gtkiconfactory.h"
38 #include "gtkicontheme.h"
39 #include "gtkimage.h"
40 #include "gtkinvisible.h"
41 #include "gtkmain.h"
42 #include "gtkplug.h"
43 #include "gtkstock.h"
44 #include "gtkwindow.h"
45 #include "gtkintl.h"
46 #include "gtkalias.h"
47
48 static GSList *source_widgets = NULL;
49
50 typedef struct _GtkDragSourceSite GtkDragSourceSite;
51 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
52 typedef struct _GtkDragDestSite GtkDragDestSite;
53 typedef struct _GtkDragDestInfo GtkDragDestInfo;
54 typedef struct _GtkDragAnim GtkDragAnim;
55 typedef struct _GtkDragFindData GtkDragFindData;
56
57
58 typedef enum 
59 {
60   GTK_DRAG_STATUS_DRAG,
61   GTK_DRAG_STATUS_WAIT,
62   GTK_DRAG_STATUS_DROP
63 } GtkDragStatus;
64
65 struct _GtkDragSourceSite 
66 {
67   GdkModifierType    start_button_mask;
68   GtkTargetList     *target_list;        /* Targets for drag data */
69   GdkDragAction      actions;            /* Possible actions */
70
71   /* Drag icon */
72   GtkImageType icon_type;
73   union
74   {
75     GtkImagePixmapData pixmap;
76     GtkImagePixbufData pixbuf;
77     GtkImageStockData stock;
78     GtkImageIconNameData name;
79   } icon_data;
80   GdkBitmap *icon_mask;
81
82   GdkColormap       *colormap;           /* Colormap for drag icon */
83
84   /* Stored button press information to detect drag beginning */
85   gint               state;
86   gint               x, y;
87 };
88   
89 struct _GtkDragSourceInfo 
90 {
91   GtkWidget         *widget;
92   GtkTargetList     *target_list; /* Targets for drag data */
93   GdkDragAction      possible_actions; /* Actions allowed by source */
94   GdkDragContext    *context;     /* drag context */
95   GtkWidget         *icon_window; /* Window for drag */
96   GtkWidget         *fallback_icon; /* Window for drag used on other screens */
97   GtkWidget         *ipc_widget;  /* GtkInvisible for grab, message passing */
98   GdkCursor         *cursor;      /* Cursor for drag */
99   gint hot_x, hot_y;              /* Hot spot for drag */
100   gint button;                    /* mouse button starting drag */
101
102   GtkDragStatus      status;      /* drag status */
103   GdkEvent          *last_event;  /* pending event */
104
105   gint               start_x, start_y; /* Initial position */
106   gint               cur_x, cur_y;     /* Current Position */
107   GdkScreen         *cur_screen;       /* Current screen for pointer */
108
109   guint32            grab_time;   /* timestamp for initial grab */
110   GList             *selections;  /* selections we've claimed */
111   
112   GtkDragDestInfo   *proxy_dest;  /* Set if this is a proxy drag */
113
114   guint              update_idle;      /* Idle function to update the drag */
115   guint              drop_timeout;     /* Timeout for aborting drop */
116   guint              destroy_icon : 1; /* If true, destroy icon_window
117                                         */
118   guint              have_grab : 1;    /* Do we still have the pointer grab
119                                         */
120   GdkPixbuf         *icon_pixbuf;
121   GdkCursor         *drag_cursors[6];
122 };
123
124 struct _GtkDragDestSite 
125 {
126   GtkDestDefaults    flags;
127   GtkTargetList     *target_list;
128   GdkDragAction      actions;
129   GdkWindow         *proxy_window;
130   GdkDragProtocol    proxy_protocol;
131   guint              do_proxy : 1;
132   guint              proxy_coords : 1;
133   guint              have_drag : 1;
134 };
135   
136 struct _GtkDragDestInfo 
137 {
138   GtkWidget         *widget;       /* Widget in which drag is in */
139   GdkDragContext    *context;      /* Drag context */
140   GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
141   GtkSelectionData  *proxy_data;   /* Set while retrieving proxied data */
142   guint              dropped : 1;     /* Set after we receive a drop */
143   guint32            proxy_drop_time; /* Timestamp for proxied drop */
144   guint              proxy_drop_wait : 1; /* Set if we are waiting for a
145                                            * status reply before sending
146                                            * a proxied drop on.
147                                            */
148   gint               drop_x, drop_y; /* Position of drop */
149 };
150
151 #define DROP_ABORT_TIME 300000
152
153 #define ANIM_STEP_TIME 50
154 #define ANIM_STEP_LENGTH 50
155 #define ANIM_MIN_STEPS 5
156 #define ANIM_MAX_STEPS 10
157
158 struct _GtkDragAnim 
159 {
160   GtkDragSourceInfo *info;
161   gint step;
162   gint n_steps;
163 };
164
165 struct _GtkDragFindData 
166 {
167   gint x;
168   gint y;
169   GdkDragContext *context;
170   GtkDragDestInfo *info;
171   gboolean found;
172   gboolean toplevel;
173   gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
174                         gint x, gint y, guint32 time);
175   guint32 time;
176 };
177
178 /* Enumeration for some targets we handle internally */
179
180 enum {
181   TARGET_MOTIF_SUCCESS = 0x40000000,
182   TARGET_MOTIF_FAILURE,
183   TARGET_DELETE
184 };
185
186 /* Drag icons */
187
188 static GdkPixmap   *default_icon_pixmap = NULL;
189 static GdkPixmap   *default_icon_mask = NULL;
190 static GdkColormap *default_icon_colormap = NULL;
191 static gint         default_icon_hot_x;
192 static gint         default_icon_hot_y;
193
194 /* Forward declarations */
195 static void          gtk_drag_get_event_actions (GdkEvent        *event, 
196                                                  gint             button,
197                                                  GdkDragAction    actions,
198                                                  GdkDragAction   *suggested_action,
199                                                  GdkDragAction   *possible_actions);
200 static GdkCursor *   gtk_drag_get_cursor         (GdkDisplay     *display,
201                                                   GdkDragAction   action,
202                                                   GtkDragSourceInfo *info);
203 static void          gtk_drag_update_cursor      (GtkDragSourceInfo *info);
204 static GtkWidget    *gtk_drag_get_ipc_widget     (GdkScreen      *screen);
205 static void          gtk_drag_release_ipc_widget (GtkWidget      *widget);
206
207 static gboolean      gtk_drag_highlight_expose   (GtkWidget      *widget,
208                                                   GdkEventExpose *event,
209                                                   gpointer        data);
210
211 static void     gtk_drag_selection_received     (GtkWidget        *widget,
212                                                  GtkSelectionData *selection_data,
213                                                  guint32           time,
214                                                  gpointer          data);
215 static void     gtk_drag_find_widget            (GtkWidget        *widget,
216                                                  GtkDragFindData  *data);
217 static void     gtk_drag_proxy_begin            (GtkWidget        *widget,
218                                                  GtkDragDestInfo  *dest_info,
219                                                  guint32           time);
220 static void     gtk_drag_dest_realized          (GtkWidget        *widget);
221 static void     gtk_drag_dest_hierarchy_changed (GtkWidget        *widget,
222                                                  GtkWidget        *previous_toplevel);
223 static void     gtk_drag_dest_site_destroy      (gpointer          data);
224 static void     gtk_drag_dest_leave             (GtkWidget        *widget,
225                                                  GdkDragContext   *context,
226                                                  guint             time);
227 static gboolean gtk_drag_dest_motion            (GtkWidget        *widget,
228                                                  GdkDragContext   *context,
229                                                  gint              x,
230                                                  gint              y,
231                                                  guint             time);
232 static gboolean gtk_drag_dest_drop              (GtkWidget        *widget,
233                                                  GdkDragContext   *context,
234                                                  gint              x,
235                                                  gint              y,
236                                                  guint             time);
237
238 static GtkDragDestInfo *  gtk_drag_get_dest_info     (GdkDragContext *context,
239                                                       gboolean        create);
240 static GtkDragSourceInfo *gtk_drag_get_source_info   (GdkDragContext *context,
241                                                       gboolean        create);
242 static void               gtk_drag_clear_source_info (GdkDragContext *context);
243
244 static void gtk_drag_source_check_selection    (GtkDragSourceInfo *info, 
245                                                 GdkAtom            selection,
246                                                 guint32            time);
247 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
248                                                 guint32            time);
249 static void gtk_drag_drop                      (GtkDragSourceInfo *info,
250                                                 guint32            time);
251 static void gtk_drag_drop_finished             (GtkDragSourceInfo *info,
252                                                 gboolean           success,
253                                                 guint              time);
254 static void gtk_drag_cancel                    (GtkDragSourceInfo *info,
255                                                 guint32            time);
256
257 static gint gtk_drag_source_event_cb           (GtkWidget         *widget,
258                                                 GdkEvent          *event,
259                                                 gpointer           data);
260 static void gtk_drag_source_site_destroy       (gpointer           data);
261 static void gtk_drag_selection_get             (GtkWidget         *widget, 
262                                                 GtkSelectionData  *selection_data,
263                                                 guint              sel_info,
264                                                 guint32            time,
265                                                 gpointer           data);
266 static gint gtk_drag_anim_timeout              (gpointer           data);
267 static void gtk_drag_remove_icon               (GtkDragSourceInfo *info);
268 static void gtk_drag_source_info_destroy       (GtkDragSourceInfo *info);
269 static void gtk_drag_add_update_idle           (GtkDragSourceInfo *info);
270
271 static void gtk_drag_update                    (GtkDragSourceInfo *info,
272                                                 GdkScreen         *screen,
273                                                 gint               x_root,
274                                                 gint               y_root,
275                                                 GdkEvent          *event);
276 static gint gtk_drag_motion_cb                 (GtkWidget         *widget, 
277                                                 GdkEventMotion    *event, 
278                                                 gpointer           data);
279 static gint gtk_drag_key_cb                    (GtkWidget         *widget, 
280                                                 GdkEventKey       *event, 
281                                                 gpointer           data);
282 static gint gtk_drag_button_release_cb         (GtkWidget         *widget, 
283                                                 GdkEventButton    *event, 
284                                                 gpointer           data);
285 static gint gtk_drag_abort_timeout             (gpointer           data);
286
287 /************************
288  * Cursor and Icon data *
289  ************************/
290
291 #include "gtkdndcursors.h"
292
293 static struct {
294   GdkDragAction action;
295   const gchar  *name;
296   const guint8 *data;
297   GdkPixbuf    *pixbuf;
298   GdkCursor    *cursor;
299 } drag_cursors[] = {
300   { GDK_ACTION_DEFAULT, NULL },
301   { GDK_ACTION_ASK,   "dnd-ask",  dnd_cursor_ask,  NULL, NULL },
302   { GDK_ACTION_COPY,  "dnd-copy", dnd_cursor_copy, NULL, NULL },
303   { GDK_ACTION_MOVE,  "dnd-move", dnd_cursor_move, NULL, NULL },
304   { GDK_ACTION_LINK,  "dnd-link", dnd_cursor_link, NULL, NULL },
305   { 0              ,  "dnd-none", dnd_cursor_none, NULL, NULL },
306 };
307
308 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
309
310 /*********************
311  * Utility functions *
312  *********************/
313
314 static void
315 set_can_change_screen (GtkWidget *widget,
316                        gboolean   can_change_screen)
317 {
318   can_change_screen = can_change_screen != FALSE;
319   
320   g_object_set_data (G_OBJECT (widget), I_("gtk-dnd-can-change-screen"),
321                      GUINT_TO_POINTER (can_change_screen));
322 }
323
324 static gboolean
325 get_can_change_screen (GtkWidget *widget)
326 {
327   return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
328
329 }
330
331 /*************************************************************
332  * gtk_drag_get_ipc_widget:
333  *     Return a invisible, off-screen, override-redirect
334  *     widget for IPC.
335  *   arguments:
336  *     
337  *   results:
338  *************************************************************/
339
340 static GtkWidget *
341 gtk_drag_get_ipc_widget (GdkScreen *screen)
342 {
343   GtkWidget *result;
344   GSList *drag_widgets = g_object_get_data (G_OBJECT (screen), 
345                                             "gtk-dnd-ipc-widgets");
346   
347   if (drag_widgets)
348     {
349       GSList *tmp = drag_widgets;
350       result = drag_widgets->data;
351       drag_widgets = drag_widgets->next;
352       g_object_set_data (G_OBJECT (screen),
353                          I_("gtk-dnd-ipc-widgets"),
354                          drag_widgets);
355       g_slist_free_1 (tmp);
356     }
357   else
358     {
359       result = gtk_invisible_new_for_screen (screen);
360       gtk_widget_show (result);
361     }
362
363   return result;
364 }
365
366 /***************************************************************
367  * gtk_drag_release_ipc_widget:
368  *     Releases widget retrieved with gtk_drag_get_ipc_widget ()
369  *   arguments:
370  *     widget: the widget to release.
371  *   results:
372  ***************************************************************/
373
374 static void
375 gtk_drag_release_ipc_widget (GtkWidget *widget)
376 {
377   GdkScreen *screen = gtk_widget_get_screen (widget);
378   GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
379                                             "gtk-dnd-ipc-widgets");
380   drag_widgets = g_slist_prepend (drag_widgets, widget);
381   g_object_set_data (G_OBJECT (screen),
382                      I_("gtk-dnd-ipc-widgets"),
383                      drag_widgets);
384 }
385
386 static guint32
387 gtk_drag_get_event_time (GdkEvent *event)
388 {
389   guint32 tm = GDK_CURRENT_TIME;
390   
391   if (event)
392     switch (event->type)
393       {
394       case GDK_MOTION_NOTIFY:
395         tm = event->motion.time; break;
396       case GDK_BUTTON_PRESS:
397       case GDK_2BUTTON_PRESS:
398       case GDK_3BUTTON_PRESS:
399       case GDK_BUTTON_RELEASE:
400         tm = event->button.time; break;
401       case GDK_KEY_PRESS:
402       case GDK_KEY_RELEASE:
403         tm = event->key.time; break;
404       case GDK_ENTER_NOTIFY:
405       case GDK_LEAVE_NOTIFY:
406         tm = event->crossing.time; break;
407       case GDK_PROPERTY_NOTIFY:
408         tm = event->property.time; break;
409       case GDK_SELECTION_CLEAR:
410       case GDK_SELECTION_REQUEST:
411       case GDK_SELECTION_NOTIFY:
412         tm = event->selection.time; break;
413       case GDK_PROXIMITY_IN:
414       case GDK_PROXIMITY_OUT:
415         tm = event->proximity.time; break;
416       default:                  /* use current time */
417         break;
418       }
419   
420   return tm;
421 }
422
423 static void
424 gtk_drag_get_event_actions (GdkEvent *event, 
425                             gint button, 
426                             GdkDragAction  actions,
427                             GdkDragAction *suggested_action,
428                             GdkDragAction *possible_actions)
429 {
430   *suggested_action = 0;
431   *possible_actions = 0;
432
433   if (event)
434     {
435       GdkModifierType state = 0;
436       
437       switch (event->type)
438         {
439         case GDK_MOTION_NOTIFY:
440           state = event->motion.state;
441           break;
442         case GDK_BUTTON_PRESS:
443         case GDK_2BUTTON_PRESS:
444         case GDK_3BUTTON_PRESS:
445         case GDK_BUTTON_RELEASE:
446           state = event->button.state;
447           break;
448         case GDK_KEY_PRESS:
449         case GDK_KEY_RELEASE:
450           state = event->key.state;
451           break;
452         case GDK_ENTER_NOTIFY:
453         case GDK_LEAVE_NOTIFY:
454           state = event->crossing.state;
455           break;
456         default:
457           break;
458         }
459
460       if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
461         {
462           *suggested_action = GDK_ACTION_ASK;
463           *possible_actions = actions;
464         }
465       else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
466         {
467           if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
468             {
469               if (actions & GDK_ACTION_LINK)
470                 {
471                   *suggested_action = GDK_ACTION_LINK;
472                   *possible_actions = GDK_ACTION_LINK;
473                 }
474             }
475           else if (state & GDK_CONTROL_MASK)
476             {
477               if (actions & GDK_ACTION_COPY)
478                 {
479                   *suggested_action = GDK_ACTION_COPY;
480                   *possible_actions = GDK_ACTION_COPY;
481                 }
482               return;
483             }
484           else
485             {
486               if (actions & GDK_ACTION_MOVE)
487                 {
488                   *suggested_action = GDK_ACTION_MOVE;
489                   *possible_actions = GDK_ACTION_MOVE;
490                 }
491               return;
492             }
493         }
494       else
495         {
496           *possible_actions = actions;
497
498           if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
499             *suggested_action = GDK_ACTION_ASK;
500           else if (actions & GDK_ACTION_COPY)
501             *suggested_action =  GDK_ACTION_COPY;
502           else if (actions & GDK_ACTION_MOVE)
503             *suggested_action = GDK_ACTION_MOVE;
504           else if (actions & GDK_ACTION_LINK)
505             *suggested_action = GDK_ACTION_LINK;
506         }
507     }
508   else
509     {
510       *possible_actions = actions;
511       
512       if (actions & GDK_ACTION_COPY)
513         *suggested_action =  GDK_ACTION_COPY;
514       else if (actions & GDK_ACTION_MOVE)
515         *suggested_action = GDK_ACTION_MOVE;
516       else if (actions & GDK_ACTION_LINK)
517         *suggested_action = GDK_ACTION_LINK;
518     }
519 }
520
521 static gboolean
522 gtk_drag_can_use_rgba_cursor (GdkDisplay *display, 
523                               gint        width,
524                               gint        height)
525 {
526   guint max_width, max_height;
527   
528   if (!gdk_display_supports_cursor_color (display))
529     return FALSE;
530
531   if (!gdk_display_supports_cursor_alpha (display))
532     return FALSE;
533
534   gdk_display_get_maximal_cursor_size (display, 
535                                        &max_width,
536                                        &max_height);
537   if (width > max_width || height > max_height)
538     {
539        /* can't use rgba cursor (too large) */
540       return FALSE;
541     }
542
543   return TRUE;
544 }
545
546 static GdkCursor *
547 gtk_drag_get_cursor (GdkDisplay        *display,
548                      GdkDragAction      action,
549                      GtkDragSourceInfo *info)
550 {
551   gint i, j;
552
553   /* reconstruct the cursors for each new drag (thus !info),
554    * to catch cursor theme changes 
555    */ 
556   if (!info)
557     {
558       for (i = 0 ; i < n_drag_cursors - 1; i++)
559         if (drag_cursors[i].cursor != NULL)
560           {
561             gdk_cursor_unref (drag_cursors[i].cursor);
562             drag_cursors[i].cursor = NULL;
563           }
564     }
565  
566   for (i = 0 ; i < n_drag_cursors - 1; i++)
567     if (drag_cursors[i].action == action)
568       break;
569
570   if (drag_cursors[i].pixbuf == NULL)
571     drag_cursors[i].pixbuf = 
572       gdk_pixbuf_new_from_inline (-1, drag_cursors[i].data, FALSE, NULL);
573
574   if (drag_cursors[i].cursor != NULL)
575     {
576       if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
577         {
578           gdk_cursor_unref (drag_cursors[i].cursor);
579           drag_cursors[i].cursor = NULL;
580         }
581     }
582   
583   if (drag_cursors[i].cursor == NULL)
584     drag_cursors[i].cursor = gdk_cursor_new_from_name (display, drag_cursors[i].name);
585   
586   if (drag_cursors[i].cursor == NULL)
587     drag_cursors[i].cursor = gdk_cursor_new_from_pixbuf (display, drag_cursors[i].pixbuf, 0, 0);
588
589   if (info && info->icon_pixbuf) 
590     {
591       gint cursor_width, cursor_height;
592       gint icon_width, icon_height;
593       gint width, height;
594       GdkPixbuf *cursor_pixbuf, *pixbuf;
595       GtkAnchorType icon_anchor;
596       gint hot_x = 0, hot_y = 0;
597       gint icon_x, icon_y, ref_x, ref_y;
598       gboolean found;
599
600       if (info->drag_cursors[i] != NULL)
601         {
602           if (display == gdk_cursor_get_display (info->drag_cursors[i]))
603             return info->drag_cursors[i];
604           
605           gdk_cursor_unref (info->drag_cursors[i]);
606           info->drag_cursors[i] = NULL;
607         }
608
609       cursor_pixbuf = gdk_cursor_get_image (drag_cursors[i].cursor);
610       if (!cursor_pixbuf)
611         {
612           cursor_pixbuf = g_object_ref (drag_cursors[i].pixbuf);
613           icon_anchor = GTK_ANCHOR_NORTH_WEST;
614           icon_x = icon_y = -2;
615         }
616       else
617         {
618           if (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"))
619             hot_x = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"));
620           
621           if (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"))
622             hot_y = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"));
623
624           found = FALSE;
625           for (j = 0; j < 10; j++)
626             {
627               const gchar *opt;
628               gchar key[32];
629               gchar **toks;
630
631               g_snprintf (key, 32, "comment%d", j);
632               opt = gdk_pixbuf_get_option (cursor_pixbuf, key);
633               if (opt && g_str_has_prefix ("icon-attach:", opt))
634                 {
635                   toks = g_strsplit (opt + strlen ("icon-attach:"), "'", -1);
636                   if (g_strv_length (toks) != 3)
637                     {
638                       g_strfreev (toks);
639                       break;
640                     }
641                   icon_anchor = atoi (toks[0]);
642                   icon_x = atoi (toks[1]);
643                   icon_y = atoi (toks[2]);
644
645                   g_strfreev (toks);
646                   found = TRUE;
647                   break;
648                 }
649             }
650           if (!found)
651             {
652               icon_anchor = GTK_ANCHOR_NORTH_WEST;
653               icon_x = (gint) -2 * gdk_pixbuf_get_width (cursor_pixbuf) / 16.0; 
654               icon_y = (gint) -2 * gdk_pixbuf_get_height (cursor_pixbuf) / 16.0; 
655             }
656         }
657       
658       icon_width = gdk_pixbuf_get_width (info->icon_pixbuf);
659       icon_height = gdk_pixbuf_get_height (info->icon_pixbuf);
660       cursor_width = gdk_pixbuf_get_width (cursor_pixbuf);
661       cursor_height = gdk_pixbuf_get_height (cursor_pixbuf);
662
663       switch (icon_anchor)
664         {
665         case GTK_ANCHOR_NORTH:
666         case GTK_ANCHOR_CENTER:
667         case GTK_ANCHOR_SOUTH:
668           icon_x += icon_width / 2;
669           break;
670         case GTK_ANCHOR_NORTH_EAST:
671         case GTK_ANCHOR_EAST:
672         case GTK_ANCHOR_SOUTH_EAST:
673           icon_x += icon_width;
674           break;
675         default: ;
676         }
677
678       switch (icon_anchor)
679         {
680         case GTK_ANCHOR_WEST:
681         case GTK_ANCHOR_CENTER:
682         case GTK_ANCHOR_EAST:
683           icon_y += icon_height / 2;
684           break;
685         case GTK_ANCHOR_SOUTH_WEST:
686         case GTK_ANCHOR_SOUTH:
687         case GTK_ANCHOR_SOUTH_EAST:
688           icon_x += icon_height;
689           break;
690         default: ;
691         }
692
693       ref_x = MAX (hot_x, icon_x);
694       ref_y = MAX (hot_y, icon_y);
695       width = ref_x + MAX (cursor_width - hot_x, icon_width - icon_x);
696       height = ref_y + MAX (cursor_height - hot_y, icon_height - icon_y);
697          
698       if (gtk_drag_can_use_rgba_cursor (display, width, height))
699         {
700           pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
701                                    width, height); 
702           
703           gdk_pixbuf_fill (pixbuf, 0xff000000);
704           
705           gdk_pixbuf_composite (info->icon_pixbuf, pixbuf,
706                                 ref_x - icon_x, ref_y - icon_y, 
707                                 icon_width, icon_height,
708                                 ref_x - icon_x, ref_y - icon_y, 
709                                 1.0, 1.0, 
710                                 GDK_INTERP_BILINEAR, 255);
711           
712           gdk_pixbuf_composite (cursor_pixbuf, pixbuf,
713                                 ref_x - hot_x, ref_y - hot_y, 
714                                 cursor_width, cursor_height,
715                                 ref_x - hot_x, ref_y - hot_y,
716                                 1.0, 1.0, 
717                                 GDK_INTERP_BILINEAR, 255);
718           
719           info->drag_cursors[i] = 
720             gdk_cursor_new_from_pixbuf (display, pixbuf, 0, 0);
721           
722           g_object_unref (pixbuf);
723         }
724       
725       g_object_unref (cursor_pixbuf);
726       
727       if (info->drag_cursors[i] != NULL)
728         return info->drag_cursors[i];
729     }
730  
731   return drag_cursors[i].cursor;
732 }
733
734 static void
735 gtk_drag_update_cursor (GtkDragSourceInfo *info)
736 {
737   GdkCursor *cursor;
738   gint i;
739
740   if (!info->have_grab)
741     return;
742
743   for (i = 0 ; i < n_drag_cursors - 1; i++)
744     if (info->cursor == drag_cursors[i].cursor ||
745         info->cursor == info->drag_cursors[i])
746       break;
747   
748   if (i == n_drag_cursors)
749     return;
750
751   cursor = gtk_drag_get_cursor (gdk_cursor_get_display (info->cursor), 
752                                 drag_cursors[i].action, info);
753   
754   if (cursor != info->cursor)
755     {
756       gdk_pointer_grab (info->ipc_widget->window, FALSE,
757                         GDK_POINTER_MOTION_MASK |
758                         GDK_BUTTON_RELEASE_MASK,
759                         NULL,
760                         cursor, info->grab_time);
761       info->cursor = cursor;
762     }
763 }
764
765 /********************
766  * Destination side *
767  ********************/
768
769 /*************************************************************
770  * gtk_drag_get_data:
771  *     Get the data for a drag or drop
772  *   arguments:
773  *     context - drag context
774  *     target  - format to retrieve the data in.
775  *     time    - timestamp of triggering event.
776  *     
777  *   results:
778  *************************************************************/
779
780 void 
781 gtk_drag_get_data (GtkWidget      *widget,
782                    GdkDragContext *context,
783                    GdkAtom         target,
784                    guint32         time)
785 {
786   GtkWidget *selection_widget;
787
788   g_return_if_fail (GTK_IS_WIDGET (widget));
789   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
790   g_return_if_fail (!context->is_source);
791
792   selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
793
794   g_object_ref (context);
795   g_object_ref (widget);
796   
797   g_signal_connect (selection_widget, "selection_received",
798                     G_CALLBACK (gtk_drag_selection_received), widget);
799
800   g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
801
802   gtk_selection_convert (selection_widget,
803                          gdk_drag_get_selection (context),
804                          target,
805                          time);
806 }
807
808
809 /*************************************************************
810  * gtk_drag_get_source_widget:
811  *     Get the widget the was the source of this drag, if
812  *     the drag originated from this application.
813  *   arguments:
814  *     context: The drag context for this drag
815  *   results:
816  *     The source widget, or NULL if the drag originated from
817  *     a different application.
818  *************************************************************/
819
820 GtkWidget *
821 gtk_drag_get_source_widget (GdkDragContext *context)
822 {
823   GSList *tmp_list;
824
825   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
826   g_return_val_if_fail (!context->is_source, NULL);
827   
828   tmp_list = source_widgets;
829   while (tmp_list)
830     {
831       GtkWidget *ipc_widget = tmp_list->data;
832       
833       if (ipc_widget->window == context->source_window)
834         {
835           GtkDragSourceInfo *info;
836           info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
837
838           return info ? info->widget : NULL;
839         }
840
841       tmp_list = tmp_list->next;
842     }
843
844   return NULL;
845 }
846
847 /*************************************************************
848  * gtk_drag_finish:
849  *     Notify the drag source that the transfer of data
850  *     is complete.
851  *   arguments:
852  *     context: The drag context for this drag
853  *     success: Was the data successfully transferred?
854  *     time:    The timestamp to use when notifying the destination.
855  *   results:
856  *************************************************************/
857
858 void 
859 gtk_drag_finish (GdkDragContext *context,
860                  gboolean        success,
861                  gboolean        del,
862                  guint32         time)
863 {
864   GdkAtom target = GDK_NONE;
865
866   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
867   g_return_if_fail (!context->is_source);
868
869   if (success && del)
870     {
871       target = gdk_atom_intern_static_string ("DELETE");
872     }
873   else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
874     {
875       target = gdk_atom_intern_static_string (success ? 
876                                               "XmTRANSFER_SUCCESS" : 
877                                               "XmTRANSFER_FAILURE");
878     }
879
880   if (target != GDK_NONE)
881     {
882       GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
883
884       g_object_ref (context);
885       
886       g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
887       g_signal_connect (selection_widget, "selection_received",
888                         G_CALLBACK (gtk_drag_selection_received),
889                         NULL);
890       
891       gtk_selection_convert (selection_widget,
892                              gdk_drag_get_selection (context),
893                              target,
894                              time);
895     }
896   
897   if (!(success && del))
898     gdk_drop_finish (context, success, time);
899 }
900
901 /*************************************************************
902  * gtk_drag_highlight_expose:
903  *     Callback for expose_event for highlighted widgets.
904  *   arguments:
905  *     widget:
906  *     event:
907  *     data:
908  *   results:
909  *************************************************************/
910
911 static gboolean
912 gtk_drag_highlight_expose (GtkWidget      *widget,
913                            GdkEventExpose *event,
914                            gpointer        data)
915 {
916   gint x, y, width, height;
917   
918   if (GTK_WIDGET_DRAWABLE (widget))
919     {
920       cairo_t *cr;
921       
922       if (GTK_WIDGET_NO_WINDOW (widget))
923         {
924           x = widget->allocation.x;
925           y = widget->allocation.y;
926           width = widget->allocation.width;
927           height = widget->allocation.height;
928         }
929       else
930         {
931           x = 0;
932           y = 0;
933           gdk_drawable_get_size (widget->window, &width, &height);
934         }
935       
936       gtk_paint_shadow (widget->style, widget->window,
937                         GTK_STATE_NORMAL, GTK_SHADOW_OUT,
938                         NULL, widget, "dnd",
939                         x, y, width, height);
940
941       cr = gdk_cairo_create (widget->window);
942       cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
943       cairo_set_line_width (cr, 1.0);
944       cairo_rectangle (cr,
945                        x + 0.5, y + 0.5,
946                        width - 1, height - 1);
947       cairo_stroke (cr);
948       cairo_destroy (cr);
949     }
950
951   return FALSE;
952 }
953
954 /*************************************************************
955  * gtk_drag_highlight:
956  *     Highlight the given widget in the default manner.
957  *   arguments:
958  *     widget:
959  *   results:
960  *************************************************************/
961
962 void 
963 gtk_drag_highlight (GtkWidget  *widget)
964 {
965   g_return_if_fail (GTK_IS_WIDGET (widget));
966
967   g_signal_connect_after (widget, "expose_event",
968                           G_CALLBACK (gtk_drag_highlight_expose),
969                           NULL);
970
971   gtk_widget_queue_draw (widget);
972 }
973
974 /*************************************************************
975  * gtk_drag_unhighlight:
976  *     Refresh the given widget to remove the highlight.
977  *   arguments:
978  *     widget:
979  *   results:
980  *************************************************************/
981
982 void 
983 gtk_drag_unhighlight (GtkWidget *widget)
984 {
985   g_return_if_fail (GTK_IS_WIDGET (widget));
986
987   g_signal_handlers_disconnect_by_func (widget,
988                                         gtk_drag_highlight_expose,
989                                         NULL);
990   
991   gtk_widget_queue_draw (widget);
992 }
993
994 static void
995 gtk_drag_dest_set_internal (GtkWidget       *widget,
996                             GtkDragDestSite *site)
997 {
998   GtkDragDestSite *old_site;
999   
1000   g_return_if_fail (widget != NULL);
1001
1002   /* HACK, do this in the destroy */
1003   old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1004   if (old_site)
1005     {
1006       g_signal_handlers_disconnect_by_func (widget,
1007                                             gtk_drag_dest_realized,
1008                                             old_site);
1009       g_signal_handlers_disconnect_by_func (widget,
1010                                             gtk_drag_dest_hierarchy_changed,
1011                                             old_site);
1012     }
1013
1014   if (GTK_WIDGET_REALIZED (widget))
1015     gtk_drag_dest_realized (widget);
1016
1017   g_signal_connect (widget, "realize",
1018                     G_CALLBACK (gtk_drag_dest_realized), site);
1019   g_signal_connect (widget, "hierarchy_changed",
1020                     G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
1021
1022   g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
1023                           site, gtk_drag_dest_site_destroy);
1024 }
1025                             
1026
1027 /*************************************************************
1028  * gtk_drag_dest_set:
1029  *     Register a drop site, and possibly add default behaviors.
1030  *   arguments:
1031  *     widget:    
1032  *     flags:     Which types of default drag behavior to use
1033  *     targets:   Table of targets that can be accepted
1034  *     n_targets: Number of of entries in targets
1035  *     actions:   
1036  *   results:
1037  *************************************************************/
1038
1039 void 
1040 gtk_drag_dest_set   (GtkWidget            *widget,
1041                      GtkDestDefaults       flags,
1042                      const GtkTargetEntry *targets,
1043                      gint                  n_targets,
1044                      GdkDragAction         actions)
1045 {
1046   GtkDragDestSite *site;
1047   
1048   g_return_if_fail (GTK_IS_WIDGET (widget));
1049
1050   site = g_new (GtkDragDestSite, 1);
1051
1052   site->flags = flags;
1053   site->have_drag = FALSE;
1054   if (targets)
1055     site->target_list = gtk_target_list_new (targets, n_targets);
1056   else
1057     site->target_list = NULL;
1058   site->actions = actions;
1059   site->do_proxy = FALSE;
1060   site->proxy_window = NULL;
1061
1062   gtk_drag_dest_set_internal (widget, site);
1063 }
1064
1065 /*************************************************************
1066  * gtk_drag_dest_set_proxy:
1067  *     Set up this widget to proxy drags elsewhere.
1068  *   arguments:
1069  *     widget:          
1070  *     proxy_window:    window to which forward drag events
1071  *     protocol:        Drag protocol which the dest widget accepts
1072  *     use_coordinates: If true, send the same coordinates to the
1073  *                      destination, because it is a embedded 
1074  *                      subwindow.
1075  *   results:
1076  *************************************************************/
1077
1078 void 
1079 gtk_drag_dest_set_proxy (GtkWidget      *widget,
1080                          GdkWindow      *proxy_window,
1081                          GdkDragProtocol protocol,
1082                          gboolean        use_coordinates)
1083 {
1084   GtkDragDestSite *site;
1085   
1086   g_return_if_fail (GTK_IS_WIDGET (widget));
1087   g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
1088
1089   site = g_new (GtkDragDestSite, 1);
1090
1091   site->flags = 0;
1092   site->have_drag = FALSE;
1093   site->target_list = NULL;
1094   site->actions = 0;
1095   site->proxy_window = proxy_window;
1096   if (proxy_window)
1097     g_object_ref (proxy_window);
1098   site->do_proxy = TRUE;
1099   site->proxy_protocol = protocol;
1100   site->proxy_coords = use_coordinates;
1101
1102   gtk_drag_dest_set_internal (widget, site);
1103 }
1104
1105 /*************************************************************
1106  * gtk_drag_dest_unset
1107  *     Unregister this widget as a drag target.
1108  *   arguments:
1109  *     widget:
1110  *   results:
1111  *************************************************************/
1112
1113 void 
1114 gtk_drag_dest_unset (GtkWidget *widget)
1115 {
1116   g_return_if_fail (GTK_IS_WIDGET (widget));
1117
1118   g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
1119 }
1120
1121 /**
1122  * gtk_drag_dest_get_target_list:
1123  * @widget: a #GtkWidget
1124  * 
1125  * Returns the list of targets this widget can accept from
1126  * drag-and-drop.
1127  * 
1128  * Return value: the #GtkTargetList, or %NULL if none
1129  **/
1130 GtkTargetList*
1131 gtk_drag_dest_get_target_list (GtkWidget *widget)
1132 {
1133   GtkDragDestSite *site;
1134
1135   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1136   
1137   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1138
1139   return site ? site->target_list : NULL;  
1140 }
1141
1142 /**
1143  * gtk_drag_dest_set_target_list:
1144  * @widget: a #GtkWidget that's a drag destination
1145  * @target_list: list of droppable targets, or %NULL for none
1146  * 
1147  * Sets the target types that this widget can accept from drag-and-drop.
1148  * The widget must first be made into a drag destination with
1149  * gtk_drag_dest_set().
1150  **/
1151 void
1152 gtk_drag_dest_set_target_list (GtkWidget      *widget,
1153                                GtkTargetList  *target_list)
1154 {
1155   GtkDragDestSite *site;
1156
1157   g_return_if_fail (GTK_IS_WIDGET (widget));
1158   
1159   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1160   
1161   if (!site)
1162     {
1163       g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
1164                  "to make the widget into a drag destination");
1165       return;
1166     }
1167
1168   if (target_list)
1169     gtk_target_list_ref (target_list);
1170   
1171   if (site->target_list)
1172     gtk_target_list_unref (site->target_list);
1173
1174   site->target_list = target_list;
1175 }
1176
1177 /**
1178  * gtk_drag_dest_add_text_targets:
1179  * @widget: a #GtkWidget that's a drag destination
1180  *
1181  * Add the text targets supported by #GtkSelection to
1182  * the target list of the drag destination. The targets
1183  * are added with @info = 0. If you need another value, 
1184  * use gtk_target_list_add_text_targets() and
1185  * gtk_drag_dest_set_target_list().
1186  * 
1187  * Since: 2.6
1188  **/
1189 void
1190 gtk_drag_dest_add_text_targets (GtkWidget *widget)
1191 {
1192   GtkTargetList *target_list;
1193
1194   target_list = gtk_drag_dest_get_target_list (widget);
1195   if (target_list)
1196     gtk_target_list_ref (target_list);
1197   else
1198     target_list = gtk_target_list_new (NULL, 0);
1199   gtk_target_list_add_text_targets (target_list, 0);
1200   gtk_drag_dest_set_target_list (widget, target_list);
1201   gtk_target_list_unref (target_list);
1202 }
1203
1204 /**
1205  * gtk_drag_dest_add_image_targets:
1206  * @widget: a #GtkWidget that's a drag destination
1207  *
1208  * Add the image targets supported by #GtkSelection to
1209  * the target list of the drag destination. The targets
1210  * are added with @info = 0. If you need another value, 
1211  * use gtk_target_list_add_image_targets() and
1212  * gtk_drag_dest_set_target_list().
1213  * 
1214  * Since: 2.6
1215  **/
1216 void
1217 gtk_drag_dest_add_image_targets (GtkWidget *widget)
1218 {
1219   GtkTargetList *target_list;
1220
1221   target_list = gtk_drag_dest_get_target_list (widget);
1222   if (target_list)
1223     gtk_target_list_ref (target_list);
1224   else
1225     target_list = gtk_target_list_new (NULL, 0);
1226   gtk_target_list_add_image_targets (target_list, 0, FALSE);
1227   gtk_drag_dest_set_target_list (widget, target_list);
1228   gtk_target_list_unref (target_list);
1229 }
1230
1231 /**
1232  * gtk_drag_dest_add_uri_targets:
1233  * @widget: a #GtkWidget that's a drag destination
1234  *
1235  * Add the URI targets supported by #GtkSelection to
1236  * the target list of the drag destination. The targets
1237  * are added with @info = 0. If you need another value, 
1238  * use gtk_target_list_add_uri_targets() and
1239  * gtk_drag_dest_set_target_list().
1240  * 
1241  * Since: 2.6
1242  **/
1243 void
1244 gtk_drag_dest_add_uri_targets (GtkWidget *widget)
1245 {
1246   GtkTargetList *target_list;
1247
1248   target_list = gtk_drag_dest_get_target_list (widget);
1249   if (target_list)
1250     gtk_target_list_ref (target_list);
1251   else
1252     target_list = gtk_target_list_new (NULL, 0);
1253   gtk_target_list_add_uri_targets (target_list, 0);
1254   gtk_drag_dest_set_target_list (widget, target_list);
1255   gtk_target_list_unref (target_list);
1256 }
1257
1258 /*************************************************************
1259  * _gtk_drag_dest_handle_event:
1260  *     Called from widget event handling code on Drag events
1261  *     for destinations.
1262  *
1263  *   arguments:
1264  *     toplevel: Toplevel widget that received the event
1265  *     event:
1266  *   results:
1267  *************************************************************/
1268
1269 void
1270 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1271                             GdkEvent  *event)
1272 {
1273   GtkDragDestInfo *info;
1274   GdkDragContext *context;
1275
1276   g_return_if_fail (toplevel != NULL);
1277   g_return_if_fail (event != NULL);
1278
1279   context = event->dnd.context;
1280
1281   info = gtk_drag_get_dest_info (context, TRUE);
1282
1283   /* Find the widget for the event */
1284   switch (event->type)
1285     {
1286     case GDK_DRAG_ENTER:
1287       break;
1288       
1289     case GDK_DRAG_LEAVE:
1290       if (info->widget)
1291         {
1292           gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1293           info->widget = NULL;
1294         }
1295       break;
1296       
1297     case GDK_DRAG_MOTION:
1298     case GDK_DROP_START:
1299       {
1300         GtkDragFindData data;
1301         gint tx, ty;
1302
1303         if (event->type == GDK_DROP_START)
1304           {
1305             info->dropped = TRUE;
1306             /* We send a leave here so that the widget unhighlights
1307              * properly.
1308              */
1309             if (info->widget)
1310               {
1311                 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1312                 info->widget = NULL;
1313               }
1314           }
1315
1316 #ifdef GDK_WINDOWING_X11
1317         /* Hackaround for: http://bugzilla.gnome.org/show_bug.cgi?id=136112
1318          *
1319          * Currently gdk_window_get_position doesn't provide reliable
1320          * information for embedded windows, so we call the much more
1321          * expensive gdk_window_get_origin().
1322          */
1323         if (GTK_IS_PLUG (toplevel))
1324           gdk_window_get_origin (toplevel->window, &tx, &ty);
1325         else
1326 #endif /* GDK_WINDOWING_X11 */
1327           gdk_window_get_position (toplevel->window, &tx, &ty);
1328
1329         data.x = event->dnd.x_root - tx;
1330         data.y = event->dnd.y_root - ty;
1331         data.context = context;
1332         data.info = info;
1333         data.found = FALSE;
1334         data.toplevel = TRUE;
1335         data.callback = (event->type == GDK_DRAG_MOTION) ?
1336           gtk_drag_dest_motion : gtk_drag_dest_drop;
1337         data.time = event->dnd.time;
1338         
1339         gtk_drag_find_widget (toplevel, &data);
1340
1341         if (info->widget && !data.found)
1342           {
1343             gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1344             info->widget = NULL;
1345           }
1346         
1347         /* Send a reply.
1348          */
1349         if (event->type == GDK_DRAG_MOTION)
1350           {
1351             if (!data.found)
1352               gdk_drag_status (context, 0, event->dnd.time);
1353           }
1354         else if (event->type == GDK_DROP_START && !info->proxy_source)
1355           {
1356             gdk_drop_reply (context, data.found, event->dnd.time);
1357             if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1358               gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1359           }
1360       }
1361       break;
1362
1363     default:
1364       g_assert_not_reached ();
1365     }
1366 }
1367
1368 /**
1369  * gtk_drag_dest_find_target:
1370  * @widget: drag destination widget
1371  * @context: drag context
1372  * @target_list: list of droppable targets, or %NULL to use
1373  *    gtk_drag_dest_get_target_list (@widget).
1374  * 
1375  * Looks for a match between @context->targets and the
1376  * @dest_target_list, returning the first matching target, otherwise
1377  * returning %GDK_NONE. @dest_target_list should usually be the return
1378  * value from gtk_drag_dest_get_target_list(), but some widgets may
1379  * have different valid targets for different parts of the widget; in
1380  * that case, they will have to implement a drag_motion handler that
1381  * passes the correct target list to this function.
1382  * 
1383  * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1384  **/
1385 GdkAtom
1386 gtk_drag_dest_find_target (GtkWidget      *widget,
1387                            GdkDragContext *context,
1388                            GtkTargetList  *target_list)
1389 {
1390   GList *tmp_target;
1391   GList *tmp_source = NULL;
1392   GtkWidget *source_widget;
1393
1394   g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1395   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1396   g_return_val_if_fail (!context->is_source, GDK_NONE);
1397
1398
1399   source_widget = gtk_drag_get_source_widget (context);
1400
1401   if (target_list == NULL)
1402     target_list = gtk_drag_dest_get_target_list (widget);
1403   
1404   if (target_list == NULL)
1405     return GDK_NONE;
1406   
1407   tmp_target = target_list->list;
1408   while (tmp_target)
1409     {
1410       GtkTargetPair *pair = tmp_target->data;
1411       tmp_source = context->targets;
1412       while (tmp_source)
1413         {
1414           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1415             {
1416               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1417                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1418                 return pair->target;
1419               else
1420                 break;
1421             }
1422           tmp_source = tmp_source->next;
1423         }
1424       tmp_target = tmp_target->next;
1425     }
1426
1427   return GDK_NONE;
1428 }
1429
1430 static void
1431 gtk_drag_selection_received (GtkWidget        *widget,
1432                              GtkSelectionData *selection_data,
1433                              guint32           time,
1434                              gpointer          data)
1435 {
1436   GdkDragContext *context;
1437   GtkDragDestInfo *info;
1438   GtkWidget *drop_widget;
1439
1440   drop_widget = data;
1441
1442   context = g_object_get_data (G_OBJECT (widget), "drag-context");
1443   info = gtk_drag_get_dest_info (context, FALSE);
1444
1445   if (info->proxy_data && 
1446       info->proxy_data->target == selection_data->target)
1447     {
1448       gtk_selection_data_set (info->proxy_data,
1449                               selection_data->type,
1450                               selection_data->format,
1451                               selection_data->data,
1452                               selection_data->length);
1453       gtk_main_quit ();
1454       return;
1455     }
1456
1457   if (selection_data->target == gdk_atom_intern_static_string ("DELETE"))
1458     {
1459       gtk_drag_finish (context, TRUE, FALSE, time);
1460     }
1461   else if ((selection_data->target == gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS")) ||
1462            (selection_data->target == gdk_atom_intern_static_string ("XmTRANSFER_FAILURE")))
1463     {
1464       /* Do nothing */
1465     }
1466   else
1467     {
1468       GtkDragDestSite *site;
1469
1470       site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1471
1472       if (site && site->target_list)
1473         {
1474           guint target_info;
1475
1476           if (gtk_target_list_find (site->target_list, 
1477                                     selection_data->target,
1478                                     &target_info))
1479             {
1480               if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1481                   selection_data->length >= 0)
1482                 g_signal_emit_by_name (drop_widget,
1483                                        "drag_data_received",
1484                                        context, info->drop_x, info->drop_y,
1485                                        selection_data,
1486                                        target_info, time);
1487             }
1488         }
1489       else
1490         {
1491           g_signal_emit_by_name (drop_widget,
1492                                  "drag_data_received",
1493                                  context, info->drop_x, info->drop_y,
1494                                  selection_data,
1495                                  0, time);
1496         }
1497       
1498       if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1499         {
1500
1501           gtk_drag_finish (context, 
1502                            (selection_data->length >= 0),
1503                            (context->action == GDK_ACTION_MOVE),
1504                            time);
1505         }
1506       
1507       g_object_unref (drop_widget);
1508     }
1509
1510   g_signal_handlers_disconnect_by_func (widget,
1511                                         gtk_drag_selection_received,
1512                                         data);
1513   
1514   g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
1515   g_object_unref (context);
1516
1517   gtk_drag_release_ipc_widget (widget);
1518 }
1519
1520 static void
1521 prepend_and_ref_widget (GtkWidget *widget,
1522                         gpointer   data)
1523 {
1524   GSList **slist_p = data;
1525
1526   *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1527 }
1528
1529 /*************************************************************
1530  * gtk_drag_find_widget:
1531  *     Recursive callback used to locate widgets for 
1532  *     DRAG_MOTION and DROP_START events.
1533  *   arguments:
1534  *     
1535  *   results:
1536  *************************************************************/
1537
1538 static void
1539 gtk_drag_find_widget (GtkWidget       *widget,
1540                       GtkDragFindData *data)
1541 {
1542   GtkAllocation new_allocation;
1543   gint allocation_to_window_x = 0;
1544   gint allocation_to_window_y = 0;
1545   gint x_offset = 0;
1546   gint y_offset = 0;
1547
1548   if (data->found || !GTK_WIDGET_MAPPED (widget) || !GTK_WIDGET_SENSITIVE (widget))
1549     return;
1550
1551   /* Note that in the following code, we only count the
1552    * position as being inside a WINDOW widget if it is inside
1553    * widget->window; points that are outside of widget->window
1554    * but within the allocation are not counted. This is consistent
1555    * with the way we highlight drag targets.
1556    *
1557    * data->x,y are relative to widget->parent->window (if
1558    * widget is not a toplevel, widget->window otherwise).
1559    * We compute the allocation of widget in the same coordinates,
1560    * clipping to widget->window, and all intermediate
1561    * windows. If data->x,y is inside that, then we translate
1562    * our coordinates to be relative to widget->window and
1563    * recurse.
1564    */  
1565   new_allocation = widget->allocation;
1566
1567   if (widget->parent)
1568     {
1569       gint tx, ty;
1570       GdkWindow *window = widget->window;
1571
1572       /* Compute the offset from allocation-relative to
1573        * window-relative coordinates.
1574        */
1575       allocation_to_window_x = widget->allocation.x;
1576       allocation_to_window_y = widget->allocation.y;
1577
1578       if (!GTK_WIDGET_NO_WINDOW (widget))
1579         {
1580           /* The allocation is relative to the parent window for
1581            * window widgets, not to widget->window.
1582            */
1583           gdk_window_get_position (window, &tx, &ty);
1584           
1585           allocation_to_window_x -= tx;
1586           allocation_to_window_y -= ty;
1587         }
1588
1589       new_allocation.x = 0 + allocation_to_window_x;
1590       new_allocation.y = 0 + allocation_to_window_y;
1591       
1592       while (window && window != widget->parent->window)
1593         {
1594           GdkRectangle window_rect = { 0, 0, 0, 0 };
1595           
1596           gdk_drawable_get_size (window, &window_rect.width, &window_rect.height);
1597
1598           gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1599
1600           gdk_window_get_position (window, &tx, &ty);
1601           new_allocation.x += tx;
1602           x_offset += tx;
1603           new_allocation.y += ty;
1604           y_offset += ty;
1605           
1606           window = gdk_window_get_parent (window);
1607         }
1608
1609       if (!window)              /* Window and widget heirarchies didn't match. */
1610         return;
1611     }
1612
1613   if (data->toplevel ||
1614       ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1615        (data->x < new_allocation.x + new_allocation.width) && 
1616        (data->y < new_allocation.y + new_allocation.height)))
1617     {
1618       /* First, check if the drag is in a valid drop site in
1619        * one of our children 
1620        */
1621       if (GTK_IS_CONTAINER (widget))
1622         {
1623           GtkDragFindData new_data = *data;
1624           GSList *children = NULL;
1625           GSList *tmp_list;
1626           
1627           new_data.x -= x_offset;
1628           new_data.y -= y_offset;
1629           new_data.found = FALSE;
1630           new_data.toplevel = FALSE;
1631           
1632           /* need to reference children temporarily in case the
1633            * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1634            */
1635           gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1636           for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1637             {
1638               if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1639                 gtk_drag_find_widget (tmp_list->data, &new_data);
1640               g_object_unref (tmp_list->data);
1641             }
1642           g_slist_free (children);
1643           
1644           data->found = new_data.found;
1645         }
1646
1647       /* If not, and this widget is registered as a drop site, check to
1648        * emit "drag_motion" to check if we are actually in
1649        * a drop site.
1650        */
1651       if (!data->found &&
1652           g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1653         {
1654           data->found = data->callback (widget,
1655                                         data->context,
1656                                         data->x - x_offset - allocation_to_window_x,
1657                                         data->y - y_offset - allocation_to_window_y,
1658                                         data->time);
1659           /* If so, send a "drag_leave" to the last widget */
1660           if (data->found)
1661             {
1662               if (data->info->widget && data->info->widget != widget)
1663                 {
1664                   gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1665                 }
1666               data->info->widget = widget;
1667             }
1668         }
1669     }
1670 }
1671
1672 static void
1673 gtk_drag_proxy_begin (GtkWidget       *widget, 
1674                       GtkDragDestInfo *dest_info,
1675                       guint32          time)
1676 {
1677   GtkDragSourceInfo *source_info;
1678   GList *tmp_list;
1679   GdkDragContext *context;
1680   GtkWidget *ipc_widget;
1681
1682   if (dest_info->proxy_source)
1683     {
1684       gdk_drag_abort (dest_info->proxy_source->context, time);
1685       gtk_drag_source_info_destroy (dest_info->proxy_source);
1686       dest_info->proxy_source = NULL;
1687     }
1688   
1689   ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1690   context = gdk_drag_begin (ipc_widget->window,
1691                             dest_info->context->targets);
1692
1693   source_info = gtk_drag_get_source_info (context, TRUE);
1694
1695   source_info->ipc_widget = ipc_widget;
1696   source_info->widget = gtk_widget_ref (widget);
1697
1698   source_info->target_list = gtk_target_list_new (NULL, 0);
1699   tmp_list = dest_info->context->targets;
1700   while (tmp_list)
1701     {
1702       gtk_target_list_add (source_info->target_list, 
1703                            GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1704       tmp_list = tmp_list->next;
1705     }
1706
1707   source_info->proxy_dest = dest_info;
1708   
1709   g_signal_connect (ipc_widget,
1710                     "selection_get",
1711                     G_CALLBACK (gtk_drag_selection_get),
1712                     source_info);
1713   
1714   dest_info->proxy_source = source_info;
1715 }
1716
1717 static void
1718 gtk_drag_dest_info_destroy (gpointer data)
1719 {
1720   GtkDragDestInfo *info = data;
1721
1722   g_free (info);
1723 }
1724
1725 static GtkDragDestInfo *
1726 gtk_drag_get_dest_info (GdkDragContext *context,
1727                         gboolean        create)
1728 {
1729   GtkDragDestInfo *info;
1730   static GQuark info_quark = 0;
1731   if (!info_quark)
1732     info_quark = g_quark_from_static_string ("gtk-dest-info");
1733   
1734   info = g_object_get_qdata (G_OBJECT (context), info_quark);
1735   if (!info && create)
1736     {
1737       info = g_new (GtkDragDestInfo, 1);
1738       info->widget = NULL;
1739       info->context = context;
1740       info->proxy_source = NULL;
1741       info->proxy_data = NULL;
1742       info->dropped = FALSE;
1743       info->proxy_drop_wait = FALSE;
1744       g_object_set_qdata_full (G_OBJECT (context), info_quark,
1745                                info, gtk_drag_dest_info_destroy);
1746     }
1747
1748   return info;
1749 }
1750
1751 static GQuark dest_info_quark = 0;
1752
1753 static GtkDragSourceInfo *
1754 gtk_drag_get_source_info (GdkDragContext *context,
1755                           gboolean        create)
1756 {
1757   GtkDragSourceInfo *info;
1758   if (!dest_info_quark)
1759     dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1760   
1761   info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1762   if (!info && create)
1763     {
1764       info = g_new0 (GtkDragSourceInfo, 1);
1765       info->context = context;
1766       g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1767     }
1768
1769   return info;
1770 }
1771
1772 static void
1773 gtk_drag_clear_source_info (GdkDragContext *context)
1774 {
1775   g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1776 }
1777
1778 static void
1779 gtk_drag_dest_realized (GtkWidget *widget)
1780 {
1781   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1782
1783   if (GTK_WIDGET_TOPLEVEL (toplevel))
1784     gdk_window_register_dnd (toplevel->window);
1785 }
1786
1787 static void
1788 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1789                                  GtkWidget *previous_toplevel)
1790 {
1791   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1792
1793   if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1794     gdk_window_register_dnd (toplevel->window);
1795 }
1796
1797 static void
1798 gtk_drag_dest_site_destroy (gpointer data)
1799 {
1800   GtkDragDestSite *site = data;
1801
1802   if (site->proxy_window)
1803     g_object_unref (site->proxy_window);
1804     
1805   if (site->target_list)
1806     gtk_target_list_unref (site->target_list);
1807
1808   g_free (site);
1809 }
1810
1811 /*
1812  * Default drag handlers
1813  */
1814 static void  
1815 gtk_drag_dest_leave (GtkWidget      *widget,
1816                      GdkDragContext *context,
1817                      guint           time)
1818 {
1819   GtkDragDestSite *site;
1820
1821   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1822   g_return_if_fail (site != NULL);
1823
1824   if (site->do_proxy)
1825     {
1826       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1827
1828       if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1829         {
1830           gdk_drag_abort (info->proxy_source->context, time);
1831           gtk_drag_source_info_destroy (info->proxy_source);
1832           info->proxy_source = NULL;
1833         }
1834       
1835       return;
1836     }
1837   else
1838     {
1839       if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1840         gtk_drag_unhighlight (widget);
1841
1842       if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1843         g_signal_emit_by_name (widget, "drag_leave",
1844                                context, time);
1845       
1846       site->have_drag = FALSE;
1847     }
1848 }
1849
1850 static gboolean
1851 gtk_drag_dest_motion (GtkWidget      *widget,
1852                       GdkDragContext *context,
1853                       gint            x,
1854                       gint            y,
1855                       guint           time)
1856 {
1857   GtkDragDestSite *site;
1858   GdkDragAction action = 0;
1859   gboolean retval;
1860
1861   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1862   g_return_val_if_fail (site != NULL, FALSE);
1863
1864   if (site->do_proxy)
1865     {
1866       GdkAtom selection;
1867       GdkEvent *current_event;
1868       GdkWindow *dest_window;
1869       GdkDragProtocol proto;
1870         
1871       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1872
1873       if (!info->proxy_source || info->proxy_source->widget != widget)
1874         gtk_drag_proxy_begin (widget, info, time);
1875
1876       current_event = gtk_get_current_event ();
1877
1878       if (site->proxy_window)
1879         {
1880           dest_window = site->proxy_window;
1881           proto = site->proxy_protocol;
1882         }
1883       else
1884         {
1885           gdk_drag_find_window_for_screen (info->proxy_source->context,
1886                                            NULL,
1887                                            gdk_drawable_get_screen (current_event->dnd.window),
1888                                            current_event->dnd.x_root, 
1889                                            current_event->dnd.y_root,
1890                                            &dest_window, &proto);
1891         }
1892       
1893       gdk_drag_motion (info->proxy_source->context, 
1894                        dest_window, proto,
1895                        current_event->dnd.x_root, 
1896                        current_event->dnd.y_root, 
1897                        context->suggested_action, 
1898                        context->actions, time);
1899
1900       if (!site->proxy_window && dest_window)
1901         g_object_unref (dest_window);
1902
1903       selection = gdk_drag_get_selection (info->proxy_source->context);
1904       if (selection && 
1905           selection != gdk_drag_get_selection (info->context))
1906         gtk_drag_source_check_selection (info->proxy_source, selection, time);
1907
1908       gdk_event_free (current_event);
1909       
1910       return TRUE;
1911     }
1912
1913   if (site->flags & GTK_DEST_DEFAULT_MOTION)
1914     {
1915       if (context->suggested_action & site->actions)
1916         action = context->suggested_action;
1917       else
1918         {
1919           gint i;
1920           
1921           for (i=0; i<8; i++)
1922             {
1923               if ((site->actions & (1 << i)) &&
1924                   (context->actions & (1 << i)))
1925                 {
1926                   action = (1 << i);
1927                   break;
1928                 }
1929             }
1930         }
1931       
1932       if (action && gtk_drag_dest_find_target (widget, context, NULL))
1933         {
1934           if (!site->have_drag)
1935             {
1936               site->have_drag = TRUE;
1937               if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1938                 gtk_drag_highlight (widget);
1939             }
1940           
1941           gdk_drag_status (context, action, time);
1942         }
1943       else
1944         {
1945           gdk_drag_status (context, 0, time);
1946           return TRUE;
1947         }
1948     }
1949
1950   g_signal_emit_by_name (widget, "drag_motion",
1951                          context, x, y, time, &retval);
1952
1953   return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1954 }
1955
1956 static gboolean
1957 gtk_drag_dest_drop (GtkWidget        *widget,
1958                     GdkDragContext   *context,
1959                     gint              x,
1960                     gint              y,
1961                     guint             time)
1962 {
1963   GtkDragDestSite *site;
1964   GtkDragDestInfo *info;
1965
1966   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1967   g_return_val_if_fail (site != NULL, FALSE);
1968
1969   info = gtk_drag_get_dest_info (context, FALSE);
1970   g_return_val_if_fail (info != NULL, FALSE);
1971
1972   info->drop_x = x;
1973   info->drop_y = y;
1974
1975   if (site->do_proxy)
1976     {
1977       if (info->proxy_source || 
1978           (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1979         {
1980           gtk_drag_drop (info->proxy_source, time);
1981         }
1982       else
1983         {
1984           /* We need to synthesize a motion event, wait for a status,
1985            * and, if we get a good one, do a drop.
1986            */
1987           
1988           GdkEvent *current_event;
1989           GdkAtom selection;
1990           GdkWindow *dest_window;
1991           GdkDragProtocol proto;
1992           
1993           gtk_drag_proxy_begin (widget, info, time);
1994           info->proxy_drop_wait = TRUE;
1995           info->proxy_drop_time = time;
1996           
1997           current_event = gtk_get_current_event ();
1998
1999           if (site->proxy_window)
2000             {
2001               dest_window = site->proxy_window;
2002               proto = site->proxy_protocol;
2003             }
2004           else
2005             {
2006               gdk_drag_find_window_for_screen (info->proxy_source->context,
2007                                                NULL,
2008                                                gdk_drawable_get_screen (current_event->dnd.window),
2009                                                current_event->dnd.x_root, 
2010                                                current_event->dnd.y_root,
2011                                                &dest_window, &proto);
2012             }
2013
2014           gdk_drag_motion (info->proxy_source->context, 
2015                            dest_window, proto,
2016                            current_event->dnd.x_root, 
2017                            current_event->dnd.y_root, 
2018                            context->suggested_action, 
2019                            context->actions, time);
2020
2021           if (!site->proxy_window && dest_window)
2022             g_object_unref (dest_window);
2023
2024           selection = gdk_drag_get_selection (info->proxy_source->context);
2025           if (selection && 
2026               selection != gdk_drag_get_selection (info->context))
2027             gtk_drag_source_check_selection (info->proxy_source, selection, time);
2028
2029           gdk_event_free (current_event);
2030         }
2031
2032       return TRUE;
2033     }
2034   else
2035     {
2036       gboolean retval;
2037
2038       if (site->flags & GTK_DEST_DEFAULT_DROP)
2039         {
2040           GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
2041       
2042           if (target == GDK_NONE)
2043             {
2044               gtk_drag_finish (context, FALSE, FALSE, time);
2045               return TRUE;
2046             }
2047           else
2048             gtk_drag_get_data (widget, context, target, time);
2049         }
2050
2051       g_signal_emit_by_name (widget, "drag_drop",
2052                              context, x, y, time, &retval);
2053
2054       return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
2055     }
2056 }
2057
2058 /***************
2059  * Source side *
2060  ***************/
2061
2062 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
2063  * so that we can set the icon from the source site information
2064  */
2065 static GdkDragContext *
2066 gtk_drag_begin_internal (GtkWidget         *widget,
2067                          GtkDragSourceSite *site,
2068                          GtkTargetList     *target_list,
2069                          GdkDragAction      actions,
2070                          gint               button,
2071                          GdkEvent          *event)
2072 {
2073   GtkDragSourceInfo *info;
2074   GList *targets = NULL;
2075   GList *tmp_list;
2076   guint32 time = GDK_CURRENT_TIME;
2077   GdkDragAction possible_actions, suggested_action;
2078   GdkDragContext *context;
2079   GtkWidget *ipc_widget;
2080   GdkCursor *cursor;
2081  
2082   ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
2083   
2084   gtk_drag_get_event_actions (event, button, actions,
2085                               &suggested_action, &possible_actions);
2086   
2087   cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), 
2088                                 suggested_action,
2089                                 NULL);
2090   
2091   if (event)
2092     time = gdk_event_get_time (event);
2093
2094   if (gdk_pointer_grab (ipc_widget->window, FALSE,
2095                         GDK_POINTER_MOTION_MASK |
2096                         GDK_BUTTON_RELEASE_MASK, NULL,
2097                         cursor, time) != GDK_GRAB_SUCCESS)
2098     {
2099       gtk_drag_release_ipc_widget (ipc_widget);
2100       return NULL;
2101     }
2102
2103   gdk_keyboard_grab (ipc_widget->window, FALSE, time);
2104     
2105   /* We use a GTK grab here to override any grabs that the widget
2106    * we are dragging from might have held
2107    */
2108   gtk_grab_add (ipc_widget);
2109   
2110   tmp_list = g_list_last (target_list->list);
2111   while (tmp_list)
2112     {
2113       GtkTargetPair *pair = tmp_list->data;
2114       targets = g_list_prepend (targets, 
2115                                 GINT_TO_POINTER (pair->target));
2116       tmp_list = tmp_list->prev;
2117     }
2118
2119   source_widgets = g_slist_prepend (source_widgets, ipc_widget);
2120
2121   context = gdk_drag_begin (ipc_widget->window, targets);
2122   g_list_free (targets);
2123   
2124   info = gtk_drag_get_source_info (context, TRUE);
2125   
2126   info->ipc_widget = ipc_widget;
2127   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), info);
2128
2129   info->widget = gtk_widget_ref (widget);
2130   
2131   info->button = button;
2132   info->cursor = cursor;
2133   info->target_list = target_list;
2134   gtk_target_list_ref (target_list);
2135
2136   info->possible_actions = actions;
2137
2138   info->status = GTK_DRAG_STATUS_DRAG;
2139   info->last_event = NULL;
2140   info->selections = NULL;
2141   info->icon_window = NULL;
2142   info->destroy_icon = FALSE;
2143
2144   /* Set cur_x, cur_y here so if the "drag_begin" signal shows
2145    * the drag icon, it will be in the right place
2146    */
2147   if (event && event->type == GDK_MOTION_NOTIFY)
2148     {
2149       info->cur_screen = gtk_widget_get_screen (widget);
2150       info->cur_x = event->motion.x_root;
2151       info->cur_y = event->motion.y_root;
2152     }
2153   else 
2154     {
2155       gdk_display_get_pointer (gtk_widget_get_display (widget),
2156                                &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
2157     }
2158
2159   g_signal_emit_by_name (widget, "drag_begin", info->context);
2160
2161   /* Ensure that we have an icon before we start the drag; the
2162    * application may have set one in ::drag_begin, or it may
2163    * not have set one.
2164    */
2165   if (!info->icon_window && !info->icon_pixbuf)
2166     {
2167       if (!site || site->icon_type == GTK_IMAGE_EMPTY)
2168         gtk_drag_set_icon_default (context);
2169       else
2170         switch (site->icon_type)
2171           {
2172           case GTK_IMAGE_PIXMAP:
2173             gtk_drag_set_icon_pixmap (context,
2174                                       site->colormap,
2175                                       site->icon_data.pixmap.pixmap,
2176                                       site->icon_mask,
2177                                       -2, -2);
2178             break;
2179           case GTK_IMAGE_PIXBUF:
2180             gtk_drag_set_icon_pixbuf (context,
2181                                       site->icon_data.pixbuf.pixbuf,
2182                                       -2, -2);
2183             break;
2184           case GTK_IMAGE_STOCK:
2185             gtk_drag_set_icon_stock (context,
2186                                      site->icon_data.stock.stock_id,
2187                                      -2, -2);
2188             break;
2189           case GTK_IMAGE_ICON_NAME:
2190             gtk_drag_set_icon_name (context,
2191                                     site->icon_data.name.icon_name,
2192                                     -2, -2);
2193             break;
2194           case GTK_IMAGE_EMPTY:
2195           default:
2196             g_assert_not_reached();
2197             break;
2198           }
2199     }
2200
2201   /* We need to composite the icon into the cursor, if we are
2202    * not using an icon window.
2203    */
2204   if (info->icon_pixbuf)  
2205     {
2206       cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), 
2207                                     suggested_action,
2208                                     info);
2209   
2210       if (cursor != info->cursor)
2211         {
2212           gdk_pointer_grab (widget->window, FALSE,
2213                             GDK_POINTER_MOTION_MASK |
2214                             GDK_BUTTON_RELEASE_MASK,
2215                             NULL,
2216                             cursor, time);
2217           info->cursor = cursor;
2218         }
2219     }
2220     
2221   if (event && event->type == GDK_MOTION_NOTIFY)
2222     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
2223   else
2224     gtk_drag_update (info, info->screen, info->cur_x, info->cur_y, event);
2225
2226   info->start_x = info->cur_x;
2227   info->start_y = info->cur_y;
2228
2229   g_signal_connect (info->ipc_widget, "button_release_event",
2230                     G_CALLBACK (gtk_drag_button_release_cb), info);
2231   g_signal_connect (info->ipc_widget, "motion_notify_event",
2232                     G_CALLBACK (gtk_drag_motion_cb), info);
2233   g_signal_connect (info->ipc_widget, "key_press_event",
2234                     G_CALLBACK (gtk_drag_key_cb), info);
2235   g_signal_connect (info->ipc_widget, "key_release_event",
2236                     G_CALLBACK (gtk_drag_key_cb), info);
2237   g_signal_connect (info->ipc_widget, "selection_get",
2238                     G_CALLBACK (gtk_drag_selection_get), info);
2239
2240   info->have_grab = TRUE;
2241   info->grab_time = time;
2242
2243   return info->context;
2244 }
2245
2246 /**
2247  * gtk_drag_begin:
2248  * @widget: the source widget.
2249  * @targets: The targets (data formats) in which the
2250  *    source can provide the data.
2251  * @actions: A bitmask of the allowed drag actions for this drag.
2252  * @button: The button the user clicked to start the drag.
2253  * @event: The event that triggered the start of the drag.
2254  * 
2255  * Initiates a drag on the source side. The function
2256  * only needs to be used when the application is
2257  * starting drags itself, and is not needed when
2258  * gtk_drag_source_set() is used.
2259  * 
2260  * Return value: the context for this drag.
2261  **/
2262 GdkDragContext *
2263 gtk_drag_begin (GtkWidget         *widget,
2264                 GtkTargetList     *targets,
2265                 GdkDragAction      actions,
2266                 gint               button,
2267                 GdkEvent          *event)
2268 {
2269   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2270   g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
2271   g_return_val_if_fail (targets != NULL, NULL);
2272
2273   return gtk_drag_begin_internal (widget, NULL, targets,
2274                                   actions, button, event);
2275 }
2276
2277 /*************************************************************
2278  * gtk_drag_source_set:
2279  *     Register a drop site, and possibly add default behaviors.
2280  *   arguments:
2281  *     widget:
2282  *     start_button_mask: Mask of allowed buttons to start drag
2283  *     targets:           Table of targets for this source
2284  *     n_targets:
2285  *     actions:           Actions allowed for this source
2286  *   results:
2287  *************************************************************/
2288
2289 void 
2290 gtk_drag_source_set (GtkWidget            *widget,
2291                      GdkModifierType       start_button_mask,
2292                      const GtkTargetEntry *targets,
2293                      gint                  n_targets,
2294                      GdkDragAction         actions)
2295 {
2296   GtkDragSourceSite *site;
2297
2298   g_return_if_fail (GTK_IS_WIDGET (widget));
2299
2300   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2301
2302   gtk_widget_add_events (widget,
2303                          gtk_widget_get_events (widget) |
2304                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2305                          GDK_BUTTON_MOTION_MASK);
2306
2307   if (site)
2308     {
2309       if (site->target_list)
2310         gtk_target_list_unref (site->target_list);
2311     }
2312   else
2313     {
2314       site = g_new0 (GtkDragSourceSite, 1);
2315
2316       site->icon_type = GTK_IMAGE_EMPTY;
2317       
2318       g_signal_connect (widget, "button_press_event",
2319                         G_CALLBACK (gtk_drag_source_event_cb),
2320                         site);
2321       g_signal_connect (widget, "button_release_event",
2322                         G_CALLBACK (gtk_drag_source_event_cb),
2323                         site);
2324       g_signal_connect (widget, "motion_notify_event",
2325                         G_CALLBACK (gtk_drag_source_event_cb),
2326                         site);
2327       
2328       g_object_set_data_full (G_OBJECT (widget),
2329                               I_("gtk-site-data"), 
2330                               site, gtk_drag_source_site_destroy);
2331     }
2332
2333   site->start_button_mask = start_button_mask;
2334
2335   site->target_list = gtk_target_list_new (targets, n_targets);
2336
2337   site->actions = actions;
2338 }
2339
2340 /*************************************************************
2341  * gtk_drag_source_unset
2342  *     Unregister this widget as a drag source.
2343  *   arguments:
2344  *     widget:
2345  *   results:
2346  *************************************************************/
2347
2348 void 
2349 gtk_drag_source_unset (GtkWidget        *widget)
2350 {
2351   GtkDragSourceSite *site;
2352
2353   g_return_if_fail (GTK_IS_WIDGET (widget));
2354
2355   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2356
2357   if (site)
2358     {
2359       g_signal_handlers_disconnect_by_func (widget,
2360                                             gtk_drag_source_event_cb,
2361                                             site);
2362       g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
2363     }
2364 }
2365
2366 /**
2367  * gtk_drag_source_get_target_list:
2368  * @widget: a #GtkWidget
2369  *
2370  * Gets the list of targets this widget can provide for
2371  * drag-and-drop.
2372  *
2373  * Return value: the #GtkTargetList, or %NULL if none
2374  *
2375  * Since: 2.4
2376  **/
2377 GtkTargetList *
2378 gtk_drag_source_get_target_list (GtkWidget *widget)
2379 {
2380   GtkDragSourceSite *site;
2381
2382   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2383
2384   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2385
2386   return site ? site->target_list : NULL;
2387 }
2388
2389 /**
2390  * gtk_drag_source_set_target_list:
2391  * @widget: a #GtkWidget that's a drag source
2392  * @target_list: list of draggable targets, or %NULL for none
2393  *
2394  * Changes the target types that this widget offers for drag-and-drop.
2395  * The widget must first be made into a drag source with
2396  * gtk_drag_source_set().
2397  *
2398  * Since: 2.4
2399  **/
2400 void
2401 gtk_drag_source_set_target_list (GtkWidget     *widget,
2402                                  GtkTargetList *target_list)
2403 {
2404   GtkDragSourceSite *site;
2405
2406   g_return_if_fail (GTK_IS_WIDGET (widget));
2407
2408   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2409   if (site == NULL)
2410     {
2411       g_warning ("gtk_drag_source_set_target_list() requires the widget "
2412                  "to already be a drag source.");
2413       return;
2414     }
2415
2416   if (target_list)
2417     gtk_target_list_ref (target_list);
2418
2419   if (site->target_list)
2420     gtk_target_list_unref (site->target_list);
2421
2422   site->target_list = target_list;
2423 }
2424
2425 /**
2426  * gtk_drag_source_add_text_targets:
2427  * @widget: a #GtkWidget that's is a drag source
2428  *
2429  * Add the text targets supported by #GtkSelection to
2430  * the target list of the drag source.  The targets
2431  * are added with @info = 0. If you need another value, 
2432  * use gtk_target_list_add_text_targets() and
2433  * gtk_drag_source_set_target_list().
2434  * 
2435  * Since: 2.6
2436  **/
2437 void
2438 gtk_drag_source_add_text_targets (GtkWidget *widget)
2439 {
2440   GtkTargetList *target_list;
2441
2442   target_list = gtk_drag_source_get_target_list (widget);
2443   if (target_list)
2444     gtk_target_list_ref (target_list);
2445   else
2446     target_list = gtk_target_list_new (NULL, 0);
2447   gtk_target_list_add_text_targets (target_list, 0);
2448   gtk_drag_source_set_target_list (widget, target_list);
2449   gtk_target_list_unref (target_list);
2450 }
2451
2452 /**
2453  * gtk_drag_source_add_image_targets:
2454  * @widget: a #GtkWidget that's is a drag source
2455  *
2456  * Add the writable image targets supported by #GtkSelection to
2457  * the target list of the drag source. The targets
2458  * are added with @info = 0. If you need another value, 
2459  * use gtk_target_list_add_image_targets() and
2460  * gtk_drag_source_set_target_list().
2461  * 
2462  * Since: 2.6
2463  **/
2464 void
2465 gtk_drag_source_add_image_targets (GtkWidget *widget)
2466 {
2467   GtkTargetList *target_list;
2468
2469   target_list = gtk_drag_source_get_target_list (widget);
2470   if (target_list)
2471     gtk_target_list_ref (target_list);
2472   else
2473     target_list = gtk_target_list_new (NULL, 0);
2474   gtk_target_list_add_image_targets (target_list, 0, TRUE);
2475   gtk_drag_source_set_target_list (widget, target_list);
2476   gtk_target_list_unref (target_list);
2477 }
2478
2479 /**
2480  * gtk_drag_source_add_uri_targets:
2481  * @widget: a #GtkWidget that's is a drag source
2482  *
2483  * Add the URI targets supported by #GtkSelection to
2484  * the target list of the drag source.  The targets
2485  * are added with @info = 0. If you need another value, 
2486  * use gtk_target_list_add_uri_targets() and
2487  * gtk_drag_source_set_target_list().
2488  * 
2489  * Since: 2.6
2490  **/
2491 void
2492 gtk_drag_source_add_uri_targets (GtkWidget *widget)
2493 {
2494   GtkTargetList *target_list;
2495
2496   target_list = gtk_drag_source_get_target_list (widget);
2497   if (target_list)
2498     gtk_target_list_ref (target_list);
2499   else
2500     target_list = gtk_target_list_new (NULL, 0);
2501   gtk_target_list_add_uri_targets (target_list, 0);
2502   gtk_drag_source_set_target_list (widget, target_list);
2503   gtk_target_list_unref (target_list);
2504 }
2505
2506 static void
2507 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2508 {
2509   switch (site->icon_type)
2510     {
2511     case GTK_IMAGE_EMPTY:
2512       break;
2513     case GTK_IMAGE_PIXMAP:
2514       if (site->icon_data.pixmap.pixmap)
2515         g_object_unref (site->icon_data.pixmap.pixmap);
2516       if (site->icon_mask)
2517         g_object_unref (site->icon_mask);
2518       break;
2519     case GTK_IMAGE_PIXBUF:
2520       g_object_unref (site->icon_data.pixbuf.pixbuf);
2521       break;
2522     case GTK_IMAGE_STOCK:
2523       g_free (site->icon_data.stock.stock_id);
2524       break;
2525     case GTK_IMAGE_ICON_NAME:
2526       g_free (site->icon_data.name.icon_name);
2527       break;
2528     default:
2529       g_assert_not_reached();
2530       break;
2531     }
2532   site->icon_type = GTK_IMAGE_EMPTY;
2533   
2534   if (site->colormap)
2535     g_object_unref (site->colormap);
2536   site->colormap = NULL;
2537 }
2538
2539 /**
2540  * gtk_drag_source_set_icon:
2541  * @widget: a #GtkWidget
2542  * @colormap: the colormap of the icon
2543  * @pixmap: the image data for the icon
2544  * @mask: the transparency mask for an image.
2545  * 
2546  * Sets the icon that will be used for drags from a particular widget
2547  * from a pixmap/mask. GTK+ retains references for the arguments, and 
2548  * will release them when they are no longer needed.
2549  * Use gtk_drag_source_set_icon_pixbuf() instead.
2550  **/
2551 void 
2552 gtk_drag_source_set_icon (GtkWidget     *widget,
2553                           GdkColormap   *colormap,
2554                           GdkPixmap     *pixmap,
2555                           GdkBitmap     *mask)
2556 {
2557   GtkDragSourceSite *site;
2558
2559   g_return_if_fail (GTK_IS_WIDGET (widget));
2560   g_return_if_fail (GDK_IS_COLORMAP (colormap));
2561   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2562   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2563
2564   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2565   g_return_if_fail (site != NULL);
2566   
2567   g_object_ref (colormap);
2568   g_object_ref (pixmap);
2569   if (mask)
2570     g_object_ref (mask);
2571
2572   gtk_drag_source_unset_icon (site);
2573
2574   site->icon_type = GTK_IMAGE_PIXMAP;
2575   
2576   site->icon_data.pixmap.pixmap = pixmap;
2577   site->icon_mask = mask;
2578   site->colormap = colormap;
2579 }
2580
2581 /**
2582  * gtk_drag_source_set_icon_pixbuf:
2583  * @widget: a #GtkWidget
2584  * @pixbuf: the #GdkPixbuf for the drag icon
2585  * 
2586  * Sets the icon that will be used for drags from a particular widget
2587  * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will 
2588  * release it when it is no longer needed.
2589  **/
2590 void 
2591 gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
2592                                  GdkPixbuf   *pixbuf)
2593 {
2594   GtkDragSourceSite *site;
2595
2596   g_return_if_fail (GTK_IS_WIDGET (widget));
2597   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2598
2599   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2600   g_return_if_fail (site != NULL); 
2601   g_object_ref (pixbuf);
2602
2603   gtk_drag_source_unset_icon (site);
2604
2605   site->icon_type = GTK_IMAGE_PIXBUF;
2606   site->icon_data.pixbuf.pixbuf = pixbuf;
2607 }
2608
2609 /**
2610  * gtk_drag_source_set_icon_stock:
2611  * @widget: a #GtkWidget
2612  * @stock_id: the ID of the stock icon to use
2613  *
2614  * Sets the icon that will be used for drags from a particular source
2615  * to a stock icon. 
2616  **/
2617 void 
2618 gtk_drag_source_set_icon_stock (GtkWidget   *widget,
2619                                 const gchar *stock_id)
2620 {
2621   GtkDragSourceSite *site;
2622
2623   g_return_if_fail (GTK_IS_WIDGET (widget));
2624   g_return_if_fail (stock_id != NULL);
2625
2626   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2627   g_return_if_fail (site != NULL);
2628   
2629   gtk_drag_source_unset_icon (site);
2630
2631   site->icon_type = GTK_IMAGE_STOCK;
2632   site->icon_data.stock.stock_id = g_strdup (stock_id);
2633 }
2634
2635 /**
2636  * gtk_drag_source_set_icon_name:
2637  * @widget: a #GtkWidget
2638  * @icon_name: name of icon to use
2639  * 
2640  * Sets the icon that will be used for drags from a particular source
2641  * to a themed icon. See the docs for #GtkIconTheme for more details.
2642  *
2643  * Since: 2.8
2644  **/
2645 void 
2646 gtk_drag_source_set_icon_name (GtkWidget   *widget,
2647                                const gchar *icon_name)
2648 {
2649   GtkDragSourceSite *site;
2650
2651   g_return_if_fail (GTK_IS_WIDGET (widget));
2652   g_return_if_fail (icon_name != NULL);
2653
2654   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2655   g_return_if_fail (site != NULL);
2656
2657   gtk_drag_source_unset_icon (site);
2658
2659   site->icon_type = GTK_IMAGE_ICON_NAME;
2660   site->icon_data.name.icon_name = g_strdup (icon_name);
2661 }
2662
2663 static void
2664 gtk_drag_get_icon (GtkDragSourceInfo *info,
2665                    GtkWidget        **icon_window,
2666                    gint              *hot_x,
2667                    gint              *hot_y)
2668 {
2669   if (get_can_change_screen (info->icon_window))
2670     gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2671                            info->cur_screen);
2672       
2673   if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2674     {
2675       if (!info->fallback_icon)
2676         {
2677           gint save_hot_x, save_hot_y;
2678           gboolean save_destroy_icon;
2679           GtkWidget *save_icon_window;
2680           
2681           /* HACK to get the appropriate icon
2682            */
2683           save_icon_window = info->icon_window;
2684           save_hot_x = info->hot_x;
2685           save_hot_y = info->hot_x;
2686           save_destroy_icon = info->destroy_icon;
2687
2688           info->icon_window = NULL;
2689           gtk_drag_set_icon_default (info->context);
2690           info->fallback_icon = info->icon_window;
2691           
2692           info->icon_window = save_icon_window;
2693           info->hot_x = save_hot_x;
2694           info->hot_y = save_hot_y;
2695           info->destroy_icon = save_destroy_icon;
2696         }
2697       
2698       gtk_widget_hide (info->icon_window);
2699       
2700       *icon_window = info->fallback_icon;
2701       gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2702       
2703       if (!default_icon_pixmap)
2704         {
2705           *hot_x = -2;
2706           *hot_y = -2;
2707         }
2708       else
2709         {
2710           *hot_x = default_icon_hot_x;
2711           *hot_y = default_icon_hot_y;
2712         }
2713     }
2714   else
2715     {
2716       if (info->fallback_icon)
2717         gtk_widget_hide (info->fallback_icon);
2718       
2719       *icon_window = info->icon_window;
2720       *hot_x = info->hot_x;
2721       *hot_y = info->hot_y;
2722     }
2723 }
2724
2725 static void
2726 gtk_drag_update_icon (GtkDragSourceInfo *info)
2727 {
2728   if (info->icon_window)
2729     {
2730       GtkWidget *icon_window;
2731       gint hot_x, hot_y;
2732   
2733       gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
2734       
2735       gtk_window_move (GTK_WINDOW (icon_window), 
2736                        info->cur_x - hot_x, 
2737                        info->cur_y - hot_y);
2738
2739       if (GTK_WIDGET_VISIBLE (icon_window))
2740         gdk_window_raise (icon_window->window);
2741       else
2742         gtk_widget_show (icon_window);
2743     }
2744 }
2745
2746 static void 
2747 gtk_drag_set_icon_window (GdkDragContext *context,
2748                           GtkWidget      *widget,
2749                           gint            hot_x,
2750                           gint            hot_y,
2751                           gboolean        destroy_on_release)
2752 {
2753   GtkDragSourceInfo *info;
2754
2755   info = gtk_drag_get_source_info (context, FALSE);
2756   gtk_drag_remove_icon (info);
2757
2758   if (widget)
2759     gtk_widget_ref (widget);  
2760   
2761   info->icon_window = widget;
2762   info->hot_x = hot_x;
2763   info->hot_y = hot_y;
2764   info->destroy_icon = destroy_on_release;
2765
2766   if (widget && info->icon_pixbuf)
2767     {
2768       g_object_unref (info->icon_pixbuf);
2769       info->icon_pixbuf = NULL;
2770     }
2771
2772   gtk_drag_update_cursor (info);
2773   gtk_drag_update_icon (info);
2774 }
2775
2776 /**
2777  * gtk_drag_set_icon_widget:
2778  * @context: the context for a drag. (This must be called 
2779           with a  context for the source side of a drag)
2780  * @widget: a toplevel window to use as an icon.
2781  * @hot_x: the X offset within @widget of the hotspot.
2782  * @hot_y: the Y offset within @widget of the hotspot.
2783  * 
2784  * Changes the icon for a widget to a given widget. GTK+
2785  * will not destroy the icon, so if you don't want
2786  * it to persist, you should connect to the "drag_end" 
2787  * signal and destroy it yourself.
2788  **/
2789 void 
2790 gtk_drag_set_icon_widget (GdkDragContext    *context,
2791                           GtkWidget         *widget,
2792                           gint               hot_x,
2793                           gint               hot_y)
2794 {
2795   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2796   g_return_if_fail (context->is_source);
2797   g_return_if_fail (GTK_IS_WIDGET (widget));
2798
2799   gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2800 }
2801
2802 static void
2803 icon_window_realize (GtkWidget *window,
2804                      GdkPixbuf *pixbuf)
2805 {
2806   GdkPixmap *pixmap;
2807   GdkPixmap *mask;
2808
2809   gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
2810                                                   gtk_widget_get_colormap (window),
2811                                                   &pixmap, &mask, 128);
2812   
2813   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2814   g_object_unref (pixmap);
2815   
2816   if (mask)
2817     {
2818       gtk_widget_shape_combine_mask (window, mask, 0, 0);
2819       g_object_unref (mask);
2820     }
2821 }
2822
2823 static void
2824 set_icon_stock_pixbuf (GdkDragContext    *context,
2825                        const gchar       *stock_id,
2826                        GdkPixbuf         *pixbuf,
2827                        gint               hot_x,
2828                        gint               hot_y,
2829                        gboolean           force_window)
2830 {
2831   GtkWidget *window;
2832   gint width, height;
2833   GdkScreen *screen;
2834   GdkDisplay *display;
2835
2836   g_return_if_fail (context != NULL);
2837   g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2838   g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2839
2840   screen = gdk_drawable_get_screen (context->source_window);
2841
2842   /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
2843   gtk_widget_push_colormap (NULL);
2844   window = gtk_window_new (GTK_WINDOW_POPUP);
2845   gtk_window_set_screen (GTK_WINDOW (window), screen);
2846   set_can_change_screen (window, TRUE);
2847   gtk_widget_pop_colormap ();
2848
2849   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2850   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2851
2852   if (stock_id)
2853     {
2854       pixbuf = gtk_widget_render_icon (window, stock_id,
2855                                        GTK_ICON_SIZE_DND, NULL);
2856
2857       if (!pixbuf)
2858         {
2859           g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2860           gtk_widget_destroy (window);
2861           return;
2862         }
2863
2864     }
2865   else
2866     g_object_ref (pixbuf);
2867
2868   display = gdk_drawable_get_display (context->source_window);
2869   width = gdk_pixbuf_get_width (pixbuf);
2870   height = gdk_pixbuf_get_height (pixbuf);
2871
2872   if (!force_window &&
2873       gtk_drag_can_use_rgba_cursor (display, width + 2, height + 2))
2874     {
2875       GtkDragSourceInfo *info;
2876
2877       gtk_widget_destroy (window);
2878
2879       info = gtk_drag_get_source_info (context, FALSE);
2880
2881       if (info->icon_pixbuf)
2882         g_object_unref (info->icon_pixbuf);
2883       info->icon_pixbuf = pixbuf;
2884
2885       gtk_drag_set_icon_window (context, NULL, hot_x, hot_y, TRUE);
2886     }
2887   else
2888     {
2889       gtk_widget_set_size_request (window, width, height);
2890
2891       g_signal_connect_closure (window, "realize",
2892                                 g_cclosure_new (G_CALLBACK (icon_window_realize),
2893                                                 pixbuf,
2894                                                 (GClosureNotify)g_object_unref),
2895                                 FALSE);
2896                     
2897       gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2898    }
2899 }
2900
2901 /**
2902  * gtk_drag_set_icon_pixbuf:
2903  * @context: the context for a drag. (This must be called 
2904  *            with a  context for the source side of a drag)
2905  * @pixbuf: the #GdkPixbuf to use as the drag icon.
2906  * @hot_x: the X offset within @widget of the hotspot.
2907  * @hot_y: the Y offset within @widget of the hotspot.
2908  * 
2909  * Sets @pixbuf as the icon for a given drag.
2910  **/
2911 void 
2912 gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
2913                            GdkPixbuf      *pixbuf,
2914                            gint            hot_x,
2915                            gint            hot_y)
2916 {
2917   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2918   g_return_if_fail (context->is_source);
2919   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2920
2921   set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
2922 }
2923
2924 /**
2925  * gtk_drag_set_icon_stock:
2926  * @context: the context for a drag. (This must be called 
2927  *            with a  context for the source side of a drag)
2928  * @stock_id: the ID of the stock icon to use for the drag.
2929  * @hot_x: the X offset within the icon of the hotspot.
2930  * @hot_y: the Y offset within the icon of the hotspot.
2931  * 
2932  * Sets the icon for a given drag from a stock ID.
2933  **/
2934 void 
2935 gtk_drag_set_icon_stock  (GdkDragContext *context,
2936                           const gchar    *stock_id,
2937                           gint            hot_x,
2938                           gint            hot_y)
2939 {
2940   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2941   g_return_if_fail (context->is_source);
2942   g_return_if_fail (stock_id != NULL);
2943   
2944   set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y, FALSE);
2945 }
2946
2947 /**
2948  * gtk_drag_set_icon_pixmap:
2949  * @context: the context for a drag. (This must be called 
2950  *            with a  context for the source side of a drag)
2951  * @colormap: the colormap of the icon 
2952  * @pixmap: the image data for the icon 
2953  * @mask: the transparency mask for the icon
2954  * @hot_x: the X offset within @pixmap of the hotspot.
2955  * @hot_y: the Y offset within @pixmap of the hotspot.
2956  * 
2957  * Sets @pixmap as the icon for a given drag. GTK+ retains
2958  * references for the arguments, and will release them when
2959  * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2960  * will be more convenient to use.
2961  **/
2962 void 
2963 gtk_drag_set_icon_pixmap (GdkDragContext    *context,
2964                           GdkColormap       *colormap,
2965                           GdkPixmap         *pixmap,
2966                           GdkBitmap         *mask,
2967                           gint               hot_x,
2968                           gint               hot_y)
2969 {
2970   GtkWidget *window;
2971   GdkScreen *screen;
2972   gint width, height;
2973       
2974   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2975   g_return_if_fail (context->is_source);
2976   g_return_if_fail (GDK_IS_COLORMAP (colormap));
2977   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2978   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2979
2980   screen = gdk_colormap_get_screen (colormap);
2981   
2982   g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
2983   g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
2984   
2985   gdk_drawable_get_size (pixmap, &width, &height);
2986
2987   gtk_widget_push_colormap (colormap);
2988
2989   window = gtk_window_new (GTK_WINDOW_POPUP);
2990   gtk_window_set_screen (GTK_WINDOW (window), screen);
2991   set_can_change_screen (window, FALSE);
2992   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2993   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2994
2995   gtk_widget_pop_colormap ();
2996
2997   gtk_widget_set_size_request (window, width, height);
2998   gtk_widget_realize (window);
2999
3000   gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
3001   
3002   if (mask)
3003     gtk_widget_shape_combine_mask (window, mask, 0, 0);
3004
3005   gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3006 }
3007
3008 /**
3009  * gtk_drag_set_icon_name:
3010  * @context: the context for a drag. (This must be called 
3011  *            with a context for the source side of a drag)
3012  * @icon_name: name of icon to use
3013  * @hot_x: the X offset of the hotspot within the icon
3014  * @hot_y: the Y offset of the hotspot within the icon
3015  * 
3016  * Sets the icon for a given drag from a named themed icon. See
3017  * the docs for #GtkIconTheme for more details. Note that the
3018  * size of the icon depends on the icon theme (the icon is
3019  * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus 
3020  * @hot_x and @hot_y have to be used with care.
3021  *
3022  * Since: 2.8
3023  **/
3024 void 
3025 gtk_drag_set_icon_name (GdkDragContext *context,
3026                         const gchar    *icon_name,
3027                         gint            hot_x,
3028                         gint            hot_y)
3029 {
3030   GdkScreen *screen;
3031   GtkSettings *settings;
3032   GtkIconTheme *icon_theme;
3033   GdkPixbuf *pixbuf;
3034   gint width, height, icon_size;
3035
3036   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3037   g_return_if_fail (context->is_source);
3038   g_return_if_fail (icon_name != NULL);
3039
3040   screen = gdk_drawable_get_screen (context->source_window);
3041   g_return_if_fail (screen != NULL);
3042
3043   settings = gtk_settings_get_for_screen (screen);
3044   if (gtk_icon_size_lookup_for_settings (settings,
3045                                          GTK_ICON_SIZE_DND,
3046                                          &width, &height))
3047     icon_size = MAX (width, height);
3048   else 
3049     icon_size = 32; /* default value for GTK_ICON_SIZE_DND */ 
3050
3051   icon_theme = gtk_icon_theme_get_for_screen (screen);
3052
3053   pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name,
3054                                      icon_size, 0, NULL);
3055   if (pixbuf)
3056     set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3057   else
3058     g_warning ("Cannot load drag icon from icon name %s", icon_name);
3059 }
3060
3061 /**
3062  * gtk_drag_set_icon_default:
3063  * @context: the context for a drag. (This must be called 
3064              with a  context for the source side of a drag)
3065  * 
3066  * Sets the icon for a particular drag to the default
3067  * icon.
3068  **/
3069 void 
3070 gtk_drag_set_icon_default (GdkDragContext    *context)
3071 {
3072   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3073   g_return_if_fail (context->is_source);
3074
3075   if (!default_icon_pixmap)
3076     gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
3077   else
3078     gtk_drag_set_icon_pixmap (context, 
3079                               default_icon_colormap, 
3080                               default_icon_pixmap, 
3081                               default_icon_mask,
3082                               default_icon_hot_x,
3083                               default_icon_hot_y);
3084 }
3085
3086 /**
3087  * gtk_drag_set_default_icon:
3088  * @colormap: the colormap of the icon
3089  * @pixmap: the image data for the icon
3090  * @mask: the transparency mask for an image.
3091  * @hot_x: The X offset within @widget of the hotspot.
3092  * @hot_y: The Y offset within @widget of the hotspot.
3093  * 
3094  * Changes the default drag icon. GTK+ retains references for the
3095  * arguments, and will release them when they are no longer needed.
3096  * This function is obsolete. The default icon should now be changed
3097  * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
3098  **/
3099 void 
3100 gtk_drag_set_default_icon (GdkColormap   *colormap,
3101                            GdkPixmap     *pixmap,
3102                            GdkBitmap     *mask,
3103                            gint           hot_x,
3104                            gint           hot_y)
3105 {
3106   g_return_if_fail (GDK_IS_COLORMAP (colormap));
3107   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
3108   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
3109   
3110   if (default_icon_colormap)
3111     g_object_unref (default_icon_colormap);
3112   if (default_icon_pixmap)
3113     g_object_unref (default_icon_pixmap);
3114   if (default_icon_mask)
3115     g_object_unref (default_icon_mask);
3116
3117   default_icon_colormap = colormap;
3118   g_object_ref (colormap);
3119   
3120   default_icon_pixmap = pixmap;
3121   g_object_ref (pixmap);
3122
3123   default_icon_mask = mask;
3124   if (mask)
3125     g_object_ref (mask);
3126   
3127   default_icon_hot_x = hot_x;
3128   default_icon_hot_y = hot_y;
3129 }
3130
3131
3132 /*************************************************************
3133  * _gtk_drag_source_handle_event:
3134  *     Called from widget event handling code on Drag events
3135  *     for drag sources.
3136  *
3137  *   arguments:
3138  *     toplevel: Toplevel widget that received the event
3139  *     event:
3140  *   results:
3141  *************************************************************/
3142
3143 void
3144 _gtk_drag_source_handle_event (GtkWidget *widget,
3145                                GdkEvent  *event)
3146 {
3147   GtkDragSourceInfo *info;
3148   GdkDragContext *context;
3149
3150   g_return_if_fail (widget != NULL);
3151   g_return_if_fail (event != NULL);
3152
3153   context = event->dnd.context;
3154   info = gtk_drag_get_source_info (context, FALSE);
3155   if (!info)
3156     return;
3157
3158   switch (event->type)
3159     {
3160     case GDK_DRAG_STATUS:
3161       {
3162         GdkCursor *cursor;
3163
3164         if (info->proxy_dest)
3165           {
3166             if (!event->dnd.send_event)
3167               {
3168                 if (info->proxy_dest->proxy_drop_wait)
3169                   {
3170                     gboolean result = context->action != 0;
3171                     
3172                     /* Aha - we can finally pass the MOTIF DROP on... */
3173                     gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
3174                     if (result)
3175                       gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
3176                     else
3177                       gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
3178                   }
3179                 else
3180                   {
3181                     gdk_drag_status (info->proxy_dest->context,
3182                                      event->dnd.context->action,
3183                                      event->dnd.time);
3184                   }
3185               }
3186           }
3187         else if (info->have_grab)
3188           {
3189             cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
3190                                           event->dnd.context->action,
3191                                           info);
3192             if (info->cursor != cursor)
3193               {
3194                 gdk_pointer_grab (widget->window, FALSE,
3195                                   GDK_POINTER_MOTION_MASK |
3196                                   GDK_BUTTON_RELEASE_MASK,
3197                                   NULL,
3198                                   cursor, info->grab_time);
3199                 info->cursor = cursor;
3200               }
3201             
3202             if (info->last_event)
3203               gtk_drag_add_update_idle (info);
3204           }
3205       }
3206       break;
3207       
3208     case GDK_DROP_FINISHED:
3209       gtk_drag_drop_finished (info, TRUE, event->dnd.time);
3210       break;
3211     default:
3212       g_assert_not_reached ();
3213     }
3214 }
3215
3216 /*************************************************************
3217  * gtk_drag_source_check_selection:
3218  *     Check if we've set up handlers/claimed the selection
3219  *     for a given drag. If not, add them.
3220  *   arguments:
3221  *     
3222  *   results:
3223  *************************************************************/
3224
3225 static void
3226 gtk_drag_source_check_selection (GtkDragSourceInfo *info, 
3227                                  GdkAtom            selection,
3228                                  guint32            time)
3229 {
3230   GList *tmp_list;
3231
3232   tmp_list = info->selections;
3233   while (tmp_list)
3234     {
3235       if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
3236         return;
3237       tmp_list = tmp_list->next;
3238     }
3239
3240   gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
3241                                        info->ipc_widget,
3242                                        selection,
3243                                        time);
3244   info->selections = g_list_prepend (info->selections,
3245                                      GUINT_TO_POINTER (selection));
3246
3247   tmp_list = info->target_list->list;
3248   while (tmp_list)
3249     {
3250       GtkTargetPair *pair = tmp_list->data;
3251
3252       gtk_selection_add_target (info->ipc_widget,
3253                                 selection,
3254                                 pair->target,
3255                                 pair->info);
3256       tmp_list = tmp_list->next;
3257     }
3258   
3259   if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
3260     {
3261       gtk_selection_add_target (info->ipc_widget,
3262                                 selection,
3263                                 gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS"),
3264                                 TARGET_MOTIF_SUCCESS);
3265       gtk_selection_add_target (info->ipc_widget,
3266                                 selection,
3267                                 gdk_atom_intern_static_string ("XmTRANSFER_FAILURE"),
3268                                 TARGET_MOTIF_FAILURE);
3269     }
3270
3271   gtk_selection_add_target (info->ipc_widget,
3272                             selection,
3273                             gdk_atom_intern_static_string ("DELETE"),
3274                             TARGET_DELETE);
3275 }
3276
3277 /*************************************************************
3278  * gtk_drag_drop_finished:
3279  *     Clean up from the drag, and display snapback, if necessary.
3280  *   arguments:
3281  *     info:
3282  *     success:
3283  *     time:
3284  *   results:
3285  *************************************************************/
3286
3287 static void
3288 gtk_drag_drop_finished (GtkDragSourceInfo *info,
3289                         gboolean           success,
3290                         guint              time)
3291 {
3292   gtk_drag_source_release_selections (info, time); 
3293
3294   if (info->proxy_dest)
3295     {
3296       /* The time from the event isn't reliable for Xdnd drags */
3297       gtk_drag_finish (info->proxy_dest->context, success, FALSE, 
3298                        info->proxy_dest->proxy_drop_time);
3299       gtk_drag_source_info_destroy (info);
3300     }
3301   else
3302     {
3303       if (success)
3304         {
3305           gtk_drag_source_info_destroy (info);
3306         }
3307       else
3308         {
3309           GtkDragAnim *anim = g_new (GtkDragAnim, 1);
3310           anim->info = info;
3311           anim->step = 0;
3312           
3313           anim->n_steps = MAX (info->cur_x - info->start_x,
3314                                info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
3315           anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
3316
3317           info->cur_screen = gtk_widget_get_screen (info->widget);
3318
3319           if (!info->icon_window)
3320             set_icon_stock_pixbuf (info->context, NULL, info->icon_pixbuf, 
3321                                    0, 0, TRUE);
3322
3323           gtk_drag_update_icon (info);
3324           
3325           /* Mark the context as dead, so if the destination decides
3326            * to respond really late, we still are OK.
3327            */
3328           gtk_drag_clear_source_info (info->context);
3329           g_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
3330         }
3331     }
3332 }
3333
3334 static void
3335 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
3336                                     guint32            time)
3337 {
3338   GdkDisplay *display = gtk_widget_get_display (info->widget);
3339   GList *tmp_list = info->selections;
3340   
3341   while (tmp_list)
3342     {
3343       GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
3344       if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
3345         gtk_selection_owner_set_for_display (display, NULL, selection, time);
3346
3347       tmp_list = tmp_list->next;
3348     }
3349
3350   g_list_free (info->selections);
3351   info->selections = NULL;
3352 }
3353
3354 /*************************************************************
3355  * gtk_drag_drop:
3356  *     Send a drop event.
3357  *   arguments:
3358  *     
3359  *   results:
3360  *************************************************************/
3361
3362 static void
3363 gtk_drag_drop (GtkDragSourceInfo *info, 
3364                guint32            time)
3365 {
3366   if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
3367     {
3368       GtkSelectionData selection_data;
3369       GList *tmp_list;
3370       /* GTK+ traditionally has used application/x-rootwin-drop, but the
3371        * XDND spec specifies x-rootwindow-drop.
3372        */
3373       GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3374       GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3375       
3376       tmp_list = info->target_list->list;
3377       while (tmp_list)
3378         {
3379           GtkTargetPair *pair = tmp_list->data;
3380           
3381           if (pair->target == target1 || pair->target == target2)
3382             {
3383               selection_data.selection = GDK_NONE;
3384               selection_data.target = pair->target;
3385               selection_data.data = NULL;
3386               selection_data.length = -1;
3387               
3388               g_signal_emit_by_name (info->widget, "drag_data_get",
3389                                      info->context, &selection_data,
3390                                      pair->info,
3391                                      time);
3392               
3393               /* FIXME: Should we check for length >= 0 here? */
3394               gtk_drag_drop_finished (info, TRUE, time);
3395               return;
3396             }
3397           tmp_list = tmp_list->next;
3398         }
3399       gtk_drag_drop_finished (info, FALSE, time);
3400     }
3401   else
3402     {
3403       if (info->icon_window)
3404         gtk_widget_hide (info->icon_window);
3405         
3406       gdk_drag_drop (info->context, time);
3407       info->drop_timeout = g_timeout_add (DROP_ABORT_TIME,
3408                                           gtk_drag_abort_timeout,
3409                                           info);
3410     }
3411 }
3412
3413 /*
3414  * Source side callbacks.
3415  */
3416
3417 static gint
3418 gtk_drag_source_event_cb (GtkWidget      *widget,
3419                           GdkEvent       *event,
3420                           gpointer        data)
3421 {
3422   GtkDragSourceSite *site;
3423   gboolean retval = FALSE;
3424   site = (GtkDragSourceSite *)data;
3425
3426   switch (event->type)
3427     {
3428     case GDK_BUTTON_PRESS:
3429       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3430         {
3431           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
3432           site->x = event->button.x;
3433           site->y = event->button.y;
3434         }
3435       break;
3436       
3437     case GDK_BUTTON_RELEASE:
3438       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3439         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
3440       break;
3441       
3442     case GDK_MOTION_NOTIFY:
3443       if (site->state & event->motion.state & site->start_button_mask)
3444         {
3445           /* FIXME: This is really broken and can leave us
3446            * with a stuck grab
3447            */
3448           int i;
3449           for (i=1; i<6; i++)
3450             {
3451               if (site->state & event->motion.state & 
3452                   GDK_BUTTON1_MASK << (i - 1))
3453                 break;
3454             }
3455
3456           if (gtk_drag_check_threshold (widget, site->x, site->y,
3457                                         event->motion.x, event->motion.y))
3458             {
3459               site->state = 0;
3460               gtk_drag_begin_internal (widget, site, site->target_list,
3461                                        site->actions, 
3462                                        i, event);
3463
3464               retval = TRUE;
3465             }
3466         }
3467       break;
3468       
3469     default:                    /* hit for 2/3BUTTON_PRESS */
3470       break;
3471     }
3472   
3473   return retval;
3474 }
3475
3476 static void 
3477 gtk_drag_source_site_destroy (gpointer data)
3478 {
3479   GtkDragSourceSite *site = data;
3480
3481   if (site->target_list)
3482     gtk_target_list_unref (site->target_list);
3483
3484   gtk_drag_source_unset_icon (site);
3485   g_free (site);
3486 }
3487
3488 static void
3489 gtk_drag_selection_get (GtkWidget        *widget, 
3490                         GtkSelectionData *selection_data,
3491                         guint             sel_info,
3492                         guint32           time,
3493                         gpointer          data)
3494 {
3495   GtkDragSourceInfo *info = data;
3496   static GdkAtom null_atom = GDK_NONE;
3497   guint target_info;
3498
3499   if (!null_atom)
3500     null_atom = gdk_atom_intern_static_string ("NULL");
3501
3502   switch (sel_info)
3503     {
3504     case TARGET_DELETE:
3505       g_signal_emit_by_name (info->widget,
3506                              "drag_data_delete", 
3507                              info->context);
3508       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3509       break;
3510     case TARGET_MOTIF_SUCCESS:
3511       gtk_drag_drop_finished (info, TRUE, time);
3512       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3513       break;
3514     case TARGET_MOTIF_FAILURE:
3515       gtk_drag_drop_finished (info, FALSE, time);
3516       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3517       break;
3518     default:
3519       if (info->proxy_dest)
3520         {
3521           /* This is sort of dangerous and needs to be thought
3522            * through better
3523            */
3524           info->proxy_dest->proxy_data = selection_data;
3525           gtk_drag_get_data (info->widget,
3526                              info->proxy_dest->context,
3527                              selection_data->target,
3528                              time);
3529           gtk_main ();
3530           info->proxy_dest->proxy_data = NULL;
3531         }
3532       else
3533         {
3534           if (gtk_target_list_find (info->target_list, 
3535                                     selection_data->target, 
3536                                     &target_info))
3537             {
3538               g_signal_emit_by_name (info->widget, "drag_data_get",
3539                                      info->context,
3540                                      selection_data,
3541                                      target_info,
3542                                      time);
3543             }
3544         }
3545       break;
3546     }
3547 }
3548
3549 static gint
3550 gtk_drag_anim_timeout (gpointer data)
3551 {
3552   GtkDragAnim *anim = data;
3553   gint x, y;
3554   gboolean retval;
3555
3556   GDK_THREADS_ENTER ();
3557   
3558   if (anim->step == anim->n_steps)
3559     {
3560       gtk_drag_source_info_destroy (anim->info);
3561       g_free (anim);
3562
3563       retval = FALSE;
3564     }
3565   else
3566     {
3567       x = (anim->info->start_x * (anim->step + 1) +
3568            anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3569       y = (anim->info->start_y * (anim->step + 1) +
3570            anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3571       if (anim->info->icon_window)
3572         {
3573           GtkWidget *icon_window;
3574           gint hot_x, hot_y;
3575           
3576           gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);   
3577           gtk_window_move (GTK_WINDOW (icon_window), 
3578                            x - hot_x, 
3579                            y - hot_y);
3580         }
3581   
3582       anim->step++;
3583
3584       retval = TRUE;
3585     }
3586
3587   GDK_THREADS_LEAVE ();
3588
3589   return retval;
3590 }
3591
3592 static void
3593 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3594 {
3595   if (info->icon_window)
3596     {
3597       gtk_widget_hide (info->icon_window);
3598       if (info->destroy_icon)
3599         gtk_widget_destroy (info->icon_window);
3600
3601       if (info->fallback_icon)
3602         {
3603           gtk_widget_destroy (info->fallback_icon);
3604           info->fallback_icon = NULL;
3605         }
3606
3607       g_object_unref (info->icon_window);
3608       info->icon_window = NULL;
3609     }
3610 }
3611
3612 static void
3613 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3614 {
3615   gint i;
3616
3617   for (i = 0; i < n_drag_cursors; i++)
3618     {
3619       if (info->drag_cursors[i] != NULL)
3620         {
3621           gdk_cursor_unref (info->drag_cursors[i]);
3622           info->drag_cursors[i] = NULL;
3623         }
3624     }
3625
3626   gtk_drag_remove_icon (info);
3627
3628   if (info->icon_pixbuf)
3629     g_object_unref (info->icon_pixbuf);
3630
3631   if (!info->proxy_dest)
3632     g_signal_emit_by_name (info->widget, "drag_end", 
3633                            info->context);
3634
3635   if (info->widget)
3636     g_object_unref (info->widget);
3637
3638
3639   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3640                                         gtk_drag_button_release_cb,
3641                                         info);
3642   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3643                                         gtk_drag_motion_cb,
3644                                         info);
3645   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3646                                         gtk_drag_key_cb,
3647                                         info);
3648   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3649                                         gtk_drag_selection_get,
3650                                         info);
3651
3652   gtk_selection_remove_all (info->ipc_widget);
3653   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), NULL);
3654   source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3655   gtk_drag_release_ipc_widget (info->ipc_widget);
3656
3657   gtk_target_list_unref (info->target_list);
3658
3659   gtk_drag_clear_source_info (info->context);
3660   g_object_unref (info->context);
3661
3662   if (info->drop_timeout)
3663     g_source_remove (info->drop_timeout);
3664
3665   g_free (info);
3666 }
3667
3668 static gboolean
3669 gtk_drag_update_idle (gpointer data)
3670 {
3671   GtkDragSourceInfo *info = data;
3672   GdkWindow *dest_window;
3673   GdkDragProtocol protocol;
3674   GdkAtom selection;
3675
3676   GdkDragAction action;
3677   GdkDragAction possible_actions;
3678   guint32 time;
3679
3680   GDK_THREADS_ENTER ();
3681
3682   info->update_idle = 0;
3683     
3684   time = gtk_drag_get_event_time (info->last_event);
3685   gtk_drag_get_event_actions (info->last_event,
3686                               info->button, 
3687                               info->possible_actions,
3688                               &action, &possible_actions);
3689   gtk_drag_update_icon (info);
3690   gdk_drag_find_window_for_screen (info->context,
3691                                    info->icon_window ? info->icon_window->window : NULL,
3692                                    info->cur_screen, info->cur_x, info->cur_y,
3693                                    &dest_window, &protocol);
3694   
3695   if (!gdk_drag_motion (info->context, dest_window, protocol,
3696                         info->cur_x, info->cur_y, action, 
3697                         possible_actions,
3698                         time))
3699     {
3700       gdk_event_free ((GdkEvent *)info->last_event);
3701       info->last_event = NULL;
3702     }
3703   
3704   if (dest_window)
3705     g_object_unref (dest_window);
3706
3707   selection = gdk_drag_get_selection (info->context);
3708   if (selection)
3709     gtk_drag_source_check_selection (info, selection, time);
3710
3711   GDK_THREADS_LEAVE ();
3712
3713   return FALSE;
3714 }
3715
3716 static void
3717 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
3718 {
3719   /* We use an idle lowerthan GDK_PRIORITY_REDRAW so that exposes
3720    * from the last move can catch up before we move again.
3721    */
3722   if (!info->update_idle)
3723     info->update_idle = g_idle_add_full (GDK_PRIORITY_REDRAW + 5,
3724                                          gtk_drag_update_idle,
3725                                          info,
3726                                          NULL);
3727 }
3728
3729 /**
3730  * gtk_drag_update:
3731  * @info: DragSourceInfo for the drag
3732  * @screen: new screen
3733  * @x_root: new X position 
3734  * @y_root: new y position
3735  * @event: event received requiring update
3736  * 
3737  * Updates the status of the drag; called when the
3738  * cursor moves or the modifier changes
3739  **/
3740 static void
3741 gtk_drag_update (GtkDragSourceInfo *info,
3742                  GdkScreen         *screen,
3743                  gint               x_root,
3744                  gint               y_root,
3745                  GdkEvent          *event)
3746 {
3747   info->cur_screen = screen;
3748   info->cur_x = x_root;
3749   info->cur_y = y_root;
3750   if (info->last_event)
3751     {
3752       gdk_event_free ((GdkEvent *)info->last_event);
3753       info->last_event = NULL;
3754     }
3755   if (event)
3756     info->last_event = gdk_event_copy ((GdkEvent *)event);
3757
3758   gtk_drag_add_update_idle (info);
3759 }
3760
3761 /*************************************************************
3762  * gtk_drag_end:
3763  *     Called when the user finishes to drag, either by
3764  *     releasing the mouse, or by pressing Esc.
3765  *   arguments:
3766  *     info: Source info for the drag
3767  *     time: Timestamp for ending the drag
3768  *   results:
3769  *************************************************************/
3770
3771 static void
3772 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3773 {
3774   GdkEvent *send_event;
3775   GtkWidget *source_widget = info->widget;
3776   GdkDisplay *display = gtk_widget_get_display (source_widget);
3777
3778   if (info->update_idle)
3779     {
3780       g_source_remove (info->update_idle);
3781       info->update_idle = 0;
3782     }
3783   
3784   if (info->last_event)
3785     {
3786       gdk_event_free (info->last_event);
3787       info->last_event = NULL;
3788     }
3789   
3790   info->have_grab = FALSE;
3791   
3792   gdk_display_pointer_ungrab (display, time);
3793   gdk_display_keyboard_ungrab (display, time);
3794   gtk_grab_remove (info->ipc_widget);
3795
3796   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3797                                         gtk_drag_button_release_cb,
3798                                         info);
3799   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3800                                         gtk_drag_motion_cb,
3801                                         info);
3802   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3803                                         gtk_drag_key_cb,
3804                                         info);
3805
3806   /* Send on a release pair to the original 
3807    * widget to convince it to release its grab. We need to
3808    * call gtk_propagate_event() here, instead of 
3809    * gtk_widget_event() because widget like GtkList may
3810    * expect propagation.
3811    */
3812
3813   send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3814   send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3815   send_event->button.send_event = TRUE;
3816   send_event->button.time = time;
3817   send_event->button.x = 0;
3818   send_event->button.y = 0;
3819   send_event->button.axes = NULL;
3820   send_event->button.state = 0;
3821   send_event->button.button = info->button;
3822   send_event->button.device = gdk_display_get_core_pointer (display);
3823   send_event->button.x_root = 0;
3824   send_event->button.y_root = 0;
3825
3826   gtk_propagate_event (source_widget, send_event);
3827   gdk_event_free (send_event);
3828 }
3829
3830 /*************************************************************
3831  * gtk_drag_cancel:
3832  *    Called on cancellation of a drag, either by the user
3833  *    or programmatically.
3834  *   arguments:
3835  *     info: Source info for the drag
3836  *     time: Timestamp for ending the drag
3837  *   results:
3838  *************************************************************/
3839
3840 static void
3841 gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
3842 {
3843   gtk_drag_end (info, time);
3844   gdk_drag_abort (info->context, time);
3845   gtk_drag_drop_finished (info, FALSE, time);
3846 }
3847
3848 /*************************************************************
3849  * gtk_drag_motion_cb:
3850  *     "motion_notify_event" callback during drag.
3851  *   arguments:
3852  *     
3853  *   results:
3854  *************************************************************/
3855
3856 static gint
3857 gtk_drag_motion_cb (GtkWidget      *widget, 
3858                     GdkEventMotion *event, 
3859                     gpointer        data)
3860 {
3861   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3862   GdkScreen *screen;
3863   gint x_root, y_root;
3864
3865   if (event->is_hint)
3866     {
3867       GdkDisplay *display = gtk_widget_get_display (widget);
3868       
3869       gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
3870       event->x_root = x_root;
3871       event->y_root = y_root;
3872     }
3873   else
3874     screen = gdk_event_get_screen ((GdkEvent *)event);
3875
3876   gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
3877
3878   return TRUE;
3879 }
3880
3881 /*************************************************************
3882  * gtk_drag_key_cb:
3883  *     "key_press/release_event" callback during drag.
3884  *   arguments:
3885  *     
3886  *   results:
3887  *************************************************************/
3888
3889 static gint 
3890 gtk_drag_key_cb (GtkWidget         *widget, 
3891                  GdkEventKey       *event, 
3892                  gpointer           data)
3893 {
3894   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3895   GdkModifierType state;
3896   GdkWindow *root_window;
3897   
3898   if (event->type == GDK_KEY_PRESS)
3899     {
3900       if (event->keyval == GDK_Escape)
3901         {
3902           gtk_drag_cancel (info, event->time);
3903
3904           return TRUE;
3905         }
3906     }
3907
3908   /* Now send a "motion" so that the modifier state is updated */
3909
3910   /* The state is not yet updated in the event, so we need
3911    * to query it here. We could use XGetModifierMapping, but
3912    * that would be overkill.
3913    */
3914   root_window = gtk_widget_get_root_window (widget);
3915   gdk_window_get_pointer (root_window, NULL, NULL, &state);
3916
3917   event->state = state;
3918   gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
3919
3920   return TRUE;
3921 }
3922
3923 /*************************************************************
3924  * gtk_drag_button_release_cb:
3925  *     "button_release_event" callback during drag.
3926  *   arguments:
3927  *     
3928  *   results:
3929  *************************************************************/
3930
3931 static gint
3932 gtk_drag_button_release_cb (GtkWidget      *widget, 
3933                             GdkEventButton *event, 
3934                             gpointer        data)
3935 {
3936   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3937
3938   if (event->button != info->button)
3939     return FALSE;
3940
3941   if ((info->context->action != 0) && (info->context->dest_window != NULL))
3942     {
3943       gtk_drag_end (info, event->time);
3944       gtk_drag_drop (info, event->time);
3945     }
3946   else
3947     {
3948       gtk_drag_cancel (info, event->time);
3949     }
3950
3951   return TRUE;
3952 }
3953
3954 static gint
3955 gtk_drag_abort_timeout (gpointer data)
3956 {
3957   GtkDragSourceInfo *info = data;
3958   guint32 time = GDK_CURRENT_TIME;
3959
3960   GDK_THREADS_ENTER ();
3961
3962   if (info->proxy_dest)
3963     time = info->proxy_dest->proxy_drop_time;
3964
3965   info->drop_timeout = 0;
3966   gtk_drag_drop_finished (info, FALSE, time);
3967   
3968   GDK_THREADS_LEAVE ();
3969   
3970   return FALSE;
3971 }
3972
3973 /**
3974  * gtk_drag_check_threshold:
3975  * @widget: a #GtkWidget
3976  * @start_x: X coordinate of start of drag
3977  * @start_y: Y coordinate of start of drag
3978  * @current_x: current X coordinate
3979  * @current_y: current Y coordinate
3980  * 
3981  * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3982  * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
3983  * should trigger the beginning of a drag-and-drop operation.
3984  *
3985  * Return Value: %TRUE if the drag threshold has been passed.
3986  **/
3987 gboolean
3988 gtk_drag_check_threshold (GtkWidget *widget,
3989                           gint       start_x,
3990                           gint       start_y,
3991                           gint       current_x,
3992                           gint       current_y)
3993 {
3994   gint drag_threshold;
3995
3996   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
3997
3998   g_object_get (gtk_widget_get_settings (widget),
3999                 "gtk-dnd-drag-threshold", &drag_threshold,
4000                 NULL);
4001   
4002   return (ABS (current_x - start_x) > drag_threshold ||
4003           ABS (current_y - start_y) > drag_threshold);
4004 }
4005
4006 #define __GTK_DND_C__
4007 #include "gtkaliasdef.c"