X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/3f480da37ca0840ddbe48b908d511d2e9fc20bf4..d9506e775853406953c4b3ecd6f57b85abdafdfd:/src/msw/ole/droptgt.cpp diff --git a/src/msw/ole/droptgt.cpp b/src/msw/ole/droptgt.cpp index 0bb7f00ad4..b6d353d98c 100644 --- a/src/msw/ole/droptgt.cpp +++ b/src/msw/ole/droptgt.cpp @@ -42,8 +42,7 @@ #include #endif -#include "wx/dataobj.h" -#include "wx/msw/ole/droptgt.h" +#include "wx/dnd.h" #ifndef __WIN32__ #include @@ -61,37 +60,42 @@ class wxIDropTarget : public IDropTarget { public: - wxIDropTarget(wxDropTarget *p); - ~wxIDropTarget(); + wxIDropTarget(wxDropTarget *p); + ~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 +112,6 @@ wxIDropTarget::wxIDropTarget(wxDropTarget *pTarget) { m_cRef = 0; m_pTarget = pTarget; - m_cfFormat = wxDF_INVALID; m_pIDataObject = NULL; } @@ -136,29 +139,35 @@ STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource, POINTL pt, DWORD *pdwEffect) { - wxLogDebug("IDropTarget::DragEnter"); + wxLogDebug(wxT("IDropTarget::DragEnter")); - wxASSERT( m_pIDataObject == NULL ); + wxASSERT( m_pIDataObject == NULL ); - if ( !m_pTarget->IsAcceptedData(pIDataSource) ) { - // we don't accept this kind of data - *pdwEffect = DROPEFFECT_NONE; + if ( !m_pTarget->IsAcceptedData(pIDataSource) ) { + // we don't accept this kind of data + *pdwEffect = DROPEFFECT_NONE; - return S_OK; - } - - // 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("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 +183,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("ScreenToClient"); + } + + *pdwEffect = ConvertDragResultToEffect( + m_pTarget->OnDragOver(pt.x, pt.y, result) + ); + + return S_OK; } // Name : wxIDropTarget::DragLeave @@ -187,7 +213,7 @@ STDMETHODIMP wxIDropTarget::DragOver(DWORD grfKeyState, // Notes : good place to do any clean-up STDMETHODIMP wxIDropTarget::DragLeave() { - wxLogDebug("IDropTarget::DragLeave"); + wxLogDebug(wxT("IDropTarget::DragLeave")); // remove the UI feedback m_pTarget->OnLeave(); @@ -212,45 +238,41 @@ STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource, POINTL pt, DWORD *pdwEffect) { - wxLogDebug("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); + 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 ); + + // by default, nothing happens + *pdwEffect = DROPEFFECT_NONE; + + // we need client coordinates to pass to wxWin functions + if ( !ScreenToClient(m_hwnd, (POINT *)&pt) ) + { + wxLogLastError("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 +283,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 +303,189 @@ 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("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("RegisterDragDrop", hr); - return FALSE; - } + // we will need the window handle for coords transformation later + m_pIDropTarget->SetHwnd((HWND)hwnd); - return TRUE; + return TRUE; } void wxDropTarget::Revoke(WXHWND hwnd) { - HRESULT hr = ::RevokeDragDrop((HWND) hwnd); + HRESULT hr = ::RevokeDragDrop((HWND) hwnd); - if ( FAILED(hr) ) { - wxLogApiError("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)) { - // 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; + return TRUE; +} + +// copy the data from the data source to the target data object +bool wxDropTarget::GetData() +{ + 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 { + wxLogLastError("IDataObject::SetData()"); + } + } + else { + wxLogLastError("IDataObject::GetData()"); + } + + 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 char *)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, *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 - 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); - } - } + switch ( dwEffect ) { + case DROPEFFECT_COPY: + return wxDragCopy; - bool bResult = OnDropFiles(x, y, nFiles, (const char**) aszFiles); + case DROPEFFECT_MOVE: + return wxDragMove; - // free memory - for ( n = 0; n < nFiles; n++ ) { - delete [] aszFiles[n]; - } - delete [] aszFiles; + default: + wxFAIL_MSG(wxT("invalid value in ConvertDragEffectToResult")); + // fall through - return bResult; + case DROPEFFECT_NONE: + return wxDragNone; + } } -size_t wxFileDropTarget::GetFormatCount() const +static DWORD ConvertDragResultToEffect(wxDragResult result) { - return 1; -} + switch ( result ) { + case wxDragCopy: + return DROPEFFECT_COPY; -wxDataFormat wxFileDropTarget::GetFormat(size_t WXUNUSED(n)) const -{ -#ifdef __WIN32__ - return wxDF_FILENAME; -#else - // TODO: how to implement this in WIN16? - return wxDF_TEXT; -#endif + case wxDragMove: + return DROPEFFECT_MOVE; + + default: + wxFAIL_MSG(wxT("invalid value in ConvertDragResultToEffect")); + // fall through + + case wxDragNone: + return DROPEFFECT_NONE; + } } #endif