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