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