]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/ole/droptgt.cpp
Fix crash in wxDC::GetMultiLineTextExtent() after last commit.
[wxWidgets.git] / src / msw / ole / droptgt.cpp
index 7101ef5eb2c2c06913e22501fb1506b6c98c8cbc..4f0b716dde85f6b0974fd0a8531144ff297755b7 100644 (file)
@@ -1,12 +1,12 @@
 ///////////////////////////////////////////////////////////////////////////////
-// Name:        ole/droptgt.cpp
+// Name:        src/msw/ole/droptgt.cpp
 // Purpose:     wxDropTarget implementation
 // Author:      Vadim Zeitlin
 // Modified by:
 // Created:
 // RCS-ID:      $Id$
 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
-// Licence:     wxWindows license
+// Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 
 // ============================================================================
 // headers
 // ----------------------------------------------------------------------------
 
-#ifdef __GNUG__
-#pragma implementation "droptgt.h"
-#endif
-
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 #if defined(__BORLANDC__)
-#pragma hdrstop
+    #pragma hdrstop
 #endif
 
-#include "wx/setup.h"
+#if wxUSE_OLE && wxUSE_DRAG_AND_DROP
+
+#ifndef WX_PRECOMP
+    #include "wx/msw/wrapwin.h"
+    #include "wx/log.h"
+#endif
 
-#if wxUSE_DRAG_AND_DROP
+#include "wx/msw/private.h"
 
-#include "wx/log.h"
+#ifdef __WXWINCE__
+    #include <winreg.h>
+    #include <ole2.h>
+#endif
 
 #ifdef __WIN32__
-    #ifndef __GNUWIN32__
+    #if !defined(__GNUWIN32__) || wxUSE_NORLANDER_HEADERS
         #include <shlobj.h>            // for DROPFILES structure
     #endif
 #else
     #include <shellapi.h>
 #endif
 
-#include "wx/dataobj.h"
-#include "wx/msw/ole/droptgt.h"
-
-#ifndef __WIN32__
-    #include <ole2.h>
-    #include <olestd.h>
-#endif
+#include "wx/dnd.h"
 
 #include "wx/msw/ole/oleutils.h"
 
+#include <initguid.h>
+
+// Some (very) old SDKs don't define IDropTargetHelper, so define our own
+// version of it here.
+struct wxIDropTargetHelper : public IUnknown
+{
+    virtual HRESULT STDMETHODCALLTYPE DragEnter(HWND hwndTarget,
+                                                IDataObject *pDataObject,
+                                                POINT *ppt,
+                                                DWORD dwEffect) = 0;
+    virtual HRESULT STDMETHODCALLTYPE DragLeave() = 0;
+    virtual HRESULT STDMETHODCALLTYPE DragOver(POINT *ppt, DWORD dwEffect) = 0;
+    virtual HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObject,
+                                           POINT *ppt,
+                                           DWORD dwEffect) = 0;
+    virtual HRESULT STDMETHODCALLTYPE Show(BOOL fShow) = 0;
+};
+
+namespace
+{
+    DEFINE_GUID(wxCLSID_DragDropHelper,
+                0x4657278A,0x411B,0x11D2,0x83,0x9A,0x00,0xC0,0x4F,0xD9,0x18,0xD0);
+    DEFINE_GUID(wxIID_IDropTargetHelper,
+                0x4657278B,0x411B,0x11D2,0x83,0x9A,0x00,0xC0,0x4F,0xD9,0x18,0xD0);
+}
+
 // ----------------------------------------------------------------------------
 // IDropTarget interface: forward all interesting things to wxDropTarget
 // (the name is unfortunate, but wx_I_DropTarget is not at all the same thing
 class wxIDropTarget : public IDropTarget
 {
 public:
-  wxIDropTarget(wxDropTarget *p);
- ~wxIDropTarget();
+    wxIDropTarget(wxDropTarget *p);
   virtual ~wxIDropTarget();
 
-  // IDropTarget methods
-  STDMETHODIMP DragEnter(LPDATAOBJECT, DWORD, POINTL, LPDWORD);
-  STDMETHODIMP DragOver(DWORD, POINTL, LPDWORD);
-  STDMETHODIMP DragLeave(void);
-  STDMETHODIMP Drop(LPDATAOBJECT, DWORD, POINTL, LPDWORD);
+    // accessors for wxDropTarget
+    HWND GetHWND() const { return m_hwnd; }
+    void SetHwnd(HWND hwnd) { m_hwnd = hwnd; }
 
-  // we assume that if QueryGetData() returns S_OK, than we can really get data
-  // in this format, so we remember here the format for which QueryGetData()
-  // succeeded
-  void SetSupportedFormat(wxDataFormat cfFormat) { m_cfFormat = cfFormat; }
+    // IDropTarget methods
+    STDMETHODIMP DragEnter(LPDATAOBJECT, DWORD, POINTL, LPDWORD);
+    STDMETHODIMP DragOver(DWORD, POINTL, LPDWORD);
+    STDMETHODIMP DragLeave();
+    STDMETHODIMP Drop(LPDATAOBJECT, DWORD, POINTL, LPDWORD);
 
-  DECLARE_IUNKNOWN_METHODS;
+    DECLARE_IUNKNOWN_METHODS;
 
 protected:
-  IDataObject   *m_pIDataObject;  // !NULL between DragEnter and DragLeave/Drop
-  wxDropTarget  *m_pTarget;       // the real target (we're just a proxy)
+    IDataObject  *m_pIDataObject; // !NULL between DragEnter and DragLeave/Drop
+    wxDropTarget *m_pTarget;      // the real target (we're just a proxy)
 
-  wxDataFormat   m_cfFormat;      // the format in which to ask for data
+    HWND          m_hwnd;         // window we're associated with
 
-private:
-  static inline DWORD GetDropEffect(DWORD flags);
+    // get default drop effect for given keyboard flags
+    static DWORD GetDropEffect(DWORD flags, wxDragResult defaultAction, DWORD pdwEffect);
+
+    wxDECLARE_NO_COPY_CLASS(wxIDropTarget);
 };
 
