// XTPControlComboBoxExt.cpp : implementation of the CXTPControlComboBoxExt class.
//
// This file is a part of the XTREME COMMANDBARS MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Resource.h"

#include "Common/XTPResourceManager.h"
#include "Common/XTPPropExchange.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPHookManager.h"

#include "XTPCommandBarsDefines.h"
#include "XTPControl.h"
#include "XTPControlButton.h"
#include "XTPControlPopup.h"
#include "XTPCommandBar.h"
#include "XTPPopupBar.h"
#include "XTPControlEdit.h"
#include "XTPControlComboBox.h"
#include "XTPControlComboBoxExt.h"

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

#define XTP_FONTCOMBOSTYLE_DEFAULTCHARSETONLY 1


// reserve lobyte for charset
#define PRINTER_FONT 0x0100
#define TT_FONT 0x0200
#define DEVICE_FONT 0x0400

#define MAX_POINT_SIZE 10

static int nFontSizes[] =
	{8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72};

static void OnCharFormatChanged(CHARFORMAT& cf, CXTPControl* pControl)
{
	NMXTPCHARHDR fnm;
	fnm.cf = cf;

	pControl->NotifySite(XTP_FN_SETFORMAT, &fnm);
}


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

IMPLEMENT_XTP_CONTROL(CXTPControlSizeComboBox, CXTPControlComboBox)

CXTPControlSizeComboBox::CXTPControlSizeComboBox()
{
	SetWidth(50);
	SetDropDownListStyle();

	m_nLogVert = 0;
	m_nTwipsLast = 0;
}

CXTPControlSizeComboBox::~CXTPControlSizeComboBox()
{

}

BOOL CXTPControlSizeComboBox::OnSetPopup(BOOL bPopup)
{
	if (bPopup && m_pCommandBar)
	{
		NMXTPCHARHDR fnm;
		fnm.cf.dwMask = 0;

		NotifySite(XTP_FN_GETFORMAT, &fnm);

		if ((fnm.cf.dwMask & (CFM_FACE | CFM_CHARSET)) == (CFM_FACE | CFM_CHARSET))
		{
			CString strFaceName = fnm.cf.szFaceName;
			CWindowDC dc(CWnd::GetDesktopWindow());
			EnumFontSizes(dc, strFaceName);
		}
	}

	return CXTPControlComboBox::OnSetPopup(bPopup);
}

void CXTPControlSizeComboBox::OnExecute()
{
	int nSize = GetTwipSize();
	if (nSize == -2)
	{
		XTPResourceManager()->ShowMessageBox(XTP_IDS_INVALID_NUMBER, MB_OK | MB_ICONINFORMATION);
	}
	else if ((nSize >= 0 && nSize < 20) || nSize > 32760)
	{
		XTPResourceManager()->ShowMessageBox(XTP_IDS_INVALID_FONTSIZE, MB_OK | MB_ICONINFORMATION);
	}
	else if (nSize > 0)
	{
		CHARFORMAT cf;
		cf.cbSize = sizeof(CHARFORMAT);
		cf.dwMask = CFM_SIZE;
		cf.yHeight = nSize;
		OnCharFormatChanged(cf, this);
	}

	CXTPControlComboBox::OnExecute();
}

void CXTPControlSizeComboBox::EnumFontSizes(CDC& dc, LPCTSTR pFontName)
{
	ResetContent();
	if (pFontName == NULL)
		return;
	if (pFontName[0] == NULL)
		return;

	ASSERT(dc.m_hDC != NULL);
	if (dc.m_hDC == NULL)
		return;
	m_nLogVert = dc.GetDeviceCaps(LOGPIXELSY);

	::EnumFontFamilies(dc.m_hDC, pFontName,
		(FONTENUMPROC) EnumSizeCallBack, (LPARAM) this);
}

BOOL CALLBACK AFX_EXPORT CXTPControlSizeComboBox::EnumSizeCallBack(LOGFONT FAR* /*lplf*/,
		LPNEWTEXTMETRIC lpntm, int FontType, LPVOID lpv)
{
	CXTPControlSizeComboBox* pThis = (CXTPControlSizeComboBox*)lpv;
	ASSERT(pThis != NULL);
	if (!pThis)
		return FALSE;

	TCHAR buf[MAX_POINT_SIZE];
	if (
		(FontType & TRUETYPE_FONTTYPE) ||
		!((FontType & TRUETYPE_FONTTYPE) || (FontType & RASTER_FONTTYPE))
		) // if truetype or vector font
	{
		// this occurs when there is a truetype and nontruetype version of a font
		if (pThis->GetCount() != 0)
			pThis->ResetContent();

		for (int i = 0; i < 16; i++)
		{
			wsprintf(buf, _T("%d"), nFontSizes[i]);
			pThis->AddString(buf);
		}
		return FALSE; // don't call me again
	}
	// calc character height in pixels
	pThis->InsertSize(MulDiv(lpntm->tmHeight-lpntm->tmInternalLeading,
		1440, pThis->m_nLogVert));
	return TRUE; // call me again
}

