]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd-quartz.c
gtk: gdk_drawable_get_screen/visual => gdk_window_get_screen/visual
[~andy/gtk] / gtk / gtkdnd-quartz.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "gdkconfig.h"
33
34 #include "gdk/gdkkeysyms.h"
35
36 #include "gtkdnd.h"
37 #include "gtkiconfactory.h"
38 #include "gtkicontheme.h"
39 #include "gtkimage.h"
40 #include "gtkinvisible.h"
41 #include "gtkmain.h"
42 #include "gtkplug.h"
43 #include "gtkstock.h"
44 #include "gtkwindow.h"
45 #include "gtkintl.h"
46 #include "gtkquartz.h"
47 #include "gdk/quartz/gdkquartz.h"
48
49 typedef struct _GtkDragSourceSite GtkDragSourceSite;
50 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
51 typedef struct _GtkDragDestSite GtkDragDestSite;
52 typedef struct _GtkDragDestInfo GtkDragDestInfo;
53 typedef struct _GtkDragFindData GtkDragFindData;
54
55 static void     gtk_drag_find_widget            (GtkWidget        *widget,
56                                                  GtkDragFindData  *data);
57 static void     gtk_drag_dest_site_destroy      (gpointer          data);
58 static void     gtk_drag_dest_leave             (GtkWidget        *widget,
59                                                  GdkDragContext   *context,
60                                                  guint             time);
61 static GtkDragDestInfo *gtk_drag_get_dest_info  (GdkDragContext   *context,
62                                                  gboolean          create);
63 static void gtk_drag_source_site_destroy        (gpointer           data);
64
65 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
66                                                     gboolean        create);
67
68 extern GdkDragContext *gdk_quartz_drag_source_context (); /* gdk/quartz/gdkdnd-quartz.c */
69
70 struct _GtkDragSourceSite 
71 {
72   GdkModifierType    start_button_mask;
73   GtkTargetList     *target_list;        /* Targets for drag data */
74   GdkDragAction      actions;            /* Possible actions */
75
76   /* Drag icon */
77   GtkImageType icon_type;
78   union
79   {
80     GtkImagePixbufData pixbuf;
81     GtkImageStockData stock;
82     GtkImageIconNameData name;
83   } icon_data;
84
85   /* Stored button press information to detect drag beginning */
86   gint               state;
87   gint               x, y;
88 };
89
90 struct _GtkDragSourceInfo 
91 {
92   GtkWidget         *source_widget;
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   NSEvent           *nsevent;     /* what started it */
98   gint hot_x, hot_y;              /* Hot spot for drag */
99   GdkPixbuf         *icon_pixbuf;
100   gboolean           success;
101   gboolean           delete;
102 };
103
104 struct _GtkDragDestSite 
105 {
106   GtkDestDefaults    flags;
107   GtkTargetList     *target_list;
108   GdkDragAction      actions;
109   guint              have_drag : 1;
110   guint              track_motion : 1;
111 };
112
113 struct _GtkDragDestInfo 
114 {
115   GtkWidget         *widget;       /* Widget in which drag is in */
116   GdkDragContext    *context;      /* Drag context */
117   guint              dropped : 1;     /* Set after we receive a drop */
118   gint               drop_x, drop_y; /* Position of drop */
119 };
120
121 struct _GtkDragFindData 
122 {
123   gint x;
124   gint y;
125   GdkDragContext *context;
126   GtkDragDestInfo *info;
127   gboolean found;
128   gboolean toplevel;
129   gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
130                         gint x, gint y, guint32 time);
131   guint32 time;
132 };
133
134
135 @interface GtkDragSourceOwner : NSObject {
136   GtkDragSourceInfo *info;
137 }
138
139 @end
140
141 @implementation GtkDragSourceOwner
142 -(void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
143 {
144   guint target_info;
145   GtkSelectionData selection_data;
146
147   selection_data.selection = GDK_NONE;
148   selection_data.data = NULL;
149   selection_data.length = -1;
150   selection_data.target = _gtk_quartz_pasteboard_type_to_atom (type);
151
152   if (gtk_target_list_find (info->target_list, 
153                             selection_data.target, 
154                             &target_info)) 
155     {
156       g_signal_emit_by_name (info->widget, "drag-data-get",
157                              info->context,
158                              &selection_data,
159                              target_info,
160                              time);
161
162       if (selection_data.length >= 0)
163         _gtk_quartz_set_selection_data_for_pasteboard (sender, &selection_data);
164       
165       g_free (selection_data.data);
166     }
167 }
168
169 - (id)initWithInfo:(GtkDragSourceInfo *)anInfo
170 {
171   self = [super init];
172
173   if (self) 
174     {
175       info = anInfo;
176     }
177
178   return self;
179 }
180
181 @end
182
183 void 
184 gtk_drag_get_data (GtkWidget      *widget,
185                    GdkDragContext *context,
186                    GdkAtom         target,
187                    guint32         time)
188 {
189   id <NSDraggingInfo> dragging_info;
190   NSPasteboard *pasteboard;
191   GtkSelectionData *selection_data;
192   GtkDragDestInfo *info;
193   GtkDragDestSite *site;
194
195   dragging_info = gdk_quartz_drag_context_get_dragging_info_libgtk_only (context);
196   pasteboard = [dragging_info draggingPasteboard];
197
198   info = gtk_drag_get_dest_info (context, FALSE);
199   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
200
201   selection_data = _gtk_quartz_get_selection_data_from_pasteboard (pasteboard,
202                                                                    target, 0);
203
204   if (site && site->target_list)
205     {
206       guint target_info;
207       
208       if (gtk_target_list_find (site->target_list, 
209                                 selection_data->target,
210                                 &target_info))
211         {
212           if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
213               selection_data->length >= 0)
214             g_signal_emit_by_name (widget,
215                                    "drag-data-received",
216                                    context, info->drop_x, info->drop_y,
217                                    selection_data,
218                                    target_info, time);
219         }
220     }
221   else
222     {
223       g_signal_emit_by_name (widget,
224                              "drag-data-received",
225                              context, info->drop_x, info->drop_y,
226                              selection_data,
227                              0, time);
228     }
229   
230   if (site && site->flags & GTK_DEST_DEFAULT_DROP)
231     {
232       gtk_drag_finish (context, 
233                        (selection_data->length >= 0),
234                        (context->action == GDK_ACTION_MOVE),
235                        time);
236     }      
237 }
238
239 void 
240 gtk_drag_finish (GdkDragContext *context,
241                  gboolean        success,
242                  gboolean        del,
243                  guint32         time)
244 {
245   GtkDragSourceInfo *info;
246   GdkDragContext* source_context = gdk_quartz_drag_source_context ();
247
248   if (source_context)
249     {
250       info = gtk_drag_get_source_info (source_context, FALSE);
251       if (info)
252         {
253           info->success = success;
254           info->delete = del;
255         }
256     }
257 }
258
259 static void
260 gtk_drag_dest_info_destroy (gpointer data)
261 {
262   GtkDragDestInfo *info = data;
263
264   g_free (info);
265 }
266
267 static GtkDragDestInfo *
268 gtk_drag_get_dest_info (GdkDragContext *context,
269                         gboolean        create)
270 {
271   GtkDragDestInfo *info;
272   static GQuark info_quark = 0;
273   if (!info_quark)
274     info_quark = g_quark_from_static_string ("gtk-dest-info");
275   
276   info = g_object_get_qdata (G_OBJECT (context), info_quark);
277   if (!info && create)
278     {
279       info = g_new (GtkDragDestInfo, 1);
280       info->widget = NULL;
281       info->context = context;
282       info->dropped = FALSE;
283       g_object_set_qdata_full (G_OBJECT (context), info_quark,
284                                info, gtk_drag_dest_info_destroy);
285     }
286
287   return info;
288 }
289
290 static GQuark dest_info_quark = 0;
291
292 static GtkDragSourceInfo *
293 gtk_drag_get_source_info (GdkDragContext *context,
294                           gboolean        create)
295 {
296   GtkDragSourceInfo *info;
297
298   if (!dest_info_quark)
299     dest_info_quark = g_quark_from_static_string ("gtk-source-info");
300   
301   info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
302   if (!info && create)
303     {
304       info = g_new0 (GtkDragSourceInfo, 1);
305       info->context = context;
306       g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
307     }
308
309   return info;
310 }
311
312 static void
313 gtk_drag_clear_source_info (GdkDragContext *context)
314 {
315   g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
316 }
317
318 GtkWidget *
319 gtk_drag_get_source_widget (GdkDragContext *context)
320 {
321   GtkDragSourceInfo *info;
322   GdkDragContext* real_source_context = gdk_quartz_drag_source_context();
323
324   if (!real_source_context)
325     return NULL;
326
327   info = gtk_drag_get_source_info (real_source_context, FALSE);
328   if (!info)
329      return NULL;
330
331   return info->source_widget;
332 }
333
334 /*************************************************************
335  * gtk_drag_highlight_expose:
336  *     Callback for expose_event for highlighted widgets.
337  *   arguments:
338  *     widget:
339  *     event:
340  *     data:
341  *   results:
342  *************************************************************/
343
344 static gboolean
345 gtk_drag_highlight_expose (GtkWidget      *widget,
346                            GdkEventExpose *event,
347                            gpointer        data)
348 {
349   gint x, y, width, height;
350   
351   if (gtk_widget_is_drawable (widget))
352     {
353       cairo_t *cr;
354       GdkWindow *window = gtk_widget_get_window (widget);
355       
356       if (!gtk_widget_get_has_window (widget))
357         {
358           GtkAllocation allocation;
359
360           gtk_widget_get_allocation (widget, &allocation);
361
362           x = allocation.x;
363           y = allocation.y;
364           width = allocation.width;
365           height = allocation.height;
366         }
367       else
368         {
369           x = 0;
370           y = 0;
371           gdk_drawable_get_size (window, &width, &height);
372         }
373       
374       gtk_paint_shadow (gtk_widget_get_style (widget), window,
375                         GTK_STATE_NORMAL, GTK_SHADOW_OUT,
376                         NULL, widget, "dnd",
377                         x, y, width, height);
378
379       cr = gdk_cairo_create (window);
380       cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
381       cairo_set_line_width (cr, 1.0);
382       cairo_rectangle (cr,
383                        x + 0.5, y + 0.5,
384                        width - 1, height - 1);
385       cairo_stroke (cr);
386       cairo_destroy (cr);
387     }
388
389   return FALSE;
390 }
391
392 /*************************************************************
393  * gtk_drag_highlight:
394  *     Highlight the given widget in the default manner.
395  *   arguments:
396  *     widget:
397  *   results:
398  *************************************************************/
399
400 void 
401 gtk_drag_highlight (GtkWidget  *widget)
402 {
403   g_return_if_fail (GTK_IS_WIDGET (widget));
404
405   g_signal_connect_after (widget, "expose-event",
406                           G_CALLBACK (gtk_drag_highlight_expose),
407                           NULL);
408
409   gtk_widget_queue_draw (widget);
410 }
411
412 /*************************************************************
413  * gtk_drag_unhighlight:
414  *     Refresh the given widget to remove the highlight.
415  *   arguments:
416  *     widget:
417  *   results:
418  *************************************************************/
419
420 void 
421 gtk_drag_unhighlight (GtkWidget *widget)
422 {
423   g_return_if_fail (GTK_IS_WIDGET (widget));
424
425   g_signal_handlers_disconnect_by_func (widget,
426                                         gtk_drag_highlight_expose,
427                                         NULL);
428   
429   gtk_widget_queue_draw (widget);
430 }
431
432 static NSWindow *
433 get_toplevel_nswindow (GtkWidget *widget)
434 {
435   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
436   GdkWindow *window = gtk_widget_get_window (toplevel);
437   
438   if (gtk_widget_is_toplevel (toplevel) && window)
439     return [gdk_quartz_window_get_nsview (window) window];
440   else
441     return NULL;
442 }
443
444 static void
445 register_types (GtkWidget *widget, GtkDragDestSite *site)
446 {
447   if (site->target_list)
448     {
449       NSWindow *nswindow = get_toplevel_nswindow (widget);
450       NSSet *types;
451       NSAutoreleasePool *pool;
452
453       if (!nswindow)
454         return;
455
456       pool = [[NSAutoreleasePool alloc] init];
457       types = _gtk_quartz_target_list_to_pasteboard_types (site->target_list);
458
459       [nswindow registerForDraggedTypes:[types allObjects]];
460
461       [types release];
462       [pool release];
463     }
464 }
465
466 static void
467 gtk_drag_dest_realized (GtkWidget *widget, 
468                         gpointer   user_data)
469 {
470   GtkDragDestSite *site = user_data;
471
472   register_types (widget, site);
473 }
474
475 static void
476 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
477                                  GtkWidget *previous_toplevel,
478                                  gpointer   user_data)
479 {
480   GtkDragDestSite *site = user_data;
481
482   register_types (widget, site);
483 }
484
485 static void
486 gtk_drag_dest_site_destroy (gpointer data)
487 {
488   GtkDragDestSite *site = data;
489     
490   if (site->target_list)
491     gtk_target_list_unref (site->target_list);
492
493   g_free (site);
494 }
495
496 void 
497 gtk_drag_dest_set (GtkWidget            *widget,
498                    GtkDestDefaults       flags,
499                    const GtkTargetEntry *targets,
500                    gint                  n_targets,
501                    GdkDragAction         actions)
502 {
503   GtkDragDestSite *old_site, *site;
504
505   g_return_if_fail (GTK_IS_WIDGET (widget));
506
507   old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
508
509   site = g_new (GtkDragDestSite, 1);
510   site->flags = flags;
511   site->have_drag = FALSE;
512   if (targets)
513     site->target_list = gtk_target_list_new (targets, n_targets);
514   else
515     site->target_list = NULL;
516   site->actions = actions;
517
518   if (old_site)
519     site->track_motion = old_site->track_motion;
520   else
521     site->track_motion = FALSE;
522
523   gtk_drag_dest_unset (widget);
524
525   if (gtk_widget_get_realized (widget))
526     gtk_drag_dest_realized (widget, site);
527
528   g_signal_connect (widget, "realize",
529                     G_CALLBACK (gtk_drag_dest_realized), site);
530   g_signal_connect (widget, "hierarchy-changed",
531                     G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
532
533   g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
534                           site, gtk_drag_dest_site_destroy);
535 }
536
537 void 
538 gtk_drag_dest_set_proxy (GtkWidget      *widget,
539                          GdkWindow      *proxy_window,
540                          GdkDragProtocol protocol,
541                          gboolean        use_coordinates)
542 {
543   g_warning ("gtk_drag_dest_set_proxy is not supported on Mac OS X.");
544 }
545
546 void 
547 gtk_drag_dest_unset (GtkWidget *widget)
548 {
549   GtkDragDestSite *old_site;
550
551   g_return_if_fail (GTK_IS_WIDGET (widget));
552
553   old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
554   if (old_site)
555     {
556       g_signal_handlers_disconnect_by_func (widget,
557                                             gtk_drag_dest_realized,
558                                             old_site);
559       g_signal_handlers_disconnect_by_func (widget,
560                                             gtk_drag_dest_hierarchy_changed,
561                                             old_site);
562     }
563
564   g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
565 }
566
567 GtkTargetList*
568 gtk_drag_dest_get_target_list (GtkWidget *widget)
569 {
570   GtkDragDestSite *site;
571
572   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
573   
574   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
575
576   return site ? site->target_list : NULL;  
577 }
578
579 void
580 gtk_drag_dest_set_target_list (GtkWidget      *widget,
581                                GtkTargetList  *target_list)
582 {
583   GtkDragDestSite *site;
584
585   g_return_if_fail (GTK_IS_WIDGET (widget));
586   
587   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
588   
589   if (!site)
590     {
591       g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
592                  "to make the widget into a drag destination");
593       return;
594     }
595
596   if (target_list)
597     gtk_target_list_ref (target_list);
598   
599   if (site->target_list)
600     gtk_target_list_unref (site->target_list);
601
602   site->target_list = target_list;
603
604   register_types (widget, site);
605 }
606
607 void
608 gtk_drag_dest_add_text_targets (GtkWidget *widget)
609 {
610   GtkTargetList *target_list;
611
612   target_list = gtk_drag_dest_get_target_list (widget);
613   if (target_list)
614     gtk_target_list_ref (target_list);
615   else
616     target_list = gtk_target_list_new (NULL, 0);
617   gtk_target_list_add_text_targets (target_list, 0);
618   gtk_drag_dest_set_target_list (widget, target_list);
619   gtk_target_list_unref (target_list);
620 }
621
622 void
623 gtk_drag_dest_add_image_targets (GtkWidget *widget)
624 {
625   GtkTargetList *target_list;
626
627   target_list = gtk_drag_dest_get_target_list (widget);
628   if (target_list)
629     gtk_target_list_ref (target_list);
630   else
631     target_list = gtk_target_list_new (NULL, 0);
632   gtk_target_list_add_image_targets (target_list, 0, FALSE);
633   gtk_drag_dest_set_target_list (widget, target_list);
634   gtk_target_list_unref (target_list);
635 }
636
637 void
638 gtk_drag_dest_add_uri_targets (GtkWidget *widget)
639 {
640   GtkTargetList *target_list;
641
642   target_list = gtk_drag_dest_get_target_list (widget);
643   if (target_list)
644     gtk_target_list_ref (target_list);
645   else
646     target_list = gtk_target_list_new (NULL, 0);
647   gtk_target_list_add_uri_targets (target_list, 0);
648   gtk_drag_dest_set_target_list (widget, target_list);
649   gtk_target_list_unref (target_list);
650 }
651
652 static void
653 prepend_and_ref_widget (GtkWidget *widget,
654                         gpointer   data)
655 {
656   GSList **slist_p = data;
657
658   *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
659 }
660
661 static void
662 gtk_drag_find_widget (GtkWidget       *widget,
663                       GtkDragFindData *data)
664 {
665   GtkAllocation new_allocation;
666   gint allocation_to_window_x = 0;
667   gint allocation_to_window_y = 0;
668   gint x_offset = 0;
669   gint y_offset = 0;
670
671   if (data->found || !gtk_widget_get_mapped (widget) || !gtk_widget_get_sensitive (widget))
672     return;
673
674   /* Note that in the following code, we only count the
675    * position as being inside a WINDOW widget if it is inside
676    * widget->window; points that are outside of widget->window
677    * but within the allocation are not counted. This is consistent
678    * with the way we highlight drag targets.
679    *
680    * data->x,y are relative to widget->parent->window (if
681    * widget is not a toplevel, widget->window otherwise).
682    * We compute the allocation of widget in the same coordinates,
683    * clipping to widget->window, and all intermediate
684    * windows. If data->x,y is inside that, then we translate
685    * our coordinates to be relative to widget->window and
686    * recurse.
687    */  
688   gtk_widget_get_allocation (widget, &new_allocation);
689
690   if (gtk_widget_get_parent (widget))
691     {
692       gint tx, ty;
693       GdkWindow *window = gtk_widget_get_window (widget);
694       GdkWindow *parent_window;
695       GtkAllocation allocation;
696
697       parent_window = gtk_widget_get_window (gtk_widget_get_parent (widget));
698
699       /* Compute the offset from allocation-relative to
700        * window-relative coordinates.
701        */
702       gtk_widget_get_allocation (widget, &allocation);
703       allocation_to_window_x = allocation.x;
704       allocation_to_window_y = allocation.y;
705
706       if (gtk_widget_get_has_window (widget))
707         {
708           /* The allocation is relative to the parent window for
709            * window widgets, not to widget->window.
710            */
711           gdk_window_get_position (window, &tx, &ty);
712           
713           allocation_to_window_x -= tx;
714           allocation_to_window_y -= ty;
715         }
716
717       new_allocation.x = 0 + allocation_to_window_x;
718       new_allocation.y = 0 + allocation_to_window_y;
719       
720       while (window && window != parent_window)
721         {
722           GdkRectangle window_rect = { 0, 0, 0, 0 };
723           
724           gdk_drawable_get_size (window, &window_rect.width, &window_rect.height);
725
726           gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
727
728           gdk_window_get_position (window, &tx, &ty);
729           new_allocation.x += tx;
730           x_offset += tx;
731           new_allocation.y += ty;
732           y_offset += ty;
733           
734           window = gdk_window_get_parent (window);
735         }
736
737       if (!window)              /* Window and widget heirarchies didn't match. */
738         return;
739     }
740
741   if (data->toplevel ||
742       ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
743        (data->x < new_allocation.x + new_allocation.width) && 
744        (data->y < new_allocation.y + new_allocation.height)))
745     {
746       /* First, check if the drag is in a valid drop site in
747        * one of our children 
748        */
749       if (GTK_IS_CONTAINER (widget))
750         {
751           GtkDragFindData new_data = *data;
752           GSList *children = NULL;
753           GSList *tmp_list;
754           
755           new_data.x -= x_offset;
756           new_data.y -= y_offset;
757           new_data.found = FALSE;
758           new_data.toplevel = FALSE;
759           
760           /* need to reference children temporarily in case the
761            * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
762            */
763           gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
764           for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
765             {
766               if (!new_data.found && gtk_widget_is_drawable (tmp_list->data))
767                 gtk_drag_find_widget (tmp_list->data, &new_data);
768               g_object_unref (tmp_list->data);
769             }
770           g_slist_free (children);
771           
772           data->found = new_data.found;
773         }
774
775       /* If not, and this widget is registered as a drop site, check to
776        * emit "drag-motion" to check if we are actually in
777        * a drop site.
778        */
779       if (!data->found &&
780           g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
781         {
782           data->found = data->callback (widget,
783                                         data->context,
784                                         data->x - x_offset - allocation_to_window_x,
785                                         data->y - y_offset - allocation_to_window_y,
786                                         data->time);
787           /* If so, send a "drag-leave" to the last widget */
788           if (data->found)
789             {
790               if (data->info->widget && data->info->widget != widget)
791                 {
792                   gtk_drag_dest_leave (data->info->widget, data->context, data->time);
793                 }
794               data->info->widget = widget;
795             }
796         }
797     }
798 }
799
800 static void  
801 gtk_drag_dest_leave (GtkWidget      *widget,
802                      GdkDragContext *context,
803                      guint           time)
804 {
805   GtkDragDestSite *site;
806
807   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
808   g_return_if_fail (site != NULL);
809
810   if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
811     gtk_drag_unhighlight (widget);
812   
813   if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
814       site->track_motion)
815     g_signal_emit_by_name (widget, "drag-leave", context, time);
816   
817   site->have_drag = FALSE;
818 }
819
820 static gboolean
821 gtk_drag_dest_motion (GtkWidget      *widget,
822                       GdkDragContext *context,
823                       gint            x,
824                       gint            y,
825                       guint           time)
826 {
827   GtkDragDestSite *site;
828   GdkDragAction action = 0;
829   gboolean retval;
830
831   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
832   g_return_val_if_fail (site != NULL, FALSE);
833
834   if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
835     {
836       if (context->suggested_action & site->actions)
837         action = context->suggested_action;
838       
839       if (action && gtk_drag_dest_find_target (widget, context, NULL))
840         {
841           if (!site->have_drag)
842             {
843               site->have_drag = TRUE;
844               if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
845                 gtk_drag_highlight (widget);
846             }
847           
848           gdk_drag_status (context, action, time);
849         }
850       else
851         {
852           gdk_drag_status (context, 0, time);
853           if (!site->track_motion)
854             return TRUE;
855         }
856     }
857
858   g_signal_emit_by_name (widget, "drag-motion",
859                          context, x, y, time, &retval);
860
861   return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
862 }
863
864 static gboolean
865 gtk_drag_dest_drop (GtkWidget        *widget,
866                     GdkDragContext   *context,
867                     gint              x,
868                     gint              y,
869                     guint             time)
870 {
871   GtkDragDestSite *site;
872   GtkDragDestInfo *info;
873   gboolean retval;
874
875   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
876   g_return_val_if_fail (site != NULL, FALSE);
877
878   info = gtk_drag_get_dest_info (context, FALSE);
879   g_return_val_if_fail (info != NULL, FALSE);
880
881   info->drop_x = x;
882   info->drop_y = y;
883
884   if (site->flags & GTK_DEST_DEFAULT_DROP)
885     {
886       GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
887
888       if (target == GDK_NONE)
889         {
890           gtk_drag_finish (context, FALSE, FALSE, time);
891           return TRUE;
892         }
893       else
894         gtk_drag_get_data (widget, context, target, time);
895     }
896   
897   g_signal_emit_by_name (widget, "drag-drop",
898                          context, x, y, time, &retval);
899
900   return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
901 }
902
903 void
904 gtk_drag_dest_set_track_motion (GtkWidget *widget,
905                                 gboolean   track_motion)
906 {
907   GtkDragDestSite *site;
908
909   g_return_if_fail (GTK_IS_WIDGET (widget));
910
911   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
912   
913   g_return_if_fail (site != NULL);
914
915   site->track_motion = track_motion != FALSE;
916 }
917
918 gboolean
919 gtk_drag_dest_get_track_motion (GtkWidget *widget)
920 {
921   GtkDragDestSite *site;
922
923   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
924
925   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
926
927   if (site)
928     return site->track_motion;
929
930   return FALSE;
931 }
932
933 void
934 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
935                              GdkEvent  *event)
936 {
937   GtkDragDestInfo *info;
938   GdkDragContext *context;
939
940   g_return_if_fail (toplevel != NULL);
941   g_return_if_fail (event != NULL);
942
943   context = event->dnd.context;
944
945   info = gtk_drag_get_dest_info (context, TRUE);
946
947   /* Find the widget for the event */
948   switch (event->type)
949     {
950     case GDK_DRAG_ENTER:
951       break;
952
953     case GDK_DRAG_LEAVE:
954       if (info->widget)
955         {
956           gtk_drag_dest_leave (info->widget, context, event->dnd.time);
957           info->widget = NULL;
958         }
959       break;
960
961     case GDK_DRAG_MOTION:
962     case GDK_DROP_START:
963       {
964         GtkDragFindData data;
965         gint tx, ty;
966
967         if (event->type == GDK_DROP_START)
968           {
969             info->dropped = TRUE;
970             /* We send a leave here so that the widget unhighlights
971              * properly.
972              */
973             if (info->widget)
974               {
975                 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
976                 info->widget = NULL;
977               }
978           }
979
980         gdk_window_get_position (gtk_widget_get_window (toplevel), &tx, &ty);
981         
982         data.x = event->dnd.x_root - tx;
983         data.y = event->dnd.y_root - ty;
984         data.context = context;
985         data.info = info;
986         data.found = FALSE;
987         data.toplevel = TRUE;
988         data.callback = (event->type == GDK_DRAG_MOTION) ?
989           gtk_drag_dest_motion : gtk_drag_dest_drop;
990         data.time = event->dnd.time;
991         
992         gtk_drag_find_widget (toplevel, &data);
993
994         if (info->widget && !data.found)
995           {
996             gtk_drag_dest_leave (info->widget, context, event->dnd.time);
997             info->widget = NULL;
998           }
999
1000         /* Send a reply.
1001          */
1002         if (event->type == GDK_DRAG_MOTION)
1003           {
1004             if (!data.found)
1005               gdk_drag_status (context, 0, event->dnd.time);
1006           }
1007
1008         break;
1009       default:
1010         g_assert_not_reached ();
1011       }
1012     }
1013 }
1014
1015
1016 GdkAtom
1017 gtk_drag_dest_find_target (GtkWidget      *widget,
1018                            GdkDragContext *context,
1019                            GtkTargetList  *target_list)
1020 {
1021   id <NSDraggingInfo> dragging_info;
1022   NSPasteboard *pasteboard;
1023   GtkWidget *source_widget;
1024   GList *tmp_target;
1025   GList *tmp_source = NULL;
1026   GList *source_targets;
1027
1028   g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1029   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1030   g_return_val_if_fail (!context->is_source, GDK_NONE);
1031
1032   dragging_info = gdk_quartz_drag_context_get_dragging_info_libgtk_only (context);
1033   pasteboard = [dragging_info draggingPasteboard];
1034
1035   source_widget = gtk_drag_get_source_widget (context);
1036
1037   if (target_list == NULL)
1038     target_list = gtk_drag_dest_get_target_list (widget);
1039   
1040   if (target_list == NULL)
1041     return GDK_NONE;
1042
1043   source_targets = _gtk_quartz_pasteboard_types_to_atom_list ([pasteboard types]);
1044   tmp_target = target_list->list;
1045   while (tmp_target)
1046     {
1047       GtkTargetPair *pair = tmp_target->data;
1048       tmp_source = source_targets;
1049       while (tmp_source)
1050         {
1051           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1052             {
1053               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1054                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1055                 {
1056                   g_list_free (source_targets);
1057                   return pair->target;
1058                 }
1059               else
1060                 break;
1061             }
1062           tmp_source = tmp_source->next;
1063         }
1064       tmp_target = tmp_target->next;
1065     }
1066
1067   g_list_free (source_targets);
1068   return GDK_NONE;
1069 }
1070
1071 static gboolean
1072 gtk_drag_begin_idle (gpointer arg)
1073 {
1074   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1075   GdkDragContext* context = (GdkDragContext*) arg;
1076   GtkDragSourceInfo* info = gtk_drag_get_source_info (context, FALSE);
1077   NSWindow *nswindow;
1078   NSPasteboard *pasteboard;
1079   GtkDragSourceOwner *owner;
1080   NSPoint point;
1081   NSSet *types;
1082   NSImage *drag_image;
1083
1084   g_assert (info != NULL);
1085
1086   pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1087   owner = [[GtkDragSourceOwner alloc] initWithInfo:info];
1088
1089   types = _gtk_quartz_target_list_to_pasteboard_types (info->target_list);
1090
1091   [pasteboard declareTypes:[types allObjects] owner:owner];
1092
1093   [owner release];
1094   [types release];
1095
1096   if ((nswindow = get_toplevel_nswindow (info->source_widget)) == NULL)
1097      return FALSE;
1098   
1099   /* Ref the context. It's unreffed when the drag has been aborted */
1100   g_object_ref (info->context);
1101
1102   /* FIXME: If the event isn't a mouse event, use the global cursor position instead */
1103   point = [info->nsevent locationInWindow];
1104
1105   drag_image = _gtk_quartz_create_image_from_pixbuf (info->icon_pixbuf);
1106
1107   [nswindow dragImage:drag_image
1108                    at:point
1109                offset:NSMakeSize(0, 0)
1110                 event:info->nsevent
1111            pasteboard:pasteboard
1112                source:nswindow
1113             slideBack:YES];
1114
1115   [info->nsevent release];
1116   [drag_image release];
1117
1118   [pool release];
1119
1120   return FALSE;
1121 }
1122
1123 static GdkDragContext *
1124 gtk_drag_begin_internal (GtkWidget         *widget,
1125                          GtkDragSourceSite *site,
1126                          GtkTargetList     *target_list,
1127                          GdkDragAction      actions,
1128                          gint               button,
1129                          GdkEvent          *event)
1130 {
1131   GtkDragSourceInfo *info;
1132   GdkDragContext *context;
1133   NSWindow *nswindow;
1134
1135   context = gdk_drag_begin (NULL, NULL);
1136   context->is_source = TRUE;
1137
1138   info = gtk_drag_get_source_info (context, TRUE);
1139   
1140   info->source_widget = g_object_ref (widget);
1141   info->widget = g_object_ref (widget);
1142   info->target_list = target_list;
1143   gtk_target_list_ref (target_list);
1144
1145   info->possible_actions = actions;
1146   
1147   g_signal_emit_by_name (widget, "drag-begin", info->context);
1148
1149   /* Ensure that we have an icon before we start the drag; the
1150    * application may have set one in ::drag_begin, or it may
1151    * not have set one.
1152    */
1153   if (!info->icon_pixbuf)
1154     {
1155       if (!site || site->icon_type == GTK_IMAGE_EMPTY)
1156         gtk_drag_set_icon_default (context);
1157       else
1158         switch (site->icon_type)
1159           {
1160           case GTK_IMAGE_PIXBUF:
1161             gtk_drag_set_icon_pixbuf (context,
1162                                       site->icon_data.pixbuf.pixbuf,
1163                                       -2, -2);
1164             break;
1165           case GTK_IMAGE_STOCK:
1166             gtk_drag_set_icon_stock (context,
1167                                      site->icon_data.stock.stock_id,
1168                                      -2, -2);
1169             break;
1170           case GTK_IMAGE_ICON_NAME:
1171             gtk_drag_set_icon_name (context,
1172                                     site->icon_data.name.icon_name,
1173                                     -2, -2);
1174             break;
1175           case GTK_IMAGE_EMPTY:
1176           default:
1177             g_assert_not_reached();
1178             break;
1179           }
1180     }
1181
1182   nswindow = get_toplevel_nswindow (widget);
1183   info->nsevent = [nswindow currentEvent];
1184   [info->nsevent retain];
1185
1186   /* drag will begin in an idle handler to avoid nested run loops */
1187
1188   g_idle_add_full (G_PRIORITY_HIGH_IDLE, gtk_drag_begin_idle, context, NULL);
1189
1190   gdk_pointer_ungrab (0);
1191
1192   return context;
1193 }
1194
1195 GdkDragContext *
1196 gtk_drag_begin (GtkWidget         *widget,
1197                 GtkTargetList     *targets,
1198                 GdkDragAction      actions,
1199                 gint               button,
1200                 GdkEvent          *event)
1201 {
1202   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1203   g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
1204   g_return_val_if_fail (targets != NULL, NULL);
1205
1206   return gtk_drag_begin_internal (widget, NULL, targets,
1207                                   actions, button, event);
1208 }
1209
1210
1211 static gboolean
1212 gtk_drag_source_event_cb (GtkWidget      *widget,
1213                           GdkEvent       *event,
1214                           gpointer        data)
1215 {
1216   GtkDragSourceSite *site;
1217   gboolean retval = FALSE;
1218   site = (GtkDragSourceSite *)data;
1219
1220   switch (event->type)
1221     {
1222     case GDK_BUTTON_PRESS:
1223       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
1224         {
1225           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
1226           site->x = event->button.x;
1227           site->y = event->button.y;
1228         }
1229       break;
1230       
1231     case GDK_BUTTON_RELEASE:
1232       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
1233         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
1234       break;
1235       
1236     case GDK_MOTION_NOTIFY:
1237       if (site->state & event->motion.state & site->start_button_mask)
1238         {
1239           /* FIXME: This is really broken and can leave us
1240            * with a stuck grab
1241            */
1242           int i;
1243           for (i=1; i<6; i++)
1244             {
1245               if (site->state & event->motion.state & 
1246                   GDK_BUTTON1_MASK << (i - 1))
1247                 break;
1248             }
1249
1250           if (gtk_drag_check_threshold (widget, site->x, site->y,
1251                                         event->motion.x, event->motion.y))
1252             {
1253               site->state = 0;
1254               gtk_drag_begin_internal (widget, site, site->target_list,
1255                                        site->actions, 
1256                                        i, event);
1257
1258               retval = TRUE;
1259             }
1260         }
1261       break;
1262       
1263     default:                    /* hit for 2/3BUTTON_PRESS */
1264       break;
1265     }
1266   
1267   return retval;
1268 }
1269
1270 void 
1271 gtk_drag_source_set (GtkWidget            *widget,
1272                      GdkModifierType       start_button_mask,
1273                      const GtkTargetEntry *targets,
1274                      gint                  n_targets,
1275                      GdkDragAction         actions)
1276 {
1277   GtkDragSourceSite *site;
1278
1279   g_return_if_fail (GTK_IS_WIDGET (widget));
1280
1281   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1282
1283   gtk_widget_add_events (widget,
1284                          gtk_widget_get_events (widget) |
1285                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1286                          GDK_BUTTON_MOTION_MASK);
1287
1288   if (site)
1289     {
1290       if (site->target_list)
1291         gtk_target_list_unref (site->target_list);
1292     }
1293   else
1294     {
1295       site = g_new0 (GtkDragSourceSite, 1);
1296
1297       site->icon_type = GTK_IMAGE_EMPTY;
1298       
1299       g_signal_connect (widget, "button-press-event",
1300                         G_CALLBACK (gtk_drag_source_event_cb),
1301                         site);
1302       g_signal_connect (widget, "button-release-event",
1303                         G_CALLBACK (gtk_drag_source_event_cb),
1304                         site);
1305       g_signal_connect (widget, "motion-notify-event",
1306                         G_CALLBACK (gtk_drag_source_event_cb),
1307                         site);
1308       
1309       g_object_set_data_full (G_OBJECT (widget),
1310                               I_("gtk-site-data"), 
1311                               site, gtk_drag_source_site_destroy);
1312     }
1313
1314   site->start_button_mask = start_button_mask;
1315
1316   site->target_list = gtk_target_list_new (targets, n_targets);
1317
1318   site->actions = actions;
1319 }
1320
1321 /*************************************************************
1322  * gtk_drag_source_unset
1323  *     Unregister this widget as a drag source.
1324  *   arguments:
1325  *     widget:
1326  *   results:
1327  *************************************************************/
1328
1329 void 
1330 gtk_drag_source_unset (GtkWidget *widget)
1331 {
1332   GtkDragSourceSite *site;
1333
1334   g_return_if_fail (GTK_IS_WIDGET (widget));
1335
1336   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1337
1338   if (site)
1339     {
1340       g_signal_handlers_disconnect_by_func (widget,
1341                                             gtk_drag_source_event_cb,
1342                                             site);
1343       g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
1344     }
1345 }
1346
1347 GtkTargetList *
1348 gtk_drag_source_get_target_list (GtkWidget *widget)
1349 {
1350   GtkDragSourceSite *site;
1351
1352   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1353
1354   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1355
1356   return site ? site->target_list : NULL;
1357
1358 }
1359
1360 void
1361 gtk_drag_source_set_target_list (GtkWidget     *widget,
1362                                  GtkTargetList *target_list)
1363 {
1364   GtkDragSourceSite *site;
1365
1366   g_return_if_fail (GTK_IS_WIDGET (widget));
1367
1368   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1369   if (site == NULL)
1370     {
1371       g_warning ("gtk_drag_source_set_target_list() requires the widget "
1372                  "to already be a drag source.");
1373       return;
1374     }
1375
1376   if (target_list)
1377     gtk_target_list_ref (target_list);
1378
1379   if (site->target_list)
1380     gtk_target_list_unref (site->target_list);
1381
1382   site->target_list = target_list;
1383 }
1384
1385 /**
1386  * gtk_drag_source_add_text_targets:
1387  * @widget: a #GtkWidget that's is a drag source
1388  *
1389  * Add the text targets supported by #GtkSelection to
1390  * the target list of the drag source.  The targets
1391  * are added with @info = 0. If you need another value, 
1392  * use gtk_target_list_add_text_targets() and
1393  * gtk_drag_source_set_target_list().
1394  * 
1395  * Since: 2.6
1396  **/
1397 void
1398 gtk_drag_source_add_text_targets (GtkWidget *widget)
1399 {
1400   GtkTargetList *target_list;
1401
1402   target_list = gtk_drag_source_get_target_list (widget);
1403   if (target_list)
1404     gtk_target_list_ref (target_list);
1405   else
1406     target_list = gtk_target_list_new (NULL, 0);
1407   gtk_target_list_add_text_targets (target_list, 0);
1408   gtk_drag_source_set_target_list (widget, target_list);
1409   gtk_target_list_unref (target_list);
1410 }
1411
1412 void
1413 gtk_drag_source_add_image_targets (GtkWidget *widget)
1414 {
1415   GtkTargetList *target_list;
1416
1417   target_list = gtk_drag_source_get_target_list (widget);
1418   if (target_list)
1419     gtk_target_list_ref (target_list);
1420   else
1421     target_list = gtk_target_list_new (NULL, 0);
1422   gtk_target_list_add_image_targets (target_list, 0, TRUE);
1423   gtk_drag_source_set_target_list (widget, target_list);
1424   gtk_target_list_unref (target_list);
1425 }
1426
1427 void
1428 gtk_drag_source_add_uri_targets (GtkWidget *widget)
1429 {
1430   GtkTargetList *target_list;
1431
1432   target_list = gtk_drag_source_get_target_list (widget);
1433   if (target_list)
1434     gtk_target_list_ref (target_list);
1435   else
1436     target_list = gtk_target_list_new (NULL, 0);
1437   gtk_target_list_add_uri_targets (target_list, 0);
1438   gtk_drag_source_set_target_list (widget, target_list);
1439   gtk_target_list_unref (target_list);
1440 }
1441
1442 static void
1443 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
1444 {
1445   switch (site->icon_type)
1446     {
1447     case GTK_IMAGE_EMPTY:
1448       break;
1449     case GTK_IMAGE_PIXBUF:
1450       g_object_unref (site->icon_data.pixbuf.pixbuf);
1451       break;
1452     case GTK_IMAGE_STOCK:
1453       g_free (site->icon_data.stock.stock_id);
1454       break;
1455     case GTK_IMAGE_ICON_NAME:
1456       g_free (site->icon_data.name.icon_name);
1457       break;
1458     default:
1459       g_assert_not_reached();
1460       break;
1461     }
1462   site->icon_type = GTK_IMAGE_EMPTY;
1463 }
1464
1465 static void 
1466 gtk_drag_source_site_destroy (gpointer data)
1467 {
1468   GtkDragSourceSite *site = data;
1469
1470   if (site->target_list)
1471     gtk_target_list_unref (site->target_list);
1472
1473   gtk_drag_source_unset_icon (site);
1474   g_free (site);
1475 }
1476
1477 void 
1478 gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
1479                                  GdkPixbuf   *pixbuf)
1480 {
1481   GtkDragSourceSite *site;
1482
1483   g_return_if_fail (GTK_IS_WIDGET (widget));
1484   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1485
1486   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1487   g_return_if_fail (site != NULL); 
1488   g_object_ref (pixbuf);
1489
1490   gtk_drag_source_unset_icon (site);
1491
1492   site->icon_type = GTK_IMAGE_PIXBUF;
1493   site->icon_data.pixbuf.pixbuf = pixbuf;
1494 }
1495
1496 /**
1497  * gtk_drag_source_set_icon_stock:
1498  * @widget: a #GtkWidget
1499  * @stock_id: the ID of the stock icon to use
1500  *
1501  * Sets the icon that will be used for drags from a particular source
1502  * to a stock icon. 
1503  **/
1504 void 
1505 gtk_drag_source_set_icon_stock (GtkWidget   *widget,
1506                                 const gchar *stock_id)
1507 {
1508   GtkDragSourceSite *site;
1509
1510   g_return_if_fail (GTK_IS_WIDGET (widget));
1511   g_return_if_fail (stock_id != NULL);
1512
1513   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1514   g_return_if_fail (site != NULL);
1515   
1516   gtk_drag_source_unset_icon (site);
1517
1518   site->icon_type = GTK_IMAGE_STOCK;
1519   site->icon_data.stock.stock_id = g_strdup (stock_id);
1520 }
1521
1522 /**
1523  * gtk_drag_source_set_icon_name:
1524  * @widget: a #GtkWidget
1525  * @icon_name: name of icon to use
1526  * 
1527  * Sets the icon that will be used for drags from a particular source
1528  * to a themed icon. See the docs for #GtkIconTheme for more details.
1529  *
1530  * Since: 2.8
1531  **/
1532 void 
1533 gtk_drag_source_set_icon_name (GtkWidget   *widget,
1534                                const gchar *icon_name)
1535 {
1536   GtkDragSourceSite *site;
1537
1538   g_return_if_fail (GTK_IS_WIDGET (widget));
1539   g_return_if_fail (icon_name != NULL);
1540
1541   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1542   g_return_if_fail (site != NULL);
1543
1544   gtk_drag_source_unset_icon (site);
1545
1546   site->icon_type = GTK_IMAGE_ICON_NAME;
1547   site->icon_data.name.icon_name = g_strdup (icon_name);
1548 }
1549
1550
1551 /**
1552  * gtk_drag_set_icon_widget:
1553  * @context: the context for a drag. (This must be called 
1554           with a  context for the source side of a drag)
1555  * @widget: a toplevel window to use as an icon.
1556  * @hot_x: the X offset within @widget of the hotspot.
1557  * @hot_y: the Y offset within @widget of the hotspot.
1558  * 
1559  * Changes the icon for a widget to a given widget. GTK+
1560  * will not destroy the icon, so if you don't want
1561  * it to persist, you should connect to the "drag-end" 
1562  * signal and destroy it yourself.
1563  **/
1564 void 
1565 gtk_drag_set_icon_widget (GdkDragContext    *context,
1566                           GtkWidget         *widget,
1567                           gint               hot_x,
1568                           gint               hot_y)
1569 {
1570   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1571   g_return_if_fail (context->is_source);
1572   g_return_if_fail (GTK_IS_WIDGET (widget));
1573
1574   g_warning ("gtk_drag_set_icon_widget is not supported on Mac OS X");
1575 }
1576
1577 static void
1578 set_icon_stock_pixbuf (GdkDragContext    *context,
1579                        const gchar       *stock_id,
1580                        GdkPixbuf         *pixbuf,
1581                        gint               hot_x,
1582                        gint               hot_y)
1583 {
1584   GtkDragSourceInfo *info;
1585
1586   info = gtk_drag_get_source_info (context, FALSE);
1587
1588   if (stock_id)
1589     {
1590       pixbuf = gtk_widget_render_icon (info->widget, stock_id,
1591                                        GTK_ICON_SIZE_DND, NULL);
1592
1593       if (!pixbuf)
1594         {
1595           g_warning ("Cannot load drag icon from stock_id %s", stock_id);
1596           return;
1597         }
1598     }
1599   else
1600     g_object_ref (pixbuf);
1601
1602   if (info->icon_pixbuf)
1603     g_object_unref (info->icon_pixbuf);
1604   info->icon_pixbuf = pixbuf;
1605   info->hot_x = hot_x;
1606   info->hot_y = hot_y;
1607 }
1608
1609 /**
1610  * gtk_drag_set_icon_pixbuf:
1611  * @context: the context for a drag. (This must be called 
1612  *            with a  context for the source side of a drag)
1613  * @pixbuf: the #GdkPixbuf to use as the drag icon.
1614  * @hot_x: the X offset within @widget of the hotspot.
1615  * @hot_y: the Y offset within @widget of the hotspot.
1616  * 
1617  * Sets @pixbuf as the icon for a given drag.
1618  **/
1619 void 
1620 gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
1621                            GdkPixbuf      *pixbuf,
1622                            gint            hot_x,
1623                            gint            hot_y)
1624 {
1625   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1626   g_return_if_fail (context->is_source);
1627   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1628
1629   set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
1630 }
1631
1632 /**
1633  * gtk_drag_set_icon_stock:
1634  * @context: the context for a drag. (This must be called 
1635  *            with a  context for the source side of a drag)
1636  * @stock_id: the ID of the stock icon to use for the drag.
1637  * @hot_x: the X offset within the icon of the hotspot.
1638  * @hot_y: the Y offset within the icon of the hotspot.
1639  * 
1640  * Sets the icon for a given drag from a stock ID.
1641  **/
1642 void 
1643 gtk_drag_set_icon_stock  (GdkDragContext *context,
1644                           const gchar    *stock_id,
1645                           gint            hot_x,
1646                           gint            hot_y)
1647 {
1648
1649   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1650   g_return_if_fail (context->is_source);
1651   g_return_if_fail (stock_id != NULL);
1652
1653   set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
1654 }
1655
1656 /**
1657  * gtk_drag_set_icon_name:
1658  * @context: the context for a drag. (This must be called 
1659  *            with a context for the source side of a drag)
1660  * @icon_name: name of icon to use
1661  * @hot_x: the X offset of the hotspot within the icon
1662  * @hot_y: the Y offset of the hotspot within the icon
1663  * 
1664  * Sets the icon for a given drag from a named themed icon. See
1665  * the docs for #GtkIconTheme for more details. Note that the
1666  * size of the icon depends on the icon theme (the icon is
1667  * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus 
1668  * @hot_x and @hot_y have to be used with care.
1669  *
1670  * Since: 2.8
1671  **/
1672 void 
1673 gtk_drag_set_icon_name (GdkDragContext *context,
1674                         const gchar    *icon_name,
1675                         gint            hot_x,
1676                         gint            hot_y)
1677 {
1678   GdkScreen *screen;
1679   GtkSettings *settings;
1680   GtkIconTheme *icon_theme;
1681   GdkPixbuf *pixbuf;
1682   gint width, height, icon_size;
1683
1684   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1685   g_return_if_fail (context->is_source);
1686   g_return_if_fail (icon_name != NULL);
1687
1688   screen = gdk_window_get_screen (context->source_window);
1689   g_return_if_fail (screen != NULL);
1690
1691   settings = gtk_settings_get_for_screen (screen);
1692   if (gtk_icon_size_lookup_for_settings (settings,
1693                                          GTK_ICON_SIZE_DND,
1694                                          &width, &height))
1695     icon_size = MAX (width, height);
1696   else 
1697     icon_size = 32; /* default value for GTK_ICON_SIZE_DND */ 
1698
1699   icon_theme = gtk_icon_theme_get_for_screen (screen);
1700
1701   pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name,
1702                                      icon_size, 0, NULL);
1703   if (pixbuf)
1704     set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
1705   else
1706     g_warning ("Cannot load drag icon from icon name %s", icon_name);
1707 }
1708
1709 /**
1710  * gtk_drag_set_icon_default:
1711  * @context: the context for a drag. (This must be called 
1712              with a  context for the source side of a drag)
1713  * 
1714  * Sets the icon for a particular drag to the default
1715  * icon.
1716  **/
1717 void 
1718 gtk_drag_set_icon_default (GdkDragContext    *context)
1719 {
1720   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1721   g_return_if_fail (context->is_source);
1722
1723   gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
1724 }
1725
1726 static void
1727 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
1728 {
1729   if (info->icon_pixbuf)
1730     g_object_unref (info->icon_pixbuf);
1731
1732   g_signal_emit_by_name (info->widget, "drag-end", 
1733                          info->context);
1734
1735   if (info->source_widget)
1736     g_object_unref (info->source_widget);
1737
1738   if (info->widget)
1739     g_object_unref (info->widget);
1740
1741   gtk_target_list_unref (info->target_list);
1742
1743   gtk_drag_clear_source_info (info->context);
1744   g_object_unref (info->context);
1745
1746   g_free (info);
1747 }
1748
1749 static gboolean
1750 drag_drop_finished_idle_cb (gpointer data)
1751 {
1752   gtk_drag_source_info_destroy (data);
1753   return FALSE;
1754 }
1755
1756 static void
1757 gtk_drag_drop_finished (GtkDragSourceInfo *info)
1758 {
1759   if (info->success && info->delete)
1760     g_signal_emit_by_name (info->source_widget, "drag-data-delete",
1761                            info->context);
1762
1763   /* Workaround for the fact that the NS API blocks until the drag is
1764    * over. This way the context is still valid when returning from
1765    * drag_begin, even if it will still be quite useless. See bug #501588.
1766   */
1767   g_idle_add (drag_drop_finished_idle_cb, info);
1768 }
1769
1770 /*************************************************************
1771  * _gtk_drag_source_handle_event:
1772  *     Called from widget event handling code on Drag events
1773  *     for drag sources.
1774  *
1775  *   arguments:
1776  *     toplevel: Toplevel widget that received the event
1777  *     event:
1778  *   results:
1779  *************************************************************/
1780
1781 void
1782 _gtk_drag_source_handle_event (GtkWidget *widget,
1783                                GdkEvent  *event)
1784 {
1785   GtkDragSourceInfo *info;
1786   GdkDragContext *context;
1787
1788   g_return_if_fail (widget != NULL);
1789   g_return_if_fail (event != NULL);
1790
1791   context = event->dnd.context;
1792   info = gtk_drag_get_source_info (context, FALSE);
1793   if (!info)
1794     return;
1795
1796   switch (event->type)
1797     {
1798     case GDK_DROP_FINISHED:
1799       gtk_drag_drop_finished (info);
1800       break;
1801     default:
1802       g_assert_not_reached ();
1803     }  
1804 }
1805
1806
1807 gboolean
1808 gtk_drag_check_threshold (GtkWidget *widget,
1809                           gint       start_x,
1810                           gint       start_y,
1811                           gint       current_x,
1812                           gint       current_y)
1813 {
1814   gint drag_threshold;
1815
1816   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1817
1818   g_object_get (gtk_widget_get_settings (widget),
1819                 "gtk-dnd-drag-threshold", &drag_threshold,
1820                 NULL);
1821   
1822   return (ABS (current_x - start_x) > drag_threshold ||
1823           ABS (current_y - start_y) > drag_threshold);
1824 }