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(wxIID_IDropTargetHelper
,
74 0x4657278B,0x411B,0x11D2,0x83,0x9A,0x00,0xC0,0x4F,0xD9,0x18,0xD0);
77 // ----------------------------------------------------------------------------
78 // IDropTarget interface: forward all interesting things to wxDropTarget
79 // (the name is unfortunate, but wx_I_DropTarget is not at all the same thing
80 // as wxDropTarget which is 'public' class, while this one is private)
81 // ----------------------------------------------------------------------------
83 class wxIDropTarget
: public IDropTarget
86 wxIDropTarget(wxDropTarget
*p
);
87 virtual ~wxIDropTarget();
89 // accessors for wxDropTarget
90 HWND
GetHWND() const { return m_hwnd
; }
91 void SetHwnd(HWND hwnd
) { m_hwnd
= hwnd
; }
93 // IDropTarget methods
94 STDMETHODIMP
DragEnter(LPDATAOBJECT
, DWORD
, POINTL
, LPDWORD
);
95 STDMETHODIMP
DragOver(DWORD
, POINTL
, LPDWORD
);
96 STDMETHODIMP
DragLeave();
97 STDMETHODIMP
Drop(LPDATAOBJECT
, DWORD
, POINTL
, LPDWORD
);
99 DECLARE_IUNKNOWN_METHODS
;
102 IDataObject
*m_pIDataObject
; // !NULL between DragEnter and DragLeave/Drop
103 wxDropTarget
*m_pTarget
; // the real target (we're just a proxy)
105 HWND m_hwnd
; // window we're associated with
107 // get default drop effect for given keyboard flags
108 static DWORD
GetDropEffect(DWORD flags
, wxDragResult defaultAction
, DWORD pdwEffect
);
110 wxDECLARE_NO_COPY_CLASS(wxIDropTarget
);
113 // ----------------------------------------------------------------------------
115 // ----------------------------------------------------------------------------
117 static wxDragResult
ConvertDragEffectToResult(DWORD dwEffect
);
118 static DWORD
ConvertDragResultToEffect(wxDragResult result
);
120 // ============================================================================
121 // wxIDropTarget implementation
122 // ============================================================================
124 // Name : static wxIDropTarget::GetDropEffect
125 // Purpose : determine the drop operation from keyboard/mouse state.
126 // Returns : DWORD combined from DROPEFFECT_xxx constants
127 // Params : [in] DWORD flags kbd & mouse flags as passed to
128 // IDropTarget methods
129 // [in] wxDragResult defaultAction the default action of the drop target
130 // [in] DWORD pdwEffect the supported actions of the drop
131 // source passed to IDropTarget methods
132 // Notes : We do "move" normally and "copy" if <Ctrl> is pressed,
133 // which is the standard behaviour (currently there is no
134 // way to redefine it)
135 DWORD
wxIDropTarget::GetDropEffect(DWORD flags
,
136 wxDragResult defaultAction
,
139 DWORD effectiveAction
;
140 if ( defaultAction
== wxDragCopy
)
141 effectiveAction
= flags
& MK_SHIFT
? DROPEFFECT_MOVE
: DROPEFFECT_COPY
;
143 effectiveAction
= flags
& MK_CONTROL
? DROPEFFECT_COPY
: DROPEFFECT_MOVE
;
145 if ( !(effectiveAction
& pdwEffect
) )
147 // the action is not supported by drag source, fall back to something
148 // that it does support
149 if ( pdwEffect
& DROPEFFECT_MOVE
)
150 effectiveAction
= DROPEFFECT_MOVE
;
151 else if ( pdwEffect
& DROPEFFECT_COPY
)
152 effectiveAction
= DROPEFFECT_COPY
;
153 else if ( pdwEffect
& DROPEFFECT_LINK
)
154 effectiveAction
= DROPEFFECT_LINK
;
156 effectiveAction
= DROPEFFECT_NONE
;
159 return effectiveAction
;
162 wxIDropTarget::wxIDropTarget(wxDropTarget
*pTarget
)
165 m_pIDataObject
= NULL
;
168 wxIDropTarget::~wxIDropTarget()
172 BEGIN_IID_TABLE(wxIDropTarget
)
177 IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget
)
179 // Name : wxIDropTarget::DragEnter
180 // Purpose : Called when the mouse enters the window (dragging something)
182 // Params : [in] IDataObject *pIDataSource : source data
183 // [in] DWORD grfKeyState : kbd & mouse state
184 // [in] POINTL pt : mouse coordinates
185 // [in/out]DWORD *pdwEffect : effect flag
186 // In: Supported effects
187 // Out: Resulting effect
189 STDMETHODIMP
wxIDropTarget::DragEnter(IDataObject
*pIDataSource
,
194 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::DragEnter"));
196 wxASSERT_MSG( m_pIDataObject
== NULL
,
197 wxT("drop target must have data object") );
199 // show the list of formats supported by the source data object for the
200 // debugging purposes, this is quite useful sometimes - please don't remove
202 IEnumFORMATETC
*penumFmt
;
203 if ( SUCCEEDED(pIDataSource
->EnumFormatEtc(DATADIR_GET
, &penumFmt
)) )
206 while ( penumFmt
->Next(1, &fmt
, NULL
) == S_OK
)
208 wxLogDebug(wxT("Drop source supports format %s"),
209 wxDataObject::GetFormatName(fmt
.cfFormat
));
216 wxLogLastError(wxT("IDataObject::EnumFormatEtc"));
220 // for use in OnEnter and OnDrag calls
221 m_pTarget
->MSWSetDataSource(pIDataSource
);
223 // get hold of the data object
224 m_pIDataObject
= pIDataSource
;
225 m_pIDataObject
->AddRef();
227 if ( !m_pTarget
->MSWIsAcceptedData(pIDataSource
) ) {
228 // we don't accept this kind of data
229 *pdwEffect
= DROPEFFECT_NONE
;
233 // we need client coordinates to pass to wxWin functions
234 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
236 wxLogLastError(wxT("ScreenToClient"));
239 // give some visual feedback
240 *pdwEffect
= ConvertDragResultToEffect(
241 m_pTarget
->OnEnter(pt
.x
, pt
.y
, ConvertDragEffectToResult(
242 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
))
248 const wxDragResult res
= ConvertDragEffectToResult(*pdwEffect
);
249 m_pTarget
->MSWUpdateDragImageOnEnter(pt
.x
, pt
.y
, res
);
250 m_pTarget
->MSWUpdateDragImageOnDragOver(pt
.x
, pt
.y
, res
);
257 // Name : wxIDropTarget::DragOver
258 // Purpose : Indicates that the mouse was moved inside the window represented
259 // by this drop target.
261 // Params : [in] DWORD grfKeyState kbd & mouse state
262 // [in] POINTL pt mouse coordinates
263 // [in/out]LPDWORD pdwEffect current effect flag
264 // Notes : We're called on every WM_MOUSEMOVE, so this function should be
266 STDMETHODIMP
wxIDropTarget::DragOver(DWORD grfKeyState
,
270 // there are too many of them... wxLogDebug("IDropTarget::DragOver");
273 if ( m_pIDataObject
) {
274 result
= ConvertDragEffectToResult(
275 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
));
278 // can't accept data anyhow normally
282 if ( result
!= wxDragNone
) {
283 // we need client coordinates to pass to wxWin functions
284 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
286 wxLogLastError(wxT("ScreenToClient"));
289 *pdwEffect
= ConvertDragResultToEffect(
290 m_pTarget
->OnDragOver(pt
.x
, pt
.y
, result
)
294 *pdwEffect
= DROPEFFECT_NONE
;
298 m_pTarget
->MSWUpdateDragImageOnDragOver(pt
.x
, pt
.y
,
299 ConvertDragEffectToResult(*pdwEffect
));
304 // Name : wxIDropTarget::DragLeave
305 // Purpose : Informs the drop target that the operation has left its window.
307 // Notes : good place to do any clean-up
308 STDMETHODIMP
wxIDropTarget::DragLeave()
310 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::DragLeave"));
312 // remove the UI feedback
313 m_pTarget
->OnLeave();
315 // release the held object
316 RELEASE_AND_NULL(m_pIDataObject
);
319 m_pTarget
->MSWUpdateDragImageOnLeave();
324 // Name : wxIDropTarget::Drop
325 // Purpose : Instructs the drop target to paste data that was just now
328 // Params : [in] IDataObject *pIDataSource the data to paste
329 // [in] DWORD grfKeyState kbd & mouse state
330 // [in] POINTL pt where the drop occurred?
331 // [in/out]DWORD *pdwEffect operation effect
333 STDMETHODIMP
wxIDropTarget::Drop(IDataObject
*pIDataSource
,
338 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::Drop"));
340 // TODO I don't know why there is this parameter, but so far I assume
341 // that it's the same we've already got in DragEnter
342 wxASSERT( m_pIDataObject
== pIDataSource
);
344 // we need client coordinates to pass to wxWin functions
345 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
347 wxLogLastError(wxT("ScreenToClient"));
350 // first ask the drop target if it wants data
351 if ( m_pTarget
->OnDrop(pt
.x
, pt
.y
) ) {
352 // it does, so give it the data source
353 m_pTarget
->MSWSetDataSource(pIDataSource
);
355 // and now it has the data
356 wxDragResult rc
= ConvertDragEffectToResult(
357 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
));
358 rc
= m_pTarget
->OnData(pt
.x
, pt
.y
, rc
);
359 if ( wxIsDragResultOk(rc
) ) {
360 // operation succeeded
361 *pdwEffect
= ConvertDragResultToEffect(rc
);
364 *pdwEffect
= DROPEFFECT_NONE
;
368 // OnDrop() returned false, no need to copy data
369 *pdwEffect
= DROPEFFECT_NONE
;
372 // release the held object
373 RELEASE_AND_NULL(m_pIDataObject
);
376 m_pTarget
->MSWUpdateDragImageOnData(pt
.x
, pt
.y
,
377 ConvertDragEffectToResult(*pdwEffect
));
382 // ============================================================================
383 // wxDropTarget implementation
384 // ============================================================================
386 // ----------------------------------------------------------------------------
388 // ----------------------------------------------------------------------------
390 wxDropTarget::wxDropTarget(wxDataObject
*dataObj
)
391 : wxDropTargetBase(dataObj
),
392 m_dropTargetHelper(NULL
)
394 // create an IDropTarget implementation which will notify us about d&d
396 m_pIDropTarget
= new wxIDropTarget(this);
397 m_pIDropTarget
->AddRef();
400 wxDropTarget::~wxDropTarget()
402 ReleaseInterface(m_pIDropTarget
);
405 // ----------------------------------------------------------------------------
406 // [un]register drop handler
407 // ----------------------------------------------------------------------------
409 bool wxDropTarget::Register(WXHWND hwnd
)
412 // RegisterDragDrop not available on Windows CE >= 400?
413 // Or maybe we can dynamically load them from ceshell.dll
415 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
421 // May exist in later WinCE versions
423 hr
= ::CoLockObjectExternal(m_pIDropTarget
, TRUE
, FALSE
);
425 wxLogApiError(wxT("CoLockObjectExternal"), hr
);
430 hr
= ::RegisterDragDrop((HWND
) hwnd
, m_pIDropTarget
);
432 // May exist in later WinCE versions
434 ::CoLockObjectExternal(m_pIDropTarget
, FALSE
, FALSE
);
436 wxLogApiError(wxT("RegisterDragDrop"), hr
);
440 // we will need the window handle for coords transformation later
441 m_pIDropTarget
->SetHwnd((HWND
)hwnd
);
443 MSWInitDragImageSupport();
449 void wxDropTarget::Revoke(WXHWND hwnd
)
451 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
452 // Not available, see note above
455 HRESULT hr
= ::RevokeDragDrop((HWND
) hwnd
);
458 wxLogApiError(wxT("RevokeDragDrop"), hr
);
461 // May exist in later WinCE versions
463 ::CoLockObjectExternal(m_pIDropTarget
, FALSE
, TRUE
);
466 MSWEndDragImageSupport();
468 // remove window reference
469 m_pIDropTarget
->SetHwnd(0);
473 // ----------------------------------------------------------------------------
474 // base class pure virtuals
475 // ----------------------------------------------------------------------------
477 // OnDrop() is called only if we previously returned true from
478 // IsAcceptedData(), so no need to check anything here
479 bool wxDropTarget::OnDrop(wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
))
484 // copy the data from the data source to the target data object
485 bool wxDropTarget::GetData()
487 wxDataFormat format
= MSWGetSupportedFormat(m_pIDataSource
);
488 if ( format
== wxDF_INVALID
) {
494 fmtMemory
.cfFormat
= format
;
495 fmtMemory
.ptd
= NULL
;
496 fmtMemory
.dwAspect
= DVASPECT_CONTENT
;
497 fmtMemory
.lindex
= -1;
498 fmtMemory
.tymed
= TYMED_HGLOBAL
; // TODO to add other media
502 HRESULT hr
= m_pIDataSource
->GetData(&fmtMemory
, &stm
);
503 if ( SUCCEEDED(hr
) ) {
504 IDataObject
*dataObject
= m_dataObject
->GetInterface();
506 hr
= dataObject
->SetData(&fmtMemory
, &stm
, TRUE
);
507 if ( SUCCEEDED(hr
) ) {
511 wxLogApiError(wxT("IDataObject::SetData()"), hr
);
515 wxLogApiError(wxT("IDataObject::GetData()"), hr
);
521 // ----------------------------------------------------------------------------
522 // callbacks used by wxIDropTarget
523 // ----------------------------------------------------------------------------
525 // we need a data source, so wxIDropTarget gives it to us using this function
526 void wxDropTarget::MSWSetDataSource(IDataObject
*pIDataSource
)
528 m_pIDataSource
= pIDataSource
;
531 // determine if we accept data of this type
532 bool wxDropTarget::MSWIsAcceptedData(IDataObject
*pIDataSource
) const
534 return MSWGetSupportedFormat(pIDataSource
) != wxDF_INVALID
;
537 // ----------------------------------------------------------------------------
539 // ----------------------------------------------------------------------------
541 wxDataFormat
wxDropTarget::GetMatchingPair()
543 return MSWGetSupportedFormat( m_pIDataSource
);
546 wxDataFormat
wxDropTarget::MSWGetSupportedFormat(IDataObject
*pIDataSource
) const
548 // this strucutre describes a data of any type (first field will be
549 // changing) being passed through global memory block.
550 static FORMATETC s_fmtMemory
= {
555 TYMED_HGLOBAL
// TODO is it worth supporting other tymeds here?
558 // get the list of supported formats
559 size_t nFormats
= m_dataObject
->GetFormatCount(wxDataObject::Set
);
561 wxDataFormat
*formats
;
562 formats
= nFormats
== 1 ? &format
: new wxDataFormat
[nFormats
];
564 m_dataObject
->GetAllFormats(formats
, wxDataObject::Set
);
566 // cycle through all supported formats
568 for ( n
= 0; n
< nFormats
; n
++ ) {
569 s_fmtMemory
.cfFormat
= formats
[n
];
571 // NB: don't use SUCCEEDED macro here: QueryGetData returns S_FALSE
572 // for file drag and drop (format == CF_HDROP)
573 if ( pIDataSource
->QueryGetData(&s_fmtMemory
) == S_OK
) {
580 if ( formats
!= &format
) {
581 // free memory if we allocated it
585 return n
< nFormats
? format
: wxFormatInvalid
;
588 // ----------------------------------------------------------------------------
589 // drag image functions
590 // ----------------------------------------------------------------------------
593 wxDropTarget::MSWEndDragImageSupport()
595 // release drop target helper
596 if ( m_dropTargetHelper
!= NULL
)
598 m_dropTargetHelper
->Release();
599 m_dropTargetHelper
= NULL
;
604 wxDropTarget::MSWInitDragImageSupport()
606 // Use the default drop target helper to show shell drag images
607 CoCreateInstance(CLSID_DragDropHelper
, NULL
, CLSCTX_INPROC_SERVER
,
608 wxIID_IDropTargetHelper
, (LPVOID
*)&m_dropTargetHelper
);
612 wxDropTarget::MSWUpdateDragImageOnData(wxCoord x
,
614 wxDragResult dragResult
)
616 // call corresponding event on drop target helper
617 if ( m_dropTargetHelper
!= NULL
)
620 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
621 m_dropTargetHelper
->Drop(m_pIDataSource
, &pt
, dwEffect
);
626 wxDropTarget::MSWUpdateDragImageOnDragOver(wxCoord x
,
628 wxDragResult dragResult
)
630 // call corresponding event on drop target helper
631 if ( m_dropTargetHelper
!= NULL
)
634 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
635 m_dropTargetHelper
->DragOver(&pt
, dwEffect
);
640 wxDropTarget::MSWUpdateDragImageOnEnter(wxCoord x
,
642 wxDragResult dragResult
)
644 // call corresponding event on drop target helper
645 if ( m_dropTargetHelper
!= NULL
)
648 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
649 m_dropTargetHelper
->DragEnter(m_pIDropTarget
->GetHWND(), m_pIDataSource
, &pt
, dwEffect
);
654 wxDropTarget::MSWUpdateDragImageOnLeave()
656 // call corresponding event on drop target helper
657 if ( m_dropTargetHelper
!= NULL
)
659 m_dropTargetHelper
->DragLeave();
663 // ----------------------------------------------------------------------------
665 // ----------------------------------------------------------------------------
667 static wxDragResult
ConvertDragEffectToResult(DWORD dwEffect
)
669 switch ( dwEffect
) {
670 case DROPEFFECT_COPY
:
673 case DROPEFFECT_LINK
:
676 case DROPEFFECT_MOVE
:
680 wxFAIL_MSG(wxT("invalid value in ConvertDragEffectToResult"));
683 case DROPEFFECT_NONE
:
688 static DWORD
ConvertDragResultToEffect(wxDragResult result
)
692 return DROPEFFECT_COPY
;
695 return DROPEFFECT_LINK
;
698 return DROPEFFECT_MOVE
;
701 wxFAIL_MSG(wxT("invalid value in ConvertDragResultToEffect"));
705 return DROPEFFECT_NONE
;
709 #endif // wxUSE_OLE && wxUSE_DRAG_AND_DROP