]> git.saurik.com Git - wxWidgets.git/blame - src/msw/ole/droptgt.cpp
Allow for multiple CHM controllers in app (don't leak dynamic libs)
[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>
6c9a19aa 9// Licence: wxWindows licence
bbf1f0e5
KB
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);
22f3361e
VZ
88
89 DECLARE_NO_COPY_CLASS(wxIDropTarget)
bbf1f0e5
KB
90};
91
c9057ae1
VZ
92// ----------------------------------------------------------------------------
93// private functions
94// ----------------------------------------------------------------------------
95
96static wxDragResult ConvertDragEffectToResult(DWORD dwEffect);
97static DWORD ConvertDragResultToEffect(wxDragResult result);
98
bbf1f0e5
KB
99// ============================================================================
100// wxIDropTarget implementation
101// ============================================================================
102
8ee9d618 103// Name : static wxIDropTarget::GetDropEffect
bbf1f0e5 104// Purpose : determine the drop operation from keyboard/mouse state.
3f480da3 105// Returns : DWORD combined from DROPEFFECT_xxx constants
bbf1f0e5
KB
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,
3f480da3 109// which is the standard behaviour (currently there is no
bbf1f0e5
KB
110// way to redefine it)
111DWORD wxIDropTarget::GetDropEffect(DWORD flags)
112{
113 return flags & MK_CONTROL ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
114}
115
116wxIDropTarget::wxIDropTarget(wxDropTarget *pTarget)
3f480da3 117{
bbf1f0e5 118 m_pTarget = pTarget;
3f480da3 119 m_pIDataObject = NULL;
bbf1f0e5
KB
120}
121
3f480da3
VZ
122wxIDropTarget::~wxIDropTarget()
123{
bbf1f0e5
KB
124}
125
126BEGIN_IID_TABLE(wxIDropTarget)
127 ADD_IID(Unknown)
128 ADD_IID(DropTarget)
129END_IID_TABLE;
130
131IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget)
132
bbf1f0e5
KB
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
3f480da3 140// Notes :
bbf1f0e5
KB
141STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource,
142 DWORD grfKeyState,
143 POINTL pt,
144 DWORD *pdwEffect)
145{
c5639a87 146 wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragEnter"));
bbf1f0e5 147
c5639a87
VZ
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
444ad3a7 152 // debugging purposes, this is quite useful sometimes - please don't remove
c5639a87
VZ
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
bbf1f0e5 171
8ee9d618
VZ
172 if ( !m_pTarget->IsAcceptedData(pIDataSource) ) {
173 // we don't accept this kind of data
174 *pdwEffect = DROPEFFECT_NONE;
bbf1f0e5 175
8ee9d618
VZ
176 return S_OK;
177 }
bbf1f0e5 178
8ee9d618
VZ
179 // get hold of the data object
180 m_pIDataObject = pIDataSource;
181 m_pIDataObject->AddRef();
bbf1f0e5 182
8ee9d618
VZ
183 // we need client coordinates to pass to wxWin functions
184 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
185 {
f6bcfd97 186 wxLogLastError(wxT("ScreenToClient"));
8ee9d618 187 }
bbf1f0e5 188
8ee9d618
VZ
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;
bbf1f0e5
KB
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
3f480da3 206// Notes : We're called on every WM_MOUSEMOVE, so this function should be
bbf1f0e5
KB
207// very efficient.
208STDMETHODIMP wxIDropTarget::DragOver(DWORD grfKeyState,
209 POINTL pt,
210 LPDWORD pdwEffect)
211{
8ee9d618 212 // there are too many of them... wxLogDebug("IDropTarget::DragOver");
bbf1f0e5 213
8ee9d618
VZ
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 }
c9057ae1 222
8ee9d618
VZ
223 // we need client coordinates to pass to wxWin functions
224 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
225 {
f6bcfd97 226 wxLogLastError(wxT("ScreenToClient"));
8ee9d618 227 }
c9057ae1 228
8ee9d618
VZ
229 *pdwEffect = ConvertDragResultToEffect(
230 m_pTarget->OnDragOver(pt.x, pt.y, result)
231 );
232
233 return S_OK;
bbf1f0e5
KB
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
240STDMETHODIMP wxIDropTarget::DragLeave()
241{
c5639a87 242 wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::DragLeave"));
bbf1f0e5
KB
243
244 // remove the UI feedback
245 m_pTarget->OnLeave();
246
247 // release the held object
248 RELEASE_AND_NULL(m_pIDataObject);
3f480da3 249
bbf1f0e5
KB
250 return S_OK;
251}
252
253// Name : wxIDropTarget::Drop
3f480da3 254// Purpose : Instructs the drop target to paste data that was just now
bbf1f0e5
KB
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
3f480da3
VZ
261// Notes :
262STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
263 DWORD grfKeyState,
264 POINTL pt,
bbf1f0e5
KB
265 DWORD *pdwEffect)
266{
c5639a87 267 wxLogTrace(wxTRACE_OleCalls, wxT("IDropTarget::Drop"));
9e2896e5
VZ
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
8ee9d618
VZ
276 // we need client coordinates to pass to wxWin functions
277 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
278 {
f6bcfd97 279 wxLogLastError(wxT("ScreenToClient"));
8ee9d618
VZ
280 }
281
9e2896e5
VZ
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
8ee9d618 288 wxDragResult rc = ConvertDragEffectToResult(GetDropEffect(grfKeyState));
87a1e308 289 rc = m_pTarget->OnData(pt.x, pt.y, rc);
8ee9d618 290 if ( wxIsDragResultOk(rc) ) {
9e2896e5 291 // operation succeeded
8ee9d618 292 *pdwEffect = ConvertDragResultToEffect(rc);
9e2896e5
VZ
293 }
294 //else: *pdwEffect is already DROPEFFECT_NONE
bbf1f0e5 295 }
9e2896e5 296 //else: OnDrop() returned FALSE, no need to copy data
bbf1f0e5 297
9e2896e5
VZ
298 // release the held object
299 RELEASE_AND_NULL(m_pIDataObject);
bbf1f0e5 300
9e2896e5 301 return S_OK;
bbf1f0e5
KB
302}
303
304// ============================================================================
305// wxDropTarget implementation
306// ============================================================================
307
308// ----------------------------------------------------------------------------
309// ctor/dtor
310// ----------------------------------------------------------------------------
311
9e2896e5
VZ
312wxDropTarget::wxDropTarget(wxDataObject *dataObj)
313 : wxDropTargetBase(dataObj)
bbf1f0e5 314{
9e2896e5
VZ
315 // create an IDropTarget implementation which will notify us about d&d
316 // operations.
317 m_pIDropTarget = new wxIDropTarget(this);
318 m_pIDropTarget->AddRef();
bbf1f0e5
KB
319}
320
321wxDropTarget::~wxDropTarget()
322{
9e2896e5 323 ReleaseInterface(m_pIDropTarget);
bbf1f0e5
KB
324}
325
326// ----------------------------------------------------------------------------
327// [un]register drop handler
328// ----------------------------------------------------------------------------
329
330bool wxDropTarget::Register(WXHWND hwnd)
331{
9e2896e5
VZ
332 HRESULT hr = ::CoLockObjectExternal(m_pIDropTarget, TRUE, FALSE);
333 if ( FAILED(hr) ) {
161f4f73 334 wxLogApiError(wxT("CoLockObjectExternal"), hr);
9e2896e5
VZ
335 return FALSE;
336 }
bbf1f0e5 337
9e2896e5
VZ
338 hr = ::RegisterDragDrop((HWND) hwnd, m_pIDropTarget);
339 if ( FAILED(hr) ) {
340 ::CoLockObjectExternal(m_pIDropTarget, FALSE, FALSE);
bbf1f0e5 341
161f4f73 342 wxLogApiError(wxT("RegisterDragDrop"), hr);
9e2896e5
VZ
343 return FALSE;
344 }
bbf1f0e5 345
8ee9d618
VZ
346 // we will need the window handle for coords transformation later
347 m_pIDropTarget->SetHwnd((HWND)hwnd);
348
9e2896e5 349 return TRUE;
bbf1f0e5
KB
350}
351
352void wxDropTarget::Revoke(WXHWND hwnd)
353{
9e2896e5 354 HRESULT hr = ::RevokeDragDrop((HWND) hwnd);
bbf1f0e5 355
9e2896e5 356 if ( FAILED(hr) ) {
161f4f73 357 wxLogApiError(wxT("RevokeDragDrop"), hr);
9e2896e5 358 }
bbf1f0e5 359
9e2896e5 360 ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE);
8ee9d618
VZ
361
362 m_pIDropTarget->SetHwnd(0);
bbf1f0e5
KB
363}
364
365// ----------------------------------------------------------------------------
9e2896e5 366// base class pure virtuals
bbf1f0e5 367// ----------------------------------------------------------------------------
9e2896e5
VZ
368
369// OnDrop() is called only if we previously returned TRUE from
370// IsAcceptedData(), so no need to check anything here
371bool 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
377bool wxDropTarget::GetData()
bbf1f0e5 378{
9e2896e5
VZ
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 {
444ad3a7 406 wxLogApiError(wxT("IDataObject::SetData()"), hr);
9e2896e5
VZ
407 }
408 }
409 else {
444ad3a7 410 wxLogApiError(wxT("IDataObject::GetData()"), hr);
bbf1f0e5 411 }
bbf1f0e5 412
9e2896e5 413 return rc;
bbf1f0e5
KB
414}
415
9e2896e5
VZ
416// ----------------------------------------------------------------------------
417// callbacks used by wxIDropTarget
418// ----------------------------------------------------------------------------
bbf1f0e5 419
9e2896e5
VZ
420// we need a data source, so wxIDropTarget gives it to us using this function
421void wxDropTarget::SetDataSource(IDataObject *pIDataSource)
bbf1f0e5 422{
9e2896e5 423 m_pIDataSource = pIDataSource;
bbf1f0e5
KB
424}
425
9e2896e5
VZ
426// determine if we accept data of this type
427bool wxDropTarget::IsAcceptedData(IDataObject *pIDataSource) const
bbf1f0e5 428{
9e2896e5 429 return GetSupportedFormat(pIDataSource) != wxDF_INVALID;
bbf1f0e5
KB
430}
431
9e2896e5
VZ
432// ----------------------------------------------------------------------------
433// helper functions
434// ----------------------------------------------------------------------------
435
436wxDataFormat wxDropTarget::GetSupportedFormat(IDataObject *pIDataSource) const
bbf1f0e5 437{
9e2896e5
VZ
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);
33ac7e6f 450 wxDataFormat format;
c5639a87 451 wxDataFormat *formats;
9e2896e5
VZ
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
90bc25c7 475 return n < nFormats ? format : wxFormatInvalid;
bbf1f0e5
KB
476}
477
c9057ae1
VZ
478// ----------------------------------------------------------------------------
479// private functions
480// ----------------------------------------------------------------------------
481
482static wxDragResult ConvertDragEffectToResult(DWORD dwEffect)
483{
484 switch ( dwEffect ) {
485 case DROPEFFECT_COPY:
486 return wxDragCopy;
487
e6d318c2
RD
488 case DROPEFFECT_LINK:
489 return wxDragLink;
490
c9057ae1
VZ
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
503static DWORD ConvertDragResultToEffect(wxDragResult result)
504{
505 switch ( result ) {
506 case wxDragCopy:
507 return DROPEFFECT_COPY;
508
e6d318c2
RD
509 case wxDragLink:
510 return DROPEFFECT_LINK;
511
c9057ae1
VZ
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
bbf1f0e5 524#endif
47d67540 525 // wxUSE_DRAG_AND_DROP