///////////////////////////////////////////////////////////////////////////////
-// 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 <zeitlin@dptmaths.ens-cachan.fr>
-// Licence: wxWindows license
+// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// 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
-#include "wx/log.h"
+#ifndef WX_PRECOMP
+ #include "wx/msw/wrapwin.h"
+ #include "wx/log.h"
+#endif
+
+#include "wx/msw/private.h"
+
+#ifdef __WXWINCE__
+ #include <winreg.h>
+ #include <ole2.h>
+#endif
#ifdef __WIN32__
#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 "wx/dnd.h"
-#ifndef __WIN32__
- #include <ole2.h>
- #include <olestd.h>
-#endif
-
#include "wx/msw/ole/oleutils.h"
+#include <initguid.h>
+
+// 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
virtual ~wxIDropTarget();
// accessors for wxDropTarget
+ HWND GetHWND() const { return m_hwnd; }
void SetHwnd(HWND hwnd) { m_hwnd = hwnd; }
// IDropTarget methods
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);
};
// ----------------------------------------------------------------------------
// 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 <Ctrl> 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;
}
// 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,
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;
}
// 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();
// release the held object
RELEASE_AND_NULL(m_pIDataObject);
+ // update drag image
+ m_pTarget->MSWUpdateDragImageOnLeave();
+
return S_OK;
}
// 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) )
{
// 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;
}
// ----------------------------------------------------------------------------
wxDropTarget::wxDropTarget(wxDataObject *dataObj)
- : wxDropTargetBase(dataObj)
+ : wxDropTargetBase(dataObj),
+ m_dropTargetHelper(NULL)
{
// create an IDropTarget implementation which will notify us about d&d
// operations.
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;
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) ) {
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;
// ----------------------------------------------------------------------------
// 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.
// 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);
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
// ----------------------------------------------------------------------------
case DROPEFFECT_COPY:
return wxDragCopy;
+ case DROPEFFECT_LINK:
+ return wxDragLink;
+
case DROPEFFECT_MOVE:
return wxDragMove;
case wxDragCopy:
return DROPEFFECT_COPY;
+ case wxDragLink:
+ return DROPEFFECT_LINK;
+
case wxDragMove:
return DROPEFFECT_MOVE;
}
}
-#endif
- // wxUSE_DRAG_AND_DROP
+#endif // wxUSE_OLE && wxUSE_DRAG_AND_DROP