// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"

#ifdef AFXCTL_PAGE_SEG
#pragma code_seg(AFXCTL_PAGE_SEG)
#endif

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define new DEBUG_NEW

/////////////////////////////////////////////////////////////////////////////
// CFontPropPage implementation

BEGIN_MESSAGE_MAP(CFontPropPage, CStockPropPage)
	//{{AFX_MSG_MAP(CFontPropPage)
	ON_WM_PAINT()
	ON_CBN_SELCHANGE(AFX_IDC_FONTPROP, OnSelchangeFontprop)
	ON_CBN_EDITUPDATE(AFX_IDC_FONTNAMES, OnEditupdateFontnames)
	ON_CBN_EDITUPDATE(AFX_IDC_FONTSIZES, OnEditupdateFontsizes)
	ON_CBN_SELCHANGE(AFX_IDC_FONTNAMES, OnSelchangeFontnames)
	ON_CBN_SELCHANGE(AFX_IDC_FONTSIZES, OnSelchangeFontsizes)
	ON_CBN_SELCHANGE(AFX_IDC_FONTSTYLES, OnSelchangeFontstyles)
	ON_CBN_EDITCHANGE(AFX_IDC_FONTSTYLES, OnEditchangeFontstyles)
	ON_BN_CLICKED(AFX_IDC_STRIKEOUT, OnStrikeout)
	ON_BN_CLICKED(AFX_IDC_UNDERLINE, OnUnderline)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CFontPropPage::CFontPropPage() :
	CStockPropPage(IDD, AFX_IDS_FONT_PPG_CAPTION)
{
	//{{AFX_DATA_INIT(CFontPropPage)
	//}}AFX_DATA_INIT
}

BOOL _AfxStringFromCy(CString& str, CY& cy)
{
	VARIANTARG varCy;
	VARIANTARG varBstr;
	AfxVariantInit(&varCy);
	AfxVariantInit(&varBstr);
	V_VT(&varCy) = VT_CY;
	V_CY(&varCy) = cy;
	if (FAILED(VariantChangeType(&varBstr, &varCy, 0, VT_BSTR)))
	{
		VariantClear(&varCy);
		VariantClear(&varBstr);
		return FALSE;
	}
	str = V_BSTR(&varBstr);
	VariantClear(&varCy);
	VariantClear(&varBstr);
	return TRUE;
}

BOOL _AfxCyFromString(CY& cy, LPCTSTR psz)
{
	USES_CONVERSION;

	VARIANTARG varBstr;
	VARIANTARG varCy;
	AfxVariantInit(&varBstr);
	AfxVariantInit(&varCy);
	V_VT(&varBstr) = VT_BSTR;
	V_BSTR(&varBstr) = SysAllocString(T2COLE(psz));
	if (FAILED(VariantChangeType(&varCy, &varBstr, 0, VT_CY)))
	{
		VariantClear(&varBstr);
		VariantClear(&varCy);
		return FALSE;
	}
	cy = V_CY(&varCy);
	VariantClear(&varBstr);
	VariantClear(&varCy);
	return TRUE;
}

#define DSx     0x00660046L
#define DSna    0x00220326L

void _AfxDrawMaskedBitmap(CDC* pDC, CBitmap* pbmp, CBitmap* pbmpMask,
	int x, int y, int cx, int cy)
{
	COLORREF oldBkColor = pDC->SetBkColor(RGB(255, 255, 255));
	COLORREF oldTextColor = pDC->SetTextColor(RGB(0, 0, 0));

	CDC dcCompat;
	dcCompat.CreateCompatibleDC(pDC);
	CBitmap* pbmpSave = dcCompat.SelectObject(pbmp);
	pDC->BitBlt(x, y, cx, cy, &dcCompat, 0, 0, DSx);
	dcCompat.SelectObject(pbmpMask);
	pDC->BitBlt(x, y, cx, cy, &dcCompat, 0, 0, DSna);
	dcCompat.SelectObject(pbmp);
	pDC->BitBlt(x, y, cx, cy, &dcCompat, 0, 0, DSx);
	dcCompat.SelectObject(pbmpSave);

	pDC->SetBkColor(oldBkColor);
	pDC->SetTextColor(oldTextColor);
}

void _AfxInitMaskFromBitmap(CBitmap* pbmp, CBitmap* pbmpMask)
{
	BITMAP bmp;
	pbmp->GetObject(sizeof (BITMAP), &bmp);
	pbmpMask->CreateBitmap(bmp.bmWidth, bmp.bmHeight, 1, 1, NULL);

	CDC dcDst;
	dcDst.CreateCompatibleDC(NULL);
	CDC dcSrc;
	dcSrc.CreateCompatibleDC(NULL);
	CBitmap* pbmpSaveDst = dcDst.SelectObject(pbmpMask);
	CBitmap* pbmpSaveSrc = dcSrc.SelectObject(pbmp);

	COLORREF oldBkColor = dcSrc.SetBkColor(dcSrc.GetPixel(0, 0));
	dcDst.BitBlt(0, 0, bmp.bmWidth, bmp.bmHeight, &dcSrc, 0, 0, NOTSRCCOPY);
	dcSrc.SetBkColor(oldBkColor);
}

