1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/ole/droptgt.cpp 
   3 // Purpose:     wxDropTarget implementation 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  23 #if defined(__BORLANDC__) 
  27 #if wxUSE_OLE && wxUSE_DRAG_AND_DROP 
  30     #include "wx/msw/wrapwin.h" 
  34 #include "wx/msw/private.h" 
  42     #if !defined(__GNUWIN32__) || wxUSE_NORLANDER_HEADERS 
  43         #include <shlobj.h>            // for DROPFILES structure 
  51 #include "wx/msw/ole/oleutils.h" 
  53 // ---------------------------------------------------------------------------- 
  54 // IDropTarget interface: forward all interesting things to wxDropTarget 
  55 // (the name is unfortunate, but wx_I_DropTarget is not at all the same thing 
  56 //  as wxDropTarget which is 'public' class, while this one is private) 
  57 // ---------------------------------------------------------------------------- 
  59 class wxIDropTarget 
: public IDropTarget
 
  62     wxIDropTarget(wxDropTarget 
*p
); 
  63     virtual ~wxIDropTarget(); 
  65     // accessors for wxDropTarget 
  66     void SetHwnd(HWND hwnd
) { m_hwnd 
= hwnd
; } 
  68     // IDropTarget methods 
  69     STDMETHODIMP 
DragEnter(LPDATAOBJECT
, DWORD
, POINTL
, LPDWORD
); 
  70     STDMETHODIMP 
DragOver(DWORD
, POINTL
, LPDWORD
); 
  71     STDMETHODIMP 
DragLeave(); 
  72     STDMETHODIMP 
Drop(LPDATAOBJECT
, DWORD
, POINTL
, LPDWORD
); 
  74     DECLARE_IUNKNOWN_METHODS
; 
  77     IDataObject  
*m_pIDataObject
; // !NULL between DragEnter and DragLeave/Drop 
  78     wxDropTarget 
*m_pTarget
;      // the real target (we're just a proxy) 
  80     HWND          m_hwnd
;         // window we're associated with 
  82     // get default drop effect for given keyboard flags 
  83     static DWORD 
GetDropEffect(DWORD flags
, wxDragResult defaultAction
, DWORD pdwEffect
); 
  85     DECLARE_NO_COPY_CLASS(wxIDropTarget
) 
  88 // ---------------------------------------------------------------------------- 
  90 // ---------------------------------------------------------------------------- 
  92 static wxDragResult 
ConvertDragEffectToResult(DWORD dwEffect
); 
  93 static DWORD 
ConvertDragResultToEffect(wxDragResult result
); 
  95 // ============================================================================ 
  96 // wxIDropTarget implementation 
  97 // ============================================================================ 
  99 // Name    : static wxIDropTarget::GetDropEffect 
 100 // Purpose : determine the drop operation from keyboard/mouse state. 
 101 // Returns : DWORD combined from DROPEFFECT_xxx constants 
 102 // Params  : [in] DWORD flags                 kbd & mouse flags as passed to 
 103 //                                            IDropTarget methods 
 104 //           [in] wxDragResult defaultAction  the default action of the drop target 
 105 //           [in] DWORD pdwEffect             the supported actions of the drop  
 106 //                                            source passed to IDropTarget methods 
 107 // Notes   : We do "move" normally and "copy" if <Ctrl> is pressed, 
 108 //           which is the standard behaviour (currently there is no 
 109 //           way to redefine it) 
 110 DWORD 