void CXTPControlSizeComboBox::InsertSize(int nSize)
{
	ASSERT(nSize > 0);
	DWORD dwSize = (DWORD)nSize;

	CString strTwips = TwipsToPointString(nSize);

	if (FindStringExact(-1, strTwips) == CB_ERR)
	{
		int nIndex = -1;
		int nPos = 0;
		DWORD dw;
		while ((dw = (DWORD)GetItemData(nPos)) != (DWORD)CB_ERR)
		{
			if (dw > dwSize)
			{
				nIndex = nPos;
				break;
			}
			nPos++;
		}
		nIndex = InsertString(nIndex, strTwips);
		ASSERT(nIndex != CB_ERR);
		if (nIndex != CB_ERR)
			SetItemData(nIndex, dwSize);
	}
}

CString AFX_CDECL CXTPControlSizeComboBox::TwipsToPointString(int nTwips)
{
	CString str;

	if (nTwips >= 0)
	{
		// round to nearest half point
		nTwips = (nTwips + 5) / 10;
		if ((nTwips % 2) == 0)
			str.Format(_T("%i"), nTwips / 2);
		else
			str.Format(_T("%.1f"), (float)nTwips / 2.0F);
	}

	return str;
}

int CXTPControlSizeComboBox::GetTwipSize() const
{
	return GetTwipSize(GetEditText());
}

int CXTPControlSizeComboBox::GetTwipSize(LPCTSTR lpszText)
{
	// return values
	// -2 -- error
	// -1 -- edit box empty
	// >= 0 -- font size in twips

	while (*lpszText == ' ' || *lpszText == '\t')
	{
		lpszText++;
	}

	if (lpszText[0] == NULL)
		return -1; // no text in control

	double d = _tcstod(lpszText, (LPTSTR*)&lpszText);
	while (*lpszText == ' ' || *lpszText == '\t')
	{
		lpszText++;
	}

	if (*lpszText != NULL)
		return -2;   // not terminated properly

	return (d < 0.) ? 0 : (int)(d * 20.);
}

void CXTPControlSizeComboBox::SetTwipSize(int nTwips)
{
	if (HasFocus())
		return;

	if (nTwips != GetTwipSize())
	{
		SetEditText(TwipsToPointString(nTwips));
	}

	m_nTwipsLast = nTwips;
}

//////////////////////////////////////////////////////////////////////
// CXTPControlFontComboBox Class
//////////////////////////////////////////////////////////////////////



CXTPControlFontComboBoxList::CFontDesc::CFontDesc(LPCTSTR lpszName, LPCTSTR lpszScript, BYTE nCharSet,
	BYTE nPitchAndFamily, DWORD dwFlags)
{
	m_strName = lpszName;
	m_strScript = lpszScript;
	m_nCharSet = nCharSet;
	m_nPitchAndFamily = nPitchAndFamily;
	m_dwFlags = dwFlags;
}



