536 lines
15 KiB
C++
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);
|
|
}
|