X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/223d09f6b523aac674ef9b72a883dfa8d37c5d4e..8fbdfa4faf61ecc4177d9952d3f3718cf4514ae6:/src/msw/ole/droptgt.cpp?ds=sidebyside diff --git a/src/msw/ole/droptgt.cpp b/src/msw/ole/droptgt.cpp index 003a29891a..d1e4a97b11 100644 --- a/src/msw/ole/droptgt.cpp +++ b/src/msw/ole/droptgt.cpp @@ -30,20 +30,22 @@ #include "wx/setup.h" -#if wxUSE_DRAG_AND_DROP +#if wxUSE_OLE && wxUSE_DRAG_AND_DROP #include "wx/log.h" #ifdef __WIN32__ - #ifndef __GNUWIN32__ + #if !defined(__GNUWIN32__) || wxUSE_NORLANDER_HEADERS + #if wxCHECK_W32API_VERSION( 1, 0 ) + #include + #endif #include // for DROPFILES structure #endif #else #include #endif -#include "wx/dataobj.h" -#include "wx/msw/ole/droptgt.h" +#include "wx/dnd.h" #ifndef __WIN32__ #include @@ -61,37 +63,42 @@ 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 + 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 inline DWORD GetDropEffect(DWORD flags); }; +// ---------------------------------------------------------------------------- +// 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 @@ -108,7 +115,6 @@ wxIDropTarget::wxIDropTarget(wxDropTarget *pTarget) { m_cRef = 0; m_pTarget = pTarget; - m_cfFormat = wxDF_INVALID; m_pIDataObject = NULL; } @@ -136,29 +142,57 @@ STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource, POINTL pt, DWORD *pdwEffect) { - wxLogDebug(wxT("IDropTarget::DragEnter")); - - wxASSERT( m_pIDataObject == NULL ); - - if ( !m_pTarget->IsAcceptedData(pIDataSource) ) { - // we don't accept this kind of data - *pdwEffect = DROPEFFECT_NONE; + 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)); + } + + 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)) + ) + ); - return S_OK; + return S_OK; } // Name : wxIDropTarget::DragOver @@ -174,11 +208,28 @@ 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)); + } + 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 @@ -187,7 +238,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(); @@ -212,45 +263,41 @@ STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource, POINTL pt, DWORD *pdwEffect) { - wxLogDebug(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 ); - - 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 ); + + // 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")); } - } - 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->SetDataSource(pIDataSource); + + // and now it has the data + wxDragResult rc = ConvertDragEffectToResult(GetDropEffect(grfKeyState)); + rc = m_pTarget->OnData(pt.x, pt.y, rc); + if ( wxIsDragResultOk(rc) ) { + // operation succeeded + *pdwEffect = ConvertDragResultToEffect(rc); + } + //else: *pdwEffect is already DROPEFFECT_NONE + } + //else: OnDrop() returned FALSE, no need to copy data - return S_OK; + // release the held object + RELEASE_AND_NULL(m_pIDataObject); + + return S_OK; } // ============================================================================ @@ -261,17 +308,18 @@ STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource, // ctor/dtor // ---------------------------------------------------------------------------- -wxDropTarget::wxDropTarget() +wxDropTarget::wxDropTarget(wxDataObject *dataObj) + : wxDropTargetBase(dataObj) { - // 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,140 +328,196 @@ wxDropTarget::~wxDropTarget() bool wxDropTarget::Register(WXHWND hwnd) { - HRESULT hr = ::CoLockObjectExternal(m_pIDropTarget, TRUE, FALSE); - if ( FAILED(hr) ) { - wxLogApiError("CoLockObjectExternal", hr); - return FALSE; - } + HRESULT hr = ::CoLockObjectExternal(m_pIDropTarget, TRUE, FALSE); + if ( FAILED(hr) ) { + wxLogApiError(wxT("CoLockObjectExternal"), hr); + return FALSE; + } - hr = ::RegisterDragDrop((HWND) hwnd, m_pIDropTarget); - if ( FAILED(hr) ) { - ::CoLockObjectExternal(m_pIDropTarget, FALSE, FALSE); + hr = ::RegisterDragDrop((HWND) hwnd, m_pIDropTarget); + if ( FAILED(hr) ) { + ::CoLockObjectExternal(m_pIDropTarget, FALSE, FALSE); - wxLogApiError("RegisterDragDrop", hr); - return FALSE; - } + 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; } void wxDropTarget::Revoke(WXHWND hwnd) { - HRESULT hr = ::RevokeDragDrop((HWND) hwnd); + HRESULT hr = ::RevokeDragDrop((HWND) hwnd); + + if ( FAILED(hr) ) { + wxLogApiError(wxT("RevokeDragDrop"), hr); + } - if ( FAILED(hr) ) { - wxLogApiError("RevokeDragDrop", hr); - } + ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE); - ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE); + m_pIDropTarget->SetHwnd(0); } // ---------------------------------------------------------------------------- -// 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 = GetSupportedFormat(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; + 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 +// ---------------------------------------------------------------------------- -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::SetDataSource(IDataObject *pIDataSource) { - return OnDropText(x, y, (const wxChar *)pData); + m_pIDataSource = pIDataSource; } -size_t wxTextDropTarget::GetFormatCount() const +// determine if we accept data of this type +bool wxDropTarget::IsAcceptedData(IDataObject *pIDataSource) const { - return 1; + return GetSupportedFormat(pIDataSource) != wxDF_INVALID; } -wxDataFormat wxTextDropTarget::GetFormat(size_t WXUNUSED(n)) const +// ---------------------------------------------------------------------------- +// helper functions +// ---------------------------------------------------------------------------- + +wxDataFormat wxDropTarget::GetSupportedFormat(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 -// ============================================================================ +// ---------------------------------------------------------------------------- +// private functions +// ---------------------------------------------------------------------------- -bool wxFileDropTarget::OnDrop(long x, long y, const void *pData) +static wxDragResult ConvertDragEffectToResult(DWORD dwEffect) { - // 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(wxT("In wxFileDropTarget::OnDrop DragQueryFile returned %d " - "characters, %d expected."), len2, len - 1); - } - } + switch ( dwEffect ) { + case DROPEFFECT_COPY: + return wxDragCopy; - bool bResult = OnDropFiles(x, y, nFiles, (const wxChar**) aszFiles); + case DROPEFFECT_LINK: + return wxDragLink; - // free memory - for ( n = 0; n < nFiles; n++ ) { - delete [] aszFiles[n]; - } - delete [] aszFiles; + case DROPEFFECT_MOVE: + return wxDragMove; - return bResult; -} + default: + wxFAIL_MSG(wxT("invalid value in ConvertDragEffectToResult")); + // fall through -size_t wxFileDropTarget::GetFormatCount() const -{ - return 1; + case DROPEFFECT_NONE: + return wxDragNone; + } } -wxDataFormat wxFileDropTarget::GetFormat(size_t WXUNUSED(n)) const +static DWORD ConvertDragResultToEffect(wxDragResult result) { -#ifdef __WIN32__ - return wxDF_FILENAME; -#else - // TODO: how to implement this in WIN16? - return wxDF_TEXT; -#endif + 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