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