+// ----------------------------------------------------------------------------
+// private functions
+// ----------------------------------------------------------------------------
+
+static wxDragResult ConvertDragEffectToResult(DWORD dwEffect);
+static DWORD ConvertDragResultToEffect(wxDragResult result);
+
 // ============================================================================
 // wxIDropTarget implementation
 // ============================================================================
 
-// Name    : static wxDropTarget::GetDropEffect
+// Name    : static wxIDropTarget::GetDropEffect
 // Purpose : determine the drop operation from keyboard/mouse state.
 // Returns : DWORD combined from DROPEFFECT_xxx constants
-// Params  : [in] DWORD flags       kbd & mouse flags as passed to
-//                                  IDropTarget methods
+// Params  : [in] DWORD flags                 kbd & mouse flags as passed to
+//                                            IDropTarget methods
+//           [in] wxDragResult defaultAction  the default action of the drop target
+//           [in] DWORD pdwEffect             the supported actions of the drop
+//                                            source passed to IDropTarget methods
 // Notes   : We do "move" normally and "copy" if <Ctrl> is pressed,
 //           which is the standard behaviour (currently there is no
 //           way to redefine it)
-DWORD wxIDropTarget::GetDropEffect(DWORD flags)
+DWORD wxIDropTarget::GetDropEffect(DWORD flags,
+                                   wxDragResult defaultAction,
+                                   DWORD pdwEffect)
 {
-  return flags & MK_CONTROL ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
+    DWORD effectiveAction;
+    if ( defaultAction == wxDragCopy )
+        effectiveAction = flags & MK_SHIFT ? DROPEFFECT_MOVE : DROPEFFECT_COPY;
+    else
+        effectiveAction = flags & MK_CONTROL ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
+
+    if ( !(effectiveAction & pdwEffect) )
+    {
+        // the action is not supported by drag source, fall back to something
+        // that it does support
+        if ( pdwEffect & DROPEFFECT_MOVE )
+            effectiveAction = DROPEFFECT_MOVE;
+        else if ( pdwEffect & DROPEFFECT_COPY )
+            effectiveAction = DROPEFFECT_COPY;
+        else if ( pdwEffect & DROPEFFECT_LINK )
+            effectiveAction = DROPEFFECT_LINK;
+        else
+            effectiveAction = DROPEFFECT_NONE;
+    }
+
+    return effectiveAction;
 }
 
 wxIDropTarget::wxIDropTarget(wxDropTarget *pTarget)
 {
-  m_cRef         = 0;
   m_pTarget      = pTarget;
-  m_cfFormat     = wxDF_INVALID;
   m_pIDataObject = NULL;
 }
 
