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.

454 lines
9.8 KiB

  1. #include "ctlspriv.h"
  2. #include "olestuff.h"
  3. //------------------------------------------------------------------------------
  4. STDAPI GetItemObject(CONTROLINFO *pci, UINT uMsg, const IID *piid, LPNMOBJECTNOTIFY pnon)
  5. {
  6. pnon->piid = piid;
  7. pnon->pObject = NULL;
  8. pnon->hResult = E_NOINTERFACE;
  9. CCSendNotify(pci, uMsg, &pnon->hdr);
  10. ASSERT(SUCCEEDED(pnon->hResult) ? (pnon->pObject != NULL) : (pnon->pObject == NULL));
  11. return pnon->hResult;
  12. }
  13. //------------------------------------------------------------------------------
  14. class CDragProxy : public IDropTarget
  15. {
  16. public:
  17. // IUnknown
  18. STDMETHODIMP QueryInterface(REFIID, void **);
  19. STDMETHODIMP_(ULONG) AddRef();
  20. STDMETHODIMP_(ULONG) Release();
  21. // IDropTarget
  22. STDMETHODIMP DragEnter(IDataObject *, DWORD, POINTL, DWORD *);
  23. STDMETHODIMP DragOver(DWORD, POINTL, DWORD *);
  24. STDMETHODIMP DragLeave();
  25. STDMETHODIMP Drop(IDataObject *, DWORD, POINTL, DWORD *);
  26. CDragProxy(HWND hwnd, PFNDRAGCB pfn);
  27. BOOL Register();
  28. void RevokeAndFreeCB();
  29. private:
  30. ~CDragProxy();
  31. int _cRef; // object reference count
  32. HWND _hwnd; // window that owns us
  33. PFNDRAGCB _pfnCallback; // callback for that window
  34. IDataObject *_pdtobj; // data object being dragged
  35. IDropTarget *_pdtgtItem; // drop target of item under mouse
  36. int _idItem; // id of item under mouse
  37. DWORD _dwFlags;
  38. int _idDefault; // id to use when outside a drag etc
  39. DWORD _dwEffectItem; // DROPEFFECT returned for item under mouse
  40. DWORD _fKeysLast; // key flags from last DragOver
  41. POINTL _ptLast; // location of last DragOver
  42. DWORD _dwEffectLast; // effect available from last DragOver
  43. HMODULE _hmodOLE; // OLE32 ref, also indicates we did a Register()
  44. void SetTargetItem(int id, DWORD dwFlags);
  45. void SetDropTarget(IDropTarget *pdt);
  46. void UpdateSelection(DWORD dwEffect);
  47. LRESULT CallCB(UINT code, WPARAM wp, LPARAM lp);
  48. };
  49. //------------------------------------------------------------------------------
  50. STDAPI_(HDRAGPROXY) CreateDragProxy(HWND hwnd, PFNDRAGCB pfn, BOOL bRegister)
  51. {
  52. CDragProxy *pdp = new CDragProxy(hwnd, pfn);
  53. //
  54. // register as needed
  55. //
  56. if (pdp && bRegister && !pdp->Register())
  57. {
  58. pdp->Release();
  59. pdp = NULL;
  60. }
  61. return (HDRAGPROXY)pdp;
  62. }
  63. STDAPI_(void) DestroyDragProxy(HDRAGPROXY hdp)
  64. {
  65. if (hdp)
  66. {
  67. ((CDragProxy *)hdp)->RevokeAndFreeCB();
  68. ((CDragProxy *)hdp)->Release();
  69. }
  70. }
  71. STDAPI GetDragProxyTarget(HDRAGPROXY hdp, IDropTarget **ppdtgt)
  72. {
  73. if (hdp)
  74. {
  75. *ppdtgt = SAFECAST((CDragProxy *)hdp, IDropTarget *);
  76. ((CDragProxy *)hdp)->AddRef();
  77. return NOERROR;
  78. }
  79. *ppdtgt = NULL;
  80. return E_FAIL;
  81. }
  82. //------------------------------------------------------------------------------
  83. CDragProxy::CDragProxy(HWND hwnd, PFNDRAGCB pfn)
  84. : _hwnd(hwnd), _pfnCallback(pfn),
  85. _cRef(1),
  86. _hmodOLE(NULL),
  87. _pdtobj(NULL),
  88. _pdtgtItem(NULL),
  89. _dwEffectItem(DROPEFFECT_NONE)
  90. {
  91. _idDefault = _idItem = (int)CallCB(DPX_DRAGHIT, 0, 0);
  92. }
  93. CDragProxy::~CDragProxy()
  94. {
  95. DragLeave();
  96. }
  97. HRESULT CDragProxy::QueryInterface(REFIID iid, void **ppv)
  98. {
  99. if (IsEqualIID(iid, IID_IDropTarget) || IsEqualIID(iid, IID_IUnknown))
  100. {
  101. *ppv = SAFECAST(this, IDropTarget *);
  102. }
  103. else
  104. {
  105. *ppv = NULL;
  106. return E_NOINTERFACE;
  107. }
  108. _cRef++;
  109. return NOERROR;
  110. }
  111. ULONG CDragProxy::AddRef()
  112. {
  113. return ++_cRef;
  114. }
  115. ULONG CDragProxy::Release()
  116. {
  117. if (--_cRef)
  118. return _cRef;
  119. delete this;
  120. return 0;
  121. }
  122. HRESULT CDragProxy::DragEnter(IDataObject *pdo, DWORD fKeys, POINTL pt, DWORD *pdwEffect)
  123. {
  124. //
  125. // some sanity
  126. //
  127. ASSERT(!_pdtgtItem);
  128. ASSERT(!_pdtobj);
  129. if (!pdo)
  130. {
  131. ASSERT(FALSE);
  132. return E_INVALIDARG;
  133. }
  134. //
  135. // make sure our callback will allow us to do d/d now
  136. //
  137. if (!CallCB(DPX_ENTER, 0, 0))
  138. return E_FAIL;
  139. //
  140. // save away the data object
  141. //
  142. pdo->AddRef();
  143. _pdtobj = pdo;
  144. //
  145. // and process this like a DragOver
  146. //
  147. DragOver(fKeys, pt, pdwEffect);
  148. //
  149. // always succeed DragEnter
  150. //
  151. return NOERROR;
  152. }
  153. HRESULT CDragProxy::DragLeave()
  154. {
  155. //
  156. // release any drop target that we are holding
  157. //
  158. SetDropTarget(NULL);
  159. _idItem = _idDefault;
  160. //
  161. // if we had a data object then we were actually dragging
  162. //
  163. if (_pdtobj)
  164. {
  165. CallCB(DPX_LEAVE, 0, 0);
  166. IDataObject* p = _pdtobj;
  167. _pdtobj = NULL;
  168. p->Release();
  169. }
  170. //
  171. // all done
  172. //
  173. return NOERROR;
  174. }
  175. HRESULT CDragProxy::DragOver(DWORD fKeys, POINTL pt, DWORD *pdwEffect)
  176. {
  177. DWORD dwFlags = 0;
  178. HRESULT hres;
  179. int id;
  180. ASSERT(_pdtobj);
  181. //
  182. // save the current drag state
  183. //
  184. _fKeysLast = fKeys;
  185. _ptLast = pt;
  186. _dwEffectLast = *pdwEffect;
  187. //
  188. // make sure we have the correct drop target for this location
  189. //
  190. id = (int)CallCB(DPX_DRAGHIT, (WPARAM)&dwFlags, (LPARAM)&pt);
  191. SetTargetItem(id, dwFlags);
  192. //
  193. // do we have a target to drop on?
  194. //
  195. if (_pdtgtItem)
  196. {
  197. //
  198. // forward the DragOver along to the item's drop target (if any)
  199. //
  200. hres = _pdtgtItem->DragOver(fKeys, pt, pdwEffect);
  201. }
  202. else
  203. {
  204. //
  205. // can't drop here
  206. //
  207. *pdwEffect = DROPEFFECT_NONE;
  208. hres = NOERROR;
  209. }
  210. //
  211. // and update our selection state accordingly
  212. //
  213. UpdateSelection(*pdwEffect);
  214. return hres;
  215. }
  216. HRESULT CDragProxy::Drop(IDataObject *pdo, DWORD fKeys, POINTL pt, DWORD *pdwEffect)
  217. {
  218. HRESULT hres;
  219. AddRef();
  220. //
  221. // do we have a target to drop on?
  222. //
  223. if (_pdtgtItem)
  224. {
  225. // From a comment in browseui, there's apparently a chance to put up UI
  226. // which could cause us to get re-entered. Hard to believe, but see if
  227. // this fixes the fault:
  228. //
  229. IDropTarget * pdtCur = _pdtgtItem;
  230. _pdtgtItem = NULL;
  231. //
  232. // do the drop
  233. //
  234. hres = pdtCur->Drop(pdo, fKeys, pt, pdwEffect);
  235. //
  236. // we call our DragLeave below but we don't want the item's to be
  237. // called (since it already saw the Drop) so we release right away
  238. //
  239. pdtCur->Release();
  240. }
  241. else
  242. {
  243. //
  244. // can't drop here
  245. //
  246. *pdwEffect = DROPEFFECT_NONE;
  247. hres = NOERROR;
  248. }
  249. //
  250. // now clean up
  251. //
  252. DragLeave();
  253. Release();
  254. return hres;
  255. }
  256. void CDragProxy::SetTargetItem(int id, DWORD dwFlags)
  257. {
  258. //
  259. // anything to do?
  260. //
  261. if (id == _idItem && dwFlags == _dwFlags)
  262. return;
  263. //
  264. // deselect the old item (if any)
  265. //
  266. // the GETOBJECT below could take a long time and we don't want a
  267. // lingering highlight on the object we are leaving
  268. //
  269. UpdateSelection(DROPEFFECT_NONE);
  270. //
  271. // get a drop target for the new item
  272. //
  273. _idItem = id;
  274. _dwFlags = dwFlags;
  275. NMOBJECTNOTIFY non;
  276. non.iItem = id;
  277. non.dwFlags = dwFlags;
  278. if (!_pdtobj || FAILED((HRESULT)CallCB(DPX_GETOBJECT, 0, (LPARAM)&non)))
  279. non.pObject = NULL;
  280. //
  281. // use this drop target (if any)
  282. //
  283. SetDropTarget((IDropTarget*)non.pObject);
  284. //
  285. // release our ref from the GETOBJECT above
  286. //
  287. if (non.pObject)
  288. ((IDropTarget*)non.pObject)->Release();
  289. }
  290. void CDragProxy::SetDropTarget(IDropTarget *pdt)
  291. {
  292. //
  293. // NOTE: we intentionally skip the test for drop-target equality here
  294. // this allows controls owners to share a target among multiple items
  295. // while retaining the proper leave/enter sequence...
  296. //
  297. // BOGUS: we should actually compare here when the Internet Toolbar gets
  298. // fixed (see comment in CDragProxy::SetTargetItem). anybody who wants
  299. // to share a target like this should just do the right hit-testing in
  300. // their DragOver implementation
  301. //
  302. //
  303. // make sure nothing is selected
  304. //
  305. UpdateSelection(DROPEFFECT_NONE);
  306. //
  307. // leave/release the old item
  308. //
  309. if (_pdtgtItem)
  310. {
  311. _pdtgtItem->DragLeave();
  312. _pdtgtItem->Release();
  313. }
  314. //
  315. // store the new item
  316. //
  317. _pdtgtItem = pdt;
  318. //
  319. // addref/enter the new item
  320. //
  321. if (_pdtgtItem)
  322. {
  323. ASSERT(_pdtobj); // must have a data object by now
  324. _pdtgtItem->AddRef();
  325. DWORD dwEffect = _dwEffectLast;
  326. if (FAILED(_pdtgtItem->DragEnter(_pdtobj, _fKeysLast, _ptLast, &dwEffect)))
  327. dwEffect = DROPEFFECT_NONE;
  328. //
  329. // update the selection
  330. //
  331. UpdateSelection(dwEffect);
  332. }
  333. }
  334. void CDragProxy::UpdateSelection(DWORD dwEffect)
  335. {
  336. //
  337. // anything to do?
  338. //
  339. if (dwEffect == _dwEffectItem)
  340. return;
  341. //
  342. // update the flags and tell the callback they changed
  343. //
  344. _dwEffectItem = dwEffect;
  345. CallCB(DPX_SELECT, (WPARAM)_idItem, (LPARAM)dwEffect);
  346. }
  347. LRESULT CDragProxy::CallCB(UINT code, WPARAM wp, LPARAM lp)
  348. {
  349. return _pfnCallback ? _pfnCallback(_hwnd, code, wp, lp) : (LRESULT)-1;
  350. }
  351. BOOL CDragProxy::Register()
  352. {
  353. _hmodOLE = PrivLoadOleLibrary();
  354. if (_hmodOLE)
  355. {
  356. if (SUCCEEDED(PrivCoInitialize(_hmodOLE)))
  357. {
  358. if (SUCCEEDED(PrivRegisterDragDrop(_hmodOLE, _hwnd, this)))
  359. return TRUE;
  360. PrivCoUninitialize(_hmodOLE);
  361. }
  362. PrivFreeOleLibrary(_hmodOLE);
  363. _hmodOLE = NULL;
  364. }
  365. return FALSE;
  366. }
  367. void CDragProxy::RevokeAndFreeCB()
  368. {
  369. if (_hmodOLE)
  370. {
  371. PrivRevokeDragDrop(_hmodOLE, _hwnd);
  372. PrivCoUninitialize(_hmodOLE);
  373. PrivFreeOleLibrary(_hmodOLE);
  374. }
  375. _pfnCallback = NULL;
  376. }