]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/ole/droptgt.cpp
Fix wxHtmlHelpData::SetTempDir() to behave correctly without trailing slash.
[wxWidgets.git] / src / msw / ole / droptgt.cpp
index 7072586f09c8c9241cddd744562368fb73855c32..182ae4e3d0b3efa89c9ec3de461df99cc29ecfb8 100644 (file)
@@ -1,10 +1,9 @@
 ///////////////////////////////////////////////////////////////////////////////
-// 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 licence
 ///////////////////////////////////////////////////////////////////////////////
 // headers
 // ----------------------------------------------------------------------------
 
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
-#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
+
 #include "wx/msw/private.h"
-#include "wx/log.h"
 
 #ifdef __WXWINCE__
     #include <winreg.h>
@@ -42,9 +39,6 @@
 
 #ifdef __WIN32__
     #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/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
@@ -68,6 +88,7 @@ public:
     virtual ~wxIDropTarget();
 
     // accessors for wxDropTarget
+    HWND GetHWND() const { return m_hwnd; }
     void SetHwnd(HWND hwnd) { m_hwnd = hwnd; }
 
     // IDropTarget methods
@@ -85,9 +106,9 @@ protected:
     HWND          m_hwnd;         // window we're associated with
 
     // get default drop effect for given keyboard flags
-    static inline DWORD GetDropEffect(DWORD flags, wxDragResult defaultAction);
+    static DWORD GetDropEffect(DWORD flags, wxDragResult defaultAction, DWORD pdwEffect);
 
-    DECLARE_NO_COPY_CLASS(wxIDropTarget)
+    wxDECLARE_NO_COPY_CLASS(wxIDropTarget);
 };
 
 // ----------------------------------------------------------------------------
@@ -104,16 +125,39 @@ static DWORD ConvertDragResultToEffect(wxDragResult result);
 // 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, wxDragResult defaultAction)
+DWORD wxIDropTarget::GetDropEffect(DWORD flags,
+                                   wxDragResult defaultAction,
+                                   DWORD pdwEffect)
 {
-  if (defaultAction == wxDragCopy)
-    return flags & MK_SHIFT ? DROPEFFECT_MOVE : DROPEFFECT_COPY;
-  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)
@@ -136,10 +180,12 @@ 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,
@@ -149,7 +195,7 @@ STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource,
     wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragEnter"));
 
     wxASSERT_MSG( m_pIDataObject == NULL,
-                  _T("drop target must have data object") );
+                  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
@@ -160,7 +206,7 @@ STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource,
         FORMATETC fmt;
         while ( penumFmt->Next(1, &fmt, NULL) == S_OK )
         {
-            wxLogDebug(_T("Drop source supports format %s"),
+            wxLogDebug(wxT("Drop source supports format %s"),
                        wxDataObject::GetFormatName(fmt.cfFormat));
         }
 
@@ -168,44 +214,54 @@ STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource,
     }
     else
     {
-        wxLogLastError(_T("IDataObject::EnumFormatEtc"));
+        wxLogLastError(wxT("IDataObject::EnumFormatEtc"));
     }
 #endif // 0
 
-    if ( !m_pTarget->IsAcceptedData(pIDataSource) ) {
-        // we don't accept this kind of data
-        *pdwEffect = DROPEFFECT_NONE;
-
-        return S_OK;
-    }
+    // for use in OnEnter and OnDrag calls
+    m_pTarget->MSWSetDataSource(pIDataSource);
 
     // 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) )
+    if ( !m_pTarget->MSWIsAcceptedData(pIDataSource) ) {
+        // we don't accept this kind of data
+        *pdwEffect = DROPEFFECT_NONE;
+    }
+    else
     {
-        wxLogLastError(wxT("ScreenToClient"));
+        // 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))
+                        )
+                     );
     }
 
-    // give some visual feedback
-    *pdwEffect = ConvertDragResultToEffect(
-        m_pTarget->OnEnter(pt.x, pt.y, ConvertDragEffectToResult(
-            GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction()))
-                    )
-                 );
+    // update drag image
+    const wxDragResult res = ConvertDragEffectToResult(*pdwEffect);
+    m_pTarget->MSWUpdateDragImageOnEnter(pt.x, pt.y, res);
+    m_pTarget->MSWUpdateDragImageOnDragOver(pt.x, pt.y, res);
 
     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,
@@ -217,22 +273,31 @@ STDMETHODIMP wxIDropTarget::DragOver(DWORD   grfKeyState,
     wxDragResult result;
     if ( m_pIDataObject ) {
         result = ConvertDragEffectToResult(
-            GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction()));
+            GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction(), *pdwEffect));
     }
     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"));
+    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;
     }
 
-    *pdwEffect = ConvertDragResultToEffect(
-                    m_pTarget->OnDragOver(pt.x, pt.y, result)
-                 );
+    // update drag image
+    m_pTarget->MSWUpdateDragImageOnDragOver(pt.x, pt.y,
+                                            ConvertDragEffectToResult(*pdwEffect));
 
     return S_OK;
 }
@@ -251,6 +316,9 @@ STDMETHODIMP wxIDropTarget::DragLeave()
   // release the held object
   RELEASE_AND_NULL(m_pIDataObject);
 
+  // update drag image
+  m_pTarget->MSWUpdateDragImageOnLeave();
+
   return S_OK;
 }
 
@@ -258,10 +326,10 @@ 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,
@@ -274,9 +342,6 @@ STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
     //      that it's the same we've already got in DragEnter
     wxASSERT( m_pIDataObject == pIDataSource );
 