@@ -126,59 +181,126 @@ IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget)
 // Name    : wxIDropTarget::DragEnter
 // Purpose : Called when the mouse enters the window (dragging something)
 // Returns : S_OK
-// Params  : [in] IDataObject *pIDataSource : source data
-//           [in] DWORD        grfKeyState  : kbd & mouse state
-//           [in] POINTL       pt           : mouse coordinates
-//           [out]DWORD       *pdwEffect    : effect flag
+// Params  : [in]    IDataObject *pIDataSource : source data
+//           [in]    DWORD        grfKeyState  : kbd & mouse state
+//           [in]    POINTL       pt           : mouse coordinates
+//           [in/out]DWORD       *pdwEffect    : effect flag
+//                                               In:  Supported effects
+//                                               Out: Resulting effect
 // Notes   :
 STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource,
                                       DWORD        grfKeyState,
                                       POINTL       pt,
                                       DWORD       *pdwEffect)
 {
-  wxLogDebug(T("IDropTarget::DragEnter"));
-
-  wxASSERT( m_pIDataObject == NULL );
+    wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragEnter"));
+
+    wxASSERT_MSG( m_pIDataObject == NULL,
+                  wxT("drop target must have data object") );
+
+    // show the list of formats supported by the source data object for the
+    // debugging purposes, this is quite useful sometimes - please don't remove
+#if 0
+    IEnumFORMATETC *penumFmt;
+    if ( SUCCEEDED(pIDataSource->EnumFormatEtc(DATADIR_GET, &penumFmt)) )
+    {
+        FORMATETC fmt;
+        while ( penumFmt->Next(1, &fmt, NULL) == S_OK )
+        {
+            wxLogDebug(wxT("Drop source supports format %s"),
+                       wxDataObject::GetFormatName(fmt.cfFormat));
+        }
+
+        penumFmt->Release();
+    }
+    else
+    {
+        wxLogLastError(wxT("IDataObject::EnumFormatEtc"));
+    }
+#endif // 0
 
-  if ( !m_pTarget->IsAcceptedData(pIDataSource) ) {
-    // we don't accept this kind of data
-    *pdwEffect = DROPEFFECT_NONE;
+    // for use in OnEnter and OnDrag calls
+    m_pTarget->MSWSetDataSource(pIDataSource);
 
-    return S_OK;
-  }
+    // get hold of the data object
+    m_pIDataObject = pIDataSource;
+    m_pIDataObject->AddRef();
 
-  // TODO should check the point also?
+    if ( !m_pTarget->MSWIsAcceptedData(pIDataSource) ) {
+        // we don't accept this kind of data
+        *pdwEffect = DROPEFFECT_NONE;
+    }
+    else
+    {
+        // we need client coordinates to pass to wxWin functions
+        if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
+        {
+            wxLogLastError(wxT("ScreenToClient"));
+        }
+
+        // give some visual feedback
+        *pdwEffect = ConvertDragResultToEffect(
+            m_pTarget->OnEnter(pt.x, pt.y, ConvertDragEffectToResult(
+                GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction(), *pdwEffect))
+                        )
+                     );
+    }
 
-  *pdwEffect = GetDropEffect(grfKeyState);
+    // update drag image
+    const wxDragResult res = ConvertDragEffectToResult(*pdwEffect);
+    m_pTarget->MSWUpdateDragImageOnEnter(pt.x, pt.y, res);
+    m_pTarget->MSWUpdateDragImageOnDragOver(pt.x, pt.y, res);
 
-  // get hold of the data object
-  m_pIDataObject = pIDataSource;
-  m_pIDataObject->AddRef();
+    return S_OK;
+}
 
-  // give some visual feedback
-  m_pTarget->OnEnter();
 
-  return S_OK;
-}
 
 // Name    : wxIDropTarget::DragOver
 // Purpose : Indicates that the mouse was moved inside the window represented
 //           by this drop target.
 // Returns : S_OK
