|
|
/*--------------------------------------------------------------------------*
* * 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<FontSet::iterator, bool> 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); }
|