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