void CFontPropPage::DoDataExchange(CDataExchange* pDX)
{
	//{{AFX_DATA_MAP(CFontPropPage)
	DDX_Control(pDX, AFX_IDC_FONTPROP, m_FontProp);
	DDX_Control(pDX, AFX_IDC_SAMPLEBOX, m_SampleBox);
	DDX_Control(pDX, AFX_IDC_FONTSTYLES, m_FontStyles);
	DDX_Control(pDX, AFX_IDC_FONTSIZES, m_FontSizes);
	DDX_Control(pDX, AFX_IDC_FONTNAMES, m_FontNames);
	//}}AFX_DATA_MAP

	if (pDX->m_bSaveAndValidate)
	{
		FONTOBJECT fobj;
		fobj.strName = m_FontNames.GetCurrentName();

		if (fobj.strName.IsEmpty())
			return;

		m_FontSizes.GetPointSize(fobj.cySize);

		if (m_nCurrentStyle & NTM_REGULAR)
			fobj.sWeight = FW_REGULAR;
		if (m_nCurrentStyle & NTM_BOLD)
		{
			fobj.sWeight = FW_BOLD;
			fobj.bBold = TRUE;
		}
		else
			fobj.bBold = FALSE;
		if (m_nCurrentStyle & NTM_ITALIC)
			fobj.bItalic = TRUE;
		else
			fobj.bItalic = FALSE;
		fobj.bUnderline = m_bUnderline;
		fobj.bStrikethrough = m_bStrikeOut;

		SetFontProps(pDX, fobj, m_strPropName);
	}

	FONTOBJECT fobj;
	MERGEOBJECT mobj;
	if (!m_strPropName.IsEmpty() && GetFontProps(pDX, &fobj, m_strPropName, &mobj))
	{
		if (fobj.bBold && fobj.bItalic)
			m_nCurrentStyle = NTM_BOLD | NTM_ITALIC;
		else if (fobj.bBold)
			m_nCurrentStyle = NTM_BOLD;
		else if (fobj.bItalic)
			m_nCurrentStyle = NTM_ITALIC;
		else
			m_nCurrentStyle = NTM_REGULAR;
		m_nActualStyle = m_nCurrentStyle;
		m_bUnderline = fobj.bUnderline;
		m_bStrikeOut = fobj.bStrikethrough;

		mobj.bNameOK = TRUE;
		mobj.bSizeOK = TRUE;
		mobj.bStyleOK = TRUE;
		mobj.bUnderlineOK = TRUE;
		mobj.bStrikethroughOK = TRUE;

		_AfxStringFromCy(m_strFontSize, fobj.cySize);
		SelectFontFromList(fobj.strName, &mobj);
	}
}

BOOL CFontPropPage::SetFontProps(CDataExchange* pDX, FONTOBJECT fobj, LPCTSTR pszPropName)
{
	USES_CONVERSION;

	BOOL bStatus = FALSE;
	COleDispatchDriver PropDispDriver;

	// Set the properties for all the objects
	ASSERT_KINDOF(COlePropertyPage, pDX->m_pDlgWnd);
	COlePropertyPage* propDialog = (COlePropertyPage*)(pDX->m_pDlgWnd);

	ULONG nObjects;
	LPDISPATCH* ppDisp = GetObjectArray(&nObjects);

	for (ULONG i = 0; i < nObjects; i++)
	{
		DISPID dwDispID;

		// Get the Dispatch ID for the property and if successful set the value
		LPCOLESTR lpOleStr = T2COLE(pszPropName);
		if (SUCCEEDED(ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr,
			1, m_lcid, &dwDispID)))
		{
			LPDISPATCH pFontDisp = NULL;

			// Get property
			PropDispDriver.AttachDispatch(ppDisp[i], FALSE);
			TRY
				PropDispDriver.GetProperty(dwDispID, VT_DISPATCH, &pFontDisp);
			END_TRY
			PropDispDriver.DetachDispatch();

			if (pFontDisp == NULL)
				continue;

			// Get font interface
			IFont * pFont;
			HRESULT hresult = pFontDisp->QueryInterface(IID_IFont, (void**)&pFont);
			if (hresult == S_OK)
			{
				// Set font characteristics
				if (propDialog->GetControlStatus(AFX_IDC_FONTNAMES))
				{
					BSTR bstrName = fobj.strName.AllocSysString();
					pFont->put_Name(bstrName);
					SysFreeString(bstrName);
				}
				if (propDialog->GetControlStatus(AFX_IDC_FONTSIZES))
					pFont->put_Size(fobj.cySize);
				if (propDialog->GetControlStatus(AFX_IDC_FONTSTYLES))
				{
					pFont->put_Bold(fobj.bBold);
					pFont->put_Italic(fobj.bItalic);
					pFont->put_Weight(fobj.sWeight);
				}
				if (propDialog->GetControlStatus(AFX_IDC_UNDERLINE))
					pFont->put_Underline(fobj.bUnderline);
				if (propDialog->GetControlStatus(AFX_IDC_STRIKEOUT))
					pFont->put_Strikethrough(fobj.bStrikethrough);

				// Release the font interface
				RELEASE(pFont);
				bStatus = TRUE;
			}

			// Release the font dispatch interface
			RELEASE(pFontDisp);
		}
	}
	return bStatus;
}

