Leaked source code of windows server 2003
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.

441 lines
9.9 KiB

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