-// Params  : [in] DWORD   grfKeyState     kbd & mouse state
-//           [in] POINTL  pt              mouse coordinates
-//           [out]LPDWORD pdwEffect       effect flag
+// Params  : [in]    DWORD   grfKeyState     kbd & mouse state
+//           [in]    POINTL  pt              mouse coordinates
+//           [in/out]LPDWORD pdwEffect       current effect flag
 // Notes   : We're called on every WM_MOUSEMOVE, so this function should be
 //           very efficient.
 STDMETHODIMP wxIDropTarget::DragOver(DWORD   grfKeyState,
                                      POINTL  pt,
                                      LPDWORD pdwEffect)
 {
-  // there are too many of them... wxLogDebug("IDropTarget::DragOver");
+    // there are too many of them... wxLogDebug("IDropTarget::DragOver");
 
-  *pdwEffect = m_pIDataObject == NULL ? DROPEFFECT_NONE
-                                      : GetDropEffect(grfKeyState);
-  return S_OK;
+    wxDragResult result;
+    if ( m_pIDataObject ) {
+        result = ConvertDragEffectToResult(
+            GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction(), *pdwEffect));
+    }
+    else {
+        // can't accept data anyhow normally
+        result = wxDragNone;
+    }
+
+    if ( result != wxDragNone ) {
+        // we need client coordinates to pass to wxWin functions
+        if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
+        {
+            wxLogLastError(wxT("ScreenToClient"));
+        }
+
+        *pdwEffect = ConvertDragResultToEffect(
+                        m_pTarget->OnDragOver(pt.x, pt.y, result)
+                     );
+    }
+    else {
+        *pdwEffect = DROPEFFECT_NONE;
+    }
+
+    // update drag image
+    m_pTarget->MSWUpdateDragImageOnDragOver(pt.x, pt.y,
+                                            ConvertDragEffectToResult(*pdwEffect));
+
+    return S_OK;
 }
 
 // Name    : wxIDropTarget::DragLeave
@@ -187,7 +309,7 @@ STDMETHODIMP wxIDropTarget::DragOver(DWORD   grfKeyState,
 // Notes   : good place to do any clean-up
 STDMETHODIMP wxIDropTarget::DragLeave()
 {
-  wxLogDebug(T("IDropTarget::DragLeave"));
+  wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragLeave"));
 
   // remove the UI feedback
   m_pTarget->OnLeave();
@@ -195,6 +317,9 @@ STDMETHODIMP wxIDropTarget::DragLeave()
   // release the held object
   RELEASE_AND_NULL(m_pIDataObject);
 
+  // update drag image
+  m_pTarget->MSWUpdateDragImageOnLeave();
+
   return S_OK;
 }
 
@@ -202,55 +327,58 @@ STDMETHODIMP wxIDropTarget::DragLeave()
 // Purpose : Instructs the drop target to paste data that was just now
 //           dropped on it.
 // Returns : S_OK
-// Params  : [in] IDataObject *pIDataSource     the data to paste
-//           [in] DWORD        grfKeyState      kbd & mouse state
-//           [in] POINTL       pt               where the drop occured?
-//           [ouy]DWORD       *pdwEffect        operation effect
+// Params  : [in]    IDataObject *pIDataSource     the data to paste
+//           [in]    DWORD        grfKeyState      kbd & mouse state
+//           [in]    POINTL       pt               where the drop occurred?
+//           [in/out]DWORD       *pdwEffect        operation effect
 // Notes   :
 STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
                                  DWORD        grfKeyState,
                                  POINTL       pt,
                                  DWORD       *pdwEffect)
 {
-  wxLogDebug(T("IDropTarget::Drop"));
-
-  // TODO I don't know why there is this parameter, but so far I assume
-  //      that it's the same we've already got in DragEnter
-  wxASSERT( m_pIDataObject == pIDataSource );
-
-  STGMEDIUM stm;
-  *pdwEffect = DROPEFFECT_NONE;
-
-  // should be set by SetSupportedFormat() call
-  wxASSERT( m_cfFormat != wxDF_INVALID );
-
-  FORMATETC fmtMemory;
-  fmtMemory.cfFormat  = m_cfFormat;
-  fmtMemory.ptd       = NULL;
-  fmtMemory.dwAspect  = DVASPECT_CONTENT;
-  fmtMemory.lindex    = -1;
-  fmtMemory.tymed     = TYMED_HGLOBAL;  // TODO to add other media
-
-  HRESULT hr = pIDataSource->GetData(&fmtMemory, &stm);
-  if ( SUCCEEDED(hr) ) {
-    if ( stm.hGlobal != NULL ) {
-      if ( m_pTarget->OnDrop(pt.x, pt.y, GlobalLock(stm.hGlobal)) )
-        *pdwEffect = GetDropEffect(grfKeyState);
-      //else: DROPEFFECT_NONE
-
-      GlobalUnlock(stm.hGlobal);
-      ReleaseStgMedium(&stm);
+    wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::Drop"));
+
+    // TODO I don't know why there is this parameter, but so far I assume
+    //      that it's the same we've already got in DragEnter
+    wxASSERT( m_pIDataObject == pIDataSource );
+
+    // we need client coordinates to pass to wxWin functions
+    if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
+    {
+        wxLogLastError(wxT("ScreenToClient"));
     }
-  }
-  else
-  {
-    // wxLogApiError("GetData", hr);
-  }
 
-  // release the held object
-  RELEASE_AND_NULL(m_pIDataObject);
+    // first ask the drop target if it wants data
+    if ( m_pTarget->OnDrop(pt.x, pt.y) ) {
+        // it does, so give it the data source
+        m_pTarget->MSWSetDataSource(pIDataSource);
+
+        // and now it has the data
+        wxDragResult rc = ConvertDragEffectToResult(
+            GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction(), *pdwEffect));
+        rc = m_pTarget->OnData(pt.x, pt.y, rc);
+        if ( wxIsDragResultOk(rc) ) {
+            // operation succeeded
+            *pdwEffect = ConvertDragResultToEffect(rc);
+        }
+        else {
+            *pdwEffect = DROPEFFECT_NONE;
+        }
+    }
+    else {
+        // OnDrop() returned false, no need to copy data
+        *pdwEffect = DROPEFFECT_NONE;
+    }
 
-  return S_OK;
+    // release the held object
+    RELEASE_AND_NULL(m_pIDataObject);
+
+    // update drag image
+    m_pTarget->MSWUpdateDragImageOnData(pt.x, pt.y,
+                                        ConvertDragEffectToResult(*pdwEffect));
+
+    return S_OK;
 }
 
 // ============================================================================
