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.

657 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-2000, 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, cchText)
  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. LONG cchText) //@parm cch in story or 0
  281. {
  282. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::BindToCp");
  283. if(cp > cchText/2 && _pRuns) // Start from end of story
  284. {
  285. _iRun = Count() - 1;
  286. if(_iRun >= 0)
  287. {
  288. _ich = _pRuns->Elem(_iRun)->_cch;
  289. return cchText + Move(cp - cchText);
  290. }
  291. }
  292. _iRun = 0;
  293. _ich = 0;
  294. return Move(cp);
  295. }
  296. /*
  297. * CRunPtrBase::Move(cch)
  298. *
  299. * @mfunc
  300. * Move this RunPtr by (signed) cch chars. If it lands on the
  301. * end of a run, it automatically goes to the start of the next run
  302. * (if one exists).
  303. *
  304. * @rdesc
  305. * Count of characters actually moved
  306. */
  307. LONG CRunPtrBase::Move(
  308. LONG cch) //@parm signed count of chars to move this RunPtr by
  309. {
  310. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::Move");
  311. if(!cch || !IsValid())
  312. return cch;
  313. LONG cchSave = cch;
  314. if(cch < 0)
  315. while(cch < 0)
  316. {
  317. if(-cch <= _ich)
  318. {
  319. _ich += cch; // Target is in this run
  320. cch = 0;
  321. break;
  322. }
  323. // Otherwise, we need to go to previous run. First add count
  324. // of chars from start of current run to current postion.
  325. cch += _ich;
  326. if(_iRun <= 0) // Already in first run
  327. {
  328. _iRun = 0;
  329. _ich = 0; // Move to run beginning
  330. break;
  331. }
  332. _ich = _pRuns->Elem(--_iRun)->_cch; // Move to previous run
  333. }
  334. else
  335. {
  336. LONG cchRun;
  337. CTxtRun *pRun = GetRun(0);
  338. while(cch > 0) // Move forward
  339. {
  340. cchRun = pRun->_cch;
  341. _ich += cch;
  342. if(_ich < cchRun) // Target is in this run
  343. {
  344. cch = 0; // Signal countdown completed
  345. break; // (if _ich = cchRun, go to
  346. } // next run)
  347. cch = _ich - cchRun; // Move to next run
  348. if(++_iRun >= Count()) // Ran past end, back up to
  349. { // end of story
  350. --_iRun;
  351. Assert(_iRun == Count() - 1);
  352. Assert(_pRuns->Elem(_iRun)->_cch == cchRun);
  353. _ich = cchRun;
  354. break;
  355. }
  356. _ich = 0; // Start at new BOL
  357. pRun = (CTxtRun *)((BYTE *)pRun + _pRuns->Size());
  358. }
  359. }
  360. // NB! we check the invariant at the end to handle the case where
  361. // we are updating the cp for a floating range (i.e., we know that
  362. // the cp is invalid, so we fix it up). So we have to check for
  363. // validity *after* the fixup.
  364. _TEST_INVARIANT_
  365. return cchSave - cch; // Return TRUE if countdown
  366. } // completed
  367. /*
  368. * CRunPtrBase::CountRuns(&cRun, cchMax, cp, cchText)
  369. *
  370. * @mfunc
  371. * Count characters up to <p cRun> runs away or <p cchMax> chars,
  372. * whichever comes first. If the target run and <p cchMax> are both
  373. * beyond the corresponding end of the document, count up thru the
  374. * closest run (0 or Count() - 1). The direction of counting is
  375. * determined by the sign of <p cRun>. To count without being limited
  376. * by <p cchMax>, set it equal to tomForward. An initial partial
  377. * run counts as a run, i.e., if cRun > 0 and _ich < cch in current
  378. * run or if cRun < 0 and _ich > 0, that counts as a run.
  379. *
  380. * @rdesc
  381. * Return the signed cch counted and set <p cRun> equal to count of runs
  382. * actually counted. If no runs are allocated, the text is treated as
  383. * a single run. If <p cRun> = 0, -_ich is returned. If <p cRun> <gt> 0
  384. * and this run ptr points to the end of the last run, no change is made
  385. * and 0 is returned.
  386. *
  387. * @devnote
  388. * The maximum count capability is included to be able to count units in
  389. * a range. The <p tp> argument is needed for getting the text length
  390. * when no runs exist and <p cRun> selects forward counting.
  391. */
  392. LONG CRunPtrBase::CountRuns (
  393. LONG & cRun, //@parm Count of runs to get cch for
  394. LONG cchMax, //@parm Maximum char count
  395. LONG cp, //@parm CRchTxtPtr::GetCp()
  396. LONG cchText) const //@parm Text length of associated story
  397. {
  398. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::CountRuns");
  399. _TEST_INVARIANT_
  400. LONG cch;
  401. if(!cRun) // Nothing to do
  402. return 0;
  403. // Simple special single-run case
  404. if(!IsValid()) // No runs instantiated: act as a
  405. { // single run
  406. if(cRun > 0) // Count forward
  407. {
  408. cch = cchText - cp; // Partial count to end of run
  409. cRun = 1; // No more than one run
  410. }
  411. else // Count backward
  412. {
  413. cch = -cp; // Partial count to start of run
  414. cRun = -1; // No less than minus one run
  415. }
  416. if(!cch) // No partial run, so no runs
  417. cRun = 0; // counted
  418. return cch;
  419. }
  420. // General case for which runs are instantiated
  421. LONG cb = _pRuns->Size(); // Size of text run element
  422. LONG iDir;
  423. LONG iRun = _iRun; // Cache run index for current run
  424. LONG j, k; // Handy integers
  425. CTxtRun * pRun = GetRun(0); // Not NULL since runs exist
  426. if(cRun < 0) // Try to count backward cRun runs
  427. {
  428. iDir = -1;
  429. cb = -cb; // Byte count to previous element
  430. cch = _ich; // Remaining cch in current run
  431. if(cch) // If cch != 0, initial run counts
  432. cRun++; // as a run: 1 less for for loop
  433. cRun = max(cRun, -iRun); // Don't let for loop overshoot
  434. }
  435. else
  436. { // Try to count forward cRun runs
  437. Assert(cRun > 0);
  438. iDir = 1;
  439. cch = pRun->_cch - _ich; // Remaining cch in current run
  440. if(cch) // If cch != 0, initial run counts
  441. cRun--; // as a run: 1 less for for loop
  442. k = Count() - iRun - 1; // k = # runs following current run
  443. cRun = min(cRun, k); // Don't let for loop overshoot
  444. }
  445. k = cch; // Remember if initial cch != 0
  446. for(j = cRun; j && cch < cchMax; j -= iDir)
  447. {
  448. pRun = (CTxtRun *)((BYTE *)pRun + cb); // Point at following run
  449. cch += pRun->_cch; // Add in its count
  450. }
  451. if(k) // Initial partial run counts as
  452. cRun += iDir; // a run
  453. cRun -= j; // Discount any runs not counted
  454. // if |cch| >= cchMax
  455. return iDir*cch; // Return total cch bypassed
  456. }
  457. /*
  458. * CRunPtrBase::FindRun (pcpMin, pcpMost, cpMin, cch)
  459. *
  460. * @mfunc
  461. * Set *<p pcpMin> = closest run cpMin <lt>= range cpMin, and
  462. * set *<p pcpMost> = closest run cpMost <gt>= range cpMost
  463. *
  464. * @devnote
  465. * This routine plays a role analogous to CTxtRange::FindParagraph
  466. * (pcpMin, pcpMost), but needs extra arguments since this run ptr does
  467. * not know the range cp's. This run ptr is located at the range active
  468. * end, which is determined by the range's signed length <p cch> in
  469. * conjunction with <p cpMin>.
  470. */
  471. void CRunPtrBase::FindRun (
  472. LONG *pcpMin, //@parm Out parm for bounding-run cpMin
  473. LONG *pcpMost, //@parm Out parm for bounding-run cpMost
  474. LONG cpMin, //@parm Range cpMin
  475. LONG cch, //@parm Range signed length
  476. LONG cchText) const //@parm Story length
  477. {
  478. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::FindRun");
  479. if(!IsValid())
  480. {
  481. if(pcpMin) // Run is whole story
  482. *pcpMin = 0;
  483. if(pcpMost)
  484. *pcpMost = cchText;
  485. return;
  486. }
  487. BOOL fMove; // Controls Move for pcpMost
  488. CRunPtrBase rp((CRunPtrBase&)(*this)); // Clone this runptr to keep it const
  489. rp.AdjustForward(); // Select forward run
  490. if(pcpMin)
  491. { // If cch != 0, rp is sure to end up
  492. fMove = cch; // at cpMin, so pcpMost needs Move
  493. if(cch > 0) // rp is at cpMost, so move it to
  494. rp.Move(-cch); // cpMin
  495. *pcpMin = cpMin - rp._ich; // Subtract off offset in this run
  496. }
  497. else
  498. fMove = cch < 0; // Need to move to get pcpMost
  499. if(pcpMost)
  500. {
  501. cch = abs(cch);
  502. if(fMove) // Move to cpMost = cpMin + cch,
  503. rp.Move(cch); // i.e., range's cpMost
  504. if(cch)
  505. rp.AdjustBackward(); // Since nondegenerate range
  506. *pcpMost = cpMin + cch // Add remaining cch in run to range's
  507. + rp.GetCchLeft(); // cpMost
  508. }
  509. }
  510. /*
  511. * CRunPtrBase::AdjustBackward()
  512. *
  513. * @mfunc
  514. * If the cp for this run ptr is at the "boundary" or edge between two
  515. * runs, then make sure this run ptr points to the end of the first run.
  516. *
  517. * @comm
  518. * This function does nothing unless this run ptr points to the beginning
  519. * or the end of a run. This function may be needed in those cases
  520. * because a cp at the beginning of a run is identical to the cp for the
  521. * end of the previous run (if it exists), i.e., such an "edge" cp is
  522. * ambiguous, and you may need to be sure that this run ptr points to the
  523. * end of the first run.
  524. *
  525. * For example, consider a run that describes characters at cp's 0 to 10
  526. * followed by a run that describes characters at cp's 11 through 12. For
  527. * a cp of 11, it is possible for the run ptr to be either at the *end*
  528. * of the first run or at the *beginning* of the second run.
  529. *
  530. * @rdesc nothing
  531. */
  532. void CRunPtrBase::AdjustBackward()
  533. {
  534. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::AdjustBackward");
  535. _TEST_INVARIANT_
  536. if(!_ich && PrevRun()) // If at start of run that isn't
  537. _ich = _pRuns->Elem(_iRun)->_cch; // the first, go to end of
  538. } // previous run
  539. /*
  540. * CRunPtrBase::AdjustForward()
  541. *
  542. * @mfunc
  543. * If the cp for this run ptr is at the "boundary" or edge between two
  544. * runs, then make sure this run ptr points to the start of the second
  545. * run.
  546. *
  547. * @rdesc
  548. * nothing
  549. *
  550. * @xref
  551. * <mf CRunPtrBase::AdjustBackward>
  552. */
  553. void CRunPtrBase::AdjustForward()
  554. {
  555. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::AdjustForward");
  556. _TEST_INVARIANT_
  557. if(!IsValid())
  558. return;
  559. CTxtRun *pRun = _pRuns->Elem(_iRun);
  560. if(pRun->_cch == _ich) // If at end of run, go to start
  561. NextRun(); // of next run (if it exists)
  562. }
  563. /*
  564. * CRunPtrBase::SetToNull()
  565. *
  566. * @mfunc
  567. * Sets all run pointer information to be NULL. This
  568. * is designed to handle the response to clearing document runs
  569. * such as when we convert from rich text to plain text.
  570. *
  571. * @rdesc
  572. * VOID
  573. */
  574. void CRunPtrBase::SetToNull()
  575. {
  576. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRunPtrBase::SetToNull");
  577. _pRuns = NULL;
  578. _iRun = 0;
  579. _ich = 0;
  580. }