1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/ole/droptgt.cpp
3 // Purpose: wxDropTarget implementation
4 // Author: Vadim Zeitlin
7 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
22 #if defined(__BORLANDC__)
26 #if wxUSE_OLE && wxUSE_DRAG_AND_DROP
29 #include "wx/msw/wrapwin.h"
33 #include "wx/msw/private.h"
41 #if !defined(__GNUWIN32__) || wxUSE_NORLANDER_HEADERS
42 #include <shlobj.h> // for DROPFILES structure
50 #include "wx/msw/ole/oleutils.h"
54 // Some (very) old SDKs don't define IDropTargetHelper, so define our own
55 // version of it here.
56 struct wxIDropTargetHelper
: public IUnknown
58 virtual HRESULT STDMETHODCALLTYPE
DragEnter(HWND hwndTarget
,
59 IDataObject
*pDataObject
,
62 virtual HRESULT STDMETHODCALLTYPE
DragLeave() = 0;
63 virtual HRESULT STDMETHODCALLTYPE
DragOver(POINT
*ppt
, DWORD dwEffect
) = 0;
64 virtual HRESULT STDMETHODCALLTYPE
Drop(IDataObject
*pDataObject
,
67 virtual HRESULT STDMETHODCALLTYPE
Show(BOOL fShow
) = 0;
72 DEFINE_GUID(wxCLSID_DragDropHelper
,
73 0x4657278A,0x411B,0x11D2,0x83,0x9A,0x00,0xC0,0x4F,0xD9,0x18,0xD0);
74 DEFINE_GUID(wxIID_IDropTargetHelper
,
75 0x4657278B,0x411B,0x11D2,0x83,0x9A,0x00,0xC0,0x4F,0xD9,0x18,0xD0);
78 // ----------------------------------------------------------------------------
79 // IDropTarget interface: forward all interesting things to wxDropTarget
80 // (the name is unfortunate, but wx_I_DropTarget is not at all the same thing
81 // as wxDropTarget which is 'public' class, while this one is private)
82 // ----------------------------------------------------------------------------
84 class wxIDropTarget
: public IDropTarget
87 wxIDropTarget(wxDropTarget
*p
);
88 virtual ~wxIDropTarget();
90 // accessors for wxDropTarget
91 HWND
GetHWND() const { return m_hwnd
; }
92 void SetHwnd(HWND hwnd
) { m_hwnd
= hwnd
; }
94 // IDropTarget methods
95 STDMETHODIMP
DragEnter(LPDATAOBJECT
, DWORD
, POINTL
, LPDWORD
);
96 STDMETHODIMP
DragOver(DWORD
, POINTL
, LPDWORD
);
97 STDMETHODIMP
DragLeave();
98 STDMETHODIMP
Drop(LPDATAOBJECT
, DWORD
, POINTL
, LPDWORD
);
100 DECLARE_IUNKNOWN_METHODS
;
103 IDataObject
*m_pIDataObject
; // !NULL between DragEnter and DragLeave/Drop
104 wxDropTarget
*m_pTarget
; // the real target (we're just a proxy)
106 HWND m_hwnd
; // window we're associated with
108 // get default drop effect for given keyboard flags
109 static DWORD
GetDropEffect(DWORD flags
, wxDragResult defaultAction
, DWORD pdwEffect
);
111 wxDECLARE_NO_COPY_CLASS(wxIDropTarget
);
114 // ----------------------------------------------------------------------------
116 // ----------------------------------------------------------------------------
118 static wxDragResult
ConvertDragEffectToResult(DWORD dwEffect
);
119 static DWORD
ConvertDragResultToEffect(wxDragResult result
);
121 // ============================================================================
122 // wxIDropTarget implementation
123 // ============================================================================
125 // Name : static wxIDropTarget::GetDropEffect
126 // Purpose : determine the drop operation from keyboard/mouse state.
127 // Returns : DWORD combined from DROPEFFECT_xxx constants
128 // Params : [in] DWORD flags kbd & mouse flags as passed to
129 // IDropTarget methods
130 // [in] wxDragResult defaultAction the default action of the drop target
131 // [in] DWORD pdwEffect the supported actions of the drop
132 // source passed to IDropTarget methods
133 // Notes : We do "move" normally and "copy" if <Ctrl> is pressed,
134 // which is the standard behaviour (currently there is no
135 // way to redefine it)
136 DWORD
wxIDropTarget::GetDropEffect(DWORD flags
,
137 wxDragResult defaultAction
,
140 DWORD effectiveAction
;
141 if ( defaultAction
== wxDragCopy
)
142 effectiveAction
= flags
& MK_SHIFT
? DROPEFFECT_MOVE
: DROPEFFECT_COPY
;
144 effectiveAction
= flags
& MK_CONTROL
? DROPEFFECT_COPY
: DROPEFFECT_MOVE
;
146 if ( !(effectiveAction
& pdwEffect
) )
148 // the action is not supported by drag source, fall back to something
149 // that it does support
150 if ( pdwEffect
& DROPEFFECT_MOVE
)
151 effectiveAction
= DROPEFFECT_MOVE
;
152 else if ( pdwEffect
& DROPEFFECT_COPY
)
153 effectiveAction
= DROPEFFECT_COPY
;
154 else if ( pdwEffect
& DROPEFFECT_LINK
)
155 effectiveAction
= DROPEFFECT_LINK
;
157 effectiveAction
= DROPEFFECT_NONE
;
160 return effectiveAction
;
163 wxIDropTarget::wxIDropTarget(wxDropTarget
*pTarget
)
166 m_pIDataObject
= NULL
;
169 wxIDropTarget::~wxIDropTarget()
173 BEGIN_IID_TABLE(wxIDropTarget
)
178 IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget
)
180 // Name : wxIDropTarget::DragEnter
181 // Purpose : Called when the mouse enters the window (dragging something)
183 // Params : [in] IDataObject *pIDataSource : source data
184 // [in] DWORD grfKeyState : kbd & mouse state
185 // [in] POINTL pt : mouse coordinates
186 // [in/out]DWORD *pdwEffect : effect flag
187 // In: Supported effects
188 // Out: Resulting effect
190 STDMETHODIMP
wxIDropTarget::DragEnter(IDataObject
*pIDataSource
,
195 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::DragEnter"));
197 wxASSERT_MSG( m_pIDataObject
== NULL
,
198 wxT("drop target must have data object") );
200 // show the list of formats supported by the source data object for the
201 // debugging purposes, this is quite useful sometimes - please don't remove
203 IEnumFORMATETC
*penumFmt
;
204 if ( SUCCEEDED(pIDataSource
->EnumFormatEtc(DATADIR_GET
, &penumFmt
)) )
207 while ( penumFmt
->Next(1, &fmt
, NULL
) == S_OK
)
209 wxLogDebug(wxT("Drop source supports format %s"),
210 wxDataObject::GetFormatName(fmt
.cfFormat
));
217 wxLogLastError(wxT("IDataObject::EnumFormatEtc"));
221 // for use in OnEnter and OnDrag calls
222 m_pTarget
->MSWSetDataSource(pIDataSource
);
224 // get hold of the data object
225 m_pIDataObject
= pIDataSource
;
226 m_pIDataObject
->AddRef();
228 if ( !m_pTarget
->MSWIsAcceptedData(pIDataSource
) ) {
229 // we don't accept this kind of data
230 *pdwEffect
= DROPEFFECT_NONE
;
234 // we need client coordinates to pass to wxWin functions
235 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
237 wxLogLastError(wxT("ScreenToClient"));
240 // give some visual feedback
241 *pdwEffect
= ConvertDragResultToEffect(
242 m_pTarget
->OnEnter(pt
.x
, pt
.y
, ConvertDragEffectToResult(
243 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
))
249 const wxDragResult res
= ConvertDragEffectToResult(*pdwEffect
);
250 m_pTarget
->MSWUpdateDragImageOnEnter(pt
.x
, pt
.y
, res
);
251 m_pTarget
->MSWUpdateDragImageOnDragOver(pt
.x
, pt
.y
, res
);
258 // Name : wxIDropTarget::DragOver
259 // Purpose : Indicates that the mouse was moved inside the window represented
260 // by this drop target.
262 // Params : [in] DWORD grfKeyState kbd & mouse state
263 // [in] POINTL pt mouse coordinates
264 // [in/out]LPDWORD pdwEffect current effect flag
265 // Notes : We're called on every WM_MOUSEMOVE, so this function should be
267 STDMETHODIMP
wxIDropTarget::DragOver(DWORD grfKeyState
,
271 // there are too many of them... wxLogDebug("IDropTarget::DragOver");
274 if ( m_pIDataObject
) {
275 result
= ConvertDragEffectToResult(
276 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
));
279 // can't accept data anyhow normally
283 if ( result
!= wxDragNone
) {
284 // we need client coordinates to pass to wxWin functions
285 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
287 wxLogLastError(wxT("ScreenToClient"));
290 *pdwEffect
= ConvertDragResultToEffect(
291 m_pTarget
->OnDragOver(pt
.x
, pt
.y
, result
)
295 *pdwEffect
= DROPEFFECT_NONE
;
299 m_pTarget
->MSWUpdateDragImageOnDragOver(pt
.x
, pt
.y
,
300 ConvertDragEffectToResult(*pdwEffect
));
305 // Name : wxIDropTarget::DragLeave
306 // Purpose : Informs the drop target that the operation has left its window.
308 // Notes : good place to do any clean-up
309 STDMETHODIMP
wxIDropTarget::DragLeave()
311 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::DragLeave"));
313 // remove the UI feedback
314 m_pTarget
->OnLeave();
316 // release the held object
317 RELEASE_AND_NULL(m_pIDataObject
);
320 m_pTarget
->MSWUpdateDragImageOnLeave();
325 // Name : wxIDropTarget::Drop
326 // Purpose : Instructs the drop target to paste data that was just now
329 // Params : [in] IDataObject *pIDataSource the data to paste
330 // [in] DWORD grfKeyState kbd & mouse state
331 // [in] POINTL pt where the drop occurred?
332 // [in/out]DWORD *pdwEffect operation effect
334 STDMETHODIMP
wxIDropTarget::Drop(IDataObject
*pIDataSource
,
339 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::Drop"));
341 // TODO I don't know why there is this parameter, but so far I assume
342 // that it's the same we've already got in DragEnter
343 wxASSERT( m_pIDataObject
== pIDataSource
);
345 // we need client coordinates to pass to wxWin functions
346 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
348 wxLogLastError(wxT("ScreenToClient"));
351 // first ask the drop target if it wants data
352 if ( m_pTarget
->OnDrop(pt
.x
, pt
.y
) ) {
353 // it does, so give it the data source
354 m_pTarget
->MSWSetDataSource(pIDataSource
);
356 // and now it has the data
357 wxDragResult rc
= ConvertDragEffectToResult(
358 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
));
359 rc
= m_pTarget
->OnData(pt
.x
, pt
.y
, rc
);
360 if ( wxIsDragResultOk(rc
) ) {
361 // operation succeeded
362 *pdwEffect
= ConvertDragResultToEffect(rc
);
365 *pdwEffect
= DROPEFFECT_NONE
;
369 // OnDrop() returned false, no need to copy data
370 *pdwEffect
= DROPEFFECT_NONE
;
373 // release the held object
374 RELEASE_AND_NULL(m_pIDataObject
);
377 m_pTarget
->MSWUpdateDragImageOnData(pt
.x
, pt
.y
,
378 ConvertDragEffectToResult(*pdwEffect
));
383 // ============================================================================
384 // wxDropTarget implementation
385 // ============================================================================
387 // ----------------------------------------------------------------------------
389 // ----------------------------------------------------------------------------
391 wxDropTarget::wxDropTarget(wxDataObject
*dataObj
)
392 : wxDropTargetBase(dataObj
),
393 m_dropTargetHelper(NULL
)
395 // create an IDropTarget implementation which will notify us about d&d
397 m_pIDropTarget
= new wxIDropTarget(this);
398 m_pIDropTarget
->AddRef();
401 wxDropTarget::~wxDropTarget()
403 ReleaseInterface(m_pIDropTarget
);
406 // ----------------------------------------------------------------------------
407 // [un]register drop handler
408 // ----------------------------------------------------------------------------
410 bool wxDropTarget::Register(WXHWND hwnd
)
413 // RegisterDragDrop not available on Windows CE >= 400?
414 // Or maybe we can dynamically load them from ceshell.dll
416 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
422 // May exist in later WinCE versions
424 hr
= ::CoLockObjectExternal(m_pIDropTarget
, TRUE
, FALSE
);
426 wxLogApiError(wxT("CoLockObjectExternal"), hr
);
431 hr
= ::RegisterDragDrop((HWND
) hwnd
, m_pIDropTarget
);
433 // May exist in later WinCE versions
435 ::CoLockObjectExternal(m_pIDropTarget
, FALSE
, FALSE
);
437 wxLogApiError(wxT("RegisterDragDrop"), hr
);
441 // we will need the window handle for coords transformation later
442 m_pIDropTarget
->SetHwnd((HWND
)hwnd
);
444 MSWInitDragImageSupport();
450 void wxDropTarget::Revoke(WXHWND hwnd
)
452 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
453 // Not available, see note above
456 HRESULT hr
= ::RevokeDragDrop((HWND
) hwnd
);
459 wxLogApiError(wxT("RevokeDragDrop"), hr
);
462 // May exist in later WinCE versions
464 ::CoLockObjectExternal(m_pIDropTarget
, FALSE
, TRUE
);
467 MSWEndDragImageSupport();
469 // remove window reference
470 m_pIDropTarget
->SetHwnd(0);
474 // ----------------------------------------------------------------------------
475 // base class pure virtuals
476 // ----------------------------------------------------------------------------
478 // OnDrop() is called only if we previously returned true from
479 // IsAcceptedData(), so no need to check anything here
480 bool wxDropTarget::OnDrop(wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
))
485 // copy the data from the data source to the target data object
486 bool wxDropTarget::GetData()
488 wxDataFormat format
= MSWGetSupportedFormat(m_pIDataSource
);
489 if ( format
== wxDF_INVALID
) {
495 fmtMemory
.cfFormat
= format
;
496 fmtMemory
.ptd
= NULL
;
497 fmtMemory
.dwAspect
= DVASPECT_CONTENT
;
498 fmtMemory
.lindex
= -1;
499 fmtMemory
.tymed
= TYMED_HGLOBAL
; // TODO to add other media
503 HRESULT hr
= m_pIDataSource
->GetData(&fmtMemory
, &stm
);
504 if ( SUCCEEDED(hr
) ) {
505 IDataObject
*dataObject
= m_dataObject
->GetInterface();
507 hr
= dataObject
->SetData(&fmtMemory
, &stm
, TRUE
);
508 if ( SUCCEEDED(hr
) ) {
512 wxLogApiError(wxT("IDataObject::SetData()"), hr
);
516 wxLogApiError(wxT("IDataObject::GetData()"), hr
);
522 // ----------------------------------------------------------------------------
523 // callbacks used by wxIDropTarget
524 // ----------------------------------------------------------------------------
526 // we need a data source, so wxIDropTarget gives it to us using this function
527 void wxDropTarget::MSWSetDataSource(IDataObject
*pIDataSource
)
529 m_pIDataSource
= pIDataSource
;
532 // determine if we accept data of this type
533 bool wxDropTarget::MSWIsAcceptedData(IDataObject
*pIDataSource
) const
535 return MSWGetSupportedFormat(pIDataSource
) != wxDF_INVALID
;
538 // ----------------------------------------------------------------------------
540 // ----------------------------------------------------------------------------
542 wxDataFormat
wxDropTarget::GetMatchingPair()
544 return MSWGetSupportedFormat( m_pIDataSource
);
547 wxDataFormat
wxDropTarget::MSWGetSupportedFormat(IDataObject
*pIDataSource
) const
549 // this strucutre describes a data of any type (first field will be
550 // changing) being passed through global memory block.
551 static FORMATETC s_fmtMemory
= {
556 TYMED_HGLOBAL
// TODO is it worth supporting other tymeds here?
559 // get the list of supported formats
560 size_t nFormats
= m_dataObject
->GetFormatCount(wxDataObject::Set
);
562 wxDataFormat
*formats
;
563 formats
= nFormats
== 1 ? &format
: new wxDataFormat
[nFormats
];
565 m_dataObject
->GetAllFormats(formats
, wxDataObject::Set
);
567 // cycle through all supported formats
569 for ( n
= 0; n
< nFormats
; n
++ ) {
570 s_fmtMemory
.cfFormat
= formats
[n
];
572 // NB: don't use SUCCEEDED macro here: QueryGetData returns S_FALSE
573 // for file drag and drop (format == CF_HDROP)
574 if ( pIDataSource
->QueryGetData(&s_fmtMemory
) == S_OK
) {
581 if ( formats
!= &format
) {
582 // free memory if we allocated it
586 return n
< nFormats
? format
: wxFormatInvalid
;
589 // ----------------------------------------------------------------------------
590 // drag image functions
591 // ----------------------------------------------------------------------------
594 wxDropTarget::MSWEndDragImageSupport()
596 // release drop target helper
597 if ( m_dropTargetHelper
!= NULL
)
599 m_dropTargetHelper
->Release();
600 m_dropTargetHelper
= NULL
;
605 wxDropTarget::MSWInitDragImageSupport()
607 // Use the default drop target helper to show shell drag images
608 CoCreateInstance(wxCLSID_DragDropHelper
, NULL
, CLSCTX_INPROC_SERVER
,
609 wxIID_IDropTargetHelper
, (LPVOID
*)&m_dropTargetHelper
);
613 wxDropTarget::MSWUpdateDragImageOnData(wxCoord x
,
615 wxDragResult dragResult
)
617 // call corresponding event on drop target helper
618 if ( m_dropTargetHelper
!= NULL
)
621 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
622 m_dropTargetHelper
->Drop(m_pIDataSource
, &pt
, dwEffect
);
627 wxDropTarget::MSWUpdateDragImageOnDragOver(wxCoord x
,
629 wxDragResult dragResult
)
631 // call corresponding event on drop target helper
632 if ( m_dropTargetHelper
!= NULL
)
635 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
636 m_dropTargetHelper
->DragOver(&pt
, dwEffect
);
641 wxDropTarget::MSWUpdateDragImageOnEnter(wxCoord x
,
643 wxDragResult dragResult
)
645 // call corresponding event on drop target helper
646 if ( m_dropTargetHelper
!= NULL
)
649 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
650 m_dropTargetHelper
->DragEnter(m_pIDropTarget
->GetHWND(), m_pIDataSource
, &pt
, dwEffect
);
655 wxDropTarget::MSWUpdateDragImageOnLeave()
657 // call corresponding event on drop target helper
658 if ( m_dropTargetHelper
!= NULL
)
660 m_dropTargetHelper
->DragLeave();
664 // ----------------------------------------------------------------------------
666 // ----------------------------------------------------------------------------
668 static wxDragResult
ConvertDragEffectToResult(DWORD dwEffect
)
670 switch ( dwEffect
) {
671 case DROPEFFECT_COPY
:
674 case DROPEFFECT_LINK
:
677 case DROPEFFECT_MOVE
:
681 wxFAIL_MSG(wxT("invalid value in ConvertDragEffectToResult"));
684 case DROPEFFECT_NONE
:
689 static DWORD
ConvertDragResultToEffect(wxDragResult result
)
693 return DROPEFFECT_COPY
;
696 return DROPEFFECT_LINK
;
699 return DROPEFFECT_MOVE
;
702 wxFAIL_MSG(wxT("invalid value in ConvertDragResultToEffect"));
706 return DROPEFFECT_NONE
;
710 #endif // wxUSE_OLE && wxUSE_DRAG_AND_DROP