]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/ole/droptgt.cpp
Worked around an apparent bug in Windows whereby some deferred positioning
[wxWidgets.git] / src / msw / ole / droptgt.cpp
index c9f5feb506068ab10aaf6cd1cbf0cc3d2d3d2488..7072586f09c8c9241cddd744562368fb73855c32 100644 (file)
@@ -6,7 +6,7 @@
 // Created:
 // RCS-ID:      $Id$
 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
-// Licence:     wxWindows license
+// Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 
 // ============================================================================
@@ -17,7 +17,7 @@
 // headers
 // ----------------------------------------------------------------------------
 
-#ifdef __GNUG__
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
 #pragma implementation "droptgt.h"
 #endif
 
 
 #include "wx/setup.h"
 
-#if wxUSE_DRAG_AND_DROP
+#if wxUSE_OLE && 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
+        #if wxCHECK_W32API_VERSION( 1, 0 )
+            #include "wx/msw/wrapwin.h"
+        #endif
         #include <shlobj.h>            // for DROPFILES structure
     #endif
 #else
 
 #include "wx/dnd.h"
 
-#ifndef __WIN32__
-    #include <ole2.h>
-    #include <olestd.h>
-#endif
-
 #include "wx/msw/ole/oleutils.h"
 
 // ----------------------------------------------------------------------------
 class wxIDropTarget : public IDropTarget
 {
 public:
-  wxIDropTarget(wxDropTarget *p);
- ~wxIDropTarget();
+    wxIDropTarget(wxDropTarget *p);
+    virtual ~wxIDropTarget();
+
+    // accessors for wxDropTarget
+    void SetHwnd(HWND hwnd) { m_hwnd = hwnd; }
 
-  // IDropTarget methods
-  STDMETHODIMP DragEnter(LPDATAOBJECT, DWORD, POINTL, LPDWORD);
-  STDMETHODIMP DragOver(DWORD, POINTL, LPDWORD);
-  STDMETHODIMP DragLeave(void);
-  STDMETHODIMP Drop(LPDATAOBJECT, DWORD, POINTL, LPDWORD);
+    // 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)
 
-private:
-  static inline DWORD GetDropEffect(DWORD flags);
+    HWND          m_hwnd;         // window we're associated with
+
+    // get default drop effect for given keyboard flags
+    static inline DWORD GetDropEffect(DWORD flags, wxDragResult defaultAction);
+
+    DECLARE_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
@@ -91,14 +109,15 @@ private:
 // 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)
 {
+  if (defaultAction == wxDragCopy)
+    return flags & MK_SHIFT ? DROPEFFECT_MOVE : DROPEFFECT_COPY;
   return flags & MK_CONTROL ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
 }
 
 wxIDropTarget::wxIDropTarget(wxDropTarget *pTarget)
 {
-  m_cRef         = 0;
   m_pTarget      = pTarget;
   m_pIDataObject = NULL;
 }
@@ -127,29 +146,57 @@ STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource,
                                       POINTL       pt,
                                       DWORD       *pdwEffect)
 {
-  wxLogDebug(wxT("IDropTarget::DragEnter"));
-
-  wxASSERT( m_pIDataObject == NULL );
+    wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragEnter"));
+
+    wxASSERT_MSG( m_pIDataObject == NULL,
+                  _T("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(_T("Drop source supports format %s"),
+                       wxDataObject::GetFormatName(fmt.cfFormat));
+        }
 
-  if ( !m_pTarget->IsAcceptedData(pIDataSource) ) {
-    // we don't accept this kind of data
-    *pdwEffect = DROPEFFECT_NONE;
+        penumFmt->Release();
+    }
+    else
+    {
+        wxLogLastError(_T("IDataObject::EnumFormatEtc"));
+    }
+#endif // 0
 
-    return S_OK;
-  }
+    if ( !m_pTarget->IsAcceptedData(pIDataSource) ) {
+        // we don't accept this kind of data
+        *pdwEffect = DROPEFFECT_NONE;
 
-  // TODO should check the point also?
+        return S_OK;
+    }
 
-  *pdwEffect = GetDropEffect(grfKeyState);
+    // get hold of the data object
+    m_pIDataObject = pIDataSource;
+    m_pIDataObject->AddRef();
 