BOOL CFontPropPage::GetFontProps(CDataExchange* pDX, FONTOBJECT* pfobj, LPCTSTR pszPropName, MERGEOBJECT* pmobj)
{
	USES_CONVERSION;
	BOOL bStatus = FALSE;

	COleDispatchDriver PropDispDriver;

	// Set the properties for all the objects
	ASSERT_KINDOF(COlePropertyPage, pDX->m_pDlgWnd);
	COlePropertyPage* propDialog = (COlePropertyPage*)(pDX->m_pDlgWnd);

	ULONG nObjects;
	LPDISPATCH* ppDisp = GetObjectArray(&nObjects);

	for (ULONG i = 0; i < nObjects; i++)
	{
		DISPID dwDispID;

		// Get the Dispatch ID for the property and if successful get the value
		LPCOLESTR lpOleStr = T2COLE(pszPropName);
		if (SUCCEEDED(ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr,
			1, m_lcid, &dwDispID)))
		{
			LPDISPATCH pFontDisp;

			// Get property
			PropDispDriver.AttachDispatch(ppDisp[i], FALSE);
			PropDispDriver.GetProperty(dwDispID, VT_DISPATCH, &pFontDisp);
			PropDispDriver.DetachDispatch();

			if (pFontDisp == NULL)
				continue;

			// Get font interface
			IFont * pFont;
			HRESULT hresult = pFontDisp->QueryInterface(IID_IFont, (void**)&pFont);
			if (hresult == S_OK)
			{
				BOOL bTemp;

				// Set font characteristics
				OLECHAR *pszName;
				pFont->get_Name(&pszName);
				if (_tcscmp(OLE2CT(pszName), pfobj->strName) != 0 && i != 0)
					pmobj->bNameOK = FALSE;
				pfobj->strName = pszName;
				SysFreeString(pszName);

				CY cyTemp;
				pFont->get_Size(&cyTemp);
				if ((cyTemp.Lo != pfobj->cySize.Lo || cyTemp.Hi != pfobj->cySize.Hi) && i != 0)
					pmobj->bSizeOK = FALSE;
				pfobj->cySize = cyTemp;

				pFont->get_Bold(&bTemp);
				if (pfobj->bBold != bTemp && i != 0)
					pmobj->bStyleOK = FALSE;
				pfobj->bBold = bTemp;

				pFont->get_Italic(&bTemp);
				if (pfobj->bItalic != bTemp && i != 0)
					pmobj->bStyleOK = FALSE;
				pfobj->bItalic = bTemp;

				pFont->get_Underline(&bTemp);
				if (pfobj->bUnderline != bTemp && i != 0)
					pmobj->bUnderlineOK = FALSE;
				pfobj->bUnderline = bTemp;

				pFont->get_Strikethrough(&bTemp);
				if (pfobj->bStrikethrough != bTemp && i != 0)
					pmobj->bStrikethroughOK = FALSE;
				pfobj->bStrikethrough = bTemp;

				short sTemp;

				pFont->get_Weight(&sTemp);
				if (pfobj->sWeight != sTemp && i != 0)
					pmobj->bStyleOK = FALSE;
				pfobj->sWeight = sTemp;

				// Release the font interface
				RELEASE(pFont);
				bStatus = TRUE;
			}

			// Release font interface
			RELEASE(pFontDisp);
		}
	}
	return bStatus;
}

