Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

485 lines
14 KiB

  1. /*--------------------------------------------------------------------------*
  2. *
  3. * Microsoft Windows
  4. * Copyright (C) Microsoft Corporation, 1992 - 1999
  5. *
  6. * File: fontlink.cpp
  7. *
  8. * Contents: Implementation file for CFontLinker
  9. *
  10. * History: 17-Aug-98 jeffro Created
  11. *
  12. *--------------------------------------------------------------------------*/
  13. #include "stdafx.h"
  14. #include "fontlink.h"
  15. #include "macros.h"
  16. #ifdef DBG
  17. CTraceTag tagFontlink (_T("Font Linking"), _T("Font Linking"));
  18. #endif
  19. /*+-------------------------------------------------------------------------*
  20. * GetFontFromDC
  21. *
  22. * Returns the font that's currently selected into a DC
  23. *--------------------------------------------------------------------------*/
  24. HFONT GetFontFromDC (HDC hdc)
  25. {
  26. HFONT hFont = (HFONT) SelectObject (hdc, GetStockObject (SYSTEM_FONT));
  27. SelectObject (hdc, hFont);
  28. return (hFont);
  29. }
  30. /*+-------------------------------------------------------------------------*
  31. * CFontLinker::CFontLinker
  32. *
  33. *
  34. *--------------------------------------------------------------------------*/
  35. CFontLinker::CFontLinker ()
  36. {
  37. m_cPendingPostPaints = 0;
  38. }
  39. /*+-------------------------------------------------------------------------*
  40. * CFontLinker::~CFontLinker
  41. *
  42. *
  43. *--------------------------------------------------------------------------*/
  44. CFontLinker::~CFontLinker ()
  45. {
  46. ASSERT (m_cPendingPostPaints == 0);
  47. ReleaseFonts();
  48. }
  49. /*+-------------------------------------------------------------------------*
  50. * CFontLinker::ReleaseFonts
  51. *
  52. * Releases all fonts returned by IMLangFontLink
  53. *--------------------------------------------------------------------------*/
  54. void CFontLinker::ReleaseFonts()
  55. {
  56. /*
  57. * release the fonts
  58. */
  59. std::for_each (m_FontsToRelease.begin(), m_FontsToRelease.end(),
  60. FontReleaser (GetFontLink()));
  61. /*
  62. * purge the caches
  63. */
  64. m_FontsToRelease.clear();
  65. m_CodePages.clear();
  66. }
  67. /*+-------------------------------------------------------------------------*
  68. * CFontLinker::OnCustomDraw
  69. *
  70. * NM_CUSTOMDRAW handler for CFontLinker.
  71. *--------------------------------------------------------------------------*/
  72. LRESULT CFontLinker::OnCustomDraw (NMCUSTOMDRAW* pnmcd)
  73. {
  74. switch (pnmcd->dwDrawStage & ~CDDS_SUBITEM)
  75. {
  76. case CDDS_PREPAINT: return (OnCustomDraw_PrePaint (pnmcd));
  77. case CDDS_POSTPAINT: return (OnCustomDraw_PostPaint (pnmcd));
  78. case CDDS_ITEMPREPAINT: return (OnCustomDraw_ItemPrePaint (pnmcd));
  79. }
  80. return (CDRF_DODEFAULT);
  81. }
  82. /*+-------------------------------------------------------------------------*
  83. * CFontLinker::OnCustomDraw_PrePaint
  84. *
  85. * NM_CUSTOMDRAW (CDDS_PREPAINT) handler for CFontLinker.
  86. *--------------------------------------------------------------------------*/
  87. LRESULT CFontLinker::OnCustomDraw_PrePaint (NMCUSTOMDRAW* pnmcd)
  88. {
  89. m_cPendingPostPaints++; // this line must be before the Trace
  90. Trace (tagFontlink, _T("(0x%08X) PrePaint(%d):---------------------------------------------------------"), this, m_cPendingPostPaints);
  91. /*
  92. * Under certain rare, timing-dependent circumstances (see bug 96465),
  93. * we can get nested calls to custom draw from the listview control.
  94. * If this is not a nested custom draw, our font and codepage collections
  95. * should be empty.
  96. */
  97. if (m_cPendingPostPaints == 1)
  98. {
  99. ASSERT (m_FontsToRelease.empty());
  100. ASSERT (m_CodePages.empty());
  101. }
  102. /*
  103. * we always need a CDDS_POSTPAINT so we can keep our accounting correct
  104. */
  105. LRESULT rc = CDRF_NOTIFYPOSTPAINT;
  106. /*
  107. * get draw notifications for each item and subitem if any items
  108. * are localizable
  109. */
  110. if (IsAnyItemLocalizable())
  111. rc |= CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYSUBITEMDRAW;
  112. return (rc);
  113. }
  114. /*+-------------------------------------------------------------------------*
  115. * CFontLinker::OnCustomDraw_PostPaint
  116. *
  117. * NM_CUSTOMDRAW (CDDS_POSTPAINT) handler for CFontLinker.
  118. *--------------------------------------------------------------------------*/
  119. LRESULT CFontLinker::OnCustomDraw_PostPaint (NMCUSTOMDRAW* pnmcd)
  120. {
  121. Trace (tagFontlink, _T("(0x%08X) PostPaint(%d):--------------------------------------------------------"), this, m_cPendingPostPaints);
  122. m_cPendingPostPaints--; // this line must be after the Trace
  123. /*
  124. * if this is the final CDDS_POSTPAINT we'll get, release our fonts
  125. */
  126. if (m_cPendingPostPaints == 0)
  127. {
  128. Trace (tagFontlink, _T("(0x%08X) releasing fonts..."), this);
  129. ReleaseFonts ();
  130. }
  131. return (CDRF_DODEFAULT);
  132. }
  133. /*+-------------------------------------------------------------------------*
  134. * CFontLinker::OnCustomDraw_ItemPrePaint
  135. *
  136. * NM_CUSTOMDRAW (CDDS_ITEMPAINT) handler for CFontLinker.
  137. *--------------------------------------------------------------------------*/
  138. LRESULT CFontLinker::OnCustomDraw_ItemPrePaint (NMCUSTOMDRAW* pnmcd)
  139. {
  140. DECLARE_SC(sc, TEXT("CFontLinker::OnCustomDraw_ItemPrePaint"));
  141. /*
  142. * if this item isn't localizable, do the default thing
  143. */
  144. if (!IsItemLocalizable (pnmcd))
  145. return (CDRF_DODEFAULT);
  146. #ifdef DBG
  147. USES_CONVERSION;
  148. TCHAR pszPrefix[80];
  149. sc = StringCchPrintf(pszPrefix, countof(pszPrefix), _T("(0x%08X) ItemPrePaint: "), this);
  150. if (sc)
  151. sc.TraceAndClear(); // Truncation is okay, so ignore return.
  152. LOGFONT lf;
  153. HFONT hFont;
  154. hFont = GetFontFromDC (pnmcd->hdc);
  155. GetObject (hFont, sizeof (lf), &lf);
  156. Trace (tagFontlink, _T("%sdefault font = (face=%s, weight=%d)"),
  157. pszPrefix, lf.lfFaceName, lf.lfWeight);
  158. /*
  159. * compute all of the fonts needed for this;
  160. * if we couldn't, do the default thing
  161. */
  162. Trace (tagFontlink, _T("%s text = \"%s\""),
  163. pszPrefix, W2CT (GetItemText(pnmcd).data()));
  164. #endif
  165. CRichText rt (pnmcd->hdc, GetItemText (pnmcd));
  166. if (!ComposeRichText (rt))
  167. {
  168. Trace (tagFontlink, _T("%s unable to determine font, using default"), pszPrefix);
  169. return (CDRF_DODEFAULT);
  170. }
  171. /*
  172. * if the default font in the DC is sufficient, do the default thing
  173. */
  174. if (rt.IsDefaultFontSufficient ())
  175. {
  176. Trace (tagFontlink, _T("%s default font is sufficient"), pszPrefix);
  177. return (CDRF_DODEFAULT);
  178. }
  179. /*
  180. * if the default font isn't sufficient, but there's a single
  181. * font that is, select it into the DC and let the control draw
  182. * the text
  183. */
  184. if (rt.IsSingleFontSufficient ())
  185. {
  186. #ifdef DBG
  187. hFont = rt.GetSufficientFont();
  188. GetObject (hFont, sizeof (lf), &lf);
  189. Trace (tagFontlink, _T("%s using single font = (face=%s, weight=%d)"),
  190. pszPrefix, lf.lfFaceName, lf.lfWeight);
  191. #endif
  192. SelectObject (pnmcd->hdc, rt.GetSufficientFont());
  193. return (CDRF_NEWFONT);
  194. }
  195. /*
  196. * TODO: handle drawing the icon and indented text
  197. */
  198. Trace (tagFontlink, _T("%s (punting...)"), pszPrefix);
  199. return (CDRF_DODEFAULT);
  200. /*
  201. * if we get here, two or more fonts are required to draw the
  202. * text; draw it ourselves, and tell the control not to do anything
  203. */
  204. rt.Draw (&pnmcd->rc, GetDrawTextFlags());
  205. return (CDRF_SKIPDEFAULT);
  206. }
  207. /*+-------------------------------------------------------------------------*
  208. * CFontLinker::ComposeRichText
  209. *
  210. * Computes all of the fonts required to draw a given Unicode string
  211. *--------------------------------------------------------------------------*/
  212. bool CFontLinker::ComposeRichText (CRichText& rt)
  213. {
  214. /*
  215. * get the code pages for the given DC's font
  216. */
  217. DWORD dwDefaultFontCodePages;
  218. if (!GetFontCodePages (rt.m_hdc, rt.m_hDefaultFont, dwDefaultFontCodePages))
  219. return (false);
  220. IMLangFontLink* pFontLink = GetFontLink();
  221. if (pFontLink == NULL)
  222. return (false);
  223. const LPCWSTR pszText = rt.m_strText.data();
  224. const int cchText = rt.m_strText.length();
  225. int cchDone = 0;
  226. DWORD dwPriorityCodePages = NULL;
  227. /*
  228. * build up the collection of TextSegmentFontInfos for the text
  229. */
  230. while (cchDone < cchText)
  231. {
  232. TextSegmentFontInfo tsfi;
  233. DWORD dwTextCodePages;
  234. /*
  235. * find out which code pages support the next segment of text
  236. */
  237. if (FAILED(pFontLink->GetStrCodePages (pszText + cchDone,
  238. cchText - cchDone,
  239. dwPriorityCodePages,
  240. &dwTextCodePages, &tsfi.cch)) )
  241. {
  242. rt.m_TextSegments.clear();
  243. return (false);
  244. }
  245. /*
  246. * if the default font can render the text, things are easy
  247. */
  248. if (dwDefaultFontCodePages & dwTextCodePages)
  249. tsfi.hFont = rt.m_hDefaultFont;
  250. /*
  251. * otherwise, ask IFontLink for the font to use
  252. */
  253. else
  254. {
  255. /*
  256. * get the font
  257. */
  258. if (FAILED (pFontLink->MapFont (rt.m_hdc, dwTextCodePages,
  259. rt.m_hDefaultFont, &tsfi.hFont)))
  260. {
  261. rt.m_TextSegments.clear();
  262. return (false);
  263. }
  264. /*
  265. * add this font to the set of fonts to release when we're done
  266. */
  267. std::pair<FontSet::iterator, bool> rc =
  268. m_FontsToRelease.insert (tsfi.hFont);
  269. /*
  270. * if it was already there, release it now to keep
  271. * the ref counts right
  272. */
  273. if (!rc.second)
  274. pFontLink->ReleaseFont (tsfi.hFont);
  275. }
  276. rt.m_TextSegments.push_back (tsfi);
  277. cchDone += tsfi.cch;
  278. }
  279. return (true);
  280. }
  281. /*+-------------------------------------------------------------------------*
  282. * CFontLinker::GetMultiLang
  283. *
  284. *
  285. *--------------------------------------------------------------------------*/
  286. IMultiLanguage* CFontLinker::GetMultiLang ()
  287. {
  288. if (m_spMultiLang == NULL)
  289. m_spMultiLang.CreateInstance (CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER);
  290. return (m_spMultiLang);
  291. }
  292. /*+-------------------------------------------------------------------------*
  293. * CFontLinker::GetFontLink
  294. *
  295. *
  296. *--------------------------------------------------------------------------*/
  297. IMLangFontLink* CFontLinker::GetFontLink ()
  298. {
  299. if (m_spFontLink == NULL)
  300. m_spFontLink = GetMultiLang ();
  301. return (m_spFontLink);
  302. }
  303. /*+-------------------------------------------------------------------------*
  304. * CFontLinker::GetFontCodePages
  305. *
  306. * Returns a bit mask representing the code pages supported by the font.
  307. *--------------------------------------------------------------------------*/
  308. bool CFontLinker::GetFontCodePages (
  309. HDC hdc,
  310. HFONT hFont,
  311. DWORD& dwFontCodePages)
  312. {
  313. /*
  314. * check the code page cache to see if we've
  315. * asked MLang about this font before
  316. */
  317. FontToCodePagesMap::const_iterator itCodePages = m_CodePages.find (hFont);
  318. if (itCodePages != m_CodePages.end())
  319. {
  320. dwFontCodePages = itCodePages->second;
  321. return (true);
  322. }
  323. /*
  324. * this font isn't in our code page cache yet;
  325. * ask MLang for the code pages
  326. */
  327. IMLangFontLink* pFontLink = GetFontLink();
  328. if (pFontLink == NULL)
  329. return (false);
  330. if (FAILED (pFontLink->GetFontCodePages (hdc, hFont, &dwFontCodePages)))
  331. return (false);
  332. /*
  333. * put the code pages in the cache
  334. */
  335. m_CodePages[hFont] = dwFontCodePages;
  336. return (true);
  337. }
  338. /*+-------------------------------------------------------------------------*
  339. * CRichText::Draw
  340. *
  341. *
  342. *--------------------------------------------------------------------------*/
  343. bool CRichText::Draw (
  344. LPCRECT rect, /* i:rect to draw in */
  345. UINT uFormat, /* i:DrawText format flags */
  346. LPRECT prectRemaining /*=NULL*/) /* o:space remaining after drawing */
  347. const
  348. {
  349. HFONT hOriginalFont = GetFontFromDC (m_hdc);
  350. CRect rectDraw = rect;
  351. LPCWSTR pszDraw = m_strText.data();
  352. TextSegmentFontInfoCollection::const_iterator it = m_TextSegments.begin();
  353. /*
  354. * draw each segment
  355. */
  356. while (it != m_TextSegments.end())
  357. {
  358. /*
  359. * select the font for this segment
  360. */
  361. SelectObject (m_hdc, it->hFont);
  362. /*
  363. * measure the width of this segment
  364. */
  365. CRect rectMeasure = rectDraw;
  366. DrawTextW (m_hdc, pszDraw, it->cch, rectMeasure, uFormat | DT_CALCRECT);
  367. /*
  368. * draw this segment
  369. */
  370. DrawTextW (m_hdc, pszDraw, it->cch, rectDraw, uFormat);
  371. /*
  372. * set up for the next segment
  373. */
  374. pszDraw += it->cch;
  375. rectDraw.left = rectMeasure.right;
  376. ++it;
  377. /*
  378. * if we've run out of rect to draw in, short out
  379. */
  380. if (rectDraw.IsRectEmpty ())
  381. break;
  382. }
  383. /*
  384. * if the caller wants it, return the remaining rectangle after drawing
  385. */
  386. if (prectRemaining != NULL)
  387. *prectRemaining = rectDraw;
  388. /*
  389. * re-select the original font
  390. */
  391. SelectObject (m_hdc, hOriginalFont);
  392. return (true);
  393. }