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