/*--------------------------------------------------------------------------* * * Microsoft Windows * Copyright (C) Microsoft Corporation, 1992 - 1999 * * File: fontlink.cpp * * Contents: Implementation file for CFontLinker * * History: 17-Aug-98 jeffro Created * *--------------------------------------------------------------------------*/ #include "stdafx.h" #include "fontlink.h" #include "macros.h" #ifdef DBG CTraceTag tagFontlink (_T("Font Linking"), _T("Font Linking")); #endif /*+-------------------------------------------------------------------------* * GetFontFromDC * * Returns the font that's currently selected into a DC *--------------------------------------------------------------------------*/ HFONT GetFontFromDC (HDC hdc) { HFONT hFont = (HFONT) SelectObject (hdc, GetStockObject (SYSTEM_FONT)); SelectObject (hdc, hFont); return (hFont); } /*+-------------------------------------------------------------------------* * CFontLinker::CFontLinker * * *--------------------------------------------------------------------------*/ CFontLinker::CFontLinker () { m_cPendingPostPaints = 0; } /*+-------------------------------------------------------------------------* * CFontLinker::~CFontLinker * * *--------------------------------------------------------------------------*/ CFontLinker::~CFontLinker () { ASSERT (m_cPendingPostPaints == 0); ReleaseFonts(); } /*+-------------------------------------------------------------------------* * CFontLinker::ReleaseFonts * * Releases all fonts returned by IMLangFontLink *--------------------------------------------------------------------------*/ void CFontLinker::ReleaseFonts() { /* * release the fonts */ std::for_each (m_FontsToRelease.begin(), m_FontsToRelease.end(), FontReleaser (GetFontLink())); /* * purge the caches */ m_FontsToRelease.clear(); m_CodePages.clear(); } /*+-------------------------------------------------------------------------* * CFontLinker::OnCustomDraw * * NM_CUSTOMDRAW handler for CFontLinker. *--------------------------------------------------------------------------*/ LRESULT CFontLinker::OnCustomDraw (NMCUSTOMDRAW* pnmcd) { switch (pnmcd->dwDrawStage & ~CDDS_SUBITEM) { case CDDS_PREPAINT: return (OnCustomDraw_PrePaint (pnmcd)); case CDDS_POSTPAINT: return (OnCustomDraw_PostPaint (pnmcd)); case CDDS_ITEMPREPAINT: return (OnCustomDraw_ItemPrePaint (pnmcd)); } return (CDRF_DODEFAULT); } /*+-------------------------------------------------------------------------* * CFontLinker::OnCustomDraw_PrePaint * * NM_CUSTOMDRAW (CDDS_PREPAINT) handler for CFontLinker. *--------------------------------------------------------------------------*/ LRESULT CFontLinker::OnCustomDraw_PrePaint (NMCUSTOMDRAW* pnmcd) { m_cPendingPostPaints++; // this line must be before the Trace Trace (tagFontlink, _T("(0x%08X) PrePaint(%d):---------------------------------------------------------"), this, m_cPendingPostPaints); /* * Under certain rare, timing-dependent circumstances (see bug 96465), * we can get nested calls to custom draw from the listview control. * If this is not a nested custom draw, our font and codepage collections * should be empty. */ if (m_cPendingPostPaints == 1) { ASSERT (m_FontsToRelease.empty()); ASSERT (m_CodePages.empty()); } /* * we always need a CDDS_POSTPAINT so we can keep our accounting correct */ LRESULT rc = CDRF_NOTIFYPOSTPAINT; /* * get draw notifications for each item and subitem if any items * are localizable */ if (IsAnyItemLocalizable()) rc |= CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYSUBITEMDRAW; return (rc); } /*+-------------------------------------------------------------------------* * CFontLinker::OnCustomDraw_PostPaint * * NM_CUSTOMDRAW (CDDS_POSTPAINT) handler for CFontLinker. *--------------------------------------------------------------------------*/ LRESULT CFontLinker::OnCustomDraw_PostPaint (NMCUSTOMDRAW* pnmcd) { Trace (tagFontlink, _T("(0x%08X) PostPaint(%d):--------------------------------------------------------"), this, m_cPendingPostPaints); m_cPendingPostPaints--; // this line must be after the Trace /* * if this is the final CDDS_POSTPAINT we'll get, release our fonts */ if (m_cPendingPostPaints == 0) { Trace (tagFontlink, _T("(0x%08X) releasing fonts..."), this); ReleaseFonts (); } return (CDRF_DODEFAULT); } /*+-------------------------------------------------------------------------* * CFontLinker::OnCustomDraw_ItemPrePaint * * NM_CUSTOMDRAW (CDDS_ITEMPAINT) handler for CFontLinker. *--------------------------------------------------------------------------*/ LRESULT CFontLinker::OnCustomDraw_ItemPrePaint (NMCUSTOMDRAW* pnmcd) { DECLARE_SC(sc, TEXT("CFontLinker::OnCustomDraw_ItemPrePaint")); /* * if this item isn't localizable, do the default thing */ if (!IsItemLocalizable (pnmcd)) return (CDRF_DODEFAULT); #ifdef DBG USES_CONVERSION; TCHAR pszPrefix[80]; sc = StringCchPrintf(pszPrefix, countof(pszPrefix), _T("(0x%08X) ItemPrePaint: "), this); if (sc) sc.TraceAndClear(); // Truncation is okay, so ignore return. LOGFONT lf; HFONT hFont; hFont = GetFontFromDC (pnmcd->hdc); GetObject (hFont, sizeof (lf), &lf); Trace (tagFontlink, _T("%sdefault font = (face=%s, weight=%d)"), pszPrefix, lf.lfFaceName, lf.lfWeight); /* * compute all of the fonts needed for this; * if we couldn't, do the default thing */ Trace (tagFontlink, _T("%s text = \"%s\""), pszPrefix, W2CT (GetItemText(pnmcd).data())); #endif CRichText rt (pnmcd->hdc, GetItemText (pnmcd)); if (!ComposeRichText (rt)) { Trace (tagFontlink, _T("%s unable to determine font, using default"), pszPrefix); return (CDRF_DODEFAULT); } /* * if the default font in the DC is sufficient, do the default thing */ if (rt.IsDefaultFontSufficient ()) { Trace (tagFontlink, _T("%s default font is sufficient"), pszPrefix); return (CDRF_DODEFAULT); } /* * if the default font isn't sufficient, but there's a single * font that is, select it into the DC and let the control draw * the text */ if (rt.IsSingleFontSufficient ()) { #ifdef DBG hFont = rt.GetSufficientFont(); GetObject (hFont, sizeof (lf), &lf); Trace (tagFontlink, _T("%s using single font = (face=%s, weight=%d)"), pszPrefix, lf.lfFaceName, lf.lfWeight); #endif SelectObject (pnmcd->hdc, rt.GetSufficientFont()); return (CDRF_NEWFONT); } /* * TODO: handle drawing the icon and indented text */ Trace (tagFontlink, _T("%s (punting...)"), pszPrefix); return (CDRF_DODEFAULT); /* * if we get here, two or more fonts are required to draw the * text; draw it ourselves, and tell the control not to do anything */ rt.Draw (&pnmcd->rc, GetDrawTextFlags()); return (CDRF_SKIPDEFAULT); } /*+-------------------------------------------------------------------------* * CFontLinker::ComposeRichText * * Computes all of the fonts required to draw a given Unicode string *--------------------------------------------------------------------------*/ bool CFontLinker::ComposeRichText (CRichText& rt) { /* * get the code pages for the given DC's font */ DWORD dwDefaultFontCodePages; if (!GetFontCodePages (rt.m_hdc, rt.m_hDefaultFont, dwDefaultFontCodePages)) return (false); IMLangFontLink* pFontLink = GetFontLink(); if (pFontLink == NULL) return (false); const LPCWSTR pszText = rt.m_strText.data(); const int cchText = rt.m_strText.length(); int cchDone = 0; DWORD dwPriorityCodePages = NULL; /* * build up the collection of TextSegmentFontInfos for the text */ while (cchDone < cchText) { TextSegmentFontInfo tsfi; DWORD dwTextCodePages; /* * find out which code pages support the next segment of text */ if (FAILED(pFontLink->GetStrCodePages (pszText + cchDone, cchText - cchDone, dwPriorityCodePages, &dwTextCodePages, &tsfi.cch)) ) { rt.m_TextSegments.clear(); return (false); } /* * if the default font can render the text, things are easy */ if (dwDefaultFontCodePages & dwTextCodePages) tsfi.hFont = rt.m_hDefaultFont; /* * otherwise, ask IFontLink for the font to use */ else { /* * get the font */ if (FAILED (pFontLink->MapFont (rt.m_hdc, dwTextCodePages, rt.m_hDefaultFont, &tsfi.hFont))) { rt.m_TextSegments.clear(); return (false); } /* * add this font to the set of fonts to release when we're done */ std::pair rc = m_FontsToRelease.insert (tsfi.hFont); /* * if it was already there, release it now to keep * the ref counts right */ if (!rc.second) pFontLink->ReleaseFont (tsfi.hFont); } rt.m_TextSegments.push_back (tsfi); cchDone += tsfi.cch; } return (true); } /*+-------------------------------------------------------------------------* * CFontLinker::GetMultiLang * * *--------------------------------------------------------------------------*/ IMultiLanguage* CFontLinker::GetMultiLang () { if (m_spMultiLang == NULL) m_spMultiLang.CreateInstance (CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER); return (m_spMultiLang); } /*+-------------------------------------------------------------------------* * CFontLinker::GetFontLink * * *--------------------------------------------------------------------------*/ IMLangFontLink* CFontLinker::GetFontLink () { if (m_spFontLink == NULL) m_spFontLink = GetMultiLang (); return (m_spFontLink); } /*+-------------------------------------------------------------------------* * CFontLinker::GetFontCodePages * * Returns a bit mask representing the code pages supported by the font. *--------------------------------------------------------------------------*/ bool CFontLinker::GetFontCodePages ( HDC hdc, HFONT hFont, DWORD& dwFontCodePages) { /* * check the code page cache to see if we've * asked MLang about this font before */ FontToCodePagesMap::const_iterator itCodePages = m_CodePages.find (hFont); if (itCodePages != m_CodePages.end()) { dwFontCodePages = itCodePages->second; return (true); } /* * this font isn't in our code page cache yet; * ask MLang for the code pages */ IMLangFontLink* pFontLink = GetFontLink(); if (pFontLink == NULL) return (false); if (FAILED (pFontLink->GetFontCodePages (hdc, hFont, &dwFontCodePages))) return (false); /* * put the code pages in the cache */ m_CodePages[hFont] = dwFontCodePages; return (true); } /*+-------------------------------------------------------------------------* * CRichText::Draw * * *--------------------------------------------------------------------------*/ bool CRichText::Draw ( LPCRECT rect, /* i:rect to draw in */ UINT uFormat, /* i:DrawText format flags */ LPRECT prectRemaining /*=NULL*/) /* o:space remaining after drawing */ const { HFONT hOriginalFont = GetFontFromDC (m_hdc); CRect rectDraw = rect; LPCWSTR pszDraw = m_strText.data(); TextSegmentFontInfoCollection::const_iterator it = m_TextSegments.begin(); /* * draw each segment */ while (it != m_TextSegments.end()) { /* * select the font for this segment */ SelectObject (m_hdc, it->hFont); /* * measure the width of this segment */ CRect rectMeasure = rectDraw; DrawTextW (m_hdc, pszDraw, it->cch, rectMeasure, uFormat | DT_CALCRECT); /* * draw this segment */ DrawTextW (m_hdc, pszDraw, it->cch, rectDraw, uFormat); /* * set up for the next segment */ pszDraw += it->cch; rectDraw.left = rectMeasure.right; ++it; /* * if we've run out of rect to draw in, short out */ if (rectDraw.IsRectEmpty ()) break; } /* * if the caller wants it, return the remaining rectangle after drawing */ if (prectRemaining != NULL) *prectRemaining = rectDraw; /* * re-select the original font */ SelectObject (m_hdc, hOriginalFont); return (true); }