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.

4539 lines
122 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module SELECT.CPP -- Implement the CTxtSelection class |
  5. *
  6. * This module implements the internal CTxtSelection methods.
  7. * See select2.c and range2.c for the ITextSelection methods
  8. *
  9. * Authors: <nl>
  10. * RichEdit 1.0 code: David R. Fulmer
  11. * Christian Fortini (initial conversion to C++)
  12. * Murray Sargent <nl>
  13. *
  14. * @devnote
  15. * The selection UI is one of the more intricate parts of an editor.
  16. * One common area of confusion is the "ambiguous cp", that is,
  17. * a cp at the beginning of one line, which is also the cp at the
  18. * end of the previous line. We control which location to use by
  19. * the _fCaretNotAtBOL flag. Specifically, the caret is OK at the
  20. * beginning of the line (BOL) (_fCaretNotAtBOL = FALSE) except in
  21. * three cases:
  22. *
  23. * 1) the user clicked at or past the end of a wrapped line,
  24. * 2) the user typed End key on a wrapped line,
  25. * 3) the active end of a nondegenerate selection is at the EOL.
  26. *
  27. * Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
  28. */
  29. #include "_common.h"
  30. #include "_select.h"
  31. #include "_edit.h"
  32. #include "_disp.h"
  33. #include "_measure.h"
  34. #include "_font.h"
  35. #include "_rtfconv.h"
  36. #include "_antievt.h"
  37. #ifndef NOLINESERVICES
  38. #include "_ols.h"
  39. #endif
  40. ASSERTDATA
  41. // ======================= Invariant stuff and Constructors ======================================================
  42. #define DEBUG_CLASSNAME CTxtSelection
  43. #include "_invar.h"
  44. #ifdef DEBUG
  45. BOOL
  46. CTxtSelection::Invariant() const
  47. {
  48. // FUTURE: maybe add some thoughtful asserts...
  49. static LONG numTests = 0;
  50. numTests++; // how many times we've been called
  51. if(IsInOutlineView() && _cch)
  52. {
  53. LONG cpMin, cpMost;
  54. GetRange(cpMin, cpMost);
  55. CTxtPtr tp(_rpTX); // Scan range for an EOP
  56. tp.SetCp(cpMin);
  57. // _fSelHasEop flag may be off when last cr selected so don't
  58. // assert in that case.
  59. if (GetPed()->GetAdjustedTextLength() != cpMost)
  60. {
  61. AssertSz((unsigned)(tp.FindEOP(cpMost - cpMin) != 0) == _fSelHasEOP,
  62. "Incorrect CTxtSelection::_fSelHasEOP");
  63. }
  64. }
  65. return CTxtRange::Invariant();
  66. }
  67. #endif
  68. CTxtSelection::CTxtSelection(CDisplay * const pdp) :
  69. CTxtRange(pdp->GetPed())
  70. {
  71. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::CTxtSelection");
  72. Assert(pdp);
  73. Assert(GetPed());
  74. _fSel = TRUE; // This range is a selection
  75. _pdp = pdp;
  76. _hbmpCaret = NULL;
  77. _fEOP = FALSE; // Haven't typed a CR
  78. // Set show-selection flag to inverse of hide-selection flag in ped
  79. _fShowSelection = !GetPed()->fHideSelection();
  80. // When we are initialized we don't have a selection therefore,
  81. // we do want to show caret.
  82. _fShowCaret = TRUE;
  83. }
  84. void SelectionNull(CTxtEdit *ped)
  85. {
  86. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "SelectionNull");
  87. if(ped)
  88. ped->SetSelectionToNull();
  89. }
  90. CTxtSelection::~CTxtSelection()
  91. {
  92. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::~CTxtSelection");
  93. DeleteCaretBitmap(FALSE);
  94. // Notify edit object that we are gone (if there's a nonNULL ped, i.e.,
  95. // if the selection isn't a zombie).
  96. SelectionNull(GetPed());
  97. }
  98. //////////////////////////////// Assignments /////////////////////////////////////////
  99. CRchTxtPtr& CTxtSelection::operator =(const CRchTxtPtr& rtp)
  100. {
  101. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::operator =");
  102. _TEST_INVARIANT_
  103. return CTxtRange::operator =(rtp);
  104. }
  105. CTxtRange& CTxtSelection::operator =(const CTxtRange &rg)
  106. {
  107. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::operator =");
  108. _TEST_INVARIANT_
  109. return CTxtRange::operator =(rg);
  110. }
  111. ////////////////////// Update caret & selection mechanism ///////////////////////////////
  112. /*
  113. * CTxtSelection::Update(fScrollIntoView)
  114. *
  115. * @mfunc
  116. * Update selection and/or caret on screen. As a side
  117. * effect, this methods ends deferring updates.
  118. *
  119. * @rdesc
  120. * TRUE if success, FALSE otherwise
  121. */
  122. BOOL CTxtSelection::Update (
  123. BOOL fScrollIntoView) //@parm TRUE if should scroll caret into view
  124. {
  125. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::Update");
  126. LONG cch;
  127. LONG cchSave = _cch;
  128. LONG cchText = GetTextLength();
  129. LONG cp, cpMin, cpMost;
  130. LONG cpSave = GetCp();
  131. BOOL fMoveBack = _fMoveBack;
  132. CTxtEdit *ped = GetPed();
  133. _fUpdatedFromCp0 = FALSE;
  134. if(!ped->fInplaceActive() || ped->IsStreaming())
  135. {
  136. // Nothing to do while inactive or streaming in text or RTF data
  137. return TRUE;
  138. }
  139. if(!_cch) // Update _cpAnchor, etc.
  140. {
  141. while(GetPF()->IsTableRowDelimiter() && _rpTX.GetChar() != ENDFIELD)
  142. {
  143. if(_fMoveBack)
  144. {
  145. if(!BackupCRLF(CSC_NORMAL, FALSE)) // Don't leave at table row start
  146. _fMoveBack = FALSE;
  147. }
  148. else
  149. AdvanceCRLF(CSC_NORMAL, FALSE); // Bypass table row start
  150. }
  151. UpdateForAutoWord();
  152. }
  153. if(_cch && (_nSelExpandLevel || _fSelExpandCell))
  154. {
  155. BOOL fInTable = GetPF()->InTable();
  156. if(!fInTable)
  157. {
  158. CFormatRunPtr rp(_rpPF);
  159. rp.Move(-_cch);
  160. fInTable = (ped->GetParaFormat(rp.GetFormat()))->InTable();
  161. }
  162. if(fInTable)
  163. {
  164. if(_nSelExpandLevel)
  165. FindRow(&cpMin, &cpMost, _nSelExpandLevel);
  166. else
  167. FindCell(&cpMin, &cpMost);
  168. Set(cpMost, cpMost - cpMin);
  169. if(!_fSelExpandCell)
  170. _nSelExpandLevel = 0;
  171. }
  172. }
  173. if(GetPF()->InTable()) // Don't leave IP in cell
  174. { // that's vertically merged
  175. if(fMoveBack) // with cell above
  176. {
  177. while(GetPrevChar() == NOTACHAR)
  178. { // Move before NOTACHAR and
  179. Move(-2, _cch); // either CELL or TRD's CR
  180. if(CRchTxtPtr::GetChar() != CELL)
  181. {
  182. Assert(GetPrevChar() == STARTFIELD);
  183. Move(-3, _cch); // Move before TRD START and
  184. break; // preceding TRD END
  185. }
  186. }
  187. }
  188. else
  189. while(CRchTxtPtr::GetChar() == NOTACHAR)
  190. Move(2, _cch); // Move past NOTACHAR CELL
  191. }
  192. if(IsInOutlineView() && !ped->IsMouseDown() && _rpPF.IsValid())
  193. {
  194. CPFRunPtr rp(*this);
  195. cp = GetCp();
  196. GetRange(cpMin, cpMost);
  197. if(_cch && (cpMin || cpMost < cchText))
  198. {
  199. LONG *pcpMin = &cpMin;
  200. LONG *pcpMost = &cpMost;
  201. // If selection contains an EOP, expand to para boundaries
  202. if(_fSelHasEOP)
  203. {
  204. if(_fMoveBack ^ (_cch < 0)) // Decreasing selection
  205. { // size: move active end
  206. if(_fMoveBack)
  207. pcpMost = NULL; // to StartOf para
  208. else
  209. pcpMin = NULL; // to EndOf para
  210. }
  211. Expander(tomParagraph, TRUE, NULL, pcpMin, pcpMost);
  212. }
  213. LONG cpMinSave = cpMin; // Save initial cp's to see if
  214. LONG cpMostSave = cpMost; // we need to Set() below
  215. // The following handles selection expansion correctly, but
  216. // not compression; need logic like that preceding Expander()
  217. rp.Move(cpMin - cp); // Start at cpMin
  218. if(rp.IsCollapsed())
  219. cpMin += rp.FindExpandedBackward();
  220. rp.AdjustForward();
  221. BOOL fCpMinCollapsed = rp.IsCollapsed();
  222. rp.Move(cpMost - cpMin); // Go to cpMost
  223. Assert(cpMost == rp.CalculateCp());
  224. if(rp.IsCollapsed())
  225. cpMost += rp.FindExpandedForward();
  226. if(fCpMinCollapsed || rp.IsCollapsed() && cpMost < cchText)
  227. {
  228. if(rp.IsCollapsed())
  229. {
  230. rp.Move(cpMin - cpMost);
  231. rp.AdjustForward();
  232. cpMost = cpMin;
  233. }
  234. else
  235. cpMin = cpMost;
  236. }
  237. if(cpMin != cpMinSave || cpMost != cpMostSave)
  238. Set(cpMost, cpMost - cpMin);
  239. }
  240. if(!_cch && rp.IsCollapsed()) // Note: above may have collapsed
  241. { // selection...
  242. cch = fMoveBack ? rp.FindExpandedBackward() : 0;
  243. if(rp.IsCollapsed())
  244. cch = rp.FindExpanded();
  245. Move(cch, FALSE);
  246. rp.AdjustForward();
  247. if(cch <= 0 && rp.IsCollapsed() && _rpTX.IsAfterEOP())
  248. BackupCRLF(CSC_NORMAL, FALSE);
  249. _fCaretNotAtBOL = FALSE;
  250. }
  251. }
  252. // Don't let active end be in hidden text, unless selection is
  253. // nondegenerate with active end at cp 0 and other end unhidden.
  254. CCFRunPtr rp(*this);
  255. cp = GetCp();
  256. GetRange(cpMin, cpMost);
  257. if(_cch && (cpMin || cpMost < cchText))
  258. {
  259. rp.Move(cpMin - cp); // Start at cpMin
  260. BOOL fHidden = cpMin && rp.IsInHidden();
  261. rp.Move(cpMost - cpMin); // Go to cpMost
  262. if(fHidden) // It's hidden, so collapse
  263. Collapser(tomEnd); // selection at End for treatment
  264. else if(rp.IsInHidden() && // cpMin OK, how about cpMost?
  265. cpMost < cchText)
  266. { // Check both sides of edge
  267. Collapser(tomEnd); // collapse selection at end
  268. }
  269. }
  270. if(!_cch && rp.IsInHidden()) // Note: above may have collapsed
  271. { // selection...
  272. cch = fMoveBack ? rp.FindUnhiddenBackward() : 0;
  273. if(!fMoveBack || rp.IsHidden())
  274. cch = rp.FindUnhidden();
  275. Move(cch, FALSE);
  276. _fCaretNotAtBOL = FALSE;
  277. }
  278. if((cchSave ^ _cch) < 0) // Don't change active end
  279. FlipRange();
  280. if(!_cch && cchSave) // Fixups changed nondegenerate
  281. { // selection to IP. Update
  282. Update_iFormat(-1); // _iFormat and _fCaretNotAtBOL
  283. _fCaretNotAtBOL = FALSE;
  284. }
  285. if(!_cch && _fCaretNotAtBOL // For IP case, make sure it is on new line if
  286. && _rpTX.IsAfterEOP()) // IP after EOP
  287. _fCaretNotAtBOL = FALSE;
  288. _TEST_INVARIANT_
  289. CheckTableIP(TRUE); // If IP bet TRED & CELL, ensure
  290. // CELL displayed on own line
  291. // Recalc up to active end (caret)
  292. if(!_pdp->WaitForRecalc(GetCp(), -1)) // Line recalc failure
  293. Set(0, 0); // Put caret at start of text
  294. ShowCaret(ped->_fFocus);
  295. UpdateCaret(fScrollIntoView); // Update Caret position, possibly
  296. // scrolling it into view
  297. ped->TxShowCaret(FALSE);
  298. UpdateSelection(); // Show new selection
  299. ped->TxShowCaret(TRUE);
  300. if(!cpSave && GetCp() && !_cch) // If insertion point & moved away
  301. _fUpdatedFromCp0 = TRUE; // from cp = 0, set flag so that
  302. // nonUI inserts can go back to 0
  303. return TRUE;
  304. }
  305. /*
  306. * CTxtSelection::CheckSynchCharSet(dwCharFlags)
  307. *
  308. * @mfunc
  309. * Check if the current keyboard matches the current font's charset;
  310. * if not, call CheckChangeFont to find the right font
  311. *
  312. * @rdesc
  313. * Current keyboard codepage
  314. */
  315. UINT CTxtSelection::CheckSynchCharSet(
  316. QWORD qwCharFlags)
  317. {
  318. CTxtEdit *ped = GetPed();
  319. LONG iFormat = GetiFormat();
  320. const CCharFormat *pCF = ped->GetCharFormat(iFormat);
  321. BYTE iCharRep = pCF->_iCharRep;
  322. HKL hkl = GetKeyboardLayout(0xFFFFFFFF); // Force refresh
  323. WORD wlidKbd = LOWORD(hkl);
  324. BYTE iCharRepKbd = CharRepFromLID(wlidKbd);
  325. UINT uCodePageKbd = CodePageFromCharRep(iCharRepKbd);
  326. // If current font is not set correctly,
  327. // change to a font preferred by current keyboard.
  328. // To summarize the logic below:
  329. // Check that lcidKbd is valid
  330. // Check that current charset differs from current keyboard
  331. // Check that current keyboard is legit in a single codepage control
  332. // Check that current charset isn't SYMBOL, DEFAULT, or OEM
  333. if (wlidKbd && iCharRep != iCharRepKbd &&
  334. (!ped->_fSingleCodePage || iCharRepKbd == ANSI_INDEX ||
  335. uCodePageKbd == (ped->_pDocInfo ?
  336. ped->_pDocInfo->_wCpg :
  337. GetSystemDefaultCodePage())) &&
  338. iCharRep != SYMBOL_INDEX && iCharRep != OEM_INDEX &&
  339. !(IsFECharRep(iCharRepKbd) && iCharRep == ANSI_INDEX))
  340. {
  341. CheckChangeFont(hkl, iCharRepKbd, iFormat, qwCharFlags);
  342. }
  343. return uCodePageKbd;
  344. }
  345. /*
  346. * CTxtSelection::MatchKeyboardToPara()
  347. *
  348. * @mfunc
  349. * Match the keyboard to the current paragraph direction. If the paragraph
  350. * is an RTL paragraph then the keyboard will be switched to an RTL
  351. * keyboard, and vice versa.
  352. *
  353. * @rdesc
  354. * TRUE iff a keyboard was changed
  355. *
  356. * @devnote
  357. * We use the following tests when trying to find a keyboard to match the
  358. * paragraph direction:
  359. *
  360. * See if the current keyboard matches the direction of the paragraph.
  361. *
  362. * Search backward from rtp looking for a charset that matches the
  363. * direction of the paragraph.
  364. *
  365. * Search forward from rtp looking for a charset that matches the
  366. * direction of the paragraph.
  367. *
  368. * See if the default charformat charset matches the direction of the
  369. * paragraph.
  370. *
  371. * See if there's only a single keyboard that matches the paragraph
  372. * direction.
  373. *
  374. * If all this fails, just leave the keyboard alone.
  375. */
  376. BOOL CTxtSelection::MatchKeyboardToPara()
  377. {
  378. CTxtEdit *ped = GetPed();
  379. if(!ped->IsBiDi() || !GetPed()->_fFocus || GetPed()->_fIMEInProgress)
  380. return FALSE;
  381. const CParaFormat *pPF = GetPF();
  382. if(pPF->IsTableRowDelimiter())
  383. return FALSE;
  384. BOOL fRTLPara = (pPF->_wEffects & PFE_RTLPARA) != 0;// Get paragraph direction
  385. if(W32->IsBiDiLcid(LOWORD(GetKeyboardLayout(0))) == fRTLPara)
  386. return FALSE;
  387. // Current keyboard direction didn't match paragraph direction...
  388. BYTE iCharRep;
  389. HKL hkl = 0;
  390. const CCharFormat * pCF;
  391. CFormatRunPtr rpCF(_rpCF);
  392. // Look backward in text, trying to find a CharSet that matches
  393. // the paragraph direction.
  394. do
  395. {
  396. pCF = ped->GetCharFormat(rpCF.GetFormat());
  397. iCharRep = pCF->_iCharRep;
  398. if(IsRTLCharRep(iCharRep) == fRTLPara)
  399. hkl = W32->CheckChangeKeyboardLayout(iCharRep);
  400. } while (!hkl && rpCF.PrevRun());
  401. if(!hkl)
  402. {
  403. // Didn't find an appropriate charformat so reset run pointer
  404. // and look forward instead
  405. rpCF = _rpCF;
  406. while (!hkl && rpCF.NextRun())
  407. {
  408. pCF = ped->GetCharFormat(rpCF.GetFormat());
  409. iCharRep = pCF->_iCharRep;
  410. if(IsRTLCharRep(iCharRep) == fRTLPara)
  411. hkl = W32->CheckChangeKeyboardLayout(iCharRep);
  412. }
  413. if(!hkl)
  414. {
  415. // Still didn't find an appropriate charformat so see if
  416. // default charformat matches paragraph direction.
  417. pCF = ped->GetCharFormat(rpCF.GetFormat());
  418. iCharRep = pCF->_iCharRep;
  419. if(IsRTLCharRep(iCharRep) == fRTLPara)
  420. hkl = W32->CheckChangeKeyboardLayout(iCharRep);
  421. if(!hkl)
  422. {
  423. // If even that didn't work, walk through the list of
  424. // keyboards and grab the first one we come to that matches
  425. // the paragraph direction.
  426. pCF = NULL;
  427. hkl = W32->FindDirectionalKeyboard(fRTLPara);
  428. }
  429. }
  430. }
  431. if (hkl && ped->_fFocus && IsCaretShown())
  432. {
  433. CreateCaret();
  434. ped->TxShowCaret(TRUE);
  435. }
  436. return hkl ? TRUE : FALSE;
  437. }
  438. /*
  439. * CTxtSelection::GetCaretPoint(&rcClient, pt, &rp, fBeforeCp)
  440. *
  441. * @mfunc
  442. * This routine determines where the caret should be positioned
  443. * on screen.
  444. * This routine is just a call to PointFromTp(), except for the Bidi
  445. * case. In that case if we are told to retrieve formatting from the
  446. * forward CP, we draw the caret at the logical left edge of the CP.
  447. * Else, we draw it at the logical right edge of the previous CP.
  448. *
  449. * @rdesc
  450. * TRUE if we didn't OOM.
  451. */
  452. BOOL CTxtSelection::GetCaretPoint(
  453. RECTUV &rcClient,
  454. POINTUV &pt,
  455. CLinePtr *prp,
  456. BOOL fBeforeCp)
  457. {
  458. CDispDim dispdim;
  459. CRchTxtPtr rtp(*this);
  460. UINT taMode = TA_BASELINE | TA_LOGICAL;
  461. if(GetPed()->IsBiDi() && _rpCF.IsValid())
  462. {
  463. if(_fHomeOrEnd) // Home/End
  464. taMode |= _fCaretNotAtBOL ? TA_ENDOFLINE : TA_STARTOFLINE;
  465. else if(!GetIchRunCF() || !GetCchLeftRunCF())
  466. {
  467. // In a Bidi context on a run boundary where the reverse level
  468. // changes, then we should respect the fBeforeCp flag.
  469. BYTE bLevelBwd, bLevelFwd;
  470. BOOL fStart = FALSE;
  471. LONG cp = rtp._rpTX.GetCp();
  472. CBiDiLevel level;
  473. bLevelBwd = bLevelFwd = rtp.IsParaRTL() ? 1 : 0;
  474. rtp._rpCF.AdjustBackward();
  475. if (cp)
  476. bLevelBwd = rtp._rpCF.GetLevel();
  477. rtp._rpCF.AdjustForward();
  478. if (cp != rtp._rpTX.GetTextLength())
  479. {
  480. bLevelFwd = rtp._rpCF.GetLevel(&level);
  481. fStart = level._fStart;
  482. }
  483. if((bLevelBwd != bLevelFwd || fStart) && !fBeforeCp && rtp.Move(-1))
  484. {
  485. // Direction change at cp, caret in prev CF run, and can
  486. // backspace to previous char: then get to the right of
  487. // previous char
  488. taMode |= TA_RIGHT;
  489. _fCaretNotAtBOL = !rtp._rpTX.IsAfterEOP();
  490. }
  491. }
  492. }
  493. if (_pdp->PointFromTp(rtp, &rcClient, _fCaretNotAtBOL, pt, prp, taMode, &dispdim) < 0)
  494. return FALSE;
  495. return TRUE;
  496. }
  497. /*
  498. * CTxtSelection::UpdateCaret(fScrollIntoView, bForceCaret)
  499. *
  500. * @mfunc
  501. * This routine updates caret/selection active end on screen.
  502. * It figures its position, size, clipping, etc. It can optionally
  503. * scroll the caret into view.
  504. *
  505. * @rdesc
  506. * TRUE if view was scrolled, FALSE otherwise
  507. *
  508. * @devnote
  509. * The caret is actually shown on screen only if _fShowCaret is TRUE.
  510. */
  511. BOOL CTxtSelection::UpdateCaret (
  512. BOOL fScrollIntoView, //@parm If TRUE, scroll caret into view if we have
  513. BOOL bForceCaret) // focus or if not and selection isn't hidden
  514. {
  515. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::UpdateCaret");
  516. _TEST_INVARIANT_
  517. if(_pdp->IsFrozen()) // If display is currently frozen
  518. { // save call for another time
  519. _pdp->SaveUpdateCaret(fScrollIntoView);
  520. return FALSE;
  521. }
  522. CTxtEdit *ped = GetPed();
  523. if(ped->IsStreaming()) // Don't bother doing anything if we
  524. return FALSE; // are loading in text or RTF data
  525. if(!ped->fInplaceActive()) // If not inplace active, set up
  526. { // for when focus is regained
  527. if(fScrollIntoView)
  528. ped->_fScrollCaretOnFocus = TRUE;
  529. return FALSE;
  530. }
  531. while(!_cch && _rpTX.IsAtTRD(STARTFIELD))
  532. {
  533. // Don't leave selection at start of a row; move it to start of first
  534. // cell. REVIEW: this restriction could be relaxed with some work, and
  535. // it would be nice to do so for ease of programmability. Specifically,
  536. // Whenever text would be inserted immediately before a table row,
  537. // one would be sure that doesn't become part of the table row-start
  538. // delimiter paragraph, i.e., is inserted in the previous paragraph
  539. // (if that one isn't a TRD para), or is inserted in its own paragraph.
  540. AdvanceCRLF(CSC_NORMAL, FALSE);
  541. }
  542. DWORD dwScrollBars = ped->TxGetScrollBars();
  543. BOOL fAutoVScroll = FALSE;
  544. BOOL fAutoUScroll = FALSE;
  545. BOOL fBeforeCp = _rpTX.IsAfterEOP();
  546. POINTUV pt;
  547. CLinePtr rp(_pdp);
  548. RECTUV rcClient;
  549. RECTUV rcView;
  550. LONG dupView, dvpView;
  551. LONG upScroll = _pdp->GetUpScroll();
  552. LONG vpScroll = _pdp->GetVpScroll();
  553. INT dvpAbove = 0; // Ascent of line above & beyond IP
  554. INT dvpAscent; // Ascent of IP
  555. INT dvpAscentLine;
  556. LONG vpBase; // Base of IP & line
  557. INT vpBelow = 0; // Descent of line below & beyond IP
  558. INT dvpDescent; // Descent of IP
  559. INT dvpDescentLine;
  560. INT vpSum;
  561. LONG vpViewTop, vpViewBottom;
  562. if(ped->_fFocus && (_fShowCaret || bForceCaret))
  563. {
  564. _fShowCaret = TRUE; // We're trying to force caret to display so set this flag to true
  565. if(!_fDualFontMode && !_fNoKeyboardUpdate && !_fIsChar && !_fHomeOrEnd)
  566. {
  567. // Avoid re-entrance to CheckChangeKeyboardLayout
  568. _fNoKeyboardUpdate = TRUE;
  569. // If we're in "dual font" mode, charset change is only
  570. // temporary and we don't want to change keyboard layout
  571. CheckChangeKeyboardLayout();
  572. if(!fBeforeCp && ped->IsBiDi() && _rpCF.IsValid() &&
  573. (!_rpCF.GetIch() || !_rpCF.GetCchLeft()))
  574. {
  575. _rpCF.AdjustBackward();
  576. BOOL fRTLPrevRun = IsRTLCharRep(GetCF()->_iCharRep);
  577. _rpCF.AdjustForward();
  578. if (fRTLPrevRun != IsRTLCharRep(GetCF()->_iCharRep) &&
  579. fRTLPrevRun != W32->IsBiDiLcid(GetKeyboardLCID()))
  580. {
  581. fBeforeCp = TRUE;
  582. }
  583. }
  584. _fNoKeyboardUpdate = FALSE;
  585. }
  586. }
  587. // Get client rectangle once to save various callers getting it
  588. ped->TxGetClientRect(&rcClient);
  589. _pdp->GetViewRect(rcView, &rcClient);
  590. // View can be bigger than client rect because insets can be negative.
  591. // We don't want the caret to be any bigger than the client view otherwise
  592. // the caret will leave pixel dust on other windows.
  593. vpViewTop = max(rcView.top, rcClient.top);
  594. vpViewBottom = min(rcView.bottom, rcClient.bottom);
  595. if(ped->IsInPageView())
  596. {
  597. LONG vpHeight = _pdp->GetCurrentPageHeight();
  598. if(vpHeight)
  599. {
  600. vpHeight += rcView.top;
  601. if(vpHeight < vpViewBottom)
  602. vpViewBottom = vpHeight;
  603. }
  604. }
  605. dupView = rcView.right - rcView.left;
  606. dvpView = vpViewBottom - vpViewTop;
  607. if(fScrollIntoView)
  608. {
  609. fAutoVScroll = (dwScrollBars & ES_AUTOVSCROLL) != 0;
  610. fAutoUScroll = (dwScrollBars & ES_AUTOHSCROLL) != 0;
  611. // If we're not forcing a scroll, only scroll if window has focus
  612. // or selection isn't hidden
  613. if (!ped->Get10Mode() || !GetForceScrollCaret())
  614. fScrollIntoView = ped->_fFocus || !ped->fHideSelection();
  615. }
  616. if(!fScrollIntoView && (fAutoVScroll || fAutoUScroll))
  617. { // Would scroll but don't have
  618. ped->_fScrollCaretOnFocus = TRUE; // focus. Signal to scroll
  619. if (!ped->Get10Mode() || !GetAutoVScroll())
  620. fAutoVScroll = fAutoUScroll = FALSE; // when we do get focus
  621. }
  622. SetAutoVScroll(FALSE);
  623. if (!_cch && IsInOutlineView() && IsCollapsed())
  624. goto not_visible;
  625. if (!GetCaretPoint(rcClient, pt, &rp, fBeforeCp))
  626. goto not_visible;
  627. // HACK ALERT - Because plain-text multiline controls do not have the
  628. // automatic EOP, we need to special case their processing here because
  629. // if you are at the end of the document and last character is an EOP,
  630. // you need to be on the next line in the display not the current line.
  631. if(CheckPlainTextFinalEOP()) // Terminated by an EOP
  632. {
  633. LONG Align = GetPF()->_bAlignment;
  634. LONG dvpHeight;
  635. pt.u = rcView.left; // Default left
  636. if(Align == PFA_CENTER)
  637. pt.u = (rcView.left + rcView.right)/2;
  638. else if(Align == PFA_RIGHT)
  639. pt.u = rcView.right;
  640. pt.u -= upScroll; // Absolute coordinate
  641. // Bump the y up a line. We get away with the calculation because
  642. // the document is plain text so all lines have the same height.
  643. // Also, note that the rp below is used only for height
  644. // calculations, so it is perfectly valid for the same reason
  645. // even though it is not actually pointing to the correct line.
  646. // (I told you this is a hack.)
  647. dvpHeight = rp->GetHeight();
  648. pt.v += dvpHeight;
  649. // Apply hack to PageView case as well
  650. if (ped->IsInPageView())
  651. vpViewBottom += dvpHeight;
  652. }
  653. _upCaret = pt.u;
  654. vpBase = pt.v;
  655. // Compute caret height, ascent, and descent
  656. dvpAscent = GetCaretHeight(&dvpDescent);
  657. dvpAscent -= dvpDescent;
  658. // Default to line empty case. Use what came back from the default
  659. // calculation above.
  660. dvpDescentLine = dvpDescent;
  661. dvpAscentLine = dvpAscent;
  662. if(rp.IsValid())
  663. {
  664. if(rp->GetDescent() != -1)
  665. {
  666. // Line has been measured so we can use the line's values
  667. dvpDescentLine = rp->GetDescent();
  668. dvpAscentLine = rp->GetHeight() - dvpDescentLine;
  669. }
  670. }
  671. if(dvpAscent + dvpDescent == 0)
  672. {
  673. dvpAscent = dvpAscentLine;
  674. dvpDescent = dvpDescentLine;
  675. }
  676. else
  677. {
  678. // This is a bit counter-intuitive at first. Basically, even if
  679. // the caret should be large (e.g., due to a large font at the
  680. // insertion point), we can only make it as big as the line. If
  681. // a character is inserted, then the line becomes bigger, and we
  682. // can make the caret the correct size.
  683. dvpAscent = min(dvpAscent, dvpAscentLine);
  684. dvpDescent = min(dvpDescent, dvpDescentLine);
  685. }
  686. if(fAutoVScroll)
  687. {
  688. Assert(dvpDescentLine >= dvpDescent);
  689. Assert(dvpAscentLine >= dvpAscent);
  690. vpBelow = dvpDescentLine - dvpDescent;
  691. dvpAbove = dvpAscentLine - dvpAscent;
  692. vpSum = dvpAscent;
  693. // Scroll as much as possible into view, giving priorities
  694. // primarily to IP and secondarily ascents
  695. if(vpSum > dvpView)
  696. {
  697. dvpAscent = dvpView;
  698. dvpDescent = 0;
  699. dvpAbove = 0;
  700. vpBelow = 0;
  701. }
  702. else if((vpSum += dvpDescent) > dvpView)
  703. {
  704. dvpDescent = dvpView - dvpAscent;
  705. dvpAbove = 0;
  706. vpBelow = 0;
  707. }
  708. else if((vpSum += dvpAbove) > dvpView)
  709. {
  710. dvpAbove = dvpView - (vpSum - dvpAbove);
  711. vpBelow = 0;
  712. }
  713. else if((vpSum += vpBelow) > dvpView)
  714. vpBelow = dvpView - (vpSum - vpBelow);
  715. }
  716. else
  717. {
  718. AssertSz(dvpAbove == 0, "dvpAbove non-zero");
  719. AssertSz(vpBelow == 0, "vpBelow non-zero");
  720. }
  721. // Update real caret x pos (constant during vertical moves)
  722. _upCaretReally = _upCaret - rcView.left + upScroll;
  723. if (!(dwScrollBars & ES_AUTOHSCROLL) && // Not auto UScrolling
  724. !_pdp->IsUScrollEnabled()) // and no scrollbar
  725. {
  726. if (_upCaret < rcView.left) // Caret off left edge
  727. _upCaret = rcView.left;
  728. else if(_upCaret + GetCaretDelta() > rcView.right)// Caret off right edge
  729. _upCaret = rcView.right - duCaret; // Back caret up to
  730. } // exactly the right edge
  731. // From this point on we need a new caret
  732. _fCaretCreated = FALSE;
  733. if(ped->_fFocus)
  734. ped->TxShowCaret(FALSE); // Hide old caret before
  735. // making a new one
  736. if(vpBase + dvpDescent + vpBelow > vpViewTop &&
  737. vpBase - dvpAscent - dvpAbove < vpViewBottom)
  738. {
  739. if(vpBase - dvpAscent - dvpAbove < vpViewTop) // Caret is partially
  740. { // visible
  741. if(fAutoVScroll) // Top isn't visible
  742. goto scrollit;
  743. Assert(dvpAbove == 0);
  744. dvpAscent = vpBase - vpViewTop; // Change ascent to amount
  745. if(vpBase < vpViewTop) // visible
  746. { // Move base to top
  747. dvpDescent += dvpAscent;
  748. dvpAscent = 0;
  749. vpBase = vpViewTop;
  750. }
  751. }
  752. if(vpBase + dvpDescent + vpBelow > vpViewBottom)
  753. {
  754. if(fAutoVScroll) // Bottom isn't visible
  755. goto scrollit;
  756. Assert(vpBelow == 0);
  757. dvpDescent = vpViewBottom - vpBase; // Change descent to amount
  758. if(vpBase > vpViewBottom) // visible
  759. { // Move base to bottom
  760. dvpAscent += dvpDescent;
  761. dvpDescent = 0;
  762. vpBase = vpViewBottom;
  763. }
  764. }
  765. // Anything still visible?
  766. if(dvpAscent <= 0 && dvpDescent <= 0)
  767. goto not_visible;
  768. // If left or right isn't visible, scroll or set non_visible
  769. if (_upCaret < rcView.left || // Left isn't visible
  770. _upCaret + GetCaretDelta() > rcView.right)// Right isn't visible
  771. {
  772. if(fAutoUScroll)
  773. goto scrollit;
  774. goto not_visible;
  775. }
  776. _vpCaret = vpBase - dvpAscent;
  777. _dvpCaret = (INT) dvpAscent + dvpDescent;
  778. }
  779. else if(fAutoUScroll || fAutoVScroll) // Caret isn't visible
  780. goto scrollit; // scroll it into view
  781. else
  782. {
  783. not_visible:
  784. // Caret isn't visible, don't show it
  785. _upCaret = -32000;
  786. _vpCaret = -32000;
  787. _dvpCaret = 1;
  788. }
  789. // Now update caret for real on screen. We only want to show the caret
  790. // if it is in the view and there is no selection.
  791. if(ped->_fFocus && _fShowCaret)
  792. {
  793. CreateCaret();
  794. ped->TxShowCaret(TRUE);
  795. }
  796. return FALSE;
  797. scrollit:
  798. if(fAutoVScroll)
  799. {
  800. // Scroll to top for cp = 0. This is important if the first line
  801. // contains object(s) taller than the client area is high. The
  802. // resulting behavior agrees with the Word UI in all ways except in
  803. // Backspacing (deleting) the char at cp = 0 when it is followed by
  804. // other chars that preceed the large object.
  805. if(!GetCp())
  806. vpScroll = 0;
  807. else if(ped->IsInPageView())
  808. vpScroll += vpBase - dvpAscent - dvpAbove;
  809. else if(vpBase - dvpAscent - dvpAbove < vpViewTop) // Top invisible
  810. vpScroll -= vpViewTop - (vpBase - dvpAscent - dvpAbove); // Make it so
  811. else if(vpBase + dvpDescent + vpBelow > vpViewBottom) // Bottom invisible
  812. {
  813. vpScroll += vpBase + dvpDescent + vpBelow - vpViewBottom; // Make it so
  814. // Don't do following special adjust if the current line is bigger
  815. // than the client area
  816. if(rp->GetHeight() < vpViewBottom - vpViewTop)
  817. {
  818. vpScroll = _pdp->AdjustToDisplayLastLine(vpBase + rp->GetHeight(),
  819. vpScroll);
  820. }
  821. }
  822. }
  823. if(fAutoUScroll)
  824. {
  825. // We don't scroll in chunks since sytem edit control doesn't
  826. if(_upCaret < rcView.left) // Left invisible
  827. upScroll -= rcView.left - _upCaret; // Make it visible
  828. else if(_upCaret + GetCaretDelta() > rcView.right)// Right invisible
  829. upScroll += _upCaret + duCaret - rcView.left - dupView;// Make it visible
  830. }
  831. if(vpScroll != _pdp->GetVpScroll() || upScroll != _pdp->GetUpScroll())
  832. {
  833. if (_pdp->ScrollView(upScroll, vpScroll, FALSE, FALSE) == FALSE)
  834. {
  835. if(ped->_fFocus && _fShowCaret)
  836. {
  837. CreateCaret();
  838. ped->TxShowCaret(TRUE);
  839. }
  840. return FALSE;
  841. }
  842. return TRUE;
  843. }
  844. return FALSE;
  845. }
  846. /*
  847. * CTxtSelection::GetCaretHeight(pdvpDescent)
  848. *
  849. * @mfunc
  850. * Calculate the height of the caret
  851. *
  852. * @rdesc
  853. * Caret height, <lt> 0 if failed
  854. */
  855. INT CTxtSelection::GetCaretHeight (
  856. INT *pdvpDescent) const //@parm Out parm to receive caret descent
  857. {
  858. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::GetCaretHeight");
  859. // (undefined if the return value is <lt> 0)
  860. _TEST_INVARIANT_
  861. CLock lock; // Uses global (shared) FontCache
  862. CTxtEdit *ped = GetPed();
  863. const CCharFormat *pCF = ped->GetCharFormat(_iFormat);
  864. const CDevDesc *pdd = _pdp->GetDdRender();
  865. HDC hdc = pdd->GetDC();
  866. if(!hdc)
  867. return -1;
  868. LONG yHeight = -1;
  869. LONG dypInch = MulDiv(GetDeviceCaps(hdc, LOGPIXELSY), _pdp->GetZoomNumerator(), _pdp->GetZoomDenominator());
  870. CCcs *pccs = ped->GetCcs(pCF, dypInch);
  871. if(!pccs)
  872. goto ret;
  873. LONG yOffset, yAdjust;
  874. pccs->GetOffset(pCF, dypInch, &yOffset, &yAdjust);
  875. SHORT yAdjustFE;
  876. yAdjustFE = pccs->AdjustFEHeight(!fUseUIFont() && ped->_pdp->IsMultiLine());
  877. if(pdvpDescent)
  878. *pdvpDescent = pccs->_yDescent + yAdjustFE - yAdjust - yOffset;
  879. yHeight = pccs->_yHeight + (yAdjustFE << 1);
  880. pccs->Release();
  881. ret:
  882. pdd->ReleaseDC(hdc);
  883. return yHeight;
  884. }
  885. /*
  886. * CTxtSelection::ShowCaret(fShow)
  887. *
  888. * @mfunc
  889. * Hide or show caret
  890. *
  891. * @rdesc
  892. * TRUE if caret was previously shown, FALSE if it was hidden
  893. */
  894. BOOL CTxtSelection::ShowCaret (
  895. BOOL fShow) //@parm TRUE for showing, FALSE for hiding
  896. {
  897. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::ShowCaret");
  898. _TEST_INVARIANT_
  899. const BOOL fRet = _fShowCaret;
  900. if(fRet != fShow)
  901. {
  902. _fShowCaret = fShow;
  903. if(GetPed()->_fFocus || GetPed()->fInOurHost())
  904. {
  905. if(fShow && !_fCaretCreated)
  906. CreateCaret();
  907. GetPed()->TxShowCaret(fShow);
  908. }
  909. }
  910. return fRet;
  911. }
  912. /*
  913. * CTxtSelection::IsCaretInView()
  914. *
  915. * @mfunc
  916. * Returns TRUE iff caret is inside visible view
  917. */
  918. BOOL CTxtSelection::IsCaretInView() const
  919. {
  920. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::IsCaretInView");
  921. _TEST_INVARIANT_
  922. RECTUV rc;
  923. _pdp->GetViewRect(rc);
  924. return (_upCaret + duCaret > rc.left) &&
  925. (_upCaret < rc.right) &&
  926. (_vpCaret + _dvpCaret > rc.top) &&
  927. (_vpCaret < rc.bottom);
  928. }
  929. /*
  930. * CTxtSelection::IsCaretHorizontal()
  931. *
  932. * @mfunc
  933. * Returns TRUE iff caret is horizontal
  934. * FUTURE murrays (keithcu) The selection needs to keep track
  935. * of what layout the selection is in so it can answer these
  936. * kinds of questions
  937. */
  938. BOOL CTxtSelection::IsCaretHorizontal() const
  939. {
  940. return !IsUVerticalTflow(_pdp->GetTflow());
  941. }
  942. /*
  943. * CTxtSelection::CaretNotAtBOL()
  944. *
  945. * @mfunc
  946. * Returns TRUE iff caret is not allowed at BOL
  947. */
  948. BOOL CTxtSelection::CaretNotAtBOL() const
  949. {
  950. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::CaretNotAtBOL");
  951. _TEST_INVARIANT_
  952. return _cch ? (_cch > 0) : _fCaretNotAtBOL;
  953. }
  954. /*
  955. * CTxtSelection::CheckTableIP(fShowCellLine)
  956. *
  957. * @mfunc
  958. * Open/close up display line if selection is an insertion point
  959. * at a CELL mark preceded by table-row end delimiter for fOpenLine
  960. * = TRUE/FALSE, respectively
  961. */
  962. void CTxtSelection::CheckTableIP(
  963. BOOL fShowCellLine) //@parm Open/close up line for CELL if preceded by TRED
  964. {
  965. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::CheckTableIP");
  966. if (!_cch && (fShowCellLine ^ _fShowCellLine) &&
  967. _rpTX.GetChar() == CELL && _rpTX.IsAfterTRD(ENDFIELD))
  968. {
  969. _fShowCellLine = fShowCellLine;
  970. _pdp->RecalcLine(GetCp());
  971. }
  972. }
  973. /*
  974. * CTxtSelection::LineLength(pcp)
  975. *
  976. * @mfunc
  977. * get # unselected chars on lines touched by current selection
  978. *
  979. * @rdesc
  980. * said number of chars
  981. */
  982. LONG CTxtSelection::LineLength(
  983. LONG *pcp) const
  984. {
  985. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::LineLength");
  986. _TEST_INVARIANT_
  987. LONG cch;
  988. CLinePtr rp(_pdp);
  989. if(!_cch) // Insertion point
  990. {
  991. rp.SetCp(GetCp(), _fCaretNotAtBOL);
  992. cch = rp.GetAdjustedLineLength();
  993. *pcp = GetCp() - rp.GetIch();
  994. }
  995. else
  996. {
  997. LONG cpMin, cpMost, cchLast;
  998. GetRange(cpMin, cpMost);
  999. rp.SetCp(cpMin, FALSE); // Selections can't start at EOL
  1000. cch = rp.GetIch();
  1001. *pcp = cpMin - cch;
  1002. rp.SetCp(cpMost, TRUE); // Selections can't end at BOL
  1003. // Remove trailing EOP, if it exists and isn't already selected
  1004. cchLast = rp.GetAdjustedLineLength() - rp.GetIch();
  1005. if(cchLast > 0)
  1006. cch += cchLast;
  1007. }
  1008. return cch;
  1009. }
  1010. /*
  1011. * CTxtSelection::ShowSelection(fShow)
  1012. *
  1013. * @mfunc
  1014. * Update, hide or show selection on screen
  1015. *
  1016. * @rdesc
  1017. * TRUE iff selection was previously shown
  1018. */
  1019. BOOL CTxtSelection::ShowSelection (
  1020. BOOL fShow) //@parm TRUE for showing, FALSE for hiding
  1021. {
  1022. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::ShowSelection");
  1023. _TEST_INVARIANT_
  1024. const BOOL fShowPrev = _fShowSelection;
  1025. const BOOL fInplaceActive = GetPed()->fInplaceActive();
  1026. LONG cpSelSave = _cpSel;
  1027. LONG cchSelSave = _cchSel;
  1028. // Sleep(1000);
  1029. _fShowSelection = fShow;
  1030. if(fShowPrev && !fShow)
  1031. {
  1032. if(cchSelSave) // Hide old selection
  1033. {
  1034. // Set up selection before telling the display to update
  1035. _cpSel = 0;
  1036. _cchSel = 0;
  1037. if(fInplaceActive)
  1038. _pdp->InvertRange(cpSelSave, cchSelSave, selSetNormal);
  1039. }
  1040. }
  1041. else if(!fShowPrev && fShow)
  1042. {
  1043. if(_cch) // Show new selection
  1044. {
  1045. // Set up selection before telling the display to update
  1046. _cpSel = GetCp();
  1047. _cchSel = _cch;
  1048. if(fInplaceActive)
  1049. _pdp->InvertRange(GetCp(), _cch, selSetHiLite);
  1050. }
  1051. }
  1052. return fShowPrev;
  1053. }
  1054. /*
  1055. * CTxtSelection::UpdateSelection()
  1056. *
  1057. * @mfunc
  1058. * Updates selection on screen
  1059. *
  1060. * Note:
  1061. * This method inverts the delta between old and new selections
  1062. */
  1063. void CTxtSelection::UpdateSelection()
  1064. {
  1065. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::UpdateSelection");
  1066. _TEST_INVARIANT_
  1067. LONG cp = GetCp();
  1068. LONG cpNA = cp - _cch;
  1069. LONG cpSelNA = _cpSel - _cchSel;
  1070. LONG cpMin, cpMost;
  1071. LONG cpMinSel = 0;
  1072. LONG cpMostSel = 0;
  1073. CObjectMgr* pobjmgr = NULL;
  1074. LONG NumObjInSel = 0, NumObjInOldSel = 0;
  1075. LONG cpSelSave = _cpSel;
  1076. LONG cchSelSave = _cchSel;
  1077. GetRange(cpMin, cpMost);
  1078. //We need to know if there were objects is the previous and current
  1079. //selections to determine how they should be selected.
  1080. if(GetPed()->HasObjects())
  1081. {
  1082. pobjmgr = GetPed()->GetObjectMgr();
  1083. if(pobjmgr)
  1084. {
  1085. CTxtRange tr(GetPed(), _cpSel, _cchSel);
  1086. tr.GetRange(cpMinSel, cpMostSel);
  1087. NumObjInSel = pobjmgr->CountObjectsInRange(cpMin, cpMost);
  1088. NumObjInOldSel = pobjmgr->CountObjectsInRange(cpMinSel, cpMostSel);
  1089. }
  1090. }
  1091. //If the old selection contained a single object and nothing else
  1092. //we need to notify the object manager that this is no longer the
  1093. //case if the selection is changing.
  1094. if (NumObjInOldSel && (abs(_cchSel) == 1) &&
  1095. !(cpMin == cpMinSel && cpMost == cpMostSel))
  1096. {
  1097. if(pobjmgr)
  1098. pobjmgr->HandleSingleSelect(GetPed(), cpMinSel, /* fHilite */ FALSE);
  1099. }
  1100. // Update selection data before the invert so the selection can be
  1101. // painted by the render
  1102. _cpSel = GetCp();
  1103. _cchSel = _cch;
  1104. if(_fShowSelection)
  1105. {
  1106. if(!_cch || !cchSelSave || // Old/new selection missing,
  1107. cpMost < min(cpSelSave, cpSelNA) || // or new preceeds old,
  1108. cpMin > max(cpSelSave, cpSelNA)) // or new follows old, so
  1109. { // they don't intersect
  1110. if(_cch)
  1111. _pdp->InvertRange(cp, _cch, selSetHiLite);
  1112. if(cchSelSave)
  1113. _pdp->InvertRange(cpSelSave, cchSelSave, selSetNormal);
  1114. }
  1115. else
  1116. {
  1117. if(cpNA != cpSelNA) // Old & new dead ends differ
  1118. { // Invert text between them
  1119. _pdp->InvertRange(cpNA, cpNA - cpSelNA, selUpdateNormal);
  1120. }
  1121. if(cp != cpSelSave) // Old & new active ends differ
  1122. { // Invert text between them
  1123. _pdp->InvertRange(cp, cp - cpSelSave, selUpdateHiLite);
  1124. }
  1125. }
  1126. }
  1127. // If new selection contains a single object and nothing else, we need
  1128. // to notify object manager as long as it's not the same object.
  1129. if (NumObjInSel && abs(_cch) == 1 &&
  1130. (cpMin != cpMinSel || cpMost != cpMostSel))
  1131. {
  1132. if(pobjmgr)
  1133. pobjmgr->HandleSingleSelect(GetPed(), cpMin, /* fHiLite */ TRUE);
  1134. }
  1135. }
  1136. /*
  1137. * CTxtSelection::SetSelection(cpFirst, cpMost)
  1138. *
  1139. * @mfunc
  1140. * Set selection between two cp's
  1141. *
  1142. * @devnote
  1143. * <p cpFirst> and <p cpMost> must be greater than 0, but may extend
  1144. * past the current max cp. In that case, the cp will be truncated to
  1145. * the max cp (at the end of the text).
  1146. */
  1147. void CTxtSelection::SetSelection (
  1148. LONG cpMin, //@parm Start of selection and dead end
  1149. LONG cpMost) //@parm End of selection and active end
  1150. {
  1151. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::SetSelection");
  1152. _TEST_INVARIANT_
  1153. CTxtEdit *ped = GetPed();
  1154. StopGroupTyping();
  1155. if(ped->HasObjects())
  1156. {
  1157. CObjectMgr* pobjmgr = GetPed()->GetObjectMgr();
  1158. if(pobjmgr)
  1159. {
  1160. COleObject *pobjactive = pobjmgr->GetInPlaceActiveObject();
  1161. if (pobjactive)
  1162. {
  1163. if (pobjactive != pobjmgr->GetObjectFromCp(cpMin) || cpMost - cpMin > 1)
  1164. pobjactive->DeActivateObj();
  1165. }
  1166. }
  1167. }
  1168. _fCaretNotAtBOL = FALSE; // Put caret for ambiguous cp at BOL
  1169. Set(cpMost, cpMost - cpMin); // Set() validates cpMin, cpMost
  1170. if(GetPed()->fInplaceActive()) // Inplace active:
  1171. Update(!ped->Get10Mode() ? TRUE : !ped->fHideSelection()); // update selection now
  1172. else
  1173. {
  1174. // Update selection data used for screen display so whenever we
  1175. // get displayed the selection will be displayed.
  1176. _cpSel = GetCp();
  1177. _cchSel = _cch;
  1178. if(!ped->fHideSelection())
  1179. {
  1180. // Selection isn't hidden so tell container to update display
  1181. // when it feels like.
  1182. ped->TxInvalidate();
  1183. ped->TxUpdateWindow();
  1184. }
  1185. }
  1186. CancelModes(); // Cancel word selection mode
  1187. }
  1188. /*
  1189. * CTxtSelection::PointInSel(pt, prcClient, Hit)
  1190. *
  1191. * @mfunc
  1192. * Figures whether a given point is within the selection
  1193. *
  1194. * @rdesc
  1195. * TRUE if point inside selection, FALSE otherwise
  1196. */
  1197. BOOL CTxtSelection::PointInSel (
  1198. const POINTUV pt, //@parm Point in containing window client coords
  1199. RECTUV *prcClient, //@parm Client rectangle can be NULL if active
  1200. HITTEST Hit) const //@parm May be computer Hit value
  1201. {
  1202. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::PointInSel");
  1203. _TEST_INVARIANT_
  1204. if(!_cch || Hit && Hit < HT_Text) // Degenerate range (no selection):
  1205. return FALSE; // mouse can't be in, or Hit not
  1206. // in text
  1207. LONG cpActual;
  1208. _pdp->CpFromPoint(pt, prcClient, NULL, NULL, FALSE, &Hit, NULL, &cpActual);
  1209. if(Hit < HT_Text)
  1210. return FALSE;
  1211. LONG cpMin, cpMost;
  1212. GetRange(cpMin, cpMost);
  1213. return cpActual >= cpMin && cpActual < cpMost;
  1214. }
  1215. ////////////////////////////////// Selection with the mouse ///////////////////////////////////
  1216. /*
  1217. * CTxtSelection::SetCaret(pt, fUpdate)
  1218. *
  1219. * @mfunc
  1220. * Sets caret at a given point
  1221. *
  1222. * @devnote
  1223. * In the plain-text case, placing the caret at the beginning of the
  1224. * line following the final EOP requires some extra code, since the
  1225. * underlying rich-text engine doesn't assign a line to a final EOP
  1226. * (plain-text doesn't currently have the rich-text final EOP). We
  1227. * handle this by checking to see if the count of lines times the
  1228. * plain-text line height is below the actual y position. If so, we
  1229. * move the cp to the end of the story.
  1230. */
  1231. void CTxtSelection::SetCaret(
  1232. const POINTUV pt, //@parm Point of click
  1233. BOOL fUpdate) //@parm If TRUE, update the selection/caret
  1234. {
  1235. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::SetCaret");
  1236. _TEST_INVARIANT_
  1237. LONG cp, cpActual;
  1238. CDispDim dispdim;
  1239. HITTEST Hit;
  1240. RECTUV rcView;
  1241. CLinePtr rp(_pdp);
  1242. CRchTxtPtr rtp(GetPed());
  1243. LONG vp;
  1244. StopGroupTyping();
  1245. // Set caret at point
  1246. if(_pdp->CpFromPoint(pt, NULL, &rtp, &rp, FALSE, &Hit, &dispdim, &cpActual) >= 0)
  1247. {
  1248. cp = rtp.GetCp();
  1249. // If the resolved CP is greater than the cp we are above, then we
  1250. // want to stay backwards.
  1251. BOOL fBeforeCp = cp <= cpActual;
  1252. // Set selection to the correct location. If plain-text
  1253. // multiline control, we need to check to see if pt.v is below
  1254. // the last line of text. If so and if the text ends with an EOP,
  1255. // we need to set the cp at the end of the story and set up to
  1256. // display the caret at the beginning of the line below the last
  1257. // line of text
  1258. if(!IsRich() && _pdp->IsMultiLine()) // Plain-text,
  1259. { // multiline control
  1260. _pdp->GetViewRect(rcView, NULL);
  1261. vp = pt.v + _pdp->GetVpScroll() - rcView.top;
  1262. if(vp > _pdp->LineCount()*rp->GetHeight()) // Below last line of
  1263. { // text
  1264. rtp.Move(tomForward); // Move rtp to end of text
  1265. if(rtp._rpTX.IsAfterEOP()) // If text ends with an
  1266. { // EOP, set up to move
  1267. cp = rtp.GetCp(); // selection there
  1268. rp.Move(-rp.GetIch()); // Set rp._ich = 0 to
  1269. } // set _fCaretNotAtBOL
  1270. } // = FALSE to display
  1271. } // caret at next BOL
  1272. Set(cp, 0);
  1273. if(GetPed()->IsBiDi())
  1274. {
  1275. if(!fBeforeCp)
  1276. _rpCF.AdjustBackward();
  1277. else
  1278. _rpCF.AdjustForward();
  1279. Set_iCF(_rpCF.GetFormat());
  1280. }
  1281. _fCaretNotAtBOL = rp.GetIch() != 0; // Caret OK at BOL if click
  1282. if(fUpdate)
  1283. Update(TRUE);
  1284. else
  1285. UpdateForAutoWord();
  1286. _SelMode = smNone; // Cancel word selection mode
  1287. }
  1288. }
  1289. /*
  1290. * CTxtSelection::SelectWord(pt)
  1291. *
  1292. * @mfunc
  1293. * Select word around a given point
  1294. */
  1295. void CTxtSelection::SelectWord (
  1296. const POINTUV pt) //@parm Point of click
  1297. {
  1298. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::SelectWord");
  1299. _TEST_INVARIANT_
  1300. // Get rp where the hit is
  1301. if(_pdp->CpFromPoint(pt, NULL, this, NULL, FALSE) >= 0)
  1302. {
  1303. if(GetPF()->IsTableRowDelimiter()) // Select table row
  1304. {
  1305. _cch = 0; // Start with IP at pt
  1306. Expander(tomRow, TRUE, NULL, &_cpAnchorMin, &_cpAnchorMost);
  1307. }
  1308. else
  1309. { // Select word at IP
  1310. if(GetCp() == GetAdjustedTextLength())
  1311. { // Special case since
  1312. LONG cpMax = GetTextLength(); // FindWordBreak() can't
  1313. Set(cpMax, cpMax - GetCp()); // move forward in this case
  1314. }
  1315. else
  1316. {
  1317. _cch = 0; // Start with IP at pt
  1318. FindWordBreak(WB_MOVEWORDRIGHT, FALSE); // Go to end of word
  1319. FindWordBreak(WB_MOVEWORDLEFT, TRUE); // Extend to start of word
  1320. }
  1321. GetRange(_cpAnchorMin, _cpAnchorMost);
  1322. GetRange(_cpWordMin, _cpWordMost);
  1323. if(!_fInAutoWordSel)
  1324. _SelMode = smWord;
  1325. // cpMost needs to be the active end
  1326. if(_cch < 0)
  1327. FlipRange();
  1328. }
  1329. Update(FALSE);
  1330. }
  1331. }
  1332. /*
  1333. * CTxtSelection::SelectUnit(pt, Unit)
  1334. *
  1335. * @mfunc
  1336. * Select line/paragraph around a given point and enter
  1337. * line/paragraph selection mode. In Outline View, convert
  1338. * SelectLine to SelectPara, and SelectPara to SelectPara
  1339. * along with all subordinates
  1340. */
  1341. void CTxtSelection::SelectUnit (
  1342. const POINTUV pt, //@parm Point of click
  1343. LONG Unit) //@parm tomLine or tomParagraph
  1344. {
  1345. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::SelectPara");
  1346. _TEST_INVARIANT_
  1347. AssertSz(Unit == tomLine || Unit == tomParagraph,
  1348. "CTxtSelection::SelectPara: Unit must equal tomLine/tomParagraph");
  1349. LONG nHeading;
  1350. CLinePtr rp(_pdp);
  1351. // Get rp and selection active end where the hit is
  1352. if(_pdp->CpFromPoint(pt, NULL, this, &rp, FALSE) >= 0)
  1353. {
  1354. LONG cchBackward, cchForward;
  1355. BOOL fOutline = IsInOutlineView();
  1356. if(Unit == tomLine && !fOutline) // SelectLine
  1357. {
  1358. _cch = 0; // Start with insertion
  1359. cchBackward = -rp.GetIch(); // point at pt
  1360. cchForward = rp->_cch;
  1361. _SelMode = smLine;
  1362. }
  1363. else // SelectParagraph
  1364. {
  1365. cchBackward = rp.FindParagraph(FALSE); // Go to start of para
  1366. cchForward = rp.FindParagraph(TRUE); // Extend to end of para
  1367. _SelMode = smPara;
  1368. }
  1369. Move(cchBackward, FALSE);
  1370. if(Unit == tomParagraph && fOutline) // Move para in outline
  1371. { // view
  1372. rp.AdjustBackward(); // If heading, include
  1373. nHeading = rp.GetHeading(); // subordinate paras
  1374. if(nHeading)
  1375. {
  1376. for(; rp.NextRun(); cchForward += rp->_cch)
  1377. {
  1378. LONG n = rp.GetHeading();
  1379. if(n && n <= nHeading)
  1380. break;
  1381. }
  1382. }
  1383. }
  1384. Move(cchForward, TRUE);
  1385. GetRange(_cpAnchorMin, _cpAnchorMost);
  1386. Update(FALSE);
  1387. }
  1388. }
  1389. /*
  1390. * CTxtSelection::SelectAll()
  1391. *
  1392. * @mfunc
  1393. * Select all text in story
  1394. */
  1395. void CTxtSelection::SelectAll()
  1396. {
  1397. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::SelectAll");
  1398. _TEST_INVARIANT_
  1399. StopGroupTyping();
  1400. LONG cchText = GetTextLength();
  1401. Set(cchText, cchText);
  1402. Update(FALSE);
  1403. }
  1404. /*
  1405. * CTxtSelection::ExtendSelection(pt)
  1406. *
  1407. * @mfunc
  1408. * Extend/Shrink selection (moves active end) to given point
  1409. */
  1410. void CTxtSelection::ExtendSelection (
  1411. const POINTUV pt) //@parm Point to extend to
  1412. {
  1413. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::ExtendSelection");
  1414. _TEST_INVARIANT_
  1415. LONG cch;
  1416. LONG cchPrev = _cch;
  1417. LONG cp;
  1418. LONG cpMin, cpMost;
  1419. BOOL fAfterEOP;
  1420. const BOOL fWasInAutoWordSel = _fInAutoWordSel;
  1421. HITTEST hit;
  1422. INT iDir = 0;
  1423. CTxtEdit * ped = GetPed();
  1424. CLinePtr rp(_pdp);
  1425. CRchTxtPtr rtp(ped);
  1426. StopGroupTyping();
  1427. // Get rp and rtp at the point pt
  1428. if(_pdp->CpFromPoint(pt, NULL, &rtp, &rp, TRUE, &hit) < 0 || hit == HT_RightOfText)
  1429. return;
  1430. // If we are in word, line, or paragraph select mode, we need to make
  1431. // sure the active end is correct. If we are extending backward from
  1432. // the first Unit selected, we want the active end to be at cpMin. If
  1433. // we are extending forward from the first Unit selected, we want the
  1434. // active end to be at cpMost.
  1435. if(_SelMode != smNone)
  1436. {
  1437. cch = _cpAnchorMost - _cpAnchorMin;
  1438. GetRange(cpMin, cpMost);
  1439. cp = rtp.GetCp();
  1440. if(cp <= cpMin && _cch > 0) // If active end changes,
  1441. Set(_cpAnchorMin, -cch); // select the original
  1442. // Unit (will be extended
  1443. if(cp >= cpMost && _cch < 0) // below)
  1444. Set(_cpAnchorMost, cch);
  1445. }
  1446. cch = rp.GetIch();
  1447. if(_SelMode > smWord && cch == rp->_cch) // If in line or para select
  1448. { // modes and pt at EOL,
  1449. rtp.Move(-cch); // make sure we stay on that
  1450. rp.Move(-cch); // line
  1451. cch = 0;
  1452. }
  1453. SetCp(rtp.GetCp(), TRUE); // Move active end to pt
  1454. // Caret OK at BOL _unless_
  1455. _fCaretNotAtBOL = _cch > 0 || cch == rp->_cch;// forward selection
  1456. // Now adjust selection
  1457. if(_SelMode == smLine) // depending on mode
  1458. { // Extend selection by line
  1459. if(_cch >= 0) // Active end at cpMost
  1460. cch -= rp->_cch; // Setup to add chars to EOL
  1461. Move(-cch, TRUE);
  1462. }
  1463. else if(_SelMode == smPara)
  1464. Move(rp.FindParagraph(_cch >= 0), TRUE);// Extend selection by para
  1465. else
  1466. {
  1467. // If the sign of _cch has changed this means that the direction
  1468. // of the selection is changing and we want to reset the auto
  1469. // selection information.
  1470. if((_cch ^ cchPrev) < 0)
  1471. {
  1472. _fAutoSelectAborted = FALSE;
  1473. _cpWordMin = _cpAnchorMin;
  1474. _cpWordMost = _cpAnchorMost;
  1475. }
  1476. cp = rtp.GetCp();
  1477. fAfterEOP = rtp._rpTX.IsAfterEOP();
  1478. _fInAutoWordSel = _SelMode != smWord && GetPed()->TxGetAutoWordSel()
  1479. && !_fAutoSelectAborted
  1480. && (cp < _cpWordMin || cp > _cpWordMost);
  1481. if(_fInAutoWordSel && !fWasInAutoWordSel)
  1482. {
  1483. CTxtPtr txtptr(GetPed(), _cpAnchor);
  1484. // Extend both ends dead to word boundaries
  1485. ExtendToWordBreak(fAfterEOP,
  1486. _cch < 0 ? WB_MOVEWORDLEFT : WB_MOVEWORDRIGHT);
  1487. if(_cch < 0)
  1488. {
  1489. // Direction is left so update word border on left
  1490. _cpWordPrev = _cpWordMin;
  1491. _cpWordMin = GetCp();
  1492. }
  1493. else
  1494. {
  1495. // Direction is right so update word border on right
  1496. _cpWordPrev = _cpWordMost;
  1497. _cpWordMost = GetCp();
  1498. }
  1499. // If we are at start of a word already, we don't need to extend
  1500. // selection in other direction
  1501. if(!txtptr.IsAtBOWord() && txtptr.GetChar() != ' ')
  1502. {
  1503. FlipRange();
  1504. Move(_cpAnchor - GetCp(), TRUE);// Extend from anchor
  1505. FindWordBreak(_cch < 0 ? WB_MOVEWORDLEFT : WB_MOVEWORDRIGHT, TRUE);
  1506. if(_cch > 0) // Direction is right so
  1507. _cpWordMost = GetCp(); // update word border on right
  1508. else // Direction is left so
  1509. _cpWordMin = GetCp(); // update word border on left
  1510. FlipRange();
  1511. }
  1512. }
  1513. else if(_fInAutoWordSel || _SelMode == smWord)
  1514. {
  1515. // Save direction
  1516. iDir = cp <= _cpWordMin ? WB_MOVEWORDLEFT : WB_MOVEWORDRIGHT;
  1517. if(_SelMode == smWord) // Extend selection by word
  1518. {
  1519. if(cp > _cpAnchorMost || cp < _cpAnchorMin)
  1520. FindWordBreak(iDir, TRUE);
  1521. else if(_cch <= 0) // Maintain current active end
  1522. Set(_cpAnchorMin, _cpAnchorMin - _cpAnchorMost);
  1523. else
  1524. Set(_cpAnchorMost, _cpAnchorMost - _cpAnchorMin);
  1525. }
  1526. else
  1527. ExtendToWordBreak(fAfterEOP, iDir);
  1528. if(_fInAutoWordSel)
  1529. {
  1530. if(WB_MOVEWORDLEFT == iDir)
  1531. {
  1532. // Direction is left so update word border on left
  1533. _cpWordPrev = _cpWordMin;
  1534. _cpWordMin = GetCp();
  1535. }
  1536. else
  1537. {
  1538. // Direction is right so update word border on right
  1539. _cpWordPrev = _cpWordMost;
  1540. _cpWordMost = GetCp();
  1541. }
  1542. }
  1543. }
  1544. else if(fWasInAutoWordSel)
  1545. {
  1546. // If we are in between where the previous word ended and
  1547. // the cp we auto selected to, then we want to stay in
  1548. // auto select mode.
  1549. if(_cch < 0)
  1550. {
  1551. if(cp >= _cpWordMin && cp < _cpWordPrev)
  1552. {
  1553. // Set direction for end of word search
  1554. iDir = WB_MOVEWORDLEFT;
  1555. // Mark that we are still in auto select mode
  1556. _fInAutoWordSel = TRUE;
  1557. }
  1558. }
  1559. else if(cp <= _cpWordMost && cp >= _cpWordPrev)
  1560. {
  1561. // Mark that we are still in auto select mode
  1562. _fInAutoWordSel = TRUE;
  1563. // Set direction for end of word search
  1564. iDir = WB_MOVEWORDRIGHT;
  1565. }
  1566. //We have to check to see if we are on the boundary between
  1567. //words because we don't want to extend the selection until
  1568. //we are actually beyond the current word.
  1569. if(cp != _cpWordMost && cp != _cpWordMin)
  1570. {
  1571. if(_fInAutoWordSel)
  1572. {
  1573. // Auto selection still on so make sure we have the
  1574. // entire word we are on selected
  1575. ExtendToWordBreak(fAfterEOP, iDir);
  1576. }
  1577. else
  1578. {
  1579. // FUTURE: Word has a behavior where it extends the
  1580. // selection one word at a time unless you back up
  1581. // and then start extending the selection again, in
  1582. // which case it extends one char at a time. We
  1583. // follow this behavior. However, Word will resume
  1584. // extending a word at a time if you continue extending
  1585. // for several words. We just keep extending on char
  1586. // at a time. We might want to change this sometime.
  1587. _fAutoSelectAborted = TRUE;
  1588. }
  1589. }
  1590. }
  1591. if(_fAutoSelectAborted)
  1592. {
  1593. // If we are in the range of a word we previously selected
  1594. // we want to leave that selected. If we have moved back
  1595. // a word we want to pop back an entire word. Otherwise,
  1596. // leave the cp were it is.
  1597. if(_cch < 0)
  1598. {
  1599. if(cp > _cpWordMin && cp < _cpWordPrev)
  1600. {
  1601. // In the range leave the range at the beginning of the word
  1602. ExtendToWordBreak(fAfterEOP, WB_MOVEWORDLEFT);
  1603. }
  1604. else if(cp >= _cpWordPrev)
  1605. {
  1606. AutoSelGoBackWord(&_cpWordMin,
  1607. WB_MOVEWORDRIGHT, WB_MOVEWORDLEFT);
  1608. }
  1609. }
  1610. else if(cp < _cpWordMost && cp >= _cpWordPrev)
  1611. {
  1612. // In the range leave the range at the beginning of the word
  1613. ExtendToWordBreak(fAfterEOP, WB_MOVEWORDRIGHT);
  1614. }
  1615. else if(cp < _cpWordPrev)
  1616. {
  1617. AutoSelGoBackWord(&_cpWordMost,
  1618. WB_MOVEWORDLEFT, WB_MOVEWORDRIGHT);
  1619. }
  1620. }
  1621. }
  1622. // An OLE object cannot have an anchor point <b> inside </b> it,
  1623. // but sometimes we'd like it to behave like a word. So, if
  1624. // the direction changed, the object has to stay selected --
  1625. // this is the "right thing" (kind of word selection mode)
  1626. // If we had something selected and the direction changed
  1627. if(cchPrev && (_cch ^ cchPrev) < 0)
  1628. {
  1629. FlipRange();
  1630. // See if an object was selected on the other end
  1631. BOOL fObjectWasSelected = (_cch > 0 ? _rpTX.GetChar() : GetPrevChar())
  1632. == WCH_EMBEDDING;
  1633. // If it was, we want it to stay selected
  1634. if(fObjectWasSelected)
  1635. Move(_cch > 0 ? 1 : -1, TRUE);
  1636. FlipRange();
  1637. }
  1638. Update(TRUE);
  1639. }
  1640. /*
  1641. * CTxtSelection::ExtendToWordBreak (fAfterEOP, iAction)
  1642. *
  1643. * @mfunc
  1644. * Moves active end of selection to the word break in the direction
  1645. * given by iDir unless fAfterEOP = TRUE. When this is TRUE, the
  1646. * cursor just follows an EOP marker and selection should be suppressed.
  1647. * Otherwise moving the cursor to the left of the left margin would
  1648. * select the EOP on the line above, and moving the cursor to the
  1649. * right of the right margin would select the first word in the line
  1650. * below.
  1651. */
  1652. void CTxtSelection::ExtendToWordBreak (
  1653. BOOL fAfterEOP, //@parm Cursor is after an EOP
  1654. INT iAction) //@parm Word break action (WB_MOVEWORDRIGHT/LEFT)
  1655. {
  1656. if(!fAfterEOP)
  1657. FindWordBreak(iAction, TRUE);
  1658. }
  1659. /*
  1660. * CTxtSelection::CancelModes(fAutoWordSel)
  1661. *
  1662. * @mfunc
  1663. * Cancel either all modes or Auto Select Word mode only
  1664. */
  1665. void CTxtSelection::CancelModes (
  1666. BOOL fAutoWordSel) //@parm TRUE cancels Auto Select Word mode only
  1667. { // FALSE cancels word, line and para sel mode
  1668. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::CancelModes");
  1669. _TEST_INVARIANT_
  1670. if(fAutoWordSel)
  1671. {
  1672. if(_fInAutoWordSel)
  1673. {
  1674. _fInAutoWordSel = FALSE;
  1675. _fAutoSelectAborted = FALSE;
  1676. }
  1677. }
  1678. else
  1679. _SelMode = smNone;
  1680. }
  1681. /////////////////////////////////// Keyboard movements ////////////////////////////////////
  1682. /*
  1683. * CTxtSelection::Left(fCtrl, fExtend)
  1684. *
  1685. * @mfunc
  1686. * do what cursor-keypad left-arrow key is supposed to do
  1687. *
  1688. * @rdesc
  1689. * TRUE iff movement occurred
  1690. *
  1691. * @comm
  1692. * Left/Right-arrow IPs can go to within one character (treating CRLF
  1693. * as a character) of EOL. They can never be at the actual EOL, so
  1694. * _fCaretNotAtBOL is always FALSE for these cases. This includes
  1695. * the case with a right-arrow collapsing a selection that goes to
  1696. * the EOL, i.e, the caret ends up at the next BOL. Furthermore,
  1697. * these cases don't care whether the initial caret position is at
  1698. * the EOL or the BOL of the next line. All other cursor keypad
  1699. * commands may care.
  1700. *
  1701. * @devnote
  1702. * fExtend is TRUE iff Shift key is pressed or being simulated
  1703. */
  1704. BOOL CTxtSelection::Left (
  1705. BOOL fCtrl, //@parm TRUE iff Ctrl key is pressed (or being simulated)
  1706. BOOL fExtend) //@parm Extend range iff TRUE
  1707. {
  1708. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::Left");
  1709. _TEST_INVARIANT_
  1710. CancelModes();
  1711. StopGroupTyping();
  1712. if(!fExtend && _cch) // Collapse selection to
  1713. { // nearest whole Unit before
  1714. LONG cp; // cpMin
  1715. if(fCtrl)
  1716. Expander(tomWord, FALSE, NULL, &cp, NULL);
  1717. Collapser(tomStart); // Collapse to cpMin
  1718. }
  1719. else // Not collapsing selection
  1720. {
  1721. if (!GetCp() || // Already at beginning of
  1722. !BypassHiddenText(tomBackward, fExtend))// story
  1723. {
  1724. Beep();
  1725. return FALSE;
  1726. }
  1727. if(IsInOutlineView() && (_fSelHasEOP || // If outline view with EOP
  1728. fExtend && _rpTX.IsAfterEOP())) // now or will have after
  1729. { // this command,
  1730. return Up(FALSE, fExtend); // treat as up arrow
  1731. }
  1732. if(fCtrl) // WordLeft
  1733. FindWordBreak(WB_MOVEWORDLEFT, fExtend);
  1734. else // CharLeft
  1735. BackupCRLF(CSC_SNAPTOCLUSTER, fExtend);
  1736. }
  1737. _fCaretNotAtBOL = FALSE; // Caret always OK at BOL
  1738. Update(TRUE);
  1739. return TRUE;
  1740. }
  1741. /*
  1742. * CTxtSelection::Right(fCtrl, fExtend)
  1743. *
  1744. * @mfunc
  1745. * do what cursor-keypad right-arrow key is supposed to do
  1746. *
  1747. * @rdesc
  1748. * TRUE iff movement occurred
  1749. *
  1750. * @comm
  1751. * Right-arrow selection can go to the EOL, but the cp of the other
  1752. * end identifies whether the selection ends at the EOL or starts at
  1753. * the beginning of the next line. Hence here and in general for
  1754. * selections, _fCaretNotAtBOL is not needed to resolve EOL/BOL
  1755. * ambiguities. It should be set to FALSE to get the correct
  1756. * collapse character. See also comments for Left() above.
  1757. *
  1758. * @devnote
  1759. * fExtend is TRUE iff Shift key is pressed or being simulated
  1760. */
  1761. BOOL CTxtSelection::Right (
  1762. BOOL fCtrl, //@parm TRUE iff Ctrl key is pressed (or being simulated)
  1763. BOOL fExtend) //@parm Extend range iff TRUE
  1764. {
  1765. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::Right");
  1766. _TEST_INVARIANT_
  1767. CancelModes();
  1768. StopGroupTyping();
  1769. if(!fExtend && _cch) // Collapse selection to
  1770. { // nearest whole Unit after
  1771. LONG cp; // cpMost
  1772. if(fCtrl)
  1773. Expander(tomWord, FALSE, NULL, NULL, &cp);
  1774. Collapser(tomEnd);
  1775. }
  1776. else // Not collapsing selection
  1777. {
  1778. LONG cchText = fExtend ? GetTextLength() : GetAdjustedTextLength();
  1779. if (GetCp() >= cchText || // Already at end of story
  1780. !BypassHiddenText(tomForward, fExtend))
  1781. {
  1782. Beep(); // Tell the user
  1783. return FALSE;
  1784. }
  1785. if(IsInOutlineView() && _fSelHasEOP) // If outline view with EOP
  1786. return Down(FALSE, fExtend); // Treat as down arrow
  1787. if(fCtrl) // WordRight
  1788. FindWordBreak(WB_MOVEWORDRIGHT, fExtend);
  1789. else // CharRight
  1790. AdvanceCRLF(CSC_SNAPTOCLUSTER, fExtend);
  1791. }
  1792. _fCaretNotAtBOL = fExtend; // If extending to EOL, need
  1793. Update(TRUE); // TRUE to get _upCaretReally
  1794. return TRUE; // at EOL
  1795. }
  1796. /*
  1797. * CTxtSelection::Up(fCtrl, fExtend)
  1798. *
  1799. * @mfunc
  1800. * do what cursor-keypad up-arrow key is supposed to do
  1801. *
  1802. * @rdesc
  1803. * TRUE iff movement occurred
  1804. *
  1805. * @comm
  1806. * Up arrow doesn't go to EOL regardless of _upCaretPosition (stays
  1807. * to left of EOL break character), so _fCaretNotAtBOL is always FALSE
  1808. * for Up arrow. Ctrl-Up/Down arrows always end up at BOPs or the EOD.
  1809. *
  1810. * @devnote
  1811. * fExtend is TRUE iff Shift key is pressed or being simulated
  1812. */
  1813. BOOL CTxtSelection::Up (
  1814. BOOL fCtrl, //@parm TRUE iff Ctrl key is pressed (or being simulated)
  1815. BOOL fExtend) //@parm Extend range iff TRUE
  1816. {
  1817. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::Up");
  1818. _TEST_INVARIANT_
  1819. LONG cch;
  1820. LONG cchSave = _cch; // Save starting position for
  1821. LONG cpSave = GetCp(); // change check
  1822. BOOL fCollapse = _cch && !fExtend; // Collapse nondegenerate sel
  1823. BOOL fPTNotAtEnd;
  1824. POINTUV pt;
  1825. CLinePtr rp(_pdp);
  1826. LONG upCaretReally = _upCaretReally; // Save desired caret x pos
  1827. CancelModes();
  1828. StopGroupTyping();
  1829. if(fCollapse) // Collapse selection at cpMin
  1830. {
  1831. Collapser(tomTrue);
  1832. _fCaretNotAtBOL = FALSE; // Selections can't begin at
  1833. } // EOL
  1834. if(_pdp->PointFromTp(*this, NULL, _fCaretNotAtBOL, pt, &rp, 0, NULL) < 0)
  1835. return FALSE;
  1836. if(fCtrl) // Move to beginning of para
  1837. {
  1838. if (!fCollapse && // If no selection collapsed
  1839. rp > 0 && !rp.GetIch()) // and are at BOL,
  1840. { // backup to prev BOL to make
  1841. rp.PrevRun(); // sure we move to prev. para
  1842. Move(-rp->_cch, fExtend);
  1843. }
  1844. Move(rp.FindParagraph(FALSE), fExtend); // Go to beginning of para
  1845. _fCaretNotAtBOL = FALSE; // Caret always OK at BOL
  1846. }
  1847. else // Move up a line
  1848. { // If on first line, can't
  1849. Assert(rp >= 0); // go up
  1850. if(InTable())
  1851. {
  1852. WCHAR ch;
  1853. LONG cpRowStart;
  1854. CTxtRange rg(*this); // Need rg for rg.FindRow()
  1855. rg.Move(-rp.GetIch(), fExtend); // Move to beginning of line
  1856. rp.SetIch(0);
  1857. LONG cpBOL = rg.GetCp(); // Save current cp for check
  1858. while(1) // While previous char is
  1859. { // CELL or start of row,
  1860. cch = 0; // move to start of row
  1861. do
  1862. { // Look at previous char &
  1863. cch = rg.BackupCRLF(CSC_NORMAL, fExtend);// save cch moved
  1864. ch = rg._rpTX.GetChar();
  1865. } // Backup over span of table
  1866. while(rg.GetCp() && ch == STARTFIELD);// row starts
  1867. if(ch != CELL)
  1868. {
  1869. if(cch < 0 && ch != STARTFIELD)
  1870. rg.AdvanceCRLF(CSC_NORMAL, fExtend);// Prev char not CELL
  1871. break; // or row start: move past it
  1872. }
  1873. rg.FindRow(&cpRowStart, NULL); // Backup to start of
  1874. rg.SetCp(cpRowStart, fExtend); // current table row
  1875. }
  1876. if(rg.GetCp() < cpBOL) // Moved back
  1877. {
  1878. CLinePtr rp0(_pdp); // Move rp to new position
  1879. rp0.SetCp(rg.GetCp(), FALSE, 1);
  1880. rp = rp0;
  1881. }
  1882. if(rp > 0) // Row above exists, so move
  1883. SetCp(rg.GetCp(), fExtend); // selection to start of row
  1884. }
  1885. fPTNotAtEnd = !CheckPlainTextFinalEOP();// Always TRUE for rich text
  1886. if(rp == 0 && fPTNotAtEnd) // Can't move up
  1887. UpdateCaret(TRUE); // Be sure caret in view
  1888. else
  1889. {
  1890. BOOL fSelHasEOPInOV = IsInOutlineView() && _fSelHasEOP;
  1891. if(fSelHasEOPInOV && _cch > 0)
  1892. {
  1893. rp.AdjustBackward();
  1894. cch = rp->_cch;
  1895. rp.Move(-cch); // Go to start of line
  1896. Assert(!rp.GetIch());
  1897. cch -= rp.FindParagraph(FALSE); // Ensure start of para in
  1898. } // case of word wrap
  1899. else
  1900. {
  1901. cch = 0;
  1902. if(fPTNotAtEnd)
  1903. {
  1904. cch = rp.GetIch();
  1905. rp--;
  1906. }
  1907. cch += rp->_cch;
  1908. }
  1909. Move(-cch, fExtend); // Move to previous BOL
  1910. if(fSelHasEOPInOV && !_fSelHasEOP) // If sel had EOP but doesn't
  1911. { // after Move, must be IP
  1912. Assert(!_cch); // Suppress restore of
  1913. upCaretReally = -1; // _upCaretReally
  1914. }
  1915. else if(!SetUpPosition(upCaretReally,// Set this cp corresponding
  1916. rp, TRUE, fExtend)) // to upCaretReally here, but
  1917. { // agree on Down()
  1918. Set(cpSave, cchSave); // Failed: restore selection
  1919. }
  1920. }
  1921. }
  1922. if(GetCp() == cpSave && _cch == cchSave)
  1923. {
  1924. // Continue to select to the beginning of the first line
  1925. // This is what 1.0 is doing
  1926. if(fExtend)
  1927. return Home(fCtrl, TRUE);
  1928. _upCaretReally = upCaretReally;
  1929. Beep(); // Nothing changed, so beep
  1930. return FALSE;
  1931. }
  1932. Update(TRUE); // Update and then restore
  1933. if(!_cch && !fCtrl && upCaretReally >= 0) // _upCaretReally conditionally
  1934. _upCaretReally = upCaretReally; // Need to use _cch instead of
  1935. // cchSave in case of collapse
  1936. return TRUE;
  1937. }
  1938. /*
  1939. * CTxtSelection::Down(fCtrl, fExtend)
  1940. *
  1941. * @mfunc
  1942. * do what cursor-keypad down-arrow key is supposed to do
  1943. *
  1944. * @rdesc
  1945. * TRUE iff movement occurred
  1946. *
  1947. * @comm
  1948. * Down arrow can go to the EOL if the _upCaretPosition (set by
  1949. * horizontal motions) is past the end of the line, so
  1950. * _fCaretNotAtBOL needs to be TRUE for this case.
  1951. *
  1952. * @devnote
  1953. * fExtend is TRUE iff Shift key is pressed or being simulated
  1954. */
  1955. BOOL CTxtSelection::Down (
  1956. BOOL fCtrl, //@parm TRUE iff Ctrl key is pressed (or being simulated)
  1957. BOOL fExtend) //@parm Extend range iff TRUE
  1958. {
  1959. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::Down");
  1960. _TEST_INVARIANT_
  1961. LONG cch;
  1962. LONG cchSave = _cch; // Save starting position for
  1963. LONG cpSave = GetCp(); // change check
  1964. BOOL fCollapse = _cch && !fExtend; // Collapse nondegenerate sel
  1965. POINTUV pt;
  1966. CLinePtr rp(_pdp);
  1967. LONG upCaretReally = _upCaretReally; // Save _upCaretReally
  1968. CancelModes();
  1969. StopGroupTyping();
  1970. if(fCollapse) // Collapse at cpMost
  1971. {
  1972. Collapser(tomEnd);
  1973. _fCaretNotAtBOL = TRUE; // Selections can't end at BOL
  1974. }
  1975. LONG ili = _pdp->PointFromTp(*this, NULL, _fCaretNotAtBOL, pt, &rp, 0, NULL);
  1976. if(ili < 0)
  1977. return FALSE;
  1978. if(fCtrl) // Move to next para
  1979. {
  1980. Move(rp.FindParagraph(TRUE), fExtend); // Go to end of para
  1981. if(IsInOutlineView() && !BypassHiddenText(tomForward, fExtend))
  1982. SetCp(cpSave, fExtend);
  1983. else
  1984. _fCaretNotAtBOL = FALSE; // Next para is never at EOL
  1985. }
  1986. else if(_pdp->WaitForRecalcIli(ili + 1)) // Go to next line
  1987. {
  1988. BOOL fSelHasEOPInOV = IsInOutlineView() && _fSelHasEOP;
  1989. if(fSelHasEOPInOV && _cch < 0)
  1990. cch = rp.FindParagraph(TRUE);
  1991. else
  1992. {
  1993. cch = rp.GetCchLeft(); // Move selection to end
  1994. rp.NextRun(); // of current line
  1995. }
  1996. Move(cch, fExtend);
  1997. while(GetPrevChar() == CELL) // Went past cell end:
  1998. { // goto end of row
  1999. LONG cpRowEnd;
  2000. do
  2001. {
  2002. FindRow(NULL, &cpRowEnd);
  2003. SetCp(cpRowEnd, fExtend);
  2004. }
  2005. while(_rpTX.GetChar() == CELL); // Table at end of cell
  2006. CLinePtr rp0(_pdp);
  2007. rp0.SetCp(cpRowEnd, _fCaretNotAtBOL, 1);
  2008. rp = rp0;
  2009. }
  2010. if(fSelHasEOPInOV && !_fSelHasEOP) // If sel had EOP but doesn't
  2011. { // after Move, must be IP
  2012. Assert(!_cch); // Suppress restore of
  2013. upCaretReally = -1; // _upCaretReally
  2014. }
  2015. else if(!SetUpPosition(upCaretReally, // Set *this to cp <--> x
  2016. rp, FALSE, fExtend))
  2017. { // Failed: restore selection
  2018. Set(cpSave, cchSave);
  2019. }
  2020. }
  2021. else if(!fExtend) // No more lines to pass
  2022. // && _pdp->GetVScroll() + _pdp->GetDvpView() < _pdp->GetHeight())
  2023. {
  2024. if (!IsRich() && _pdp->IsMultiLine() && // Plain-text, multiline
  2025. !_fCaretNotAtBOL) // control with caret OK
  2026. { // at BOL
  2027. cch = Move(rp.GetCchLeft(), fExtend);// Move selection to end
  2028. if(!_rpTX.IsAfterEOP()) // If control doesn't end
  2029. Move(-cch, fExtend); // with EOP, go back
  2030. }
  2031. UpdateCaret(TRUE); // Be sure caret in view
  2032. }
  2033. if(GetCp() == cpSave && _cch == cchSave)
  2034. {
  2035. // Continue to select to the end of the lastline
  2036. // This is what 1.0 is doing.
  2037. if(fExtend)
  2038. return End(fCtrl, TRUE);
  2039. _upCaretReally = upCaretReally;
  2040. Beep(); // Nothing changed, so beep
  2041. return FALSE;
  2042. }
  2043. Update(TRUE); // Update and then
  2044. if(!_cch && !fCtrl && upCaretReally >= 0) // restore _upCaretReally
  2045. _upCaretReally = upCaretReally; // Need to use _cch instead of
  2046. return TRUE; // cchSave in case of collapse
  2047. }
  2048. /*
  2049. * CTxtSelection::SetUpPosition(upCaret, rp, fBottomLine, fExtend)
  2050. *
  2051. * @mfunc
  2052. * Put this text ptr at cp nearest to xCaret. If xCaret is in right
  2053. * margin, we put caret either at EOL (for lines with no para mark),
  2054. * or just before para mark
  2055. *
  2056. * @rdesc
  2057. * TRUE iff could create measurer
  2058. */
  2059. BOOL CTxtSelection::SetUpPosition(
  2060. LONG upCaret, //@parm Desired horizontal coordinate
  2061. CLinePtr& rp, //@parm Line ptr identifying line to check
  2062. BOOL fBottomLine, //@parm TRUE if use bottom line of nested display
  2063. BOOL fExtend) //@parm Extend range iff TRUE
  2064. {
  2065. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::SetUpPosition");
  2066. _TEST_INVARIANT_
  2067. LONG cch = 0;
  2068. if(IsInOutlineView())
  2069. {
  2070. BOOL fSelHasEOP = _fSelHasEOP;
  2071. rp.AdjustForward();
  2072. _fCaretNotAtBOL = FALSE; // Leave at start of line
  2073. while(rp->_fCollapsed)
  2074. {
  2075. if(_fMoveBack)
  2076. {
  2077. if(!rp.PrevRun()) // No more uncollapsed text
  2078. return FALSE; // before current cp
  2079. cch -= rp->_cch;
  2080. }
  2081. else
  2082. {
  2083. cch += rp->_cch;
  2084. if(!rp.NextRun()) // No more uncollapsed text
  2085. return FALSE; // after current cp
  2086. if(fExtend && _cch > 0)
  2087. _fCaretNotAtBOL = TRUE; // Leave at end of line
  2088. }
  2089. }
  2090. if(cch)
  2091. Move(cch, fExtend);
  2092. if(fSelHasEOP)
  2093. return TRUE;
  2094. }
  2095. POINTUV pt;
  2096. UINT talign = TA_BASELINE;
  2097. if(fBottomLine)
  2098. {
  2099. if(rp->IsNestedLayout())
  2100. talign = TA_TOP | TA_CELLTOP;
  2101. else
  2102. fBottomLine = FALSE;
  2103. }
  2104. if(_pdp->PointFromTp(*this, NULL, FALSE, pt, NULL, talign, NULL) < 0)
  2105. return FALSE;
  2106. if(fBottomLine)
  2107. pt.v += rp->GetHeight() - 3;
  2108. HITTEST hit;
  2109. RECTUV rcView;
  2110. _pdp->GetViewRect(rcView, NULL);
  2111. if(!upCaret && _rpTX.IsAtTRD(STARTFIELD))
  2112. {
  2113. // At table row start at position before row: move over to get into
  2114. // first cell. This solves some of the up/down-arrow errors for
  2115. // upCaret = 0, but the caret still won't move for some nested table
  2116. // scenarios (the needed upCaret measurement is more complicated than
  2117. // that given here).
  2118. LONG dupInch = MulDiv(_pdp->GetDxpInch(), _pdp->GetZoomNumerator(), _pdp->GetZoomDenominator());
  2119. LONG dup = MulDiv(GetPF()->_dxOffset, dupInch, LX_PER_INCH);
  2120. CTxtPtr tp(_rpTX);
  2121. while(tp.IsAtTRD(STARTFIELD))
  2122. {
  2123. upCaret += dup;
  2124. tp.AdvanceCRLF(FALSE);
  2125. }
  2126. }
  2127. pt.u = upCaret + rcView.left;
  2128. LONG cp = _pdp->CpFromPoint(pt, NULL, NULL, &rp, FALSE, &hit, NULL, NULL);
  2129. if(cp < 0)
  2130. return FALSE; // If failed, restore sel
  2131. SetCp(cp, fExtend);
  2132. _fCaretNotAtBOL = rp.GetIch() != 0;
  2133. return TRUE;
  2134. }
  2135. /*
  2136. * CTxtSelection::GetUpCaretReally()
  2137. *
  2138. * @mfunc
  2139. * Get _upCaretReally - horizontal scrolling + left margin
  2140. *
  2141. * @rdesc
  2142. * x caret really
  2143. */
  2144. LONG CTxtSelection::GetUpCaretReally()
  2145. {
  2146. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::GetUpCaretReally");
  2147. _TEST_INVARIANT_
  2148. RECTUV rcView;
  2149. _pdp->GetViewRect(rcView);
  2150. return _upCaretReally - _pdp->GetUpScroll() + rcView.left;
  2151. }
  2152. /*
  2153. * CTxtSelection::Home(fCtrl, fExtend)
  2154. *
  2155. * @mfunc
  2156. * do what cursor-keypad Home key is supposed to do
  2157. *
  2158. * @rdesc
  2159. * TRUE iff movement occurred
  2160. *
  2161. * @devnote
  2162. * fExtend is TRUE iff Shift key is pressed or being simulated
  2163. */
  2164. BOOL CTxtSelection::Home (
  2165. BOOL fCtrl, //@parm TRUE iff Ctrl key is pressed (or being simulated)
  2166. BOOL fExtend) //@parm Extend range iff TRUE
  2167. {
  2168. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::Home");
  2169. _TEST_INVARIANT_
  2170. const LONG cchSave = _cch;
  2171. const LONG cpSave = GetCp();
  2172. CancelModes();
  2173. StopGroupTyping();
  2174. if(fCtrl) // Move to start of document
  2175. SetCp(0, fExtend);
  2176. else
  2177. {
  2178. CLinePtr rp(_pdp);
  2179. if(_cch && !fExtend) // Collapse at cpMin
  2180. {
  2181. Collapser(tomStart);
  2182. _fCaretNotAtBOL = FALSE; // Selections can't start at
  2183. } // EOL
  2184. rp.SetCp(GetCp(), _fCaretNotAtBOL, 2); // Define line ptr for
  2185. Move(-rp.GetIch(), fExtend); // current state. Now BOL
  2186. }
  2187. _fCaretNotAtBOL = FALSE; // Caret always goes to BOL
  2188. _fHomeOrEnd = TRUE;
  2189. if(!MatchKeyboardToPara() && GetCp() == cpSave && _cch == cchSave)
  2190. {
  2191. Beep(); // No change, so beep
  2192. _fHomeOrEnd = FALSE;
  2193. return FALSE;
  2194. }
  2195. Update(TRUE);
  2196. _fHomeOrEnd = FALSE;
  2197. _fUpdatedFromCp0 = FALSE; // UI commands don't set this
  2198. return TRUE;
  2199. }
  2200. /*
  2201. * CTxtSelection::End(fCtrl, fExtend)
  2202. *
  2203. * @mfunc
  2204. * do what cursor-keypad End key is supposed to do
  2205. *
  2206. * @rdesc
  2207. * TRUE iff movement occurred
  2208. *
  2209. * @comm
  2210. * On lines without paragraph marks (EOP), End can go all the way
  2211. * to the EOL. Since this character position (cp) is the same as
  2212. * that for the start of the next line, we need _fCaretNotAtBOL to
  2213. * distinguish between the two possible caret positions.
  2214. *
  2215. * @devnote
  2216. * fExtend is TRUE iff Shift key is pressed or being simulated
  2217. */
  2218. BOOL CTxtSelection::End (
  2219. BOOL fCtrl, //@parm TRUE iff Ctrl key is pressed (or being simulated)
  2220. BOOL fExtend) //@parm Extend range iff TRUE
  2221. {
  2222. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::End");
  2223. _TEST_INVARIANT_
  2224. LONG cch;
  2225. const LONG cchSave = _cch;
  2226. const LONG cpSave = GetCp();
  2227. CLinePtr rp(_pdp);
  2228. CancelModes();
  2229. StopGroupTyping();
  2230. if(fCtrl) // Move to end of document
  2231. {
  2232. SetCp(GetTextLength(), fExtend);
  2233. _fCaretNotAtBOL = FALSE;
  2234. goto Exit;
  2235. }
  2236. else if(!fExtend && _cch) // Collapse at cpMost
  2237. {
  2238. Collapser(tomEnd);
  2239. _fCaretNotAtBOL = TRUE; // Selections can't end at BOL
  2240. }
  2241. rp.SetCp(GetCp(), _fCaretNotAtBOL, 2); // Initialize line ptr on
  2242. // on innermost line
  2243. cch = rp.GetCchLeft(); // Default target pos in line
  2244. if(!Move(cch, fExtend)) // Move active end to EOL
  2245. goto Exit; // Failed (at end of story)
  2246. if(!fExtend && rp->_cchEOP && _rpTX.IsAfterEOP())// Not extending & have
  2247. cch += BackupCRLF(CSC_NORMAL, FALSE); // EOP so backup before EOP
  2248. _fCaretNotAtBOL = cch != 0; // Decide ambiguous caret pos
  2249. // by whether at BOL
  2250. Exit:
  2251. if(!MatchKeyboardToPara() && GetCp() == cpSave && _cch == cchSave)
  2252. {
  2253. Beep(); // No change, so Beep
  2254. return FALSE;
  2255. }
  2256. _fHomeOrEnd = TRUE;
  2257. Update(TRUE);
  2258. _fHomeOrEnd = FALSE;
  2259. return TRUE;
  2260. }
  2261. /*
  2262. * CTxtSelection::PageUp(fCtrl, fExtend)
  2263. *
  2264. * @mfunc
  2265. * do what cursor-keypad PgUp key is supposed to do
  2266. *
  2267. * @rdesc
  2268. * TRUE iff movement occurred
  2269. *
  2270. * @devnote
  2271. * fExtend is TRUE iff Shift key is pressed or being simulated
  2272. */
  2273. BOOL CTxtSelection::PageUp (
  2274. BOOL fCtrl, //@parm TRUE iff Ctrl key is pressed (or being simulated)
  2275. BOOL fExtend) //@parm Extend range iff TRUE
  2276. {
  2277. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::PageUp");
  2278. _TEST_INVARIANT_
  2279. const LONG cchSave = _cch;
  2280. const LONG cpSave = GetCp();
  2281. LONG upCaretReally = _upCaretReally;
  2282. CancelModes();
  2283. StopGroupTyping();
  2284. if(_cch && !fExtend) // Collapse selection
  2285. {
  2286. Collapser(tomStart);
  2287. _fCaretNotAtBOL = FALSE;
  2288. }
  2289. if(fCtrl) // Ctrl-PgUp: move to top
  2290. { // of visible view for
  2291. SetCp(_pdp->IsMultiLine() // multiline but top of
  2292. ? _pdp->GetFirstVisibleCp() : 0, fExtend); // text for SL
  2293. _fCaretNotAtBOL = FALSE;
  2294. }
  2295. else if(_pdp->GetFirstVisibleCp() == 0) // PgUp in top Pg: move to
  2296. { // start of document
  2297. SetCp(0, fExtend);
  2298. _fCaretNotAtBOL = FALSE;
  2299. }
  2300. else // PgUp with scrolling to go
  2301. { // Scroll up one windowful
  2302. ScrollWindowful(SB_PAGEUP, fExtend); // leaving caret at same
  2303. } // position in window
  2304. if(GetCp() == cpSave && _cch == cchSave) // Beep if no change
  2305. {
  2306. Beep();
  2307. return FALSE;
  2308. }
  2309. Update(TRUE);
  2310. if(GetCp()) // Maintain x offset on page
  2311. _upCaretReally = upCaretReally; // up/down
  2312. _fUpdatedFromCp0 = FALSE; // UI commands don't set this
  2313. return TRUE;
  2314. }
  2315. /*
  2316. * CTxtSelection::PageDown(fCtrl, fExtend)
  2317. *
  2318. * @mfunc
  2319. * do what cursor-keypad PgDn key is supposed to do
  2320. *
  2321. * @rdesc
  2322. * TRUE iff movement occurred
  2323. *
  2324. * @devnote
  2325. * fExtend is TRUE iff Shift key is pressed or being simulated
  2326. */
  2327. BOOL CTxtSelection::PageDown (
  2328. BOOL fCtrl, //@parm TRUE iff Ctrl key is pressed (or being simulated)
  2329. BOOL fExtend) //@parm Extend range iff TRUE
  2330. {
  2331. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::PageDown");
  2332. _TEST_INVARIANT_
  2333. const LONG cchSave = _cch;
  2334. LONG cpMostVisible;
  2335. const LONG cpSave = GetCp();
  2336. POINTUV pt;
  2337. CLinePtr rp(_pdp);
  2338. LONG upCaretReally = _upCaretReally;
  2339. CancelModes();
  2340. StopGroupTyping();
  2341. if(_cch && !fExtend) // Collapse selection
  2342. {
  2343. Collapser(tomStart);
  2344. _fCaretNotAtBOL = TRUE;
  2345. }
  2346. _pdp->GetCliVisible(&cpMostVisible, fCtrl);
  2347. if(fCtrl) // Move to end of last
  2348. { // entirely visible line
  2349. RECTUV rcView;
  2350. SetCp(cpMostVisible, fExtend);
  2351. if(_pdp->PointFromTp(*this, NULL, TRUE, pt, &rp, TA_TOP) < 0)
  2352. return FALSE;
  2353. _fCaretNotAtBOL = TRUE;
  2354. _pdp->GetViewRect(rcView);
  2355. if(rp > 0 && pt.v + rp->GetHeight() > rcView.bottom)
  2356. {
  2357. Move(-rp->_cch, fExtend);
  2358. rp--;
  2359. }
  2360. if(!fExtend && !rp.GetCchLeft() && rp->_cchEOP)
  2361. {
  2362. BackupCRLF(CSC_NORMAL, FALSE); // After backing up over EOP,
  2363. _fCaretNotAtBOL = FALSE; // caret can't be at EOL
  2364. }
  2365. }
  2366. else if(cpMostVisible == GetTextLength())
  2367. { // Move to end of text
  2368. SetCp(GetTextLength(), fExtend);
  2369. _fCaretNotAtBOL = !_rpTX.IsAfterEOP();
  2370. }
  2371. else if(!ScrollWindowful(SB_PAGEDOWN, fExtend))// Scroll down 1 windowful
  2372. return FALSE;
  2373. if(GetCp() == cpSave && _cch == cchSave) // Beep if no change
  2374. {
  2375. Beep();
  2376. return FALSE;
  2377. }
  2378. Update(TRUE);
  2379. _upCaretReally = upCaretReally;
  2380. return TRUE;
  2381. }
  2382. /*
  2383. * CTxtSelection::ScrollWindowful(wparam, fExtend)
  2384. *
  2385. * @mfunc
  2386. * Sroll up or down a windowful
  2387. *
  2388. * @rdesc
  2389. * TRUE iff movement occurred
  2390. */
  2391. BOOL CTxtSelection::ScrollWindowful (
  2392. WPARAM wparam, //@parm SB_PAGEDOWN or SB_PAGEUP
  2393. BOOL fExtend) //@parm Extend range iff TRUE
  2394. {
  2395. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::ScrollWindowful");
  2396. _TEST_INVARIANT_
  2397. // Scroll windowful
  2398. POINTUV pt; // leaving caret at same
  2399. CLinePtr rp(_pdp); // point on screen
  2400. LONG cpFirstVisible = _pdp->GetFirstVisibleCp();
  2401. LONG cpLastVisible;
  2402. LONG cpMin;
  2403. LONG cpMost;
  2404. GetRange(cpMin, cpMost);
  2405. // Get last character in the view
  2406. _pdp->GetCliVisible(&cpLastVisible, TRUE);
  2407. // Is active end in visible area of control?
  2408. if((cpMin < cpFirstVisible && _cch <= 0) || (cpMost > cpLastVisible && _cch >= 0))
  2409. {
  2410. // Not in view - we need to calculate a new range for selection
  2411. SetCp(cpFirstVisible, fExtend);
  2412. // Real caret postion is now at beginning of line
  2413. _upCaretReally = 0;
  2414. }
  2415. if(_pdp->PointFromTp(*this, NULL, _fCaretNotAtBOL, pt, &rp, TA_TOP) < 0)
  2416. return FALSE;
  2417. // The point is visible so use that
  2418. pt.u = _upCaretReally;
  2419. pt.v += rp->GetHeight() / 2;
  2420. _pdp->VScroll(wparam, 0);
  2421. if(fExtend)
  2422. {
  2423. // Disable auto word select -- if we have to use ExtendSelection()
  2424. // for non-mouse operations, let's try to get rid of its side-effects
  2425. BOOL fInAutoWordSel = _fInAutoWordSel;
  2426. _fInAutoWordSel = FALSE;
  2427. ExtendSelection(pt);
  2428. _fInAutoWordSel = fInAutoWordSel;
  2429. }
  2430. else
  2431. SetCaret(pt, FALSE);
  2432. return TRUE;
  2433. }
  2434. //////////////////////////// Keyboard support /////////////////////////////////
  2435. /*
  2436. * CTxtSelection::CheckChangeKeyboardLayout()
  2437. *
  2438. * @mfunc
  2439. * Change keyboard for new font, or font at new character position.
  2440. *
  2441. * @comm
  2442. * Using only the currently loaded KBs, locate one that will support
  2443. * the insertion points font. This is called anytime a character format
  2444. * change occurs, or the insert font (caret position) changes.
  2445. *
  2446. * @devnote
  2447. * The current KB is preferred. If a previous association
  2448. * was made, see if the KB is still loaded in the system and if so use
  2449. * it. Otherwise, locate a suitable KB, preferring KB's that have
  2450. * the same charset ID as their default, preferred charset. If no match
  2451. * can be made then nothing changes.
  2452. */
  2453. void CTxtSelection::CheckChangeKeyboardLayout ()
  2454. {
  2455. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::CheckChangeKeyboardLayout");
  2456. CTxtEdit * const ped = GetPed(); // Document context
  2457. if (ped && ped->_fFocus && !ped->fUseUIFont() &&// If ped, focus, not UIFont,
  2458. ped->IsAutoKeyboard() && // autokeyboard,
  2459. !ped->_fIMEInProgress && // not in IME composition,
  2460. ped->GetAdjustedTextLength() && // not empty control, and
  2461. _rpTX.GetPrevChar() != WCH_EMBEDDING) // not an object, then
  2462. { // check kbd change
  2463. LONG iFormat = GetiFormat();
  2464. const CCharFormat *pCF = ped->GetCharFormat(iFormat);
  2465. BYTE iCharRep = pCF->_iCharRep;
  2466. if (!IsFECharRep(iCharRep) &&
  2467. (iCharRep != ANSI_INDEX || !IsFELCID((WORD)GetKeyboardLayout(0))) &&
  2468. !fc().GetInfoFlags(pCF->_iFont).fNonBiDiAscii)
  2469. {
  2470. // Don't do auto-kbd inside FE or single-codepage ASCII font.
  2471. W32->CheckChangeKeyboardLayout(iCharRep);
  2472. }
  2473. }
  2474. }
  2475. /*
  2476. * CTxtSelection::CheckChangeFont (hkl, cpg, iSelFormat, qwCharFlags)
  2477. *
  2478. * @mfunc
  2479. * Change font for new keyboard layout.
  2480. *
  2481. * @comm
  2482. * If no previous preferred font has been associated with this KB, then
  2483. * locate a font in the document suitable for this KB.
  2484. *
  2485. * @rdesc
  2486. * TRUE iff suitable font is found
  2487. *
  2488. * @devnote
  2489. * This routine is called via WM_INPUTLANGCHANGEREQUEST message
  2490. * (a keyboard layout switch). This routine can also be called
  2491. * from WM_INPUTLANGCHANGE, but we are called more, and so this
  2492. * is less efficient.
  2493. *
  2494. * Exact match is done via charset ID bitmask. If a match was previously
  2495. * made, use it. A user can force the insertion font to be associated
  2496. * to a keyboard if the control key is held through the KB changing
  2497. * process. The association is broken when another KB associates to
  2498. * the font. If no match can be made then nothing changes.
  2499. */
  2500. bool CTxtSelection::CheckChangeFont (
  2501. const HKL hkl, //@parm Keyboard Layout to go
  2502. UINT iCharRep, //@parm Character repertoire to use
  2503. LONG iSelFormat, //@parm Format to use for selection case
  2504. QWORD qwCharFlags) //@parm 0 if called from WM_INPUTLANGCHANGE/WM_INPUTLANGCHANGEREQUEST
  2505. {
  2506. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::CheckChangeFont");
  2507. CTxtEdit * const ped = GetPed();
  2508. if (!ped->IsAutoFont() || // EXIT if auto font is turned off
  2509. _cch && !qwCharFlags) // or if kbd change with nondegenerate
  2510. return true; // selection (WM_INPUTLANGCHANGEREQUEST)
  2511. // Set new format using current format and new KB info.
  2512. LONG iCurrentFormat = _cch ? iSelFormat : _iFormat;
  2513. const CCharFormat *pCF = ped->GetCharFormat(iCurrentFormat);
  2514. CCharFormat CF = *pCF;
  2515. WORD wLangID = LOWORD(hkl);
  2516. CF._lcid = wLangID;
  2517. CF._iCharRep = iCharRep;
  2518. if (pCF->_lcid == wLangID && iCharRep == pCF->_iCharRep)
  2519. {
  2520. if (ped->_fFocus && IsCaretShown())
  2521. {
  2522. CreateCaret();
  2523. ped->TxShowCaret(TRUE);
  2524. }
  2525. return true;
  2526. }
  2527. CCFRunPtr rp(*this);
  2528. int iMatchFont = MATCH_FONT_SIG;
  2529. // If current is a primary US or UK kbd. We allow matching ASCII fonts
  2530. if ((!qwCharFlags || qwCharFlags & FASCII) &&
  2531. PRIMARYLANGID(wLangID) == LANG_ENGLISH &&
  2532. IN_RANGE (SUBLANG_ENGLISH_US, SUBLANGID(wLangID), SUBLANG_ENGLISH_UK) &&
  2533. wLangID == HIWORD((DWORD_PTR)hkl))
  2534. {
  2535. iMatchFont |= MATCH_ASCII;
  2536. }
  2537. if (rp.GetPreferredFontInfo(
  2538. iCharRep,
  2539. CF._iCharRep,
  2540. CF._iFont,
  2541. CF._yHeight,
  2542. CF._bPitchAndFamily,
  2543. iCurrentFormat,
  2544. iMatchFont))
  2545. {
  2546. if(IsFECharRep(iCharRep) || iCharRep == THAI_INDEX || IsBiDiCharRep(iCharRep))
  2547. ped->OrCharFlags(FontSigFromCharRep(iCharRep));
  2548. if (!_cch)
  2549. {
  2550. SetCharFormat(&CF, SCF_NOKBUPDATE, NULL, CFM_FACE | CFM_CHARSET | CFM_LCID | CFM_SIZE, CFM2_NOCHARSETCHECK);
  2551. if(ped->IsComplexScript())
  2552. UpdateCaret(FALSE);
  2553. }
  2554. else
  2555. {
  2556. // Create a format and use it for the selection
  2557. LONG iCF;
  2558. ICharFormatCache *pf = GetCharFormatCache();
  2559. pf->Cache(&CF, &iCF);
  2560. #ifndef NOLINESERVICES
  2561. if (g_pols)
  2562. g_pols->DestroyLine(NULL);
  2563. #endif
  2564. Set_iCF(iCF);
  2565. pf->Release(iCF); // pf->Cache AddRef it
  2566. _fUseiFormat = TRUE;
  2567. }
  2568. return true;
  2569. }
  2570. return false;
  2571. }
  2572. //////////////////////////// PutChar, Delete, Replace //////////////////////////////////
  2573. /*
  2574. * CTxtSelection::PutChar(ch, dwFlags, publdr)
  2575. *
  2576. * @mfunc
  2577. * Insert or overtype a character
  2578. *
  2579. * @rdesc
  2580. * TRUE if successful
  2581. */
  2582. BOOL CTxtSelection::PutChar (
  2583. DWORD ch, //@parm Char to put
  2584. DWORD dwFlags, //@parm Overtype mode and whether keyboard input
  2585. IUndoBuilder *publdr, //@parm If non-NULL, where to put anti-events
  2586. LCID lcid) //@parm If nonzero, lcid to use for char
  2587. {
  2588. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::PutChar");
  2589. _TEST_INVARIANT_
  2590. BOOL fOver = dwFlags & 1;
  2591. CTxtEdit *ped = GetPed();
  2592. CFreezeDisplay fd(GetPed()->_pdp);
  2593. if(ch == TAB && GetPF()->InTable() && !(dwFlags & KBD_CTRL))
  2594. {
  2595. LONG cchSave = _cch;
  2596. LONG cpMin, cpMost;
  2597. LONG cpSave = GetCp();
  2598. LONG Delta;
  2599. BOOL fMoveBack = (GetKeyboardFlags() & SHIFT) != 0;
  2600. do
  2601. {
  2602. if(!fMoveBack) // TAB w/o Shift key: select
  2603. { // contents of next cell
  2604. if(!_rpTX.IsAtTRD(ENDFIELD))
  2605. {
  2606. if(GetPrevChar() == CELL) // Handle empty cell case
  2607. Move(1, FALSE);
  2608. EndOf(tomCell, FALSE, &Delta);
  2609. }
  2610. if(_rpTX.IsAtTRD(ENDFIELD))
  2611. {
  2612. AdvanceCRLF(CSC_NORMAL, FALSE); // Bypass end-of-row delimiter
  2613. if(!_rpTX.IsAtTRD(STARTFIELD)) // Tabbed past end of table:
  2614. {
  2615. Move(-2, FALSE); // Backup before table-row end
  2616. return InsertTableRow(GetPF(), publdr, TRUE);
  2617. }
  2618. AdvanceCRLF(CSC_NORMAL, FALSE); // Bypass start-of-row delimiter
  2619. }
  2620. }
  2621. else // Shift+TAB: select contents
  2622. { // of previous cell
  2623. if(_cch || !_rpTX.IsAtTRD(ENDFIELD))
  2624. {
  2625. FindCell(&cpMin, NULL); // StartOf() w/o Update()'s
  2626. SetCp(cpMin, FALSE);
  2627. }
  2628. if(GetPrevChar() == CELL)
  2629. Move(-1, FALSE); // Backspace over previous CELL
  2630. else
  2631. {
  2632. if(!_rpTX.IsAfterTRD(ENDFIELD))
  2633. {
  2634. Assert(_rpTX.IsAfterTRD(STARTFIELD));
  2635. BackupCRLF(CSC_NORMAL, FALSE);
  2636. if(!_rpTX.IsAfterTRD(ENDFIELD))
  2637. {
  2638. Set(cpSave, cchSave); // Restore selection
  2639. Beep(); // Tell user illegal key
  2640. return FALSE;
  2641. }
  2642. }
  2643. Move(-3, FALSE); // Backspace over row-end
  2644. } // delimiter and CELL
  2645. }
  2646. if(!InTable())
  2647. break;
  2648. FindCell(&cpMin, &cpMost);
  2649. Assert(cpMost > cpMin);
  2650. cpMost--; // Don't select the CELL mark
  2651. Set(cpMost, cpMost - cpMin);
  2652. }
  2653. while(cpMost == cpMin + 1 && _rpTX.GetPrevChar() == NOTACHAR);
  2654. Update(TRUE);
  2655. return TRUE;
  2656. }
  2657. if(_nSelExpandLevel)
  2658. {
  2659. Collapser(tomStart);
  2660. while(_rpTX.IsAtTRD(STARTFIELD))
  2661. AdvanceCRLF(CSC_NORMAL, FALSE);
  2662. LONG cpMin, cpMost;
  2663. FindCell(&cpMin, &cpMost);
  2664. Set(cpMin, cpMin - cpMost + 1);
  2665. }
  2666. // EOPs might be entered by ITextSelection::TypeText()
  2667. if(IsEOP(ch))
  2668. return _pdp->IsMultiLine() // EOP isn't allowed in
  2669. ? InsertEOP(publdr, ch) : FALSE;// single line controls
  2670. if(publdr)
  2671. {
  2672. publdr->SetNameID(UID_TYPING);
  2673. publdr->StartGroupTyping();
  2674. }
  2675. // FUTURE: a Unicode lead surrogate needs to have a trail surrogate, i.e.,
  2676. // two 16-bit chars. A more thorough check would worry about this.
  2677. if ((DWORD)GetTextLength() >= ped->TxGetMaxLength() &&
  2678. ((_cch == -1 || !_cch && fOver) && _rpTX.IsAtEOP() ||
  2679. _cch == 1 && _rpTX.IsAfterEOP()))
  2680. {
  2681. // Can't overtype a CR, so need to insert new char but no room
  2682. ped->GetCallMgr()->SetMaxText();
  2683. return FALSE;
  2684. }
  2685. if((!fOver || !_cch && GetCp() == GetTextLength()) &&
  2686. !CheckTextLength(1)) // Return if we can't
  2687. { // add even 1 more char
  2688. return FALSE;
  2689. }
  2690. // The following if statement implements Word95's "Smart Quote" feature.
  2691. // To build this in, we still need an API to turn it on and off. This
  2692. // could be EM_SETSMARTQUOTES with wparam turning the feature on or off.
  2693. // murrays. NB: this needs localization for French, German, and many
  2694. // other languages (unless system can provide open/close chars given
  2695. // an LCID).
  2696. if((ch == '\'' || ch == '"') && // Smart quotes
  2697. SmartQuotesEnabled() &&
  2698. PRIMARYLANGID(GetKeyboardLayout(0)) == LANG_ENGLISH)
  2699. {
  2700. LONG cp = GetCpMin(); // Open vs close depends
  2701. CTxtPtr tp(ped, cp - 1); // on char preceding
  2702. // selection cpMin
  2703. ch = (ch == '"') ? RDBLQUOTE : RQUOTE; // Default close quote
  2704. // or apostrophe. If at
  2705. WCHAR chp = tp.GetChar();
  2706. if(!cp || IsWhiteSpace(chp) || chp == '(') // BOStory or preceded
  2707. ch--; // by whitespace, use
  2708. } // open quote/apos
  2709. WCHAR str[2]; // Might need to insert
  2710. LONG cch = 1; // Unicode surrogate pair
  2711. str[0] = (WCHAR)ch; // Default single code
  2712. if(ch > 65535) // Higher-plane char or
  2713. { // invalid
  2714. if(ch > 0x10FFFF)
  2715. return FALSE; // Invalid (above plane 17)
  2716. ch -= 0x10000; // Higher-plane char:
  2717. str[0] = 0xD800 + (ch >> 10); // convert to surrogate pair
  2718. str[1] = 0xDC00 + (ch & 0x3FF);
  2719. cch = 2;
  2720. }
  2721. // Some languages, e.g., Thai and Vietnamese, require verifying the input
  2722. // sequence order before submitting it to the backing store.
  2723. BOOL fBaseChar = TRUE; // Assume ch is a base consonant
  2724. if(!IsInputSequenceValid(str, cch, fOver, &fBaseChar))
  2725. {
  2726. SetDualFontMode(FALSE); // Ignore bad sequence
  2727. return FALSE;
  2728. }
  2729. DWORD iCharRepDefault = ped->GetCharFormat(-1)->_iCharRep;
  2730. QWORD qw = GetCharFlags(str, cch, iCharRepDefault);
  2731. ped->OrCharFlags(qw, publdr);
  2732. // BEFORE we do "dual-font", we sync the keyboard and current font's
  2733. // (_iFormat) charset if it hasn't been done.
  2734. const CCharFormat *pCFCurrent = NULL;
  2735. CCharFormat CF = *ped->GetCharFormat(GetiFormat());
  2736. BYTE iCharRep = CF._iCharRep;
  2737. BOOL fRestoreCF = FALSE;
  2738. if(ped->IsAutoFont())
  2739. {
  2740. UINT uKbdcpg = 0;
  2741. BOOL fFEKbd = FALSE;
  2742. if(!(ped->_fIMEInProgress) && !(ped->Get10Mode() && iCharRep == MAC_INDEX))
  2743. uKbdcpg = CheckSynchCharSet(qw);
  2744. if (fUseUIFont() && ch <= 0x0FF)
  2745. {
  2746. // For UIFont, we need to format ANSI characters
  2747. // so we will not have different formats between typing and
  2748. // WM_SETTEXT.
  2749. if (!ped->_fIMEInProgress && qw == FHILATIN1)
  2750. {
  2751. // Use Ansi font if based font or current font is FE.
  2752. if(IsFECharRep(iCharRepDefault) || IsFECharRep(iCharRep))
  2753. SetupDualFont(); // Use Ansi font for HiAnsi
  2754. }
  2755. else if (qw & FASCII && (GetCharRepMask(TRUE) & FASCII) == FASCII)
  2756. {
  2757. CCharFormat CFDefault = *ped->GetCharFormat(-1);
  2758. if (IsRich() && IsBiDiCharRep(CFDefault._iCharRep) &&
  2759. !W32->IsBiDiCodePage(uKbdcpg))
  2760. {
  2761. CFDefault._iCharRep = ANSI_INDEX;
  2762. }
  2763. SetCharFormat(&CFDefault, SCF_NOKBUPDATE, publdr, CFM_CHARSET | CFM_FACE | CFM_SIZE,
  2764. CFM2_CHARFORMAT | CFM2_NOCHARSETCHECK | CFM2_HOLDITEMIZE);
  2765. _fUseiFormat = FALSE;
  2766. pCFCurrent = &CF;
  2767. fRestoreCF = ped->_fIMEInProgress;
  2768. }
  2769. }
  2770. else if(!fUseUIFont() && iCharRep != ANSI_INDEX &&
  2771. (ped->_fDualFont && iCharRep != SYMBOL_INDEX &&
  2772. (((fFEKbd = (ped->_fIMEInProgress || W32->IsFECodePage(uKbdcpg))) && ch < 127 && IsASCIIAlpha(ch)) ||
  2773. (!fFEKbd && IsFECharRep(ped->GetCharFormat(GetiFormat())->_iCharRep) && ch < 127))
  2774. || ped->_fHbrCaps))
  2775. {
  2776. SetupDualFont();
  2777. pCFCurrent = &CF;
  2778. fRestoreCF = ped->_fIMEInProgress;
  2779. }
  2780. }
  2781. // = Indic/Thai overtyping convention =
  2782. //
  2783. // The deal is that we will overwrite the cluster if ch is a cluster-start char
  2784. // otherwise we just insert. This new convention was proposed by SA Office2000.
  2785. //
  2786. // Abc.Def.Ghi
  2787. // Typing X at D Abc.X.Ghi
  2788. // Typing y and z Abc.Xyz.Ghi
  2789. if(fOver && fBaseChar)
  2790. { // If nothing selected and
  2791. if(!_cch && !_rpTX.IsAtEOP()) // not at EOP char, try
  2792. { // to select char at IP
  2793. LONG iFormatSave = Get_iCF(); // Remember char's format
  2794. AdvanceCRLF(CSC_SNAPTOCLUSTER, TRUE);
  2795. ReplaceRange(0, NULL, publdr,
  2796. SELRR_REMEMBERENDIP); // Delete this character.
  2797. ReleaseFormats(_iFormat, -1);
  2798. _iFormat = iFormatSave; // Restore char's format.
  2799. }
  2800. }
  2801. else if(_SelMode == smWord && ch != TAB && _cch)// Replace word selection
  2802. {
  2803. // The code below wants the active end to be at the end of the
  2804. // word. Make sure this is so.
  2805. // FUTURE: (alexgo, andreib), _cch will only be less than zero
  2806. // in certain weird timing situations where we get a mouse move
  2807. // message in between the double click and mouse up messages.
  2808. // we should rethink how we process messages && the ordering thereof.
  2809. if(_cch < 0)
  2810. FlipRange();
  2811. // Leave word break chars
  2812. CTxtPtr tp(_rpTX); // at end of selection
  2813. Assert(_cch > 0);
  2814. tp.Move(-1);
  2815. if(tp.GetCp() && tp.FindWordBreak(WB_ISDELIMITER))// Delimiter at sel end
  2816. FindWordBreak(WB_LEFTBREAK, TRUE); // Backspace over it, etc.
  2817. }
  2818. _fIsChar = TRUE; // Tell CDisplay::UpdateView
  2819. _fDontUpdateFmt = TRUE; // we're PuttingChar()
  2820. LONG iFormat = GetiFormat(); // Save current value
  2821. LONG cchSave = _cch;
  2822. LONG cpSave = GetCp();
  2823. if(AdjustEndEOP(NEWCHARS) && publdr) // Remember what sel was
  2824. HandleSelectionAEInfo(ped, publdr, cpSave, // for undo
  2825. cchSave,GetCp(), _cch, SELAE_MERGE);
  2826. if(!_cch)
  2827. Set_iCF(iFormat);
  2828. _fDontUpdateFmt = FALSE;
  2829. if(ped->_fUpperCase)
  2830. CharUpperBuff(str, cch);
  2831. else if(ped->_fLowerCase)
  2832. CharLowerBuff(str, cch);
  2833. if(!_cch)
  2834. {
  2835. if(iCharRep == DEFAULT_INDEX)
  2836. {
  2837. CCharFormat CFTemp;
  2838. if (qw & FFE) // Find a better charset for FE char
  2839. CFTemp._iCharRep = MatchFECharRep(qw, GetFontSignatureFromFace(CF._iFont));
  2840. else
  2841. CFTemp._iCharRep = W32->CharRepFromFontSig(qw);
  2842. SetCharFormat(&CFTemp, SCF_NOKBUPDATE, NULL, CFM_CHARSET, CFM2_NOCHARSETCHECK);
  2843. }
  2844. else if(iCharRep == SYMBOL_INDEX && dwFlags & KBD_CHAR && ch > 255)
  2845. {
  2846. UINT cpg = CodePageFromCharRep(GetKeyboardCharRep(0)); // If 125x, convert char
  2847. if(IN_RANGE(1250, cpg, 1257)) // back to ANSI for storing
  2848. { // SYMBOL_CHARSET chars
  2849. BYTE ach;
  2850. WCTMB(cpg, 0, str, cch, (char *)&ach, 1, NULL, NULL, NULL);
  2851. ch = ach;
  2852. }
  2853. }
  2854. }
  2855. if(lcid && !_fDualFontMode)
  2856. {
  2857. WORD uKbdcpg = PRIMARYLANGID(lcid);
  2858. if (!(ped->_fIMEInProgress
  2859. || IsFELCID(uKbdcpg) && ch < 127
  2860. || ped->_fHbrCaps))
  2861. {
  2862. const CCharFormat *pCF = GetPed()->GetCharFormat(iFormat);
  2863. if(uKbdcpg != PRIMARYLANGID(pCF->_lcid) && !IsFECharRep(pCF->_iCharRep))
  2864. {
  2865. CCharFormat CFTemp;
  2866. CFTemp._lcid = lcid;
  2867. SetCharFormat(&CFTemp, SCF_NOKBUPDATE, NULL, CFM_LCID, 0);
  2868. }
  2869. }
  2870. }
  2871. if(dwFlags & KBD_CHAR || iCharRep == SYMBOL_INDEX)
  2872. ReplaceRange(cch, str, publdr, SELRR_REMEMBERRANGE, NULL, RR_UNHIDE);
  2873. else
  2874. CleanseAndReplaceRange(cch, str, TRUE, publdr, NULL, NULL, RR_UNHIDE);
  2875. _rpPF.AdjustBackward();
  2876. if(GetPF()->IsTableRowDelimiter()) // Inserted char into TRD
  2877. InsertEOP(publdr, 0); // Insert new EOP with
  2878. _rpPF.AdjustForward(); // nonTRD paraformat
  2879. _fIsChar = FALSE;
  2880. // Restore font for Hebrew CAPS. Note that FE font is not restored
  2881. // (is handled by IME).
  2882. if(pCFCurrent && (W32->UsingHebrewKeyboard() || fRestoreCF))
  2883. SetCharFormat(pCFCurrent, SCF_NOKBUPDATE, NULL, CFM_FACE | CFM_CHARSET | CFM_SIZE, CFM2_NOCHARSETCHECK);
  2884. else if(iFormat != Get_iFormat())
  2885. CheckChangeKeyboardLayout();
  2886. SetDualFontMode(FALSE);
  2887. // Autocorrect
  2888. if (!(dwFlags & KBD_NOAUTOCORRECT) && ped->_pDocInfo &&
  2889. ped->_pDocInfo->_pfnAutoCorrect)
  2890. {
  2891. ped->AutoCorrect(this, ch, publdr);
  2892. }
  2893. if (!_pdp->IsFrozen())
  2894. CheckUpdateWindow(); // Need to update display
  2895. // for pending chars.
  2896. return TRUE;
  2897. }
  2898. /*
  2899. * CTxtSelection::CheckUpdateWindow()
  2900. *
  2901. * @mfunc
  2902. * If it's time to update the window, after pending-typed characters,
  2903. * do so now. This is needed because WM_PAINT has a lower priority
  2904. * than WM_CHAR.
  2905. */
  2906. void CTxtSelection::CheckUpdateWindow()
  2907. {
  2908. DWORD ticks = GetTickCount();
  2909. DWORD delta = ticks - _ticksPending;
  2910. if(!_ticksPending)
  2911. _ticksPending = ticks;
  2912. else if(delta >= ticksPendingUpdate)
  2913. GetPed()->TxUpdateWindow();
  2914. }
  2915. /*
  2916. * CTxtSelection::InsertTableRow(pPF, publdr, fFixCellBorders)
  2917. *
  2918. * @mfunc
  2919. * Insert empty table row with parameters given by pPF
  2920. *
  2921. * @rdesc
  2922. * Count of CELL and NOTACHAR chars inserted if successful; else 0
  2923. */
  2924. LONG CTxtSelection::InsertTableRow (
  2925. const CParaFormat *pPF, //@parm CParaFormat to use for delimiters
  2926. IUndoBuilder *publdr, //@parm If non-NULL, where to put anti-events
  2927. BOOL fFixCellBorders) //@parm TRUE if this row should have bottom = top cell brdrs
  2928. {
  2929. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::InsertTableRow");
  2930. _cch = 0;
  2931. AssertSz(!fFixCellBorders || _rpTX.IsAtTRD(ENDFIELD),
  2932. "CTxtSelection::InsertTableRow: illegal selection cp");
  2933. LONG cpSave = GetCp();
  2934. CTxtRange rg(*this); // Use rg to get sel undo right
  2935. if(fFixCellBorders)
  2936. StopGroupTyping();
  2937. LONG cchCells = rg.InsertTableRow(pPF, publdr);
  2938. if(!cchCells)
  2939. return 0;
  2940. if(!fFixCellBorders)
  2941. {
  2942. SetCp(rg.GetCp(), FALSE);
  2943. return cchCells;
  2944. }
  2945. // Make cell bottom borders of now next-to-last row have same widths
  2946. // as the corresponding top borders
  2947. LONG cpMin;
  2948. CParaFormat PF = *GetPF();
  2949. CELLPARMS rgCellParms[MAX_TABLE_CELLS];
  2950. const CELLPARMS *prgCellParms = PF.GetCellParms();
  2951. for(LONG i = 0; i < PF._bTabCount; i++)
  2952. {
  2953. rgCellParms[i] = prgCellParms[i];
  2954. rgCellParms[i].SetBrdrWidthBottom(prgCellParms[i].GetBrdrWidthTop());
  2955. }
  2956. PF._iTabs = GetTabsCache()->Cache((LONG *)&rgCellParms[0],
  2957. (CELL_EXTRA + 1)*PF._bTabCount);
  2958. rg.Set(GetCp(), -2);
  2959. rg.SetParaFormat(&PF, publdr, PFM_TABSTOPS, PFM2_ALLOWTRDCHANGE);
  2960. rg.FindRow(&cpMin, NULL, PF._bTableLevel);
  2961. rg.Set(cpMin, -2);
  2962. rg.SetParaFormat(&PF, publdr, PFM_TABSTOPS, PFM2_ALLOWTRDCHANGE);
  2963. GetTabsCache()->Release(PF._iTabs);
  2964. Move(4, FALSE); // 1st cell of new row
  2965. if(publdr)
  2966. HandleSelectionAEInfo(GetPed(), publdr,
  2967. cpSave, 0, GetCp(), 0, SELAE_MERGE);
  2968. Update(TRUE);
  2969. return cchCells;
  2970. }
  2971. /*
  2972. * CTxtSelection::InsertEOP(publdr, ch)
  2973. *
  2974. * @mfunc
  2975. * Insert EOP character(s)
  2976. *
  2977. * @rdesc
  2978. * TRUE if successful
  2979. */
  2980. BOOL CTxtSelection::InsertEOP (
  2981. IUndoBuilder *publdr, //@parm If non-NULL, where to put anti-events
  2982. WCHAR ch) //@parm Possible EOP char
  2983. {
  2984. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::InsertEOP");
  2985. _TEST_INVARIANT_
  2986. LONG cchEOP = GetPed()->fUseCRLF() ? 2 : 1;
  2987. DWORD dwFlags = RR_NO_TRD_CHECK | RR_UNHIDE | RR_UNLINK;
  2988. BOOL fResult = FALSE;
  2989. BOOL fShift = (ch == VT);
  2990. const CParaFormat *pPF = CRchTxtPtr::GetPF(); // Get paragraph format
  2991. BOOL fInTable = pPF->InTable();
  2992. BOOL fNumbering = pPF->_wNumbering;
  2993. WCHAR szEOP[] = {CR, LF, 0};
  2994. CTxtEdit *ped = GetPed();
  2995. if(ch && (GetPed()->fUseCRLF() || IN_RANGE(VT, ch, FF)))
  2996. {
  2997. if(ch == VT)
  2998. {
  2999. CTxtPtr tp(_rpTX); // Don't allow VT after table
  3000. if(_cch > 0) // row delimiter, since TRDs
  3001. tp.Move(-_cch); // have special nonparagraph
  3002. if(tp.IsAfterTRD(0)) // properties.
  3003. ch = CR;
  3004. dwFlags = RR_NO_TRD_CHECK | RR_UNHIDE;
  3005. }
  3006. szEOP[0] = ch;
  3007. cchEOP = 1;
  3008. }
  3009. _fEOP = TRUE;
  3010. // The user hit Enter: do 1 of 4 things:
  3011. //
  3012. // 1) If at start of doc (or at start of cell at start of doc), insert
  3013. // a nontable paragraph in front of table
  3014. // 2) If following a row terminator, insert an empty row with same
  3015. // properties as the row terminator
  3016. // 3) Insert a paragraph mark
  3017. // 4) If two Enters in a row before an EOP, turn off numbering
  3018. if(_rpTX.IsAtTRD(ENDFIELD))
  3019. {
  3020. AssertSz(pPF->IsTableRowDelimiter(),
  3021. "CTxtSelection::InsertEOP: invalid paraformat");
  3022. return InsertTableRow(pPF, publdr, TRUE);
  3023. }
  3024. if(publdr)
  3025. {
  3026. publdr->StartGroupTyping();
  3027. publdr->SetNameID(UID_TYPING);
  3028. }
  3029. if(fInTable && _rpTX.IsAtStartOfCell() && !fShift)
  3030. {
  3031. Move(-2, FALSE);
  3032. if(GetCp() && !_rpTX.IsAtStartOfCell())
  3033. Move(2, FALSE);
  3034. }
  3035. if(!GetCch())
  3036. {
  3037. LONG iFormat = _iFormat;
  3038. dwFlags |= RR_NO_CHECK_TABLE_SEL;
  3039. if(CheckLinkProtection(dwFlags, iFormat))
  3040. Set_iCF(iFormat);
  3041. if(fNumbering && _rpTX.IsAfterEOP() && _rpTX.IsAtEOP())
  3042. {
  3043. // Two enters in a row turn off numbering
  3044. CParaFormat PF;
  3045. PF._wNumbering = 0;
  3046. PF._dxOffset = 0;
  3047. SetParaFormat(&PF, publdr, PFM_NUMBERING | PFM_OFFSET, PFM2_PARAFORMAT);
  3048. }
  3049. }
  3050. if(CheckTextLength(cchEOP)) // If cchEOP chars can fit...
  3051. {
  3052. CFreezeDisplay fd(GetPed()->_pdp);
  3053. LONG iFormatSave = Get_iCF(); // Save CharFormat before EOP
  3054. // Get_iCF() does AddRefFormat()
  3055. if(fNumbering) // Bullet paragraph: EOP has
  3056. { // desired bullet CharFormat
  3057. CFormatRunPtr rpCF(_rpCF); // Get run pointers for locating
  3058. CTxtPtr rpTX(_rpTX); // EOP CharFormat
  3059. rpCF.Move(rpTX.FindEOP(tomForward));
  3060. rpCF.AdjustBackward();
  3061. Set_iCF(rpCF.GetFormat()); // Set _iFormat to EOP CharFormat
  3062. }
  3063. // Put in appropriate EOP mark
  3064. fResult = ReplaceRange(cchEOP, szEOP, publdr,
  3065. SELRR_REMEMBERRANGE, NULL, dwFlags);
  3066. if (ped->_pDocInfo && ped->_pDocInfo->_pfnAutoCorrect)
  3067. ped->AutoCorrect(this, ch == 0 ? CR : ch, publdr);
  3068. _rpPF.AdjustBackward();
  3069. if(GetPF()->IsTableRowDelimiter()) // EOP just inserted before table
  3070. { // row
  3071. Move(-cchEOP, FALSE); // Backup before EOP
  3072. CTxtRange rg(*this); // Use clone so don't change undo
  3073. rg.Set(GetCp(), -cchEOP); // Select EOP just inserted
  3074. CParaFormat PF = *GetPed()->GetParaFormat(-1);
  3075. PF._wEffects &= ~PFE_TABLE; // Default not in table
  3076. PF._bTableLevel = GetPF()->_bTableLevel - 1;
  3077. if(PF._bTableLevel)
  3078. PF._wEffects |= PFE_TABLE; // It's in a table
  3079. Assert(PF._bTableLevel >= 0);
  3080. rg.SetParaFormat(&PF, publdr, PFM_ALL2, PFM2_ALLOWTRDCHANGE);
  3081. }
  3082. else
  3083. _rpPF.AdjustForward();
  3084. Set_iCF(iFormatSave); // Restore _iFormat if changed
  3085. ReleaseFormats(iFormatSave, -1); // Release iFormatSave
  3086. }
  3087. return fResult;
  3088. }
  3089. /*
  3090. * CTxtSelection::DeleteWithTRDCheck(publdr. selaemode, pcchMove, dwFlags)
  3091. *
  3092. * @mfunc
  3093. * Delete text in this range, inserting an EOP in place of the text
  3094. * if the range ends at a table-row start delimiter
  3095. *
  3096. * @rdesc
  3097. * Count of new characters added
  3098. *
  3099. * @devnote
  3100. * moves this text pointer to end of replaced text and
  3101. * may move text block and formatting arrays
  3102. */
  3103. LONG CTxtSelection::DeleteWithTRDCheck (
  3104. IUndoBuilder * publdr, //@parm UndoBuilder to receive antievents
  3105. SELRR selaemode, //@parm Controls how selection antievents are to be generated.
  3106. LONG * pcchMove, //@parm number of chars moved after replace
  3107. DWORD dwFlags) //@parm ReplaceRange flags
  3108. {
  3109. TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CTxtRange::ReplaceRange");
  3110. if(IsUpdatedFromCp0())
  3111. {
  3112. SetCp(0, FALSE);
  3113. _fUpdatedFromCp0 = FALSE;
  3114. }
  3115. return CTxtRange::DeleteWithTRDCheck(publdr, selaemode, pcchMove, dwFlags);
  3116. }
  3117. /*
  3118. * CTxtSelection::Delete(fCtrl, publdr)
  3119. *
  3120. * @mfunc
  3121. * Delete the selection. If fCtrl is true, this method deletes from
  3122. * min of selection to end of word
  3123. *
  3124. * @rdesc
  3125. * TRUE if successful
  3126. */
  3127. BOOL CTxtSelection::Delete (
  3128. DWORD fCtrl, //@parm If TRUE, Ctrl key depressed
  3129. IUndoBuilder *publdr) //@parm if non-NULL, where to put anti-events
  3130. {
  3131. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::Delete");
  3132. _TEST_INVARIANT_
  3133. SELRR mode = SELRR_REMEMBERRANGE;
  3134. AssertSz(!GetPed()->TxGetReadOnly(), "CTxtSelection::Delete(): read only");
  3135. if(!_cch)
  3136. BypassHiddenText(tomForward, FALSE);
  3137. if(publdr)
  3138. {
  3139. publdr->StopGroupTyping();
  3140. publdr->SetNameID(UID_DELETE);
  3141. }
  3142. if(fCtrl)
  3143. { // Delete to word end from cpMin
  3144. Collapser(tomStart); // (won't necessarily repaint,
  3145. FindWordBreak(WB_MOVEWORDRIGHT, TRUE);// since won't delete it)
  3146. }
  3147. if(!_cch) // No selection
  3148. { // Try to select char at IP
  3149. mode = SELRR_REMEMBERCPMIN;
  3150. if(_rpTX.GetChar() == CELL || // Don't try to delete CELL mark
  3151. !AdvanceCRLF(CSC_SNAPTOCLUSTER, TRUE))
  3152. { // End of text, nothing to delete
  3153. Beep(); // Only executed in plain text,
  3154. return FALSE; // since else there's always
  3155. } // a final EOP to select
  3156. _fMoveBack = TRUE; // Convince Update_iFormat() to
  3157. _fUseiFormat = TRUE; // use forward format
  3158. }
  3159. if(AdjustEndEOP(NONEWCHARS) && !_cch)
  3160. Update(FALSE);
  3161. else
  3162. ReplaceRange(0, NULL, publdr, mode); // Delete selection
  3163. return TRUE;
  3164. }
  3165. /*
  3166. * CTxtSelection::BackSpace(fCtrl, publdr)
  3167. *
  3168. * @mfunc
  3169. * do what keyboard BackSpace key is supposed to do
  3170. *
  3171. * @rdesc
  3172. * TRUE iff movement occurred
  3173. *
  3174. * @comm
  3175. * This routine should probably use the Move methods, i.e., it's
  3176. * logical, not directional
  3177. *
  3178. * @devnote
  3179. * _fExtend is TRUE iff Shift key is pressed or being simulated
  3180. */
  3181. BOOL CTxtSelection::Backspace (
  3182. BOOL fCtrl, //@parm TRUE iff Ctrl key is pressed (or being simulated)
  3183. IUndoBuilder *publdr) //@parm If not-NULL, where to put the antievents
  3184. {
  3185. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::Backspace");
  3186. _TEST_INVARIANT_
  3187. SELRR mode = _cch ? SELRR_REMEMBERRANGE : SELRR_REMEMBERENDIP;
  3188. AssertSz(!GetPed()->TxGetReadOnly(),
  3189. "CTxtSelection::Backspace(): read only");
  3190. _fCaretNotAtBOL = FALSE;
  3191. if(publdr)
  3192. {
  3193. publdr->SetNameID(UID_TYPING);
  3194. if(_cch || fCtrl)
  3195. publdr->StopGroupTyping();
  3196. }
  3197. if(fCtrl) // Delete word left
  3198. {
  3199. if(!GetCpMin()) // Beginning of story:
  3200. { // no word to delete
  3201. Beep();
  3202. return FALSE;
  3203. }
  3204. Collapser(tomStart); // First collapse to cpMin
  3205. }
  3206. if(!_cch) // Empty selection
  3207. { // Try to select previous char
  3208. unsigned ch = _rpTX.GetPrevChar();
  3209. if (ch == CELL || ch == CR && // Don't delete CELL mark
  3210. _rpTX.IsAfterTRD(0) || // or table-row delimiter
  3211. !BypassHiddenText(tomBackward, FALSE) ||
  3212. (fCtrl ? !FindWordBreak(WB_MOVEWORDLEFT, TRUE) :
  3213. !BackupCRLF(CSC_NOMULTICHARBACKUP, TRUE)))
  3214. { // Nothing to delete
  3215. Beep();
  3216. return FALSE;
  3217. }
  3218. if(publdr && !fCtrl)
  3219. publdr->StartGroupTyping();
  3220. }
  3221. ReplaceRange(0, NULL, publdr, mode); // Delete selection
  3222. return TRUE;
  3223. }
  3224. /*
  3225. * CTxtSelection::ReplaceRange(cchNew, pch, publdr, SELRRMode, pcchMove, dwFlags)
  3226. *
  3227. * @mfunc
  3228. * Replace selected text by new given text and update screen according
  3229. * to _fShowCaret and _fShowSelection
  3230. *
  3231. * @rdesc
  3232. * length of text inserted
  3233. */
  3234. LONG CTxtSelection::ReplaceRange (
  3235. LONG cchNew, //@parm Length of replacing text or -1 to request
  3236. // <p pch> sz length
  3237. const WCHAR *pch, //@parm Replacing text
  3238. IUndoBuilder *publdr, //@parm If non-NULL, where to put anti-events
  3239. SELRR SELRRMode, //@parm what to do about selection anti-events.
  3240. LONG* pcchMove, //@parm number of chars moved after replacing
  3241. DWORD dwFlags) //@parm Special flags
  3242. {
  3243. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::ReplaceRange");
  3244. _TEST_INVARIANT_
  3245. LONG cchNewSave;
  3246. LONG cchText = GetTextLength();
  3247. LONG cpMin, cpMost;
  3248. LONG cpSave;
  3249. BOOL fDeleteAll = FALSE;
  3250. BOOL fHeading = FALSE;
  3251. const BOOL fUpdateView = _fShowSelection;
  3252. CancelModes();
  3253. if(cchNew < 0)
  3254. cchNew = wcslen(pch);
  3255. if(!_cch && !cchNew) // Nothing to do
  3256. return 0;
  3257. if(!GetPed()->IsStreaming()) // If not pasting,
  3258. {
  3259. if(_cch != cchText && cchNew &&
  3260. (IsInOutlineView() && IsCollapsed() ||
  3261. IsHidden() && !(dwFlags & RR_UNHIDE)))
  3262. { // Don't insert into collapsed
  3263. Beep(); // or hidden region (should
  3264. return 0; // only happen if whole story
  3265. } // collapsed or hidden)
  3266. if(!(dwFlags & RR_NO_CHECK_TABLE_SEL))
  3267. CheckTableSelection(FALSE, TRUE, NULL, 0);
  3268. }
  3269. GetPed()->GetCallMgr()->SetSelectionChanged();
  3270. dwFlags |= RR_NO_LP_CHECK; // Check is done by
  3271. // CheckTableSelection()
  3272. GetRange(cpMin, cpMost);
  3273. if(cpMin > min(_cpSel, _cpSel + _cchSel) ||// If new sel doesn't
  3274. cpMost < max(_cpSel, _cpSel + _cchSel)) // contain all of old
  3275. { // sel, remove old sel
  3276. ShowSelection(FALSE);
  3277. _fShowCaret = TRUE;
  3278. }
  3279. _fCaretNotAtBOL = FALSE;
  3280. _fShowSelection = FALSE; // Suppress the flashies
  3281. // If we are streaming in text or RTF data, don't bother with incremental
  3282. // recalcs. The data transfer engine will take care of a final recalc
  3283. if(!GetPed()->IsStreaming())
  3284. {
  3285. // Do this before calling ReplaceRange() so that UpdateView() works
  3286. // AROO !!! Do this before replacing the text or the format ranges!!!
  3287. if(!_pdp->WaitForRecalc(cpMin, -1))
  3288. {
  3289. Tracef(TRCSEVERR, "WaitForRecalc(%ld) failed", cpMin);
  3290. cchNew = 0; // Nothing inserted
  3291. goto err;
  3292. }
  3293. }
  3294. if(publdr)
  3295. {
  3296. Assert(SELRRMode != SELRR_IGNORE);
  3297. // Use selection AntiEvent mode to determine what to do for undo
  3298. LONG cp = cpMin;
  3299. LONG cch = 0;
  3300. if(SELRRMode == SELRR_REMEMBERRANGE)
  3301. {
  3302. cp = GetCp();
  3303. cch = _cch;
  3304. }
  3305. else if(SELRRMode == SELRR_REMEMBERENDIP)
  3306. cp = cpMost;
  3307. else
  3308. Assert(SELRRMode == SELRR_REMEMBERCPMIN);
  3309. HandleSelectionAEInfo(GetPed(), publdr, cp, cch, cpMin + cchNew,
  3310. 0, SELAE_MERGE);
  3311. }
  3312. if(_cch == cchText && !cchNew && !(dwFlags & RR_NEW_CHARS)) // For delete all, set
  3313. { // up to choose Normal
  3314. fDeleteAll = TRUE; // or Heading 1
  3315. FlipRange();
  3316. fHeading = IsInOutlineView() && IsHeadingStyle(GetPF()->_sStyle);
  3317. }
  3318. cpSave = cpMin;
  3319. cchNewSave = cchNew;
  3320. cchNew = CTxtRange::ReplaceRange(cchNew, pch, publdr, SELRR_IGNORE, pcchMove, dwFlags);
  3321. _cchSel = 0; // No displayed selection
  3322. _cpSel = GetCp();
  3323. cchText = GetTextLength(); // Update total text length
  3324. _fShowSelection = fUpdateView;
  3325. if(cchNew != cchNewSave)
  3326. {
  3327. Tracef(TRCSEVERR, "CRchTxtPtr::ReplaceRange(%ld, %ld, %ld) failed", GetCp(), cpMost - cpMin, cchNew);
  3328. goto err;
  3329. }
  3330. if(fDeleteAll) // When all text is deleted
  3331. { // use Normal style unless
  3332. CParaFormat PF; // in Outline View and first
  3333. PF._sStyle = fHeading ? STYLE_HEADING_1 : STYLE_NORMAL;
  3334. SetParaStyle(&PF, NULL, PFM_STYLE); // para was a heading
  3335. if(GetPed()->IsBiDi())
  3336. {
  3337. if(GetPed()->_fFocus && !GetPed()->_fIMEInProgress)
  3338. {
  3339. MatchKeyboardToPara();
  3340. CheckSynchCharSet(0);
  3341. }
  3342. }
  3343. else
  3344. Update_iFormat(-1);
  3345. }
  3346. // Only update caret if inplace active
  3347. if(GetPed()->fInplaceActive())
  3348. UpdateCaret(fUpdateView); // May need to scroll
  3349. else // Update caret when we get
  3350. GetPed()->_fScrollCaretOnFocus = TRUE; // focus again
  3351. return cchNew;
  3352. err:
  3353. TRACEERRSZSC("CTxtSelection::ReplaceRange()", E_FAIL);
  3354. Tracef(TRCSEVERR, "CTxtSelection::ReplaceRange(%ld, %ld)", cpMost - cpMin, cchNew);
  3355. Tracef(TRCSEVERR, "cchText %ld", cchText);
  3356. return cchNew;
  3357. }
  3358. /*
  3359. * CTxtSelection::GetPF()
  3360. *
  3361. * @mfunc
  3362. * Return ptr to CParaFormat at active end of this selection. If no PF
  3363. * runs are allocated, then return ptr to default format. If active
  3364. * end is at cpMost of a nondegenerate selection, use the PF of the
  3365. * previous char (last char in selection).
  3366. *
  3367. * @rdesc
  3368. * Ptr to CParaFormat at active end of this selection
  3369. */
  3370. const CParaFormat* CTxtSelection::GetPF()
  3371. {
  3372. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtSelection::GetPF");
  3373. if(_cch > 0)
  3374. _rpPF.AdjustBackward();
  3375. const CParaFormat* pPF = GetPed()->GetParaFormat(_rpPF.GetFormat());
  3376. if(_cch > 0)
  3377. _rpPF.AdjustForward();
  3378. return pPF;
  3379. }
  3380. /*
  3381. * CTxtSelection::SetCharFormat(pCF, flags, publdr, dwMask, dwMask2)
  3382. *
  3383. * @mfunc
  3384. * apply CCharFormat *pCF to this selection. If range is an IP
  3385. * and fApplyToWord is TRUE, then apply CCharFormat to word surrounding
  3386. * this insertion point
  3387. *
  3388. * @rdesc
  3389. * HRESULT = NOERROR if no error
  3390. */
  3391. HRESULT CTxtSelection::SetCharFormat (
  3392. const CCharFormat *pCF, //@parm Ptr to CCharFormat to fill with results
  3393. DWORD flags, //@parm If SCF_WORD and selection is an IP,
  3394. // use enclosing word
  3395. IUndoBuilder *publdr, //@parm Undo context
  3396. DWORD dwMask, //@parm CHARFORMAT2 mask
  3397. DWORD dwMask2) //@parm Second mask
  3398. {
  3399. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::SetCharFormat");
  3400. HRESULT hr = 0;
  3401. LONG iFormat = _iFormat;
  3402. if(publdr)
  3403. publdr->StopGroupTyping();
  3404. /*
  3405. * The code below applies character formatting to a double-clicked
  3406. * selection the way Word does it, that is, not applying the formatting to
  3407. * the last character in the selection if that character is a blank.
  3408. *
  3409. * See also the corresponding code in CTxtRange::GetCharFormat().
  3410. */
  3411. CCharFormat CF;
  3412. LONG cpMin, cpMost;
  3413. LONG cch = GetRange(cpMin, cpMost);;
  3414. BOOL fCheckKeyboard = (flags & SCF_NOKBUPDATE) == 0;
  3415. if(_SelMode == smWord && (flags & SCF_USEUIRULES) && cch)
  3416. {
  3417. // In word select mode, don't include final blank in SetCharFormat
  3418. CTxtPtr tpLast(GetPed(), cpMost - 1);
  3419. if(tpLast.GetChar() == ' ') // Selection ends with a blank:
  3420. {
  3421. cpMost--; // Setup end point to end at last
  3422. cch--; // char in selection
  3423. fCheckKeyboard = FALSE;
  3424. flags &= ~SCF_WORD;
  3425. }
  3426. }
  3427. BYTE iCharRep = pCF->_iCharRep;
  3428. // Smart SB/DB Font Apply Feature
  3429. if (cch && IsRich() && // > 0 chars in rich text
  3430. !GetPed()->_fSingleCodePage && // Not in single cp mode
  3431. (dwMask & CFM_FACE)) // font change
  3432. {
  3433. if (!(dwMask & CFM_CHARSET) || iCharRep == DEFAULT_INDEX)
  3434. {
  3435. CF = *pCF;
  3436. CF._iCharRep = GetFirstAvailCharRep(GetFontSignatureFromFace(CF._iFont));
  3437. pCF = &CF;
  3438. }
  3439. dwMask2 |= CFM2_MATCHFONT; // Signal to match font charsets
  3440. }
  3441. // Selection is being set to a charformat
  3442. if(_cch && IsRich())
  3443. GetPed()->SetfSelChangeCharFormat();
  3444. if(_cch && cch < abs(_cch))
  3445. {
  3446. CTxtRange rg(*this);
  3447. rg.Set(cpMin, -cch); // Use smaller cch
  3448. hr = rg.SetCharFormat(pCF, flags, publdr, dwMask, dwMask2);
  3449. }
  3450. else
  3451. hr = CTxtRange::SetCharFormat(pCF, flags, publdr, dwMask, dwMask2);
  3452. if(fCheckKeyboard && (dwMask & CFM_CHARSET) && _iFormat != iFormat)
  3453. CheckChangeKeyboardLayout();
  3454. _fIsChar = TRUE;
  3455. UpdateCaret(!GetPed()->fHideSelection());
  3456. _fIsChar = FALSE;
  3457. return hr;
  3458. }
  3459. /*
  3460. * CTxtSelection::SetParaFormat(pPF, publdr, dwMask, dwMask2)
  3461. *
  3462. * @mfunc
  3463. * apply CParaFormat *pPF to this selection.
  3464. *
  3465. * @rdesc
  3466. * HRESULT = NOERROR if no error
  3467. */
  3468. HRESULT CTxtSelection::SetParaFormat (
  3469. const CParaFormat* pPF, //@parm ptr to CParaFormat to apply
  3470. IUndoBuilder *publdr, //@parm Undo context for this operation
  3471. DWORD dwMask, //@parm Mask to use
  3472. DWORD dwMask2) //@parm Mask for internal flags
  3473. {
  3474. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::SetParaFormat");
  3475. CFreezeDisplay fd(GetPed()->_pdp);
  3476. if(publdr)
  3477. publdr->StopGroupTyping();
  3478. // Apply the format
  3479. HRESULT hr = CTxtRange::SetParaFormat(pPF, publdr, dwMask, dwMask2);
  3480. UpdateCaret(!GetPed()->Get10Mode() || IsCaretInView());
  3481. return hr;
  3482. }
  3483. /*
  3484. * CTxtSelection::SetSelectionInfo (pselchg)
  3485. *
  3486. * @mfunc Fills out data members in a SELCHANGE structure
  3487. */
  3488. void CTxtSelection::SetSelectionInfo(
  3489. SELCHANGE *pselchg) //@parm SELCHANGE structure to use
  3490. {
  3491. LONG cpMin, cpMost;
  3492. LONG cch = GetRange(cpMin, cpMost);;
  3493. pselchg->chrg.cpMin = cpMin;
  3494. pselchg->chrg.cpMost = cpMost;
  3495. pselchg->seltyp = SEL_EMPTY;
  3496. // OR in the following selection type flags if active:
  3497. //
  3498. // SEL_EMPTY: insertion point
  3499. // SEL_TEXT: at least one character selected
  3500. // SEL_MULTICHAR: more than one character selected
  3501. // SEL_OBJECT: at least one object selected
  3502. // SEL_MULTIOJBECT: more than one object selected
  3503. //
  3504. // Note that the flags are OR'ed together.
  3505. if(cch)
  3506. {
  3507. LONG cObjects = GetObjectCount(); // Total object count
  3508. if(cObjects) // There are objects:
  3509. { // get count in range
  3510. CObjectMgr *pobjmgr = GetPed()->GetObjectMgr();
  3511. Assert(pobjmgr);
  3512. cObjects = pobjmgr->CountObjectsInRange(cpMin, cpMost);
  3513. if(cObjects > 0)
  3514. {
  3515. pselchg->seltyp |= SEL_OBJECT;
  3516. if(cObjects > 1)
  3517. pselchg->seltyp |= SEL_MULTIOBJECT;
  3518. }
  3519. }
  3520. cch -= cObjects;
  3521. AssertSz(cch >= 0, "objects are overruning the selection");
  3522. if(cch > 0)
  3523. {
  3524. pselchg->seltyp |= SEL_TEXT;
  3525. if(cch > 1)
  3526. pselchg->seltyp |= SEL_MULTICHAR;
  3527. }
  3528. }
  3529. }
  3530. /*
  3531. * CTxtSelection::UpdateForAutoWord ()
  3532. *
  3533. * @mfunc Update state to prepare for auto word selection
  3534. *
  3535. * @rdesc void
  3536. */
  3537. void CTxtSelection::UpdateForAutoWord()
  3538. {
  3539. AssertSz(!_cch,
  3540. "CTxtSelection::UpdateForAutoWord: Selection isn't degenerate");
  3541. // If enabled, prepare Auto Word Sel
  3542. if(GetPed()->TxGetAutoWordSel())
  3543. {
  3544. CTxtPtr tp(_rpTX);
  3545. // Move anchor to new location
  3546. _cpAnchor = GetCp();
  3547. // Remember that FindWordBreak moves tp's cp
  3548. // (aren't side effects wonderful?
  3549. tp.FindWordBreak(WB_MOVEWORDRIGHT);
  3550. _cpAnchorMost =_cpWordMost = tp.GetCp();
  3551. tp.FindWordBreak(WB_MOVEWORDLEFT);
  3552. _cpAnchorMin = _cpWordMin = tp.GetCp();
  3553. _fAutoSelectAborted = FALSE;
  3554. }
  3555. }
  3556. /*
  3557. * CTxtSelection::AutoSelGoBackWord(pcpToUpdate, iDirToPrevWord, iDirToNextWord)
  3558. *
  3559. * @mfunc Backup a word in auto word selection
  3560. */
  3561. void CTxtSelection::AutoSelGoBackWord(
  3562. LONG * pcpToUpdate, //@parm end of word selection to update
  3563. int iDirToPrevWord, //@parm direction to next word
  3564. int iDirToNextWord) //@parm direction to previous word
  3565. {
  3566. if (GetCp() >= _cpAnchorMin &&
  3567. GetCp() <= _cpAnchorMost)
  3568. {
  3569. // We are back in the first word. Here we want to pop
  3570. // back to a selection anchored by the original selection
  3571. Set(GetCp(), GetCp() - _cpAnchor);
  3572. _fAutoSelectAborted = FALSE;
  3573. _cpWordMin = _cpAnchorMin;
  3574. _cpWordMost = _cpAnchorMost;
  3575. }
  3576. else
  3577. {
  3578. // pop back a word
  3579. *pcpToUpdate = _cpWordPrev;
  3580. CTxtPtr tp(_rpTX);
  3581. _cpWordPrev = GetCp() + tp.FindWordBreak(iDirToPrevWord);
  3582. FindWordBreak(iDirToNextWord, TRUE);
  3583. }
  3584. }
  3585. /*
  3586. * CTxtSelection::InitClickForAutWordSel (pt)
  3587. *
  3588. * @mfunc Init auto selection for click with shift key down
  3589. *
  3590. * @rdesc void
  3591. */
  3592. void CTxtSelection::InitClickForAutWordSel(
  3593. const POINTUV pt) //@parm Point of click
  3594. {
  3595. // If enabled, prepare Auto Word Sel
  3596. if(GetPed()->TxGetAutoWordSel())
  3597. {
  3598. // If auto word selection is occuring we want to pretend
  3599. // that the click is really part of extending the selection.
  3600. // Therefore, we want the auto word select data to look as
  3601. // if the user had been extending the selection via the
  3602. // mouse all along. So we set the word borders to the
  3603. // word that would have been previously selected.
  3604. // Need this for finding word breaks
  3605. CRchTxtPtr rtp(GetPed());
  3606. LONG cpClick = _pdp->CpFromPoint(pt, NULL, &rtp, NULL, TRUE);
  3607. int iDir = -1;
  3608. if(cpClick < 0)
  3609. {
  3610. // If this fails what can we do? Prentend it didn't happen!
  3611. // We can do this because it will only make the UI act a
  3612. // little funny and chances are the user won't even notice.
  3613. return;
  3614. }
  3615. // Assume click is within anchor word
  3616. _cpWordMost = _cpAnchorMost;
  3617. _cpWordMin = _cpAnchorMin;
  3618. if(cpClick > _cpAnchorMost)
  3619. {
  3620. // Click is after anchor word, so set cpMost appropriately
  3621. iDir = WB_MOVEWORDLEFT;
  3622. rtp.FindWordBreak(WB_MOVEWORDLEFT);
  3623. _cpWordMost = rtp.GetCp();
  3624. }
  3625. // Click is before the anchor word
  3626. else if(cpClick < _cpAnchorMost)
  3627. {
  3628. // Click is before anchor word, so set cpMin appropriately.
  3629. iDir = WB_MOVEWORDRIGHT;
  3630. rtp.FindWordBreak(WB_MOVEWORDRIGHT);
  3631. _cpWordMin = rtp.GetCp();
  3632. }
  3633. if(iDir != -1)
  3634. {
  3635. rtp.FindWordBreak(iDir);
  3636. _cpWordPrev = rtp.GetCp();
  3637. }
  3638. }
  3639. }
  3640. /*
  3641. * CTxtSelection::CreateCaret ()
  3642. *
  3643. * @mfunc Create a caret
  3644. *
  3645. * @devnote
  3646. * The caret is characterized by a height (_dvpCaret), a keyboard
  3647. * direction (if BiDi), a width (1 to 8, since OS can't handle carets
  3648. * larger than 8 pixels), and an italic state. One could cache this
  3649. * info thereby avoiding computing the caret on every keystroke.
  3650. */
  3651. void CTxtSelection::CreateCaret()
  3652. {
  3653. CTxtEdit * ped = GetPed();
  3654. const CCharFormat *pCF = ped->GetCharFormat(_iFormat);
  3655. DWORD dwCaretType = 0;
  3656. BOOL fItalic;
  3657. LONG y = min(_dvpCaret, 512);
  3658. y = max(0, y);
  3659. fItalic = pCF->_dwEffects & CFE_ITALIC && _dvpCaret > 15; //9 pt/15 pixels looks bad
  3660. // Caret shape reflects current charset
  3661. if(ped->IsComplexScript() && IsComplexKbdInstalled())
  3662. {
  3663. // Custom carets aren't italicized
  3664. LCID lcid = GetKeyboardLCID();
  3665. #ifdef FANCY_CARET
  3666. fItalic = 0;
  3667. dwCaretType = CARET_CUSTOM;
  3668. #endif
  3669. if (W32->IsBiDiLcid(lcid))
  3670. {
  3671. dwCaretType = CARET_CUSTOM | CARET_BIDI;
  3672. fItalic = 0;
  3673. }
  3674. #ifdef FANCY_CARET
  3675. else if (PRIMARYLANGID(lcid) == LANG_THAI)
  3676. dwCaretType = CARET_CUSTOM | CARET_THAI;
  3677. else if (W32->IsIndicLcid(lcid))
  3678. dwCaretType = CARET_CUSTOM | CARET_INDIC;
  3679. #endif
  3680. }
  3681. //REVIEW (keithcu) Vertical text can't do complex scripts or italic carets (yet?)
  3682. if (ped->_pdp->GetTflow() != tflowES)
  3683. {
  3684. fItalic = 0;
  3685. dwCaretType = 0;
  3686. }
  3687. INT dx = duCaret;
  3688. DWORD dwCaretInfo = (_dvpCaret << 16) | (dx << 8) | (dwCaretType << 4) |
  3689. (fItalic << 1) | !_cch;
  3690. #ifndef NOFEPROCESSING
  3691. if (ped->_fKoreanBlockCaret)
  3692. {
  3693. // Support Korean block caret during Kor IME composition
  3694. // Basically, we want to create a caret using the width and height of the
  3695. // character at current cp.
  3696. CDisplay * pdp = ped->_pdp;
  3697. LONG cpMin, cpMost;
  3698. POINTUV ptStart, ptEnd;
  3699. GetRange(cpMin, cpMost);
  3700. CRchTxtPtr rtp(ped, cpMin);
  3701. // REVIEW: generalize PointFromTp to return both values (so one call)
  3702. if (pdp->PointFromTp(rtp, NULL, FALSE, ptStart, NULL, TA_TOP+TA_LEFT) != -1 &&
  3703. pdp->PointFromTp(rtp, NULL, FALSE, ptEnd, NULL, TA_BOTTOM+TA_RIGHT) != -1)
  3704. {
  3705. // Destroy whatever caret bitmap we had previously
  3706. DeleteCaretBitmap(TRUE);
  3707. LONG iCharWidth = abs(ptEnd.u - ptStart.u);
  3708. LONG iCharHeight = abs(ptEnd.v - ptStart.v);
  3709. // REVIEW: honwch do we need to SetCaretPos for all Tflows
  3710. if (IsCaretHorizontal())
  3711. ped->TxCreateCaret(0, iCharWidth, iCharHeight);
  3712. else
  3713. ped->TxCreateCaret(0, iCharHeight, iCharWidth);
  3714. switch (_pdp->GetTflow())
  3715. {
  3716. case tflowES:
  3717. ped->TxSetCaretPos(ptStart.u, ptStart.v);
  3718. break;
  3719. case tflowSW:
  3720. ped->TxSetCaretPos(ptStart.u, ptEnd.v);
  3721. break;
  3722. case tflowWN:
  3723. ped->TxSetCaretPos(ptEnd.u, ptEnd.v);
  3724. break;
  3725. case tflowNE:
  3726. ped->TxSetCaretPos(ptEnd.u, ptStart.v);
  3727. break;
  3728. }
  3729. _fCaretCreated = TRUE;
  3730. }
  3731. return;
  3732. }
  3733. #endif
  3734. // We always create the caret bitmap on the fly since it
  3735. // may be of arbitrary size
  3736. if (dwCaretInfo != _dwCaretInfo)
  3737. {
  3738. _dwCaretInfo = dwCaretInfo; // Update caret info
  3739. // Destroy whatever caret bitmap we had previously
  3740. DeleteCaretBitmap(FALSE);
  3741. if (y && y == _dvpCaret && (_cch || fItalic || dwCaretType))
  3742. {
  3743. LONG dy = 4; // Assign value to suppress
  3744. LONG i; // compiler warning
  3745. WORD rgCaretBitMap[512];
  3746. WORD wBits = 0x0020;
  3747. if(_cch) // Create blank bitmap if
  3748. { // selection is nondegenerate
  3749. y = 1; // (allows others to query
  3750. wBits = 0; // OS where caret is)
  3751. fItalic = FALSE;
  3752. }
  3753. if(fItalic)
  3754. {
  3755. i = (5*y)/16 - 1; // System caret can't be wider
  3756. i = min(i, 7); // than 8 bits
  3757. wBits = 1 << i; // Make larger italic carets
  3758. dy = y/7; // more vertical. Ideal is
  3759. dy = max(dy, 4); // usually up 4 over 1, but
  3760. } // if bigger go up 5 over 1...
  3761. for(i = y; i--; )
  3762. {
  3763. rgCaretBitMap[i] = wBits;
  3764. if(fItalic && !(i % dy))
  3765. wBits /= 2;
  3766. }
  3767. if(!fItalic && !_cch && dwCaretType)
  3768. {
  3769. #ifdef FANCY_CARET
  3770. dwCaretType &= ~CARET_CUSTOM;
  3771. // Create an appropriate shape
  3772. switch (dwCaretType)
  3773. {
  3774. case CARET_BIDI:
  3775. // BiDi is a caret with a little triangle on top (flag shape pointing left)
  3776. rgCaretBitMap[0] = 0x00E0;
  3777. rgCaretBitMap[1] = 0x0060;
  3778. break;
  3779. case CARET_THAI:
  3780. // Thai is an L-like shape (same as system edit control)
  3781. rgCaretBitMap[y-2] = 0x0030;
  3782. rgCaretBitMap[y-1] = 0x0038;
  3783. break;
  3784. case CARET_INDIC:
  3785. // Indic is a T-like shape
  3786. rgCaretBitMap[0] = 0x00F8;
  3787. rgCaretBitMap[1] = 0x0070;
  3788. break;
  3789. default:
  3790. if (ped->IsBiDi())
  3791. {
  3792. // Non-BiDi caret in BiDi document (flag shape pointing right)
  3793. rgCaretBitMap[0] = 0x0038;
  3794. rgCaretBitMap[1] = 0x0030;
  3795. }
  3796. }
  3797. #else
  3798. // No fancy caret, we only setup CARET_BIDI case
  3799. rgCaretBitMap[0] = 0x00E0;
  3800. rgCaretBitMap[1] = 0x0060;
  3801. #endif
  3802. }
  3803. _hbmpCaret = (HBITMAP)CreateBitmap(8, y, 1, 1, rgCaretBitMap);
  3804. }
  3805. }
  3806. if (IsCaretHorizontal())
  3807. ped->TxCreateCaret(_hbmpCaret, dx, _dvpCaret);
  3808. else
  3809. ped->TxCreateCaret(_hbmpCaret, _dvpCaret, dx);
  3810. _fCaretCreated = TRUE;
  3811. LONG xShift = _hbmpCaret ? 2 : 0;
  3812. if(fItalic)
  3813. {
  3814. // TODO: figure out better shift algorithm. Use CCcs::_xOverhang?
  3815. if(pCF->_iFont == IFONT_TMSNEWRMN)
  3816. xShift = 4;
  3817. xShift += y/16;
  3818. }
  3819. xShift = _upCaret - xShift;
  3820. if (_pdp->GetTflow() == tflowSW || _pdp->GetTflow() == tflowWN)
  3821. ped->TxSetCaretPos(xShift, _vpCaret + _dvpCaret);
  3822. else
  3823. ped->TxSetCaretPos(xShift, _vpCaret);
  3824. }
  3825. /*
  3826. * CTxtSelection::DeleteCaretBitmap (fReset)
  3827. *
  3828. * @mfunc DeleteCaretBitmap
  3829. */
  3830. void CTxtSelection::DeleteCaretBitmap(
  3831. BOOL fReset)
  3832. {
  3833. if(_hbmpCaret)
  3834. {
  3835. DestroyCaret();
  3836. DeleteObject((void *)_hbmpCaret);
  3837. _hbmpCaret = NULL;
  3838. }
  3839. if(fReset)
  3840. _dwCaretInfo = 0;
  3841. }
  3842. /*
  3843. * CTxtSelection::SetDelayedSelectionRange (cp, cch)
  3844. *
  3845. * @mfunc sets the selection range such that it won't take effect until
  3846. * the control is "stable"
  3847. */
  3848. void CTxtSelection::SetDelayedSelectionRange(
  3849. LONG cp, //@parm Active end
  3850. LONG cch) //@parm Signed extension
  3851. {
  3852. CSelPhaseAdjuster *pspa;
  3853. pspa = (CSelPhaseAdjuster *)GetPed()->GetCallMgr()->GetComponent(
  3854. COMP_SELPHASEADJUSTER);
  3855. Assert(pspa);
  3856. pspa->CacheRange(cp, cch);
  3857. }
  3858. /*
  3859. * CTxtSelection::CheckPlainTextFinalEOP ()
  3860. *
  3861. * @mfunc
  3862. * returns TRUE if this is a plain-text, multiline control with caret
  3863. * allowed at BOL and the selection at the end of the story following
  3864. * and EOP
  3865. *
  3866. * @rdesc
  3867. * TRUE if all of the conditions above are met
  3868. */
  3869. BOOL CTxtSelection::CheckPlainTextFinalEOP()
  3870. {
  3871. return !IsRich() && _pdp->IsMultiLine() && // Plain-text, multiline
  3872. !_fCaretNotAtBOL && // with caret OK at BOL,
  3873. GetCp() == GetTextLength() && // & cp at end of story
  3874. _rpTX.IsAfterEOP();
  3875. }
  3876. /*
  3877. * CTxtSelection::StopGroupTyping()
  3878. *
  3879. * @mfunc
  3880. * Tell undo manager to stop group typing
  3881. */
  3882. void CTxtSelection::StopGroupTyping()
  3883. {
  3884. IUndoMgr * pundo = GetPed()->GetUndoMgr();
  3885. CheckTableIP(FALSE);
  3886. if(pundo)
  3887. pundo->StopGroupTyping();
  3888. }
  3889. /*
  3890. * CTxtSelection::SetupDualFont()
  3891. *
  3892. * @mfunc checks to see if dual font support is necessary; in this case,
  3893. * switching to an English font if English text is entered into
  3894. * an FE run
  3895. * @rdesc
  3896. * A pointer to the current CharFormat if the font has to be changed.
  3897. */
  3898. void CTxtSelection::SetupDualFont()
  3899. {
  3900. CTxtEdit * ped = GetPed();
  3901. CCharFormat CF;
  3902. CF._iCharRep = ANSI_INDEX;
  3903. CCFRunPtr rp(*this);
  3904. if (rp.GetPreferredFontInfo(
  3905. ANSI_INDEX,
  3906. CF._iCharRep,
  3907. CF._iFont,
  3908. CF._yHeight,
  3909. CF._bPitchAndFamily,
  3910. _iFormat,
  3911. IGNORE_CURRENT_FONT))
  3912. {
  3913. if (!_cch)
  3914. SetCharFormat(&CF, SCF_NOKBUPDATE, NULL, CFM_FACE | CFM_CHARSET | CFM_SIZE, 0);
  3915. else
  3916. {
  3917. // For selection, we need to set the character format at cpMin+1
  3918. // and use the format for the selection.
  3919. CTxtRange rg(ped, GetCpMin() + 1, 0);
  3920. rg.SetCharFormat(&CF, SCF_NOKBUPDATE, NULL, CFM_FACE | CFM_CHARSET | CFM_SIZE, 0);
  3921. Set_iCF(rg.Get_iCF());
  3922. GetCharFormatCache()->Release(_iFormat); // rg.Get_iCF() AddRefs it
  3923. _fUseiFormat = TRUE;
  3924. }
  3925. SetDualFontMode(TRUE);
  3926. }
  3927. }
  3928. //
  3929. // CSelPhaseAdjuster methods
  3930. //
  3931. /*
  3932. * CSelPhaseAdjuster::CSelPhaseAdjuster (ped)
  3933. *
  3934. * @mfunc constructor
  3935. */
  3936. CSelPhaseAdjuster::CSelPhaseAdjuster(
  3937. CTxtEdit *ped) //@parm the edit context
  3938. {
  3939. _cp = _cch = -1;
  3940. _ped = ped;
  3941. _ped->GetCallMgr()->RegisterComponent((IReEntrantComponent *)this,
  3942. COMP_SELPHASEADJUSTER);
  3943. }
  3944. /*
  3945. * CSelPhaseAdjuster::~CSelPhaseAdjuster()
  3946. *
  3947. * @mfunc destructor
  3948. */
  3949. CSelPhaseAdjuster::~CSelPhaseAdjuster()
  3950. {
  3951. // Save some indirections
  3952. CTxtEdit *ped = _ped;
  3953. if(_cp != -1)
  3954. {
  3955. ped->GetSel()->SetSelection(_cp - _cch, _cp);
  3956. // If the selection is updated, then we invalidate the
  3957. // entire display because the old selection can still
  3958. // appear otherwise because the part of the screen that
  3959. // it was on is not updated.
  3960. if(ped->fInplaceActive())
  3961. {
  3962. // Tell entire client rectangle to update.
  3963. // FUTURE: The smaller we make this the better.
  3964. ped->TxInvalidate();
  3965. }
  3966. }
  3967. ped->GetCallMgr()->RevokeComponent((IReEntrantComponent *)this);
  3968. }
  3969. /*
  3970. * CSelPhaseAdjuster::CacheRange(cp, cch)
  3971. *
  3972. * @mfunc tells this class the selection range to remember
  3973. */
  3974. void CSelPhaseAdjuster::CacheRange(
  3975. LONG cp, //@parm Active end to remember
  3976. LONG cch) //@parm Signed extension to remember
  3977. {
  3978. _cp = cp;
  3979. _cch = cch;
  3980. }