Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

562 lines
14 KiB

  1. #include "droptgt.h"
  2. #define TF_DRAGDROP TF_BAND
  3. #define MAX_DROPTARGETS 3
  4. class CDropTargetWrap : public IDropTarget
  5. {
  6. public:
  7. // *** IUnknown ***
  8. virtual STDMETHODIMP_(ULONG) AddRef(void);
  9. virtual STDMETHODIMP_(ULONG) Release(void);
  10. virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  11. // *** IDropTarget methods ***
  12. virtual STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  13. virtual STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  14. virtual STDMETHODIMP DragLeave(void);
  15. virtual STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  16. CDropTargetWrap(IDropTarget** ppdtg, HWND hwnd);
  17. protected:
  18. ~CDropTargetWrap();
  19. private:
  20. int _cRef;
  21. int _count;
  22. IDropTarget* _rgpdt[MAX_DROPTARGETS];
  23. DWORD _rgdwEffect[MAX_DROPTARGETS];
  24. HWND _hwnd;
  25. };
  26. CDropTargetWrap::CDropTargetWrap(IDropTarget** ppdt, HWND hwnd)
  27. : _hwnd(hwnd)
  28. {
  29. _cRef = 1;
  30. for (int i = 0; i < MAX_DROPTARGETS; i++, ppdt++) {
  31. if (*ppdt) {
  32. _rgpdt[_count] = *ppdt;
  33. _rgpdt[_count]->AddRef();
  34. _count++;
  35. }
  36. }
  37. }
  38. CDropTargetWrap::~CDropTargetWrap()
  39. {
  40. for (int i = 0 ; i < _count ; i++)
  41. {
  42. _rgpdt[i]->Release();
  43. }
  44. }
  45. IDropTarget* DropTargetWrap_CreateInstance(IDropTarget* pdtPrimary, IDropTarget* pdtSecondary, HWND hwnd, IDropTarget* pdt3)
  46. {
  47. // no point in wrapping nothing...
  48. if (pdtPrimary || pdtSecondary || pdt3)
  49. {
  50. IDropTarget* pdt[MAX_DROPTARGETS] = { pdtPrimary, pdtSecondary, pdt3 };
  51. CDropTargetWrap* pdtw = new CDropTargetWrap(pdt, hwnd);
  52. if (pdtw)
  53. {
  54. return SAFECAST(pdtw, IDropTarget*);
  55. }
  56. }
  57. return NULL;
  58. }
  59. HRESULT CDropTargetWrap::QueryInterface(REFIID riid, void **ppvObj)
  60. {
  61. static const QITAB qit[] = {
  62. QITABENT(CDropTargetWrap, IDropTarget),
  63. { 0 },
  64. };
  65. return QISearch(this, qit, riid, ppvObj);
  66. }
  67. ULONG CDropTargetWrap::AddRef(void)
  68. {
  69. _cRef++;
  70. return _cRef;
  71. }
  72. ULONG CDropTargetWrap::Release(void)
  73. {
  74. _cRef--;
  75. if (_cRef > 0)
  76. return _cRef;
  77. delete this;
  78. return 0;
  79. }
  80. /*----------------------------------------------------------
  81. Purpose: IDropTarget::DragEnter method
  82. The *pdwEffect that is returned is the first valid value
  83. of all the drop targets' returned effects.
  84. */
  85. HRESULT CDropTargetWrap::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  86. {
  87. DWORD dwEffectOut = DROPEFFECT_NONE;
  88. for (int i = 0 ; i < _count ; i++)
  89. {
  90. _rgdwEffect[i] = *pdwEffect;
  91. if (SUCCEEDED(_rgpdt[i]->DragEnter(pdtobj, grfKeyState, ptl, &_rgdwEffect[i])))
  92. {
  93. if (dwEffectOut == DROPEFFECT_NONE)
  94. {
  95. dwEffectOut = _rgdwEffect[i];
  96. }
  97. }
  98. else
  99. {
  100. _rgdwEffect[i] = DROPEFFECT_NONE;
  101. }
  102. }
  103. *pdwEffect = dwEffectOut;
  104. return(S_OK);
  105. }
  106. /*----------------------------------------------------------
  107. Purpose: IDropTarget::DragOver method
  108. */
  109. HRESULT CDropTargetWrap::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  110. {
  111. DWORD dwEffectOut = DROPEFFECT_NONE;
  112. for (int i = 0 ; i < _count ; i++)
  113. {
  114. _rgdwEffect[i] = *pdwEffect;
  115. if (SUCCEEDED(_rgpdt[i]->DragOver(grfKeyState, ptl, &_rgdwEffect[i])))
  116. {
  117. if (dwEffectOut == DROPEFFECT_NONE)
  118. dwEffectOut = _rgdwEffect[i];
  119. }
  120. else
  121. {
  122. _rgdwEffect[i] = DROPEFFECT_NONE;
  123. }
  124. }
  125. *pdwEffect = dwEffectOut;
  126. return(S_OK);
  127. }
  128. /*----------------------------------------------------------
  129. Purpose: IDropTarget::DragLeave method
  130. */
  131. HRESULT CDropTargetWrap::DragLeave(void)
  132. {
  133. for (int i = 0 ; i < _count ; i++)
  134. {
  135. _rgpdt[i]->DragLeave();
  136. }
  137. return(S_OK);
  138. }
  139. /*----------------------------------------------------------
  140. Purpose: IDropTarget::Drop method
  141. */
  142. HRESULT CDropTargetWrap::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  143. {
  144. DWORD dwEffectOut = DROPEFFECT_NONE;
  145. int i;
  146. BOOL fDropTried = FALSE;
  147. for (i = 0 ; (DROPEFFECT_NONE == dwEffectOut) && i < _count ; i++)
  148. {
  149. if ((_rgdwEffect[i] && *pdwEffect) && !fDropTried)
  150. {
  151. dwEffectOut = *pdwEffect;
  152. _rgpdt[i]->Drop(pdtobj, grfKeyState, pt, &dwEffectOut);
  153. fDropTried = TRUE;
  154. }
  155. else
  156. {
  157. _rgpdt[i]->DragLeave();
  158. }
  159. }
  160. *pdwEffect = dwEffectOut;
  161. return(S_OK);
  162. }
  163. //=============================================================================
  164. // CDelegateDropTarget
  165. //
  166. // This class implements IDropTarget given an IDelegateDropTargetCB interface.
  167. // It handles all hit testing, caching, and scrolling for you.
  168. //
  169. //=============================================================================
  170. #undef CDropTargetWrap
  171. CDelegateDropTarget::CDelegateDropTarget()
  172. {
  173. TraceMsg(TF_SHDLIFE, "ctor CDelegateDropTarget %x", this);
  174. }
  175. CDelegateDropTarget::~CDelegateDropTarget()
  176. {
  177. TraceMsg(TF_SHDLIFE, "dtor CDelegateDropTarget %x", this);
  178. ASSERT(!_pDataObj);
  179. ATOMICRELEASE(_pDataObj);
  180. ASSERT(!_pdtCur);
  181. ATOMICRELEASE(_pdtCur);
  182. }
  183. HRESULT CDelegateDropTarget::Init()
  184. {
  185. HRESULT hres = GetWindowsDDT(&_hwndLock, &_hwndScroll);
  186. // We lock _hwndLock and do scrolling against _hwndScroll.
  187. // These can be different hwnds, but certain restrictions apply:
  188. if (_hwndLock != _hwndScroll)
  189. {
  190. BOOL fValid = IsChild(_hwndLock, _hwndScroll);
  191. if (!fValid)
  192. {
  193. TraceMsg(TF_DRAGDROP, "ctor CDelegateDropTarget: invalid windows %x and %x!", _hwndLock, _hwndScroll);
  194. _hwndLock = _hwndScroll = NULL;
  195. }
  196. }
  197. return hres;
  198. }
  199. void CDelegateDropTarget::_ReleaseCurrentDropTarget()
  200. {
  201. if (_pdtCur)
  202. {
  203. _pdtCur->DragLeave();
  204. ATOMICRELEASE(_pdtCur);
  205. }
  206. }
  207. /*----------------------------------------------------------
  208. Purpose: IDropTarget::DragEnter method
  209. */
  210. HRESULT CDelegateDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
  211. {
  212. // We can be re-entered due to ui on thread
  213. if (_pDataObj != NULL)
  214. {
  215. TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragEnter called a second time!");
  216. *pdwEffect = DROPEFFECT_NONE;
  217. return S_OK;
  218. }
  219. TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragEnter with *pdwEffect=%x", *pdwEffect);
  220. ASSERT(!_pDataObj);
  221. _pDataObj = pdtobj;
  222. _pDataObj->AddRef();
  223. // cache state
  224. //
  225. // wait until first DragOver to get valid info
  226. //
  227. _fPrime = FALSE;
  228. _dwEffectOut = DROPEFFECT_NONE;
  229. // set up auto-scroll info
  230. //
  231. ASSERT(pdtobj);
  232. _DragEnter(_hwndLock, ptl, pdtobj);
  233. DAD_InitScrollData(&_asd);
  234. _ptLast.x = _ptLast.y = 0x7fffffff; // put bogus value to force redraw
  235. HitTestDDT(HTDDT_ENTER, NULL, NULL, NULL);
  236. return S_OK;
  237. }
  238. /*----------------------------------------------------------
  239. Purpose: IDropTarget::DragOver method
  240. */
  241. HRESULT CDelegateDropTarget::DragOver(DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
  242. {
  243. HRESULT hres = S_OK;
  244. DWORD_PTR itemNew;
  245. POINT pt;
  246. DWORD dwEffectScroll = 0;
  247. DWORD dwEffectOut = 0;
  248. BOOL fSameImage = FALSE;
  249. DWORD dwCustDropEffect = 0;
  250. if (_pDataObj == NULL)
  251. {
  252. ASSERT(0); // DragEnter should be called before.
  253. return E_FAIL;
  254. }
  255. // convert to window coords
  256. pt.x = ptl.x;
  257. pt.y = ptl.y;
  258. ScreenToClient(_hwndScroll, &pt);
  259. if (DAD_AutoScroll(_hwndScroll, &_asd, &pt))
  260. dwEffectScroll = DROPEFFECT_SCROLL;
  261. //
  262. // If we are dragging over on a different item, get its IDropTarget
  263. // interface or adjust itemNew to -1.
  264. //
  265. if (SUCCEEDED(HitTestDDT(HTDDT_OVER, &pt, &itemNew, &dwCustDropEffect)) &&
  266. (itemNew != _itemOver || !_fPrime))
  267. {
  268. _fPrime = TRUE;
  269. _ReleaseCurrentDropTarget();
  270. _itemOver = itemNew;
  271. GetObjectDDT(_itemOver, IID_IDropTarget, (LPVOID*)&_pdtCur);
  272. if (_pdtCur)
  273. {
  274. // There's an IDropTarget for this hit, use it
  275. dwEffectOut = *pdwEffect;
  276. hres = _pdtCur->DragEnter(_pDataObj, grfKeyState, ptl, &dwEffectOut);
  277. if (FAILED(hres))
  278. dwEffectOut = DROPEFFECT_NONE;
  279. }
  280. else
  281. {
  282. // No IDropTarget, no effect
  283. dwEffectOut = DROPEFFECT_NONE;
  284. }
  285. }
  286. else
  287. {
  288. //
  289. // No change in the selection. We assume that *pdwEffect stays
  290. // the same during the same drag-loop as long as the key state doesn't change.
  291. //
  292. if ((_grfKeyState != grfKeyState) && _pdtCur)
  293. {
  294. dwEffectOut = *pdwEffect;
  295. hres = _pdtCur->DragOver(grfKeyState, ptl, &dwEffectOut);
  296. TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragOver DragOver()d id:%d dwEffect:%4x hres:%d", _itemOver, dwEffectOut, hres);
  297. }
  298. else
  299. {
  300. // Same item and same key state. Use the previous dwEffectOut.
  301. dwEffectOut = _dwEffectOut;
  302. fSameImage = TRUE;
  303. }
  304. }
  305. _grfKeyState = grfKeyState; // store these for the next Drop
  306. _dwEffectOut = dwEffectOut; // and DragOver
  307. // Is the Custdrop effect valid ?
  308. if (dwCustDropEffect != DROPEFFECT_NONE)
  309. {
  310. //Yes then set the effect to Custdrop effect along with scroll effect
  311. *pdwEffect = dwCustDropEffect | dwEffectScroll;
  312. }
  313. else
  314. {
  315. //No , set the effect to dwEffectOut along with scroll effect
  316. *pdwEffect = dwEffectOut | dwEffectScroll;
  317. }
  318. TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragOver (*pdwEffect=%x)", *pdwEffect);
  319. if (!(fSameImage && pt.x==_ptLast.x && pt.y==_ptLast.y))
  320. {
  321. _DragMove(_hwndLock, ptl);
  322. _ptLast.x = ptl.x;
  323. _ptLast.y = ptl.y;
  324. }
  325. return hres;
  326. }
  327. /*----------------------------------------------------------
  328. Purpose: IDropTarget::DragLeave method
  329. */
  330. HRESULT CDelegateDropTarget::DragLeave()
  331. {
  332. HitTestDDT(HTDDT_LEAVE, NULL, NULL, NULL);
  333. _ReleaseCurrentDropTarget();
  334. TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragLeave");
  335. ATOMICRELEASE(_pDataObj);
  336. DAD_DragLeave();
  337. return S_OK;
  338. }
  339. /*----------------------------------------------------------
  340. Purpose: IDropTarget::Drop method
  341. */
  342. HRESULT CDelegateDropTarget::Drop(IDataObject *pdtobj,
  343. DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
  344. {
  345. HRESULT hres = S_OK;
  346. BOOL bDropHandled = FALSE;
  347. TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::Drop (*pdwEffect=%x)", *pdwEffect);
  348. //
  349. // According to AlexGo (OLE), this is by-design. We should make it sure
  350. // that we use pdtobj instead of pdtobj.
  351. //
  352. //ASSERT(pdtobj == _pDataObj);
  353. pdtobj->AddRef();
  354. _pDataObj->Release();
  355. _pDataObj = pdtobj;
  356. //
  357. // Note that we don't use the drop position intentionally,
  358. // so that it matches to the last destination feedback.
  359. //
  360. if (_pdtCur)
  361. {
  362. // use this local because if _pdtCur::Drop does a UnlockWindow
  363. // then hits an error and needs to put up a dialog,
  364. // we could get re-entered
  365. IDropTarget *pdtCur = _pdtCur;
  366. _pdtCur = NULL;
  367. // HACK ALERT!!!!
  368. //
  369. // If we don't call LVUtil_DragEnd here, we'll be able to leave
  370. // dragged icons visible when the menu is displayed. However, because
  371. // we are calling IDropTarget::Drop() which may create some modeless
  372. // dialog box or something, we can not ensure the locked state of
  373. // the list view -- LockWindowUpdate() can lock only one window at
  374. // a time. Therefore, we skip this call only if the _pdtCur
  375. // is a subclass of CIDLDropTarget, assuming its Drop calls
  376. // CDefView::DragEnd (or CIDLDropTarget_DragDropMenu) appropriately.
  377. //
  378. #if 0 // later
  379. if (!IsIDLDropTarget(pdtCur))
  380. #endif
  381. {
  382. //
  383. // This will hide the dragged image.
  384. //
  385. DAD_DragLeave();
  386. //
  387. // We need to reset the drag image list so that the user
  388. // can start another drag&drop while we are in this
  389. // Drop() member function call.
  390. //
  391. // NOTE: we don't have to worry about the DAD_DragLeave
  392. // (called from during the DragLeave call at the end of
  393. // this function) cancelling the potential above-mentioned
  394. // drag&drop loop. If such a beast is going on, it should
  395. // complete before pdtCur->Drop returns.
  396. //
  397. DAD_SetDragImage(NULL, NULL);
  398. }
  399. if (S_FALSE != OnDropDDT(pdtCur, _pDataObj, &grfKeyState, pt, pdwEffect))
  400. pdtCur->Drop(_pDataObj, grfKeyState, pt, pdwEffect);
  401. else
  402. pdtCur->DragLeave(); // should be okay even if OnDrop did this already
  403. pdtCur->Release();
  404. }
  405. else
  406. {
  407. //
  408. // We come here if Drop is called without DragMove (with DragEnter).
  409. //
  410. *pdwEffect = DROPEFFECT_NONE;
  411. }
  412. //
  413. // Clean up everything (OLE won't call DragLeave after Drop).
  414. //
  415. DragLeave();
  416. return hres;
  417. }
  418. // ******************************************************************
  419. // dummy drop target to only call DAD_DragEnterEx() on DragEnter();
  420. // ******************************************************************
  421. HRESULT CDropDummy::QueryInterface(REFIID riid, void **ppvObj)
  422. {
  423. static const QITAB qit[] = {
  424. QITABENT(CDropDummy, IDropTarget),
  425. { 0 },
  426. };
  427. return QISearch(this, qit, riid, ppvObj);
  428. }
  429. ULONG CDropDummy::AddRef(void)
  430. {
  431. _cRef++;
  432. return _cRef;
  433. }
  434. ULONG CDropDummy::Release(void)
  435. {
  436. _cRef--;
  437. if (_cRef > 0)
  438. return _cRef;
  439. delete this;
  440. return 0;
  441. }
  442. /*----------------------------------------------------------
  443. Purpose: IDropTarget::DragEnter method
  444. simply call DAD_DragEnterEx2() to get custom drag cursor
  445. drawing.
  446. */
  447. HRESULT CDropDummy::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  448. {
  449. ASSERT(pdtobj);
  450. _DragEnter(_hwndLock, ptl, pdtobj);
  451. *pdwEffect = DROPEFFECT_NONE;
  452. return(S_OK);
  453. }
  454. HRESULT CDropDummy::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  455. {
  456. _DragMove(_hwndLock, ptl);
  457. *pdwEffect = DROPEFFECT_NONE;
  458. return S_OK;
  459. }