]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd-quartz.c
Merge branch 'master' into toolpalette
[~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_DRAWABLE (widget))
357     {
358       cairo_t *cr;
359       
360       if (GTK_WIDGET_NO_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_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       NSArray *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];
459       [pool release];
460     }
461 }
462
463 static void
464 gtk_drag_dest_realized (GtkWidget *widget, 
465                         gpointer   user_data)
466 {
467   GtkDragDestSite *site = user_data;
468
469   register_types (widget, site);
470 }
471
472 static void
473 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
474                                  GtkWidget *previous_toplevel,
475                                  gpointer   user_data)
476 {
477   GtkDragDestSite *site = user_data;
478
479   register_types (widget, site);
480 }
481
482 static void
483 gtk_drag_dest_site_destroy (gpointer data)
484 {
485   GtkDragDestSite *site = data;
486     
487   if (site->target_list)
488     gtk_target_list_unref (site->target_list);
489
490   g_free (site);
491 }
492
493 void 
494 gtk_drag_dest_set (GtkWidget            *widget,
495                    GtkDestDefaults       flags,
496                    const GtkTargetEntry *targets,
497                    gint                  n_targets,
498                    GdkDragAction         actions)
499 {
500   GtkDragDestSite *old_site, *site;
501
502   g_return_if_fail (GTK_IS_WIDGET (widget));
503
504   old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
505
506   gtk_drag_dest_unset (widget);
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   if (GTK_WIDGET_REALIZED (widget))
523     gtk_drag_dest_realized (widget, site);
524
525   g_signal_connect (widget, "realize",
526                     G_CALLBACK (gtk_drag_dest_realized), site);
527   g_signal_connect (widget, "hierarchy-changed",
528                     G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
529
530   g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
531                           site, gtk_drag_dest_site_destroy);
532 }
533
534 void 
535 gtk_drag_dest_set_proxy (GtkWidget      *widget,
536                          GdkWindow      *proxy_window,
537                          GdkDragProtocol protocol,
538                          gboolean        use_coordinates)
539 {
540   g_warning ("gtk_drag_dest_set_proxy is not supported on Mac OS X.");
541 }
542
543 void 
544 gtk_drag_dest_unset (GtkWidget *widget)
545 {
546   GtkDragDestSite *old_site;
547
548   g_return_if_fail (GTK_IS_WIDGET (widget));
549
550   old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
551   if (old_site)
552     {
553       g_signal_handlers_disconnect_by_func (widget,
554                                             gtk_drag_dest_realized,
555                                             old_site);
556       g_signal_handlers_disconnect_by_func (widget,
557                                             gtk_drag_dest_hierarchy_changed,
558                                             old_site);
559     }
560
561   g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
562 }
563
564 GtkTargetList*
565 gtk_drag_dest_get_target_list (GtkWidget *widget)
566 {
567   GtkDragDestSite *site;
568
569   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
570   
571   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
572
573   return site ? site->target_list : NULL;  
574 }
575
576 void
577 gtk_drag_dest_set_target_list (GtkWidget      *widget,
578                                GtkTargetList  *target_list)
579 {
580   GtkDragDestSite *site;
581
582   g_return_if_fail (GTK_IS_WIDGET (widget));
583   
584   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
585   
586   if (!site)
587     {
588       g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
589                  "to make the widget into a drag destination");
590       return;
591     }
592
593   if (target_list)
594     gtk_target_list_ref (target_list);
595   
596   if (site->target_list)
597     gtk_target_list_unref (site->target_list);
598
599   site->target_list = target_list;
600
601   register_types (widget, site);
602 }
603
604 void
605 gtk_drag_dest_add_text_targets (GtkWidget *widget)
606 {
607   GtkTargetList *target_list;
608
609   target_list = gtk_drag_dest_get_target_list (widget);
610   if (target_list)
611     gtk_target_list_ref (target_list);
612   else
613     target_list = gtk_target_list_new (NULL, 0);
614   gtk_target_list_add_text_targets (target_list, 0);
615   gtk_drag_dest_set_target_list (widget, target_list);
616   gtk_target_list_unref (target_list);
617 }
618
619 void
620 gtk_drag_dest_add_image_targets (GtkWidget *widget)
621 {
622   GtkTargetList *target_list;
623
624   target_list = gtk_drag_dest_get_target_list (widget);
625   if (target_list)
626     gtk_target_list_ref (target_list);
627   else
628     target_list = gtk_target_list_new (NULL, 0);
629   gtk_target_list_add_image_targets (target_list, 0, FALSE);
630   gtk_drag_dest_set_target_list (widget, target_list);
631   gtk_target_list_unref (target_list);
632 }
633
634 void
635 gtk_drag_dest_add_uri_targets (GtkWidget *widget)
636 {
637   GtkTargetList *target_list;
638
639   target_list = gtk_drag_dest_get_target_list (widget);
640   if (target_list)
641     gtk_target_list_ref (target_list);
642   else
643     target_list = gtk_target_list_new (NULL, 0);
644   gtk_target_list_add_uri_targets (target_list, 0);
645   gtk_drag_dest_set_target_list (widget, target_list);
646   gtk_target_list_unref (target_list);
647 }
648
649 static void
650 prepend_and_ref_widget (GtkWidget *widget,
651                         gpointer   data)
652 {
653   GSList **slist_p = data;
654
655   *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
656 }
657
658 static void
659 gtk_drag_find_widget (GtkWidget       *widget,
660                       GtkDragFindData *data)
661 {
662   GtkAllocation new_allocation;
663   gint allocation_to_window_x = 0;
664   gint allocation_to_window_y = 0;
665   gint x_offset = 0;
666   gint y_offset = 0;
667
668   if (data->found || !GTK_WIDGET_MAPPED (widget) || !GTK_WIDGET_SENSITIVE (widget))
669     return;
670
671   /* Note that in the following code, we only count the
672    * position as being inside a WINDOW widget if it is inside
673    * widget->window; points that are outside of widget->window
674    * but within the allocation are not counted. This is consistent
675    * with the way we highlight drag targets.
676    *
677    * data->x,y are relative to widget->parent->window (if
678    * widget is not a toplevel, widget->window otherwise).
679    * We compute the allocation of widget in the same coordinates,
680    * clipping to widget->window, and all intermediate
681    * windows. If data->x,y is inside that, then we translate
682    * our coordinates to be relative to widget->window and
683    * recurse.
684    */  
685   new_allocation = widget->allocation;
686
687   if (widget->parent)
688     {
689       gint tx, ty;
690       GdkWindow *window = widget->window;
691
692       /* Compute the offset from allocation-relative to
693        * window-relative coordinates.
694        */
695       allocation_to_window_x = widget->allocation.x;
696       allocation_to_window_y = widget->allocation.y;
697
698       if (!GTK_WIDGET_NO_WINDOW (widget))
699         {
700           /* The allocation is relative to the parent window for
701            * window widgets, not to widget->window.
702            */
703           gdk_window_get_position (window, &tx, &ty);
704           
705           allocation_to_window_x -= tx;
706           allocation_to_window_y -= ty;
707         }
708
709       new_allocation.x = 0 + allocation_to_window_x;
710       new_allocation.y = 0 + allocation_to_window_y;
711       
712       while (window && window != widget->parent->window)
713         {
714           GdkRectangle window_rect = { 0, 0, 0, 0 };
715           
716           gdk_drawable_get_size (window, &window_rect.width, &window_rect.height);
717
718           gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
719
720           gdk_window_get_position (window, &tx, &ty);
721           new_allocation.x += tx;
722           x_offset += tx;
723           new_allocation.y += ty;
724           y_offset += ty;
725           
726           window = gdk_window_get_parent (window);
727         }
728
729       if (!window)              /* Window and widget heirarchies didn't match. */
730         return;
731     }
732
733   if (data->toplevel ||
734       ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
735        (data->x < new_allocation.x + new_allocation.width) && 
736        (data->y < new_allocation.y + new_allocation.height)))
737     {
738       /* First, check if the drag is in a valid drop site in
739        * one of our children 
740        */
741       if (GTK_IS_CONTAINER (widget))
742         {
743           GtkDragFindData new_data = *data;
744           GSList *children = NULL;
745           GSList *tmp_list;
746           
747           new_data.x -= x_offset;
748           new_data.y -= y_offset;
749           new_data.found = FALSE;
750           new_data.toplevel = FALSE;
751           
752           /* need to reference children temporarily in case the
753            * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
754            */
755           gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
756           for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
757             {
758               if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
759                 gtk_drag_find_widget (tmp_list->data, &new_data);
760               g_object_unref (tmp_list->data);
761             }
762           g_slist_free (children);
763           
764           data->found = new_data.found;
765         }
766
767       /* If not, and this widget is registered as a drop site, check to
768        * emit "drag-motion" to check if we are actually in
769        * a drop site.
770        */
771       if (!data->found &&
772           g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
773         {
774           data->found = data->callback (widget,
775                                         data->context,
776                                         data->x - x_offset - allocation_to_window_x,
777                                         data->y - y_offset - allocation_to_window_y,
778                                         data->time);
779           /* If so, send a "drag-leave" to the last widget */
780           if (data->found)
781             {
782               if (data->info->widget && data->info->widget != widget)
783                 {
784                   gtk_drag_dest_leave (data->info->widget, data->context, data->time);
785                 }
786               data->info->widget = widget;
787             }
788         }
789     }
790 }
791
792 static void  
793 gtk_drag_dest_leave (GtkWidget      *widget,
794                      GdkDragContext *context,
795                      guint           time)
796 {
797   GtkDragDestSite *site;
798
799   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
800   g_return_if_fail (site != NULL);
801
802   if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
803     gtk_drag_unhighlight (widget);
804   
805   if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
806       site->track_motion)
807     g_signal_emit_by_name (widget, "drag-leave", context, time);
808   
809   site->have_drag = FALSE;
810 }
811
812 static gboolean
813 gtk_drag_dest_motion (GtkWidget      *widget,
814                       GdkDragContext *context,
815                       gint            x,
816                       gint            y,
817                       guint           time)
818 {
819   GtkDragDestSite *site;
820   GdkDragAction action = 0;
821   gboolean retval;
822
823   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
824   g_return_val_if_fail (site != NULL, FALSE);
825
826   if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
827     {
828       if (context->suggested_action & site->actions)
829         action = context->suggested_action;
830       
831       if (action && gtk_drag_dest_find_target (widget, context, NULL))
832         {
833           if (!site->have_drag)
834             {
835               site->have_drag = TRUE;
836               if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
837                 gtk_drag_highlight (widget);
838             }
839           
840           gdk_drag_status (context, action, time);
841         }
842       else
843         {
844           gdk_drag_status (context, 0, time);
845           if (!site->track_motion)
846             return TRUE;
847         }
848     }
849
850   g_signal_emit_by_name (widget, "drag-motion",
851                          context, x, y, time, &retval);
852
853   return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
854 }
855
856 static gboolean
857 gtk_drag_dest_drop (GtkWidget        *widget,
858                     GdkDragContext   *context,
859                     gint              x,
860                     gint              y,
861                     guint             time)
862 {
863   GtkDragDestSite *site;
864   GtkDragDestInfo *info;
865   gboolean retval;
866
867   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
868   g_return_val_if_fail (site != NULL, FALSE);
869
870   info = gtk_drag_get_dest_info (context, FALSE);
871   g_return_val_if_fail (info != NULL, FALSE);
872
873   info->drop_x = x;
874   info->drop_y = y;
875
876   if (site->flags & GTK_DEST_DEFAULT_DROP)
877     {
878       GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
879
880       if (target == GDK_NONE)
881         {
882           gtk_drag_finish (context, FALSE, FALSE, time);
883           return TRUE;
884         }
885       else
886         gtk_drag_get_data (widget, context, target, time);
887     }
888   
889   g_signal_emit_by_name (widget, "drag-drop",
890                          context, x, y, time, &retval);
891
892   return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
893 }
894
895 void
896 gtk_drag_dest_set_track_motion (GtkWidget *widget,
897                                 gboolean   track_motion)
898 {
899   GtkDragDestSite *site;
900
901   g_return_if_fail (GTK_IS_WIDGET (widget));
902
903   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
904   
905   g_return_if_fail (site != NULL);
906
907   site->track_motion = track_motion != FALSE;
908 }
909
910 gboolean
911 gtk_drag_dest_get_track_motion (GtkWidget *widget)
912 {
913   GtkDragDestSite *site;
914
915   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
916
917   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
918
919   if (site)
920     return site->track_motion;
921
922   return FALSE;
923 }
924
925 void
926 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
927                              GdkEvent  *event)
928 {
929   GtkDragDestInfo *info;
930   GdkDragContext *context;
931
932   g_return_if_fail (toplevel != NULL);
933   g_return_if_fail (event != NULL);
934
935   context = event->dnd.context;
936
937   info = gtk_drag_get_dest_info (context, TRUE);
938
939   /* Find the widget for the event */
940   switch (event->type)
941     {
942     case GDK_DRAG_ENTER:
943       break;
944
945     case GDK_DRAG_LEAVE:
946       if (info->widget)
947         {
948           gtk_drag_dest_leave (info->widget, context, event->dnd.time);
949           info->widget = NULL;
950         }
951       break;
952
953     case GDK_DRAG_MOTION:
954     case GDK_DROP_START:
955       {
956         GtkDragFindData data;
957         gint tx, ty;
958
959         if (event->type == GDK_DROP_START)
960           {
961             info->dropped = TRUE;
962             /* We send a leave here so that the widget unhighlights
963              * properly.
964              */
965             if (info->widget)
966               {
967                 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
968                 info->widget = NULL;
969               }
970           }
971
972         gdk_window_get_position (toplevel->window, &tx, &ty);
973         
974         data.x = event->dnd.x_root - tx;
975         data.y = event->dnd.y_root - ty;
976         data.context = context;
977         data.info = info;
978         data.found = FALSE;
979         data.toplevel = TRUE;
980         data.callback = (event->type == GDK_DRAG_MOTION) ?
981           gtk_drag_dest_motion : gtk_drag_dest_drop;
982         data.time = event->dnd.time;
983         
984         gtk_drag_find_widget (toplevel, &data);
985
986         if (info->widget && !data.found)
987           {
988             gtk_drag_dest_leave (info->widget, context, event->dnd.time);
989             info->widget = NULL;
990           }
991
992         /* Send a reply.
993          */
994         if (event->type == GDK_DRAG_MOTION)
995           {
996             if (!data.found)
997               gdk_drag_status (context, 0, event->dnd.time);
998           }
999
1000         break;
1001       default:
1002         g_assert_not_reached ();
1003       }
1004     }
1005 }
1006
1007
1008 GdkAtom
1009 gtk_drag_dest_find_target (GtkWidget      *widget,
1010                            GdkDragContext *context,
1011                            GtkTargetList  *target_list)
1012 {
1013   id <NSDraggingInfo> dragging_info;
1014   NSPasteboard *pasteboard;
1015   GtkWidget *source_widget;
1016   GList *tmp_target;
1017   GList *tmp_source = NULL;
1018   GList *source_targets;
1019
1020   g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1021   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1022   g_return_val_if_fail (!context->is_source, GDK_NONE);
1023
1024   dragging_info = gdk_quartz_drag_context_get_dragging_info_libgtk_only (context);
1025   pasteboard = [dragging_info draggingPasteboard];
1026
1027   source_widget = gtk_drag_get_source_widget (context);
1028
1029   if (target_list == NULL)
1030     target_list = gtk_drag_dest_get_target_list (widget);
1031   
1032   if (target_list == NULL)
1033     return GDK_NONE;
1034
1035   source_targets = _gtk_quartz_pasteboard_types_to_atom_list ([pasteboard types]);
1036   tmp_target = target_list->list;
1037   while (tmp_target)
1038     {
1039       GtkTargetPair *pair = tmp_target->data;
1040       tmp_source = source_targets;
1041       while (tmp_source)
1042         {
1043           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1044             {
1045               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1046                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1047                 {
1048                   g_list_free (source_targets);
1049                   return pair->target;
1050                 }
1051               else
1052                 break;
1053             }
1054           tmp_source = tmp_source->next;
1055         }
1056       tmp_target = tmp_target->next;
1057     }
1058
1059   g_list_free (source_targets);
1060   return GDK_NONE;
1061 }
1062
1063 static gboolean
1064 gtk_drag_begin_idle (gpointer arg)
1065 {
1066   GdkDragContext* context = (GdkDragContext*) arg;
1067   GtkDragSourceInfo* info = gtk_drag_get_source_info (context, FALSE);
1068   NSWindow *nswindow;
1069   NSPasteboard *pasteboard;
1070   GtkDragSourceOwner *owner;
1071   NSPoint point;
1072
1073   g_assert (info != NULL);
1074
1075   pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1076   owner = [[GtkDragSourceOwner alloc] initWithInfo:info];
1077
1078   [pasteboard declareTypes:_gtk_quartz_target_list_to_pasteboard_types (info->target_list) owner:owner];
1079
1080   if ((nswindow = get_toplevel_nswindow (info->source_widget)) == NULL)
1081      return FALSE;
1082   
1083   /* Ref the context. It's unreffed when the drag has been aborted */
1084   g_object_ref (info->context);
1085
1086   /* FIXME: If the event isn't a mouse event, use the global cursor position instead */
1087   point = [info->nsevent locationInWindow];
1088
1089   [nswindow dragImage:_gtk_quartz_create_image_from_pixbuf (info->icon_pixbuf)
1090                    at:point
1091                offset:NSMakeSize(0, 0)
1092                 event:info->nsevent
1093            pasteboard:pasteboard
1094                source:nswindow
1095             slideBack:YES];
1096
1097   [info->nsevent release];
1098
1099   return FALSE;
1100 }
1101
1102 static GdkDragContext *
1103 gtk_drag_begin_internal (GtkWidget         *widget,
1104                          GtkDragSourceSite *site,
1105                          GtkTargetList     *target_list,
1106                          GdkDragAction      actions,
1107                          gint               button,
1108                          GdkEvent          *event)
1109 {
1110   GtkDragSourceInfo *info;
1111   GdkDragContext *context;
1112   NSWindow *nswindow;
1113
1114   context = gdk_drag_begin (NULL, NULL);
1115   context->is_source = TRUE;
1116
1117   info = gtk_drag_get_source_info (context, TRUE);
1118   
1119   info->source_widget = g_object_ref (widget);
1120   info->widget = g_object_ref (widget);
1121   info->target_list = target_list;
1122   gtk_target_list_ref (target_list);
1123
1124   info->possible_actions = actions;
1125   
1126   g_signal_emit_by_name (widget, "drag-begin", info->context);
1127
1128   /* Ensure that we have an icon before we start the drag; the
1129    * application may have set one in ::drag_begin, or it may
1130    * not have set one.
1131    */
1132   if (!info->icon_pixbuf)
1133     {
1134       if (!site || site->icon_type == GTK_IMAGE_EMPTY)
1135         gtk_drag_set_icon_default (context);
1136       else
1137         switch (site->icon_type)
1138           {
1139           case GTK_IMAGE_PIXMAP:
1140             /* This is not supported, so just set a small transparent pixbuf
1141              * since we need to have something.
1142              */
1143             if (0)
1144               gtk_drag_set_icon_pixmap (context,
1145                                         site->colormap,
1146                                         site->icon_data.pixmap.pixmap,
1147                                         site->icon_mask,
1148                                         -2, -2);
1149             else
1150               {
1151                 GdkPixbuf *pixbuf;
1152
1153                 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 1, 1);
1154                 gdk_pixbuf_fill (pixbuf, 0xffffff);
1155
1156                 gtk_drag_set_icon_pixbuf (context,
1157                                           pixbuf,
1158                                           0, 0);
1159
1160                 g_object_unref (pixbuf);
1161               }
1162             break;
1163           case GTK_IMAGE_PIXBUF:
1164             gtk_drag_set_icon_pixbuf (context,
1165                                       site->icon_data.pixbuf.pixbuf,
1166                                       -2, -2);
1167             break;
1168           case GTK_IMAGE_STOCK:
1169             gtk_drag_set_icon_stock (context,
1170                                      site->icon_data.stock.stock_id,
1171                                      -2, -2);
1172             break;
1173           case GTK_IMAGE_ICON_NAME:
1174             gtk_drag_set_icon_name (context,
1175                                     site->icon_data.name.icon_name,
1176                                     -2, -2);
1177             break;
1178           case GTK_IMAGE_EMPTY:
1179           default:
1180             g_assert_not_reached();
1181             break;
1182           }
1183     }
1184
1185   nswindow = get_toplevel_nswindow (widget);
1186   info->nsevent = [nswindow currentEvent];
1187   [info->nsevent retain];
1188
1189   /* drag will begin in an idle handler to avoid nested run loops */
1190
1191   g_idle_add_full (G_PRIORITY_HIGH_IDLE, gtk_drag_begin_idle, context, NULL);
1192
1193   gdk_pointer_ungrab (0);
1194
1195   return context;
1196 }
1197
1198 GdkDragContext *
1199 gtk_drag_begin (GtkWidget         *widget,
1200                 GtkTargetList     *targets,
1201                 GdkDragAction      actions,
1202                 gint               button,
1203                 GdkEvent          *event)
1204 {
1205   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1206   g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1207   g_return_val_if_fail (targets != NULL, NULL);
1208
1209   return gtk_drag_begin_internal (widget, NULL, targets,
1210                                   actions, button, event);
1211 }
1212
1213
1214 static gboolean
1215 gtk_drag_source_event_cb (GtkWidget      *widget,
1216                           GdkEvent       *event,
1217                           gpointer        data)
1218 {
1219   GtkDragSourceSite *site;
1220   gboolean retval = FALSE;
1221   site = (GtkDragSourceSite *)data;
1222
1223   switch (event->type)
1224     {
1225     case GDK_BUTTON_PRESS:
1226       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
1227         {
1228           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
1229           site->x = event->button.x;
1230           site->y = event->button.y;
1231         }
1232       break;
1233       
1234     case GDK_BUTTON_RELEASE:
1235       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
1236         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
1237       break;
1238       
1239     case GDK_MOTION_NOTIFY:
1240       if (site->state & event->motion.state & site->start_button_mask)
1241         {
1242           /* FIXME: This is really broken and can leave us
1243            * with a stuck grab
1244            */
1245           int i;
1246           for (i=1; i<6; i++)
1247             {
1248               if (site->state & event->motion.state & 
1249                   GDK_BUTTON1_MASK << (i - 1))
1250                 break;
1251             }
1252
1253           if (gtk_drag_check_threshold (widget, site->x, site->y,
1254                                         event->motion.x, event->motion.y))
1255             {
1256               site->state = 0;
1257               gtk_drag_begin_internal (widget, site, site->target_list,
1258                                        site->actions, 
1259                                        i, event);
1260
1261               retval = TRUE;
1262             }
1263         }
1264       break;
1265       
1266     default:                    /* hit for 2/3BUTTON_PRESS */
1267       break;
1268     }
1269   
1270   return retval;
1271 }
1272
1273 void 
1274 gtk_drag_source_set (GtkWidget            *widget,
1275                      GdkModifierType       start_button_mask,
1276                      const GtkTargetEntry *targets,
1277                      gint                  n_targets,
1278                      GdkDragAction         actions)
1279 {
1280   GtkDragSourceSite *site;
1281
1282   g_return_if_fail (GTK_IS_WIDGET (widget));
1283
1284   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1285
1286   gtk_widget_add_events (widget,
1287                          gtk_widget_get_events (widget) |
1288                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1289                          GDK_BUTTON_MOTION_MASK);
1290
1291   if (site)
1292     {
1293       if (site->target_list)
1294         gtk_target_list_unref (site->target_list);
1295     }
1296   else
1297     {
1298       site = g_new0 (GtkDragSourceSite, 1);
1299
1300       site->icon_type = GTK_IMAGE_EMPTY;
1301       
1302       g_signal_connect (widget, "button-press-event",
1303                         G_CALLBACK (gtk_drag_source_event_cb),
1304                         site);
1305       g_signal_connect (widget, "button-release-event",
1306                         G_CALLBACK (gtk_drag_source_event_cb),
1307                         site);
1308       g_signal_connect (widget, "motion-notify-event",
1309                         G_CALLBACK (gtk_drag_source_event_cb),
1310                         site);
1311       
1312       g_object_set_data_full (G_OBJECT (widget),
1313                               I_("gtk-site-data"), 
1314                               site, gtk_drag_source_site_destroy);
1315     }
1316
1317   site->start_button_mask = start_button_mask;
1318
1319   site->target_list = gtk_target_list_new (targets, n_targets);
1320
1321   site->actions = actions;
1322 }
1323
1324 /*************************************************************
1325  * gtk_drag_source_unset
1326  *     Unregister this widget as a drag source.
1327  *   arguments:
1328  *     widget:
1329  *   results:
1330  *************************************************************/
1331
1332 void 
1333 gtk_drag_source_unset (GtkWidget *widget)
1334 {
1335   GtkDragSourceSite *site;
1336
1337   g_return_if_fail (GTK_IS_WIDGET (widget));
1338
1339   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1340
1341   if (site)
1342     {
1343       g_signal_handlers_disconnect_by_func (widget,
1344                                             gtk_drag_source_event_cb,
1345                                             site);
1346       g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
1347     }
1348 }
1349
1350 GtkTargetList *
1351 gtk_drag_source_get_target_list (GtkWidget *widget)
1352 {
1353   GtkDragSourceSite *site;
1354
1355   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1356
1357   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1358
1359   return site ? site->target_list : NULL;
1360
1361 }
1362
1363 void
1364 gtk_drag_source_set_target_list (GtkWidget     *widget,
1365                                  GtkTargetList *target_list)
1366 {
1367   GtkDragSourceSite *site;
1368
1369   g_return_if_fail (GTK_IS_WIDGET (widget));
1370
1371   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1372   if (site == NULL)
1373     {
1374       g_warning ("gtk_drag_source_set_target_list() requires the widget "
1375                  "to already be a drag source.");
1376       return;
1377     }
1378
1379   if (target_list)
1380     gtk_target_list_ref (target_list);
1381
1382   if (site->target_list)
1383     gtk_target_list_unref (site->target_list);
1384
1385   site->target_list = target_list;
1386 }
1387
1388 /**
1389  * gtk_drag_source_add_text_targets:
1390  * @widget: a #GtkWidget that's is a drag source
1391  *
1392  * Add the text targets supported by #GtkSelection to
1393  * the target list of the drag source.  The targets
1394  * are added with @info = 0. If you need another value, 
1395  * use gtk_target_list_add_text_targets() and
1396  * gtk_drag_source_set_target_list().
1397  * 
1398  * Since: 2.6
1399  **/
1400 void
1401 gtk_drag_source_add_text_targets (GtkWidget *widget)
1402 {
1403   GtkTargetList *target_list;
1404
1405   target_list = gtk_drag_source_get_target_list (widget);
1406   if (target_list)
1407     gtk_target_list_ref (target_list);
1408   else
1409     target_list = gtk_target_list_new (NULL, 0);
1410   gtk_target_list_add_text_targets (target_list, 0);
1411   gtk_drag_source_set_target_list (widget, target_list);
1412   gtk_target_list_unref (target_list);
1413 }
1414
1415 void
1416 gtk_drag_source_add_image_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_image_targets (target_list, 0, TRUE);
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_uri_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_uri_targets (target_list, 0);
1441   gtk_drag_source_set_target_list (widget, target_list);
1442   gtk_target_list_unref (target_list);
1443 }
1444
1445 static void
1446 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
1447 {
1448   switch (site->icon_type)
1449     {
1450     case GTK_IMAGE_EMPTY:
1451       break;
1452     case GTK_IMAGE_PIXMAP:
1453       if (site->icon_data.pixmap.pixmap)
1454         g_object_unref (site->icon_data.pixmap.pixmap);
1455       if (site->icon_mask)
1456         g_object_unref (site->icon_mask);
1457       break;
1458     case GTK_IMAGE_PIXBUF:
1459       g_object_unref (site->icon_data.pixbuf.pixbuf);
1460       break;
1461     case GTK_IMAGE_STOCK:
1462       g_free (site->icon_data.stock.stock_id);
1463       break;
1464     case GTK_IMAGE_ICON_NAME:
1465       g_free (site->icon_data.name.icon_name);
1466       break;
1467     default:
1468       g_assert_not_reached();
1469       break;
1470     }
1471   site->icon_type = GTK_IMAGE_EMPTY;
1472   
1473   if (site->colormap)
1474     g_object_unref (site->colormap);
1475   site->colormap = NULL;
1476 }
1477
1478 static void 
1479 gtk_drag_source_site_destroy (gpointer data)
1480 {
1481   GtkDragSourceSite *site = data;
1482
1483   if (site->target_list)
1484     gtk_target_list_unref (site->target_list);
1485
1486   gtk_drag_source_unset_icon (site);
1487   g_free (site);
1488 }
1489
1490 void 
1491 gtk_drag_source_set_icon (GtkWidget     *widget,
1492                           GdkColormap   *colormap,
1493                           GdkPixmap     *pixmap,
1494                           GdkBitmap     *mask)
1495 {
1496   GtkDragSourceSite *site;
1497
1498   g_return_if_fail (GTK_IS_WIDGET (widget));
1499   g_return_if_fail (GDK_IS_COLORMAP (colormap));
1500   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
1501   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
1502
1503   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1504   g_return_if_fail (site != NULL);
1505   
1506   g_object_ref (colormap);
1507   g_object_ref (pixmap);
1508   if (mask)
1509     g_object_ref (mask);
1510
1511   gtk_drag_source_unset_icon (site);
1512
1513   site->icon_type = GTK_IMAGE_PIXMAP;
1514   
1515   site->icon_data.pixmap.pixmap = pixmap;
1516   site->icon_mask = mask;
1517   site->colormap = colormap;
1518 }
1519
1520 void 
1521 gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
1522                                  GdkPixbuf   *pixbuf)
1523 {
1524   GtkDragSourceSite *site;
1525
1526   g_return_if_fail (GTK_IS_WIDGET (widget));
1527   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1528
1529   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1530   g_return_if_fail (site != NULL); 
1531   g_object_ref (pixbuf);
1532
1533   gtk_drag_source_unset_icon (site);
1534
1535   site->icon_type = GTK_IMAGE_PIXBUF;
1536   site->icon_data.pixbuf.pixbuf = pixbuf;
1537 }
1538
1539 /**
1540  * gtk_drag_source_set_icon_stock:
1541  * @widget: a #GtkWidget
1542  * @stock_id: the ID of the stock icon to use
1543  *
1544  * Sets the icon that will be used for drags from a particular source
1545  * to a stock icon. 
1546  **/
1547 void 
1548 gtk_drag_source_set_icon_stock (GtkWidget   *widget,
1549                                 const gchar *stock_id)
1550 {
1551   GtkDragSourceSite *site;
1552
1553   g_return_if_fail (GTK_IS_WIDGET (widget));
1554   g_return_if_fail (stock_id != NULL);
1555
1556   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1557   g_return_if_fail (site != NULL);
1558   
1559   gtk_drag_source_unset_icon (site);
1560
1561   site->icon_type = GTK_IMAGE_STOCK;
1562   site->icon_data.stock.stock_id = g_strdup (stock_id);
1563 }
1564
1565 /**
1566  * gtk_drag_source_set_icon_name:
1567  * @widget: a #GtkWidget
1568  * @icon_name: name of icon to use
1569  * 
1570  * Sets the icon that will be used for drags from a particular source
1571  * to a themed icon. See the docs for #GtkIconTheme for more details.
1572  *
1573  * Since: 2.8
1574  **/
1575 void 
1576 gtk_drag_source_set_icon_name (GtkWidget   *widget,
1577                                const gchar *icon_name)
1578 {
1579   GtkDragSourceSite *site;
1580
1581   g_return_if_fail (GTK_IS_WIDGET (widget));
1582   g_return_if_fail (icon_name != NULL);
1583
1584   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1585   g_return_if_fail (site != NULL);
1586
1587   gtk_drag_source_unset_icon (site);
1588
1589   site->icon_type = GTK_IMAGE_ICON_NAME;
1590   site->icon_data.name.icon_name = g_strdup (icon_name);
1591 }
1592
1593
1594 /**
1595  * gtk_drag_set_icon_widget:
1596  * @context: the context for a drag. (This must be called 
1597           with a  context for the source side of a drag)
1598  * @widget: a toplevel window to use as an icon.
1599  * @hot_x: the X offset within @widget of the hotspot.
1600  * @hot_y: the Y offset within @widget of the hotspot.
1601  * 
1602  * Changes the icon for a widget to a given widget. GTK+
1603  * will not destroy the icon, so if you don't want
1604  * it to persist, you should connect to the "drag-end" 
1605  * signal and destroy it yourself.
1606  **/
1607 void 
1608 gtk_drag_set_icon_widget (GdkDragContext    *context,
1609                           GtkWidget         *widget,
1610                           gint               hot_x,
1611                           gint               hot_y)
1612 {
1613   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1614   g_return_if_fail (context->is_source);
1615   g_return_if_fail (GTK_IS_WIDGET (widget));
1616
1617   g_warning ("gtk_drag_set_icon_widget is not supported on Mac OS X");
1618 }
1619
1620 static void
1621 set_icon_stock_pixbuf (GdkDragContext    *context,
1622                        const gchar       *stock_id,
1623                        GdkPixbuf         *pixbuf,
1624                        gint               hot_x,
1625                        gint               hot_y)
1626 {
1627   GtkDragSourceInfo *info;
1628
1629   info = gtk_drag_get_source_info (context, FALSE);
1630
1631   if (stock_id)
1632     {
1633       pixbuf = gtk_widget_render_icon (info->widget, stock_id,
1634                                        GTK_ICON_SIZE_DND, NULL);
1635
1636       if (!pixbuf)
1637         {
1638           g_warning ("Cannot load drag icon from stock_id %s", stock_id);
1639           return;
1640         }
1641     }
1642   else
1643     g_object_ref (pixbuf);
1644
1645   if (info->icon_pixbuf)
1646     g_object_unref (info->icon_pixbuf);
1647   info->icon_pixbuf = pixbuf;
1648   info->hot_x = hot_x;
1649   info->hot_y = hot_y;
1650 }
1651
1652 /**
1653  * gtk_drag_set_icon_pixbuf:
1654  * @context: the context for a drag. (This must be called 
1655  *            with a  context for the source side of a drag)
1656  * @pixbuf: the #GdkPixbuf to use as the drag icon.
1657  * @hot_x: the X offset within @widget of the hotspot.
1658  * @hot_y: the Y offset within @widget of the hotspot.
1659  * 
1660  * Sets @pixbuf as the icon for a given drag.
1661  **/
1662 void 
1663 gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
1664                            GdkPixbuf      *pixbuf,
1665                            gint            hot_x,
1666                            gint            hot_y)
1667 {
1668   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1669   g_return_if_fail (context->is_source);
1670   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1671
1672   set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
1673 }
1674
1675 /**
1676  * gtk_drag_set_icon_stock:
1677  * @context: the context for a drag. (This must be called 
1678  *            with a  context for the source side of a drag)
1679  * @stock_id: the ID of the stock icon to use for the drag.
1680  * @hot_x: the X offset within the icon of the hotspot.
1681  * @hot_y: the Y offset within the icon of the hotspot.
1682  * 
1683  * Sets the icon for a given drag from a stock ID.
1684  **/
1685 void 
1686 gtk_drag_set_icon_stock  (GdkDragContext *context,
1687                           const gchar    *stock_id,
1688                           gint            hot_x,
1689                           gint            hot_y)
1690 {
1691
1692   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1693   g_return_if_fail (context->is_source);
1694   g_return_if_fail (stock_id != NULL);
1695
1696   set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
1697 }
1698
1699 /**
1700  * gtk_drag_set_icon_pixmap:
1701  * @context: the context for a drag. (This must be called 
1702  *            with a  context for the source side of a drag)
1703  * @colormap: the colormap of the icon 
1704  * @pixmap: the image data for the icon 
1705  * @mask: the transparency mask for the icon
1706  * @hot_x: the X offset within @pixmap of the hotspot.
1707  * @hot_y: the Y offset within @pixmap of the hotspot.
1708  * 
1709  * Sets @pixmap as the icon for a given drag. GTK+ retains
1710  * references for the arguments, and will release them when
1711  * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
1712  * will be more convenient to use.
1713  **/
1714 void 
1715 gtk_drag_set_icon_pixmap (GdkDragContext    *context,
1716                           GdkColormap       *colormap,
1717                           GdkPixmap         *pixmap,
1718                           GdkBitmap         *mask,
1719                           gint               hot_x,
1720                           gint               hot_y)
1721 {
1722   GdkPixbuf *pixbuf;
1723
1724   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1725   g_return_if_fail (context->is_source);
1726   g_return_if_fail (GDK_IS_COLORMAP (colormap));
1727   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
1728
1729   pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, colormap,
1730                                          0, 0, /* src */
1731                                          0, 0, /* dst */
1732                                          -1, -1);
1733
1734   gtk_drag_set_icon_pixbuf (context, pixbuf, hot_x, hot_y);
1735   g_object_unref (pixbuf);
1736 }
1737
1738 /**
1739  * gtk_drag_set_icon_name:
1740  * @context: the context for a drag. (This must be called 
1741  *            with a context for the source side of a drag)
1742  * @icon_name: name of icon to use
1743  * @hot_x: the X offset of the hotspot within the icon
1744  * @hot_y: the Y offset of the hotspot within the icon
1745  * 
1746  * Sets the icon for a given drag from a named themed icon. See
1747  * the docs for #GtkIconTheme for more details. Note that the
1748  * size of the icon depends on the icon theme (the icon is
1749  * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus 
1750  * @hot_x and @hot_y have to be used with care.
1751  *
1752  * Since: 2.8
1753  **/
1754 void 
1755 gtk_drag_set_icon_name (GdkDragContext *context,
1756                         const gchar    *icon_name,
1757                         gint            hot_x,
1758                         gint            hot_y)
1759 {
1760   GdkScreen *screen;
1761   GtkSettings *settings;
1762   GtkIconTheme *icon_theme;
1763   GdkPixbuf *pixbuf;
1764   gint width, height, icon_size;
1765
1766   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1767   g_return_if_fail (context->is_source);
1768   g_return_if_fail (icon_name != NULL);
1769
1770   screen = gdk_drawable_get_screen (context->source_window);
1771   g_return_if_fail (screen != NULL);
1772
1773   settings = gtk_settings_get_for_screen (screen);
1774   if (gtk_icon_size_lookup_for_settings (settings,
1775                                          GTK_ICON_SIZE_DND,
1776                                          &width, &height))
1777     icon_size = MAX (width, height);
1778   else 
1779     icon_size = 32; /* default value for GTK_ICON_SIZE_DND */ 
1780
1781   icon_theme = gtk_icon_theme_get_for_screen (screen);
1782
1783   pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name,
1784                                      icon_size, 0, NULL);
1785   if (pixbuf)
1786     set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
1787   else
1788     g_warning ("Cannot load drag icon from icon name %s", icon_name);
1789 }
1790
1791 /**
1792  * gtk_drag_set_icon_default:
1793  * @context: the context for a drag. (This must be called 
1794              with a  context for the source side of a drag)
1795  * 
1796  * Sets the icon for a particular drag to the default
1797  * icon.
1798  **/
1799 void 
1800 gtk_drag_set_icon_default (GdkDragContext    *context)
1801 {
1802   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1803   g_return_if_fail (context->is_source);
1804
1805   gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
1806 }
1807
1808 void 
1809 gtk_drag_set_default_icon (GdkColormap   *colormap,
1810                            GdkPixmap     *pixmap,
1811                            GdkBitmap     *mask,
1812                            gint           hot_x,
1813                            gint           hot_y)
1814 {
1815   g_warning ("gtk_drag_set_default_icon is not supported on Mac OS X.");
1816 }
1817
1818 static void
1819 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
1820 {
1821   if (info->icon_pixbuf)
1822     g_object_unref (info->icon_pixbuf);
1823
1824   g_signal_emit_by_name (info->widget, "drag-end", 
1825                          info->context);
1826
1827   if (info->source_widget)
1828     g_object_unref (info->source_widget);
1829
1830   if (info->widget)
1831     g_object_unref (info->widget);
1832
1833   gtk_target_list_unref (info->target_list);
1834
1835   gtk_drag_clear_source_info (info->context);
1836   g_object_unref (info->context);
1837
1838   g_free (info);
1839 }
1840
1841 static gboolean
1842 drag_drop_finished_idle_cb (gpointer data)
1843 {
1844   gtk_drag_source_info_destroy (data);
1845   return FALSE;
1846 }
1847
1848 static void
1849 gtk_drag_drop_finished (GtkDragSourceInfo *info)
1850 {
1851   if (info->success && info->delete)
1852     g_signal_emit_by_name (info->source_widget, "drag-data-delete",
1853                            info->context);
1854
1855   /* Workaround for the fact that the NS API blocks until the drag is
1856    * over. This way the context is still valid when returning from
1857    * drag_begin, even if it will still be quite useless. See bug #501588.
1858   */
1859   g_idle_add (drag_drop_finished_idle_cb, info);
1860 }
1861
1862 /*************************************************************
1863  * _gtk_drag_source_handle_event:
1864  *     Called from widget event handling code on Drag events
1865  *     for drag sources.
1866  *
1867  *   arguments:
1868  *     toplevel: Toplevel widget that received the event
1869  *     event:
1870  *   results:
1871  *************************************************************/
1872
1873 void
1874 _gtk_drag_source_handle_event (GtkWidget *widget,
1875                                GdkEvent  *event)
1876 {
1877   GtkDragSourceInfo *info;
1878   GdkDragContext *context;
1879
1880   g_return_if_fail (widget != NULL);
1881   g_return_if_fail (event != NULL);
1882
1883   context = event->dnd.context;
1884   info = gtk_drag_get_source_info (context, FALSE);
1885   if (!info)
1886     return;
1887
1888   switch (event->type)
1889     {
1890     case GDK_DROP_FINISHED:
1891       gtk_drag_drop_finished (info);
1892       break;
1893     default:
1894       g_assert_not_reached ();
1895     }  
1896 }
1897
1898
1899 gboolean
1900 gtk_drag_check_threshold (GtkWidget *widget,
1901                           gint       start_x,
1902                           gint       start_y,
1903                           gint       current_x,
1904                           gint       current_y)
1905 {
1906   gint drag_threshold;
1907
1908   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1909
1910   g_object_get (gtk_widget_get_settings (widget),
1911                 "gtk-dnd-drag-threshold", &drag_threshold,
1912                 NULL);
1913   
1914   return (ABS (current_x - start_x) > drag_threshold ||
1915           ABS (current_y - start_y) > drag_threshold);
1916 }
1917
1918 #define __GTK_DND_C__
1919 #include "gtkaliasdef.c"