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.

475 lines
13 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. /*
  141. * if this item isn't localizable, do the default thing
  142. */
  143. if (!IsItemLocalizable (pnmcd))
  144. return (CDRF_DODEFAULT);
  145. #ifdef DBG
  146. USES_CONVERSION;
  147. TCHAR pszPrefix[80];
  148. wsprintf (pszPrefix, _T("(0x%08X) ItemPrePaint: "), this);
  149. LOGFONT lf;
  150. HFONT hFont;
  151. hFont = GetFontFromDC (pnmcd->hdc);
  152. GetObject (hFont, sizeof (lf), &lf);
  153. Trace (tagFontlink, _T("%sdefault font = (face=%s, weight=%d)"),
  154. pszPrefix, lf.lfFaceName, lf.lfWeight);
  155. /*
  156. * compute all of the fonts needed for this;
  157. * if we couldn't, do the default thing
  158. */
  159. Trace (tagFontlink, _T("%s text = \"%s\""),
  160. pszPrefix, W2CT (GetItemText(pnmcd).data()));
  161. #endif
  162. CRichText rt (pnmcd->hdc, GetItemText (pnmcd));
  163. if (!ComposeRichText (rt))
  164. {
  165. Trace (tagFontlink, _T("%s unable to determine font, using default"), pszPrefix);
  166. return (CDRF_DODEFAULT);
  167. }
  168. /*
  169. * if the default font in the DC is sufficient, do the default thing
  170. */
  171. if (rt.IsDefaultFontSufficient ())
  172. {
  173. Trace (tagFontlink, _T("%s default font is sufficient"), pszPrefix);
  174. return (CDRF_DODEFAULT);
  175. }
  176. /*
  177. * if the default font isn't sufficient, but there's a single
  178. * font that is, select it into the DC and let the control draw
  179. * the text
  180. */
  181. if (rt.IsSingleFontSufficient ())
  182. {
  183. #ifdef DBG
  184. hFont = rt.GetSufficientFont();
  185. GetObject (hFont, sizeof (lf), &lf);
  186. Trace (tagFontlink, _T("%s using single font = (face=%s, weight=%d)"),
  187. pszPrefix, lf.lfFaceName, lf.lfWeight);
  188. #endif
  189. SelectObject (pnmcd->hdc, rt.GetSufficientFont());
  190. return (CDRF_NEWFONT);
  191. }
  192. /*
  193. * TODO: handle drawing the icon and indented text
  194. */
  195. Trace (tagFontlink, _T("%s (punting...)"), pszPrefix);
  196. return (CDRF_DODEFAULT);
  197. /*
  198. * if we get here, two or more fonts are required to draw the
  199. * text; draw it ourselves, and tell the control not to do anything
  200. */
  201. rt.Draw (&pnmcd->rc, GetDrawTextFlags());
  202. return (CDRF_SKIPDEFAULT);
  203. }
  204. /*+-------------------------------------------------------------------------*
  205. * CFontLinker::ComposeRichText
  206. *
  207. * Computes all of the fonts required to draw a given Unicode string
  208. *--------------------------------------------------------------------------*/
  209. bool CFontLinker::ComposeRichText (CRichText& rt)
  210. {
  211. /*
  212. * get the code pages for the given DC's font
  213. */
  214. DWORD dwDefaultFontCodePages;
  215. if (!GetFontCodePages (rt.m_hdc, rt.m_hDefaultFont, dwDefaultFontCodePages))
  216. return (false);
  217. IMLangFontLink* pFontLink = GetFontLink();
  218. if (pFontLink == NULL)
  219. return (false);
  220. const LPCWSTR pszText = rt.m_strText.data();
  221. const int cchText = rt.m_strText.length();
  222. int cchDone = 0;
  223. DWORD dwPriorityCodePages = NULL;
  224. /*
  225. * build up the collection of TextSegmentFontInfos for the text
  226. */
  227. while (cchDone < cchText)
  228. {
  229. TextSegmentFontInfo tsfi;
  230. DWORD dwTextCodePages;
  231. /*
  232. * find out which code pages support the next segment of text
  233. */
  234. pFontLink->GetStrCodePages (pszText + cchDone,
  235. cchText - cchDone,
  236. dwPriorityCodePages,
  237. &dwTextCodePages, &tsfi.cch);
  238. /*
  239. * if the default font can render the text, things are easy
  240. */
  241. if (dwDefaultFontCodePages & dwTextCodePages)
  242. tsfi.hFont = rt.m_hDefaultFont;
  243. /*
  244. * otherwise, ask IFontLink for the font to use
  245. */
  246. else
  247. {
  248. /*
  249. * get the font
  250. */
  251. if (FAILED (pFontLink->MapFont (rt.m_hdc, dwTextCodePages,
  252. rt.m_hDefaultFont, &tsfi.hFont)))
  253. {
  254. rt.m_TextSegments.clear();
  255. return (false);
  256. }
  257. /*
  258. * add this font to the set of fonts to release when we're done
  259. */
  260. std::pair<FontSet::iterator, bool> rc =
  261. m_FontsToRelease.insert (tsfi.hFont);
  262. /*
  263. * if it was already there, release it now to keep
  264. * the ref counts right
  265. */
  266. if (!rc.second)
  267. pFontLink->ReleaseFont (tsfi.hFont);
  268. }
  269. rt.m_TextSegments.push_back (tsfi);
  270. cchDone += tsfi.cch;
  271. }
  272. return (true);
  273. }
  274. /*+-------------------------------------------------------------------------*
  275. * CFontLinker::GetMultiLang
  276. *
  277. *
  278. *--------------------------------------------------------------------------*/
  279. IMultiLanguage* CFontLinker::GetMultiLang ()
  280. {
  281. if (m_spMultiLang == NULL)
  282. m_spMultiLang.CreateInstance (CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER);
  283. return (m_spMultiLang);
  284. }
  285. /*+-------------------------------------------------------------------------*
  286. * CFontLinker::GetFontLink
  287. *
  288. *
  289. *--------------------------------------------------------------------------*/
  290. IMLangFontLink* CFontLinker::GetFontLink ()
  291. {
  292. if (m_spFontLink == NULL)
  293. m_spFontLink = GetMultiLang ();
  294. return (m_spFontLink);
  295. }
  296. /*+-------------------------------------------------------------------------*
  297. * CFontLinker::GetFontCodePages
  298. *
  299. * Returns a bit mask representing the code pages supported by the font.
  300. *--------------------------------------------------------------------------*/
  301. bool CFontLinker::GetFontCodePages (
  302. HDC hdc,
  303. HFONT hFont,
  304. DWORD& dwFontCodePages)
  305. {
  306. /*
  307. * check the code page cache to see if we've
  308. * asked MLang about this font before
  309. */
  310. FontToCodePagesMap::const_iterator itCodePages = m_CodePages.find (hFont);
  311. if (itCodePages != m_CodePages.end())
  312. {
  313. dwFontCodePages = itCodePages->second;
  314. return (true);
  315. }
  316. /*
  317. * this font isn't in our code page cache yet;
  318. * ask MLang for the code pages
  319. */
  320. IMLangFontLink* pFontLink = GetFontLink();
  321. if (pFontLink == NULL)
  322. return (false);
  323. if (FAILED (pFontLink->GetFontCodePages (hdc, hFont, &dwFontCodePages)))
  324. return (false);
  325. /*
  326. * put the code pages in the cache
  327. */
  328. m_CodePages[hFont] = dwFontCodePages;
  329. return (true);
  330. }
  331. /*+-------------------------------------------------------------------------*
  332. * CRichText::Draw
  333. *
  334. *
  335. *--------------------------------------------------------------------------*/
  336. bool CRichText::Draw (
  337. LPCRECT rect, /* i:rect to draw in */
  338. UINT uFormat, /* i:DrawText format flags */
  339. LPRECT prectRemaining /*=NULL*/) /* o:space remaining after drawing */
  340. const
  341. {
  342. HFONT hOriginalFont = GetFontFromDC (m_hdc);
  343. CRect rectDraw = rect;
  344. LPCWSTR pszDraw = m_strText.data();
  345. TextSegmentFontInfoCollection::const_iterator it = m_TextSegments.begin();
  346. /*
  347. * draw each segment
  348. */
  349. while (it != m_TextSegments.end())
  350. {
  351. /*
  352. * select the font for this segment
  353. */
  354. SelectObject (m_hdc, it->hFont);
  355. /*
  356. * measure the width of this segment
  357. */
  358. CRect rectMeasure = rectDraw;
  359. DrawTextW (m_hdc, pszDraw, it->cch, rectMeasure, uFormat | DT_CALCRECT);
  360. /*
  361. * draw this segment
  362. */
  363. DrawTextW (m_hdc, pszDraw, it->cch, rectDraw, uFormat);
  364. /*
  365. * set up for the next segment
  366. */
  367. pszDraw += it->cch;
  368. rectDraw.left = rectMeasure.right;
  369. ++it;
  370. /*
  371. * if we've run out of rect to draw in, short out
  372. */
  373. if (rectDraw.IsRectEmpty ())
  374. break;
  375. }
  376. /*
  377. * if the caller wants it, return the remaining rectangle after drawing
  378. */
  379. if (prectRemaining != NULL)
  380. *prectRemaining = rectDraw;
  381. /*
  382. * re-select the original font
  383. */
  384. SelectObject (m_hdc, hOriginalFont);
  385. return (true);
  386. }