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