]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkdnd-win32.c
Internal GDK error reporting changes: (gdk_win32_gdi_failed) New function
[~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 "gdkprivate.h"
39 #include "gdkwin32.h"
40
41 #ifdef OLE2_DND
42 #include <ole2.h>
43 #else
44 #include <objbase.h>
45 #endif
46
47 #ifdef _MSC_VER
48 #include <shlobj.h>
49 #include <shlguid.h>
50 #endif
51
52 #ifndef _MSC_VER
53 static IID IID_IUnknown = {
54   0x00000000, 0x0000, 0x0000, { 0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 } };
55 static IID IID_IDropSource = {
56   0x00000121, 0x0000, 0x0000, { 0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 } };
57 static IID IID_IDropTarget = {
58   0x00000122, 0x0000, 0x0000, { 0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 } };
59 #endif
60
61 #include <gdk/gdk.h>
62
63 typedef struct _GdkDragContextPrivate GdkDragContextPrivate;
64
65 typedef enum {
66   GDK_DRAG_STATUS_DRAG,
67   GDK_DRAG_STATUS_MOTION_WAIT,
68   GDK_DRAG_STATUS_ACTION_WAIT,
69   GDK_DRAG_STATUS_DROP
70 } GtkDragStatus;
71
72 typedef enum {
73   GDK_DRAG_SOURCE,
74   GDK_DRAG_TARGET
75 } GdkDragKind;
76
77 #ifdef OLE2_DND
78
79 #define PRINT_GUID(guid) \
80   g_print ("guid = %.08x-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
81            ((gulong *)  guid)[0], \
82            ((gushort *) guid)[2], \
83            ((gushort *) guid)[3], \
84            ((guchar *)  guid)[8], \
85            ((guchar *)  guid)[9], \
86            ((guchar *)  guid)[10], \
87            ((guchar *)  guid)[11], \
88            ((guchar *)  guid)[12], \
89            ((guchar *)  guid)[13], \
90            ((guchar *)  guid)[14], \
91            ((guchar *)  guid)[15]);
92
93
94 #endif /* OLE2_DND */
95
96 /* Structure that holds information about a drag in progress.
97  * this is used on both source and destination sides.
98  */
99 struct _GdkDragContextPrivate {
100   GdkDragContext context;
101
102   guint   ref_count;
103
104   guint16 last_x;               /* Coordinates from last event */
105   guint16 last_y;
106   HWND  dest_xid;
107   guint drag_status;            /* Current status of drag */
108 };
109
110 GdkDragContext *current_dest_drag = NULL;
111
112 /* Drag Contexts */
113
114 static GList *contexts;
115
116 GdkDragContext *
117 gdk_drag_context_new        (void)
118 {
119   GdkDragContextPrivate *result;
120
121   result = g_new0 (GdkDragContextPrivate, 1);
122
123   result->ref_count = 1;
124
125   contexts = g_list_prepend (contexts, result);
126
127   return (GdkDragContext *)result;
128 }
129
130 void
131 gdk_drag_context_ref (GdkDragContext *context)
132 {
133   g_return_if_fail (context != NULL);
134
135   ((GdkDragContextPrivate *)context)->ref_count++;
136 }
137
138 void
139 gdk_drag_context_unref (GdkDragContext *context)
140 {
141   GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
142   
143   g_return_if_fail (context != NULL);
144
145   private->ref_count--;
146
147   GDK_NOTE (DND, g_print ("gdk_drag_context_unref: %d%s\n",
148                           private->ref_count,
149                           (private->ref_count == 0 ? " freeing" : "")));
150
151   if (private->ref_count == 0)
152     {
153       g_dataset_destroy (private);
154       
155       g_list_free (context->targets);
156
157       if (context->source_window)
158         gdk_window_unref (context->source_window);
159
160       if (context->dest_window)
161         gdk_window_unref (context->dest_window);
162
163       contexts = g_list_remove (contexts, private);
164       g_free (private);
165     }
166 }
167
168 #if 0
169
170 static GdkDragContext *
171 gdk_drag_context_find (gboolean is_source,
172                        HWND     source_xid,
173                        HWND     dest_xid)
174 {
175   GList *tmp_list = contexts;
176   GdkDragContext *context;
177
178   while (tmp_list)
179     {
180       context = (GdkDragContext *)tmp_list->data;
181
182       if ((!context->is_source == !is_source) &&
183           ((source_xid == None) || (context->source_window &&
184             (GDK_WINDOW_XWINDOW (context->source_window) == source_xid))) &&
185           ((dest_xid == None) || (context->dest_window &&
186             (GDK_WINDOW_XWINDOW (context->dest_window) == dest_xid))))
187         return context;
188       
189       tmp_list = tmp_list->next;
190     }
191
192   return NULL;
193 }
194
195 #endif
196
197 typedef struct {
198 #ifdef OLE2_DND
199   IDropTarget idt;
200 #endif
201   GdkDragContext *context;
202 } target_drag_context;
203
204 typedef struct {
205 #ifdef OLE2_DND
206   IDropSource ids;
207 #endif
208   GdkDragContext *context;
209 } source_drag_context;
210
211 #ifdef OLE2_DND
212
213 static ULONG STDMETHODCALLTYPE
214 m_add_ref_target (IDropTarget __RPC_FAR *This)
215 {
216   target_drag_context *ctx = (target_drag_context *) This;
217   GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
218
219   GDK_NOTE (DND, g_print ("m_add_ref_target\n"));
220   gdk_drag_context_ref (ctx->context);
221   
222   return private->ref_count;
223 }
224
225 static HRESULT STDMETHODCALLTYPE
226 m_query_interface_target (IDropTarget __RPC_FAR *This,
227                           REFIID riid,
228                           void __RPC_FAR *__RPC_FAR *ppvObject)
229 {
230   GDK_NOTE (DND, g_print ("m_query_interface_target\n"));
231
232   *ppvObject = NULL;
233
234   PRINT_GUID (riid);
235
236   if (IsEqualGUID (riid, &IID_IUnknown))
237     {
238       g_print ("...IUnknown\n");
239       m_add_ref_target (This);
240       *ppvObject = This;
241       return S_OK;
242     }
243   else if (IsEqualGUID (riid, &IID_IDropTarget))
244     {
245       g_print ("...IDropTarget\n");
246       m_add_ref_target (This);
247       *ppvObject = This;
248       return S_OK;
249     }
250   else
251     {
252       g_print ("...Huh?\n");
253       return E_NOINTERFACE;
254     }
255 }
256
257 static ULONG STDMETHODCALLTYPE
258 m_release_target (IDropTarget __RPC_FAR *This)
259 {
260   target_drag_context *ctx = (target_drag_context *) This;
261   GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
262
263   GDK_NOTE (DND, g_print ("m_release_target\n"));
264   gdk_drag_context_unref (ctx->context);
265
266   if (private->ref_count == 1)
267     {
268       gdk_drag_context_unref (ctx->context);
269       return 0;
270     }
271   else
272     return private->ref_count - 1;
273 }
274
275 static HRESULT STDMETHODCALLTYPE 
276 m_drag_enter (IDropTarget __RPC_FAR *This,
277               IDataObject __RPC_FAR *pDataObj,
278               DWORD grfKeyState,
279               POINTL pt,
280               DWORD __RPC_FAR *pdwEffect)
281 {
282   GDK_NOTE (DND, g_print ("m_drag_enter\n"));
283   return E_UNEXPECTED;
284 }
285
286 static HRESULT STDMETHODCALLTYPE
287 m_drag_over (IDropTarget __RPC_FAR *This,
288              DWORD grfKeyState,
289              POINTL pt,
290              DWORD __RPC_FAR *pdwEffect)
291 {
292   GDK_NOTE (DND, g_print ("m_drag_over\n"));
293   return E_UNEXPECTED;
294 }
295
296 static HRESULT STDMETHODCALLTYPE
297 m_drag_leave (IDropTarget __RPC_FAR *This)
298 {
299   GDK_NOTE (DND, g_print ("m_drag_leave\n"));
300   return E_UNEXPECTED;
301 }
302
303 static HRESULT STDMETHODCALLTYPE
304 m_drop (IDropTarget __RPC_FAR *This,
305         IDataObject __RPC_FAR *pDataObj,
306         DWORD grfKeyState,
307         POINTL pt,
308         DWORD __RPC_FAR *pdwEffect)
309 {
310   GDK_NOTE (DND, g_print ("m_drop\n"));
311   return E_UNEXPECTED;
312 }
313
314 static ULONG STDMETHODCALLTYPE
315 m_add_ref_source (IDropSource __RPC_FAR *This)
316 {
317   source_drag_context *ctx = (source_drag_context *) This;
318   GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
319
320   GDK_NOTE (DND, g_print ("m_add_ref_source\n"));
321   gdk_drag_context_ref (ctx->context);
322   
323   return private->ref_count;
324 }
325
326 static HRESULT STDMETHODCALLTYPE
327 m_query_interface_source (IDropSource __RPC_FAR *This,
328                           REFIID riid,
329                           void __RPC_FAR *__RPC_FAR *ppvObject)
330 {
331   GDK_NOTE (DND, g_print ("m_query_interface_source\n"));
332
333   *ppvObject = NULL;
334
335   PRINT_GUID (riid);
336   if (IsEqualGUID (riid, &IID_IUnknown))
337     {
338       g_print ("...IUnknown\n");
339       m_add_ref_source (This);
340       *ppvObject = This;
341       return S_OK;
342     }
343   else if (IsEqualGUID (riid, &IID_IDropSource))
344     {
345       g_print ("...IDropSource\n");
346       m_add_ref_source (This);
347       *ppvObject = This;
348       return S_OK;
349     }
350   else
351     {
352       g_print ("...Huh?\n");
353       return E_NOINTERFACE;
354     }
355 }
356
357 static ULONG STDMETHODCALLTYPE
358 m_release_source (IDropSource __RPC_FAR *This)
359 {
360   source_drag_context *ctx = (source_drag_context *) This;
361   GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
362
363   GDK_NOTE (DND, g_print ("m_release_source\n"));
364   gdk_drag_context_unref (ctx->context);
365
366   if (private->ref_count == 1)
367     {
368       gdk_drag_context_unref (ctx->context);
369       return 0;
370     }
371   else
372     return private->ref_count - 1;
373 }
374
375 static HRESULT STDMETHODCALLTYPE
376 m_query_continue_drag (IDropSource __RPC_FAR *This,
377                        BOOL fEscapePressed,
378                        DWORD grfKeyState)
379 {
380   GDK_NOTE (DND, g_print ("m_query_continue_drag\n"));
381   return E_UNEXPECTED;
382 }
383
384 static HRESULT STDMETHODCALLTYPE
385 m_give_feedback (IDropSource __RPC_FAR *This,
386                  DWORD dwEffect)
387 {
388   GDK_NOTE (DND, g_print ("m_give_feedback\n"));
389   return E_UNEXPECTED;
390 }
391
392 static HRESULT STDMETHODCALLTYPE
393 m_query_interface_object (IDataObject __RPC_FAR *This,
394                           REFIID riid,
395                           void __RPC_FAR *__RPC_FAR *ppvObject)
396 {
397   return E_UNEXPECTED;
398 }
399
400 static ULONG STDMETHODCALLTYPE
401 m_add_ref_object (IDataObject __RPC_FAR *This)
402 {
403   return E_UNEXPECTED;
404 }
405
406 static ULONG STDMETHODCALLTYPE
407 m_release_object (IDataObject __RPC_FAR *This)
408 {
409   return E_UNEXPECTED;
410 }
411
412 static HRESULT STDMETHODCALLTYPE
413 m_get_data (IDataObject __RPC_FAR *This,
414             FORMATETC *pFormatEtc,
415             STGMEDIUM *pMedium)
416 {
417   return E_UNEXPECTED;
418 }
419
420 static HRESULT STDMETHODCALLTYPE
421 m_get_data_here (IDataObject __RPC_FAR *This,
422                  FORMATETC *pFormatEtc,
423                  STGMEDIUM *pMedium)
424 {
425   return E_UNEXPECTED;
426 }
427
428 static HRESULT STDMETHODCALLTYPE
429 m_query_get_data (IDataObject __RPC_FAR *This,
430                   FORMATETC *pFormatEtc)
431 {
432   return E_UNEXPECTED;
433 }
434
435 static HRESULT STDMETHODCALLTYPE
436 m_get_canonical_format_etc (IDataObject __RPC_FAR *This,
437                             FORMATETC *pFormatEtcIn,
438                             FORMATETC *pFormatEtcOut)
439 {
440   return E_UNEXPECTED;
441 }
442
443 static HRESULT STDMETHODCALLTYPE
444 m_set_data (IDataObject __RPC_FAR *This,
445             FORMATETC *pFormatEtc,
446             STGMEDIUM *pMedium,
447             BOOL fRelease)
448 {
449   return E_UNEXPECTED;
450 }
451
452 static HRESULT STDMETHODCALLTYPE
453 m_enum_format_etc (IDataObject __RPC_FAR *This,
454                    DWORD dwDirection,
455                    IEnumFORMATETC **ppEnumFormatEtc)
456 {
457   return E_UNEXPECTED;
458 }
459
460 static HRESULT STDMETHODCALLTYPE
461  m_d_advise (IDataObject __RPC_FAR *This,
462              FORMATETC *pFormatetc,
463              DWORD advf,
464              IAdviseSink *pAdvSink,
465              DWORD *pdwConnection)
466 {
467   return E_UNEXPECTED;
468 }
469
470 static HRESULT STDMETHODCALLTYPE
471  m_d_unadvise (IDataObject __RPC_FAR *This,
472                DWORD dwConnection)
473 {
474   return E_UNEXPECTED;
475 }
476
477 static HRESULT STDMETHODCALLTYPE
478 m_enum_d_advise (IDataObject __RPC_FAR *This,
479                  IEnumSTATDATA **ppenumAdvise)
480 {
481   return E_UNEXPECTED;
482 }
483                 
484 static IDropTargetVtbl idt_vtbl = {
485   m_query_interface_target,
486   m_add_ref_target,
487   m_release_target,
488   m_drag_enter,
489   m_drag_over,
490   m_drag_leave,
491   m_drop
492 };
493
494 static IDropSourceVtbl ids_vtbl = {
495   m_query_interface_source,
496   m_add_ref_source,
497   m_release_source,
498   m_query_continue_drag,
499   m_give_feedback
500 };
501
502 static IDataObjectVtbl ido_vtbl = {
503   m_query_interface_object,
504   m_add_ref_object,
505   m_release_object,
506   m_get_data,
507   m_get_data_here,
508   m_query_get_data,
509   m_get_canonical_format_etc,
510   m_set_data,
511   m_enum_format_etc,
512   m_d_advise,
513   m_d_unadvise,
514   m_enum_d_advise
515 };
516
517 #endif /* OLE2_DND */
518
519 static target_drag_context *
520 target_context_new (void)
521 {
522   target_drag_context *result;
523
524   result = g_new0 (target_drag_context, 1);
525
526 #ifdef OLE2_DND
527   result->idt.lpVtbl = &idt_vtbl;
528 #endif
529
530   result->context = gdk_drag_context_new ();
531
532   return result;
533 }
534
535 static source_drag_context *
536 source_context_new (void)
537 {
538   source_drag_context *result;
539
540   result = g_new0 (source_drag_context, 1);
541
542 #ifdef OLE2_DND
543   result->ids.lpVtbl = &ids_vtbl;
544 #endif
545
546   result->context = gdk_drag_context_new ();
547
548   return result;
549 }
550
551 #ifdef _MSC_VER
552
553 /* From MS Knowledge Base article Q130698 */
554
555 /* resolve_link() fills the filename and path buffer
556  * with relevant information
557  * hWnd         - calling app's window handle.
558  *
559  * lpszLinkName - name of the link file passed into the function.
560  *
561  * lpszPath     - the buffer that will receive the file pathname.
562  */
563
564 static HRESULT 
565 resolve_link(HWND hWnd,
566              LPCTSTR lpszLinkName,
567              LPSTR lpszPath,
568              LPSTR lpszDescription)
569 {
570   HRESULT hres;
571   IShellLink *psl;
572   WIN32_FIND_DATA wfd;
573
574   /* Assume Failure to start with: */
575   *lpszPath = 0;
576   if (lpszDescription)
577     *lpszDescription = 0;
578
579   /* Call CoCreateInstance to obtain the IShellLink interface
580    * pointer. This call fails if CoInitialize is not called, so it is
581    * assumed that CoInitialize has been called.
582    */
583
584   hres = CoCreateInstance (&CLSID_ShellLink,
585                            NULL,
586                            CLSCTX_INPROC_SERVER,
587                            &IID_IShellLink,
588                            (LPVOID *)&psl);
589   if (SUCCEEDED (hres))
590    {
591      IPersistFile *ppf;
592      
593      /* The IShellLink interface supports the IPersistFile
594       * interface. Get an interface pointer to it.
595       */
596      hres = psl->lpVtbl->QueryInterface (psl,
597                                          &IID_IPersistFile,
598                                          (LPVOID *) &ppf);
599      if (SUCCEEDED (hres))
600        {
601          WORD wsz[MAX_PATH];
602
603          /* Convert the given link name string to wide character string. */
604          MultiByteToWideChar (CP_ACP, 0,
605                               lpszLinkName,
606                               -1, wsz, MAX_PATH);
607          /* Load the file. */
608          hres = ppf->lpVtbl->Load (ppf, wsz, STGM_READ);
609          if (SUCCEEDED (hres))
610            {
611              /* Resolve the link by calling the Resolve()
612               * interface function.
613               */
614              hres = psl->lpVtbl->Resolve(psl,  hWnd,
615                                          SLR_ANY_MATCH |
616                                          SLR_NO_UI);
617              if (SUCCEEDED (hres))
618                {
619                  hres = psl->lpVtbl->GetPath (psl, lpszPath,
620                                               MAX_PATH,
621                                               (WIN32_FIND_DATA*)&wfd,
622                                               0);
623
624                  if (SUCCEEDED (hres) && lpszDescription != NULL)
625                    {
626                      hres = psl->lpVtbl->GetDescription (psl,
627                                                          lpszDescription,
628                                                          MAX_PATH );
629
630                      if (!SUCCEEDED (hres))
631                        return FALSE;
632                    }
633                }
634            }
635          ppf->lpVtbl->Release (ppf);
636        }
637      psl->lpVtbl->Release (psl);
638    }
639   return SUCCEEDED (hres);
640 }
641
642 #else
643
644 #define resolve_link(hWnd, lpszLinkName, lpszPath, lpszDescription) FALSE
645
646 #endif
647
648 static GdkFilterReturn
649 gdk_dropfiles_filter (GdkXEvent *xev,
650                       GdkEvent  *event,
651                       gpointer   data)
652 {
653   GdkDragContext *context;
654   GdkDragContextPrivate *private;
655   static GdkAtom text_uri_list_atom = GDK_NONE;
656   GString *result;
657   MSG *msg = (MSG *) xev;
658   HANDLE hdrop;
659   POINT pt;
660   gint nfiles, i, k;
661   guchar fileName[MAX_PATH], linkedFile[MAX_PATH];
662   
663   if (text_uri_list_atom == GDK_NONE)
664     text_uri_list_atom = gdk_atom_intern ("text/uri-list", FALSE);
665
666   if (msg->message == WM_DROPFILES)
667     {
668       GDK_NOTE (DND, g_print ("WM_DROPFILES: %#x\n", msg->hwnd));
669
670       context = gdk_drag_context_new ();
671       private = (GdkDragContextPrivate *) context;
672       context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
673       context->is_source = FALSE;
674       context->source_window = gdk_parent_root;
675       context->dest_window = event->any.window;
676       gdk_window_ref (context->dest_window);
677       /* WM_DROPFILES drops are always file names */
678       context->targets =
679         g_list_append (NULL, GUINT_TO_POINTER (text_uri_list_atom));
680       current_dest_drag = context;
681
682       event->dnd.type = GDK_DROP_START;
683       event->dnd.context = current_dest_drag;
684       gdk_drag_context_ref (current_dest_drag);
685       
686       hdrop = (HANDLE) msg->wParam;
687       DragQueryPoint (hdrop, &pt);
688       ClientToScreen (msg->hwnd, &pt);
689
690       event->dnd.x_root = pt.x;
691       event->dnd.y_root = pt.y;
692       event->dnd.time = msg->time;
693
694       nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
695
696       result = g_string_new (NULL);
697       for (i = 0; i < nfiles; i++)
698         {
699           g_string_append (result, "file:");
700           DragQueryFile (hdrop, i, fileName, MAX_PATH);
701
702           /* Resolve shortcuts */
703           if (resolve_link (msg->hwnd, fileName, linkedFile, NULL))
704             {
705               g_string_append (result, linkedFile);
706               GDK_NOTE (DND, g_print ("...%s link to %s\n",
707                                       fileName, linkedFile));
708             }
709           else
710             {
711               g_string_append (result, fileName);
712               GDK_NOTE (DND, g_print ("...%s\n", fileName));
713             }
714           g_string_append (result, "\015\012");
715         }
716       gdk_sel_prop_store (gdk_parent_root, text_uri_list_atom, 8,
717                           result->str, result->len + 1);
718
719       DragFinish (hdrop);
720       
721       return GDK_FILTER_TRANSLATE;
722     }
723   else
724     return GDK_FILTER_CONTINUE;
725 }
726
727 /*************************************************************
728  ************************** Public API ***********************
729  *************************************************************/
730
731 void
732 gdk_dnd_init (void)
733 {
734   HRESULT hres;
735 #ifdef OLE2_DND
736   hres = OleInitialize (NULL);
737
738   if (! SUCCEEDED (hres))
739     g_error ("OleInitialize failed");
740 #endif
741 }      
742
743 void
744 gdk_win32_dnd_exit (void)
745 {
746 #ifdef OLE2_DND
747   OleUninitialize ();
748 #endif
749 }
750
751 /* Source side */
752
753 static void
754 gdk_drag_do_leave (GdkDragContext *context, guint32 time)
755 {
756   if (context->dest_window)
757     {
758       GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
759       gdk_window_unref (context->dest_window);
760       context->dest_window = NULL;
761     }
762 }
763
764 GdkDragContext * 
765 gdk_drag_begin (GdkWindow     *window,
766                 GList         *targets)
767 {
768   GList *tmp_list;
769   source_drag_context *ctx;
770   
771   g_return_val_if_fail (window != NULL, NULL);
772
773   GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
774
775   ctx = source_context_new ();
776   ctx->context->is_source = TRUE;
777   ctx->context->source_window = window;
778   gdk_window_ref (window);
779
780   tmp_list = g_list_last (targets);
781   ctx->context->targets = NULL;
782   while (tmp_list)
783     {
784       ctx->context->targets = g_list_prepend (ctx->context->targets,
785                                               tmp_list->data);
786       tmp_list = tmp_list->prev;
787     }
788
789   ctx->context->actions = 0;
790
791 #if 0
792   DoDragDrop (...);
793 #endif
794   return ctx->context;
795 }
796
797 guint32
798 gdk_drag_get_protocol (guint32          xid,
799                        GdkDragProtocol *protocol)
800 {
801   /* This isn't used */
802   return 0;
803 }
804
805 void
806 gdk_drag_find_window (GdkDragContext  *context,
807                       GdkWindow       *drag_window,
808                       gint             x_root,
809                       gint             y_root,
810                       GdkWindow      **dest_window,
811                       GdkDragProtocol *protocol)
812 {
813   GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
814   GdkDrawablePrivate *drag_window_private = (GdkDrawablePrivate*) drag_window;
815   HWND recipient;
816   POINT pt;
817
818   GDK_NOTE (DND, g_print ("gdk_drag_find_window: %#x +%d+%d\n",
819                           (drag_window ? GDK_DRAWABLE_XID (drag_window) : 0),
820                           x_root, y_root));
821
822   pt.x = x_root;
823   pt.y = y_root;
824   recipient = WindowFromPoint (pt);
825   if (recipient == NULL)
826     *dest_window = NULL;
827   else
828     {
829       *dest_window = gdk_window_lookup (recipient);
830       if (*dest_window)
831         gdk_window_ref (*dest_window);
832       *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
833     }
834 }
835
836 gboolean
837 gdk_drag_motion (GdkDragContext *context,
838                  GdkWindow      *dest_window,
839                  GdkDragProtocol protocol,
840                  gint            x_root, 
841                  gint            y_root,
842                  GdkDragAction   suggested_action,
843                  GdkDragAction   possible_actions,
844                  guint32         time)
845 {
846   return FALSE;
847 }
848
849 void
850 gdk_drag_drop (GdkDragContext *context,
851                guint32         time)
852 {
853   g_return_if_fail (context != NULL);
854
855   g_warning ("gdk_drag_drop: not implemented\n");
856 }
857
858 void
859 gdk_drag_abort (GdkDragContext *context,
860                 guint32         time)
861 {
862   g_return_if_fail (context != NULL);
863
864   gdk_drag_do_leave (context, time);
865 }
866
867 /* Destination side */
868
869 void
870 gdk_drag_status (GdkDragContext   *context,
871                  GdkDragAction     action,
872                  guint32           time)
873 {
874   GDK_NOTE (DND, g_print ("gdk_drag_status\n"));
875 }
876
877 void 
878 gdk_drop_reply (GdkDragContext   *context,
879                 gboolean          ok,
880                 guint32           time)
881 {
882 }
883
884 void
885 gdk_drop_finish (GdkDragContext   *context,
886                  gboolean          success,
887                  guint32           time)
888 {
889 }
890
891 static GdkFilterReturn
892 gdk_destroy_filter (GdkXEvent *xev,
893                     GdkEvent  *event,
894                     gpointer   data)
895 {
896 #ifdef OLE2_DND
897   MSG *msg = (MSG *) xev;
898
899   if (msg->message == WM_DESTROY)
900     {
901       IDropTarget *idtp = (IDropTarget *) data;
902
903       GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %#x\n", msg->hwnd));
904       RevokeDragDrop (msg->hwnd);
905       CoLockObjectExternal (idtp, FALSE, TRUE);
906     }
907 #endif
908   return GDK_FILTER_CONTINUE;
909 }
910
911 void
912 gdk_window_register_dnd (GdkWindow      *window)
913 {
914 #ifdef OLE2_DND
915   target_drag_context *context;
916   HRESULT hres;
917 #endif
918
919   g_return_if_fail (window != NULL);
920
921   GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %#x\n",
922                           GDK_DRAWABLE_XID (window)));
923
924   /* We always claim to accept dropped files, but in fact we might not,
925    * of course. This function is called in such a way that it cannot know
926    * whether the window (widget) in question actually accepts files
927    * (in gtk, data of type text/uri-list) or not.
928    */
929   gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
930   DragAcceptFiles (GDK_DRAWABLE_XID (window), TRUE);
931
932 #ifdef OLE2_DND
933   /* Register for OLE2 d&d */
934   context = target_context_new ();
935   hres = CoLockObjectExternal ((IUnknown *) &context->idt, TRUE, FALSE);
936   if (!SUCCEEDED (hres))
937     OTHER_API_FAILED ("CoLockObjectExternal");
938   else
939     {
940       hres = RegisterDragDrop (GDK_DRAWABLE_XID (window), &context->idt);
941       if (hres == DRAGDROP_E_ALREADYREGISTERED)
942         {
943           g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
944           CoLockObjectExternal ((IUnknown *) &context->idt, FALSE, FALSE);
945         }
946       else if (!SUCCEEDED (hres))
947         OTHER_API_FAILED ("RegisterDragDrop");
948       else
949         {
950           gdk_window_add_filter (window, gdk_destroy_filter, &context->idt);
951         }
952     }
953 #endif
954 }
955
956 /*************************************************************
957  * gdk_drag_get_selection:
958  *     Returns the selection atom for the current source window
959  *   arguments:
960  *
961  *   results:
962  *************************************************************/
963
964 GdkAtom
965 gdk_drag_get_selection (GdkDragContext *context)
966 {
967   if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
968     return gdk_win32_dropfiles_atom;
969   else if (context->protocol == GDK_DRAG_PROTO_OLE2)
970     return gdk_ole2_dnd_atom;
971   else
972     return GDK_NONE;
973 }