]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkdnd-win32.c
Print debugging output only if asked for, not always.
[~andy/gtk] / gdk / win32 / gdkdnd-win32.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 1998-2002 Tor Lillqvist
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
26  */
27
28 #include <config.h>
29 #include <string.h>
30
31 /* #define OLE2_DND */
32
33 #define INITGUID
34
35 #include "gdkdnd.h"
36 #include "gdkproperty.h"
37 #include "gdkinternals.h"
38 #include "gdkprivate-win32.h"
39
40 #ifdef OLE2_DND
41 #include <ole2.h>
42 #else
43 #include <objbase.h>
44 #endif
45
46 #include <shlobj.h>
47 #include <shlguid.h>
48
49 #include <gdk/gdk.h>
50
51 typedef struct _GdkDragContextPrivateWin32 GdkDragContextPrivateWin32;
52
53 typedef enum {
54   GDK_DRAG_STATUS_DRAG,
55   GDK_DRAG_STATUS_MOTION_WAIT,
56   GDK_DRAG_STATUS_ACTION_WAIT,
57   GDK_DRAG_STATUS_DROP
58 } GtkDragStatus;
59
60 typedef enum {
61   GDK_DRAG_SOURCE,
62   GDK_DRAG_TARGET
63 } GdkDragKind;
64
65 #ifdef OLE2_DND
66
67 #define PRINT_GUID(guid) \
68   g_print ("guid = %.08lx-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
69            ((gulong *)  guid)[0], \
70            ((gushort *) guid)[2], \
71            ((gushort *) guid)[3], \
72            ((guchar *)  guid)[8], \
73            ((guchar *)  guid)[9], \
74            ((guchar *)  guid)[10], \
75            ((guchar *)  guid)[11], \
76            ((guchar *)  guid)[12], \
77            ((guchar *)  guid)[13], \
78            ((guchar *)  guid)[14], \
79            ((guchar *)  guid)[15]);
80
81
82 static FORMATETC *formats;
83 static int nformats;
84
85 #endif /* OLE2_DND */
86
87 /* Structure that holds information about a drag in progress.
88  * this is used on both source and destination sides.
89  */
90 struct _GdkDragContextPrivateWin32 {
91 #ifdef OLE2_DND
92   gint    ref_count;
93 #endif
94   guint16 last_x;               /* Coordinates from last event */
95   guint16 last_y;
96   HWND dest_xid;
97   guint drag_status : 4;        /* Current status of drag */
98   guint drop_failed : 1;        /* Whether the drop was unsuccessful */
99 };
100
101 #define GDK_DRAG_CONTEXT_PRIVATE_DATA(context) ((GdkDragContextPrivateWin32 *) GDK_DRAG_CONTEXT (context)->windowing_data)
102
103 static GdkDragContext *current_dest_drag = NULL;
104
105 static void gdk_drag_context_init       (GdkDragContext      *dragcontext);
106 static void gdk_drag_context_class_init (GdkDragContextClass *klass);
107 static void gdk_drag_context_finalize   (GObject              *object);
108
109 static gpointer parent_class = NULL;
110 static GList *contexts;
111
112 GType
113 gdk_drag_context_get_type (void)
114 {
115   static GType object_type = 0;
116
117   if (!object_type)
118     {
119       static const GTypeInfo object_info =
120       {
121         sizeof (GdkDragContextClass),
122         (GBaseInitFunc) NULL,
123         (GBaseFinalizeFunc) NULL,
124         (GClassInitFunc) gdk_drag_context_class_init,
125         NULL,           /* class_finalize */
126         NULL,           /* class_data */
127         sizeof (GdkDragContext),
128         0,              /* n_preallocs */
129         (GInstanceInitFunc) gdk_drag_context_init,
130       };
131       
132       object_type = g_type_register_static (G_TYPE_OBJECT,
133                                             "GdkDragContext",
134                                             &object_info, 0);
135     }
136   
137   return object_type;
138 }
139
140 static void
141 gdk_drag_context_init (GdkDragContext *dragcontext)
142 {
143   GdkDragContextPrivateWin32 *private = g_new0 (GdkDragContextPrivateWin32, 1);
144
145   dragcontext->windowing_data = private;
146 #ifdef OLE2_DND
147   private->ref_count = 1;
148 #endif
149
150   contexts = g_list_prepend (contexts, dragcontext);
151 }
152
153 static void
154 gdk_drag_context_class_init (GdkDragContextClass *klass)
155 {
156   GObjectClass *object_class = G_OBJECT_CLASS (klass);
157
158   parent_class = g_type_class_peek_parent (klass);
159
160   object_class->finalize = gdk_drag_context_finalize;
161 }
162
163 static void
164 gdk_drag_context_finalize (GObject *object)
165 {
166   GdkDragContext *context = GDK_DRAG_CONTEXT (object);
167   GdkDragContextPrivateWin32 *private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
168  
169   GDK_NOTE (DND, g_print ("gdk_drag_context_finalize\n"));
170  
171   g_list_free (context->targets);
172
173   if (context->source_window)
174     {
175       g_object_unref (context->source_window);
176     }
177   
178   if (context->dest_window)
179     g_object_unref (context->dest_window);
180   
181   contexts = g_list_remove (contexts, context);
182
183   if (context == current_dest_drag)
184     current_dest_drag = NULL;
185
186   g_free (private);
187   
188   G_OBJECT_CLASS (parent_class)->finalize (object);
189 }
190
191 /* Drag Contexts */
192
193 GdkDragContext *
194 gdk_drag_context_new (void)
195 {
196   return g_object_new (gdk_drag_context_get_type (), NULL);
197 }
198
199 void
200 gdk_drag_context_ref (GdkDragContext *context)
201 {
202   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
203
204   g_object_ref (context);
205 }
206
207 void
208 gdk_drag_context_unref (GdkDragContext *context)
209 {
210   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
211
212   g_object_unref (context);
213 }
214
215 static GdkDragContext *
216 gdk_drag_context_find (gboolean   is_source,
217                        GdkWindow *source,
218                        GdkWindow *dest)
219 {
220   GList *tmp_list = contexts;
221   GdkDragContext *context;
222   GdkDragContextPrivateWin32 *private;
223
224   while (tmp_list)
225     {
226       context = (GdkDragContext *)tmp_list->data;
227       private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
228
229       if ((!context->is_source == !is_source) &&
230           ((source == NULL) || (context->source_window && (context->source_window == source))) &&
231           ((dest == NULL) || (context->dest_window && (context->dest_window == dest))))
232         return context;
233       
234       tmp_list = tmp_list->next;
235     }
236   
237   return NULL;
238 }
239
240
241 typedef struct {
242 #ifdef OLE2_DND
243   IDropTarget idt;
244 #endif
245   GdkDragContext *context;
246 } target_drag_context;
247
248 typedef struct {
249 #ifdef OLE2_DND
250   IDropSource ids;
251 #endif
252   GdkDragContext *context;
253 } source_drag_context;
254
255 #ifdef OLE2_DND
256
257 typedef struct {
258   IDataObject ido;
259   int ref_count;
260 } data_object;
261
262 typedef struct {
263   IEnumFORMATETC ief;
264   int ref_count;
265   int ix;
266 } enum_formats;
267
268 static enum_formats *enum_formats_new (void);
269
270 static ULONG STDMETHODCALLTYPE
271 idroptarget_addref (LPDROPTARGET This)
272 {
273   target_drag_context *ctx = (target_drag_context *) This;
274   GdkDragContextPrivateWin32 *private = GDK_DRAG_CONTEXT_PRIVATE_DATA (ctx->context);
275   int ref_count = ++private->ref_count;
276
277   gdk_drag_context_ref (ctx->context);
278   GDK_NOTE (DND, g_print ("idroptarget_addref %p %d\n", This, ref_count));
279   
280   return ref_count;
281 }
282
283 static HRESULT STDMETHODCALLTYPE
284 idroptarget_queryinterface (LPDROPTARGET This,
285                             REFIID       riid,
286                             LPVOID      *ppvObject)
287 {
288   GDK_NOTE (DND, g_print ("idroptarget_queryinterface %p\n", This));
289
290   *ppvObject = NULL;
291
292   PRINT_GUID (riid);
293
294   if (IsEqualGUID (riid, &IID_IUnknown))
295     {
296       g_print ("...IUnknown\n");
297       idroptarget_addref (This);
298       *ppvObject = This;
299       return S_OK;
300     }
301   else if (IsEqualGUID (riid, &IID_IDropTarget))
302     {
303       g_print ("...IDropTarget\n");
304       idroptarget_addref (This);
305       *ppvObject = This;
306       return S_OK;
307     }
308   else
309     {
310       g_print ("...Huh?\n");
311       return E_NOINTERFACE;
312     }
313 }
314
315 static ULONG STDMETHODCALLTYPE
316 idroptarget_release (LPDROPTARGET This)
317 {
318   target_drag_context *ctx = (target_drag_context *) This;
319   GdkDragContextPrivateWin32 *private = GDK_DRAG_CONTEXT_PRIVATE_DATA (ctx->context);
320   int ref_count = --private->ref_count;
321
322   gdk_drag_context_unref (ctx->context);
323   GDK_NOTE (DND, g_print ("idroptarget_release %p %d\n", This, ref_count));
324
325   if (ref_count == 0)
326     g_free (This);
327
328   return ref_count;
329 }
330
331 static HRESULT STDMETHODCALLTYPE 
332 idroptarget_dragenter (LPDROPTARGET This,
333                        LPDATAOBJECT pDataObj,
334                        DWORD        grfKeyState,
335                        POINTL       pt,
336                        LPDWORD      pdwEffect)
337 {
338   GDK_NOTE (DND, g_print ("idroptarget_dragenter %p\n", This));
339
340   return E_UNEXPECTED;
341 }
342
343 static HRESULT STDMETHODCALLTYPE
344 idroptarget_dragover (LPDROPTARGET This,
345                       DWORD        grfKeyState,
346                       POINTL       pt,
347                       LPDWORD      pdwEffect)
348 {
349   GDK_NOTE (DND, g_print ("idroptarget_dragover %p\n", This));
350
351   return E_UNEXPECTED;
352 }
353
354 static HRESULT STDMETHODCALLTYPE
355 idroptarget_dragleave (LPDROPTARGET This)
356 {
357   GDK_NOTE (DND, g_print ("idroptarget_dragleave %p\n", This));
358
359   return E_UNEXPECTED;
360 }
361
362 static HRESULT STDMETHODCALLTYPE
363 idroptarget_drop (LPDROPTARGET This,
364                   LPDATAOBJECT pDataObj,
365                   DWORD        grfKeyState,
366                   POINTL       pt,
367                   LPDWORD      pdwEffect)
368 {
369   GDK_NOTE (DND, g_print ("idroptarget_drop %p\n", This));
370
371   return E_UNEXPECTED;
372 }
373
374 static ULONG STDMETHODCALLTYPE
375 idropsource_addref (LPDROPSOURCE This)
376 {
377   source_drag_context *ctx = (source_drag_context *) This;
378   GdkDragContextPrivateWin32 *private = GDK_DRAG_CONTEXT_PRIVATE_DATA (ctx->context);
379
380   gdk_drag_context_ref (ctx->context);
381   GDK_NOTE (DND, g_print ("idropsource_addref %p %d\n",
382                           This, private->ref_count));
383   
384   return private->ref_count;
385 }
386
387 static HRESULT STDMETHODCALLTYPE
388 idropsource_queryinterface (LPDROPSOURCE This,
389                             REFIID       riid,
390                             LPVOID      *ppvObject)
391 {
392   GDK_NOTE (DND, g_print ("idropsource_queryinterface %p\n", This));
393
394   *ppvObject = NULL;
395
396   PRINT_GUID (riid);
397   if (IsEqualGUID (riid, &IID_IUnknown))
398     {
399       g_print ("...IUnknown\n");
400       idropsource_addref (This);
401       *ppvObject = This;
402       return S_OK;
403     }
404   else if (IsEqualGUID (riid, &IID_IDropSource))
405     {
406       g_print ("...IDropSource\n");
407       idropsource_addref (This);
408       *ppvObject = This;
409       return S_OK;
410     }
411   else
412     {
413       g_print ("...Huh?\n");
414       return E_NOINTERFACE;
415     }
416 }
417
418 static ULONG STDMETHODCALLTYPE
419 idropsource_release (LPDROPSOURCE This)
420 {
421   source_drag_context *ctx = (source_drag_context *) This;
422   GdkDragContextPrivateWin32 *private = GDK_DRAG_CONTEXT_PRIVATE_DATA (ctx->context);
423   int ref_count = --private->ref_count;
424
425   gdk_drag_context_unref (ctx->context);
426   GDK_NOTE (DND, g_print ("idropsource_release %p %d\n", This, ref_count));
427
428   if (ref_count == 0)
429     g_free (This);
430
431   return ref_count;
432 }
433
434 static HRESULT STDMETHODCALLTYPE
435 idropsource_querycontinuedrag (LPDROPSOURCE This,
436                                BOOL         fEscapePressed,
437                                DWORD        grfKeyState)
438 {
439   GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %p\n", This));
440
441   return E_UNEXPECTED;
442 }
443
444 static HRESULT STDMETHODCALLTYPE
445 idropsource_givefeedback (LPDROPSOURCE This,
446                           DWORD        dwEffect)
447 {
448   GDK_NOTE (DND, g_print ("idropsource_givefeedback %p\n", This));
449
450   return E_UNEXPECTED;
451 }
452
453 static ULONG STDMETHODCALLTYPE
454 idataobject_addref (LPDATAOBJECT This)
455 {
456   data_object *dobj = (data_object *) This;
457   int ref_count = ++dobj->ref_count;
458
459   GDK_NOTE (DND, g_print ("idataobject_addref %p %d\n", This, ref_count));
460
461   return ref_count;
462 }
463
464 static HRESULT STDMETHODCALLTYPE
465 idataobject_queryinterface (LPDATAOBJECT This,
466                             REFIID       riid,
467                             LPVOID      *ppvObject)
468 {
469   GDK_NOTE (DND, g_print ("idataobject_queryinterface %p\n", This));
470
471   *ppvObject = NULL;
472
473   PRINT_GUID (riid);
474   if (IsEqualGUID (riid, &IID_IUnknown))
475     {
476       g_print ("...IUnknown\n");
477       idataobject_addref (This);
478       *ppvObject = This;
479       return S_OK;
480     }
481   else if (IsEqualGUID (riid, &IID_IDataObject))
482     {
483       g_print ("...IDataObject\n");
484       idataobject_addref (This);
485       *ppvObject = This;
486       return S_OK;
487     }
488   else
489     {
490       g_print ("...Huh?\n");
491       return E_NOINTERFACE;
492     }
493 }
494
495 static ULONG STDMETHODCALLTYPE
496 idataobject_release (LPDATAOBJECT This)
497 {
498   data_object *dobj = (data_object *) This;
499   int ref_count = --dobj->ref_count;
500
501   GDK_NOTE (DND, g_print ("idataobject_release %p %d\n", This, ref_count));
502
503   if (ref_count == 0)
504     g_free (This);
505
506   return ref_count;
507 }
508
509 static HRESULT STDMETHODCALLTYPE
510 idataobject_getdata (LPDATAOBJECT This,
511                      LPFORMATETC  pFormatEtc,
512                      LPSTGMEDIUM  pMedium)
513 {
514   GDK_NOTE (DND, g_print ("idataobject_getdata %p\n", This));
515
516   return E_UNEXPECTED;
517 }
518
519 static HRESULT STDMETHODCALLTYPE
520 idataobject_getdatahere (LPDATAOBJECT This,
521                          LPFORMATETC  pFormatEtc,
522                          LPSTGMEDIUM  pMedium)
523 {
524   GDK_NOTE (DND, g_print ("idataobject_getdatahere %p\n", This));
525
526   return E_UNEXPECTED;
527 }
528
529 static HRESULT STDMETHODCALLTYPE
530 idataobject_querygetdata (LPDATAOBJECT This,
531                           LPFORMATETC  pFormatEtc)
532 {
533   int i;
534
535   GDK_NOTE (DND, g_print ("idataobject_querygetdata %p %#x", This, pFormatEtc->cfFormat));
536
537   for (i = 0; i < nformats; i++)
538     if (pFormatEtc->cfFormat == formats[i].cfFormat)
539       {
540         GDK_NOTE (DND, g_print (" S_OK\n"));
541         return S_OK;
542       }
543
544   GDK_NOTE (DND, g_print (" DV_E_FORMATETC\n"));
545   return DV_E_FORMATETC;
546 }
547
548 static HRESULT STDMETHODCALLTYPE
549 idataobject_getcanonicalformatetc (LPDATAOBJECT This,
550                                    LPFORMATETC  pFormatEtcIn,
551                                    LPFORMATETC  pFormatEtcOut)
552 {
553   GDK_NOTE (DND, g_print ("idataobject_getcanonicalformatetc %p\n", This));
554
555   return E_FAIL;
556 }
557
558 static HRESULT STDMETHODCALLTYPE
559 idataobject_setdata (LPDATAOBJECT This,
560                      LPFORMATETC  pFormatEtc,
561                      LPSTGMEDIUM  pMedium,
562                      BOOL         fRelease)
563 {
564   GDK_NOTE (DND, g_print ("idataobject_setdata %p\n", This));
565
566   return E_UNEXPECTED;
567 }
568
569 static HRESULT STDMETHODCALLTYPE
570 idataobject_enumformatetc (LPDATAOBJECT     This,
571                            DWORD            dwDirection,
572                            LPENUMFORMATETC *ppEnumFormatEtc)
573 {
574   GDK_NOTE (DND, g_print ("idataobject_enumformatetc %p\n", This));
575
576   if (dwDirection != DATADIR_GET)
577     return E_NOTIMPL;
578
579   *ppEnumFormatEtc = &enum_formats_new ()->ief;
580   return S_OK;
581 }
582
583 static HRESULT STDMETHODCALLTYPE
584 idataobject_dadvise (LPDATAOBJECT This,
585                      LPFORMATETC  pFormatetc,
586                      DWORD        advf,
587                      LPADVISESINK pAdvSink,
588                      DWORD       *pdwConnection)
589 {
590   GDK_NOTE (DND, g_print ("idataobject_dadvise %p\n", This));
591
592   return E_FAIL;
593 }
594
595 static HRESULT STDMETHODCALLTYPE
596 idataobject_dunadvise (LPDATAOBJECT This,
597                        DWORD         dwConnection)
598 {
599   GDK_NOTE (DND, g_print ("idataobject_dunadvise %p\n", This));
600
601   return E_FAIL;
602 }
603
604 static HRESULT STDMETHODCALLTYPE
605 idataobject_enumdadvise (LPDATAOBJECT    This,
606                          LPENUMSTATDATA *ppenumAdvise)
607 {
608   GDK_NOTE (DND, g_print ("idataobject_enumdadvise %p\n", This));
609
610   return E_FAIL;
611 }
612                 
613 static ULONG STDMETHODCALLTYPE
614 ienumformatetc_addref (LPENUMFORMATETC This)
615 {
616   enum_formats *en = (enum_formats *) This;
617   int ref_count = ++en->ref_count;
618
619   GDK_NOTE (DND, g_print ("ienumformatetc_addref %p %d\n", This, ref_count));
620
621   return ref_count;
622 }
623
624 static HRESULT STDMETHODCALLTYPE
625 ienumformatetc_queryinterface (LPENUMFORMATETC This,
626                                REFIID          riid,
627                                LPVOID         *ppvObject)
628 {
629   GDK_NOTE (DND, g_print ("ienumformatetc_queryinterface %p\n", This));
630
631   *ppvObject = NULL;
632
633   PRINT_GUID (riid);
634   if (IsEqualGUID (riid, &IID_IUnknown))
635     {
636       g_print ("...IUnknown\n");
637       ienumformatetc_addref (This);
638       *ppvObject = This;
639       return S_OK;
640     }
641   else if (IsEqualGUID (riid, &IID_IEnumFORMATETC))
642     {
643       g_print ("...IEnumFORMATETC\n");
644       ienumformatetc_addref (This);
645       *ppvObject = This;
646       return S_OK;
647     }
648   else
649     {
650       g_print ("...Huh?\n");
651       return E_NOINTERFACE;
652     }
653 }
654
655 static ULONG STDMETHODCALLTYPE
656 ienumformatetc_release (LPENUMFORMATETC This)
657 {
658   enum_formats *en = (enum_formats *) This;
659   int ref_count = --en->ref_count;
660
661   GDK_NOTE (DND, g_print ("ienumformatetc_release %p %d\n", This, ref_count));
662
663   if (ref_count == 0)
664     g_free (This);
665
666   return ref_count;
667 }
668
669 static HRESULT STDMETHODCALLTYPE
670 ienumformatetc_next (LPENUMFORMATETC This,
671                      ULONG           celt,
672                      LPFORMATETC     elts,
673                      ULONG          *nelt)
674 {
675   enum_formats *en = (enum_formats *) This;
676   int i, n;
677
678   GDK_NOTE (DND, g_print ("ienumformatetc_next %p %d %ld\n", This, en->ix, celt));
679
680   n = 0;
681   for (i = 0; i < celt; i++)
682     {
683       if (en->ix >= nformats)
684         break;
685       elts[i] = formats[en->ix++];
686       n++;
687     }
688
689   if (nelt != NULL)
690     *nelt = n;
691
692   if (n == celt)
693     return S_OK;
694   else
695     return S_FALSE;
696 }
697
698 static HRESULT STDMETHODCALLTYPE
699 ienumformatetc_skip (LPENUMFORMATETC This,
700                      ULONG           celt)
701 {
702   enum_formats *en = (enum_formats *) This;
703
704   GDK_NOTE (DND, g_print ("ienumformatetc_skip %p %d %ld\n", This, en->ix, celt));
705   en->ix += celt;
706
707   return S_OK;
708 }
709
710 static HRESULT STDMETHODCALLTYPE
711 ienumformatetc_reset (LPENUMFORMATETC This)
712 {
713   enum_formats *en = (enum_formats *) This;
714
715   GDK_NOTE (DND, g_print ("ienumformatetc_reset %p\n", This));
716
717   en->ix = 0;
718
719   return S_OK;
720 }
721
722 static HRESULT STDMETHODCALLTYPE
723 ienumformatetc_clone (LPENUMFORMATETC  This,
724                       LPENUMFORMATETC *ppEnumFormatEtc)
725 {
726   enum_formats *en = (enum_formats *) This;
727   enum_formats *new;
728
729   GDK_NOTE (DND, g_print ("ienumformatetc_clone %p\n", This));
730
731   new = enum_formats_new ();
732
733   new->ix = en->ix;
734
735   *ppEnumFormatEtc = &new->ief;
736
737   return S_OK;
738 }
739
740 static IDropTargetVtbl idt_vtbl = {
741   idroptarget_queryinterface,
742   idroptarget_addref,
743   idroptarget_release,
744   idroptarget_dragenter,
745   idroptarget_dragover,
746   idroptarget_dragleave,
747   idroptarget_drop
748 };
749
750 static IDropSourceVtbl ids_vtbl = {
751   idropsource_queryinterface,
752   idropsource_addref,
753   idropsource_release,
754   idropsource_querycontinuedrag,
755   idropsource_givefeedback
756 };
757
758 static IDataObjectVtbl ido_vtbl = {
759   idataobject_queryinterface,
760   idataobject_addref,
761   idataobject_release,
762   idataobject_getdata,
763   idataobject_getdatahere,
764   idataobject_querygetdata,
765   idataobject_getcanonicalformatetc,
766   idataobject_setdata,
767   idataobject_enumformatetc,
768   idataobject_dadvise,
769   idataobject_dunadvise,
770   idataobject_enumdadvise
771 };
772
773 static IEnumFORMATETCVtbl ief_vtbl = {
774   ienumformatetc_queryinterface,
775   ienumformatetc_addref,
776   ienumformatetc_release,
777   ienumformatetc_next,
778   ienumformatetc_skip,
779   ienumformatetc_reset,
780   ienumformatetc_clone
781 };
782
783
784 static target_drag_context *
785 target_context_new (void)
786 {
787   target_drag_context *result;
788
789   result = g_new0 (target_drag_context, 1);
790
791   result->idt.lpVtbl = &idt_vtbl;
792
793   result->context = gdk_drag_context_new ();
794   result->context->is_source = FALSE;
795
796   GDK_NOTE (DND, g_print ("target_context_new: %p\n", result));
797
798   return result;
799 }
800
801 static source_drag_context *
802 source_context_new (void)
803 {
804   source_drag_context *result;
805
806   result = g_new0 (source_drag_context, 1);
807
808   result->ids.lpVtbl = &ids_vtbl;
809
810   result->context = gdk_drag_context_new ();
811   result->context->is_source = TRUE;
812
813   GDK_NOTE (DND, g_print ("source_context_new: %p\n", result));
814
815   return result;
816 }
817
818 static data_object *
819 data_object_new (void)
820 {
821   data_object *result;
822
823   result = g_new0 (data_object, 1);
824
825   result->ido.lpVtbl = &ido_vtbl;
826   result->ref_count = 1;
827
828   GDK_NOTE (DND, g_print ("data_object_new: %p\n", result));
829
830   return result;
831 }
832
833
834 static enum_formats *
835 enum_formats_new (void)
836 {
837   enum_formats *result;
838
839   result = g_new0 (enum_formats, 1);
840
841   result->ief.lpVtbl = &ief_vtbl;
842   result->ref_count = 1;
843   result->ix = 0;
844
845   GDK_NOTE (DND, g_print ("enum_formats_new: %p\n", result));
846
847   return result;
848 }
849
850 #endif
851
852 /* From MS Knowledge Base article Q130698 */
853
854 static HRESULT 
855 resolve_link (HWND     hWnd,
856               guchar  *lpszLinkName,
857               guchar **lpszPath)
858 {
859   HRESULT hres;
860   IShellLinkA *pslA = NULL;
861   IShellLinkW *pslW = NULL;
862   IPersistFile *ppf = NULL;
863
864   /* Assume failure to start with: */
865   *lpszPath = 0;
866
867   /* Call CoCreateInstance to obtain the IShellLink interface
868    * pointer. This call fails if CoInitialize is not called, so it is
869    * assumed that CoInitialize has been called.
870    */
871
872   if (G_WIN32_HAVE_WIDECHAR_API ())
873     hres = CoCreateInstance (&CLSID_ShellLink,
874                              NULL,
875                              CLSCTX_INPROC_SERVER,
876                              &IID_IShellLinkW,
877                              (LPVOID *)&pslW);
878   else
879     hres = CoCreateInstance (&CLSID_ShellLink,
880                              NULL,
881                              CLSCTX_INPROC_SERVER,
882                              &IID_IShellLinkA,
883                              (LPVOID *)&pslA);
884
885   if (SUCCEEDED (hres))
886    {
887      
888      /* The IShellLink interface supports the IPersistFile
889       * interface. Get an interface pointer to it.
890       */
891      if (G_WIN32_HAVE_WIDECHAR_API ())
892        hres = pslW->lpVtbl->QueryInterface (pslW,
893                                             &IID_IPersistFile,
894                                             (LPVOID *) &ppf);
895      else
896        hres = pslA->lpVtbl->QueryInterface (pslA,
897                                             &IID_IPersistFile,
898                                             (LPVOID *) &ppf);
899    }     
900
901   if (SUCCEEDED (hres))
902     {
903       /* Convert the given link name string to wide character string. */
904       wchar_t *wsz = g_utf8_to_utf16 (lpszLinkName, -1, NULL, NULL, NULL);
905
906       /* Load the file. */
907       hres = ppf->lpVtbl->Load (ppf, wsz, STGM_READ);
908       g_free (wsz);
909     }
910   
911   if (SUCCEEDED (hres))
912     {
913       /* Resolve the link by calling the Resolve()
914        * interface function.
915        */
916       if (G_WIN32_HAVE_WIDECHAR_API ())
917         hres = pslW->lpVtbl->Resolve (pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
918       else
919         hres = pslA->lpVtbl->Resolve (pslA, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
920     }
921
922   if (SUCCEEDED (hres))
923     {
924       if (G_WIN32_HAVE_WIDECHAR_API ())
925         {
926           wchar_t wtarget[MAX_PATH];
927
928           hres = pslW->lpVtbl->GetPath (pslW, wtarget, MAX_PATH, NULL, 0);
929           if (SUCCEEDED (hres))
930             *lpszPath = g_utf16_to_utf8 (wtarget, -1, NULL, NULL, NULL);
931         }
932       else
933         {
934           guchar cptarget[MAX_PATH];
935
936           hres = pslA->lpVtbl->GetPath (pslA, cptarget, MAX_PATH, NULL, 0);
937           if (SUCCEEDED (hres))
938             *lpszPath = g_locale_to_utf8 (cptarget, -1, NULL, NULL, NULL);
939         }
940     }
941   
942   if (ppf)
943     ppf->lpVtbl->Release (ppf);
944   if (pslW)
945     pslW->lpVtbl->Release (pslW);
946   if (pslA)
947     pslA->lpVtbl->Release (pslA);
948
949   return SUCCEEDED (hres);
950 }
951
952 static GdkFilterReturn
953 gdk_dropfiles_filter (GdkXEvent *xev,
954                       GdkEvent  *event,
955                       gpointer   data)
956 {
957   GdkDragContext *context;
958   GdkDragContextPrivateWin32 *private;
959   GString *result;
960   MSG *msg = (MSG *) xev;
961   HANDLE hdrop;
962   POINT pt;
963   gint nfiles, i;
964   guchar *fileName, *linkedFile;
965   
966   if (msg->message == WM_DROPFILES)
967     {
968       GDK_NOTE (DND, g_print ("WM_DROPFILES: %p\n", msg->hwnd));
969
970       context = gdk_drag_context_new ();
971       private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
972       context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
973       context->is_source = FALSE;
974       context->source_window = _gdk_parent_root;
975       g_object_ref (context->source_window);
976       context->dest_window = event->any.window;
977       g_object_ref (context->dest_window);
978       /* WM_DROPFILES drops are always file names */
979       context->targets =
980         g_list_append (NULL, GUINT_TO_POINTER (_text_uri_list));
981       current_dest_drag = context;
982
983       event->dnd.type = GDK_DROP_START;
984       event->dnd.context = current_dest_drag;
985       
986       hdrop = (HANDLE) msg->wParam;
987       DragQueryPoint (hdrop, &pt);
988       ClientToScreen (msg->hwnd, &pt);
989
990       event->dnd.x_root = pt.x;
991       event->dnd.y_root = pt.y;
992       event->dnd.time = _gdk_win32_get_next_tick (msg->time);
993
994       nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
995
996       result = g_string_new (NULL);
997       for (i = 0; i < nfiles; i++)
998         {
999           gchar *uri;
1000
1001           if (G_WIN32_HAVE_WIDECHAR_API ())
1002             {
1003               wchar_t wfn[MAX_PATH];
1004
1005               DragQueryFileW (hdrop, i, wfn, MAX_PATH);
1006               fileName = g_utf16_to_utf8 (wfn, -1, NULL, NULL, NULL);
1007             }
1008           else
1009             {
1010               char cpfn[MAX_PATH];
1011
1012               DragQueryFileA (hdrop, i, cpfn, MAX_PATH);
1013               fileName = g_locale_to_utf8 (cpfn, -1, NULL, NULL, NULL);
1014             }
1015
1016           /* Resolve shortcuts */
1017           if (resolve_link (msg->hwnd, fileName, &linkedFile))
1018             {
1019               uri = g_filename_to_uri (linkedFile, NULL, NULL);
1020               g_free (linkedFile);
1021               if (uri != NULL)
1022                 {
1023                   g_string_append (result, uri);
1024                   GDK_NOTE (DND, g_print ("... %s link to %s: %s\n",
1025                                           fileName, linkedFile, uri));
1026                   g_free (uri);
1027                 }
1028             }
1029           else
1030             {
1031               uri = g_filename_to_uri (fileName, NULL, NULL);
1032               if (uri != NULL)
1033                 {
1034                   g_string_append (result, uri);
1035                   GDK_NOTE (DND, g_print ("... %s: %s\n", fileName, uri));
1036                   g_free (uri);
1037                 }
1038             }
1039           g_free (fileName);
1040           g_string_append (result, "\015\012");
1041         }
1042       _gdk_dropfiles_store (result->str);
1043       g_string_free (result, FALSE);
1044
1045       DragFinish (hdrop);
1046       
1047       return GDK_FILTER_TRANSLATE;
1048     }
1049   else
1050     return GDK_FILTER_CONTINUE;
1051 }
1052
1053 /*************************************************************
1054  ************************** Public API ***********************
1055  *************************************************************/
1056
1057 void
1058 _gdk_dnd_init (void)
1059 {
1060 #ifdef OLE2_DND
1061   HRESULT hres;
1062   hres = OleInitialize (NULL);
1063
1064   if (! SUCCEEDED (hres))
1065     g_error ("OleInitialize failed");
1066
1067   nformats = 2;
1068   formats = g_new (FORMATETC, nformats);
1069
1070   formats[0].cfFormat = CF_TEXT;
1071   formats[0].ptd = NULL;
1072   formats[0].dwAspect = DVASPECT_CONTENT;
1073   formats[0].lindex = -1;
1074   formats[0].tymed = TYMED_HGLOBAL;
1075   
1076   formats[1].cfFormat = CF_GDIOBJFIRST;
1077   formats[1].ptd = NULL;
1078   formats[1].dwAspect = DVASPECT_CONTENT;
1079   formats[1].lindex = -1;
1080   formats[1].tymed = TYMED_HGLOBAL;
1081 #endif
1082 }      
1083
1084 void
1085 _gdk_win32_dnd_exit (void)
1086 {
1087 #ifdef OLE2_DND
1088   OleUninitialize ();
1089 #endif
1090 }
1091
1092 /* Source side */
1093
1094 static void
1095 local_send_leave (GdkDragContext *context,
1096                   guint32         time)
1097 {
1098   GdkEvent tmp_event;
1099   
1100   if ((current_dest_drag != NULL) &&
1101       (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1102       (current_dest_drag->source_window == context->source_window))
1103     {
1104       tmp_event.dnd.type = GDK_DRAG_LEAVE;
1105       tmp_event.dnd.window = context->dest_window;
1106       /* Pass ownership of context to the event */
1107       tmp_event.dnd.send_event = FALSE;
1108       tmp_event.dnd.context = current_dest_drag;
1109       tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1110
1111       current_dest_drag = NULL;
1112       
1113       gdk_event_put (&tmp_event);
1114     }
1115 }
1116
1117 static void
1118 local_send_enter (GdkDragContext *context,
1119                   guint32         time)
1120 {
1121   GdkEvent tmp_event;
1122   GdkDragContextPrivateWin32 *private;
1123   GdkDragContext *new_context;
1124
1125   private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
1126   
1127   if (current_dest_drag != NULL)
1128     {
1129       gdk_drag_context_unref (current_dest_drag);
1130       current_dest_drag = NULL;
1131     }
1132
1133   new_context = gdk_drag_context_new ();
1134   new_context->protocol = GDK_DRAG_PROTO_LOCAL;
1135   new_context->is_source = FALSE;
1136
1137   new_context->source_window = context->source_window;
1138   g_object_ref (new_context->source_window);
1139   new_context->dest_window = context->dest_window;
1140   g_object_ref (new_context->dest_window);
1141
1142   new_context->targets = g_list_copy (context->targets);
1143
1144   gdk_window_set_events (new_context->source_window,
1145                          gdk_window_get_events (new_context->source_window) |
1146                          GDK_PROPERTY_CHANGE_MASK);
1147   new_context->actions = context->actions;
1148
1149   tmp_event.dnd.type = GDK_DRAG_ENTER;
1150   tmp_event.dnd.window = context->dest_window;
1151   tmp_event.dnd.send_event = FALSE;
1152   tmp_event.dnd.context = new_context;
1153   tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1154   
1155   current_dest_drag = new_context;
1156   
1157   gdk_event_put (&tmp_event);
1158 }
1159
1160 static void
1161 local_send_motion (GdkDragContext *context,
1162                    gint            x_root, 
1163                    gint            y_root,
1164                    GdkDragAction   action,
1165                    guint32         time)
1166 {
1167   GdkEvent tmp_event;
1168   
1169   if ((current_dest_drag != NULL) &&
1170       (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1171       (current_dest_drag->source_window == context->source_window))
1172     {
1173       tmp_event.dnd.type = GDK_DRAG_MOTION;
1174       tmp_event.dnd.window = current_dest_drag->dest_window;
1175       tmp_event.dnd.send_event = FALSE;
1176       tmp_event.dnd.context = current_dest_drag;
1177       tmp_event.dnd.time = time;
1178
1179       current_dest_drag->suggested_action = action;
1180       current_dest_drag->actions = current_dest_drag->suggested_action;
1181
1182       tmp_event.dnd.x_root = x_root;
1183       tmp_event.dnd.y_root = y_root;
1184
1185       (GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag))->last_x = x_root;
1186       (GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag))->last_y = y_root;
1187
1188       GDK_DRAG_CONTEXT_PRIVATE_DATA (context)->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1189       
1190       gdk_event_put (&tmp_event);
1191     }
1192 }
1193
1194 static void
1195 local_send_drop (GdkDragContext *context,
1196                  guint32         time)
1197 {
1198   GdkEvent tmp_event;
1199   
1200   if ((current_dest_drag != NULL) &&
1201       (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1202       (current_dest_drag->source_window == context->source_window))
1203     {
1204       GdkDragContextPrivateWin32 *private;
1205       private = GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag);
1206
1207       /* Pass ownership of context to the event */
1208       tmp_event.dnd.type = GDK_DROP_START;
1209       tmp_event.dnd.window = current_dest_drag->dest_window;
1210       tmp_event.dnd.send_event = FALSE;
1211       tmp_event.dnd.context = current_dest_drag;
1212       tmp_event.dnd.time = GDK_CURRENT_TIME;
1213       
1214       tmp_event.dnd.x_root = private->last_x;
1215       tmp_event.dnd.y_root = private->last_y;
1216       
1217       current_dest_drag = NULL;
1218
1219       gdk_event_put (&tmp_event);
1220     }
1221
1222 }
1223
1224 static void
1225 gdk_drag_do_leave (GdkDragContext *context,
1226                    guint32         time)
1227 {
1228   if (context->dest_window)
1229     {
1230       GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
1231
1232       switch (context->protocol)
1233         {
1234         case GDK_DRAG_PROTO_LOCAL:
1235           local_send_leave (context, time);
1236           break;
1237         default:
1238           break;
1239         }
1240
1241       g_object_unref (context->dest_window);
1242       context->dest_window = NULL;
1243     }
1244 }
1245
1246 GdkDragContext * 
1247 gdk_drag_begin (GdkWindow *window,
1248                 GList     *targets)
1249 {
1250 #ifndef OLE2_DND
1251   GList *tmp_list;
1252   GdkDragContext *new_context;
1253
1254   g_return_val_if_fail (window != NULL, NULL);
1255
1256   new_context = gdk_drag_context_new ();
1257   new_context->is_source = TRUE;
1258   new_context->source_window = window;
1259   g_object_ref (window);
1260
1261   tmp_list = g_list_last (targets);
1262   new_context->targets = NULL;
1263   while (tmp_list)
1264     {
1265       new_context->targets = g_list_prepend (new_context->targets,
1266                                              tmp_list->data);
1267       tmp_list = tmp_list->prev;
1268     }
1269
1270   new_context->actions = 0;
1271
1272   return new_context;
1273 #else
1274   source_drag_context *ctx;
1275   GList *tmp_list;
1276   data_object *dobj;
1277   HRESULT hResult;
1278   DWORD dwEffect;
1279   HGLOBAL global;
1280   STGMEDIUM medium;
1281
1282   g_return_val_if_fail (window != NULL, NULL);
1283
1284   GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
1285
1286   ctx = source_context_new ();
1287   ctx->context->protocol = GDK_DRAG_PROTO_OLE2;
1288   ctx->context->source_window = window;
1289   g_object_ref (window);
1290
1291   tmp_list = g_list_last (targets);
1292   ctx->context->targets = NULL;
1293   while (tmp_list)
1294     {
1295       ctx->context->targets = g_list_prepend (ctx->context->targets,
1296                                               tmp_list->data);
1297       tmp_list = tmp_list->prev;
1298     }
1299
1300   ctx->context->actions = 0;
1301
1302   dobj = data_object_new ();
1303
1304   global = GlobalAlloc (GMEM_FIXED, sizeof (ctx));
1305
1306   memcpy (&global, ctx, sizeof (ctx));
1307
1308   medium.tymed = TYMED_HGLOBAL;
1309   medium.hGlobal = global;
1310   medium.pUnkForRelease = NULL;
1311
1312   dobj->ido.lpVtbl->SetData (&dobj->ido, &formats[1], &medium, TRUE);
1313
1314   hResult = DoDragDrop (&dobj->ido, &ctx->ids, DROPEFFECT_MOVE|DROPEFFECT_COPY, &dwEffect);
1315
1316   GDK_NOTE (DND, g_print ("DoDragDrop returned %s\n",
1317                           (hResult == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" :
1318                            (hResult == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" :
1319                             (hResult == E_UNEXPECTED ? "E_UNEXPECTED" :
1320                              g_strdup_printf ("%#.8lx", hResult))))));
1321
1322   dobj->ido.lpVtbl->Release (&dobj->ido);
1323   ctx->ids.lpVtbl->Release (&ctx->ids);
1324
1325   return ctx->context;
1326 #endif
1327 }
1328
1329 guint32
1330 gdk_drag_get_protocol_for_display (GdkDisplay      *display,
1331                                    guint32          xid,
1332                                    GdkDragProtocol *protocol)
1333 {
1334   GdkWindow *window;
1335
1336   GDK_NOTE (DND, g_print ("gdk_drag_get_protocol\n"));
1337
1338   window = gdk_window_lookup (xid);
1339
1340   if (GPOINTER_TO_INT (gdk_drawable_get_data (window, "gdk-dnd-registered")))
1341     {
1342       *protocol = GDK_DRAG_PROTO_LOCAL;
1343       return xid;
1344     }
1345
1346   return 0;
1347 }
1348
1349 typedef struct {
1350   gint x;
1351   gint y;
1352   HWND ignore;
1353   HWND result;
1354 } find_window_enum_arg;
1355
1356 static BOOL CALLBACK
1357 find_window_enum_proc (HWND   hwnd,
1358                        LPARAM lparam)
1359 {
1360   RECT rect;
1361   POINT tl, br;
1362   find_window_enum_arg *a = (find_window_enum_arg *) lparam;
1363
1364   if (hwnd == a->ignore)
1365     return TRUE;
1366
1367   if (!IsWindowVisible (hwnd))
1368     return TRUE;
1369
1370   tl.x = tl.y = 0;
1371   ClientToScreen (hwnd, &tl);
1372   GetClientRect (hwnd, &rect);
1373   br.x = rect.right;
1374   br.y = rect.bottom;
1375   ClientToScreen (hwnd, &br);
1376
1377   if (a->x >= tl.x && a->y >= tl.y && a->x < br.x && a->y < br.y)
1378     {
1379       a->result = hwnd;
1380       return FALSE;
1381     }
1382   else
1383     return TRUE;
1384 }
1385
1386 void
1387 gdk_drag_find_window_for_screen (GdkDragContext  *context,
1388                                  GdkWindow       *drag_window,
1389                                  GdkScreen       *screen,
1390                                  gint             x_root,
1391                                  gint             y_root,
1392                                  GdkWindow      **dest_window,
1393                                  GdkDragProtocol *protocol)
1394 {
1395   find_window_enum_arg a;
1396
1397   a.x = x_root - _gdk_offset_x;
1398   a.y = y_root - _gdk_offset_y;
1399   a.ignore = drag_window ? GDK_WINDOW_HWND (drag_window) : NULL;
1400   a.result = NULL;
1401
1402   EnumWindows (find_window_enum_proc, (LPARAM) &a);
1403
1404   if (a.result == NULL)
1405     *dest_window = NULL;
1406   else
1407     {
1408       *dest_window = gdk_win32_handle_table_lookup (GPOINTER_TO_UINT (a.result));
1409       if (*dest_window)
1410         {
1411           *dest_window = gdk_window_get_toplevel (*dest_window);
1412           g_object_ref (*dest_window);
1413         }
1414
1415       if (context->source_window)
1416         *protocol = GDK_DRAG_PROTO_LOCAL;
1417       else
1418         *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
1419     }
1420
1421   GDK_NOTE (DND,
1422             g_print ("gdk_drag_find_window: %p %+d%+d: %p: %p %d\n",
1423                      (drag_window ? GDK_WINDOW_HWND (drag_window) : NULL),
1424                      x_root, y_root,
1425                      a.result,
1426                      (*dest_window ? GDK_WINDOW_HWND (*dest_window) : NULL),
1427                      *protocol));
1428 }
1429
1430 gboolean
1431 gdk_drag_motion (GdkDragContext *context,
1432                  GdkWindow      *dest_window,
1433                  GdkDragProtocol protocol,
1434                  gint            x_root, 
1435                  gint            y_root,
1436                  GdkDragAction   suggested_action,
1437                  GdkDragAction   possible_actions,
1438                  guint32         time)
1439 {
1440   GdkDragContextPrivateWin32 *private;
1441
1442   g_return_val_if_fail (context != NULL, FALSE);
1443
1444   GDK_NOTE (DND, g_print ("gdk_drag_motion\n"));
1445
1446   private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
1447   
1448   if (context->dest_window != dest_window)
1449     {
1450       GdkEvent temp_event;
1451
1452       /* Send a leave to the last destination */
1453       gdk_drag_do_leave (context, time);
1454       private->drag_status = GDK_DRAG_STATUS_DRAG;
1455
1456       /* Check if new destination accepts drags, and which protocol */
1457       if (dest_window)
1458         {
1459           context->dest_window = dest_window;
1460           g_object_ref (context->dest_window);
1461           context->protocol = protocol;
1462
1463           switch (protocol)
1464             {
1465             case GDK_DRAG_PROTO_LOCAL:
1466               local_send_enter (context, time);
1467               break;
1468
1469             default:
1470               break;
1471             }
1472           context->suggested_action = suggested_action;
1473         }
1474       else
1475         {
1476           context->dest_window = NULL;
1477           context->action = 0;
1478         }
1479
1480       /* Push a status event, to let the client know that
1481        * the drag changed 
1482        */
1483
1484       temp_event.dnd.type = GDK_DRAG_STATUS;
1485       temp_event.dnd.window = context->source_window;
1486       /* We use this to signal a synthetic status. Perhaps
1487        * we should use an extra field...
1488        */
1489       temp_event.dnd.send_event = TRUE;
1490
1491       temp_event.dnd.context = context;
1492       temp_event.dnd.time = time;
1493
1494       gdk_event_put (&temp_event);
1495     }
1496   else
1497     {
1498       context->suggested_action = suggested_action;
1499     }
1500
1501   /* Send a drag-motion event */
1502
1503   private->last_x = x_root;
1504   private->last_y = y_root;
1505       
1506   if (context->dest_window)
1507     {
1508       if (private->drag_status == GDK_DRAG_STATUS_DRAG)
1509         {
1510           switch (context->protocol)
1511             {
1512             case GDK_DRAG_PROTO_LOCAL:
1513               local_send_motion (context, x_root, y_root, suggested_action, time);
1514               break;
1515               
1516             case GDK_DRAG_PROTO_NONE:
1517               g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
1518               break;
1519
1520             default:
1521               break;
1522             }
1523         }
1524       else
1525         return TRUE;
1526     }
1527
1528   return FALSE;
1529 }
1530
1531 void
1532 gdk_drag_drop (GdkDragContext *context,
1533                guint32         time)
1534 {
1535   g_return_if_fail (context != NULL);
1536
1537   GDK_NOTE (DND, g_print ("gdk_drag_drop\n"));
1538
1539   if (context->dest_window)
1540     {
1541       switch (context->protocol)
1542         {
1543         case GDK_DRAG_PROTO_LOCAL:
1544           local_send_drop (context, time);
1545           break;
1546
1547         case GDK_DRAG_PROTO_NONE:
1548           g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
1549           break;
1550
1551         default:
1552           break;
1553         }
1554     }
1555 }
1556
1557 void
1558 gdk_drag_abort (GdkDragContext *context,
1559                 guint32         time)
1560 {
1561   g_return_if_fail (context != NULL);
1562
1563   GDK_NOTE (DND, g_print ("gdk_drag_abort\n"));
1564
1565   gdk_drag_do_leave (context, time);
1566 }
1567
1568 /* Destination side */
1569
1570 void
1571 gdk_drag_status (GdkDragContext *context,
1572                  GdkDragAction   action,
1573                  guint32         time)
1574 {
1575   GdkDragContextPrivateWin32 *private;
1576   GdkDragContext *src_context;
1577   GdkEvent tmp_event;
1578
1579   g_return_if_fail (context != NULL);
1580
1581   private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
1582
1583   context->action = action;
1584
1585   src_context = gdk_drag_context_find (TRUE,
1586                                        context->source_window,
1587                                        context->dest_window);
1588
1589   if (src_context)
1590     {
1591       GdkDragContextPrivateWin32 *private = GDK_DRAG_CONTEXT_PRIVATE_DATA (src_context);
1592       
1593       if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
1594         private->drag_status = GDK_DRAG_STATUS_DRAG;
1595
1596       tmp_event.dnd.type = GDK_DRAG_STATUS;
1597       tmp_event.dnd.window = context->source_window;
1598       tmp_event.dnd.send_event = FALSE;
1599       tmp_event.dnd.context = src_context;
1600       tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1601
1602       if (action == GDK_ACTION_DEFAULT)
1603         action = 0;
1604       
1605       src_context->action = action;
1606       
1607       gdk_event_put (&tmp_event);
1608     }
1609 }
1610
1611 void 
1612 gdk_drop_reply (GdkDragContext *context,
1613                 gboolean        ok,
1614                 guint32         time)
1615 {
1616   g_return_if_fail (context != NULL);
1617
1618   GDK_NOTE (DND, g_print ("gdk_drop_reply\n"));
1619
1620   if (context->dest_window)
1621     {
1622       switch (context->protocol)
1623         {
1624         case GDK_DRAG_PROTO_WIN32_DROPFILES:
1625           _gdk_dropfiles_store (NULL);
1626           break;
1627
1628         default:
1629           break;
1630         }
1631     }
1632 }
1633
1634 void
1635 gdk_drop_finish (GdkDragContext *context,
1636                  gboolean        success,
1637                  guint32         time)
1638 {
1639   GdkDragContextPrivateWin32 *private;
1640   GdkDragContext *src_context;
1641   GdkEvent tmp_event;
1642         
1643   g_return_if_fail (context != NULL);
1644
1645   GDK_NOTE (DND, g_print ("gdk_drop_finish"));
1646
1647   private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
1648
1649   src_context = gdk_drag_context_find (TRUE,
1650                                        context->source_window,
1651                                        context->dest_window);
1652   if (src_context)
1653     {
1654       tmp_event.dnd.type = GDK_DROP_FINISHED;
1655       tmp_event.dnd.window = src_context->source_window;
1656       tmp_event.dnd.send_event = FALSE;
1657       tmp_event.dnd.context = src_context;
1658
1659       gdk_event_put (&tmp_event);
1660     }
1661 }
1662
1663 #ifdef OLE2_DND
1664
1665 static GdkFilterReturn
1666 gdk_destroy_filter (GdkXEvent *xev,
1667                     GdkEvent  *event,
1668                     gpointer   data)
1669 {
1670   MSG *msg = (MSG *) xev;
1671
1672   if (msg->message == WM_DESTROY)
1673     {
1674       IDropTarget *idtp = (IDropTarget *) data;
1675
1676       GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %p\n", msg->hwnd));
1677 #if 0
1678       idtp->lpVtbl->Release (idtp);
1679 #endif
1680       RevokeDragDrop (msg->hwnd);
1681       CoLockObjectExternal (idtp, FALSE, TRUE);
1682     }
1683   return GDK_FILTER_CONTINUE;
1684 }
1685 #endif
1686
1687 void
1688 gdk_window_register_dnd (GdkWindow *window)
1689 {
1690 #ifdef OLE2_DND
1691   target_drag_context *ctx;
1692   HRESULT hres;
1693 #endif
1694
1695   g_return_if_fail (window != NULL);
1696
1697   if (GPOINTER_TO_INT (gdk_drawable_get_data (window, "gdk-dnd-registered")))
1698     return;
1699
1700   gdk_drawable_set_data (window, "gdk-dnd-registered", GINT_TO_POINTER(TRUE), NULL);
1701
1702   GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %p\n",
1703                           GDK_WINDOW_HWND (window)));
1704
1705   /* We always claim to accept dropped files, but in fact we might not,
1706    * of course. This function is called in such a way that it cannot know
1707    * whether the window (widget) in question actually accepts files
1708    * (in gtk, data of type text/uri-list) or not.
1709    */
1710   gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
1711   DragAcceptFiles (GDK_WINDOW_HWND (window), TRUE);
1712
1713 #ifdef OLE2_DND
1714   /* Register for OLE2 d&d */
1715   ctx = target_context_new ();
1716   ctx->context->protocol = GDK_DRAG_PROTO_OLE2;
1717   hres = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUE, FALSE);
1718   if (!SUCCEEDED (hres))
1719     OTHER_API_FAILED ("CoLockObjectExternal");
1720   else
1721     {
1722       hres = RegisterDragDrop (GDK_WINDOW_HWND (window), &ctx->idt);
1723       if (hres == DRAGDROP_E_ALREADYREGISTERED)
1724         {
1725           g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
1726 #if 0
1727           ctx->idt.lpVtbl->Release (&ctx->idt);
1728 #endif
1729           CoLockObjectExternal ((IUnknown *) &ctx->idt, FALSE, FALSE);
1730         }
1731       else if (!SUCCEEDED (hres))
1732         OTHER_API_FAILED ("RegisterDragDrop");
1733       else
1734         {
1735           gdk_window_add_filter (window, gdk_destroy_filter, &ctx->idt);
1736         }
1737     }
1738 #endif
1739 }
1740
1741 /*************************************************************
1742  * gdk_drag_get_selection:
1743  *     Returns the selection atom for the current source window
1744  *   arguments:
1745  *
1746  *   results:
1747  *************************************************************/
1748
1749 GdkAtom
1750 gdk_drag_get_selection (GdkDragContext *context)
1751 {
1752   if (context->protocol == GDK_DRAG_PROTO_LOCAL)
1753     return _local_dnd;
1754   else if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
1755     return _gdk_win32_dropfiles;
1756   else if (context->protocol == GDK_DRAG_PROTO_OLE2)
1757     return _gdk_ole2_dnd;
1758   else
1759     return GDK_NONE;
1760 }
1761
1762 gboolean 
1763 gdk_drag_drop_succeeded (GdkDragContext *context)
1764 {
1765   GdkDragContextPrivateWin32 *private;
1766
1767   g_return_val_if_fail (context != NULL, FALSE);
1768
1769   private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
1770
1771   /* FIXME: Can we set drop_failed when the drop has failed? */
1772   return !private->drop_failed;
1773 }