/** * @file XTPCheckListBox.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/XTPVC80Helpers.h" #include "Common/XTPTypeId.h" #include "Common/XTPCasting.h" #include "Common/XTPFramework.h" #include "Common/XTPSystemHelpers.h" #include "Common/XTPResourceManager.h" #include "Common/XTPWinThemeWrapper.h" #include "Common/XTPSynchro.h" #include "Common/XTPGdiObjects.h" #include "Common/XTPDrawHelpers.h" #include "Common/XTPImageManager.h" #include "Common/ScrollBar/XTPScrollBase.h" #include "Common/ScrollBar/XTPScrollBarCtrl.h" #include "Common/ScrollBar/XTPScrollable.h" #include "Controls/Resource.h" #include "Controls/Util/XTPControlTheme.h" #include "Controls/ListBox/XTPListBox.h" #include "Controls/ListBox/XTPCheckListBox.h" #ifndef LAYOUT_BITMAPORIENTATIONPRESERVED # define LAYOUT_BITMAPORIENTATIONPRESERVED 0x00000008 #endif #include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.h" #ifdef _DEBUG # define new DEBUG_NEW # undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CCheckListState CXTPCheckListBox::CCheckListState::CCheckListState(BOOL bListBox3D) : m_sizeCheck(XTP_DPI_X(12), XTP_DPI_Y(12)) , idUnchecked(bListBox3D ? IDR_XAML_ICON_CHECKBOX3D_UNCHECKED : IDR_XAML_ICON_CHECKBOX_UNCHECKED) , idChecked(bListBox3D ? IDR_XAML_ICON_CHECKBOX3D_CHECKED : IDR_XAML_ICON_CHECKBOX_CHECKED) , idMixed(bListBox3D ? IDR_XAML_ICON_CHECKBOX3D_MIXED : IDR_XAML_ICON_CHECKBOX_MIXED) , m_bIconsSet(FALSE) { // As far as we cannot know the valid themed part size upfront we assume its default value // which might not be correct but better than nothing. m_sizeCheckThemed.cx = GetSystemMetrics(SM_CXMENUCHECK); m_sizeCheckThemed.cy = GetSystemMetrics(SM_CYMENUCHECK); } void CXTPCheckListBox::CCheckListState::Draw(CDC* pDC, int nCheck, CPoint pt, CSize sz) { if (!m_bIconsSet) { XTPImageManager()->SetVectorIcon(_T("RT_XAML"), XTPToUIntChecked(idChecked), XTPToUIntChecked(idChecked), XTPToUIntChecked(m_sizeCheck.cx)); XTPImageManager()->SetVectorIcon(_T("RT_XAML"), XTPToUIntChecked(idUnchecked), XTPToUIntChecked(idUnchecked), XTPToUIntChecked(m_sizeCheck.cx)); XTPImageManager()->SetVectorIcon(_T("RT_XAML"), XTPToUIntChecked(idMixed), XTPToUIntChecked(idMixed), XTPToUIntChecked(m_sizeCheck.cx)); m_bIconsSet = TRUE; } CXTPImageManagerIcon* pIcon = NULL; switch (nCheck) { case 0: pIcon = XTPImageManager()->GetImage(XTPToUIntChecked(idUnchecked), m_sizeCheck.cx); break; case 1: pIcon = XTPImageManager()->GetImage(XTPToUIntChecked(idChecked), m_sizeCheck.cx); break; case 2: pIcon = XTPImageManager()->GetImage(XTPToUIntChecked(idMixed), m_sizeCheck.cx); break; default: _ASSERTE(FALSE); break; } ASSERT_VALID(pIcon); if (pIcon->IsVectorIcon()) pIcon->Draw(pDC, pt, sz); } CXTPCheckListBox::CCheckListState::~CCheckListState() { } ///////////////////////////////////////////////////////////////////////////// // CHECK_DATA struct CXTPCheckListBox::CHECK_DATA { public: int m_nCheck; BOOL m_bEnabled; DWORD_PTR m_dwUserData; CHECK_DATA() { m_nCheck = 0; m_bEnabled = TRUE; m_dwUserData = 0; } }; ///////////////////////////////////////////////////////////////////////////// // CXTPCheckListBox CXTPCheckListBox::CXTPCheckListBox(BOOL bListBox3D /* = FALSE */) : m_cyText(0) , m_nStyle(0) , m_checkListState(bListBox3D) { m_themeHelper = new CXTPWinThemeWrapper(); } CXTPCheckListBox::~CXTPCheckListBox() { SAFE_DELETE(m_themeHelper); } IMPLEMENT_DYNAMIC(CXTPCheckListBox, CXTPListBox) #include "Common/Base/Diagnostic/XTPBeginAfxMap.h" BEGIN_MESSAGE_MAP(CXTPCheckListBox, CXTPListBox) //{{AFX_MSG_MAP(CXTPCheckListBox) ON_WM_LBUTTONDOWN() ON_WM_KEYDOWN() ON_WM_CREATE() ON_WM_LBUTTONDBLCLK() ON_MESSAGE(WM_SETFONT, OnSetFont) ON_MESSAGE(LB_ADDSTRING, OnLBAddString) ON_MESSAGE(LB_FINDSTRING, OnLBFindString) ON_MESSAGE(LB_FINDSTRINGEXACT, OnLBFindStringExact) ON_MESSAGE(LB_GETITEMDATA, OnLBGetItemData) ON_MESSAGE(LB_GETTEXT, OnLBGetText) ON_MESSAGE(LB_INSERTSTRING, OnLBInsertString) ON_MESSAGE(LB_SELECTSTRING, OnLBSelectString) ON_MESSAGE(LB_SETITEMDATA, OnLBSetItemData) ON_MESSAGE(LB_SETITEMHEIGHT, OnLBSetItemHeight) //}}AFX_MSG_MAP END_MESSAGE_MAP() #include "Common/Base/Diagnostic/XTPEndAfxMap.h" BOOL CXTPCheckListBox::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { if (!(dwStyle & LBS_OWNERDRAWVARIABLE)) // must be one or the other { dwStyle |= LBS_OWNERDRAWFIXED; } return CXTPListBox::Create(dwStyle, rect, pParentWnd, nID); } void CXTPCheckListBox::SetCheckStyle(UINT nStyle) { _ASSERTE(nStyle == 0 || nStyle == BS_CHECKBOX || nStyle == BS_AUTOCHECKBOX || nStyle == BS_AUTO3STATE || nStyle == BS_3STATE); m_nStyle = nStyle; } void CXTPCheckListBox::SetCheck(int nIndex, int nCheck) { _ASSERTE(::IsWindow(m_hWnd)); if (nCheck == 2) { if (m_nStyle == BS_CHECKBOX || m_nStyle == BS_AUTOCHECKBOX) return; } LRESULT lResult = DefWindowProc(LB_GETITEMDATA, XTPToWPARAM(nIndex), 0); if (lResult != LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)lResult; if (pState == NULL) pState = new CHECK_DATA; pState->m_nCheck = nCheck; VERIFY(DefWindowProc(LB_SETITEMDATA, XTPToWPARAM(nIndex), XTPToLPARAM(pState)) != LB_ERR); InvalidateCheck(nIndex); } } int CXTPCheckListBox::GetCheck(int nIndex) { _ASSERTE(::IsWindow(m_hWnd)); LRESULT lResult = DefWindowProc(LB_GETITEMDATA, XTPToWPARAM(nIndex), 0); if (lResult != LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)lResult; if (pState != NULL) return pState->m_nCheck; } return 0; // The default } void CXTPCheckListBox::Enable(int nIndex, BOOL bEnabled) { _ASSERTE(::IsWindow(m_hWnd)); LRESULT lResult = DefWindowProc(LB_GETITEMDATA, XTPToWPARAM(nIndex), 0); if (lResult != LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)lResult; if (pState == NULL) pState = new CHECK_DATA; pState->m_bEnabled = bEnabled; VERIFY(DefWindowProc(LB_SETITEMDATA, XTPToWPARAM(nIndex), XTPToLPARAM(pState)) != LB_ERR); InvalidateItem(nIndex); } } int CXTPCheckListBox::IsEnabled(int nIndex) { _ASSERTE(::IsWindow(m_hWnd)); LRESULT lResult = DefWindowProc(LB_GETITEMDATA, XTPToWPARAM(nIndex), 0); if (lResult != LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)lResult; if (pState != NULL) return pState->m_bEnabled; } return TRUE; // The default } CRect CXTPCheckListBox::OnGetCheckPosition(CRect, CRect rectCheckBox) { return rectCheckBox; } void CXTPCheckListBox::DrawItem(LPDRAWITEMSTRUCT lpDIS) { CDC* pDC = CDC::FromHandle(lpDIS->hDC); if (NULL != m_pTheme) { m_pTheme->DrawItem(pDC, this, lpDIS); } /* // Must be LBS_OWNERDRAWVARIABLE or LBS_OWNERDRAWFIXED and LBS_HASSTRINGS _ASSERTE(GetStyle() & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)); _ASSERTE(GetStyle() & (LBS_HASSTRINGS)); CDC* pDC = CDC::FromHandle(lpDIS->hDC); CRect rcItem(lpDIS->rcItem); rcItem.left += 2; if (((LONG)(lpDIS->itemID) >= 0) && (lpDIS->itemAction & (ODA_DRAWENTIRE | ODA_SELECT))) { int cyItem = GetItemHeight(lpDIS->itemID); BOOL fDisabled = !IsWindowEnabled() || !IsEnabled(lpDIS->itemID); COLORREF newTextColor = fDisabled ? RGB(0x80, 0x80, 0x80) : GetSysColor(COLOR_WINDOWTEXT); // light gray COLORREF oldTextColor = pDC->SetTextColor(newTextColor); COLORREF newBkColor = GetSysColor(COLOR_WINDOW); COLORREF oldBkColor = pDC->SetBkColor(newBkColor); if (newTextColor == newBkColor) newTextColor = RGB(0xC0, 0xC0, 0xC0); // dark gray if (!fDisabled && ((lpDIS->itemState & ODS_SELECTED) != 0)) { pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT)); pDC->SetBkColor(GetSysColor(COLOR_HIGHLIGHT)); } if (m_cyText == 0) VERIFY(cyItem >= CalcMinimumItemHeight()); CString strText; GetText(lpDIS->itemID, strText); pDC->ExtTextOut(rcItem.left + 2, rcItem.top, ETO_OPAQUE, &rcItem, strText, strText.GetLength(), NULL); pDC->SetTextColor(oldTextColor); pDC->SetBkColor(oldBkColor); } if ((lpDIS->itemAction & ODA_FOCUS) != 0) pDC->DrawFocusRect(&rcItem); */ } int CXTPCheckListBox::GetFontHeight() { CWindowDC dc(NULL); // get the screen device context // select the control's font into the window dc. CFont* pOldFont = dc.SelectObject(GetFont()); // get the text metrics for the device context that // is using the control's font, this will give us the // height in pixels that's what we will use for the // list item's height. TEXTMETRIC tm; dc.GetTextMetrics(&tm); // restore the dc with it's original font. dc.SelectObject(pOldFont); // return the desired row height. return tm.tmHeight; } void CXTPCheckListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMIS) { lpMIS->itemHeight = XTPToUIntChecked(m_checkListState.m_sizeCheck.cy + ::GetSystemMetrics(SM_CYEDGE)); } void CXTPCheckListBox::Init() { CXTPListBox::Init(); // If windows XP themes are supported, initialize the // theme wrapper used to display the check boxes. m_themeHelper->OpenThemeData(m_hWnd, L"BUTTON"); } bool CXTPCheckListBox::PreDrawItemThemed(CDC* pDC, DRAWITEMSTRUCT& drawItem, int nCheck, int cyItem) { bool bRet = false; // Draw the check boxes using the theme API's only if the app is themed. if (m_themeHelper->IsAppThemeReady()) { BOOL bEnabled = IsWindowEnabled() && IsEnabled(XTPToInt(drawItem.itemID)); int nState = 0; switch (nCheck) { case 0: // not checked nState = (bEnabled ? CBS_UNCHECKEDNORMAL : CBS_UNCHECKEDDISABLED); break; case 1: // checked nState = (bEnabled ? CBS_CHECKEDNORMAL : CBS_CHECKEDDISABLED); break; case 2: // intermediate nState = (bEnabled ? CBS_MIXEDNORMAL : CBS_MIXEDDISABLED); break; } SIZE size; if (SUCCEEDED(m_themeHelper->GetThemePartSize(pDC->m_hDC, BP_CHECKBOX, nState, NULL, TS_TRUE, &size))) { m_checkListState.m_sizeCheckThemed = size; CRect rectCheck = drawItem.rcItem; rectCheck.left += 1; rectCheck.top += max(0, (cyItem - size.cy) / 2); rectCheck.right = rectCheck.left + size.cx; rectCheck.bottom = rectCheck.top + size.cy; CRect rectItem = drawItem.rcItem; rectItem.right = rectItem.left + size.cx + 2; CRect rectCheckBox = OnGetCheckPosition(rectItem, rectCheck); if (SUCCEEDED(m_themeHelper->DrawThemeBackground(pDC->m_hDC, BP_CHECKBOX, nState, &rectCheckBox, NULL))) { m_bLastItemDrawnThemed = TRUE; bRet = true; } } } return bRet; } void CXTPCheckListBox::PreDrawItemNonThemed(CDC* pDC, DRAWITEMSTRUCT& drawItem, int nCheck, int cyItem) { CDC bitmapDC; if (bitmapDC.CreateCompatibleDC(pDC)) { // redraw background. pDC->FillSolidRect(&drawItem.rcItem, ::GetSysColor(COLOR_WINDOW)); #if _MSC_VER >= 1200 // MFC 6.0 // Change Compatible DC to LTR since the bitmap is LTR DWORD dwLayoutBitmapDC = bitmapDC.GetLayout(); bitmapDC.SetLayout(0); #endif CRect rectCheck = drawItem.rcItem; rectCheck.left += 1; rectCheck.top += max(0, (cyItem - m_checkListState.m_sizeCheck.cy) / 2); rectCheck.right = rectCheck.left + m_checkListState.m_sizeCheck.cx; rectCheck.bottom = rectCheck.top + m_checkListState.m_sizeCheck.cy; CRect rectItem = drawItem.rcItem; rectItem.right = rectItem.left + m_checkListState.m_sizeCheck.cx + 2; CRect rectCheckBox = OnGetCheckPosition(rectItem, rectCheck); pDC->FillSolidRect(rectItem, pDC->GetBkColor()); #if _MSC_VER >= 1200 // MFC 6.0 DWORD dwLayoutDC = pDC->GetLayout(); // Change destination DC layout to preserve bitmap orientation pDC->SetLayout(dwLayoutDC | LAYOUT_BITMAPORIENTATIONPRESERVED); #endif m_checkListState.Draw(pDC, nCheck, rectCheckBox.TopLeft(), rectCheckBox.Size()); #if _MSC_VER >= 1200 // MFC 6.0 // Restore DC layout pDC->SetLayout(dwLayoutDC); bitmapDC.SetLayout(dwLayoutBitmapDC); #endif m_bLastItemDrawnThemed = FALSE; } } void CXTPCheckListBox::PreDrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { DRAWITEMSTRUCT drawItem; MEMCPY_S(&drawItem, lpDrawItemStruct, sizeof(DRAWITEMSTRUCT)); if ((((LONG)drawItem.itemID) >= 0) && ((drawItem.itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) != 0)) { int cyItem = GetItemHeight(XTPToInt(drawItem.itemID)); CDC* pDC = CDC::FromHandle(drawItem.hDC); COLORREF newBkColor = GetSysColor(COLOR_WINDOW); BOOL fDisabled = !IsWindowEnabled() || !IsEnabled(XTPToInt(drawItem.itemID)); if ((drawItem.itemState & ODS_SELECTED) && !fDisabled) newBkColor = GetSysColor(COLOR_HIGHLIGHT); COLORREF oldBkColor = pDC->SetBkColor(newBkColor); int nCheck = GetCheck(XTPToInt(drawItem.itemID)); if (!PreDrawItemThemed(pDC, drawItem, nCheck, cyItem)) { PreDrawItemNonThemed(pDC, drawItem, nCheck, cyItem); } pDC->SetBkColor(oldBkColor); } if (drawItem.itemData != 0 && drawItem.itemData != (UINT)LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)drawItem.itemData; drawItem.itemData = pState->m_dwUserData; } drawItem.rcItem.left = drawItem.rcItem.left + (m_bLastItemDrawnThemed ? m_checkListState.m_sizeCheckThemed.cx : m_checkListState.m_sizeCheck.cx) + 3; DrawItem(&drawItem); } /* void CXTPCheckListBox::PreDrawItem(LPDRAWITEMSTRUCT lpDIS) { DRAWITEMSTRUCT drawItem; MEMCPY_S(&drawItem, lpDIS, sizeof(DRAWITEMSTRUCT)); if ((((LONG)drawItem.itemID) >= 0) && ((drawItem.itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) != 0)) { int cyItem = GetItemHeight(drawItem.itemID); CDC* pDC = CDC::FromHandle(drawItem.hDC); COLORREF newBkColor = GetSysColor(COLOR_WINDOW); COLORREF oldBkColor = pDC->SetBkColor(newBkColor); CDC bitmapDC; if (bitmapDC.CreateCompatibleDC(pDC)) { int nCheck = GetCheck(drawItem.itemID); HBITMAP hOldBitmap = (HBITMAP)::SelectObject(bitmapDC.m_hDC, m_checkListState.m_hbitmapCheck); CRect rectCheck = drawItem.rcItem; rectCheck.left += 1; rectCheck.top += 1 + __max(0, (cyItem - m_checkListState.m_sizeCheck.cy) / 2); rectCheck.right = rectCheck.left + m_checkListState.m_sizeCheck.cx; rectCheck.bottom = rectCheck.top + m_checkListState.m_sizeCheck.cy; CRect rectItem = drawItem.rcItem; rectItem.right = rectItem.left + m_checkListState.m_sizeCheck.cx + 2; CRect rectCheckBox = OnGetCheckPosition(rectItem, rectCheck); CBrush brush(newBkColor); pDC->FillRect(rectItem, &brush); pDC->BitBlt(rectCheckBox.left, rectCheckBox.top, m_checkListState.m_sizeCheck.cx, m_checkListState.m_sizeCheck.cy, &bitmapDC, m_checkListState.m_sizeCheck.cx * nCheck, 0, SRCCOPY); ::SelectObject(bitmapDC.m_hDC, hOldBitmap); brush.DeleteObject(); } pDC->SetBkColor(oldBkColor); bitmapDC.DeleteDC(); } if (drawItem.itemData != 0 && drawItem.itemData != (UINT)LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)drawItem.itemData; drawItem.itemData = pState->m_dwUserData; } drawItem.rcItem.left = drawItem.rcItem.left + m_checkListState.m_sizeCheck.cx + 2; DrawItem(&drawItem); }*/ void CXTPCheckListBox::PreMeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { int cyItem = CalcMinimumItemHeight(); MEASUREITEMSTRUCT measureItem; MEMCPY_S(&measureItem, lpMeasureItemStruct, sizeof(MEASUREITEMSTRUCT)); measureItem.itemHeight = XTPToUIntChecked(cyItem); measureItem.itemWidth = (UINT)-1; // WINBUG: Windows95 and Windows NT disagree on what this value // should be. According to the docs, they are both wrong if (GetStyle() & LBS_OWNERDRAWVARIABLE) { LRESULT lResult = DefWindowProc(LB_GETITEMDATA, measureItem.itemID, 0); if (lResult != LB_ERR) measureItem.itemData = (UINT)lResult; else measureItem.itemData = 0; // WINBUG: This is only done in the LBS_OWNERDRAWVARIABLE case // because Windows 95 does not initialize itemData to zero in the // case of LBS_OWNERDRAWFIXED list boxes (it is stack garbage). if (measureItem.itemData != 0 && measureItem.itemData != (UINT)LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)measureItem.itemData; measureItem.itemData = pState->m_dwUserData; } } MeasureItem(&measureItem); lpMeasureItemStruct->itemHeight = max(measureItem.itemHeight, (UINT)cyItem); lpMeasureItemStruct->itemWidth = measureItem.itemWidth; } int CXTPCheckListBox::PreCompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct) { COMPAREITEMSTRUCT compareItem; MEMCPY_S(&compareItem, lpCompareItemStruct, sizeof(COMPAREITEMSTRUCT)); if (compareItem.itemData1 != 0 && compareItem.itemData1 != (UINT)LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)compareItem.itemData1; compareItem.itemData1 = pState->m_dwUserData; } if (compareItem.itemData2 != 0 && compareItem.itemData2 != (UINT)LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)compareItem.itemData2; compareItem.itemData2 = pState->m_dwUserData; } return CompareItem(&compareItem); } void CXTPCheckListBox::PreDeleteItem(LPDELETEITEMSTRUCT lpDeleteItemStruct) { DELETEITEMSTRUCT deleteItem; MEMCPY_S(&deleteItem, lpDeleteItemStruct, sizeof(DELETEITEMSTRUCT)); // WINBUG: The following if block is required because Windows NT // version 3.51 does not properly fill out the LPDELETEITEMSTRUCT. if (deleteItem.itemData == 0) { LRESULT lResult = DefWindowProc(LB_GETITEMDATA, deleteItem.itemID, 0); if (lResult != LB_ERR) deleteItem.itemData = (UINT)lResult; } if (deleteItem.itemData != 0 && deleteItem.itemData != (UINT)LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)deleteItem.itemData; deleteItem.itemData = pState->m_dwUserData; delete pState; } DeleteItem(&deleteItem); } BOOL CXTPCheckListBox::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { switch (message) { case WM_DRAWITEM: _ASSERTE(pResult == NULL); // no return value expected PreDrawItem((LPDRAWITEMSTRUCT)lParam); break; case WM_MEASUREITEM: _ASSERTE(pResult == NULL); // no return value expected PreMeasureItem((LPMEASUREITEMSTRUCT)lParam); break; case WM_COMPAREITEM: _ASSERTE(pResult != NULL); // return value expected if (pResult == NULL) break; *pResult = PreCompareItem((LPCOMPAREITEMSTRUCT)lParam); break; case WM_DELETEITEM: _ASSERTE(pResult == NULL); // no return value expected PreDeleteItem((LPDELETEITEMSTRUCT)lParam); break; default: return CXTPListBox::OnChildNotify(message, wParam, lParam, pResult); } return TRUE; } void CXTPCheckListBox::PreSubclassWindow() { CXTPListBox::PreSubclassWindow(); #ifdef _DEBUG // CCheckListBoxes must be owner drawn _ASSERTE(GetStyle() & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)); #endif } int CXTPCheckListBox::CalcMinimumItemHeight() { int nResult; if ((GetStyle() & (LBS_HASSTRINGS | LBS_OWNERDRAWFIXED)) == (LBS_HASSTRINGS | LBS_OWNERDRAWFIXED)) { CClientDC dc(this); CFont* pOldFont = dc.SelectObject(GetFont()); TEXTMETRIC tm; VERIFY(dc.GetTextMetrics(&tm)); dc.SelectObject(pOldFont); m_cyText = tm.tmHeight; nResult = max(m_checkListState.m_sizeCheck.cy + 1, m_cyText); } else { nResult = m_checkListState.m_sizeCheck.cy + 1; } return nResult; } void CXTPCheckListBox::InvalidateCheck(int nIndex) { CRect rect; GetItemRect(nIndex, rect); rect.right = rect.left + m_checkListState.m_sizeCheck.cx + 2; InvalidateRect(rect, FALSE); } void CXTPCheckListBox::InvalidateItem(int nIndex) { CRect rect; GetItemRect(nIndex, rect); InvalidateRect(rect, FALSE); } int CXTPCheckListBox::CheckFromPoint(CPoint point, BOOL& bInCheck) { // assume did not hit anything bInCheck = FALSE; int nIndex = -1; if ((GetStyle() & (LBS_OWNERDRAWFIXED | LBS_MULTICOLUMN)) == LBS_OWNERDRAWFIXED) { // optimized case for ownerdraw fixed, single column int cyItem = GetItemHeight(0); if (point.y < cyItem * GetCount()) { nIndex = GetTopIndex() + point.y / cyItem; if (point.x < m_checkListState.m_sizeCheck.cx + 2) ++bInCheck; } } else { // general case for ownerdraw variable or multiple column int i; for (i = GetTopIndex(); i < GetCount(); i++) { CRect itemRect; GetItemRect(i, &itemRect); if (itemRect.PtInRect(point)) { nIndex = i; if (point.x < itemRect.left + m_checkListState.m_sizeCheck.cx + 2) ++bInCheck; break; } } } return nIndex; } void CXTPCheckListBox::SetSelectionCheck(int nCheck) { int iSelectedItem, nSelectedItems = GetSelCount(); if (nSelectedItems > 0) { CArray rgiSelectedItems; rgiSelectedItems.SetSize(nSelectedItems); int* piSelectedItems = rgiSelectedItems.GetData(); GetSelItems(nSelectedItems, piSelectedItems); for (iSelectedItem = 0; iSelectedItem < nSelectedItems; iSelectedItem++) { if (IsEnabled(piSelectedItems[iSelectedItem])) { SetCheck(piSelectedItems[iSelectedItem], nCheck); InvalidateCheck(piSelectedItems[iSelectedItem]); } } } } void CXTPCheckListBox::OnLButtonDown(UINT nFlags, CPoint point) { CRect itemRect; CRect clientRect; GetClientRect(clientRect); int nIndex; for (nIndex = GetTopIndex(); nIndex < GetCount(); nIndex++) { GetItemRect(nIndex, &itemRect); if (!clientRect.PtInRect(itemRect.TopLeft())) break; if (itemRect.PtInRect(point) && IsEnabled(nIndex)) { if (m_nStyle != BS_CHECKBOX && m_nStyle != BS_3STATE) { if (point.x - itemRect.left < m_checkListState.m_sizeCheck.cx + 2) { CWnd* pParent = GetParent(); _ASSERTE(pParent); int nModulo = (m_nStyle == BS_AUTO3STATE) ? 3 : 2; int nCheck = GetCheck(nIndex); nCheck = (nCheck == nModulo) ? nCheck - 1 : nCheck; SetCheck(nIndex, (nCheck + 1) % nModulo); InvalidateCheck(nIndex); CXTPListBox::OnLButtonDown(nFlags, point); // Inform of check pParent->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), CLBN_CHKCHANGE), (LPARAM)m_hWnd); return; } } else return; // Swallow LButtons for disabled items } } // do default listbox selection logic CXTPListBox::OnLButtonDown(nFlags, point); } void CXTPCheckListBox::OnLButtonDblClk(UINT nFlags, CPoint point) { CRect itemRect; CRect clientRect; GetClientRect(clientRect); int nIndex; for (nIndex = GetTopIndex(); nIndex < GetCount(); nIndex++) { GetItemRect(nIndex, &itemRect); if (!clientRect.PtInRect(itemRect.TopLeft())) break; if (itemRect.PtInRect(point) && IsEnabled(nIndex)) { if (m_nStyle != BS_CHECKBOX && m_nStyle != BS_3STATE) { CWnd* pParent = GetParent(); _ASSERTE(pParent); int nModulo = (m_nStyle == BS_AUTO3STATE) ? 3 : 2; int nCheck = GetCheck(nIndex); nCheck = (nCheck == nModulo) ? nCheck - 1 : nCheck; SetCheck(nIndex, (nCheck + 1) % nModulo); InvalidateCheck(nIndex); CXTPListBox::OnLButtonDown(nFlags, point); // Inform of check pParent->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), CLBN_CHKCHANGE), (LPARAM)m_hWnd); return; } else return; // Swallow LButtons for disabled items } } CXTPListBox::OnLButtonDblClk(nFlags, point); } void CXTPCheckListBox::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if (nChar == VK_SPACE) { int nIndex = GetCaretIndex(); CWnd* pParent = GetParent(); _ASSERTE(pParent); if (nIndex != LB_ERR) { if (m_nStyle != BS_CHECKBOX && m_nStyle != BS_3STATE) { if ((GetStyle() & LBS_MULTIPLESEL) != 0) { if (IsEnabled(nIndex)) { BOOL bSelected = GetSel(nIndex); if (bSelected) { int nModulo = (m_nStyle == BS_AUTO3STATE) ? 3 : 2; int nCheck = GetCheck(nIndex); nCheck = (nCheck == nModulo) ? nCheck - 1 : nCheck; SetCheck(nIndex, (nCheck + 1) % nModulo); // Inform of check pParent->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), CLBN_CHKCHANGE), (LPARAM)m_hWnd); } SetSel(nIndex, !bSelected); } else SetSel(nIndex, FALSE); // unselect disabled items return; } else { // If there is a selection, the space bar toggles that check, // all other keys are the same as a standard listbox. if (IsEnabled(nIndex)) { int nModulo = (m_nStyle == BS_AUTO3STATE) ? 3 : 2; int nCheck = GetCheck(nIndex); nCheck = (nCheck == nModulo) ? nCheck - 1 : nCheck; int nNewCheck = (nCheck + 1) % nModulo; SetCheck(nIndex, nNewCheck); InvalidateCheck(nIndex); if (GetStyle() & LBS_EXTENDEDSEL) { // The listbox is a multi-select listbox, and the user // clicked on a selected check, so change the check on all // of the selected items. SetSelectionCheck(nNewCheck); } // Inform of check pParent->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), CLBN_CHKCHANGE), (LPARAM)m_hWnd); } else SetSel(nIndex, FALSE); // unselect disabled items return; } } } } CXTPListBox::OnKeyDown(nChar, nRepCnt, nFlags); } int CXTPCheckListBox::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CXTPListBox::OnCreate(lpCreateStruct) == -1) return -1; if ((GetStyle() & (LBS_OWNERDRAWFIXED | LBS_HASSTRINGS)) == (LBS_OWNERDRAWFIXED | LBS_HASSTRINGS)) { SetItemHeight(0, XTPToUIntChecked(CalcMinimumItemHeight())); } return 0; } LRESULT CXTPCheckListBox::OnSetFont(WPARAM, LPARAM) { Default(); if ((GetStyle() & (LBS_OWNERDRAWFIXED | LBS_HASSTRINGS)) == (LBS_OWNERDRAWFIXED | LBS_HASSTRINGS)) { SetItemHeight(0, XTPToUIntChecked(CalcMinimumItemHeight())); } return 0; } LRESULT CXTPCheckListBox::OnLBAddString(WPARAM wParam, LPARAM lParam) { CHECK_DATA* pState = NULL; if (!(GetStyle() & LBS_HASSTRINGS)) { pState = new CHECK_DATA; pState->m_dwUserData = XTPToUIntPtr(lParam); lParam = XTPToLPARAM(pState); } LRESULT lResult = DefWindowProc(LB_ADDSTRING, wParam, lParam); if (lResult == LB_ERR && pState != NULL) delete pState; return lResult; } LRESULT CXTPCheckListBox::OnLBFindString(WPARAM wParam, LPARAM lParam) { if (GetStyle() & LBS_HASSTRINGS) return DefWindowProc(LB_FINDSTRING, wParam, lParam); int nIndex = (int)wParam; if (nIndex == -1) nIndex = 0; for (; nIndex < GetCount(); nIndex++) if ((UINT_PTR)lParam == GetItemData(nIndex)) return nIndex; return LB_ERR; } LRESULT CXTPCheckListBox::OnLBFindStringExact(WPARAM wParam, LPARAM lParam) { if (GetStyle() & (LBS_HASSTRINGS | LBS_SORT)) return DefWindowProc(LB_FINDSTRINGEXACT, wParam, lParam); int nIndex = (int)wParam; if (nIndex == -1) nIndex = 0; for (; nIndex < GetCount(); nIndex++) if ((UINT_PTR)lParam == GetItemData(nIndex)) return nIndex; return LB_ERR; } LRESULT CXTPCheckListBox::OnLBGetItemData(WPARAM wParam, LPARAM lParam) { LRESULT lResult = DefWindowProc(LB_GETITEMDATA, wParam, lParam); if (lResult != LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)lResult; if (pState == NULL) return 0; // default lResult = XTPToLRESULT(pState->m_dwUserData); } return lResult; } LRESULT CXTPCheckListBox::OnLBGetText(WPARAM wParam, LPARAM lParam) { LRESULT lResult = DefWindowProc(LB_GETTEXT, wParam, lParam); if (GetStyle() & LBS_HASSTRINGS) return lResult; if (lResult != LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)lParam; if (pState != NULL) lParam = XTPToLPARAM(pState->m_dwUserData); } return lResult; } LRESULT CXTPCheckListBox::OnLBInsertString(WPARAM wParam, LPARAM lParam) { CHECK_DATA* pState = NULL; if (!(GetStyle() & LBS_HASSTRINGS)) { pState = new CHECK_DATA; pState->m_dwUserData = XTPToUIntPtr(lParam); lParam = XTPToLPARAM(pState); } LRESULT lResult = DefWindowProc(LB_INSERTSTRING, wParam, lParam); if (lResult == LB_ERR && pState != NULL) delete pState; return lResult; } LRESULT CXTPCheckListBox::OnLBSelectString(WPARAM wParam, LPARAM lParam) { if (GetStyle() & LBS_HASSTRINGS) return DefWindowProc(LB_SELECTSTRING, wParam, lParam); int nIndex = (int)wParam; if (nIndex == -1) nIndex = 0; for (; nIndex < GetCount(); nIndex++) if ((UINT_PTR)lParam == GetItemData(nIndex)) { SetCurSel(nIndex); return nIndex; } return LB_ERR; } LRESULT CXTPCheckListBox::OnLBSetItemData(WPARAM wParam, LPARAM lParam) { LRESULT lResult = DefWindowProc(LB_GETITEMDATA, XTPToWPARAM(wParam), 0); if (lResult != LB_ERR) { CHECK_DATA* pState = (CHECK_DATA*)lResult; if (pState == NULL) pState = new CHECK_DATA; pState->m_dwUserData = XTPToUIntPtr(lParam); lResult = DefWindowProc(LB_SETITEMDATA, wParam, XTPToLPARAM(pState)); if (lResult == LB_ERR) delete pState; } return lResult; } LRESULT CXTPCheckListBox::OnLBSetItemHeight(WPARAM wParam, LPARAM lParam) { int nHeight = max(CalcMinimumItemHeight(), (int)LOWORD(lParam)); return DefWindowProc(LB_SETITEMHEIGHT, wParam, MAKELPARAM(nHeight, 0)); }