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