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