X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/bbf1f0e5cffb8c01696eb26e254857a61f017d70..0d53638f7147c18153f63fdfc096b17be6e22a27:/src/msw/ole/droptgt.cpp diff --git a/src/msw/ole/droptgt.cpp b/src/msw/ole/droptgt.cpp index 7ff89237d5..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$ +// Modified by: +// Created: // Copyright: (c) 1998 Vadim Zeitlin -// Licence: wxWindows license +// Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// // ============================================================================ @@ -17,40 +16,64 @@ // headers // ---------------------------------------------------------------------------- -#ifdef __GNUG__ -#pragma implementation "droptgt.h" -#endif - // For compilers that support precompilation, includes "wx.h". -#define IN_WX_MAIN_CPP #include "wx/wxprec.h" #if defined(__BORLANDC__) -#pragma hdrstop + #pragma hdrstop #endif -#include +#if wxUSE_OLE && wxUSE_DRAG_AND_DROP -#if USE_DRAG_AND_DROP +#ifndef WX_PRECOMP + #include "wx/msw/wrapwin.h" + #include "wx/log.h" +#endif -#include +#include "wx/msw/private.h" -#ifdef __WIN32__ -#ifndef __GNUWIN32__ -#include // for DROPFILES structure +#ifdef __WXWINCE__ + #include + #include #endif + +#ifdef __WIN32__ + #if !defined(__GNUWIN32__) || wxUSE_NORLANDER_HEADERS + #include // for DROPFILES structure + #endif #else -#include + #include #endif -#include +#include "wx/dnd.h" -#ifndef __WIN32__ -#include -#include -#endif +#include "wx/msw/ole/oleutils.h" -#include +#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 @@ -61,59 +84,90 @@ 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 +// Returns : DWORD combined from DROPEFFECT_xxx constants +// 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 +// 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 = 0; - m_pIDataObject = NULL; + m_pIDataObject = NULL; } -wxIDropTarget::~wxIDropTarget() -{ +wxIDropTarget::~wxIDropTarget() +{ } BEGIN_IID_TABLE(wxIDropTarget) @@ -123,100 +177,129 @@ END_IID_TABLE; IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget) -#if 0 - STDMETHODIMP wxIDropTarget::QueryInterface(REFIID riid, void **ppv) - { -// wxLogQueryInterface(wxIDropTarget, riid); - - if ( IsIidFromList(riid, ms_aIids, WXSIZEOF(ms_aIids)) ) { - *ppv = this; - AddRef(); - - return S_OK; - } - else { - *ppv = NULL; - - return (HRESULT) E_NOINTERFACE; - } - } - - STDMETHODIMP_(ULONG) wxIDropTarget::AddRef() - { - wxLogAddRef(wxIDropTarget, m_cRef); - - return ++m_cRef; - } - - STDMETHODIMP_(ULONG) wxIDropTarget::Release() - { - wxLogRelease(wxIDropTarget, m_cRef); - - if ( --m_cRef == 0 ) { - delete this; - return 0; - } - else - return m_cRef; - } -#endif - // 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 -// Notes : +// 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("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)); + } + + penumFmt->Release(); + } + else + { + wxLogLastError(wxT("IDataObject::EnumFormatEtc")); + } +#endif // 0 - return S_OK; - } + // for use in OnEnter and OnDrag calls + m_pTarget->MSWSetDataSource(pIDataSource); - // @@ should check the point also? - - *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(); + 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)) + ) + ); + } - // give some visual feedback - m_pTarget->OnEnter(); + // 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; + 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 -// Notes : We're called on every WM_MOUSEMOVE, so this function should be +// 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 @@ -225,70 +308,76 @@ STDMETHODIMP wxIDropTarget::DragOver(DWORD grfKeyState, // Notes : good place to do any clean-up STDMETHODIMP wxIDropTarget::DragLeave() { - wxLogDebug("IDropTarget::DragLeave"); + wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragLeave")); // remove the UI feedback m_pTarget->OnLeave(); // release the held object RELEASE_AND_NULL(m_pIDataObject); - + + // update drag image + m_pTarget->MSWUpdateDragImageOnLeave(); + return S_OK; } // Name : wxIDropTarget::Drop -// Purpose : Instructs the drop target to paste data that was just now +// 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 -// Notes : -STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource, - DWORD grfKeyState, - POINTL pt, +// 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("IDropTarget::Drop"); - - // @@ 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 != 0 ); - - FORMATETC fmtMemory; - fmtMemory.cfFormat = m_cfFormat; - fmtMemory.ptd = NULL; - fmtMemory.dwAspect = DVASPECT_CONTENT; - fmtMemory.lindex = -1; - fmtMemory.tymed = TYMED_HGLOBAL; // @@@@ 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; } // ============================================================================ @@ -299,17 +388,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); } // ---------------------------------------------------------------------------- @@ -318,142 +409,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); + } + + // May exist in later WinCE versions +#ifndef __WXWINCE__ + ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE); +#endif - ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE); + 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)) { - // 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); - // @ 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(s_fmtMemory.cfFormat); - return TRUE; + return true; +} + +// copy the data from the data source to the target data object +bool wxDropTarget::GetData() +{ + wxDataFormat format = MSWGetSupportedFormat(m_pIDataSource); + if ( format == wxDF_INVALID ) { + 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 FALSE; + return rc; } -// ============================================================================ -// wxTextDropTarget -// ============================================================================ +// ---------------------------------------------------------------------------- +// callbacks used by wxIDropTarget +// ---------------------------------------------------------------------------- -bool wxTextDropTarget::OnDrop(long x, long y, const void *pData) +// we need a data source, so wxIDropTarget gives it to us using this function +void wxDropTarget::MSWSetDataSource(IDataObject *pIDataSource) { - return OnDropText(x, y, (const char *)pData); + m_pIDataSource = pIDataSource; } -size_t wxTextDropTarget::GetFormatCount() const +// determine if we accept data of this type +bool wxDropTarget::MSWIsAcceptedData(IDataObject *pIDataSource) const { - return 1; + return MSWGetSupportedFormat(pIDataSource) != wxDF_INVALID; } -wxDataFormat wxTextDropTarget::GetFormat(size_t WXUNUSED(n)) const +// ---------------------------------------------------------------------------- +// helper functions +// ---------------------------------------------------------------------------- + +wxDataFormat wxDropTarget::GetMatchingPair() { - return CF_TEXT; + return MSWGetSupportedFormat( m_pIDataSource ); } -// ============================================================================ -// wxFileDropTarget -// ============================================================================ +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. + 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; + } + } -bool wxFileDropTarget::OnDrop(long x, long y, const void *pData) + if ( formats != &format ) { + // free memory if we allocated it + delete [] formats; + } + + return n < nFormats ? format : wxFormatInvalid; +} + +// ---------------------------------------------------------------------------- +// drag image functions +// ---------------------------------------------------------------------------- + +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; // @@ it works, but I'm not sure about it - - // get number of files (magic value -1) - UINT nFiles = ::DragQueryFile(hdrop, -1, NULL, 0); - - // for each file get the length, allocate memory and then get the name - char **aszFiles = new char *[nFiles]; - UINT len, n; - for ( n = 0; n < nFiles; n++ ) { - // +1 for terminating NUL - len = ::DragQueryFile(hdrop, n, NULL, 0) + 1; - - aszFiles[n] = new char[len]; - - UINT len2 = ::DragQueryFile(hdrop, n, aszFiles[n], len); - if ( len2 != len - 1 ) { - wxLogDebug("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 char**) 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 CF_HDROP; -#else - // TODO: how to implement this in WIN16? - return CF_TEXT; -#endif + // call corresponding event on drop target helper + if ( m_dropTargetHelper != NULL ) + { + m_dropTargetHelper->DragLeave(); + } } -#endif - // USE_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