// Name: ole/droptgt.cpp
// Purpose: wxDropTarget implementation
// Author: Vadim Zeitlin
-// Modified by:
-// Created:
+// Modified by:
+// Created:
// RCS-ID: $Id$
// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
// Licence: wxWindows license
#endif
// For compilers that support precompilation, includes "wx.h".
-#define IN_WX_MAIN_CPP
#include "wx/wxprec.h"
#if defined(__BORLANDC__)
#pragma hdrstop
#endif
-#include <wx/setup.h>
+#include "wx/setup.h"
-#if USE_DRAG_AND_DROP
+#if wxUSE_OLE && wxUSE_DRAG_AND_DROP
-#include <wx/log.h>
+#include "wx/log.h"
#ifdef __WIN32__
-#ifndef __GNUWIN32__
-#include <shlobj.h> // for DROPFILES structure
-#endif
+ #if !defined(__GNUWIN32__) || wxUSE_NORLANDER_HEADERS
+ #if wxCHECK_W32API_VERSION( 1, 0 )
+ #include <windows.h>
+ #endif
+ #include <shlobj.h> // for DROPFILES structure
+ #endif
#else
-#include <shellapi.h>
+ #include <shellapi.h>
#endif
-#include <wx/msw/ole/droptgt.h>
+#include "wx/dnd.h"
#ifndef __WIN32__
-#include <ole2.h>
-#include <olestd.h>
+ #include <ole2.h>
+ #include <olestd.h>
#endif
-#include <wx/msw/ole/oleutils.h>
+#include "wx/msw/ole/oleutils.h"
// ----------------------------------------------------------------------------
// IDropTarget interface: forward all interesting things to wxDropTarget
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
+// Returns : DWORD combined from DROPEFFECT_xxx constants
// Params : [in] DWORD flags kbd & mouse flags as passed to
// IDropTarget methods
// Notes : We do "move" normally and "copy" if <Ctrl> 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)
{
}
wxIDropTarget::wxIDropTarget(wxDropTarget *pTarget)
-{
- m_cRef = 0;
+{
m_pTarget = pTarget;
- m_cfFormat = (wxDataFormat) 0;
- m_pIDataObject = NULL;
+ m_pIDataObject = NULL;
}
-wxIDropTarget::~wxIDropTarget()
-{
+wxIDropTarget::~wxIDropTarget()
+{
}
BEGIN_IID_TABLE(wxIDropTarget)
// [in] DWORD grfKeyState : kbd & mouse state
// [in] POINTL pt : mouse coordinates
// [out]DWORD *pdwEffect : effect flag
-// Notes :
+// Notes :
STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource,
DWORD grfKeyState,
POINTL pt,
DWORD *pdwEffect)
{
- wxLogDebug("IDropTarget::DragEnter");
-
- wxASSERT( m_pIDataObject == NULL );
+ 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
- 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;
- }
+ return S_OK;
+ }
- // @@ 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();
+ // 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
// 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
+// 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));
+ }
+ 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
// 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);
-
+
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,
+// 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 );
+
+ // 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;
}
// ============================================================================
// 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);
}
// ----------------------------------------------------------------------------
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(wxT("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(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);
- // @ 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((wxDataFormat) 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;
+ }
+
+ 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::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;
+ 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; // @@ 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);
- }
- }
+ switch ( dwEffect ) {
+ case DROPEFFECT_COPY:
+ return wxDragCopy;
- bool bResult = OnDropFiles(x, y, nFiles, (const char**) 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
- // USE_DRAG_AND_DROP
+ // wxUSE_DRAG_AND_DROP