BOOL CFontPropPage::OnInitDialog()
{
	CStockPropPage::OnInitDialog();
	OnObjectsChanged();
	IgnoreApply(AFX_IDC_FONTPROP);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CFontPropPage::FillFacenameList()
{
	// Clear the list
	m_FontNames.ResetContent();

	// Create a DC to enumerate
	CClientDC dc(NULL);
	int rc = EnumFontFamilies(dc.GetSafeHdc(), (LPCTSTR) NULL, (FONTENUMPROC) CFontPropPage::EnumFontFamiliesCallBack, (LPARAM) this);

	// Select the first one
	if (m_FontNames.SetCurSel(0) != CB_ERR)
	{
		// Fill the size list
		FillSizeList();
	}
	else
	{
		m_FontNames.EnableWindow(FALSE);
		m_FontSizes.EnableWindow(FALSE);
		m_FontStyles.EnableWindow(FALSE);
		GetDlgItem(AFX_IDC_STRIKEOUT)->EnableWindow(FALSE);
		GetDlgItem(AFX_IDC_UNDERLINE)->EnableWindow(FALSE);
	}
}

int CALLBACK CFontPropPage::EnumFontFamiliesCallBack(ENUMLOGFONT *lpelf, NEWTEXTMETRIC *, int FontType, LPARAM lParam)
{
	CFontPropPage *pDlg = (CFontPropPage *)lParam;
	ASSERT(pDlg);
	pDlg->m_FontNames.AddFont(&lpelf->elfLogFont, FontType);
	return 1;
}

void CFontPropPage::FillSizeList()
{
	// Clear the size list
	m_FontSizes.ResetContent();
	m_FontStyles.ResetContent();
	m_nStyles = 0L;

	// Fill with "real" sizes
	CString strFaceName;
	m_FontNames.GetLBText(m_FontNames.GetCurSel(), strFaceName);
	CClientDC dc(NULL);
	int rc = EnumFontFamilies(dc.GetSafeHdc(), (LPCTSTR) strFaceName, (FONTENUMPROC) CFontPropPage::EnumFontFamiliesCallBack2, (LPARAM) this);

	// Check if we have a font that is either a vector or Truettype font
	if (m_FontNames.GetFontType() != RASTER_FONTTYPE)
	{
		// Fill with "common" sizes
		static int TTDefaults[] = { 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48 };
		for (int i = 0; i < _countof(TTDefaults); i++)
			m_FontSizes.AddSize(TTDefaults[i], 0);
	}

	// See what fonts are native
	BOOL    bRegular = (BOOL)(m_nStyles & NTM_REGULAR);
	BOOL    bBold = (BOOL)(m_nStyles & NTM_BOLD);
	BOOL    bItalic = (BOOL)(m_nStyles & NTM_ITALIC);
	BOOL    bBoldItalic = (BOOL)((m_nStyles & NTM_BOLD) &&
								 (m_nStyles & NTM_ITALIC));

	// Allow for "synthesized" italic && bold variants
	if (bRegular)
		bBold = bItalic = TRUE;
	if (bBold || bItalic)
		bBoldItalic = TRUE;

	// Fill the styles list box
	CString strStyle;

	int nEntry;
	if (bRegular)
	{
		strStyle.LoadString(AFX_IDS_REGULAR);
		nEntry = m_FontStyles.AddString(strStyle);
		m_FontStyles.SetItemData(nEntry, (DWORD)NTM_REGULAR);
	}
	if (bBold)
	{
		strStyle.LoadString(AFX_IDS_BOLD);
		nEntry = m_FontStyles.AddString(strStyle);
		m_FontStyles.SetItemData(nEntry, (DWORD)NTM_BOLD);
	}
	if (bItalic)
	{
		strStyle.LoadString(AFX_IDS_ITALIC);
		nEntry = m_FontStyles.AddString(strStyle);
		m_FontStyles.SetItemData(nEntry, (DWORD)NTM_ITALIC);
	}
	if (bBoldItalic)
	{
		strStyle.LoadString(AFX_IDS_BOLDITALIC);
		nEntry = m_FontStyles.AddString(strStyle);
		m_FontStyles.SetItemData(nEntry, (DWORD)NTM_ITALIC|NTM_BOLD);
	}

	// Set the point size
	if (m_FontSizes.FindString(-1, m_strFontSize) != CB_ERR)
	{
		nEntry = m_FontSizes.SelectString(-1, m_strFontSize);
		if (nEntry == CB_ERR)
			return;
	}
	else
	{
		// Point size is not in the list so just fill the edit box
		// and don't select anything from the list
		m_FontSizes.SetCurSel(-1);
		m_FontSizes.SetWindowText(m_strFontSize);
	}

	// Set the styles combo box selection
	BOOL bFound = FALSE;
	int nMaxEntries = m_FontStyles.GetCount();
	for (int nEntry3 = 0; nEntry3 < nMaxEntries; nEntry3++)
	{
		if (m_FontStyles.GetItemData(nEntry3) == m_nActualStyle)
		{
			m_FontStyles.SetCurSel(nEntry3);
			bFound = TRUE;
		}
	}

	if (!bFound)
	{
		m_FontStyles.SetCurSel(0);      // Set style to regular
		m_nCurrentStyle = NTM_REGULAR;
	}
	else
		m_nCurrentStyle = m_nActualStyle;

	// Redraw the sample
	UpdateSampleFont();
}

int CALLBACK CFontPropPage::EnumFontFamiliesCallBack2(
	ENUMLOGFONT* lpelf, NEWTEXTMETRIC* lpntm, int FontType, LPARAM lParam)
{
	CFontPropPage *pDlg = (CFontPropPage *)lParam;
	ASSERT(pDlg != NULL);

	if (FontType & TRUETYPE_FONTTYPE)
	{
		if (!(lpntm->ntmFlags & (NTM_BOLD | NTM_ITALIC)))
			pDlg->m_nStyles |= NTM_REGULAR;

		if (lpntm->ntmFlags & NTM_ITALIC)
			pDlg->m_nStyles |= NTM_ITALIC;

		if (lpntm->ntmFlags & NTM_BOLD)
			pDlg->m_nStyles |= NTM_BOLD;
	}
	else
	{
		if (FontType & RASTER_FONTTYPE)
		{
			int height = lpntm->tmHeight - lpntm->tmInternalLeading;
			pDlg->m_FontSizes.AddSize(MulDiv(height, 72, afxData.cyPixelsPerInch), height);
		}

		if (lpelf->elfLogFont.lfWeight >= FW_BOLD && lpelf->elfLogFont.lfItalic)
			pDlg->m_nStyles |= NTM_BOLD | NTM_ITALIC;
		else if (lpelf->elfLogFont.lfWeight >= FW_BOLD)
			pDlg->m_nStyles |= NTM_BOLD;
		else if (lpelf->elfLogFont.lfItalic)
			pDlg->m_nStyles |= NTM_ITALIC;
		else
			pDlg->m_nStyles |= NTM_REGULAR;
	}

	return 1;
}

#define DX_BITMAP        20
#define DY_BITMAP        12

/////////////////////////////////////////////////////////////////////////////
// CFontComboBox

CFontComboBox::CFontComboBox() : CComboBox()
{
	m_bmpTrueType.LoadBitmap(AFX_IDB_TRUETYPE);
	_AfxInitMaskFromBitmap(&m_bmpTrueType, &m_bmpMask);
}

CFontComboBox::~CFontComboBox()
{
}

int CFontComboBox::AddFont(LOGFONT *pLF, DWORD FontType)
{
	int nEntry;
	FONTITEM_PPG* pFontItem = NULL;

	// Font already in the combobox
	if (FindString(-1, (LPCTSTR) pLF->lfFaceName) != CB_ERR)
		return CB_ERR;

	// allocate some memory for the FONTITEM_PPG structure
	TRY
	{
		pFontItem = new FONTITEM_PPG;
	}
	CATCH( CMemoryException, e )
	{
		return CB_ERR;
	}
	END_CATCH

	ASSERT( pFontItem );
	pFontItem->lf = *pLF;
	pFontItem->dwFontType = FontType;

	nEntry = AddString( (LPCTSTR) pFontItem->lf.lfFaceName );

	if (nEntry == CB_ERR)
		delete pFontItem;
	else
		SetItemData( nEntry, (DWORD) pFontItem );

	return nEntry;
}

FONTITEM_PPG* CFontComboBox::GetFontItem(int sel)
{
	if (sel == -1)
		sel = GetCurSel();

	if (sel == -1)
	{
		CString str;

		GetWindowText( str );
		sel = FindString( -1, str );
		if (sel == CB_ERR)
			sel = 0;
	}

	ASSERT( GetItemData(sel) );
	return (FONTITEM_PPG*) GetItemData(sel);
}

LPLOGFONT CFontComboBox::GetLogFont(int sel)
{
	return &GetFontItem(sel)->lf;
}

DWORD CFontComboBox::GetFontType(int sel)
{
	return GetFontItem(sel)->dwFontType;
}

CString CFontComboBox::GetCurrentName()
{
	CString str;
	GetWindowText(str);
	return str;
}

void CFontComboBox::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
	ASSERT( lpDIS->CtlType == ODT_COMBOBOX );

	// make sure this is a *real* item
	if (lpDIS->itemID == -1)
		return;

	CDC* pDC = CDC::FromHandle(lpDIS->hDC);
	FONTITEM_PPG* pFI = (FONTITEM_PPG*)lpDIS->itemData;    // pointer to a FONTITEM storied in item data
	LOGFONT* pLF = &pFI->lf;
	COLORREF crBk, crText;
	TEXTMETRIC tm;
	int x, y;

	// Calculate the colors to use
	crBk = pDC->SetBkColor(
		GetSysColor(lpDIS->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW) );
	crText = pDC->SetTextColor(
		GetSysColor(lpDIS->itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT) );

	// Calculate the position of the text
	pDC->GetTextMetrics( &tm );
	x = LOWORD(GetDialogBaseUnits()) / 4;
	y = (lpDIS->rcItem.bottom + lpDIS->rcItem.top - tm.tmHeight) / 2;

	// Draw the text
	pDC->ExtTextOut(lpDIS->rcItem.left + DX_BITMAP + 2 * x, y, ETO_CLIPPED | ETO_OPAQUE,
		&lpDIS->rcItem,(LPCTSTR) pLF->lfFaceName,
		lstrlen((LPCTSTR) pLF->lfFaceName), NULL );

	// Put the colors back as they were
	pDC->SetTextColor( crText );
	pDC->SetBkColor( crBk );

	// Draw the TrueType bitmap
	if (pFI->dwFontType & TRUETYPE_FONTTYPE)
	{
		int dy;
		dy = ((lpDIS->rcItem.bottom - lpDIS->rcItem.top) - DY_BITMAP) / 2;
		_AfxDrawMaskedBitmap(pDC, &m_bmpTrueType, &m_bmpMask,
			x, lpDIS->rcItem.top + dy, DX_BITMAP, DY_BITMAP);
	}

	// Draw the focus rect if needed
	if (lpDIS->itemState & ODS_FOCUS)
		pDC->DrawFocusRect( &lpDIS->rcItem );
}