wxIDropTarget::GetDropEffect(DWORD flags
, 
 111                                    wxDragResult defaultAction
, 
 114     DWORD effectiveAction
; 
 115     if ( defaultAction 
== wxDragCopy 
) 
 116         effectiveAction 
= flags 
& MK_SHIFT 
? DROPEFFECT_MOVE 
: DROPEFFECT_COPY
; 
 118         effectiveAction 
= flags 
& MK_CONTROL 
? DROPEFFECT_COPY 
: DROPEFFECT_MOVE
; 
 120     if ( !(effectiveAction 
& pdwEffect
) ) 
 122         // the action is not supported by drag source, fall back to something 
 123         // that it does support 
 124         if ( pdwEffect 
& DROPEFFECT_MOVE 
) 
 125             effectiveAction 
= DROPEFFECT_MOVE
; 
 126         else if ( pdwEffect 
& DROPEFFECT_COPY 
) 
 127             effectiveAction 
= DROPEFFECT_COPY
; 
 128         else if ( pdwEffect 
& DROPEFFECT_LINK 
) 
 129             effectiveAction 
= DROPEFFECT_LINK
; 
 131             effectiveAction 
= DROPEFFECT_NONE
; 
 134     return effectiveAction
; 
 137 wxIDropTarget::wxIDropTarget(wxDropTarget 
*pTarget
) 
 140   m_pIDataObject 
= NULL
; 
 143 wxIDropTarget::~wxIDropTarget() 
 147 BEGIN_IID_TABLE(wxIDropTarget
) 
 152 IMPLEMENT_IUNKNOWN_METHODS(wxIDropTarget
) 
 154 // Name    : wxIDropTarget::DragEnter 
 155 // Purpose : Called when the mouse enters the window (dragging something) 
 157 // Params  : [in]    IDataObject *pIDataSource : source data 
 158 //           [in]    DWORD        grfKeyState  : kbd & mouse state 
 159 //           [in]    POINTL       pt           : mouse coordinates 
 160 //           [in/out]DWORD       *pdwEffect    : effect flag 
 161 //                                               In:  Supported effects 
 162 //                                               Out: Resulting effect 
 164 STDMETHODIMP 
wxIDropTarget::DragEnter(IDataObject 
*pIDataSource
, 
 169     wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::DragEnter")); 
 171     wxASSERT_MSG( m_pIDataObject 
== NULL
, 
 172                   _T("drop target must have data object") ); 
 174     // show the list of formats supported by the source data object for the 
 175     // debugging purposes, this is quite useful sometimes - please don't remove 
 177     IEnumFORMATETC 
*penumFmt
; 
 178     if ( SUCCEEDED(pIDataSource
->EnumFormatEtc(DATADIR_GET
, &penumFmt
)) ) 
 181         while ( penumFmt
->Next(1, &fmt
, NULL
) == S_OK 
) 
 183             wxLogDebug(_T("Drop source supports format %s"), 
 184                        wxDataObject::GetFormatName(fmt
.cfFormat
)); 
 191         wxLogLastError(_T("IDataObject::EnumFormatEtc")); 
 195     if ( !m_pTarget
->IsAcceptedData(pIDataSource
) ) { 
 196         // we don't accept this kind of data 
 197         *pdwEffect 
= DROPEFFECT_NONE
; 
 202     // get hold of the data object 
 203     m_pIDataObject 
= pIDataSource
; 
 204     m_pIDataObject
->AddRef(); 
 206     // we need client coordinates to pass to wxWin functions 
 207     if ( !ScreenToClient(m_hwnd
, (POINT 
*)&pt
) ) 
 209         wxLogLastError(wxT("ScreenToClient")); 
 212     // give some visual feedback 
 213     *pdwEffect 
= ConvertDragResultToEffect( 
 214         m_pTarget
->OnEnter(pt
.x
, pt
.y
, ConvertDragEffectToResult( 
 215             GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
)) 
 222 // Name    : wxIDropTarget::DragOver 
 223 // Purpose : Indicates that the mouse was moved inside the window represented 
 224 //           by this drop target. 
 226 // Params  : [in]    DWORD   grfKeyState     kbd & mouse state 
 227 //           [in]    POINTL  pt              mouse coordinates 
 228 //           [in/out]LPDWORD pdwEffect       current effect flag 
 229 // Notes   : We're called on every WM_MOUSEMOVE, so this function should be 
 231 STDMETHODIMP 
wxIDropTarget::DragOver(DWORD   grfKeyState
, 
 235     // there are too many of them... wxLogDebug("IDropTarget::DragOver"); 
 238     if ( m_pIDataObject 
) { 
 239         result 
= ConvertDragEffectToResult( 
 240             GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
)); 
 243         // can't accept data anyhow normally 
 247     // we need client coordinates to pass to wxWin functions 
 248     if ( !ScreenToClient(m_hwnd
, (POINT 
*)&pt
) ) 
 250         wxLogLastError(wxT("ScreenToClient")); 
 253     *pdwEffect 
= ConvertDragResultToEffect( 
 254                     m_pTarget
->OnDragOver(pt
.x
, pt
.y
, result
) 
 260 // Name    : wxIDropTarget::DragLeave 
 261 // Purpose : Informs the drop target that the operation has left its window. 
 263 // Notes   : good place to do any clean-up 
 264 STDMETHODIMP 
wxIDropTarget::DragLeave() 
 266   wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::DragLeave")); 
 268   // remove the UI feedback 
 269   m_pTarget
->OnLeave(); 
 271   // release the held object 
 272   RELEASE_AND_NULL(m_pIDataObject
); 
 277 // Name    : wxIDropTarget::Drop 
 278 // Purpose : Instructs the drop target to paste data that was just now 
 281 // Params  : [in]    IDataObject *pIDataSource     the data to paste 
 282 //           [in]    DWORD        grfKeyState      kbd & mouse state 
 283 //           [in]    POINTL       pt               where the drop occurred? 
 284 //           [in/out]DWORD       *pdwEffect        operation effect 
 286 STDMETHODIMP 
wxIDropTarget::Drop(IDataObject 
*pIDataSource
, 
 291     wxLogTrace(wxTRACE_OleCalls
, wxT("IDropTarget::Drop")); 
 293     // TODO I don't know why there is this parameter, but so far I assume 
 294     //      that it's the same we've already got in DragEnter 
 295     wxASSERT( m_pIDataObject 
== pIDataSource 
); 
 297     // we need client coordinates to pass to wxWin functions 
 298     if ( !ScreenToClient(m_hwnd
, (POINT 
*)&pt
) ) 
 300         wxLogLastError(wxT("ScreenToClient")); 
 303     // first ask the drop target if it wants data 
 304     if ( m_pTarget
->OnDrop(pt
.x
, pt
.y
) ) { 
 305         // it does, so give it the data source 
 306         m_pTarget
->SetDataSource(pIDataSource
); 
 308         // and now it has the data 
 309         wxDragResult rc 
= ConvertDragEffectToResult( 
 310             GetDropEffect(grfKeyState
, m_pTarget
->GetDefaultAction(), *pdwEffect
)); 
 311         rc 
= m_pTarget
->OnData(pt
.x
, pt
.y
, rc
); 
 312         if ( wxIsDragResultOk(rc
) ) { 
 313             // operation succeeded 
 314             *pdwEffect 
= ConvertDragResultToEffect(rc
); 
 317             *pdwEffect 
= DROPEFFECT_NONE
; 
 321         // OnDrop() returned false, no need to copy data 
 322         *pdwEffect 
= DROPEFFECT_NONE
; 
 325     // release the held object 
 326     RELEASE_AND_NULL(m_pIDataObject
); 
 331 // ============================================================================ 
 332 // wxDropTarget implementation 
 333 // ============================================================================ 
 335 // ---------------------------------------------------------------------------- 
 337 // ---------------------------------------------------------------------------- 
 339 wxDropTarget::wxDropTarget(wxDataObject 
*dataObj
) 
 340             : wxDropTargetBase(dataObj
) 
 342     // create an IDropTarget implementation which will notify us about d&d 
 344     m_pIDropTarget 
= new wxIDropTarget(this); 
 345     m_pIDropTarget
->AddRef(); 
 348 wxDropTarget::~wxDropTarget() 
 350     ReleaseInterface(m_pIDropTarget
); 
 353 // ---------------------------------------------------------------------------- 
 354 // [un]register drop handler 
 355 // ---------------------------------------------------------------------------- 
 357 bool wxDropTarget::Register(WXHWND hwnd
) 
 360     // RegisterDragDrop not available on Windows CE >= 400? 
 361     // Or maybe we can dynamically load them from ceshell.dll 
 363 #if defined(__WXWINCE__) && _WIN32_WCE >= 400 
 369     // May exist in later WinCE versions 
 371     hr 
= ::CoLockObjectExternal(m_pIDropTarget
, TRUE
, FALSE
); 
 373         wxLogApiError(wxT("CoLockObjectExternal"), hr
); 
 378     hr 
= ::RegisterDragDrop((HWND
) hwnd
, m_pIDropTarget
); 
 380     // May exist in later WinCE versions 
 382         ::CoLockObjectExternal(m_pIDropTarget
, FALSE
, FALSE
); 
 384         wxLogApiError(wxT("RegisterDragDrop"), hr
); 
 388     // we will need the window handle for coords transformation later 
 389     m_pIDropTarget
->SetHwnd((HWND
)hwnd
); 
 395 void wxDropTarget::Revoke(WXHWND hwnd
) 
 397 #if defined(__WXWINCE__) && _WIN32_WCE >= 400 
 398     // Not available, see note above 
 401     HRESULT hr 
= ::RevokeDragDrop((HWND
) hwnd
); 
 404         wxLogApiError(wxT("RevokeDragDrop"), hr
); 
 407     // May exist in later WinCE versions 
 409     ::CoLockObjectExternal(m_pIDropTarget
, FALSE
, TRUE
); 
 412     m_pIDropTarget
->SetHwnd(0); 
 416 // ---------------------------------------------------------------------------- 
 417 // base class pure virtuals 
 418 // ---------------------------------------------------------------------------- 
 420 // OnDrop() is called only if we previously returned true from 
 421 // IsAcceptedData(), so no need to check anything here 
 422 bool wxDropTarget::OnDrop(wxCoord 
WXUNUSED(x
), wxCoord 
WXUNUSED(y
)) 
 427 // copy the data from the data source to the target data object 
 428 bool wxDropTarget::GetData() 
 430     wxDataFormat format 
= GetSupportedFormat(m_pIDataSource
); 
 431     if ( format 
== wxDF_INVALID 
) { 
 432         // this is strange because IsAcceptedData() succeeded previously! 
 433         wxFAIL_MSG(wxT("strange - did supported formats list change?")); 
 440     fmtMemory
.cfFormat  
= format
; 
 441     fmtMemory
.ptd       
= NULL
; 
 442     fmtMemory
.dwAspect  
= DVASPECT_CONTENT
; 
 443     fmtMemory
.lindex    
= -1; 
 444     fmtMemory
.tymed     
= TYMED_HGLOBAL
;  // TODO to add other media 
 448     HRESULT hr 
= m_pIDataSource
->GetData(&fmtMemory
, &stm
); 
 449     if ( SUCCEEDED(hr
) ) { 
 450         IDataObject 
*dataObject 
= m_dataObject
->GetInterface(); 
 452         hr 
= dataObject
->SetData(&fmtMemory
, &stm
, TRUE
); 
 453         if ( SUCCEEDED(hr
) ) { 
 457             wxLogApiError(wxT("IDataObject::SetData()"), hr
); 
 461         wxLogApiError(wxT("IDataObject::GetData()"), hr
); 
 467 // ---------------------------------------------------------------------------- 
 468 // callbacks used by wxIDropTarget 
 469 // ---------------------------------------------------------------------------- 
 471 // we need a data source, so wxIDropTarget gives it to us using this function 
 472 void wxDropTarget::SetDataSource(IDataObject 
*pIDataSource
) 
 474     m_pIDataSource 
= pIDataSource
; 
 477 // determine if we accept data of this type 
 478 bool wxDropTarget::IsAcceptedData(IDataObject 
*pIDataSource
) const 
 480     return GetSupportedFormat(pIDataSource
) != wxDF_INVALID
; 
 483 // ---------------------------------------------------------------------------- 
 485 // ---------------------------------------------------------------------------- 
 487 wxDataFormat 
wxDropTarget::GetSupportedFormat(IDataObject 
*pIDataSource
) const 
 489     // this strucutre describes a data of any type (first field will be 
 490     // changing) being passed through global memory block. 
 491     static FORMATETC s_fmtMemory 
= { 
 496         TYMED_HGLOBAL       
// TODO is it worth supporting other tymeds here? 
 499     // get the list of supported formats 
 500     size_t nFormats 
= m_dataObject
->GetFormatCount(wxDataObject::Set
); 
 502     wxDataFormat 
*formats
; 
 503     formats 
= nFormats 
== 1 ? &format 
:  new wxDataFormat
[nFormats
]; 
 505     m_dataObject
->GetAllFormats(formats
, wxDataObject::Set
); 
 507     // cycle through all supported formats 
 509     for ( n 
= 0; n 
< nFormats
; n
++ ) { 
 510         s_fmtMemory
.cfFormat 
= formats
[n
]; 
 512         // NB: don't use SUCCEEDED macro here: QueryGetData returns S_FALSE 
 513         //     for file drag and drop (format == CF_HDROP) 
 514         if ( pIDataSource
->QueryGetData(&s_fmtMemory
) == S_OK 
) { 
 521     if ( formats 
!= &format 
) { 
 522         // free memory if we allocated it 
 526     return n 
< nFormats 
? format 
: wxFormatInvalid
; 
 529 // ---------------------------------------------------------------------------- 
 531 // ---------------------------------------------------------------------------- 
 533 static wxDragResult 
ConvertDragEffectToResult(DWORD dwEffect
) 
 535     switch ( dwEffect 
) { 
 536         case DROPEFFECT_COPY
: 
 539         case DROPEFFECT_LINK
: 
 542         case DROPEFFECT_MOVE
: 
 546             wxFAIL_MSG(wxT("invalid value in ConvertDragEffectToResult")); 
 549         case DROPEFFECT_NONE
: 
 554 static DWORD 
ConvertDragResultToEffect(wxDragResult result
) 
 558             return DROPEFFECT_COPY
; 
 561             return DROPEFFECT_LINK
; 
 564             return DROPEFFECT_MOVE
; 
 567             wxFAIL_MSG(wxT("invalid value in ConvertDragResultToEffect")); 
 571             return DROPEFFECT_NONE
; 
 575 #endif // wxUSE_OLE && wxUSE_DRAG_AND_DROP