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