-    // by default, nothing happens
-    *pdwEffect = DROPEFFECT_NONE;
-
     // we need client coordinates to pass to wxWin functions
     if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
     {
@@ -286,23 +351,32 @@ STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
     // 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);
+        m_pTarget->MSWSetDataSource(pIDataSource);
 
         // and now it has the data
         wxDragResult rc = ConvertDragEffectToResult(
-            GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction()));
+            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 is already DROPEFFECT_NONE
+        else {
+            *pdwEffect = DROPEFFECT_NONE;
+        }
+    }
+    else {
+        // OnDrop() returned false, no need to copy data
+        *pdwEffect = DROPEFFECT_NONE;
     }
-    //else: OnDrop() returned false, no need to copy data
 
     // release the held object
     RELEASE_AND_NULL(m_pIDataObject);
 
+    // update drag image
+    m_pTarget->MSWUpdateDragImageOnData(pt.x, pt.y,
+                                        ConvertDragEffectToResult(*pdwEffect));
+
     return S_OK;
 }
 
@@ -315,7 +389,8 @@ STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
 // ----------------------------------------------------------------------------
 
 wxDropTarget::wxDropTarget(wxDataObject *dataObj)
-            : wxDropTargetBase(dataObj)
+            : wxDropTargetBase(dataObj),
+              m_dropTargetHelper(NULL)
 {
     // create an IDropTarget implementation which will notify us about d&d
     // operations.
@@ -366,6 +441,8 @@ bool wxDropTarget::Register(WXHWND hwnd)
     // we will need the window handle for coords transformation later
     m_pIDropTarget->SetHwnd((HWND)hwnd);
 
+    MSWInitDragImageSupport();
+
     return true;
 #endif
 }
@@ -387,6 +464,9 @@ void wxDropTarget::Revoke(WXHWND hwnd)
     ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE);
 #endif
 
+    MSWEndDragImageSupport();
+
+    // remove window reference
     m_pIDropTarget->SetHwnd(0);
 #endif
 }
@@ -405,11 +485,8 @@ bool wxDropTarget::OnDrop(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y))
 // copy the data from the data source to the target data object
 bool wxDropTarget::GetData()
 {
-    wxDataFormat format = GetSupportedFormat(m_pIDataSource);
+    wxDataFormat format = MSWGetSupportedFormat(m_pIDataSource);
     if ( format == wxDF_INVALID ) {
-        // this is strange because IsAcceptedData() succeeded previously!
-        wxFAIL_MSG(wxT("strange - did supported formats list change?"));
-
         return false;
     }
 
@@ -447,22 +524,27 @@ bool wxDropTarget::GetData()
 // ----------------------------------------------------------------------------
 
 // we need a data source, so wxIDropTarget gives it to us using this function
-void wxDropTarget::SetDataSource(IDataObject *pIDataSource)
+void wxDropTarget::MSWSetDataSource(IDataObject *pIDataSource)
 {
     m_pIDataSource = pIDataSource;
 }
 
 // determine if we accept data of this type
-bool wxDropTarget::IsAcceptedData(IDataObject *pIDataSource) const
+bool wxDropTarget::MSWIsAcceptedData(IDataObject *pIDataSource) const
 {
-    return GetSupportedFormat(pIDataSource) != wxDF_INVALID;
+    return MSWGetSupportedFormat(pIDataSource) != wxDF_INVALID;
 }
 
 // ----------------------------------------------------------------------------
 // helper functions
 // ----------------------------------------------------------------------------
 
-wxDataFormat wxDropTarget::GetSupportedFormat(IDataObject *pIDataSource) const
+wxDataFormat wxDropTarget::GetMatchingPair()
+{
+    return MSWGetSupportedFormat( m_pIDataSource );
+}
+
+wxDataFormat wxDropTarget::MSWGetSupportedFormat(IDataObject *pIDataSource) const
 {
     // this strucutre describes a data of any type (first field will be
     // changing) being passed through global memory block.
@@ -504,6 +586,81 @@ wxDataFormat wxDropTarget::GetSupportedFormat(IDataObject *pIDataSource) const
     return n < nFormats ? format : wxFormatInvalid;
 }
 
+// ----------------------------------------------------------------------------
+// drag image functions
+// ----------------------------------------------------------------------------
+
+void
+wxDropTarget::MSWEndDragImageSupport()
+{
+    // release drop target helper
+    if ( m_dropTargetHelper != NULL )
+    {
+        m_dropTargetHelper->Release();
+        m_dropTargetHelper = NULL;
+    }
+}
+
+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);
+}
+
+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);
+    }
+}
+
+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);
+    }
+}
+
+void
+wxDropTarget::MSWUpdateDragImageOnEnter(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->DragEnter(m_pIDropTarget->GetHWND(), m_pIDataSource, &pt, dwEffect);
+    }
+}
+
+void
+wxDropTarget::MSWUpdateDragImageOnLeave()
+{
+    // call corresponding event on drop target helper
+    if ( m_dropTargetHelper != NULL )
+    {
+        m_dropTargetHelper->DragLeave();
+    }
+}
+
 // ----------------------------------------------------------------------------
 // private functions
 // ----------------------------------------------------------------------------
@@ -550,5 +707,4 @@ static DWORD ConvertDragResultToEffect(wxDragResult result)
     }
 }
 
-#endif
- // wxUSE_DRAG_AND_DROP
+#endif // wxUSE_OLE && wxUSE_DRAG_AND_DROP