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