mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-10-02 01:03:19 +02:00
775dc8a9c0
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4 8ced0084-cf51-0410-be5f-012b33b47a6e
570 lines
18 KiB
C++
570 lines
18 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/ole/droptgt.cpp
|
|
// Purpose: wxDropTarget implementation
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created:
|
|
// RCS-ID: $Id: droptgt.cpp 44229 2007-01-15 19:02:29Z VZ $
|
|
// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
|
|
// Licence: wxWindows licence
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// Declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#if defined(__BORLANDC__)
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_OLE && wxUSE_DRAG_AND_DROP
|
|
|
|
#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
|
|
#include <shlobj.h> // for DROPFILES structure
|
|
#endif
|
|
#else
|
|
#include <shellapi.h>
|
|
#endif
|
|
|
|
#include "wx/dnd.h"
|
|
|
|
#include "wx/msw/ole/oleutils.h"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// IDropTarget interface: forward all interesting things to wxDropTarget
|
|
// (the name is unfortunate, but wx_I_DropTarget is not at all the same thing
|
|
// as wxDropTarget which is 'public' class, while this one is private)
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class wxIDropTarget : public IDropTarget
|
|
{
|
|
public:
|
|
wxIDropTarget(wxDropTarget *p);
|
|
virtual ~wxIDropTarget();
|
|
|
|
// accessors for wxDropTarget
|
|
void SetHwnd(HWND hwnd) { m_hwnd = hwnd; }
|
|
|
|
// IDropTarget methods
|
|
STDMETHODIMP DragEnter(LPDATAOBJECT, DWORD, POINTL, LPDWORD);
|
|
STDMETHODIMP DragOver(DWORD, POINTL, LPDWORD);
|
|
STDMETHODIMP DragLeave();
|
|
STDMETHODIMP Drop(LPDATAOBJECT, DWORD, POINTL, LPDWORD);
|
|
|
|
DECLARE_IUNKNOWN_METHODS;
|
|
|
|
protected:
|
|
IDataObject *m_pIDataObject; // !NULL between DragEnter and DragLeave/Drop
|
|
wxDropTarget *m_pTarget; // the real target (we're just a proxy)
|
|
|
|
HWND m_hwnd; // window we're associated with
|
|
|
|
// get default drop effect for given keyboard flags
|
|
static DWORD GetDropEffect(DWORD flags, wxDragResult defaultAction, DWORD pdwEffect);
|
|
|
|
DECLARE_NO_COPY_CLASS(wxIDropTarget)
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// private functions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
static wxDragResult ConvertDragEffectToResult(DWORD dwEffect);
|
|
static DWORD ConvertDragResultToEffect(wxDragResult result);
|
|
|
|
// ============================================================================
|
|
// wxIDropTarget implementation
|
|
// ============================================================================
|
|
|
|
// 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
|
|
// 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,
|
|
wxDragResult defaultAction,
|
|
DWORD pdwEffect)
|
|
{
|
|
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_pTarget = pTarget;
|
|
m_pIDataObject = NULL;
|
|
}
|
|
|
|
wxIDropTarget::~wxIDropTarget()
|
|
{
|
|
}
|
|
|
|
BEGIN_IID_TABLE(wxIDropTarget)
|
|
ADD_IID(Unknown)
|
|
ADD_IID(DropTarget)
|
|
END_IID_TABLE;
|
|
|
|
IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget)
|
|
|
|
// 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 :
|
|
STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource,
|
|
DWORD grfKeyState,
|
|
POINTL pt,
|
|
DWORD *pdwEffect)
|
|
{
|
|
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;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// 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
|
|
*pdwEffect = ConvertDragResultToEffect(
|
|
m_pTarget->OnEnter(pt.x, pt.y, ConvertDragEffectToResult(
|
|
GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction(), *pdwEffect))
|
|
)
|
|
);
|
|
|
|
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
|
|
// very efficient.
|
|
STDMETHODIMP wxIDropTarget::DragOver(DWORD grfKeyState,
|
|
POINTL pt,
|
|
LPDWORD pdwEffect)
|
|
{
|
|
// there are too many of them... wxLogDebug("IDropTarget::DragOver");
|
|
|
|
wxDragResult result;
|
|
if ( m_pIDataObject ) {
|
|
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"));
|
|
}
|
|
|
|
*pdwEffect = ConvertDragResultToEffect(
|
|
m_pTarget->OnDragOver(pt.x, pt.y, result)
|
|
);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Name : wxIDropTarget::DragLeave
|
|
// Purpose : Informs the drop target that the operation has left its window.
|
|
// Returns : S_OK
|
|
// Notes : good place to do any clean-up
|
|
STDMETHODIMP wxIDropTarget::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
|
|
// 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 occurred?
|
|
// [ouy]DWORD *pdwEffect operation effect
|
|
// Notes :
|
|
STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
|
|
DWORD grfKeyState,
|
|
POINTL pt,
|
|
DWORD *pdwEffect)
|
|
{
|
|
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"));
|
|
}
|
|
|
|
// 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, 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;
|
|
}
|
|
|
|
// release the held object
|
|
RELEASE_AND_NULL(m_pIDataObject);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// ============================================================================
|
|
// wxDropTarget implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// ctor/dtor
|
|
// ----------------------------------------------------------------------------
|
|
|
|
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();
|
|
}
|
|
|
|
wxDropTarget::~wxDropTarget()
|
|
{
|
|
ReleaseInterface(m_pIDropTarget);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// [un]register drop handler
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxDropTarget::Register(WXHWND hwnd)
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
// we will need the window handle for coords transformation later
|
|
m_pIDropTarget->SetHwnd((HWND)hwnd);
|
|
|
|
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
|
|
|
|
m_pIDropTarget->SetHwnd(0);
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// base class pure virtuals
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// 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()
|
|
{
|
|
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 rc;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// callbacks used by wxIDropTarget
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// we need a data source, so wxIDropTarget gives it to us using this function
|
|
void wxDropTarget::SetDataSource(IDataObject *pIDataSource)
|
|
{
|
|
m_pIDataSource = pIDataSource;
|
|
}
|
|
|
|
// determine if we accept data of this type
|
|
bool wxDropTarget::IsAcceptedData(IDataObject *pIDataSource) const
|
|
{
|
|
return GetSupportedFormat(pIDataSource) != wxDF_INVALID;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// helper functions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxDataFormat wxDropTarget::GetSupportedFormat(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;
|
|
}
|
|
}
|
|
|
|
if ( formats != &format ) {
|
|
// free memory if we allocated it
|
|
delete [] formats;
|
|
}
|
|
|
|
return n < nFormats ? format : wxFormatInvalid;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// 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
|