void CFontComboBox::DeleteItem(LPDELETEITEMSTRUCT lpDIS)
{
	FONTITEM_PPG* pFI;

	if (lpDIS->itemID == -1)
		return;

	ASSERT( lpDIS->CtlType == ODT_COMBOBOX );

	pFI = GetFontItem(lpDIS->itemID);

	// Free the FONTITEM_PPG created in CFontComboBox::AddFont()
	ASSERT(pFI);
	delete pFI;
}

/////////////////////////////////////////////////////////////////////////////
// CSizeComboBox

int CSizeComboBox::AddSize(int PointSize, LONG lfHeight)
{
	if (lfHeight == 0)
		lfHeight = MulDiv( -afxData.cyPixelsPerInch, PointSize, 72 );

	CString str;
	wsprintf(str.GetBuffer(16), _T("%d"), PointSize);
	str.ReleaseBuffer();

	int nMaxEntries = GetCount();
	int nEntry;

	// we use positive height values for non-truetype fonts, negitive for true type
	if (lfHeight > 0)
	{
		for (nEntry = 0; nEntry < nMaxEntries; nEntry++)
		{
			int iComp = (int)(lfHeight - GetHeight(nEntry));
			if (!iComp)
				return CB_ERR;
			if (iComp < 0)
				break;
		}
	}
	else
	{
		for (nEntry = 0; nEntry < nMaxEntries; nEntry++)
		{
			int iComp = (int)(lfHeight - GetHeight(nEntry));
			if (!iComp)
				return CB_ERR;
			if (iComp > 0)
				break;
		}
	}

	if (nEntry == nMaxEntries)
		nEntry = -1;
	nEntry = InsertString(nEntry, str);
	if (nEntry != CB_ERR)
		SetItemData(nEntry, (DWORD)lfHeight);

	return nEntry;
}