@@ -261,17 +389,19 @@ STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
 // ctor/dtor
 // ----------------------------------------------------------------------------
 
-wxDropTarget::wxDropTarget()
+wxDropTarget::wxDropTarget(wxDataObject *dataObj)
+            : wxDropTargetBase(dataObj),
+              m_dropTargetHelper(NULL)
 {
-  // create an IDropTarget implementation which will notify us about
-  // d&d operations.
-  m_pIDropTarget = new wxIDropTarget(this);
-  m_pIDropTarget->AddRef();
+    // create an IDropTarget implementation which will notify us about d&d
+    // operations.
+    m_pIDropTarget = new wxIDropTarget(this);
+    m_pIDropTarget->AddRef();
 }
 
 wxDropTarget::~wxDropTarget()
 {
-  ReleaseInterface(m_pIDropTarget);
+    ReleaseInterface(m_pIDropTarget);
 }
 
 // ----------------------------------------------------------------------------
@@ -280,141 +410,302 @@ wxDropTarget::~wxDropTarget()
 
 bool wxDropTarget::Register(WXHWND hwnd)
 {
-  HRESULT hr = ::CoLockObjectExternal(m_pIDropTarget, TRUE, FALSE);
-  if ( FAILED(hr) ) {
-    wxLogApiError("CoLockObjectExternal", hr);
-    return FALSE;
-  }
+    // FIXME
+    // RegisterDragDrop not available on Windows CE >= 400?
+    // Or maybe we can dynamically load them from ceshell.dll
+    // or similar.
+#if defined(__WXWINCE__) && _WIN32_WCE >= 400
+    wxUnusedVar(hwnd);
+    return false;
+#else
+    HRESULT hr;
+
+    // May exist in later WinCE versions
+#ifndef __WXWINCE__
+    hr = ::CoLockObjectExternal(m_pIDropTarget, TRUE, FALSE);
+    if ( FAILED(hr) ) {
+        wxLogApiError(wxT("CoLockObjectExternal"), hr);
+        return false;
+    }
+#endif
+
+    hr = ::RegisterDragDrop((HWND) hwnd, m_pIDropTarget);
+    if ( FAILED(hr) ) {
+    // May exist in later WinCE versions
+#ifndef __WXWINCE__
+        ::CoLockObjectExternal(m_pIDropTarget, FALSE, FALSE);
+#endif
+        wxLogApiError(wxT("RegisterDragDrop"), hr);
+        return false;
+    }
 
-  hr = ::RegisterDragDrop((HWND) hwnd, m_pIDropTarget);
-  if ( FAILED(hr) ) {
-    ::CoLockObjectExternal(m_pIDropTarget, FALSE, FALSE);
+    // we will need the window handle for coords transformation later
+    m_pIDropTarget->SetHwnd((HWND)hwnd);
 
-    wxLogApiError("RegisterDragDrop", hr);
-    return FALSE;
-  }
+    MSWInitDragImageSupport();
 
-  return TRUE;
+    return true;
+#endif
 }
 
 void wxDropTarget::Revoke(WXHWND hwnd)
 {
-  HRESULT hr = ::RevokeDragDrop((HWND) hwnd);
+#if defined(__WXWINCE__) && _WIN32_WCE >= 400
+    // Not available, see note above
+    wxUnusedVar(hwnd);
+#else
+    HRESULT hr = ::RevokeDragDrop((HWND) hwnd);
 
-  if ( FAILED(hr) ) {
-    wxLogApiError("RevokeDragDrop", hr);
-  }
+    if ( FAILED(hr) ) {
+        wxLogApiError(wxT("RevokeDragDrop"), hr);
+    }
 
-  ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE);
+    // May exist in later WinCE versions
+#ifndef __WXWINCE__
+    ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE);
+#endif
+
+    MSWEndDragImageSupport();
+
+    // remove window reference
+    m_pIDropTarget->SetHwnd(0);
+#endif
 }
 
 // ----------------------------------------------------------------------------