-  // get hold of the data object
-  m_pIDataObject = pIDataSource;
-  m_pIDataObject->AddRef();
+    // we need client coordinates to pass to wxWin functions
+    if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
+    {
+        wxLogLastError(wxT("ScreenToClient"));
+    }
 
-  // give some visual feedback
-  m_pTarget->OnEnter();
+    // give some visual feedback
+    *pdwEffect = ConvertDragResultToEffect(
+        m_pTarget->OnEnter(pt.x, pt.y, ConvertDragEffectToResult(
+            GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction()))
+                    )
+                 );
 
-  return S_OK;
+    return S_OK;
 }
 
 // Name    : wxIDropTarget::DragOver
@@ -165,11 +212,29 @@ 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()));
+    }
+    else {
+        // can't accept data anyhow normally
+        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)
+                 );
+
+    return S_OK;
 }
 
 // Name    : wxIDropTarget::DragLeave
@@ -178,7 +243,7 @@ STDMETHODIMP wxIDropTarget::DragOver(DWORD   grfKeyState,
 // Notes   : good place to do any clean-up
 STDMETHODIMP wxIDropTarget::DragLeave()
 {
-  wxLogDebug(wxT("IDropTarget::DragLeave"));
+  wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragLeave"));
 
   // remove the UI feedback
   m_pTarget->OnLeave();
@@ -203,7 +268,7 @@ STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
                                  POINTL       pt,
                                  DWORD       *pdwEffect)
 {
-    wxLogDebug(wxT("IDropTarget::Drop"));
+    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
@@ -212,19 +277,28 @@ STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
     // by default, nothing happens
     *pdwEffect = DROPEFFECT_NONE;
 
+    // we need client coordinates to pass to wxWin functions
+    if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
+    {
+        wxLogLastError(wxT("ScreenToClient"));
+    }
+
     // 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->SetDataSource(pIDataSource);
 
         // and now it has the data
-        if ( m_pTarget->OnData(pt.x, pt.y) ) {
+        wxDragResult rc = ConvertDragEffectToResult(
+            GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction()));
+        rc = m_pTarget->OnData(pt.x, pt.y, rc);
+        if ( wxIsDragResultOk(rc) ) {
             // operation succeeded
-            *pdwEffect = GetDropEffect(grfKeyState);
+            *pdwEffect = ConvertDragResultToEffect(rc);
         }
         //else: *pdwEffect is already DROPEFFECT_NONE
     }
-    //else: OnDrop() returned FALSE, no need to copy data
+    //else: OnDrop() returned false, no need to copy data
 
     // release the held object
     RELEASE_AND_NULL(m_pIDataObject);
@@ -260,43 +334,72 @@ wxDropTarget::~wxDropTarget()
 
 bool wxDropTarget::Register(WXHWND hwnd)
 {
-    HRESULT hr = ::CoLockObjectExternal(m_pIDropTarget, TRUE, 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("CoLockObjectExternal", hr);
-        return FALSE;
+        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);
-
-        wxLogApiError("RegisterDragDrop", hr);
-        return FALSE;
+#endif
+        wxLogApiError(wxT("RegisterDragDrop"), hr);
+        return false;
     }
 
-    return TRUE;
+    // we will need the window handle for coords transformation later
+    m_pIDropTarget->SetHwnd((HWND)hwnd);
+
+    return true;
+#endif
 }
 
 void wxDropTarget::Revoke(WXHWND 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);
+        wxLogApiError(wxT("RevokeDragDrop"), hr);
     }
 
+    // May exist in later WinCE versions
+#ifndef __WXWINCE__
     ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE);
+#endif
+
+    m_pIDropTarget->SetHwnd(0);
+#endif
 }
 
 // ----------------------------------------------------------------------------
 // base class pure virtuals
 // ----------------------------------------------------------------------------
 
-// OnDrop() is called only if we previously returned TRUE from
+// 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;
+    return true;
 }
 
 // copy the data from the data source to the target data object
@@ -307,7 +410,7 @@ bool wxDropTarget::GetData()
         // this is strange because IsAcceptedData() succeeded previously!
         wxFAIL_MSG(wxT("strange - did supported formats list change?"));
 
