📄 ospcheckcomboboxex.cpp
字号:
// CheckComboBox.cpp
//
// Written by Magnus Egelberg (magnus.egelberg@lundalogik.se)
//
// Copyright (C) 1999, Lundalogik AB, Sweden. All rights reserved.
//
//
#include "stdafx.h"
#include "OSPComboBox.h"
#include "OSPCheckComboBoxEx.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
static WNDPROC m_pWndProc = 0;
static COSPCheckComboBoxEx *m_pComboBox = 0;
BEGIN_MESSAGE_MAP(COSPCheckComboBoxEx, CComboBox)
//{{AFX_MSG_MAP(COSPCheckComboBoxEx)
ON_MESSAGE(WM_CTLCOLORLISTBOX, OnCtlColorListBox)
ON_MESSAGE(WM_GETTEXT, OnGetText)
ON_MESSAGE(WM_GETTEXTLENGTH, OnGetTextLength)
ON_CONTROL_REFLECT(CBN_DROPDOWN, OnDropDown)
ON_CONTROL_REFLECT(CBN_DBLCLK, OnDblclk)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//
// The subclassed COMBOLBOX message handler
//
extern "C" LRESULT FAR PASCAL ComboBoxListBoxProcEx(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
switch (nMsg) {
case WM_RBUTTONDOWN: {
// If you want to select all/unselect all using the
// right button, remove this ifdef. Personally, I don't really like it
#if FALSE
if (m_pComboBox != 0) {
INT nCount = m_pComboBox->GetCount();
INT nSelCount = 0;
for (INT i = 0; i < nCount; i++) {
if (m_pComboBox->GetCheck(i))
nSelCount++;
}
m_pComboBox->SelectAll(nSelCount != nCount);
// Make sure to invalidate this window as well
InvalidateRect(hWnd, 0, FALSE);
m_pComboBox->GetParent()->SendMessage(WM_COMMAND, MAKELONG(GetWindowLong(m_pComboBox->m_hWnd, GWL_ID), CBN_SELCHANGE), (LPARAM)m_pComboBox->m_hWnd);
}
#endif
m_pComboBox->GetParent()->SendMessage(WM_COMMAND, MAKELONG(GetWindowLong(m_pComboBox->m_hWnd, GWL_ID), CBN_CLOSEUP), (LPARAM)m_pComboBox->m_hWnd);
break;
}
// Make the combobox always return -1 as the current selection. This
// causes the lpDrawItemStruct->itemID in DrawItem() to be -1
// when the always-visible-portion of the combo is drawn
case LB_GETCURSEL: {
return -1;
}
case WM_CHAR: {
if (wParam == VK_SPACE) {
// Get the current selection
INT nIndex = CallWindowProcA(m_pWndProc, hWnd, LB_GETCURSEL, wParam, lParam);
CRect rcItem;
SendMessage(hWnd, LB_GETITEMRECT, nIndex, (LONG)(VOID *)&rcItem);
InvalidateRect(hWnd, rcItem, FALSE);
// Invert the check mark
m_pComboBox->SetCheck(nIndex, !m_pComboBox->GetCheck(nIndex));
// Notify that selection has changed
m_pComboBox->GetParent()->SendMessage(WM_COMMAND, MAKELONG(GetWindowLong(m_pComboBox->m_hWnd, GWL_ID), CBN_SELCHANGE), (LPARAM)m_pComboBox->m_hWnd);
return 0;
}
break;
}
case WM_LBUTTONDOWN: {
CRect rcClient;
GetClientRect(hWnd, rcClient);
CPoint pt;
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
if (PtInRect(rcClient, pt)) {
INT nItemHeight = SendMessage(hWnd, LB_GETITEMHEIGHT, 0, 0);
INT nTopIndex = SendMessage(hWnd, LB_GETTOPINDEX, 0, 0);
// Compute which index to check/uncheck
INT nIndex = nTopIndex + pt.y / nItemHeight;
CRect rcItem;
SendMessage(hWnd, LB_GETITEMRECT, nIndex, (LONG)(VOID *)&rcItem);
if (PtInRect(rcItem, pt)) {
// Invalidate this window
InvalidateRect(hWnd, rcItem, FALSE);
m_pComboBox->SetCheck(nIndex, !m_pComboBox->GetCheck(nIndex));
// Notify that selection has changed
m_pComboBox->GetParent()->SendMessage(WM_COMMAND, MAKELONG(GetWindowLong(m_pComboBox->m_hWnd, GWL_ID), CBN_SELCHANGE), (LPARAM)m_pComboBox->m_hWnd);
}
}
// Do the default handling now (such as close the popup
// window when clicked outside)
break;
}
case WM_LBUTTONUP: {
// Don't do anything here. This causes the combobox popup
// windows to remain open after a selection has been made
return 0;
}
}
return CallWindowProc(m_pWndProc, hWnd, nMsg, wParam, lParam);
}
COSPCheckComboBoxEx::COSPCheckComboBoxEx()
{
m_hListBox = 0;
m_bTextUpdated = FALSE;
m_bItemHeightSet = FALSE;
}
COSPCheckComboBoxEx::~COSPCheckComboBoxEx()
{
}
BOOL COSPCheckComboBoxEx::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
// Remove the CBS_SIMPLE and CBS_DROPDOWN styles and add the one I'm designed for
dwStyle &= ~0xF;
dwStyle |= CBS_DROPDOWNLIST;
// Make sure to use the CBS_OWNERDRAWVARIABLE style
dwStyle |= CBS_OWNERDRAWVARIABLE;
// Use default strings. We need the itemdata to store checkmarks
dwStyle |= CBS_HASSTRINGS;
return CComboBox::Create(dwStyle, rect, pParentWnd, nID);
}
LRESULT COSPCheckComboBoxEx::OnCtlColorListBox(WPARAM wParam, LPARAM lParam)
{
// If the listbox hasn't been subclassed yet, do so...
if (m_hListBox == 0) {
HWND hWnd = (HWND)lParam;
if (hWnd != 0 && hWnd != m_hWnd) {
// Save the listbox handle
m_hListBox = hWnd;
// Do the subclassing
m_pWndProc = (WNDPROC)GetWindowLong(m_hListBox, GWL_WNDPROC);
SetWindowLong(m_hListBox, GWL_WNDPROC, (LONG)ComboBoxListBoxProcEx);
}
}
return DefWindowProc(WM_CTLCOLORLISTBOX, wParam, lParam);
}
void COSPCheckComboBoxEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
HDC dc = lpDrawItemStruct->hDC;
CRect rcBitmap = lpDrawItemStruct->rcItem;
CRect rcText = lpDrawItemStruct->rcItem;
CString strText;
// 0 - No check, 1 - Empty check, 2 - Checked
INT nCheck = 0;
// Check if we are drawing the static portion of the combobox
if ((LONG)lpDrawItemStruct->itemID < 0) {
// Make sure the m_strText member is updated
RecalcText();
// Get the text
strText = m_strText;
// Don't draw any boxes on this item
nCheck = 0;
}
// Otherwise it is one of the items
else {
GetLBText(lpDrawItemStruct->itemID, strText);
// [ Modified by P.J.Lee -- Change the data fromat to struct.
// nCheck = 1 + (GetItemData(lpDrawItemStruct->itemID) != 0);
nCheck = ((CheckComboBoxItemData*)GetItemDataPtr(lpDrawItemStruct->itemID))->itemState;
// ]
TEXTMETRIC metrics;
GetTextMetrics(dc, &metrics);
rcBitmap.left = 0;
rcBitmap.right = rcBitmap.left + metrics.tmHeight + metrics.tmExternalLeading + 6;
rcBitmap.top += 1;
rcBitmap.bottom -= 1;
rcText.left = rcBitmap.right;
}
if (nCheck >= 0) {
SetBkColor(dc, GetSysColor(COLOR_WINDOW));
SetTextColor(dc, GetSysColor(COLOR_WINDOWTEXT));
UINT nState = DFCS_BUTTONCHECK;
// [ Modified by P.J.Lee -- Change the check state define.
//if (nCheck > 1)
if (nCheck == COMBOBOX_CHECKED)
nState |= DFCS_CHECKED;
// ]
// Draw the checkmark using DrawFrameControl
DrawFrameControl(dc, rcBitmap, DFC_BUTTON, nState);
}
if (lpDrawItemStruct->itemState & ODS_SELECTED) {
SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
}
else {
SetBkColor(dc, GetSysColor(COLOR_WINDOW));
SetTextColor(dc, GetSysColor(COLOR_WINDOWTEXT));
}
// Erase and draw
ExtTextOut(dc, 0, 0, ETO_OPAQUE, &rcText, 0, 0, 0);
DrawText(dc, ' ' + strText, strText.GetLength() + 1, &rcText, DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS);
if ((lpDrawItemStruct->itemState & (ODS_FOCUS|ODS_SELECTED)) == (ODS_FOCUS|ODS_SELECTED))
DrawFocusRect(dc, &rcText);
}
void COSPCheckComboBoxEx::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
CClientDC dc(this);
CFont *pFont = dc.SelectObject(GetFont());
if (pFont != 0) {
TEXTMETRIC metrics;
dc.GetTextMetrics(&metrics);
lpMeasureItemStruct->itemHeight = metrics.tmHeight + metrics.tmExternalLeading;
// An extra height of 2 looks good I think.
// Otherwise the list looks a bit crowded...
lpMeasureItemStruct->itemHeight += 2;
// This is needed since the WM_MEASUREITEM message is sent before
// MFC hooks everything up if used in i dialog. So adjust the
// static portion of the combo box now
if (!m_bItemHeightSet) {
m_bItemHeightSet = TRUE;
SetItemHeight(-1, lpMeasureItemStruct->itemHeight);
}
dc.SelectObject(pFont);
}
}
//
// Make sure the combobox window handle is updated since
// there may be many COSPCheckComboBoxEx windows active
//
void COSPCheckComboBoxEx::OnDropDown()
{
m_pComboBox = this;
}
//
// Selects/unselects all items in the list
//
void COSPCheckComboBoxEx::SelectAll(BOOL bCheck)
{
INT nCount = GetCount();
for (INT i = 0; i < nCount; i++)
SetCheck(i, bCheck);
}
//
// By adding this message handler, we may use CWnd::GetText()
//
LRESULT COSPCheckComboBoxEx::OnGetText(WPARAM wParam, LPARAM lParam)
{
// Make sure the text is updated
RecalcText();
if (lParam == 0)
return 0;
// Copy the 'fake' window text
lstrcpyn((LPSTR)lParam, m_strText, (INT)wParam);
return m_strText.GetLength();
}
//
// By adding this message handler, we may use CWnd::GetTextLength()
//
LRESULT COSPCheckComboBoxEx::OnGetTextLength(WPARAM, LPARAM)
{
// Make sure the text is updated
RecalcText();
return m_strText.GetLength();
}
//
// This routine steps thru all the items and builds
// a string containing the checked items
//
void COSPCheckComboBoxEx::RecalcText()
{
if (!m_bTextUpdated) {
CString strText;
// Get the list count
INT nCount = GetCount();
// Get the list separator
TCHAR szBuffer[10] = {0};
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SLIST, szBuffer, sizeof(szBuffer));
CString strSeparator = szBuffer;
// If none found, the the ';'
if (strSeparator.GetLength() == 0)
strSeparator = ';';
// Trim extra spaces
strSeparator.TrimRight();
// And one...
strSeparator += ' ';
for (INT i = 0; i < nCount; i++) {
// [ Modified by P.J.Lee
//if (GetItemData(i)) {
if (GetCheck(i)) {
CString strItem;
GetLBText(i, strItem);
if (!strText.IsEmpty())
strText += strSeparator;
strText += strItem;
}
}
// Set the text
m_strText = strText;
m_bTextUpdated = TRUE;
}
}
INT COSPCheckComboBoxEx::SetCheck(INT nIndex, BOOL bFlag)
{
// [ Modified by P.J.Lee -- Get the check state.
CheckComboBoxItemData* pdata = (CheckComboBoxItemData*)GetItemDataPtr(nIndex);
if( !pdata )
return -1;
pdata->itemState = bFlag ? COMBOBOX_CHECKED : COMBOBOX_UNCHECKED;
INT nResult = SetItemDataPtr(nIndex, pdata);
//INT nResult = SetItemData(nIndex, bFlag);
// ]
if (nResult < 0)
return nResult;
// Signal that the text need updating
m_bTextUpdated = FALSE;
// Redraw the window
Invalidate(FALSE);
return nResult;
}
BOOL COSPCheckComboBoxEx::GetCheck(INT nIndex)
{
// [ Modified by P.J.Lee -- Get the check state.
CheckComboBoxItemData* pdata = (CheckComboBoxItemData*)GetItemDataPtr(nIndex);
if( !pdata )
return FALSE;
return pdata->itemState == COMBOBOX_CHECKED;
//return GetItemData(nIndex);
}
void COSPCheckComboBoxEx::ClearCaption()
{
m_strText="";
Invalidate(TRUE);
}
//////////////////////////////////////////////////////////////////////////
// Override the AddRow function of OSPComboBox. Add by P.J.Lee
void COSPCheckComboBoxEx::DeleteRow(int row)
{
TRACE("Delete Row (%d)\n", row);
CheckComboBoxItemData *pData = (CheckComboBoxItemData*)GetAttachedData(row);
if (pData)
delete pData;
VERIFY(-1 < DeleteString(row));
m_bTextUpdated = FALSE;
RecalcText();
}
void COSPCheckComboBoxEx::DeleteAllRows()
{
TRACE0("Deleted OSPComboBox ALL ROWS!\n");
CheckComboBoxItemData *pData;
for(int row = GetCount(); row; )
if((pData = (CheckComboBoxItemData*)GetAttachedData(--row)) != NULL)
delete pData;
ResetContent();
}
CheckComboBoxItemData*
COSPCheckComboBoxEx::GetAttachedData(int row)
{
if (row < 0)
return 0;
CheckComboBoxItemData* pData = (CheckComboBoxItemData*) GetItemDataPtr(row);
if (0 == pData)
return 0;
return pData;
}
void COSPCheckComboBoxEx::OnDblclk()
{
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -