/** * @file XTPControlComboBoxExt.cpp * * @copyright * (c) 1998-2025 Codejock Software, All Rights Reserved. * * This source file is the property of Codejock Software and must not be * redistributed by any means without the explicit written permission of * Codejock Software. * * The use of this source code is governed by the terms and conditions specified * in the Toolkit Pro license agreement. Codejock Software grants you, as a * single software developer, the limited right to use this software on one * computer only. * * Contact Information: * support@codejock.com * http://www.codejock.com * */ #include "StdAfx.h" #include "Common/XTPTypeId.h" #include "Common/XTPCasting.h" #include "Common/XTPFramework.h" #include "Common/XTPSystemHelpers.h" #include "Common/XTPSynchro.h" #include "Common/XTPApplication.h" #include "Common/XTPSingleton.h" #include "Common/XTPGdiObjects.h" #include "Common/XTPResourceManager.h" #include "Common/PropExchange/XTPPropExchangeSection.h" #include "Common/XTPPropExchange.h" #include "Common/XTPDrawHelpers.h" #include "Common/XTPHookManager.h" #include "Common/XTPColorManager.h" #include "Common/ScrollBar/XTPScrollInfo.h" #include "CommandBars/Resource.h" #include "CommandBars/XTPCommandBarsDefines.h" #include "CommandBars/XTPPaintManager.h" #include "CommandBars/XTPControl.h" #include "CommandBars/XTPControlButton.h" #include "CommandBars/XTPControlPopup.h" #include "CommandBars/XTPCommandBar.h" #include "CommandBars/XTPPopupBar.h" #include "CommandBars/XTPControlEdit.h" #include "CommandBars/XTPControlComboBox.h" #include "CommandBars/XTPControlComboBoxExt.h" #include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.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(XTP_DPI_X(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; _ASSERTE(dc.m_hDC != NULL); if (dc.m_hDC == NULL) return; m_nLogVert = dc.GetDeviceCaps(LOGPIXELSY); ::EnumFontFamilies(dc.m_hDC, pFontName, reinterpret_cast(reinterpret_cast(EnumSizeCallBack)), XTPToLPARAM(this)); } BOOL CALLBACK AFX_EXPORT CXTPControlSizeComboBox::EnumSizeCallBack(LOGFONT FAR* /*lplf*/, LPNEWTEXTMETRIC lpntm, int FontType, LPVOID lpv) { CXTPControlSizeComboBox* pThis = (CXTPControlSizeComboBox*)lpv; _ASSERTE(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) { _ASSERTE(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); _ASSERTE(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; BOOL bWin4 = XTPSystemVersion()->IsWinNT4OrGreater(); CWindowDC dc(CWnd::GetDesktopWindow()); if (bWin4) { ::EnumFontFamiliesEx(dc, &lf, reinterpret_cast( reinterpret_cast(EnumFamScreenCallBackEx)), XTPToLPARAM(this), NULL); } else { ::EnumFontFamilies(dc, NULL, reinterpret_cast( reinterpret_cast(EnumFamScreenCallBack)), XTPToLPARAM(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 = DWORD((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 = DWORD((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(0 != dwStyleListBox); SetWidth(XTP_DPI_X(150)); SetDropDownWidth(XTP_DPI_X(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); _ASSERTE(pDesc != NULL); if (!pDesc) return; _ASSERTE(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 { _ASSERTE(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 = XTPToDWORD(dwStyleListBox); CMapStringToPtr map; GetListBoxCtrl()->ResetContent(); CXTPControlFontComboBoxList::CFontDescHolder& s_fontHolder = CXTPSingleton::Instance(); s_fontHolder.EnumFontFamilies(); // now walk through the fonts and remove (charset) from fonts with only one CArray& 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 = GetListBoxCtrl()->AddString(str); _ASSERTE(nIndex >= 0); if (nIndex >= 0) // no error GetListBoxCtrl()->SetItemDataPtr(nIndex, pDesc); } } IMPLEMENT_XTP_COMMANDBAR(CXTPControlFontComboBoxList, CXTPControlComboBoxList) void CXTPControlFontComboBoxList::Copy(CXTPCommandBar* pCommandBar, BOOL bRecursive) { if (this != pCommandBar) { CXTPPopupBar::Copy(pCommandBar, bRecursive); _ASSERTE(pCommandBar->IsKindOf(RUNTIME_CLASS(CXTPControlFontComboBoxList))); m_dwStyleListBox = ((CXTPControlFontComboBoxList*)pCommandBar)->m_dwStyleListBox; if (!m_hWnd) CreateListBox(); EnumFontFamiliesEx(0 != 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(0 != m_dwStyleListBox); } }