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