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.

1317 lines
40 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module FRUNPTR.C -- FormatRunPtr methods |
  5. *
  6. * common code to handle character and paragraph format runs
  7. *
  8. * Original Authors: <nl>
  9. * Original RichEdit 1.0 code: David R. Fulmer <nl>
  10. * Christian Fortini <nl>
  11. * Murray Sargent <nl>
  12. *
  13. * History:
  14. * 6/25/95 alexgo convert to use Auto-Doc and simplified backing
  15. * store model
  16. *
  17. * @devnote
  18. * BOR and EOR mean Beginning Of Run and End Of Run, respectively
  19. *
  20. * Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved.
  21. */
  22. #include "_common.h"
  23. #include "_edit.h"
  24. #include "_frunptr.h"
  25. #include "_rtext.h"
  26. #include "_font.h"
  27. ASSERTDATA
  28. //
  29. // Invariant stuff
  30. //
  31. #define DEBUG_CLASSNAME CFormatRunPtr
  32. #include "_invar.h"
  33. #ifdef DEBUG
  34. /*
  35. * CFormatRunPtr::Invariant
  36. *
  37. * @mfunc Invariant for format run pointers
  38. *
  39. * @rdesc BOOL
  40. */
  41. BOOL CFormatRunPtr::Invariant() const
  42. {
  43. if(IsValid())
  44. {
  45. CFormatRun *prun = GetRun(0);
  46. if(prun && _iRun)
  47. {
  48. Assert(prun->_cch > 0);
  49. }
  50. }
  51. else
  52. {
  53. Assert(_ich == 0);
  54. }
  55. return CRunPtrBase::Invariant();
  56. }
  57. #endif
  58. /*
  59. * CFormatRunPtr::InitRuns(ich, cch, iFormat, ppfrs)
  60. *
  61. * @mfunc
  62. * Setup this format run ptr for rich-text operation, namely,
  63. * allocate CArray<lt>CFormatRun<gt> if not allocated, assign it to this
  64. * run ptr's _pRuns, add initial run if no runs are present, and store
  65. * initial cch and ich
  66. *
  67. * @rdesc
  68. * TRUE if succeeds
  69. */
  70. BOOL CFormatRunPtr::InitRuns(
  71. LONG ich, //@parm # chars in initial run
  72. LONG cch, //@parm char offset in initial run
  73. CFormatRuns **ppfrs) //@parm ptr to CFormatRuns ptr
  74. {
  75. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::InitRuns");
  76. _TEST_INVARIANT_
  77. AssertSz( ppfrs,
  78. "FRP::InitRuns: illegal ptr to runs");
  79. AssertSz( !IsValid(),
  80. "FRP::InitRuns: ptr already valid");
  81. if(!*ppfrs) // Allocate format runs
  82. {
  83. _pRuns = (CRunArray *) new CFormatRuns();
  84. if(!_pRuns)
  85. goto NoRAM;
  86. *ppfrs = (CFormatRuns *)_pRuns;
  87. }
  88. else // Format runs already alloc'd
  89. _pRuns = (CRunArray *)*ppfrs; // Cache ptr to runs
  90. if(!Count()) // No runs yet, so add one
  91. {
  92. CFormatRun *pRun= Add(1, NULL);
  93. if(!pRun)
  94. goto NoRAM;
  95. #ifdef DEBUG
  96. PvSet(*(void**)_pRuns);
  97. #endif
  98. _ich = ich;
  99. ZeroMemory(pRun, sizeof(*pRun));
  100. pRun->_cch = cch; // Define its _cch
  101. pRun->_iFormat = -1; // and _iFormat
  102. }
  103. else
  104. BindToCp(ich); // Format runs are in place
  105. return TRUE;
  106. NoRAM:
  107. TRACEERRSZSC("CFormatRunPtr::InitRuns: Out Of RAM", E_OUTOFMEMORY);
  108. return FALSE;
  109. }
  110. /*
  111. * CFormatRunPtr::Delete(cch, pf, cchMove)
  112. *
  113. * @mfunc
  114. * Delete/modify runs starting at this run ptr up to cch chars. <nl>
  115. * There are 7 possibilities: <nl>
  116. * 1. cch comes out of this run with count left over, i.e.,
  117. * cch <lt>= (*this)->_cch - _ich && (*this)->_cch > cch
  118. * (simple: no runs deleted/merged, just subtract cch) <nl>
  119. * 2. cch comes out of this run and empties run and doc
  120. * (simple: no runs left to delete/merge) <nl>
  121. * 3. cch comes out of this run and empties run, which is last
  122. * (need to delete run, no merge possibility) <nl>
  123. * 4. cch comes out of this run and empties run, which is first
  124. * (need to delete run, no merge possibility) <nl>
  125. * 5. cch exceeds count available in this run and this run is last
  126. * (simple: treat as 3.) <nl>
  127. * 6. cch comes out of this run and empties run with runs before
  128. * and after (need to delete run; merge possibility) <nl>
  129. * 7. cch comes partly out of this run and partly out of later run(s)
  130. * (may need to delete and merge) <nl>
  131. *
  132. * @comm
  133. * PARAFORMATs have two special cases that use the cchMove argument set
  134. * up in CRchTxtPtr::ReplaceRange().
  135. */
  136. void CFormatRunPtr::Delete(
  137. LONG cch, //@parm # chars to modify format runs for
  138. IFormatCache *pf, //@parm IFormatCache ptr for ReleaseFormat
  139. LONG cchMove) //@parm cch to move between runs (always 0 for CF)
  140. {
  141. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::Delete");
  142. _TEST_INVARIANT_
  143. // We should not have any boundary cases for empty or NULL pointers.
  144. // (i.e. if there's no text, then nobody should be calling delete).
  145. Assert(IsValid());
  146. LONG cchEnd = 0; // Probably unnecessary: see below
  147. LONG cRun = 1;
  148. BOOL fLast = (_iRun == Count() - 1);
  149. LONG ifmtEnd, ifmtStart;
  150. CFormatRun * pRun = Elem(_iRun);
  151. CFormatRun * pRunRp;
  152. LONG cchChunk = pRun->_cch - _ich;
  153. CFormatRunPtr rp(*this); // Clone this run ptr
  154. CBiDiLevel levelStart = {0,0};
  155. CBiDiLevel levelEnd = {0,0};
  156. rp.AdjustBackward(); // If at BOR, move to prev EOR
  157. ifmtStart = rp.GetRun(0)->_iFormat; // to get start format
  158. levelStart = rp.GetRun(0)->_level; // and level
  159. rp = *this; // In case RpAdjustCp() backed up
  160. // Process deletes confined to this run first, since their logic tends to
  161. // clutter up other cases
  162. AssertSz(cch >= 0, "FRP::Delete: cch < 0");
  163. if(fLast) // Handle oversized cch on last
  164. cch = min(cch, cchChunk); // run here
  165. if(cch <= cchChunk) // cch comes out of this run
  166. {
  167. pRun->_cch -= cch;
  168. Assert(pRun->_cch >= 0);
  169. if(cchMove) // If nonzero here, we are
  170. { // deleting EOP at end of run
  171. rp.AdjustForward(); // Adjust rp to beginning of
  172. goto move; // next run and go move cchMove
  173. } // chars back into this run
  174. if(pRun->_cch) // Something left in run: done
  175. return;
  176. // Note: _ich = 0
  177. if(!_iRun || fLast) // This run is either first
  178. { // or last
  179. AdjustBackward(); // If last, go to prev EOR
  180. if(_ich) // This run is empty so delete
  181. cRun++; // Compensate for cRun-- coming up
  182. ifmtStart = -2; // No runs eligible for merging
  183. } // so use unmatchable ifmtStart
  184. rp.NextRun(); // Set up to get next _iFormat
  185. }
  186. else
  187. {
  188. rp.AdvanceCp(cch); // Move clone to end of delete
  189. pRunRp = rp.GetRun(0);
  190. cRun = rp._iRun - _iRun // If at EOR, then need to add
  191. + (rp._ich == pRunRp->_cch); // one more run to delete
  192. pRun->_cch = _ich; // Shorten this run to _ich chars
  193. pRunRp->_cch -= rp._ich; // Shorten last run by rp._ich
  194. rp._ich = 0;
  195. Assert(pRunRp->_cch >= 0);
  196. AssertSz(cRun > 0, "FRP: bogus runptr");
  197. if(!_iRun) // First run?
  198. ifmtStart = -2; // Then we cannot merge runs so
  199. } // set to unmergable format
  200. ifmtEnd = -3; // Default invalid format at end
  201. if(rp.IsValid())
  202. {
  203. // FUTURE (murrays): probably rp is always valid here now and
  204. // pRun->_cch is nonzero
  205. pRun = rp.GetRun(0);
  206. if (pRun->_cch) // run not empty
  207. {
  208. ifmtEnd = pRun->_iFormat; // Remember end format and count
  209. levelEnd = pRun->_level;
  210. cchEnd = pRun->_cch; // in case of merge
  211. }
  212. else if(rp._iRun != rp.Count() - 1) // run not last
  213. {
  214. pRun = rp.GetRun(1);
  215. ifmtEnd = pRun->_iFormat; // Remember end format and count
  216. levelEnd = pRun->_level;
  217. cchEnd = pRun->_cch; // in case of merge
  218. }
  219. }
  220. rp = *this; // Default to delete this run
  221. if(_ich) // There are chars in this run
  222. {
  223. if(cchMove + _ich == 0) // Need to combine all chars of
  224. { // this run with run after del,
  225. pf->AddRef(ifmtEnd); // so setup merge below using
  226. ifmtStart = ifmtEnd; // ifmtEnd. This run then takes
  227. pf->Release(GetRun(0)->_iFormat);
  228. GetRun(0)->_iFormat = ifmtEnd; // place of run after del.
  229. GetRun(0)->_level = levelEnd;
  230. cchMove = 0; // cchMove all accounted for
  231. }
  232. rp.NextRun(); // Don't delete this run; start
  233. cRun--; // with next one
  234. }
  235. AdjustBackward(); // If !_ich, go to prev EOR
  236. if(ifmtEnd >=0 && // Same formats: merge runs
  237. ifmtEnd == ifmtStart &&
  238. levelStart == levelEnd)
  239. {
  240. GetRun(0)->_cch += cchEnd; // Add last-run cch to this one's
  241. Assert(GetRun(0)->_cch >= 0);
  242. cRun++; // Setup to eat last run
  243. }
  244. if(cRun > 0) // There are run(s) to delete
  245. {
  246. rp.Remove(cRun, pf);
  247. if(!Count()) // If no more runs, keep this rp
  248. _ich = _iRun = 0; // valid by pointing at cp = 0
  249. }
  250. move:
  251. if(cchMove) // Need to move some cch between
  252. { // this run and next (See
  253. GetRun(0)->_cch += cchMove; // CRchTxtPtr::ReplaceRange())
  254. rp.GetRun(0)->_cch -= cchMove;
  255. Assert(GetRun(0)->_cch >= 0);
  256. Assert(rp.GetRun(0)->_cch >= 0);
  257. Assert(_iRun < rp._iRun);
  258. if(!rp.GetRun(0)->_cch) // If all chars moved out of rp's
  259. rp.Remove(1, pf); // run, delete it
  260. if(cchMove < 0) // Moved -cchMove chars from this
  261. { // run to next
  262. if(!GetRun(0)->_cch)
  263. Remove(1, pf);
  264. else
  265. _iRun++; // Keep this run ptr in sync with
  266. _ich = -cchMove; // cp (can't use NextRun() due
  267. } // to Invariants)
  268. }
  269. AdjustForward(); // Don't leave ptr at EOR unless
  270. } // there are no more runs
  271. /*
  272. * CFormatRunPtr::InsertFormat(cch, ifmt, pf)
  273. *
  274. * @mfunc
  275. * Insert cch chars with format ifmt into format runs starting at
  276. * this run ptr
  277. *
  278. * @rdesc
  279. * count of characters added
  280. *
  281. * @devnote
  282. * It is the caller's responsibility to ensure that we are in the
  283. * "normal" or "empty" state. A format run pointer doesn't know about
  284. * CTxtStory, so it can't create the run array without outside help.
  285. */
  286. LONG CFormatRunPtr::InsertFormat(
  287. LONG cch, //@parm # chars to insert
  288. LONG ifmt, //@parm format to use
  289. IFormatCache *pf) //@parm pointer to IFormatCache to AddRefFormat
  290. {
  291. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::InsertFormat");
  292. LONG cRun;
  293. CFormatRun *pRun;
  294. CFormatRun *pRunPrev;
  295. LONG cchRun; // Current-run length,
  296. LONG ich; // offset, and
  297. LONG iFormat; // format
  298. _TEST_INVARIANT_
  299. Assert(_pRuns);
  300. if(!IsValid())
  301. {
  302. // Empty run case (occurs when inserting after all text is deleted)
  303. pRun = Add(1, NULL);
  304. goto StoreNewRunData; // (located at end of function)
  305. }
  306. // Go to previous run if at a boundary case
  307. AdjustBackward();
  308. pRun = Elem(_iRun); // Try other cases
  309. cchRun = pRun->_cch;
  310. iFormat = pRun->_iFormat;
  311. ich = _ich;
  312. // Same run case. Note that there is an additional boundary case; if we
  313. // are the _end_ of one run, then the next run may have the necessary
  314. // format.
  315. if(ifmt == iFormat) // IP already has correct fmt
  316. {
  317. pRun->_cch += cch;
  318. _ich += cch; // Inc offset to keep in sync
  319. return cch;
  320. }
  321. if(_ich == pRun->_cch && _iRun < _pRuns->Count() - 1)
  322. {
  323. AdjustForward();
  324. pRun = Elem(_iRun);
  325. Assert(pRun);
  326. if(pRun->_iFormat == ifmt)
  327. {
  328. pRun->_cch += cch;
  329. _ich += cch;
  330. return cch;
  331. }
  332. AdjustBackward();
  333. }
  334. // Prior run case (needed when formatting change occurs on line break
  335. // and caret is at beginning of new line)
  336. if(!ich && _iRun > 0 ) // IP at start of run
  337. {
  338. pRunPrev = GetPtr(pRun, -1);
  339. if( ifmt == pRunPrev->_iFormat) // Prev run has same format:
  340. { // add count to prev run and
  341. pRunPrev->_cch += cch;
  342. return cch;
  343. }
  344. }
  345. // Create new run[s] cases. There is a special case for a format
  346. // run of zero length: just re-use it.
  347. if(!pRun->_cch)
  348. {
  349. // This assert has been toned down to ignore a plain text control
  350. // being forced into IME Rich Composition.
  351. AssertSz( /* FALSE */ pRun->_iFormat == -1 && Count() == 1,
  352. "CFormatRunPtr::InsertFormat: 0-length run");
  353. pf->Release(pRun->_iFormat);
  354. }
  355. else // Need to create 1 or 2 new
  356. { // runs for insertion
  357. cRun = 1; // Default 1 new run
  358. if(ich && ich < cchRun) // Not at beginning or end of
  359. cRun++; // run, so need two new runs
  360. // The following insert call adds one or two runs at the current
  361. // position. If the new run is inserted at the beginning or end
  362. // of the current run, the latter needs no change; however, if
  363. // the new run splits the current run in two, both pieces have
  364. // to be updated (cRun == 2 case).
  365. pRun = Insert(cRun); // Insert cRun run(s)
  366. if(!pRun) // Out of RAM. Can't insert
  367. { // new format, but can keep
  368. _ich += cch; // run ptr and format runs
  369. GetRun(0)->_cch += cch; // valid. Note: doesn't
  370. return cch; // signal any error; no access
  371. } // to _ped->_fErrSpace
  372. if(ich) // Not at beginning of run,
  373. {
  374. pRunPrev = pRun; // Previous run is current run
  375. IncPtr(pRun); // New run is next run
  376. VALIDATE_PTR(pRun);
  377. pRun->_cch = cch; // Keep NextRun() invariant happy
  378. NextRun(); // Point this runptr at it too
  379. if(cRun == 2) // Are splitting current run
  380. { // _iFormat's are already set
  381. AssertSz(pRunPrev->_iFormat == iFormat,
  382. "CFormatRunPtr::InsertFormat: bad format inserted");
  383. pRunPrev->_cch = ich; // Divide up original cch
  384. GetPtr(pRun, 1)->_cch // accordingly
  385. = cchRun - ich;
  386. pf->AddRef(iFormat); // Addref iFormat for extra run
  387. }
  388. }
  389. }
  390. StoreNewRunData:
  391. pf->AddRef(ifmt); // Addref ifmt
  392. ZeroMemory(pRun, sizeof(*pRun));
  393. pRun->_iFormat = ifmt; // Store insert format and count
  394. pRun->_cch = cch; // of new run
  395. _ich = cch; // cp goes at end of insertion
  396. return cch;
  397. }
  398. /*
  399. * CFormatRunPtr::MergeRuns(iRun, pf)
  400. *
  401. * @mfunc
  402. * Merge adjacent runs that have the same format between this run
  403. * <md CFormatRunPtr::_iRun> and that for <p iRun>
  404. *
  405. * @comm
  406. * Changes this run ptr
  407. */
  408. void CFormatRunPtr::MergeRuns(
  409. LONG iRun, //@parm last run to check (can preceed or follow
  410. // <md CFormatRunPtr::_iRun>)
  411. IFormatCache *pf) //@parm pointer to IFormatCache to ReleaseFormat
  412. {
  413. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::MergeRuns");
  414. LONG cch;
  415. LONG cRuns = iRun - _iRun;
  416. LONG iDirection = 1; // Default going forward
  417. CFormatRun *pRun;
  418. _TEST_INVARIANT_
  419. if(cRuns < 0)
  420. {
  421. cRuns = -cRuns;
  422. iDirection = -1;
  423. }
  424. if(!IsValid()) // Allow starting run to be
  425. { // invalid
  426. Assert(FALSE); // I think this is old...
  427. ChgRun(iDirection);
  428. }
  429. while(cRuns--)
  430. {
  431. if(!GetRun(0)->_cch && !_iRun && _iRun < Count() - 1)
  432. {
  433. if(iDirection > 0)
  434. PrevRun();
  435. Remove(1, pf);
  436. continue;
  437. }
  438. pRun = GetRun(0); // Save the current run
  439. if(!ChgRun(iDirection)) // Go to next (or prev) run
  440. return; // No more runs to check
  441. if(pRun->SameFormat(GetRun(0)))
  442. { // Like formatted runs
  443. if(iDirection > 0) // Point at the first of the
  444. PrevRun(); // two runs
  445. cch = GetRun(0)->_cch; // Save its count
  446. Remove(1, pf); // Remove it
  447. GetRun(0)->_cch += cch; // Add its count to the other's,
  448. } // i.e., they're merged
  449. }
  450. }
  451. /*
  452. * CFormatRunPtr::Remove(cRun, flag, pf)
  453. *
  454. * @mfunc
  455. * Remove cRun runs starting at _iRun
  456. */
  457. void CFormatRunPtr::Remove(
  458. LONG cRun,
  459. IFormatCache *pf)
  460. {
  461. CFormatRun *pRun = GetRun(0); // Point at run(s) to delete
  462. for(LONG j = 0; j < cRun; j++, IncPtr(pRun))
  463. pf->Release(pRun->_iFormat); // Decrement run reference count
  464. CRunPtr<CFormatRun>::Remove(cRun);
  465. }
  466. /*
  467. * CFormatRunPtr::SetFormat(ifmt, cch, pf, pLevel)
  468. *
  469. * @mfunc
  470. * Set format for up to cch chars of this run to ifmt, splitting run
  471. * as needed, and returning the character count actually processed
  472. *
  473. * @rdesc
  474. * character count of run chunk processed, CP_INFINITE on failure
  475. * this points at next run
  476. *
  477. * Comments:
  478. * Changes this run ptr. cch must be >= 0.
  479. *
  480. * Note 1) for the first run in a series, _ich may not = 0, and 2) cch
  481. * may be <lt>, =, or <gt> the count remaining in the run. The algorithm
  482. * doesn't split runs when the format doesn't change.
  483. */
  484. LONG CFormatRunPtr::SetFormat(
  485. LONG ifmt, //@parm format index to use
  486. LONG cch, //@parm character count of remaining format range
  487. IFormatCache * pf, //@parm pointer to IFormatCache to
  488. CBiDiLevel* pLevel) //@parm pointer to BiDi level structure
  489. {
  490. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::SetFormat");
  491. // AddRefFormat/ReleaseFormat
  492. LONG cchChunk;
  493. LONG iFormat;
  494. CFormatRun * pRun;
  495. CFormatRun * pChgRun; // run that was reformatted
  496. CBiDiLevel level;
  497. _TEST_INVARIANT_
  498. if(!IsValid())
  499. return 0;
  500. pRun = GetRun(0); // pRun points at current run in
  501. cchChunk = pRun->_cch - _ich; // this function
  502. iFormat = pRun->_iFormat;
  503. level = pRun->_level;
  504. pChgRun = pRun;
  505. AssertSz(cch, "Have to have characters to format!");
  506. AssertSz(pRun->_cch, "uh-oh, empty format run detected");
  507. if(ifmt != iFormat || (pLevel && level != *pLevel)) // New and current formats differ
  508. {
  509. AssertSz(cchChunk, "Caller did not call AdjustForward");
  510. if(_ich) // Not at either end of run: need
  511. { // to split into two runs of
  512. if(!(pRun = Insert(1))) // counts _ich and _pRun->_cch
  513. { // - _ich, respectively
  514. return CP_INFINITE; // Out of RAM: do nothing; just
  515. } // keep current format
  516. pRun->_cch = _ich;
  517. pRun->_iFormat = iFormat; // New run has same format
  518. pRun->_level = level; // and same level
  519. pf->AddRef(iFormat); // Increment format ref count
  520. NextRun(); // Go to second (original) run
  521. IncPtr(pRun); // Point pRun at current run
  522. pRun->_cch = cchChunk; // Note: IncPtr is a bit more
  523. pChgRun = pRun;
  524. } // efficient than GetRun, but
  525. // trickier to code right
  526. if(cch < cchChunk) // cch doesn't cover whole run:
  527. { // need to split into two runs
  528. if(!(pRun = Insert(1)))
  529. {
  530. // Out of RAM, so formatting's wrong, oh well. We actually
  531. // "processed" all of the characters, so return that (though
  532. // the tail end formatting isn't split out right)
  533. return cch;
  534. }
  535. pRun->_cch = cch; // New run gets the cch
  536. pRun->_iFormat = ifmt; // and the new format
  537. pChgRun = pRun;
  538. IncPtr(pRun); // Point pRun at current run
  539. pRun->_cch = cchChunk - cch; // Set leftover count
  540. }
  541. else // cch as big or bigger than
  542. { // current run
  543. pf->Release(iFormat); // Free run's current format
  544. pRun->_iFormat = ifmt; // Change it to new format
  545. pChgRun = pRun;
  546. } // May get merged later
  547. pf->AddRef(ifmt); // Increment new format ref count
  548. }
  549. else if( cchChunk == 0 )
  550. {
  551. pRun->_cch += cch;
  552. cchChunk = cch;
  553. }
  554. // record embedding level to changed run
  555. if (pLevel)
  556. pChgRun->_level = *pLevel;
  557. cch = min(cch, cchChunk);
  558. AdvanceCp(cch);
  559. AdjustForward();
  560. return cch;
  561. }
  562. /*
  563. * CFormatRunPtr::GetFormat()
  564. *
  565. * @mfunc
  566. * return format index at current run pointer position
  567. *
  568. * @rdesc
  569. * current format index
  570. */
  571. short CFormatRunPtr::GetFormat() const
  572. {
  573. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::GetFormat");
  574. _TEST_INVARIANT_
  575. return IsValid() ? GetRun(0)->_iFormat : -1;
  576. }
  577. /*
  578. * CFormatRunPtr::SplitFormat(IFormatCache*)
  579. *
  580. * @mfunc
  581. * Split a format run
  582. *
  583. * @rdesc
  584. * If succeeded the run pointer moves to the next splitted run
  585. */
  586. void CFormatRunPtr::SplitFormat(IFormatCache* pf)
  587. {
  588. if (!_ich || _ich == GetRun(0)->_cch)
  589. return;
  590. CFormatRun* pRun = GetRun(0);
  591. LONG iFormat = pRun->_iFormat;
  592. LONG cch = pRun->_cch - _ich;
  593. CBiDiLevel level = pRun->_level;
  594. if (pRun = Insert(1))
  595. {
  596. pRun->_cch = _ich;
  597. pRun->_iFormat = iFormat;
  598. pRun->_level = level;
  599. pf->AddRef(iFormat);
  600. NextRun();
  601. IncPtr(pRun);
  602. pRun->_cch = cch;
  603. }
  604. }
  605. /*
  606. * CFormatRunPtr::SetLevel(level)
  607. *
  608. * @mfunc
  609. * Set run's embedding level
  610. */
  611. void CFormatRunPtr::SetLevel (CBiDiLevel& level)
  612. {
  613. if (!IsValid())
  614. {
  615. Assert(FALSE);
  616. return;
  617. }
  618. CFormatRun* pRun = GetRun(0);
  619. if (pRun)
  620. pRun->_level = level;
  621. }
  622. BYTE CFormatRunPtr::GetLevel (CBiDiLevel* pLevel)
  623. {
  624. CFormatRun* pRun;
  625. if (!IsValid() || !(pRun = GetRun(0)))
  626. {
  627. Assert(FALSE);
  628. if (pLevel)
  629. {
  630. pLevel->_value = 0;
  631. pLevel->_fStart = FALSE;
  632. }
  633. return 0;
  634. }
  635. if (pLevel)
  636. *pLevel = pRun->_level;
  637. return pRun->_level._value;
  638. }
  639. /*
  640. * CFormatRunPtr::AdjustFormatting(cch, pf)
  641. *
  642. * @mfunc
  643. * Use the same format index for the cch chars at this run ptr
  644. * as that immediately preceeding it (if on run edge).
  645. *
  646. * @devnote
  647. * This runptr ends up pointing at what was the preceeding run,
  648. * since the current run has been moved into the preceeding run.
  649. *
  650. * FUTURE: might be better to take the cch equal to chars in
  651. * the following run.
  652. */
  653. void CFormatRunPtr::AdjustFormatting(
  654. LONG cch, //@parm Count of chars to extend formatting
  655. IFormatCache *pf) //@parm Format cache ptr for AddRef/Release
  656. {
  657. if(!IsValid())
  658. return; // Nothing to merge
  659. CFormatRunPtr rp(*this);
  660. CBiDiLevel level;
  661. // Move this run ptr to end of
  662. AdjustBackward(); // preceeding run (if at run edge)
  663. rp.AdjustForward(); // (merge may delete run at entry)
  664. if(_iRun != rp._iRun) // On a format edge: copy previous
  665. { // format index over
  666. GetLevel(&level);
  667. rp.SetFormat(GetFormat(), cch, pf, &level); // Format cch chars at this
  668. rp.MergeRuns(_iRun, pf); // runptr
  669. }
  670. }
  671. ///////////////////////////// CCFRunPtr ///////////////////////////////
  672. CCFRunPtr::CCFRunPtr(const CRchTxtPtr &rtp)
  673. : CFormatRunPtr(rtp._rpCF)
  674. {
  675. _ped = rtp.GetPed();
  676. }
  677. CCFRunPtr::CCFRunPtr(const CFormatRunPtr &rp, CTxtEdit *ped)
  678. : CFormatRunPtr(rp)
  679. {
  680. _ped = ped;
  681. }
  682. /*
  683. * CCFRunPtr::IsMask(dwMask, MaskOp)
  684. *
  685. * @mfunc
  686. * return TRUE according to the mask operation MaskOp operating on
  687. * _dwEffects.
  688. *
  689. * @rdesc
  690. * TRUE if bits in CCharFormat::dwEffects correspond to those in dwMask
  691. */
  692. BOOL CCFRunPtr::IsMask(
  693. DWORD dwMask, //@parm Bit mask to use on dwEffects
  694. MASKOP MaskOp) //@parm Logic operation for bits
  695. {
  696. DWORD dwEffects = _ped->GetCharFormat(GetFormat())->_dwEffects;
  697. if(MaskOp == MO_EXACT) // Bit masks must be identical
  698. return dwEffects == dwMask;
  699. dwEffects &= dwMask;
  700. if(MaskOp == MO_OR) // TRUE if one or more effect bits
  701. return dwEffects != 0; // identified by mask are on
  702. if(MaskOp == MO_AND) // TRUE if all effect bits
  703. return dwEffects == dwMask; // identified by mask are on
  704. AssertSz(FALSE, "CCFRunPtr::IsMask: illegal mask operation");
  705. return FALSE;
  706. }
  707. /*
  708. * CCFRunPtr::IsInHidden()
  709. *
  710. * @mfunc
  711. * return TRUE if CCharFormat for this run ptr has CFE_HIDDEN bit set
  712. *
  713. * @rdesc
  714. * TRUE if CCharFormat for this run ptr has CFE_HIDDEN bit set
  715. */
  716. BOOL CCFRunPtr::IsInHidden()
  717. {
  718. AdjustForward();
  719. BOOL fHidden = IsHidden();
  720. if(_ich)
  721. return fHidden;
  722. AdjustBackward();
  723. return fHidden && IsHidden();
  724. }
  725. /*
  726. * CCFRunPtr::FindUnhidden()
  727. *
  728. * @mfunc
  729. * Find nearest expanded CF going forward. If none, find nearest going
  730. * backward. If none, go to start of document
  731. *
  732. * @rdesc
  733. * cch to nearest expanded CF as explained in function description
  734. *
  735. * @devnote
  736. * changes this run ptr
  737. */
  738. LONG CCFRunPtr::FindUnhidden()
  739. {
  740. LONG cch = FindUnhiddenForward();
  741. if(IsHidden())
  742. cch = FindUnhiddenBackward();
  743. return cch;
  744. }
  745. /*
  746. * CCFRunPtr::FindUnhiddenForward()
  747. *
  748. * @mfunc
  749. * Find nearest expanded CF going forward. If none, go to EOD
  750. *
  751. * @rdesc
  752. * cch to nearest expanded CF going forward
  753. *
  754. * @devnote
  755. * changes this run ptr
  756. */
  757. LONG CCFRunPtr::FindUnhiddenForward()
  758. {
  759. LONG cch = 0;
  760. AdjustForward();
  761. while(IsHidden())
  762. {
  763. cch += GetCchLeft();
  764. if(!NextRun())
  765. break;
  766. }
  767. return cch;
  768. }
  769. /*
  770. * CCFRunPtr::MatchFormatSignature
  771. *
  772. * @mfunc
  773. * Match the current format's font signature with the script (index to codepage).
  774. * It takes care single-codepage fonts which implicitly supports ASCII range.
  775. *
  776. * @rdesc
  777. * return how font matched
  778. */
  779. inline int CCFRunPtr::MatchFormatSignature (
  780. const CCharFormat* pCF,
  781. int iScript,
  782. int iMatchCurrent,
  783. DWORD* pdwFontSig)
  784. {
  785. DWORD dwFontSig = 0;
  786. if (GetFontSignatureFromFace(pCF->_iFont, &dwFontSig) != 0)
  787. {
  788. if (pdwFontSig)
  789. *pdwFontSig = dwFontSig;
  790. if (iMatchCurrent & MATCH_ASCII && fc().GetInfoFlags(pCF->_iFont).fNonBiDiAscii)
  791. return MATCH_ASCII;
  792. if (W32->GetFontSigFromScript(iScript) & ~(fASCII >> 8) & dwFontSig)
  793. return MATCH_FONT_SIG;
  794. }
  795. return 0;
  796. }
  797. /*
  798. * CCFRunPtr::GetPreferredFontInfo( cpg, bCharSet, iFont, yHeight, bPitchAndFamily,
  799. * iFormat, iMatchCurrent )
  800. *
  801. * @mfunc
  802. * Find the preferred font for the given code page around the range.
  803. *
  804. * @rdesc
  805. * boolean true if suitable font found, false otherwise.
  806. */
  807. bool CCFRunPtr::GetPreferredFontInfo(
  808. int cpg,
  809. BYTE& bRetCharSet,
  810. SHORT& iFont,
  811. SHORT& yHeight, // return in twips
  812. BYTE& bPitchAndFamily,
  813. int iFormat,
  814. int iMatchCurrent)
  815. {
  816. int i, iScript;
  817. bool fr = false;
  818. static int const MAX_FONTSEARCH = 256;
  819. const CCharFormat *pCF;
  820. const CCharFormat *pCFCurrent;
  821. const CCharFormat *pCFPrevious = NULL;
  822. int iMatch = 0; // how signature match?
  823. DWORD dwCurrentFontSig = 0;
  824. SHORT yNewHeight = 0;
  825. BYTE bCharSet = GetCharSet(cpg, &iScript);
  826. bool fUseUIFont = _ped->fUseUIFont() || _ped->Get10Mode();
  827. Assert(!(iMatchCurrent & MATCH_ASCII) || bCharSet == ANSI_CHARSET);
  828. if(_ped->fUseUIFont())
  829. pCFCurrent = _ped->GetCharFormat(-1); // Plain text or UI font specified
  830. else
  831. pCFCurrent = _ped->GetCharFormat(iFormat != -1 ? iFormat : GetFormat());
  832. if ((iMatchCurrent & MATCH_FONT_SIG) &&
  833. (iMatch = MatchFormatSignature(pCFCurrent, iScript, iMatchCurrent, &dwCurrentFontSig)) != 0)
  834. {
  835. pCF = pCFCurrent; // Setup to use it
  836. }
  837. else
  838. {
  839. // Try searching backwards
  840. if (IsValid()) // If doc has CF runs
  841. AdjustBackward();
  842. i = MAX_FONTSEARCH; // Don't be searching for years
  843. pCF = _ped->GetCharFormat(GetFormat());
  844. while (i--)
  845. {
  846. if (bCharSet == pCF->_bCharSet) // Equal charset ids?
  847. {
  848. pCFPrevious = pCF;
  849. break;
  850. }
  851. if (!PrevRun()) // Done searching?
  852. break;
  853. pCF = _ped->GetCharFormat(GetFormat());
  854. }
  855. pCF = pCFPrevious;
  856. }
  857. // Try match charset if requested
  858. if(!pCF && iMatchCurrent == MATCH_CURRENT_CHARSET)
  859. {
  860. CCcs* pccs = fc().GetCcs(pCFCurrent, W32->GetYPerInchScreenDC());
  861. if (pccs)
  862. {
  863. if (pccs->BestCharSet(bCharSet, 1, MATCH_CURRENT_CHARSET) != 1)
  864. pCF = pCFCurrent; // Current font can do it
  865. pccs->Release();
  866. }
  867. }
  868. // Try default document format
  869. if (!pCF)
  870. {
  871. pCF = _ped->GetCharFormat(-1);
  872. if (bCharSet != pCF->_bCharSet) // Diff charset ids?
  873. pCF = NULL;
  874. }
  875. yHeight = pCFCurrent->_yHeight; // assume current height
  876. if (!pCF)
  877. {
  878. // Default to table if no match.
  879. fr = W32->GetPreferredFontInfo(
  880. cpg, fUseUIFont, iFont, (BYTE&)yNewHeight, bPitchAndFamily );
  881. if (!_ped->_fAutoFontSizeAdjust && (cpg == CP_THAI || cpg == THAI_INDEX))
  882. // Kick in font size adjusting in first bind to Thai.
  883. _ped->_fAutoFontSizeAdjust = TRUE;
  884. }
  885. if (pCF)
  886. {
  887. // Found previous or current font
  888. iFont = pCF->_iFont;
  889. bPitchAndFamily = pCF->_bPitchAndFamily;
  890. if (pCF == pCFCurrent && (iMatchCurrent & MATCH_FONT_SIG) &&
  891. (IsFECharSet(pCF->_bCharSet) && W32->IsFECodePageFont(dwCurrentFontSig) ||
  892. iMatch == MATCH_ASCII && bCharSet == ANSI_CHARSET))
  893. {
  894. // The current font matches the requested signature.
  895. // If it's a FarEast or ASCII font. We leave the charset intact.
  896. bRetCharSet = pCF->_bCharSet;
  897. return true;
  898. }
  899. }
  900. if (_ped->_fAutoFontSizeAdjust && iFont != pCFCurrent->_iFont)
  901. {
  902. if (IsValid())
  903. {
  904. // If the last run format is available. We will scale the size relative to it.
  905. AdjustBackward();
  906. if (GetIch() > 0)
  907. {
  908. pCFCurrent = _ped->GetCharFormat(GetFormat());
  909. yHeight = pCFCurrent->_yHeight;
  910. }
  911. AdjustForward();
  912. }
  913. if (iFont != pCFCurrent->_iFont)
  914. {
  915. // Scale the height relative to the preceding format
  916. if (pCF)
  917. yNewHeight = GetFontLegitimateSize(iFont, fUseUIFont, cpg);
  918. if (yNewHeight)
  919. {
  920. // Get legitimate size of the current font
  921. SHORT yDefHeight = GetFontLegitimateSize(pCFCurrent->_iFont, fUseUIFont, GetCodePage(pCFCurrent->_bCharSet));
  922. // Calculate the new height relative to the current height
  923. if (yDefHeight)
  924. {
  925. if (fUseUIFont)
  926. {
  927. // For UIFont, we only convert from one preferred size to another preferred size.
  928. if (pCFCurrent->_yHeight / TWIPS_PER_POINT == yDefHeight)
  929. yHeight = yNewHeight * TWIPS_PER_POINT;
  930. }
  931. else
  932. yHeight = (SHORT)MulDiv(pCFCurrent->_yHeight, yNewHeight, yDefHeight);
  933. }
  934. }
  935. }
  936. }
  937. if (!yHeight)
  938. yHeight = (SHORT)MulDiv(pCFCurrent->_yHeight, yNewHeight, 10);
  939. return pCF || fr;
  940. }
  941. /*
  942. * CCFRunPtr::FindUnhiddenBackward()
  943. *
  944. * @mfunc
  945. * Find nearest expanded CF going backward. If none, go to BOD
  946. *
  947. * @rdesc
  948. * cch to nearest expanded CF going backward
  949. *
  950. * @devnote
  951. * changes this run ptr
  952. */
  953. LONG CCFRunPtr::FindUnhiddenBackward()
  954. {
  955. LONG cch = 0;
  956. AdjustBackward();
  957. while(IsHidden())
  958. {
  959. cch -= GetIch();
  960. if(!_iRun)
  961. break;
  962. _ich = 0;
  963. AdjustBackward();
  964. }
  965. return cch;
  966. }
  967. ///////////////////////////// CPFRunPtr ///////////////////////////////
  968. CPFRunPtr::CPFRunPtr(const CRchTxtPtr &rtp)
  969. : CFormatRunPtr(rtp._rpPF)
  970. {
  971. _ped = rtp.GetPed();
  972. }
  973. /*
  974. * CPFRunPtr::FindHeading(cch, lHeading)
  975. *
  976. * @mfunc
  977. * Find heading with number lHeading (e.g., = 1 for Heading 1) or above
  978. * in a range starting at this PFrun pointer. If successful, this run
  979. * ptr points at the matching run; else it remains unchanged.
  980. *
  981. * @rdesc
  982. * cch to matching heading or tomBackward if not found
  983. *
  984. * @devnote
  985. * changes this run ptr
  986. */
  987. LONG CPFRunPtr::FindHeading(
  988. LONG cch, //@parm Max cch to move
  989. LONG& lHeading) //@parm Lowest lHeading to match
  990. {
  991. LONG cchSave = cch;
  992. LONG ichSave = _ich;
  993. LONG iRunSave = _iRun;
  994. LONG OutlineLevel;
  995. Assert((unsigned)lHeading <= NHSTYLES);
  996. if(!IsValid())
  997. return tomBackward;
  998. while(TRUE)
  999. {
  1000. OutlineLevel = GetOutlineLevel();
  1001. if (!(OutlineLevel & 1) &&
  1002. (!lHeading || (lHeading - 1)*2 >= OutlineLevel))
  1003. {
  1004. lHeading = OutlineLevel/2 + 1; // Return heading # found
  1005. return cchSave - cch; // Return how far away it was
  1006. }
  1007. if(cch >= 0)
  1008. {
  1009. cch -= GetCchLeft();
  1010. if(cch <= 0 || !NextRun())
  1011. break;
  1012. }
  1013. else
  1014. {
  1015. cch += GetIch();
  1016. if(cch > 0 || !_iRun)
  1017. break;
  1018. AdjustBackward();
  1019. }
  1020. }
  1021. _ich = ichSave;
  1022. _iRun = iRunSave;
  1023. return tomBackward; // Didn't find desired heading
  1024. }
  1025. /*
  1026. * CPFRunPtr::IsCollapsed()
  1027. *
  1028. * @mfunc
  1029. * return TRUE if CParaFormat for this run ptr has PFE_COLLAPSED bit set
  1030. *
  1031. * @rdesc
  1032. * TRUE if CParaFormat for this run ptr has PFE_COLLAPSED bit set
  1033. */
  1034. BOOL CPFRunPtr::IsCollapsed()
  1035. {
  1036. return (_ped->GetParaFormat(GetFormat())->_wEffects & PFE_COLLAPSED) != 0;
  1037. }
  1038. /*
  1039. * CPFRunPtr::InTable()
  1040. *
  1041. * @mfunc
  1042. * return TRUE if CParaFormat for this run ptr has PFE_TABLE bit set
  1043. *
  1044. * @rdesc
  1045. * TRUE if CParaFormat for this run ptr has PFE_TABLE bit set
  1046. */
  1047. BOOL CPFRunPtr::InTable()
  1048. {
  1049. return (_ped->GetParaFormat(GetFormat())->_wEffects & PFE_TABLE) != 0;
  1050. }
  1051. /*
  1052. * CPFRunPtr::FindExpanded()
  1053. *
  1054. * @mfunc
  1055. * Find nearest expanded PF going forward. If none, find nearest going
  1056. * backward. If none, go to start of document
  1057. *
  1058. * @rdesc
  1059. * cch to nearest expanded PF as explained in function description
  1060. *
  1061. * @devnote
  1062. * advances this run ptr the amount returned (cch)
  1063. */
  1064. LONG CPFRunPtr::FindExpanded()
  1065. {
  1066. LONG cch, cchRun;
  1067. for(cch = 0; IsCollapsed(); cch += cchRun) // Try to find expanded PF
  1068. { // run going forward
  1069. cchRun = GetCchLeft();
  1070. if(!NextRun()) // Aren't any
  1071. {
  1072. AdvanceCp(-cch); // Go back to starting point
  1073. return FindExpandedBackward(); // Try to find expanded PF
  1074. } // run going backward
  1075. }
  1076. return cch;
  1077. }
  1078. /*
  1079. * CPFRunPtr::FindExpandedForward()
  1080. *
  1081. * @mfunc
  1082. * Find nearest expanded PF going forward. If none, go to EOD
  1083. *
  1084. * @rdesc
  1085. * cch to nearest expanded PF going forward
  1086. *
  1087. * @devnote
  1088. * advances this run ptr the amount returned (cch)
  1089. */
  1090. LONG CPFRunPtr::FindExpandedForward()
  1091. {
  1092. LONG cch = 0;
  1093. while(IsCollapsed())
  1094. {
  1095. LONG cchLeft = GetCchLeft();
  1096. _ich += cchLeft; // Update _ich in case
  1097. cch += cchLeft; // if(!NextRun()) breaks
  1098. if(!NextRun())
  1099. break;
  1100. }
  1101. return cch;
  1102. }
  1103. /*
  1104. * CPFRunPtr::FindExpandedBackward()
  1105. *
  1106. * @mfunc
  1107. * Find nearest expanded PF going backward. If none, go to BOD
  1108. *
  1109. * @rdesc
  1110. * cch to nearest expanded PF going backward
  1111. *
  1112. * @devnote
  1113. * advances this run ptr the amount returned (cch)
  1114. */
  1115. LONG CPFRunPtr::FindExpandedBackward()
  1116. {
  1117. LONG cch = 0;
  1118. while(IsCollapsed())
  1119. {
  1120. cch -= GetIch();
  1121. _ich = 0;
  1122. if(!_iRun)
  1123. break;
  1124. AdjustBackward();
  1125. }
  1126. return cch;
  1127. }
  1128. /*
  1129. * CPFRunPtr::GetOutlineLevel()
  1130. *
  1131. * @mfunc
  1132. * Find outline level this rp is pointing at
  1133. *
  1134. * @rdesc
  1135. * Outline level this rp is pointing at
  1136. */
  1137. LONG CPFRunPtr::GetOutlineLevel()
  1138. {
  1139. const CParaFormat *pPF = _ped->GetParaFormat(GetFormat());
  1140. LONG OutlineLevel = pPF->_bOutlineLevel;
  1141. AssertSz(IsHeadingStyle(pPF->_sStyle) ^ (OutlineLevel & 1),
  1142. "CPFRunPtr::GetOutlineLevel: sStyle/bOutlineLevel mismatch");
  1143. return OutlineLevel;
  1144. }
  1145. /*
  1146. * CPFRunPtr::GetStyle()
  1147. *
  1148. * @mfunc
  1149. * Find style this rp is pointing at
  1150. *
  1151. * @rdesc
  1152. * Style this rp is pointing at
  1153. */
  1154. LONG CPFRunPtr::GetStyle()
  1155. {
  1156. const CParaFormat *pPF = _ped->GetParaFormat(GetFormat());
  1157. LONG Style = pPF->_sStyle;
  1158. AssertSz(IsHeadingStyle(Style) ^ (pPF->_bOutlineLevel & 1),
  1159. "CPFRunPtr::GetStyle: sStyle/bOutlineLevel mismatch");
  1160. return Style;
  1161. }