void CXTPControlFontComboBoxList::CFontDescHolder::EnumFontFamilies()
{
	if (m_arrayFontDesc.GetSize() == 0)
	{
		LOGFONT lf;
		memset(&lf, 0, sizeof(LOGFONT));
		lf.lfCharSet = DEFAULT_CHARSET;

		DWORD dwVersion = ::GetVersion();
		BOOL bWin4 = (BYTE)dwVersion >= 4;
		CWindowDC dc(CWnd::GetDesktopWindow());
		if (bWin4)
		{
			::EnumFontFamiliesEx(dc, &lf,
				(FONTENUMPROC) EnumFamScreenCallBackEx, (LPARAM) this, NULL);
		}
		else
		{
			::EnumFontFamilies(dc, NULL,
				(FONTENUMPROC) EnumFamScreenCallBack, (LPARAM) this);
		}
	}
}
void CXTPControlFontComboBoxList::CFontDescHolder::AddFont(ENUMLOGFONT* pelf, DWORD dwType, LPCTSTR lpszScript)
{
	LOGFONT& lf = pelf->elfLogFont;
	if (lf.lfCharSet == MAC_CHARSET) // don't put in MAC fonts, commdlg doesn't either
		return;

	// Don't display vertical font for FE platform
	if ((lf.lfFaceName[0] == '@'))
		return;

	for (int i = 0; i < m_arrayFontDesc.GetSize(); i++)
	{
		CFontDesc* pDesc = (CFontDesc*)m_arrayFontDesc[i];
		if (pDesc->m_strName == lf.lfFaceName)
			return;
	}

	// don't put in non-printer raster fonts
	CFontDesc* pDesc = new CFontDesc(lf.lfFaceName, lpszScript,
		lf.lfCharSet, lf.lfPitchAndFamily, dwType);

	CString strName = lf.lfFaceName;
	int nIndex = 0;
	for (nIndex = 0; nIndex < m_arrayFontDesc.GetSize(); nIndex++)
	{
		if (strName < m_arrayFontDesc[nIndex]->m_strName)
			break;
	}
	m_arrayFontDesc.InsertAt(nIndex, pDesc);
}


BOOL CALLBACK AFX_EXPORT CXTPControlFontComboBoxList::CFontDescHolder::EnumFamScreenCallBack(ENUMLOGFONT* pelf,
	NEWTEXTMETRICEX* /*lpntm*/, int FontType, LPVOID pThis)
{
	// don't put in non-printer raster fonts
	if (FontType & RASTER_FONTTYPE)
		return 1;
	DWORD dwData = (FontType & TRUETYPE_FONTTYPE) ? TT_FONT : 0;
	((CXTPControlFontComboBoxList::CFontDescHolder *)pThis)->AddFont(pelf, dwData);
	return 1;
}


BOOL CALLBACK AFX_EXPORT CXTPControlFontComboBoxList::CFontDescHolder::EnumFamScreenCallBackEx(ENUMLOGFONTEX* pelf,
	NEWTEXTMETRICEX* /*lpntm*/, int FontType, LPVOID pThis)
{
	// don't put in non-printer raster fonts
	if (FontType & RASTER_FONTTYPE)
		return 1;
	DWORD dwData = (FontType & TRUETYPE_FONTTYPE) ? TT_FONT : 0;
	((CXTPControlFontComboBoxList::CFontDescHolder *)pThis)->AddFont((ENUMLOGFONT*)pelf, dwData, CString((TCHAR*)pelf->elfScript));
	return 1;
}

void CXTPControlFontComboBoxList::CreateListBox()
{
	m_dwStyle |= LBS_SORT;
	CXTPControlComboBoxList::CreateListBox();
}


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

IMPLEMENT_XTP_CONTROL(CXTPControlFontComboBox, CXTPControlComboBox)

CXTPControlFontComboBox::CXTPControlFontComboBox(DWORD dwStyleListBox)
{
	if (m_pCommandBar)
	{
		m_pCommandBar->InternalRelease();
	}

	m_pCommandBar = new CXTPControlFontComboBoxList();
	((CXTPControlFontComboBoxList*)m_pCommandBar)->CreateListBox();

	((CXTPControlFontComboBoxList*)m_pCommandBar)->EnumFontFamiliesEx(dwStyleListBox);


	SetWidth(150);
	SetDropDownWidth(250);
	SetDropDownListStyle();
	m_bAutoComplete = TRUE;
}

CXTPControlFontComboBox::~CXTPControlFontComboBox()
{
}


