Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1008 lines
28 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: chklist.cpp
//
// This file contains the implementation of the CheckList control.
//
//--------------------------------------------------------------------------
#include "aclpriv.h"
//
// Text and Background colors
//
#define TEXT_COLOR COLOR_WINDOWTEXT
#define BK_COLOR COLOR_WINDOW
//
// Default dimensions for child controls. All are in dialog units.
// Currently only the column width is user-adjustable (via the
// CLM_SETCOLUMNWIDTH message).
//
#define DEFAULT_COLUMN_WIDTH 40
#define DEFAULT_CHECK_WIDTH 9
#define DEFAULT_HORZ_SPACE 7
#define DEFAULT_VERTICAL_SPACE 3
#define DEFAULT_ITEM_HEIGHT 8
//
// 16 bits are used for the control ID's, divided into n bits for
// the subitem (least significant) and 16-n bits for the item index.
//
// ID_SUBITEM_BITS can be adjusted to control the maximum number of
// items and subitems. For example, to allow up to 7 subitems and 8k
// items, set ID_SUBITEM_BITS to 3.
//
// Use the low 2 bits for the subitem index, the rest for the item index.
// (4 subitems max, 16k items max)
#define ID_SUBITEM_BITS 2
#define ID_SUBITEM_MASK ((1 << ID_SUBITEM_BITS) - 1)
#define GET_ITEM(id) ((id) >> ID_SUBITEM_BITS)
#define GET_SUBITEM(id) ((id) & ID_SUBITEM_MASK)
#define MAKE_CTRL_ID(i, s) (0xffff & (((i) << ID_SUBITEM_BITS) | ((s) & ID_SUBITEM_MASK)))
#define MAKE_LABEL_ID(i) MAKE_CTRL_ID(i, 0)
// Note that the subitem (column) index is one-based for the checkboxes
// (the zero column is the label). The item (row) index is zero-based.
#define MAX_CHECK_COLUMNS ID_SUBITEM_MASK
TCHAR const c_szStaticClass[] = TEXT("STATIC");
TCHAR const c_szButtonClass[] = TEXT("BUTTON");
class CCheckList
{
private:
LONG m_cItems;
LONG m_cSubItems;
RECT m_rcItemLabel;
LONG m_nCheckPos[MAX_CHECK_COLUMNS];
LONG m_cxCheckBox;
LONG m_cxCheckColumn;
HWND m_hwndCheckFocus;
LPTSTR m_pszColumnDesc[MAX_CHECK_COLUMNS];
int m_cWheelDelta;
static UINT g_ucScrollLines;
private:
CCheckList(HWND hWnd, LPCREATESTRUCT lpcs);
~CCheckList(void);
LRESULT MsgCommand(HWND hWnd, WORD idCmd, WORD wNotify, HWND hwndCtrl);
void MsgPaint(HWND hWnd, HDC hdc);
void MsgVScroll(HWND hWnd, int nCode, int nPos);
void MsgMouseWheel(HWND hWnd, WORD fwFlags, int zDelta);
void MsgButtonDown(HWND hWnd, WPARAM fwFlags, int xPos, int yPos);
void MsgEnable(HWND hWnd, BOOL fEnabled);
void MsgSize(HWND hWnd, DWORD dwSizeType, LONG nWidth, LONG nHeight);
LONG AddItem(HWND hWnd, LPCTSTR pszLabel, LPARAM lParam);
void SetState(HWND hWnd, WORD iItem, WORD iSubItem, LONG lState);
LONG GetState(HWND hWnd, WORD iItem, WORD iSubItem);
void SetColumnWidth(HWND hWnd, LONG cxDialog, LONG cxColumn);
void ResetContent(HWND hWnd);
LONG GetVisibleCount(HWND hWnd);
LONG GetTopIndex(HWND hWnd);
void SetTopIndex(HWND hWnd, LONG nIndex)
{ m_cWheelDelta = 0; MsgVScroll(hWnd, SB_THUMBPOSITION, nIndex * m_rcItemLabel.bottom); }
void EnsureVisible(HWND hWnd, LONG nIndex);
void DrawCheckFocusRect(HWND hWnd, HWND hwndCheck, BOOL fDraw);
void GetColumnDescriptions(HWND hWnd);
public:
static LRESULT CALLBACK WindowProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
};
BOOL RegisterCheckListWndClass(void)
{
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = CCheckList::WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hModule;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(BK_COLOR+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = TEXT(WC_CHECKLIST);
return (BOOL)RegisterClass(&wc);
}
UINT CCheckList::g_ucScrollLines = (UINT)-1;
CCheckList::CCheckList(HWND hWnd, LPCREATESTRUCT lpcs)
: m_cItems(0), m_hwndCheckFocus(NULL), m_cWheelDelta(0)
{
TraceEnter(TRACE_CHECKLIST, "CCheckList::CCheckList");
TraceAssert(hWnd != NULL);
TraceAssert(lpcs != NULL);
//
// Get number of check columns
//
m_cSubItems = lpcs->style & CLS_CHECKMASK;
//
// Convert default coordinates from dialog units to pixels
//
RECT rc;
rc.left = DEFAULT_CHECK_WIDTH;
rc.right = DEFAULT_COLUMN_WIDTH;
rc.top = rc.bottom = 0;
MapDialogRect(lpcs->hwndParent, &rc);
// Save the converted values
m_cxCheckBox = rc.left;
m_cxCheckColumn = rc.right;
rc.left = DEFAULT_HORZ_SPACE;
rc.top = DEFAULT_VERTICAL_SPACE;
rc.right = 10; // bogus (unused)
rc.bottom = DEFAULT_VERTICAL_SPACE + DEFAULT_ITEM_HEIGHT;
MapDialogRect(lpcs->hwndParent, &rc);
// Save the converted values
m_rcItemLabel = rc;
//
// Get info for mouse wheel scrolling
//
if ((UINT)-1 == g_ucScrollLines)
{
g_ucScrollLines = 3; // default
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &g_ucScrollLines, 0);
}
TraceLeaveVoid();
}
CCheckList::~CCheckList(void)
{
for (LONG j = 0; j < m_cSubItems; j++)
{
LocalFreeString(&m_pszColumnDesc[j]);
}
}
LRESULT
CCheckList::MsgCommand(HWND hWnd, WORD idCmd, WORD wNotify, HWND hwndCtrl)
{
TraceEnter(TRACE_CHECKLIST, "CCheckList::MsgCommand");
// Should only get notifications from visible, enabled, check boxes
TraceAssert(GET_ITEM(idCmd) < m_cItems);
TraceAssert(0 < GET_SUBITEM(idCmd) && GET_SUBITEM(idCmd) <= m_cSubItems);
TraceAssert(hwndCtrl && IsWindowEnabled(hwndCtrl));
switch (wNotify)
{
case BN_CLICKED:
{
NM_CHECKLIST nmc;
nmc.hdr.hwndFrom = hWnd;
nmc.hdr.idFrom = GetDlgCtrlID(hWnd);
nmc.hdr.code = CLN_CLICK;
nmc.iItem = GET_ITEM(idCmd);
nmc.iSubItem = GET_SUBITEM(idCmd);
nmc.dwState = (DWORD)SendMessage(hwndCtrl, BM_GETCHECK, 0, 0);
if (!IsWindowEnabled(hwndCtrl))
nmc.dwState |= CLST_DISABLED;
nmc.dwItemData = GetWindowLongPtr(GetDlgItem(hWnd, MAKE_LABEL_ID(nmc.iItem)),
GWLP_USERDATA);
nmc.cchTextMax = 0;
nmc.pszText = NULL;
SendMessage(GetParent(hWnd),
WM_NOTIFY,
nmc.hdr.idFrom,
(LPARAM)&nmc);
}
break;
case BN_SETFOCUS:
if (m_hwndCheckFocus != hwndCtrl) // Has the focus moved?
{
// Remember where the focus is
m_hwndCheckFocus = hwndCtrl;
// Make sure the row is scrolled into view
EnsureVisible(hWnd, GET_ITEM(idCmd));
}
// Always draw the focus rect
DrawCheckFocusRect(hWnd, hwndCtrl, TRUE);
break;
case BN_KILLFOCUS:
// Remove the focus rect
m_hwndCheckFocus = NULL;
DrawCheckFocusRect(hWnd, hwndCtrl, FALSE);
break;
}
TraceLeaveValue(0);
}
void
CCheckList::MsgPaint(HWND hWnd, HDC hdc)
{
if (hdc == NULL && m_hwndCheckFocus != NULL)
{
// This will cause a focus rect to be drawn after the window and
// all checkboxes have been painted.
PostMessage(hWnd,
WM_COMMAND,
GET_WM_COMMAND_MPS(GetDlgCtrlID(m_hwndCheckFocus), m_hwndCheckFocus, BN_SETFOCUS));
}
// Default paint
DefWindowProc(hWnd, WM_PAINT, (WPARAM)hdc, 0);
}
void
CCheckList::MsgVScroll(HWND hWnd, int nCode, int nPos)
{
UINT cScrollUnitsPerLine;
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
if (!GetScrollInfo(hWnd, SB_VERT, &si))
return;
cScrollUnitsPerLine = m_rcItemLabel.bottom;
// One page is always visible, so adjust the range to a more useful value
si.nMax -= si.nPage - 1;
switch (nCode)
{
case SB_LINEUP:
// "line" is the height of one item (includes the space in between)
nPos = si.nPos - cScrollUnitsPerLine;
break;
case SB_LINEDOWN:
nPos = si.nPos + cScrollUnitsPerLine;
break;
case SB_PAGEUP:
nPos = si.nPos - si.nPage;
break;
case SB_PAGEDOWN:
nPos = si.nPos + si.nPage;
break;
case SB_TOP:
nPos = si.nMin;
break;
case SB_BOTTOM:
nPos = si.nMax;
break;
case SB_ENDSCROLL:
nPos = si.nPos; // don't go anywhere
break;
case SB_THUMBTRACK:
// Do nothing here to allow tracking
// nPos = si.nPos; // Do this to prevent tracking
case SB_THUMBPOSITION:
// nothing to do here... nPos is passed in
break;
}
// Make sure the new position is within the range
if (nPos < si.nMin)
nPos = si.nMin;
else if (nPos > si.nMax)
nPos = si.nMax;
if (nPos != si.nPos) // are we moving?
{
SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
ScrollWindow(hWnd, 0, si.nPos - nPos, NULL, NULL);
}
}
void
CCheckList::MsgMouseWheel(HWND hWnd, WORD fwFlags, int iWheelDelta)
{
int cDetants;
if ((fwFlags & (MK_SHIFT | MK_CONTROL)) || 0 == g_ucScrollLines)
return;
TraceEnter(TRACE_CHECKLIST, "CCheckList::MsgMouseWheel");
// Update count of scroll amount
m_cWheelDelta -= iWheelDelta;
cDetants = m_cWheelDelta / WHEEL_DELTA;
if (0 == cDetants)
TraceLeaveVoid();
m_cWheelDelta %= WHEEL_DELTA;
if (WS_VSCROLL & GetWindowLong(hWnd, GWL_STYLE))
{
SCROLLINFO si;
UINT cScrollUnitsPerLine;
UINT cLinesPerPage;
UINT cLinesPerDetant;
// Get the scroll amount of one line
cScrollUnitsPerLine = m_rcItemLabel.bottom;
TraceAssert(cScrollUnitsPerLine > 0);
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS;
if (!GetScrollInfo(hWnd, SB_VERT, &si))
TraceLeaveVoid();
// The size of a page is at least one line, and
// leaves one line of overlap
cLinesPerPage = (si.nPage - cScrollUnitsPerLine) / cScrollUnitsPerLine;
cLinesPerPage = max(1, cLinesPerPage);
// Don't scroll more than one page per detant
cLinesPerDetant = min(cLinesPerPage, g_ucScrollLines);
si.nPos += cDetants * cLinesPerDetant * cScrollUnitsPerLine;
MsgVScroll(hWnd, SB_THUMBTRACK, si.nPos);
}
TraceLeaveVoid();
}
void
CCheckList::MsgButtonDown(HWND hWnd, WPARAM /*fwFlags*/, int /*xPos*/, int yPos)
{
LONG nItemIndex;
HWND hwndCheck;
RECT rc;
// Get position of the top visible item in client coords
nItemIndex = GetTopIndex(hWnd);
hwndCheck = GetDlgItem(hWnd, MAKE_CTRL_ID(nItemIndex, 0));
GetWindowRect(hwndCheck, &rc);
MapWindowPoints(NULL, hWnd, (LPPOINT)&rc, 2);
// Find nearest item
nItemIndex += (yPos - rc.top + m_rcItemLabel.top/2)/m_rcItemLabel.bottom;
nItemIndex = max(0, min(nItemIndex, m_cItems - 1)); // 0 <= y < m_cItems
// Set focus to first subitem that is enabled
for (LONG j = 1; j <= m_cSubItems; j++)
{
hwndCheck = GetDlgItem(hWnd, MAKE_CTRL_ID(nItemIndex, j));
if (IsWindowEnabled(hwndCheck))
{
SetFocus(hwndCheck);
break;
}
}
}
void
CCheckList::MsgEnable(HWND hWnd, BOOL fEnabled)
{
static BOOL bInMsgEnable = FALSE;
if (!bInMsgEnable)
{
bInMsgEnable = TRUE;
for (LONG i = 0; i < m_cItems; i++)
{
for (LONG j = 1; j <= m_cSubItems; j++)
{
EnableWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(i, j)), fEnabled);
}
}
if (!fEnabled)
EnableWindow(hWnd, TRUE);
bInMsgEnable = FALSE;
}
}
void
CCheckList::MsgSize(HWND hWnd, DWORD dwSizeType, LONG nWidth, LONG nHeight)
{
TraceEnter(TRACE_CHECKLIST, "CCheckList::MsgSize");
TraceAssert(hWnd != NULL);
if (dwSizeType == SIZE_RESTORED)
{
RECT rc;
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = m_cItems * m_rcItemLabel.bottom + m_rcItemLabel.top - 1;
si.nPage = nHeight; // ^^^^^^^^^ extra space
SetScrollInfo(hWnd, SB_VERT, &si, FALSE);
// Don't trust the width value passed in, since SetScrollInfo may
// affect it if the scroll bar is turning on or off.
GetClientRect(hWnd, &rc);
nWidth = rc.right;
// If the scrollbar is turned on, artificially bump up the width
// by the width of the scrollbar, so the boxes don't jump to the left
// when we have a scrollbar.
if ((UINT)si.nMax >= si.nPage)
nWidth += GetSystemMetrics(SM_CYHSCROLL);
SetColumnWidth(hWnd, nWidth, m_cxCheckColumn);
}
TraceLeaveVoid();
}
LONG
CCheckList::AddItem(HWND hWnd, LPCTSTR pszLabel, LPARAM lParam)
{
HWND hwndNew;
HWND hwndPrev;
RECT rc;
LONG cyOffset;
TraceEnter(TRACE_CHECKLIST, "CCheckList::AddItem");
TraceAssert(hWnd != NULL);
TraceAssert(pszLabel != NULL && !IsBadStringPtr(pszLabel, MAX_PATH));
// If this is the first item, get column descriptions
if (0 == m_cItems)
GetColumnDescriptions(hWnd);
// Calculate the position of the new static label
rc = m_rcItemLabel;
cyOffset = m_cItems * m_rcItemLabel.bottom;
OffsetRect(&rc, 0, cyOffset);
// Create a new label control
hwndNew = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
c_szStaticClass,
pszLabel,
WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP | SS_NOPREFIX,// | WS_GROUP,
rc.left,
rc.top,
rc.right - rc.left,
rc.bottom - rc.top,
hWnd,
(HMENU)IntToPtr(MAKE_LABEL_ID(m_cItems)),
hModule,
NULL);
if (!hwndNew)
TraceLeaveValue(-1);
// Save item data
SetWindowLongPtr(hwndNew, GWLP_USERDATA, lParam);
// Set the font
SendMessage(hwndNew,
WM_SETFONT,
SendMessage(GetParent(hWnd), WM_GETFONT, 0, 0),
0);
// Set Z-order position just after the last checkbox. This keeps
// tab order correct.
if (m_cItems > 0)
{
hwndPrev = GetDlgItem(hWnd, MAKE_CTRL_ID(m_cItems - 1, m_cSubItems));
SetWindowPos(hwndNew,
hwndPrev,
0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
// Create new checkboxes
for (LONG j = 0; j < m_cSubItems; j++)
{
// Build window text for the control. The text is
// hidden, but used for accessibility. (341042)
LPCTSTR pszCheckText = pszLabel;
LPTSTR pszT = NULL;
if (m_pszColumnDesc[j] &&
FormatStringID(&pszT, hModule, IDS_FMT_CHECKLABEL, pszLabel, m_pszColumnDesc[j]))
{
pszCheckText = pszT;
}
hwndPrev = hwndNew;
hwndNew = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
c_szButtonClass,
pszCheckText,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_NOTIFY | BS_FLAT | BS_AUTOCHECKBOX,
m_nCheckPos[j],
rc.top,
m_cxCheckBox,
rc.bottom - rc.top,
hWnd,
(HMENU)IntToPtr(MAKE_CTRL_ID(m_cItems, j + 1)),
hModule,
NULL);
LocalFreeString(&pszT);
if (!hwndNew)
{
while (j >= 0)
{
DestroyWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(m_cItems, j)));
j--;
}
TraceLeaveValue(-1);
}
SetWindowPos(hwndNew,
hwndPrev,
0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
// We now officially have a new item
m_cItems++;
//
// The last thing is to calculate the scroll range
//
LONG nBottom = rc.bottom;
GetClientRect(hWnd, &rc);
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = nBottom + m_rcItemLabel.top - 1;
si.nPage = rc.bottom; // ^^^^^^^^^ extra space
SetScrollInfo(hWnd, SB_VERT, &si, FALSE);
TraceLeaveValue(m_cItems - 1); // return the index of the new item
}
void
CCheckList::SetState(HWND hWnd, WORD iItem, WORD iSubItem, LONG lState)
{
TraceEnter(TRACE_CHECKLIST, "CCheckList::SetState");
TraceAssert(hWnd != NULL);
TraceAssert(iItem < m_cItems);
TraceAssert(0 < iSubItem && iSubItem <= m_cSubItems);
HWND hwndCtrl = GetDlgItem(hWnd, MAKE_CTRL_ID(iItem, iSubItem));
if (hwndCtrl != NULL)
{
SendMessage(hwndCtrl, BM_SETCHECK, lState & CLST_CHECKED, 0);
EnableWindow(hwndCtrl, !(lState & CLST_DISABLED));
}
TraceLeaveVoid();
}
LONG
CCheckList::GetState(HWND hWnd, WORD iItem, WORD iSubItem)
{
LONG lState = 0;
TraceEnter(TRACE_CHECKLIST, "CCheckList::GetState");
TraceAssert(hWnd != NULL);
TraceAssert(iItem < m_cItems);
TraceAssert(0 < iSubItem && iSubItem <= m_cSubItems);
HWND hwndCtrl = GetDlgItem(hWnd, MAKE_CTRL_ID(iItem, iSubItem));
if (hwndCtrl != NULL)
{
lState = (LONG)SendMessage(hwndCtrl, BM_GETCHECK, 0, 0);
TraceAssert(!(lState & BST_INDETERMINATE));
if (!IsWindowEnabled(hwndCtrl))
lState |= CLST_DISABLED;
}
TraceLeaveValue(lState);
}
void
CCheckList::SetColumnWidth(HWND hWnd, LONG cxDialog, LONG cxColumn)
{
LONG j;
TraceEnter(TRACE_CHECKLIST, "CCheckList::SetColumnWidth");
TraceAssert(hWnd != NULL);
TraceAssert(cxColumn > 10);
m_cxCheckColumn = cxColumn;
if (m_cSubItems > 0)
{
m_nCheckPos[m_cSubItems-1] = cxDialog // dlg width
- m_rcItemLabel.left // right margin
- (cxColumn + m_cxCheckBox)/2; // 1/2 col & 1/2 checkbox
for (j = m_cSubItems - 1; j > 0; j--)
m_nCheckPos[j-1] = m_nCheckPos[j] - cxColumn;
// (leftmost check pos) - (horz margin)
m_rcItemLabel.right = m_nCheckPos[0] - m_rcItemLabel.left;
}
else
m_rcItemLabel.right = cxDialog - m_rcItemLabel.left;
LONG nTop = m_rcItemLabel.top;
LONG nBottom = m_rcItemLabel.bottom;
for (LONG i = 0; i < m_cItems; i++)
{
MoveWindow(GetDlgItem(hWnd, MAKE_LABEL_ID(i)),
m_rcItemLabel.left,
nTop,
m_rcItemLabel.right - m_rcItemLabel.left,
nBottom - nTop,
FALSE);
for (j = 0; j < m_cSubItems; j++)
{
MoveWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(i, j + 1)),
m_nCheckPos[j],
nTop,
m_cxCheckBox,
nBottom - nTop,
FALSE);
}
nTop += m_rcItemLabel.bottom;
nBottom += m_rcItemLabel.bottom;
}
TraceLeaveVoid();
}
void
CCheckList::ResetContent(HWND hWnd)
{
for (LONG i = 0; i < m_cItems; i++)
for (LONG j = 0; j <= m_cSubItems; j++)
DestroyWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(i, j)));
// Hide the scroll bar
ShowScrollBar(hWnd, SB_VERT, FALSE);
m_cItems = 0;
}
LONG
CCheckList::GetVisibleCount(HWND hWnd)
{
LONG nCount = 1;
RECT rc;
if (GetClientRect(hWnd, &rc) && m_rcItemLabel.bottom > 0)
nCount = max(1, rc.bottom / m_rcItemLabel.bottom);
return nCount;
}
LONG
CCheckList::GetTopIndex(HWND hWnd)
{
LONG nIndex = 0;
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
if (GetScrollInfo(hWnd, SB_VERT, &si) && m_rcItemLabel.bottom > 0)
nIndex = max(0, si.nPos / m_rcItemLabel.bottom);
return nIndex;
}
void
CCheckList::EnsureVisible(HWND hWnd, LONG nItemIndex)
{
LONG nTopIndex = GetTopIndex(hWnd);
// Note that the top item may only be partially visible,
// so we need to test for equality here. Raid #208449
if (nItemIndex <= nTopIndex)
{
SetTopIndex(hWnd, nItemIndex);
}
else
{
LONG nVisible = GetVisibleCount(hWnd);
if (nItemIndex >= nTopIndex + nVisible)
SetTopIndex(hWnd, nItemIndex - nVisible + 1);
}
}
void
CCheckList::DrawCheckFocusRect(HWND hWnd, HWND hwndCheck, BOOL fDraw)
{
RECT rcCheck;
HDC hdc;
TraceEnter(TRACE_CHECKLIST, "CCheckList::DrawCheckFocusRect");
TraceAssert(hWnd != NULL);
TraceAssert(hwndCheck != NULL);
GetWindowRect(hwndCheck, &rcCheck);
MapWindowPoints(NULL, hWnd, (LPPOINT)&rcCheck, 2);
InflateRect(&rcCheck, 2, 2); // draw *outside* the checkbox
hdc = GetDC(hWnd);
if (hdc)
{
// Always erase before drawing, since we may already be
// partially visible and drawing is an XOR operation.
// (Don't want to leave any turds on the screen.)
FrameRect(hdc, &rcCheck, GetSysColorBrush(BK_COLOR));
if (fDraw)
{
SetTextColor(hdc, GetSysColor(TEXT_COLOR));
SetBkColor(hdc, GetSysColor(BK_COLOR));
DrawFocusRect(hdc, &rcCheck);
}
ReleaseDC(hWnd, hdc);
}
TraceLeaveVoid();
}
void
CCheckList::GetColumnDescriptions(HWND hWnd)
{
//
// Get column descriptions for accessibility
//
TCHAR szDescription[MAX_PATH];
NM_CHECKLIST nmc;
nmc.hdr.hwndFrom = hWnd;
nmc.hdr.idFrom = GetDlgCtrlID(hWnd);
nmc.hdr.code = CLN_GETCOLUMNDESC;
nmc.iItem = 0;
nmc.dwState = 0;
nmc.dwItemData = 0;
nmc.cchTextMax = ARRAYSIZE(szDescription);
nmc.pszText = szDescription;
for (LONG j = 0; j < m_cSubItems; j++)
{
szDescription[0] = TEXT('\0');
nmc.iSubItem = j+1;
SendMessage(GetParent(hWnd),
WM_NOTIFY,
nmc.hdr.idFrom,
(LPARAM)&nmc);
LocalFreeString(&m_pszColumnDesc[j]);
if (szDescription[0])
LocalAllocString(&m_pszColumnDesc[j], szDescription);
}
}
LRESULT
CALLBACK
CCheckList::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = 0;
CCheckList *pThis = (CCheckList*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
TraceEnter(TRACE_CHECKLIST, "CCheckList::WindowProc");
TraceAssert(hWnd != NULL);
switch (uMsg)
{
case WM_NCCREATE:
pThis = new CCheckList(hWnd, (LPCREATESTRUCT)lParam);
if (pThis != NULL)
{
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
lResult = TRUE;
}
break;
case WM_NCDESTROY:
delete pThis;
break;
case WM_COMMAND:
TraceAssert(pThis != NULL);
lResult = pThis->MsgCommand(hWnd,
GET_WM_COMMAND_ID(wParam, lParam),
GET_WM_COMMAND_CMD(wParam, lParam),
GET_WM_COMMAND_HWND(wParam, lParam));
break;
case WM_CTLCOLORSTATIC:
TraceAssert(pThis != NULL);
SetBkMode((HDC)wParam, TRANSPARENT);
SetTextColor((HDC)wParam, GetSysColor(TEXT_COLOR));
SetBkColor((HDC)wParam, GetSysColor(BK_COLOR));
lResult = (LRESULT)GetSysColorBrush(BK_COLOR);
break;
case WM_PAINT:
TraceAssert(pThis != NULL);
pThis->MsgPaint(hWnd, (HDC)wParam);
break;
case WM_VSCROLL:
TraceAssert(pThis != NULL);
pThis->MsgVScroll(hWnd,
(int)(short)GET_WM_VSCROLL_CODE(wParam, lParam),
(int)(short)GET_WM_VSCROLL_POS(wParam, lParam));
break;
case WM_MOUSEWHEEL:
TraceAssert(pThis != NULL);
pThis->MsgMouseWheel(hWnd,
LOWORD(wParam),
(int)(short)HIWORD(wParam));
break;
case WM_LBUTTONDOWN:
TraceAssert(pThis != NULL);
pThis->MsgButtonDown(hWnd,
wParam,
(int)(short)LOWORD(lParam),
(int)(short)HIWORD(lParam));
break;
case WM_ENABLE:
TraceAssert(pThis != NULL);
pThis->MsgEnable(hWnd, (BOOL)wParam);
break;
case WM_SETFONT:
TraceAssert(pThis != NULL);
{
for (LONG i = 0; i < pThis->m_cItems; i++)
SendDlgItemMessage(hWnd,
MAKE_LABEL_ID(i),
WM_SETFONT,
wParam,
lParam);
}
break;
case WM_SIZE:
TraceAssert(pThis != NULL);
pThis->MsgSize(hWnd, (DWORD)wParam, LOWORD(lParam), HIWORD(lParam));
break;
case CLM_ADDITEM:
TraceAssert(pThis != NULL);
lResult = pThis->AddItem(hWnd, (LPCTSTR)wParam, lParam);
break;
case CLM_GETITEMCOUNT:
TraceAssert(pThis != NULL);
lResult = pThis->m_cItems;
break;
case CLM_SETSTATE:
TraceAssert(pThis != NULL);
pThis->SetState(hWnd, LOWORD(wParam), HIWORD(wParam), (LONG)lParam);
break;
case CLM_GETSTATE:
TraceAssert(pThis != NULL);
lResult = pThis->GetState(hWnd, LOWORD(wParam), HIWORD(wParam));
break;
case CLM_SETCOLUMNWIDTH:
TraceAssert(pThis != NULL);
{
RECT rc;
LONG cxDialog;
GetClientRect(hWnd, &rc);
cxDialog = rc.right;
rc.right = (LONG)lParam;
MapDialogRect(GetParent(hWnd), &rc);
pThis->SetColumnWidth(hWnd, cxDialog, rc.right);
}
break;
case CLM_SETITEMDATA:
TraceAssert(GET_ITEM(wParam) < (ULONG)pThis->m_cItems);
SetWindowLongPtr(GetDlgItem(hWnd, MAKE_LABEL_ID((int)wParam)),
GWLP_USERDATA,
lParam);
break;
case CLM_GETITEMDATA:
TraceAssert(GET_ITEM(wParam) < (ULONG)pThis->m_cItems);
lResult = GetWindowLongPtr(GetDlgItem(hWnd, MAKE_LABEL_ID((int)wParam)),
GWLP_USERDATA);
break;
case CLM_RESETCONTENT:
TraceAssert(pThis != NULL);
pThis->ResetContent(hWnd);
break;
case CLM_GETVISIBLECOUNT:
TraceAssert(pThis != NULL);
lResult = pThis->GetVisibleCount(hWnd);
break;
case CLM_GETTOPINDEX:
TraceAssert(pThis != NULL);
lResult = pThis->GetTopIndex(hWnd);
break;
case CLM_SETTOPINDEX:
TraceAssert(pThis != NULL);
pThis->SetTopIndex(hWnd, (LONG)wParam);
break;
case CLM_ENSUREVISIBLE:
TraceAssert(pThis != NULL);
pThis->EnsureVisible(hWnd, (LONG)wParam);
break;
//
// Always refer to the chklist window for help. Don't pass
// one of the child window handles here.
//
case WM_HELP:
((LPHELPINFO)lParam)->hItemHandle = hWnd;
lResult = SendMessage(GetParent(hWnd), uMsg, wParam, lParam);
break;
case WM_CONTEXTMENU:
lResult = SendMessage(GetParent(hWnd), uMsg, (WPARAM)hWnd, lParam);
break;
default:
lResult = DefWindowProc(hWnd, uMsg, wParam, lParam);
}
TraceLeaveValue(lResult);
}