]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkdnd-win32.c
Implement some more stubs for pygtk, bug #346713.
[~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_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       context->actions = GDK_ACTION_COPY;
982       context->suggested_action = GDK_ACTION_COPY;
983       current_dest_drag = context;
984
985       event->dnd.type = GDK_DROP_START;
986       event->dnd.context = current_dest_drag;
987       
988       hdrop = (HANDLE) msg->wParam;
989       DragQueryPoint (hdrop, &pt);
990       ClientToScreen (msg->hwnd, &pt);
991
992       event->dnd.x_root = pt.x + _gdk_offset_x;
993       event->dnd.y_root = pt.y + _gdk_offset_y;
994       event->dnd.time = _gdk_win32_get_next_tick (msg->time);
995
996       nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
997
998       result = g_string_new (NULL);
999       for (i = 0; i < nfiles; i++)
1000         {
1001           gchar *uri;
1002
1003           if (G_WIN32_HAVE_WIDECHAR_API ())
1004             {
1005               wchar_t wfn[MAX_PATH];
1006
1007               DragQueryFileW (hdrop, i, wfn, MAX_PATH);
1008               fileName = g_utf16_to_utf8 (wfn, -1, NULL, NULL, NULL);
1009             }
1010           else
1011             {
1012               char cpfn[MAX_PATH];
1013
1014               DragQueryFileA (hdrop, i, cpfn, MAX_PATH);
1015               fileName = g_locale_to_utf8 (cpfn, -1, NULL, NULL, NULL);
1016             }
1017
1018           /* Resolve shortcuts */
1019           if (resolve_link (msg->hwnd, fileName, &linkedFile))
1020             {
1021               uri = g_filename_to_uri (linkedFile, NULL, NULL);
1022               g_free (linkedFile);
1023               if (uri != NULL)
1024                 {
1025                   g_string_append (result, uri);
1026                   GDK_NOTE (DND, g_print ("... %s link to %s: %s\n",
1027                                           fileName, linkedFile, uri));
1028                   g_free (uri);
1029                 }
1030             }
1031           else
1032             {
1033               uri = g_filename_to_uri (fileName, NULL, NULL);
1034               if (uri != NULL)
1035                 {
1036                   g_string_append (result, uri);
1037                   GDK_NOTE (DND, g_print ("... %s: %s\n", fileName, uri));
1038                   g_free (uri);
1039                 }
1040             }
1041           g_free (fileName);
1042           g_string_append (result, "\015\012");
1043         }
1044       _gdk_dropfiles_store (result->str);
1045       g_string_free (result, FALSE);
1046
1047       DragFinish (hdrop);
1048       
1049       return GDK_FILTER_TRANSLATE;
1050     }
1051   else
1052     return GDK_FILTER_CONTINUE;
1053 }
1054
1055 /*************************************************************
1056  ************************** Public API ***********************
1057  *************************************************************/
1058
1059 void
1060 _gdk_dnd_init (void)
1061 {
1062 #ifdef OLE2_DND
1063   HRESULT hres;
1064   hres = OleInitialize (NULL);
1065
1066   if (! SUCCEEDED (hres))
1067     g_error ("OleInitialize failed");
1068
1069   nformats = 2;
1070   formats = g_new (FORMATETC, nformats);
1071
1072   formats[0].cfFormat = CF_TEXT;
1073   formats[0].ptd = NULL;
1074   formats[0].dwAspect = DVASPECT_CONTENT;
1075   formats[0].lindex = -1;
1076   formats[0].tymed = TYMED_HGLOBAL;
1077   
1078   formats[1].cfFormat = CF_GDIOBJFIRST;
1079   formats[1].ptd = NULL;
1080   formats[1].dwAspect = DVASPECT_CONTENT;
1081   formats[1].lindex = -1;
1082   formats[1].tymed = TYMED_HGLOBAL;
1083 #endif
1084 }      
1085
1086 void
1087 _gdk_win32_dnd_exit (void)
1088 {
1089 #ifdef OLE2_DND
1090   OleUninitialize ();
1091 #endif
1092 }
1093
1094 /* Source side */
1095
1096 static void
1097 local_send_leave (GdkDragContext *context,
1098                   guint32         time)
1099 {
1100   GdkEvent tmp_event;
1101   
1102   if ((current_dest_drag != NULL) &&
1103       (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1104       (current_dest_drag->source_window == context->source_window))
1105     {
1106       tmp_event.dnd.type = GDK_DRAG_LEAVE;
1107       tmp_event.dnd.window = context->dest_window;
1108       /* Pass ownership of context to the event */
1109       tmp_event.dnd.send_event = FALSE;
1110       tmp_event.dnd.context = current_dest_drag;
1111       tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1112
1113       current_dest_drag = NULL;
1114       
1115       gdk_event_put (&tmp_event);
1116     }
1117 }
1118
1119 static void
1120 local_send_enter (GdkDragContext *context,
1121                   guint32         time)
1122 {
1123   GdkEvent tmp_event;
1124   GdkDragContextPrivateWin32 *private;
1125   GdkDragContext *new_context;
1126
1127   private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
1128   
1129   if (current_dest_drag != NULL)
1130     {
1131       gdk_drag_context_unref (current_dest_drag);
1132       current_dest_drag = NULL;
1133     }
1134
1135   new_context = gdk_drag_context_new ();
1136   new_context->protocol = GDK_DRAG_PROTO_LOCAL;
1137   new_context->is_source = FALSE;
1138
1139   new_context->source_window = context->source_window;
1140   g_object_ref (new_context->source_window);
1141   new_context->dest_window = context->dest_window;
1142   g_object_ref (new_context->dest_window);
1143
1144   new_context->targets = g_list_copy (context->targets);
1145
1146   gdk_window_set_events (new_context->source_window,
1147                          gdk_window_get_events (new_context->source_window) |
1148                          GDK_PROPERTY_CHANGE_MASK);
1149   new_context->actions = context->actions;
1150
1151   tmp_event.dnd.type = GDK_DRAG_ENTER;
1152   tmp_event.dnd.window = context->dest_window;
1153   tmp_event.dnd.send_event = FALSE;
1154   tmp_event.dnd.context = new_context;
1155   tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1156   
1157   current_dest_drag = new_context;
1158   
1159   gdk_event_put (&tmp_event);
1160 }
1161
1162 static void
1163 local_send_motion (GdkDragContext *context,
1164                    gint            x_root, 
1165                    gint            y_root,
1166                    GdkDragAction   action,
1167                    guint32         time)
1168 {
1169   GdkEvent tmp_event;
1170   
1171   if ((current_dest_drag != NULL) &&
1172       (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1173       (current_dest_drag->source_window == context->source_window))
1174     {
1175       tmp_event.dnd.type = GDK_DRAG_MOTION;
1176       tmp_event.dnd.window = current_dest_drag->dest_window;
1177       tmp_event.dnd.send_event = FALSE;
1178       tmp_event.dnd.context = current_dest_drag;
1179       tmp_event.dnd.time = time;
1180
1181       current_dest_drag->suggested_action = action;
1182       current_dest_drag->actions = current_dest_drag->suggested_action;
1183
1184       tmp_event.dnd.x_root = x_root;
1185       tmp_event.dnd.y_root = y_root;
1186
1187       (GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag))->last_x = x_root;
1188       (GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag))->last_y = y_root;
1189
1190       GDK_DRAG_CONTEXT_PRIVATE_DATA (context)->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1191       
1192       gdk_event_put (&tmp_event);
1193     }
1194 }
1195
1196 static void
1197 local_send_drop (GdkDragContext *context,
1198                  guint32         time)
1199 {
1200   GdkEvent tmp_event;
1201   
1202   if ((current_dest_drag != NULL) &&
1203       (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1204       (current_dest_drag->source_window == context->source_window))
1205     {
1206       GdkDragContextPrivateWin32 *private;
1207       private = GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag);
1208
1209       /* Pass ownership of context to the event */
1210       tmp_event.dnd.type = GDK_DROP_START;
1211       tmp_event.dnd.window = current_dest_drag->dest_window;
1212       tmp_event.dnd.send_event = FALSE;
1213       tmp_event.dnd.context = current_dest_drag;
1214       tmp_event.dnd.time = GDK_CURRENT_TIME;
1215       
1216       tmp_event.dnd.x_root = private->last_x;
1217       tmp_event.dnd.y_root = private->last_y;
1218       
1219       current_dest_drag = NULL;
1220
1221       gdk_event_put (&tmp_event);
1222     }
1223
1224 }
1225
1226 static void
1227 gdk_drag_do_leave (GdkDragContext *context,
1228                    guint32         time)
1229 {
1230   if (context->dest_window)
1231     {
1232       GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
1233
1234       switch (context->protocol)
1235         {
1236         case GDK_DRAG_PROTO_LOCAL:
1237           local_send_leave (context, time);
1238           break;
1239         default:
1240           break;
1241         }
1242
1243       g_object_unref (context->dest_window);
1244       context->dest_window = NULL;
1245     }
1246 }
1247
1248 GdkDragContext * 
1249 gdk_drag_begin (GdkWindow *window,
1250                 GList     *targets)
1251 {
1252 #ifndef OLE2_DND
1253   GList *tmp_list;
1254   GdkDragContext *new_context;
1255
1256   g_return_val_if_fail (window != NULL, NULL);
1257
1258   new_context = gdk_drag_context_new ();
1259   new_context->is_source = TRUE;
1260   new_context->source_window = window;
1261   g_object_ref (window);
1262
1263   tmp_list = g_list_last (targets);
1264   new_context->targets = NULL;
1265   while (tmp_list)
1266     {
1267       new_context->targets = g_list_prepend (new_context->targets,
1268                                              tmp_list->data);
1269       tmp_list = tmp_list->prev;
1270     }
1271
1272   new_context->actions = 0;
1273
1274   return new_context;
1275 #else
1276   source_drag_context *ctx;
1277   GList *tmp_list;
1278   data_object *dobj;
1279   HRESULT hResult;
1280   DWORD dwEffect;
1281   HGLOBAL global;
1282   STGMEDIUM medium;
1283
1284   g_return_val_if_fail (window != NULL, NULL);
1285
1286   GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
1287
1288   ctx = source_context_new ();
1289   ctx->context->protocol = GDK_DRAG_PROTO_OLE2;
1290   ctx->context->source_window = window;
1291   g_object_ref (window);
1292
1293   tmp_list = g_list_last (targets);
1294   ctx->context->targets = NULL;
1295   while (tmp_list)
1296     {
1297       ctx->context->targets = g_list_prepend (ctx->context->targets,
1298                                               tmp_list->data);
1299       tmp_list = tmp_list->prev;
1300     }
1301
1302   ctx->context->actions = 0;
1303
1304   dobj = data_object_new ();
1305
1306   global = GlobalAlloc (GMEM_FIXED, sizeof (ctx));
1307
1308   memcpy (&global, ctx, sizeof (ctx));
1309
1310   medium.tymed = TYMED_HGLOBAL;
1311   medium.hGlobal = global;
1312   medium.pUnkForRelease = NULL;
1313
1314   dobj->ido.lpVtbl->SetData (&dobj->ido, &formats[1], &medium, TRUE);
1315
1316   hResult = DoDragDrop (&dobj->ido, &ctx->ids, DROPEFFECT_MOVE|DROPEFFECT_COPY, &dwEffect);
1317
1318   GDK_NOTE (DND, g_print ("DoDragDrop returned %s\n",
1319                           (hResult == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" :
1320                            (hResult == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" :
1321                             (hResult == E_UNEXPECTED ? "E_UNEXPECTED" :
1322                              g_strdup_printf ("%#.8lx", hResult))))));
1323
1324   dobj->ido.lpVtbl->Release (&dobj->ido);
1325   ctx->ids.lpVtbl->Release (&ctx->ids);
1326
1327   return ctx->context;
1328 #endif
1329 }
1330
1331 guint32
1332 gdk_drag_get_protocol_for_display (GdkDisplay      *display,
1333                                    guint32          xid,
1334                                    GdkDragProtocol *protocol)
1335 {
1336   GdkWindow *window;
1337
1338   GDK_NOTE (DND, g_print ("gdk_drag_get_protocol\n"));
1339
1340   window = gdk_window_lookup (xid);
1341
1342   if (GPOINTER_TO_INT (gdk_drawable_get_data (window, "gdk-dnd-registered")))
1343     {
1344       *protocol = GDK_DRAG_PROTO_LOCAL;
1345       return xid;
1346     }
1347
1348   return 0;
1349 }
1350
1351 typedef struct {
1352   gint x;
1353   gint y;
1354   HWND ignore;
1355   HWND result;
1356 } find_window_enum_arg;
1357
1358 static BOOL CALLBACK
1359 find_window_enum_proc (HWND   hwnd,
1360                        LPARAM lparam)
1361 {
1362   RECT rect;
1363   POINT tl, br;
1364   find_window_enum_arg *a = (find_window_enum_arg *) lparam;
1365
1366   if (hwnd == a->ignore)
1367     return TRUE;
1368
1369   if (!IsWindowVisible (hwnd))
1370     return TRUE;
1371
1372   tl.x = tl.y = 0;
1373   ClientToScreen (hwnd, &tl);
1374   GetClientRect (hwnd, &rect);
1375   br.x = rect.right;
1376   br.y = rect.bottom;
1377   ClientToScreen (hwnd, &br);
1378
1379   if (a->x >= tl.x && a->y >= tl.y && a->x < br.x && a->y < br.y)
1380     {
1381       a->result = hwnd;
1382       return FALSE;
1383     }
1384   else
1385     return TRUE;
1386 }
1387
1388 void
1389 gdk_drag_find_window_for_screen (GdkDragContext  *context,
1390                                  GdkWindow       *drag_window,
1391                                  GdkScreen       *screen,
1392                                  gint             x_root,
1393                                  gint             y_root,
1394                                  GdkWindow      **dest_window,
1395                                  GdkDragProtocol *protocol)
1396 {
1397   find_window_enum_arg a;
1398
1399   a.x = x_root - _gdk_offset_x;
1400   a.y = y_root - _gdk_offset_y;
1401   a.ignore = drag_window ? GDK_WINDOW_HWND (drag_window) : NULL;
1402   a.result = NULL;
1403
1404   EnumWindows (find_window_enum_proc, (LPARAM) &a);
1405
1406   if (a.result == NULL)
1407     *dest_window = NULL;
1408   else
1409     {
1410       *dest_window = gdk_win32_handle_table_lookup (GPOINTER_TO_UINT (a.result));
1411       if (*dest_window)
1412         {
1413           *dest_window = gdk_window_get_toplevel (*dest_window);
1414           g_object_ref (*dest_window);
1415         }
1416
1417       if (context->source_window)
1418         *protocol = GDK_DRAG_PROTO_LOCAL;
1419       else
1420         *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
1421     }
1422
1423   GDK_NOTE (DND,
1424             g_print ("gdk_drag_find_window: %p %+d%+d: %p: %p %d\n",
1425                      (drag_window ? GDK_WINDOW_HWND (drag_window) : NULL),
1426                      x_root, y_root,
1427                      a.result,
1428                      (*dest_window ? GDK_WINDOW_HWND (*dest_window) : NULL),
1429                      *protocol));
1430 }
1431
1432 gboolean
1433 gdk_drag_motion (GdkDragContext *context,
1434                  GdkWindow      *dest_window,
1435                  GdkDragProtocol protocol,
1436                  gint            x_root, 
1437                  gint            y_root,
1438                  GdkDragAction   suggested_action,
1439                  GdkDragAction   possible_actions,
1440                  guint32         time)
1441 {
1442   GdkDragContextPrivateWin32 *private;
1443
1444   g_return_val_if_fail (context != NULL, FALSE);
1445
1446   GDK_NOTE (DND, g_print ("gdk_drag_motion\n"));
1447
1448   private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
1449   
1450   if (context->dest_window != dest_window)
1451     {
1452       GdkEvent temp_event;
1453
1454       /* Send a leave to the last destination */
1455       gdk_drag_do_leave (context, time);
1456       private->drag_status = GDK_DRAG_STATUS_DRAG;
1457
1458       /* Check if new destination accepts drags, and which protocol */
1459       if (dest_window)
1460         {
1461           context->dest_window = dest_window;
1462           g_object_ref (context->dest_window);
1463           context->protocol = protocol;
1464
1465           switch (protocol)
1466             {
1467             case GDK_DRAG_PROTO_LOCAL:
1468               local_send_enter (context, time);
1469               break;
1470
1471             default:
1472               break;
1473             }
1474           context->suggested_action = suggested_action;
1475         }
1476       else
1477         {
1478           context->dest_window = NULL;
1479           context->action = 0;
1480         }
1481
1482       /* Push a status event, to let the client know that
1483        * the drag changed 
1484        */
1485
1486       temp_event.dnd.type = GDK_DRAG_STATUS;
1487       temp_event.dnd.window = context->source_window;
1488       /* We use this to signal a synthetic status. Perhaps
1489        * we should use an extra field...
1490        */
1491       temp_event.dnd.send_event = TRUE;
1492
1493       temp_event.dnd.context = context;
1494       temp_event.dnd.time = time;
1495
1496       gdk_event_put (&temp_event);
1497     }
1498   else
1499     {
1500       context->suggested_action = suggested_action;
1501     }
1502
1503   /* Send a drag-motion event */
1504
1505   private->last_x = x_root;
1506   private->last_y = y_root;
1507       
1508   if (context->dest_window)
1509     {
1510       if (private->drag_status == GDK_DRAG_STATUS_DRAG)
1511         {
1512           switch (context->protocol)
1513             {
1514             case GDK_DRAG_PROTO_LOCAL:
1515               local_send_motion (context, x_root, y_root, suggested_action, time);
1516               break;
1517               
1518             case GDK_DRAG_PROTO_NONE:
1519               g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
1520               break;
1521
1522             default:
1523               break;
1524             }
1525         }
1526       else
1527         return TRUE;
1528     }
1529
1530   return FALSE;
1531 }
1532
1533 void
1534 gdk_drag_drop (GdkDragContext *context,
1535                guint32         time)
1536 {
1537   g_return_if_fail (context != NULL);
1538
1539   GDK_NOTE (DND, g_print ("gdk_drag_drop\n"));
1540
1541   if (context->dest_window)
1542     {
1543       switch (context->protocol)
1544         {
1545         case GDK_DRAG_PROTO_LOCAL:
1546           local_send_drop (context, time);
1547           break;
1548
1549         case GDK_DRAG_PROTO_NONE:
1550           g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
1551           break;
1552
1553         default:
1554           break;
1555         }
1556     }
1557 }
1558
1559 void
1560 gdk_drag_abort (GdkDragContext *context,
1561                 guint32         time)
1562 {
1563   g_return_if_fail (context != NULL);
1564
1565   GDK_NOTE (DND, g_print ("gdk_drag_abort\n"));
1566
1567   gdk_drag_do_leave (context, time);
1568 }
1569
1570 /* Destination side */
1571
1572 void
1573 gdk_drag_status (GdkDragContext *context,
1574                  GdkDragAction   action,
1575                  guint32         time)
1576 {
1577   GdkDragContextPrivateWin32 *private;
1578   GdkDragContext *src_context;
1579   GdkEvent tmp_event;
1580
1581   g_return_if_fail (context != NULL);
1582
1583   private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
1584
1585   context->action = action;
1586
1587   src_context = gdk_drag_context_find (TRUE,
1588                                        context->source_window,
1589                                        context->dest_window);
1590
1591   if (src_context)
1592     {
1593       GdkDragContextPrivateWin32 *private = GDK_DRAG_CONTEXT_PRIVATE_DATA (src_context);
1594       
1595       if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
1596         private->drag_status = GDK_DRAG_STATUS_DRAG;
1597
1598       tmp_event.dnd.type = GDK_DRAG_STATUS;
1599       tmp_event.dnd.window = context->source_window;
1600       tmp_event.dnd.send_event = FALSE;
1601       tmp_event.dnd.context = src_context;
1602       tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1603
1604       if (action == GDK_ACTION_DEFAULT)
1605         action = 0;
1606       
1607       src_context->action = action;
1608       
1609       gdk_event_put (&tmp_event);
1610     }
1611 }
1612
1613 void 
1614 gdk_drop_reply (GdkDragContext *context,
1615                 gboolean        ok,
1616                 guint32         time)
1617 {
1618   g_return_if_fail (context != NULL);
1619
1620   GDK_NOTE (DND, g_print ("gdk_drop_reply\n"));
1621
1622   if (context->dest_window)
1623     {
1624       switch (context->protocol)
1625         {
1626         case GDK_DRAG_PROTO_WIN32_DROPFILES:
1627           _gdk_dropfiles_store (NULL);
1628           break;
1629
1630         default:
1631           break;
1632         }
1633     }
1634 }
1635
1636 void
1637 gdk_drop_finish (GdkDragContext *context,
1638                  gboolean        success,
1639                  guint32         time)
1640 {
1641   GdkDragContextPrivateWin32 *private;
1642   GdkDragContext *src_context;
1643   GdkEvent tmp_event;
1644         
1645   g_return_if_fail (context != NULL);
1646
1647   GDK_NOTE (DND, g_print ("gdk_drop_finish"));
1648
1649   private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
1650
1651   src_context = gdk_drag_context_find (TRUE,
1652                                        context->source_window,
1653                                        context->dest_window);
1654   if (src_context)
1655     {
1656       tmp_event.dnd.type = GDK_DROP_FINISHED;
1657       tmp_event.dnd.window = src_context->source_window;
1658       tmp_event.dnd.send_event = FALSE;
1659       tmp_event.dnd.context = src_context;
1660
1661       gdk_event_put (&tmp_event);
1662     }
1663 }
1664
1665 #ifdef OLE2_DND
1666
1667 static GdkFilterReturn
1668 gdk_destroy_filter (GdkXEvent *xev,
1669                     GdkEvent  *event,
1670                     gpointer   data)
1671 {
1672   MSG *msg = (MSG *) xev;
1673
1674   if (msg->message == WM_DESTROY)
1675     {
1676       IDropTarget *idtp = (IDropTarget *) data;
1677
1678       GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %p\n", msg->hwnd));
1679 #if 0
1680       idtp->lpVtbl->Release (idtp);
1681 #endif
1682       RevokeDragDrop (msg->hwnd);
1683       CoLockObjectExternal (idtp, FALSE, TRUE);
1684     }
1685   return GDK_FILTER_CONTINUE;
1686 }
1687 #endif
1688
1689 void
1690 gdk_window_register_dnd (GdkWindow *window)
1691 {
1692 #ifdef OLE2_DND
1693   target_drag_context *ctx;
1694   HRESULT hres;
1695 #endif
1696
1697   g_return_if_fail (window != NULL);
1698
1699   if (GPOINTER_TO_INT (gdk_drawable_get_data (window, "gdk-dnd-registered")))
1700     return;
1701
1702   gdk_drawable_set_data (window, "gdk-dnd-registered", GINT_TO_POINTER(TRUE), NULL);
1703
1704   GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %p\n",
1705                           GDK_WINDOW_HWND (window)));
1706
1707   /* We always claim to accept dropped files, but in fact we might not,
1708    * of course. This function is called in such a way that it cannot know
1709    * whether the window (widget) in question actually accepts files
1710    * (in gtk, data of type text/uri-list) or not.
1711    */
1712   gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
1713   DragAcceptFiles (GDK_WINDOW_HWND (window), TRUE);
1714
1715 #ifdef OLE2_DND
1716   /* Register for OLE2 d&d */
1717   ctx = target_context_new ();
1718   ctx->context->protocol = GDK_DRAG_PROTO_OLE2;
1719   hres = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUE, FALSE);
1720   if (!SUCCEEDED (hres))
1721     OTHER_API_FAILED ("CoLockObjectExternal");
1722   else
1723     {
1724       hres = RegisterDragDrop (GDK_WINDOW_HWND (window), &ctx->idt);
1725       if (hres == DRAGDROP_E_ALREADYREGISTERED)
1726         {
1727           g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
1728 #if 0
1729           ctx->idt.lpVtbl->Release (&ctx->idt);
1730 #endif
1731           CoLockObjectExternal ((IUnknown *) &ctx->idt, FALSE, FALSE);
1732         }
1733       else if (!SUCCEEDED (hres))
1734         OTHER_API_FAILED ("RegisterDragDrop");
1735       else
1736         {
1737           gdk_window_add_filter (window, gdk_destroy_filter, &ctx->idt);
1738         }
1739     }
1740 #endif
1741 }
1742
1743 /*************************************************************
1744  * gdk_drag_get_selection:
1745  *     Returns the selection atom for the current source window
1746  *   arguments:
1747  *
1748  *   results:
1749  *************************************************************/
1750
1751 GdkAtom
1752 gdk_drag_get_selection (GdkDragContext *context)
1753 {
1754   if (context->protocol == GDK_DRAG_PROTO_LOCAL)
1755     return _local_dnd;
1756   else if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
1757     return _gdk_win32_dropfiles;
1758   else if (context->protocol == GDK_DRAG_PROTO_OLE2)
1759     return _gdk_ole2_dnd;
1760   else
1761     return GDK_NONE;
1762 }
1763
1764 gboolean 
1765 gdk_drag_drop_succeeded (GdkDragContext *context)
1766 {
1767   GdkDragContextPrivateWin32 *private;
1768
1769   g_return_val_if_fail (context != NULL, FALSE);
1770
1771   private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
1772
1773   /* FIXME: Can we set drop_failed when the drop has failed? */
1774   return !private->drop_failed;
1775 }