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