-        return FALSE;
+        return false;
     }
 
     STGMEDIUM stm;
@@ -318,7 +421,7 @@ bool wxDropTarget::GetData()
     fmtMemory.lindex    = -1;
     fmtMemory.tymed     = TYMED_HGLOBAL;  // TODO to add other media
 
-    bool rc = FALSE;
+    bool rc = false;
 
     HRESULT hr = m_pIDataSource->GetData(&fmtMemory, &stm);
     if ( SUCCEEDED(hr) ) {
@@ -326,14 +429,14 @@ bool wxDropTarget::GetData()
 
         hr = dataObject->SetData(&fmtMemory, &stm, TRUE);
         if ( SUCCEEDED(hr) ) {
-            rc = TRUE;
+            rc = true;
         }
         else {
-            wxLogLastError("IDataObject::SetData()");
+            wxLogApiError(wxT("IDataObject::SetData()"), hr);
         }
     }
     else {
-        wxLogLastError("IDataObject::GetData()");
+        wxLogApiError(wxT("IDataObject::GetData()"), hr);
     }
 
     return rc;
@@ -373,7 +476,8 @@ wxDataFormat wxDropTarget::GetSupportedFormat(IDataObject *pIDataSource) const
 
     // get the list of supported formats
     size_t nFormats = m_dataObject->GetFormatCount(wxDataObject::Set);
-    wxDataFormat format, *formats;
+    wxDataFormat format;
+    wxDataFormat *formats;
     formats = nFormats == 1 ? &format :  new wxDataFormat[nFormats];
 
     m_dataObject->GetAllFormats(formats, wxDataObject::Set);
@@ -397,42 +501,53 @@ wxDataFormat wxDropTarget::GetSupportedFormat(IDataObject *pIDataSource) const
         delete [] formats;
     }
 
-    return n < nFormats ? format : wxDF_INVALID;
+    return n < nFormats ? format : wxFormatInvalid;
 }
 
 // ----------------------------------------------------------------------------
-// wxTextDropTarget
+// private functions
 // ----------------------------------------------------------------------------
 
-wxTextDropTarget::wxTextDropTarget()
-                : wxDropTarget(new wxTextDataObject)
+static wxDragResult ConvertDragEffectToResult(DWORD dwEffect)
 {
-}
+    switch ( dwEffect ) {
+        case DROPEFFECT_COPY:
+            return wxDragCopy;
 
-bool wxTextDropTarget::OnData(wxCoord x, wxCoord y)
-{
-    if ( !GetData() )
-        return FALSE;
+        case DROPEFFECT_LINK:
+            return wxDragLink;
 
-    return OnDropText(x, y, ((wxTextDataObject *)m_dataObject)->GetText());
-}
+        case DROPEFFECT_MOVE:
+            return wxDragMove;
 
-// ----------------------------------------------------------------------------
-// wxFileDropTarget
-// ----------------------------------------------------------------------------
+        default:
+            wxFAIL_MSG(wxT("invalid value in ConvertDragEffectToResult"));
+            // fall through
 
-wxFileDropTarget::wxFileDropTarget()
-                : wxDropTarget(new wxFileDataObject)
-{
+        case DROPEFFECT_NONE:
+            return wxDragNone;
+    }
 }
 
-bool wxFileDropTarget::OnData(wxCoord x, wxCoord y)
+static DWORD ConvertDragResultToEffect(wxDragResult result)
 {
-    if ( !GetData() )
-        return FALSE;
+    switch ( result ) {
+        case wxDragCopy:
+            return DROPEFFECT_COPY;
+
+        case wxDragLink:
+            return DROPEFFECT_LINK;
 
-    return OnDropFiles(x, y,
-                       ((wxFileDataObject *)m_dataObject)->GetFilenames());
+        case wxDragMove:
+            return DROPEFFECT_MOVE;
+
+        default:
+            wxFAIL_MSG(wxT("invalid value in ConvertDragResultToEffect"));
+            // fall through
+
+        case wxDragNone:
+            return DROPEFFECT_NONE;
+    }
 }
 
 #endif