]> git.saurik.com Git - wxWidgets.git/blob - src/msw/ole/droptgt.cpp
Return an empty string from wxDataFormat::GetId if there was an error
[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 void SetHwnd(HWND hwnd) { m_hwnd = hwnd; }
67
68 // IDropTarget methods
69 STDMETHODIMP DragEnter(LPDATAOBJECT, DWORD, POINTL, LPDWORD);
70 STDMETHODIMP DragOver(DWORD, POINTL, LPDWORD);
71 STDMETHODIMP DragLeave();
72 STDMETHODIMP Drop(LPDATAOBJECT, DWORD, POINTL, LPDWORD);
73
74 DECLARE_IUNKNOWN_METHODS;
75
76 protected:
77 IDataObject *m_pIDataObject; // !NULL between DragEnter and DragLeave/Drop
78 wxDropTarget *m_pTarget; // the real target (we're just a proxy)
79
80 HWND m_hwnd; // window we're associated with
81
82 // get default drop effect for given keyboard flags
83 static DWORD GetDropEffect(DWORD flags, wxDragResult defaultAction, DWORD pdwEffect);
84
85 DECLARE_NO_COPY_CLASS(wxIDropTarget)
86 };
87
88 // ----------------------------------------------------------------------------
89 // private functions
90 // ----------------------------------------------------------------------------
91
92 static wxDragResult ConvertDragEffectToResult(DWORD dwEffect);
93 static DWORD ConvertDragResultToEffect(wxDragResult result);
94
95 // ============================================================================
96 // wxIDropTarget implementation
97 // ============================================================================
98
99 // Name : static wxIDropTarget::GetDropEffect
100 // Purpose : determine the drop operation from keyboard/mouse state.
101 // Returns : DWORD combined from DROPEFFECT_xxx constants
102 // Params : [in] DWORD flags kbd & mouse flags as passed to
103 // IDropTarget methods
104 // Notes : We do "move" normally and "copy" if <Ctrl> is pressed,
105 // which is the standard behaviour (currently there is no
106 // way to redefine it)
107 DWORD wxIDropTarget::GetDropEffect(DWORD flags,
108 wxDragResult defaultAction,
109 DWORD pdwEffect)
110 {
111 DWORD effectiveAction;
112 if ( defaultAction == wxDragCopy )
113 effectiveAction = flags & MK_SHIFT ? DROPEFFECT_MOVE : DROPEFFECT_COPY;
114 else
115 effectiveAction = flags & MK_CONTROL ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
116
117 if ( !(effectiveAction & pdwEffect) )
118 {
119 // the action is not supported by drag source, fall back to something
120 // that it does support
121 if ( pdwEffect & DROPEFFECT_MOVE )
122 effectiveAction = DROPEFFECT_MOVE;
123 else if ( pdwEffect & DROPEFFECT_COPY )
124 effectiveAction = DROPEFFECT_COPY;
125 else if ( pdwEffect & DROPEFFECT_LINK )
126 effectiveAction = DROPEFFECT_LINK;
127 else
128 effectiveAction = DROPEFFECT_NONE;
129 }
130
131 return effectiveAction;
132 }
133
134 wxIDropTarget::wxIDropTarget(wxDropTarget *pTarget)
135 {
136 m_pTarget = pTarget;
137 m_pIDataObject = NULL;
138 }
139
140 wxIDropTarget::~wxIDropTarget()
141 {
142 }
143
144 BEGIN_IID_TABLE(wxIDropTarget)
145 ADD_IID(Unknown)
146 ADD_IID(DropTarget)
147 END_IID_TABLE;
148
149 IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget)
150
151 // Name : wxIDropTarget::DragEnter
152 // Purpose : Called when the mouse enters the window (dragging something)
153 // Returns : S_OK
154 // Params : [in] IDataObject *pIDataSource : source data
155 // [in] DWORD grfKeyState : kbd & mouse state
156 // [in] POINTL pt : mouse coordinates
157 // [out]DWORD *pdwEffect : effect flag
158 // Notes :
159 STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource,
160 DWORD grfKeyState,
161 POINTL pt,
162 DWORD *pdwEffect)
163 {
164 wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragEnter"));
165
166 wxASSERT_MSG( m_pIDataObject == NULL,
167 _T("drop target must have data object") );
168
169 // show the list of formats supported by the source data object for the
170 // debugging purposes, this is quite useful sometimes - please don't remove
171 #if 0
172 IEnumFORMATETC *penumFmt;
173 if ( SUCCEEDED(pIDataSource->EnumFormatEtc(DATADIR_GET, &penumFmt)) )
174 {
175 FORMATETC fmt;
176 while ( penumFmt->Next(1, &fmt, NULL) == S_OK )
177 {
178 wxLogDebug(_T("Drop source supports format %s"),
179 wxDataObject::GetFormatName(fmt.cfFormat));
180 }
181
182 penumFmt->Release();
183 }
184 else
185 {
186 wxLogLastError(_T("IDataObject::EnumFormatEtc"));
187 }
188 #endif // 0
189
190 if ( !m_pTarget->IsAcceptedData(pIDataSource) ) {
191 // we don't accept this kind of data
192 *pdwEffect = DROPEFFECT_NONE;
193
194 return S_OK;
195 }
196
197 // get hold of the data object
198 m_pIDataObject = pIDataSource;
199 m_pIDataObject->AddRef();
200
201 // we need client coordinates to pass to wxWin functions
202 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
203 {
204 wxLogLastError(wxT("ScreenToClient"));
205 }
206
207 // give some visual feedback
208 *pdwEffect = ConvertDragResultToEffect(
209 m_pTarget->OnEnter(pt.x, pt.y, ConvertDragEffectToResult(
210 GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction(), *pdwEffect))
211 )
212 );
213
214 return S_OK;
215 }
216
217 // Name : wxIDropTarget::DragOver
218 // Purpose : Indicates that the mouse was moved inside the window represented
219 // by this drop target.
220 // Returns : S_OK
221 // Params : [in] DWORD grfKeyState kbd & mouse state
222 // [in] POINTL pt mouse coordinates
223 // [out]LPDWORD pdwEffect effect flag
224 // Notes : We're called on every WM_MOUSEMOVE, so this function should be
225 // very efficient.
226 STDMETHODIMP wxIDropTarget::DragOver(DWORD grfKeyState,
227 POINTL pt,
228 LPDWORD pdwEffect)
229 {
230 // there are too many of them... wxLogDebug("IDropTarget::DragOver");
231
232 wxDragResult result;
233 if ( m_pIDataObject ) {
234 result = ConvertDragEffectToResult(
235 GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction(), *pdwEffect));
236 }
237 else {
238 // can't accept data anyhow normally
239 result = wxDragNone;
240 }
241
242 // we need client coordinates to pass to wxWin functions
243 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
244 {
245 wxLogLastError(wxT("ScreenToClient"));
246 }
247
248 *pdwEffect = ConvertDragResultToEffect(
249 m_pTarget->OnDragOver(pt.x, pt.y, result)
250 );
251
252 return S_OK;
253 }
254
255 // Name : wxIDropTarget::DragLeave
256 // Purpose : Informs the drop target that the operation has left its window.
257 // Returns : S_OK
258 // Notes : good place to do any clean-up
259 STDMETHODIMP wxIDropTarget::DragLeave()
260 {
261 wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragLeave"));
262
263 // remove the UI feedback
264 m_pTarget->OnLeave();
265
266 // release the held object
267 RELEASE_AND_NULL(m_pIDataObject);
268
269 return S_OK;
270 }
271
272 // Name : wxIDropTarget::Drop
273 // Purpose : Instructs the drop target to paste data that was just now
274 // dropped on it.
275 // Returns : S_OK
276 // Params : [in] IDataObject *pIDataSource the data to paste
277 // [in] DWORD grfKeyState kbd & mouse state
278 // [in] POINTL pt where the drop occurred?
279 // [ouy]DWORD *pdwEffect operation effect
280 // Notes :
281 STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
282 DWORD grfKeyState,
283 POINTL pt,
284 DWORD *pdwEffect)
285 {
286 wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::Drop"));
287
288 // TODO I don't know why there is this parameter, but so far I assume
289 // that it's the same we've already got in DragEnter
290 wxASSERT( m_pIDataObject == pIDataSource );
291
292 // we need client coordinates to pass to wxWin functions
293 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
294 {
295 wxLogLastError(wxT("ScreenToClient"));
296 }
297
298 // first ask the drop target if it wants data
299 if ( m_pTarget->OnDrop(pt.x, pt.y) ) {
300 // it does, so give it the data source
301 m_pTarget->SetDataSource(pIDataSource);
302
303 // and now it has the data
304 wxDragResult rc = ConvertDragEffectToResult(
305 GetDropEffect(grfKeyState, m_pTarget->GetDefaultAction(), *pdwEffect));
306 rc = m_pTarget->OnData(pt.x, pt.y, rc);
307 if ( wxIsDragResultOk(rc) ) {
308 // operation succeeded
309 *pdwEffect = ConvertDragResultToEffect(rc);
310 }
311 else {
312 *pdwEffect = DROPEFFECT_NONE;
313 }
314 }
315 else {
316 // OnDrop() returned false, no need to copy data
317 *pdwEffect = DROPEFFECT_NONE;
318 }
319
320 // release the held object
321 RELEASE_AND_NULL(m_pIDataObject);
322
323 return S_OK;
324 }
325
326 // ============================================================================
327 // wxDropTarget implementation
328 // ============================================================================
329
330 // ----------------------------------------------------------------------------
331 // ctor/dtor
332 // ----------------------------------------------------------------------------
333
334 wxDropTarget::wxDropTarget(wxDataObject *dataObj)
335 : wxDropTargetBase(dataObj)
336 {
337 // create an IDropTarget implementation which will notify us about d&d
338 // operations.
339 m_pIDropTarget = new wxIDropTarget(this);
340 m_pIDropTarget->AddRef();
341 }
342
343 wxDropTarget::~wxDropTarget()
344 {
345 ReleaseInterface(m_pIDropTarget);
346 }
347
348 // ----------------------------------------------------------------------------
349 // [un]register drop handler
350 // ----------------------------------------------------------------------------
351
352 bool wxDropTarget::Register(WXHWND hwnd)
353 {
354 // FIXME
355 // RegisterDragDrop not available on Windows CE >= 400?
356 // Or maybe we can dynamically load them from ceshell.dll
357 // or similar.
358 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
359 wxUnusedVar(hwnd);
360 return false;
361 #else
362 HRESULT hr;
363
364 // May exist in later WinCE versions
365 #ifndef __WXWINCE__
366 hr = ::CoLockObjectExternal(m_pIDropTarget, TRUE, FALSE);
367 if ( FAILED(hr) ) {
368 wxLogApiError(wxT("CoLockObjectExternal"), hr);
369 return false;
370 }
371 #endif
372
373 hr = ::RegisterDragDrop((HWND) hwnd, m_pIDropTarget);
374 if ( FAILED(hr) ) {
375 // May exist in later WinCE versions
376 #ifndef __WXWINCE__
377 ::CoLockObjectExternal(m_pIDropTarget, FALSE, FALSE);
378 #endif
379 wxLogApiError(wxT("RegisterDragDrop"), hr);
380 return false;
381 }
382
383 // we will need the window handle for coords transformation later
384 m_pIDropTarget->SetHwnd((HWND)hwnd);
385
386 return true;
387 #endif
388 }
389
390 void wxDropTarget::Revoke(WXHWND hwnd)
391 {
392 #if defined(__WXWINCE__) && _WIN32_WCE >= 400
393 // Not available, see note above
394 wxUnusedVar(hwnd);
395 #else
396 HRESULT hr = ::RevokeDragDrop((HWND) hwnd);
397
398 if ( FAILED(hr) ) {
399 wxLogApiError(wxT("RevokeDragDrop"), hr);
400 }
401
402 // May exist in later WinCE versions
403 #ifndef __WXWINCE__
404 ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE);
405 #endif
406
407 m_pIDropTarget->SetHwnd(0);
408 #endif
409 }
410
411 // ----------------------------------------------------------------------------
412 // base class pure virtuals
413 // ----------------------------------------------------------------------------
414
415 // OnDrop() is called only if we previously returned true from
416 // IsAcceptedData(), so no need to check anything here
417 bool wxDropTarget::OnDrop(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y))
418 {
419 return true;
420 }
421
422 // copy the data from the data source to the target data object
423 bool wxDropTarget::GetData()
424 {
425 wxDataFormat format = GetSupportedFormat(m_pIDataSource);
426 if ( format == wxDF_INVALID ) {
427 // this is strange because IsAcceptedData() succeeded previously!
428 wxFAIL_MSG(wxT("strange - did supported formats list change?"));
429
430 return false;
431 }
432
433 STGMEDIUM stm;
434 FORMATETC fmtMemory;
435 fmtMemory.cfFormat = format;
436 fmtMemory.ptd = NULL;
437 fmtMemory.dwAspect = DVASPECT_CONTENT;
438 fmtMemory.lindex = -1;
439 fmtMemory.tymed = TYMED_HGLOBAL; // TODO to add other media
440
441 bool rc = false;
442
443 HRESULT hr = m_pIDataSource->GetData(&fmtMemory, &stm);
444 if ( SUCCEEDED(hr) ) {
445 IDataObject *dataObject = m_dataObject->GetInterface();
446
447 hr = dataObject->SetData(&fmtMemory, &stm, TRUE);
448 if ( SUCCEEDED(hr) ) {
449 rc = true;
450 }
451 else {
452 wxLogApiError(wxT("IDataObject::SetData()"), hr);
453 }
454 }
455 else {
456 wxLogApiError(wxT("IDataObject::GetData()"), hr);
457 }
458
459 return rc;
460 }
461
462 // ----------------------------------------------------------------------------
463 // callbacks used by wxIDropTarget
464 // ----------------------------------------------------------------------------
465
466 // we need a data source, so wxIDropTarget gives it to us using this function
467 void wxDropTarget::SetDataSource(IDataObject *pIDataSource)
468 {
469 m_pIDataSource = pIDataSource;
470 }
471
472 // determine if we accept data of this type
473 bool wxDropTarget::IsAcceptedData(IDataObject *pIDataSource) const
474 {
475 return GetSupportedFormat(pIDataSource) != wxDF_INVALID;
476 }
477
478 // ----------------------------------------------------------------------------
479 // helper functions
480 // ----------------------------------------------------------------------------
481
482 wxDataFormat wxDropTarget::GetSupportedFormat(IDataObject *pIDataSource) const
483 {
484 // this strucutre describes a data of any type (first field will be
485 // changing) being passed through global memory block.
486 static FORMATETC s_fmtMemory = {
487 0,
488 NULL,
489 DVASPECT_CONTENT,
490 -1,
491 TYMED_HGLOBAL // TODO is it worth supporting other tymeds here?
492 };
493
494 // get the list of supported formats
495 size_t nFormats = m_dataObject->GetFormatCount(wxDataObject::Set);
496 wxDataFormat format;
497 wxDataFormat *formats;
498 formats = nFormats == 1 ? &format : new wxDataFormat[nFormats];
499
500 m_dataObject->GetAllFormats(formats, wxDataObject::Set);
501
502 // cycle through all supported formats
503 size_t n;
504 for ( n = 0; n < nFormats; n++ ) {
505 s_fmtMemory.cfFormat = formats[n];
506
507 // NB: don't use SUCCEEDED macro here: QueryGetData returns S_FALSE
508 // for file drag and drop (format == CF_HDROP)
509 if ( pIDataSource->QueryGetData(&s_fmtMemory) == S_OK ) {
510 format = formats[n];
511
512 break;
513 }
514 }
515
516 if ( formats != &format ) {
517 // free memory if we allocated it
518 delete [] formats;
519 }
520
521 return n < nFormats ? format : wxFormatInvalid;
522 }
523
524 // ----------------------------------------------------------------------------
525 // private functions
526 // ----------------------------------------------------------------------------
527
528 static wxDragResult ConvertDragEffectToResult(DWORD dwEffect)
529 {
530 switch ( dwEffect ) {
531 case DROPEFFECT_COPY:
532 return wxDragCopy;
533
534 case DROPEFFECT_LINK:
535 return wxDragLink;
536
537 case DROPEFFECT_MOVE:
538 return wxDragMove;
539
540 default:
541 wxFAIL_MSG(wxT("invalid value in ConvertDragEffectToResult"));
542 // fall through
543
544 case DROPEFFECT_NONE:
545 return wxDragNone;
546 }
547 }
548
549 static DWORD ConvertDragResultToEffect(wxDragResult result)
550 {
551 switch ( result ) {
552 case wxDragCopy:
553 return DROPEFFECT_COPY;
554
555 case wxDragLink:
556 return DROPEFFECT_LINK;
557
558 case wxDragMove:
559 return DROPEFFECT_MOVE;
560
561 default:
562 wxFAIL_MSG(wxT("invalid value in ConvertDragResultToEffect"));
563 // fall through
564
565 case wxDragNone:
566 return DROPEFFECT_NONE;
567 }
568 }
569
570 #endif // wxUSE_OLE && wxUSE_DRAG_AND_DROP