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