/** * @file XTPComboBox.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/XTPDrawHelpers.h" #include "Common/XTPMarkupRender.h" #include "Common/XTPColorManager.h" #include "Common/Base/Types/XTPSize.h" #ifdef _XTP_INCLUDE_MARKUP # include "GraphicLibrary/GdiPlus/XTPGdiPlus.h" using namespace Gdiplus; using namespace Gdiplus::DllExports; # include "Markup/DeviceContext/XTPGdiPlus.h" #endif #include #include "Markup/XTPMarkupObject.h" #include "Markup/XTPMarkupInputElement.h" #include "Markup/XTPMarkupUIElement.h" #include "Markup/XTPMarkupContext.h" #include "Markup/XTPMarkupDrawingContext.h" #include "Controls/Defines.h" #include "Controls/Util/XTPControlTheme.h" #include "Controls/Util/XTPGlobal.h" #include "Controls/Combo/XTPComboBox.h" #include "Controls/Combo/XTPComboBoxTheme.h" #include "Controls/Combo/Themes/XTPComboBoxThemeResource.h" #include "Controls/Combo/Themes/XTPComboBoxThemeDefault.h" #include "Controls/Combo/Themes/XTPComboBoxThemeOffice2000.h" #include "Controls/Combo/Themes/XTPComboBoxThemeOffice2003.h" #include "Controls/Combo/Themes/XTPComboBoxThemeOfficeXPFlat.h" #include "Controls/Combo/Themes/XTPComboBoxThemeVisualStudio.h" #include "Controls/Combo/Themes/XTPComboBoxThemeVisualStudio2010.h" #include "Controls/Combo/Themes/XTPComboBoxThemeOffice2013.h" #include "Controls/Combo/Themes/XTPComboBoxThemeVisualStudio2012.h" #include "Controls/Combo/Themes/XTPComboBoxThemeVisualStudio2015.h" #include "Controls/Combo/Themes/XTPComboBoxThemeVisualStudio2017.h" #include "Controls/Combo/Themes/XTPComboBoxThemeVisualStudio2019.h" #include "Controls/Combo/Themes/XTPComboBoxThemeVisualStudio2022.h" #include "Controls/Combo/Themes/XTPComboBoxThemeNativeWindows10.h" #include "Controls/Combo/Themes/XTPComboBoxThemeNativeWindows11.h" #include "Controls/Combo/XTPComboBoxEditCtrl.h" #include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.h" #ifdef _DEBUG # define new DEBUG_NEW # undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif CXTPComboBox::CXTPComboBox() { m_bHighlighted = FALSE; m_bFocused = FALSE; m_bFlatStyle = FALSE; m_bUseVisualStyle = TRUE; m_pTheme = NULL; m_bPreSubclassInit = TRUE; m_bAutoComp = FALSE; m_bDisableAC = FALSE; m_pTheme = NULL; VERIFY(SetTheme(xtpControlThemeDefault)); m_pWndEdit = new CXTPComboBoxEditCtrl(); m_bEnableMarkup = FALSE; m_pMarkupContext = NULL; } CXTPComboBox::~CXTPComboBox() { CMDTARGET_RELEASE(m_pTheme); SAFE_DELETE(m_pWndEdit); XTPMarkupReleaseContext(m_pMarkupContext, TRUE); } IMPLEMENT_DYNAMIC(CXTPComboBox, CComboBox) #include "Common/Base/Diagnostic/XTPBeginAfxMap.h" BEGIN_MESSAGE_MAP(CXTPComboBox, CComboBox) //{{AFX_MSG_MAP(CXTPComboBox) ON_WM_CREATE() ON_WM_PAINT() ON_WM_MOUSEMOVE() ON_WM_KILLFOCUS() ON_WM_SETFOCUS() ON_WM_SYSCOLORCHANGE() ON_WM_CTLCOLOR() ON_WM_CTLCOLOR_REFLECT() ON_CONTROL_REFLECT_EX(CBN_EDITUPDATE, OnEditUpdate) ON_CONTROL_REFLECT_EX(CBN_CLOSEUP, OnCloseUp) ON_CONTROL_REFLECT_EX(CBN_DROPDOWN, OnDropDown) //}}AFX_MSG_MAP ON_MESSAGE_VOID(WM_MOUSELEAVE, OnMouseLeave) ON_MESSAGE(WM_XTP_SETCONTROLTHEME, OnSetTheme) END_MESSAGE_MAP() #include "Common/Base/Diagnostic/XTPEndAfxMap.h" void CXTPComboBox::Initialize(bool bAutoFont /*= true*/) { // set the font for the combobox. if (bAutoFont) { SetFont(&XTPAuxData().xtpFont); } } void CXTPComboBox::RedrawComboBox(BOOL bUpdateWindow) { if (m_hWnd) Invalidate(FALSE); if (m_hWnd && bUpdateWindow) UpdateWindow(); } void CXTPComboBox::RefreshMetrics() { if (m_pTheme) m_pTheme->RefreshMetrics(this); if (::IsWindow(m_hWnd)) RedrawComboBox(); } void CXTPComboBox::Init() { RefreshMetrics(); Invalidate(FALSE); HWND hWndEdit = 0; GetDlgItem(1001, &hWndEdit); if (hWndEdit && m_pWndEdit) { m_pWndEdit->SubclassWindow(hWndEdit); } } void CXTPComboBox::PreSubclassWindow() { CComboBox::PreSubclassWindow(); if (m_bPreSubclassInit) { // Initialize the control. Init(); } } int CXTPComboBox::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CComboBox::OnCreate(lpCreateStruct) == -1) return -1; // Initialize the control. Init(); return 0; } BOOL CXTPComboBox::PreCreateWindow(CREATESTRUCT& cs) { if (!CComboBox::PreCreateWindow(cs)) return FALSE; // When creating controls dynamically Init() must // be called from OnCreate() and not from // PreSubclassWindow(). m_bPreSubclassInit = FALSE; return TRUE; } LRESULT CXTPComboBox::OnSetTheme(WPARAM wParam, LPARAM /*lParam*/) { XTPControlTheme eTheme = (XTPControlTheme)wParam; SetTheme(eTheme); return 0; } BOOL CXTPComboBox::SetTheme(XTPControlTheme nTheme) { CMDTARGET_RELEASE(m_pTheme); switch (nTheme) { case xtpControlThemeDefault: case xtpControlThemeNativeWinXP: m_pTheme = new CXTPComboBoxThemeDefault(); break; case xtpControlThemeResource: m_pTheme = new CXTPComboBoxThemeResource(); break; case xtpControlThemeOffice2000: m_pTheme = new CXTPComboBoxThemeOffice2000(); break; case xtpControlThemeOfficeXP: case xtpControlThemeFlat: case xtpControlThemeUltraFlat: m_pTheme = new CXTPComboBoxThemeOfficeXPFlat(nTheme); break; case xtpControlThemeOffice2013: m_pTheme = new CXTPComboBoxThemeOffice2013(); break; case xtpControlThemeOffice2003: m_pTheme = new CXTPComboBoxThemeOffice2003(); break; case xtpControlThemeVisualStudio2005: case xtpControlThemeVisualStudio2008: m_pTheme = new CXTPComboBoxThemeVisualStudio(); break; case xtpControlThemeVisualStudio2010: m_pTheme = new CXTPComboBoxThemeVisualStudio2010(); break; case xtpControlThemeVisualStudio2012Light: case xtpControlThemeVisualStudio2012Dark: m_pTheme = new CXTPComboBoxThemeVisualStudio2012( nTheme == xtpControlThemeVisualStudio2012Light); break; case xtpControlThemeVisualStudio2015: m_pTheme = new CXTPComboBoxThemeVisualStudio2015(); break; case xtpControlThemeVisualStudio2017: m_pTheme = new CXTPComboBoxThemeVisualStudio2017(); break; case xtpControlThemeVisualStudio2019: m_pTheme = new CXTPComboBoxThemeVisualStudio2019(); break; case xtpControlThemeVisualStudio2022: m_pTheme = new CXTPComboBoxThemeVisualStudio2022(); break; case xtpControlThemeNativeWindows10: m_pTheme = new CXTPComboBoxThemeNativeWindows10(); break; case xtpControlThemeNativeWindows11: m_pTheme = new CXTPComboBoxThemeNativeWindows11(); break; default: m_pTheme = new CXTPComboBoxTheme(); } m_bUseVisualStyle = (nTheme == xtpControlThemeDefault || nTheme == xtpControlThemeNativeWinXP); RefreshMetrics(); if (m_hWnd) Invalidate(FALSE); return (m_pTheme != NULL); } void CXTPComboBox::SetTheme(CXTPComboBoxTheme* pPaintManager) { CMDTARGET_RELEASE(m_pTheme); m_pTheme = pPaintManager; m_bUseVisualStyle = (pPaintManager->GetThemeStyle() == xtpControlThemeDefault); if (m_pTheme) { m_pTheme->RefreshMetrics(this); } if (::IsWindow(m_hWnd)) { Invalidate(FALSE); } } CXTPComboBoxTheme* CXTPComboBox::GetTheme() { return m_pTheme; } const CXTPComboBoxTheme* CXTPComboBox::GetTheme() const { return m_pTheme; } void CXTPComboBox::SetUseVisualStyle(BOOL bUseVisualStyle /* = TRUE*/) { m_bUseVisualStyle = bUseVisualStyle; RefreshMetrics(); if (m_hWnd) Invalidate(FALSE); } void CXTPComboBox::OnPaint() { CPaintDC paintDC(this); CXTPClientRect rc(this); CXTPBufferDC dcMem(paintDC); HBRUSH hBrush = GetClientBrush(&dcMem); FillRect(dcMem, rc, hBrush); if ((GetStyle() & 3) == CBS_SIMPLE || m_bUseVisualStyle) { CComboBox::DefWindowProc(WM_PAINT, (WPARAM)dcMem.m_hDC, 0); } else { // Resource theme and GDI themes are using DefWindowProc before its own drawing DefWindowProc(WM_PAINT, (WPARAM)dcMem.GetSafeHdc(), 0); if (!m_pTheme->DrawComboBox(&dcMem, this)) { CComboBox::DefWindowProc(WM_PAINT, (WPARAM)dcMem.m_hDC, 0); } } } HBRUSH CXTPComboBox::GetClientBrush(CDC* pDC) { CWnd* pwndEdit = GetWindow(GW_CHILD); BOOL bDisabled = !IsWindowEnabled() || (pwndEdit && pwndEdit->GetStyle() & ES_READONLY); HBRUSH hBrush = (HBRUSH)::SendMessage(::GetParent(m_hWnd), UINT(bDisabled ? WM_CTLCOLORSTATIC : WM_CTLCOLOREDIT), (WPARAM)pDC->GetSafeHdc(), (LPARAM)m_hWnd); if (hBrush) return hBrush; return GetSysColorBrush(bDisabled ? COLOR_3DFACE : COLOR_WINDOW); } CRect CXTPComboBox::GetComboBoxRect() const { CXTPClientRect rc(this); return rc; } void CXTPComboBox::OnMouseLeave() { Default(); if (m_pWndEdit && m_pWndEdit->GetSafeHwnd()) { CPoint pt; GetCursorPos(&pt); CXTPWindowRect rcEdit(m_pWndEdit); if (rcEdit.PtInRect(pt)) { TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_pWndEdit->GetSafeHwnd(), HOVER_DEFAULT }; _TrackMouseEvent(&tme); SetHighlighted(TRUE); return; } } SetHighlighted(FALSE); } void CXTPComboBox::SetHighlighted(BOOL bHot) { if (bHot != m_bHighlighted) { m_bHighlighted = bHot; RedrawFocusedFrame(); if (bHot) { TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd, HOVER_DEFAULT }; _TrackMouseEvent(&tme); } RefreshMetrics(); } } void CXTPComboBox::OnMouseMove(UINT nFlags, CPoint point) { CRect rc; GetClientRect(&rc); BOOL bHot = rc.PtInRect(point) && !m_bFocused; SetHighlighted(bHot); CComboBox::OnMouseMove(nFlags, point); } void CXTPComboBox::OnKillFocus(CWnd* pNewWnd) { CComboBox::OnKillFocus(pNewWnd); if (pNewWnd != m_pWndEdit) { m_bFocused = FALSE; RefreshMetrics(); RedrawFocusedFrame(); } } void CXTPComboBox::OnSetFocus(CWnd* pOldWnd) { CComboBox::OnSetFocus(pOldWnd); m_bFocused = TRUE; m_bHighlighted = FALSE; RedrawFocusedFrame(); } BOOL CXTPComboBox::NeedRedraw() { return m_bUseVisualStyle; } void CXTPComboBox::RedrawFocusedFrame() { if (((GetStyle() & 3) != CBS_SIMPLE) && NeedRedraw()) { Invalidate(FALSE); } } void CXTPComboBox::OnSysColorChange() { CComboBox::OnSysColorChange(); RefreshMetrics(); Invalidate(FALSE); } BOOL CXTPComboBox::PreTranslateMessage(MSG* pMsg) { // Make sure that the keystrokes continue to the edit control. if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP) { // if tab, return or escape key, just use default. switch (pMsg->wParam) { case VK_DELETE: case VK_BACK: { if (m_bAutoComp) { m_bDisableAC = (pMsg->message == WM_KEYDOWN); } break; } case VK_TAB: case VK_RETURN: case VK_ESCAPE: { return CComboBox::PreTranslateMessage(pMsg); } } // If the combo box has an edit control, don't allow // the framework to process accelerators, let the edit // control handle it instead. GetEditSel() will return // CB_ERR if there is no edit control present... if (GetEditSel() != (DWORD)CB_ERR) { ::TranslateMessage(pMsg); ::DispatchMessage(pMsg); return TRUE; } } return CComboBox::PreTranslateMessage(pMsg); } BOOL CXTPComboBox::OnEditUpdate() { // if we are not to auto update the text, get outta here if (m_bAutoComp) { if (m_bDisableAC) { Default(); } else { // Get the text in the edit box CString strItemTyped; GetWindowText(strItemTyped); int nLength = strItemTyped.GetLength(); if (nLength >= 1) { // Currently selected range DWORD dwCurSel = GetEditSel(); int nStart = LOWORD(dwCurSel); int nEnd = HIWORD(dwCurSel); // Search for, and select in, and string in the combo box that is prefixed // by the text in the edit box if (SelectString(-1, strItemTyped) == CB_ERR) { SetWindowText( strItemTyped); // No text selected, so restore what was there before if (dwCurSel != (DWORD)CB_ERR) { SetEditSel(nStart, nEnd); // restore cursor postion } } // Set the text selection as the additional text that we have added if (nEnd < nLength && dwCurSel != (DWORD)CB_ERR) { SetEditSel(nStart, nEnd); } else { SetEditSel(nLength, -1); } } } } return FALSE; } BOOL CXTPComboBox::OnDropDown() { Invalidate(); return FALSE; } BOOL CXTPComboBox::OnCloseUp() { Invalidate(); return FALSE; } HBRUSH CXTPComboBox::CtlColor(CDC* pDC, UINT nCtlColor) { if (!m_pTheme) return NULL; switch (nCtlColor) { // in mode DropList when combobox without focus, in this mode combobox does not have edit // control case CTLCOLOR_EDIT: { pDC->SetTextColor(m_pTheme->GetTextColor()); // does not work, if control is disabled pDC->SetBkColor(m_pTheme->GetBackColor()); // brush used for fill self combobox in both modes DropList and DropDown return m_pTheme->GetBackgroundBrush(); } // when combobox is disabled in DropList mode, in this mode combobox does not have edit // control case CTLCOLOR_STATIC: { pDC->SetTextColor(m_pTheme->GetTextColor()); // does not work, if control is disabled pDC->SetBkColor(m_pTheme->GetBackColor()); return m_pTheme->GetBackgroundBrush(); } default: { _ASSERTE(FALSE); return NULL; } } } HBRUSH CXTPComboBox::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { if (!m_pTheme) return CComboBox::OnCtlColor(pDC, pWnd, nCtlColor); switch (nCtlColor) { // when combobox DropDown mode, inner Edit is enabled case CTLCOLOR_EDIT: { pDC->SetTextColor(m_pTheme->GetTextColor()); pDC->SetBkColor(m_pTheme->GetBackColor()); return m_pTheme->GetBackgroundBrush(); } // when combobox is expanded in both modes DropDown and DropList case CTLCOLOR_LISTBOX: { pDC->SetTextColor(m_pTheme->GetTextColor()); pDC->SetBkColor(m_pTheme->GetBackColor()); return m_pTheme->GetBackgroundBrush(); } // when combobox is disabled in DropDown mode, inner Edit is disabled case CTLCOLOR_STATIC: { pDC->SetTextColor(m_pTheme->GetTextColor()); // does not work, if control is disabled pDC->SetBkColor(m_pTheme->GetBackColor()); return m_pTheme->GetBackgroundBrush(); } default: { _ASSERTE(FALSE); return CComboBox::OnCtlColor(pDC, pWnd, nCtlColor); } } } void CXTPComboBox::SetBackColor(COLORREF crBack) { if (m_pTheme) { m_pTheme->SetBackColor(crBack); } } COLORREF CXTPComboBox::GetBackColor() const { return m_pTheme ? m_pTheme->GetBackColor() : GetXtremeColor(COLOR_WINDOW); } void CXTPComboBox::SetTextColor(COLORREF crText) { if (m_pTheme) { m_pTheme->SetTextColor(crText); } } COLORREF CXTPComboBox::GetTextColor() const { return m_pTheme ? m_pTheme->GetTextColor() : GetXtremeColor(COLOR_WINDOWTEXT); } void CXTPComboBox::EnableMarkup(BOOL bEnableMarkup) { m_bEnableMarkup = bEnableMarkup; XTPMarkupReleaseContext(m_pMarkupContext, TRUE); if (m_bEnableMarkup) { m_pMarkupContext = XTPMarkupCreateContext(m_hWnd, TRUE); } } void CXTPComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { if (lpDrawItemStruct->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) { CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); CRect rcItem(lpDrawItemStruct->rcItem); if (lpDrawItemStruct->itemState & ODS_SELECTED) pDC->FillSolidRect(rcItem, GetSysColor(COLOR_HIGHLIGHT)); else pDC->FillSolidRect(rcItem, GetBackColor()); int nLen = (int)::SendMessage(m_hWnd, CB_GETLBTEXTLEN, lpDrawItemStruct->itemID, 0); CString str; LPTSTR lpszBufer = str.GetBufferSetLength(nLen); ::SendMessage(m_hWnd, CB_GETLBTEXT, lpDrawItemStruct->itemID, (LPARAM)lpszBufer); str.ReleaseBuffer(); CXTPMarkupUIElement* m_pUIElement = m_pMarkupContext->Parse(str); if (m_pUIElement) { m_pUIElement->Arrange(rcItem); CXTPMarkupDrawingContext dc(lpDrawItemStruct->hDC); m_pUIElement->Render(&dc); XTPMarkupReleaseElement(m_pUIElement); } } } BOOL CXTPComboBox::IsDroppedDown() const { #include struct COMBOBOXINFO { DWORD cbSize; RECT rcItem; RECT rcButton; DWORD stateButton; HWND hwndCombo; HWND hwndItem; HWND hwndList; }; #include BOOL bDropped = FALSE; COMBOBOXINFO cbi = { sizeof(COMBOBOXINFO) }; if (0 != ::SendMessage(*this, CB_GETCOMBOBOXINFO, 0, XTPToLPARAM(&cbi))) bDropped = (NULL != cbi.hwndList && ::IsWindowVisible(cbi.hwndList)); else bDropped = (0 != ::SendMessage(*this, CB_GETDROPPEDSTATE, 0, 0)); return bDropped; }