X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/161f4f7380e945b0769ad5f7122df55b8777396a..4a2d030adfa836f6ada1830c9057170d053bcc64:/src/msw/ole/droptgt.cpp?ds=sidebyside diff --git a/src/msw/ole/droptgt.cpp b/src/msw/ole/droptgt.cpp index aeb5185577..182ae4e3d0 100644 --- a/src/msw/ole/droptgt.cpp +++ b/src/msw/ole/droptgt.cpp @@ -1,12 +1,11 @@ /////////////////////////////////////////////////////////////////////////////// -// 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 -// Licence: wxWindows license +// Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// // ============================================================================ @@ -17,22 +16,26 @@ // 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 -#if wxUSE_DRAG_AND_DROP +#ifndef WX_PRECOMP + #include "wx/msw/wrapwin.h" + #include "wx/log.h" +#endif -#include "wx/log.h" +#include "wx/msw/private.h" + +#ifdef __WXWINCE__ + #include + #include +#endif #ifdef __WIN32__ #if !defined(__GNUWIN32__) || wxUSE_NORLANDER_HEADERS @@ -44,13 +47,34 @@ #include "wx/dnd.h" -#ifndef __WIN32__ - #include - #include -#endif - #include "wx/msw/ole/oleutils.h" +#include + +// 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 @@ -64,6 +88,7 @@ public: virtual ~wxIDropTarget(); // accessors for wxDropTarget + HWND GetHWND() const { return m_hwnd; } void SetHwnd(HWND hwnd) { m_hwnd = hwnd; } // IDropTarget methods @@ -81,7 +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); + static DWORD GetDropEffect(DWORD flags, wxDragResult defaultAction, DWORD pdwEffect); + + wxDECLARE_NO_COPY_CLASS(wxIDropTarget); }; // ---------------------------------------------------------------------------- @@ -98,19 +125,43 @@ 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 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_pIDataObject = NULL; } @@ -129,54 +180,88 @@ 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(wxT("IDropTarget::DragEnter")); + wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragEnter")); - wxASSERT( m_pIDataObject == NULL ); + wxASSERT_MSG( m_pIDataObject == NULL, + wxT("drop target must have data object") ); - if ( !m_pTarget->IsAcceptedData(pIDataSource) ) { - // we don't accept this kind of data - *pdwEffect = DROPEFFECT_NONE; + // 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)); + } - return S_OK; + penumFmt->Release(); + } + else + { + wxLogLastError(wxT("IDataObject::EnumFormatEtc")); } +#endif // 0 + + // 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)) - ) - ); + // 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, @@ -187,22 +272,32 @@ STDMETHODIMP wxIDropTarget::DragOver(DWORD grfKeyState, wxDragResult result; if ( m_pIDataObject ) { - result = ConvertDragEffectToResult(GetDropEffect(grfKeyState)); + result = ConvertDragEffectToResult( + 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; } @@ -213,7 +308,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(); @@ -221,6 +316,9 @@ STDMETHODIMP wxIDropTarget::DragLeave() // release the held object RELEASE_AND_NULL(m_pIDataObject); + // update drag image + m_pTarget->MSWUpdateDragImageOnLeave(); + return S_OK; } @@ -228,25 +326,22 @@ 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(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 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) ) { @@ -256,22 +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)); + 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 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; } @@ -284,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. @@ -303,59 +409,85 @@ 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(wxT("CoLockObjectExternal"), hr); - return FALSE; + 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; + return false; } // we will need the window handle for coords transformation later m_pIDropTarget->SetHwnd((HWND)hwnd); - return TRUE; + MSWInitDragImageSupport(); + + 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(wxT("RevokeDragDrop"), hr); } + // May exist in later WinCE versions +#ifndef __WXWINCE__ ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE); +#endif + + MSWEndDragImageSupport(); + // remove window reference 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 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; + return false; } STGMEDIUM stm; @@ -366,7 +498,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) ) { @@ -374,14 +506,14 @@ bool wxDropTarget::GetData() hr = dataObject->SetData(&fmtMemory, &stm, TRUE); if ( SUCCEEDED(hr) ) { - rc = TRUE; + rc = true; } else { - wxLogLastError(wxT("IDataObject::SetData()")); + wxLogApiError(wxT("IDataObject::SetData()"), hr); } } else { - wxLogLastError(wxT("IDataObject::GetData()")); + wxLogApiError(wxT("IDataObject::GetData()"), hr); } return rc; @@ -392,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. @@ -422,7 +559,7 @@ wxDataFormat wxDropTarget::GetSupportedFormat(IDataObject *pIDataSource) const // get the list of supported formats size_t nFormats = m_dataObject->GetFormatCount(wxDataObject::Set); wxDataFormat format; - wxDataFormat *formats; + wxDataFormat *formats; formats = nFormats == 1 ? &format : new wxDataFormat[nFormats]; m_dataObject->GetAllFormats(formats, wxDataObject::Set); @@ -449,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 // ---------------------------------------------------------------------------- @@ -459,6 +671,9 @@ static wxDragResult ConvertDragEffectToResult(DWORD dwEffect) case DROPEFFECT_COPY: return wxDragCopy; + case DROPEFFECT_LINK: + return wxDragLink; + case DROPEFFECT_MOVE: return wxDragMove; @@ -477,6 +692,9 @@ static DWORD ConvertDragResultToEffect(wxDragResult result) case wxDragCopy: return DROPEFFECT_COPY; + case wxDragLink: + return DROPEFFECT_LINK; + case wxDragMove: return DROPEFFECT_MOVE; @@ -489,5 +707,4 @@ static DWORD ConvertDragResultToEffect(wxDragResult result) } } -#endif - // wxUSE_DRAG_AND_DROP +#endif // wxUSE_OLE && wxUSE_DRAG_AND_DROP