-// determine if we accept data of this type
+// base class pure virtuals
 // ----------------------------------------------------------------------------
-bool wxDropTarget::IsAcceptedData(IDataObject *pIDataSource) const
+
+// OnDrop() is called only if we previously returned true from
+// IsAcceptedData(), so no need to check anything here
+bool wxDropTarget::OnDrop(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y))
+{
+    return true;
+}
+
+// copy the data from the data source to the target data object
+bool wxDropTarget::GetData()
 {
-  // this strucutre describes a data of any type (first field will be
-  // changing) being passed through global memory block.
-  static FORMATETC s_fmtMemory = {
-    0,
-    NULL,
-    DVASPECT_CONTENT,
-    -1,
-    TYMED_HGLOBAL
-  };
-
-  // cycle thorugh all supported formats
-  for ( size_t n = 0; n < GetFormatCount(); n++ ) {
-    s_fmtMemory.cfFormat = GetFormat(n);
-    // NB: don't use SUCCEEDED macro here: QueryGetData returns 1 (whatever it
-    //     means) for file drag and drop
-    if ( pIDataSource->QueryGetData(&s_fmtMemory) == S_OK ) {
-      // remember this format: we'll later ask for data in it
-      m_pIDropTarget->SetSupportedFormat((unsigned int)s_fmtMemory.cfFormat);
-      return TRUE;
+    wxDataFormat format = MSWGetSupportedFormat(m_pIDataSource);
+    if ( format == wxDF_INVALID ) {
+        return false;
     }
-  }
 
-  return FALSE;
+    STGMEDIUM stm;
+    FORMATETC fmtMemory;
+    fmtMemory.cfFormat  = format;
+    fmtMemory.ptd       = NULL;
+    fmtMemory.dwAspect  = DVASPECT_CONTENT;
+    fmtMemory.lindex    = -1;
+    fmtMemory.tymed     = TYMED_HGLOBAL;  // TODO to add other media
+
+    bool rc = false;
+
+    HRESULT hr = m_pIDataSource->GetData(&fmtMemory, &stm);
+    if ( SUCCEEDED(hr) ) {
+        IDataObject *dataObject = m_dataObject->GetInterface();
+
+        hr = dataObject->SetData(&fmtMemory, &stm, TRUE);
+        if ( SUCCEEDED(hr) ) {
+            rc = true;
+        }
+        else {
+            wxLogApiError(wxT("IDataObject::SetData()"), hr);
+        }
+    }
+    else {
+        wxLogApiError(wxT("IDataObject::GetData()"), hr);
+    }
+
+    return rc;
 }
 
-// ============================================================================
-// wxTextDropTarget
-// ============================================================================
+// ----------------------------------------------------------------------------
+// callbacks used by wxIDropTarget
+// ----------------------------------------------------------------------------
+
+// we need a data source, so wxIDropTarget gives it to us using this function
+void wxDropTarget::MSWSetDataSource(IDataObject *pIDataSource)
+{
+    m_pIDataSource = pIDataSource;
+}
 
-bool wxTextDropTarget::OnDrop(long x, long y, const void *pData)
+// determine if we accept data of this type
+bool wxDropTarget::MSWIsAcceptedData(IDataObject *pIDataSource) const
 {
-  return OnDropText(x, y, (const wxChar *)pData);
+    return MSWGetSupportedFormat(pIDataSource) != wxDF_INVALID;
 }
 
-size_t wxTextDropTarget::GetFormatCount() const
+// ----------------------------------------------------------------------------
+// helper functions
+// ----------------------------------------------------------------------------
+
+wxDataFormat wxDropTarget::GetMatchingPair()
 {
-  return 1;
+    return MSWGetSupportedFormat( m_pIDataSource );
 }
 
-wxDataFormat wxTextDropTarget::GetFormat(size_t WXUNUSED(n)) const
+wxDataFormat wxDropTarget::MSWGetSupportedFormat(IDataObject *pIDataSource) const
 {
-  return wxDF_TEXT;
+    // this strucutre describes a data of any type (first field will be
+    // changing) being passed through global memory block.
+    static FORMATETC s_fmtMemory = {
+        0,
+        NULL,
+        DVASPECT_CONTENT,
+        -1,
+        TYMED_HGLOBAL       // TODO is it worth supporting other tymeds here?
+    };
+
+    // get the list of supported formats
+    size_t nFormats = m_dataObject->GetFormatCount(wxDataObject::Set);
+    wxDataFormat format;
+    wxDataFormat *formats;
+    formats = nFormats == 1 ? &format :  new wxDataFormat[nFormats];
+
+    m_dataObject->GetAllFormats(formats, wxDataObject::Set);
+
+    // cycle through all supported formats
+    size_t n;
+    for ( n = 0; n < nFormats; n++ ) {
+        s_fmtMemory.cfFormat = formats[n];
+
+        // NB: don't use SUCCEEDED macro here: QueryGetData returns S_FALSE
+        //     for file drag and drop (format == CF_HDROP)
+        if ( pIDataSource->QueryGetData(&s_fmtMemory) == S_OK ) {
+            format = formats[n];
+
+            break;
+        }
+    }
+
+    if ( formats != &format ) {
+        // free memory if we allocated it
+        delete [] formats;
+    }
+
+    return n < nFormats ? format : wxFormatInvalid;
 }
 
-// ============================================================================
-// wxFileDropTarget
-// ============================================================================
+// ----------------------------------------------------------------------------
+// drag image functions
+// ----------------------------------------------------------------------------
 
-bool wxFileDropTarget::OnDrop(long x, long y, const void *pData)
+void
+wxDropTarget::MSWEndDragImageSupport()
 {
-  // the documentation states that the first member of DROPFILES structure
-  // is a "DWORD offset of double NUL terminated file list". What they mean by
-  // this (I wonder if you see it immediately) is that the list starts at
-  // ((char *)&(pDropFiles.pFiles)) + pDropFiles.pFiles. We're also advised to
-  // use DragQueryFile to work with this structure, but not told where and how
-  // to get HDROP.
-  HDROP hdrop = (HDROP)pData;   // NB: it works, but I'm not sure about it
-
-  // get number of files (magic value -1)
-  UINT nFiles = ::DragQueryFile(hdrop, (unsigned)-1, NULL, 0u);
-
-  // for each file get the length, allocate memory and then get the name
-  wxChar **aszFiles = new wxChar *[nFiles];
-  UINT len, n;
-  for ( n = 0; n < nFiles; n++ ) {
-    // +1 for terminating NUL
-    len = ::DragQueryFile(hdrop, n, NULL, 0) + 1;
-
-    aszFiles[n] = new wxChar[len];
-
-    UINT len2 = ::DragQueryFile(hdrop, n, aszFiles[n], len);
-    if ( len2 != len - 1 ) {
-      wxLogDebug(T("In wxFileDropTarget::OnDrop DragQueryFile returned %d "
-                    "characters, %d expected."), len2, len - 1);
+    // release drop target helper
+    if ( m_dropTargetHelper != NULL )
+    {
+        m_dropTargetHelper->Release();
+        m_dropTargetHelper = NULL;
     }
-  }
+}
 
-  bool bResult = OnDropFiles(x, y, nFiles, (const wxChar**) aszFiles);
+void
+wxDropTarget::MSWInitDragImageSupport()
+{
+    // Use the default drop target helper to show shell drag images
+    CoCreateInstance(wxCLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,
+                     wxIID_IDropTargetHelper, (LPVOID*)&m_dropTargetHelper);
+}
 
-  // free memory
-  for ( n = 0; n < nFiles; n++ ) {
-    delete [] aszFiles[n];
-  }
-  delete [] aszFiles;
+void
+wxDropTarget::MSWUpdateDragImageOnData(wxCoord x,
+                                       wxCoord y,
+                                       wxDragResult dragResult)
+{
+    // call corresponding event on drop target helper
+    if ( m_dropTargetHelper != NULL )
+    {
+        POINT pt = {x, y};
+        DWORD dwEffect = ConvertDragResultToEffect(dragResult);
+        m_dropTargetHelper->Drop(m_pIDataSource, &pt, dwEffect);
+    }
+}
 
-  return bResult;
+void
+wxDropTarget::MSWUpdateDragImageOnDragOver(wxCoord x,
+                                           wxCoord y,
+                                           wxDragResult dragResult)
+{
+    // call corresponding event on drop target helper
+    if ( m_dropTargetHelper != NULL )
+    {
+        POINT pt = {x, y};
+        DWORD dwEffect = ConvertDragResultToEffect(dragResult);
+        m_dropTargetHelper->DragOver(&pt, dwEffect);
+    }
 }
 
-size_t wxFileDropTarget::GetFormatCount() const
+void
+wxDropTarget::MSWUpdateDragImageOnEnter(wxCoord x,
+                                        wxCoord y,
+                                        wxDragResult dragResult)
 {
-  return 1;
+    // call corresponding event on drop target helper
+    if ( m_dropTargetHelper != NULL )
+    {
+        POINT pt = {x, y};
+        DWORD dwEffect = ConvertDragResultToEffect(dragResult);
+        m_dropTargetHelper->DragEnter(m_pIDropTarget->GetHWND(), m_pIDataSource, &pt, dwEffect);
+    }
 }
 
-wxDataFormat wxFileDropTarget::GetFormat(size_t WXUNUSED(n)) const
+void
+wxDropTarget::MSWUpdateDragImageOnLeave()
 {
-#ifdef __WIN32__
-  return wxDF_FILENAME;
-#else
-  // TODO: how to implement this in WIN16?
-  return wxDF_TEXT;
-#endif
+    // call corresponding event on drop target helper
+    if ( m_dropTargetHelper != NULL )
+    {
+        m_dropTargetHelper->DragLeave();
+    }
 }
 
-#endif
- // wxUSE_DRAG_AND_DROP
+// ----------------------------------------------------------------------------
+// private functions
+// ----------------------------------------------------------------------------
+
+static wxDragResult ConvertDragEffectToResult(DWORD dwEffect)
+{
+    switch ( dwEffect ) {
+        case DROPEFFECT_COPY:
+            return wxDragCopy;
+
+        case DROPEFFECT_LINK:
+            return wxDragLink;
+
+        case DROPEFFECT_MOVE:
+            return wxDragMove;
+
+        default:
+            wxFAIL_MSG(wxT("invalid value in ConvertDragEffectToResult"));
+            // fall through
+
+        case DROPEFFECT_NONE:
+            return wxDragNone;
+    }
+}
+
+static DWORD ConvertDragResultToEffect(wxDragResult result)
+{
+    switch ( result ) {
+        case wxDragCopy:
+            return DROPEFFECT_COPY;
+
+        case wxDragLink:
+            return DROPEFFECT_LINK;
+
+        case wxDragMove:
+            return DROPEFFECT_MOVE;
+
+        default:
+            wxFAIL_MSG(wxT("invalid value in ConvertDragResultToEffect"));
+            // fall through
+
+        case wxDragNone:
+            return DROPEFFECT_NONE;
+    }
+}
+
+#endif // wxUSE_OLE && wxUSE_DRAG_AND_DROP