]> git.saurik.com Git - wxWidgets.git/blob - src/msw/ole/droptgt.cpp
1c68f26e4a9e231c05753c5d7baf5e8ba84d50db
[wxWidgets.git] / src / msw / ole / droptgt.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/ole/droptgt.cpp
3 // Purpose: wxDropTarget implementation
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created:
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // Declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #if defined(__BORLANDC__)
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_OLE && wxUSE_DRAG_AND_DROP
28
29 #ifndef WX_PRECOMP
30 #include "wx/msw/wrapwin.h"
31 #include "wx/log.h"
32 #endif
33
34 #include "wx/msw/private.h"
35
36 #ifdef __WXWINCE__
37 #include <winreg.h>
38 #include <ole2.h>
39 #endif
40
41 #ifdef __WIN32__
42 #if !defined(__GNUWIN32__) || wxUSE_NORLANDER_HEADERS
43 #include <shlobj.h> // for DROPFILES structure
44 #endif
45 #else
46 #include <shellapi.h>
47 #endif
48
49 #include "wx/dnd.h"
50
51 #include "wx/msw/ole/oleutils.h"
52
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 // ----------------------------------------------------------------------------
58
59 class wxIDropTarget : public IDropTarget
60 {
61 public:
62 wxIDropTarget(wxDropTarget *p);
63 virtual ~wxIDropTarget();
64
65 // accessors for wxDropTarget
66 HWND GetHWND() const { return m_hwnd; }
67 void SetHwnd(HWND hwnd) { m_hwnd = hwnd; }
68
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);
74
75 DECLARE_IUNKNOWN_METHODS;
76
77 protected:
78 IDataObject *m_pIDataObject; // !NULL between DragEnter and DragLeave/Drop
79 wxDropTarget *m_pTarget; // the real target (we're just a proxy)
80
81 HWND m_hwnd; // window we're associated with
82
83 // get default drop effect for given keyboard flags
84 static DWORD GetDropEffect(DWORD flags, wxDragResult defaultAction, DWORD pdwEffect);
85
86 wxDECLARE_NO_COPY_CLASS(wxIDropTarget);
87 };
88
89 // ----------------------------------------------------------------------------
90 // private functions
91 // ----------------------------------------------------------------------------
92
93 static wxDragResult ConvertDragEffectToResult(DWORD dwEffect);
94 static DWORD ConvertDragResultToEffect(wxDragResult result);
95
96 // ============================================================================
97 // wxIDropTarget implementation
98 // ============================================================================
99
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,
113 DWORD pdwEffect)
114 {
115 DWORD effectiveAction;
116 if ( defaultAction == wxDragCopy )
117 effectiveAction = flags & MK_SHIFT ? DROPEFFECT_MOVE : DROPEFFECT_COPY;
118 else
119 effectiveAction = flags & MK_CONTROL ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
120
121 if ( !(effectiveAction & pdwEffect) )
122 {
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;
131 else
132 effectiveAction = DROPEFFECT_NONE;
133 }
134
135 return effectiveAction;
136 }
137
138 wxIDropTarget::wxIDropTarget(wxDropTarget *pTarget)
139 {
140 m_pTarget = pTarget;
141 m_pIDataObject = NULL;
142 }
143
144 wxIDropTarget::~wxIDropTarget()
145 {
146 }
147
148 BEGIN_IID_TABLE(wxIDropTarget)
149 ADD_IID(Unknown)
150 ADD_IID(DropTarget)
151 END_IID_TABLE;
152
153 IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget)
154
155 // Name : wxIDropTarget::DragEnter
156 // Purpose : Called when the mouse enters the window (dragging something)
157 // Returns : S_OK
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
164 // Notes :
165 STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource,
166 DWORD grfKeyState,
167 POINTL pt,
168 DWORD *pdwEffect)
169 {
170 wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragEnter"));
171
172 wxASSERT_MSG( m_pIDataObject == NULL,
173 wxT("drop target must have data object") );
174
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
177 #if 0
178 IEnumFORMATETC *penumFmt;
179 if ( SUCCEEDED(pIDataSource->EnumFormatEtc(DATADIR_GET, &penumFmt)) )
180 {
181 FORMATETC fmt;
182 while ( penumFmt->Next(1, &fmt, NULL) == S_OK )
183 {
184 wxLogDebug(wxT("Drop source supports format %s"),
185 wxDataObject::GetFormatName(fmt.cfFormat));
186 }
187
188 penumFmt->Release();
189 }
190 else
191 {
192 wxLogLastError(wxT("IDataObject::EnumFormatEtc"));
193 }
194 #endif // 0
195
196 // for use in OnEnter and OnDrag calls
197 m_pTarget->MSWSetDataSource(pIDataSource);
198
199 // get hold of the data object
200 m_pIDataObject = pIDataSource;
201 m_pIDataObject->AddRef();
202
203 if ( !m_pTarget->MSWIsAcceptedData(pIDataSource) ) {
204 // we don't accept this kind of data
205 *pdwEffect = DROPEFFECT_NONE;
206 }
207 else
208 {
209 // we need client coordinates to pass to wxWin functions
210 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
211 {
212 wxLogLastError(wxT("ScreenToClient"));
213 }
214
215 // give some visual feedback
216 *pdwEffect = ConvertDragResultToEffect(
217 m_pTarget->OnEnter(pt.x, pt.y, ConvertDragEffectToResult(
218 GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction(), *pdwEffect))
219 )
220 );
221 }
222
223 // update drag image
224 const wxDragResult res = ConvertDragEffectToResult(*pdwEffect);
225 m_pTarget->MSWUpdateDragImageOnEnter(pt.x, pt.y, res);
226 m_pTarget->MSWUpdateDragImageOnDragOver(pt.x, pt.y, res);
227
228 return S_OK;
229 }
230
231
232
233 // Name : wxIDropTarget::DragOver
234 // Purpose : Indicates that the mouse was moved inside the window represented
235 // by this drop target.
236 // Returns : S_OK
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
241 // very efficient.
242 STDMETHODIMP wxIDropTarget::DragOver(DWORD grfKeyState,
243 POINTL pt,
244 LPDWORD pdwEffect)
245 {
246 // there are too many of them... wxLogDebug("IDropTarget::DragOver");
247
248 wxDragResult result;
249 if ( m_pIDataObject ) {
250 result = ConvertDragEffectToResult(
251 GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction(), *pdwEffect));
252 }
253 else {
254 // can't accept data anyhow normally
255 result = wxDragNone;
256 }
257
258 if ( result != wxDragNone ) {
259 // we need client coordinates to pass to wxWin functions
260 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
261 {
262 wxLogLastError(wxT("ScreenToClient"));
263 }
264
265 *pdwEffect = ConvertDragResultToEffect(
266 m_pTarget->OnDragOver(pt.x, pt.y, result)
267 );
268 }
269 else {
270 *pdwEffect = DROPEFFECT_NONE;
271 }
272
273 // update drag image
274 m_pTarget->MSWUpdateDragImageOnDragOver(pt.x, pt.y,
275 ConvertDragEffectToResult(*pdwEffect));
276
277 return S_OK;
278 }
279
280 // Name : wxIDropTarget::DragLeave
281 // Purpose : Informs the drop target that the operation has left its window.
282 // Returns : S_OK
283 // Notes : good place to do any clean-up
284 STDMETHODIMP wxIDropTarget::DragLeave()
285 {
286 wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragLeave"));
287
288 // remove the UI feedback
289 m_pTarget->OnLeave();
290
291 // release the held object
292 RELEASE_AND_NULL(m_pIDataObject);
293
294 // update drag image
295 m_pTarget->MSWUpdateDragImageOnLeave();
296
297 return S_OK;
298 }
299
300 // Name : wxIDropTarget::Drop
301 // Purpose : Instructs the drop target to paste data that was just now
302 // dropped on it.
303 // Returns : S_OK
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
308 // Notes :
309 STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
310 DWORD grfKeyState,
311 POINTL pt,
312 DWORD *pdwEffect)
313 {
314 wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::Drop"));
315
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 );
319
320 // we need client coordinates to pass to wxWin functions
321 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
322 {
323 wxLogLastError(wxT("ScreenToClient"));
324 }
325
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);
330
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);
338 }
339 else {
340 *pdwEffect = DROPEFFECT_NONE;
341 }
342 }
343 else {
344 // OnDrop() returned false, no need to copy data
345 *pdwEffect = DROPEFFECT_NONE;
346 }
347
348 // release the held object
349 RELEASE_AND_NULL(m_pIDataObject);
350
351 // update drag image
352 m_pTarget->MSWUpdateDragImageOnData(pt.x, pt.y,
353 ConvertDragEffectToResult(*pdwEffect));
354
355 return S_OK;
356 }
357
358 // ============================================================================
359 // wxDropTarget implementation
360 // ============================================================================
361
362 // ----------------------------------------------------------------------------
363 // ctor/dtor
364 // ----------------------------------------------------------------------------
365
366 wxDropTarget::wxDropTarget(wxDataObject *dataObj)
367 : wxDropTargetBase(dataObj),
368 m_dropTargetHelper(NULL)
369 {
370 // create an IDropTarget implementation which will notify us about d&d
371 // operations.
372 m_pIDropTarget = new wxIDropTarget(this);
373 m_pIDropTarget->AddRef();
374 }
375
376 wxDropTarget::~wxDropTarget()
377 {
378 ReleaseInterface(m_pIDropTarget);
379 }
380
381 // ----------------------------------------------------------------------------
382 // [un]register drop handler
383 // ----------------------------------------------------------------------------
384
385 bool wxDropTarget::Register(WXHWND hwnd)
386 {
387 // FIXME
388 // RegisterDragDrop not available on Windows CE >= 400?
389 // Or maybe we can dynamically load them from ceshell.dll
390 // or similar.
391 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
392 wxUnusedVar(hwnd);
393 return false;
394 #else
395 HRESULT hr;
396
397 // May exist in later WinCE versions
398 #ifndef __WXWINCE__
399 hr = ::CoLockObjectExternal(m_pIDropTarget, TRUE, FALSE);
400 if ( FAILED(hr) ) {
401 wxLogApiError(wxT("CoLockObjectExternal"), hr);
402 return false;
403 }
404 #endif
405
406 hr = ::RegisterDragDrop((HWND) hwnd, m_pIDropTarget);
407 if ( FAILED(hr) ) {
408 // May exist in later WinCE versions
409 #ifndef __WXWINCE__
410 ::CoLockObjectExternal(m_pIDropTarget, FALSE, FALSE);
411 #endif
412 wxLogApiError(wxT("RegisterDragDrop"), hr);
413 return false;
414 }
415
416 // we will need the window handle for coords transformation later
417 m_pIDropTarget->SetHwnd((HWND)hwnd);
418
419 MSWInitDragImageSupport();
420
421 return true;
422 #endif
423 }
424
425 void wxDropTarget::Revoke(WXHWND hwnd)
426 {
427 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
428 // Not available, see note above
429 wxUnusedVar(hwnd);
430 #else
431 HRESULT hr = ::RevokeDragDrop((HWND) hwnd);
432
433 if ( FAILED(hr) ) {
434 wxLogApiError(wxT("RevokeDragDrop"), hr);
435 }
436
437 // May exist in later WinCE versions
438 #ifndef __WXWINCE__
439 ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE);
440 #endif
441
442 MSWEndDragImageSupport();
443
444 // remove window reference
445 m_pIDropTarget->SetHwnd(0);
446 #endif
447 }
448
449 // ----------------------------------------------------------------------------
450 // base class pure virtuals
451 // ----------------------------------------------------------------------------
452
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))
456 {
457 return true;
458 }
459
460 // copy the data from the data source to the target data object
461 bool wxDropTarget::GetData()
462 {
463 wxDataFormat format = MSWGetSupportedFormat(m_pIDataSource);
464 if ( format == wxDF_INVALID ) {
465 return false;
466 }
467
468 STGMEDIUM stm;
469 FORMATETC fmtMemory;
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
475
476 bool rc = false;
477
478 HRESULT hr = m_pIDataSource->GetData(&fmtMemory, &stm);
479 if ( SUCCEEDED(hr) ) {
480 IDataObject *dataObject = m_dataObject->GetInterface();
481
482 hr = dataObject->SetData(&fmtMemory, &stm, TRUE);
483 if ( SUCCEEDED(hr) ) {
484 rc = true;
485 }
486 else {
487 wxLogApiError(wxT("IDataObject::SetData()"), hr);
488 }
489 }
490 else {
491 wxLogApiError(wxT("IDataObject::GetData()"), hr);
492 }
493
494 return rc;
495 }
496
497 // ----------------------------------------------------------------------------
498 // callbacks used by wxIDropTarget
499 // ----------------------------------------------------------------------------
500
501 // we need a data source, so wxIDropTarget gives it to us using this function
502 void wxDropTarget::MSWSetDataSource(IDataObject *pIDataSource)
503 {
504 m_pIDataSource = pIDataSource;
505 }
506
507 // determine if we accept data of this type
508 bool wxDropTarget::MSWIsAcceptedData(IDataObject *pIDataSource) const
509 {
510 return MSWGetSupportedFormat(pIDataSource) != wxDF_INVALID;
511 }
512
513 // ----------------------------------------------------------------------------
514 // helper functions
515 // ----------------------------------------------------------------------------
516
517 wxDataFormat wxDropTarget::GetMatchingPair()
518 {
519 return MSWGetSupportedFormat( m_pIDataSource );
520 }
521
522 wxDataFormat wxDropTarget::MSWGetSupportedFormat(IDataObject *pIDataSource) const
523 {
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 = {
527 0,
528 NULL,
529 DVASPECT_CONTENT,
530 -1,
531 TYMED_HGLOBAL // TODO is it worth supporting other tymeds here?
532 };
533
534 // get the list of supported formats
535 size_t nFormats = m_dataObject->GetFormatCount(wxDataObject::Set);
536 wxDataFormat format;
537 wxDataFormat *formats;
538 formats = nFormats == 1 ? &format : new wxDataFormat[nFormats];
539
540 m_dataObject->GetAllFormats(formats, wxDataObject::Set);
541
542 // cycle through all supported formats
543 size_t n;
544 for ( n = 0; n < nFormats; n++ ) {
545 s_fmtMemory.cfFormat = formats[n];
546
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 ) {
550 format = formats[n];
551
552 break;
553 }
554 }
555
556 if ( formats != &format ) {
557 // free memory if we allocated it
558 delete [] formats;
559 }
560
561 return n < nFormats ? format : wxFormatInvalid;
562 }
563
564 // ----------------------------------------------------------------------------
565 // drag image functions
566 // ----------------------------------------------------------------------------
567
568 void
569 wxDropTarget::MSWEndDragImageSupport()
570 {
571 // release drop target helper
572 if ( m_dropTargetHelper != NULL )
573 {
574 m_dropTargetHelper->Release();
575 m_dropTargetHelper = NULL;
576 }
577 }
578
579 void
580 wxDropTarget::MSWInitDragImageSupport()
581 {
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);
585 }
586
587 void
588 wxDropTarget::MSWUpdateDragImageOnData(wxCoord x,
589 wxCoord y,
590 wxDragResult dragResult)
591 {
592 // call corresponding event on drop target helper
593 if ( m_dropTargetHelper != NULL )
594 {
595 POINT pt = {x, y};
596 DWORD dwEffect = ConvertDragResultToEffect(dragResult);
597 m_dropTargetHelper->Drop(m_pIDataSource, &pt, dwEffect);
598 }
599 }
600
601 void
602 wxDropTarget::MSWUpdateDragImageOnDragOver(wxCoord x,
603 wxCoord y,
604 wxDragResult dragResult)
605 {
606 // call corresponding event on drop target helper
607 if ( m_dropTargetHelper != NULL )
608 {
609 POINT pt = {x, y};
610 DWORD dwEffect = ConvertDragResultToEffect(dragResult);
611 m_dropTargetHelper->DragOver(&pt, dwEffect);
612 }
613 }
614
615 void
616 wxDropTarget::MSWUpdateDragImageOnEnter(wxCoord x,
617 wxCoord y,
618 wxDragResult dragResult)
619 {
620 // call corresponding event on drop target helper
621 if ( m_dropTargetHelper != NULL )
622 {
623 POINT pt = {x, y};
624 DWORD dwEffect = ConvertDragResultToEffect(dragResult);
625 m_dropTargetHelper->DragEnter(m_pIDropTarget->GetHWND(), m_pIDataSource, &pt, dwEffect);
626 }
627 }
628
629 void
630 wxDropTarget::MSWUpdateDragImageOnLeave()
631 {
632 // call corresponding event on drop target helper
633 if ( m_dropTargetHelper != NULL )
634 {
635 m_dropTargetHelper->DragLeave();
636 }
637 }
638
639 // ----------------------------------------------------------------------------
640 // private functions
641 // ----------------------------------------------------------------------------
642
643 static wxDragResult ConvertDragEffectToResult(DWORD dwEffect)
644 {
645 switch ( dwEffect ) {
646 case DROPEFFECT_COPY:
647 return wxDragCopy;
648
649 case DROPEFFECT_LINK:
650 return wxDragLink;
651
652 case DROPEFFECT_MOVE:
653 return wxDragMove;
654
655 default:
656 wxFAIL_MSG(wxT("invalid value in ConvertDragEffectToResult"));
657 // fall through
658
659 case DROPEFFECT_NONE:
660 return wxDragNone;
661 }
662 }
663
664 static DWORD ConvertDragResultToEffect(wxDragResult result)
665 {
666 switch ( result ) {
667 case wxDragCopy:
668 return DROPEFFECT_COPY;
669
670 case wxDragLink:
671 return DROPEFFECT_LINK;
672
673 case wxDragMove:
674 return DROPEFFECT_MOVE;
675
676 default:
677 wxFAIL_MSG(wxT("invalid value in ConvertDragResultToEffect"));
678 // fall through
679
680 case wxDragNone:
681 return DROPEFFECT_NONE;
682 }
683 }
684
685 #endif // wxUSE_OLE && wxUSE_DRAG_AND_DROP