void CXTPControlFontComboBox::OnExecute()
{
	CHARFORMAT cf;
	cf.cbSize = sizeof(CHARFORMAT);
	cf.szFaceName[0] = NULL;

	// this will retrieve the font entered in the edit control
	// it tries to match the font to something already present in the combo box
	// this effectively ignores case of a font the user enters
	// if a user enters arial, this will cause it to become Arial
	CString str = GetEditText();

	// if font name box is not empty
	if (!str.IsEmpty())
	{
		cf.dwMask = CFM_FACE | CFM_CHARSET;
		int nIndex = FindStringExact(-1, str);
		if (nIndex != CB_ERR)
		{
			CXTPControlFontComboBoxList::CFontDesc* pDesc = (CXTPControlFontComboBoxList::CFontDesc*)GetItemData(nIndex);
			ASSERT(pDesc != NULL);
			if (!pDesc)
				return;
			ASSERT(pDesc->m_strName.GetLength() < LF_FACESIZE);
		#if (_RICHEDIT_VER >= 0x0200)
			lstrcpyn(cf.szFaceName, pDesc->m_strName, LF_FACESIZE);
		#else
			lstrcpynA(cf.szFaceName, XTP_CT2CA(pDesc->m_strName), LF_FACESIZE);
		#endif
			cf.bCharSet = pDesc->m_nCharSet;
			cf.bPitchAndFamily = pDesc->m_nPitchAndFamily;
		}
		else // unknown font
		{
			ASSERT(str.GetLength() < LF_FACESIZE);

		#if (_RICHEDIT_VER >= 0x0200)
			lstrcpyn(cf.szFaceName, str, LF_FACESIZE);
		#else
			lstrcpynA(cf.szFaceName, XTP_CT2CA(str), LF_FACESIZE);
		#endif
			cf.bCharSet = DEFAULT_CHARSET;
			cf.bPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
		}
		OnCharFormatChanged(cf, this);
	}

	CXTPControlComboBox::OnExecute();
}


void CXTPControlFontComboBox::SetCharFormat(CHARFORMAT& cf)
{
	if (HasFocus())
		return;

	// the selection must be same font and charset to display correctly
	if ((cf.dwMask & (CFM_FACE | CFM_CHARSET)) == (CFM_FACE | CFM_CHARSET))
		SetEditText(CString(cf.szFaceName));
	else
		SetEditText(_T(""));
}



void CXTPControlFontComboBoxList::EnumFontFamiliesEx(BOOL dwStyleListBox)
{
	m_dwStyleListBox = dwStyleListBox;

	CMapStringToPtr map;

	((CListBox*)this)->ResetContent();

	static CXTPControlFontComboBoxList::CFontDescHolder s_fontHolder;
	s_fontHolder.EnumFontFamilies();

	// now walk through the fonts and remove (charset) from fonts with only one
	CArray<CFontDesc*, CFontDesc*>& arrFontDesc = s_fontHolder.m_arrayFontDesc;

	int nCount = (int)arrFontDesc.GetSize();
	int i;
	// walk through fonts adding names to string map
	// first time add value 0, after that add value 1
	for (i = 0; i < nCount; i++)
	{
		CFontDesc* pDesc = arrFontDesc[i];
		void* pv = NULL;
		if (map.Lookup(pDesc->m_strName, pv)) // found it
		{
			if (pv == NULL) // only one entry so far
			{
				map.RemoveKey(pDesc->m_strName);
				map.SetAt(pDesc->m_strName, (void*)1);
			}
		}
		else // not found
			map.SetAt(pDesc->m_strName, (void*)0);
	}

	for (i = 0; i < nCount; i++)
	{
		CFontDesc* pDesc = arrFontDesc[i];
		CString str = pDesc->m_strName;
		void* pv = NULL;
		VERIFY(map.Lookup(str, pv));
		if (pv != NULL && !pDesc->m_strScript.IsEmpty())
		{
			str += " (";
			str += pDesc->m_strScript;
			str += ")";
		}

		int nIndex = ((CListBox*)this)->AddString(str);
		ASSERT(nIndex >= 0);
		if (nIndex >= 0) //no error
			((CListBox*)this)->SetItemDataPtr(nIndex, pDesc);
	}
}


IMPLEMENT_XTP_COMMANDBAR(CXTPControlFontComboBoxList, CXTPControlComboBoxList)

void CXTPControlFontComboBoxList::Copy(CXTPCommandBar* pCommandBar, BOOL bRecursive)
{
	CXTPPopupBar::Copy(pCommandBar, bRecursive);

	ASSERT(pCommandBar->IsKindOf(RUNTIME_CLASS(CXTPControlFontComboBoxList)));
	m_dwStyleListBox = ((CXTPControlFontComboBoxList*)pCommandBar)->m_dwStyleListBox;

	if (!m_hWnd)
		CreateListBox();

	EnumFontFamiliesEx(m_dwStyleListBox);

}

void CXTPControlFontComboBoxList::DoPropExchange(CXTPPropExchange* pPX)
{
	CXTPPopupBar::DoPropExchange(pPX);

	PX_DWord(pPX, _T("StyleListBox"), m_dwStyleListBox, XTP_FONTCOMBOSTYLE_DEFAULTCHARSETONLY);

	if (pPX->IsLoading())
	{
		if (!m_hWnd)
			CreateListBox();

		EnumFontFamiliesEx(m_dwStyleListBox);
	}
}