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.

1992 lines
51 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module - RENDER.CPP |
  5. * CRenderer class
  6. *
  7. * Authors:
  8. * RichEdit 1.0 code: David R. Fulmer
  9. * Christian Fortini (initial conversion to C++)
  10. * Murray Sargent
  11. *
  12. * Copyright (c) 1995-1998 Microsoft Corporation. All rights reserved.
  13. */
  14. #include "_common.h"
  15. #include "_render.h"
  16. #include "_font.h"
  17. #include "_disp.h"
  18. #include "_edit.h"
  19. #include "_select.h"
  20. #include "_objmgr.h"
  21. #include "_coleobj.h"
  22. // Default colors for background and text on window's host printer
  23. const COLORREF RGB_WHITE = RGB(255, 255, 255);
  24. const COLORREF RGB_BLACK = RGB(0, 0, 0);
  25. const COLORREF RGB_BLUE = RGB(0, 0, 255);
  26. ASSERTDATA
  27. extern const COLORREF g_Colors[];
  28. static HBITMAP g_hbitmapSubtext = 0;
  29. static HBITMAP g_hbitmapExpandedHeading = 0;
  30. static HBITMAP g_hbitmapCollapsedHeading = 0;
  31. static HBITMAP g_hbitmapEmptyHeading = 0;
  32. void EraseTextOut(HDC hdc, const RECT *prc)
  33. {
  34. ::ExtTextOutA(hdc, 0, 0, ETO_OPAQUE, prc, NULL, 0, NULL);
  35. }
  36. HRESULT InitializeOutlineBitmaps()
  37. {
  38. g_hbitmapSubtext = LoadBitmap(hinstRE, MAKEINTRESOURCE(BITMAP_ID_SUBTEXT));
  39. g_hbitmapExpandedHeading = LoadBitmap(hinstRE, MAKEINTRESOURCE(BITMAP_ID_EXPANDED_HEADING));
  40. g_hbitmapCollapsedHeading = LoadBitmap(hinstRE, MAKEINTRESOURCE(BITMAP_ID_COLLAPSED_HEADING));
  41. g_hbitmapEmptyHeading = LoadBitmap(hinstRE, MAKEINTRESOURCE(BITMAP_ID_EMPTY_HEADING));
  42. if (!g_hbitmapSubtext ||
  43. !g_hbitmapExpandedHeading ||
  44. !g_hbitmapCollapsedHeading ||
  45. !g_hbitmapEmptyHeading)
  46. {
  47. return E_OUTOFMEMORY;
  48. }
  49. return NOERROR;
  50. }
  51. void ReleaseOutlineBitmaps()
  52. {
  53. if (g_hbitmapSubtext)
  54. {
  55. DeleteObject(g_hbitmapSubtext);
  56. g_hbitmapSubtext = 0;
  57. }
  58. if (g_hbitmapExpandedHeading)
  59. {
  60. DeleteObject(g_hbitmapExpandedHeading);
  61. g_hbitmapExpandedHeading = 0;
  62. }
  63. if (g_hbitmapCollapsedHeading)
  64. {
  65. DeleteObject(g_hbitmapCollapsedHeading);
  66. g_hbitmapCollapsedHeading = 0;
  67. }
  68. if (g_hbitmapEmptyHeading)
  69. {
  70. DeleteObject(g_hbitmapEmptyHeading);
  71. g_hbitmapEmptyHeading = 0;
  72. }
  73. }
  74. /*
  75. * IsTooSimilar(cr1, cr2)
  76. *
  77. * @mfunc
  78. * Return TRUE if the colors cr1 and cr2 are so similar that they
  79. * are hard to distinguish. Used for deciding to use reverse video
  80. * selection instead of system selection colors.
  81. *
  82. * @rdesc
  83. * TRUE if cr1 is too similar to cr2 to be used for selection
  84. *
  85. * @devnote
  86. * The formula below uses RGB. It might be better to use some other
  87. * color representation such as hue, saturation, and luminosity
  88. */
  89. BOOL IsTooSimilar(
  90. COLORREF cr1, //@parm First color for comparison
  91. COLORREF cr2) //@parm Second color for comparison
  92. {
  93. if((cr1 | cr2) & 0xFF000000) // One color and/or the other
  94. return FALSE; // isn't RGB, so algorithm
  95. // doesn't apply
  96. LONG DeltaR = abs(GetRValue(cr1) - GetRValue(cr2));
  97. LONG DeltaG = abs(GetGValue(cr1) - GetGValue(cr2));
  98. LONG DeltaB = abs(GetBValue(cr1) - GetBValue(cr2));
  99. return DeltaR + DeltaG + DeltaB < 80;
  100. }
  101. CRenderer::CRenderer (const CDisplay * const pdp) :
  102. CMeasurer (pdp)
  103. {
  104. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::CRenderer");
  105. Init();
  106. }
  107. CRenderer::CRenderer (const CDisplay * const pdp, const CRchTxtPtr &rtp) :
  108. CMeasurer (pdp, rtp)
  109. {
  110. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::CRenderer");
  111. Init();
  112. }
  113. /*
  114. * CRenderer::Init()
  115. *
  116. * @mfunc
  117. * Initialize most things to zero
  118. */
  119. void CRenderer::Init()
  120. {
  121. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::Init");
  122. _fRenderer = TRUE;
  123. static const RECT zrect = { 0, 0, 0, 0 };
  124. _rcView = zrect;
  125. _rcRender = zrect;
  126. _rc = zrect;
  127. _xWidthLine = 0;
  128. _dwFlags = 0;
  129. _ptCur.x = 0;
  130. _ptCur.y = 0;
  131. _crCurBackground = CLR_INVALID;
  132. _crCurTextColor = CLR_INVALID;
  133. _hdc = NULL;
  134. CTxtSelection *psel = GetPed()->GetSel();
  135. _fRenderSelection = psel && psel->GetShowSelection();
  136. // Get accelerator offset if any
  137. _cpAccelerator = GetPed()->GetCpAccelerator();
  138. _plogpalette = NULL;
  139. }
  140. /*
  141. * CRenderer::StartRender (&rcView, &rcRender, yHeightBitmap)
  142. *
  143. * @mfunc
  144. * Prepare this renderer for rendering operations
  145. *
  146. * @rdesc
  147. * FALSE if nothing to render, TRUE otherwise
  148. */
  149. BOOL CRenderer::StartRender (
  150. const RECT &rcView, //@parm View rectangle
  151. const RECT &rcRender, //@parm Rectangle to render
  152. const LONG yHeightBitmap) //@parm Height of bitmap for offscreen DC
  153. {
  154. CTxtEdit *ped = GetPed();
  155. BOOL fInOurHost = ped->fInOurHost();
  156. BOOL fTransparent = _pdp->IsTransparent();
  157. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::StartRender");
  158. // If this a metafile or a transparent control we better not be trying
  159. // to render off screen. Therefore, the bitmap height must be 0.
  160. AssertSz(!_pdp->IsMetafile() && !fTransparent || !yHeightBitmap,
  161. "CRenderer::StartRender: Metafile and request for off-screen DC");
  162. AssertSz(_hdc == NULL, "Calling StartRender() twice?");
  163. _hdc = _pdp->GetDC();
  164. #ifdef Boustrophedon
  165. // Note: to make this a para effect, need to turn this property on/off
  166. // as new paras are encountered. I.e., more work needed (plus need to
  167. // define PFE_BOUSTROPHEDON).
  168. //if(_pPF->_wEffects & PFE_BOUSTROPHEDON)
  169. {
  170. XFORM xform = {-1, 0, 0, 1, rcView.right, 0};
  171. SetGraphicsMode(_hdc, GM_ADVANCED);
  172. SetWorldTransform(_hdc, &xform);
  173. }
  174. #endif
  175. // Set view and rendering rects
  176. _rcView = rcView;
  177. _rcRender = rcRender;
  178. if(!fInOurHost || !_pdp->IsPrinter())
  179. {
  180. // If we are displaying to a window, or we are not in the window's
  181. // host, we use the colors specified by the host. For text and
  182. // foreground.
  183. _crBackground = ped->TxGetBackColor();
  184. _crTextColor = ped->TxGetForeColor();
  185. }
  186. else
  187. {
  188. // When the window's host is printing, the default colors are white
  189. // for the background and black for the text.
  190. _crBackground = RGB_WHITE;
  191. _crTextColor = RGB_BLACK;
  192. }
  193. ::SetBkColor (_hdc, _crBackground);
  194. _crCurBackground = _crBackground;
  195. // If this isn't a metafile, we set a flag indicating whether we
  196. // can safely erase the background
  197. _fErase = !fTransparent;
  198. if(_pdp->IsMetafile() || !_pdp->IsMain())
  199. {
  200. // Since this isn't the main display or it is a metafile,
  201. // we want to ignore the logic to render selections
  202. _fRenderSelection = FALSE;
  203. // This is a metafile or a printer so clear the render rectangle
  204. // and then pretend we are transparent.
  205. _fErase = FALSE;
  206. if(!fTransparent) // If control is not transparent,
  207. EraseTextOut(_hdc, &rcRender); // clear display
  208. }
  209. // Set text alignment
  210. // Its much faster to draw using top/left alignment than to draw
  211. // using baseline alignment.
  212. SetTextAlign(_hdc, TA_TOP | TA_LEFT);
  213. SetBkMode(_hdc, TRANSPARENT);
  214. _fUseOffScreenDC = FALSE; // Assume we won't use offscreen DC
  215. if(yHeightBitmap)
  216. {
  217. HPALETTE hpal = fInOurHost ? ped->TxGetPalette() :
  218. (HPALETTE) GetCurrentObject(_hdc, OBJ_PAL);
  219. // Create off-screen DC for rendering
  220. if(!_osdc.Init(_hdc, _rcRender.right - _rcRender.left, yHeightBitmap, _crBackground))
  221. return FALSE;
  222. _osdc.SelectPalette(hpal);
  223. _fUseOffScreenDC = TRUE; // We are using offscreen rendering
  224. }
  225. // For hack around ExtTextOutW OnWin9x EMF problems
  226. _fEnhancedMetafileDC = IsEnhancedMetafileDC(_hdc);
  227. return TRUE;
  228. }
  229. /*
  230. * CRenderer::EndRender()
  231. *
  232. * @mfunc
  233. * If _fErase, erase any remaining areas between _rcRender and _rcView
  234. */
  235. void CRenderer::EndRender()
  236. {
  237. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::EndRender");
  238. AssertSz(_hdc, "CRenderer::EndRender() - No rendering DC");
  239. if(_fErase)
  240. {
  241. RECT rc = _rcRender;
  242. if(_ptCur.y < _rcRender.bottom)
  243. {
  244. rc.top = _ptCur.y;
  245. EraseTextOut(_hdc, &rc);
  246. }
  247. }
  248. }
  249. /*
  250. * CRenderer::NewLine (&li)
  251. *
  252. * @mfunc
  253. * Init this CRenderer for rendering the specified line
  254. */
  255. void CRenderer::NewLine (const CLine &li)
  256. {
  257. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::NewLine");
  258. _li = li;
  259. Assert(GetCp() + _li._cch <= GetTextLength());
  260. _xWidthLine = _li._xWidth;
  261. _li._xWidth = 0;
  262. _ptCur.x = _rcView.left - _pdp->GetXScroll();
  263. _fSelected = _fSelectedPrev = FALSE;
  264. }
  265. /*
  266. * CRenderer::SetUpOffScreenDC(xAdj, yAdj)
  267. *
  268. * @mfunc
  269. * Setup renderer for using an off-screen DC
  270. *
  271. * @rdesc
  272. * NULL - an error occurred<nl>
  273. * ~NULL - DC to save
  274. */
  275. HDC CRenderer::SetUpOffScreenDC(
  276. LONG& xAdj, //@parm Offset to x
  277. LONG& yAdj) //@parm Offset to y
  278. {
  279. // Save render DC
  280. HDC hdcSave = _hdc;
  281. LONG yHeightRC = _rc.bottom - _rc.top;
  282. // Replace render DC with an off-screen DC
  283. _hdc = _osdc.GetDC();
  284. if(!_hdc)
  285. {
  286. // There is no off-screen renderer
  287. // This better be a line marked for a one-time off-screen rendering
  288. // that wasn't. Note: standard cases for this happening are a line
  289. // that would have been displayed is scrolled off the screen
  290. // because an update of the selection.
  291. AssertSz(_li._bFlags & fliOffScreenOnce, "Unexpected off screen DC failure");
  292. _hdc = hdcSave;
  293. return NULL;
  294. }
  295. AssertSz(!GetPed()->_fTransparent, "Off-screen render of tranparent control");
  296. _crCurTextColor = CLR_INVALID;
  297. if(_pccs)
  298. {
  299. // There is current a character format for the run so we need to
  300. // get in sync with that since the off screen DC isn't necessarily
  301. // set to that font.
  302. // Get the character format and set up the font
  303. SetFontAndColor(GetCF());
  304. }
  305. // We are rendering to a transparent background
  306. _fErase = FALSE;
  307. // Clear bitmap
  308. ::SetBkColor(_hdc, _crBackground);
  309. _osdc.FillBitmap(_rcRender.right - _rcRender.left, yHeightRC);
  310. //Clear top of rcRender if necessary
  311. RECT rcErase = _rcRender;
  312. rcErase.top = _ptCur.y;
  313. rcErase.bottom = rcErase.top + _li._yHeight;
  314. //If the first line, erase to edge of rcRender
  315. if (rcErase.top <= _rcView.top)
  316. rcErase.top = min(_rcView.top, _rcRender.top);
  317. rcErase.bottom = _rc.top;
  318. if (rcErase.bottom > rcErase.top)
  319. EraseTextOut(hdcSave, &rcErase);
  320. // Restore background color if necessary
  321. if(_crBackground != _crCurBackground)
  322. ::SetBkColor(_hdc, _crCurBackground);
  323. SetBkMode(_hdc, TRANSPARENT);
  324. // Store y adjustment to use in rendering off-screen bitmap
  325. yAdj = _rc.top;
  326. // Normalize _rc and _ptCur height for rendering to off-screen bitmap
  327. _ptCur.y -= yAdj;
  328. _rc.top = 0;
  329. _rc.bottom -= yAdj;
  330. // Store x adjustment to use in rendering off-screen bitmap
  331. xAdj = _rcRender.left;
  332. // Adjust _rcRender & _rcView so they are relative to x of 0
  333. _rcRender.left -= xAdj;
  334. _rcRender.right -= xAdj;
  335. _rcView.left -= xAdj;
  336. _rcView.right -= xAdj;
  337. _rc.left -= xAdj;
  338. _rc.right -= xAdj;
  339. _ptCur.x -= xAdj;
  340. return hdcSave;
  341. }
  342. /*
  343. * CRenderer::RenderOffScreenBitmap(hdc, xAdj, yAdj)
  344. *
  345. * @mfunc
  346. * Render off screen bitmap and restore the state of the render.
  347. */
  348. void CRenderer::RenderOffScreenBitmap(
  349. HDC hdc, //@parm DC to render to
  350. LONG xAdj, //@parm offset to real x base
  351. LONG yAdj) //@parm offset to real y base
  352. {
  353. // Palettes for rendering bitmap
  354. HPALETTE hpalOld = NULL;
  355. HPALETTE hpalNew = NULL;
  356. // Restore x offsets
  357. _rc.left += xAdj;
  358. _rc.right += xAdj;
  359. _rcRender.left += xAdj;
  360. _rcRender.right += xAdj;
  361. _rcView.left += xAdj;
  362. _rcView.right += xAdj;
  363. _ptCur.x += xAdj;
  364. // Restore y offsets
  365. _ptCur.y += yAdj;
  366. _rc.top += yAdj;
  367. _rc.bottom += yAdj;
  368. // Create a palette if one is needed
  369. if(_plogpalette)
  370. W32->ManagePalette(hdc, _plogpalette, hpalOld, hpalNew);
  371. // Render bitmap to real DC and restore _ptCur & _rc
  372. _osdc.RenderBitMap(hdc, xAdj, yAdj, _rcRender.right - _rcRender.left, _rc.bottom - _rc.top);
  373. // Restore palette after render if necessary
  374. if(_plogpalette)
  375. {
  376. W32->ManagePalette(hdc, _plogpalette, hpalOld, hpalNew);
  377. CoTaskMemFree(_plogpalette);
  378. _plogpalette = NULL;
  379. }
  380. // Restore HDC to actual render DC
  381. _hdc = hdc;
  382. // Set this flag to what it should be for restored DC
  383. _fErase = TRUE;
  384. _crCurTextColor = CLR_INVALID;
  385. // Reset screen DC font
  386. // Set up font on non-screen DC
  387. // Force color resynch
  388. if(!FormatIsChanged()) // Not on a new block,
  389. SetFontAndColor(GetCF()); // so just set font and color
  390. else
  391. { // On new block,
  392. ResetCachediFormat(); // so reset everything
  393. SetNewFont();
  394. }
  395. }
  396. /*
  397. * CRenderer::RenderLine (&li)
  398. *
  399. * @mfunc
  400. * Render visible part of current line
  401. *
  402. * @rdesc
  403. * TRUE if success, FALSE if failed
  404. *
  405. * @devnote
  406. * Only call this from CLine::RenderLine()
  407. */
  408. BOOL CRenderer::RenderLine (
  409. CLine & li)
  410. {
  411. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::RenderLine");
  412. BYTE bUnderlineSave = 0;
  413. LONG cch;
  414. LONG cchChunk;
  415. LONG cchInTextRun;
  416. LONG cpSelMin, cpSelMost;
  417. BOOL fAccelerator = FALSE;
  418. HDC hdcSave = NULL;
  419. LONG nSurrogates = 0;
  420. const WCHAR * pstrToRender;
  421. CTempWcharBuf twcb;
  422. WCHAR chPassword = GetPasswordChar();
  423. LONG xAdj = 0;
  424. LONG yAdj = 0;
  425. UpdatePF();
  426. // This is used as a temporary buffer so that we can guarantee that we will
  427. // display an entire format run in one ExtTextOut.
  428. WCHAR * pszTempBuffer = NULL;
  429. // Get range to display for this rendering
  430. GetPed()->GetSelRangeForRender(&cpSelMin, &cpSelMost);
  431. NewLine(li); // Init render at start of line
  432. _ptCur.x += _li._xLeft; // Add in line left indent
  433. SetClipRect();
  434. if(cpSelMost != cpSelMin && cpSelMost == GetCp())
  435. _fSelectedPrev = TRUE;
  436. // We use an off screen DC if there are characters to render and the
  437. // measurer determined that this line needs off-screen rendering. The
  438. // main reason for the measurer deciding that a line needs off screen
  439. // rendering is if there are multiple formats in the line.
  440. if(_li._cch > 0 && _fUseOffScreenDC && (_li._bFlags & fliUseOffScreenDC))
  441. {
  442. // Set up an off-screen DC if we can. Note that if this fails,
  443. // we just use the regular DC which won't look as nice but
  444. // will at least display something readable.
  445. hdcSave = SetUpOffScreenDC(xAdj, yAdj);
  446. // Is this a uniform text being rendered off screen?
  447. if(_li._bFlags & fliOffScreenOnce)
  448. {
  449. // Yes - turn off special rendering since line has been rendered
  450. li._bFlags &= ~(fliOffScreenOnce | fliUseOffScreenDC);
  451. }
  452. }
  453. // Allow for special rendering at start of line
  454. LONG x = _ptCur.x;
  455. RenderStartLine();
  456. Assert(GetCp() + _li._cch <= GetTextLength());
  457. cch = _li._cch;
  458. if(chPassword && IsRich())
  459. {
  460. // It is kind of odd to allow rich text password edit controls.
  461. // However, it does make it that much easier to create a password
  462. // edit control since you don't have to know to change the box to
  463. // plain. Anyway, if there is such a thing, we don't want to put
  464. // out password characters for EOPs in general and the final EOP
  465. // specifically. Therefore, the following ...
  466. if(_pdp->IsMultiLine())
  467. cch -= _li._cchEOP;
  468. else
  469. cch = GetPed()->GetAdjustedTextLength();
  470. }
  471. for(; cch > 0; cch -= cchChunk)
  472. {
  473. // Initial chunk (number of character to render in a single TextOut)
  474. // is min between CHARFORMAT run length and line length. Start with
  475. // count of characters left in current format run
  476. cchChunk = GetCchLeftRunCF();
  477. AssertSz(cchChunk != 0, "empty CHARFORMAT run");
  478. DWORD dwEffects = GetCF()->_dwEffects;
  479. if(dwEffects & CFE_HIDDEN) // Don't display hidden text
  480. {
  481. Advance(cchChunk);
  482. continue;
  483. }
  484. // Limit chunk to count of characters we want to display.
  485. cchChunk = min(cch, cchChunk);
  486. // Get count of characters in text run
  487. pstrToRender = _rpTX.GetPch(cchInTextRun);
  488. AssertSz(cchInTextRun > 0, "empty text run");
  489. if (cchInTextRun < cchChunk || chPassword || dwEffects & CFE_ALLCAPS ||
  490. _li._bFlags & fliHasSurrogates)
  491. {
  492. // The amount of data in the backing store run is less than the
  493. // number of characters we wish to display. We will copy the
  494. // data out of the backing store.
  495. // Allocate a buffer if it is needed
  496. if(!pszTempBuffer)
  497. {
  498. // Allocate buffer big enough to handle all future
  499. // requests in this loop.
  500. pszTempBuffer = twcb.GetBuf(cch);
  501. }
  502. // Point at buffer
  503. pstrToRender = pszTempBuffer;
  504. if(chPassword)
  505. {
  506. // Fill buffer with password characters
  507. for (int i = 0; i < cchChunk; i++)
  508. pszTempBuffer[i] = chPassword;
  509. }
  510. else
  511. { // Password not requested so copy text from backing
  512. // store to buffer
  513. _rpTX.GetText(cchChunk, pszTempBuffer);
  514. if(dwEffects & CFE_ALLCAPS)
  515. CharUpperBuff(pszTempBuffer, cchChunk);
  516. }
  517. }
  518. if(_cpAccelerator != -1)
  519. {
  520. LONG cpCur = GetCp(); // Get current cp
  521. // Does accelerator character fall in this chunk?
  522. if (cpCur < _cpAccelerator &&
  523. cpCur + cchChunk > _cpAccelerator)
  524. {
  525. // Yes. Reduce chunk to char just before accelerator
  526. cchChunk = _cpAccelerator - cpCur;
  527. }
  528. // Is this character the accelerator?
  529. else if(cpCur == _cpAccelerator)
  530. { // Set chunk size to 1 since only
  531. cchChunk = 1; // want to output underlined char
  532. fAccelerator = TRUE; // Tell downstream routines that
  533. // we're handling accelerator
  534. _cpAccelerator = -1; // Only 1 accelerator per line
  535. }
  536. }
  537. // Reduce chunk to account for selection if we are rendering for a
  538. // display that cares about selections.
  539. if(_fRenderSelection && cpSelMin != cpSelMost)
  540. {
  541. LONG cchSel = cpSelMin - GetCp();
  542. if(cchSel > 0)
  543. cchChunk = min(cchChunk, cchSel);
  544. else if(GetCp() < cpSelMost)
  545. {
  546. cchSel = cpSelMost - GetCp();
  547. if(cchSel >= cch)
  548. _fSelectToEOL = TRUE;
  549. else
  550. cchChunk = min(cchChunk, cchSel);
  551. _fSelected = TRUE; // cpSelMin <= GetCp() < cpSelMost
  552. } // so current run is selected
  553. }
  554. // If start of CCharFormat run, select font and color
  555. if(FormatIsChanged() || _fSelected != _fSelectedPrev)
  556. {
  557. ResetCachediFormat();
  558. _fSelectedPrev = _fSelected;
  559. if(!SetNewFont())
  560. return FALSE; // Failed
  561. }
  562. if(fAccelerator)
  563. {
  564. bUnderlineSave = _bUnderlineType;
  565. SetupUnderline(CFU_UNDERLINE);
  566. }
  567. // Allow for further reduction of the chunk and rendering of
  568. // interleaved rich text elements
  569. if(RenderChunk(cchChunk, pstrToRender, cch))
  570. {
  571. AssertSz(cchChunk > 0, "CRenderer::RenderLine(): cchChunk == 0");
  572. _fSelected = FALSE;
  573. continue;
  574. }
  575. AssertSz(cchChunk > 0,"CRenderer::RenderLine() - cchChunk == 0");
  576. #ifdef UNICODE_SURROGATES
  577. if(_li._bFlags & fliHasSurrogates)
  578. {
  579. WCHAR ch;
  580. WCHAR *pchD = pszTempBuffer;
  581. WCHAR *pchS = pszTempBuffer;
  582. for(int i = cchChunk; i--; )
  583. {
  584. ch = *pchS++;
  585. if (IN_RANGE(0xD800, ch, 0xDBFF) && i &&
  586. IN_RANGE(0xDC00, *pchS, 0xDFFF))
  587. {
  588. // Project down into plane 0
  589. ch = (ch << 10) | (*pchS++ & 0x3FF);
  590. }
  591. *pchD++ = ch;
  592. }
  593. nSurrogates = pchS - pchD;
  594. }
  595. #endif
  596. _fLastChunk = (cchChunk == cch);
  597. RenderText(pstrToRender, cchChunk - nSurrogates); // Render the text
  598. nSurrogates = 0;
  599. if(fAccelerator)
  600. {
  601. _bUnderlineType = bUnderlineSave;
  602. fAccelerator = FALSE; // Turn off special accelerator
  603. } // processing
  604. Advance(cchChunk);
  605. // Break if we went past right of render rect.
  606. if(_ptCur.x >= min(_rcView.right, _rcRender.right))
  607. {
  608. cch -= cchChunk;
  609. break;
  610. }
  611. }
  612. EndRenderLine(hdcSave, xAdj, yAdj, x);
  613. Advance(cch);
  614. return TRUE; // Success
  615. }
  616. /*
  617. * CRenderer::EndRenderLine (hdcSave, xAdj, yAdj, x)
  618. *
  619. * @mfunc
  620. * Finish up rendering of line, drawing table borders, rendering
  621. * offscreen DC, and erasing to right of render rect if necessary.
  622. */
  623. void CRenderer::EndRenderLine(
  624. HDC hdcSave,
  625. LONG xAdj,
  626. LONG yAdj,
  627. LONG x)
  628. {
  629. // Display table borders (only 1 pixel wide)
  630. if(_pPF->InTable() && _li._bFlags & fliFirstInPara)
  631. {
  632. const LONG *prgxTabs = _pPF->GetTabs();
  633. COLORREF crf = _crTextColor;
  634. LONG icr = _pPF->_dwBorderColor & 0x1F;
  635. if (IN_RANGE(1, icr, 16))
  636. crf = g_Colors[icr-1];
  637. HPEN pen = CreatePen(PS_SOLID, 0, crf);
  638. LONG h = LXtoDX(_pPF->_dxOffset);
  639. LONG dx = LXtoDX(_pPF->_dxStartIndent);
  640. x -= h;
  641. LONG xRight = x + LXtoDX(GetTabPos(prgxTabs[_pPF->_bTabCount - 1])) - dx;
  642. LONG yTop = _ptCur.y;
  643. LONG yBot = yTop + _li._yHeight;
  644. if(pen)
  645. {
  646. HPEN oldPen = SelectPen(_hdc, pen);
  647. MoveToEx(_hdc, x, yTop, NULL);
  648. LineTo (_hdc, xRight, yTop);
  649. if(!_li._fNextInTable)
  650. {
  651. MoveToEx(_hdc, x, yBot - 1, NULL);
  652. LineTo (_hdc, xRight, yBot - 1);
  653. }
  654. h = 0;
  655. for(LONG i = _pPF->_bTabCount; i >= 0; i--)
  656. {
  657. MoveToEx(_hdc, x + h, yTop, NULL);
  658. LineTo (_hdc, x + h, yBot);
  659. if (i)
  660. h = LXtoDX(GetTabPos(*prgxTabs++)) - dx;
  661. }
  662. if(oldPen)
  663. SelectPen(_hdc, oldPen);
  664. DeleteObject(pen);
  665. }
  666. }
  667. if(hdcSave)
  668. RenderOffScreenBitmap(hdcSave, xAdj, yAdj);
  669. // Handle setting background color. We need to do this for each line
  670. // because we return the background color to the default after each
  671. // line so that opaquing will work correctly.
  672. if(_crBackground != _crCurBackground)
  673. {
  674. ::SetBkColor(_hdc, _crBackground); // Tell window background color
  675. _crCurBackground = _crBackground;
  676. }
  677. }
  678. /*
  679. * CRenderer::UpdatePalette (pobj)
  680. *
  681. * @mfunc
  682. * Stores palette information so that we can render any OLE objects
  683. * correctly in a bitmap.
  684. */
  685. void CRenderer::UpdatePalette(
  686. COleObject *pobj) //@parm OLE object wrapper.
  687. {
  688. #ifndef PEGASUS
  689. LOGPALETTE *plogpalette = NULL;
  690. LOGPALETTE *plogpaletteMerged;
  691. IViewObject *pviewobj;
  692. // Get IViewObject interface information so we can build a palette
  693. // to render the object correctly.
  694. if (((pobj->GetIUnknown())->QueryInterface(IID_IViewObject,
  695. (void **) &pviewobj)) != NOERROR)
  696. {
  697. // Couldn't get it, so pretend this didn't happen
  698. return;
  699. }
  700. // Get logical palette information from object
  701. if(pviewobj->GetColorSet(DVASPECT_CONTENT, -1, NULL, NULL,
  702. NULL, &plogpalette) != NOERROR || !plogpalette)
  703. {
  704. // Couldn't get it, so pretend this didn't happen
  705. goto CleanUp;
  706. }
  707. if(!_plogpalette)
  708. { // No palette entries yet
  709. _plogpalette = plogpalette; // Just use the one returned
  710. goto CleanUp;
  711. }
  712. // We have had other palette entries. We just reallocate the table
  713. // and put the newest entry on the end. This is crude, we might
  714. // sweep the table and actually merge it. However, this code
  715. // should be executed relatively infrequently and therefore, crude
  716. // should be good enough.
  717. // Allocate a new table - Note the " - 1" in the end has to do with
  718. // the fact that LOGPALETTE is defined to have one entry already.
  719. AssertSz(_plogpalette->palNumEntries + plogpalette->palNumEntries >= 1,
  720. "CRenderer::UpdatePalette - invalid palettes to merge");
  721. plogpaletteMerged = (LOGPALETTE *) CoTaskMemAlloc(sizeof(LOGPALETTE) +
  722. ((_plogpalette->palNumEntries + plogpalette->palNumEntries - 1) * sizeof(PALETTEENTRY)));
  723. if(!plogpaletteMerged) // Memory allocation failed
  724. goto CleanTempPalette; // Just pretend it didn't happen
  725. // Copy in original table.
  726. memcpy(&plogpaletteMerged->palPalEntry[0], &_plogpalette->palPalEntry[0],
  727. _plogpalette->palNumEntries * sizeof(PALETTEENTRY));
  728. // Put new data at end
  729. memcpy(&plogpaletteMerged->palPalEntry[_plogpalette->palNumEntries],
  730. &plogpalette->palPalEntry[0],
  731. plogpalette->palNumEntries * sizeof(PALETTEENTRY));
  732. // Set the version number and count
  733. plogpaletteMerged->palVersion = plogpalette->palVersion;
  734. plogpaletteMerged->palNumEntries = _plogpalette->palNumEntries
  735. + plogpalette->palNumEntries;
  736. // Replace current palette table with merged table
  737. CoTaskMemFree(_plogpalette);
  738. _plogpalette = plogpaletteMerged;
  739. CleanTempPalette:
  740. CoTaskMemFree(plogpalette);
  741. CleanUp:
  742. // Release object we got since we don't need it any more
  743. pviewobj->Release();
  744. #endif
  745. }
  746. /*
  747. * CRenderer::RenderChunk (&cchChunk, pstrToRender, cch)
  748. *
  749. * @mfunc
  750. * Method reducing the length of the chunk (number of character
  751. * rendered in one RenderText) and to render items interleaved in text.
  752. *
  753. * @rdesc
  754. * TRUE if this method actually rendered the chunk,
  755. * FALSE if it just updated cchChunk and rendering is still needed
  756. */
  757. BOOL CRenderer::RenderChunk(
  758. LONG& cchChunk, //@parm in: chunk cch; out: # chars rendered
  759. // if return TRUE; else # chars yet to render
  760. const WCHAR *pstrToRender, //@parm String to render up to cchChunk chars
  761. LONG cch) //@parm # chars left to render on line
  762. {
  763. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::RenderChunk");
  764. LONG cchT;
  765. LONG cchvalid;
  766. LONG i;
  767. const TCHAR *pchT;
  768. COleObject *pobj;
  769. CObjectMgr *pobjmgr;
  770. // If line has objects, reduce cchChunk to go to next object only
  771. if(_li._bFlags & fliHasOle)
  772. {
  773. pchT = pstrToRender;
  774. cchvalid = cchChunk;
  775. // Search for object in chunk
  776. for( i = 0; i < cchvalid && *pchT != WCH_EMBEDDING; i++ )
  777. pchT++;
  778. if( i == 0 )
  779. {
  780. // First character is object so display object
  781. pobjmgr = GetPed()->GetObjectMgr();
  782. pobj = pobjmgr->GetObjectFromCp(GetCp());
  783. if(pobj)
  784. {
  785. LONG yAscent, yDescent, objwidth;
  786. pobj->MeasureObj(_dypInch, _dxpInch, objwidth, yAscent, yDescent, _li._yDescent);
  787. SetClipLeftRight(_li._xWidth + objwidth);
  788. if (W32->FUsePalette() && (_li._bFlags & fliUseOffScreenDC) && _pdp->IsMain())
  789. {
  790. // Keep track of palette needed for rendering bitmap
  791. UpdatePalette(pobj);
  792. }
  793. pobj->DrawObj(_pdp, _dypInch, _dxpInch, _hdc, _pdp->IsMetafile(), &_ptCur, &_rc,
  794. _li._yHeight - _li._yDescent, _li._yDescent);
  795. _ptCur.x += objwidth;
  796. _li._xWidth += objwidth;
  797. }
  798. cchChunk = 1;
  799. // Both tabs and object code need to advance the run pointer past
  800. // each character processed.
  801. Advance(1);
  802. return TRUE;
  803. }
  804. else
  805. {
  806. // Limit chunk to character before object
  807. cchChunk -= cchvalid - i;
  808. }
  809. }
  810. // If line has tabs, reduce cchChunk
  811. if(_li._bFlags & fliHasTabs)
  812. {
  813. for(cchT = 0, pchT = pstrToRender;
  814. cchT < cchChunk && *pchT != TAB && *pchT != CELL
  815. && *pchT != SOFTHYPHEN
  816. ; pchT++, cchT++)
  817. {
  818. // this loop body intentionally left blank
  819. }
  820. if(!cchT)
  821. {
  822. // First char is a tab, render it and any that follow
  823. if(*pchT == SOFTHYPHEN)
  824. {
  825. if(cch == 1) // Only render soft hyphen at EOL
  826. {
  827. TCHAR chT = '-';
  828. RenderText(&chT, 1);
  829. }
  830. Advance(1); // Skip those within line
  831. cchChunk = 1;
  832. }
  833. else
  834. cchChunk = RenderTabs(cchChunk);
  835. Assert (cchChunk > 0);
  836. return TRUE;
  837. }
  838. cchChunk = cchT; // Update cchChunk not to incl trailing tabs
  839. }
  840. // If line has special characters, reduce cchChunk to go to next special character
  841. if(_li._bFlags & fliHasSpecialChars)
  842. {
  843. pchT = pstrToRender;
  844. cchvalid = cchChunk;
  845. // Search for special character in chunk
  846. for( i = 0; i < cchvalid && *pchT != EURO; i++)
  847. pchT++;
  848. if(i == 0)
  849. {
  850. for(; i < cchvalid && *pchT == EURO; i++)
  851. pchT++;
  852. cchChunk = i;
  853. }
  854. else
  855. {
  856. // Limit chunk to character before object
  857. cchChunk -= cchvalid - i;
  858. }
  859. }
  860. return FALSE;
  861. }
  862. /*
  863. * CRenderer::SetClipRect()
  864. *
  865. * @mfunc
  866. * Helper to set clipping rect for the line
  867. */
  868. void CRenderer::SetClipRect()
  869. {
  870. //Nominal clipping values
  871. _rc = _rcRender;
  872. _rc.top = _ptCur.y;
  873. _rc.bottom = _rc.top + _li._yHeight;
  874. //Clip to rcView
  875. _rc.left = max(_rc.left, _rcView.left);
  876. _rc.right = min(_rc.right, _rcView.right);
  877. _rc.top = max(_rc.top, _rcView.top);
  878. _rc.bottom = min(_rc.bottom, _rcView.bottom);
  879. }
  880. /*
  881. * CRenderer::SetClipLeftRight (xWidth)
  882. *
  883. * @mfunc
  884. * Helper to sets left and right of clipping/erase rect.
  885. *
  886. * @rdesc
  887. * Sets _rc left and right
  888. */
  889. void CRenderer::SetClipLeftRight(
  890. LONG xWidth) //@parm Width of chunk to render
  891. {
  892. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::SetClipLeftRight");
  893. //Nominal values
  894. _rc.left = _ptCur.x;
  895. _rc.right = _rc.left + xWidth;
  896. //Constrain left and right based on rcView, rcRender
  897. _rc.left = max(_rc.left, _rcView.left);
  898. _rc.left = max(_rc.left, _rcRender.left);
  899. _rc.right = max(_rc.right, _rc.left);
  900. _rc.right = min(_rc.right, _rcView.right);
  901. _rc.right = min(_rc.right, _rcRender.right);
  902. }
  903. /*
  904. * CRenderer::GetConvertMode()
  905. *
  906. * @mfunc
  907. * Return the mode that should really be used in the RenderText call
  908. */
  909. CONVERTMODE CRenderer::GetConvertMode()
  910. {
  911. CONVERTMODE cm = (CONVERTMODE)_pccs->_bConvertMode;
  912. // For hack around ExtTextOutW Win95 problems.
  913. if (cm != CVT_LOWBYTE && W32->OnWin9x() && (_pdp->IsMetafile() || _fEnhancedMetafileDC))
  914. return CVT_WCTMB;
  915. if (cm != CVT_LOWBYTE && _pdp->IsMetafile() && !_fEnhancedMetafileDC)
  916. return CVT_WCTMB; // WMF cant store Unicode so we cant use ExtTextOutW
  917. return cm;
  918. }
  919. /*
  920. * CRenderer::RenderExtTextOut (x, y, fuOptions, &rc, pwchRun, cch, rgdxp)
  921. *
  922. * @mfunc
  923. * Calls ExtTextOut and handles disabled text. There is duplicate logic in OlsDrawGlyphs, but
  924. * the params are different so that was simplest way.
  925. *
  926. */
  927. void CRenderer::RenderExtTextOut(LONG x, LONG y, UINT fuOptions, RECT *prc, PCWSTR pch, UINT cch, const INT *rgdxp)
  928. {
  929. CONVERTMODE cm = GetConvertMode();
  930. if (prc->left == prc->right)
  931. return;
  932. if(_fDisabled)
  933. {
  934. if(_crForeDisabled != _crShadowDisabled)
  935. {
  936. // The shadow should be offset by a hairline point, namely
  937. // 3/4 of a point. Calculate how big this is in device units,
  938. // but make sure it is at least 1 pixel.
  939. DWORD offset = MulDiv(3, _dypInch, 4*72);
  940. offset = max(offset, 1);
  941. // Draw shadow
  942. SetTextColor(_crShadowDisabled);
  943. W32->REExtTextOut(cm, _pccs->_wCodePage, _hdc, x + offset, y + offset,
  944. fuOptions, prc, pch, cch, rgdxp, _fFEFontOnNonFEWin9x);
  945. // Now set drawing mode to transparent
  946. fuOptions &= ~ETO_OPAQUE;
  947. }
  948. SetTextColor(_crForeDisabled);
  949. }
  950. W32->REExtTextOut(cm, _pccs->_wCodePage, _hdc, x, y, fuOptions, prc, pch, cch, rgdxp, _fFEFontOnNonFEWin9x);
  951. }
  952. /*
  953. * CRenderer::RenderText (pch, cch)
  954. *
  955. * @mfunc
  956. * Render text in the current context of this CRenderer
  957. *
  958. *
  959. * @devnote
  960. * Renders text only: does not do tabs or OLE objects
  961. */
  962. void CRenderer::RenderText(
  963. const WCHAR *pch, //@parm Text to render
  964. LONG cch) //@parm Length of text to render
  965. {
  966. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::RenderText");
  967. LONG yOffsetForChar, cchT;
  968. // Variables used for calculating length of underline.
  969. LONG xWidthSelPastEOL = 0;
  970. UINT fuOptions = _pdp->IsMain() ? ETO_CLIPPED : 0;
  971. LONG xWidth;
  972. LONG tempwidth;
  973. CTempBuf rgdx;
  974. //Reset clip rectangle to greater of view/render rectangle
  975. _rc.left = max(_rcRender.left, _rcView.left);
  976. _rc.right = min(_rcRender.right, _rcView.right);
  977. // Trim all nondisplayable linebreaking chars off end
  978. while(cch && IsASCIIEOP(pch[cch - 1]))
  979. cch--;
  980. int *pdx = (int *)rgdx.GetBuf(cch * sizeof(int));
  981. // Measure width of text to write so next point to write can be
  982. // calculated.
  983. xWidth = 0;
  984. for(cchT = 0;
  985. cchT < cch && xWidth < _rc.right - _ptCur.x;
  986. cchT++)
  987. {
  988. tempwidth = 0;
  989. #ifdef UNICODE_SURROGATES
  990. Assert(!IN_RANGE(0xD800, *pch, 0xDFFF));
  991. #endif
  992. if (!IN_RANGE(0x300, *pch, 0x36F) && !_pccs->Include(*pch, tempwidth))
  993. {
  994. TRACEERRSZSC("CRenderer::RenderText(): Error filling CCcs", E_FAIL);
  995. return;
  996. }
  997. *pdx++ = tempwidth;
  998. pch++;
  999. xWidth += tempwidth;
  1000. }
  1001. // Go back to start of chunk
  1002. cch = cchT;
  1003. pch -= cch;
  1004. pdx -= cch;
  1005. if(_fLastChunk && _fSelectToEOL && _li._cchEOP)
  1006. {
  1007. // Use the width of the current font's space to highlight
  1008. if(!_pccs->Include(' ', tempwidth))
  1009. {
  1010. TRACEERRSZSC("CRenderer::RenderText(): Error no length of space", E_FAIL);
  1011. return;
  1012. }
  1013. xWidthSelPastEOL = tempwidth + _pccs->_xOverhang;
  1014. xWidth += xWidthSelPastEOL;
  1015. _fSelectToEOL = FALSE; // Reset the flag
  1016. }
  1017. _li._xWidth += xWidth;
  1018. // Setup for drawing selections via ExtTextOut.
  1019. if(_fSelected || _crBackground != _crCurBackground)
  1020. {
  1021. SetClipLeftRight(xWidth);
  1022. if(_fSelected)
  1023. {
  1024. CTxtSelection *psel = GetPed()->GetSelNC();
  1025. if (_pPF->InTable() && GetPrevChar() == CELL && psel &&
  1026. psel->fHasCell() && GetCp() == psel->GetCpMin())
  1027. {
  1028. _rc.left -= LXtoDX(_pPF->_dxOffset);
  1029. }
  1030. }
  1031. fuOptions = ETO_CLIPPED | ETO_OPAQUE;
  1032. }
  1033. yOffsetForChar = _ptCur.y + _li._yHeight - _li._yDescent + _pccs->_yDescent - _pccs->_yHeight;
  1034. LONG yOffset, yAdjust;
  1035. _pccs->GetOffset(GetCF(), _dypInch, &yOffset, &yAdjust);
  1036. yOffsetForChar -= yOffset + yAdjust;
  1037. RenderExtTextOut(_ptCur.x, yOffsetForChar, fuOptions, &_rc, pch, cch, pdx);
  1038. // Calculate width to draw for underline/strikeout
  1039. if(_bUnderlineType != CFU_UNDERLINENONE || _fStrikeOut)
  1040. {
  1041. LONG xWidthToDraw = xWidth - xWidthSelPastEOL;
  1042. LONG xStart = _ptCur.x;
  1043. LONG xEnd = xStart + xWidthToDraw;
  1044. xStart = max(xStart, _rcRender.left);
  1045. xStart = max(xStart, _rcView.left);
  1046. xEnd = min(xEnd, _rcRender.right);
  1047. xEnd = min(xEnd, _rcView.right);
  1048. xWidthToDraw = xEnd - xStart;
  1049. if(xWidthToDraw > 0)
  1050. {
  1051. LONG y = _ptCur.y + _li._yHeight - _li._yDescent;
  1052. y -= yOffset + yAdjust;
  1053. // Render underline if required
  1054. if(_bUnderlineType != CFU_UNDERLINENONE)
  1055. RenderUnderline(xStart, y + _pccs->_dyULOffset, xWidthToDraw, _pccs->_dyULWidth);
  1056. // Render strikeout if required
  1057. if(_fStrikeOut)
  1058. RenderStrikeOut(xStart, y + _pccs->_dySOOffset, xWidthToDraw, _pccs->_dySOWidth);
  1059. }
  1060. }
  1061. _fSelected = FALSE;
  1062. _ptCur.x += xWidth; // Update current point
  1063. }
  1064. /*
  1065. * CRenderer::RenderTabs (cchMax)
  1066. *
  1067. * @mfunc
  1068. * Render a span of zero or more tab characters in chunk *this
  1069. *
  1070. * @rdesc
  1071. * number of tabs rendered
  1072. *
  1073. * @devnote
  1074. * *this is advanced by number of tabs rendered
  1075. * MS - tabs should be rendered using opaquing rect of adjacent string
  1076. */
  1077. LONG CRenderer::RenderTabs(
  1078. LONG cchMax) //@parm Max cch to render (cch in chunk)
  1079. {
  1080. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::RenderTabs");
  1081. LONG cch = cchMax;
  1082. LONG ch = GetChar();
  1083. LONG chPrev = 0;
  1084. LONG xTab, xTabs;
  1085. for(xTabs = 0; cch && (ch == TAB || ch == CELL); cch--)
  1086. {
  1087. xTab = MeasureTab(ch);
  1088. _li._xWidth += xTab; // Advance internal width
  1089. xTabs += xTab; // Accumulate width of tabbed
  1090. Advance(1); // region
  1091. chPrev = ch;
  1092. ch = GetChar();
  1093. }
  1094. if(_li._xWidth > _xWidthLine)
  1095. {
  1096. xTabs = 0;
  1097. _li._xWidth = _xWidthLine;
  1098. }
  1099. if(xTabs)
  1100. {
  1101. LONG dx = 0;
  1102. LONG xGap = 0;
  1103. if(_fSelected && chPrev == CELL && ch != CR && _pPF->InTable())
  1104. {
  1105. LONG cpSelMin, cpSelMost;
  1106. GetPed()->GetSelRangeForRender(&cpSelMin, &cpSelMost);
  1107. if(GetCp() == cpSelMin || GetCp() == cpSelMost)
  1108. {
  1109. xGap = LXtoDX(_pPF->_dxOffset);
  1110. if(GetCp() == cpSelMost)
  1111. {
  1112. dx = xGap;
  1113. xGap = 0;
  1114. }
  1115. }
  1116. }
  1117. SetClipLeftRight(xTabs - dx);
  1118. if(_rc.left < _rc.right) // Something to erase
  1119. {
  1120. if(_fSelected) // Use selection background color
  1121. {
  1122. COLORREF cr = GetPed()->TxGetSysColor(COLOR_HIGHLIGHT);
  1123. if (!UseXOR(cr))
  1124. {
  1125. ::SetBkColor (_hdc, cr);
  1126. _crCurTextColor = GetPed()->TxGetSysColor(COLOR_HIGHLIGHTTEXT);
  1127. }
  1128. else
  1129. {
  1130. const CCharFormat* pCF = GetCF();
  1131. ::SetBkColor (_hdc, (pCF->_dwEffects & CFE_AUTOBACKCOLOR) ?
  1132. _crBackground ^ RGB_WHITE : pCF->_crBackColor ^ RGB_WHITE);
  1133. _crCurTextColor = (pCF->_dwEffects & CFE_AUTOCOLOR) ?
  1134. _crTextColor ^ RGB_WHITE : pCF->_crTextColor ^ RGB_WHITE;
  1135. }
  1136. }
  1137. // Paint background with appropriate color
  1138. if(_fSelected || _crBackground != _crCurBackground)
  1139. EraseTextOut(_hdc, &_rc);
  1140. // Render underline if required
  1141. dx = _rc.right - _rc.left;
  1142. LONG y = _ptCur.y + _li._yHeight - _li._yDescent;
  1143. LONG yOffset, yAdjust;
  1144. _pccs->GetOffset(GetCF(), _dypInch, &yOffset, &yAdjust);
  1145. y -= yOffset + yAdjust;
  1146. if(_bUnderlineType != CFU_UNDERLINENONE)
  1147. RenderUnderline(_rc.left, y + _pccs->_dyULOffset, dx, _pccs->_dyULWidth);
  1148. // Render strikeout if required
  1149. if(_fStrikeOut)
  1150. RenderStrikeOut(_rc.left, y + _pccs->_dySOOffset, dx, _pccs->_dySOWidth);
  1151. if(_fSelected) // Restore colors
  1152. ::SetBkColor(_hdc, _crCurBackground);
  1153. }
  1154. _ptCur.x += xTabs; // Update current point
  1155. }
  1156. return cchMax - cch; // Return # tabs rendered
  1157. }
  1158. /*
  1159. * CRenderer::SetNewFont()
  1160. *
  1161. * @mfunc
  1162. * Select appropriate font and color in the _hdc based on the
  1163. * current character format. Also sets the background color
  1164. * and mode.
  1165. *
  1166. * @rdesc
  1167. * TRUE if it succeeds
  1168. *
  1169. * @devnote
  1170. * The calling chain must be protected by a CLock, since this present
  1171. * routine access the global (shared) FontCache facility.
  1172. */
  1173. BOOL CRenderer::SetNewFont()
  1174. {
  1175. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::SetNewFont");
  1176. const CCharFormat *pCF = GetCF();
  1177. DWORD dwEffects = pCF->_dwEffects;
  1178. // Release previous font in use
  1179. if(_pccs)
  1180. _pccs->Release();
  1181. Assert(_fTarget == FALSE);
  1182. _pccs = GetCcs(pCF);
  1183. if(!_pccs)
  1184. {
  1185. TRACEERRSZSC("CRenderer::SetNewFont(): no CCcs", E_FAIL);
  1186. return FALSE;
  1187. }
  1188. // Select font in _hdc
  1189. AssertSz(_pccs->_hfont, "CRenderer::SetNewFont _pccs->_hfont is NULL");
  1190. SetFontAndColor(pCF);
  1191. // Assume no underlining
  1192. _bUnderlineType = CFU_UNDERLINENONE;
  1193. // We want to draw revision marks and hyperlinks with underlining, so
  1194. // just fake out our font information.
  1195. if(dwEffects & (CFE_UNDERLINE | CFE_REVISED | CFE_LINK))
  1196. SetupUnderline((dwEffects & CFE_LINK) ? CFU_UNDERLINE : pCF->_bUnderlineType);
  1197. _fStrikeOut = (dwEffects & (CFE_STRIKEOUT | CFE_DELETED)) != 0;
  1198. return TRUE;
  1199. }
  1200. /*
  1201. * CRenderer::SetupUnderline (UnderlineType)
  1202. *
  1203. * @mfunc
  1204. * Setup internal variables for underlining
  1205. */
  1206. void CRenderer::SetupUnderline(
  1207. LONG UnderlineType)
  1208. {
  1209. _bUnderlineType = (BYTE) UnderlineType & 0xF; // Low nibble gives type
  1210. _bUnderlineClrIdx = (BYTE) UnderlineType/16; // High nibble gives color
  1211. }
  1212. /*
  1213. * CRenderer::SetFontAndColor (pCF)
  1214. *
  1215. * @mfunc
  1216. * Select appropriate font and color in the _hdc based on the
  1217. * current character format. Also sets the background color
  1218. * and mode.
  1219. */
  1220. void CRenderer::SetFontAndColor(
  1221. const CCharFormat *pCF) //@parm Character format for colors
  1222. {
  1223. CTxtEdit *ped = GetPed();
  1224. _fDisabled = FALSE;
  1225. if((pCF->_dwEffects & (CFE_AUTOCOLOR | CFE_DISABLED))
  1226. == (CFE_AUTOCOLOR | CFE_DISABLED))
  1227. {
  1228. _fDisabled = TRUE;
  1229. _crForeDisabled = ped->TxGetSysColor(COLOR_3DSHADOW);
  1230. _crShadowDisabled = ped->TxGetSysColor(COLOR_3DHILIGHT);
  1231. }
  1232. _fFEFontOnNonFEWin9x = FALSE;
  1233. if (IsFECharSet(pCF->_bCharSet) && W32->OnWin9x() && !W32->OnWin9xFE())
  1234. {
  1235. _fFEFontOnNonFEWin9x = TRUE;
  1236. }
  1237. SelectFont(_hdc, _pccs->_hfont);
  1238. // Compute height and descent if not yet done
  1239. if(_li._yHeight == -1)
  1240. {
  1241. SHORT yAdjustFE = _pccs->AdjustFEHeight(!fUseUIFont() && ped->_pdp->IsMultiLine());
  1242. // Note: this assumes plain text
  1243. // Should be used only for single line control
  1244. _li._yHeight = _pccs->_yHeight + (yAdjustFE << 1);
  1245. _li._yDescent = _pccs->_yDescent + yAdjustFE;
  1246. }
  1247. SetTextColor(GetTextColor(pCF)); // Set current text color
  1248. COLORREF cr;
  1249. if(_fSelected) // Set current background color
  1250. {
  1251. cr = GetPed()->TxGetSysColor(COLOR_HIGHLIGHT);
  1252. if (UseXOR(cr))
  1253. {
  1254. // There are 2 cases to be concerned with
  1255. // 1) if the background color is the same as the selection color or
  1256. // 2) if 1.0 Window and the background color is NOT system default
  1257. cr = (pCF->_dwEffects & CFE_AUTOBACKCOLOR) ?
  1258. _crBackground ^ RGB_WHITE : pCF->_crBackColor ^ RGB_WHITE;
  1259. }
  1260. }
  1261. else if(pCF->_dwEffects & CFE_AUTOBACKCOLOR)
  1262. cr = _crBackground;
  1263. else //Text has some kind of back color
  1264. cr = pCF->_crBackColor;
  1265. if(cr != _crCurBackground)
  1266. {
  1267. ::SetBkColor(_hdc, cr); // Tell window background color
  1268. _crCurBackground = cr; // Remember current background color
  1269. _fBackgroundColor = _crBackground != cr; // Change render settings so we won't
  1270. } // fill with background color
  1271. }
  1272. /*
  1273. * CRenderer::SetTextColor (cr)
  1274. *
  1275. * @mfunc
  1276. * Select given text color in the _hdc
  1277. * Used to maintain _crCurTextColor cache
  1278. */
  1279. void CRenderer::SetTextColor(
  1280. COLORREF cr) //@parm color to set in the dc
  1281. {
  1282. if(cr != _crCurTextColor)
  1283. {
  1284. _crCurTextColor = cr;
  1285. ::SetTextColor(_hdc, cr);
  1286. }
  1287. }
  1288. /*
  1289. * CRenderer::GetTextColor(pCF)
  1290. *
  1291. * @mfunc
  1292. * Return text color for pCF. Depends on _bRevAuthor, display tech
  1293. *
  1294. * FUTURE (keithcu) It might be nice to have black or blue selected text be
  1295. * white, but to have all other colors stay their other colors. What do we
  1296. * do if the backcolor is blue??
  1297. *
  1298. * @rdesc
  1299. * text color
  1300. */
  1301. COLORREF CRenderer::GetTextColor(
  1302. const CCharFormat *pCF) //@parm CCharFormat specifying text color
  1303. {
  1304. if(_fSelected)
  1305. {
  1306. // There are 2 cases where XOR for selection is needed
  1307. // 1) if the background is the same as the selection background
  1308. // 2) if 1.0 window and the background isn't the system default window
  1309. // background color
  1310. // if this doesn't match the above case just return the cr
  1311. if (!UseXOR(GetPed()->TxGetSysColor(COLOR_HIGHLIGHT)))
  1312. return GetPed()->TxGetSysColor(COLOR_HIGHLIGHTTEXT);
  1313. // xor the current text color for the selected text color
  1314. return (pCF->_dwEffects & CFE_AUTOCOLOR) ? _crTextColor ^ RGB_WHITE :
  1315. pCF->_crTextColor ^ RGB_WHITE;
  1316. }
  1317. // The following could be generalized to return a different color for
  1318. // links that have been visited for this text instance (need to define
  1319. // extra CCharFormat::_dwEffects internal flag to ID these links)
  1320. if(pCF->_dwEffects & CFE_LINK)
  1321. {
  1322. // Blue doesnt show up very well against dark backgrounds.
  1323. // In these situations, use the system selected text color.
  1324. COLORREF crBackground = (pCF->_dwEffects & CFE_AUTOBACKCOLOR)
  1325. ? _crBackground : pCF->_crBackColor;
  1326. if (IsTooSimilar(crBackground, RGB_BLACK) || IsTooSimilar(crBackground, RGB_BLUE))
  1327. {
  1328. COLORREF crHighlightText = GetPed()->TxGetSysColor(COLOR_HIGHLIGHTTEXT);
  1329. if (IsTooSimilar(crBackground, crHighlightText))
  1330. {
  1331. // Background is similar to highlight, use window text color
  1332. return GetPed()->TxGetSysColor(COLOR_WINDOWTEXT);
  1333. }
  1334. else
  1335. {
  1336. return crHighlightText;
  1337. }
  1338. }
  1339. else
  1340. {
  1341. return RGB_BLUE;
  1342. }
  1343. }
  1344. if(pCF->_bRevAuthor) // Rev author
  1345. {
  1346. // Limit color of rev authors to 0 through 7.
  1347. return rgcrRevisions[(pCF->_bRevAuthor - 1) & REVMASK];
  1348. }
  1349. COLORREF cr = (pCF->_dwEffects & CFE_AUTOCOLOR) ? _crTextColor : pCF->_crTextColor;
  1350. if(cr == RGB_WHITE) // Text is white
  1351. {
  1352. COLORREF crBackground = (pCF->_dwEffects & CFE_AUTOBACKCOLOR)
  1353. ? _crBackground : pCF->_crBackColor;
  1354. if(crBackground != RGB_WHITE)
  1355. {
  1356. // Background color isn't white, so white text is probably
  1357. // visible unless display device is only black/white. So we
  1358. // switch to black text on such devices.
  1359. if (GetDeviceCaps(_hdc, NUMCOLORS) == 2 ||
  1360. GetDeviceCaps(_hdc, TECHNOLOGY) == DT_PLOTTER)
  1361. {
  1362. cr = RGB_BLACK;
  1363. }
  1364. }
  1365. }
  1366. return cr;
  1367. }
  1368. /*
  1369. * CRenderer::RenderStartLine()
  1370. *
  1371. * @mfunc
  1372. * Render possible outline symbol and bullet if at start of line
  1373. *
  1374. * @rdesc
  1375. * TRUE if this method succeeded
  1376. */
  1377. BOOL CRenderer::RenderStartLine()
  1378. {
  1379. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::RenderStartLine");
  1380. BOOL fDrawBack = !(GetCF()->_dwEffects & CFE_AUTOBACKCOLOR) && GetPed()->_fExtendBackColor;
  1381. RECT rcErase = _rcRender;
  1382. rcErase.top = _ptCur.y;
  1383. rcErase.bottom = min(rcErase.top + _li._yHeight, _rcRender.bottom);
  1384. //If the first line, erase to edge of rcRender
  1385. if (rcErase.top <= _rcView.top)
  1386. rcErase.top = min(_rcView.top, _rcRender.top);
  1387. if (_fErase && !fDrawBack)
  1388. EraseTextOut(GetDC(), &rcErase);
  1389. // Fill line with background color if we are in fExtendBackColor mode
  1390. if (fDrawBack)
  1391. {
  1392. // capture the old color so we reset it to what it was when we're finished
  1393. COLORREF crOld = ::SetBkColor(GetDC(), GetCF()->_crBackColor);
  1394. EraseTextOut(GetDC(), &_rc);
  1395. // reset background color to the old color
  1396. ::SetBkColor(GetDC(), crOld);
  1397. //Erase the remainder of the background area
  1398. if (_fErase)
  1399. {
  1400. RECT rcTemp = rcErase;
  1401. //Erase the top part if necessary
  1402. if (rcErase.top < _rc.top)
  1403. {
  1404. rcTemp.bottom = _rc.top;
  1405. EraseTextOut(GetDC(), &rcTemp);
  1406. }
  1407. //Erase the left and right parts if necessary
  1408. rcTemp.top = _rc.top;
  1409. rcTemp.bottom = _rc.bottom;
  1410. if (rcErase.left < _rc.left)
  1411. {
  1412. rcTemp.right = _rc.left;
  1413. EraseTextOut(GetDC(), &rcTemp);
  1414. }
  1415. if (rcErase.right > _rc.right)
  1416. {
  1417. rcTemp.left = _rc.right;
  1418. rcTemp.right = rcErase.right;
  1419. EraseTextOut(GetDC(), &rcTemp);
  1420. }
  1421. }
  1422. }
  1423. if(IsRich() && (_li._bFlags & fliFirstInPara))
  1424. {
  1425. if(IsInOutlineView())
  1426. RenderOutlineSymbol();
  1427. if(_pPF->_wNumbering && !fUseLineServices())
  1428. RenderBullet();
  1429. }
  1430. // Reset format if there is special background color for previous line.
  1431. // Otherwise, current line with the same format will not re-paint with the
  1432. // special background color
  1433. if (_fBackgroundColor)
  1434. {
  1435. _iFormat = -10; // Reset to invalid format
  1436. // Assume that there is no special background color for the line
  1437. _fBackgroundColor = FALSE;
  1438. }
  1439. // Handle setting background color. If the current background
  1440. // color is different than the default, we need to set the background
  1441. // to this because the end of line processing reset the color so
  1442. // that opaquing would work.
  1443. if(_crBackground != _crCurBackground)
  1444. {
  1445. // Tell the window the background color
  1446. ::SetBkColor(_hdc, _crCurBackground);
  1447. _fBackgroundColor = TRUE;
  1448. }
  1449. return TRUE;
  1450. }
  1451. /*
  1452. * CRenderer::RenderOutlineSymbol()
  1453. *
  1454. * @mfunc
  1455. * Render outline symbol for current paragraph
  1456. *
  1457. * @rdesc
  1458. * TRUE if outline symbol rendered
  1459. */
  1460. BOOL CRenderer::RenderOutlineSymbol()
  1461. {
  1462. AssertSz(IsInOutlineView(),
  1463. "CRenderer::RenderOutlineSymbol called when not in outline view");
  1464. HBITMAP hbitmap;
  1465. LONG height;
  1466. LONG width;
  1467. LONG x = _ptCur.x - _li._xLeft + LXtoDX(lDefaultTab/2 * _pPF->_bOutlineLevel);
  1468. LONG y = _ptCur.y;
  1469. if(!g_hbitmapSubtext && InitializeOutlineBitmaps() != NOERROR)
  1470. return FALSE;
  1471. HDC hMemDC = CreateCompatibleDC(_hdc); // REVIEW: performance
  1472. if(!hMemDC)
  1473. return FALSE; //REVIEW: out of memory
  1474. if(_pPF->_bOutlineLevel & 1) // Subtext
  1475. {
  1476. width = BITMAP_WIDTH_SUBTEXT;
  1477. height = BITMAP_HEIGHT_SUBTEXT;
  1478. hbitmap = g_hbitmapSubtext;
  1479. }
  1480. else // Heading
  1481. {
  1482. width = BITMAP_WIDTH_HEADING;
  1483. height = BITMAP_HEIGHT_HEADING;
  1484. hbitmap = g_hbitmapEmptyHeading;
  1485. CPFRunPtr rp(*this); // Check next PF for other
  1486. LONG cch = _li._cch; // outline symbols
  1487. if(_li._cch < rp.GetCchLeft()) // Set cch = count to heading
  1488. { // EOP
  1489. CTxtPtr tp(_rpTX);
  1490. cch = tp.FindEOP(tomForward);
  1491. }
  1492. rp.AdvanceCp(cch); // Go to next paragraph
  1493. if(rp.IsCollapsed())
  1494. hbitmap = g_hbitmapCollapsedHeading;
  1495. else if(_pPF->_bOutlineLevel < rp.GetOutlineLevel())
  1496. hbitmap = g_hbitmapExpandedHeading;
  1497. }
  1498. if(!hbitmap)
  1499. return FALSE;
  1500. HBITMAP hbitmapDefault = (HBITMAP)SelectObject(hMemDC, hbitmap);
  1501. // REVIEW: what if the background color changes? Also, use a TT font
  1502. // for symbols
  1503. LONG h = _pdp->Zoom(height);
  1504. LONG dy = _li._yHeight - _li._yDescent - h;
  1505. if(dy > 0)
  1506. dy /= 2;
  1507. else
  1508. dy = -dy;
  1509. StretchBlt(_hdc, x, y + dy, _pdp->Zoom(width), h, hMemDC, 0, 0, width, height, SRCCOPY);
  1510. SelectObject(hMemDC, hbitmapDefault);
  1511. DeleteDC(hMemDC);
  1512. return TRUE;
  1513. }
  1514. /*
  1515. * CRenderer::RenderBullet()
  1516. *
  1517. * @mfunc
  1518. * Render bullet at start of line
  1519. *
  1520. * @rdesc
  1521. * TRUE if this method succeeded
  1522. */
  1523. BOOL CRenderer::RenderBullet()
  1524. {
  1525. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::RenderBullet");
  1526. AssertSz(_pPF->_wNumbering,
  1527. "CRenderer::RenderBullet called for non-bullet");
  1528. // Width of the bullet character
  1529. LONG xWidth;
  1530. // FUTURE: Unicode bullet is L'\x2022' We want to migrate to this and
  1531. // other bullets
  1532. LONG cch;
  1533. CCharFormat CF;
  1534. WCHAR szBullet[CCHMAXNUMTOSTR];
  1535. CCcs *pccs = GetCcsBullet(&CF);
  1536. if(!pccs) // Bullet is suppressed because
  1537. return TRUE; // preceding EOP is VT
  1538. if(_pccs)
  1539. _pccs->Release();
  1540. _pccs = pccs;
  1541. // Default to no underline
  1542. _bUnderlineType = CFU_UNDERLINENONE;
  1543. if(_pPF->IsListNumbered() && CF._dwEffects & CFE_UNDERLINE)
  1544. _bUnderlineType = (BYTE) CF._bUnderlineType & 0xF;
  1545. SetFontAndColor(&CF);
  1546. BYTE bFlagSave = _li._bFlags;
  1547. LONG dxOffset = LXtoDX(max(_pPF->_dxOffset, _pPF->_wNumberingTab));
  1548. LONG xSave = _ptCur.x;
  1549. LONG xWidthLineSave = _xWidthLine;
  1550. _li._bFlags = 0;
  1551. // Set-up to render bullet in one chunk
  1552. cch = GetBullet(szBullet, _pccs, &xWidth);
  1553. dxOffset = max(dxOffset, xWidth);
  1554. _xWidthLine = dxOffset;
  1555. if(IsInOutlineView())
  1556. dxOffset = _li._xLeft - LXtoDX(lDefaultTab/2 * (_pPF->_bOutlineLevel + 1));
  1557. _ptCur.x -= dxOffset;
  1558. // Render bullet
  1559. _fLastChunk = TRUE;
  1560. RenderText(szBullet, cch);
  1561. // Restore render vars to continue with remainder of line.
  1562. _ptCur.x = xSave;
  1563. _xWidthLine = xWidthLineSave;
  1564. _li._bFlags = bFlagSave;
  1565. _li._xWidth = 0;
  1566. // This releases the _pccs that we put in for the bullet
  1567. SetNewFont();
  1568. return TRUE;
  1569. }
  1570. /*
  1571. * CRenderer::RenderUnderline(xStart, yStart, xLength, yThickness)
  1572. *
  1573. * @mfunc
  1574. * Render underline
  1575. */
  1576. void CRenderer::RenderUnderline(
  1577. LONG xStart, //@parm Horizontal start of underline
  1578. LONG yStart, //@parm Vertical start of underline
  1579. LONG xLength, //@parm Length of underline
  1580. LONG yThickness) //@parm Thickness of underline
  1581. {
  1582. BOOL fUseLS = fUseLineServices();
  1583. COLORREF crUnderline;
  1584. RECT rcT;
  1585. if (!_bUnderlineClrIdx ||
  1586. GetPed()->GetEffectColor(_bUnderlineClrIdx, &crUnderline) != NOERROR ||
  1587. crUnderline == tomAutoColor || crUnderline == tomUndefined)
  1588. {
  1589. crUnderline = _crCurTextColor;
  1590. }
  1591. if (_bUnderlineType != CFU_INVERT &&
  1592. !IN_RANGE(CFU_UNDERLINEDOTTED, _bUnderlineType, CFU_UNDERLINEWAVE))
  1593. {
  1594. // Regular single underline case
  1595. // Calculate where to put underline
  1596. rcT.top = yStart;
  1597. if (CFU_UNDERLINETHICK == _bUnderlineType)
  1598. {
  1599. rcT.top -= yThickness;
  1600. yThickness += yThickness;
  1601. }
  1602. // There are some cases were the following can occur - particularly
  1603. // with bullets on Japanese systems.
  1604. if(!fUseLS && rcT.top >= _ptCur.y + _li._yHeight)
  1605. rcT.top = _ptCur.y + _li._yHeight - yThickness;
  1606. rcT.bottom = rcT.top + yThickness;
  1607. rcT.left = xStart;
  1608. rcT.right = xStart + xLength;
  1609. FillRectWithColor(&rcT, crUnderline);
  1610. return;
  1611. }
  1612. if(_bUnderlineType == CFU_INVERT) // Fake selection.
  1613. { // NOTE, not really
  1614. rcT.top = _ptCur.y; // how we should invert text!!
  1615. rcT.left = xStart; // check out IME invert.
  1616. rcT.bottom = rcT.top + _li._yHeight - _li._yDescent + _pccs->_yDescent;
  1617. rcT.right = rcT.left + xLength;
  1618. InvertRect(_hdc, &rcT);
  1619. return;
  1620. }
  1621. if(IN_RANGE(CFU_UNDERLINEDOTTED, _bUnderlineType, CFU_UNDERLINEWAVE))
  1622. {
  1623. static const char pen[] = {PS_DOT, PS_DASH, PS_DASHDOT, PS_DASHDOTDOT, PS_SOLID};
  1624. HPEN hPen = CreatePen(pen[_bUnderlineType - CFU_UNDERLINEDOTTED],
  1625. 1, crUnderline);
  1626. if(hPen)
  1627. {
  1628. HPEN hPenOld = SelectPen(_hdc, hPen);
  1629. LONG right = xStart + xLength;
  1630. MoveToEx(_hdc, xStart, yStart, NULL);
  1631. if(_bUnderlineType == CFU_UNDERLINEWAVE)
  1632. {
  1633. LONG dy = 1; // Vertical displacement
  1634. LONG x = xStart + 1; // x coordinate
  1635. right++; // Round up rightmost x
  1636. for( ; x < right; dy = -dy, x += 2)
  1637. LineTo(_hdc, x, yStart + dy);
  1638. }
  1639. else
  1640. LineTo(_hdc, right, yStart);
  1641. if(hPenOld) // Restore original pen.
  1642. SelectPen(_hdc, hPenOld);
  1643. DeleteObject(hPen);
  1644. }
  1645. }
  1646. }
  1647. /*
  1648. * CRenderer::RenderStrikeOut(xStart, yStart, xWidth, yThickness)
  1649. *
  1650. * @mfunc
  1651. * Render strikeout
  1652. */
  1653. void CRenderer::RenderStrikeOut(
  1654. LONG xStart, //@parm Horizontal start of strikeout
  1655. LONG yStart, //@parm Vertical start of strikeout
  1656. LONG xLength, //@parm Length of strikeout
  1657. LONG yThickness) //@parm Thickness of strikeout
  1658. {
  1659. RECT rcT;
  1660. // Calculate where to put strikeout rectangle
  1661. rcT.top = yStart;
  1662. rcT.bottom = yStart + yThickness;
  1663. rcT.left = xStart;
  1664. rcT.right = xStart + xLength;
  1665. FillRectWithColor(&rcT, GetTextColor(GetCF()));
  1666. }
  1667. /*
  1668. * CRenderer::FillRectWithTextColor(prc)
  1669. *
  1670. * @mfunc
  1671. * Fill input rectangle with current color of text
  1672. */
  1673. void CRenderer::FillRectWithColor(
  1674. RECT * prc, //@parm Rectangle to fill with color
  1675. COLORREF cr) //@parm Color to use
  1676. {
  1677. // Create a brush with the text color
  1678. HBRUSH hbrush = CreateSolidBrush(_fDisabled ? _crForeDisabled : cr);
  1679. // Note if the CreateSolidBrush fails we just ignore it since there
  1680. // isn't anything we can do about it anyway.
  1681. if(hbrush)
  1682. {
  1683. // Save old brush
  1684. HBRUSH hbrushOld = (HBRUSH)SelectObject(_hdc, hbrush);
  1685. // Fill rectangle for underline
  1686. PatBlt(_hdc, prc->left, prc->top, prc->right - prc->left,
  1687. prc->bottom - prc->top, PATCOPY);
  1688. SelectObject(_hdc, hbrushOld); // Put old brush back
  1689. DeleteObject(hbrush); // Free brush we created.
  1690. }
  1691. }