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