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.

664 lines
16 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module RUNPTR.C -- Text run and run pointer class |
  5. *
  6. * Original Authors: <nl>
  7. * Original RichEdit code: David R. Fulmer
  8. * Christian Fortini
  9. * Murray Sargent
  10. *
  11. * History: <nl>
  12. * 6/25/95 alexgo Commented and Cleaned up.
  13. *
  14. * Copyright (c) 1995-1997, Microsoft Corporation. All rights reserved.
  15. */
  16. #include "_common.h"
  17. #include "_runptr.h"
  18. #include "_text.h"
  19. ASSERTDATA
  20. //
  21. // Invariant stuff
  22. //
  23. #define DEBUG_CLASSNAME CRunPtrBase
  24. #include "_invar.h"
  25. // =========================== CRunPtrBase class ==================================================
  26. #ifdef DEBUG
  27. /*
  28. * CRunPtrBase::Invariant()
  29. *
  30. * @mfunc
  31. * Debug-only function that validates the internal state consistency
  32. * for CRunPtrBase
  33. *
  34. * @rdesc
  35. * TRUE always (failures assert)
  36. */
  37. BOOL CRunPtrBase::Invariant() const
  38. {
  39. if(!IsValid())
  40. {
  41. Assert(_iRun == 0 && _ich >= 0); // CLinePtr can have _ich > 0
  42. }
  43. else
  44. {
  45. Assert(_iRun < _pRuns->Count());
  46. LONG cch = _pRuns->Elem(_iRun)->_cch;
  47. Assert((DWORD)_ich <= (DWORD)cch);
  48. }
  49. return TRUE;
  50. }
  51. /*
  52. * CRunPtrBase::ValidatePtr(pRun)
  53. *
  54. * @mfunc
  55. * Debug-only validation method that asserts if pRun doesn't point
  56. * to a valid text run
  57. */
  58. void CRunPtrBase::ValidatePtr(void *pRun) const
  59. {
  60. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::ValidatePtr");
  61. AssertSz(IsValid() && pRun >= _pRuns->Elem(0) &&
  62. pRun <= _pRuns->Elem(Count() - 1),
  63. "CRunPtr::ValidatePtr: illegal ptr");
  64. }
  65. /*
  66. * CRunPtrBase::CalcTextLength()
  67. *
  68. * @mfunc
  69. * Calculate length of text by summing text runs accessible by this
  70. * run ptr
  71. *
  72. * @rdesc
  73. * length of text so calculated, or -1 if failed
  74. */
  75. LONG CRunPtrBase::CalcTextLength() const
  76. {
  77. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::CalcTextLength");
  78. AssertSz(_pRuns,
  79. "CTxtPtr::CalcTextLength() - Invalid operation on single run CRunPtr");
  80. LONG i = Count();
  81. if(!i)
  82. return 0;
  83. LONG cb = _pRuns->Size();
  84. LONG cchText = 0;
  85. CTxtRun *pRun = _pRuns->Elem(0);
  86. while(i--)
  87. {
  88. cchText += pRun->_cch;
  89. pRun = (CTxtRun *)((BYTE *)pRun + cb);
  90. }
  91. return cchText;
  92. }
  93. #endif
  94. /*
  95. * CRunPtrBase::GetCchLeft()
  96. *
  97. * @mfunc
  98. * Calculate count of chars left in run starting at current cp.
  99. * Complements GetIch(), which is length of text up to this cp.
  100. *
  101. * @rdesc
  102. * Count of chars so calculated
  103. */
  104. LONG CRunPtrBase::GetCchLeft() const
  105. {
  106. return GetRun(0)->_cch - GetIch();
  107. }
  108. /*
  109. * CRunPtrBase::CRunPtrBase(pRuns)
  110. *
  111. * @mfunc constructor
  112. */
  113. CRunPtrBase::CRunPtrBase(
  114. CRunArray *pRuns) //@parm The Run array for the run ptr
  115. {
  116. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::CRunPtrBase");
  117. _pRuns = pRuns;
  118. _iRun = 0;
  119. _ich = 0;
  120. //make sure everything has been initialized
  121. Assert(sizeof(CRunPtrBase) == (sizeof(_pRuns) + sizeof(_iRun)
  122. + sizeof(_ich)));
  123. }
  124. /*
  125. * CRunPtrBase::CRunPtrBase(rp)
  126. *
  127. * Copy Constructor
  128. */
  129. CRunPtrBase::CRunPtrBase(
  130. CRunPtrBase& rp) //@parm Other run pointer to initialize from
  131. {
  132. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::CRunPtrBase");
  133. *this = rp;
  134. }
  135. /*
  136. * CRunPtrBase::SetRun(iRun, ich)
  137. *
  138. * @mfunc
  139. * Sets this run ptr to the given run. If it does not
  140. * exist, then we set ourselves to the closest valid run
  141. *
  142. * @rdesc
  143. * TRUE if moved to iRun
  144. */
  145. BOOL CRunPtrBase::SetRun(
  146. LONG iRun, //@parm Run index to use
  147. LONG ich) //@parm Char index within run to use
  148. {
  149. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::SetRun");
  150. _TEST_INVARIANT_
  151. BOOL bRet = TRUE;
  152. LONG count = Count();
  153. // Set the run
  154. if(!IsValid()) // No runs instantiated:
  155. return FALSE; // leave this rp alone
  156. if(iRun >= count) // Validate iRun
  157. {
  158. bRet = FALSE;
  159. iRun = count - 1; // If (!count), negative iRun
  160. } // is handled by following if
  161. if(iRun < 0)
  162. {
  163. bRet = FALSE;
  164. iRun = 0;
  165. }
  166. _iRun = iRun;
  167. // Set the offset
  168. _ich = ich;
  169. CTxtRun *pRun = _pRuns->Elem(iRun);
  170. _ich = min(ich, pRun->_cch);
  171. return bRet;
  172. }
  173. /*
  174. * CRunPtrBase::NextRun()
  175. *
  176. * @mfunc
  177. * Change this RunPtr to that for the next text run
  178. *
  179. * @rdesc
  180. * TRUE if succeeds, i.e., target run exists
  181. */
  182. BOOL CRunPtrBase::NextRun()
  183. {
  184. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::NextRun");
  185. _TEST_INVARIANT_
  186. if(_pRuns && _iRun < Count() - 1)
  187. {
  188. ++_iRun;
  189. _ich = 0;
  190. return TRUE;
  191. }
  192. return FALSE;
  193. }
  194. /*
  195. * CRunPtrBase::PrevRun()
  196. *
  197. * @mfunc
  198. * Change this RunPtr to that for the previous text run
  199. *
  200. * @rdesc
  201. * TRUE if succeeds, i.e., target run exists
  202. */
  203. BOOL CRunPtrBase::PrevRun()
  204. {
  205. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::PrevRun");
  206. _TEST_INVARIANT_
  207. if(_pRuns)
  208. {
  209. _ich = 0;
  210. if(_iRun > 0)
  211. {
  212. _iRun--;
  213. return TRUE;
  214. }
  215. }
  216. return FALSE;
  217. }
  218. /*
  219. * CRunPtrBase::GetRun(cRun)
  220. *
  221. * @mfunc
  222. * Get address of the TxtRun that is cRun runs away from the run
  223. * pointed to by this RunPtr
  224. *
  225. * @rdesc
  226. * ptr to the CTxtRun cRun's away
  227. */
  228. CTxtRun* CRunPtrBase::GetRun(
  229. LONG cRun) const //@parm signed count of runs to reach target CTxtRun
  230. {
  231. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::GetRun");
  232. _TEST_INVARIANT_
  233. Assert(IsValid()); // Common problem...
  234. return _pRuns->Elem(_iRun + cRun);
  235. }
  236. /*
  237. * CRunPtrBase::CalculateCp()
  238. *
  239. * @mfunc
  240. * Get cp of this RunPtr
  241. *
  242. * @rdesc
  243. * cp of this RunPtr
  244. *
  245. * @devnote
  246. * May be computationally expensive if there are many elements
  247. * in the array (we have to run through them all to sum cch's.
  248. * Used by TOM collections and Move commands, so needs to be fast.
  249. */
  250. LONG CRunPtrBase::CalculateCp () const
  251. {
  252. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::GetCp");
  253. _TEST_INVARIANT_
  254. Assert(IsValid());
  255. LONG cb = _pRuns->Size();
  256. LONG cp = _ich; // Correct result if _iRun = 0
  257. LONG iRun = _iRun;
  258. if(!iRun)
  259. return cp;
  260. CTxtRun *pRun = GetRun(0);
  261. while(iRun--)
  262. {
  263. Assert(pRun);
  264. pRun = (CTxtRun *)((BYTE *)pRun - cb);
  265. cp += pRun->_cch;
  266. }
  267. return cp;
  268. }
  269. /*
  270. * CRunPtrBase::BindToCp(cp)
  271. *
  272. * @mfunc
  273. * Set this RunPtr to correspond to a cp.
  274. *
  275. * @rdesc
  276. * the cp actually set to
  277. */
  278. LONG CRunPtrBase::BindToCp(
  279. LONG cp) //@parm character position to move this RunPtr to
  280. {
  281. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::BindToCp");
  282. _iRun = 0;
  283. _ich = 0;
  284. return AdvanceCp(cp);
  285. }
  286. /*
  287. * CRunPtrBase::AdvanceCp(cch)
  288. *
  289. * @mfunc
  290. * Advance this RunPtr by (signed) cch chars. If it lands on the
  291. * end of a run, it automatically goes to the start of the next run
  292. * (if one exists).
  293. *
  294. * @rdesc
  295. * Count of characters actually moved
  296. */
  297. LONG CRunPtrBase::AdvanceCp(
  298. LONG cch) //@parm signed count of chars to move this RunPtr by
  299. {
  300. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::AdvanceCp");
  301. if(!cch || !IsValid())
  302. return cch;
  303. LONG cchSave = cch;
  304. if(cch < 0)
  305. while(cch < 0)
  306. {
  307. if(-cch <= _ich)
  308. {
  309. _ich += cch; // Target is in this run
  310. cch = 0;
  311. break;
  312. }
  313. // Otherwise, we need to go to previous run. First add count
  314. // of chars from start of current run to current postion.
  315. cch += _ich;
  316. if(_iRun <= 0) // Already in first run
  317. {
  318. _iRun = 0;
  319. _ich = 0; // Move to run beginning
  320. break;
  321. }
  322. _ich = _pRuns->Elem(--_iRun)->_cch; // Move to previous run
  323. }
  324. else
  325. {
  326. LONG cchRun;
  327. CTxtRun *pRun = GetRun(0);
  328. while(cch > 0) // Move forward
  329. {
  330. cchRun = pRun->_cch;
  331. _ich += cch;
  332. if(_ich < cchRun) // Target is in this run
  333. {
  334. cch = 0; // Signal countdown completed
  335. break; // (if _ich = cchRun, go to
  336. } // next run)
  337. cch = _ich - cchRun; // Advance to next run
  338. if(++_iRun >= Count()) // Ran past end, back up to
  339. { // end of story
  340. --_iRun;
  341. Assert(_iRun == Count() - 1);
  342. Assert(_pRuns->Elem(_iRun)->_cch == cchRun);
  343. _ich = cchRun;
  344. break;
  345. }
  346. _ich = 0; // Start at new BOL
  347. pRun = (CTxtRun *)((BYTE *)pRun + _pRuns->Size());
  348. }
  349. }
  350. // NB! we check the invariant at the end to handle the case where
  351. // we are updating the cp for a floating range (i.e., we know that
  352. // the cp is invalid, so we fix it up). So we have to check for
  353. // validity *after* the fixup.
  354. _TEST_INVARIANT_
  355. return cchSave - cch; // Return TRUE if countdown
  356. } // completed
  357. /*
  358. * CRunPtrBase::CountRuns(&cRun, cchMax, cp, cchText)
  359. *
  360. * @mfunc
  361. * Count characters up to <p cRun> runs away or <p cchMax> chars,
  362. * whichever comes first. If the target run and <p cchMax> are both
  363. * beyond the corresponding end of the document, count up thru the
  364. * closest run (0 or Count() - 1). The direction of counting is
  365. * determined by the sign of <p cRun>. To count without being limited
  366. * by <p cchMax>, set it equal to tomForward. An initial partial
  367. * run counts as a run, i.e., if cRun > 0 and _ich < cch in current
  368. * run or if cRun < 0 and _ich > 0, that counts as a run.
  369. *
  370. * @rdesc
  371. * Return the signed cch counted and set <p cRun> equal to count of runs
  372. * actually counted. If no runs are allocated, the text is treated as
  373. * a single run. If <p cRun> = 0, -_ich is returned. If <p cRun> <gt> 0
  374. * and this run ptr points to the end of the last run, no change is made
  375. * and 0 is returned.
  376. *
  377. * @devnote
  378. * The maximum count capability is included to be able to count units in
  379. * a range. The <p tp> argument is needed for getting the text length
  380. * when no runs exist and <p cRun> selects forward counting.
  381. */
  382. LONG CRunPtrBase::CountRuns (
  383. LONG & cRun, //@parm Count of runs to get cch for
  384. LONG cchMax, //@parm Maximum char count
  385. LONG cp, //@parm CRchTxtPtr::GetCp()
  386. LONG cchText) const //@parm Text length of associated story
  387. {
  388. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::CountRuns");
  389. _TEST_INVARIANT_
  390. LONG cch;
  391. if(!cRun) // Nothing to do
  392. return 0;
  393. // Simple special single-run case
  394. if(!IsValid()) // No runs instantiated: act as a
  395. { // single run
  396. if(cRun > 0) // Count forward
  397. {
  398. cch = cchText - cp; // Partial count to end of run
  399. cRun = 1; // No more than one run
  400. }
  401. else // Count backward
  402. {
  403. cch = -cp; // Partial count to start of run
  404. cRun = -1; // No less than minus one run
  405. }
  406. if(!cch) // No partial run, so no runs
  407. cRun = 0; // counted
  408. return cch;
  409. }
  410. // General case for which runs are instantiated
  411. LONG cb = _pRuns->Size(); // Size of text run element
  412. LONG iDir;
  413. LONG iRun = _iRun; // Cache run index for current run
  414. LONG j, k; // Handy integers
  415. CTxtRun * pRun = GetRun(0); // Not NULL since runs exist
  416. if(cRun < 0) // Try to count backward cRun runs
  417. {
  418. iDir = -1;
  419. cb = -cb; // Byte count to previous element
  420. cch = _ich; // Remaining cch in current run
  421. if(cch) // If cch != 0, initial run counts
  422. cRun++; // as a run: 1 less for for loop
  423. cRun = max(cRun, -iRun); // Don't let for loop overshoot
  424. }
  425. else
  426. { // Try to count forward cRun runs
  427. Assert(cRun > 0);
  428. iDir = 1;
  429. cch = pRun->_cch - _ich; // Remaining cch in current run
  430. if(cch) // If cch != 0, initial run counts
  431. cRun--; // as a run: 1 less for for loop
  432. k = Count() - iRun - 1; // k = # runs following current run
  433. cRun = min(cRun, k); // Don't let for loop overshoot
  434. }
  435. k = cch; // Remember if initial cch != 0
  436. for(j = cRun; j && cch < cchMax; j -= iDir)
  437. {
  438. pRun = (CTxtRun *)((BYTE *)pRun + cb); // Point at following run
  439. cch += pRun->_cch; // Add in its count
  440. }
  441. if(k) // Initial partial run counts as
  442. cRun += iDir; // a run
  443. cRun -= j; // Discount any runs not counted
  444. // if |cch| >= cchMax
  445. return iDir*cch; // Return total cch bypassed
  446. }
  447. /*
  448. * CRunPtrBase::FindRun (pcpMin, pcpMost, cpMin, cch)
  449. *
  450. * @mfunc
  451. * Set *<p pcpMin> = closest run cpMin <lt>= range cpMin, and
  452. * set *<p pcpMost> = closest run cpMost <gt>= range cpMost
  453. *
  454. * @devnote
  455. * This routine plays a role analogous to CTxtRange::FindParagraph
  456. * (pcpMin, pcpMost), but needs extra arguments since this run ptr does
  457. * not know the range cp's. This run ptr is located at the range active
  458. * end, which is determined by the range's signed length <p cch> in
  459. * conjunction with <p cpMin>.
  460. */
  461. void CRunPtrBase::FindRun (
  462. LONG *pcpMin, //@parm Out parm for bounding-run cpMin
  463. LONG *pcpMost, //@parm Out parm for bounding-run cpMost
  464. LONG cpMin, //@parm Range cpMin
  465. LONG cch, //@parm Range signed length
  466. LONG cchText) const //@parm Story length
  467. {
  468. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::FindRun");
  469. if(!IsValid())
  470. {
  471. if(pcpMin) // Run is whole story
  472. *pcpMin = 0;
  473. if(pcpMost)
  474. *pcpMost = cchText;
  475. return;
  476. }
  477. BOOL fAdvanceCp; // Controls AdvanceCp for pcpMost
  478. CRunPtrBase rp((CRunPtrBase&)(*this)); // Clone this runptr to keep it const
  479. rp.AdjustForward(); // Select forward run
  480. if(pcpMin)
  481. { // If cch != 0, rp is sure to end up
  482. fAdvanceCp = cch; // at cpMin, so pcpMost needs advance
  483. if(cch > 0) // rp is at cpMost, so move it to
  484. rp.AdvanceCp(-cch); // cpMin
  485. *pcpMin = cpMin - rp._ich; // Subtract off offset in this run
  486. }
  487. else
  488. fAdvanceCp = cch < 0; // Need to advance to get pcpMost
  489. if(pcpMost)
  490. {
  491. cch = abs(cch);
  492. if(fAdvanceCp) // Advance to cpMost = cpMin + cch,
  493. rp.AdvanceCp(cch); // i.e., range's cpMost
  494. if(cch)
  495. rp.AdjustBackward(); // Since nondegenerate range
  496. *pcpMost = cpMin + cch // Add remaining cch in run to range's
  497. + rp.GetCchLeft(); // cpMost
  498. }
  499. }
  500. /*
  501. * CRunPtrBase::AdjustBackward()
  502. *
  503. * @mfunc
  504. * If the cp for this run ptr is at the "boundary" or edge between two
  505. * runs, then make sure this run ptr points to the end of the first run.
  506. *
  507. * @comm
  508. * This function does nothing unless this run ptr points to the beginning
  509. * or the end of a run. This function may be needed in those cases
  510. * because a cp at the beginning of a run is identical to the cp for the
  511. * end of the previous run (if it exists), i.e., such an "edge" cp is
  512. * ambiguous, and you may need to be sure that this run ptr points to the
  513. * end of the first run.
  514. *
  515. * For example, consider a run that describes characters at cp's 0 to 10
  516. * followed by a run that describes characters at cp's 11 through 12. For
  517. * a cp of 11, it is possible for the run ptr to be either at the *end*
  518. * of the first run or at the *beginning* of the second run.
  519. *
  520. * @rdesc nothing
  521. */
  522. void CRunPtrBase::AdjustBackward()
  523. {
  524. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::AdjustBackward");
  525. _TEST_INVARIANT_
  526. if(!_ich && PrevRun()) // If at start of run that isn't
  527. _ich = _pRuns->Elem(_iRun)->_cch; // the first, go to end of
  528. } // previous run
  529. /*
  530. * CRunPtrBase::AdjustForward()
  531. *
  532. * @mfunc
  533. * If the cp for this run ptr is at the "boundary" or edge between two
  534. * runs, then make sure this run ptr points to the start of the second
  535. * run.
  536. *
  537. * @rdesc
  538. * nothing
  539. *
  540. * @xref
  541. * <mf CRunPtrBase::AdjustBackward>
  542. */
  543. void CRunPtrBase::AdjustForward()
  544. {
  545. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::AdjustForward");
  546. _TEST_INVARIANT_
  547. if(!IsValid())
  548. return;
  549. CTxtRun *pRun = _pRuns->Elem(_iRun);
  550. if(pRun->_cch == _ich) // If at end of run, go to start
  551. NextRun(); // of next run (if it exists)
  552. }
  553. /*
  554. * CRunPtrBase::IsValid()
  555. *
  556. * @mfunc
  557. * indicates whether the current run pointer is in the empty
  558. * or NULL states (i.e. "invalid" states).
  559. *
  560. * @rdesc
  561. * TRUE is in the empty or NULL state, FALSE otherwise.
  562. */
  563. BOOL CRunPtrBase::IsValid() const
  564. {
  565. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::IsValid");
  566. return _pRuns && _pRuns->Count();
  567. }
  568. /*
  569. * CRunPtrBase::SetToNull()
  570. *
  571. * @mfunc
  572. * Sets all run pointer information to be NULL. This
  573. * is designed to handle the response to clearing document runs
  574. * such as when we convert from rich text to plain text.
  575. *
  576. * @rdesc
  577. * VOID
  578. */
  579. void CRunPtrBase::SetToNull()
  580. {
  581. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::SetToNull");
  582. _pRuns = NULL;
  583. _iRun = 0;
  584. _ich = 0;
  585. }