void CSizeComboBox::GetPointSize(CY& cy)
{
	TCHAR szText[20];
	GetWindowText(szText, 20);
	cy.Lo = 0;
	cy.Hi = 0;
	_AfxCyFromString(cy, szText);
}

LONG CSizeComboBox::GetHeight(int sel)
{
	if (sel == -1)
		sel = GetCurSel();

	if (sel == -1)
	{
		TCHAR szText[20];
		GetWindowText(szText, 20);
		sel = FindString( -1, szText);
		if (sel == CB_ERR)
		{
			CY cyTmp;
			cyTmp.Lo = 0;
			cyTmp.Hi = 0;
			_AfxCyFromString(cyTmp, szText);
			int PointSize = (int)((cyTmp.Lo + 5000) / 10000);
			if (PointSize != 0)
				return MulDiv(-afxData.cyPixelsPerInch, PointSize, 72);
			else
				sel = 0;
		}
	}

	return (LONG) GetItemData(sel);
}

void CSizeComboBox::UpdateLogFont( LPLOGFONT lpLF, int sel )
{
	ASSERT(lpLF);

	lpLF->lfHeight = (int)GetHeight(sel);
	lpLF->lfWidth = 0;
}

/////////////////////////////////////////////////////////////////////////////
// CFontPropPage message handlers

