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.

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