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

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1996 - 1999
  6. //
  7. // File: chklist.cpp
  8. //
  9. // This file contains the implementation of the CheckList control.
  10. //
  11. //--------------------------------------------------------------------------
  12. #include "aclpriv.h"
  13. //
  14. // Text and Background colors
  15. //
  16. #define TEXT_COLOR COLOR_WINDOWTEXT
  17. #define BK_COLOR COLOR_WINDOW
  18. //
  19. // Default dimensions for child controls. All are in dialog units.
  20. // Currently only the column width is user-adjustable (via the
  21. // CLM_SETCOLUMNWIDTH message).
  22. //
  23. #define DEFAULT_COLUMN_WIDTH 40
  24. #define DEFAULT_CHECK_WIDTH 9
  25. #define DEFAULT_HORZ_SPACE 7
  26. #define DEFAULT_VERTICAL_SPACE 3
  27. #define DEFAULT_ITEM_HEIGHT 8
  28. //
  29. // 16 bits are used for the control ID's, divided into n bits for
  30. // the subitem (least significant) and 16-n bits for the item index.
  31. //
  32. // ID_SUBITEM_BITS can be adjusted to control the maximum number of
  33. // items and subitems. For example, to allow up to 7 subitems and 8k
  34. // items, set ID_SUBITEM_BITS to 3.
  35. //
  36. // Use the low 2 bits for the subitem index, the rest for the item index.
  37. // (4 subitems max, 16k items max)
  38. #define ID_SUBITEM_BITS 2
  39. #define ID_SUBITEM_MASK ((1 << ID_SUBITEM_BITS) - 1)
  40. #define GET_ITEM(id) ((id) >> ID_SUBITEM_BITS)
  41. #define GET_SUBITEM(id) ((id) & ID_SUBITEM_MASK)
  42. #define MAKE_CTRL_ID(i, s) (0xffff & (((i) << ID_SUBITEM_BITS) | ((s) & ID_SUBITEM_MASK)))
  43. #define MAKE_LABEL_ID(i) MAKE_CTRL_ID(i, 0)
  44. // Note that the subitem (column) index is one-based for the checkboxes
  45. // (the zero column is the label). The item (row) index is zero-based.
  46. #define MAX_CHECK_COLUMNS ID_SUBITEM_MASK
  47. TCHAR const c_szStaticClass[] = TEXT("STATIC");
  48. TCHAR const c_szButtonClass[] = TEXT("BUTTON");
  49. class CCheckList
  50. {
  51. private:
  52. LONG m_cItems;
  53. LONG m_cSubItems;
  54. RECT m_rcItemLabel;
  55. LONG m_nCheckPos[MAX_CHECK_COLUMNS];
  56. LONG m_cxCheckBox;
  57. LONG m_cxCheckColumn;
  58. HWND m_hwndCheckFocus;
  59. LPTSTR m_pszColumnDesc[MAX_CHECK_COLUMNS];
  60. int m_cWheelDelta;
  61. static UINT g_ucScrollLines;
  62. private:
  63. CCheckList(HWND hWnd, LPCREATESTRUCT lpcs);
  64. ~CCheckList(void);
  65. LRESULT MsgCommand(HWND hWnd, WORD idCmd, WORD wNotify, HWND hwndCtrl);
  66. void MsgPaint(HWND hWnd, HDC hdc);
  67. void MsgVScroll(HWND hWnd, int nCode, int nPos);
  68. void MsgMouseWheel(HWND hWnd, WORD fwFlags, int zDelta);
  69. void MsgButtonDown(HWND hWnd, WPARAM fwFlags, int xPos, int yPos);
  70. void MsgEnable(HWND hWnd, BOOL fEnabled);
  71. void MsgSize(HWND hWnd, DWORD dwSizeType, LONG nWidth, LONG nHeight);
  72. LONG AddItem(HWND hWnd, LPCTSTR pszLabel, LPARAM lParam);
  73. void SetState(HWND hWnd, WORD iItem, WORD iSubItem, LONG lState);
  74. LONG GetState(HWND hWnd, WORD iItem, WORD iSubItem);
  75. void SetColumnWidth(HWND hWnd, LONG cxDialog, LONG cxColumn);
  76. void ResetContent(HWND hWnd);
  77. LONG GetVisibleCount(HWND hWnd);
  78. LONG GetTopIndex(HWND hWnd);
  79. void SetTopIndex(HWND hWnd, LONG nIndex)
  80. { m_cWheelDelta = 0; MsgVScroll(hWnd, SB_THUMBPOSITION, nIndex * m_rcItemLabel.bottom); }
  81. void EnsureVisible(HWND hWnd, LONG nIndex);
  82. void DrawCheckFocusRect(HWND hWnd, HWND hwndCheck, BOOL fDraw);
  83. void GetColumnDescriptions(HWND hWnd);
  84. public:
  85. static LRESULT CALLBACK WindowProc(HWND hWnd,
  86. UINT uMsg,
  87. WPARAM wParam,
  88. LPARAM lParam);
  89. };
  90. BOOL RegisterCheckListWndClass(void)
  91. {
  92. WNDCLASS wc;
  93. wc.style = 0;
  94. wc.lpfnWndProc = CCheckList::WindowProc;
  95. wc.cbClsExtra = 0;
  96. wc.cbWndExtra = 0;
  97. wc.hInstance = hModule;
  98. wc.hIcon = NULL;
  99. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  100. wc.hbrBackground = (HBRUSH)(BK_COLOR+1);
  101. wc.lpszMenuName = NULL;
  102. wc.lpszClassName = TEXT(WC_CHECKLIST);
  103. return (BOOL)RegisterClass(&wc);
  104. }
  105. UINT CCheckList::g_ucScrollLines = (UINT)-1;
  106. CCheckList::CCheckList(HWND hWnd, LPCREATESTRUCT lpcs)
  107. : m_cItems(0), m_hwndCheckFocus(NULL), m_cWheelDelta(0)
  108. {
  109. TraceEnter(TRACE_CHECKLIST, "CCheckList::CCheckList");
  110. TraceAssert(hWnd != NULL);
  111. TraceAssert(lpcs != NULL);
  112. //
  113. // Get number of check columns
  114. //
  115. m_cSubItems = lpcs->style & CLS_CHECKMASK;
  116. //
  117. // Convert default coordinates from dialog units to pixels
  118. //
  119. RECT rc;
  120. rc.left = DEFAULT_CHECK_WIDTH;
  121. rc.right = DEFAULT_COLUMN_WIDTH;
  122. rc.top = rc.bottom = 0;
  123. MapDialogRect(lpcs->hwndParent, &rc);
  124. // Save the converted values
  125. m_cxCheckBox = rc.left;
  126. m_cxCheckColumn = rc.right;
  127. rc.left = DEFAULT_HORZ_SPACE;
  128. rc.top = DEFAULT_VERTICAL_SPACE;
  129. rc.right = 10; // bogus (unused)
  130. rc.bottom = DEFAULT_VERTICAL_SPACE + DEFAULT_ITEM_HEIGHT;
  131. MapDialogRect(lpcs->hwndParent, &rc);
  132. // Save the converted values
  133. m_rcItemLabel = rc;
  134. //
  135. // Get info for mouse wheel scrolling
  136. //
  137. if ((UINT)-1 == g_ucScrollLines)
  138. {
  139. g_ucScrollLines = 3; // default
  140. SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &g_ucScrollLines, 0);
  141. }
  142. TraceLeaveVoid();
  143. }
  144. CCheckList::~CCheckList(void)
  145. {
  146. for (LONG j = 0; j < m_cSubItems; j++)
  147. {
  148. LocalFreeString(&m_pszColumnDesc[j]);
  149. }
  150. }
  151. LRESULT
  152. CCheckList::MsgCommand(HWND hWnd, WORD idCmd, WORD wNotify, HWND hwndCtrl)
  153. {
  154. TraceEnter(TRACE_CHECKLIST, "CCheckList::MsgCommand");
  155. // Should only get notifications from visible, enabled, check boxes
  156. TraceAssert(GET_ITEM(idCmd) < m_cItems);
  157. TraceAssert(0 < GET_SUBITEM(idCmd) && GET_SUBITEM(idCmd) <= m_cSubItems);
  158. TraceAssert(hwndCtrl && IsWindowEnabled(hwndCtrl));
  159. switch (wNotify)
  160. {
  161. case BN_CLICKED:
  162. {
  163. NM_CHECKLIST nmc;
  164. nmc.hdr.hwndFrom = hWnd;
  165. nmc.hdr.idFrom = GetDlgCtrlID(hWnd);
  166. nmc.hdr.code = CLN_CLICK;
  167. nmc.iItem = GET_ITEM(idCmd);
  168. nmc.iSubItem = GET_SUBITEM(idCmd);
  169. nmc.dwState = (DWORD)SendMessage(hwndCtrl, BM_GETCHECK, 0, 0);
  170. if (!IsWindowEnabled(hwndCtrl))
  171. nmc.dwState |= CLST_DISABLED;
  172. nmc.dwItemData = GetWindowLongPtr(GetDlgItem(hWnd, MAKE_LABEL_ID(nmc.iItem)),
  173. GWLP_USERDATA);
  174. nmc.cchTextMax = 0;
  175. nmc.pszText = NULL;
  176. SendMessage(GetParent(hWnd),
  177. WM_NOTIFY,
  178. nmc.hdr.idFrom,
  179. (LPARAM)&nmc);
  180. }
  181. break;
  182. case BN_SETFOCUS:
  183. if (m_hwndCheckFocus != hwndCtrl) // Has the focus moved?
  184. {
  185. // Remember where the focus is
  186. m_hwndCheckFocus = hwndCtrl;
  187. // Make sure the row is scrolled into view
  188. EnsureVisible(hWnd, GET_ITEM(idCmd));
  189. }
  190. // Always draw the focus rect
  191. DrawCheckFocusRect(hWnd, hwndCtrl, TRUE);
  192. break;
  193. case BN_KILLFOCUS:
  194. // Remove the focus rect
  195. m_hwndCheckFocus = NULL;
  196. DrawCheckFocusRect(hWnd, hwndCtrl, FALSE);
  197. break;
  198. }
  199. TraceLeaveValue(0);
  200. }
  201. void
  202. CCheckList::MsgPaint(HWND hWnd, HDC hdc)
  203. {
  204. if (hdc == NULL && m_hwndCheckFocus != NULL)
  205. {
  206. // This will cause a focus rect to be drawn after the window and
  207. // all checkboxes have been painted.
  208. PostMessage(hWnd,
  209. WM_COMMAND,
  210. GET_WM_COMMAND_MPS(GetDlgCtrlID(m_hwndCheckFocus), m_hwndCheckFocus, BN_SETFOCUS));
  211. }
  212. // Default paint
  213. DefWindowProc(hWnd, WM_PAINT, (WPARAM)hdc, 0);
  214. }
  215. void
  216. CCheckList::MsgVScroll(HWND hWnd, int nCode, int nPos)
  217. {
  218. UINT cScrollUnitsPerLine;
  219. SCROLLINFO si;
  220. si.cbSize = sizeof(si);
  221. si.fMask = SIF_ALL;
  222. if (!GetScrollInfo(hWnd, SB_VERT, &si))
  223. return;
  224. cScrollUnitsPerLine = m_rcItemLabel.bottom;
  225. // One page is always visible, so adjust the range to a more useful value
  226. si.nMax -= si.nPage - 1;
  227. switch (nCode)
  228. {
  229. case SB_LINEUP:
  230. // "line" is the height of one item (includes the space in between)
  231. nPos = si.nPos - cScrollUnitsPerLine;
  232. break;
  233. case SB_LINEDOWN:
  234. nPos = si.nPos + cScrollUnitsPerLine;
  235. break;
  236. case SB_PAGEUP:
  237. nPos = si.nPos - si.nPage;
  238. break;
  239. case SB_PAGEDOWN:
  240. nPos = si.nPos + si.nPage;
  241. break;
  242. case SB_TOP:
  243. nPos = si.nMin;
  244. break;
  245. case SB_BOTTOM:
  246. nPos = si.nMax;
  247. break;
  248. case SB_ENDSCROLL:
  249. nPos = si.nPos; // don't go anywhere
  250. break;
  251. case SB_THUMBTRACK:
  252. // Do nothing here to allow tracking
  253. // nPos = si.nPos; // Do this to prevent tracking
  254. case SB_THUMBPOSITION:
  255. // nothing to do here... nPos is passed in
  256. break;
  257. }
  258. // Make sure the new position is within the range
  259. if (nPos < si.nMin)
  260. nPos = si.nMin;
  261. else if (nPos > si.nMax)
  262. nPos = si.nMax;
  263. if (nPos != si.nPos) // are we moving?
  264. {
  265. SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
  266. ScrollWindow(hWnd, 0, si.nPos - nPos, NULL, NULL);
  267. }
  268. }
  269. void
  270. CCheckList::MsgMouseWheel(HWND hWnd, WORD fwFlags, int iWheelDelta)
  271. {
  272. int cDetants;
  273. if ((fwFlags & (MK_SHIFT | MK_CONTROL)) || 0 == g_ucScrollLines)
  274. return;
  275. TraceEnter(TRACE_CHECKLIST, "CCheckList::MsgMouseWheel");
  276. // Update count of scroll amount
  277. m_cWheelDelta -= iWheelDelta;
  278. cDetants = m_cWheelDelta / WHEEL_DELTA;
  279. if (0 == cDetants)
  280. TraceLeaveVoid();
  281. m_cWheelDelta %= WHEEL_DELTA;
  282. if (WS_VSCROLL & GetWindowLong(hWnd, GWL_STYLE))
  283. {
  284. SCROLLINFO si;
  285. UINT cScrollUnitsPerLine;
  286. UINT cLinesPerPage;
  287. UINT cLinesPerDetant;
  288. // Get the scroll amount of one line
  289. cScrollUnitsPerLine = m_rcItemLabel.bottom;
  290. TraceAssert(cScrollUnitsPerLine > 0);
  291. si.cbSize = sizeof(SCROLLINFO);
  292. si.fMask = SIF_PAGE | SIF_POS;
  293. if (!GetScrollInfo(hWnd, SB_VERT, &si))
  294. TraceLeaveVoid();
  295. // The size of a page is at least one line, and
  296. // leaves one line of overlap
  297. cLinesPerPage = (si.nPage - cScrollUnitsPerLine) / cScrollUnitsPerLine;
  298. cLinesPerPage = max(1, cLinesPerPage);
  299. // Don't scroll more than one page per detant
  300. cLinesPerDetant = min(cLinesPerPage, g_ucScrollLines);
  301. si.nPos += cDetants * cLinesPerDetant * cScrollUnitsPerLine;
  302. MsgVScroll(hWnd, SB_THUMBTRACK, si.nPos);
  303. }
  304. TraceLeaveVoid();
  305. }
  306. void
  307. CCheckList::MsgButtonDown(HWND hWnd, WPARAM /*fwFlags*/, int /*xPos*/, int yPos)
  308. {
  309. LONG nItemIndex;
  310. HWND hwndCheck;
  311. RECT rc;
  312. // Get position of the top visible item in client coords
  313. nItemIndex = GetTopIndex(hWnd);
  314. hwndCheck = GetDlgItem(hWnd, MAKE_CTRL_ID(nItemIndex, 0));
  315. GetWindowRect(hwndCheck, &rc);
  316. MapWindowPoints(NULL, hWnd, (LPPOINT)&rc, 2);
  317. // Find nearest item
  318. nItemIndex += (yPos - rc.top + m_rcItemLabel.top/2)/m_rcItemLabel.bottom;
  319. nItemIndex = max(0, min(nItemIndex, m_cItems - 1)); // 0 <= y < m_cItems
  320. // Set focus to first subitem that is enabled
  321. for (LONG j = 1; j <= m_cSubItems; j++)
  322. {
  323. hwndCheck = GetDlgItem(hWnd, MAKE_CTRL_ID(nItemIndex, j));
  324. if (IsWindowEnabled(hwndCheck))
  325. {
  326. SetFocus(hwndCheck);
  327. break;
  328. }
  329. }
  330. }
  331. void
  332. CCheckList::MsgEnable(HWND hWnd, BOOL fEnabled)
  333. {
  334. static BOOL bInMsgEnable = FALSE;
  335. if (!bInMsgEnable)
  336. {
  337. bInMsgEnable = TRUE;
  338. for (LONG i = 0; i < m_cItems; i++)
  339. {
  340. for (LONG j = 1; j <= m_cSubItems; j++)
  341. {
  342. EnableWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(i, j)), fEnabled);
  343. }
  344. }
  345. if (!fEnabled)
  346. EnableWindow(hWnd, TRUE);
  347. bInMsgEnable = FALSE;
  348. }
  349. }
  350. void
  351. CCheckList::MsgSize(HWND hWnd, DWORD dwSizeType, LONG nWidth, LONG nHeight)
  352. {
  353. TraceEnter(TRACE_CHECKLIST, "CCheckList::MsgSize");
  354. TraceAssert(hWnd != NULL);
  355. if (dwSizeType == SIZE_RESTORED)
  356. {
  357. RECT rc;
  358. SCROLLINFO si;
  359. si.cbSize = sizeof(si);
  360. si.fMask = SIF_RANGE | SIF_PAGE;
  361. si.nMin = 0;
  362. si.nMax = m_cItems * m_rcItemLabel.bottom + m_rcItemLabel.top - 1;
  363. si.nPage = nHeight; // ^^^^^^^^^ extra space
  364. SetScrollInfo(hWnd, SB_VERT, &si, FALSE);
  365. // Don't trust the width value passed in, since SetScrollInfo may
  366. // affect it if the scroll bar is turning on or off.
  367. GetClientRect(hWnd, &rc);
  368. nWidth = rc.right;
  369. // If the scrollbar is turned on, artificially bump up the width
  370. // by the width of the scrollbar, so the boxes don't jump to the left
  371. // when we have a scrollbar.
  372. if ((UINT)si.nMax >= si.nPage)
  373. nWidth += GetSystemMetrics(SM_CYHSCROLL);
  374. SetColumnWidth(hWnd, nWidth, m_cxCheckColumn);
  375. }
  376. TraceLeaveVoid();
  377. }
  378. LONG
  379. CCheckList::AddItem(HWND hWnd, LPCTSTR pszLabel, LPARAM lParam)
  380. {
  381. HWND hwndNew;
  382. HWND hwndPrev;
  383. RECT rc;
  384. LONG cyOffset;
  385. TraceEnter(TRACE_CHECKLIST, "CCheckList::AddItem");
  386. TraceAssert(hWnd != NULL);
  387. TraceAssert(pszLabel != NULL && !IsBadStringPtr(pszLabel, MAX_PATH));
  388. // If this is the first item, get column descriptions
  389. if (0 == m_cItems)
  390. GetColumnDescriptions(hWnd);
  391. // Calculate the position of the new static label
  392. rc = m_rcItemLabel;
  393. cyOffset = m_cItems * m_rcItemLabel.bottom;
  394. OffsetRect(&rc, 0, cyOffset);
  395. // Create a new label control
  396. hwndNew = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
  397. c_szStaticClass,
  398. pszLabel,
  399. WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP | SS_NOPREFIX,// | WS_GROUP,
  400. rc.left,
  401. rc.top,
  402. rc.right - rc.left,
  403. rc.bottom - rc.top,
  404. hWnd,
  405. (HMENU)IntToPtr(MAKE_LABEL_ID(m_cItems)),
  406. hModule,
  407. NULL);
  408. if (!hwndNew)
  409. TraceLeaveValue(-1);
  410. // Save item data
  411. SetWindowLongPtr(hwndNew, GWLP_USERDATA, lParam);
  412. // Set the font
  413. SendMessage(hwndNew,
  414. WM_SETFONT,
  415. SendMessage(GetParent(hWnd), WM_GETFONT, 0, 0),
  416. 0);
  417. // Set Z-order position just after the last checkbox. This keeps
  418. // tab order correct.
  419. if (m_cItems > 0)
  420. {
  421. hwndPrev = GetDlgItem(hWnd, MAKE_CTRL_ID(m_cItems - 1, m_cSubItems));
  422. SetWindowPos(hwndNew,
  423. hwndPrev,
  424. 0, 0, 0, 0,
  425. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  426. }
  427. // Create new checkboxes
  428. for (LONG j = 0; j < m_cSubItems; j++)
  429. {
  430. // Build window text for the control. The text is
  431. // hidden, but used for accessibility. (341042)
  432. LPCTSTR pszCheckText = pszLabel;
  433. LPTSTR pszT = NULL;
  434. if (m_pszColumnDesc[j] &&
  435. FormatStringID(&pszT, hModule, IDS_FMT_CHECKLABEL, pszLabel, m_pszColumnDesc[j]))
  436. {
  437. pszCheckText = pszT;
  438. }
  439. hwndPrev = hwndNew;
  440. hwndNew = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
  441. c_szButtonClass,
  442. pszCheckText,
  443. WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_NOTIFY | BS_FLAT | BS_AUTOCHECKBOX,
  444. m_nCheckPos[j],
  445. rc.top,
  446. m_cxCheckBox,
  447. rc.bottom - rc.top,
  448. hWnd,
  449. (HMENU)IntToPtr(MAKE_CTRL_ID(m_cItems, j + 1)),
  450. hModule,
  451. NULL);
  452. LocalFreeString(&pszT);
  453. if (!hwndNew)
  454. {
  455. while (j >= 0)
  456. {
  457. DestroyWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(m_cItems, j)));
  458. j--;
  459. }
  460. TraceLeaveValue(-1);
  461. }
  462. SetWindowPos(hwndNew,
  463. hwndPrev,
  464. 0, 0, 0, 0,
  465. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  466. }
  467. // We now officially have a new item
  468. m_cItems++;
  469. //
  470. // The last thing is to calculate the scroll range
  471. //
  472. LONG nBottom = rc.bottom;
  473. GetClientRect(hWnd, &rc);
  474. SCROLLINFO si;
  475. si.cbSize = sizeof(si);
  476. si.fMask = SIF_RANGE | SIF_PAGE;
  477. si.nMin = 0;
  478. si.nMax = nBottom + m_rcItemLabel.top - 1;
  479. si.nPage = rc.bottom; // ^^^^^^^^^ extra space
  480. SetScrollInfo(hWnd, SB_VERT, &si, FALSE);
  481. TraceLeaveValue(m_cItems - 1); // return the index of the new item
  482. }
  483. void
  484. CCheckList::SetState(HWND hWnd, WORD iItem, WORD iSubItem, LONG lState)
  485. {
  486. TraceEnter(TRACE_CHECKLIST, "CCheckList::SetState");
  487. TraceAssert(hWnd != NULL);
  488. TraceAssert(iItem < m_cItems);
  489. TraceAssert(0 < iSubItem && iSubItem <= m_cSubItems);
  490. HWND hwndCtrl = GetDlgItem(hWnd, MAKE_CTRL_ID(iItem, iSubItem));
  491. if (hwndCtrl != NULL)
  492. {
  493. SendMessage(hwndCtrl, BM_SETCHECK, lState & CLST_CHECKED, 0);
  494. EnableWindow(hwndCtrl, !(lState & CLST_DISABLED));
  495. }
  496. TraceLeaveVoid();
  497. }
  498. LONG
  499. CCheckList::GetState(HWND hWnd, WORD iItem, WORD iSubItem)
  500. {
  501. LONG lState = 0;
  502. TraceEnter(TRACE_CHECKLIST, "CCheckList::GetState");
  503. TraceAssert(hWnd != NULL);
  504. TraceAssert(iItem < m_cItems);
  505. TraceAssert(0 < iSubItem && iSubItem <= m_cSubItems);
  506. HWND hwndCtrl = GetDlgItem(hWnd, MAKE_CTRL_ID(iItem, iSubItem));
  507. if (hwndCtrl != NULL)
  508. {
  509. lState = (LONG)SendMessage(hwndCtrl, BM_GETCHECK, 0, 0);
  510. TraceAssert(!(lState & BST_INDETERMINATE));
  511. if (!IsWindowEnabled(hwndCtrl))
  512. lState |= CLST_DISABLED;
  513. }
  514. TraceLeaveValue(lState);
  515. }
  516. void
  517. CCheckList::SetColumnWidth(HWND hWnd, LONG cxDialog, LONG cxColumn)
  518. {
  519. LONG j;
  520. TraceEnter(TRACE_CHECKLIST, "CCheckList::SetColumnWidth");
  521. TraceAssert(hWnd != NULL);
  522. TraceAssert(cxColumn > 10);
  523. m_cxCheckColumn = cxColumn;
  524. if (m_cSubItems > 0)
  525. {
  526. m_nCheckPos[m_cSubItems-1] = cxDialog // dlg width
  527. - m_rcItemLabel.left // right margin
  528. - (cxColumn + m_cxCheckBox)/2; // 1/2 col & 1/2 checkbox
  529. for (j = m_cSubItems - 1; j > 0; j--)
  530. m_nCheckPos[j-1] = m_nCheckPos[j] - cxColumn;
  531. // (leftmost check pos) - (horz margin)
  532. m_rcItemLabel.right = m_nCheckPos[0] - m_rcItemLabel.left;
  533. }
  534. else
  535. m_rcItemLabel.right = cxDialog - m_rcItemLabel.left;
  536. LONG nTop = m_rcItemLabel.top;
  537. LONG nBottom = m_rcItemLabel.bottom;
  538. for (LONG i = 0; i < m_cItems; i++)
  539. {
  540. MoveWindow(GetDlgItem(hWnd, MAKE_LABEL_ID(i)),
  541. m_rcItemLabel.left,
  542. nTop,
  543. m_rcItemLabel.right - m_rcItemLabel.left,
  544. nBottom - nTop,
  545. FALSE);
  546. for (j = 0; j < m_cSubItems; j++)
  547. {
  548. MoveWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(i, j + 1)),
  549. m_nCheckPos[j],
  550. nTop,
  551. m_cxCheckBox,
  552. nBottom - nTop,
  553. FALSE);
  554. }
  555. nTop += m_rcItemLabel.bottom;
  556. nBottom += m_rcItemLabel.bottom;
  557. }
  558. TraceLeaveVoid();
  559. }
  560. void
  561. CCheckList::ResetContent(HWND hWnd)
  562. {
  563. for (LONG i = 0; i < m_cItems; i++)
  564. for (LONG j = 0; j <= m_cSubItems; j++)
  565. DestroyWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(i, j)));
  566. // Hide the scroll bar
  567. ShowScrollBar(hWnd, SB_VERT, FALSE);
  568. m_cItems = 0;
  569. }
  570. LONG
  571. CCheckList::GetVisibleCount(HWND hWnd)
  572. {
  573. LONG nCount = 1;
  574. RECT rc;
  575. if (GetClientRect(hWnd, &rc) && m_rcItemLabel.bottom > 0)
  576. nCount = max(1, rc.bottom / m_rcItemLabel.bottom);
  577. return nCount;
  578. }
  579. LONG
  580. CCheckList::GetTopIndex(HWND hWnd)
  581. {
  582. LONG nIndex = 0;
  583. SCROLLINFO si;
  584. si.cbSize = sizeof(si);
  585. si.fMask = SIF_POS;
  586. if (GetScrollInfo(hWnd, SB_VERT, &si) && m_rcItemLabel.bottom > 0)
  587. nIndex = max(0, si.nPos / m_rcItemLabel.bottom);
  588. return nIndex;
  589. }
  590. void
  591. CCheckList::EnsureVisible(HWND hWnd, LONG nItemIndex)
  592. {
  593. LONG nTopIndex = GetTopIndex(hWnd);
  594. // Note that the top item may only be partially visible,
  595. // so we need to test for equality here. Raid #208449
  596. if (nItemIndex <= nTopIndex)
  597. {
  598. SetTopIndex(hWnd, nItemIndex);
  599. }
  600. else
  601. {
  602. LONG nVisible = GetVisibleCount(hWnd);
  603. if (nItemIndex >= nTopIndex + nVisible)
  604. SetTopIndex(hWnd, nItemIndex - nVisible + 1);
  605. }
  606. }
  607. void
  608. CCheckList::DrawCheckFocusRect(HWND hWnd, HWND hwndCheck, BOOL fDraw)
  609. {
  610. RECT rcCheck;
  611. HDC hdc;
  612. TraceEnter(TRACE_CHECKLIST, "CCheckList::DrawCheckFocusRect");
  613. TraceAssert(hWnd != NULL);
  614. TraceAssert(hwndCheck != NULL);
  615. GetWindowRect(hwndCheck, &rcCheck);
  616. MapWindowPoints(NULL, hWnd, (LPPOINT)&rcCheck, 2);
  617. InflateRect(&rcCheck, 2, 2); // draw *outside* the checkbox
  618. hdc = GetDC(hWnd);
  619. if (hdc)
  620. {
  621. // Always erase before drawing, since we may already be
  622. // partially visible and drawing is an XOR operation.
  623. // (Don't want to leave any turds on the screen.)
  624. FrameRect(hdc, &rcCheck, GetSysColorBrush(BK_COLOR));
  625. if (fDraw)
  626. {
  627. SetTextColor(hdc, GetSysColor(TEXT_COLOR));
  628. SetBkColor(hdc, GetSysColor(BK_COLOR));
  629. DrawFocusRect(hdc, &rcCheck);
  630. }
  631. ReleaseDC(hWnd, hdc);
  632. }
  633. TraceLeaveVoid();
  634. }
  635. void
  636. CCheckList::GetColumnDescriptions(HWND hWnd)
  637. {
  638. //
  639. // Get column descriptions for accessibility
  640. //
  641. TCHAR szDescription[MAX_PATH];
  642. NM_CHECKLIST nmc;
  643. nmc.hdr.hwndFrom = hWnd;
  644. nmc.hdr.idFrom = GetDlgCtrlID(hWnd);
  645. nmc.hdr.code = CLN_GETCOLUMNDESC;
  646. nmc.iItem = 0;
  647. nmc.dwState = 0;
  648. nmc.dwItemData = 0;
  649. nmc.cchTextMax = ARRAYSIZE(szDescription);
  650. nmc.pszText = szDescription;
  651. for (LONG j = 0; j < m_cSubItems; j++)
  652. {
  653. szDescription[0] = TEXT('\0');
  654. nmc.iSubItem = j+1;
  655. SendMessage(GetParent(hWnd),
  656. WM_NOTIFY,
  657. nmc.hdr.idFrom,
  658. (LPARAM)&nmc);
  659. LocalFreeString(&m_pszColumnDesc[j]);
  660. if (szDescription[0])
  661. LocalAllocString(&m_pszColumnDesc[j], szDescription);
  662. }
  663. }
  664. LRESULT
  665. CALLBACK
  666. CCheckList::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  667. {
  668. LRESULT lResult = 0;
  669. CCheckList *pThis = (CCheckList*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
  670. TraceEnter(TRACE_CHECKLIST, "CCheckList::WindowProc");
  671. TraceAssert(hWnd != NULL);
  672. switch (uMsg)
  673. {
  674. case WM_NCCREATE:
  675. pThis = new CCheckList(hWnd, (LPCREATESTRUCT)lParam);
  676. if (pThis != NULL)
  677. {
  678. SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
  679. lResult = TRUE;
  680. }
  681. break;
  682. case WM_NCDESTROY:
  683. delete pThis;
  684. break;
  685. case WM_COMMAND:
  686. TraceAssert(pThis != NULL);
  687. lResult = pThis->MsgCommand(hWnd,
  688. GET_WM_COMMAND_ID(wParam, lParam),
  689. GET_WM_COMMAND_CMD(wParam, lParam),
  690. GET_WM_COMMAND_HWND(wParam, lParam));
  691. break;
  692. case WM_CTLCOLORSTATIC:
  693. TraceAssert(pThis != NULL);
  694. SetBkMode((HDC)wParam, TRANSPARENT);
  695. SetTextColor((HDC)wParam, GetSysColor(TEXT_COLOR));
  696. SetBkColor((HDC)wParam, GetSysColor(BK_COLOR));
  697. lResult = (LRESULT)GetSysColorBrush(BK_COLOR);
  698. break;
  699. case WM_PAINT:
  700. TraceAssert(pThis != NULL);
  701. pThis->MsgPaint(hWnd, (HDC)wParam);
  702. break;
  703. case WM_VSCROLL:
  704. TraceAssert(pThis != NULL);
  705. pThis->MsgVScroll(hWnd,
  706. (int)(short)GET_WM_VSCROLL_CODE(wParam, lParam),
  707. (int)(short)GET_WM_VSCROLL_POS(wParam, lParam));
  708. break;
  709. case WM_MOUSEWHEEL:
  710. TraceAssert(pThis != NULL);
  711. pThis->MsgMouseWheel(hWnd,
  712. LOWORD(wParam),
  713. (int)(short)HIWORD(wParam));
  714. break;
  715. case WM_LBUTTONDOWN:
  716. TraceAssert(pThis != NULL);
  717. pThis->MsgButtonDown(hWnd,
  718. wParam,
  719. (int)(short)LOWORD(lParam),
  720. (int)(short)HIWORD(lParam));
  721. break;
  722. case WM_ENABLE:
  723. TraceAssert(pThis != NULL);
  724. pThis->MsgEnable(hWnd, (BOOL)wParam);
  725. break;
  726. case WM_SETFONT:
  727. TraceAssert(pThis != NULL);
  728. {
  729. for (LONG i = 0; i < pThis->m_cItems; i++)
  730. SendDlgItemMessage(hWnd,
  731. MAKE_LABEL_ID(i),
  732. WM_SETFONT,
  733. wParam,
  734. lParam);
  735. }
  736. break;
  737. case WM_SIZE:
  738. TraceAssert(pThis != NULL);
  739. pThis->MsgSize(hWnd, (DWORD)wParam, LOWORD(lParam), HIWORD(lParam));
  740. break;
  741. case CLM_ADDITEM:
  742. TraceAssert(pThis != NULL);
  743. lResult = pThis->AddItem(hWnd, (LPCTSTR)wParam, lParam);
  744. break;
  745. case CLM_GETITEMCOUNT:
  746. TraceAssert(pThis != NULL);
  747. lResult = pThis->m_cItems;
  748. break;
  749. case CLM_SETSTATE:
  750. TraceAssert(pThis != NULL);
  751. pThis->SetState(hWnd, LOWORD(wParam), HIWORD(wParam), (LONG)lParam);
  752. break;
  753. case CLM_GETSTATE:
  754. TraceAssert(pThis != NULL);
  755. lResult = pThis->GetState(hWnd, LOWORD(wParam), HIWORD(wParam));
  756. break;
  757. case CLM_SETCOLUMNWIDTH:
  758. TraceAssert(pThis != NULL);
  759. {
  760. RECT rc;
  761. LONG cxDialog;
  762. GetClientRect(hWnd, &rc);
  763. cxDialog = rc.right;
  764. rc.right = (LONG)lParam;
  765. MapDialogRect(GetParent(hWnd), &rc);
  766. pThis->SetColumnWidth(hWnd, cxDialog, rc.right);
  767. }
  768. break;
  769. case CLM_SETITEMDATA:
  770. TraceAssert(GET_ITEM(wParam) < (ULONG)pThis->m_cItems);
  771. SetWindowLongPtr(GetDlgItem(hWnd, MAKE_LABEL_ID((int)wParam)),
  772. GWLP_USERDATA,
  773. lParam);
  774. break;
  775. case CLM_GETITEMDATA:
  776. TraceAssert(GET_ITEM(wParam) < (ULONG)pThis->m_cItems);
  777. lResult = GetWindowLongPtr(GetDlgItem(hWnd, MAKE_LABEL_ID((int)wParam)),
  778. GWLP_USERDATA);
  779. break;
  780. case CLM_RESETCONTENT:
  781. TraceAssert(pThis != NULL);
  782. pThis->ResetContent(hWnd);
  783. break;
  784. case CLM_GETVISIBLECOUNT:
  785. TraceAssert(pThis != NULL);
  786. lResult = pThis->GetVisibleCount(hWnd);
  787. break;
  788. case CLM_GETTOPINDEX:
  789. TraceAssert(pThis != NULL);
  790. lResult = pThis->GetTopIndex(hWnd);
  791. break;
  792. case CLM_SETTOPINDEX:
  793. TraceAssert(pThis != NULL);
  794. pThis->SetTopIndex(hWnd, (LONG)wParam);
  795. break;
  796. case CLM_ENSUREVISIBLE:
  797. TraceAssert(pThis != NULL);
  798. pThis->EnsureVisible(hWnd, (LONG)wParam);
  799. break;
  800. //
  801. // Always refer to the chklist window for help. Don't pass
  802. // one of the child window handles here.
  803. //
  804. case WM_HELP:
  805. ((LPHELPINFO)lParam)->hItemHandle = hWnd;
  806. lResult = SendMessage(GetParent(hWnd), uMsg, wParam, lParam);
  807. break;
  808. case WM_CONTEXTMENU:
  809. lResult = SendMessage(GetParent(hWnd), uMsg, (WPARAM)hWnd, lParam);
  810. break;
  811. default:
  812. lResult = DefWindowProc(hWnd, uMsg, wParam, lParam);
  813. }
  814. TraceLeaveValue(lResult);
  815. }