void CFontPropPage::OnEditupdateFontnames()
{
	// When the users entry matches an entry in the list, select it
	CString str;
	m_FontNames.GetWindowText(str);
	int nEntry = m_FontNames.FindStringExact(-1, str);
	if (nEntry != CB_ERR)
	{
		m_FontNames.SetCurSel(nEntry);
		m_FontNames.SetEditSel(-1, -1);

		// Re-fill the size list
		FillSizeList();
	}
}

void CFontPropPage::OnEditupdateFontsizes()
{
	// when the users entry matches an entry in the list, select it
	m_FontSizes.GetWindowText(m_strFontSize);
	int nEntry = m_FontSizes.FindStringExact(-1, m_strFontSize);
	if (nEntry != CB_ERR)
	{
		m_FontSizes.SetCurSel(nEntry);
		m_FontSizes.SetEditSel(-1, -1);

		// Update the sample text
		UpdateSampleFont();
	}
}

void CFontPropPage::OnSelchangeFontnames()
{
	FillSizeList();
}

void CFontPropPage::UpdateSampleFont()
{
	ASSERT(m_FontNames.GetFontItem());

	LOGFONT lf = *m_FontNames.GetLogFont();
	m_FontSizes.UpdateLogFont( &lf );

	// Handle styles
	if (m_nCurrentStyle & NTM_BOLD)
		lf.lfWeight = FW_BOLD;
	else
		lf.lfWeight = FW_REGULAR;
	if (m_nCurrentStyle & NTM_ITALIC)
		lf.lfItalic = TRUE;
	else
		lf.lfItalic = FALSE;

	lf.lfStrikeOut = (unsigned char)m_bStrikeOut;
	lf.lfUnderline = (unsigned char)m_bUnderline;

	SampleFont.DeleteObject();
	SampleFont.CreateFontIndirect( &lf );

	CRect rcSample;
	m_SampleBox.GetWindowRect( &rcSample );
	ScreenToClient( &rcSample );

	InvalidateRect( rcSample );
	UpdateWindow();
}

void CFontPropPage::OnPaint()
{
	CPaintDC dc(this);
	CRect rcText;
	CFont *oldFont;
	CSize TextExtent;
	COLORREF crText;
	TEXTMETRIC tm;
	int bkMode, len, x, y;
	CString strSample;

	strSample.LoadString(AFX_IDS_SAMPLETEXT);

	// If there is no sample font abort
	if (!SampleFont.GetSafeHandle())
		return;

	// Get the bounding box
	m_SampleBox.GetWindowRect( &rcText );
	ScreenToClient( &rcText );

	// Select the new font and colors into the dc
	oldFont = dc.SelectObject( &SampleFont );
	crText = dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
	bkMode = dc.SetBkMode(TRANSPARENT);

	// Calculate the position of the text
	dc.GetTextMetrics( &tm );

	len = strSample.GetLength();
	TextExtent = dc.GetTextExtent(strSample, len);
	TextExtent.cy = tm.tmAscent - tm.tmInternalLeading;

	if ((TextExtent.cx >= (rcText.right - rcText.left)) ||
			(TextExtent.cx <= 0))
		x = rcText.left;
	else
		x = rcText.left + ((rcText.right - rcText.left) - TextExtent.cx) / 2;

	y = min(rcText.bottom,
		rcText.bottom - ((rcText.bottom - rcText.top) - TextExtent.cy) / 2);

	// Draw it
	dc.ExtTextOut(x, y - (tm.tmAscent), ETO_CLIPPED, &rcText,
		strSample, len, NULL);

	// Put the DC back the way it was
	dc.SetBkMode(bkMode);
	dc.SetTextColor(crText);

	if (oldFont)
		dc.SelectObject(oldFont);
}

void CFontPropPage::OnSelchangeFontsizes()
{
	int nEntry = m_FontSizes.GetCurSel();
	if (nEntry != CB_ERR)
	{
		m_FontSizes.GetLBText(nEntry, m_strFontSize);
		UpdateSampleFont();
	}
}
void CFontPropPage::OnSelchangeFontstyles()
{
	int nEntry = m_FontStyles.GetCurSel();
	m_nCurrentStyle = m_FontStyles.GetItemData(nEntry);
	m_nActualStyle = m_nCurrentStyle;

	// Update the sample font
	UpdateSampleFont();
}

void CFontPropPage::OnEditchangeFontstyles()
{
	// when the users entry matches an entry in the list, select it
	CString str;
	m_FontStyles.GetWindowText(str);
	int nEntry = m_FontStyles.FindStringExact(-1, str);
	if (nEntry != CB_ERR)
	{
		m_FontStyles.SetCurSel(nEntry);
		m_FontStyles.SetEditSel(-1, -1);

		// Update the sample text
		m_nCurrentStyle = m_FontStyles.GetItemData(nEntry);
		m_nActualStyle = m_nCurrentStyle;
		UpdateSampleFont();
	}
}

