// NGDisabledItemComboBox.cpp : implementation file
//
// This code is derived from that accompanying the article
// "CComboBox with disabled items" published by Petr Novotny
// (Petr.Novotny@antek.cz) on www.codeproject.com
//
// Modifications for compatibility with dropped lists by Anna-Jayne Metcalfe
// (ajm@sonardyne.co.uk or code@annasplace.me.uk)
//
#include "StdAfx.h"
#include "NGDisabledItemComboBox.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
const UINT nMessage=::RegisterWindowMessage( _T("ComboSelEndOK") ); // _T("") needed for Unicode builds
/////////////////////////////////////////////////////////////////////////////
// CNGDisabledItemComboBox
CNGDisabledItemComboBox::CNGDisabledItemComboBox(void)
{
m_ListBox.SetParent(this);
}
CNGDisabledItemComboBox::~CNGDisabledItemComboBox(void)
{
}
// default implementation
BOOL CNGDisabledItemComboBox::IsItemEnabled(UINT nIndex) const
{
if (nIndex < (DWORD)GetCount())
{
DWORD uData=GetItemData(nIndex);
return (uData&1);
}
return TRUE; // whatever
}
BEGIN_MESSAGE_MAP(CNGDisabledItemComboBox, CComboBox)
//{{AFX_MSG_MAP(CNGDisabledItemComboBox)
ON_WM_CHARTOITEM()
ON_CONTROL_REFLECT(CBN_SELENDOK, OnSelendok)
ON_WM_KEYDOWN()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_CTLCOLORLISTBOX, OnCtlColor)
ON_REGISTERED_MESSAGE(nMessage, OnRealSelEndOK)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CNGDisabledItemComboBox message handlers
void CNGDisabledItemComboBox::PreSubclassWindow(void)
{
ASSERT( (GetStyle() & (CBS_OWNERDRAWFIXED | CBS_HASSTRINGS)) == (CBS_OWNERDRAWFIXED | CBS_HASSTRINGS) );
CComboBox::PreSubclassWindow();
}
void CNGDisabledItemComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle (lpDrawItemStruct->hDC);
if (((LONG)(lpDrawItemStruct->itemID) >= 0) &&
(lpDrawItemStruct->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)))
{
BOOL bDisabled = !IsWindowEnabled () || !IsItemEnabled(lpDrawItemStruct->itemID);
COLORREF newTextColor = bDisabled ?
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 (!bDisabled && ((lpDrawItemStruct->itemState & ODS_SELECTED) != 0))
{
pDC->SetTextColor (GetSysColor (COLOR_HIGHLIGHTTEXT));
pDC->SetBkColor (GetSysColor (COLOR_HIGHLIGHT));
}
CString sText;
GetLBText(lpDrawItemStruct->itemID, sText);
const RECT &rc=lpDrawItemStruct->rcItem;
pDC->ExtTextOut(rc.left + 2,
rc.top + 2,// + max(0, (cyItem - m_cyText) / 2),
ETO_OPAQUE, &rc,
sText, sText.GetLength (), NULL);
pDC->SetTextColor (oldTextColor);
pDC->SetBkColor (oldBkColor);
}
else if ((LONG)(lpDrawItemStruct->itemID)<0) // drawing edit text
{
COLORREF newTextColor = GetSysColor (COLOR_WINDOWTEXT); // light gray
COLORREF oldTextColor = pDC->SetTextColor (newTextColor);
COLORREF newBkColor = GetSysColor (COLOR_WINDOW);
COLORREF oldBkColor = pDC->SetBkColor (newBkColor);
if ((lpDrawItemStruct->itemState & ODS_SELECTED) != 0)
{
pDC->SetTextColor (GetSysColor (COLOR_HIGHLIGHTTEXT));
pDC->SetBkColor (GetSysColor (COLOR_HIGHLIGHT));
}
CString sText;
GetWindowText(sText);
const RECT &rc=lpDrawItemStruct->rcItem;
pDC->ExtTextOut(rc.left + 2,
rc.top + 2,// + max(0, (cyItem - m_cyText) / 2),
ETO_OPAQUE, &rc,
sText, sText.GetLength (), NULL);
pDC->SetTextColor (oldTextColor);
pDC->SetBkColor (oldBkColor);
}
if ((lpDrawItemStruct->itemAction & ODA_FOCUS) != 0)
pDC->DrawFocusRect(&lpDrawItemStruct->rcItem);
}
void CNGDisabledItemComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
// TODO: Add your code to determine the size of specified item
UNREFERENCED_PARAMETER(lpMeasureItemStruct);
}
int CNGDisabledItemComboBox::OnCharToItem(UINT nChar, CListBox* pListBox, UINT nIndex)
{
// TODO: Add your message handler code here and/or call default
int nItem = CComboBox::OnCharToItem(nChar, pListBox, nIndex);
if ( (nItem >= 0) && !IsItemEnabled(nItem))
{
return -2;
}
return nItem;
}
void CNGDisabledItemComboBox::OnSelendok(void)
{
GetWindowText(m_sSavedText);
PostMessage(nMessage);
}
LRESULT CNGDisabledItemComboBox::OnRealSelEndOK(WPARAM,LPARAM)
{
CString sCurrentText;
GetWindowText(sCurrentText);
int nIndex = FindStringExact(-1, sCurrentText);
if (nIndex >=0 && !IsItemEnabled(nIndex))
{
SetWindowText(m_sSavedText);
GetParent()->SendMessage( WM_COMMAND,
MAKELONG( GetWindowLong(m_hWnd, GWL_ID), CBN_SELCHANGE),
(LPARAM)m_hWnd);
}
return 0;
}
LRESULT CNGDisabledItemComboBox::OnCtlColor(WPARAM,LPARAM lParam)
{
if ( (m_ListBox.m_hWnd == NULL) && (lParam != 0) && (lParam != (LPARAM)m_hWnd) )
{
m_ListBox.SubclassWindow((HWND)lParam);
}
return Default();
}
void CNGDisabledItemComboBox::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (!GetDroppedState())
{
switch (nChar)
{
case VK_UP:
{
// Go up until we find a valid selection
// then set the current selection to that one
int nSel = GetCurSel();
if (nSel != CB_ERR)
{
while (nSel > 0)
{
if (IsItemEnabled(--nSel))
{
SetCurSel(nSel);
break;
}
}
}
}
return;
case VK_DOWN:
{
// Go up until we find a valid selection
// then set the current selection to that one
int nSel = GetCurSel();
if (nSel != CB_ERR)
{
while (nSel < GetCount() )
{
if (IsItemEnabled(++nSel))
{
SetCurSel(nSel);
break;
}
}
}
}
return;
case VK_PRIOR:
{
// Select first non-disabled item
int nSel = 0;
while (nSel <= GetCount() )
{
if (IsItemEnabled(nSel))
{
SetCurSel(nSel);
break;
}
nSel++;
}
}
return;
case VK_NEXT:
{
// Select last non-disabled item
int nSel = GetCount();
while (nSel > 0)
{
if (IsItemEnabled(--nSel))
{
SetCurSel(nSel);
break;
}
}
}
return;
default:
break;
}
}
CComboBox::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CNGDisabledItemComboBox::PostNcDestroy(void)
{
// m_ListBox.UnsubclassWindow();
m_ListBox.Detach();
CComboBox::PostNcDestroy();
}
/////////////////////////////////////////////////////////////////////////////
// CListBoxInsideComboBox
CListBoxInsideComboBox::CListBoxInsideComboBox(void)
{
m_pParent = NULL;
}
CListBoxInsideComboBox::~CListBoxInsideComboBox(void)
{
}
BEGIN_MESSAGE_MAP(CListBoxInsideComboBox, CWnd)
//{{AFX_MSG_MAP(CListBoxInsideComboBox)
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_KEYDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CListBoxInsideComboBox operations
void CListBoxInsideComboBox::SetParent(CNGDisabledItemComboBox* pCB)
{
m_pParent = pCB;
}
/////////////////////////////////////////////////////////////////////////////
// CListBoxInsideComboBox message handlers
void CListBoxInsideComboBox::OnMouseMove(UINT nFlags, CPoint point)
{
CRect rect;
GetClientRect(rect);
if (rect.PtInRect(point))
{
BOOL bOutside = FALSE;
int nIndex = ((CListBox*)this)->ItemFromPoint(point, bOutside);
if (!bOutside && !m_pParent->IsItemEnabled(nIndex))
{
return; // don't click there
}
}
CWnd::OnMouseMove(nFlags, point);
}
void CListBoxInsideComboBox::OnLButtonDown(UINT nFlags, CPoint point)
{
CRect rect;
GetClientRect(rect);
if (rect.PtInRect(point))
{
BOOL bOutside = FALSE;
int nIndex = ((CListBox*)this)->ItemFromPoint(point, bOutside);
if (!bOutside && !m_pParent->IsItemEnabled(nIndex))
{
return; // don't click there
}
}
CWnd::OnLButtonDown(nFlags, point);
}
void CListBoxInsideComboBox::OnLButtonUp(UINT nFlags, CPoint point)
{
CRect rect;
GetClientRect(rect);
if (rect.PtInRect(point))
{
BOOL bOutside = FALSE;
int nIndex = ((CListBox*)this)->ItemFromPoint(point, bOutside);
if (!bOutside && !m_pParent->IsItemEnabled(nIndex))
{
return; // don't click there
}
}
CWnd::OnLButtonUp(nFlags, point);
}
void CListBoxInsideComboBox::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (m_pParent->GetDroppedState())
{
switch (nChar)
{
case VK_LEFT:
case VK_UP:
{
// Go up until we find a valid selection
// then set the current selection to that one
int nSel = ((CListBox*)this)->GetCurSel();
if (nSel != LB_ERR)
{
while (nSel > 0)
{
if (m_pParent->IsItemEnabled(--nSel))
{
((CListBox*)this)->SetCurSel(nSel);
break;
}
}
}
}
return;
case VK_RIGHT:
case VK_DOWN:
{
// Go up until we find a valid selection
// then set the current selection to that one
int nSel = ((CListBox*)this)->GetCurSel();
if (nSel != LB_ERR)
{
while (nSel < ((CListBox*)this)->GetCount() )
{
if (m_pParent->IsItemEnabled(++nSel))
{
((CListBox*)this)->SetCurSel(nSel);
break;
}
}
}
}
return;
case VK_HOME:
case VK_PRIOR:
{
// Select first non-disabled item
int nSel = 0;
while (nSel <= ((CListBox*)this)->GetCount() )
{
if (m_pParent->IsItemEnabled(nSel))
{
((CListBox*)this)->SetCurSel(nSel);
break;
}
nSel++;
}
}
return;
case VK_END:
case VK_NEXT:
{
// Select last non-disabled item
int nSel = ((CListBox*)this)->GetCount();
while (nSel > 0)
{
if (m_pParent->IsItemEnabled(--nSel))
{
((CListBox*)this)->SetCurSel(nSel);
break;
}
}
}
return;
default:
break;
}
}
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}