1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/ole/droptgt.cpp
3 // Purpose: wxDropTarget implementation
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
23 #if defined(__BORLANDC__)
27 #if wxUSE_OLE && wxUSE_DRAG_AND_DROP
30 #include "wx/msw/wrapwin.h"
34 #include "wx/msw/private.h"
42 #if !defined(__GNUWIN32__) || wxUSE_NORLANDER_HEADERS
43 #include <shlobj.h> // for DROPFILES structure
51 #include "wx/msw/ole/oleutils.h"
55 // Some (very) old SDKs don't define IDropTargetHelper, so define our own
56 // version of it here.
57 struct wxIDropTargetHelper
: public IUnknown
59 virtual HRESULT STDMETHODCALLTYPE
DragEnter(HWND hwndTarget
,
60 IDataObject
*pDataObject
,
63 virtual HRESULT STDMETHODCALLTYPE
DragLeave() = 0;
64 virtual HRESULT STDMETHODCALLTYPE
DragOver(POINT
*ppt
, DWORD dwEffect
) = 0;
65 virtual HRESULT STDMETHODCALLTYPE
Drop(IDataObject
*pDataObject
,
68 virtual HRESULT STDMETHODCALLTYPE
Show(BOOL fShow
) = 0;
73 DEFINE_GUID(wxCLSID_DragDropHelper
,
74 0x4657278A,0x411B,0x11D2,0x83,0x9A,0x00,0xC0,0x4F,0xD9,0x18,0xD0);
75 DEFINE_GUID(wxIID_IDropTargetHelper
,
76 0x4657278B,0x411B,0x11D2,0x83,0x9A,0x00,0xC0,0x4F,0xD9,0x18,0xD0);
79 // ----------------------------------------------------------------------------
80 // IDropTarget interface: forward all interesting things to wxDropTarget
81 // (the name is unfortunate, but wx_I_DropTarget is not at all the same thing
82 // as wxDropTarget which is 'public' class, while this one is private)
83 // ----------------------------------------------------------------------------
85 class wxIDropTarget
: public IDropTarget
88 wxIDropTarget(wxDropTarget
*p
);
89 virtual ~wxIDropTarget();
91 // accessors for wxDropTarget
92 HWND
GetHWND() const { return m_hwnd
; }
93 void SetHwnd(HWND hwnd
) { m_hwnd
= hwnd
; }
95 // IDropTarget methods
96 STDMETHODIMP
DragEnter(LPDATAOBJECT
, DWORD
, POINTL
, LPDWORD
);
97 STDMETHODIMP
DragOver(DWORD
, POINTL
, LPDWORD
);
98 STDMETHODIMP
DragLeave();
99 STDMETHODIMP
Drop(LPDATAOBJECT
, DWORD
, POINTL
, LPDWORD
);
101 DECLARE_IUNKNOWN_METHODS
;
104 IDataObject
*m_pIDataObject
; // !NULL between DragEnter and DragLeave/Drop
105 wxDropTarget
*m_pTarget
; // the real target (we're just a proxy)
107 HWND m_hwnd
; // window we're associated with
109 // get default drop effect for given keyboard flags
110 static DWORD
GetDropEffect(DWORD flags
, wxDragResult defaultAction
, DWORD pdwEffect
);
112 wxDECLARE_NO_COPY_CLASS(wxIDropTarget
);
115 // ----------------------------------------------------------------------------
117 // ----------------------------------------------------------------------------
119 static wxDragResult
ConvertDragEffectToResult(DWORD dwEffect
);
120 static DWORD
ConvertDragResultToEffect(wxDragResult result
);
122 // ============================================================================
123 // wxIDropTarget implementation
124 // ============================================================================
126 // Name : static wxIDropTarget::GetDropEffect
127 // Purpose : determine the drop operation from keyboard/mouse state.
128 // Returns : DWORD combined from DROPEFFECT_xxx constants
129 // Params : [in] DWORD flags kbd & mouse flags as passed to
130 // IDropTarget methods
131 // [in] wxDragResult defaultAction the default action of the drop target
132 // [in] DWORD pdwEffect the supported actions of the drop
133 // source passed to IDropTarget methods
134 // Notes : We do "move" normally and "copy" if <Ctrl> is pressed,
135 // which is the standard behaviour (currently there is no
136 // way to redefine it)
137 DWORD
wxIDropTarget::GetDropEffect(DWORD flags
,
138 wxDragResult defaultAction
,
141 DWORD effectiveAction
;
142 if ( defaultAction
== wxDragCopy
)
143 effectiveAction
= flags
& MK_SHIFT
? DROPEFFECT_MOVE
: DROPEFFECT_COPY
;
145 effectiveAction
= flags
& MK_CONTROL
? DROPEFFECT_COPY
: DROPEFFECT_MOVE
;
147 if ( !(effectiveAction
& pdwEffect
) )
149 // the action is not supported by drag source, fall back to something
150 // that it does support
151 if ( pdwEffect
& DROPEFFECT_MOVE
)
152 effectiveAction
= DROPEFFECT_MOVE
;
153 else if ( pdwEffect
& DROPEFFECT_COPY
)
154 effectiveAction
= DROPEFFECT_COPY
;
155 else if ( pdwEffect
& DROPEFFECT_LINK
)
156 effectiveAction
= DROPEFFECT_LINK
;
158 effectiveAction
= DROPEFFECT_NONE
;
161 return effectiveAction
;
164 wxIDropTarget::wxIDropTarget(wxDropTarget
*pTarget
)
167 m_pIDataObject
= NULL
;
170 wxIDropTarget::~wxIDropTarget()
174 BEGIN_IID_TABLE(wxIDropTarget
)
179 IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget
)
181 // Name : wxIDropTarget::DragEnter
182 // Purpose : Called when the mouse enters the window (dragging something)
184 // Params : [in] IDataObject *pIDataSource : source data
185 // [in] DWORD grfKeyState : kbd & mouse state
186 // [in] POINTL pt : mouse coordinates
187 // [in/out]DWORD *pdwEffect : effect flag
188 // In: Supported effects
189 // Out: Resulting effect
191 STDMETHODIMP
wxIDropTarget::DragEnter(IDataObject
*pIDataSource
,
196 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::DragEnter"));
198 wxASSERT_MSG( m_pIDataObject
== NULL
,
199 wxT("drop target must have data object") );
201 // show the list of formats supported by the source data object for the
202 // debugging purposes, this is quite useful sometimes - please don't remove
204 IEnumFORMATETC
*penumFmt
;
205 if ( SUCCEEDED(pIDataSource
->EnumFormatEtc(DATADIR_GET
, &penumFmt
)) )
208 while ( penumFmt
->Next(1, &fmt
, NULL
) == S_OK
)
210 wxLogDebug(wxT("Drop source supports format %s"),
211 wxDataObject::GetFormatName(fmt
.cfFormat
));
218 wxLogLastError(wxT("IDataObject::EnumFormatEtc"));
222 // for use in OnEnter and OnDrag calls
223 m_pTarget
->MSWSetDataSource(pIDataSource
);
225 // get hold of the data object
226 m_pIDataObject
= pIDataSource
;
227 m_pIDataObject
->AddRef();
229 if ( !m_pTarget
->MSWIsAcceptedData(pIDataSource
) ) {
230 // we don't accept this kind of data
231 *pdwEffect
= DROPEFFECT_NONE
;
235 // we need client coordinates to pass to wxWin functions
236 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
238 wxLogLastError(wxT("ScreenToClient"));
241 // give some visual feedback
242 *pdwEffect
= ConvertDragResultToEffect(
243 m_pTarget
->OnEnter(pt
.x
, pt
.y
, ConvertDragEffectToResult(
244 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
))
250 const wxDragResult res
= ConvertDragEffectToResult(*pdwEffect
);
251 m_pTarget
->MSWUpdateDragImageOnEnter(pt
.x
, pt
.y
, res
);
252 m_pTarget
->MSWUpdateDragImageOnDragOver(pt
.x
, pt
.y
, res
);
259 // Name : wxIDropTarget::DragOver
260 // Purpose : Indicates that the mouse was moved inside the window represented
261 // by this drop target.
263 // Params : [in] DWORD grfKeyState kbd & mouse state
264 // [in] POINTL pt mouse coordinates
265 // [in/out]LPDWORD pdwEffect current effect flag
266 // Notes : We're called on every WM_MOUSEMOVE, so this function should be
268 STDMETHODIMP
wxIDropTarget::DragOver(DWORD grfKeyState
,
272 // there are too many of them... wxLogDebug("IDropTarget::DragOver");
275 if ( m_pIDataObject
) {
276 result
= ConvertDragEffectToResult(
277 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
));
280 // can't accept data anyhow normally
284 if ( result
!= wxDragNone
) {
285 // we need client coordinates to pass to wxWin functions
286 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
288 wxLogLastError(wxT("ScreenToClient"));
291 *pdwEffect
= ConvertDragResultToEffect(
292 m_pTarget
->OnDragOver(pt
.x
, pt
.y
, result
)
296 *pdwEffect
= DROPEFFECT_NONE
;
300 m_pTarget
->MSWUpdateDragImageOnDragOver(pt
.x
, pt
.y
,
301 ConvertDragEffectToResult(*pdwEffect
));
306 // Name : wxIDropTarget::DragLeave
307 // Purpose : Informs the drop target that the operation has left its window.
309 // Notes : good place to do any clean-up
310 STDMETHODIMP
wxIDropTarget::DragLeave()
312 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::DragLeave"));
314 // remove the UI feedback
315 m_pTarget
->OnLeave();
317 // release the held object
318 RELEASE_AND_NULL(m_pIDataObject
);
321 m_pTarget
->MSWUpdateDragImageOnLeave();
326 // Name : wxIDropTarget::Drop
327 // Purpose : Instructs the drop target to paste data that was just now
330 // Params : [in] IDataObject *pIDataSource the data to paste
331 // [in] DWORD grfKeyState kbd & mouse state
332 // [in] POINTL pt where the drop occurred?
333 // [in/out]DWORD *pdwEffect operation effect
335 STDMETHODIMP
wxIDropTarget::Drop(IDataObject
*pIDataSource
,
340 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::Drop"));
342 // TODO I don't know why there is this parameter, but so far I assume
343 // that it's the same we've already got in DragEnter
344 wxASSERT( m_pIDataObject
== pIDataSource
);
346 // we need client coordinates to pass to wxWin functions
347 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
349 wxLogLastError(wxT("ScreenToClient"));
352 // first ask the drop target if it wants data
353 if ( m_pTarget
->OnDrop(pt
.x
, pt
.y
) ) {
354 // it does, so give it the data source
355 m_pTarget
->MSWSetDataSource(pIDataSource
);
357 // and now it has the data
358 wxDragResult rc
= ConvertDragEffectToResult(
359 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
));
360 rc
= m_pTarget
->OnData(pt
.x
, pt
.y
, rc
);
361 if ( wxIsDragResultOk(rc
) ) {
362 // operation succeeded
363 *pdwEffect
= ConvertDragResultToEffect(rc
);
366 *pdwEffect
= DROPEFFECT_NONE
;
370 // OnDrop() returned false, no need to copy data
371 *pdwEffect
= DROPEFFECT_NONE
;
374 // release the held object
375 RELEASE_AND_NULL(m_pIDataObject
);
378 m_pTarget
->MSWUpdateDragImageOnData(pt
.x
, pt
.y
,
379 ConvertDragEffectToResult(*pdwEffect
));
384 // ============================================================================
385 // wxDropTarget implementation
386 // ============================================================================
388 // ----------------------------------------------------------------------------
390 // ----------------------------------------------------------------------------
392 wxDropTarget::wxDropTarget(wxDataObject
*dataObj
)
393 : wxDropTargetBase(dataObj
),
394 m_dropTargetHelper(NULL
)
396 // create an IDropTarget implementation which will notify us about d&d
398 m_pIDropTarget
= new wxIDropTarget(this);
399 m_pIDropTarget
->AddRef();
402 wxDropTarget::~wxDropTarget()
404 ReleaseInterface(m_pIDropTarget
);
407 // ----------------------------------------------------------------------------
408 // [un]register drop handler
409 // ----------------------------------------------------------------------------
411 bool wxDropTarget::Register(WXHWND hwnd
)
414 // RegisterDragDrop not available on Windows CE >= 400?
415 // Or maybe we can dynamically load them from ceshell.dll
417 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
423 // May exist in later WinCE versions
425 hr
= ::CoLockObjectExternal(m_pIDropTarget
, TRUE
, FALSE
);
427 wxLogApiError(wxT("CoLockObjectExternal"), hr
);
432 hr
= ::RegisterDragDrop((HWND
) hwnd
, m_pIDropTarget
);
434 // May exist in later WinCE versions
436 ::CoLockObjectExternal(m_pIDropTarget
, FALSE
, FALSE
);
438 wxLogApiError(wxT("RegisterDragDrop"), hr
);
442 // we will need the window handle for coords transformation later
443 m_pIDropTarget
->SetHwnd((HWND
)hwnd
);
445 MSWInitDragImageSupport();
451 void wxDropTarget::Revoke(WXHWND hwnd
)
453 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
454 // Not available, see note above
457 HRESULT hr
= ::RevokeDragDrop((HWND
) hwnd
);
460 wxLogApiError(wxT("RevokeDragDrop"), hr
);
463 // May exist in later WinCE versions
465 ::CoLockObjectExternal(m_pIDropTarget
, FALSE
, TRUE
);
468 MSWEndDragImageSupport();
470 // remove window reference
471 m_pIDropTarget
->SetHwnd(0);
475 // ----------------------------------------------------------------------------
476 // base class pure virtuals
477 // ----------------------------------------------------------------------------
479 // OnDrop() is called only if we previously returned true from
480 // IsAcceptedData(), so no need to check anything here
481 bool wxDropTarget::OnDrop(wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
))
486 // copy the data from the data source to the target data object
487 bool wxDropTarget::GetData()
489 wxDataFormat format
= MSWGetSupportedFormat(m_pIDataSource
);
490 if ( format
== wxDF_INVALID
) {
496 fmtMemory
.cfFormat
= format
;
497 fmtMemory
.ptd
= NULL
;
498 fmtMemory
.dwAspect
= DVASPECT_CONTENT
;
499 fmtMemory
.lindex
= -1;
500 fmtMemory
.tymed
= TYMED_HGLOBAL
; // TODO to add other media
504 HRESULT hr
= m_pIDataSource
->GetData(&fmtMemory
, &stm
);
505 if ( SUCCEEDED(hr
) ) {
506 IDataObject
*dataObject
= m_dataObject
->GetInterface();
508 hr
= dataObject
->SetData(&fmtMemory
, &stm
, TRUE
);
509 if ( SUCCEEDED(hr
) ) {
513 wxLogApiError(wxT("IDataObject::SetData()"), hr
);
517 wxLogApiError(wxT("IDataObject::GetData()"), hr
);
523 // ----------------------------------------------------------------------------
524 // callbacks used by wxIDropTarget
525 // ----------------------------------------------------------------------------
527 // we need a data source, so wxIDropTarget gives it to us using this function
528 void wxDropTarget::MSWSetDataSource(IDataObject
*pIDataSource
)
530 m_pIDataSource
= pIDataSource
;
533 // determine if we accept data of this type
534 bool wxDropTarget::MSWIsAcceptedData(IDataObject
*pIDataSource
) const
536 return MSWGetSupportedFormat(pIDataSource
) != wxDF_INVALID
;
539 // ----------------------------------------------------------------------------
541 // ----------------------------------------------------------------------------
543 wxDataFormat
wxDropTarget::GetMatchingPair()
545 return MSWGetSupportedFormat( m_pIDataSource
);
548 wxDataFormat
wxDropTarget::MSWGetSupportedFormat(IDataObject
*pIDataSource
) const
550 // this strucutre describes a data of any type (first field will be
551 // changing) being passed through global memory block.
552 static FORMATETC s_fmtMemory
= {
557 TYMED_HGLOBAL
// TODO is it worth supporting other tymeds here?
560 // get the list of supported formats
561 size_t nFormats
= m_dataObject
->GetFormatCount(wxDataObject::Set
);
563 wxDataFormat
*formats
;
564 formats
= nFormats
== 1 ? &format
: new wxDataFormat
[nFormats
];
566 m_dataObject
->GetAllFormats(formats
, wxDataObject::Set
);
568 // cycle through all supported formats
570 for ( n
= 0; n
< nFormats
; n
++ ) {
571 s_fmtMemory
.cfFormat
= formats
[n
];
573 // NB: don't use SUCCEEDED macro here: QueryGetData returns S_FALSE
574 // for file drag and drop (format == CF_HDROP)
575 if ( pIDataSource
->QueryGetData(&s_fmtMemory
) == S_OK
) {
582 if ( formats
!= &format
) {
583 // free memory if we allocated it
587 return n
< nFormats
? format
: wxFormatInvalid
;
590 // ----------------------------------------------------------------------------
591 // drag image functions
592 // ----------------------------------------------------------------------------
595 wxDropTarget::MSWEndDragImageSupport()
597 // release drop target helper
598 if ( m_dropTargetHelper
!= NULL
)
600 m_dropTargetHelper
->Release();
601 m_dropTargetHelper
= NULL
;
606 wxDropTarget::MSWInitDragImageSupport()
608 // Use the default drop target helper to show shell drag images
609 CoCreateInstance(wxCLSID_DragDropHelper
, NULL
, CLSCTX_INPROC_SERVER
,
610 wxIID_IDropTargetHelper
, (LPVOID
*)&m_dropTargetHelper
);
614 wxDropTarget::MSWUpdateDragImageOnData(wxCoord x
,
616 wxDragResult dragResult
)
618 // call corresponding event on drop target helper
619 if ( m_dropTargetHelper
!= NULL
)
622 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
623 m_dropTargetHelper
->Drop(m_pIDataSource
, &pt
, dwEffect
);
628 wxDropTarget::MSWUpdateDragImageOnDragOver(wxCoord x
,
630 wxDragResult dragResult
)
632 // call corresponding event on drop target helper
633 if ( m_dropTargetHelper
!= NULL
)
636 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
637 m_dropTargetHelper
->DragOver(&pt
, dwEffect
);
642 wxDropTarget::MSWUpdateDragImageOnEnter(wxCoord x
,
644 wxDragResult dragResult
)
646 // call corresponding event on drop target helper
647 if ( m_dropTargetHelper
!= NULL
)
650 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
651 m_dropTargetHelper
->DragEnter(m_pIDropTarget
->GetHWND(), m_pIDataSource
, &pt
, dwEffect
);
656 wxDropTarget::MSWUpdateDragImageOnLeave()
658 // call corresponding event on drop target helper
659 if ( m_dropTargetHelper
!= NULL
)
661 m_dropTargetHelper
->DragLeave();
665 // ----------------------------------------------------------------------------
667 // ----------------------------------------------------------------------------
669 static wxDragResult
ConvertDragEffectToResult(DWORD dwEffect
)
671 switch ( dwEffect
) {
672 case DROPEFFECT_COPY
:
675 case DROPEFFECT_LINK
:
678 case DROPEFFECT_MOVE
:
682 wxFAIL_MSG(wxT("invalid value in ConvertDragEffectToResult"));
685 case DROPEFFECT_NONE
:
690 static DWORD
ConvertDragResultToEffect(wxDragResult result
)
694 return DROPEFFECT_COPY
;
697 return DROPEFFECT_LINK
;
700 return DROPEFFECT_MOVE
;
703 wxFAIL_MSG(wxT("invalid value in ConvertDragResultToEffect"));
707 return DROPEFFECT_NONE
;
711 #endif // wxUSE_OLE && wxUSE_DRAG_AND_DROP