]> git.saurik.com Git - wxWidgets.git/blame - src/msw/ole/droptgt.cpp
wxCaret MSW bug fixes
[wxWidgets.git] / src / msw / ole / droptgt.cpp
CommitLineData
bbf1f0e5
KB
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".
bbf1f0e5
KB
25#include "wx/wxprec.h"
26
27#if defined(__BORLANDC__)
28#pragma hdrstop
29#endif
30
31#include <wx/setup.h>
32
47d67540 33#if wxUSE_DRAG_AND_DROP
bbf1f0e5
KB
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/msw/ole/droptgt.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
60class wxIDropTarget : public IDropTarget
61{
62public:
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 // @@ we assume that if QueryGetData() returns S_OK, than we can really
73 // get data in this format, so we remember here the format for which
74 // QueryGetData() succeeded
75 void SetSupportedFormat(wxDataFormat cfFormat) { m_cfFormat = cfFormat; }
76
77 DECLARE_IUNKNOWN_METHODS;
78
79protected:
80 IDataObject *m_pIDataObject; // !NULL between DragEnter and DragLeave/Drop
81 wxDropTarget *m_pTarget; // the real target (we're just a proxy)
82
83 wxDataFormat m_cfFormat; // the format in which to ask for data
84
85private:
86 static inline DWORD GetDropEffect(DWORD flags);
87};
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)
101DWORD wxIDropTarget::GetDropEffect(DWORD flags)
102{
103 return flags & MK_CONTROL ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
104}
105
106wxIDropTarget::wxIDropTarget(wxDropTarget *pTarget)
107{
108 m_cRef = 0;
109 m_pTarget = pTarget;
b3324be2 110 m_cfFormat = (wxDataFormat) 0;
bbf1f0e5
KB
111 m_pIDataObject = NULL;
112}
113
114wxIDropTarget::~wxIDropTarget()
115{
116}
117
118BEGIN_IID_TABLE(wxIDropTarget)
119 ADD_IID(Unknown)
120 ADD_IID(DropTarget)
121END_IID_TABLE;
122
123IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget)
124
bbf1f0e5
KB
125// Name : wxIDropTarget::DragEnter
126// Purpose : Called when the mouse enters the window (dragging something)
127// Returns : S_OK
128// Params : [in] IDataObject *pIDataSource : source data
129// [in] DWORD grfKeyState : kbd & mouse state
130// [in] POINTL pt : mouse coordinates
131// [out]DWORD *pdwEffect : effect flag
132// Notes :
133STDMETHODIMP wxIDropTarget::DragEnter(IDataObject *pIDataSource,
134 DWORD grfKeyState,
135 POINTL pt,
136 DWORD *pdwEffect)
137{
138 wxLogDebug("IDropTarget::DragEnter");
139
140 wxASSERT( m_pIDataObject == NULL );
141
142 if ( !m_pTarget->IsAcceptedData(pIDataSource) ) {
143 // we don't accept this kind of data
144 *pdwEffect = DROPEFFECT_NONE;
145
146 return S_OK;
147 }
148
149 // @@ should check the point also?
150
151 *pdwEffect = GetDropEffect(grfKeyState);
152
153 // get hold of the data object
154 m_pIDataObject = pIDataSource;
155 m_pIDataObject->AddRef();
156
157 // give some visual feedback
158 m_pTarget->OnEnter();
159
160 return S_OK;
161}
162
163// Name : wxIDropTarget::DragOver
164// Purpose : Indicates that the mouse was moved inside the window represented
165// by this drop target.
166// Returns : S_OK
167// Params : [in] DWORD grfKeyState kbd & mouse state
168// [in] POINTL pt mouse coordinates
169// [out]LPDWORD pdwEffect effect flag
170// Notes : We're called on every WM_MOUSEMOVE, so this function should be
171// very efficient.
172STDMETHODIMP wxIDropTarget::DragOver(DWORD grfKeyState,
173 POINTL pt,
174 LPDWORD pdwEffect)
175{
176 // there are too many of them... wxLogDebug("IDropTarget::DragOver");
177
178 *pdwEffect = m_pIDataObject == NULL ? DROPEFFECT_NONE
179 : GetDropEffect(grfKeyState);
180 return S_OK;
181}
182
183// Name : wxIDropTarget::DragLeave
184// Purpose : Informs the drop target that the operation has left its window.
185// Returns : S_OK
186// Notes : good place to do any clean-up
187STDMETHODIMP wxIDropTarget::DragLeave()
188{
189 wxLogDebug("IDropTarget::DragLeave");
190
191 // remove the UI feedback
192 m_pTarget->OnLeave();
193
194 // release the held object
195 RELEASE_AND_NULL(m_pIDataObject);
196
197 return S_OK;
198}
199
200// Name : wxIDropTarget::Drop
201// Purpose : Instructs the drop target to paste data that was just now
202// dropped on it.
203// Returns : S_OK
204// Params : [in] IDataObject *pIDataSource the data to paste
205// [in] DWORD grfKeyState kbd & mouse state
206// [in] POINTL pt where the drop occured?
207// [ouy]DWORD *pdwEffect operation effect
208// Notes :
209STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
210 DWORD grfKeyState,
211 POINTL pt,
212 DWORD *pdwEffect)
213{
214 wxLogDebug("IDropTarget::Drop");
215
216 // @@ I don't know why there is this parameter, but so far I assume
217 // that it's the same we've already got in DragEnter
218 wxASSERT( m_pIDataObject == pIDataSource );
219
220 STGMEDIUM stm;
221 *pdwEffect = DROPEFFECT_NONE;
222
223 // should be set by SetSupportedFormat() call
224 wxASSERT( m_cfFormat != 0 );
225
226 FORMATETC fmtMemory;
227 fmtMemory.cfFormat = m_cfFormat;
228 fmtMemory.ptd = NULL;
229 fmtMemory.dwAspect = DVASPECT_CONTENT;
230 fmtMemory.lindex = -1;
231 fmtMemory.tymed = TYMED_HGLOBAL; // @@@@ to add other media
232
233 HRESULT hr = pIDataSource->GetData(&fmtMemory, &stm);
234 if ( SUCCEEDED(hr) ) {
235 if ( stm.hGlobal != NULL ) {
236 if ( m_pTarget->OnDrop(pt.x, pt.y, GlobalLock(stm.hGlobal)) )
237 *pdwEffect = GetDropEffect(grfKeyState);
238 //else: DROPEFFECT_NONE
239
240 GlobalUnlock(stm.hGlobal);
241 ReleaseStgMedium(&stm);
242 }
243 }
244 else
245 {
246 // wxLogApiError("GetData", hr);
247 }
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
263wxDropTarget::wxDropTarget()
264{
265 // create an IDropTarget implementation which will notify us about
266 // d&d operations.
267 m_pIDropTarget = new wxIDropTarget(this);
268 m_pIDropTarget->AddRef();
269}
270
271wxDropTarget::~wxDropTarget()
272{
273 ReleaseInterface(m_pIDropTarget);
274}
275
276// ----------------------------------------------------------------------------
277// [un]register drop handler
278// ----------------------------------------------------------------------------
279
280bool wxDropTarget::Register(WXHWND hwnd)
281{
282 HRESULT hr = ::CoLockObjectExternal(m_pIDropTarget, TRUE, FALSE);
283 if ( FAILED(hr) ) {
4075e9a4 284 wxLogApiError("CoLockObjectExternal", hr);
bbf1f0e5
KB
285 return FALSE;
286 }
287
288 hr = ::RegisterDragDrop((HWND) hwnd, m_pIDropTarget);
289 if ( FAILED(hr) ) {
290 ::CoLockObjectExternal(m_pIDropTarget, FALSE, FALSE);
291
4075e9a4 292 wxLogApiError("RegisterDragDrop", hr);
bbf1f0e5
KB
293 return FALSE;
294 }
295
296 return TRUE;
297}
298
299void wxDropTarget::Revoke(WXHWND hwnd)
300{
301 HRESULT hr = ::RevokeDragDrop((HWND) hwnd);
302
4075e9a4
VZ
303 if ( FAILED(hr) ) {
304 wxLogApiError("RevokeDragDrop", hr);
bbf1f0e5
KB
305 }
306
307 ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE);
308}
309
310// ----------------------------------------------------------------------------
311// determine if we accept data of this type
312// ----------------------------------------------------------------------------
313bool wxDropTarget::IsAcceptedData(IDataObject *pIDataSource) const
314{
315 // this strucutre describes a data of any type (first field will be
316 // changing) being passed through global memory block.
317 static FORMATETC s_fmtMemory = {
318 0,
319 NULL,
320 DVASPECT_CONTENT,
321 -1,
322 TYMED_HGLOBAL
323 };
324
325 // cycle thorugh all supported formats
326 for ( size_t n = 0; n < GetFormatCount(); n++ ) {
327 s_fmtMemory.cfFormat = GetFormat(n);
328 // @ don't use SUCCEEDED macro here: QueryGetData returns 1 (whatever it
329 // means) for file drag and drop
330 if ( pIDataSource->QueryGetData(&s_fmtMemory) == S_OK ) {
331 // remember this format: we'll later ask for data in it
b3324be2 332 m_pIDropTarget->SetSupportedFormat((wxDataFormat) s_fmtMemory.cfFormat);
bbf1f0e5
KB
333 return TRUE;
334 }
335 }
336
337 return FALSE;
338}
339
340// ============================================================================
341// wxTextDropTarget
342// ============================================================================
343
344bool wxTextDropTarget::OnDrop(long x, long y, const void *pData)
345{
346 return OnDropText(x, y, (const char *)pData);
347}
348
349size_t wxTextDropTarget::GetFormatCount() const
350{
351 return 1;
352}
353
354wxDataFormat wxTextDropTarget::GetFormat(size_t WXUNUSED(n)) const
355{
b3324be2 356 return wxDF_TEXT;
bbf1f0e5
KB
357}
358
359// ============================================================================
360// wxFileDropTarget
361// ============================================================================
362
363bool wxFileDropTarget::OnDrop(long x, long y, const void *pData)
364{
365 // the documentation states that the first member of DROPFILES structure
366 // is a "DWORD offset of double NUL terminated file list". What they mean by
367 // this (I wonder if you see it immediately) is that the list starts at
368 // ((char *)&(pDropFiles.pFiles)) + pDropFiles.pFiles. We're also advised to
369 // use DragQueryFile to work with this structure, but not told where and how
370 // to get HDROP.
371 HDROP hdrop = (HDROP)pData; // @@ it works, but I'm not sure about it
372
373 // get number of files (magic value -1)
fd3f686c 374 UINT nFiles = ::DragQueryFile(hdrop, (unsigned)-1, NULL, 0u);
bbf1f0e5
KB
375
376 // for each file get the length, allocate memory and then get the name
377 char **aszFiles = new char *[nFiles];
378 UINT len, n;
379 for ( n = 0; n < nFiles; n++ ) {
380 // +1 for terminating NUL
381 len = ::DragQueryFile(hdrop, n, NULL, 0) + 1;
382
383 aszFiles[n] = new char[len];
384
385 UINT len2 = ::DragQueryFile(hdrop, n, aszFiles[n], len);
386 if ( len2 != len - 1 ) {
387 wxLogDebug("In wxFileDropTarget::OnDrop DragQueryFile returned %d "
388 "characters, %d expected.", len2, len - 1);
389 }
390 }
391
392 bool bResult = OnDropFiles(x, y, nFiles, (const char**) aszFiles);
393
394 // free memory
395 for ( n = 0; n < nFiles; n++ ) {
396 delete [] aszFiles[n];
397 }
398 delete [] aszFiles;
399
400 return bResult;
401}
402
403size_t wxFileDropTarget::GetFormatCount() const
404{
405 return 1;
406}
407
408wxDataFormat wxFileDropTarget::GetFormat(size_t WXUNUSED(n)) const
409{
410#ifdef __WIN32__
b3324be2 411 return wxDF_FILENAME;
bbf1f0e5
KB
412#else
413 // TODO: how to implement this in WIN16?
b3324be2 414 return wxDF_TEXT;
bbf1f0e5
KB
415#endif
416}
417
418#endif
47d67540 419 // wxUSE_DRAG_AND_DROP