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