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