]> Pileus Git - ~andy/gtk/blob - gtk/gtkdnd-quartz.c
Reindent.
[~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_set_track_motion (GtkWidget *widget,
850                                 gboolean   track_motion)
851 {
852   g_return_if_fail (GTK_IS_WIDGET (widget));
853
854   /* FIXME: Implement */
855 }
856
857 gboolean
858 gtk_drag_dest_get_track_motion (GtkWidget *widget)
859 {
860   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
861
862   /* FIXME: Implement */
863
864   return FALSE;
865 }
866
867 void
868 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
869                              GdkEvent  *event)
870 {
871   GtkDragDestInfo *info;
872   GdkDragContext *context;
873
874   g_return_if_fail (toplevel != NULL);
875   g_return_if_fail (event != NULL);
876
877   context = event->dnd.context;
878
879   info = gtk_drag_get_dest_info (context, TRUE);
880
881   /* Find the widget for the event */
882   switch (event->type)
883     {
884     case GDK_DRAG_ENTER:
885       break;
886
887     case GDK_DRAG_LEAVE:
888       if (info->widget)
889         {
890           gtk_drag_dest_leave (info->widget, context, event->dnd.time);
891           info->widget = NULL;
892         }
893       break;
894
895     case GDK_DRAG_MOTION:
896     case GDK_DROP_START:
897       {
898         GtkDragFindData data;
899         gint tx, ty;
900
901         if (event->type == GDK_DROP_START)
902           {
903             info->dropped = TRUE;
904             /* We send a leave here so that the widget unhighlights
905              * properly.
906              */
907             if (info->widget)
908               {
909                 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
910                 info->widget = NULL;
911               }
912           }
913
914         gdk_window_get_position (toplevel->window, &tx, &ty);
915         
916         data.x = event->dnd.x_root - tx;
917         data.y = event->dnd.y_root - ty;
918         data.context = context;
919         data.info = info;
920         data.found = FALSE;
921         data.toplevel = TRUE;
922         data.callback = (event->type == GDK_DRAG_MOTION) ?
923           gtk_drag_dest_motion : gtk_drag_dest_drop;
924         data.time = event->dnd.time;
925         
926         gtk_drag_find_widget (toplevel, &data);
927
928         if (info->widget && !data.found)
929           {
930             gtk_drag_dest_leave (info->widget, context, event->dnd.time);
931             info->widget = NULL;
932           }
933
934         /* Send a reply.
935          */
936         if (event->type == GDK_DRAG_MOTION)
937           {
938             if (!data.found)
939               gdk_drag_status (context, 0, event->dnd.time);
940           }
941
942         break;
943       default:
944         g_assert_not_reached ();
945       }
946     }
947 }
948
949
950 GdkAtom
951 gtk_drag_dest_find_target (GtkWidget      *widget,
952                            GdkDragContext *context,
953                            GtkTargetList  *target_list)
954 {
955   id <NSDraggingInfo> dragging_info = GDK_DRAG_CONTEXT_PRIVATE (context)->dragging_info;
956   NSPasteboard *pasteboard = [dragging_info draggingPasteboard];
957   GtkWidget *source_widget;
958   GList *tmp_target;
959   GList *tmp_source = NULL;
960   GList *source_targets;
961
962   g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
963   g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
964   g_return_val_if_fail (!context->is_source, GDK_NONE);
965
966   source_widget = gtk_drag_get_source_widget (context);
967
968   if (target_list == NULL)
969     target_list = gtk_drag_dest_get_target_list (widget);
970   
971   if (target_list == NULL)
972     return GDK_NONE;
973
974   source_targets = _gtk_quartz_pasteboard_types_to_atom_list ([pasteboard types]);
975   tmp_target = target_list->list;
976   while (tmp_target)
977     {
978       GtkTargetPair *pair = tmp_target->data;
979       tmp_source = source_targets;
980       while (tmp_source)
981         {
982           if (tmp_source->data == GUINT_TO_POINTER (pair->target))
983             {
984               if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
985                   (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
986                 {
987                   g_list_free (source_targets);
988                   return pair->target;
989                 }
990               else
991                 break;
992             }
993           tmp_source = tmp_source->next;
994         }
995       tmp_target = tmp_target->next;
996     }
997
998   g_list_free (source_targets);
999   return GDK_NONE;
1000 }
1001
1002 static GdkDragContext *
1003 gtk_drag_begin_internal (GtkWidget         *widget,
1004                          GtkDragSourceSite *site,
1005                          GtkTargetList     *target_list,
1006                          GdkDragAction      actions,
1007                          gint               button,
1008                          GdkEvent          *event)
1009 {
1010   GtkDragSourceInfo *info;
1011   GdkDragContext *context;
1012   NSWindow *nswindow;
1013   NSPasteboard *pasteboard;
1014   GtkDragSourceOwner *owner;
1015   NSEvent *nsevent;
1016   NSPoint point;
1017
1018   context = gdk_drag_begin (NULL, NULL);
1019   context->is_source = TRUE;
1020
1021   info = gtk_drag_get_source_info (context, TRUE);
1022   
1023   info->widget = gtk_widget_ref (widget);
1024   info->target_list = target_list;
1025   gtk_target_list_ref (target_list);
1026
1027   info->possible_actions = actions;
1028   
1029   g_signal_emit_by_name (widget, "drag_begin", info->context);
1030
1031   /* Ensure that we have an icon before we start the drag; the
1032    * application may have set one in ::drag_begin, or it may
1033    * not have set one.
1034    */
1035   if (!info->icon_pixbuf)
1036     {
1037       if (!site || site->icon_type == GTK_IMAGE_EMPTY)
1038         gtk_drag_set_icon_default (context);
1039       else
1040         switch (site->icon_type)
1041           {
1042           case GTK_IMAGE_PIXMAP:
1043             /* This is not supported, so just set a small transparent pixbuf
1044              * since we need to have something.
1045              */
1046             if (0)
1047               gtk_drag_set_icon_pixmap (context,
1048                                         site->colormap,
1049                                         site->icon_data.pixmap.pixmap,
1050                                         site->icon_mask,
1051                                         -2, -2);
1052             else
1053               {
1054                 GdkPixbuf *pixbuf;
1055
1056                 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 1, 1);
1057                 gdk_pixbuf_fill (pixbuf, 0xffffff);
1058             
1059                 gtk_drag_set_icon_pixbuf (context,
1060                                           pixbuf,
1061                                           0, 0);
1062
1063                 g_object_unref (pixbuf);
1064               }
1065             break;
1066           case GTK_IMAGE_PIXBUF:
1067             gtk_drag_set_icon_pixbuf (context,
1068                                       site->icon_data.pixbuf.pixbuf,
1069                                       -2, -2);
1070             break;
1071           case GTK_IMAGE_STOCK:
1072             gtk_drag_set_icon_stock (context,
1073                                      site->icon_data.stock.stock_id,
1074                                      -2, -2);
1075             break;
1076           case GTK_IMAGE_ICON_NAME:
1077             gtk_drag_set_icon_name (context,
1078                                     site->icon_data.name.icon_name,
1079                                     -2, -2);
1080             break;
1081           case GTK_IMAGE_EMPTY:
1082           default:
1083             g_assert_not_reached();
1084             break;
1085           }
1086     }
1087
1088   gdk_pointer_ungrab (0);
1089   
1090   pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1091   owner = [[GtkDragSourceOwner alloc] initWithInfo:info];
1092
1093   [pasteboard declareTypes:_gtk_quartz_target_list_to_pasteboard_types (target_list) owner:owner];
1094
1095   /* Ref the context. It's unreffed when the drag has been aborted */
1096   g_object_ref (info->context);
1097
1098   nswindow = get_toplevel_nswindow (widget);
1099
1100   /* FIXME: If the event isn't a mouse event, use the global cursor position instead */
1101   nsevent = [nswindow currentEvent];
1102   point = [nsevent locationInWindow];
1103
1104   [nswindow dragImage:_gtk_quartz_create_image_from_pixbuf (info->icon_pixbuf)
1105                    at:point
1106                offset:NSMakeSize(0, 0)
1107                 event:nsevent
1108            pasteboard:pasteboard
1109                source:nswindow
1110             slideBack:YES];
1111
1112   return info->context;
1113 }
1114
1115 GdkDragContext *
1116 gtk_drag_begin (GtkWidget         *widget,
1117                 GtkTargetList     *targets,
1118                 GdkDragAction      actions,
1119                 gint               button,
1120                 GdkEvent          *event)
1121 {
1122   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1123   g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1124   g_return_val_if_fail (targets != NULL, NULL);
1125
1126   return gtk_drag_begin_internal (widget, NULL, targets,
1127                                   actions, button, event);
1128 }
1129
1130
1131 static gboolean
1132 gtk_drag_source_event_cb (GtkWidget      *widget,
1133                           GdkEvent       *event,
1134                           gpointer        data)
1135 {
1136   GtkDragSourceSite *site;
1137   gboolean retval = FALSE;
1138   site = (GtkDragSourceSite *)data;
1139
1140   switch (event->type)
1141     {
1142     case GDK_BUTTON_PRESS:
1143       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
1144         {
1145           site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
1146           site->x = event->button.x;
1147           site->y = event->button.y;
1148         }
1149       break;
1150       
1151     case GDK_BUTTON_RELEASE:
1152       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
1153         site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
1154       break;
1155       
1156     case GDK_MOTION_NOTIFY:
1157       if (site->state & event->motion.state & site->start_button_mask)
1158         {
1159           /* FIXME: This is really broken and can leave us
1160            * with a stuck grab
1161            */
1162           int i;
1163           for (i=1; i<6; i++)
1164             {
1165               if (site->state & event->motion.state & 
1166                   GDK_BUTTON1_MASK << (i - 1))
1167                 break;
1168             }
1169
1170           if (gtk_drag_check_threshold (widget, site->x, site->y,
1171                                         event->motion.x, event->motion.y))
1172             {
1173               site->state = 0;
1174               gtk_drag_begin_internal (widget, site, site->target_list,
1175                                        site->actions, 
1176                                        i, event);
1177
1178               retval = TRUE;
1179             }
1180         }
1181       break;
1182       
1183     default:                    /* hit for 2/3BUTTON_PRESS */
1184       break;
1185     }
1186   
1187   return retval;
1188 }
1189
1190 void 
1191 gtk_drag_source_set (GtkWidget            *widget,
1192                      GdkModifierType       start_button_mask,
1193                      const GtkTargetEntry *targets,
1194                      gint                  n_targets,
1195                      GdkDragAction         actions)
1196 {
1197   GtkDragSourceSite *site;
1198
1199   g_return_if_fail (GTK_IS_WIDGET (widget));
1200
1201   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1202
1203   gtk_widget_add_events (widget,
1204                          gtk_widget_get_events (widget) |
1205                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1206                          GDK_BUTTON_MOTION_MASK);
1207
1208   if (site)
1209     {
1210       if (site->target_list)
1211         gtk_target_list_unref (site->target_list);
1212     }
1213   else
1214     {
1215       site = g_new0 (GtkDragSourceSite, 1);
1216
1217       site->icon_type = GTK_IMAGE_EMPTY;
1218       
1219       g_signal_connect (widget, "button_press_event",
1220                         G_CALLBACK (gtk_drag_source_event_cb),
1221                         site);
1222       g_signal_connect (widget, "button_release_event",
1223                         G_CALLBACK (gtk_drag_source_event_cb),
1224                         site);
1225       g_signal_connect (widget, "motion_notify_event",
1226                         G_CALLBACK (gtk_drag_source_event_cb),
1227                         site);
1228       
1229       g_object_set_data_full (G_OBJECT (widget),
1230                               I_("gtk-site-data"), 
1231                               site, gtk_drag_source_site_destroy);
1232     }
1233
1234   site->start_button_mask = start_button_mask;
1235
1236   site->target_list = gtk_target_list_new (targets, n_targets);
1237
1238   site->actions = actions;
1239 }
1240
1241 /*************************************************************
1242  * gtk_drag_source_unset
1243  *     Unregister this widget as a drag source.
1244  *   arguments:
1245  *     widget:
1246  *   results:
1247  *************************************************************/
1248
1249 void 
1250 gtk_drag_source_unset (GtkWidget *widget)
1251 {
1252   GtkDragSourceSite *site;
1253
1254   g_return_if_fail (GTK_IS_WIDGET (widget));
1255
1256   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1257
1258   if (site)
1259     {
1260       g_signal_handlers_disconnect_by_func (widget,
1261                                             gtk_drag_source_event_cb,
1262                                             site);
1263       g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
1264     }
1265 }
1266
1267 GtkTargetList *
1268 gtk_drag_source_get_target_list (GtkWidget *widget)
1269 {
1270   GtkDragSourceSite *site;
1271
1272   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1273
1274   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1275
1276   return site ? site->target_list : NULL;
1277
1278 }
1279
1280 void
1281 gtk_drag_source_set_target_list (GtkWidget     *widget,
1282                                  GtkTargetList *target_list)
1283 {
1284   GtkDragSourceSite *site;
1285
1286   g_return_if_fail (GTK_IS_WIDGET (widget));
1287
1288   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1289   if (site == NULL)
1290     {
1291       g_warning ("gtk_drag_source_set_target_list() requires the widget "
1292                  "to already be a drag source.");
1293       return;
1294     }
1295
1296   if (target_list)
1297     gtk_target_list_ref (target_list);
1298
1299   if (site->target_list)
1300     gtk_target_list_unref (site->target_list);
1301
1302   site->target_list = target_list;
1303 }
1304
1305 /**
1306  * gtk_drag_source_add_text_targets:
1307  * @widget: a #GtkWidget that's is a drag source
1308  *
1309  * Add the text targets supported by #GtkSelection to
1310  * the target list of the drag source.  The targets
1311  * are added with @info = 0. If you need another value, 
1312  * use gtk_target_list_add_text_targets() and
1313  * gtk_drag_source_set_target_list().
1314  * 
1315  * Since: 2.6
1316  **/
1317 void
1318 gtk_drag_source_add_text_targets (GtkWidget *widget)
1319 {
1320   GtkTargetList *target_list;
1321
1322   target_list = gtk_drag_source_get_target_list (widget);
1323   if (target_list)
1324     gtk_target_list_ref (target_list);
1325   else
1326     target_list = gtk_target_list_new (NULL, 0);
1327   gtk_target_list_add_text_targets (target_list, 0);
1328   gtk_drag_source_set_target_list (widget, target_list);
1329   gtk_target_list_unref (target_list);
1330 }
1331
1332 void
1333 gtk_drag_source_add_image_targets (GtkWidget *widget)
1334 {
1335   GtkTargetList *target_list;
1336
1337   target_list = gtk_drag_source_get_target_list (widget);
1338   if (target_list)
1339     gtk_target_list_ref (target_list);
1340   else
1341     target_list = gtk_target_list_new (NULL, 0);
1342   gtk_target_list_add_image_targets (target_list, 0, TRUE);
1343   gtk_drag_source_set_target_list (widget, target_list);
1344   gtk_target_list_unref (target_list);
1345 }
1346
1347 void
1348 gtk_drag_source_add_uri_targets (GtkWidget *widget)
1349 {
1350   GtkTargetList *target_list;
1351
1352   target_list = gtk_drag_source_get_target_list (widget);
1353   if (target_list)
1354     gtk_target_list_ref (target_list);
1355   else
1356     target_list = gtk_target_list_new (NULL, 0);
1357   gtk_target_list_add_uri_targets (target_list, 0);
1358   gtk_drag_source_set_target_list (widget, target_list);
1359   gtk_target_list_unref (target_list);
1360 }
1361
1362 static void
1363 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
1364 {
1365   switch (site->icon_type)
1366     {
1367     case GTK_IMAGE_EMPTY:
1368       break;
1369     case GTK_IMAGE_PIXMAP:
1370       if (site->icon_data.pixmap.pixmap)
1371         g_object_unref (site->icon_data.pixmap.pixmap);
1372       if (site->icon_mask)
1373         g_object_unref (site->icon_mask);
1374       break;
1375     case GTK_IMAGE_PIXBUF:
1376       g_object_unref (site->icon_data.pixbuf.pixbuf);
1377       break;
1378     case GTK_IMAGE_STOCK:
1379       g_free (site->icon_data.stock.stock_id);
1380       break;
1381     case GTK_IMAGE_ICON_NAME:
1382       g_free (site->icon_data.name.icon_name);
1383       break;
1384     default:
1385       g_assert_not_reached();
1386       break;
1387     }
1388   site->icon_type = GTK_IMAGE_EMPTY;
1389   
1390   if (site->colormap)
1391     g_object_unref (site->colormap);
1392   site->colormap = NULL;
1393 }
1394
1395 static void 
1396 gtk_drag_source_site_destroy (gpointer data)
1397 {
1398   GtkDragSourceSite *site = data;
1399
1400   if (site->target_list)
1401     gtk_target_list_unref (site->target_list);
1402
1403   gtk_drag_source_unset_icon (site);
1404   g_free (site);
1405 }
1406
1407 void 
1408 gtk_drag_source_set_icon (GtkWidget     *widget,
1409                           GdkColormap   *colormap,
1410                           GdkPixmap     *pixmap,
1411                           GdkBitmap     *mask)
1412 {
1413   GtkDragSourceSite *site;
1414
1415   g_return_if_fail (GTK_IS_WIDGET (widget));
1416   g_return_if_fail (GDK_IS_COLORMAP (colormap));
1417   g_return_if_fail (GDK_IS_PIXMAP (pixmap));
1418   g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
1419
1420   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1421   g_return_if_fail (site != NULL);
1422   
1423   g_object_ref (colormap);
1424   g_object_ref (pixmap);
1425   if (mask)
1426     g_object_ref (mask);
1427
1428   gtk_drag_source_unset_icon (site);
1429
1430   site->icon_type = GTK_IMAGE_PIXMAP;
1431   
1432   site->icon_data.pixmap.pixmap = pixmap;
1433   site->icon_mask = mask;
1434   site->colormap = colormap;
1435 }
1436
1437 void 
1438 gtk_drag_source_set_icon_pixbuf (GtkWidget   *widget,
1439                                  GdkPixbuf   *pixbuf)
1440 {
1441   GtkDragSourceSite *site;
1442
1443   g_return_if_fail (GTK_IS_WIDGET (widget));
1444   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1445
1446   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1447   g_return_if_fail (site != NULL); 
1448   g_object_ref (pixbuf);
1449
1450   gtk_drag_source_unset_icon (site);
1451
1452   site->icon_type = GTK_IMAGE_PIXBUF;
1453   site->icon_data.pixbuf.pixbuf = pixbuf;
1454 }
1455
1456 /**
1457  * gtk_drag_source_set_icon_stock:
1458  * @widget: a #GtkWidget
1459  * @stock_id: the ID of the stock icon to use
1460  *
1461  * Sets the icon that will be used for drags from a particular source
1462  * to a stock icon. 
1463  **/
1464 void 
1465 gtk_drag_source_set_icon_stock (GtkWidget   *widget,
1466                                 const gchar *stock_id)
1467 {
1468   GtkDragSourceSite *site;
1469
1470   g_return_if_fail (GTK_IS_WIDGET (widget));
1471   g_return_if_fail (stock_id != NULL);
1472
1473   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1474   g_return_if_fail (site != NULL);
1475   
1476   gtk_drag_source_unset_icon (site);
1477
1478   site->icon_type = GTK_IMAGE_STOCK;
1479   site->icon_data.stock.stock_id = g_strdup (stock_id);
1480 }
1481
1482 /**
1483  * gtk_drag_source_set_icon_name:
1484  * @widget: a #GtkWidget
1485  * @icon_name: name of icon to use
1486  * 
1487  * Sets the icon that will be used for drags from a particular source
1488  * to a themed icon. See the docs for #GtkIconTheme for more details.
1489  *
1490  * Since: 2.8
1491  **/
1492 void 
1493 gtk_drag_source_set_icon_name (GtkWidget   *widget,
1494                                const gchar *icon_name)
1495 {
1496   GtkDragSourceSite *site;
1497
1498   g_return_if_fail (GTK_IS_WIDGET (widget));
1499   g_return_if_fail (icon_name != NULL);
1500
1501   site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1502   g_return_if_fail (site != NULL);
1503
1504   gtk_drag_source_unset_icon (site);
1505
1506   site->icon_type = GTK_IMAGE_ICON_NAME;
1507   site->icon_data.name.icon_name = g_strdup (icon_name);
1508 }
1509
1510
1511 /**
1512  * gtk_drag_set_icon_widget:
1513  * @context: the context for a drag. (This must be called 
1514           with a  context for the source side of a drag)
1515  * @widget: a toplevel window to use as an icon.
1516  * @hot_x: the X offset within @widget of the hotspot.
1517  * @hot_y: the Y offset within @widget of the hotspot.
1518  * 
1519  * Changes the icon for a widget to a given widget. GTK+
1520  * will not destroy the icon, so if you don't want
1521  * it to persist, you should connect to the "drag_end" 
1522  * signal and destroy it yourself.
1523  **/
1524 void 
1525 gtk_drag_set_icon_widget (GdkDragContext    *context,
1526                           GtkWidget         *widget,
1527                           gint               hot_x,
1528                           gint               hot_y)
1529 {
1530   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1531   g_return_if_fail (context->is_source);
1532   g_return_if_fail (GTK_IS_WIDGET (widget));
1533
1534   g_warning ("gtk_drag_set_icon_widget is not supported on Mac OS X");
1535 }
1536
1537 static void
1538 set_icon_stock_pixbuf (GdkDragContext    *context,
1539                        const gchar       *stock_id,
1540                        GdkPixbuf         *pixbuf,
1541                        gint               hot_x,
1542                        gint               hot_y)
1543 {
1544   GtkDragSourceInfo *info;
1545
1546   info = gtk_drag_get_source_info (context, FALSE);
1547
1548   if (stock_id)
1549     {
1550       pixbuf = gtk_widget_render_icon (info->widget, stock_id,
1551                                        GTK_ICON_SIZE_DND, NULL);
1552
1553       if (!pixbuf)
1554         {
1555           g_warning ("Cannot load drag icon from stock_id %s", stock_id);
1556           return;
1557         }
1558     }
1559   else
1560     g_object_ref (pixbuf);
1561
1562   if (info->icon_pixbuf)
1563     g_object_unref (info->icon_pixbuf);
1564   info->icon_pixbuf = pixbuf;
1565   info->hot_x = hot_x;
1566   info->hot_y = hot_y;
1567 }
1568
1569 /**
1570  * gtk_drag_set_icon_pixbuf:
1571  * @context: the context for a drag. (This must be called 
1572  *            with a  context for the source side of a drag)
1573  * @pixbuf: the #GdkPixbuf to use as the drag icon.
1574  * @hot_x: the X offset within @widget of the hotspot.
1575  * @hot_y: the Y offset within @widget of the hotspot.
1576  * 
1577  * Sets @pixbuf as the icon for a given drag.
1578  **/
1579 void 
1580 gtk_drag_set_icon_pixbuf  (GdkDragContext *context,
1581                            GdkPixbuf      *pixbuf,
1582                            gint            hot_x,
1583                            gint            hot_y)
1584 {
1585   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1586   g_return_if_fail (context->is_source);
1587   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1588
1589   set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
1590 }
1591
1592 /**
1593  * gtk_drag_set_icon_stock:
1594  * @context: the context for a drag. (This must be called 
1595  *            with a  context for the source side of a drag)
1596  * @stock_id: the ID of the stock icon to use for the drag.
1597  * @hot_x: the X offset within the icon of the hotspot.
1598  * @hot_y: the Y offset within the icon of the hotspot.
1599  * 
1600  * Sets the icon for a given drag from a stock ID.
1601  **/
1602 void 
1603 gtk_drag_set_icon_stock  (GdkDragContext *context,
1604                           const gchar    *stock_id,
1605                           gint            hot_x,
1606                           gint            hot_y)
1607 {
1608
1609   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1610   g_return_if_fail (context->is_source);
1611   g_return_if_fail (stock_id != NULL);
1612
1613   set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
1614 }
1615
1616 /**
1617  * gtk_drag_set_icon_pixmap:
1618  * @context: the context for a drag. (This must be called 
1619  *            with a  context for the source side of a drag)
1620  * @colormap: the colormap of the icon 
1621  * @pixmap: the image data for the icon 
1622  * @mask: the transparency mask for the icon
1623  * @hot_x: the X offset within @pixmap of the hotspot.
1624  * @hot_y: the Y offset within @pixmap of the hotspot.
1625  * 
1626  * Sets @pixmap as the icon for a given drag. GTK+ retains
1627  * references for the arguments, and will release them when
1628  * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
1629  * will be more convenient to use.
1630  **/
1631 void 
1632 gtk_drag_set_icon_pixmap (GdkDragContext    *context,
1633                           GdkColormap       *colormap,
1634                           GdkPixmap         *pixmap,
1635                           GdkBitmap         *mask,
1636                           gint               hot_x,
1637                           gint               hot_y)
1638 {
1639   g_warning ("gtk_drag_set_icon_pixmap is not supported on Mac OS X");
1640 }
1641
1642 /**
1643  * gtk_drag_set_icon_name:
1644  * @context: the context for a drag. (This must be called 
1645  *            with a context for the source side of a drag)
1646  * @icon_name: name of icon to use
1647  * @hot_x: the X offset of the hotspot within the icon
1648  * @hot_y: the Y offset of the hotspot within the icon
1649  * 
1650  * Sets the icon for a given drag from a named themed icon. See
1651  * the docs for #GtkIconTheme for more details. Note that the
1652  * size of the icon depends on the icon theme (the icon is
1653  * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus 
1654  * @hot_x and @hot_y have to be used with care.
1655  *
1656  * Since: 2.8
1657  **/
1658 void 
1659 gtk_drag_set_icon_name (GdkDragContext *context,
1660                         const gchar    *icon_name,
1661                         gint            hot_x,
1662                         gint            hot_y)
1663 {
1664   GdkScreen *screen;
1665   GtkSettings *settings;
1666   GtkIconTheme *icon_theme;
1667   GdkPixbuf *pixbuf;
1668   gint width, height, icon_size;
1669
1670   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1671   g_return_if_fail (context->is_source);
1672   g_return_if_fail (icon_name != NULL);
1673
1674   screen = gdk_drawable_get_screen (context->source_window);
1675   g_return_if_fail (screen != NULL);
1676
1677   settings = gtk_settings_get_for_screen (screen);
1678   if (gtk_icon_size_lookup_for_settings (settings,
1679                                          GTK_ICON_SIZE_DND,
1680                                          &width, &height))
1681     icon_size = MAX (width, height);
1682   else 
1683     icon_size = 32; /* default value for GTK_ICON_SIZE_DND */ 
1684
1685   icon_theme = gtk_icon_theme_get_for_screen (screen);
1686
1687   pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name,
1688                                      icon_size, 0, NULL);
1689   if (pixbuf)
1690     set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
1691   else
1692     g_warning ("Cannot load drag icon from icon name %s", icon_name);
1693 }
1694
1695 /**
1696  * gtk_drag_set_icon_default:
1697  * @context: the context for a drag. (This must be called 
1698              with a  context for the source side of a drag)
1699  * 
1700  * Sets the icon for a particular drag to the default
1701  * icon.
1702  **/
1703 void 
1704 gtk_drag_set_icon_default (GdkDragContext    *context)
1705 {
1706   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1707   g_return_if_fail (context->is_source);
1708
1709   gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
1710 }
1711
1712 void 
1713 gtk_drag_set_default_icon (GdkColormap   *colormap,
1714                            GdkPixmap     *pixmap,
1715                            GdkBitmap     *mask,
1716                            gint           hot_x,
1717                            gint           hot_y)
1718 {
1719   g_warning ("gtk_drag_set_default_icon is not supported on Mac OS X.");
1720 }
1721
1722 static void
1723 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
1724 {
1725   if (info->icon_pixbuf)
1726     g_object_unref (info->icon_pixbuf);
1727
1728   g_signal_emit_by_name (info->widget, "drag_end", 
1729                          info->context);
1730
1731   if (info->widget)
1732     g_object_unref (info->widget);
1733
1734   gtk_target_list_unref (info->target_list);
1735
1736   gtk_drag_clear_source_info (info->context);
1737   g_object_unref (info->context);
1738
1739   g_free (info);
1740 }
1741
1742 static void
1743 gtk_drag_drop_finished (GtkDragSourceInfo *info)
1744 {
1745   gtk_drag_source_info_destroy (info);
1746 }
1747
1748 /*************************************************************
1749  * _gtk_drag_source_handle_event:
1750  *     Called from widget event handling code on Drag events
1751  *     for drag sources.
1752  *
1753  *   arguments:
1754  *     toplevel: Toplevel widget that received the event
1755  *     event:
1756  *   results:
1757  *************************************************************/
1758
1759 void
1760 _gtk_drag_source_handle_event (GtkWidget *widget,
1761                                GdkEvent  *event)
1762 {
1763   GtkDragSourceInfo *info;
1764   GdkDragContext *context;
1765
1766   g_return_if_fail (widget != NULL);
1767   g_return_if_fail (event != NULL);
1768
1769   context = event->dnd.context;
1770   info = gtk_drag_get_source_info (context, FALSE);
1771   if (!info)
1772     return;
1773
1774   switch (event->type)
1775     {
1776     case GDK_DROP_FINISHED:
1777       gtk_drag_drop_finished (info);
1778       break;
1779     default:
1780       g_assert_not_reached ();
1781     }  
1782 }
1783
1784
1785 gboolean
1786 gtk_drag_check_threshold (GtkWidget *widget,
1787                           gint       start_x,
1788                           gint       start_y,
1789                           gint       current_x,
1790                           gint       current_y)
1791 {
1792   gint drag_threshold;
1793
1794   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1795
1796   g_object_get (gtk_widget_get_settings (widget),
1797                 "gtk-dnd-drag-threshold", &drag_threshold,
1798                 NULL);
1799   
1800   return (ABS (current_x - start_x) > drag_threshold ||
1801           ABS (current_y - start_y) > drag_threshold);
1802 }
1803
1804 #define __GTK_DND_C__
1805 #include "gtkaliasdef.c"