|
|
/**********************************************************************/ /** Microsoft Windows/NT **/ /** Copyright(c) Microsoft Corp., 1991-2001 **/ /**********************************************************************/
/*
CHKLIST.CPP
This file contains the implementation of the CheckList control.
*/ #include "pch.h"
#include "chklist.h"
#include <assert.h>
#include <windowsx.h>
#define TraceLeaveVoid() { return; }
#define TraceEnter(dwMask, fn) { ; }
#define TraceAssert(x) assert(x)
#define TraceLeaveValue(value) { return(value); }
//
// 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 32
#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
typedef struct _USERDATA_STRUCT_LABEL { LPARAM lParam; int nLabelHeight; int itemIndex; } USERDATA_STRUCT_LABEL, *LPUSERDATA_STRUCT_LABEL;
class CheckList { private: LONG m_cItems; LONG m_cSubItems; RECT m_rcItemLabel; LONG m_nCheckPos[MAX_CHECK_COLUMNS]; LONG m_cxCheckBox; LONG m_cxCheckColumn;
int m_nDefaultVerticalSpace; int m_nDefaultItemHeight; int m_nNewItemYPos;
HWND m_hwndCheckFocus;
BOOL m_fInMessageEnable;
int m_cWheelDelta; static UINT g_ucScrollLines;
private: CheckList(HWND hWnd, LPCREATESTRUCT lpcs);
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, LONG *pnAmountObscured = NULL); void SetTopIndex(HWND hWnd, LONG nIndex); void EnsureVisible(HWND hWnd, LONG nIndex); void DrawCheckFocusRect(HWND hWnd, HWND hwndCheck, BOOL fDraw);
public: HWND m_hWnd; static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); };
BOOL RegisterCheckListWndClass(HINSTANCE hInstance) { WNDCLASS wc;
wc.style = 0; wc.lpfnWndProc = CheckList::WindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; //AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Required for AfxGetInstanceHandle()
wc.hInstance = hInstance; //AfxGetInstanceHandle(); //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 CheckList::g_ucScrollLines = (UINT)-1;
CheckList::CheckList(HWND hWnd, LPCREATESTRUCT lpcs) : m_cItems(0), m_hwndCheckFocus(NULL), m_fInMessageEnable(FALSE), m_cWheelDelta(0) { assert(hWnd != NULL); assert(lpcs != NULL);
m_hWnd = hWnd; //
// Get number of check columns
//
m_cSubItems = lpcs->style & CLS_CHECKMASK;
// for wsecedit only
if ( m_cSubItems > 3 ) { m_cSubItems = 3; }
if( lpcs->style & CLS_LEFTALIGN ) //if checkbox is at the left of text only 1 checkbox is allowed.
{ m_cSubItems = 1; }
//
// 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;
m_nDefaultVerticalSpace = rc.top; m_nDefaultItemHeight = rc.bottom - rc.top; m_nNewItemYPos = rc.top;
//
// Get info for mouse wheel scrolling
//
if ((UINT)-1 == g_ucScrollLines) { g_ucScrollLines = 3; // default
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &g_ucScrollLines, 0); }
TraceLeaveVoid(); }
LRESULT CheckList::MsgCommand(HWND hWnd, WORD idCmd, WORD wNotify, HWND hwndCtrl) { TraceEnter(TRACE_CHECKLIST, "CheckList::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 EN_SETFOCUS: { // Make the focus go to one of the checkboxes
POINT pt; DWORD dwPos = GetMessagePos(); pt.x = GET_X_LPARAM(dwPos); pt.y = GET_Y_LPARAM(dwPos); MapWindowPoints(NULL, hWnd, &pt, 1); MsgButtonDown(hWnd, 0, pt.x, pt.y); } break;
case BN_CLICKED: { LPUSERDATA_STRUCT_LABEL lpUserData = NULL; 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; HRESULT hr = Win::GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID(nmc.iItem)), GWLP_USERDATA, (LONG_PTR&)lpUserData); //lpUserData = (LPUSERDATA_STRUCT_LABEL)
// Win::GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID(nmc.iItem)),
// GWLP_USERDATA);
if (SUCCEEDED(hr) && lpUserData) { nmc.dwItemData = lpUserData->lParam; }
SendMessage(GetParent(hWnd), WM_NOTIFY, nmc.hdr.idFrom, (LPARAM)&nmc);
} break;
case BN_SETFOCUS: if (GetFocus() != hwndCtrl) { // This causes another BN_SETFOCUS
SetFocus(hwndCtrl); } else { 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 CheckList::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 CheckList::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 CheckList::MsgMouseWheel(HWND hWnd, WORD fwFlags, int iWheelDelta) { int cDetants;
if ((fwFlags & (MK_SHIFT | MK_CONTROL)) || 0 == g_ucScrollLines) return;
TraceEnter(TRACE_CHECKLIST, "CheckList::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))
long winlong = 0; Win::GetWindowLong(hWnd, GWL_STYLE, winlong); if (WS_VSCROLL & winlong) { 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 CheckList::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); if (nItemIndex == -1) { return; } hwndCheck = GetDlgItem(hWnd, MAKE_CTRL_ID(nItemIndex, 0)); GetWindowRect(hwndCheck, &rc); MapWindowPoints(NULL, hWnd, (LPPOINT)&rc, 2);
// Find nearest item
if( hWnd == m_hWnd ) //Raid #387542, 5/9/2001
{ POINT pos = {xPos,yPos}; HWND ChildhWnd = ::ChildWindowFromPointEx(hWnd, pos, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED); if( ChildhWnd ) { LPUSERDATA_STRUCT_LABEL pUserData = NULL; HRESULT hr = Win::GetWindowLongPtr(ChildhWnd, GWLP_USERDATA, (LONG_PTR&)pUserData); //LPUSERDATA_STRUCT_LABEL pUserData = (LPUSERDATA_STRUCT_LABEL)GetWindowLongPtr(ChildhWnd, GWLP_USERDATA);
if (SUCCEEDED(hr) && pUserData) { nItemIndex = pUserData->itemIndex; } else { return; } } }
// Set focus to first subitem that is enabled
for (LONG j = 1; j <= m_cSubItems; j++) { int id = MAKE_CTRL_ID(nItemIndex, j); HWND hwndCheck = GetDlgItem(hWnd, id); if (IsWindowEnabled(hwndCheck)) { // Don't just SetFocus here. We sometimes call this during
// EN_SETFOCUS, and USER doesn't like it when you mess with
// focus during a focus change.
//
//SetFocus(hwndCheck);
PostMessage(hWnd, WM_COMMAND, GET_WM_COMMAND_MPS(id, hwndCheck, BN_SETFOCUS)); break; } } }
void CheckList::MsgEnable(HWND hWnd, BOOL fEnabled) { HWND hwndCurrentCheck; BOOL fCheckEnabled = FALSE;
if (!m_fInMessageEnable) { m_fInMessageEnable = TRUE; for (LONG i = 0; i < m_cItems; i++) { for (LONG j = 1; j <= m_cSubItems; j++) { hwndCurrentCheck = GetDlgItem(hWnd, MAKE_CTRL_ID(i, j)); //fCheckEnabled = (BOOL) GetWindowLongPtr(hwndCurrentCheck, GWLP_USERDATA);
Win::GetWindowLongPtr(hwndCurrentCheck, GWLP_USERDATA, (LONG_PTR&)fCheckEnabled);
//
// If the user of the checklist control is disabling the control
// altogether, or the current checkbox has been disabled singularly
// then disable the checkbox
//
if (!fEnabled || !fCheckEnabled) { EnableWindow(hwndCurrentCheck, FALSE); } else { EnableWindow(hwndCurrentCheck, TRUE); } } } // Note that the main chklist window must remain enabled
// for scrolling to work while "disabled".
if (!fEnabled) EnableWindow(hWnd, TRUE);
m_fInMessageEnable = FALSE; } }
void CheckList::MsgSize(HWND hWnd, DWORD dwSizeType, LONG nWidth, LONG nHeight) { TraceEnter(TRACE_CHECKLIST, "CheckList::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_nNewItemYPos - 1; si.nPage = nHeight;
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 CheckList::AddItem(HWND hWnd, LPCTSTR pszLabel, LPARAM lParam) { HWND hwndPrev = 0; LPUSERDATA_STRUCT_LABEL lpUserData = 0;
TraceEnter(TRACE_CHECKLIST, "CheckList::AddItem"); TraceAssert(hWnd != NULL); TraceAssert(pszLabel != NULL && !IsBadStringPtr(pszLabel, MAX_PATH)); if ( !hWnd || !pszLabel || IsBadStringPtr(pszLabel, MAX_PATH) ) return -1;
lpUserData = new (USERDATA_STRUCT_LABEL); if ( lpUserData ) { SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_POS; si.nPos = 0; GetScrollInfo(hWnd, SB_VERT, &si);
// Set the initial label height extra big so the control can wrap the text,
// then reset it after creating the control.
RECT rc; GetClientRect(hWnd, &rc); LONG nLabelHeight = rc.bottom;
//AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Required for AfxGetInstanceHandle()
HMODULE hModule = GetResourceModuleHandle(); //AfxGetInstanceHandle();
// Create a new label control
HWND hwndNew = CreateWindowEx(WS_EX_NOPARENTNOTIFY, TEXT("edit"), pszLabel, WS_CHILD | WS_VISIBLE | WS_GROUP | ES_MULTILINE | ES_READONLY | ES_LEFT,// | WS_GROUP,
m_rcItemLabel.left, m_nNewItemYPos - si.nPos, m_rcItemLabel.right - m_rcItemLabel.left, nLabelHeight, hWnd, (HMENU)IntToPtr(MAKE_LABEL_ID(m_cItems)), hModule, NULL); if ( hwndNew ) { HWND hwndEdit = hwndNew; //
// Reset window height after word wrap has been done.
//
LONG nLineCount = (LONG) SendMessage(hwndNew, EM_GETLINECOUNT, 0, (LPARAM) 0); nLabelHeight = nLineCount * m_nDefaultItemHeight; SetWindowPos(hwndNew, NULL, 0, 0, m_rcItemLabel.right - m_rcItemLabel.left, nLabelHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
//
// Save item data
//
lpUserData->lParam = lParam; lpUserData->nLabelHeight = nLabelHeight; lpUserData->itemIndex = m_cItems; //Raid #387542
SetLastError(0); //SetWindowLongPtr(hwndNew, GWLP_USERDATA, (LPARAM) lpUserData); //Raid #286697, 4/4/2001
HRESULT hr = Win::SetWindowLongPtr(hwndNew, GWLP_USERDATA, (LONG_PTR)lpUserData); if (SUCCEEDED(hr)) // 0 == GetLastError() )
{ // 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
DWORD dwCheckStyle = WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP | BS_NOTIFY | BS_FLAT | BS_AUTOCHECKBOX; for (LONG j = 0; j < m_cSubItems; j++) { hwndPrev = hwndNew; hwndNew = CreateWindowEx(WS_EX_NOPARENTNOTIFY, TEXT("BUTTON"), NULL, dwCheckStyle, m_nCheckPos[j], m_nNewItemYPos - si.nPos, m_cxCheckBox, m_rcItemLabel.bottom - m_rcItemLabel.top, hWnd, (HMENU)IntToPtr(MAKE_CTRL_ID(m_cItems, j + 1)), hModule, NULL); if (!hwndNew) { while (j >= 0) { DestroyWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(m_cItems, j))); j--; }
DestroyWindow (hwndEdit); delete lpUserData; TraceLeaveValue(-1); }
// Set Z-order position just after the last checkbox. This keeps
// tab order correct.
SetWindowPos(hwndNew, hwndPrev, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
//
// Default "enabled" to TRUE
//
//SetWindowLongPtr(hwndNew, GWLP_USERDATA, (LPARAM) TRUE);
hr = Win::SetWindowLongPtr(hwndNew, GWLP_USERDATA, (LONG_PTR)TRUE);
// Only want this style on the first checkbox
dwCheckStyle &= ~WS_GROUP; }
// We now officially have a new item
m_cItems++;
// calculate Y pos for next item to be inserted
m_nNewItemYPos += nLabelHeight + m_nDefaultVerticalSpace;
//
// The last thing is to set the scroll range
//
GetClientRect(hWnd, &rc); si.cbSize = sizeof(si); si.fMask = SIF_PAGE | SIF_RANGE; si.nMin = 0; si.nMax = m_nNewItemYPos - 1; si.nPage = rc.bottom;
SetScrollInfo(hWnd, SB_VERT, &si, FALSE); } else { delete lpUserData; DestroyWindow(hwndNew); } } else delete lpUserData; }
TraceLeaveValue(m_cItems - 1); // return the index of the new item
}
void CheckList::SetState(HWND hWnd, WORD iItem, WORD iSubItem, LONG lState) { HWND hwndCtrl;
TraceEnter(TRACE_CHECKLIST, "CheckList::SetState"); TraceAssert(hWnd != NULL); TraceAssert(iItem < m_cItems); TraceAssert(0 < iSubItem && iSubItem <= m_cSubItems);
if (iSubItem > 0) { hwndCtrl = GetDlgItem(hWnd, MAKE_CTRL_ID(iItem, iSubItem)); if (hwndCtrl != NULL) { //SetWindowLongPtr(hwndCtrl, GWLP_USERDATA, (LPARAM) !(lState & CLST_DISABLED));
Win::SetWindowLongPtr(hwndCtrl, GWLP_USERDATA, (LONG_PTR)!(lState & CLST_DISABLED)); SendMessage(hwndCtrl, BM_SETCHECK, lState & CLST_CHECKED, 0); EnableWindow(hwndCtrl, !(lState & CLST_DISABLED)); } }
TraceLeaveVoid(); }
LONG CheckList::GetState(HWND hWnd, WORD iItem, WORD iSubItem) { LONG lState = 0;
TraceEnter(TRACE_CHECKLIST, "CheckList::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 CheckList::SetColumnWidth(HWND hWnd, LONG cxDialog, LONG cxColumn) { LONG j; LPUSERDATA_STRUCT_LABEL pUserData = NULL; LONG nLabelHeight;
TraceEnter(TRACE_CHECKLIST, "CheckList::SetColumnWidth"); TraceAssert(hWnd != NULL); TraceAssert(cxColumn > 10);
m_cxCheckColumn = cxColumn;
long dwChkListStyle = 0; Win::GetWindowLong(hWnd, GWL_STYLE, dwChkListStyle); if (m_cSubItems > 0) { if ( dwChkListStyle & CLS_LEFTALIGN ) { // Put the checkboxes at the left of the text
int xSubItem = m_rcItemLabel.left; for (j = 0; j < m_cSubItems; j++, xSubItem += cxColumn) m_nCheckPos[j] = xSubItem; m_rcItemLabel.left = xSubItem - (cxColumn + m_cxCheckBox)/2; m_rcItemLabel.right = cxDialog - m_rcItemLabel.left; } else { 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++) { //pUserData = (LPUSERDATA_STRUCT_LABEL)
// GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)i)),
// GWLP_USERDATA);
HRESULT hr = Win::GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)i)), GWLP_USERDATA, (LONG_PTR&)pUserData); if (SUCCEEDED(hr) && pUserData != NULL) { nLabelHeight = pUserData->nLabelHeight; } else { nLabelHeight = nBottom - nTop; }
MoveWindow(GetDlgItem(hWnd, MAKE_LABEL_ID(i)), m_rcItemLabel.left, nTop, m_rcItemLabel.right - m_rcItemLabel.left, nLabelHeight, 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 += nLabelHeight + m_nDefaultVerticalSpace; nBottom += nLabelHeight + m_nDefaultVerticalSpace; }
TraceLeaveVoid(); }
void CheckList::ResetContent(HWND hWnd) { LPUSERDATA_STRUCT_LABEL pUserData = NULL; HWND hwndCurrentLabel;
for (LONG i = 0; i < m_cItems; i++) { hwndCurrentLabel = GetDlgItem(hWnd, MAKE_LABEL_ID((int)i)); //pUserData = (LPUSERDATA_STRUCT_LABEL)
// GetWindowLongPtr( hwndCurrentLabel,
// GWLP_USERDATA);
HRESULT hr = Win::GetWindowLongPtr( hwndCurrentLabel, GWLP_USERDATA, (LONG_PTR&)pUserData); if (SUCCEEDED(hr) && pUserData != NULL) { delete(pUserData); } DestroyWindow(hwndCurrentLabel);
for (LONG j = 1; j <= m_cSubItems; j++) { DestroyWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(i, j))); } }
// Hide the scroll bar
SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE); m_cItems = 0; }
LONG CheckList::GetVisibleCount(HWND hWnd) { LONG nCount = 0; RECT rc; LONG nTopIndex; LONG nAmountShown = 0; LONG nAmountObscured = 0; LPUSERDATA_STRUCT_LABEL pUserData = NULL;
if (!GetClientRect(hWnd, &rc)) { return 1; }
nTopIndex = GetTopIndex(hWnd, &nAmountObscured); if (nTopIndex == -1) { return 1; }
while ((nTopIndex < m_cItems) && (nAmountShown < rc.bottom)) { //pUserData = (LPUSERDATA_STRUCT_LABEL)
// GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nTopIndex)),
// GWLP_USERDATA);
HRESULT hr = Win::GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nTopIndex)), GWLP_USERDATA, (LONG_PTR&)pUserData); if (SUCCEEDED(hr) && pUserData) { nAmountShown += (m_nDefaultVerticalSpace + pUserData->nLabelHeight - nAmountObscured); } nAmountObscured = 0; // nAmountObscured only matters for the first iteration where
// the real top index's amount shown is being calculated
nCount++; nTopIndex++; }
//
// since that last one may be obscured see if we need to adjust nCount
//
if (nAmountShown > rc.bottom) { nCount--; }
return max(1, nCount); }
LONG CheckList::GetTopIndex(HWND hWnd, LONG *pnAmountObscured) { LONG nIndex = 0; LPUSERDATA_STRUCT_LABEL pUserData = NULL; SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_POS;
//
// initialize
//
if (pnAmountObscured != NULL) { *pnAmountObscured = 0; }
if (GetScrollInfo(hWnd, SB_VERT, &si) && m_rcItemLabel.bottom > 0) { //pUserData = (LPUSERDATA_STRUCT_LABEL)
// GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nIndex)),
// GWLP_USERDATA);
HRESULT hr = Win::GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nIndex)), GWLP_USERDATA, (LONG_PTR&)pUserData); //
// if there are no items get out
//
if (FAILED(hr) || pUserData == NULL) { return -1; }
while (si.nPos >= (m_nDefaultVerticalSpace + pUserData->nLabelHeight)) { si.nPos -= (m_nDefaultVerticalSpace + pUserData->nLabelHeight); nIndex++; //pUserData = (LPUSERDATA_STRUCT_LABEL)
// GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nIndex)),
// GWLP_USERDATA);
hr = Win::GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nIndex)), GWLP_USERDATA, (LONG_PTR&)pUserData); }
if (pnAmountObscured != NULL) { *pnAmountObscured = si.nPos; } }
return nIndex; }
void CheckList::SetTopIndex(HWND hWnd, LONG nIndex) { int i; int nPos = 0; LPUSERDATA_STRUCT_LABEL pUserData = NULL;
for (i=0; i<nIndex; i++) { //pUserData = (LPUSERDATA_STRUCT_LABEL)
// GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)i)),
// GWLP_USERDATA);
HRESULT hr = Win::GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)i)), GWLP_USERDATA, (LONG_PTR&)pUserData); if (SUCCEEDED(hr) && pUserData) { nPos += (m_nDefaultVerticalSpace + pUserData->nLabelHeight); } }
m_cWheelDelta = 0; MsgVScroll(hWnd, SB_THUMBPOSITION, nPos); }
void CheckList::EnsureVisible(HWND hWnd, LONG nItemIndex) { LONG nAmountObscured = 0; LONG nTopIndex; RECT rc; LPUSERDATA_STRUCT_LABEL pUserData = NULL;
nTopIndex = GetTopIndex(hWnd, &nAmountObscured); if (nTopIndex == -1) { return; }
// 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 if (nItemIndex == nTopIndex) { if (nAmountObscured != 0) { SetTopIndex(hWnd, nItemIndex); } } else { LONG nVisible = GetVisibleCount(hWnd);
if (nItemIndex >= nTopIndex + nVisible) { if (!GetClientRect(hWnd, &rc)) { //
// This is just best effort
//
SetTopIndex(hWnd, nItemIndex - nVisible + 1); } else { //
// Calculate what the top index should be to allow
// nItemIndex to be fully visible
//
nTopIndex = nItemIndex + 1; HRESULT hr = S_OK; do { nTopIndex--; //pUserData = (LPUSERDATA_STRUCT_LABEL)
// GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nTopIndex)),
// GWLP_USERDATA);
hr = Win::GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nTopIndex)), GWLP_USERDATA, (LONG_PTR&)pUserData); if (SUCCEEDED(hr) && pUserData != NULL) { rc.bottom -= (pUserData->nLabelHeight + m_nDefaultVerticalSpace); if (rc.bottom < 0) { nTopIndex++; } } else { //
// Should not hit this, just added to make things safe
//
rc.bottom = 0; nTopIndex = 0; } } while (rc.bottom > 0);
SetTopIndex(hWnd, nTopIndex); } } } }
void CheckList::DrawCheckFocusRect(HWND hWnd, HWND hwndCheck, BOOL fDraw) { RECT rcCheck;
TraceEnter(TRACE_CHECKLIST, "CheckList::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 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(); }
LRESULT CALLBACK CheckList::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT lResult = 0; LPUSERDATA_STRUCT_LABEL pUserData = NULL; CheckList *pThis = NULL; //(CheckList*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
HRESULT hr = Win::GetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR&)pThis); if (FAILED(hr)) { TraceAssert(false); return 0; }
TraceEnter(TRACE_CHECKLIST, "CheckList::WindowProc"); TraceAssert(hWnd != NULL);
switch (uMsg) { case WM_NCCREATE: pThis = new CheckList(hWnd, (LPCREATESTRUCT)lParam); if (pThis != NULL) { //SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
hr = Win::SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis); lResult = TRUE; } break;
case WM_DESTROY: pThis->ResetContent(hWnd); 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); //pUserData = (LPUSERDATA_STRUCT_LABEL)
// GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)wParam)),
// GWLP_USERDATA);
hr = Win::GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)wParam)), GWLP_USERDATA, (LONG_PTR&)pUserData); if (pUserData != NULL) pUserData->lParam = lParam; break;
case CLM_GETITEMDATA: TraceAssert(GET_ITEM(wParam) < (ULONG)pThis->m_cItems); //pUserData = (LPUSERDATA_STRUCT_LABEL)
// GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)wParam)),
// GWLP_USERDATA);
hr = Win::GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)wParam)), GWLP_USERDATA, (LONG_PTR&)pUserData); if (pUserData != NULL) lResult = pUserData->lParam; 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;
case WM_SETCURSOR: if (LOWORD(lParam) == HTCLIENT) { SetCursor(LoadCursor(NULL, IDC_ARROW)); lResult = TRUE; break; } // Fall Through
default: lResult = DefWindowProc(hWnd, uMsg, wParam, lParam); }
TraceLeaveValue(lResult); }
|