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.

1029 lines
24 KiB

  1. /*
  2. * DRAGDRP.C
  3. *
  4. * Purpose:
  5. * Implementation of Richedit's OLE drag drop objects (namely,
  6. * the drop target and drop source objects)
  7. *
  8. * Author:
  9. * alexgo (4/24/95)
  10. * KeithCu (12/11/99) Simplified by going to an XOR model for the caret
  11. * (just like the regular caret) and made work with various textflows.
  12. *
  13. * Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
  14. */
  15. #include "_common.h"
  16. #include "_edit.h"
  17. #include "_dragdrp.h"
  18. #include "_disp.h"
  19. #include "_select.h"
  20. #include "_font.h"
  21. #include "_measure.h"
  22. ASSERTDATA
  23. //
  24. // CDropSource PUBLIC methods
  25. //
  26. /*
  27. * CDropSource::QueryInterface (riid, ppv)
  28. */
  29. STDMETHODIMP CDropSource::QueryInterface(
  30. REFIID riid,
  31. void ** ppv)
  32. {
  33. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::QueryInterface");
  34. if( IsEqualIID(riid, IID_IUnknown) )
  35. *ppv = (IUnknown *)this;
  36. else if( IsEqualIID(riid, IID_IDropSource) )
  37. *ppv = (IDropSource *)this;
  38. else
  39. {
  40. *ppv = NULL;
  41. return E_NOINTERFACE;
  42. }
  43. AddRef();
  44. return NOERROR;
  45. }
  46. /*
  47. * CDropSource::AddRef
  48. */
  49. STDMETHODIMP_(ULONG) CDropSource::AddRef()
  50. {
  51. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::AddRef");
  52. return ++_crefs;
  53. }
  54. /*
  55. * CDropSource::Release
  56. *
  57. * @devnote. Do not even think about making an outgoing call here.
  58. * If you do, be sure make sure all callers use a
  59. * SafeReleaseAndNULL (null the pointer before releasing)
  60. * technique to avoid re-entrancy problems.
  61. */
  62. STDMETHODIMP_(ULONG) CDropSource::Release()
  63. {
  64. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::Release");
  65. _crefs--;
  66. if( _crefs == 0 )
  67. {
  68. delete this;
  69. return 0;
  70. }
  71. return _crefs;
  72. }
  73. /*
  74. * CDropSource::QueryContinueDrag (fEscapePressed, grfKeyState)
  75. *
  76. * @mfunc
  77. * determines whether or not to continue a drag drop operation
  78. *
  79. * Algorithm:
  80. * if the escape key has been pressed, cancel
  81. * if the left mouse button has been release, then attempt to
  82. * do a drop
  83. */
  84. STDMETHODIMP CDropSource::QueryContinueDrag(
  85. BOOL fEscapePressed,
  86. DWORD grfKeyState)
  87. {
  88. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::QueryContinueDrag");
  89. if(fEscapePressed)
  90. return DRAGDROP_S_CANCEL;
  91. if(!(grfKeyState & (MK_LBUTTON | MK_RBUTTON)))
  92. return DRAGDROP_S_DROP;
  93. return NOERROR;
  94. }
  95. /*
  96. * CDropSource::GiveFeedback (dwEffect)
  97. *
  98. * @mfunc
  99. * gives feedback during a drag drop operation
  100. *
  101. * Notes:
  102. * FUTURE (alexgo): maybe put in some neater feedback effects
  103. * than the standard OLE stuff??
  104. */
  105. STDMETHODIMP CDropSource::GiveFeedback(
  106. DWORD dwEffect)
  107. {
  108. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::GiveFeedback");
  109. return DRAGDROP_S_USEDEFAULTCURSORS;
  110. }
  111. /*
  112. * CDropSource::CDropSource
  113. */
  114. CDropSource::CDropSource()
  115. {
  116. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::CDropSource");
  117. _crefs = 1;
  118. }
  119. //
  120. // CDropSource PRIVATE methods
  121. //
  122. /*
  123. * CDropSource::~CDropSource
  124. */
  125. CDropSource::~CDropSource()
  126. {
  127. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::~CDropSource");
  128. ;
  129. }
  130. //
  131. // CDropTarget PUBLIC methods
  132. //
  133. /*
  134. * CDropTarget::QueryInterface (riid, ppv)
  135. */
  136. STDMETHODIMP CDropTarget::QueryInterface (
  137. REFIID riid,
  138. void ** ppv)
  139. {
  140. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::QueryInterface");
  141. if( IsEqualIID(riid, IID_IUnknown) )
  142. *ppv = (IUnknown *)this;
  143. else if( IsEqualIID(riid, IID_IDropTarget) )
  144. *ppv = (IDropTarget *)this;
  145. else
  146. {
  147. *ppv = NULL;
  148. return E_NOINTERFACE;
  149. }
  150. AddRef();
  151. return NOERROR;
  152. }
  153. /*
  154. * CDropTarget::AddRef
  155. */
  156. STDMETHODIMP_(ULONG) CDropTarget::AddRef()
  157. {
  158. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::AddRef");
  159. return ++_crefs;
  160. }
  161. /*
  162. * CDropTarget::Release()
  163. */
  164. STDMETHODIMP_(ULONG) CDropTarget::Release()
  165. {
  166. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::Release");
  167. _crefs--;
  168. if( _crefs == 0 )
  169. {
  170. delete this;
  171. return 0;
  172. }
  173. return _crefs;
  174. }
  175. /*
  176. * CDropTarget::DragEnter (pdo, grfKeyState, pt, pdwEffect)
  177. *
  178. * @mfunc
  179. * Called when OLE drag drop enters our "window"
  180. *
  181. * @devnote
  182. * First we check to see if the data object being transferred contains
  183. * any data that we support. Then we verify that the 'type' of drag
  184. * is acceptable (i.e., currently, we do not accept links).
  185. *
  186. * FUTURE: (alexgo): we may want to accept links as well.
  187. */
  188. STDMETHODIMP CDropTarget::DragEnter(
  189. IDataObject *pdo,
  190. DWORD grfKeyState,
  191. POINTL pt,
  192. DWORD * pdwEffect)
  193. {
  194. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::DragEnter");
  195. // We don't have a position yet.
  196. _cpCur = -1;
  197. HRESULT hr = NOERROR;
  198. DWORD result;
  199. CTxtSelection *psel;
  200. // At drag enter time, we should have no cached info about what the data
  201. // object supports. This flag should be cleared in DragLeave. Note
  202. // that we slightly override _dwFlags, as it's possible for a data object
  203. // given during drag drop to also generate DOI_NONE.
  204. if( !_ped )
  205. return CO_E_RELEASED;
  206. Assert(_pcallmgr == NULL);
  207. Assert(_dwFlags == 0);
  208. _pcallmgr = new CCallMgr(_ped);
  209. if( !_pcallmgr )
  210. return E_OUTOFMEMORY;
  211. // Find out if we can paste the object
  212. result = _ped->GetDTE()->CanPaste(pdo, 0, RECO_DROP);
  213. if( result )
  214. {
  215. if( result == DF_CLIENTCONTROL )
  216. _dwFlags |= DF_CLIENTCONTROL;
  217. // Create the object that implements the drag caret
  218. _pdrgcrt = new CDropCaret(_ped);
  219. if ((NULL == _pdrgcrt) || !_pdrgcrt->Init())
  220. {
  221. // Initialization failed so go without a caret
  222. delete _pdrgcrt;
  223. _pdrgcrt = NULL;
  224. }
  225. // cache the current selection so we can restore it on return
  226. psel = _ped->GetSel();
  227. Assert(psel);
  228. _cpSel = psel->GetCp();
  229. _cchSel = psel->GetCch();
  230. _dwFlags |= DF_CANDROP;
  231. // just call DragOver to handle our visual feedback
  232. hr = DragOver(grfKeyState, pt, pdwEffect);
  233. }
  234. else if (_ped->fInOurHost())
  235. {
  236. // Just tell the caller that we can't drop.
  237. *pdwEffect = DROPEFFECT_NONE;
  238. }
  239. else
  240. {
  241. // this is new behaviour for Win95 OLE; if we don't
  242. // understand anything about the data object given to us,
  243. // we return S_FALSE to allow our parent to give the
  244. // drag drop a try.
  245. // In theory, only forms^3 uses this information and
  246. // this return exposes an error in NT OLE, therefore,
  247. // we only do this now when not in our own host.
  248. hr = S_FALSE;
  249. }
  250. if( hr != NOERROR )
  251. {
  252. delete _pcallmgr;
  253. _pcallmgr = NULL;
  254. _dwFlags = 0;
  255. }
  256. return hr;
  257. }
  258. /*
  259. * CDropTarget::DragOver (grfKeyState, pt, pdwEffect)
  260. *
  261. * @mfunc
  262. * Handles the visual feedback for a drag drop operation zooming
  263. * around over text
  264. *
  265. * FUTURE (alexgo): maybe we should do some snazzy visuals here
  266. */
  267. STDMETHODIMP CDropTarget::DragOver(
  268. DWORD grfKeyState,
  269. POINTL pt,
  270. DWORD * pdwEffect)
  271. {
  272. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::DragOver");
  273. LONG cpCur = _cpCur;
  274. if( !_ped )
  275. return CO_E_RELEASED;
  276. Assert(_pcallmgr);
  277. // note if we're doing right mouse drag drop; note that we
  278. // can't do this in UpdateEffect as it's called from Drop
  279. // as well (and the mouse button is up then!)
  280. _dwFlags &= ~DF_RIGHTMOUSEDRAG;
  281. if(grfKeyState & MK_RBUTTON)
  282. _dwFlags |= DF_RIGHTMOUSEDRAG;
  283. UpdateEffect(grfKeyState, pt, pdwEffect);
  284. // only draw if we've changed position
  285. if( *pdwEffect != DROPEFFECT_NONE &&
  286. (cpCur != _cpCur || _pdrgcrt && _pdrgcrt->NoCaret()))
  287. {
  288. DrawFeedback();
  289. }
  290. return NOERROR;
  291. }
  292. /*
  293. * CDropTarget::DragLeave
  294. *
  295. * @mfunc
  296. * Called when the mouse leaves our window during drag drop. Here we clean
  297. * up any temporary state setup for the drag operation.
  298. *
  299. * @rdesc
  300. * HRESULT
  301. */
  302. STDMETHODIMP CDropTarget::DragLeave()
  303. {
  304. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::DragLeave");
  305. CTxtSelection *psel = _ped->GetSel();
  306. if( !_ped )
  307. return CO_E_RELEASED;
  308. Assert(_pcallmgr);
  309. _dwFlags = 0;
  310. // now restore the selection
  311. psel->SetSelection(_cpSel - _cchSel, _cpSel);
  312. psel->Update(FALSE);
  313. _cpSel = _cchSel = 0;
  314. delete _pcallmgr;
  315. _pcallmgr = NULL;
  316. delete _pdrgcrt;
  317. _pdrgcrt = NULL;
  318. return NOERROR;
  319. }
  320. /*
  321. * CDropTarget::Drop (pdo, grfKeyState, pt, pdwEffect)
  322. *
  323. * @mfunc
  324. * Called when the mouse button is released. We should attempt
  325. * to 'paste' the data object into a selection corresponding to
  326. * the mouse location
  327. *
  328. * @devnote
  329. * First we make sure that we can still do a paste (via UpdateEffect).
  330. * If so, then set the selection to the current point and then insert
  331. * the text.
  332. *
  333. * @rdesc
  334. * HRESULT
  335. */
  336. STDMETHODIMP CDropTarget::Drop(
  337. IDataObject *pdo,
  338. DWORD grfKeyState,
  339. POINTL ptl,
  340. DWORD * pdwEffect)
  341. {
  342. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::Drop");
  343. HRESULT hr = NOERROR;
  344. if( !_ped )
  345. return CO_E_RELEASED;
  346. Assert(_pcallmgr);
  347. CDropCleanup cleanup(this);
  348. // see if we can still drop
  349. UpdateEffect(grfKeyState, ptl, pdwEffect);
  350. // UpdateEffect will show a drop cursor but at this point we don't need one
  351. // so we hide the drop cursor here.
  352. if (_pdrgcrt)
  353. _pdrgcrt->ShowCaret(FALSE);
  354. if (_dwFlags & DF_OVERSOURCE)
  355. {
  356. *pdwEffect = DROPEFFECT_NONE;
  357. _dwFlags = 0;
  358. return NOERROR;
  359. }
  360. if(*pdwEffect & (DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK))
  361. {
  362. IUndoBuilder * publdr;
  363. CGenUndoBuilder undobldr( _ped, UB_AUTOCOMMIT, &publdr);
  364. // If this is a right mouse drag drop; handle that
  365. if(_dwFlags & DF_RIGHTMOUSEDRAG)
  366. {
  367. hr = HandleRightMouseDrop(pdo, ptl);
  368. // If S_FALSE is returned, treat drag drop normally
  369. if( hr != S_FALSE )
  370. goto Exit;
  371. }
  372. // Get an undo builder. If we already have one cached, that means
  373. // we are dropping onto the same edit instance that started the drag
  374. // In this case, we want to use the cached undo builder so that
  375. // a drag move can be undone as one "operation".
  376. if(_publdr)
  377. publdr = _publdr;
  378. CTxtSelection *psel = _ped->GetSel();
  379. psel->SetSelection(_cpCur, _cpCur);
  380. if( !_ped->IsProtectedRange(WM_PASTE, 0, 0, psel) )
  381. {
  382. hr = _ped->PasteDataObjectToRange(pdo, (CTxtRange *)psel,
  383. 0, NULL, publdr, PDOR_DROP);
  384. }
  385. // If we are dropping onto ourselves, the UI specifies
  386. // that we should select the entire range dragged. We use
  387. // _publdr as an easy way to tell if the drop originated from
  388. // this instance
  389. if(SUCCEEDED(hr) && _pdrgcrt)
  390. {
  391. // If the drop worked, then we don't want to restore the area
  392. // where the drop caret used to be since this is not out of date.
  393. _pdrgcrt->CancelRestoreCaretArea();
  394. }
  395. // Now set the selection anti-events. If the selection preceded the
  396. // paste poiont subtract its length from the redo position, since
  397. // the selection will get deleted if we are doing a DRAGMOVE within
  398. // this instance.
  399. LONG cpNext = psel->GetCp();
  400. LONG cchNext = cpNext - _cpCur;
  401. if(_cpSel < _cpCur && _publdr && (*pdwEffect & DROPEFFECT_MOVE))
  402. cpNext -= abs(_cchSel);
  403. HandleSelectionAEInfo(_ped, publdr, _cpCur, 0, cpNext, cchNext,
  404. SELAE_FORCEREPLACE);
  405. if(_publdr)
  406. {
  407. // If we are doing a drag move, then *don't* set the
  408. // selection directly on the screen--doing so will result in
  409. // unsightly UI--we'll set the selection to one spot, draw it
  410. // and then immediately move the selection somewhere else.
  411. // In this case, just change where the selection range exists.
  412. // Floating ranges and the drag-move code in ldte.c will take
  413. // care of the rest.
  414. if( *pdwEffect == DROPEFFECT_COPY )
  415. psel->SetSelection(_cpCur, psel->GetCp());
  416. else
  417. psel->Set(psel->GetCp(), cchNext);
  418. }
  419. else if(publdr)
  420. {
  421. // The drop call landed in us from outside, so we need
  422. // to fire the appropriate notifications. First, however,
  423. // commit the undo builder.
  424. publdr->SetNameID(UID_DRAGDROP);
  425. publdr->Done();
  426. if(SUCCEEDED(hr))
  427. {
  428. // Make this window the foreground window after the drop. Note
  429. // that the host needs to support ITextHost2 to really get to
  430. // be the foreground window. If they don't this is a no-op.
  431. _ped->TxSetForegroundWindow();
  432. }
  433. }
  434. // If nothing changed on the drop && the effect is a move, then return
  435. // failure. This is an ugly hack to improve drag-move scenarios; if
  436. // nothing happened on the drop, then chances are, you don't want
  437. // to have the correspong "Cut" happen on the drag source side.
  438. //
  439. // Of course, this relies on the drag source responding gracefully to
  440. // E_FAIL w/o hitting too much trauma.
  441. if (*pdwEffect == DROPEFFECT_MOVE &&
  442. !_ped->GetCallMgr()->GetChangeEvent() )
  443. {
  444. hr = E_FAIL;
  445. }
  446. }
  447. Exit:
  448. _dwFlags = 0;
  449. return hr;
  450. }
  451. /*
  452. * CDropTarget::CDropTarget (ped)
  453. */
  454. CDropTarget::CDropTarget(
  455. CTxtEdit *ped)
  456. {
  457. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::CDropTarget");
  458. _ped = ped;
  459. _crefs = 1;
  460. _dwFlags = 0;
  461. _publdr = NULL;
  462. _cpMin = -1;
  463. _cpMost = -1;
  464. _pcallmgr = NULL;
  465. }
  466. /*
  467. * CDropTarget::SetDragInfo (publdr, cpMin, cpMost)
  468. *
  469. * @mfunc
  470. * Allows the data transfer engine to cache important information
  471. * about a drag drop with this drop target.
  472. *
  473. * @devnote
  474. * Intra-instance drag drop operations can be treated as a single user
  475. * action. With cpMin and cpMost, we can disable dragging into the
  476. * range that is being dragged. This method must be called again in
  477. * order to clear the cached info. -1 for cpMin and cpMost will "clear"
  478. * those values (as 0 is a valid cp)
  479. */
  480. void CDropTarget::SetDragInfo(
  481. IUndoBuilder *publdr, //@parm Undo builder to use
  482. LONG cpMin, //@parm Minimim character position of range that being dragged
  483. LONG cpMost ) //@parm Maximum character position of range
  484. {
  485. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::SetDragInfo");
  486. _publdr = publdr;
  487. _cpMin = cpMin;
  488. _cpMost = cpMost;
  489. }
  490. /*
  491. * CDropTarget::Zombie
  492. *
  493. * @mfunc This method clears the state in this drop target object. It is
  494. * used to recover 'gracefully' from reference counting errors
  495. */
  496. void CDropTarget::Zombie()
  497. {
  498. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::Zombie");
  499. _ped = NULL;
  500. if( _pcallmgr )
  501. {
  502. delete _pcallmgr;
  503. _pcallmgr = NULL;
  504. }
  505. }
  506. //
  507. // CDropTarget PRIVATE methods
  508. //
  509. /*
  510. * CDropTarget::~CDropTarget
  511. */
  512. CDropTarget::~CDropTarget()
  513. {
  514. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::~CDropTarget");
  515. ;
  516. }
  517. /*
  518. * CDropTarget::ConvertScreenPtToClientPt (pptScreen, pptClient)
  519. *
  520. * @mfunc
  521. * OLE drag drop sends points in using screen coordinates. However,
  522. * all of our display code internally relies on client coordinates
  523. * (i.e. the coordinates relative to the window that we are being
  524. * drawn in). This routine will convert between the two
  525. *
  526. * @devnote
  527. * The client coordinates use a POINT structure instead of POINTL.
  528. * while nominally they are the same, OLE uses POINTL and the display
  529. * engine uses POINT.
  530. */
  531. void CDropTarget::ConvertScreenPtToClientPt(
  532. POINTL *pptScreen,
  533. POINT *pptClient )
  534. {
  535. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::ConvertScreenPtToClientPt");
  536. POINT ptS;
  537. pptClient->x = ptS.x = pptScreen->x;
  538. pptClient->y = ptS.y = pptScreen->y;
  539. _ped->TxScreenToClient(pptClient);
  540. }
  541. /*
  542. * CDropTarget::UpdateEffect (grfKeyState, ptl, pdwEffect)
  543. *
  544. * @mfunc
  545. * Given the keyboard state and point, and knowledge of what
  546. * the data object being transferred can offer, calculate
  547. * the correct drag drop feedback.
  548. *
  549. * @devnote
  550. * This function should only be called during a drag drop
  551. * operation; doing otherwise will simply result in a return
  552. * of DROPEFFECT_NONE.
  553. */
  554. void CDropTarget::UpdateEffect(
  555. DWORD grfKeyState,
  556. POINTL ptl,
  557. DWORD * pdwEffect)
  558. {
  559. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::UpdateEffect");
  560. BOOL fHot = FALSE;
  561. HRESULT hr;
  562. LPRICHEDITOLECALLBACK const precall = _ped->GetRECallback();
  563. if (_ped->fInplaceActive())
  564. {
  565. POINTUV pt;
  566. POINT ptClient;
  567. WORD nScrollInset;
  568. // first, find out where we are
  569. ConvertScreenPtToClientPt(&ptl, &ptClient);
  570. _ped->_pdp->PointuvFromPoint(pt, ptClient);
  571. _cpCur = _ped->_pdp->CpFromPoint(pt, NULL, NULL, NULL, FALSE);
  572. // if we are on top of the range that is being
  573. // dragged, then remeber it for later
  574. _dwFlags &= ~DF_OVERSOURCE;
  575. if( _cpCur > _cpMin && _cpCur < _cpMost )
  576. _dwFlags |= DF_OVERSOURCE;
  577. // Scroll if we need to and remember if we are in the hot zone.
  578. nScrollInset = W32->GetScrollInset();
  579. if (_pdrgcrt != NULL)
  580. _pdrgcrt->ShowCaret(FALSE);
  581. fHot = _ped->_pdp->AutoScroll(pt, nScrollInset, nScrollInset);
  582. if (_pdrgcrt != NULL)
  583. {
  584. if(!(_dwFlags & DF_OVERSOURCE) && !fHot)
  585. _pdrgcrt->ShowCaret(TRUE);
  586. else
  587. {
  588. // The hide above restored the caret so we just
  589. // need to turn off the caret while we are over the
  590. // source.
  591. _pdrgcrt->CancelRestoreCaretArea();
  592. }
  593. }
  594. }
  595. // Let the client set the effect if it wants, but first, we need
  596. // to check for protection.
  597. if( _ped->IsRich() )
  598. {
  599. // We don't allow dropping onto protected text. Note that
  600. // the _edges_ of a protected range may be dragged to; therefore,
  601. // we need to check for protection at _cpCur and _cpCur-1.
  602. // If both cp's are protected, then we are inside a protected
  603. // range.
  604. CTxtRange rg(_ped, _cpCur, 0);
  605. PROTECT iProt = rg.IsProtected(CHKPROT_EITHER);
  606. if (iProt == PROTECTED_YES ||
  607. iProt == PROTECTED_ASK &&
  608. (!_ped->IsProtectionCheckingEnabled() ||
  609. _ped->QueryUseProtection(&rg, WM_MOUSEMOVE,0, 0)))
  610. {
  611. *pdwEffect = DROPEFFECT_NONE;
  612. goto Exit;
  613. }
  614. }
  615. if( precall )
  616. {
  617. hr = precall->GetDragDropEffect(FALSE, grfKeyState, pdwEffect);
  618. // Note : RichEdit 1.0 does not check the return code of this call.
  619. // If callback specified a single effect, use it.
  620. // Otherwise pick one ourselves.
  621. // trick: (x & (x-1)) is non-zero if more than one bit is set.
  622. if (!(*pdwEffect & (*pdwEffect - 1) ))
  623. goto Exit;
  624. }
  625. // If we don't know anything about the data object or the control
  626. // is read-only, set the effect to none.
  627. // If the client is handling this, we don't worry about read-only.
  628. if (!(_dwFlags & DF_CLIENTCONTROL) &&
  629. (!(_dwFlags & DF_CANDROP) || _ped->TxGetReadOnly()))
  630. {
  631. *pdwEffect = DROPEFFECT_NONE;
  632. _cpCur = -1;
  633. // no need to do anything else
  634. return;
  635. }
  636. // if we are on top of the range that is being
  637. // dragged, then we can't drop there!
  638. if( _dwFlags & DF_OVERSOURCE )
  639. {
  640. *pdwEffect = DROPEFFECT_NONE;
  641. goto Exit;
  642. }
  643. // now check the keyboard state and the requested drop effects.
  644. if(_dwFlags & DF_CANDROP)
  645. {
  646. // if we can paste plain text, then see if a MOVE or COPY
  647. // operation was requested and set the right effect. Note
  648. // that we prefer MOVEs over COPY's in accordance with OLE
  649. // UI guidelines.
  650. // we do not yet support linking
  651. if( (grfKeyState & MK_CONTROL) && (grfKeyState & MK_SHIFT) )
  652. {
  653. //COMPATIBILITY: Richedit 1.0 did not appear to support drag
  654. //linking correctly.
  655. *pdwEffect = DROPEFFECT_NONE;
  656. }
  657. else if( !(grfKeyState & MK_CONTROL) &&
  658. (*pdwEffect & DROPEFFECT_MOVE) )
  659. {
  660. // if the control key is *not* depressed, then assume a "move"
  661. // operation (note that shift and alt or no keys will also give
  662. // a move) iff the source supports move.
  663. *pdwEffect = DROPEFFECT_MOVE;
  664. }
  665. else if( (grfKeyState & MK_CONTROL) && !((grfKeyState & MK_ALT) &&
  666. (grfKeyState & MK_SHIFT)) && (*pdwEffect & DROPEFFECT_COPY) )
  667. {
  668. // if only the control key is down and we're allowed to do a copy,
  669. // then do a copy
  670. *pdwEffect = DROPEFFECT_COPY;
  671. }
  672. else if( !(grfKeyState & MK_CONTROL) &&
  673. (*pdwEffect & DROPEFFECT_COPY) )
  674. {
  675. // if the control key is *not* depressed, and we are *not* allowed
  676. // to do a move (note that this if comes below the second one), then
  677. // do a COPY operation (if available)
  678. *pdwEffect = DROPEFFECT_COPY;
  679. }
  680. else
  681. *pdwEffect = DROPEFFECT_NONE; // not a combination that we support
  682. }
  683. else
  684. *pdwEffect = DROPEFFECT_NONE;
  685. Exit:
  686. //Add the scrolling effect if we are in the hot zone.
  687. if(fHot)
  688. *pdwEffect |= DROPEFFECT_SCROLL;
  689. }
  690. /*
  691. * CDropTarget::DrawFeedback()
  692. *
  693. * @mfunc
  694. * draws any feeback necessary on the target side (specifically, setting the
  695. * cursor
  696. *
  697. * Notes:
  698. * assumes _cpCur is correctly set.
  699. */
  700. void CDropTarget::DrawFeedback()
  701. {
  702. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::DrawFeedback");
  703. if (_ped && _ped->fInplaceActive() && _pdrgcrt != NULL)
  704. {
  705. // We need to indicate a drop location because a drop is possible
  706. _pdrgcrt->DrawCaret(_cpCur);
  707. }
  708. }
  709. /*
  710. * CDropTarget::HandleRightMouseDrop(pdo, ptl)
  711. *
  712. * @mfunc Handles calling back to the client to get a context menu
  713. * for a right-mouse drag drop.
  714. *
  715. * @rdesc HRESULT
  716. */
  717. HRESULT CDropTarget::HandleRightMouseDrop(
  718. IDataObject *pdo, //@parm Data object to drop
  719. POINTL ptl) //@parm Location of the drop (screen coords)
  720. {
  721. LPRICHEDITOLECALLBACK precall = NULL;
  722. CHARRANGE cr = {_cpCur, _cpCur};
  723. HMENU hmenu = NULL;
  724. HWND hwnd, hwndParent;
  725. precall = _ped->GetRECallback();
  726. if( !precall || _ped->Get10Mode() )
  727. return S_FALSE;
  728. // HACK ALERT! evil pointer casting going on here.
  729. precall->GetContextMenu( GCM_RIGHTMOUSEDROP, (IOleObject *)(void *)pdo, &cr, &hmenu);
  730. if( hmenu && _ped->TxGetWindow(&hwnd) == NOERROR )
  731. {
  732. hwndParent = GetParent(hwnd);
  733. if( !hwndParent )
  734. hwndParent = hwnd;
  735. TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
  736. ptl.x, ptl.y, 0, hwndParent, NULL);
  737. return NOERROR;
  738. }
  739. return S_FALSE;
  740. }
  741. /*
  742. * CDropCaret::DrawCaret (ped)
  743. *
  744. * @mfunc
  745. * Draws a "caret" to indicate where the drop will occur.
  746. */
  747. CDropCaret::CDropCaret(
  748. CTxtEdit *ped) //@parm Edit control
  749. {
  750. _ped = ped;
  751. _dvp = -1;
  752. _hdcWindow = NULL;
  753. _fCaretOn = FALSE;
  754. }
  755. /*
  756. * CDropCaret::~CDropCaret
  757. *
  758. * @mfunc
  759. * Clean up caret object
  760. */
  761. CDropCaret::~CDropCaret()
  762. {
  763. if (_ped->_pdp && _hdcWindow != NULL)
  764. {
  765. // Restore the any updated window area
  766. ShowCaret(FALSE);
  767. // Free the DC we held on to
  768. _ped->_pdp->ReleaseDC(_hdcWindow);
  769. }
  770. }
  771. /*
  772. * CDropCaret::Init ()
  773. *
  774. * @mfunc
  775. * Do initialization that can fail
  776. */
  777. BOOL CDropCaret::Init()
  778. {
  779. // Get the DC for the window
  780. _hdcWindow = _ped->_pdp->GetDC();
  781. if (NULL == _hdcWindow)
  782. {
  783. // Could not get a DC, we are toast.
  784. AssertSz(FALSE, "CDropCaret::Init could not get hdc");
  785. return FALSE;
  786. }
  787. return TRUE;
  788. }
  789. /*
  790. * CDropCaret::DrawCaret (cpCur)
  791. *
  792. * @mfunc
  793. * Draws a "caret" to indicate where the drop will occur.
  794. */
  795. void CDropCaret::DrawCaret(
  796. LONG cpCur) //@parm current cp of where drop would occur
  797. {
  798. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropCaret::DrawCaret");
  799. CLock lock; // Uses global (shared) FontCache
  800. CDisplay * pdp = _ped->_pdp;
  801. POINTUV ptNew;
  802. RECTUV rcClient;
  803. CLinePtr rp(pdp);
  804. CRchTxtPtr rtp(_ped, cpCur);
  805. //Hide caret if appropriate
  806. ShowCaret(FALSE);
  807. // We no longer have a caret to restore
  808. _dvp = -1;
  809. // Get new cp from point
  810. pdp->PointFromTp(rtp, NULL, FALSE, ptNew, &rp, TA_TOP | TA_LOGICAL);
  811. // Get client rectangle
  812. _ped->TxGetClientRect(&rcClient);
  813. // Figure out height of new caret
  814. const CCharFormat *pCF = rtp.GetCF();
  815. // Get zoomed height
  816. LONG dvpInch = MulDiv(GetDeviceCaps(_hdcWindow, LOGPIXELSY), pdp->GetZoomNumerator(), pdp->GetZoomDenominator());
  817. CCcs *pccs = _ped->GetCcs(pCF, dvpInch);
  818. if (NULL == pccs)
  819. return; // We can't do anything sensible so give up.
  820. // Save new height
  821. _dvp = pccs->_yHeight;
  822. LONG vpOffset, vpAdjust;
  823. pccs->GetOffset(pCF, dvpInch, &vpOffset, &vpAdjust);
  824. // Save new position
  825. ptNew.v += (rp->GetHeight() - rp->GetDescent() + pccs->_yDescent - _dvp - vpOffset - vpAdjust);
  826. // Release cache entry since we are done with it.
  827. pccs->Release();
  828. // Save new caret position
  829. _ptCaret.u = ptNew.u;
  830. _ptCaret.v = ptNew.v;
  831. // If new point is in client rectangle, show the caret
  832. if(PtInRect(&rcClient, ptNew))
  833. ShowCaret(TRUE);
  834. }
  835. /*
  836. * CDropCaret::ShowCaret (fShow)
  837. *
  838. * @mfunc
  839. * Toggle
  840. */
  841. void CDropCaret::ShowCaret(
  842. BOOL fShow)
  843. {
  844. if (_dvp != -1 && _fCaretOn != fShow)
  845. {
  846. RECT rc;
  847. RECTUV rcuv;
  848. rcuv.left = _ptCaret.u;
  849. rcuv.top = _ptCaret.v;
  850. rcuv.right = rcuv.left + WIDTH_DROPCARET;
  851. rcuv.bottom = rcuv.top + _dvp;
  852. _ped->_pdp->RectFromRectuv(rc, rcuv);
  853. InvertRect(_hdcWindow, &rc);
  854. _fCaretOn = fShow;
  855. }
  856. }