This commit was manufactured by cvs2svn to create branch 'captive'.
[reactos.git] / subsys / system / explorer / utility / dragdropimpl.cpp
1 /**************************************************************************
2    THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF
3    ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
4    THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
5    PARTICULAR PURPOSE.
6    Author: Leon Finker  11/2000
7    Modifications: replaced ATL by STL, Martin Fuchs 7/2003
8 **************************************************************************/
9
10 // dragdropimp.cpp: implementation of the IDataObjectImpl class.
11 //////////////////////////////////////////////////////////////////////
12 #include <shlobj.h>
13 #include <assert.h>
14
15 #include "dragdropimpl.h"
16
17 //////////////////////////////////////////////////////////////////////
18 // IDataObjectImpl Class
19 //////////////////////////////////////////////////////////////////////
20
21 IDataObjectImpl::IDataObjectImpl(IDropSourceImpl* pDropSource):
22         m_cRefCount(0),
23         m_pDropSource(pDropSource)
24 {
25 }
26
27 IDataObjectImpl::~IDataObjectImpl()
28 {
29         for(StorageArray::iterator it=_storage.begin(); it!=_storage.end(); ++it)
30                 ReleaseStgMedium(it->_medium);
31 }
32
33 STDMETHODIMP IDataObjectImpl::QueryInterface(/* [in] */ REFIID riid,
34 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
35 {
36         *ppvObject = NULL;
37         if (IID_IUnknown==riid || IID_IDataObject==riid)
38                          *ppvObject=this;
39         /*if (riid == IID_IAsyncOperation)
40                 *ppvObject=(IAsyncOperation*)this;*/
41         if (NULL!=*ppvObject)
42         {
43                 ((LPUNKNOWN)*ppvObject)->AddRef();
44                 return S_OK;
45         }
46         return E_NOINTERFACE;
47 }
48
49 STDMETHODIMP_(ULONG) IDataObjectImpl::AddRef()
50 {
51         return ++m_cRefCount;
52 }
53
54 STDMETHODIMP_(ULONG) IDataObjectImpl::Release()
55 {
56    long nTemp = --m_cRefCount;
57
58    if (nTemp == 0)
59           delete this;
60
61    return nTemp;
62 }
63
64 STDMETHODIMP IDataObjectImpl::GetData( 
65         /* [unique][in] */ FORMATETC __RPC_FAR *pformatetcIn,
66         /* [out] */ STGMEDIUM __RPC_FAR *pmedium)
67
68         if (pformatetcIn == NULL || pmedium == NULL)
69                 return E_INVALIDARG;
70
71         pmedium->hGlobal = NULL;
72
73         for(StorageArray::iterator it=_storage.begin(); it!=_storage.end(); ++it)
74         {
75                 if (pformatetcIn->tymed & it->_format->tymed &&
76                    pformatetcIn->dwAspect == it->_format->dwAspect &&
77                    pformatetcIn->cfFormat == it->_format->cfFormat)
78                 {
79                         CopyMedium(pmedium, it->_medium, it->_format);
80                         return S_OK;
81                 }
82         }
83
84         return DV_E_FORMATETC;
85 }
86
87 STDMETHODIMP IDataObjectImpl::GetDataHere( 
88         /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc,
89         /* [out][in] */ STGMEDIUM __RPC_FAR *pmedium)
90
91         return E_NOTIMPL;
92 }
93
94 STDMETHODIMP IDataObjectImpl::QueryGetData( 
95    /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc)
96
97         if (pformatetc == NULL)
98                 return E_INVALIDARG;
99
100         //support others if needed DVASPECT_THUMBNAIL  //DVASPECT_ICON   //DVASPECT_DOCPRINT
101         if (!(DVASPECT_CONTENT & pformatetc->dwAspect))
102                 return (DV_E_DVASPECT);
103
104         HRESULT  hr = DV_E_TYMED;
105
106         for(StorageArray::iterator it=_storage.begin(); it!=_storage.end(); ++it)
107         {
108            if (pformatetc->tymed & it->_format->tymed)
109            {
110                   if (pformatetc->cfFormat == it->_format->cfFormat)
111                          return S_OK;
112                   else
113                          hr = DV_E_CLIPFORMAT;
114            }
115            else
116                   hr = DV_E_TYMED;
117         }
118         return hr;
119 }
120
121 STDMETHODIMP IDataObjectImpl::GetCanonicalFormatEtc( 
122         /* [unique][in] */ FORMATETC __RPC_FAR *pformatectIn,
123         /* [out] */ FORMATETC __RPC_FAR *pformatetcOut)
124
125         if (pformatetcOut == NULL)
126                 return E_INVALIDARG;
127
128         return DATA_S_SAMEFORMATETC;
129 }
130
131 STDMETHODIMP IDataObjectImpl::SetData( 
132         /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc,
133         /* [unique][in] */ STGMEDIUM __RPC_FAR *pmedium,
134         /* [in] */ BOOL fRelease)
135
136         if (pformatetc == NULL || pmedium == NULL)
137           return E_INVALIDARG;
138
139         assert(pformatetc->tymed == pmedium->tymed);
140         FORMATETC* fetc=new FORMATETC;
141         STGMEDIUM* pStgMed = new STGMEDIUM;
142
143         if (fetc == NULL || pStgMed == NULL)
144                 return E_OUTOFMEMORY;
145
146         ZeroMemory(fetc, sizeof(FORMATETC));
147         ZeroMemory(pStgMed, sizeof(STGMEDIUM));
148
149         *fetc = *pformatetc;
150
151         if (fRelease)
152           *pStgMed = *pmedium;
153         else
154                 CopyMedium(pStgMed, pmedium, pformatetc);
155
156         DataStorage storage;
157
158         storage._format = fetc;
159         storage._medium = pStgMed;
160
161         _storage.push_back(storage);
162
163         return S_OK;
164 }
165
166 void IDataObjectImpl::CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc)
167 {
168         switch(pMedSrc->tymed)
169         {
170         case TYMED_HGLOBAL:
171                 pMedDest->hGlobal = (HGLOBAL)OleDuplicateData(pMedSrc->hGlobal, pFmtSrc->cfFormat, 0);
172                 break;
173         case TYMED_GDI:
174                 pMedDest->hBitmap = (HBITMAP)OleDuplicateData(pMedSrc->hBitmap, pFmtSrc->cfFormat, 0);
175                 break;
176         case TYMED_MFPICT:
177                 pMedDest->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(pMedSrc->hMetaFilePict, pFmtSrc->cfFormat, 0);
178                 break;
179         case TYMED_ENHMF:
180                 pMedDest->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(pMedSrc->hEnhMetaFile, pFmtSrc->cfFormat, 0);
181                 break;
182         case TYMED_FILE:
183                 pMedDest->lpszFileName = (LPOLESTR)OleDuplicateData(pMedSrc->lpszFileName, pFmtSrc->cfFormat, 0);
184                 break;
185         case TYMED_ISTREAM:
186                 pMedDest->pstm = pMedSrc->pstm;
187                 pMedSrc->pstm->AddRef();
188                 break;
189         case TYMED_ISTORAGE:
190                 pMedDest->pstg = pMedSrc->pstg;
191                 pMedSrc->pstg->AddRef();
192                 break;
193         case TYMED_NULL:
194         default:
195                 break;
196         }
197         pMedDest->tymed = pMedSrc->tymed;
198         pMedDest->pUnkForRelease = pMedSrc->pUnkForRelease;
199 }
200
201 STDMETHODIMP IDataObjectImpl::EnumFormatEtc(
202    /* [in] */ DWORD dwDirection,
203    /* [out] */ IEnumFORMATETC __RPC_FAR *__RPC_FAR *ppenumFormatEtc)
204
205         if (ppenumFormatEtc == NULL)
206           return E_POINTER;
207
208         *ppenumFormatEtc=NULL;
209         switch (dwDirection)
210         {
211           case DATADIR_GET:
212                  *ppenumFormatEtc = new EnumFormatEtcImpl(_storage);
213
214                  if (!*ppenumFormatEtc)
215                          return E_OUTOFMEMORY;
216
217                  (*ppenumFormatEtc)->AddRef(); 
218                  break;
219           
220           case DATADIR_SET:
221           default:
222                  return E_NOTIMPL;
223                  break;
224         }
225
226    return S_OK;
227 }
228
229 STDMETHODIMP IDataObjectImpl::DAdvise( 
230    /* [in] */ FORMATETC __RPC_FAR *pformatetc,
231    /* [in] */ DWORD advf,
232    /* [unique][in] */ IAdviseSink __RPC_FAR *pAdvSink,
233    /* [out] */ DWORD __RPC_FAR *pdwConnection)
234
235         return OLE_E_ADVISENOTSUPPORTED;
236 }
237
238 STDMETHODIMP IDataObjectImpl::DUnadvise( 
239    /* [in] */ DWORD dwConnection)
240 {
241         return E_NOTIMPL;
242 }
243
244 HRESULT STDMETHODCALLTYPE IDataObjectImpl::EnumDAdvise( 
245    /* [out] */ IEnumSTATDATA __RPC_FAR *__RPC_FAR *ppenumAdvise)
246 {
247         return OLE_E_ADVISENOTSUPPORTED;
248 }
249
250 //////////////////////////////////////////////////////////////////////
251 // IDropSourceImpl Class
252 //////////////////////////////////////////////////////////////////////
253
254 STDMETHODIMP IDropSourceImpl::QueryInterface(/* [in] */ REFIID riid,
255                                                                                  /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
256 {
257    *ppvObject = NULL;
258    if (IID_IUnknown==riid || IID_IDropSource==riid)
259            *ppvObject=this;
260
261         if (*ppvObject != NULL)
262         {
263            ((LPUNKNOWN)*ppvObject)->AddRef();
264                 return S_OK;
265         }
266         return E_NOINTERFACE;
267 }
268
269 STDMETHODIMP_(ULONG) IDropSourceImpl::AddRef()
270 {
271         return ++m_cRefCount;
272 }
273
274 STDMETHODIMP_(ULONG) IDropSourceImpl::Release()
275 {
276    long nTemp = --m_cRefCount;
277
278    assert(nTemp >= 0);
279
280    if (nTemp == 0)
281           delete this;
282
283    return nTemp;
284 }
285
286 STDMETHODIMP IDropSourceImpl::QueryContinueDrag( 
287         /* [in] */ BOOL fEscapePressed,
288         /* [in] */ DWORD grfKeyState)
289 {
290    if (fEscapePressed)
291           return DRAGDROP_S_CANCEL;
292    if (!(grfKeyState & (MK_LBUTTON|MK_RBUTTON)))
293    {
294           m_bDropped = true;
295           return DRAGDROP_S_DROP;
296    }
297
298    return S_OK;
299
300 }
301
302 STDMETHODIMP IDropSourceImpl::GiveFeedback(
303         /* [in] */ DWORD dwEffect)
304 {
305         return DRAGDROP_S_USEDEFAULTCURSORS;
306 }
307
308 //////////////////////////////////////////////////////////////////////
309 // EnumFormatEtcImpl Class
310 //////////////////////////////////////////////////////////////////////
311
312 EnumFormatEtcImpl::EnumFormatEtcImpl(const FormatArray& ArrFE)
313  :      m_cRefCount(0),
314         m_iCur(0)
315 {
316    for(FormatArray::const_iterator it=ArrFE.begin(); it!=ArrFE.end(); ++it)
317                 m_pFmtEtc.push_back(*it);
318 }
319
320 EnumFormatEtcImpl::EnumFormatEtcImpl(const StorageArray& ArrFE)
321  :      m_cRefCount(0),
322         m_iCur(0)
323 {
324    for(StorageArray::const_iterator it=ArrFE.begin(); it!=ArrFE.end(); ++it)
325                 m_pFmtEtc.push_back(*it->_format);
326 }
327
328 STDMETHODIMP EnumFormatEtcImpl::QueryInterface(REFIID refiid, void** ppv)
329 {
330    *ppv = NULL;
331    if (IID_IUnknown==refiid || IID_IEnumFORMATETC==refiid)
332                          *ppv=this;
333
334         if (*ppv != NULL)
335         {
336                 ((LPUNKNOWN)*ppv)->AddRef();
337                 return S_OK;
338         }
339         return E_NOINTERFACE;
340 }
341
342 STDMETHODIMP_(ULONG) EnumFormatEtcImpl::AddRef(void)
343 {
344    return ++m_cRefCount;
345 }
346
347 STDMETHODIMP_(ULONG) EnumFormatEtcImpl::Release(void)
348 {
349    long nTemp = --m_cRefCount;
350
351    assert(nTemp >= 0);
352
353    if (nTemp == 0)
354          delete this;
355
356    return nTemp; 
357 }
358
359 STDMETHODIMP EnumFormatEtcImpl::Next( ULONG celt,LPFORMATETC lpFormatEtc, ULONG* pceltFetched)
360 {
361    if (pceltFetched != NULL)
362            *pceltFetched=0;
363         
364    ULONG cReturn = celt;
365
366    if (celt <= 0 || lpFormatEtc == NULL || m_iCur >= m_pFmtEtc.size())
367           return S_FALSE;
368
369    if (pceltFetched == NULL && celt != 1) // pceltFetched can be NULL only for 1 item request
370           return S_FALSE;
371
372         while (m_iCur < m_pFmtEtc.size() && cReturn > 0)
373         {
374                 *lpFormatEtc++ = m_pFmtEtc[m_iCur++];
375                 --cReturn;
376         }
377         if (pceltFetched != NULL)
378                 *pceltFetched = celt - cReturn;
379
380         return (cReturn == 0) ? S_OK : S_FALSE;
381 }
382
383 STDMETHODIMP EnumFormatEtcImpl::Skip(ULONG celt)
384 {
385         if ((m_iCur + int(celt)) >= m_pFmtEtc.size())
386                 return S_FALSE;
387
388         m_iCur += celt;
389         return S_OK;
390 }
391
392 STDMETHODIMP EnumFormatEtcImpl::Reset(void)
393 {
394    m_iCur = 0;
395    return S_OK;
396 }
397                            
398 STDMETHODIMP EnumFormatEtcImpl::Clone(IEnumFORMATETC** ppCloneEnumFormatEtc)
399 {
400   if (ppCloneEnumFormatEtc == NULL)
401         return E_POINTER;
402           
403   EnumFormatEtcImpl* newEnum = new EnumFormatEtcImpl(m_pFmtEtc);
404
405   if (!newEnum)
406         return E_OUTOFMEMORY;
407
408   newEnum->AddRef();
409   newEnum->m_iCur = m_iCur;
410   *ppCloneEnumFormatEtc = newEnum;
411
412   return S_OK;
413 }
414
415 //////////////////////////////////////////////////////////////////////
416 // IDropTargetImpl Class
417 //////////////////////////////////////////////////////////////////////
418 IDropTargetImpl::IDropTargetImpl(HWND hTargetWnd): 
419         m_hTargetWnd(hTargetWnd),
420         m_cRefCount(0), m_bAllowDrop(false),
421         m_pDropTargetHelper(NULL), m_pSupportedFrmt(NULL)
422
423         assert(m_hTargetWnd != NULL);
424
425         if (FAILED(CoCreateInstance(CLSID_DragDropHelper,NULL,CLSCTX_INPROC_SERVER,
426                                          IID_IDropTargetHelper,(LPVOID*)&m_pDropTargetHelper)))
427                 m_pDropTargetHelper = NULL;
428 }
429
430 IDropTargetImpl::~IDropTargetImpl()
431 {
432         if (m_pDropTargetHelper != NULL)
433         {
434                 m_pDropTargetHelper->Release();
435                 m_pDropTargetHelper = NULL;
436         }
437 }
438
439 HRESULT STDMETHODCALLTYPE IDropTargetImpl::QueryInterface( /* [in] */ REFIID riid,
440                                                 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
441 {
442    *ppvObject = NULL;
443    if (IID_IUnknown==riid || IID_IDropTarget==riid)
444                          *ppvObject=this;
445
446         if (*ppvObject != NULL)
447         {
448                 ((LPUNKNOWN)*ppvObject)->AddRef();
449                 return S_OK;
450         }
451         return E_NOINTERFACE;
452 }
453
454 ULONG STDMETHODCALLTYPE IDropTargetImpl::Release()
455 {
456    long nTemp = --m_cRefCount;
457
458    assert(nTemp >= 0);
459
460    if (nTemp == 0)
461           delete this;
462
463    return nTemp;
464 }
465
466 bool IDropTargetImpl::QueryDrop(DWORD grfKeyState, LPDWORD pdwEffect)
467 {  
468         DWORD dwOKEffects = *pdwEffect; 
469
470         if (!m_bAllowDrop)
471         {
472            *pdwEffect = DROPEFFECT_NONE;
473            return false;
474         }
475         //CTRL+SHIFT  -- DROPEFFECT_LINK
476         //CTRL            -- DROPEFFECT_COPY
477         //SHIFT           -- DROPEFFECT_MOVE
478         //no modifier -- DROPEFFECT_MOVE or whatever is allowed by src
479         *pdwEffect = (grfKeyState & MK_CONTROL) ?
480                                  ( (grfKeyState & MK_SHIFT) ? DROPEFFECT_LINK : DROPEFFECT_COPY ):
481                                  ( (grfKeyState & MK_SHIFT) ? DROPEFFECT_MOVE : DROPEFFECT_NONE );
482         if (*pdwEffect == 0) 
483         {
484            // No modifier keys used by user while dragging. 
485            if (DROPEFFECT_COPY & dwOKEffects)
486                   *pdwEffect = DROPEFFECT_COPY;
487            else if (DROPEFFECT_MOVE & dwOKEffects)
488                   *pdwEffect = DROPEFFECT_MOVE; 
489            else if (DROPEFFECT_LINK & dwOKEffects)
490                   *pdwEffect = DROPEFFECT_LINK; 
491            else 
492            {
493                   *pdwEffect = DROPEFFECT_NONE;
494            }
495         } 
496         else
497         {
498            // Check if the drag source application allows the drop effect desired by user.
499            // The drag source specifies this in DoDragDrop
500            if (!(*pdwEffect & dwOKEffects))
501                   *pdwEffect = DROPEFFECT_NONE;
502         }  
503
504         return (DROPEFFECT_NONE == *pdwEffect)?false:true;
505 }       
506
507 HRESULT STDMETHODCALLTYPE IDropTargetImpl::DragEnter(
508         /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
509         /* [in] */ DWORD grfKeyState,
510         /* [in] */ POINTL pt,
511         /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
512 {
513         if (pDataObj == NULL)
514                 return E_INVALIDARG;
515
516         if (m_pDropTargetHelper)
517                 m_pDropTargetHelper->DragEnter(m_hTargetWnd, pDataObj, (LPPOINT)&pt, *pdwEffect);
518         //IEnumFORMATETC* pEnum;
519         //pDataObj->EnumFormatEtc(DATADIR_GET,&pEnum);
520         //FORMATETC ftm;
521         //for()
522         //pEnum->Next(1,&ftm,0);
523         //pEnum->Release();
524         m_pSupportedFrmt = NULL;
525
526         for(FormatArray::iterator it=m_formatetc.begin(); it!=m_formatetc.end(); ++it)
527         {
528                 m_bAllowDrop = (pDataObj->QueryGetData(&*it) == S_OK)? true: false;
529
530                 if (m_bAllowDrop)
531                 {
532                         m_pSupportedFrmt = &*it;
533                         break;
534                 }
535         }
536
537         QueryDrop(grfKeyState, pdwEffect);
538         return S_OK;
539 }
540
541 HRESULT STDMETHODCALLTYPE IDropTargetImpl::DragOver( 
542                 /* [in] */ DWORD grfKeyState,
543                 /* [in] */ POINTL pt,
544                 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
545 {
546         if (m_pDropTargetHelper)
547                 m_pDropTargetHelper->DragOver((LPPOINT)&pt, *pdwEffect);
548         QueryDrop(grfKeyState, pdwEffect);
549         return S_OK;
550 }
551
552 HRESULT STDMETHODCALLTYPE IDropTargetImpl::DragLeave()
553 {
554         if (m_pDropTargetHelper)
555                 m_pDropTargetHelper->DragLeave();
556         
557         m_bAllowDrop = false;
558         m_pSupportedFrmt = NULL;
559         return S_OK;
560 }
561
562 HRESULT STDMETHODCALLTYPE IDropTargetImpl::Drop(
563         /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
564         /* [in] */ DWORD grfKeyState, /* [in] */ POINTL pt, 
565         /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
566 {
567         if (pDataObj == NULL)
568                 return E_INVALIDARG;    
569
570         if (m_pDropTargetHelper)
571                 m_pDropTargetHelper->Drop(pDataObj, (LPPOINT)&pt, *pdwEffect);
572
573         if (QueryDrop(grfKeyState, pdwEffect))
574         {
575                 if (m_bAllowDrop && m_pSupportedFrmt != NULL)
576                 {
577                         STGMEDIUM medium;
578
579                         if (pDataObj->GetData(m_pSupportedFrmt, &medium) == S_OK)
580                         {
581                                 if (OnDrop(m_pSupportedFrmt, medium, pdwEffect)) //does derive class wants us to free medium?
582                                         ReleaseStgMedium(&medium);
583                         }
584                 }
585         }
586
587         m_bAllowDrop = false;
588         *pdwEffect = DROPEFFECT_NONE;
589         m_pSupportedFrmt = NULL;
590
591         return S_OK;
592 }