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.

701 lines
15 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module DOC.C CTxtStory and CTxtArray implementation |
  5. *
  6. * Original Authors: <nl>
  7. * Original RichEdit code: David R. Fulmer <nl>
  8. * Christian Fortini <nl>
  9. * Murray Sargent <nl>
  10. *
  11. * History: <nl>
  12. * 6/25/95 alexgo Cleanup and reorganization
  13. *
  14. * Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
  15. */
  16. #include "_common.h"
  17. #include "_doc.h"
  18. #include "_format.h"
  19. ASSERTDATA
  20. // =========================== Invariant stuff ======================
  21. #define DEBUG_CLASSNAME CTxtArray
  22. #include "_invar.h"
  23. // ======================== CTxtArray class =========================
  24. #ifdef DEBUG
  25. /*
  26. * CTxtArray::Invariant
  27. *
  28. * @mfunc Tests CTxtArray's state
  29. *
  30. * @rdesc Returns TRUE always; failures are indicated by Asserts
  31. * Actually in this routine, we return count of chars in blocks
  32. * since we need this value for one check.
  33. */
  34. BOOL CTxtArray::Invariant() const
  35. {
  36. static LONG numTests = 0;
  37. numTests++; // How many times we've been called.
  38. LONG cch = 0;
  39. LONG iMax = Count();
  40. if(iMax > 0)
  41. {
  42. CTxtBlk *ptb = Elem(0);
  43. // ptb shouldn't be NULL since we're within Count elements
  44. Assert(ptb);
  45. for(LONG i = 0; i < iMax; i++, ptb++)
  46. {
  47. LONG cchCurr = ptb->_cch;
  48. cch += cchCurr;
  49. Assert ( cchCurr >= 0 );
  50. Assert ( cchCurr <= CchOfCb(ptb->_cbBlock) );
  51. // While we're here, check range of interblock gaps
  52. Assert (ptb->_ibGap >= 0);
  53. Assert (ptb->_ibGap <= ptb->_cbBlock);
  54. LONG cchGap = CchOfCb(ptb->_ibGap);
  55. Assert ( cchGap >= 0 );
  56. Assert ( cchGap <= cchCurr );
  57. }
  58. }
  59. return cch;
  60. }
  61. #endif // DEBUG
  62. /*
  63. * CTxtArray::CTxtArray()
  64. *
  65. * @mfunc Text array constructor
  66. */
  67. CTxtArray::CTxtArray() : CArray<CTxtBlk> ()
  68. {
  69. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::CTxtArray()");
  70. AssertSz(CchOfCb(cbBlockMost) - cchGapInitial >= cchBlkInitmGapI * 2,
  71. "cchBlockMax - cchGapInitial must be at least (cchBlockInitial - cchGapInitial) * 2");
  72. Assert(!_cchText && !_iCF && !_iPF);
  73. // Make sure we have no data to initialize
  74. Assert(sizeof(CTxtArray) == sizeof(CArray<CTxtBlk>) + sizeof(_cchText) + 2*sizeof(_iCF));
  75. }
  76. /*
  77. * CTxtArray::~CTxtArray
  78. *
  79. * @mfunc Text array destructor
  80. */
  81. CTxtArray::~CTxtArray()
  82. {
  83. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::~CTxtArray");
  84. for(LONG itb = Count(); itb--; )
  85. {
  86. CTxtBlk *ptb = Elem(itb);
  87. if(ptb)
  88. ptb->FreeBlock();
  89. else
  90. AssertSz(FALSE, "CTxtArray::~CTxtArray: NULL block ptr");
  91. }
  92. }
  93. /*
  94. * CTxtArray::CalcTextLength()
  95. *
  96. * @mfunc Computes and return length of text in this text array
  97. *
  98. * @rdesc Count of character in this text array
  99. *
  100. * @devnote This call may be computationally expensive; we have to
  101. * sum up the character sizes of all of the text blocks in
  102. * the array.
  103. */
  104. LONG CTxtArray::CalcTextLength() const
  105. {
  106. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::GetCch");
  107. _TEST_INVARIANT_
  108. LONG itb = Count();
  109. CTxtBlk *ptb = Elem(0);
  110. if(!itb || !ptb)
  111. return 0;
  112. for(LONG cch = 0; itb--; ptb++)
  113. cch += ptb->_cch;
  114. return cch;
  115. }
  116. /*
  117. * CTxtArray::AddBlock(itbNew, cb)
  118. *
  119. * @mfunc create new text block
  120. *
  121. * @rdesc
  122. * FALSE if block could not be added
  123. * non-FALSE otherwise
  124. *
  125. * @comm
  126. * Side Effects:
  127. * moves text block array
  128. */
  129. BOOL CTxtArray::AddBlock(
  130. LONG itbNew, //@parm index of the new block
  131. LONG cb) //@parm size of new block; if <lt>= 0, default is used
  132. {
  133. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::AddBlock");
  134. _TEST_INVARIANT_
  135. if(cb <= 0)
  136. cb = cbBlockInitial;
  137. AssertSz(cb > 0, "CTxtArray::AddBlock() - adding block of size zero");
  138. AssertSz(cb <= cbBlockMost, "CTxtArray::AddBlock() - block too big");
  139. CTxtBlk *ptb = Insert(itbNew, 1);
  140. if(!ptb || !ptb->InitBlock(cb))
  141. {
  142. TRACEERRSZSC("TXTARRAT::AddBlock() - unable to allocate new block", E_OUTOFMEMORY);
  143. return FALSE;
  144. }
  145. return TRUE;
  146. }
  147. /*
  148. * CTxtArray::SplitBlock(itb, ichSplit, cchFirst, cchLast, fStreaming)
  149. *
  150. * @mfunc split a text block into two
  151. *
  152. * @rdesc
  153. * FALSE if the block could not be split <nl>
  154. * non-FALSE otherwise
  155. *
  156. * @comm
  157. * Side Effects: <nl>
  158. * moves text block array
  159. */
  160. BOOL CTxtArray::SplitBlock(
  161. LONG itb, //@parm index of the block to split
  162. LONG ichSplit, //@parm character index within block at which to split
  163. LONG cchFirst, //@parm desired extra space in first block
  164. LONG cchLast, //@parm desired extra space in new block
  165. BOOL fStreaming) //@parm TRUE if streaming in new text
  166. {
  167. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::SplitBlock");
  168. _TEST_INVARIANT_
  169. AssertSz(ichSplit > 0 || cchFirst > 0,
  170. "CTxtArray::SplitBlock(): splitting at beginning, but not adding anything");
  171. AssertSz(itb >= 0, "CTxtArray::SplitBlock(): negative itb");
  172. // Compute size for first half
  173. AssertSz(cchFirst + ichSplit <= CchOfCb(cbBlockMost),
  174. "CTxtArray::SplitBlock(): first size too large");
  175. cchFirst += ichSplit + cchGapInitial;
  176. cchFirst = min(cchFirst, CchOfCb(cbBlockMost));
  177. // Compute size for second half
  178. CTxtBlk *ptb = Elem(itb);
  179. if(!ptb)
  180. {
  181. AssertSz(FALSE, "CTxtArray::SplitBlock: NULL block ptr");
  182. return FALSE;
  183. }
  184. AssertSz(cchLast + ptb->_cch - ichSplit <= CchOfCb(cbBlockMost),
  185. "CTxtArray::SplitBlock(): second size too large");
  186. cchLast += ptb->_cch - ichSplit + cchGapInitial;
  187. cchLast = min(cchLast, CchOfCb(cbBlockMost));
  188. // Allocate second block and move text to it
  189. // If streaming in, allocate a block that's as big as possible so that
  190. // subsequent additions of text are faster. We always fall back to
  191. // smaller allocations so this won't cause unnecessary errors. When
  192. // we're done streaming we compress blocks, so this won't leave a
  193. // big empty gap. NOTE: ***** moves rgtb *****
  194. if(fStreaming)
  195. {
  196. LONG cb = cbBlockMost;
  197. const LONG cbMin = CbOfCch(cchLast);
  198. while(cb >= cbMin && !AddBlock(itb + 1, cb))
  199. cb -= cbBlockCombine;
  200. if(cb >= cbMin)
  201. goto got_block;
  202. }
  203. if(!AddBlock(itb + 1, CbOfCch(cchLast)))
  204. {
  205. TRACEERRSZSC("CTxtArray::SplitBlock(): unabled to add new block", E_FAIL);
  206. return FALSE;
  207. }
  208. got_block:
  209. LPBYTE pbSrc;
  210. LPBYTE pbDst;
  211. CTxtBlk *ptb1 = Elem(itb+1);
  212. ptb = Elem(itb); // Recompute ptb and ptb1 after rgtb moves
  213. if(!ptb || !ptb1)
  214. {
  215. AssertSz(FALSE, "CTxtArray::SplitBlock: NULL block ptr");
  216. return FALSE;
  217. }
  218. ptb1->_cch = ptb->_cch - ichSplit;
  219. ptb1->_ibGap = 0;
  220. pbDst = (LPBYTE) (ptb1->_pch - ptb1->_cch) + ptb1->_cbBlock;
  221. ptb->MoveGap(ptb->_cch); // make sure pch points to a continuous block of all text in ptb.
  222. pbSrc = (LPBYTE) (ptb->_pch + ichSplit);
  223. CopyMemory(pbDst, pbSrc, CbOfCch(ptb1->_cch));
  224. ptb->_cch = ichSplit;
  225. ptb->_ibGap = CbOfCch(ichSplit);
  226. // Resize first block
  227. if(CbOfCch(cchFirst) != ptb->_cbBlock)
  228. {
  229. //$ FUTURE: don't resize unless growing or shrinking considerably
  230. if(!ptb->ResizeBlock(CbOfCch(cchFirst)))
  231. {
  232. TRACEERRSZSC("TXTARRA::SplitBlock(): unabled to resize block", E_OUTOFMEMORY);
  233. return FALSE;
  234. }
  235. }
  236. return TRUE;
  237. }
  238. /*
  239. * CTxtArray::ShrinkBlocks()
  240. *
  241. * @mfunc Shrink all blocks to their minimal size
  242. */
  243. void CTxtArray::ShrinkBlocks()
  244. {
  245. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::ShrinkBlocks");
  246. _TEST_INVARIANT_
  247. for(LONG itb = Count(); itb--; )
  248. {
  249. CTxtBlk *ptb = Elem(itb);
  250. if(ptb)
  251. ptb->ResizeBlock(CbOfCch(ptb->_cch));
  252. else
  253. AssertSz(FALSE, "CTxtArray::ShrinkBlocks: NULL block ptr");
  254. }
  255. }
  256. /*
  257. * CTxtArray::RemoveBlocks(itbFirst, ctbDel)
  258. *
  259. * @mfunc remove a range of text blocks
  260. *
  261. * @comm Side Effects: <nl>
  262. * moves text block array
  263. */
  264. void CTxtArray::RemoveBlocks(
  265. LONG itbFirst, //@parm Index of first block to remove
  266. LONG ctbDel) //@parm Count of blocks to remove
  267. {
  268. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::RemoveBlocks");
  269. _TEST_INVARIANT_
  270. LONG itb = itbFirst;
  271. AssertSz(itb + ctbDel <= Count(), "CTxtArray::RemoveBlocks(): not enough blocks");
  272. for(LONG ctb = ctbDel; ctb--; itb++)
  273. {
  274. CTxtBlk *ptb = Elem(itb);
  275. if(ptb)
  276. ptb->FreeBlock();
  277. else
  278. AssertSz(FALSE, "CTxtArray::RemoveBlocks: NULL block ptr");
  279. }
  280. Remove(itbFirst, ctbDel);
  281. }
  282. /*
  283. * CTxtArray::CombineBlocks(itb)
  284. *
  285. * @mfunc combine adjacent text blocks
  286. *
  287. * @rdesc
  288. * nothing
  289. *
  290. * @comm
  291. * Side Effects: <nl>
  292. * moves text block array
  293. *
  294. * @devnote
  295. * scans blocks from itb - 1 through itb + 1 trying to combine
  296. * adjacent blocks
  297. */
  298. void CTxtArray::CombineBlocks(
  299. LONG itb) //@parm index of the first block modified
  300. {
  301. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::CombineBlocks");
  302. _TEST_INVARIANT_
  303. LONG ctb;
  304. LONG cbT;
  305. CTxtBlk *ptb, *ptb1;
  306. if(itb > 0)
  307. itb--;
  308. ctb = min(3, Count() - itb);
  309. if(ctb <= 1)
  310. return;
  311. for(; ctb > 1; ctb--)
  312. {
  313. ptb = Elem(itb); // Can we combine current
  314. ptb1 = Elem(itb+1); // and next blocks ?
  315. cbT = CbOfCch(ptb->_cch + ptb1->_cch + cchGapInitial);
  316. if(cbT <= cbBlockInitial)
  317. { // Yes
  318. if(cbT != ptb->_cbBlock && !ptb->ResizeBlock(cbT))
  319. continue;
  320. ptb ->MoveGap(ptb->_cch); // Move gaps at ends of
  321. ptb1->MoveGap(ptb1->_cch); // both blocks
  322. CopyMemory(ptb->_pch + ptb->_cch, // Copy next block text
  323. ptb1->_pch, CbOfCch(ptb1->_cch)); // into current block
  324. ptb->_cch += ptb1->_cch;
  325. ptb->_ibGap += CbOfCch(ptb1->_cch);
  326. RemoveBlocks(itb+1, 1); // Remove next block
  327. }
  328. else
  329. itb++;
  330. }
  331. }
  332. /*
  333. * CTxtArray::GetChunk(ppch, cch, pchChunk, cchCopy)
  334. *
  335. * @mfunc
  336. * Get content of text chunk in this text array into a string
  337. *
  338. * @rdesc
  339. * remaining count of characters to get
  340. */
  341. LONG CTxtArray::GetChunk(
  342. TCHAR **ppch, //@parm ptr to ptr to buffer to copy text chunk into
  343. LONG cch, //@parm length of pch buffer
  344. TCHAR *pchChunk, //@parm ptr to text chunk
  345. LONG cchCopy) const //@parm count of characters in chunk
  346. {
  347. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::GetChunk");
  348. _TEST_INVARIANT_
  349. if(cch > 0 && cchCopy > 0)
  350. {
  351. if(cch < cchCopy)
  352. cchCopy = cch; // Copy less than full chunk
  353. CopyMemory(*ppch, pchChunk, cchCopy*sizeof(TCHAR));
  354. *ppch += cchCopy; // Adjust target buffer ptr
  355. cch -= cchCopy; // Fewer chars to copy
  356. }
  357. return cch; // Remaining count to copy
  358. }
  359. const CCharFormat* CTxtArray::GetCharFormat(LONG iCF)
  360. {
  361. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtArray::GetCharFormat");
  362. const CCharFormat * pCF;
  363. if(iCF < 0)
  364. iCF = _iCF;
  365. Assert(iCF >= 0);
  366. if(FAILED(GetCharFormatCache()->Deref(iCF, &pCF)))
  367. {
  368. AssertSz(FALSE, "CTxtArray::GetCharFormat: couldn't deref iCF");
  369. pCF = NULL;
  370. }
  371. return pCF;
  372. }
  373. const CParaFormat* CTxtArray::GetParaFormat(LONG iPF)
  374. {
  375. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtArray::GetParaFormat");
  376. const CParaFormat * pPF;
  377. if(iPF < 0)
  378. iPF = _iPF;
  379. Assert(iPF >= 0);
  380. if(FAILED(GetParaFormatCache()->Deref(iPF, &pPF)))
  381. {
  382. AssertSz(FALSE, "CTxtArray::GetParaFormat: couldn't deref iPF");
  383. pPF = NULL;
  384. }
  385. return pPF;
  386. }
  387. // ======================== CTxtBlk class =================================
  388. /*
  389. * CTxtBlk::InitBlock(cb)
  390. *
  391. * @mfunc
  392. * Initialize this text block
  393. *
  394. * @rdesc
  395. * TRUE if success, FALSE if allocation failed
  396. */
  397. BOOL CTxtBlk::InitBlock(
  398. LONG cb) //@parm initial size of the text block
  399. {
  400. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtBlk::InitBlock");
  401. _pch = NULL;
  402. _cch = 0;
  403. _ibGap = 0;
  404. _cbBlock= cb;
  405. if(cb)
  406. _pch = (TCHAR*)PvAlloc(cb, GMEM_ZEROINIT);
  407. return _pch != 0;
  408. }
  409. /*
  410. * CTxtBlk::FreeBlock()
  411. *
  412. * @mfunc
  413. * Free this text block
  414. */
  415. void CTxtBlk::FreeBlock()
  416. {
  417. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtBlk::FreeBlock");
  418. FreePv(_pch);
  419. _pch = NULL;
  420. _cch = 0;
  421. _ibGap = 0;
  422. _cbBlock= 0;
  423. }
  424. /*
  425. * CTxtBlk::MoveGap(ichGap)
  426. *
  427. * @mfunc
  428. * move gap in this text block
  429. */
  430. void CTxtBlk::MoveGap(
  431. LONG ichGap) //@parm new position for the gap
  432. {
  433. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtBlk::MoveGap");
  434. LONG cbMove;
  435. LONG ibGapNew = CbOfCch(ichGap);
  436. LPBYTE pbFrom = (LPBYTE) _pch;
  437. LPBYTE pbTo;
  438. if(ibGapNew == _ibGap)
  439. return;
  440. if(ibGapNew < _ibGap)
  441. {
  442. cbMove = _ibGap - ibGapNew;
  443. pbFrom += ibGapNew;
  444. pbTo = pbFrom + _cbBlock - CbOfCch(_cch);
  445. }
  446. else
  447. {
  448. cbMove = ibGapNew - _ibGap;
  449. pbTo = pbFrom + _ibGap;
  450. pbFrom = pbTo + _cbBlock - CbOfCch(_cch);
  451. }
  452. MoveMemory(pbTo, pbFrom, cbMove);
  453. _ibGap = ibGapNew;
  454. }
  455. /*
  456. * CTxtBlk::ResizeBlock(cbNew)
  457. *
  458. * @mfunc
  459. * resize this text block
  460. *
  461. * @rdesc
  462. * FALSE if block could not be resized <nl>
  463. * non-FALSE otherwise
  464. *
  465. * @comm
  466. * Side Effects: <nl>
  467. * moves text block
  468. */
  469. BOOL CTxtBlk::ResizeBlock(
  470. LONG cbNew) //@parm the new size
  471. {
  472. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtBlk::ResizeBlock");
  473. TCHAR *pch;
  474. LONG cbMove;
  475. AssertSz(cbNew > 0, "resizing block to size <= 0");
  476. AssertSz(cbNew <= cbBlockMost, "CTxtBlk::ResizeBlock() - block too big");
  477. if(cbNew < _cbBlock)
  478. {
  479. if(_ibGap != CbOfCch(_cch))
  480. {
  481. // move text after gap down so that it doesn't get dropped
  482. cbMove = CbOfCch(_cch) - _ibGap;
  483. pch = _pch + CchOfCb(_cbBlock - cbMove);
  484. MoveMemory(pch - CchOfCb(_cbBlock - cbNew), pch, cbMove);
  485. }
  486. _cbBlock = cbNew;
  487. }
  488. pch = (TCHAR*)PvReAlloc(_pch, cbNew);
  489. if(!pch)
  490. return _cbBlock == cbNew; // FALSE if grow, TRUE if shrink
  491. _pch = pch;
  492. if(cbNew > _cbBlock)
  493. {
  494. if(_ibGap != CbOfCch(_cch)) // Move text after gap to end so that
  495. { // we don't end up with two gaps
  496. cbMove = CbOfCch(_cch) - _ibGap;
  497. pch += CchOfCb(_cbBlock - cbMove);
  498. MoveMemory(pch + CchOfCb(cbNew - _cbBlock), pch, cbMove);
  499. }
  500. _cbBlock = cbNew;
  501. }
  502. return TRUE;
  503. }
  504. // ======================== CTxtStory class ============================
  505. /*
  506. * CTxtStory::CTxtStory
  507. *
  508. * @mfunc Constructor
  509. *
  510. * @devnote Automatically allocates a text array. If we want to have a
  511. * completely empty edit control, then don't allocate a story. NB!
  512. *
  513. */
  514. CTxtStory::CTxtStory()
  515. {
  516. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtStory::CTxtStory");
  517. _pCFRuns = NULL;
  518. _pPFRuns = NULL;
  519. }
  520. /*
  521. * CTxtStory::~CTxtStory
  522. *
  523. * @mfunc Destructor
  524. */
  525. CTxtStory::~CTxtStory()
  526. {
  527. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtStory::~CTxtStory");
  528. // Remove formatting.
  529. DeleteFormatRuns();
  530. }
  531. /*
  532. * DeleteRuns ()
  533. *
  534. * @mfunc
  535. * Helper function for DeleteFormatRuns() below. Releases
  536. * formats used by format run collection before deleting the
  537. * collection
  538. */
  539. void DeleteRuns(CFormatRuns *pRuns, IFormatCache *pf)
  540. {
  541. if(pRuns) // Format runs may exist
  542. {
  543. LONG n = pRuns->Count();
  544. if(n)
  545. {
  546. CFormatRun *pRun = pRuns->Elem(0);
  547. for( ; n--; pRun++)
  548. pf->Release(pRun->_iFormat); // Free run's format
  549. }
  550. delete pRuns;
  551. }
  552. }
  553. /*
  554. * CTxtStory::DeleteFormatRuns ()
  555. *
  556. * @mfunc Convert to plain - remove format runs
  557. */
  558. void CTxtStory::DeleteFormatRuns()
  559. {
  560. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtStory::ConvertToPlain");
  561. DeleteRuns(_pCFRuns, GetCharFormatCache());
  562. DeleteRuns(_pPFRuns, GetParaFormatCache());
  563. _pCFRuns = NULL;
  564. _pPFRuns = NULL;
  565. }
  566. #ifdef DEBUG
  567. //This dumps the contents of the CTxtStory
  568. //TxtBlk & FormatRun arrays to the debug output.
  569. void CTxtStory::DbgDumpStory(void)
  570. {
  571. CTxtBlk * pblk;
  572. CFormatRun * pcfr;
  573. CFormatRun * ppfr;
  574. LONG ctxtr = 0;
  575. LONG ccfr = 0;
  576. LONG cpfr = 0;
  577. LONG i;
  578. ctxtr = _TxtArray.Count();
  579. if (_pCFRuns)
  580. ccfr = _pCFRuns->Count();
  581. if (_pPFRuns)
  582. cpfr = _pPFRuns->Count();
  583. for(i = 0; i < ctxtr; i++)
  584. {
  585. pblk = (CTxtBlk*)_TxtArray.Elem(i);
  586. Tracef(TRCSEVNONE, "TxtBlk #%d: cch = %d.", (i + 1), pblk->_cch);
  587. }
  588. for(i = 0; i < ccfr; i++)
  589. {
  590. pcfr = (CFormatRun*)_pCFRuns->Elem(i);
  591. Tracef(TRCSEVNONE, "CFR #%d: cch = %d, iFormat = %d.",(i + 1), pcfr->_cch, pcfr->_iFormat);
  592. }
  593. for(i = 0; i < cpfr; i++)
  594. {
  595. ppfr = (CFormatRun*)_pPFRuns->Elem(i);
  596. Tracef(TRCSEVNONE, "PFR #%d: cch = %d, iFormat = %d.",(i + 1), ppfr->_cch, ppfr->_iFormat);
  597. }
  598. }
  599. #endif