]> git.saurik.com Git - wxWidgets.git/blame - src/msw/ole/droptgt.cpp
fixed incorrect GetTextExtent for wxTELETYPE font
[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
47d67540 33#if wxUSE_DRAG_AND_DROP
bbf1f0e5 34
3f480da3 35#include "wx/log.h"
bbf1f0e5
KB
36
37#ifdef __WIN32__
3f480da3
VZ
38 #ifndef __GNUWIN32__
39 #include <shlobj.h> // for DROPFILES structure
40 #endif
bbf1f0e5 41#else
3f480da3 42 #include <shellapi.h>
bbf1f0e5
KB
43#endif
44
9e2896e5 45#include "wx/dnd.h"
bbf1f0e5
KB
46
47#ifndef __WIN32__
3f480da3
VZ
48 #include <ole2.h>
49 #include <olestd.h>
bbf1f0e5
KB
50#endif
51
3f480da3 52#include "wx/msw/ole/oleutils.h"
bbf1f0e5
KB
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:
8ee9d618 63 wxIDropTarget(wxDropTarget *p);
c9ce2da0
VZ
64 // suppress gcc warning
65#ifdef __GNUG__
66 virtual
67#endif
8ee9d618 68 ~wxIDropTarget();
bbf1f0e5 69
8ee9d618
VZ
70 // accessors for wxDropTarget
71 void SetHwnd(HWND hwnd) { m_hwnd = hwnd; }
bbf1f0e5 72
8ee9d618
VZ
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;
bbf1f0e5
KB
80
81protected:
8ee9d618
VZ
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
bbf1f0e5 86
8ee9d618
VZ
87 // get default drop effect for given keyboard flags
88 static inline DWORD GetDropEffect(DWORD flags);
bbf1f0e5
KB
89};
90
c9057ae1
VZ
91// ----------------------------------------------------------------------------
92// private functions
93// ----------------------------------------------------------------------------
94
95static wxDragResult ConvertDragEffectToResult(DWORD dwEffect);
96static DWORD ConvertDragResultToEffect(wxDragResult result);
97
bbf1f0e5
KB
98// ============================================================================
99// wxIDropTarget implementation
100// ============================================================================
101
8ee9d618 102// Name : static wxIDropTarget::GetDropEffect
bbf1f0e5 103// Purpose : determine the drop operation from keyboard/mouse state.
3f480da3 104// Returns : DWORD combined from DROPEFFECT_xxx constants
bbf1f0e5
KB
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,
3f480da3 108// which is the standard behaviour (currently there is no
bbf1f0e5
KB
109// way to redefine it)
110DWORD wxIDropTarget::GetDropEffect(DWORD flags)
111{
112 return flags & MK_CONTROL ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
113}
114
115wxIDropTarget::wxIDropTarget(wxDropTarget *pTarget)
3f480da3
VZ
116{
117 m_cRef = 0;
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{
8ee9d618 146 wxLogDebug(wxT("IDropTarget::DragEnter"));
bbf1f0e5 147
8ee9d618 148 wxASSERT( m_pIDataObject == NULL );
bbf1f0e5 149
8ee9d618
VZ
150 if ( !m_pTarget->IsAcceptedData(pIDataSource) ) {
151 // we don't accept this kind of data
152 *pdwEffect = DROPEFFECT_NONE;
bbf1f0e5 153
8ee9d618
VZ
154 return S_OK;
155 }
bbf1f0e5 156
8ee9d618
VZ
157 // get hold of the data object
158 m_pIDataObject = pIDataSource;
159 m_pIDataObject->AddRef();
bbf1f0e5 160
8ee9d618
VZ
161 // we need client coordinates to pass to wxWin functions
162 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
163 {
f6bcfd97 164 wxLogLastError(wxT("ScreenToClient"));
8ee9d618 165 }
bbf1f0e5 166
8ee9d618
VZ
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;
bbf1f0e5
KB
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
3f480da3 184// Notes : We're called on every WM_MOUSEMOVE, so this function should be
bbf1f0e5
KB
185// very efficient.
186STDMETHODIMP wxIDropTarget::DragOver(DWORD grfKeyState,
187 POINTL pt,
188 LPDWORD pdwEffect)
189{
8ee9d618 190 // there are too many of them... wxLogDebug("IDropTarget::DragOver");
bbf1f0e5 191
8ee9d618
VZ
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 }
c9057ae1 200
8ee9d618
VZ
201 // we need client coordinates to pass to wxWin functions
202 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
203 {
f6bcfd97 204 wxLogLastError(wxT("ScreenToClient"));
8ee9d618 205 }
c9057ae1 206
8ee9d618
VZ
207 *pdwEffect = ConvertDragResultToEffect(
208 m_pTarget->OnDragOver(pt.x, pt.y, result)
209 );
210
211 return S_OK;
bbf1f0e5
KB
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
218STDMETHODIMP wxIDropTarget::DragLeave()
219{
223d09f6 220 wxLogDebug(wxT("IDropTarget::DragLeave"));
bbf1f0e5
KB
221
222 // remove the UI feedback
223 m_pTarget->OnLeave();
224
225 // release the held object
226 RELEASE_AND_NULL(m_pIDataObject);
3f480da3 227
bbf1f0e5
KB
228 return S_OK;
229}
230
231// Name : wxIDropTarget::Drop
3f480da3 232// Purpose : Instructs the drop target to paste data that was just now
bbf1f0e5
KB
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
3f480da3
VZ
239// Notes :
240STDMETHODIMP wxIDropTarget::Drop(IDataObject *pIDataSource,
241 DWORD grfKeyState,
242 POINTL pt,
bbf1f0e5
KB
243 DWORD *pdwEffect)
244{
9e2896e5
VZ
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
8ee9d618
VZ
254 // we need client coordinates to pass to wxWin functions
255 if ( !ScreenToClient(m_hwnd, (POINT *)&pt) )
256 {
f6bcfd97 257 wxLogLastError(wxT("ScreenToClient"));
8ee9d618
VZ
258 }
259
9e2896e5
VZ
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
8ee9d618 266 wxDragResult rc = ConvertDragEffectToResult(GetDropEffect(grfKeyState));
87a1e308 267 rc = m_pTarget->OnData(pt.x, pt.y, rc);
8ee9d618 268 if ( wxIsDragResultOk(rc) ) {
9e2896e5 269 // operation succeeded
8ee9d618 270 *pdwEffect = ConvertDragResultToEffect(rc);
9e2896e5
VZ
271 }
272 //else: *pdwEffect is already DROPEFFECT_NONE
bbf1f0e5 273 }
9e2896e5 274 //else: OnDrop() returned FALSE, no need to copy data
bbf1f0e5 275
9e2896e5
VZ
276 // release the held object
277 RELEASE_AND_NULL(m_pIDataObject);
bbf1f0e5 278
9e2896e5 279 return S_OK;
bbf1f0e5
KB
280}
281
282// ============================================================================
283// wxDropTarget implementation
284// ============================================================================
285
286// ----------------------------------------------------------------------------
287// ctor/dtor
288// ----------------------------------------------------------------------------
289
9e2896e5
VZ
290wxDropTarget::wxDropTarget(wxDataObject *dataObj)
291 : wxDropTargetBase(dataObj)
bbf1f0e5 292{
9e2896e5
VZ
293 // create an IDropTarget implementation which will notify us about d&d
294 // operations.
295 m_pIDropTarget = new wxIDropTarget(this);
296 m_pIDropTarget->AddRef();
bbf1f0e5
KB
297}
298
299wxDropTarget::~wxDropTarget()
300{
9e2896e5 301 ReleaseInterface(m_pIDropTarget);
bbf1f0e5
KB
302}
303
304// ----------------------------------------------------------------------------
305// [un]register drop handler
306// ----------------------------------------------------------------------------
307
308bool wxDropTarget::Register(WXHWND hwnd)
309{
9e2896e5
VZ
310 HRESULT hr = ::CoLockObjectExternal(m_pIDropTarget, TRUE, FALSE);
311 if ( FAILED(hr) ) {
312 wxLogApiError("CoLockObjectExternal", hr);
313 return FALSE;
314 }
bbf1f0e5 315
9e2896e5
VZ
316 hr = ::RegisterDragDrop((HWND) hwnd, m_pIDropTarget);
317 if ( FAILED(hr) ) {
318 ::CoLockObjectExternal(m_pIDropTarget, FALSE, FALSE);
bbf1f0e5 319
9e2896e5
VZ
320 wxLogApiError("RegisterDragDrop", hr);
321 return FALSE;
322 }
bbf1f0e5 323
8ee9d618
VZ
324 // we will need the window handle for coords transformation later
325 m_pIDropTarget->SetHwnd((HWND)hwnd);
326
9e2896e5 327 return TRUE;
bbf1f0e5
KB
328}
329
330void wxDropTarget::Revoke(WXHWND hwnd)
331{
9e2896e5 332 HRESULT hr = ::RevokeDragDrop((HWND) hwnd);
bbf1f0e5 333
9e2896e5
VZ
334 if ( FAILED(hr) ) {
335 wxLogApiError("RevokeDragDrop", hr);
336 }
bbf1f0e5 337
9e2896e5 338 ::CoLockObjectExternal(m_pIDropTarget, FALSE, TRUE);
8ee9d618
VZ
339
340 m_pIDropTarget->SetHwnd(0);
bbf1f0e5
KB
341}
342
343// ----------------------------------------------------------------------------
9e2896e5 344// base class pure virtuals
bbf1f0e5 345// ----------------------------------------------------------------------------
9e2896e5
VZ
346
347// OnDrop() is called only if we previously returned TRUE from
348// IsAcceptedData(), so no need to check anything here
349bool 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
355bool wxDropTarget::GetData()
bbf1f0e5 356{
9e2896e5
VZ
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 {
f6bcfd97 384 wxLogLastError(wxT("IDataObject::SetData()"));
9e2896e5
VZ
385 }
386 }
387 else {
f6bcfd97 388 wxLogLastError(wxT("IDataObject::GetData()"));
bbf1f0e5 389 }
bbf1f0e5 390
9e2896e5 391 return rc;
bbf1f0e5
KB
392}
393
9e2896e5
VZ
394// ----------------------------------------------------------------------------
395// callbacks used by wxIDropTarget
396// ----------------------------------------------------------------------------
bbf1f0e5 397
9e2896e5
VZ
398// we need a data source, so wxIDropTarget gives it to us using this function
399void wxDropTarget::SetDataSource(IDataObject *pIDataSource)
bbf1f0e5 400{
9e2896e5 401 m_pIDataSource = pIDataSource;
bbf1f0e5
KB
402}
403
9e2896e5
VZ
404// determine if we accept data of this type
405bool wxDropTarget::IsAcceptedData(IDataObject *pIDataSource) const
bbf1f0e5 406{
9e2896e5 407 return GetSupportedFormat(pIDataSource) != wxDF_INVALID;
bbf1f0e5
KB
408}
409
9e2896e5
VZ
410// ----------------------------------------------------------------------------
411// helper functions
412// ----------------------------------------------------------------------------
413
414wxDataFormat wxDropTarget::GetSupportedFormat(IDataObject *pIDataSource) const
bbf1f0e5 415{
9e2896e5
VZ
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
90bc25c7 452 return n < nFormats ? format : wxFormatInvalid;
bbf1f0e5
KB
453}
454
c9057ae1
VZ
455// ----------------------------------------------------------------------------
456// private functions
457// ----------------------------------------------------------------------------
458
459static 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
477static 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
bbf1f0e5 495#endif
47d67540 496 // wxUSE_DRAG_AND_DROP