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