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