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"
53 // ----------------------------------------------------------------------------
54 // IDropTarget interface: forward all interesting things to wxDropTarget
55 // (the name is unfortunate, but wx_I_DropTarget is not at all the same thing
56 // as wxDropTarget which is 'public' class, while this one is private)
57 // ----------------------------------------------------------------------------
59 class wxIDropTarget
: public IDropTarget
62 wxIDropTarget(wxDropTarget
*p
);
63 virtual ~wxIDropTarget();
65 // accessors for wxDropTarget
66 HWND
GetHWND() const { return m_hwnd
; }
67 void SetHwnd(HWND hwnd
) { m_hwnd
= hwnd
; }
69 // IDropTarget methods
70 STDMETHODIMP
DragEnter(LPDATAOBJECT
, DWORD
, POINTL
, LPDWORD
);
71 STDMETHODIMP
DragOver(DWORD
, POINTL
, LPDWORD
);
72 STDMETHODIMP
DragLeave();
73 STDMETHODIMP
Drop(LPDATAOBJECT
, DWORD
, POINTL
, LPDWORD
);
75 DECLARE_IUNKNOWN_METHODS
;
78 IDataObject
*m_pIDataObject
; // !NULL between DragEnter and DragLeave/Drop
79 wxDropTarget
*m_pTarget
; // the real target (we're just a proxy)
81 HWND m_hwnd
; // window we're associated with
83 // get default drop effect for given keyboard flags
84 static DWORD
GetDropEffect(DWORD flags
, wxDragResult defaultAction
, DWORD pdwEffect
);
86 wxDECLARE_NO_COPY_CLASS(wxIDropTarget
);
89 // ----------------------------------------------------------------------------
91 // ----------------------------------------------------------------------------
93 static wxDragResult
ConvertDragEffectToResult(DWORD dwEffect
);
94 static DWORD
ConvertDragResultToEffect(wxDragResult result
);
96 // ============================================================================
97 // wxIDropTarget implementation
98 // ============================================================================
100 // Name : static wxIDropTarget::GetDropEffect
101 // Purpose : determine the drop operation from keyboard/mouse state.
102 // Returns : DWORD combined from DROPEFFECT_xxx constants
103 // Params : [in] DWORD flags kbd & mouse flags as passed to
104 // IDropTarget methods
105 // [in] wxDragResult defaultAction the default action of the drop target
106 // [in] DWORD pdwEffect the supported actions of the drop
107 // source passed to IDropTarget methods
108 // Notes : We do "move" normally and "copy" if <Ctrl> is pressed,
109 // which is the standard behaviour (currently there is no
110 // way to redefine it)
111 DWORD
wxIDropTarget::GetDropEffect(DWORD flags
,
112 wxDragResult defaultAction
,
115 DWORD effectiveAction
;
116 if ( defaultAction
== wxDragCopy
)
117 effectiveAction
= flags
& MK_SHIFT
? DROPEFFECT_MOVE
: DROPEFFECT_COPY
;
119 effectiveAction
= flags
& MK_CONTROL
? DROPEFFECT_COPY
: DROPEFFECT_MOVE
;
121 if ( !(effectiveAction
& pdwEffect
) )
123 // the action is not supported by drag source, fall back to something
124 // that it does support
125 if ( pdwEffect
& DROPEFFECT_MOVE
)
126 effectiveAction
= DROPEFFECT_MOVE
;
127 else if ( pdwEffect
& DROPEFFECT_COPY
)
128 effectiveAction
= DROPEFFECT_COPY
;
129 else if ( pdwEffect
& DROPEFFECT_LINK
)
130 effectiveAction
= DROPEFFECT_LINK
;
132 effectiveAction
= DROPEFFECT_NONE
;
135 return effectiveAction
;
138 wxIDropTarget::wxIDropTarget(wxDropTarget
*pTarget
)
141 m_pIDataObject
= NULL
;
144 wxIDropTarget::~wxIDropTarget()
148 BEGIN_IID_TABLE(wxIDropTarget
)
153 IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget
)
155 // Name : wxIDropTarget::DragEnter
156 // Purpose : Called when the mouse enters the window (dragging something)
158 // Params : [in] IDataObject *pIDataSource : source data
159 // [in] DWORD grfKeyState : kbd & mouse state
160 // [in] POINTL pt : mouse coordinates
161 // [in/out]DWORD *pdwEffect : effect flag
162 // In: Supported effects
163 // Out: Resulting effect
165 STDMETHODIMP
wxIDropTarget::DragEnter(IDataObject
*pIDataSource
,
170 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::DragEnter"));
172 wxASSERT_MSG( m_pIDataObject
== NULL
,
173 wxT("drop target must have data object") );
175 // show the list of formats supported by the source data object for the
176 // debugging purposes, this is quite useful sometimes - please don't remove
178 IEnumFORMATETC
*penumFmt
;
179 if ( SUCCEEDED(pIDataSource
->EnumFormatEtc(DATADIR_GET
, &penumFmt
)) )
182 while ( penumFmt
->Next(1, &fmt
, NULL
) == S_OK
)
184 wxLogDebug(wxT("Drop source supports format %s"),
185 wxDataObject::GetFormatName(fmt
.cfFormat
));
192 wxLogLastError(wxT("IDataObject::EnumFormatEtc"));
196 // for use in OnEnter and OnDrag calls
197 m_pTarget
->MSWSetDataSource(pIDataSource
);
199 // get hold of the data object
200 m_pIDataObject
= pIDataSource
;
201 m_pIDataObject
->AddRef();
203 if ( !m_pTarget
->MSWIsAcceptedData(pIDataSource
) ) {
204 // we don't accept this kind of data
205 *pdwEffect
= DROPEFFECT_NONE
;
209 // we need client coordinates to pass to wxWin functions
210 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
212 wxLogLastError(wxT("ScreenToClient"));
215 // give some visual feedback
216 *pdwEffect
= ConvertDragResultToEffect(
217 m_pTarget
->OnEnter(pt
.x
, pt
.y
, ConvertDragEffectToResult(
218 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
))
224 const wxDragResult res
= ConvertDragEffectToResult(*pdwEffect
);
225 m_pTarget
->MSWUpdateDragImageOnEnter(pt
.x
, pt
.y
, res
);
226 m_pTarget
->MSWUpdateDragImageOnDragOver(pt
.x
, pt
.y
, res
);
233 // Name : wxIDropTarget::DragOver
234 // Purpose : Indicates that the mouse was moved inside the window represented
235 // by this drop target.
237 // Params : [in] DWORD grfKeyState kbd & mouse state
238 // [in] POINTL pt mouse coordinates
239 // [in/out]LPDWORD pdwEffect current effect flag
240 // Notes : We're called on every WM_MOUSEMOVE, so this function should be
242 STDMETHODIMP
wxIDropTarget::DragOver(DWORD grfKeyState
,
246 // there are too many of them... wxLogDebug("IDropTarget::DragOver");
249 if ( m_pIDataObject
) {
250 result
= ConvertDragEffectToResult(
251 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
));
254 // can't accept data anyhow normally
258 if ( result
!= wxDragNone
) {
259 // we need client coordinates to pass to wxWin functions
260 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
262 wxLogLastError(wxT("ScreenToClient"));
265 *pdwEffect
= ConvertDragResultToEffect(
266 m_pTarget
->OnDragOver(pt
.x
, pt
.y
, result
)
270 *pdwEffect
= DROPEFFECT_NONE
;
274 m_pTarget
->MSWUpdateDragImageOnDragOver(pt
.x
, pt
.y
,
275 ConvertDragEffectToResult(*pdwEffect
));
280 // Name : wxIDropTarget::DragLeave
281 // Purpose : Informs the drop target that the operation has left its window.
283 // Notes : good place to do any clean-up
284 STDMETHODIMP
wxIDropTarget::DragLeave()
286 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::DragLeave"));
288 // remove the UI feedback
289 m_pTarget
->OnLeave();
291 // release the held object
292 RELEASE_AND_NULL(m_pIDataObject
);
295 m_pTarget
->MSWUpdateDragImageOnLeave();
300 // Name : wxIDropTarget::Drop
301 // Purpose : Instructs the drop target to paste data that was just now
304 // Params : [in] IDataObject *pIDataSource the data to paste
305 // [in] DWORD grfKeyState kbd & mouse state
306 // [in] POINTL pt where the drop occurred?
307 // [in/out]DWORD *pdwEffect operation effect
309 STDMETHODIMP
wxIDropTarget::Drop(IDataObject
*pIDataSource
,
314 wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::Drop"));
316 // TODO I don't know why there is this parameter, but so far I assume
317 // that it's the same we've already got in DragEnter
318 wxASSERT( m_pIDataObject
== pIDataSource
);
320 // we need client coordinates to pass to wxWin functions
321 if ( !ScreenToClient(m_hwnd
, (POINT
*)&pt
) )
323 wxLogLastError(wxT("ScreenToClient"));
326 // first ask the drop target if it wants data
327 if ( m_pTarget
->OnDrop(pt
.x
, pt
.y
) ) {
328 // it does, so give it the data source
329 m_pTarget
->MSWSetDataSource(pIDataSource
);
331 // and now it has the data
332 wxDragResult rc
= ConvertDragEffectToResult(
333 GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
));
334 rc
= m_pTarget
->OnData(pt
.x
, pt
.y
, rc
);
335 if ( wxIsDragResultOk(rc
) ) {
336 // operation succeeded
337 *pdwEffect
= ConvertDragResultToEffect(rc
);
340 *pdwEffect
= DROPEFFECT_NONE
;
344 // OnDrop() returned false, no need to copy data
345 *pdwEffect
= DROPEFFECT_NONE
;
348 // release the held object
349 RELEASE_AND_NULL(m_pIDataObject
);
352 m_pTarget
->MSWUpdateDragImageOnData(pt
.x
, pt
.y
,
353 ConvertDragEffectToResult(*pdwEffect
));
358 // ============================================================================
359 // wxDropTarget implementation
360 // ============================================================================
362 // ----------------------------------------------------------------------------
364 // ----------------------------------------------------------------------------
366 wxDropTarget::wxDropTarget(wxDataObject
*dataObj
)
367 : wxDropTargetBase(dataObj
),
368 m_dropTargetHelper(NULL
)
370 // create an IDropTarget implementation which will notify us about d&d
372 m_pIDropTarget
= new wxIDropTarget(this);
373 m_pIDropTarget
->AddRef();
376 wxDropTarget::~wxDropTarget()
378 ReleaseInterface(m_pIDropTarget
);
381 // ----------------------------------------------------------------------------
382 // [un]register drop handler
383 // ----------------------------------------------------------------------------
385 bool wxDropTarget::Register(WXHWND hwnd
)
388 // RegisterDragDrop not available on Windows CE >= 400?
389 // Or maybe we can dynamically load them from ceshell.dll
391 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
397 // May exist in later WinCE versions
399 hr
= ::CoLockObjectExternal(m_pIDropTarget
, TRUE
, FALSE
);
401 wxLogApiError(wxT("CoLockObjectExternal"), hr
);
406 hr
= ::RegisterDragDrop((HWND
) hwnd
, m_pIDropTarget
);
408 // May exist in later WinCE versions
410 ::CoLockObjectExternal(m_pIDropTarget
, FALSE
, FALSE
);
412 wxLogApiError(wxT("RegisterDragDrop"), hr
);
416 // we will need the window handle for coords transformation later
417 m_pIDropTarget
->SetHwnd((HWND
)hwnd
);
419 MSWInitDragImageSupport();
425 void wxDropTarget::Revoke(WXHWND hwnd
)
427 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
428 // Not available, see note above
431 HRESULT hr
= ::RevokeDragDrop((HWND
) hwnd
);
434 wxLogApiError(wxT("RevokeDragDrop"), hr
);
437 // May exist in later WinCE versions
439 ::CoLockObjectExternal(m_pIDropTarget
, FALSE
, TRUE
);
442 MSWEndDragImageSupport();
444 // remove window reference
445 m_pIDropTarget
->SetHwnd(0);
449 // ----------------------------------------------------------------------------
450 // base class pure virtuals
451 // ----------------------------------------------------------------------------
453 // OnDrop() is called only if we previously returned true from
454 // IsAcceptedData(), so no need to check anything here
455 bool wxDropTarget::OnDrop(wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
))
460 // copy the data from the data source to the target data object
461 bool wxDropTarget::GetData()
463 wxDataFormat format
= MSWGetSupportedFormat(m_pIDataSource
);
464 if ( format
== wxDF_INVALID
) {
470 fmtMemory
.cfFormat
= format
;
471 fmtMemory
.ptd
= NULL
;
472 fmtMemory
.dwAspect
= DVASPECT_CONTENT
;
473 fmtMemory
.lindex
= -1;
474 fmtMemory
.tymed
= TYMED_HGLOBAL
; // TODO to add other media
478 HRESULT hr
= m_pIDataSource
->GetData(&fmtMemory
, &stm
);
479 if ( SUCCEEDED(hr
) ) {
480 IDataObject
*dataObject
= m_dataObject
->GetInterface();
482 hr
= dataObject
->SetData(&fmtMemory
, &stm
, TRUE
);
483 if ( SUCCEEDED(hr
) ) {
487 wxLogApiError(wxT("IDataObject::SetData()"), hr
);
491 wxLogApiError(wxT("IDataObject::GetData()"), hr
);
497 // ----------------------------------------------------------------------------
498 // callbacks used by wxIDropTarget
499 // ----------------------------------------------------------------------------
501 // we need a data source, so wxIDropTarget gives it to us using this function
502 void wxDropTarget::MSWSetDataSource(IDataObject
*pIDataSource
)
504 m_pIDataSource
= pIDataSource
;
507 // determine if we accept data of this type
508 bool wxDropTarget::MSWIsAcceptedData(IDataObject
*pIDataSource
) const
510 return MSWGetSupportedFormat(pIDataSource
) != wxDF_INVALID
;
513 // ----------------------------------------------------------------------------
515 // ----------------------------------------------------------------------------
517 wxDataFormat
wxDropTarget::GetMatchingPair()
519 return MSWGetSupportedFormat( m_pIDataSource
);
522 wxDataFormat
wxDropTarget::MSWGetSupportedFormat(IDataObject
*pIDataSource
) const
524 // this strucutre describes a data of any type (first field will be
525 // changing) being passed through global memory block.
526 static FORMATETC s_fmtMemory
= {
531 TYMED_HGLOBAL
// TODO is it worth supporting other tymeds here?
534 // get the list of supported formats
535 size_t nFormats
= m_dataObject
->GetFormatCount(wxDataObject::Set
);
537 wxDataFormat
*formats
;
538 formats
= nFormats
== 1 ? &format
: new wxDataFormat
[nFormats
];
540 m_dataObject
->GetAllFormats(formats
, wxDataObject::Set
);
542 // cycle through all supported formats
544 for ( n
= 0; n
< nFormats
; n
++ ) {
545 s_fmtMemory
.cfFormat
= formats
[n
];
547 // NB: don't use SUCCEEDED macro here: QueryGetData returns S_FALSE
548 // for file drag and drop (format == CF_HDROP)
549 if ( pIDataSource
->QueryGetData(&s_fmtMemory
) == S_OK
) {
556 if ( formats
!= &format
) {
557 // free memory if we allocated it
561 return n
< nFormats
? format
: wxFormatInvalid
;
564 // ----------------------------------------------------------------------------
565 // drag image functions
566 // ----------------------------------------------------------------------------
569 wxDropTarget::MSWEndDragImageSupport()
571 // release drop target helper
572 if ( m_dropTargetHelper
!= NULL
)
574 m_dropTargetHelper
->Release();
575 m_dropTargetHelper
= NULL
;
580 wxDropTarget::MSWInitDragImageSupport()
582 // Use the default drop target helper to show shell drag images
583 CoCreateInstance(CLSID_DragDropHelper
, NULL
, CLSCTX_INPROC_SERVER
,
584 IID_IDropTargetHelper
, (LPVOID
*)&m_dropTargetHelper
);
588 wxDropTarget::MSWUpdateDragImageOnData(wxCoord x
,
590 wxDragResult dragResult
)
592 // call corresponding event on drop target helper
593 if ( m_dropTargetHelper
!= NULL
)
596 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
597 m_dropTargetHelper
->Drop(m_pIDataSource
, &pt
, dwEffect
);
602 wxDropTarget::MSWUpdateDragImageOnDragOver(wxCoord x
,
604 wxDragResult dragResult
)
606 // call corresponding event on drop target helper
607 if ( m_dropTargetHelper
!= NULL
)
610 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
611 m_dropTargetHelper
->DragOver(&pt
, dwEffect
);
616 wxDropTarget::MSWUpdateDragImageOnEnter(wxCoord x
,
618 wxDragResult dragResult
)
620 // call corresponding event on drop target helper
621 if ( m_dropTargetHelper
!= NULL
)
624 DWORD dwEffect
= ConvertDragResultToEffect(dragResult
);
625 m_dropTargetHelper
->DragEnter(m_pIDropTarget
->GetHWND(), m_pIDataSource
, &pt
, dwEffect
);
630 wxDropTarget::MSWUpdateDragImageOnLeave()
632 // call corresponding event on drop target helper
633 if ( m_dropTargetHelper
!= NULL
)
635 m_dropTargetHelper
->DragLeave();
639 // ----------------------------------------------------------------------------
641 // ----------------------------------------------------------------------------
643 static wxDragResult
ConvertDragEffectToResult(DWORD dwEffect
)
645 switch ( dwEffect
) {
646 case DROPEFFECT_COPY
:
649 case DROPEFFECT_LINK
:
652 case DROPEFFECT_MOVE
:
656 wxFAIL_MSG(wxT("invalid value in ConvertDragEffectToResult"));
659 case DROPEFFECT_NONE
:
664 static DWORD
ConvertDragResultToEffect(wxDragResult result
)
668 return DROPEFFECT_COPY
;
671 return DROPEFFECT_LINK
;
674 return DROPEFFECT_MOVE
;
677 wxFAIL_MSG(wxT("invalid value in ConvertDragResultToEffect"));
681 return DROPEFFECT_NONE
;
685 #endif // wxUSE_OLE && wxUSE_DRAG_AND_DROP