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.

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