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