]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd.c
Merge branch 'master' into broadway2
[~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 "gtkimage.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:
964  *     Get the data for a drag or drop
965  *   arguments:
966  *     context - drag context
967  *     target  - format to retrieve the data in.
968  *     time    - timestamp of triggering event.
969  *     
970  *   results:
971  *************************************************************/
972
973 void 
974 gtk_drag_get_data (GtkWidget      *widget,
975                    GdkDragContext *context,
976                    GdkAtom         target,
977                    guint32         time)
978 {
979   GtkWidget *selection_widget;
980
981   g_return_if_fail (GTK_IS_WIDGET (widget));
982   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
983
984   selection_widget = gtk_drag_get_ipc_widget (widget);
985
986   g_object_ref (context);
987   g_object_ref (widget);
988   
989   g_signal_connect (selection_widget, "selection-received",
990                     G_CALLBACK (gtk_drag_selection_received), widget);
991
992   g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
993
994   gtk_selection_convert (selection_widget,
995                          gdk_drag_get_selection (context),
996                          target,
997                          time);
998 }
999
1000
1001 /**
1002  * gtk_drag_get_source_widget:
1003  * @context: a (destination side) drag context
1004  *
1005  * Determines the source widget for a drag.
1006  *
1007  * Return value: (transfer none): if the drag is occurring
1008  *     within a single application, a pointer to the source widget.
1009  *     Otherwise, %NULL.
1010  */
1011 GtkWidget *
1012 gtk_drag_get_source_widget (GdkDragContext *context)
1013 {
1014   GSList *tmp_list;
1015
1016   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
1017   
1018   tmp_list = source_widgets;
1019   while (tmp_list)
1020     {
1021       GtkWidget *ipc_widget = tmp_list->data;
1022
1023       if (gtk_widget_get_window (ipc_widget) == gdk_drag_context_get_source_window (context))
1024         {
1025           GtkDragSourceInfo *info;
1026           info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
1027
1028           return info ? info->widget : NULL;
1029         }
1030
1031       tmp_list = tmp_list->next;
1032     }
1033
1034   return NULL;
1035 }
1036
1037 /*************************************************************
1038  * gtk_drag_finish:
1039  *     Notify the drag source that the transfer of data
1040  *     is complete.
1041  *   arguments:
1042  *     context: The drag context for this drag
1043  *     success: Was the data successfully transferred?
1044  *     time:    The timestamp to use when notifying the destination.
1045  *   results:
1046  *************************************************************/
1047
1048 void 
1049 gtk_drag_finish (GdkDragContext *context,
1050                  gboolean        success,
1051                  gboolean        del,
1052                  guint32         time)
1053 {
1054   GdkAtom target = GDK_NONE;
1055
1056   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1057
1058   if (success && del)
1059     {
1060       target = gdk_atom_intern_static_string ("DELETE");
1061     }
1062   else if (gdk_drag_context_get_protocol (context) == GDK_DRAG_PROTO_MOTIF)
1063     {
1064       target = gdk_atom_intern_static_string (success ? 
1065                                               "XmTRANSFER_SUCCESS" : 
1066                                               "XmTRANSFER_FAILURE");
1067     }
1068
1069   if (target != GDK_NONE)
1070     {
1071       GtkWidget *selection_widget = gtk_drag_get_ipc_widget_for_screen (gdk_window_get_screen (gdk_drag_context_get_source_window (context)));
1072
1073       g_object_ref (context);
1074       
1075       g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
1076       g_signal_connect (selection_widget, "selection-received",
1077                         G_CALLBACK (gtk_drag_selection_received),
1078                         NULL);
1079       
1080       gtk_selection_convert (selection_widget,
1081                              gdk_drag_get_selection (context),
1082                              target,
1083                              time);
1084     }
1085   
1086   if (!(success && del))
1087     gdk_drop_finish (context, success, time);
1088 }
1089
1090 /*************************************************************
1091  * gtk_drag_highlight_draw:
1092  *     Callback for expose_event for highlighted widgets.
1093  *   arguments:
1094  *     widget:
1095  *     event:
1096  *     data:
1097  *   results:
1098  *************************************************************/
1099
1100 static gboolean
1101 gtk_drag_highlight_draw (GtkWidget *widget,
1102                          cairo_t   *cr,
1103                          gpointer   data)
1104 {
1105   int width = gtk_widget_get_allocated_width (widget);
1106   int height = gtk_widget_get_allocated_height (widget);
1107   GtkStyleContext *context;
1108
1109   context = gtk_widget_get_style_context (widget);
1110
1111   gtk_style_context_save (context);
1112   gtk_style_context_add_class (context, GTK_STYLE_CLASS_DND);
1113
1114   gtk_render_frame (context, cr, 0, 0, width, height);
1115
1116   gtk_style_context_restore (context);
1117
1118   cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
1119   cairo_set_line_width (cr, 1.0);
1120   cairo_rectangle (cr,
1121                    0.5, 0.5,
1122                    width - 1, height - 1);
1123   cairo_stroke (cr);
1124
1125   return FALSE;
1126 }
1127
1128 /*************************************************************
1129  * gtk_drag_highlight:
1130  *     Highlight the given widget in the default manner.
1131  *   arguments:
1132  *     widget:
1133  *   results:
1134  *************************************************************/
1135
1136 void 
1137 gtk_drag_highlight (GtkWidget  *widget)
1138 {
1139   g_return_if_fail (GTK_IS_WIDGET (widget));
1140
1141   g_signal_connect_after (widget, "draw",
1142                           G_CALLBACK (gtk_drag_highlight_draw),
1143                           NULL);
1144
1145   gtk_widget_queue_draw (widget);
1146 }
1147
1148 /*************************************************************
1149  * gtk_drag_unhighlight:
1150  *     Refresh the given widget to remove the highlight.
1151  *   arguments:
1152  *     widget:
1153  *   results:
1154  *************************************************************/
1155
1156 void 
1157 gtk_drag_unhighlight (GtkWidget *widget)
1158 {
1159   g_return_if_fail (GTK_IS_WIDGET (widget));
1160
1161   g_signal_handlers_disconnect_by_func (widget,
1162                                         gtk_drag_highlight_draw,
1163                                         NULL);
1164   
1165   gtk_widget_queue_draw (widget);
1166 }
1167
1168 static void
1169 gtk_drag_dest_set_internal (GtkWidget       *widget,
1170                             GtkDragDestSite *site)
1171 {
1172   GtkDragDestSite *old_site;
1173   
1174   g_return_if_fail (widget != NULL);
1175
1176   /* HACK, do this in the destroy */
1177   old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1178   if (old_site)
1179     {
1180       g_signal_handlers_disconnect_by_func (widget,
1181                                             gtk_drag_dest_realized,
1182                                             old_site);
1183       g_signal_handlers_disconnect_by_func (widget,
1184                                             gtk_drag_dest_hierarchy_changed,
1185                                             old_site);
1186
1187       site->track_motion = old_site->track_motion;
1188     }
1189
1190   if (gtk_widget_get_realized (widget))
1191     gtk_drag_dest_realized (widget);
1192
1193   g_signal_connect (widget, "realize",
1194                     G_CALLBACK (gtk_drag_dest_realized), site);
1195   g_signal_connect (widget, "hierarchy-changed",
1196                     G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
1197
1198   g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
1199                           site, gtk_drag_dest_site_destroy);
1200 }
1201
1202 /**
1203  * gtk_drag_dest_set:
1204  * @widget: a #GtkWidget
1205  * @flags: which types of default drag behavior to use
1206  * @targets: (allow-none) (array length=n_targets): a pointer to an array of #GtkTargetEntry<!-- -->s
1207  *     indicating the drop types that this @widget will accept, or %NULL.
1208  *     Later you can access the list with gtk_drag_dest_get_target_list()
1209  *     and gtk_drag_dest_find_target().
1210  * @n_targets: the number of entries in @targets
1211  * @actions: a bitmask of possible actions for a drop onto this @widget.
1212  *
1213  * Sets a widget as a potential drop destination, and adds default behaviors.
1214  *
1215  * The default behaviors listed in @flags have an effect similar
1216  * to installing default handlers for the widget's drag-and-drop signals
1217  * (#GtkWidget:drag-motion, #GtkWidget:drag-drop, ...). They all exist
1218  * for convenience. When passing #GTK_DEST_DEFAULT_ALL for instance it is
1219  * sufficient to connect to the widget's #GtkWidget::drag-data-received
1220  * signal to get primitive, but consistent drag-and-drop support.
1221  *
1222  * Things become more complicated when you try to preview the dragged data,
1223  * as described in the documentation for #GtkWidget:drag-motion. The default
1224  * behaviors described by @flags make some assumptions, that can conflict
1225  * with your own signal handlers. For instance #GTK_DEST_DEFAULT_DROP causes
1226  * invokations of gdk_drag_status() in the context of #GtkWidget:drag-motion,
1227  * and invokations of gtk_drag_finish() in #GtkWidget:drag-data-received.
1228  * Especially the later is dramatic, when your own #GtkWidget:drag-motion
1229  * handler calls gtk_drag_get_data() to inspect the dragged data.
1230  *
1231  * There's no way to set a default action here, you can use the
1232  * #GtkWidget:drag-motion callback for that. Here's an example which selects
1233  * the action to use depending on whether the control key is pressed or not:
1234  * |[
1235  * static void
1236  * drag_motion (GtkWidget *widget,
1237  *              GdkDragContext *context,
1238  *              gint x,
1239  *              gint y,
1240  *              guint time)
1241  * {
1242  *   GdkModifierType mask;
1243  *
1244  *   gdk_window_get_pointer (gtk_widget_get_window (widget),
1245  *                           NULL, NULL, &mask);
1246  *   if (mask & GDK_CONTROL_MASK)
1247  *     gdk_drag_status (context, GDK_ACTION_COPY, time);
1248  *   else
1249  *     gdk_drag_status (context, GDK_ACTION_MOVE, time);
1250  * }
1251  * ]|
1252  */
1253 void
1254 gtk_drag_dest_set (GtkWidget            *widget,
1255                    GtkDestDefaults       flags,
1256                    const GtkTargetEntry *targets,
1257                    gint                  n_targets,
1258                    GdkDragAction         actions)
1259 {
1260   GtkDragDestSite *site;
1261   
1262   g_return_if_fail (GTK_IS_WIDGET (widget));
1263
1264   site = g_slice_new0 (GtkDragDestSite);
1265
1266   site->flags = flags;
1267   site->have_drag = FALSE;
1268   if (targets)
1269     site->target_list = gtk_target_list_new (targets, n_targets);
1270   else
1271     site->target_list = NULL;
1272   site->actions = actions;
1273   site->do_proxy = FALSE;
1274   site->proxy_window = NULL;
1275   site->track_motion = FALSE;
1276
1277   gtk_drag_dest_set_internal (widget, site);
1278 }
1279
1280 /*************************************************************
1281  * gtk_drag_dest_set_proxy:
1282  *     Set up this widget to proxy drags elsewhere.
1283  *   arguments:
1284  *     widget:          
1285  *     proxy_window:    window to which forward drag events
1286  *     protocol:        Drag protocol which the dest widget accepts
1287  *     use_coordinates: If true, send the same coordinates to the
1288  *                      destination, because it is a embedded 
1289  *                      subwindow.
1290  *   results:
1291  *************************************************************/
1292
1293 void 
1294 gtk_drag_dest_set_proxy (GtkWidget      *widget,
1295                          GdkWindow      *proxy_window,
1296                          GdkDragProtocol protocol,
1297                          gboolean        use_coordinates)
1298 {
1299   GtkDragDestSite *site;
1300   
1301   g_return_if_fail (GTK_IS_WIDGET (widget));
1302   g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
1303
1304   site = g_slice_new (GtkDragDestSite);
1305
1306   site->flags = 0;
1307   site->have_drag = FALSE;
1308   site->target_list = NULL;
1309   site->actions = 0;
1310   site->proxy_window = proxy_window;
1311   if (proxy_window)
1312     g_object_ref (proxy_window);
1313   site->do_proxy = TRUE;
1314   site->proxy_protocol = protocol;
1315   site->proxy_coords = use_coordinates;
1316   site->track_motion = FALSE;
1317
1318   gtk_drag_dest_set_internal (widget, site);
1319 }
1320
1321 /*************************************************************
1322  * gtk_drag_dest_unset
1323  *     Unregister this widget as a drag target.
1324  *   arguments:
1325  *     widget:
1326  *   results:
1327  *************************************************************/
1328
1329 void 
1330 gtk_drag_dest_unset (GtkWidget *widget)
1331 {
1332   GtkDragDestSite *old_site;
1333
1334   g_return_if_fail (GTK_IS_WIDGET (widget));
1335
1336   old_site = g_object_get_data (G_OBJECT (widget),
1337                                 "gtk-drag-dest");
1338   if (old_site)
1339     {
1340       g_signal_handlers_disconnect_by_func (widget,
1341                                             gtk_drag_dest_realized,
1342                                             old_site);
1343       g_signal_handlers_disconnect_by_func (widget,
1344                                             gtk_drag_dest_hierarchy_changed,
1345                                             old_site);
1346     }
1347
1348   g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
1349 }
1350
1351 /**
1352  * gtk_drag_dest_get_target_list:
1353  * @widget: a #GtkWidget
1354  * 
1355  * Returns the list of targets this widget can accept from
1356  * drag-and-drop.
1357  * 
1358  * Return value: the #GtkTargetList, or %NULL if none
1359  **/
1360 GtkTargetList*
1361 gtk_drag_dest_get_target_list (GtkWidget *widget)
1362 {
1363   GtkDragDestSite *site;
1364
1365   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1366   
1367   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1368
1369   return site ? site->target_list : NULL;  
1370 }
1371
1372 /**
1373  * gtk_drag_dest_set_target_list:
1374  * @widget: a #GtkWidget that's a drag destination
1375  * @target_list: (allow-none): list of droppable targets, or %NULL for none
1376  * 
1377  * Sets the target types that this widget can accept from drag-and-drop.
1378  * The widget must first be made into a drag destination with
1379  * gtk_drag_dest_set().
1380  **/
1381 void
1382 gtk_drag_dest_set_target_list (GtkWidget      *widget,
1383                                GtkTargetList  *target_list)
1384 {
1385   GtkDragDestSite *site;
1386
1387   g_return_if_fail (GTK_IS_WIDGET (widget));
1388   
1389   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1390   
1391   if (!site)
1392     {
1393       g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
1394                  "to make the widget into a drag destination");
1395       return;
1396     }
1397
1398   if (target_list)
1399     gtk_target_list_ref (target_list);
1400   
1401   if (site->target_list)
1402     gtk_target_list_unref (site->target_list);
1403
1404   site->target_list = target_list;
1405 }
1406
1407 /**
1408  * gtk_drag_dest_add_text_targets:
1409  * @widget: a #GtkWidget that's a drag destination
1410  *
1411  * Add the text targets supported by #GtkSelection to
1412  * the target list of the drag destination. The targets
1413  * are added with @info = 0. If you need another value, 
1414  * use gtk_target_list_add_text_targets() and
1415  * gtk_drag_dest_set_target_list().
1416  * 
1417  * Since: 2.6
1418  **/
1419 void
1420 gtk_drag_dest_add_text_targets (GtkWidget *widget)
1421 {
1422   GtkTargetList *target_list;
1423
1424   target_list = gtk_drag_dest_get_target_list (widget);
1425   if (target_list)
1426     gtk_target_list_ref (target_list);
1427   else
1428     target_list = gtk_target_list_new (NULL, 0);
1429   gtk_target_list_add_text_targets (target_list, 0);
1430   gtk_drag_dest_set_target_list (widget, target_list);
1431   gtk_target_list_unref (target_list);
1432 }
1433
1434 /**
1435  * gtk_drag_dest_add_image_targets:
1436  * @widget: a #GtkWidget that's a drag destination
1437  *
1438  * Add the image targets supported by #GtkSelection to
1439  * the target list of the drag destination. The targets
1440  * are added with @info = 0. If you need another value, 
1441  * use gtk_target_list_add_image_targets() and
1442  * gtk_drag_dest_set_target_list().
1443  * 
1444  * Since: 2.6
1445  **/
1446 void
1447 gtk_drag_dest_add_image_targets (GtkWidget *widget)
1448 {
1449   GtkTargetList *target_list;
1450
1451   target_list = gtk_drag_dest_get_target_list (widget);
1452   if (target_list)
1453     gtk_target_list_ref (target_list);
1454   else
1455     target_list = gtk_target_list_new (NULL, 0);
1456   gtk_target_list_add_image_targets (target_list, 0, FALSE);
1457   gtk_drag_dest_set_target_list (widget, target_list);
1458   gtk_target_list_unref (target_list);
1459 }
1460
1461 /**
1462  * gtk_drag_dest_add_uri_targets:
1463  * @widget: a #GtkWidget that's a drag destination
1464  *
1465  * Add the URI targets supported by #GtkSelection to
1466  * the target list of the drag destination. The targets
1467  * are added with @info = 0. If you need another value, 
1468  * use gtk_target_list_add_uri_targets() and
1469  * gtk_drag_dest_set_target_list().
1470  * 
1471  * Since: 2.6
1472  **/
1473 void
1474 gtk_drag_dest_add_uri_targets (GtkWidget *widget)
1475 {
1476   GtkTargetList *target_list;
1477
1478   target_list = gtk_drag_dest_get_target_list (widget);
1479   if (target_list)
1480     gtk_target_list_ref (target_list);
1481   else
1482     target_list = gtk_target_list_new (NULL, 0);
1483   gtk_target_list_add_uri_targets (target_list, 0);
1484   gtk_drag_dest_set_target_list (widget, target_list);
1485   gtk_target_list_unref (target_list);
1486 }
1487
1488 /**
1489  * gtk_drag_dest_set_track_motion:
1490  * @widget: a #GtkWidget that's a drag destination
1491  * @track_motion: whether to accept all targets
1492  * 
1493  * Tells the widget to emit ::drag-motion and ::drag-leave
1494  * events regardless of the targets and the %GTK_DEST_DEFAULT_MOTION
1495  * flag. 
1496  *
1497  * This may be used when a widget wants to do generic
1498  * actions regardless of the targets that the source offers.
1499  *
1500  * Since: 2.10
1501  **/
1502 void
1503 gtk_drag_dest_set_track_motion (GtkWidget *widget,
1504                                 gboolean   track_motion)
1505 {
1506   GtkDragDestSite *site;
1507
1508   g_return_if_fail (GTK_IS_WIDGET (widget));
1509
1510   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1511   
1512   g_return_if_fail (site != NULL);
1513
1514   site->track_motion = track_motion != FALSE;
1515 }
1516
1517 /**
1518  * gtk_drag_dest_get_track_motion:
1519  * @widget: a #GtkWidget that's a drag destination
1520  * 
1521  * Returns whether the widget has been configured to always
1522  * emit ::drag-motion signals.
1523  * 
1524  * Return Value: %TRUE if the widget always emits ::drag-motion events
1525  *
1526  * Since: 2.10
1527  **/
1528 gboolean
1529 gtk_drag_dest_get_track_motion (GtkWidget *widget)
1530 {
1531   GtkDragDestSite *site;
1532
1533   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1534
1535   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1536
1537   if (site)
1538     return site->track_motion;
1539
1540   return FALSE;
1541 }
1542
1543 /*************************************************************
1544  * _gtk_drag_dest_handle_event:
1545  *     Called from widget event handling code on Drag events
1546  *     for destinations.
1547  *
1548  *   arguments:
1549  *     toplevel: Toplevel widget that received the event
1550  *     event:
1551  *   results:
1552  *************************************************************/
1553
1554 void
1555 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1556                             GdkEvent  *event)
1557 {
1558   GtkDragDestInfo *info;
1559   GdkDragContext *context;
1560
1561   g_return_if_fail (toplevel != NULL);
1562   g_return_if_fail (event != NULL);
1563
1564   context = event->dnd.context;
1565
1566   info = gtk_drag_get_dest_info (context, TRUE);
1567
1568   /* Find the widget for the event */
1569   switch (event->type)
1570     {
1571     case GDK_DRAG_ENTER:
1572       break;
1573       
1574     case GDK_DRAG_LEAVE:
1575       if (info->widget)
1576         {
1577           gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1578           info->widget = NULL;
1579         }
1580       break;
1581       
1582     case GDK_DRAG_MOTION:
1583     case GDK_DROP_START:
1584       {
1585         GdkWindow *window;
1586         gint tx, ty;
1587         gboolean found;
1588
1589         if (event->type == GDK_DROP_START)
1590           {
1591             info->dropped = TRUE;
1592             /* We send a leave here so that the widget unhighlights
1593              * properly.
1594              */
1595             if (info->widget)
1596               {
1597                 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1598                 info->widget = NULL;
1599               }
1600           }
1601
1602         window = gtk_widget_get_window (toplevel);
1603
1604 #ifdef GDK_WINDOWING_X11
1605         /* Hackaround for: http://bugzilla.gnome.org/show_bug.cgi?id=136112
1606          *
1607          * Currently gdk_window_get_position doesn't provide reliable
1608          * information for embedded windows, so we call the much more
1609          * expensive gdk_window_get_origin().
1610          */
1611         if (GTK_IS_PLUG (toplevel))
1612           gdk_window_get_origin (window, &tx, &ty);
1613         else
1614 #endif /* GDK_WINDOWING_X11 */
1615           gdk_window_get_position (window, &tx, &ty);
1616
1617         found = gtk_drag_find_widget (toplevel,
1618                                       context,
1619                                       info,
1620                                       event->dnd.x_root - tx,
1621                                       event->dnd.y_root - ty,
1622                                       event->dnd.time,
1623                                       (event->type == GDK_DRAG_MOTION) ?
1624                                       gtk_drag_dest_motion :
1625                                       gtk_drag_dest_drop);
1626
1627         if (info->widget && !found)
1628           {
1629             gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1630             info->widget = NULL;
1631           }
1632         
1633         /* Send a reply.
1634          */
1635         if (event->type == GDK_DRAG_MOTION)
1636           {
1637             if (!found)
1638               gdk_drag_status (context, 0, event->dnd.time);
1639           }
1640         else if (event->type == GDK_DROP_START && !info->proxy_source)
1641           {
1642             gdk_drop_reply (context, found, event->dnd.time);
1643             if ((gdk_drag_context_get_protocol (context) == GDK_DRAG_PROTO_MOTIF) && !found)
1644               gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1645           }
1646       }
1647       break;
1648
1649     default:
1650       g_assert_not_reached ();
1651     }
1652 }
1653
1654 /**
1655  * gtk_drag_dest_find_target:
1656  * @widget: drag destination widget
1657  * @context: drag context
1658  * @target_list: (allow-none): list of droppable targets, or %NULL to use
1659  *    gtk_drag_dest_get_target_list (@widget).
1660  *
1661  * Looks for a match between the supported targets of @context and the
1662  * @dest_target_list, returning the first matching target, otherwise
1663  * returning %GDK_NONE. @dest_target_list should usually be the return
1664  * value from gtk_drag_dest_get_target_list(), but some widgets may
1665  * have different valid targets for different parts of the widget; in
1666  * that case, they will have to implement a drag_motion handler that
1667  * passes the correct target list to this function.
1668  *
1669  * Return value: first target that the source offers and the dest can
1670  *     accept, or %GDK_NONE
1671  **/
1672 GdkAtom
1673 gtk_drag_dest_find_target (GtkWidget      *widget,
1674                            GdkDragContext *context,
1675                            GtkTargetList  *target_list)
1676 {
1677   GList *tmp_target;
1678   GList *tmp_source = NULL;
1679   GtkWidget *source_widget;
1680
1681   g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1682   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1683
1684
1685   source_widget = gtk_drag_get_source_widget (context);
1686
1687   if (target_list == NULL)
1688     target_list = gtk_drag_dest_get_target_list (widget);
1689   
1690   if (target_list == NULL)
1691     return GDK_NONE;
1692   
1693   tmp_target = target_list->list;
1694   while (tmp_target)
1695     {
1696       GtkTargetPair *pair = tmp_target->data;
1697       tmp_source = gdk_drag_context_list_targets (context);
1698       while (tmp_source)
1699         {
1700           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1701             {
1702               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1703                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)) &&
1704                   (!(pair->flags & GTK_TARGET_OTHER_APP) || !source_widget) &&
1705                   (!(pair->flags & GTK_TARGET_OTHER_WIDGET) || (source_widget != widget)))
1706                 return pair->target;
1707               else
1708                 break;
1709             }
1710           tmp_source = tmp_source->next;
1711         }
1712       tmp_target = tmp_target->next;
1713     }
1714
1715   return GDK_NONE;
1716 }
1717
1718 static void
1719 gtk_drag_selection_received (GtkWidget        *widget,
1720                              GtkSelectionData *selection_data,
1721                              guint             time,
1722                              gpointer          data)
1723 {
1724   GdkDragContext *context;
1725   GtkDragDestInfo *info;
1726   GtkWidget *drop_widget;
1727   GdkAtom target;
1728
1729   drop_widget = data;
1730
1731   context = g_object_get_data (G_OBJECT (widget), "drag-context");
1732   info = gtk_drag_get_dest_info (context, FALSE);
1733
1734   if (info->proxy_data && 
1735       gtk_selection_data_get_target (info->proxy_data) == gtk_selection_data_get_target (selection_data))
1736     {
1737       gtk_selection_data_set (info->proxy_data,
1738                               gtk_selection_data_get_data_type (selection_data),
1739                               gtk_selection_data_get_format (selection_data),
1740                               gtk_selection_data_get_data (selection_data),
1741                               gtk_selection_data_get_length (selection_data));
1742       gtk_main_quit ();
1743       return;
1744     }
1745
1746   target = gtk_selection_data_get_target (selection_data);
1747   if (target == gdk_atom_intern_static_string ("DELETE"))
1748     {
1749       gtk_drag_finish (context, TRUE, FALSE, time);
1750     }
1751   else if ((target == gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS")) ||
1752            (target == gdk_atom_intern_static_string ("XmTRANSFER_FAILURE")))
1753     {
1754       /* Do nothing */
1755     }
1756   else
1757     {
1758       GtkDragDestSite *site;
1759
1760       site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1761
1762       if (site && site->target_list)
1763         {
1764           guint target_info;
1765
1766           if (gtk_target_list_find (site->target_list, 
1767                                     target,
1768                                     &target_info))
1769             {
1770               if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1771                   gtk_selection_data_get_length (selection_data) >= 0)
1772                 g_signal_emit_by_name (drop_widget,
1773                                        "drag-data-received",
1774                                        context, info->drop_x, info->drop_y,
1775                                        selection_data,
1776                                        target_info, time);
1777             }
1778         }
1779       else
1780         {
1781           g_signal_emit_by_name (drop_widget,
1782                                  "drag-data-received",
1783                                  context, info->drop_x, info->drop_y,
1784                                  selection_data,
1785                                  0, time);
1786         }
1787       
1788       if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1789         {
1790
1791           gtk_drag_finish (context, 
1792                            (gtk_selection_data_get_length (selection_data) >= 0),
1793                            (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
1794                            time);
1795         }
1796       
1797       g_object_unref (drop_widget);
1798     }
1799
1800   g_signal_handlers_disconnect_by_func (widget,
1801                                         gtk_drag_selection_received,
1802                                         data);
1803   
1804   g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
1805   g_object_unref (context);
1806
1807   gtk_drag_release_ipc_widget (widget);
1808 }
1809
1810 /*************************************************************
1811  * gtk_drag_find_widget:
1812  *     Function used to locate widgets for
1813  *     DRAG_MOTION and DROP_START events.
1814  *************************************************************/
1815
1816 static gboolean
1817 gtk_drag_find_widget (GtkWidget           *widget,
1818                       GdkDragContext      *context,
1819                       GtkDragDestInfo     *info,
1820                       gint                 x,
1821                       gint                 y,
1822                       guint32              time,
1823                       GtkDragDestCallback  callback)
1824 {
1825   if (!gtk_widget_get_mapped (widget) ||
1826       !gtk_widget_get_sensitive (widget))
1827     return FALSE;
1828
1829   /* Get the widget at the pointer coordinates and travel up
1830    * the widget hierarchy from there.
1831    */
1832   widget = _gtk_widget_find_at_coords (gtk_widget_get_window (widget),
1833                                        x, y, &x, &y);
1834   if (!widget)
1835     return FALSE;
1836
1837   while (widget)
1838     {
1839       GtkWidget *parent;
1840       GList *hierarchy = NULL;
1841       gboolean found = FALSE;
1842
1843       if (!gtk_widget_get_mapped (widget) ||
1844           !gtk_widget_get_sensitive (widget))
1845         return FALSE;
1846
1847       /* need to reference the entire hierarchy temporarily in case the
1848        * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
1849        */
1850       for (parent = widget;
1851            parent;
1852            parent = gtk_widget_get_parent (parent))
1853         {
1854           hierarchy = g_list_prepend (hierarchy, g_object_ref (parent));
1855         }
1856
1857       /* If the current widget is registered as a drop site, check to
1858        * emit "drag-motion" to check if we are actually in a drop
1859        * site.
1860        */
1861       if (g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1862         {
1863           found = callback (widget, context, x, y, time);
1864
1865           /* If so, send a "drag-leave" to the last widget */
1866           if (found)
1867             {
1868               if (info->widget && info->widget != widget)
1869                 {
1870                   gtk_drag_dest_leave (info->widget, context, time);
1871                 }
1872
1873               info->widget = widget;
1874             }
1875         }
1876
1877       if (!found)
1878         {
1879           /* Get the parent before unreffing the hierarchy because
1880            * invoking the callback might have destroyed the widget
1881            */
1882           parent = gtk_widget_get_parent (widget);
1883
1884           /* The parent might be going away when unreffing the
1885            * hierarchy, so also protect againt that
1886            */
1887           if (parent)
1888             g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
1889         }
1890
1891       g_list_foreach (hierarchy, (GFunc) g_object_unref, NULL);
1892       g_list_free (hierarchy);
1893
1894       if (found)
1895         return TRUE;
1896
1897       if (parent)
1898         g_object_remove_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
1899       else
1900         return FALSE;
1901
1902       if (!gtk_widget_translate_coordinates (widget, parent, x, y, &x, &y))
1903         return FALSE;
1904
1905       widget = parent;
1906     }
1907
1908   return FALSE;
1909 }
1910
1911 static void
1912 gtk_drag_proxy_begin (GtkWidget       *widget,
1913                       GtkDragDestInfo *dest_info,
1914                       guint32          time)
1915 {
1916   GtkDragSourceInfo *source_info;
1917   GList *tmp_list;
1918   GdkDragContext *context;
1919   GtkWidget *ipc_widget;
1920
1921   if (dest_info->proxy_source)
1922     {
1923       gdk_drag_abort (dest_info->proxy_source->context, time);
1924       gtk_drag_source_info_destroy (dest_info->proxy_source);
1925       dest_info->proxy_source = NULL;
1926     }
1927   
1928   ipc_widget = gtk_drag_get_ipc_widget (widget);
1929   context = gdk_drag_begin (gtk_widget_get_window (ipc_widget),
1930                             gdk_drag_context_list_targets (dest_info->context));
1931
1932   source_info = gtk_drag_get_source_info (context, TRUE);
1933
1934   source_info->ipc_widget = ipc_widget;
1935   source_info->widget = g_object_ref (widget);
1936
1937   source_info->target_list = gtk_target_list_new (NULL, 0);
1938   tmp_list = gdk_drag_context_list_targets (dest_info->context);
1939   while (tmp_list)
1940     {
1941       gtk_target_list_add (source_info->target_list,
1942                            GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1943       tmp_list = tmp_list->next;
1944     }
1945
1946   source_info->proxy_dest = dest_info;
1947   
1948   g_signal_connect (ipc_widget,
1949                     "selection-get",
1950                     G_CALLBACK (gtk_drag_selection_get),
1951                     source_info);
1952   
1953   dest_info->proxy_source = source_info;
1954 }
1955
1956 static void
1957 gtk_drag_dest_info_destroy (gpointer data)
1958 {
1959   g_slice_free (GtkDragDestInfo, data);
1960 }
1961
1962 static GtkDragDestInfo *
1963 gtk_drag_get_dest_info (GdkDragContext *context,
1964                         gboolean        create)
1965 {
1966   GtkDragDestInfo *info;
1967   static GQuark info_quark = 0;
1968   if (!info_quark)
1969     info_quark = g_quark_from_static_string ("gtk-dest-info");
1970   
1971   info = g_object_get_qdata (G_OBJECT (context), info_quark);
1972   if (!info && create)
1973     {
1974       info = g_slice_new0 (GtkDragDestInfo);
1975       info->context = context;
1976       g_object_set_qdata_full (G_OBJECT (context), info_quark,
1977                                info, gtk_drag_dest_info_destroy);
1978     }
1979
1980   return info;
1981 }
1982
1983 static GQuark dest_info_quark = 0;
1984
1985 static GtkDragSourceInfo *
1986 gtk_drag_get_source_info (GdkDragContext *context,
1987                           gboolean        create)
1988 {
1989   GtkDragSourceInfo *info;
1990   if (!dest_info_quark)
1991     dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1992   
1993   info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1994   if (!info && create)
1995     {
1996       info = g_new0 (GtkDragSourceInfo, 1);
1997       info->context = context;
1998       g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1999     }
2000
2001   return info;
2002 }
2003
2004 static void
2005 gtk_drag_clear_source_info (GdkDragContext *context)
2006 {
2007   g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
2008 }
2009
2010 static void
2011 gtk_drag_dest_realized (GtkWidget *widget)
2012 {
2013   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2014
2015   if (gtk_widget_is_toplevel (toplevel))
2016     gdk_window_register_dnd (gtk_widget_get_window (toplevel));
2017 }
2018
2019 static void
2020 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
2021                                  GtkWidget *previous_toplevel)
2022 {
2023   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2024
2025   if (gtk_widget_is_toplevel (toplevel) && gtk_widget_get_realized (toplevel))
2026     gdk_window_register_dnd (gtk_widget_get_window (toplevel));
2027 }
2028
2029 static void
2030 gtk_drag_dest_site_destroy (gpointer data)
2031 {
2032   GtkDragDestSite *site = data;
2033
2034   if (site->proxy_window)
2035     g_object_unref (site->proxy_window);
2036     
2037   if (site->target_list)
2038     gtk_target_list_unref (site->target_list);
2039
2040   g_slice_free (GtkDragDestSite, site);
2041 }
2042
2043 /*
2044  * Default drag handlers
2045  */
2046 static void  
2047 gtk_drag_dest_leave (GtkWidget      *widget,
2048                      GdkDragContext *context,
2049                      guint           time)
2050 {
2051   GtkDragDestSite *site;
2052
2053   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2054   g_return_if_fail (site != NULL);
2055
2056   if (site->do_proxy)
2057     {
2058       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2059
2060       if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
2061         {
2062           gdk_drag_abort (info->proxy_source->context, time);
2063           gtk_drag_source_info_destroy (info->proxy_source);
2064           info->proxy_source = NULL;
2065         }
2066       
2067       return;
2068     }
2069   else
2070     {
2071       if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
2072         gtk_drag_unhighlight (widget);
2073
2074       if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
2075           site->track_motion)
2076         g_signal_emit_by_name (widget, "drag-leave", context, time);
2077       
2078       site->have_drag = FALSE;
2079     }
2080 }
2081
2082 static gboolean
2083 gtk_drag_dest_motion (GtkWidget      *widget,
2084                       GdkDragContext *context,
2085                       gint            x,
2086                       gint            y,
2087                       guint           time)
2088 {
2089   GtkDragDestSite *site;
2090   GdkDragAction action = 0;
2091   gboolean retval;
2092
2093   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2094   g_return_val_if_fail (site != NULL, FALSE);
2095
2096   if (site->do_proxy)
2097     {
2098       GdkAtom selection;
2099       GdkEvent *current_event;
2100       GdkWindow *dest_window;
2101       GdkDragProtocol proto;
2102         
2103       GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2104
2105       if (!info->proxy_source || info->proxy_source->widget != widget)
2106         gtk_drag_proxy_begin (widget, info, time);
2107
2108       current_event = gtk_get_current_event ();
2109
2110       if (site->proxy_window)
2111         {
2112           dest_window = site->proxy_window;
2113           proto = site->proxy_protocol;
2114         }
2115       else
2116         {
2117           gdk_drag_find_window_for_screen (info->proxy_source->context,
2118                                            NULL,
2119                                            gdk_window_get_screen (current_event->dnd.window),
2120                                            current_event->dnd.x_root, 
2121                                            current_event->dnd.y_root,
2122                                            &dest_window, &proto);
2123         }
2124       
2125       gdk_drag_motion (info->proxy_source->context, 
2126                        dest_window, proto,
2127                        current_event->dnd.x_root, 
2128                        current_event->dnd.y_root, 
2129                        gdk_drag_context_get_suggested_action (context),
2130                        gdk_drag_context_get_actions (context),
2131                        time);
2132
2133       if (!site->proxy_window && dest_window)
2134         g_object_unref (dest_window);
2135
2136       selection = gdk_drag_get_selection (info->proxy_source->context);
2137       if (selection && 
2138           selection != gdk_drag_get_selection (info->context))
2139         gtk_drag_source_check_selection (info->proxy_source, selection, time);
2140
2141       gdk_event_free (current_event);
2142       
2143       return TRUE;
2144     }
2145
2146   if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
2147     {
2148       if (gdk_drag_context_get_suggested_action (context) & site->actions)
2149         action = gdk_drag_context_get_suggested_action (context);
2150       else
2151         {
2152           gint i;
2153           
2154           for (i = 0; i < 8; i++)
2155             {
2156               if ((site->actions & (1 << i)) &&
2157                   (gdk_drag_context_get_actions (context) & (1 << i)))
2158                 {
2159                   action = (1 << i);
2160                   break;
2161                 }
2162             }
2163         }
2164
2165       if (action && gtk_drag_dest_find_target (widget, context, NULL))
2166         {
2167           if (!site->have_drag)
2168             {
2169               site->have_drag = TRUE;
2170               if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
2171                 gtk_drag_highlight (widget);
2172             }
2173
2174           gdk_drag_status (context, action, time);
2175         }
2176       else
2177         {
2178           gdk_drag_status (context, 0, time);
2179           if (!site->track_motion)
2180             return TRUE;
2181         }
2182     }
2183
2184   g_signal_emit_by_name (widget, "drag-motion",
2185                          context, x, y, time, &retval);
2186
2187   return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
2188 }
2189
2190 static gboolean
2191 gtk_drag_dest_drop (GtkWidget        *widget,
2192                     GdkDragContext   *context,
2193                     gint              x,
2194                     gint              y,
2195                     guint             time)
2196 {
2197   GtkDragDestSite *site;
2198   GtkDragDestInfo *info;
2199
2200   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2201   g_return_val_if_fail (site != NULL, FALSE);
2202
2203   info = gtk_drag_get_dest_info (context, FALSE);
2204   g_return_val_if_fail (info != NULL, FALSE);
2205
2206   info->drop_x = x;
2207   info->drop_y = y;
2208
2209   if (site->do_proxy)
2210     {
2211       if (info->proxy_source || 
2212           (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_ROOTWIN))
2213         {
2214           gtk_drag_drop (info->proxy_source, time);
2215         }
2216       else
2217         {
2218           /* We need to synthesize a motion event, wait for a status,
2219            * and, if we get a good one, do a drop.
2220            */
2221           
2222           GdkEvent *current_event;
2223           GdkAtom selection;
2224           GdkWindow *dest_window;
2225           GdkDragProtocol proto;
2226           
2227           gtk_drag_proxy_begin (widget, info, time);
2228           info->proxy_drop_wait = TRUE;
2229           info->proxy_drop_time = time;
2230           
2231           current_event = gtk_get_current_event ();
2232
2233           if (site->proxy_window)
2234             {
2235               dest_window = site->proxy_window;
2236               proto = site->proxy_protocol;
2237             }
2238           else
2239             {
2240               gdk_drag_find_window_for_screen (info->proxy_source->context,
2241                                                NULL,
2242                                                gdk_window_get_screen (current_event->dnd.window),
2243                                                current_event->dnd.x_root, 
2244                                                current_event->dnd.y_root,
2245                                                &dest_window, &proto);
2246             }
2247
2248           gdk_drag_motion (info->proxy_source->context, 
2249                            dest_window, proto,
2250                            current_event->dnd.x_root, 
2251                            current_event->dnd.y_root, 
2252                            gdk_drag_context_get_suggested_action (context),
2253                            gdk_drag_context_get_actions (context),
2254                            time);
2255
2256           if (!site->proxy_window && dest_window)
2257             g_object_unref (dest_window);
2258
2259           selection = gdk_drag_get_selection (info->proxy_source->context);
2260           if (selection && 
2261               selection != gdk_drag_get_selection (info->context))
2262             gtk_drag_source_check_selection (info->proxy_source, selection, time);
2263
2264           gdk_event_free (current_event);
2265         }
2266
2267       return TRUE;
2268     }
2269   else
2270     {
2271       gboolean retval;
2272
2273       if (site->flags & GTK_DEST_DEFAULT_DROP)
2274         {
2275           GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
2276
2277           if (target == GDK_NONE)
2278             {
2279               gtk_drag_finish (context, FALSE, FALSE, time);
2280               return TRUE;
2281             }
2282           else 
2283             gtk_drag_get_data (widget, context, target, time);
2284         }
2285
2286       g_signal_emit_by_name (widget, "drag-drop",
2287                              context, x, y, time, &retval);
2288
2289       return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
2290     }
2291 }
2292
2293 /***************
2294  * Source side *
2295  ***************/
2296
2297 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
2298  * so that we can set the icon from the source site information
2299  */
2300 static GdkDragContext *
2301 gtk_drag_begin_internal (GtkWidget         *widget,
2302                          GtkDragSourceSite *site,
2303                          GtkTargetList     *target_list,
2304                          GdkDragAction      actions,
2305                          gint               button,
2306                          GdkEvent          *event)
2307 {
2308   GtkDragSourceInfo *info;
2309   GList *targets = NULL;
2310   GList *tmp_list;
2311   guint32 time = GDK_CURRENT_TIME;
2312   GdkDragAction possible_actions, suggested_action;
2313   GdkDragContext *context;
2314   GtkWidget *ipc_widget;
2315   GdkCursor *cursor;
2316   GdkDevice *pointer, *keyboard;
2317   GdkWindow *ipc_window;
2318
2319   pointer = keyboard = NULL;
2320   ipc_widget = gtk_drag_get_ipc_widget (widget);
2321   
2322   gtk_drag_get_event_actions (event, button, actions,
2323                               &suggested_action, &possible_actions);
2324   
2325   cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), 
2326                                 suggested_action,
2327                                 NULL);
2328   
2329   if (event)
2330     {
2331       time = gdk_event_get_time (event);
2332       if (time == GDK_CURRENT_TIME)
2333         time = gtk_get_current_event_time ();
2334
2335       pointer = gdk_event_get_device (event);
2336
2337       if (gdk_device_get_source (pointer) == GDK_SOURCE_KEYBOARD)
2338         {
2339           keyboard = pointer;
2340           pointer = gdk_device_get_associated_device (keyboard);
2341         }
2342       else
2343         keyboard = gdk_device_get_associated_device (pointer);
2344     }
2345   else
2346     {
2347       GdkDeviceManager *device_manager;
2348
2349       device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
2350       pointer = gdk_device_manager_get_client_pointer (device_manager);
2351       keyboard = gdk_device_get_associated_device (pointer);
2352     }
2353
2354   if (!pointer)
2355     return NULL;
2356
2357   ipc_window = gtk_widget_get_window (ipc_widget);
2358
2359   if (gdk_device_grab (pointer, ipc_window,
2360                        GDK_OWNERSHIP_APPLICATION, FALSE,
2361                        GDK_POINTER_MOTION_MASK |
2362                        GDK_BUTTON_RELEASE_MASK,
2363                        cursor, time) != GDK_GRAB_SUCCESS)
2364     {
2365       gtk_drag_release_ipc_widget (ipc_widget);
2366       return NULL;
2367     }
2368
2369   if (keyboard)
2370     grab_dnd_keys (ipc_widget, keyboard, time);
2371
2372   /* We use a GTK grab here to override any grabs that the widget
2373    * we are dragging from might have held
2374    */
2375   gtk_device_grab_add (ipc_widget, pointer, FALSE);
2376
2377   tmp_list = g_list_last (target_list->list);
2378   while (tmp_list)
2379     {
2380       GtkTargetPair *pair = tmp_list->data;
2381       targets = g_list_prepend (targets, 
2382                                 GINT_TO_POINTER (pair->target));
2383       tmp_list = tmp_list->prev;
2384     }
2385
2386   source_widgets = g_slist_prepend (source_widgets, ipc_widget);
2387
2388   context = gdk_drag_begin (ipc_window, targets);
2389   gdk_drag_context_set_device (context, pointer);
2390   g_list_free (targets);
2391   
2392   info = gtk_drag_get_source_info (context, TRUE);
2393   
2394   info->ipc_widget = ipc_widget;
2395   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), info);
2396
2397   info->widget = g_object_ref (widget);
2398   
2399   info->button = button;
2400   info->cursor = cursor;
2401   info->target_list = target_list;
2402   gtk_target_list_ref (target_list);
2403
2404   info->possible_actions = actions;
2405
2406   info->status = GTK_DRAG_STATUS_DRAG;
2407   info->last_event = NULL;
2408   info->selections = NULL;
2409   info->icon_window = NULL;
2410   info->destroy_icon = FALSE;
2411
2412   /* Set cur_x, cur_y here so if the "drag-begin" signal shows
2413    * the drag icon, it will be in the right place
2414    */
2415   if (event && event->type == GDK_MOTION_NOTIFY)
2416     {
2417       info->cur_screen = gtk_widget_get_screen (widget);
2418       info->cur_x = event->motion.x_root;
2419       info->cur_y = event->motion.y_root;
2420     }
2421   else
2422     {
2423       gdk_device_get_position (pointer, &info->cur_screen, &info->cur_x, &info->cur_y);
2424     }
2425
2426   g_signal_emit_by_name (widget, "drag-begin", info->context);
2427
2428   /* Ensure that we have an icon before we start the drag; the
2429    * application may have set one in ::drag_begin, or it may
2430    * not have set one.
2431    */
2432   if (!info->icon_window && !info->icon_pixbuf)
2433     {
2434       if (!site || site->icon_type == GTK_IMAGE_EMPTY)
2435         gtk_drag_set_icon_default (context);
2436       else
2437         switch (site->icon_type)
2438           {
2439           case GTK_IMAGE_PIXBUF:
2440             gtk_drag_set_icon_pixbuf (context,
2441                                       site->icon_data.pixbuf.pixbuf,
2442                                       -2, -2);
2443             break;
2444           case GTK_IMAGE_STOCK:
2445             gtk_drag_set_icon_stock (context,
2446                                      site->icon_data.stock.stock_id,
2447                                      -2, -2);
2448             break;
2449           case GTK_IMAGE_ICON_NAME:
2450             gtk_drag_set_icon_name (context,
2451                                     site->icon_data.name.icon_name,
2452                                     -2, -2);
2453             break;
2454           case GTK_IMAGE_EMPTY:
2455           default:
2456             g_assert_not_reached();
2457             break;
2458           }
2459     }
2460
2461   /* We need to composite the icon into the cursor, if we are
2462    * not using an icon window.
2463    */
2464   if (info->icon_pixbuf)  
2465     {
2466       cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), 
2467                                     suggested_action,
2468                                     info);
2469   
2470       if (cursor != info->cursor)
2471         {
2472           gdk_device_grab (pointer, gtk_widget_get_window (widget),
2473                            GDK_OWNERSHIP_APPLICATION, FALSE,
2474                            GDK_POINTER_MOTION_MASK |
2475                            GDK_BUTTON_RELEASE_MASK,
2476                            cursor, time);
2477           info->cursor = cursor;
2478         }
2479     }
2480     
2481   if (event && event->type == GDK_MOTION_NOTIFY)
2482     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
2483   else
2484     gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
2485
2486   info->start_x = info->cur_x;
2487   info->start_y = info->cur_y;
2488
2489   g_signal_connect (info->ipc_widget, "grab-broken-event",
2490                     G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
2491   g_signal_connect (info->ipc_widget, "grab-notify",
2492                     G_CALLBACK (gtk_drag_grab_notify_cb), info);
2493   g_signal_connect (info->ipc_widget, "button-release-event",
2494                     G_CALLBACK (gtk_drag_button_release_cb), info);
2495   g_signal_connect (info->ipc_widget, "motion-notify-event",
2496                     G_CALLBACK (gtk_drag_motion_cb), info);
2497   g_signal_connect (info->ipc_widget, "key-press-event",
2498                     G_CALLBACK (gtk_drag_key_cb), info);
2499   g_signal_connect (info->ipc_widget, "key-release-event",
2500                     G_CALLBACK (gtk_drag_key_cb), info);
2501   g_signal_connect (info->ipc_widget, "selection-get",
2502                     G_CALLBACK (gtk_drag_selection_get), info);
2503
2504   info->have_grab = TRUE;
2505   info->grab_time = time;
2506
2507   return info->context;
2508 }
2509
2510 /**
2511  * gtk_drag_begin:
2512  * @widget: the source widget.
2513  * @targets: The targets (data formats) in which the
2514  *    source can provide the data.
2515  * @actions: A bitmask of the allowed drag actions for this drag.
2516  * @button: The button the user clicked to start the drag.
2517  * @event: The event that triggered the start of the drag.
2518  *
2519  * Initiates a drag on the source side. The function
2520  * only needs to be used when the application is
2521  * starting drags itself, and is not needed when
2522  * gtk_drag_source_set() is used.
2523  *
2524  * The @event is used to retrieve the timestamp that will be used internally to
2525  * grab the pointer.  If @event is #NULL, then GDK_CURRENT_TIME will be used.
2526  * However, you should try to pass a real event in all cases, since that can be
2527  * used by GTK+ to get information about the start position of the drag, for
2528  * example if the @event is a GDK_MOTION_NOTIFY.
2529  *
2530  * Generally there are three cases when you want to start a drag by hand by
2531  * calling this function:
2532  *
2533  * 1. During a button-press-event handler, if you want to start a drag
2534  * immediately when the user presses the mouse button.  Pass the @event
2535  * that you have in your button-press-event handler.
2536  *
2537  * 2. During a motion-notify-event handler, if you want to start a drag
2538  * when the mouse moves past a certain threshold distance after a button-press.
2539  * Pass the @event that you have in your motion-notify-event handler.
2540  *
2541  * 3. During a timeout handler, if you want to start a drag after the mouse
2542  * button is held down for some time.  Try to save the last event that you got
2543  * from the mouse, using gdk_event_copy(), and pass it to this function
2544  * (remember to free the event with gdk_event_free() when you are done).
2545  * If you can really not pass a real event, pass #NULL instead.
2546  *
2547  * Return value: the context for this drag.
2548  **/
2549 GdkDragContext *
2550 gtk_drag_begin (GtkWidget         *widget,
2551                 GtkTargetList     *targets,
2552                 GdkDragAction      actions,
2553                 gint               button,
2554                 GdkEvent          *event)
2555 {
2556   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2557   g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
2558   g_return_val_if_fail (targets != NULL, NULL);
2559
2560   return gtk_drag_begin_internal (widget, NULL, targets,
2561                                   actions, button, event);
2562 }
2563
2564 /**
2565  * gtk_drag_source_set:
2566  * @widget: a #GtkWidget
2567  * @start_button_mask: the bitmask of buttons that can start the drag
2568  * @targets: (allow-none) (array length=n_targets): the table of targets that the drag will support,
2569  *     may be %NULL
2570  * @n_targets: the number of items in @targets
2571  * @actions: the bitmask of possible actions for a drag from this widget
2572  *
2573  * Sets up a widget so that GTK+ will start a drag operation when the user
2574  * clicks and drags on the widget. The widget must have a window.
2575  */
2576 void
2577 gtk_drag_source_set (GtkWidget            *widget,
2578                      GdkModifierType       start_button_mask,
2579                      const GtkTargetEntry *targets,
2580                      gint                  n_targets,
2581                      GdkDragAction         actions)
2582 {
2583   GtkDragSourceSite *site;
2584
2585   g_return_if_fail (GTK_IS_WIDGET (widget));
2586
2587   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2588
2589   gtk_widget_add_events (widget,
2590                          gtk_widget_get_events (widget) |
2591                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2592                          GDK_BUTTON_MOTION_MASK);
2593
2594   if (site)
2595     {
2596       if (site->target_list)
2597         gtk_target_list_unref (site->target_list);
2598     }
2599   else
2600     {
2601       site = g_slice_new0 (GtkDragSourceSite);
2602
2603       site->icon_type = GTK_IMAGE_EMPTY;
2604       
2605       g_signal_connect (widget, "button-press-event",
2606                         G_CALLBACK (gtk_drag_source_event_cb),
2607                         site);
2608       g_signal_connect (widget, "button-release-event",
2609                         G_CALLBACK (gtk_drag_source_event_cb),
2610                         site);
2611       g_signal_connect (widget, "motion-notify-event",
2612                         G_CALLBACK (gtk_drag_source_event_cb),
2613                         site);
2614       
2615       g_object_set_data_full (G_OBJECT (widget),
2616                               I_("gtk-site-data"), 
2617                               site, gtk_drag_source_site_destroy);
2618     }
2619
2620   site->start_button_mask = start_button_mask;
2621
2622   site->target_list = gtk_target_list_new (targets, n_targets);
2623
2624   site->actions = actions;
2625 }
2626
2627 /*************************************************************
2628  * gtk_drag_source_unset
2629  *     Unregister this widget as a drag source.
2630  *   arguments:
2631  *     widget:
2632  *   results:
2633  *************************************************************/
2634
2635 void 
2636 gtk_drag_source_unset (GtkWidget *widget)
2637 {
2638   GtkDragSourceSite *site;
2639
2640   g_return_if_fail (GTK_IS_WIDGET (widget));
2641
2642   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2643
2644   if (site)
2645     {
2646       g_signal_handlers_disconnect_by_func (widget,
2647                                             gtk_drag_source_event_cb,
2648                                             site);
2649       g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
2650     }
2651 }
2652
2653 /**
2654  * gtk_drag_source_get_target_list:
2655  * @widget: a #GtkWidget
2656  *
2657  * Gets the list of targets this widget can provide for
2658  * drag-and-drop.
2659  *
2660  * Return value: the #GtkTargetList, or %NULL if none
2661  *
2662  * Since: 2.4
2663  **/
2664 GtkTargetList *
2665 gtk_drag_source_get_target_list (GtkWidget *widget)
2666 {
2667   GtkDragSourceSite *site;
2668
2669   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2670
2671   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2672
2673   return site ? site->target_list : NULL;
2674 }
2675
2676 /**
2677  * gtk_drag_source_set_target_list:
2678  * @widget: a #GtkWidget that's a drag source
2679  * @target_list: (allow-none): list of draggable targets, or %NULL for none
2680  *
2681  * Changes the target types that this widget offers for drag-and-drop.
2682  * The widget must first be made into a drag source with
2683  * gtk_drag_source_set().
2684  *
2685  * Since: 2.4
2686  **/
2687 void
2688 gtk_drag_source_set_target_list (GtkWidget     *widget,
2689                                  GtkTargetList *target_list)
2690 {
2691   GtkDragSourceSite *site;
2692
2693   g_return_if_fail (GTK_IS_WIDGET (widget));
2694
2695   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2696   if (site == NULL)
2697     {
2698       g_warning ("gtk_drag_source_set_target_list() requires the widget "
2699                  "to already be a drag source.");
2700       return;
2701     }
2702
2703   if (target_list)
2704     gtk_target_list_ref (target_list);
2705
2706   if (site->target_list)
2707     gtk_target_list_unref (site->target_list);
2708
2709   site->target_list = target_list;
2710 }
2711
2712 /**
2713  * gtk_drag_source_add_text_targets:
2714  * @widget: a #GtkWidget that's is a drag source
2715  *
2716  * Add the text targets supported by #GtkSelection to
2717  * the target list of the drag source.  The targets
2718  * are added with @info = 0. If you need another value, 
2719  * use gtk_target_list_add_text_targets() and
2720  * gtk_drag_source_set_target_list().
2721  * 
2722  * Since: 2.6
2723  **/
2724 void
2725 gtk_drag_source_add_text_targets (GtkWidget *widget)
2726 {
2727   GtkTargetList *target_list;
2728
2729   target_list = gtk_drag_source_get_target_list (widget);
2730   if (target_list)
2731     gtk_target_list_ref (target_list);
2732   else
2733     target_list = gtk_target_list_new (NULL, 0);
2734   gtk_target_list_add_text_targets (target_list, 0);
2735   gtk_drag_source_set_target_list (widget, target_list);
2736   gtk_target_list_unref (target_list);
2737 }
2738
2739 /**
2740  * gtk_drag_source_add_image_targets:
2741  * @widget: a #GtkWidget that's is a drag source
2742  *
2743  * Add the writable image targets supported by #GtkSelection to
2744  * the target list of the drag source. The targets
2745  * are added with @info = 0. If you need another value, 
2746  * use gtk_target_list_add_image_targets() and
2747  * gtk_drag_source_set_target_list().
2748  * 
2749  * Since: 2.6
2750  **/
2751 void
2752 gtk_drag_source_add_image_targets (GtkWidget *widget)
2753 {
2754   GtkTargetList *target_list;
2755
2756   target_list = gtk_drag_source_get_target_list (widget);
2757   if (target_list)
2758     gtk_target_list_ref (target_list);
2759   else
2760     target_list = gtk_target_list_new (NULL, 0);
2761   gtk_target_list_add_image_targets (target_list, 0, TRUE);
2762   gtk_drag_source_set_target_list (widget, target_list);
2763   gtk_target_list_unref (target_list);
2764 }
2765
2766 /**
2767  * gtk_drag_source_add_uri_targets:
2768  * @widget: a #GtkWidget that's is a drag source
2769  *
2770  * Add the URI targets supported by #GtkSelection to
2771  * the target list of the drag source.  The targets
2772  * are added with @info = 0. If you need another value, 
2773  * use gtk_target_list_add_uri_targets() and
2774  * gtk_drag_source_set_target_list().
2775  * 
2776  * Since: 2.6
2777  **/
2778 void
2779 gtk_drag_source_add_uri_targets (GtkWidget *widget)
2780 {
2781   GtkTargetList *target_list;
2782
2783   target_list = gtk_drag_source_get_target_list (widget);
2784   if (target_list)
2785     gtk_target_list_ref (target_list);
2786   else
2787     target_list = gtk_target_list_new (NULL, 0);
2788   gtk_target_list_add_uri_targets (target_list, 0);
2789   gtk_drag_source_set_target_list (widget, target_list);
2790   gtk_target_list_unref (target_list);
2791 }
2792
2793 static void
2794 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2795 {
2796   switch (site->icon_type)
2797     {
2798     case GTK_IMAGE_EMPTY:
2799       break;
2800     case GTK_IMAGE_PIXBUF:
2801       g_object_unref (site->icon_data.pixbuf.pixbuf);
2802       break;
2803     case GTK_IMAGE_STOCK:
2804       g_free (site->icon_data.stock.stock_id);
2805       break;
2806     case GTK_IMAGE_ICON_NAME:
2807       g_free (site->icon_data.name.icon_name);
2808       break;
2809     default:
2810       g_assert_not_reached();
2811       break;
2812     }
2813   site->icon_type = GTK_IMAGE_EMPTY;
2814 }
2815
2816 /**
2817  * gtk_drag_source_set_icon_pixbuf:
2818  * @widget: a #GtkWidget
2819  * @pixbuf: the #GdkPixbuf for the drag icon
2820  * 
2821  * Sets the icon that will be used for drags from a particular widget
2822  * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will 
2823  * release it when it is no longer needed.
2824  **/
2825 void 
2826 gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
2827                                  GdkPixbuf   *pixbuf)
2828 {
2829   GtkDragSourceSite *site;
2830
2831   g_return_if_fail (GTK_IS_WIDGET (widget));
2832   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2833
2834   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2835   g_return_if_fail (site != NULL); 
2836   g_object_ref (pixbuf);
2837
2838   gtk_drag_source_unset_icon (site);
2839
2840   site->icon_type = GTK_IMAGE_PIXBUF;
2841   site->icon_data.pixbuf.pixbuf = pixbuf;
2842 }
2843
2844 /**
2845  * gtk_drag_source_set_icon_stock:
2846  * @widget: a #GtkWidget
2847  * @stock_id: the ID of the stock icon to use
2848  *
2849  * Sets the icon that will be used for drags from a particular source
2850  * to a stock icon. 
2851  **/
2852 void 
2853 gtk_drag_source_set_icon_stock (GtkWidget   *widget,
2854                                 const gchar *stock_id)
2855 {
2856   GtkDragSourceSite *site;
2857
2858   g_return_if_fail (GTK_IS_WIDGET (widget));
2859   g_return_if_fail (stock_id != NULL);
2860
2861   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2862   g_return_if_fail (site != NULL);
2863   
2864   gtk_drag_source_unset_icon (site);
2865
2866   site->icon_type = GTK_IMAGE_STOCK;
2867   site->icon_data.stock.stock_id = g_strdup (stock_id);
2868 }
2869
2870 /**
2871  * gtk_drag_source_set_icon_name:
2872  * @widget: a #GtkWidget
2873  * @icon_name: name of icon to use
2874  * 
2875  * Sets the icon that will be used for drags from a particular source
2876  * to a themed icon. See the docs for #GtkIconTheme for more details.
2877  *
2878  * Since: 2.8
2879  **/
2880 void 
2881 gtk_drag_source_set_icon_name (GtkWidget   *widget,
2882                                const gchar *icon_name)
2883 {
2884   GtkDragSourceSite *site;
2885
2886   g_return_if_fail (GTK_IS_WIDGET (widget));
2887   g_return_if_fail (icon_name != NULL);
2888
2889   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2890   g_return_if_fail (site != NULL);
2891
2892   gtk_drag_source_unset_icon (site);
2893
2894   site->icon_type = GTK_IMAGE_ICON_NAME;
2895   site->icon_data.name.icon_name = g_strdup (icon_name);
2896 }
2897
2898 static void
2899 gtk_drag_get_icon (GtkDragSourceInfo *info,
2900                    GtkWidget        **icon_window,
2901                    gint              *hot_x,
2902                    gint              *hot_y)
2903 {
2904   if (get_can_change_screen (info->icon_window))
2905     gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2906                            info->cur_screen);
2907       
2908   if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2909     {
2910       if (!info->fallback_icon)
2911         {
2912           gint save_hot_x, save_hot_y;
2913           gboolean save_destroy_icon;
2914           GtkWidget *save_icon_window;
2915           
2916           /* HACK to get the appropriate icon
2917            */
2918           save_icon_window = info->icon_window;
2919           save_hot_x = info->hot_x;
2920           save_hot_y = info->hot_x;
2921           save_destroy_icon = info->destroy_icon;
2922
2923           info->icon_window = NULL;
2924           set_icon_stock_pixbuf (info->context, 
2925                                  GTK_STOCK_DND, NULL, -2, -2, TRUE);
2926           info->fallback_icon = info->icon_window;
2927           
2928           info->icon_window = save_icon_window;
2929           info->hot_x = save_hot_x;
2930           info->hot_y = save_hot_y;
2931           info->destroy_icon = save_destroy_icon;
2932         }
2933       
2934       gtk_widget_hide (info->icon_window);
2935       
2936       *icon_window = info->fallback_icon;
2937       gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2938       
2939       *hot_x = -2;
2940       *hot_y = -2;
2941     }
2942   else
2943     {
2944       if (info->fallback_icon)
2945         gtk_widget_hide (info->fallback_icon);
2946       
2947       *icon_window = info->icon_window;
2948       *hot_x = info->hot_x;
2949       *hot_y = info->hot_y;
2950     }
2951 }
2952
2953 static void
2954 gtk_drag_update_icon (GtkDragSourceInfo *info)
2955 {
2956   if (info->icon_window)
2957     {
2958       GtkWidget *icon_window;
2959       gint hot_x, hot_y;
2960   
2961       gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
2962       
2963       gtk_window_move (GTK_WINDOW (icon_window), 
2964                        info->cur_x - hot_x, 
2965                        info->cur_y - hot_y);
2966
2967       if (gtk_widget_get_visible (icon_window))
2968         gdk_window_raise (gtk_widget_get_window (icon_window));
2969       else
2970         gtk_widget_show (icon_window);
2971     }
2972 }
2973
2974 static void 
2975 gtk_drag_set_icon_window (GdkDragContext *context,
2976                           GtkWidget      *widget,
2977                           gint            hot_x,
2978                           gint            hot_y,
2979                           gboolean        destroy_on_release)
2980 {
2981   GtkDragSourceInfo *info;
2982
2983   info = gtk_drag_get_source_info (context, FALSE);
2984   if (info == NULL)
2985     {
2986       if (destroy_on_release)
2987         gtk_widget_destroy (widget);
2988       return;
2989     }
2990
2991   gtk_drag_remove_icon (info);
2992
2993   if (widget)
2994     g_object_ref (widget);  
2995   
2996   info->icon_window = widget;
2997   info->hot_x = hot_x;
2998   info->hot_y = hot_y;
2999   info->destroy_icon = destroy_on_release;
3000
3001   if (widget && info->icon_pixbuf)
3002     {
3003       g_object_unref (info->icon_pixbuf);
3004       info->icon_pixbuf = NULL;
3005     }
3006
3007   gtk_drag_update_cursor (info);
3008   gtk_drag_update_icon (info);
3009 }
3010
3011 /**
3012  * gtk_drag_set_icon_widget:
3013  * @context: the context for a drag. (This must be called 
3014           with a  context for the source side of a drag)
3015  * @widget: a toplevel window to use as an icon.
3016  * @hot_x: the X offset within @widget of the hotspot.
3017  * @hot_y: the Y offset within @widget of the hotspot.
3018  * 
3019  * Changes the icon for a widget to a given widget. GTK+
3020  * will not destroy the icon, so if you don't want
3021  * it to persist, you should connect to the "drag-end" 
3022  * signal and destroy it yourself.
3023  **/
3024 void 
3025 gtk_drag_set_icon_widget (GdkDragContext    *context,
3026                           GtkWidget         *widget,
3027                           gint               hot_x,
3028                           gint               hot_y)
3029 {
3030   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3031   g_return_if_fail (GTK_IS_WIDGET (widget));
3032
3033   gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
3034 }
3035
3036 static void
3037 icon_window_realize (GtkWidget *window,
3038                      GdkPixbuf *pixbuf)
3039 {
3040   cairo_surface_t *surface;
3041   cairo_pattern_t *pattern;
3042   cairo_t *cr;
3043
3044   surface = gdk_window_create_similar_surface (gtk_widget_get_window (window),
3045                                                CAIRO_CONTENT_COLOR,
3046                                                gdk_pixbuf_get_width (pixbuf),
3047                                                gdk_pixbuf_get_height (pixbuf));
3048
3049   cr = cairo_create (surface);
3050   cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
3051   gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
3052   cairo_paint (cr);
3053   cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE);
3054   cairo_paint (cr);
3055   cairo_pop_group_to_source (cr);
3056   cairo_paint (cr);
3057   cairo_destroy (cr);
3058
3059   pattern = cairo_pattern_create_for_surface (surface);
3060   gdk_window_set_background_pattern (gtk_widget_get_window (window), pattern);
3061   cairo_pattern_destroy (pattern);
3062
3063   cairo_surface_destroy (surface);
3064
3065   if (gdk_pixbuf_get_has_alpha (pixbuf))
3066     {
3067       cairo_region_t *region;
3068
3069       surface = cairo_image_surface_create (CAIRO_FORMAT_A1,
3070                                             gdk_pixbuf_get_width (pixbuf),
3071                                             gdk_pixbuf_get_height (pixbuf));
3072       
3073       cr = cairo_create (surface);
3074       gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
3075       cairo_paint (cr);
3076       cairo_destroy (cr);
3077
3078       region = gdk_cairo_region_create_from_surface (surface);
3079       gtk_widget_shape_combine_region (window, region);
3080       cairo_region_destroy (region);
3081
3082       cairo_surface_destroy (surface);
3083     }
3084 }
3085
3086 static void
3087 set_icon_stock_pixbuf (GdkDragContext    *context,
3088                        const gchar       *stock_id,
3089                        GdkPixbuf         *pixbuf,
3090                        gint               hot_x,
3091                        gint               hot_y,
3092                        gboolean           force_window)
3093 {
3094   GtkWidget *window;
3095   gint width, height;
3096   GdkScreen *screen;
3097   GdkDisplay *display;
3098
3099   g_return_if_fail (context != NULL);
3100   g_return_if_fail (pixbuf != NULL || stock_id != NULL);
3101   g_return_if_fail (pixbuf == NULL || stock_id == NULL);
3102
3103   screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
3104
3105   window = gtk_window_new (GTK_WINDOW_POPUP);
3106   gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3107   gtk_window_set_screen (GTK_WINDOW (window), screen);
3108   set_can_change_screen (window, TRUE);
3109
3110   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3111   gtk_widget_set_app_paintable (window, TRUE);
3112
3113   if (stock_id)
3114     {
3115       pixbuf = gtk_widget_render_icon_pixbuf (window, stock_id,
3116                                               GTK_ICON_SIZE_DND);
3117
3118       if (!pixbuf)
3119         {
3120           g_warning ("Cannot load drag icon from stock_id %s", stock_id);
3121           gtk_widget_destroy (window);
3122           return;
3123         }
3124
3125     }
3126   else
3127     g_object_ref (pixbuf);
3128
3129   display = gdk_window_get_display (gdk_drag_context_get_source_window (context));
3130   width = gdk_pixbuf_get_width (pixbuf);
3131   height = gdk_pixbuf_get_height (pixbuf);
3132
3133   if (!force_window &&
3134       gtk_drag_can_use_rgba_cursor (display, width + 2, height + 2))
3135     {
3136       GtkDragSourceInfo *info;
3137
3138       gtk_widget_destroy (window);
3139
3140       info = gtk_drag_get_source_info (context, FALSE);
3141
3142       if (info->icon_pixbuf)
3143         g_object_unref (info->icon_pixbuf);
3144       info->icon_pixbuf = pixbuf;
3145
3146       gtk_drag_set_icon_window (context, NULL, hot_x, hot_y, TRUE);
3147     }
3148   else
3149     {
3150       gtk_widget_set_size_request (window, width, height);
3151
3152       g_signal_connect_closure (window, "realize",
3153                                 g_cclosure_new (G_CALLBACK (icon_window_realize),
3154                                                 pixbuf,
3155                                                 (GClosureNotify)g_object_unref),
3156                                 FALSE);
3157                     
3158       gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3159    }
3160 }
3161
3162 /**
3163  * gtk_drag_set_icon_pixbuf:
3164  * @context: the context for a drag. (This must be called 
3165  *            with a  context for the source side of a drag)
3166  * @pixbuf: the #GdkPixbuf to use as the drag icon.
3167  * @hot_x: the X offset within @widget of the hotspot.
3168  * @hot_y: the Y offset within @widget of the hotspot.
3169  * 
3170  * Sets @pixbuf as the icon for a given drag.
3171  **/
3172 void 
3173 gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
3174                            GdkPixbuf      *pixbuf,
3175                            gint            hot_x,
3176                            gint            hot_y)
3177 {
3178   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3179   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
3180
3181   set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3182 }
3183
3184 /**
3185  * gtk_drag_set_icon_stock:
3186  * @context: the context for a drag. (This must be called 
3187  *            with a  context for the source side of a drag)
3188  * @stock_id: the ID of the stock icon to use for the drag.
3189  * @hot_x: the X offset within the icon of the hotspot.
3190  * @hot_y: the Y offset within the icon of the hotspot.
3191  * 
3192  * Sets the icon for a given drag from a stock ID.
3193  **/
3194 void 
3195 gtk_drag_set_icon_stock  (GdkDragContext *context,
3196                           const gchar    *stock_id,
3197                           gint            hot_x,
3198                           gint            hot_y)
3199 {
3200   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3201   g_return_if_fail (stock_id != NULL);
3202   
3203   set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y, FALSE);
3204 }
3205
3206 /* XXX: This function is in gdk, too. Should it be in Cairo? */
3207 static gboolean
3208 _gtk_cairo_surface_extents (cairo_surface_t *surface,
3209                             GdkRectangle *extents)
3210 {
3211   double x1, x2, y1, y2;
3212   cairo_t *cr;
3213
3214   g_return_val_if_fail (surface != NULL, FALSE);
3215   g_return_val_if_fail (extents != NULL, FALSE);
3216
3217   cr = cairo_create (surface);
3218   cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
3219
3220   x1 = floor (x1);
3221   y1 = floor (y1);
3222   x2 = ceil (x2);
3223   y2 = ceil (y2);
3224   x2 -= x1;
3225   y2 -= y1;
3226   
3227   if (x1 < G_MININT || x1 > G_MAXINT ||
3228       y1 < G_MININT || y1 > G_MAXINT ||
3229       x2 > G_MAXINT || y2 > G_MAXINT)
3230     {
3231       extents->x = extents->y = extents->width = extents->height = 0;
3232       return FALSE;
3233     }
3234
3235   extents->x = x1;
3236   extents->y = y1;
3237   extents->width = x2;
3238   extents->height = y2;
3239
3240   return TRUE;
3241 }
3242
3243 /**
3244  * gtk_drag_set_icon_surface:
3245  * @context: the context for a drag. (This must be called
3246  *            with a context for the source side of a drag)
3247  * @surface: the surface to use as icon
3248  *
3249  * Sets @surface as the icon for a given drag. GTK+ retains
3250  * references for the arguments, and will release them when
3251  * they are no longer needed.
3252  *
3253  * To position the surface relative to the mouse, use
3254  * cairo_surface_set_device_offset() on @surface. The mouse
3255  * cursor will be positioned at the (0,0) coordinate of the
3256  * surface.
3257  **/
3258 void 
3259 gtk_drag_set_icon_surface (GdkDragContext    *context,
3260                            cairo_surface_t   *surface)
3261 {
3262   GtkWidget *window;
3263   GdkScreen *screen;
3264   GdkRectangle extents;
3265   cairo_pattern_t *pattern;
3266       
3267   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3268   g_return_if_fail (surface != NULL);
3269
3270   _gtk_cairo_surface_extents (surface, &extents);
3271
3272
3273   screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
3274
3275   window = gtk_window_new (GTK_WINDOW_POPUP);
3276   gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3277   gtk_window_set_screen (GTK_WINDOW (window), screen);
3278   set_can_change_screen (window, TRUE);
3279
3280   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3281   gtk_widget_set_app_paintable (window, TRUE);
3282
3283   gtk_widget_set_size_request (window, extents.width, extents.height);
3284   gtk_widget_realize (window);
3285
3286   if (cairo_surface_get_content (surface) != CAIRO_CONTENT_COLOR)
3287     {
3288       cairo_surface_t *saturated;
3289       cairo_region_t *region;
3290       cairo_t *cr;
3291
3292       region = gdk_cairo_region_create_from_surface (surface);
3293       cairo_region_translate (region, -extents.x, -extents.y);
3294
3295       gtk_widget_shape_combine_region (window, region);
3296       cairo_region_destroy (region);
3297
3298       /* Need to saturate the colors, so it doesn't look like semi-transparent
3299        * pixels were painted on black. */
3300       saturated = gdk_window_create_similar_surface (gtk_widget_get_window (window),
3301                                                      CAIRO_CONTENT_COLOR,
3302                                                      extents.width,
3303                                                      extents.height);
3304       
3305       cr = cairo_create (saturated);
3306       cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
3307       cairo_set_source_surface (cr, surface, -extents.x, -extents.y);
3308       cairo_paint (cr);
3309       cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE);
3310       cairo_paint (cr);
3311       cairo_pop_group_to_source (cr);
3312       cairo_paint (cr);
3313       cairo_destroy (cr);
3314     
3315       pattern = cairo_pattern_create_for_surface (saturated);
3316
3317       cairo_surface_destroy (saturated);
3318     }
3319   else
3320     {
3321       cairo_matrix_t matrix;
3322
3323       pattern = cairo_pattern_create_for_surface (surface);
3324       cairo_matrix_init_translate (&matrix, extents.x, extents.y);
3325       cairo_pattern_set_matrix (pattern, &matrix);
3326     }
3327
3328   gdk_window_set_background_pattern (gtk_widget_get_window (window), pattern);
3329
3330   gtk_drag_set_icon_window (context, window, extents.x, extents.y, TRUE);
3331 }
3332
3333 /**
3334  * gtk_drag_set_icon_name:
3335  * @context: the context for a drag. (This must be called 
3336  *            with a context for the source side of a drag)
3337  * @icon_name: name of icon to use
3338  * @hot_x: the X offset of the hotspot within the icon
3339  * @hot_y: the Y offset of the hotspot within the icon
3340  * 
3341  * Sets the icon for a given drag from a named themed icon. See
3342  * the docs for #GtkIconTheme for more details. Note that the
3343  * size of the icon depends on the icon theme (the icon is
3344  * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus 
3345  * @hot_x and @hot_y have to be used with care.
3346  *
3347  * Since: 2.8
3348  **/
3349 void 
3350 gtk_drag_set_icon_name (GdkDragContext *context,
3351                         const gchar    *icon_name,
3352                         gint            hot_x,
3353                         gint            hot_y)
3354 {
3355   GdkScreen *screen;
3356   GtkSettings *settings;
3357   GtkIconTheme *icon_theme;
3358   GdkPixbuf *pixbuf;
3359   gint width, height, icon_size;
3360
3361   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3362   g_return_if_fail (icon_name != NULL);
3363
3364   screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
3365   g_return_if_fail (screen != NULL);
3366
3367   settings = gtk_settings_get_for_screen (screen);
3368   if (gtk_icon_size_lookup_for_settings (settings,
3369                                          GTK_ICON_SIZE_DND,
3370                                          &width, &height))
3371     icon_size = MAX (width, height);
3372   else 
3373     icon_size = 32; /* default value for GTK_ICON_SIZE_DND */ 
3374
3375   icon_theme = gtk_icon_theme_get_for_screen (screen);
3376
3377   pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name,
3378                                      icon_size, 0, NULL);
3379   if (pixbuf)
3380     set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3381   else
3382     g_warning ("Cannot load drag icon from icon name %s", icon_name);
3383 }
3384
3385 /**
3386  * gtk_drag_set_icon_default:
3387  * @context: the context for a drag. (This must be called 
3388              with a  context for the source side of a drag)
3389  * 
3390  * Sets the icon for a particular drag to the default
3391  * icon.
3392  **/
3393 void 
3394 gtk_drag_set_icon_default (GdkDragContext *context)
3395 {
3396   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3397
3398   gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
3399 }
3400
3401 /*************************************************************
3402  * _gtk_drag_source_handle_event:
3403  *     Called from widget event handling code on Drag events
3404  *     for drag sources.
3405  *
3406  *   arguments:
3407  *     toplevel: Toplevel widget that received the event
3408  *     event:
3409  *   results:
3410  *************************************************************/
3411
3412 void
3413 _gtk_drag_source_handle_event (GtkWidget *widget,
3414                                GdkEvent  *event)
3415 {
3416   GtkDragSourceInfo *info;
3417   GdkDragContext *context;
3418
3419   g_return_if_fail (widget != NULL);
3420   g_return_if_fail (event != NULL);
3421
3422   context = event->dnd.context;
3423   info = gtk_drag_get_source_info (context, FALSE);
3424   if (!info)
3425     return;
3426
3427   switch (event->type)
3428     {
3429     case GDK_DRAG_STATUS:
3430       {
3431         GdkCursor *cursor;
3432
3433         if (info->proxy_dest)
3434           {
3435             if (!event->dnd.send_event)
3436               {
3437                 if (info->proxy_dest->proxy_drop_wait)
3438                   {
3439                     gboolean result = gdk_drag_context_get_selected_action (context) != 0;
3440                     
3441                     /* Aha - we can finally pass the MOTIF DROP on... */
3442                     gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
3443                     if (result)
3444                       gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
3445                     else
3446                       gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
3447                   }
3448                 else
3449                   {
3450                     gdk_drag_status (info->proxy_dest->context,
3451                                      gdk_drag_context_get_selected_action (event->dnd.context),
3452                                      event->dnd.time);
3453                   }
3454               }
3455           }
3456         else if (info->have_grab)
3457           {
3458             cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
3459                                           gdk_drag_context_get_selected_action (event->dnd.context),
3460                                           info);
3461             if (info->cursor != cursor)
3462               {
3463                 GdkDevice *pointer;
3464
3465                 pointer = gdk_drag_context_get_device (context);
3466                 gdk_device_grab (pointer, gtk_widget_get_window (widget),
3467                                  GDK_OWNERSHIP_APPLICATION, FALSE,
3468                                  GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3469                                  cursor, info->grab_time);
3470                 info->cursor = cursor;
3471               }
3472             
3473             gtk_drag_add_update_idle (info);
3474           }
3475       }
3476       break;
3477       
3478     case GDK_DROP_FINISHED:
3479       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, event->dnd.time);
3480       break;
3481     default:
3482       g_assert_not_reached ();
3483     }
3484 }
3485
3486 /*************************************************************
3487  * gtk_drag_source_check_selection:
3488  *     Check if we've set up handlers/claimed the selection
3489  *     for a given drag. If not, add them.
3490  *   arguments:
3491  *     
3492  *   results:
3493  *************************************************************/
3494
3495 static void
3496 gtk_drag_source_check_selection (GtkDragSourceInfo *info, 
3497                                  GdkAtom            selection,
3498                                  guint32            time)
3499 {
3500   GList *tmp_list;
3501
3502   tmp_list = info->selections;
3503   while (tmp_list)
3504     {
3505       if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
3506         return;
3507       tmp_list = tmp_list->next;
3508     }
3509
3510   gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
3511                                        info->ipc_widget,
3512                                        selection,
3513                                        time);
3514   info->selections = g_list_prepend (info->selections,
3515                                      GUINT_TO_POINTER (selection));
3516
3517   tmp_list = info->target_list->list;
3518   while (tmp_list)
3519     {
3520       GtkTargetPair *pair = tmp_list->data;
3521
3522       gtk_selection_add_target (info->ipc_widget,
3523                                 selection,
3524                                 pair->target,
3525                                 pair->info);
3526       tmp_list = tmp_list->next;
3527     }
3528   
3529   if (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_MOTIF)
3530     {
3531       gtk_selection_add_target (info->ipc_widget,
3532                                 selection,
3533                                 gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS"),
3534                                 TARGET_MOTIF_SUCCESS);
3535       gtk_selection_add_target (info->ipc_widget,
3536                                 selection,
3537                                 gdk_atom_intern_static_string ("XmTRANSFER_FAILURE"),
3538                                 TARGET_MOTIF_FAILURE);
3539     }
3540
3541   gtk_selection_add_target (info->ipc_widget,
3542                             selection,
3543                             gdk_atom_intern_static_string ("DELETE"),
3544                             TARGET_DELETE);
3545 }
3546
3547 /*************************************************************
3548  * gtk_drag_drop_finished:
3549  *     Clean up from the drag, and display snapback, if necessary.
3550  *   arguments:
3551  *     info:
3552  *     success:
3553  *     time:
3554  *   results:
3555  *************************************************************/
3556
3557 static void
3558 gtk_drag_drop_finished (GtkDragSourceInfo *info,
3559                         GtkDragResult      result,
3560                         guint              time)
3561 {
3562   gboolean success;
3563
3564   success = (result == GTK_DRAG_RESULT_SUCCESS);
3565   gtk_drag_source_release_selections (info, time); 
3566
3567   if (info->proxy_dest)
3568     {
3569       /* The time from the event isn't reliable for Xdnd drags */
3570       gtk_drag_finish (info->proxy_dest->context, success, FALSE, 
3571                        info->proxy_dest->proxy_drop_time);
3572       gtk_drag_source_info_destroy (info);
3573     }
3574   else
3575     {
3576       if (!success)
3577         g_signal_emit_by_name (info->widget, "drag-failed",
3578                                info->context, result, &success);
3579
3580       if (success)
3581         {
3582           gtk_drag_source_info_destroy (info);
3583         }
3584       else
3585         {
3586           GtkDragAnim *anim = g_slice_new0 (GtkDragAnim);
3587           anim->info = info;
3588           anim->step = 0;
3589
3590           anim->n_steps = MAX (info->cur_x - info->start_x,
3591                info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
3592           anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
3593
3594           info->cur_screen = gtk_widget_get_screen (info->widget);
3595
3596           if (!info->icon_window)
3597             set_icon_stock_pixbuf (info->context, NULL, info->icon_pixbuf, 
3598                                    0, 0, TRUE);
3599
3600           gtk_drag_update_icon (info);
3601
3602           /* Mark the context as dead, so if the destination decides
3603            * to respond really late, we still are OK.
3604            */
3605           gtk_drag_clear_source_info (info->context);
3606           gdk_threads_add_timeout (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
3607         }
3608     }
3609 }
3610
3611 static void
3612 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
3613                                     guint32            time)
3614 {
3615   GdkDisplay *display = gtk_widget_get_display (info->widget);
3616   GList *tmp_list = info->selections;
3617   
3618   while (tmp_list)
3619     {
3620       GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
3621       if (gdk_selection_owner_get_for_display (display, selection) == gtk_widget_get_window (info->ipc_widget))
3622         gtk_selection_owner_set_for_display (display, NULL, selection, time);
3623
3624       tmp_list = tmp_list->next;
3625     }
3626
3627   g_list_free (info->selections);
3628   info->selections = NULL;
3629 }
3630
3631 /*************************************************************
3632  * gtk_drag_drop:
3633  *     Send a drop event.
3634  *   arguments:
3635  *     
3636  *   results:
3637  *************************************************************/
3638
3639 static void
3640 gtk_drag_drop (GtkDragSourceInfo *info, 
3641                guint32            time)
3642 {
3643   if (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_ROOTWIN)
3644     {
3645       GtkSelectionData selection_data;
3646       GList *tmp_list;
3647       /* GTK+ traditionally has used application/x-rootwin-drop, but the
3648        * XDND spec specifies x-rootwindow-drop.
3649        */
3650       GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3651       GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3652       
3653       tmp_list = info->target_list->list;
3654       while (tmp_list)
3655         {
3656           GtkTargetPair *pair = tmp_list->data;
3657           
3658           if (pair->target == target1 || pair->target == target2)
3659             {
3660               selection_data.selection = GDK_NONE;
3661               selection_data.target = pair->target;
3662               selection_data.data = NULL;
3663               selection_data.length = -1;
3664               
3665               g_signal_emit_by_name (info->widget, "drag-data-get",
3666                                      info->context, &selection_data,
3667                                      pair->info,
3668                                      time);
3669               
3670               /* FIXME: Should we check for length >= 0 here? */
3671               gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3672               return;
3673             }
3674           tmp_list = tmp_list->next;
3675         }
3676       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3677     }
3678   else
3679     {
3680       if (info->icon_window)
3681         gtk_widget_hide (info->icon_window);
3682         
3683       gdk_drag_drop (info->context, time);
3684       info->drop_timeout = gdk_threads_add_timeout (DROP_ABORT_TIME,
3685                                           gtk_drag_abort_timeout,
3686                                           info);
3687     }
3688 }
3689
3690 /*
3691  * Source side callbacks.
3692  */
3693
3694 static gboolean
3695 gtk_drag_source_event_cb (GtkWidget      *widget,
3696                           GdkEvent       *event,
3697                           gpointer        data)
3698 {
3699   GtkDragSourceSite *site;
3700   gboolean retval = FALSE;
3701   site = (GtkDragSourceSite *)data;
3702
3703   switch (event->type)
3704     {
3705     case GDK_BUTTON_PRESS:
3706       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3707         {
3708           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
3709           site->x = event->button.x;
3710           site->y = event->button.y;
3711         }
3712       break;
3713       
3714     case GDK_BUTTON_RELEASE:
3715       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3716         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
3717       break;
3718       
3719     case GDK_MOTION_NOTIFY:
3720       if (site->state & event->motion.state & site->start_button_mask)
3721         {
3722           /* FIXME: This is really broken and can leave us
3723            * with a stuck grab
3724            */
3725           int i;
3726           for (i=1; i<6; i++)
3727             {
3728               if (site->state & event->motion.state & 
3729                   GDK_BUTTON1_MASK << (i - 1))
3730                 break;
3731             }
3732
3733           if (gtk_drag_check_threshold (widget, site->x, site->y,
3734                                         event->motion.x, event->motion.y))
3735             {
3736               site->state = 0;
3737               gtk_drag_begin_internal (widget, site, site->target_list,
3738                                        site->actions, 
3739                                        i, event);
3740
3741               retval = TRUE;
3742             }
3743         }
3744       break;
3745       
3746     default:                    /* hit for 2/3BUTTON_PRESS */
3747       break;
3748     }
3749   
3750   return retval;
3751 }
3752
3753 static void 
3754 gtk_drag_source_site_destroy (gpointer data)
3755 {
3756   GtkDragSourceSite *site = data;
3757
3758   if (site->target_list)
3759     gtk_target_list_unref (site->target_list);
3760
3761   gtk_drag_source_unset_icon (site);
3762   g_slice_free (GtkDragSourceSite, site);
3763 }
3764
3765 static void
3766 gtk_drag_selection_get (GtkWidget        *widget, 
3767                         GtkSelectionData *selection_data,
3768                         guint             sel_info,
3769                         guint32           time,
3770                         gpointer          data)
3771 {
3772   GtkDragSourceInfo *info = data;
3773   static GdkAtom null_atom = GDK_NONE;
3774   guint target_info;
3775
3776   if (!null_atom)
3777     null_atom = gdk_atom_intern_static_string ("NULL");
3778
3779   switch (sel_info)
3780     {
3781     case TARGET_DELETE:
3782       g_signal_emit_by_name (info->widget,
3783                              "drag-data-delete", 
3784                              info->context);
3785       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3786       break;
3787     case TARGET_MOTIF_SUCCESS:
3788       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3789       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3790       break;
3791     case TARGET_MOTIF_FAILURE:
3792       gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3793       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3794       break;
3795     default:
3796       if (info->proxy_dest)
3797         {
3798           /* This is sort of dangerous and needs to be thought
3799            * through better
3800            */
3801           info->proxy_dest->proxy_data = selection_data;
3802           gtk_drag_get_data (info->widget,
3803                              info->proxy_dest->context,
3804                              gtk_selection_data_get_target (selection_data),
3805                              time);
3806           gtk_main ();
3807           info->proxy_dest->proxy_data = NULL;
3808         }
3809       else
3810         {
3811           if (gtk_target_list_find (info->target_list, 
3812                                     gtk_selection_data_get_target (selection_data),
3813                                     &target_info))
3814             {
3815               g_signal_emit_by_name (info->widget, "drag-data-get",
3816                                      info->context,
3817                                      selection_data,
3818                                      target_info,
3819                                      time);
3820             }
3821         }
3822       break;
3823     }
3824 }
3825
3826 static gboolean
3827 gtk_drag_anim_timeout (gpointer data)
3828 {
3829   GtkDragAnim *anim;
3830   GtkDragSourceInfo *info;
3831   gint x, y;
3832   gboolean retval;
3833
3834   anim = data;
3835   info = anim->info;
3836
3837   if (anim->step == anim->n_steps)
3838     {
3839       gtk_drag_source_info_destroy (anim->info);
3840       g_slice_free (GtkDragAnim, anim);
3841
3842       retval = FALSE;
3843     }
3844   else
3845     {
3846       x = (info->start_x * (anim->step + 1) +
3847            info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3848       y = (info->start_y * (anim->step + 1) +
3849            info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3850       if (info->icon_window)
3851         {
3852           GtkWidget *icon_window;
3853           gint hot_x, hot_y;
3854
3855           gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
3856           gtk_window_move (GTK_WINDOW (icon_window),
3857                            x - hot_x,
3858                            y - hot_y);
3859         }
3860
3861       anim->step++;
3862
3863       retval = TRUE;
3864     }
3865
3866   return retval;
3867 }
3868
3869 static void
3870 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3871 {
3872   if (info->icon_window)
3873     {
3874       gtk_widget_hide (info->icon_window);
3875       if (info->destroy_icon)
3876         gtk_widget_destroy (info->icon_window);
3877
3878       if (info->fallback_icon)
3879         {
3880           gtk_widget_destroy (info->fallback_icon);
3881           info->fallback_icon = NULL;
3882         }
3883
3884       g_object_unref (info->icon_window);
3885       info->icon_window = NULL;
3886     }
3887 }
3888
3889 static void
3890 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3891 {
3892   gint i;
3893
3894   for (i = 0; i < n_drag_cursors; i++)
3895     {
3896       if (info->drag_cursors[i] != NULL)
3897         {
3898           g_object_unref (info->drag_cursors[i]);
3899           info->drag_cursors[i] = NULL;
3900         }
3901     }
3902
3903   gtk_drag_remove_icon (info);
3904
3905   if (info->icon_pixbuf)
3906     {
3907       g_object_unref (info->icon_pixbuf);
3908       info->icon_pixbuf = NULL;
3909     }
3910
3911   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3912                                         gtk_drag_grab_broken_event_cb,
3913                                         info);
3914   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3915                                         gtk_drag_grab_notify_cb,
3916                                         info);
3917   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3918                                         gtk_drag_button_release_cb,
3919                                         info);
3920   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3921                                         gtk_drag_motion_cb,
3922                                         info);
3923   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3924                                         gtk_drag_key_cb,
3925                                         info);
3926   g_signal_handlers_disconnect_by_func (info->ipc_widget,
3927                                         gtk_drag_selection_get,
3928                                         info);
3929
3930   if (!info->proxy_dest)
3931     g_signal_emit_by_name (info->widget, "drag-end", info->context);
3932
3933   if (info->widget)
3934     g_object_unref (info->widget);
3935
3936   gtk_selection_remove_all (info->ipc_widget);
3937   g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), NULL);
3938   source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3939   gtk_drag_release_ipc_widget (info->ipc_widget);
3940
3941   gtk_target_list_unref (info->target_list);
3942
3943   gtk_drag_clear_source_info (info->context);
3944   g_object_unref (info->context);
3945
3946   if (info->drop_timeout)
3947     g_source_remove (info->drop_timeout);
3948
3949   if (info->update_idle)
3950     g_source_remove (info->update_idle);
3951
3952   g_free (info);
3953 }
3954
3955 static gboolean
3956 gtk_drag_update_idle (gpointer data)
3957 {
3958   GtkDragSourceInfo *info = data;
3959   GdkWindow *dest_window;
3960   GdkDragProtocol protocol;
3961   GdkAtom selection;
3962
3963   GdkDragAction action;
3964   GdkDragAction possible_actions;
3965   guint32 time;
3966
3967   info->update_idle = 0;
3968     
3969   if (info->last_event)
3970     {
3971       time = gtk_drag_get_event_time (info->last_event);
3972       gtk_drag_get_event_actions (info->last_event,
3973                                   info->button, 
3974                                   info->possible_actions,
3975                                   &action, &possible_actions);
3976       gtk_drag_update_icon (info);
3977       gdk_drag_find_window_for_screen (info->context,
3978                                        info->icon_window ? gtk_widget_get_window (info->icon_window) : NULL,
3979                                        info->cur_screen, info->cur_x, info->cur_y,
3980                                        &dest_window, &protocol);
3981       
3982       if (!gdk_drag_motion (info->context, dest_window, protocol,
3983                             info->cur_x, info->cur_y, action, 
3984                             possible_actions,
3985                             time))
3986         {
3987           gdk_event_free ((GdkEvent *)info->last_event);
3988           info->last_event = NULL;
3989         }
3990   
3991       if (dest_window)
3992         g_object_unref (dest_window);
3993       
3994       selection = gdk_drag_get_selection (info->context);
3995       if (selection)
3996         gtk_drag_source_check_selection (info, selection, time);
3997
3998     }
3999
4000   return FALSE;
4001 }
4002
4003 static void
4004 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
4005 {
4006   /* We use an idle lower than GDK_PRIORITY_REDRAW so that exposes
4007    * from the last move can catch up before we move again.
4008    */
4009   if (!info->update_idle)
4010     info->update_idle = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW + 5,
4011                                          gtk_drag_update_idle,
4012                                          info,
4013                                          NULL);
4014 }
4015
4016 /**
4017  * gtk_drag_update:
4018  * @info: DragSourceInfo for the drag
4019  * @screen: new screen
4020  * @x_root: new X position 
4021  * @y_root: new y position
4022  * @event: event received requiring update
4023  * 
4024  * Updates the status of the drag; called when the
4025  * cursor moves or the modifier changes
4026  **/
4027 static void
4028 gtk_drag_update (GtkDragSourceInfo *info,
4029                  GdkScreen         *screen,
4030                  gint               x_root,
4031                  gint               y_root,
4032                  GdkEvent          *event)
4033 {
4034   info->cur_screen = screen;
4035   info->cur_x = x_root;
4036   info->cur_y = y_root;
4037   if (info->last_event)
4038     {
4039       gdk_event_free ((GdkEvent *)info->last_event);
4040       info->last_event = NULL;
4041     }
4042   if (event)
4043     info->last_event = gdk_event_copy ((GdkEvent *)event);
4044
4045   gtk_drag_add_update_idle (info);
4046 }
4047
4048 /*************************************************************
4049  * gtk_drag_end:
4050  *     Called when the user finishes to drag, either by
4051  *     releasing the mouse, or by pressing Esc.
4052  *   arguments:
4053  *     info: Source info for the drag
4054  *     time: Timestamp for ending the drag
4055  *   results:
4056  *************************************************************/
4057
4058 static void
4059 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
4060 {
4061   GdkEvent *send_event;
4062   GtkWidget *source_widget = info->widget;
4063   GdkDevice *pointer, *keyboard;
4064
4065   pointer = gdk_drag_context_get_device (info->context);
4066   keyboard = gdk_device_get_associated_device (pointer);
4067
4068   /* Prevent ungrab before grab (see bug 623865) */
4069   if (info->grab_time == GDK_CURRENT_TIME)
4070     time = GDK_CURRENT_TIME;
4071
4072   if (info->update_idle)
4073     {
4074       g_source_remove (info->update_idle);
4075       info->update_idle = 0;
4076     }
4077   
4078   if (info->last_event)
4079     {
4080       gdk_event_free (info->last_event);
4081       info->last_event = NULL;
4082     }
4083   
4084   info->have_grab = FALSE;
4085   
4086   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4087                                         gtk_drag_grab_broken_event_cb,
4088                                         info);
4089   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4090                                         gtk_drag_grab_notify_cb,
4091                                         info);
4092   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4093                                         gtk_drag_button_release_cb,
4094                                         info);
4095   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4096                                         gtk_drag_motion_cb,
4097                                         info);
4098   g_signal_handlers_disconnect_by_func (info->ipc_widget,
4099                                         gtk_drag_key_cb,
4100                                         info);
4101
4102   gdk_device_ungrab (pointer, time);
4103   ungrab_dnd_keys (info->ipc_widget, keyboard, time);
4104   gtk_device_grab_remove (info->ipc_widget, pointer);
4105
4106   /* Send on a release pair to the original 
4107    * widget to convince it to release its grab. We need to
4108    * call gtk_propagate_event() here, instead of 
4109    * gtk_widget_event() because widget like GtkList may
4110    * expect propagation.
4111    */
4112
4113   send_event = gdk_event_new (GDK_BUTTON_RELEASE);
4114   send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
4115   send_event->button.send_event = TRUE;
4116   send_event->button.time = time;
4117   send_event->button.x = 0;
4118   send_event->button.y = 0;
4119   send_event->button.axes = NULL;
4120   send_event->button.state = 0;
4121   send_event->button.button = info->button;
4122   send_event->button.device = pointer;
4123   send_event->button.x_root = 0;
4124   send_event->button.y_root = 0;
4125
4126   gtk_propagate_event (source_widget, send_event);
4127   gdk_event_free (send_event);
4128 }
4129
4130 /*************************************************************
4131  * gtk_drag_cancel:
4132  *    Called on cancellation of a drag, either by the user
4133  *    or programmatically.
4134  *   arguments:
4135  *     info: Source info for the drag
4136  *     time: Timestamp for ending the drag
4137  *   results:
4138  *************************************************************/
4139
4140 static void
4141 gtk_drag_cancel (GtkDragSourceInfo *info, GtkDragResult result, guint32 time)
4142 {
4143   gtk_drag_end (info, time);
4144   gdk_drag_abort (info->context, time);
4145   gtk_drag_drop_finished (info, result, time);
4146 }
4147
4148 /*************************************************************
4149  * gtk_drag_motion_cb:
4150  *     "motion-notify-event" callback during drag.
4151  *   arguments:
4152  *     
4153  *   results:
4154  *************************************************************/
4155
4156 static gboolean
4157 gtk_drag_motion_cb (GtkWidget      *widget,
4158                     GdkEventMotion *event,
4159                     gpointer        data)
4160 {
4161   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4162   GdkScreen *screen;
4163   gint x_root, y_root;
4164
4165   if (event->is_hint)
4166     {
4167       gdk_device_get_position (event->device, &screen, &x_root, &y_root);
4168       event->x_root = x_root;
4169       event->y_root = y_root;
4170     }
4171   else
4172     screen = gdk_event_get_screen ((GdkEvent *)event);
4173
4174   x_root = (gint)(event->x_root + 0.5);
4175   y_root = (gint)(event->y_root + 0.5);
4176   gtk_drag_update (info, screen, x_root, y_root, (GdkEvent *) event);
4177
4178   return TRUE;
4179 }
4180
4181 /*************************************************************
4182  * gtk_drag_key_cb:
4183  *     "key-press/release-event" callback during drag.
4184  *   arguments:
4185  *     
4186  *   results:
4187  *************************************************************/
4188
4189 #define BIG_STEP 20
4190 #define SMALL_STEP 1
4191
4192 static gboolean
4193 gtk_drag_key_cb (GtkWidget         *widget, 
4194                  GdkEventKey       *event, 
4195                  gpointer           data)
4196 {
4197   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4198   GdkModifierType state;
4199   GdkWindow *root_window;
4200   GdkDevice *pointer;
4201   gint dx, dy;
4202
4203   dx = dy = 0;
4204   state = event->state & gtk_accelerator_get_default_mod_mask ();
4205   pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
4206
4207   if (event->type == GDK_KEY_PRESS)
4208     {
4209       switch (event->keyval)
4210 {
4211         case GDK_KEY_Escape:
4212           gtk_drag_cancel (info, GTK_DRAG_RESULT_USER_CANCELLED, event->time);
4213           return TRUE;
4214
4215         case GDK_KEY_space:
4216         case GDK_KEY_Return:
4217         case GDK_KEY_ISO_Enter:
4218         case GDK_KEY_KP_Enter:
4219         case GDK_KEY_KP_Space:
4220           if ((gdk_drag_context_get_selected_action (info->context) != 0) &&
4221               (gdk_drag_context_get_dest_window (info->context) != NULL))
4222             {
4223               gtk_drag_end (info, event->time);
4224               gtk_drag_drop (info, event->time);
4225             }
4226           else
4227             {
4228               gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
4229             }
4230
4231           return TRUE;
4232
4233         case GDK_KEY_Up:
4234         case GDK_KEY_KP_Up:
4235           dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4236           break;
4237
4238         case GDK_KEY_Down:
4239         case GDK_KEY_KP_Down:
4240           dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4241           break;
4242
4243         case GDK_KEY_Left:
4244         case GDK_KEY_KP_Left:
4245           dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4246           break;
4247
4248         case GDK_KEY_Right:
4249         case GDK_KEY_KP_Right:
4250           dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4251           break;
4252         }
4253     }
4254
4255   /* Now send a "motion" so that the modifier state is updated */
4256
4257   /* The state is not yet updated in the event, so we need
4258    * to query it here. We could use XGetModifierMapping, but
4259    * that would be overkill.
4260    */
4261   root_window = gtk_widget_get_root_window (widget);
4262   gdk_window_get_device_position (root_window, pointer, NULL, NULL, &state);
4263   event->state = state;
4264
4265   if (dx != 0 || dy != 0)
4266     {
4267       info->cur_x += dx;
4268       info->cur_y += dy;
4269       gdk_device_warp (pointer,
4270                        gtk_widget_get_screen (widget),
4271                        info->cur_x, info->cur_y);
4272     }
4273
4274   gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
4275
4276   return TRUE;
4277 }
4278
4279 static gboolean
4280 gtk_drag_grab_broken_event_cb (GtkWidget          *widget,
4281                                GdkEventGrabBroken *event,
4282                                gpointer            data)
4283 {
4284   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4285
4286   /* Don't cancel if we break the implicit grab from the initial button_press.
4287    * Also, don't cancel if we re-grab on the widget or on our IPC window, for
4288    * example, when changing the drag cursor.
4289    */
4290   if (event->implicit
4291       || event->grab_window == gtk_widget_get_window (info->widget)
4292       || event->grab_window == gtk_widget_get_window (info->ipc_widget))
4293     return FALSE;
4294
4295   gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4296   return TRUE;
4297 }
4298
4299 static void
4300 gtk_drag_grab_notify_cb (GtkWidget        *widget,
4301                          gboolean          was_grabbed,
4302                          gpointer          data)
4303 {
4304   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4305   GdkDevice *pointer;
4306
4307   pointer = gdk_drag_context_get_device (info->context);
4308
4309   if (gtk_widget_device_is_shadowed (widget, pointer))
4310     {
4311       /* We have to block callbacks to avoid recursion here, because
4312          gtk_drag_cancel calls gtk_grab_remove (via gtk_drag_end) */
4313       g_signal_handlers_block_by_func (widget, gtk_drag_grab_notify_cb, data);
4314       gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4315       g_signal_handlers_unblock_by_func (widget, gtk_drag_grab_notify_cb, data);
4316     }
4317 }
4318
4319
4320 /*************************************************************
4321  * gtk_drag_button_release_cb:
4322  *     "button-release-event" callback during drag.
4323  *   arguments:
4324  *     
4325  *   results:
4326  *************************************************************/
4327
4328 static gboolean
4329 gtk_drag_button_release_cb (GtkWidget      *widget, 
4330                             GdkEventButton *event, 
4331                             gpointer        data)
4332 {
4333   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4334
4335   if (event->button != info->button)
4336     return FALSE;
4337
4338   if ((gdk_drag_context_get_selected_action (info->context) != 0) &&
4339       (gdk_drag_context_get_dest_window (info->context) != NULL))
4340     {
4341       gtk_drag_end (info, event->time);
4342       gtk_drag_drop (info, event->time);
4343     }
4344   else
4345     {
4346       gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
4347     }
4348
4349   return TRUE;
4350 }
4351
4352 static gboolean
4353 gtk_drag_abort_timeout (gpointer data)
4354 {
4355   GtkDragSourceInfo *info = data;
4356   guint32 time = GDK_CURRENT_TIME;
4357
4358   if (info->proxy_dest)
4359     time = info->proxy_dest->proxy_drop_time;
4360
4361   info->drop_timeout = 0;
4362   gtk_drag_drop_finished (info, GTK_DRAG_RESULT_TIMEOUT_EXPIRED, time);
4363   
4364   return FALSE;
4365 }
4366
4367 /**
4368  * gtk_drag_check_threshold:
4369  * @widget: a #GtkWidget
4370  * @start_x: X coordinate of start of drag
4371  * @start_y: Y coordinate of start of drag
4372  * @current_x: current X coordinate
4373  * @current_y: current Y coordinate
4374  * 
4375  * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
4376  * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
4377  * should trigger the beginning of a drag-and-drop operation.
4378  *
4379  * Return Value: %TRUE if the drag threshold has been passed.
4380  **/
4381 gboolean
4382 gtk_drag_check_threshold (GtkWidget *widget,
4383                           gint       start_x,
4384                           gint       start_y,
4385                           gint       current_x,
4386                           gint       current_y)
4387 {
4388   gint drag_threshold;
4389
4390   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
4391
4392   g_object_get (gtk_widget_get_settings (widget),
4393                 "gtk-dnd-drag-threshold", &drag_threshold,
4394                 NULL);
4395   
4396   return (ABS (current_x - start_x) > drag_threshold ||
4397           ABS (current_y - start_y) > drag_threshold);
4398 }