]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkdnd-win32.c
42e2cd307ecdeb3fc3686e0df3fd423b5277cfc8
[~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-1999 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
30 #include <string.h>
31
32 /* #define OLE2_DND */
33
34 #define INITGUID
35
36 #include "gdkdnd.h"
37 #include "gdkproperty.h"
38 #include "gdkinternals.h"
39 #include "gdkprivate-win32.h"
40
41 #ifdef OLE2_DND
42 #include <ole2.h>
43 #else
44 #include <objbase.h>
45 #endif
46
47 #include <shlobj.h>
48 #include <shlguid.h>
49
50 #include <gdk/gdk.h>
51
52 typedef struct _GdkDragContextPrivateWin32 GdkDragContextPrivateWin32;
53
54 typedef enum {
55   GDK_DRAG_STATUS_DRAG,
56   GDK_DRAG_STATUS_MOTION_WAIT,
57   GDK_DRAG_STATUS_ACTION_WAIT,
58   GDK_DRAG_STATUS_DROP
59 } GtkDragStatus;
60
61 typedef enum {
62   GDK_DRAG_SOURCE,
63   GDK_DRAG_TARGET
64 } GdkDragKind;
65
66 #ifdef OLE2_DND
67
68 #define PRINT_GUID(guid) \
69   g_print ("guid = %.08x-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
70            ((gulong *)  guid)[0], \
71            ((gushort *) guid)[2], \
72            ((gushort *) guid)[3], \
73            ((guchar *)  guid)[8], \
74            ((guchar *)  guid)[9], \
75            ((guchar *)  guid)[10], \
76            ((guchar *)  guid)[11], \
77            ((guchar *)  guid)[12], \
78            ((guchar *)  guid)[13], \
79            ((guchar *)  guid)[14], \
80            ((guchar *)  guid)[15]);
81
82
83 static FORMATETC *formats;
84 static int nformats;
85
86 #endif /* OLE2_DND */
87
88 /* Structure that holds information about a drag in progress.
89  * this is used on both source and destination sides.
90  */
91 struct _GdkDragContextPrivateWin32 {
92   gint ref_count;
93
94   guint16 last_x;               /* Coordinates from last event */
95   guint16 last_y;
96   HWND  dest_xid;
97   guint drag_status;            /* Current status of drag */
98 };
99
100 #define PRIVATE_DATA(context) ((GdkDragContextPrivateWin32 *) GDK_DRAG_CONTEXT (context)->windowing_data)
101
102 GdkDragContext *current_dest_drag = NULL;
103
104 static void gdk_drag_context_init       (GdkDragContext      *dragcontext);
105 static void gdk_drag_context_class_init (GdkDragContextClass *klass);
106 static void gdk_drag_context_finalize   (GObject              *object);
107
108 static gpointer parent_class = NULL;
109 static GList *contexts;
110
111 GType
112 gdk_drag_context_get_type (void)
113 {
114   static GType object_type = 0;
115
116   if (!object_type)
117     {
118       static const GTypeInfo object_info =
119       {
120         sizeof (GdkDragContextClass),
121         (GBaseInitFunc) NULL,
122         (GBaseFinalizeFunc) NULL,
123         (GClassInitFunc) gdk_drag_context_class_init,
124         NULL,           /* class_finalize */
125         NULL,           /* class_data */
126         sizeof (GdkDragContext),
127         0,              /* n_preallocs */
128         (GInstanceInitFunc) gdk_drag_context_init,
129       };
130       
131       object_type = g_type_register_static (G_TYPE_OBJECT,
132                                             "GdkDragContext",
133                                             &object_info);
134     }
135   
136   return object_type;
137 }
138
139 static void
140 gdk_drag_context_init (GdkDragContext *dragcontext)
141 {
142   GdkDragContextPrivateWin32 *private = g_new0 (GdkDragContextPrivateWin32, 1);
143
144   dragcontext->windowing_data = private;
145   private->ref_count = 1;
146
147   contexts = g_list_prepend (contexts, dragcontext);
148 }
149
150 static void
151 gdk_drag_context_class_init (GdkDragContextClass *klass)
152 {
153   GObjectClass *object_class = G_OBJECT_CLASS (klass);
154
155   parent_class = g_type_class_peek_parent (klass);
156
157   object_class->finalize = gdk_drag_context_finalize;
158 }
159
160 static void
161 gdk_drag_context_finalize (GObject *object)
162 {
163   GdkDragContext *context = GDK_DRAG_CONTEXT (object);
164   GdkDragContextPrivateWin32 *private = PRIVATE_DATA (context);
165   
166   g_list_free (context->targets);
167
168   if (context->source_window)
169     {
170       gdk_window_unref (context->source_window);
171     }
172   
173   if (context->dest_window)
174     gdk_window_unref (context->dest_window);
175   
176   contexts = g_list_remove (contexts, context);
177
178   g_free (private);
179   
180   G_OBJECT_CLASS (parent_class)->finalize (object);
181 }
182
183 /* Drag Contexts */
184
185 GdkDragContext *
186 gdk_drag_context_new (void)
187 {
188   return g_object_new (gdk_drag_context_get_type (), NULL);
189 }
190
191 void
192 gdk_drag_context_ref (GdkDragContext *context)
193 {
194   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
195
196   g_object_ref (G_OBJECT (context));
197 }
198
199 void
200 gdk_drag_context_unref (GdkDragContext *context)
201 {
202   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
203
204   g_object_unref (G_OBJECT (context));
205 }
206
207 #if 0
208
209 static GdkDragContext *
210 gdk_drag_context_find (gboolean is_source,
211                        HWND     source_xid,
212                        HWND     dest_xid)
213 {
214   GList *tmp_list = contexts;
215   GdkDragContext *context;
216
217   while (tmp_list)
218     {
219       context = (GdkDragContext *)tmp_list->data;
220
221       if ((!context->is_source == !is_source) &&
222           ((source_xid == None) || (context->source_window &&
223             (GDK_WINDOW_XWINDOW (context->source_window) == source_xid))) &&
224           ((dest_xid == None) || (context->dest_window &&
225             (GDK_WINDOW_XWINDOW (context->dest_window) == dest_xid))))
226         return context;
227       
228       tmp_list = tmp_list->next;
229     }
230
231   return NULL;
232 }
233
234 #endif
235
236 typedef struct {
237 #ifdef OLE2_DND
238   IDropTarget idt;
239 #endif
240   GdkDragContext *context;
241 } target_drag_context;
242
243 typedef struct {
244 #ifdef OLE2_DND
245   IDropSource ids;
246 #endif
247   GdkDragContext *context;
248 } source_drag_context;
249
250 #ifdef OLE2_DND
251
252 typedef struct {
253   IDataObject ido;
254   int ref_count;
255 } data_object;
256
257 typedef struct {
258   IEnumFORMATETC ief;
259   int ref_count;
260   int ix;
261 } enum_formats;
262
263 static enum_formats *enum_formats_new (void);
264
265 static ULONG STDMETHODCALLTYPE
266 idroptarget_addref (LPDROPTARGET This)
267 {
268   target_drag_context *ctx = (target_drag_context *) This;
269   GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
270   int ref_count = ++private->ref_count;
271
272   gdk_drag_context_ref (ctx->context);
273   GDK_NOTE (DND, g_print ("idroptarget_addref %#x %d\n", This, ref_count));
274   
275   return ref_count;
276 }
277
278 static HRESULT STDMETHODCALLTYPE
279 idroptarget_queryinterface (LPDROPTARGET This,
280                             REFIID       riid,
281                             LPVOID      *ppvObject)
282 {
283   GDK_NOTE (DND, g_print ("idroptarget_queryinterface %#x\n", This));
284
285   *ppvObject = NULL;
286
287   PRINT_GUID (riid);
288
289   if (IsEqualGUID (riid, &IID_IUnknown))
290     {
291       g_print ("...IUnknown\n");
292       idroptarget_addref (This);
293       *ppvObject = This;
294       return S_OK;
295     }
296   else if (IsEqualGUID (riid, &IID_IDropTarget))
297     {
298       g_print ("...IDropTarget\n");
299       idroptarget_addref (This);
300       *ppvObject = This;
301       return S_OK;
302     }
303   else
304     {
305       g_print ("...Huh?\n");
306       return E_NOINTERFACE;
307     }
308 }
309
310 static ULONG STDMETHODCALLTYPE
311 idroptarget_release (LPDROPTARGET This)
312 {
313   target_drag_context *ctx = (target_drag_context *) This;
314   GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
315   int ref_count = --private->ref_count;
316
317   gdk_drag_context_unref (ctx->context);
318   GDK_NOTE (DND, g_print ("idroptarget_release %#x %d\n", This, ref_count));
319
320   if (ref_count == 0)
321     g_free (This);
322
323   return ref_count;
324 }
325
326 static HRESULT STDMETHODCALLTYPE 
327 idroptarget_dragenter (LPDROPTARGET This,
328                        LPDATAOBJECT pDataObj,
329                        DWORD        grfKeyState,
330                        POINTL       pt,
331                        LPDWORD      pdwEffect)
332 {
333   GDK_NOTE (DND, g_print ("idroptarget_dragenter %#x\n", This));
334
335   return E_UNEXPECTED;
336 }
337
338 static HRESULT STDMETHODCALLTYPE
339 idroptarget_dragover (LPDROPTARGET This,
340                       DWORD        grfKeyState,
341                       POINTL       pt,
342                       LPDWORD      pdwEffect)
343 {
344   GDK_NOTE (DND, g_print ("idroptarget_dragover %#x\n", This));
345
346   return E_UNEXPECTED;
347 }
348
349 static HRESULT STDMETHODCALLTYPE
350 idroptarget_dragleave (LPDROPTARGET This)
351 {
352   GDK_NOTE (DND, g_print ("idroptarget_dragleave %#x\n", This));
353
354   return E_UNEXPECTED;
355 }
356
357 static HRESULT STDMETHODCALLTYPE
358 idroptarget_drop (LPDROPTARGET This,
359                   LPDATAOBJECT pDataObj,
360                   DWORD        grfKeyState,
361                   POINTL       pt,
362                   LPDWORD      pdwEffect)
363 {
364   GDK_NOTE (DND, g_print ("idroptarget_drop %#x\n", This));
365
366   return E_UNEXPECTED;
367 }
368
369 static ULONG STDMETHODCALLTYPE
370 idropsource_addref (LPDROPSOURCE This)
371 {
372   source_drag_context *ctx = (source_drag_context *) This;
373   GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
374
375   gdk_drag_context_ref (ctx->context);
376   GDK_NOTE (DND, g_print ("idropsource_addref %#x %d\n",
377                           This, private->ref_count));
378   
379   return private->ref_count;
380 }
381
382 static HRESULT STDMETHODCALLTYPE
383 idropsource_queryinterface (LPDROPSOURCE This,
384                             REFIID       riid,
385                             LPVOID      *ppvObject)
386 {
387   GDK_NOTE (DND, g_print ("idropsource_queryinterface %#x\n", This));
388
389   *ppvObject = NULL;
390
391   PRINT_GUID (riid);
392   if (IsEqualGUID (riid, &IID_IUnknown))
393     {
394       g_print ("...IUnknown\n");
395       idropsource_addref (This);
396       *ppvObject = This;
397       return S_OK;
398     }
399   else if (IsEqualGUID (riid, &IID_IDropSource))
400     {
401       g_print ("...IDropSource\n");
402       idropsource_addref (This);
403       *ppvObject = This;
404       return S_OK;
405     }
406   else
407     {
408       g_print ("...Huh?\n");
409       return E_NOINTERFACE;
410     }
411 }
412
413 static ULONG STDMETHODCALLTYPE
414 idropsource_release (LPDROPSOURCE This)
415 {
416   source_drag_context *ctx = (source_drag_context *) This;
417   GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
418   int ref_count = --private->ref_count;
419
420   gdk_drag_context_unref (ctx->context);
421   GDK_NOTE (DND, g_print ("idropsource_release %#x %d\n", This, ref_count));
422
423   if (ref_count == 0)
424     g_free (This);
425
426   return ref_count;
427 }
428
429 static HRESULT STDMETHODCALLTYPE
430 idropsource_querycontinuedrag (LPDROPSOURCE This,
431                                BOOL         fEscapePressed,
432                                DWORD        grfKeyState)
433 {
434   GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %#x\n", This));
435
436   return E_UNEXPECTED;
437 }
438
439 static HRESULT STDMETHODCALLTYPE
440 idropsource_givefeedback (LPDROPSOURCE This,
441                           DWORD        dwEffect)
442 {
443   GDK_NOTE (DND, g_print ("idropsource_givefeedback %#x\n", This));
444
445   return E_UNEXPECTED;
446 }
447
448 static ULONG STDMETHODCALLTYPE
449 idataobject_addref (LPDATAOBJECT This)
450 {
451   data_object *dobj = (data_object *) This;
452   int ref_count = ++dobj->ref_count;
453
454   GDK_NOTE (DND, g_print ("idataobject_addref %#x %d\n", This, ref_count));
455
456   return ref_count;
457 }
458
459 static HRESULT STDMETHODCALLTYPE
460 idataobject_queryinterface (LPDATAOBJECT This,
461                             REFIID       riid,
462                             LPVOID      *ppvObject)
463 {
464   GDK_NOTE (DND, g_print ("idataobject_queryinterface %#x\n", This));
465
466   *ppvObject = NULL;
467
468   PRINT_GUID (riid);
469   if (IsEqualGUID (riid, &IID_IUnknown))
470     {
471       g_print ("...IUnknown\n");
472       idataobject_addref (This);
473       *ppvObject = This;
474       return S_OK;
475     }
476   else if (IsEqualGUID (riid, &IID_IDataObject))
477     {
478       g_print ("...IDataObject\n");
479       idataobject_addref (This);
480       *ppvObject = This;
481       return S_OK;
482     }
483   else
484     {
485       g_print ("...Huh?\n");
486       return E_NOINTERFACE;
487     }
488 }
489
490 static ULONG STDMETHODCALLTYPE
491 idataobject_release (LPDATAOBJECT This)
492 {
493   data_object *dobj = (data_object *) This;
494   int ref_count = --dobj->ref_count;
495
496   GDK_NOTE (DND, g_print ("idataobject_release %#x %d\n", This, ref_count));
497
498   if (ref_count == 0)
499     g_free (This);
500
501   return ref_count;
502 }
503
504 static HRESULT STDMETHODCALLTYPE
505 idataobject_getdata (LPDATAOBJECT This,
506                      LPFORMATETC  pFormatEtc,
507                      LPSTGMEDIUM  pMedium)
508 {
509   GDK_NOTE (DND, g_print ("idataobject_getdata %#x\n", This));
510
511   return E_UNEXPECTED;
512 }
513
514 static HRESULT STDMETHODCALLTYPE
515 idataobject_getdatahere (LPDATAOBJECT This,
516                          LPFORMATETC  pFormatEtc,
517                          LPSTGMEDIUM  pMedium)
518 {
519   GDK_NOTE (DND, g_print ("idataobject_getdatahere %#x\n", This));
520
521   return E_UNEXPECTED;
522 }
523
524 static HRESULT STDMETHODCALLTYPE
525 idataobject_querygetdata (LPDATAOBJECT This,
526                           LPFORMATETC  pFormatEtc)
527 {
528   int i;
529
530   GDK_NOTE (DND, g_print ("idataobject_querygetdata %#x %#x", This, pFormatEtc->cfFormat));
531
532   for (i = 0; i < nformats; i++)
533     if (pFormatEtc->cfFormat == formats[i].cfFormat)
534       {
535         GDK_NOTE (DND, g_print (" S_OK\n"));
536         return S_OK;
537       }
538
539   GDK_NOTE (DND, g_print (" DV_E_FORMATETC\n"));
540   return DV_E_FORMATETC;
541 }
542
543 static HRESULT STDMETHODCALLTYPE
544 idataobject_getcanonicalformatetc (LPDATAOBJECT This,
545                                    LPFORMATETC  pFormatEtcIn,
546                                    LPFORMATETC  pFormatEtcOut)
547 {
548   GDK_NOTE (DND, g_print ("idataobject_getcanonicalformatetc %#x\n", This));
549
550   return E_FAIL;
551 }
552
553 static HRESULT STDMETHODCALLTYPE
554 idataobject_setdata (LPDATAOBJECT This,
555                      LPFORMATETC  pFormatEtc,
556                      LPSTGMEDIUM  pMedium,
557                      BOOL         fRelease)
558 {
559   GDK_NOTE (DND, g_print ("idataobject_setdata %#x\n", This));
560
561   return E_UNEXPECTED;
562 }
563
564 static HRESULT STDMETHODCALLTYPE
565 idataobject_enumformatetc (LPDATAOBJECT     This,
566                            DWORD            dwDirection,
567                            LPENUMFORMATETC *ppEnumFormatEtc)
568 {
569   GDK_NOTE (DND, g_print ("idataobject_enumformatetc %#x\n", This));
570
571   if (dwDirection != DATADIR_GET)
572     return E_NOTIMPL;
573
574   *ppEnumFormatEtc = &enum_formats_new ()->ief;
575   return S_OK;
576 }
577
578 static HRESULT STDMETHODCALLTYPE
579 idataobject_dadvise (LPDATAOBJECT This,
580                      LPFORMATETC  pFormatetc,
581                      DWORD        advf,
582                      LPADVISESINK pAdvSink,
583                      DWORD       *pdwConnection)
584 {
585   GDK_NOTE (DND, g_print ("idataobject_dadvise %#x\n", This));
586
587   return E_FAIL;
588 }
589
590 static HRESULT STDMETHODCALLTYPE
591 idataobject_dunadvise (LPDATAOBJECT This,
592                        DWORD         dwConnection)
593 {
594   GDK_NOTE (DND, g_print ("idataobject_dunadvise %#x\n", This));
595
596   return E_FAIL;
597 }
598
599 static HRESULT STDMETHODCALLTYPE
600 idataobject_enumdadvise (LPDATAOBJECT    This,
601                          LPENUMSTATDATA *ppenumAdvise)
602 {
603   GDK_NOTE (DND, g_print ("idataobject_enumdadvise %#x\n", This));
604
605   return E_FAIL;
606 }
607                 
608 static ULONG STDMETHODCALLTYPE
609 ienumformatetc_addref (LPENUMFORMATETC This)
610 {
611   enum_formats *en = (enum_formats *) This;
612   int ref_count = ++en->ref_count;
613
614   GDK_NOTE (DND, g_print ("ienumformatetc_addref %#x %d\n", This, ref_count));
615
616   return ref_count;
617 }
618
619 static HRESULT STDMETHODCALLTYPE
620 ienumformatetc_queryinterface (LPENUMFORMATETC This,
621                                REFIID          riid,
622                                LPVOID         *ppvObject)
623 {
624   GDK_NOTE (DND, g_print ("ienumformatetc_queryinterface %#x\n", This));
625
626   *ppvObject = NULL;
627
628   PRINT_GUID (riid);
629   if (IsEqualGUID (riid, &IID_IUnknown))
630     {
631       g_print ("...IUnknown\n");
632       ienumformatetc_addref (This);
633       *ppvObject = This;
634       return S_OK;
635     }
636   else if (IsEqualGUID (riid, &IID_IEnumFORMATETC))
637     {
638       g_print ("...IEnumFORMATETC\n");
639       ienumformatetc_addref (This);
640       *ppvObject = This;
641       return S_OK;
642     }
643   else
644     {
645       g_print ("...Huh?\n");
646       return E_NOINTERFACE;
647     }
648 }
649
650 static ULONG STDMETHODCALLTYPE
651 ienumformatetc_release (LPENUMFORMATETC This)
652 {
653   enum_formats *en = (enum_formats *) This;
654   int ref_count = --en->ref_count;
655
656   GDK_NOTE (DND, g_print ("ienumformatetc_release %#x %d\n", This, ref_count));
657
658   if (ref_count == 0)
659     g_free (This);
660
661   return ref_count;
662 }
663
664 static HRESULT STDMETHODCALLTYPE
665 ienumformatetc_next (LPENUMFORMATETC This,
666                      ULONG           celt,
667                      LPFORMATETC     elts,
668                      ULONG          *nelt)
669 {
670   enum_formats *en = (enum_formats *) This;
671   int i, n;
672
673   GDK_NOTE (DND, g_print ("ienumformatetc_next %#x %d %d\n", This, en->ix, celt));
674
675   n = 0;
676   for (i = 0; i < celt; i++)
677     {
678       if (en->ix >= nformats)
679         break;
680       elts[i] = formats[en->ix++];
681       n++;
682     }
683
684   if (nelt != NULL)
685     *nelt = n;
686
687   if (n == celt)
688     return S_OK;
689   else
690     return S_FALSE;
691 }
692
693 static HRESULT STDMETHODCALLTYPE
694 ienumformatetc_skip (LPENUMFORMATETC This,
695                      ULONG           celt)
696 {
697   enum_formats *en = (enum_formats *) This;
698
699   GDK_NOTE (DND, g_print ("ienumformatetc_skip %#x %d %d\n", This, en->ix, celt));
700   en->ix += celt;
701
702   return S_OK;
703 }
704
705 static HRESULT STDMETHODCALLTYPE
706 ienumformatetc_reset (LPENUMFORMATETC This)
707 {
708   enum_formats *en = (enum_formats *) This;
709
710   GDK_NOTE (DND, g_print ("ienumformatetc_reset %#x\n", This));
711
712   en->ix = 0;
713
714   return S_OK;
715 }
716
717 static HRESULT STDMETHODCALLTYPE
718 ienumformatetc_clone (LPENUMFORMATETC  This,
719                       LPENUMFORMATETC *ppEnumFormatEtc)
720 {
721   enum_formats *en = (enum_formats *) This;
722   enum_formats *new;
723
724   GDK_NOTE (DND, g_print ("ienumformatetc_clone %#x\n", This));
725
726   new = enum_formats_new ();
727
728   new->ix = en->ix;
729
730   *ppEnumFormatEtc = &new->ief;
731
732   return S_OK;
733 }
734
735 static IDropTargetVtbl idt_vtbl = {
736   idroptarget_queryinterface,
737   idroptarget_addref,
738   idroptarget_release,
739   idroptarget_dragenter,
740   idroptarget_dragover,
741   idroptarget_dragleave,
742   idroptarget_drop
743 };
744
745 static IDropSourceVtbl ids_vtbl = {
746   idropsource_queryinterface,
747   idropsource_addref,
748   idropsource_release,
749   idropsource_querycontinuedrag,
750   idropsource_givefeedback
751 };
752
753 static IDataObjectVtbl ido_vtbl = {
754   idataobject_queryinterface,
755   idataobject_addref,
756   idataobject_release,
757   idataobject_getdata,
758   idataobject_getdatahere,
759   idataobject_querygetdata,
760   idataobject_getcanonicalformatetc,
761   idataobject_setdata,
762   idataobject_enumformatetc,
763   idataobject_dadvise,
764   idataobject_dunadvise,
765   idataobject_enumdadvise
766 };
767
768 static IEnumFORMATETCVtbl ief_vtbl = {
769   ienumformatetc_queryinterface,
770   ienumformatetc_addref,
771   ienumformatetc_release,
772   ienumformatetc_next,
773   ienumformatetc_skip,
774   ienumformatetc_reset,
775   ienumformatetc_clone
776 };
777
778 #endif /* OLE2_DND */
779
780 static target_drag_context *
781 target_context_new (void)
782 {
783   target_drag_context *result;
784
785   result = g_new0 (target_drag_context, 1);
786
787 #ifdef OLE2_DND
788   result->idt.lpVtbl = &idt_vtbl;
789 #endif
790
791   result->context = gdk_drag_context_new ();
792   result->context->is_source = FALSE;
793
794   GDK_NOTE (DND, g_print ("target_context_new: %p\n", result));
795
796   return result;
797 }
798
799 static source_drag_context *
800 source_context_new (void)
801 {
802   source_drag_context *result;
803
804   result = g_new0 (source_drag_context, 1);
805
806 #ifdef OLE2_DND
807   result->ids.lpVtbl = &ids_vtbl;
808 #endif
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 #ifdef OLE2_DND
819 static data_object *
820 data_object_new (void)
821 {
822   data_object *result;
823
824   result = g_new0 (data_object, 1);
825
826   result->ido.lpVtbl = &ido_vtbl;
827   result->ref_count = 1;
828
829   GDK_NOTE (DND, g_print ("data_object_new: %#x\n", result));
830
831   return result;
832 }
833
834
835 static enum_formats *
836 enum_formats_new (void)
837 {
838   enum_formats *result;
839
840   result = g_new0 (enum_formats, 1);
841
842   result->ief.lpVtbl = &ief_vtbl;
843   result->ref_count = 1;
844   result->ix = 0;
845
846   GDK_NOTE (DND, g_print ("enum_formats_new: %#x\n", result));
847
848   return result;
849 }
850
851 #endif
852
853 /* From MS Knowledge Base article Q130698 */
854
855 /* resolve_link() fills the filename and path buffer
856  * with relevant information
857  * hWnd         - calling app's window handle.
858  *
859  * lpszLinkName - name of the link file passed into the function.
860  *
861  * lpszPath     - the buffer that will receive the file pathname.
862  */
863
864 static HRESULT 
865 resolve_link(HWND    hWnd,
866              LPCTSTR lpszLinkName,
867              LPSTR   lpszPath,
868              LPSTR   lpszDescription)
869 {
870   HRESULT hres;
871   IShellLink *psl;
872   WIN32_FIND_DATA wfd;
873
874   /* Assume Failure to start with: */
875   *lpszPath = 0;
876   if (lpszDescription)
877     *lpszDescription = 0;
878
879   /* Call CoCreateInstance to obtain the IShellLink interface
880    * pointer. This call fails if CoInitialize is not called, so it is
881    * assumed that CoInitialize has been called.
882    */
883
884   hres = CoCreateInstance (&CLSID_ShellLink,
885                            NULL,
886                            CLSCTX_INPROC_SERVER,
887                            &IID_IShellLink,
888                            (LPVOID *)&psl);
889   if (SUCCEEDED (hres))
890    {
891      IPersistFile *ppf;
892      
893      /* The IShellLink interface supports the IPersistFile
894       * interface. Get an interface pointer to it.
895       */
896      hres = psl->lpVtbl->QueryInterface (psl,
897                                          &IID_IPersistFile,
898                                          (LPVOID *) &ppf);
899      if (SUCCEEDED (hres))
900        {
901          WORD wsz[MAX_PATH];
902
903          /* Convert the given link name string to wide character string. */
904          MultiByteToWideChar (CP_ACP, 0,
905                               lpszLinkName,
906                               -1, wsz, MAX_PATH);
907          /* Load the file. */
908          hres = ppf->lpVtbl->Load (ppf, wsz, STGM_READ);
909          if (SUCCEEDED (hres))
910            {
911              /* Resolve the link by calling the Resolve()
912               * interface function.
913               */
914              hres = psl->lpVtbl->Resolve(psl,  hWnd,
915                                          SLR_ANY_MATCH |
916                                          SLR_NO_UI);
917              if (SUCCEEDED (hres))
918                {
919                  hres = psl->lpVtbl->GetPath (psl, lpszPath,
920                                               MAX_PATH,
921                                               (WIN32_FIND_DATA*)&wfd,
922                                               0);
923
924                  if (SUCCEEDED (hres) && lpszDescription != NULL)
925                    {
926                      hres = psl->lpVtbl->GetDescription (psl,
927                                                          lpszDescription,
928                                                          MAX_PATH );
929
930                      if (!SUCCEEDED (hres))
931                        return FALSE;
932                    }
933                }
934            }
935          ppf->lpVtbl->Release (ppf);
936        }
937      psl->lpVtbl->Release (psl);
938    }
939   return SUCCEEDED (hres);
940 }
941
942 static GdkFilterReturn
943 gdk_dropfiles_filter (GdkXEvent *xev,
944                       GdkEvent  *event,
945                       gpointer   data)
946 {
947   GdkDragContext *context;
948   GdkDragContextPrivateWin32 *private;
949   static GdkAtom text_uri_list_atom = GDK_NONE;
950   GString *result;
951   MSG *msg = (MSG *) xev;
952   HANDLE hdrop;
953   POINT pt;
954   gint nfiles, i;
955   guchar fileName[MAX_PATH], linkedFile[MAX_PATH];
956   
957   if (text_uri_list_atom == GDK_NONE)
958     text_uri_list_atom = gdk_atom_intern ("text/uri-list", FALSE);
959
960   if (msg->message == WM_DROPFILES)
961     {
962       GDK_NOTE (DND, g_print ("WM_DROPFILES: %#x\n", (guint) msg->hwnd));
963
964       context = gdk_drag_context_new ();
965       private = PRIVATE_DATA (context);
966       context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
967       context->is_source = FALSE;
968       context->source_window = gdk_parent_root;
969       context->dest_window = event->any.window;
970       gdk_drawable_ref (context->dest_window);
971       /* WM_DROPFILES drops are always file names */
972       context->targets =
973         g_list_append (NULL, GUINT_TO_POINTER (text_uri_list_atom));
974       current_dest_drag = context;
975
976       event->dnd.type = GDK_DROP_START;
977       event->dnd.context = current_dest_drag;
978       gdk_drag_context_ref (current_dest_drag);
979       
980       hdrop = (HANDLE) msg->wParam;
981       DragQueryPoint (hdrop, &pt);
982       ClientToScreen (msg->hwnd, &pt);
983
984       event->dnd.x_root = pt.x;
985       event->dnd.y_root = pt.y;
986       event->dnd.time = msg->time;
987
988       nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
989
990       result = g_string_new (NULL);
991       for (i = 0; i < nfiles; i++)
992         {
993           g_string_append (result, "file:");
994           DragQueryFile (hdrop, i, fileName, MAX_PATH);
995
996           /* Resolve shortcuts */
997           if (resolve_link (msg->hwnd, fileName, linkedFile, NULL))
998             {
999               g_string_append (result, linkedFile);
1000               GDK_NOTE (DND, g_print ("...%s link to %s\n",
1001                                       fileName, linkedFile));
1002             }
1003           else
1004             {
1005               g_string_append (result, fileName);
1006               GDK_NOTE (DND, g_print ("...%s\n", fileName));
1007             }
1008           g_string_append (result, "\015\012");
1009         }
1010       gdk_sel_prop_store (gdk_parent_root, text_uri_list_atom, 8,
1011                           result->str, result->len + 1);
1012
1013       DragFinish (hdrop);
1014       
1015       return GDK_FILTER_TRANSLATE;
1016     }
1017   else
1018     return GDK_FILTER_CONTINUE;
1019 }
1020
1021 /*************************************************************
1022  ************************** Public API ***********************
1023  *************************************************************/
1024
1025 void
1026 gdk_dnd_init (void)
1027 {
1028 #ifdef OLE2_DND
1029   HRESULT hres;
1030   hres = OleInitialize (NULL);
1031
1032   if (! SUCCEEDED (hres))
1033     g_error ("OleInitialize failed");
1034
1035   nformats = 2;
1036   formats = g_new (FORMATETC, nformats);
1037
1038   formats[0].cfFormat = CF_TEXT;
1039   formats[0].ptd = NULL;
1040   formats[0].dwAspect = DVASPECT_CONTENT;
1041   formats[0].lindex = -1;
1042   formats[0].tymed = TYMED_HGLOBAL;
1043   
1044   formats[1].cfFormat = CF_GDIOBJFIRST;
1045   formats[1].ptd = NULL;
1046   formats[1].dwAspect = DVASPECT_CONTENT;
1047   formats[1].lindex = -1;
1048   formats[1].tymed = TYMED_HGLOBAL;
1049 #endif
1050 }      
1051
1052 void
1053 gdk_win32_dnd_exit (void)
1054 {
1055 #ifdef OLE2_DND
1056   OleUninitialize ();
1057 #endif
1058 }
1059
1060 /* Source side */
1061
1062 static void
1063 gdk_drag_do_leave (GdkDragContext *context,
1064                    guint32         time)
1065 {
1066   if (context->dest_window)
1067     {
1068       GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
1069       gdk_drawable_unref (context->dest_window);
1070       context->dest_window = NULL;
1071     }
1072 }
1073
1074 GdkDragContext * 
1075 gdk_drag_begin (GdkWindow *window,
1076                 GList     *targets)
1077 {
1078   source_drag_context *ctx;
1079 #ifdef OLE2_DND
1080   GList *tmp_list;
1081   data_object *dobj;
1082   HRESULT hResult;
1083   DWORD dwEffect;
1084   HGLOBAL global;
1085   FORMATETC format;
1086   STGMEDIUM medium;
1087 #endif
1088
1089   g_return_val_if_fail (window != NULL, NULL);
1090
1091   GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
1092
1093   ctx = source_context_new ();
1094   ctx->context->protocol = GDK_DRAG_PROTO_OLE2;
1095   ctx->context->source_window = window;
1096   gdk_drawable_ref (window);
1097
1098 #ifdef OLE2_DND
1099   tmp_list = g_list_last (targets);
1100   ctx->context->targets = NULL;
1101   while (tmp_list)
1102     {
1103       ctx->context->targets = g_list_prepend (ctx->context->targets,
1104                                               tmp_list->data);
1105       tmp_list = tmp_list->prev;
1106     }
1107
1108   ctx->context->actions = 0;
1109
1110   dobj = data_object_new ();
1111
1112   global = GlobalAlloc (GMEM_FIXED, sizeof (ctx));
1113
1114   memcpy (&global, ctx, sizeof (ctx));
1115
1116   medium.tymed = TYMED_HGLOBAL;
1117   medium.hGlobal = global;
1118   medium.pUnkForRelease = NULL;
1119
1120   dobj->ido.lpVtbl->SetData (&dobj->ido, &formats[1], &medium, TRUE);
1121
1122   hResult = DoDragDrop (&dobj->ido, &ctx->ids, DROPEFFECT_MOVE|DROPEFFECT_COPY, &dwEffect);
1123
1124   GDK_NOTE (DND, g_print ("DoDragDrop returned %s\n",
1125                           (hResult == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" :
1126                            (hResult == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" :
1127                             (hResult == E_UNEXPECTED ? "E_UNEXPECTED" :
1128                              g_strdup_printf ("%#.8x", hResult))))));
1129
1130   dobj->ido.lpVtbl->Release (&dobj->ido);
1131   ctx->ids.lpVtbl->Release (&ctx->ids);
1132 #endif
1133   return ctx->context;
1134 }
1135
1136 guint32
1137 gdk_drag_get_protocol (guint32          xid,
1138                        GdkDragProtocol *protocol)
1139 {
1140   /* This isn't used */
1141   return 0;
1142 }
1143
1144 void
1145 gdk_drag_find_window (GdkDragContext  *context,
1146                       GdkWindow       *drag_window,
1147                       gint             x_root,
1148                       gint             y_root,
1149                       GdkWindow      **dest_window,
1150                       GdkDragProtocol *protocol)
1151 {
1152   HWND recipient;
1153   POINT pt;
1154
1155   GDK_NOTE (DND, g_print ("gdk_drag_find_window: %#x +%d+%d\n",
1156                           (drag_window ? (guint) GDK_WINDOW_HWND (drag_window) : 0),
1157                           x_root, y_root));
1158
1159   pt.x = x_root;
1160   pt.y = y_root;
1161   recipient = WindowFromPoint (pt);
1162   if (recipient == NULL)
1163     *dest_window = NULL;
1164   else
1165     {
1166       *dest_window = gdk_win32_handle_table_lookup (GPOINTER_TO_UINT(recipient));
1167       if (*dest_window)
1168         gdk_drawable_ref (*dest_window);
1169       *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
1170     }
1171 }
1172
1173 gboolean
1174 gdk_drag_motion (GdkDragContext *context,
1175                  GdkWindow      *dest_window,
1176                  GdkDragProtocol protocol,
1177                  gint            x_root, 
1178                  gint            y_root,
1179                  GdkDragAction   suggested_action,
1180                  GdkDragAction   possible_actions,
1181                  guint32         time)
1182 {
1183   GDK_NOTE (DND, g_print ("gdk_drag_motion\n"));
1184
1185   return FALSE;
1186 }
1187
1188 void
1189 gdk_drag_drop (GdkDragContext *context,
1190                guint32         time)
1191 {
1192   g_return_if_fail (context != NULL);
1193
1194   GDK_NOTE (DND, g_print ("gdk_drag_drop\n"));
1195 }
1196
1197 void
1198 gdk_drag_abort (GdkDragContext *context,
1199                 guint32         time)
1200 {
1201   g_return_if_fail (context != NULL);
1202
1203   GDK_NOTE (DND, g_print ("gdk_drag_abort\n"));
1204
1205   gdk_drag_do_leave (context, time);
1206 }
1207
1208 /* Destination side */
1209
1210 void
1211 gdk_drag_status (GdkDragContext *context,
1212                  GdkDragAction   action,
1213                  guint32         time)
1214 {
1215   GDK_NOTE (DND, g_print ("gdk_drag_status\n"));
1216 }
1217
1218 void 
1219 gdk_drop_reply (GdkDragContext *context,
1220                 gboolean        ok,
1221                 guint32         time)
1222 {
1223   GDK_NOTE (DND, g_print ("gdk_drop_reply\n"));
1224 }
1225
1226 void
1227 gdk_drop_finish (GdkDragContext *context,
1228                  gboolean        success,
1229                  guint32         time)
1230 {
1231   GDK_NOTE (DND, g_print ("gdk_drop_finish\n"));
1232 }
1233
1234 static GdkFilterReturn
1235 gdk_destroy_filter (GdkXEvent *xev,
1236                     GdkEvent  *event,
1237                     gpointer   data)
1238 {
1239 #ifdef OLE2_DND
1240   MSG *msg = (MSG *) xev;
1241
1242   if (msg->message == WM_DESTROY)
1243     {
1244       IDropTarget *idtp = (IDropTarget *) data;
1245
1246       GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %#x\n", msg->hwnd));
1247 #if 0
1248       idtp->lpVtbl->Release (idtp);
1249 #endif
1250       RevokeDragDrop (msg->hwnd);
1251       CoLockObjectExternal (idtp, FALSE, TRUE);
1252     }
1253 #endif
1254   return GDK_FILTER_CONTINUE;
1255 }
1256
1257 void
1258 gdk_window_register_dnd (GdkWindow *window)
1259 {
1260 #ifdef OLE2_DND
1261   target_drag_context *ctx;
1262   HRESULT hres;
1263 #endif
1264
1265   g_return_if_fail (window != NULL);
1266
1267   GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %#x\n",
1268                           (guint) GDK_WINDOW_HWND (window)));
1269
1270   /* We always claim to accept dropped files, but in fact we might not,
1271    * of course. This function is called in such a way that it cannot know
1272    * whether the window (widget) in question actually accepts files
1273    * (in gtk, data of type text/uri-list) or not.
1274    */
1275   gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
1276   DragAcceptFiles (GDK_WINDOW_HWND (window), TRUE);
1277
1278 #ifdef OLE2_DND
1279   /* Register for OLE2 d&d */
1280   ctx = target_context_new ();
1281   ctx->context->protocol = GDK_DRAG_PROTO_OLE2;
1282   hres = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUE, FALSE);
1283   if (!SUCCEEDED (hres))
1284     OTHER_API_FAILED ("CoLockObjectExternal");
1285   else
1286     {
1287       hres = RegisterDragDrop (GDK_WINDOW_HWND (window), &ctx->idt);
1288       if (hres == DRAGDROP_E_ALREADYREGISTERED)
1289         {
1290           g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
1291 #if 0
1292           ctx->idt.lpVtbl->Release (&ctx->idt);
1293 #endif
1294           CoLockObjectExternal ((IUnknown *) &ctx->idt, FALSE, FALSE);
1295         }
1296       else if (!SUCCEEDED (hres))
1297         OTHER_API_FAILED ("RegisterDragDrop");
1298       else
1299         {
1300           gdk_window_add_filter (window, gdk_destroy_filter, &ctx->idt);
1301         }
1302     }
1303 #endif
1304 }
1305
1306 /*************************************************************
1307  * gdk_drag_get_selection:
1308  *     Returns the selection atom for the current source window
1309  *   arguments:
1310  *
1311  *   results:
1312  *************************************************************/
1313
1314 GdkAtom
1315 gdk_drag_get_selection (GdkDragContext *context)
1316 {
1317   if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
1318     return gdk_win32_dropfiles_atom;
1319   else if (context->protocol == GDK_DRAG_PROTO_OLE2)
1320     return gdk_ole2_dnd_atom;
1321   else
1322     return GDK_NONE;
1323 }