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