void CFontPropPage::SelectFontFromList(CString strFaceName, MERGEOBJECT* pmobj)
{
	// Set the effects buttons
	CButton* pStrikeOut = (CButton*) GetDlgItem(AFX_IDC_STRIKEOUT);
	if (!pmobj->bStrikethroughOK)
		pStrikeOut->SetCheck(2);
	else if (m_bStrikeOut)
		pStrikeOut->SetCheck(1);
	else
		pStrikeOut->SetCheck(0);

	CButton* pUnderline = (CButton*) GetDlgItem(AFX_IDC_UNDERLINE);
	if (!pmobj->bUnderlineOK)
		pStrikeOut->SetCheck(2);
	else if (m_bUnderline)
		pUnderline->SetCheck(1);
	else
		pUnderline->SetCheck(0);

	// Set the font facename
	if (pmobj->bNameOK)
	{
		int nEntry1 = m_FontNames.SelectString(-1, strFaceName);
		if (nEntry1 == CB_ERR)
			return;
	}

	// Fill the size list appropriately
	FillSizeList();

	// Set the styles combo box selection
	BOOL bFound = FALSE;
	int nMaxEntries = m_FontStyles.GetCount();
	for (int nEntry3 = 0; nEntry3 < nMaxEntries; nEntry3++)
	{
		if (m_FontStyles.GetItemData(nEntry3) == m_nActualStyle)
		{
			m_FontStyles.SetCurSel(nEntry3);
			bFound = TRUE;
		}
	}

	if (pmobj->bSizeOK)
	{
		if (!bFound)
		{
			m_FontStyles.SetCurSel(0);      // Set style to regular
			m_nCurrentStyle = NTM_REGULAR;
		}
		else
			m_nCurrentStyle = m_nActualStyle;
	}

	UpdateSampleFont();
}

void CFontPropPage::OnStrikeout()
{
	CButton* pStrikeOut = (CButton*) GetDlgItem(AFX_IDC_STRIKEOUT);
	if (pStrikeOut->GetCheck() == 1)
		m_bStrikeOut = TRUE;
	else
		m_bStrikeOut = FALSE;

	UpdateSampleFont();
}

void CFontPropPage::OnUnderline()
{
	CButton* pUnderline = (CButton*) GetDlgItem(AFX_IDC_UNDERLINE);
	if (pUnderline->GetCheck() == 1)
		m_bUnderline = TRUE;
	else
		m_bUnderline = FALSE;

	UpdateSampleFont();
}

void CFontPropPage::OnSelchangeFontprop()
{
	OnSelchangePropname(m_FontProp);
}

BOOL CFontPropPage::OnEditProperty(DISPID dispid)
{
	return CStockPropPage::OnEditProperty(dispid, m_FontProp);
}

void CFontPropPage::OnObjectsChanged()
{
	ULONG nObjects;
	if (GetObjectArray(&nObjects) != NULL && m_hWnd != NULL)
	{
		FillPropnameList(IID_IFontDisp, 1, m_FontProp);

		if ( m_FontProp.GetCount() > 0 )
			FillFacenameList();
		else
		{
			m_FontNames.EnableWindow(FALSE);
			m_FontSizes.EnableWindow(FALSE);
			m_FontStyles.EnableWindow(FALSE);
			GetDlgItem(AFX_IDC_STRIKEOUT)->EnableWindow(FALSE);
			GetDlgItem(AFX_IDC_UNDERLINE)->EnableWindow(FALSE);
		}
	}

	if (m_hWnd != NULL)
		OnSelchangeFontprop();
}

/////////////////////////////////////////////////////////////////////////////
// Class factory for Font property page

#ifdef AFXCTL_FACT_SEG
#pragma code_seg(AFXCTL_FACT_SEG)
#endif

IMPLEMENT_OLECREATE_EX(CFontPropPage, "OCxx.CFontPropPage",
	0x0be35200,0x8f91,0x11ce,0x9d,0xe3,0x00,0xaa,0x00,0x4b,0xb8,0x51)

BOOL CFontPropPage::CFontPropPageFactory::UpdateRegistry(BOOL bRegister)
{
	if (bRegister)
		return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(),
			m_clsid, AFX_IDS_FONT_PPG);
	else
		return AfxOleUnregisterClass(m_clsid, NULL);
}

/////////////////////////////////////////////////////////////////////////////
// Force any extra compiler-generated code into AFX_INIT_SEG

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

IMPLEMENT_DYNCREATE(CFontPropPage, CStockPropPage)