SourceCode/PROMS/FONTS/Size Project/src/XMonoFontListCombo.cpp

536 lines
15 KiB
C++

// XMonoFontListCombo.cpp
//
// Author: Hans Dietrich
// hdietrich@gmail.com
//
// Description:
// XMonoFontListCombo.cpp implements CXMonoFontListCombo, a class used by
// CXMonoFontDialog to display font names.
//
// License:
// This software is released into the public domain. You are free to use
// it in any way you like, except that you may not sell this source code.
//
// This software is provided "as is" with no expressed or implied warranty.
// I accept no liability for any damage or loss of business that this
// software may cause.
//
///////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "XMonoFontListCombo.h"
#include "XMonoFontDialog.h"
#include "XFontSize.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#ifndef __noop
#if _MSC_VER < 1300
#define __noop ((void)0)
#endif
#endif
#undef TRACE
#define TRACE __noop
//=============================================================================
// if you want to see the TRACE output, uncomment this line:
//#include "XTrace.h"
//=============================================================================
#pragma warning(disable : 4996) // disable bogus deprecation warning
#define GLYPH_SIZE 12
#define TIMER_INIT 1
static int CALLBACK EnumFontFamExProc(const ENUMLOGFONTEX *lpelfe,
const NEWTEXTMETRICEX *lpntme,
DWORD FontType,
LPARAM lParam);
//=============================================================================
BEGIN_MESSAGE_MAP(CXMonoFontListCombo, CComboBox)
//=============================================================================
//{{AFX_MSG_MAP(CXMonoFontListCombo)
ON_WM_TIMER()
ON_WM_CTLCOLOR()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//=============================================================================
CXMonoFontListCombo::CXMonoFontListCombo()
//=============================================================================
{
m_bShowMonospacedAsBold = TRUE;
m_pDC = 0;
m_dwFontFilter = 0;
memset(&m_lfInitial, 0, sizeof(LOGFONT));
m_lfInitial.lfCharSet = DEFAULT_CHARSET;
_tcscpy(m_lfInitial.lfFaceName, _T("Courier"));
m_lfInitial.lfHeight = FontSize.GetFontHeight(8);
memcpy(&m_lfCurrent, &m_lfInitial, sizeof(LOGFONT));
}
//=============================================================================
CXMonoFontListCombo::~CXMonoFontListCombo()
//=============================================================================
{
if (m_BoldFont.GetSafeHandle())
m_BoldFont.DeleteObject();
m_FontType.DeleteImageList();
}
//=============================================================================
void CXMonoFontListCombo::PreSubclassWindow()
//=============================================================================
{
TRACE(_T("in CXMonoFontListCombo::PreSubclassWindow\n"));
TRACE(_T("m_dwFontFilter=%X\n"), m_dwFontFilter);
CreateFontTypeImages();
CFont *pFont = GetFont();
if (!pFont)
pFont = GetParent()->GetFont();
if (pFont)
{
LOGFONT lf;
pFont->GetLogFont(&lf);
lf.lfWeight = FW_BOLD;
m_BoldFont.CreateFontIndirect(&lf);
}
// enumerate fonts, fill combo
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
lf.lfCharSet = DEFAULT_CHARSET;
m_pDC = GetDC(); // use one dc for enumeration
if (m_pDC)
{
int nSavedDC = m_pDC->SaveDC();
EnumFontFamiliesEx(m_pDC->m_hDC,
&lf,
(FONTENUMPROC)EnumFontFamExProc,
(LPARAM)this,
0);
TEXTMETRIC tm;
m_pDC->GetTextMetrics(&tm);
m_pDC->RestoreDC(nSavedDC);
ReleaseDC(m_pDC);
// set height of edit box and combo list entries
int h = tm.tmHeight - tm.tmInternalLeading;
SetItemHeight(0, h);
SetItemHeight(-1, h);
}
m_pDC = 0;
// select initial font
int index = FindStringExact(-1, m_lfInitial.lfFaceName);
if (index == CB_ERR)
{
if (GetCount() > 0)
index = 0;
}
m_CurIndex = index;
if (index >= 0)
{
SetCurSel(index);
SetTimer(TIMER_INIT, 50, 0);
}
TRACE(_T("exiting CXMonoFontListCombo::PreSubclassWindow\n"));
CComboBox::PreSubclassWindow();
}
//=============================================================================
void CXMonoFontListCombo::CreateFontTypeImages()
//=============================================================================
{
ASSERT(m_FontType.GetSafeHandle() == 0);
// We load the font type images from resource 38 in COMDLG32.DLL.
// This bitmap is 100 x 24, and has 2 rows of 5 images
HMODULE hModule = ::LoadLibraryEx(_T("COMDLG32.DLL"), NULL,
DONT_RESOLVE_DLL_REFERENCES);
ASSERT(hModule);
if (!hModule)
return;
HBITMAP hBmp = (HBITMAP)::LoadImage(hModule, MAKEINTRESOURCE(38),
IMAGE_BITMAP, 100, 24, LR_DEFAULTCOLOR);
::FreeLibrary(hModule);
ASSERT(hBmp);
if (!hBmp)
return;
CClientDC dcClient(this);
// We create one image list for the selected and non-selected
// glyphs. The images are:
// 0 - TrueType non-selected
// 1 - OpenType non-selected
// 2 - TrueType selected
// 3 - OpenType selected
if (m_FontType.Create(GLYPH_SIZE, GLYPH_SIZE, ILC_COLOR32 | ILC_MASK, 4, 1))
{
CDC dcMem;
dcMem.CreateCompatibleDC(&dcClient);
CBitmap *pBmp = CBitmap::FromHandle(hBmp);
CBitmap *pOldBitmap = dcMem.SelectObject(pBmp);
// dcMem now contains the 100 x 24 bitmap (2 rows) from COMDLG32.DLL.
// The individual images are at 0, 20, 40, 60, and 80.
// We want the images at 0 and 40 in each row.
CDC dcBitmap1;
dcBitmap1.CreateCompatibleDC(&dcClient);
CBitmap bmp1;
bmp1.CreateCompatibleBitmap(&dcClient, 2*GLYPH_SIZE, GLYPH_SIZE);
// extract non-selected images from top row
CBitmap *pOldBitmap1 = dcBitmap1.SelectObject(&bmp1);
dcBitmap1.FillSolidRect(0, 0, 2*GLYPH_SIZE, GLYPH_SIZE, GetSysColor(COLOR_WINDOW));
// we want 1st (TrueType) and 3rd (OpenType) images
dcBitmap1.BitBlt(0, 0, GLYPH_SIZE, GLYPH_SIZE, &dcMem, 3, 0, SRCCOPY);
dcBitmap1.BitBlt(GLYPH_SIZE, 0, GLYPH_SIZE, GLYPH_SIZE, &dcMem, 43, 0, SRCCOPY);
dcBitmap1.SelectObject(pOldBitmap1);
m_FontType.Add(&bmp1, RGB(0,0,255)); // different mask color for each row
// extract selected images from 2nd row
dcBitmap1.SelectObject(&bmp1);
dcBitmap1.FillSolidRect(0, 0, 2*GLYPH_SIZE, GLYPH_SIZE, GetSysColor(COLOR_WINDOW));
// we want 1st (TrueType) and 3rd (OpenType) images
dcBitmap1.BitBlt(0, 0, GLYPH_SIZE, GLYPH_SIZE, &dcMem, 3, GLYPH_SIZE, SRCCOPY);
dcBitmap1.BitBlt(GLYPH_SIZE, 0, GLYPH_SIZE, GLYPH_SIZE, &dcMem, 43, GLYPH_SIZE, SRCCOPY);
dcBitmap1.SelectObject(pOldBitmap1);
m_FontType.Add(&bmp1, RGB(255,0,255)); // different mask color for each row
dcBitmap1.DeleteDC();
bmp1.DeleteObject();
dcMem.SelectObject(pOldBitmap);
dcMem.DeleteDC();
}
::DeleteObject(hBmp);
}
//=============================================================================
CXMonoFontListCombo& CXMonoFontListCombo::SetFont(LOGFONT& lf)
//=============================================================================
{
memcpy(&m_lfInitial, &lf, sizeof(LOGFONT));
memcpy(&m_lfCurrent, &lf, sizeof(LOGFONT));
return *this;
}
//=============================================================================
void CXMonoFontListCombo::DrawItem(LPDRAWITEMSTRUCT lpDIS)
//=============================================================================
{
if (lpDIS->itemID == -1)
return;
ASSERT(lpDIS->CtlType == ODT_COMBOBOX);
CRect rect = lpDIS->rcItem;
int nSavedDC = ::SaveDC(lpDIS->hDC);
CDC dc;
dc.Attach(lpDIS->hDC);
if (lpDIS->itemState & ODS_FOCUS)
dc.DrawFocusRect(&rect);
COLORREF crText = ::GetSysColor(COLOR_WINDOWTEXT);
COLORREF crBackground = ::GetSysColor(COLOR_WINDOW);
if (lpDIS->itemState & ODS_SELECTED)
{
crText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
crBackground = ::GetSysColor(COLOR_HIGHLIGHT);
}
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(crText);
dc.FillSolidRect(&rect, crBackground);
CString strFontName = _T("");
GetLBText(lpDIS->itemID, strFontName);
// draw the font type glyph
DWORD dwData = GetItemData(lpDIS->itemID);
//TRACE(_T("dwData=0x%X for %s\n"), dwData, strFontName);
CPoint point(rect.left+3, rect.top);
int nImage = -1;
if (dwData & XFONT_TRUETYPE)
nImage = 0;
else if (dwData & XFONT_OPENTYPE)
nImage = 1;
if (nImage != -1)
{
if (lpDIS->itemState & ODS_SELECTED)
nImage += 2;
if (m_FontType.GetSafeHandle())
m_FontType.Draw(&dc, nImage, point, ILD_TRANSPARENT);
}
if ((dwData & XFONT_MONOSPACED) && m_bShowMonospacedAsBold)
{
if (m_BoldFont.GetSafeHandle())
dc.SelectObject(&m_BoldFont);
}
rect.left += GLYPH_SIZE + 8;
// draw the text
dc.TextOut(rect.left, rect.top, strFontName);
dc.Detach();
::RestoreDC(lpDIS->hDC, nSavedDC);
}
//=============================================================================
// This function is the enumeration callback for font names.
// Its purpose is to fill the font combo with non-duplicate names,
// and screen out unwanted fonts based on font filter.
int CALLBACK EnumFontFamExProc(const ENUMLOGFONTEX *lpelfe,
const NEWTEXTMETRICEX *lpntme,
DWORD FontType,
LPARAM lParam)
//=============================================================================
{
CXMonoFontListCombo *pCombo = (CXMonoFontListCombo *) lParam;
ASSERT(lpelfe);
ASSERT(lpntme);
ASSERT(pCombo);
if (!lpelfe || !lpntme || !pCombo)
return FALSE; // something seriously wrong
if (pCombo->FindStringExact(-1, lpelfe->elfLogFont.lfFaceName) != CB_ERR)
{
// already in combo
return TRUE; // continue enumeration
}
// save font attributes - this will be stored in combobox item data
DWORD dwData = 0;
UINT fonttype = lpntme->ntmTm.tmPitchAndFamily & 0x6;
if (fonttype == 0)
dwData |= XFONT_RASTER;
if (fonttype == 2)
dwData |= XFONT_VECTOR;
if (lpntme->ntmTm.tmCharSet == OEM_CHARSET)
dwData |= XFONT_OEM;
if (lpntme->ntmTm.tmCharSet == SYMBOL_CHARSET)
dwData |= XFONT_SYMBOL;
if (lpntme->ntmTm.tmWeight > 600)
dwData |= XFONT_BOLD;
if (lpntme->ntmTm.tmItalic)
dwData |= XFONT_ITALIC;
DWORD weight = lpntme->ntmTm.tmWeight;
weight = weight << 16;
dwData |= weight;
UINT family = lpntme->ntmTm.tmPitchAndFamily & 0xF0;
dwData |= family;
// check if monospaced
if (pCombo->m_pDC)
{
BOOL bMonoSpaced = FALSE;
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
_tcsncpy(lf.lfFaceName, lpelfe->elfLogFont.lfFaceName,
sizeof(lf.lfFaceName)/sizeof(TCHAR)-1);
lf.lfFaceName[sizeof(lf.lfFaceName)/sizeof(TCHAR)-1] = 0;
lf.lfWeight = FW_NORMAL;
lf.lfCharSet = DEFAULT_CHARSET;
HFONT hFont = ::CreateFontIndirect(&lf);
ASSERT(hFont);
if (hFont)
{
// font is monospaced if
// (1) width of W == width of !
// (2) it is not a symbol font
HFONT hFontOld = (HFONT)::SelectObject(pCombo->m_pDC->m_hDC, hFont);
TEXTMETRIC tm;
::GetTextMetrics(pCombo->m_pDC->m_hDC, &tm);
SIZE sizeChar1;
::GetTextExtentPoint32(pCombo->m_pDC->m_hDC, _T("W"), 1, &sizeChar1);
SIZE sizeChar2;
::GetTextExtentPoint32(pCombo->m_pDC->m_hDC, _T("!"), 1, &sizeChar2);
bMonoSpaced = sizeChar1.cx == sizeChar2.cx;
if (tm.tmCharSet == SYMBOL_CHARSET)
bMonoSpaced = FALSE;
if (hFontOld)
::SelectObject(pCombo->m_pDC->m_hDC, hFontOld);
::DeleteObject(hFont);
}
if (bMonoSpaced)
dwData |= XFONT_MONOSPACED;
}
BOOL bOkToAdd = TRUE;
TRACE(_T("m_dwFontFilter=%X\n"), pCombo->m_dwFontFilter);
if (bOkToAdd && ((pCombo->m_dwFontFilter & XFONT_SHOW_SYMBOL) == 0))
{
// don't show symbol fonts
if (dwData & XFONT_SYMBOL)
bOkToAdd = FALSE;
}
if (bOkToAdd && ((pCombo->m_dwFontFilter & XFONT_SHOW_ITALIC) == 0))
{
// don't show italic fonts
if (dwData & XFONT_ITALIC)
bOkToAdd = FALSE;
}
if (bOkToAdd && ((pCombo->m_dwFontFilter & XFONT_SHOW_BOLD) == 0))
{
// don't show bold fonts
if (dwData & XFONT_BOLD)
bOkToAdd = FALSE;
}
if (bOkToAdd && ((pCombo->m_dwFontFilter & XFONT_SHOW_MONOSPACED) == 0))
{
// don't show monospaced fonts
if (dwData & XFONT_MONOSPACED)
bOkToAdd = FALSE;
}
DWORD dwSpecial = XFONT_BOLD|XFONT_ITALIC|XFONT_SYMBOL|XFONT_MONOSPACED;
if (bOkToAdd && ((pCombo->m_dwFontFilter & XFONT_SHOW_NORMAL) == 0))
{
// don't show normal fonts
if ((dwData & dwSpecial) == 0)
bOkToAdd = FALSE;
}
if (bOkToAdd)
{
// According to docs, "The function uses the NEWTEXTMETRICEX structure
// for TrueType fonts; and the TEXTMETRIC structure for other fonts."
// In practice, it seems that a NEWTEXTMETRICEX struct is always returned,
// but just to be safe we prepare for an exception.
__try
{
if ((lpntme->ntmTm.ntmFlags & NTM_TT_OPENTYPE) ||
(lpntme->ntmTm.ntmFlags & NTM_PS_OPENTYPE))
{
dwData |= XFONT_OPENTYPE;
}
else if (FontType & TRUETYPE_FONTTYPE)
{
dwData |= XFONT_TRUETYPE;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
TRACE(_T("ERROR: exception in EnumFontFamExProc()\n"));
}
int index = pCombo->AddString(lpelfe->elfLogFont.lfFaceName);
ASSERT(index >= 0);
if (index >= 0)
{
VERIFY(pCombo->SetItemData(index, dwData) != CB_ERR);
}
TRACE(_T("_____ added %s [%s] FontType=0x%X ntmFlags=0x%X tmCharSet=%d tmPitchAndFamily=0x%X dwData=0x%X \n"),
lpelfe->elfLogFont.lfFaceName,
lpelfe->elfFullName,
FontType,
lpntme->ntmTm.ntmFlags,
lpntme->ntmTm.tmCharSet,
lpntme->ntmTm.tmPitchAndFamily,
dwData);
}
return TRUE;
}
//=============================================================================
void CXMonoFontListCombo::OnTimer(UINT nIDEvent)
//=============================================================================
{
KillTimer(nIDEvent);
if (nIDEvent == TIMER_INIT)
{
// send initial selection message
GetParent()->SendMessage(WM_COMMAND,
MAKEWPARAM(GetDlgCtrlID(), CBN_SELCHANGE),
(LPARAM)m_hWnd);
if (m_CurIndex >= 0)
{
DWORD dwData = GetItemData(m_CurIndex);
// set bold typeface in edit box
SetBold(m_bShowMonospacedAsBold && (dwData & XFONT_MONOSPACED));
}
}
CComboBox::OnTimer(nIDEvent);
}
//=============================================================================
HBRUSH CXMonoFontListCombo::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
//=============================================================================
{
if (nCtlColor == CTLCOLOR_EDIT)
{
if (m_edit.GetSafeHwnd() == NULL)
{
// subclass edit box
m_edit.SubclassWindow(pWnd->GetSafeHwnd());
m_edit.SetReadOnly(TRUE);
}
}
return CComboBox::OnCtlColor(pDC, pWnd, nCtlColor);
}
//=============================================================================
LRESULT CXMonoFontListCombo::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
//=============================================================================
{
if (message == WM_CHAR)
{
// try to match first character of font name
TCHAR s[2] = { 0 };
s[0] = (TCHAR) wParam;
int index = FindString(-1, s);
if (index >= 0)
{
SetCurSel(index);
// tell parent that selection has changed
GetParent()->SendMessage(WM_COMMAND,
MAKEWPARAM(GetDlgCtrlID(), CBN_SELCHANGE), (LPARAM)m_hWnd);
}
return TRUE;
}
return CComboBox::WindowProc(message, wParam, lParam);
}