Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1925 lines
54 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module RTEXT.CPP - Rich-text ptr class |
  5. *
  6. * This text ptr consists of a plain text ptr (_rpTX), a CCharFormat
  7. * run ptr (_rpCF), and a CParaFormat run ptr (_rpPF). This module
  8. * contains the methods to manipulate this combination of run ptrs
  9. * consistently.
  10. *
  11. * Authors:<nl>
  12. * RichEdit 1.0 code: David R. Fulmer
  13. * Main implementation: Murray Sargent <nl>
  14. * Undo and notification implementations: Alex Gounares <nl>
  15. *
  16. * Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
  17. */
  18. #include "_common.h"
  19. #include "_edit.h"
  20. #include "_frunptr.h"
  21. #include "_rtext.h"
  22. #include "_disp.h"
  23. #include "_select.h"
  24. #include "_m_undo.h"
  25. #include "_antievt.h"
  26. #include "_objmgr.h"
  27. #include "_txtbrk.h"
  28. ASSERTDATA
  29. #define DEBUG_CLASSNAME CRchTxtPtr
  30. #include "_invar.h"
  31. #ifdef DEBUG
  32. /*
  33. * CRchTxtPtr::Invariant
  34. */
  35. BOOL CRchTxtPtr::Invariant( void ) const
  36. {
  37. if (m_InvariantCheckInterval < 1 || m_InvariantCheckInterval > 10)
  38. const_cast<CRchTxtPtr *>(this)->m_InvariantCheckInterval = 10;
  39. const_cast<CRchTxtPtr *>(this)->m_InvariantCheckInterval--;
  40. if (m_InvariantCheckInterval)
  41. return TRUE;
  42. unsigned ch;
  43. LONG cch;
  44. LONG cchLength = GetTextLength();
  45. LONG cp;
  46. _rpTX.Invariant();
  47. _rpCF.Invariant();
  48. _rpPF.Invariant();
  49. if(_rpCF.IsValid())
  50. {
  51. cp = _rpCF.CalculateCp();
  52. cch = _rpCF.CalcTextLength();
  53. Assert(GetCp() == cp && cchLength == cch);
  54. Assert(!_rpCF._iRun || GetPed()->IsBiDi() || _rpCF.GetRun(0)->_iFormat != _rpCF.GetRun(-1)->_iFormat);
  55. }
  56. if(_rpPF.IsValid())
  57. {
  58. cp = _rpPF.CalculateCp();
  59. cch = _rpPF.CalcTextLength();
  60. Assert(GetCp() == cp && cchLength == cch);
  61. CTxtPtr tp(_rpTX);
  62. tp.Move(_rpPF.GetCchLeft() - 1);
  63. ch = tp.GetChar();
  64. if(!IsEOP(ch))
  65. {
  66. _rpTX.MoveGapToEndOfBlock(); // Make it easier to see
  67. AssertSz(FALSE, // what's going on
  68. "CRchTxtPtr::Invariant: PF run doesn't end with EOP");
  69. }
  70. #ifdef EXTREME_CHECKING
  71. // We don't do this check normally as it is _extremely_ slow.
  72. // However, it's very useful for catching para-format run problems
  73. // Make sure each para format run ends on a paragraph mark!
  74. CFormatRunPtr rpPF(_rpPF);
  75. rpPF.BindToCp(0);
  76. tp.BindToCp(0);
  77. do
  78. {
  79. tp.Move(rpPF.GetRun(0)->_cch);
  80. if(!tp.IsAfterEOP())
  81. {
  82. AssertSz(0, "ParaFormat Run not aligned along paragraphs!");
  83. }
  84. } while( rpPF.NextRun() );
  85. #endif // EXTREME_CHECKING
  86. }
  87. return TRUE;
  88. }
  89. /*
  90. * CRchTxtPtr::GetParaNumber ()
  91. *
  92. * @mfunc
  93. * Return number of current paragraph in a numbered list. This is
  94. * 0 if the current paragraph isn't part of a list. It's 1 if it's
  95. * the first paragraph in a list, 2 if it's the second, etc.
  96. *
  97. * @rdesc
  98. * paragraph number active at this rich text ptr
  99. *
  100. * @devnote
  101. * When the display is calc'd from the beginning or recalc'd from
  102. * a previous valid position, the list number can be determined from
  103. * the display. But if CDisplayPrinter::FormatRange() works without
  104. * a display, it needs to know the number. This routine can be so used
  105. * for this purpose and for debugging the display choices.
  106. */
  107. LONG CRchTxtPtr::GetParaNumber() const
  108. {
  109. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetParaNumber");
  110. LONG ch;
  111. LONG cPara = 0;
  112. LONG n;
  113. const CParaFormat *pPF, *pPFLast = NULL;
  114. CRchTxtPtr rtp(*this);
  115. while(1)
  116. {
  117. pPF = rtp.GetPF();
  118. if(pPF->_wEffects & PFE_TABLEROWDELIMITER)
  119. break;
  120. // CParaFormat::UpdateNumber(2, pPFLast) returns:
  121. // 0 -- not a numbered list
  122. // 1 -- new numbered list or pPFLast = NULL
  123. // 2 -- list number suppressed
  124. // 3 -- different number in same list
  125. n = pPF->UpdateNumber(2, pPFLast);
  126. if(n == 0 || n == 1 && pPFLast && cPara)
  127. break;
  128. ch = rtp.GetPrevChar();
  129. if((!ch || IsASCIIEOP(ch) && !IN_RANGE(VT, ch, FF) || ch == CELL) && n != 2)
  130. cPara++;
  131. if(!ch || rtp._rpTX.IsAtStartOfCell())
  132. break;
  133. rtp._rpPF.Move(rtp._rpTX.FindEOP(tomBackward));
  134. pPFLast = pPF; // Don't need to update _rpCF
  135. } // for this calculation
  136. return cPara;
  137. }
  138. #endif // DEBUG
  139. //======================= CRchTxtPtr constructors ========================================
  140. CRchTxtPtr::CRchTxtPtr(CTxtEdit *ped) :
  141. _rpTX(ped, 0), _rpCF(NULL), _rpPF(NULL)
  142. {
  143. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::CRchTxtPtr");
  144. InitRunPtrs();
  145. }
  146. CRchTxtPtr::CRchTxtPtr(CTxtEdit *ped, LONG cp) :
  147. _rpTX(ped, cp), _rpCF(NULL), _rpPF(NULL)
  148. {
  149. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::CRchTxtPtr");
  150. InitRunPtrs();
  151. }
  152. CRchTxtPtr::CRchTxtPtr (const CRchTxtPtr& rtp) :
  153. _rpTX(rtp._rpTX), _rpCF(rtp._rpCF), _rpPF(rtp._rpPF)
  154. {
  155. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::CRchTxtPtr");
  156. _rpCF.AdjustForward(); // In case rtp is adjusted backward...
  157. _rpPF.AdjustForward();
  158. }
  159. CRchTxtPtr::CRchTxtPtr (const CDisplay * pdp) :
  160. _rpTX(pdp->GetPed(), 0), _rpCF(NULL), _rpPF(NULL)
  161. {
  162. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::CRchTxtPtr");
  163. InitRunPtrs();
  164. }
  165. /*
  166. * CRchTxtPtr::Move(cch)
  167. *
  168. * @mfunc
  169. * Move this rich-text ptr forward <p cch> characters. If <p cch>
  170. * <lt> 0, move backward by -<p cch> characters.
  171. *
  172. * @rdesc
  173. * cch actually moved
  174. */
  175. LONG CRchTxtPtr::Move(
  176. LONG cch) // @parm count of characters to move - may be <lt> 0
  177. {
  178. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::Move");
  179. if( cch != 0 )
  180. {
  181. cch = _rpTX.Move(cch);
  182. _rpCF.Move(cch);
  183. _rpPF.Move(cch);
  184. _TEST_INVARIANT_
  185. }
  186. return cch;
  187. }
  188. /*
  189. * CRchTxtPtr::AdvanceCRLF()
  190. *
  191. * @mfunc
  192. * Advance this text ptr one char, treating CRLF as a single char.
  193. *
  194. * @rdesc
  195. * cch actually moved
  196. */
  197. LONG CRchTxtPtr::AdvanceCRLF()
  198. {
  199. TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CRchTxtPtr::AdvanceCRLF");
  200. LONG cch = _rpTX.AdvanceCRLF();
  201. _rpPF.Move(cch);
  202. _rpCF.Move(cch);
  203. return cch;
  204. }
  205. /*
  206. * CRchTxtPtr::SnapToCluster(iDirection)
  207. *
  208. * @mfunc
  209. * If this text ptr is not at cluster boundary, move it to the closest one.
  210. *
  211. * @rdesc
  212. * cch actually moved
  213. */
  214. #ifndef NOCOMPLEXSCRIPTS
  215. LONG CRchTxtPtr::SnapToCluster(INT iDirection)
  216. {
  217. TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CRchTxtPtr::SnapToCluster");
  218. LONG cch = 0;
  219. LONG cp;
  220. if (GetPed()->_pbrk)
  221. {
  222. if (iDirection >= 0)
  223. {
  224. LONG cpEnd = GetPed()->GetAdjustedTextLength();
  225. while ((cp = GetCp()) < cpEnd && !GetPed()->_pbrk->CanBreakCp(BRK_CLUSTER, cp))
  226. cch += AdvanceCRLF();
  227. }
  228. else
  229. {
  230. while ((cp = GetCp()) > 0 && !GetPed()->_pbrk->CanBreakCp(BRK_CLUSTER, cp))
  231. cch += BackupCRLF();
  232. }
  233. }
  234. return cch;
  235. }
  236. #endif
  237. /*
  238. * CRchTxtPtr::BackupCRLF(fDiacriticCheck)
  239. *
  240. * @mfunc
  241. * Backup this text ptr one char, treating CRLF as a single char.
  242. *
  243. * @rdesc
  244. * cch actually moved
  245. */
  246. LONG CRchTxtPtr::BackupCRLF(
  247. BOOL fDiacriticCheck)
  248. {
  249. TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CRchTxtPtr::BackupCRLF");
  250. LONG cch = _rpTX.BackupCRLF(fDiacriticCheck);
  251. _rpPF.Move(cch);
  252. _rpCF.Move(cch);
  253. return cch;
  254. }
  255. /*
  256. * CRchTxtPtr::ValidateCp(&cp)
  257. *
  258. * @mfunc
  259. * If <p cp> <lt> 0, set it to 0; if it's <gt> text length, set it to
  260. * text length.
  261. */
  262. void CRchTxtPtr::ValidateCp(
  263. LONG &cp) const // @parm new cp for this text ptr
  264. {
  265. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::ValidateCp");
  266. LONG cchT = GetTextLength();
  267. cp = min(cp, cchT); // Be sure cp is valid
  268. cp = max(cp, 0);
  269. }
  270. /*
  271. * CRchTxtPtr::SetCp(cp)
  272. *
  273. * @mfunc
  274. * Set this rich text ptr's cp to cp
  275. */
  276. LONG CRchTxtPtr::SetCp(
  277. LONG cp) // @parm new cp for this text ptr
  278. {
  279. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::SetCp");
  280. CRchTxtPtr::Move(cp - GetCp());
  281. return GetCp();
  282. }
  283. /* CRchTxtPtr::GetIchRunXX() and CRchTxtPtr::GetCchRunXX()
  284. *
  285. * @mfunc
  286. * Text-run management to retrieve current text run cch and offset
  287. *
  288. * @rdesc
  289. * current run ich or cch
  290. *
  291. * @devnote
  292. * Use of queries like _rpCF.IsValid() instead of an inclusive fRich
  293. * allows rich-text formatting to be applied per rich-text category,
  294. * e.g., CHARFORMATs, but not necessarily PARAFORMATs. If the rp isn't
  295. * valid, _cp is used for ich and the document length is used for cch,
  296. * i.e., the values for a document describable by a single plain-text run
  297. */
  298. LONG CRchTxtPtr::GetIchRunCF()
  299. {
  300. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetIchRunCF");
  301. return _rpCF.IsValid() ? _rpCF.GetIch() : GetCp();
  302. }
  303. LONG CRchTxtPtr::GetIchRunPF()
  304. {
  305. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetIchRunPF");
  306. return _rpPF.IsValid() ? _rpPF.GetIch() : GetCp();
  307. }
  308. LONG CRchTxtPtr::GetCchRunCF()
  309. {
  310. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetCchRunCF");
  311. return _rpCF.IsValid() ? _rpCF.GetRun(0)->_cch : GetTextLength();
  312. }
  313. /* CRchTxtPtr::GetCchLeftRunCF() / GetCchLeftRunPF()
  314. *
  315. * @mfunc
  316. * Return cch left in run, i.e., cchRun - ich
  317. *
  318. * @rdesc
  319. * cch left in run
  320. */
  321. LONG CRchTxtPtr::GetCchLeftRunCF()
  322. {
  323. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetCchLeftRunCF");
  324. return _rpCF.IsValid()
  325. ? _rpCF.GetCchLeft() : GetTextLength() - GetCp();
  326. }
  327. LONG CRchTxtPtr::GetCchLeftRunPF()
  328. {
  329. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetCchLeftRunPF");
  330. return _rpPF.IsValid()
  331. ? _rpPF.GetCchLeft() : GetTextLength() - GetCp();
  332. }
  333. /*
  334. * CRchTxtPtr::FindText(cpMost, dwFlags, pch, cchToFind)
  335. *
  336. * @mfunc
  337. * Find text in a range starting at this text pointer;
  338. * if found, moves this text pointer to that position.
  339. *
  340. * @rdesc
  341. * character position of first match
  342. * <lt> 0 if no match
  343. *
  344. * @devnote
  345. * Would be easy to match a single format (like Word 6) provided
  346. * cchToFind is nonzero. Else need to search runs (also pretty easy).
  347. * For format-sensitive searches, might be easier to search for matching
  348. * format run first and then within that run search for text.
  349. */
  350. LONG CRchTxtPtr::FindText (
  351. LONG cpMost, // @parm Limit of search; <lt> 0 for end of text
  352. DWORD dwFlags, // @parm FR_MATCHCASE case must match
  353. // FR_WHOLEWORD match must be a whole word
  354. TCHAR const *pch, // @parm Text to search for
  355. LONG cchToFind) // @parm Length of text to search for
  356. {
  357. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::FindText");
  358. _TEST_INVARIANT_
  359. LONG cpSave = GetCp();
  360. LONG cpMatch = _rpTX.FindText(cpMost, dwFlags, pch, cchToFind);
  361. if(cpMatch >= 0) // cpMatch = -1 means "not found"
  362. SetRunPtrs(GetCp(), cpSave);
  363. // possible code for format-dependent Finds
  364. return cpMatch;
  365. }
  366. /*
  367. * CRchTxtPtr::GetCF()/GetPF()
  368. *
  369. * @mfunc
  370. * Return ptr to CCharFormat/CParaFormat at this text ptr. If no CF/PF runs
  371. * are allocated, then return ptr to default format
  372. *
  373. * @rdesc
  374. * Ptr to CCharFormat/CParaFormat at this text ptr
  375. */
  376. const CCharFormat* CRchTxtPtr::GetCF() const
  377. {
  378. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetCF");
  379. return ((CTxtArray *)_rpTX._pRuns)->GetCharFormat(_rpCF.GetFormat());
  380. }
  381. const CParaFormat* CRchTxtPtr::GetPF() const
  382. {
  383. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetPF");
  384. return ((CTxtArray *)_rpTX._pRuns)->GetParaFormat(_rpPF.GetFormat());
  385. }
  386. /*
  387. * CRchTxtPtr::Get_iCF()
  388. *
  389. * @mfunc
  390. * Get character format index at this text pointer
  391. *
  392. * @rdesc
  393. * Get iCF at this text pointer
  394. */
  395. LONG CRchTxtPtr::Get_iCF ()
  396. {
  397. TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CRchTxtPtr::Get_iCF");
  398. LONG iCF = _rpCF.GetFormat();
  399. GetCharFormatCache()->AddRef(iCF);
  400. return iCF;
  401. }
  402. /*
  403. * CRchTxtPtr::Get_iPF()
  404. *
  405. * @mfunc
  406. * Get paragraph format index at this text pointer
  407. *
  408. * @rdesc
  409. * Get iPF at this text pointer
  410. */
  411. LONG CRchTxtPtr::Get_iPF ()
  412. {
  413. TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CRchTxtPtr::Get_iPF");
  414. LONG iPF = _rpPF.GetFormat();
  415. GetParaFormatCache()->AddRef(iPF);
  416. return iPF;
  417. }
  418. /*
  419. * CRchTxtPtr::GetPlainText(cchBuff, pch, cpMost, fTextize, fUseCRLF)
  420. *
  421. * @mfunc
  422. * Same as CTxtPtr except that hidden text isn't copied
  423. *
  424. * @rdesc
  425. * Count of characters copied
  426. */
  427. LONG CRchTxtPtr::GetPlainText(
  428. LONG cchBuff, //@parm Buffer cch
  429. WCHAR * pch, //@parm Buffer to copy text into
  430. LONG cpMost, //@parm Largest cp to get
  431. BOOL fTextize, //@parm True if break on WCH_EMBEDDING
  432. BOOL fUseCRLF) //@parm If TRUE, CR or LF -> CRLF
  433. {
  434. LONG cchTotal = 0;
  435. CTxtEdit *ped = GetPed();
  436. CTxtPtr tp(_rpTX); // tp to get unhidden text
  437. if(!_rpCF.IsValid() || !ped->IsRich())
  438. cchTotal = tp.GetPlainText(cchBuff, pch, cpMost, fTextize, fUseCRLF);
  439. else
  440. {
  441. LONG cch = 0;
  442. LONG cp;
  443. CCFRunPtr rp(*this); // rp to check for hidden text
  444. cpMost = min(cpMost, GetPed()->GetAdjustedTextLength());
  445. for(; cchTotal < cchBuff; pch += cch)
  446. {
  447. cch = rp.FindUnhiddenForward();
  448. if(tp.GetCp() + cch >= cpMost)
  449. {
  450. SetCp(cpMost);
  451. return cchTotal;
  452. }
  453. tp.Move(cch); // Bypass hidden text
  454. for(cch = 0; !rp.IsHidden() && cch < cchBuff; )
  455. {
  456. cch += rp.GetCchLeft();
  457. if(tp.GetCp() + cch >= cpMost || !rp.NextRun())
  458. break;
  459. }
  460. if(cch) // Copy unhidden text
  461. {
  462. cp = tp.GetCp() + cch;
  463. cp = min(cp, cpMost);
  464. cch = tp.GetPlainText(cchBuff - cchTotal, pch, cp, fTextize, fUseCRLF);
  465. cchTotal += cch;
  466. }
  467. if(tp.GetCp() >= cpMost || !cch)
  468. break;
  469. }
  470. }
  471. SetCp(tp.GetCp());
  472. return cchTotal;
  473. }
  474. /*
  475. * CRchTxtPtr::ReplaceRange(cchOld, cchNew, *pch, pcpFirstRecalc, publdr,
  476. * iFormat, dwFlags)
  477. * @mfunc
  478. * Replace a range of text at this text pointer using CCharFormat iFormat
  479. * and updating other text runs as needed
  480. *
  481. * @rdesc
  482. * Count of new characters added
  483. *
  484. * @devnote
  485. * Moves this text pointer to end of replaced text.
  486. * May move text block and formatting arrays.
  487. */
  488. LONG CRchTxtPtr::ReplaceRange(
  489. LONG cchOld, //@parm length of range to replace
  490. // (<lt> 0 means to end of text)
  491. LONG cchNew, //@parm length of replacement text
  492. TCHAR const *pch, //@parm replacement text
  493. IUndoBuilder *publdr, //@parm Undo bldr to receive antievents
  494. LONG iFormat, //@parm CCharFormat iFormat to use for cchNew
  495. LONG * pcchMove, //@parm Out parm returning cch moved if paradir change
  496. DWORD dwFlags) //@parm Special flags
  497. {
  498. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::ReplaceRange");
  499. LONG cch;
  500. LONG cchEndEOP = 0; // Default 0 final EOP fixup
  501. LONG cchAdvance = 0;
  502. LONG cchBackup = 0;
  503. LONG cchMove = 0; // Default nothing to move
  504. LONG cchNextEOP = cchOld; // cch to next EOP
  505. LONG cchPrevEOP = 0; // cch back to previous EOP
  506. LONG cpFR; // between PF runs
  507. LONG cpSave = GetCp();
  508. LONG cpFormatMax;
  509. LONG cpFormatMin = cpSave; // Used for notifications
  510. LONG cpFormat = cpSave; // Will add cchOld, maybe cchMove
  511. BOOL fParaDirChange = FALSE;
  512. CTxtEdit * ped = GetPed();
  513. IAntiEvent * paeCF = NULL;
  514. IAntiEvent * paePF = NULL;
  515. CNotifyMgr * pnm;
  516. CObjectMgr * pobjmgr;
  517. CFreezeDisplay fd(ped->_pdp); // freeze until itemization is done
  518. _TEST_INVARIANT_
  519. LONG cchEnd = GetTextLength() - GetCp();
  520. LONG cOldRuns = _rpTX.Count();
  521. if(cchOld < 0 || cchOld > cchEnd)
  522. cchOld = cchEnd;
  523. if(IsRich() && cchOld == cchEnd) // Attempting to delete up
  524. { // thru final EOP
  525. cchEndEOP = (ped->fUseCRLF()) // Calc cch of final EOP
  526. ? CCH_EOD_10 : CCH_EOD_20;
  527. if(cchEndEOP <= cchOld) // Don't delete it unless
  528. cchOld -= cchEndEOP; // converting from 2.0
  529. if(_rpPF.IsValid())
  530. {
  531. _rpPF.AdjustBackward(); // If previous para is a
  532. if(GetPF()->InTable()) // table row, don't delete
  533. cchEndEOP = 0; // final para formatting
  534. _rpPF.AdjustForward();
  535. }
  536. }
  537. else if(_rpPF.IsValid()) // PARAFORMATs are enabled
  538. {
  539. _rpPF.AdjustForward();
  540. LONG iPF2 = _rpPF.GetFormat();
  541. BOOL fIsTRD2 = ped->GetParaFormat(iPF2)->IsTableRowDelimiter();
  542. BOOL fNoTrdCheck = dwFlags & RR_NO_TRD_CHECK;
  543. if(cchOld)
  544. {
  545. CFormatRunPtr rp(_rpPF);
  546. CTxtPtr tp(_rpTX); // Get tp and rp at end of
  547. // range. Need bounding para
  548. tp.Move(cchOld); // counts to save valid PF
  549. if(tp.GetCp() < ped->GetAdjustedTextLength())
  550. cchOld += tp.AdjustCRLF(1);
  551. rp.Move(cchOld); // for undo
  552. LONG iPF1 = rp.GetFormat();
  553. BOOL fIsTRD1 = ped->GetParaFormat(iPF1)->IsTableRowDelimiter();
  554. cch = 0;
  555. if(tp.IsAfterEOP()) // Range ends with an EOP
  556. {
  557. if ((tp.GetPrevChar() == CELL ||// Don't delete table cell
  558. fIsTRD2 && cchOld == 2) && // at end of range
  559. !fNoTrdCheck)
  560. {
  561. return 0; // or solo row delimiter
  562. }
  563. cch = -tp.BackupCRLF(); // Get EOP length by
  564. tp.Move(cch); // backing up over it
  565. } // Move past EOP
  566. BOOL fIsAtBOP = !GetCp() || _rpTX.IsAfterEOP();
  567. if(tp.IsAtTRD(0))
  568. {
  569. AssertSz(tp.IsAtTRD(STARTFIELD), "Illegal deletion attempt");
  570. cchNextEOP = 0;
  571. if(!fIsAtBOP)
  572. {
  573. cchOld--; // Leave end CR there
  574. if(tp.IsAfterTRD(ENDFIELD)) // If CR of TRED, move it
  575. { // into prec PF run
  576. cchMove = cchNextEOP = 1;
  577. cpFormat++;
  578. }
  579. }
  580. }
  581. else
  582. {
  583. cchNextEOP = tp.FindEOP(tomForward);// Get cch up to next EOP
  584. AssertSz(rp.GetCchLeft() >= cchNextEOP,
  585. "CRchTxtPtr::ReplaceRange: missing EOP");
  586. }
  587. if (!fIsAtBOP && cch == cchOld && // Deleting EOP alone before
  588. !rp.GetIch()) // new PARAFORMAT run start
  589. { // in para with more than EOP
  590. if(fIsTRD1)
  591. return 0; // Don't merge with table row delim
  592. cchMove = cchNextEOP; // Need to move chars up to
  593. cpFormat += cchMove; // end of next para for
  594. }
  595. cchNextEOP += cchOld; // Count from GetCp() to EOP
  596. tp.SetCp(GetCp()); // Back to this ptr's _cp
  597. if(!fIsAtBOP)
  598. {
  599. cchPrevEOP = tp.FindEOP(tomBackward);// Get cch to start of para
  600. AssertSz(_rpPF.GetIch() >= -cchPrevEOP,
  601. "CRchTxtPtr::ReplaceRange: missing EOP");
  602. }
  603. // If deleting from within one format run up to or into another, set
  604. // up to move last para in starting format run into the run following
  605. // the deleted text
  606. if(iPF1 != iPF2) // Change of format during
  607. { // deleted text not starting
  608. if(!fIsAtBOP && !cchMove && !fIsTRD1)// at BOP
  609. {
  610. cchMove = cchPrevEOP; // Get cch to start of para
  611. cpFormatMin += cchMove; // in this ptr's run for
  612. } // moving into rp's run
  613. if (((ped->GetParaFormat(iPF1)->_wEffects ^
  614. ped->GetParaFormat(iPF2)->_wEffects) & PFE_RTLPARA) &&
  615. !(fIsTRD1 | fIsTRD2))
  616. {
  617. fParaDirChange = TRUE; // Note that para direction
  618. Assert(ped->IsBiDi()); // changed
  619. }
  620. }
  621. }
  622. else
  623. {
  624. UINT ch = GetPrevChar();
  625. if (fIsTRD2 && ch == CELL && // Don't paste between CELL
  626. (_rpTX.IsAtTRD(ENDFIELD) || !fNoTrdCheck) || // & row terminator
  627. ch == NOTACHAR && !ped->IsStreaming() ||
  628. GetChar() == NOTACHAR) // or before/after NOTACHAR
  629. {
  630. return 0;
  631. }
  632. }
  633. }
  634. Assert(cchNew >= 0 && cchOld >= 0);
  635. if(!(cchNew + cchOld)) // Nothing to do (note: all
  636. { // these cch's are >= 0)
  637. if(pcchMove)
  638. *pcchMove = 0;
  639. return 0;
  640. }
  641. // If BiDi doc, expand the range to cover the boundaries that guarantee
  642. // the valid state of the BiDi level so we can undo it properly. (wchao)
  643. cpFormatMax = cpFormat + cchOld;
  644. if(ped->IsBiDi())
  645. {
  646. cchBackup = ExpandRangeFormatting (cchOld + cchEndEOP,
  647. fParaDirChange ? cchMove : 0, cchAdvance);
  648. Assert (cchBackup <= 0);
  649. if(cchMove >= 0) // In this case, cchBackup is minus
  650. { // the sum of the two previous
  651. cpFormatMin += cchBackup; // run counts
  652. Assert(cpFormatMin >= 0);
  653. }
  654. if(cchMove <= 0) // In this case cchAdvance is sum
  655. { // of next two run counts (or less)
  656. cpFormatMax += cchAdvance;
  657. Assert(cpFormatMax <= GetTextLength());
  658. }
  659. }
  660. // Handle pre-replace range notifications. This method is very
  661. // useful for delayed rendering of data copied to the clipboard.
  662. pnm = ped->GetNotifyMgr();
  663. if(pnm)
  664. {
  665. pnm->NotifyPreReplaceRange((ITxNotify *)this, cpSave, cchOld,
  666. cchNew, cpFormatMin, cpFormatMax);
  667. }
  668. if(iFormat >= 0)
  669. Check_rpCF();
  670. // Get rid of objects first. This lets us guarantee that when we
  671. // insert the objects as part of an undo, the objects themselves are
  672. // restored _after_ their corresponding WCH_EMBEDDINGs have been
  673. // added to the backing store.
  674. if(GetObjectCount())
  675. {
  676. pobjmgr = ped->GetObjectMgr();
  677. Assert(pobjmgr);
  678. pobjmgr->ReplaceRange(cpSave, cchOld, publdr);
  679. }
  680. // The anti-events used below are a bit tricky (paeCF && paePF).
  681. // Essentially, this call, CRchTxtPtr::ReplaceRange generates one
  682. // 'combo' anti-event composed of up to two formatting AE's plus
  683. // the text anti-event. These anti-events are combined together
  684. // to prevent ordering problems during undo/redo.
  685. cpFR = ReplaceRangeFormatting(cchOld + cchEndEOP, cchNew + cchEndEOP,
  686. iFormat, publdr, &paeCF, &paePF, cchMove, cchPrevEOP,
  687. cchNextEOP, cchBackup, cchAdvance);
  688. if(cchEndEOP)
  689. {
  690. // If we added in the EOP we need to back up by the EOP so
  691. // that the invariants don't get annoyed and the richtext object
  692. // doesn't get out of sync.
  693. _rpCF.Move(-cchEndEOP);
  694. _rpPF.Move(-cchEndEOP);
  695. }
  696. if(cpFR < 0)
  697. {
  698. Tracef(TRCSEVERR, "ReplaceRangeFormatting(%ld, %ld, %ld) failed", GetCp(), cchOld, cchNew);
  699. cch = 0;
  700. goto Exit;
  701. }
  702. // As noted above in the call to ReplaceRangeFormatting, the anti-events
  703. // paeCF and paePF, if non-NULL, were generated by ReplaceRangeFormatting.
  704. // In order to solve ordering problems, the anti-event generated by this
  705. // method is actually a combo anti-event of text && formatting AE's.
  706. cch = _rpTX.ReplaceRange(cchOld, cchNew, pch, publdr, paeCF, paePF);
  707. if(cch != cchNew)
  708. {
  709. Tracef(TRCSEVERR, "_rpTX.ReplaceRange(%ld, %ld, ...) failed", cchOld, cchNew);
  710. #ifndef NOFULLDEBUG
  711. // Boy, out of memory or something bad. Dump our formatting and hope
  712. // for the best.
  713. //
  714. // FUTURE: (alexgo) degrade more gracefully than losing formatting
  715. // info.
  716. // Notify every interested party that they should dump their formatting
  717. if(pnm)
  718. pnm->NotifyPreReplaceRange(NULL, CONVERT_TO_PLAIN, 0, 0, 0, 0);
  719. // Tell document to dump its format runs
  720. ped->GetTxtStory()->DeleteFormatRuns();
  721. #endif
  722. goto Exit;
  723. }
  724. AssertSz(!_rpPF.IsValid() || _rpPF.GetIch() || !GetCp() || _rpTX.IsAfterEOP(),
  725. "CRchTxtPtr::ReplaceRange: EOP not at end of PF run");
  726. // BUGBUG!! (alexgo) doesn't handle correctly the case where things fail
  727. // (due to out of memory or whatever). See also notes in CTxtPtr::HandleReplaceRange
  728. // Undo. The assert below is therefore somewhat bogus, but if it fires,
  729. // then our floating ranges are going to be in trouble until we fix
  730. // up the logic here.
  731. Assert(cch == cchNew);
  732. Exit:
  733. #ifdef DEBUG
  734. // Test invariant again before calling out to replace range notification.
  735. // In this way, we can catch bugs earlier. The invariant has its own
  736. // scope for convenience.
  737. if( 1 )
  738. {
  739. _TEST_INVARIANT_
  740. }
  741. #endif
  742. if(ped->IsBiDi() && cpSave <= ped->GetCpFirstStrong() && (cchOld | cch))
  743. {
  744. // Remember whether formatting is valid before we set context direction
  745. BOOL fCFValidBeforeSetContextDirection = _rpCF.IsValid();
  746. // Need to check the direction of the control if the input characters
  747. // control the direction.
  748. ped->SetContextDirection();
  749. // Did SetContextDirection make the formatting valid?
  750. if (!fCFValidBeforeSetContextDirection && _rpCF.IsValid())
  751. {
  752. // Our invariant is that cps should be equal if formatting is valid
  753. // so make it so!
  754. _rpCF.BindToCp(GetCp());
  755. }
  756. }
  757. if(pnm)
  758. {
  759. BOOL fTxtCellShrink = (cOldRuns > _rpTX.Count());
  760. NOTIFY_DATA notifyData;
  761. if (fTxtCellShrink)
  762. {
  763. // Setup NOTIFY_DATA
  764. notifyData.id = NOTIFY_DATA_TEXT_ID;
  765. notifyData.dwFlags = TN_TX_CELL_SHRINK;
  766. notifyData.pData = NULL;
  767. }
  768. pnm->NotifyPostReplaceRange((ITxNotify *)this, cpSave, cchOld, cch,
  769. cpFormatMin, cpFormatMax,
  770. fTxtCellShrink ? &notifyData : NULL);
  771. }
  772. ped->GetCallMgr()->SetChangeEvent(CN_TEXTCHANGED);
  773. if(pcchMove) // Only return non0 cchMove if para
  774. { // direction changed, i.e., it's
  775. *pcchMove = fParaDirChange // a "BOOL" with a useful value,
  776. ? cchMove : 0; // namely the count of chars with
  777. } // changed direction
  778. if (ped->IsComplexScript())
  779. {
  780. if (dwFlags & RR_ITMZ_NONE || (ped->IsStreaming() && (!pch || *pch != WCH_EMBEDDING)))
  781. ped->_fItemizePending = TRUE;
  782. else
  783. ItemizeReplaceRange(cchNew, fParaDirChange? cchMove : 0, publdr);
  784. }
  785. return cch;
  786. }
  787. /*
  788. * CRchTxtPtr::InitRunPtrs()
  789. *
  790. * @mfunc
  791. * Initialize Run Ptrs of this rich-text ptr to correspond to
  792. * document given by ped and to cp given by cp.
  793. */
  794. void CRchTxtPtr::InitRunPtrs()
  795. {
  796. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::InitRunPtrs");
  797. AssertSz(GetPed(), "RTP::InitRunPtrs: illegal GetPed()");
  798. LONG cp = GetCp();
  799. CTxtStory * pStory = GetPed()->GetTxtStory();
  800. LONG cchText = pStory->GetTextLength();
  801. // If there's RichData,
  802. if(pStory->_pCFRuns) // initialize format-run ptrs
  803. {
  804. _rpCF.SetRunArray((CRunArray *)pStory->_pCFRuns);
  805. _rpCF.BindToCp(cp, cchText);
  806. }
  807. if(IsRich() && pStory->_pPFRuns)
  808. {
  809. _rpPF.SetRunArray((CRunArray *)pStory->_pPFRuns);
  810. _rpPF.BindToCp(cp, cchText);
  811. }
  812. }
  813. /*
  814. * CRchTxtPtr::SetRunPtrs(cp, cpFrom)
  815. *
  816. * @mfunc set Run Ptrs of this rich-text ptr to correspond to cp
  817. *
  818. * @rdesc
  819. * TRUE unless cp is outside of doc (in which case RunPtrs are
  820. * set to nearest document end).
  821. */
  822. void CRchTxtPtr::SetRunPtrs(
  823. LONG cp, // @parm character position to move RunPtrs to
  824. LONG cpFrom) // @parm cp to start with
  825. {
  826. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::SetRunPtrs");
  827. if(cpFrom && 2*cp >= cpFrom)
  828. {
  829. _rpCF.Move(cp - cpFrom);
  830. _rpPF.Move(cp - cpFrom);
  831. }
  832. else
  833. {
  834. LONG cchText = GetTextLength();
  835. _rpCF.BindToCp(cp, cchText);
  836. _rpPF.BindToCp(cp, cchText);
  837. }
  838. }
  839. /*
  840. * CRchTxtPtr::ExpandRangeFormatting(cchRange, cchMove, &cchAdvance)
  841. *
  842. * @mfunc
  843. * In BiDi scenario, it's possible that updating a character affects the
  844. * level of the others. Such case should only happen when number is involved.
  845. *
  846. * Example: (AN)"11:30" changing '3' to 'x' will change the level of
  847. * colon from 2 to 1. Accordingly return cch back to safe itemization
  848. *
  849. * @rdesc
  850. * cch back to safe itemization
  851. */
  852. LONG CRchTxtPtr::ExpandRangeFormatting(
  853. LONG cchRange, // in: original length
  854. LONG cchMove, // in: number of chars moved after replacement
  855. LONG & cchAdvance) // out: extra chars added to range after expanding
  856. {
  857. LONG cchBackup = 0;
  858. cchAdvance = 0;
  859. if (_rpCF.IsValid())
  860. {
  861. CTxtPtr tp(_rpTX);
  862. if (!IsRich())
  863. {
  864. cchBackup = tp.FindEOP(tomBackward);
  865. tp.Move(-cchBackup + cchRange);
  866. cchAdvance = tp.FindEOP(tomForward);
  867. }
  868. else
  869. {
  870. CFormatRunPtr rp(_rpCF);
  871. LONG cp = GetCp();
  872. if (cchMove < 0)
  873. {
  874. // <cchMove> count of chars to be moved down to next paragraph
  875. cchBackup = cchMove;
  876. }
  877. else if (cchMove > 0)
  878. {
  879. // <cchMove> count of chars to be moved up to previous paragraph
  880. cchAdvance = cchMove;
  881. }
  882. // Advancing/Backing up 2 adjacent runs seems to be sufficient for now.
  883. if (cchBackup == 0)
  884. {
  885. rp.AdjustBackward();
  886. cchBackup = -rp.GetIch();
  887. if (rp.PrevRun())
  888. cchBackup -= rp.GetCchLeft();
  889. rp.Move(-cchBackup); // Restore position
  890. }
  891. // Move run pointer to end of range
  892. rp.Move(cchRange);
  893. tp.SetCp(cp + cchRange);
  894. if (cchAdvance == 0 && !tp.IsAtEOP())
  895. {
  896. rp.AdjustForward();
  897. cchAdvance += rp.GetCchLeft();
  898. if (rp.NextRun())
  899. cchAdvance += rp.GetCchLeft();
  900. }
  901. }
  902. }
  903. return cchBackup;
  904. }
  905. /*
  906. * CRchTxtPtr::ItemizeReplaceRange(cchUpdate, cchMove, publdr, fUnicodeBidi)
  907. *
  908. * @mfunc
  909. * Find out the exact range to be itemized after calling :ReplaceRange
  910. *
  911. * @rdesc
  912. * result from ItemizeRuns.
  913. * Guarantee *this* pointer wont move.
  914. */
  915. BOOL CRchTxtPtr::ItemizeReplaceRange(
  916. LONG cchUpdate,
  917. LONG cchMove, // Count of chars moved after replacing
  918. IUndoBuilder* publdr, // (they need reitemizing)
  919. BOOL fUnicodeBidi)
  920. {
  921. BOOL fr = FALSE;
  922. if (GetPed()->IsComplexScript())
  923. {
  924. Assert (cchUpdate >= 0); // the range after ReplaceRange must be degenerate
  925. CTxtPtr tp(_rpTX);
  926. LONG cp = GetCp();
  927. LONG cpStart, cpEnd;
  928. BOOL fNonUnicodeBidiRecurse = FALSE;
  929. BOOL fUseCtxLevel = FALSE;
  930. tp.Move(-cchUpdate);
  931. if (cchUpdate > 0 && GetPed()->IsRich() && fUnicodeBidi)
  932. {
  933. cpStart = cpEnd = cp;
  934. cpStart -= cchUpdate;
  935. if (GetPed()->IsBiDi())
  936. {
  937. // Fix for 7094 : Don't look at the incoming text for clues
  938. // fUseCtxLevel = TRUE;
  939. // Recurse with non-BiDi, so the run preceding/succeeding this chunk get updated
  940. fNonUnicodeBidiRecurse = TRUE;
  941. }
  942. }
  943. else
  944. {
  945. tp.FindWhiteSpaceBound(cchUpdate, cpStart, cpEnd,
  946. !GetPed()->IsRich() ? FWS_BOUNDTOPARA : 0);
  947. }
  948. if (cchMove < 0)
  949. {
  950. // <cchMove> number of text -before- the replaced range
  951. // moves down to the next paragraph.
  952. cpStart = max(cp - cchUpdate + cchMove, 0);
  953. }
  954. else if (cchMove > 0)
  955. {
  956. // <cchMove> number of text -after- the replaced range
  957. // moves up to the previous paragraph.
  958. cpEnd = min(cp + cchMove, GetPed()->GetTextLength());
  959. }
  960. {
  961. CTxtRange rg(*this, 0);
  962. rg.Set(cpEnd, cpEnd - cpStart);
  963. fr = rg.ItemizeRuns(publdr, fUnicodeBidi, fUseCtxLevel);
  964. // set pointer back to original cp
  965. // We cant use copy operator here since itemization changes format run.
  966. // It would cause invariant failure in _rpCF.
  967. cp -= rg.GetCp();
  968. _rpCF = rg._rpCF;
  969. _rpCF.Move(cp);
  970. // ItemizeRuns invalidates rg._rpPF so that the paraformat run becomes valid
  971. // and we need to advance it to the current cp.
  972. _rpPF = rg._rpPF;
  973. _rpPF.Move(cp);
  974. // Perf note: We dont want the range to be around when we recurse
  975. // since a range is a notification sink.
  976. }
  977. // Run itemization to the same range, this time forces it to be non-Bidi.
  978. if (fr && fNonUnicodeBidiRecurse)
  979. fr = ItemizeReplaceRange(cchUpdate, 0, publdr, FALSE);
  980. }
  981. return fr;
  982. }
  983. /*
  984. * CRchTxtPtr::ReplaceRangeFormatting(cchOld, cchNew, iFormat, publdr,
  985. * ppaeCF, ppaePF, cchMove, cchPrevEOP,
  986. * cchNextEOP, cchSaveBefore, cchSaveAfter)
  987. * @mfunc
  988. * Replace character and paragraph formatting at this text pointer
  989. * using CCharFormat with index iFormat
  990. *
  991. * @rdesc
  992. * count of new characters added
  993. *
  994. * @devnote
  995. * Moves _rpCF and _rpPF to end of replaced text and moves format arrays.
  996. * CCharFormat for iFormat is fully configured, i.e., no NINCHes
  997. */
  998. LONG CRchTxtPtr::ReplaceRangeFormatting(
  999. LONG cchOld, //@parm Length of range to replace
  1000. LONG cchNew, //@parm Length of replacement text
  1001. LONG iFormat, //@parm Char format to use
  1002. IUndoBuilder *publdr, //@parm UndoBuilder to receive antievents
  1003. IAntiEvent **ppaeCF, //@parm Where to return 'extra' CF anti-events
  1004. IAntiEvent **ppaePF, //@parm Where to return extra PF anti-events
  1005. LONG cchMove, //@parm cch to move between PF runs
  1006. LONG cchPrevEOP, //@parm cch from _cp back to prev EOP
  1007. LONG cchNextEOP, //@parm cch from _cp up to next EOP
  1008. LONG cchSaveBefore,//@parm cch backup for BiDi
  1009. LONG cchSaveAfter) //@parm cch advance for BiDi
  1010. {
  1011. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::ReplaceRangeFormatting");
  1012. LONG cp = GetCp();
  1013. LONG cchText = GetTextLength() + cchNew - cchOld;
  1014. LONG iRunMerge = 0;
  1015. ICharFormatCache * pcfc = GetCharFormatCache();
  1016. IParaFormatCache * ppfc = GetParaFormatCache();
  1017. AssertSz(cchOld >= 0,
  1018. "CRchTxtPtr::ReplaceRangeFormatting: Illegal cchOld");
  1019. if(_rpCF.IsValid())
  1020. {
  1021. iRunMerge = _rpCF._iRun;
  1022. if(iRunMerge > 0)
  1023. iRunMerge--;
  1024. Assert (cchSaveBefore <= 0 && cchSaveAfter >= 0);
  1025. if(cchOld + cchSaveAfter - cchSaveBefore > 0)
  1026. { // add the soon-to-be deleted
  1027. if(publdr) // formats to the undo list
  1028. {
  1029. // Include previous cchSaveBefore chars
  1030. _rpCF.Move(cchSaveBefore);
  1031. *ppaeCF = gAEDispenser.CreateReplaceFormattingAE(GetPed(),
  1032. cp + cchSaveBefore, _rpCF, cchSaveAfter + cchOld - cchSaveBefore,
  1033. pcfc, CharFormat);
  1034. // Restore _rpCF (we just want to save value not delete it)
  1035. _rpCF.Move(-cchSaveBefore);
  1036. }
  1037. if(cchOld) // Delete/modify CF runs <-->
  1038. _rpCF.Delete(cchOld, pcfc, 0); // to cchOld chars
  1039. }
  1040. // If we deleted all of text in story, don't bother adding a new
  1041. // run. Else insert/modify CF runs corresponding to cchNew chars
  1042. //
  1043. // In a plain-text control, there is no final EOP; hence the test
  1044. // for equality.
  1045. if(cchNew > 1 || cchNew && cchOld <= GetTextLength())
  1046. _rpCF.InsertFormat(cchNew, iFormat, pcfc);
  1047. if((cchOld || cchNew) && _rpCF.IsValid())// Deleting all text
  1048. { // invalidates _rpCF
  1049. _rpCF.AdjustForward();
  1050. _rpCF.MergeRuns(iRunMerge, pcfc);
  1051. _rpCF.BindToCp(cp + cchNew, cchText);
  1052. }
  1053. }
  1054. if(_rpPF.IsValid())
  1055. {
  1056. _rpPF.AdjustForward(); // Be absolutely sure that
  1057. // PF runs end with EOPs
  1058. iRunMerge = _rpPF._iRun;
  1059. if(iRunMerge > 0)
  1060. iRunMerge--;
  1061. if(cchOld) // Delete cchOld from PF runs
  1062. { // add the soon-to-be deleted
  1063. if(publdr) // formats to the undo list
  1064. {
  1065. CFormatRunPtr rp(_rpPF);
  1066. rp.Move(cchPrevEOP);
  1067. *ppaePF = gAEDispenser.CreateReplaceFormattingAE(GetPed(),
  1068. cp + cchPrevEOP, rp, cchNextEOP - cchPrevEOP,
  1069. ppfc, ParaFormat);
  1070. }
  1071. _rpPF.Delete(cchOld, ppfc, cchMove);
  1072. }
  1073. if(_rpPF.IsValid()) // Deleting all text
  1074. { // invalidates _rpPF
  1075. _rpPF.AdjustForward();
  1076. _rpPF.GetRun(0)->_cch += cchNew; // Insert cchNew into current
  1077. _rpPF._ich += cchNew; // PF run
  1078. if(cchOld || cchNew)
  1079. {
  1080. _rpPF.MergeRuns(iRunMerge, ppfc);
  1081. _rpPF.BindToCp(cp + cchNew, cchText);
  1082. }
  1083. }
  1084. }
  1085. return cchNew;
  1086. }
  1087. /*
  1088. * CRchTxtPtr::ExtendFormattingCRLF()
  1089. *
  1090. * @mfunc
  1091. * Use the same CCharFormat and CParaFormat indices for the EOP at
  1092. * this text ptr as those immediately preceding it.
  1093. *
  1094. * @devnote
  1095. * Leaves this text ptr's format ptrs at run you get from AdjustBackward
  1096. * since this run ends up including the new text.
  1097. */
  1098. void CRchTxtPtr::ExtendFormattingCRLF()
  1099. {
  1100. LONG cch = GetTextLength() - GetPed()->GetAdjustedTextLength();
  1101. CNotifyMgr *pnm = GetPed()->GetNotifyMgr();
  1102. _rpCF.AdjustFormatting(cch, GetCharFormatCache());
  1103. if(_rpPF.IsValid())
  1104. {
  1105. _rpPF.AdjustBackward();
  1106. if(!InTable())
  1107. _rpPF.AdjustFormatting(cch, GetParaFormatCache());
  1108. _rpPF.AdjustForward();
  1109. }
  1110. if(pnm)
  1111. {
  1112. // We assume that Cch is positive (or zero) here
  1113. Assert(cch >= 0);
  1114. pnm->NotifyPostReplaceRange((ITxNotify *)this, CP_INFINITE, 0, 0,
  1115. GetCp(), GetCp() + cch);
  1116. }
  1117. }
  1118. /*
  1119. * CRchTxtPtr::IsRich()
  1120. *
  1121. * @mfunc
  1122. * Determine whether rich-text operation is operable
  1123. *
  1124. * @rdesc
  1125. * TRUE if associated CTxtEdit::_fRich = 1, i.e., control is allowed
  1126. * to be rich.
  1127. */
  1128. BOOL CRchTxtPtr::IsRich()
  1129. {
  1130. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::IsRich");
  1131. return GetPed()->IsRich();
  1132. }
  1133. /*
  1134. * CRchTxtPtr::Check_rpCF()
  1135. *
  1136. * @mfunc
  1137. * enable _rpCF if it's not already enabled
  1138. *
  1139. * @rdesc
  1140. * TRUE if _rpCF is enabled
  1141. */
  1142. BOOL CRchTxtPtr::Check_rpCF()
  1143. {
  1144. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::Check_rpCF");
  1145. if(_rpCF.IsValid())
  1146. return TRUE;
  1147. if(!_rpCF.InitRuns (GetCp(), GetTextLength(),
  1148. &(GetPed()->GetTxtStory()->_pCFRuns)))
  1149. {
  1150. return FALSE;
  1151. }
  1152. CNotifyMgr *pnm = GetPed()->GetNotifyMgr(); // For notifying of changes
  1153. if(pnm)
  1154. pnm->NotifyPostReplaceRange( // Notify interested parties
  1155. (ITxNotify *)this, CP_INFINITE, // that
  1156. 0, 0, CP_INFINITE, CP_INFINITE);
  1157. return TRUE;
  1158. }
  1159. /*
  1160. * CRchTxtPtr::Check_rpPF()
  1161. *
  1162. * @mfunc
  1163. * enable _rpPF if it's not already enabled
  1164. *
  1165. * @rdesc
  1166. * TRUE if _rpPF is enabled
  1167. */
  1168. BOOL CRchTxtPtr::Check_rpPF()
  1169. {
  1170. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::Check_rpPF");
  1171. if(_rpPF.IsValid())
  1172. return TRUE;
  1173. if(!IsRich())
  1174. return FALSE;
  1175. if(!_rpPF.InitRuns (GetCp(), GetTextLength(),
  1176. &(GetPed()->GetTxtStory()->_pPFRuns)))
  1177. {
  1178. return FALSE;
  1179. }
  1180. if (IsParaRTL())
  1181. _rpPF.GetRun(0)->_level._value = 1; // Set default paragraph base level
  1182. CNotifyMgr *pnm = GetPed()->GetNotifyMgr(); // For notifying of changes
  1183. if(pnm)
  1184. pnm->NotifyPostReplaceRange( // Notify interested parties
  1185. (ITxNotify *)this, CP_INFINITE, // of the change.
  1186. 0, 0, CP_INFINITE, CP_INFINITE);
  1187. return TRUE;
  1188. }
  1189. /*
  1190. * CRchTxtPtr::FindWordBreak(action, cpMost)
  1191. *
  1192. * @mfunc
  1193. * Same as CTxtPtr::FindWordBreak(), but moves the whole rich text ptr
  1194. *
  1195. * @rdesc
  1196. * cch this rich text ptr is moved
  1197. */
  1198. LONG CRchTxtPtr::FindWordBreak(
  1199. INT action, //@parm Kind of word break to find
  1200. LONG cpMost) //@parm Limiting character position
  1201. {
  1202. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::FindWordBreak");
  1203. LONG cch = _rpTX.FindWordBreak(action, cpMost);
  1204. _rpCF.Move(cch);
  1205. _rpPF.Move(cch);
  1206. return cch;
  1207. }
  1208. /*
  1209. * CRchTxtPtr::BindToCp(dwNewCp)
  1210. *
  1211. * @mfunc
  1212. * Set cp to new value and recalculate that new position.
  1213. */
  1214. void CRchTxtPtr::BindToCp(
  1215. LONG cp) // @parm new cp for rich text
  1216. {
  1217. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::BindToCp");
  1218. _rpTX.BindToCp(cp); // Recalculate cp for plain text
  1219. // Use the InitRunPtrs routine so that the run pointers will get
  1220. // re-initialized and rebound with the correct run array. The
  1221. // run array formerly used (if any at all) is not necessarily valid
  1222. // when this function is called.
  1223. InitRunPtrs();
  1224. // Do invariant testing at end because this fixes up the rich text
  1225. // pointer in the face of backing store changes.
  1226. _TEST_INVARIANT_
  1227. }
  1228. /*
  1229. * CRchTxtPtr::CheckFormatRuns ()
  1230. *
  1231. * @mfunc
  1232. * Check the format runs against what's in CTxtStory. If
  1233. * different, forces a rebind to <p cp>
  1234. */
  1235. void CRchTxtPtr::CheckFormatRuns()
  1236. {
  1237. CTxtStory *pStory = GetPed()->GetTxtStory();
  1238. if (pStory->GetCFRuns() != (CFormatRuns *)_rpCF._pRuns ||
  1239. pStory->GetPFRuns() != (CFormatRuns *)_rpPF._pRuns)
  1240. {
  1241. InitRunPtrs();
  1242. }
  1243. _TEST_INVARIANT_
  1244. }
  1245. /*
  1246. * CRchTxtPtr::ChangeCase(cch, Type, publdr)
  1247. *
  1248. * @mfunc
  1249. * Change case of cch chars starting at this text ptr according to Type,
  1250. * which has the possible values:
  1251. *
  1252. * tomSentenceCase = 0: capitalize first letter of each sentence
  1253. * tomLowerCase = 1: change all letters to lower case
  1254. * tomUpperCase = 2: change all letters to upper case
  1255. * tomTitleCase = 3: capitalize the first letter of each word
  1256. * tomToggleCase = 4: toggle the case of each letter
  1257. *
  1258. * @rdesc
  1259. * TRUE iff a change occurred
  1260. *
  1261. * @devnote
  1262. * Since this routine only changes the case of characters, it has no
  1263. * effect on rich-text formatting. However it is part of the CRchTxtPtr
  1264. * class in order to notify the display of changes. CTxtRanges are also
  1265. * notified just in case the text blocks are modified.
  1266. */
  1267. BOOL CRchTxtPtr::ChangeCase (
  1268. LONG cch, //@parm # chars to change case for
  1269. LONG Type, //@parm Type of change case command
  1270. IUndoBuilder *publdr) //@parm UndoBuilder to receive anti-event
  1271. // for any replacements
  1272. {
  1273. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::ChangeCase");
  1274. _TEST_INVARIANT_
  1275. #define BUFFERLEN 256
  1276. LONG cchChunk, cchFirst, cchGet, cchLast, cchRep;
  1277. LONG cpSave = GetCp();
  1278. BOOL fAlpha, fToUpper, fUpper; // Flags controling case change
  1279. BOOL fChange = FALSE; // No change yet
  1280. BOOL fStart = TRUE; // Start of Word/Sentence
  1281. TCHAR * pch; // Ptr to walk rgCh with
  1282. WORD * pType; // Ptr to walk rgType with
  1283. WCHAR rgCh[BUFFERLEN]; // Char buffer to work in
  1284. WORD rgType[BUFFERLEN]; // C1_TYPE array for rgCh
  1285. if( GetCp() )
  1286. {
  1287. if( Type == tomSentenceCase )
  1288. fStart = _rpTX.IsAtBOSentence();
  1289. else if( Type == tomTitleCase )
  1290. {
  1291. // Check to see if we are at the beginning of
  1292. // a word. This is the case if the character preceding
  1293. // our current position is white space.
  1294. fStart = IsWhiteSpace(GetPrevChar());
  1295. }
  1296. }
  1297. if(cpSave + cch > GetPed()->GetAdjustedTextLength())
  1298. cch = GetPed()->GetAdjustedTextLength() - cpSave;
  1299. // Handle pre-replace range notifications. This method is very
  1300. // useful for delayed rendering of data copied to the clipboard.
  1301. CNotifyMgr *pnm = GetPed()->GetNotifyMgr();
  1302. if(pnm)
  1303. {
  1304. pnm->NotifyPreReplaceRange((ITxNotify *)this, cpSave, cch,
  1305. cch, cpSave, cpSave + cch);
  1306. }
  1307. while(cch > 0) // Do 'em all (or as many as
  1308. { // in story)
  1309. cchChunk = min(BUFFERLEN, cch); // Get next bufferful
  1310. cch -= cchChunk; // Decrement the count
  1311. cchGet = _rpTX.GetText(cchChunk, rgCh); // Manipulate chars in buffer
  1312. if(cchGet < cchChunk) // (for undo, need to use
  1313. { // ReplaceRange())
  1314. cch = 0; // No more chars in story,
  1315. if(!cchGet) // so we'll be done
  1316. break; // We're done already
  1317. cchChunk = cchGet; // Something in this chunk
  1318. }
  1319. W32->GetStringTypeEx(0, CT_CTYPE1, rgCh,// Find out whether chars are
  1320. cchChunk, rgType); // UC, LC, or neither
  1321. cchLast = 0; // Default nothing to replace
  1322. cchFirst = -1;
  1323. for(pch = rgCh, pType = rgType; // Process buffered chars
  1324. cchChunk;
  1325. cchChunk--, pch++, pType++)
  1326. {
  1327. fAlpha = *pType & (C1_UPPER | C1_LOWER); // Nonzero if UC or LC
  1328. fUpper = (*pType & C1_UPPER) != 0; // TRUE if UC
  1329. fToUpper = fStart ? TRUE : fUpper; // capitalize first letter of a
  1330. // sentence
  1331. switch(Type)
  1332. { // Decide whether to change
  1333. case tomLowerCase: // case and determine start
  1334. fToUpper = FALSE; // of word/sentence for title
  1335. break; // and sentence cases
  1336. case tomUpperCase:
  1337. fToUpper = TRUE;
  1338. break;
  1339. case tomToggleCase:
  1340. fToUpper = !fUpper;
  1341. break;
  1342. case tomSentenceCase:
  1343. if(*pch == TEXT('.')) // If sentence terminator,
  1344. fStart = TRUE; // capitalize next alpha
  1345. if(fAlpha) // If this char is alpha, next
  1346. fStart = FALSE; // char can't start a
  1347. break; // sentence
  1348. case tomTitleCase: // If this char is alpha, next
  1349. fStart = (fAlpha == 0); // char can't start a word
  1350. break;
  1351. default:
  1352. return FALSE;
  1353. }
  1354. if(fAlpha && (fToUpper ^ fUpper)) // Only change case if it
  1355. { // makes a difference (saves
  1356. if(fToUpper) // on system calls and undos)
  1357. CharUpperBuff(pch, 1);
  1358. else
  1359. CharLowerBuff(pch, 1);
  1360. fChange = TRUE; // Return value: change made
  1361. if( cchFirst == -1 ) // Save cch of unchanged
  1362. cchFirst = cchGet-cchChunk; // leading string
  1363. cchLast = cchChunk - 1; // Save cch of unchanged
  1364. } // trailing string
  1365. }
  1366. if( cchFirst == -1 )
  1367. {
  1368. Assert(cchLast == 0);
  1369. cchFirst = cchGet;
  1370. }
  1371. Move(cchFirst); // Skip unchanged leading
  1372. cchGet -= cchFirst + cchLast; // string. cchGet = cch of
  1373. // changed span.
  1374. cchRep = _rpTX.ReplaceRange(cchGet, cchGet, rgCh + cchFirst, publdr, NULL, NULL);
  1375. _rpCF.Move(cchRep);
  1376. _rpPF.Move(cchRep);
  1377. Assert(cchRep == cchGet);
  1378. Move(cchLast); // Skip unchanged trailing
  1379. } // string
  1380. if(pnm)
  1381. {
  1382. cch = GetCp() - cpSave;
  1383. pnm->NotifyPostReplaceRange((ITxNotify *)this, cpSave, cch,
  1384. cch, cpSave, GetCp());
  1385. }
  1386. return fChange;
  1387. }
  1388. // The following defines a mask for Units implemented by UnitCounter()
  1389. #define IMPL ((1 << tomCharacter) + (1 << tomWord) + (1 << tomSentence) + \
  1390. (1 << tomParagraph) + (1 << tomLine) + (1 << tomStory) + \
  1391. (1 << tomCharFormat) + (1 << tomParaFormat) + (1 << tomObject) + \
  1392. (1 << tomPage) + (1 << tomCell))
  1393. /*
  1394. * CRchTxtPtr::UnitCounter (Unit, &cUnit, cchMax, fNotAtBOL)
  1395. *
  1396. * @mfunc
  1397. * Helper function to count chars in <p cUnit> Units defined by <p Unit>
  1398. * <p cUnit> is a signed count. If it extends beyond either end of the
  1399. * story, count up to that end and update <p cUnit> accordingly. If
  1400. * <p cchMax> is nonzero, stop counting when the count exceeds <p cchMax>
  1401. * in magnitude.
  1402. *
  1403. * @rdesc
  1404. * If unit is implemented, return cch corresponding to the units counted
  1405. * (up to a maximum magnitude of <p cchMax>) and update cUnit;
  1406. * else return tomForward to signal unit not implemented and cUnit = 0.
  1407. * If unit is implemented but unavailable, e.g., tomObject with no
  1408. * embedded objects, return tomBackward.
  1409. *
  1410. * @devnote
  1411. * This is the basic engine used by the TOM CTxtRange::Move() and Index()
  1412. * methods.
  1413. */
  1414. LONG CRchTxtPtr::UnitCounter (
  1415. LONG Unit, //@parm Type of unit to count
  1416. LONG & cUnit, //@parm Count of units to count chars for
  1417. LONG cchMax, //@parm Maximum character count
  1418. BOOL fNotAtBOL) //@parm TRUE if _fSel && _fCaretNotAtBOL
  1419. {
  1420. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CRchTxtPtr::UnitCounter");
  1421. LONG action; // Gives direction and tomWord commands
  1422. LONG cch; // Collects cch counted
  1423. LONG cchText = GetTextLength();
  1424. LONG cp = GetCp();
  1425. LONG iDir = cUnit > 0 ? 1 : -1;
  1426. LONG j; // For-loop index
  1427. CDisplay *pdp; // Used for tomLine case
  1428. // Valid attribute Units are high bit plus any combo of CFE_xxx.
  1429. // CFE_REVISED is most significant value currently defined.
  1430. if(Unit > 0 && !((IMPL >> Unit) & 1) ||
  1431. Unit < 0 && (Unit & ~(2*CFM_REVISED - 1 + 0x80000000)))
  1432. {
  1433. return tomForward; // Report invalid Unit
  1434. }
  1435. if(!cUnit) // Nothing to count
  1436. return 0;
  1437. if(cchMax <= 0)
  1438. cchMax = tomForward; // No cch limit
  1439. if(cUnit < 0)
  1440. cchMax = min(cp, cchMax); // Don't go before doc start
  1441. else if(cchMax > cchText - cp)
  1442. cchMax = cchText - cp; // Don't go beyond doc end
  1443. if(Unit < 0)
  1444. {
  1445. CCFRunPtr rp(*this);
  1446. cch = rp.CountAttributes(cUnit, cchMax, cp, cchText, Unit);
  1447. goto finish;
  1448. }
  1449. switch(Unit)
  1450. {
  1451. case tomCharacter: // Smallest Unit
  1452. cp += cUnit; // Requested new cp
  1453. ValidateCp(cp); // Make sure it's OK
  1454. cch = cUnit = cp - GetCp(); // How many cch, cUnits
  1455. break; // actually moved
  1456. case tomStory: // Largest Unit
  1457. cch = (cUnit > 0) ? cchText - cp : -cp; // cch to start of story
  1458. cUnit = cch ? iDir : 0; // If already at end/start,
  1459. break; // of story, no count
  1460. case tomCharFormat: // Constant CHARFORMAT
  1461. cch = _rpCF.CountRuns(cUnit, cchMax, cp, cchText);
  1462. break;
  1463. case tomParaFormat: // Constant PARAFORMAT
  1464. cch = _rpPF.CountRuns(cUnit, cchMax, cp, cchText);
  1465. break;
  1466. case tomObject:
  1467. if(!GetObjectCount()) // No objects: can't move, so
  1468. {
  1469. cUnit = 0; // set cUnit = 0 and
  1470. return tomBackward; // signal Unit unavailable
  1471. }
  1472. cch = GetPed()->_pobjmgr->CountObjects(cUnit, GetCp());
  1473. break;
  1474. case tomCell:
  1475. {
  1476. CTxtRange rg(*this);
  1477. cch = rg.CountCells(cUnit, cchMax);
  1478. }
  1479. break;
  1480. case tomScreen: // Could be supported
  1481. if(!GetPed()->IsInPageView()) // in Normal View using
  1482. return tomBackward; // ITextSelection::Down()
  1483. Unit = tomPage; // In Page View, it's an alias
  1484. case tomPage:
  1485. case tomLine:
  1486. pdp = GetPed()->_pdp;
  1487. if(pdp) // If this story has a display
  1488. { // use a CLinePtr
  1489. CLinePtr rp(pdp);
  1490. //REVIEW (keithcu) This should be fixed. We can't trust the client to pass
  1491. //down an optimal cchMax. For best results, we should have a CountRuns and CountPages
  1492. //recalc as needed when moving down through the document.
  1493. pdp->WaitForRecalc(min(cp + cchMax, cchText), -1);
  1494. rp.SetCp(cp, FALSE);
  1495. fNotAtBOL = fNotAtBOL && rp.GetLineIndex() &&
  1496. (Unit == tomLine || rp->_fFirstOnPage);
  1497. cch = (Unit == tomLine || !cUnit || !rp.IsValid())
  1498. ? rp.CountRuns (cUnit, cchMax, cp, cchText)
  1499. : rp.CountPages(cUnit, cchMax, cp, cchText);
  1500. if (cch == tomBackward)
  1501. return tomBackward;
  1502. if(fNotAtBOL && cUnit < 0)
  1503. cUnit++; // Keep on same line/page as selection
  1504. break;
  1505. }
  1506. if(Unit == tomPage)
  1507. { // No display: no pagination
  1508. cUnit = 0;
  1509. return tomBackward;
  1510. } // For tomLine, fall thru to
  1511. // treat as tomPara
  1512. default: // tp dependent cases
  1513. { // Block to contain tp() which
  1514. CTxtPtr tp(_rpTX); // takes time to construct
  1515. if (cUnit < 0) // Counting backward
  1516. {
  1517. action = (Unit == tomWord)
  1518. ? WB_MOVEWORDLEFT : tomBackward;
  1519. }
  1520. else // Counting forward
  1521. {
  1522. action = (Unit == tomWord)
  1523. ? WB_MOVEWORDRIGHT : tomForward;
  1524. }
  1525. for (cch = 0, j = cUnit; j && abs(cch) < cchMax; j -= iDir)
  1526. {
  1527. cp = tp.GetCp(); // Save starting cp for
  1528. switch (Unit) // calculating cch for this
  1529. { // Unit
  1530. case tomWord:
  1531. tp.FindWordBreak(action);
  1532. break;
  1533. case tomSentence:
  1534. tp.FindBOSentence(action);
  1535. break;
  1536. case tomLine: // Story has no line array:
  1537. case tomParagraph: // treat as tomParagraph
  1538. tp.FindEOP(action);
  1539. break;
  1540. default:
  1541. cUnit = 0;
  1542. return tomForward; // Return error
  1543. }
  1544. if(tp.GetCp() - cp == 0) // No count:
  1545. break; // don't decrement cUnit
  1546. cch += tp.GetCp() - cp;
  1547. }
  1548. cUnit -= j; // Discount any runs not
  1549. } // counted if |cch| >= cchMax
  1550. }
  1551. finish:
  1552. if(abs(cch) > cchMax) // Keep cch within requested
  1553. { // limit
  1554. cch = cch > 0 ? cchMax : -cchMax;
  1555. if(Unit == tomCharacter)
  1556. cUnit = cch;
  1557. }
  1558. Move(cch); // Move to new position
  1559. return cch; // Total cch counted
  1560. }
  1561. /*
  1562. * Notes on RichEdit 1.0 mode:
  1563. *
  1564. * CF_UNICODETEXT should not be used in RichEdit 1.0 mode. \uN should use
  1565. * the alternative.
  1566. *
  1567. * CleanseAndReplaceRange() and the RTF reader need to ensure that any
  1568. * Unicode chars entered belong to a CharSet and stamp it accordingly.
  1569. * If no CharSet exists for the character, then blank should be used.
  1570. */
  1571. /*
  1572. * CRchTxtPtr::GetCachFromCch(cch)
  1573. *
  1574. * @mfunc
  1575. * Return count of A chars corresponding to cch W chars starting at
  1576. * this text ptr. On first call start with this text ptr at cp = 0.
  1577. *
  1578. * @rdesc
  1579. * Count of A chars between this text ptr and cp
  1580. *
  1581. * @comm
  1582. * The algorithm assumes that for a DBCS charset any character
  1583. * above 128 has two bytes, except for the halfwidth KataKana,
  1584. * which are single bytes in ShiftJis.
  1585. */
  1586. LONG CRchTxtPtr::GetCachFromCch(
  1587. LONG cch) //@parm Count of chars to check
  1588. {
  1589. BYTE iCharRep;
  1590. LONG cach = 0; // No ach counted yet
  1591. LONG cch1;
  1592. LONG cchRun; // CF run count
  1593. LONG cchValid; // Text run count
  1594. WCHAR ch;
  1595. const WCHAR *pch; // Ptr to text run
  1596. const CCharFormat *pCF;
  1597. while(cch > 0)
  1598. {
  1599. cchRun = _rpCF.IsValid()
  1600. ? _rpCF.GetCchLeft()
  1601. : GetTextLength() - GetCp();
  1602. if(!cchRun)
  1603. break; // No more text
  1604. pCF = GetCF();
  1605. iCharRep = pCF->_iCharRep;
  1606. if (!IsFECharRep(iCharRep) ||
  1607. (pCF->_dwEffects & CFE_RUNISDBCS))
  1608. {
  1609. cchRun = min(cchRun, cch);
  1610. cach += cchRun; // SBCS run or DBCS stored as
  1611. cch -= cchRun; // one byte per char
  1612. Move(cchRun);
  1613. continue;
  1614. }
  1615. pch = GetPch(cchValid);
  1616. Assert(pch);
  1617. cchValid = min(cchValid, cchRun);
  1618. for(cch1 = 0; cch > 0 && cchValid--; cch1++)
  1619. {
  1620. cch--;
  1621. ch = *pch++;
  1622. if(IN_RANGE(128, ch, 0xFFF0) &&
  1623. (iCharRep != SHIFTJIS_INDEX || !IN_RANGE(0xFF61, ch, 0xFF9F)))
  1624. {
  1625. cach++;
  1626. }
  1627. }
  1628. cach += cch1;
  1629. Move(cch1);
  1630. }
  1631. return cach;
  1632. }
  1633. /*
  1634. * CRchTxtPtr::GetCchFromCach(cach)
  1635. *
  1636. * @mfunc
  1637. * Return count of W chars corresponding to cach A chars starting at this
  1638. * text ptr. On first call start with this text ptr at cp = 0.
  1639. *
  1640. * @rdesc
  1641. * Count of W chars corresponding to cach A chars starting at this tp.
  1642. *
  1643. * @comm
  1644. * The algorithm assumes that for a DBCS charset any character
  1645. * above 128 has two bytes, except for the halfwidth KataKana,
  1646. * which are single bytes in ShiftJis.
  1647. */
  1648. LONG CRchTxtPtr::GetCchFromCach(
  1649. LONG cach) //@parm Count of ach's starting at this text ptr
  1650. {
  1651. BYTE iCharRep;
  1652. LONG cch = 0; // No ch's yet
  1653. LONG cch1;
  1654. LONG cchRun; // CF run count
  1655. LONG cchValid; // Text run count
  1656. WCHAR ch;
  1657. const WCHAR *pch; // Ptr to text run
  1658. const CCharFormat *pCF;
  1659. while(cach > 0)
  1660. {
  1661. cchRun = _rpCF.IsValid()
  1662. ? _rpCF.GetCchLeft()
  1663. : GetTextLength() - GetCp();
  1664. if(!cchRun)
  1665. break; // No more text
  1666. pCF = GetCF();
  1667. iCharRep = pCF->_iCharRep;
  1668. if (!IsFECharRep(iCharRep) ||
  1669. (pCF->_dwEffects & CFE_RUNISDBCS))
  1670. {
  1671. cchRun = min(cchRun, cach); // SBCS run or DBCS stored as
  1672. cach -= cchRun; // one byte per char
  1673. cch += cchRun;
  1674. Move(cchRun);
  1675. continue;
  1676. }
  1677. pch = GetPch(cchValid);
  1678. Assert(pch);
  1679. cchValid = min(cchValid, cchRun);
  1680. for(cch1 = 0; cach > 0 && cchValid--; cch1++)
  1681. {
  1682. cach--;
  1683. ch = *pch++;
  1684. if(IN_RANGE(128, ch, 0xFFF0) &&
  1685. (iCharRep != SHIFTJIS_INDEX || !IN_RANGE(0xFF61, ch, 0xFF9F)))
  1686. {
  1687. cach--;
  1688. }
  1689. }
  1690. cch += cch1;
  1691. Move(cch1);
  1692. }
  1693. return cch;
  1694. }
  1695. /*
  1696. * CRchTxtPtr::Zombie ()
  1697. *
  1698. * @mfunc
  1699. * Turn this object into a zombie by NULLing out its _ped member
  1700. */
  1701. void CRchTxtPtr::Zombie ()
  1702. {
  1703. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::Zombie");
  1704. _rpTX.Zombie();
  1705. _rpCF.SetToNull();
  1706. _rpPF.SetToNull();
  1707. }