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.

1265 lines
37 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows/NT **/
  3. /** Copyright(c) Microsoft Corp., 1991 **/
  4. /**********************************************************************/
  5. /*
  6. CHKLIST.CPP
  7. This file contains the implementation of the CheckList control.
  8. */
  9. #include "precomp.h"
  10. #include <windowsx.h>
  11. #include "chklist.h"
  12. #include "debug.h"
  13. #include "Richedit.h"
  14. //
  15. // Text and Background colors
  16. //
  17. #define TEXT_COLOR COLOR_WINDOWTEXT
  18. #define BK_COLOR COLOR_WINDOW
  19. //
  20. // Default dimensions for child controls. All are in dialog units.
  21. // Currently only the column width is user-adjustable (via the
  22. // CLM_SETCOLUMNWIDTH message).
  23. //
  24. #define DEFAULT_COLUMN_WIDTH 32
  25. #define DEFAULT_CHECK_WIDTH 9
  26. #define DEFAULT_HORZ_SPACE 7
  27. #define DEFAULT_VERTICAL_SPACE 3
  28. #define DEFAULT_ITEM_HEIGHT 8
  29. //
  30. // 16 bits are used for the control ID's, divided into n bits for
  31. // the subitem (least significant) and 16-n bits for the item index.
  32. //
  33. // ID_SUBITEM_BITS can be adjusted to control the maximum number of
  34. // items and subitems. For example, to allow up to 7 subitems and 8k
  35. // items, set ID_SUBITEM_BITS to 3.
  36. //
  37. // Use the low 2 bits for the subitem index, the rest for the item index.
  38. // (4 subitems max, 16k items max)
  39. #define ID_SUBITEM_BITS 2
  40. #define ID_SUBITEM_MASK ((1 << ID_SUBITEM_BITS) - 1)
  41. #define GET_ITEM(id) ((id) >> ID_SUBITEM_BITS)
  42. #define GET_SUBITEM(id) ((id) & ID_SUBITEM_MASK)
  43. #define MAKE_CTRL_ID(i, s) (0xffff & (((i) << ID_SUBITEM_BITS) | ((s) & ID_SUBITEM_MASK)))
  44. #define MAKE_LABEL_ID(i) MAKE_CTRL_ID(i, 0)
  45. // Note that the subitem (column) index is one-based for the checkboxes
  46. // (the zero column is the label). The item (row) index is zero-based.
  47. #define MAX_CHECK_COLUMNS ID_SUBITEM_MASK
  48. typedef struct _USERDATA_STRUCT_LABEL
  49. {
  50. LPARAM lParam;
  51. int nLabelHeight;
  52. } USERDATA_STRUCT_LABEL, *LPUSERDATA_STRUCT_LABEL;
  53. class CCheckList
  54. {
  55. private:
  56. LONG m_cItems;
  57. LONG m_cSubItems;
  58. RECT m_rcItemLabel;
  59. LONG m_nCheckPos[MAX_CHECK_COLUMNS];
  60. LONG m_cxCheckBox;
  61. LONG m_cxCheckColumn;
  62. int m_nDefaultVerticalSpace;
  63. int m_nDefaultItemHeight;
  64. int m_nNewItemYPos;
  65. HWND m_hwndCheckFocus;
  66. BOOL m_fInMessageEnable;
  67. int m_cWheelDelta;
  68. static UINT g_ucScrollLines;
  69. private:
  70. CCheckList(HWND hWnd, LPCREATESTRUCT lpcs);
  71. LRESULT MsgCommand(HWND hWnd, WORD idCmd, WORD wNotify, HWND hwndCtrl);
  72. void MsgPaint(HWND hWnd, HDC hdc);
  73. void MsgVScroll(HWND hWnd, int nCode, int nPos);
  74. void MsgMouseWheel(HWND hWnd, WORD fwFlags, int zDelta);
  75. void MsgButtonDown(HWND hWnd, WPARAM fwFlags, int xPos, int yPos);
  76. void MsgEnable(HWND hWnd, BOOL fEnabled);
  77. void MsgSize(HWND hWnd, DWORD dwSizeType, LONG nWidth, LONG nHeight);
  78. LONG AddItem(HWND hWnd, LPCTSTR pszLabel, LPARAM lParam);
  79. void SetState(HWND hWnd, WORD iItem, WORD iSubItem, LONG lState);
  80. LONG GetState(HWND hWnd, WORD iItem, WORD iSubItem);
  81. void SetColumnWidth(HWND hWnd, LONG cxDialog, LONG cxColumn);
  82. void ResetContent(HWND hWnd);
  83. LONG GetVisibleCount(HWND hWnd);
  84. LONG GetTopIndex(HWND hWnd, LONG *pnAmountObscured = NULL);
  85. void SetTopIndex(HWND hWnd, LONG nIndex);
  86. void EnsureVisible(HWND hWnd, LONG nIndex);
  87. void DrawCheckFocusRect(HWND hWnd, HWND hwndCheck, BOOL fDraw);
  88. public:
  89. static LRESULT CALLBACK WindowProc(HWND hWnd,
  90. UINT uMsg,
  91. WPARAM wParam,
  92. LPARAM lParam);
  93. };
  94. BOOL RegisterCheckListWndClass(void)
  95. {
  96. WNDCLASS wc;
  97. wc.style = 0;
  98. wc.lpfnWndProc = CCheckList::WindowProc;
  99. wc.cbClsExtra = 0;
  100. wc.cbWndExtra = 0;
  101. // AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Required for AfxGetInstanceHandle()
  102. wc.hInstance = _Module.GetModuleInstance(); //hModule;
  103. wc.hIcon = NULL;
  104. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  105. wc.hbrBackground = (HBRUSH)(BK_COLOR+1);
  106. wc.lpszMenuName = NULL;
  107. wc.lpszClassName = TEXT(WC_CHECKLIST);
  108. return (BOOL)RegisterClass(&wc);
  109. }
  110. UINT CCheckList::g_ucScrollLines = (UINT)-1;
  111. CCheckList::CCheckList(HWND hWnd, LPCREATESTRUCT lpcs)
  112. : m_cItems(0), m_hwndCheckFocus(NULL), m_fInMessageEnable(FALSE), m_cWheelDelta(0)
  113. {
  114. TraceEnter(TRACE_CHECKLIST, "CCheckList::CCheckList");
  115. TraceAssert(hWnd != NULL);
  116. TraceAssert(lpcs != NULL);
  117. //
  118. // Get number of check columns
  119. //
  120. m_cSubItems = lpcs->style & CLS_CHECKMASK;
  121. // for wsecedit only
  122. if ( m_cSubItems > 3 ) {
  123. m_cSubItems = 3;
  124. }
  125. //
  126. // Convert default coordinates from dialog units to pixels
  127. //
  128. RECT rc;
  129. rc.left = DEFAULT_CHECK_WIDTH;
  130. rc.right = DEFAULT_COLUMN_WIDTH;
  131. rc.top = rc.bottom = 0;
  132. MapDialogRect(lpcs->hwndParent, &rc);
  133. // Save the converted values
  134. m_cxCheckBox = rc.left;
  135. m_cxCheckColumn = rc.right;
  136. rc.left = DEFAULT_HORZ_SPACE;
  137. rc.top = DEFAULT_VERTICAL_SPACE;
  138. rc.right = 10; // bogus (unused)
  139. rc.bottom = DEFAULT_VERTICAL_SPACE + DEFAULT_ITEM_HEIGHT;
  140. MapDialogRect(lpcs->hwndParent, &rc);
  141. // Save the converted values
  142. m_rcItemLabel = rc;
  143. m_nDefaultVerticalSpace = rc.top;
  144. m_nDefaultItemHeight = rc.bottom - rc.top;
  145. m_nNewItemYPos = rc.top;
  146. //
  147. // Get info for mouse wheel scrolling
  148. //
  149. if ((UINT)-1 == g_ucScrollLines)
  150. {
  151. g_ucScrollLines = 3; // default
  152. SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &g_ucScrollLines, 0);
  153. }
  154. TraceLeaveVoid();
  155. }
  156. LRESULT
  157. CCheckList::MsgCommand(HWND hWnd, WORD idCmd, WORD wNotify, HWND hwndCtrl)
  158. {
  159. TraceEnter(TRACE_CHECKLIST, "CCheckList::MsgCommand");
  160. // Should only get notifications from visible, enabled, check boxes
  161. TraceAssert(GET_ITEM(idCmd) < m_cItems);
  162. TraceAssert(0 < GET_SUBITEM(idCmd) && GET_SUBITEM(idCmd) <= m_cSubItems);
  163. TraceAssert(hwndCtrl && IsWindowEnabled(hwndCtrl));
  164. switch (wNotify)
  165. {
  166. case EN_SETFOCUS:
  167. {
  168. // Make the focus go to one of the checkboxes
  169. POINT pt;
  170. DWORD dwPos = GetMessagePos();
  171. pt.x = GET_X_LPARAM(dwPos);
  172. pt.y = GET_Y_LPARAM(dwPos);
  173. MapWindowPoints(NULL, hWnd, &pt, 1);
  174. MsgButtonDown(hWnd, 0, pt.x, pt.y);
  175. }
  176. break;
  177. case BN_CLICKED:
  178. {
  179. LPUSERDATA_STRUCT_LABEL lpUserData;
  180. NM_CHECKLIST nmc;
  181. nmc.hdr.hwndFrom = hWnd;
  182. nmc.hdr.idFrom = GetDlgCtrlID(hWnd);
  183. nmc.hdr.code = CLN_CLICK;
  184. nmc.iItem = GET_ITEM(idCmd);
  185. nmc.iSubItem = GET_SUBITEM(idCmd);
  186. nmc.dwState = (DWORD)SendMessage(hwndCtrl, BM_GETCHECK, 0, 0);
  187. if (!IsWindowEnabled(hwndCtrl))
  188. nmc.dwState |= CLST_DISABLED;
  189. lpUserData = (LPUSERDATA_STRUCT_LABEL)
  190. GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID(nmc.iItem)),
  191. GWLP_USERDATA);
  192. nmc.dwItemData = lpUserData->lParam;
  193. SendMessage(GetParent(hWnd),
  194. WM_NOTIFY,
  195. nmc.hdr.idFrom,
  196. (LPARAM)&nmc);
  197. }
  198. break;
  199. case BN_SETFOCUS:
  200. if (GetFocus() != hwndCtrl)
  201. {
  202. // This causes another BN_SETFOCUS
  203. SetFocus(hwndCtrl);
  204. }
  205. else
  206. {
  207. if (m_hwndCheckFocus != hwndCtrl) // Has the focus moved?
  208. {
  209. // Remember where the focus is
  210. m_hwndCheckFocus = hwndCtrl;
  211. // Make sure the row is scrolled into view
  212. EnsureVisible(hWnd, GET_ITEM(idCmd));
  213. }
  214. // Always draw the focus rect
  215. DrawCheckFocusRect(hWnd, hwndCtrl, TRUE);
  216. }
  217. break;
  218. case BN_KILLFOCUS:
  219. // Remove the focus rect
  220. m_hwndCheckFocus = NULL;
  221. DrawCheckFocusRect(hWnd, hwndCtrl, FALSE);
  222. break;
  223. }
  224. TraceLeaveValue(0);
  225. }
  226. void
  227. CCheckList::MsgPaint(HWND hWnd, HDC hdc)
  228. {
  229. if (hdc == NULL && m_hwndCheckFocus != NULL)
  230. {
  231. // This will cause a focus rect to be drawn after the window and
  232. // all checkboxes have been painted.
  233. PostMessage(hWnd,
  234. WM_COMMAND,
  235. GET_WM_COMMAND_MPS(GetDlgCtrlID(m_hwndCheckFocus), m_hwndCheckFocus, BN_SETFOCUS));
  236. }
  237. // Default paint
  238. DefWindowProc(hWnd, WM_PAINT, (WPARAM)hdc, 0);
  239. }
  240. void
  241. CCheckList::MsgVScroll(HWND hWnd, int nCode, int nPos)
  242. {
  243. UINT cScrollUnitsPerLine;
  244. SCROLLINFO si;
  245. si.cbSize = sizeof(si);
  246. si.fMask = SIF_ALL;
  247. if (!GetScrollInfo(hWnd, SB_VERT, &si))
  248. return;
  249. cScrollUnitsPerLine = m_rcItemLabel.bottom;
  250. // One page is always visible, so adjust the range to a more useful value
  251. si.nMax -= si.nPage - 1;
  252. switch (nCode)
  253. {
  254. case SB_LINEUP:
  255. // "line" is the height of one item (includes the space in between)
  256. nPos = si.nPos - cScrollUnitsPerLine;
  257. break;
  258. case SB_LINEDOWN:
  259. nPos = si.nPos + cScrollUnitsPerLine;
  260. break;
  261. case SB_PAGEUP:
  262. nPos = si.nPos - si.nPage;
  263. break;
  264. case SB_PAGEDOWN:
  265. nPos = si.nPos + si.nPage;
  266. break;
  267. case SB_TOP:
  268. nPos = si.nMin;
  269. break;
  270. case SB_BOTTOM:
  271. nPos = si.nMax;
  272. break;
  273. case SB_ENDSCROLL:
  274. nPos = si.nPos; // don't go anywhere
  275. break;
  276. case SB_THUMBTRACK:
  277. // Do nothing here to allow tracking
  278. // nPos = si.nPos; // Do this to prevent tracking
  279. case SB_THUMBPOSITION:
  280. // nothing to do here... nPos is passed in
  281. break;
  282. }
  283. // Make sure the new position is within the range
  284. if (nPos < si.nMin)
  285. nPos = si.nMin;
  286. else if (nPos > si.nMax)
  287. nPos = si.nMax;
  288. if (nPos != si.nPos) // are we moving?
  289. {
  290. SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
  291. ScrollWindow(hWnd, 0, si.nPos - nPos, NULL, NULL);
  292. }
  293. }
  294. void
  295. CCheckList::MsgMouseWheel(HWND hWnd, WORD fwFlags, int iWheelDelta)
  296. {
  297. int cDetants;
  298. if ((fwFlags & (MK_SHIFT | MK_CONTROL)) || 0 == g_ucScrollLines)
  299. return;
  300. TraceEnter(TRACE_CHECKLIST, "CCheckList::MsgMouseWheel");
  301. // Update count of scroll amount
  302. m_cWheelDelta -= iWheelDelta;
  303. cDetants = m_cWheelDelta / WHEEL_DELTA;
  304. if (0 == cDetants)
  305. TraceLeaveVoid();
  306. m_cWheelDelta %= WHEEL_DELTA;
  307. if (WS_VSCROLL & GetWindowLong(hWnd, GWL_STYLE))
  308. {
  309. SCROLLINFO si;
  310. UINT cScrollUnitsPerLine;
  311. UINT cLinesPerPage;
  312. UINT cLinesPerDetant;
  313. // Get the scroll amount of one line
  314. cScrollUnitsPerLine = m_rcItemLabel.bottom;
  315. TraceAssert(cScrollUnitsPerLine > 0);
  316. si.cbSize = sizeof(SCROLLINFO);
  317. si.fMask = SIF_PAGE | SIF_POS;
  318. if (!GetScrollInfo(hWnd, SB_VERT, &si))
  319. TraceLeaveVoid();
  320. // The size of a page is at least one line, and
  321. // leaves one line of overlap
  322. cLinesPerPage = (si.nPage - cScrollUnitsPerLine) / cScrollUnitsPerLine;
  323. cLinesPerPage = max(1, cLinesPerPage);
  324. // Don't scroll more than one page per detant
  325. cLinesPerDetant = min(cLinesPerPage, g_ucScrollLines);
  326. si.nPos += cDetants * cLinesPerDetant * cScrollUnitsPerLine;
  327. MsgVScroll(hWnd, SB_THUMBTRACK, si.nPos);
  328. }
  329. TraceLeaveVoid();
  330. }
  331. void
  332. CCheckList::MsgButtonDown(HWND hWnd, WPARAM /*fwFlags*/, int /*xPos*/, int yPos)
  333. {
  334. LONG nItemIndex;
  335. HWND hwndCheck;
  336. RECT rc;
  337. // Get position of the top visible item in client coords
  338. nItemIndex = GetTopIndex(hWnd);
  339. if (nItemIndex == -1)
  340. {
  341. return;
  342. }
  343. hwndCheck = GetDlgItem(hWnd, MAKE_CTRL_ID(nItemIndex, 0));
  344. GetWindowRect(hwndCheck, &rc);
  345. MapWindowPoints(NULL, hWnd, (LPPOINT)&rc, 2);
  346. // Find nearest item
  347. nItemIndex += (yPos - rc.top + m_rcItemLabel.top/2)/m_rcItemLabel.bottom;
  348. nItemIndex = max(0, min(nItemIndex, m_cItems - 1)); // 0 <= y < m_cItems
  349. // Set focus to first subitem that is enabled
  350. for (LONG j = 1; j <= m_cSubItems; j++)
  351. {
  352. int id = MAKE_CTRL_ID(nItemIndex, j);
  353. HWND hwndCheck = GetDlgItem(hWnd, id);
  354. if (IsWindowEnabled(hwndCheck))
  355. {
  356. // Don't just SetFocus here. We sometimes call this during
  357. // EN_SETFOCUS, and USER doesn't like it when you mess with
  358. // focus during a focus change.
  359. //
  360. //SetFocus(hwndCheck);
  361. PostMessage(hWnd,
  362. WM_COMMAND,
  363. GET_WM_COMMAND_MPS(id, hwndCheck, BN_SETFOCUS));
  364. break;
  365. }
  366. }
  367. }
  368. void
  369. CCheckList::MsgEnable(HWND hWnd, BOOL fEnabled)
  370. {
  371. HWND hwndCurrentCheck;
  372. BOOL fCheckEnabled;
  373. if (!m_fInMessageEnable)
  374. {
  375. m_fInMessageEnable = TRUE;
  376. for (LONG i = 0; i < m_cItems; i++)
  377. {
  378. for (LONG j = 1; j <= m_cSubItems; j++)
  379. {
  380. hwndCurrentCheck = GetDlgItem(hWnd, MAKE_CTRL_ID(i, j));
  381. fCheckEnabled = (BOOL) GetWindowLongPtr(hwndCurrentCheck, GWLP_USERDATA);
  382. //
  383. // If the user of the checklist control is disabling the control
  384. // altogether, or the current checkbox has been disabled singularly
  385. // then disable the checkbox
  386. //
  387. if (!fEnabled || !fCheckEnabled)
  388. {
  389. EnableWindow(hwndCurrentCheck, FALSE);
  390. }
  391. else
  392. {
  393. EnableWindow(hwndCurrentCheck, TRUE);
  394. }
  395. }
  396. }
  397. // Note that the main chklist window must remain enabled
  398. // for scrolling to work while "disabled".
  399. if (!fEnabled)
  400. EnableWindow(hWnd, TRUE);
  401. m_fInMessageEnable = FALSE;
  402. }
  403. }
  404. void
  405. CCheckList::MsgSize(HWND hWnd, DWORD dwSizeType, LONG nWidth, LONG nHeight)
  406. {
  407. TraceEnter(TRACE_CHECKLIST, "CCheckList::MsgSize");
  408. TraceAssert(hWnd != NULL);
  409. if (dwSizeType == SIZE_RESTORED)
  410. {
  411. RECT rc;
  412. SCROLLINFO si;
  413. si.cbSize = sizeof(si);
  414. si.fMask = SIF_RANGE | SIF_PAGE;
  415. si.nMin = 0;
  416. si.nMax = m_nNewItemYPos - 1;
  417. si.nPage = nHeight;
  418. SetScrollInfo(hWnd, SB_VERT, &si, FALSE);
  419. // Don't trust the width value passed in, since SetScrollInfo may
  420. // affect it if the scroll bar is turning on or off.
  421. GetClientRect(hWnd, &rc);
  422. nWidth = rc.right;
  423. // If the scrollbar is turned on, artificially bump up the width
  424. // by the width of the scrollbar, so the boxes don't jump to the left
  425. // when we have a scrollbar.
  426. if ((UINT)si.nMax >= si.nPage)
  427. nWidth += GetSystemMetrics(SM_CYHSCROLL);
  428. SetColumnWidth(hWnd, nWidth, m_cxCheckColumn);
  429. }
  430. TraceLeaveVoid();
  431. }
  432. LONG
  433. CCheckList::AddItem(HWND hWnd, LPCTSTR pszLabel, LPARAM lParam)
  434. {
  435. HWND hwndNew;
  436. HWND hwndPrev;
  437. RECT rc;
  438. LONG nLabelHeight;
  439. HINSTANCE hModule;
  440. HDC hdc;
  441. TEXTRANGE tr;
  442. SIZE size;
  443. LPUSERDATA_STRUCT_LABEL lpUserData;
  444. SCROLLINFO si;
  445. LONG nLineCount = 1;
  446. LONG i, j;
  447. DWORD dwCheckStyle;
  448. TraceEnter(TRACE_CHECKLIST, "CCheckList::AddItem");
  449. TraceAssert(hWnd != NULL);
  450. TraceAssert(pszLabel != NULL && !IsBadStringPtr(pszLabel, MAX_PATH));
  451. lpUserData = new (USERDATA_STRUCT_LABEL);
  452. if (lpUserData == NULL)
  453. TraceLeaveValue(-1);
  454. si.cbSize = sizeof(si);
  455. si.fMask = SIF_POS;
  456. si.nPos = 0;
  457. GetScrollInfo(hWnd, SB_VERT, &si);
  458. // Set the initial label height extra big so the control can wrap the text,
  459. // then reset it after creating the control.
  460. GetClientRect(hWnd, &rc);
  461. nLabelHeight = rc.bottom;
  462. // AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Required for AfxGetInstanceHandle()
  463. hModule = _Module.GetModuleInstance();
  464. // Create a new label control
  465. hwndNew = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
  466. TEXT("edit"),
  467. pszLabel,
  468. WS_CHILD | WS_VISIBLE | WS_GROUP | ES_MULTILINE | ES_READONLY | ES_LEFT,// | WS_GROUP,
  469. m_rcItemLabel.left,
  470. m_nNewItemYPos - si.nPos,
  471. m_rcItemLabel.right - m_rcItemLabel.left,
  472. nLabelHeight,
  473. hWnd,
  474. (HMENU)IntToPtr(MAKE_LABEL_ID(m_cItems)),
  475. hModule,
  476. NULL);
  477. if (!hwndNew)
  478. {
  479. delete (lpUserData);
  480. TraceLeaveValue(-1);
  481. }
  482. //
  483. // Reset window height after word wrap has been done.
  484. //
  485. nLineCount = (LONG) SendMessage(hwndNew, EM_GETLINECOUNT, 0, (LPARAM) 0);
  486. nLabelHeight = nLineCount * m_nDefaultItemHeight;
  487. SetWindowPos(hwndNew,
  488. NULL,
  489. 0,
  490. 0,
  491. m_rcItemLabel.right - m_rcItemLabel.left,
  492. nLabelHeight,
  493. SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  494. //
  495. // Save item data
  496. //
  497. lpUserData->lParam = lParam;
  498. lpUserData->nLabelHeight = nLabelHeight;
  499. SetWindowLongPtr(hwndNew, GWLP_USERDATA, (LPARAM) lpUserData);
  500. // Set the font
  501. SendMessage(hwndNew,
  502. WM_SETFONT,
  503. SendMessage(GetParent(hWnd), WM_GETFONT, 0, 0),
  504. 0);
  505. // Set Z-order position just after the last checkbox. This keeps
  506. // tab order correct.
  507. if (m_cItems > 0)
  508. {
  509. hwndPrev = GetDlgItem(hWnd, MAKE_CTRL_ID(m_cItems - 1, m_cSubItems));
  510. SetWindowPos(hwndNew,
  511. hwndPrev,
  512. 0, 0, 0, 0,
  513. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  514. }
  515. // Create new checkboxes
  516. dwCheckStyle = WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP | BS_NOTIFY | BS_FLAT | BS_AUTOCHECKBOX;
  517. for (j = 0; j < m_cSubItems; j++)
  518. {
  519. hwndPrev = hwndNew;
  520. hwndNew = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
  521. TEXT("BUTTON"),
  522. NULL,
  523. dwCheckStyle,
  524. m_nCheckPos[j],
  525. m_nNewItemYPos - si.nPos,
  526. m_cxCheckBox,
  527. m_rcItemLabel.bottom - m_rcItemLabel.top,
  528. hWnd,
  529. (HMENU)IntToPtr(MAKE_CTRL_ID(m_cItems, j + 1)),
  530. hModule,
  531. NULL);
  532. if (!hwndNew)
  533. {
  534. while (j >= 0)
  535. {
  536. DestroyWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(m_cItems, j)));
  537. j--;
  538. }
  539. TraceLeaveValue(-1);
  540. }
  541. SetWindowPos(hwndNew,
  542. hwndPrev,
  543. 0, 0, 0, 0,
  544. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  545. //
  546. // Default "enabled" to TRUE
  547. //
  548. SetWindowLongPtr(hwndNew, GWLP_USERDATA, (LPARAM) TRUE);
  549. // Only want this style on the first checkbox
  550. dwCheckStyle &= ~WS_GROUP;
  551. }
  552. // We now officially have a new item
  553. m_cItems++;
  554. // calculate Y pos for next item to be inserted
  555. m_nNewItemYPos += nLabelHeight + m_nDefaultVerticalSpace;
  556. //
  557. // The last thing is to set the scroll range
  558. //
  559. GetClientRect(hWnd, &rc);
  560. si.cbSize = sizeof(si);
  561. si.fMask = SIF_PAGE | SIF_RANGE;
  562. si.nMin = 0;
  563. si.nMax = m_nNewItemYPos - 1;
  564. si.nPage = rc.bottom;
  565. SetScrollInfo(hWnd, SB_VERT, &si, FALSE);
  566. TraceLeaveValue(m_cItems - 1); // return the index of the new item
  567. }
  568. void
  569. CCheckList::SetState(HWND hWnd, WORD iItem, WORD iSubItem, LONG lState)
  570. {
  571. HWND hwndCtrl;
  572. TraceEnter(TRACE_CHECKLIST, "CCheckList::SetState");
  573. TraceAssert(hWnd != NULL);
  574. TraceAssert(iItem < m_cItems);
  575. TraceAssert(0 < iSubItem && iSubItem <= m_cSubItems);
  576. if (iSubItem > 0)
  577. {
  578. hwndCtrl = GetDlgItem(hWnd, MAKE_CTRL_ID(iItem, iSubItem));
  579. if (hwndCtrl != NULL)
  580. {
  581. SetWindowLongPtr(hwndCtrl, GWLP_USERDATA, (LPARAM) !(lState & CLST_DISABLED));
  582. SendMessage(hwndCtrl, BM_SETCHECK, lState & CLST_CHECKED, 0);
  583. EnableWindow(hwndCtrl, !(lState & CLST_DISABLED));
  584. }
  585. }
  586. TraceLeaveVoid();
  587. }
  588. LONG
  589. CCheckList::GetState(HWND hWnd, WORD iItem, WORD iSubItem)
  590. {
  591. LONG lState = 0;
  592. TraceEnter(TRACE_CHECKLIST, "CCheckList::GetState");
  593. TraceAssert(hWnd != NULL);
  594. TraceAssert(iItem < m_cItems);
  595. TraceAssert(0 < iSubItem && iSubItem <= m_cSubItems);
  596. HWND hwndCtrl = GetDlgItem(hWnd, MAKE_CTRL_ID(iItem, iSubItem));
  597. if (hwndCtrl != NULL)
  598. {
  599. lState = (LONG)SendMessage(hwndCtrl, BM_GETCHECK, 0, 0);
  600. TraceAssert(!(lState & BST_INDETERMINATE));
  601. if (!IsWindowEnabled(hwndCtrl))
  602. lState |= CLST_DISABLED;
  603. }
  604. TraceLeaveValue(lState);
  605. }
  606. void
  607. CCheckList::SetColumnWidth(HWND hWnd, LONG cxDialog, LONG cxColumn)
  608. {
  609. LONG j;
  610. LPUSERDATA_STRUCT_LABEL pUserData;
  611. LONG nLabelHeight;
  612. TraceEnter(TRACE_CHECKLIST, "CCheckList::SetColumnWidth");
  613. TraceAssert(hWnd != NULL);
  614. TraceAssert(cxColumn > 10);
  615. m_cxCheckColumn = cxColumn;
  616. if (m_cSubItems > 0)
  617. {
  618. m_nCheckPos[m_cSubItems-1] = cxDialog // dlg width
  619. - m_rcItemLabel.left // right margin
  620. - (cxColumn + m_cxCheckBox)/2; // 1/2 col & 1/2 checkbox
  621. for (j = m_cSubItems - 1; j > 0; j--)
  622. m_nCheckPos[j-1] = m_nCheckPos[j] - cxColumn;
  623. // (leftmost check pos) - (horz margin)
  624. m_rcItemLabel.right = m_nCheckPos[0] - m_rcItemLabel.left;
  625. }
  626. else
  627. m_rcItemLabel.right = cxDialog - m_rcItemLabel.left;
  628. LONG nTop = m_rcItemLabel.top;
  629. LONG nBottom = m_rcItemLabel.bottom;
  630. for (LONG i = 0; i < m_cItems; i++)
  631. {
  632. pUserData = (LPUSERDATA_STRUCT_LABEL)
  633. GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)i)),
  634. GWLP_USERDATA);
  635. if (pUserData != NULL)
  636. {
  637. nLabelHeight = pUserData->nLabelHeight;
  638. }
  639. else
  640. {
  641. nLabelHeight = nBottom - nTop;
  642. }
  643. MoveWindow(GetDlgItem(hWnd, MAKE_LABEL_ID(i)),
  644. m_rcItemLabel.left,
  645. nTop,
  646. m_rcItemLabel.right - m_rcItemLabel.left,
  647. nLabelHeight,
  648. FALSE);
  649. for (j = 0; j < m_cSubItems; j++)
  650. {
  651. MoveWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(i, j + 1)),
  652. m_nCheckPos[j],
  653. nTop,
  654. m_cxCheckBox,
  655. nBottom - nTop,
  656. FALSE);
  657. }
  658. nTop += nLabelHeight + m_nDefaultVerticalSpace;
  659. nBottom += nLabelHeight + m_nDefaultVerticalSpace;
  660. }
  661. TraceLeaveVoid();
  662. }
  663. void
  664. CCheckList::ResetContent(HWND hWnd)
  665. {
  666. LPUSERDATA_STRUCT_LABEL pUserData;
  667. HWND hwndCurrentLabel;
  668. for (LONG i = 0; i < m_cItems; i++)
  669. {
  670. hwndCurrentLabel = GetDlgItem(hWnd, MAKE_LABEL_ID((int)i));
  671. pUserData = (LPUSERDATA_STRUCT_LABEL)
  672. GetWindowLongPtr( hwndCurrentLabel,
  673. GWLP_USERDATA);
  674. if (pUserData != NULL)
  675. {
  676. delete(pUserData);
  677. }
  678. DestroyWindow(hwndCurrentLabel);
  679. for (LONG j = 1; j <= m_cSubItems; j++)
  680. {
  681. DestroyWindow(GetDlgItem(hWnd, MAKE_CTRL_ID(i, j)));
  682. }
  683. }
  684. // Hide the scroll bar
  685. SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE);
  686. m_cItems = 0;
  687. }
  688. LONG
  689. CCheckList::GetVisibleCount(HWND hWnd)
  690. {
  691. LONG nCount = 0;
  692. RECT rc;
  693. LONG nTopIndex;
  694. LONG nAmountShown = 0;
  695. LONG nAmountObscured = 0;
  696. LPUSERDATA_STRUCT_LABEL pUserData;
  697. if (!GetClientRect(hWnd, &rc))
  698. {
  699. return 1;
  700. }
  701. nTopIndex = GetTopIndex(hWnd, &nAmountObscured);
  702. if (nTopIndex == -1)
  703. {
  704. return 1;
  705. }
  706. while ((nTopIndex < m_cItems) && (nAmountShown < rc.bottom))
  707. {
  708. pUserData = (LPUSERDATA_STRUCT_LABEL)
  709. GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nTopIndex)),
  710. GWLP_USERDATA);
  711. nAmountShown += (m_nDefaultVerticalSpace + pUserData->nLabelHeight - nAmountObscured);
  712. nAmountObscured = 0; // nAmountObscured only matters for the first iteration where
  713. // the real top index's amount shown is being calculated
  714. nCount++;
  715. nTopIndex++;
  716. }
  717. //
  718. // since that last one may be obscured see if we need to adjust nCount
  719. //
  720. if (nAmountShown > rc.bottom)
  721. {
  722. nCount--;
  723. }
  724. return max(1, nCount);
  725. }
  726. LONG
  727. CCheckList::GetTopIndex(HWND hWnd, LONG *pnAmountObscured)
  728. {
  729. LONG nIndex = 0;
  730. LPUSERDATA_STRUCT_LABEL pUserData;
  731. SCROLLINFO si;
  732. si.cbSize = sizeof(si);
  733. si.fMask = SIF_POS;
  734. //
  735. // initialize
  736. //
  737. if (pnAmountObscured != NULL)
  738. {
  739. *pnAmountObscured = 0;
  740. }
  741. if (GetScrollInfo(hWnd, SB_VERT, &si) && m_rcItemLabel.bottom > 0)
  742. {
  743. pUserData = (LPUSERDATA_STRUCT_LABEL)
  744. GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nIndex)),
  745. GWLP_USERDATA);
  746. //
  747. // if there are no items get out
  748. //
  749. if (pUserData == NULL)
  750. {
  751. return -1;
  752. }
  753. while (si.nPos >= (m_nDefaultVerticalSpace + pUserData->nLabelHeight))
  754. {
  755. si.nPos -= (m_nDefaultVerticalSpace + pUserData->nLabelHeight);
  756. nIndex++;
  757. pUserData = (LPUSERDATA_STRUCT_LABEL)
  758. GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nIndex)),
  759. GWLP_USERDATA);
  760. }
  761. if (pnAmountObscured != NULL)
  762. {
  763. *pnAmountObscured = si.nPos;
  764. }
  765. }
  766. return nIndex;
  767. }
  768. void
  769. CCheckList::SetTopIndex(HWND hWnd, LONG nIndex)
  770. {
  771. int i;
  772. int nPos = 0;
  773. LPUSERDATA_STRUCT_LABEL pUserData;
  774. for (i=0; i<nIndex; i++)
  775. {
  776. pUserData = (LPUSERDATA_STRUCT_LABEL)
  777. GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)i)),
  778. GWLP_USERDATA);
  779. nPos += (m_nDefaultVerticalSpace + pUserData->nLabelHeight);
  780. }
  781. m_cWheelDelta = 0;
  782. MsgVScroll(hWnd, SB_THUMBPOSITION, nPos);
  783. }
  784. void
  785. CCheckList::EnsureVisible(HWND hWnd, LONG nItemIndex)
  786. {
  787. LONG nAmountObscured = 0;
  788. LONG nTopIndex;
  789. RECT rc;
  790. LPUSERDATA_STRUCT_LABEL pUserData;
  791. nTopIndex = GetTopIndex(hWnd, &nAmountObscured);
  792. if (nTopIndex == -1)
  793. {
  794. return;
  795. }
  796. // Note that the top item may only be partially visible,
  797. // so we need to test for equality here. Raid #208449
  798. if (nItemIndex < nTopIndex)
  799. {
  800. SetTopIndex(hWnd, nItemIndex);
  801. }
  802. else if (nItemIndex == nTopIndex)
  803. {
  804. if (nAmountObscured != 0)
  805. {
  806. SetTopIndex(hWnd, nItemIndex);
  807. }
  808. }
  809. else
  810. {
  811. LONG nVisible = GetVisibleCount(hWnd);
  812. if (nItemIndex >= nTopIndex + nVisible)
  813. {
  814. if (!GetClientRect(hWnd, &rc))
  815. {
  816. //
  817. // This is just best effort
  818. //
  819. SetTopIndex(hWnd, nItemIndex - nVisible + 1);
  820. }
  821. else
  822. {
  823. //
  824. // Calculate what the top index should be to allow
  825. // nItemIndex to be fully visible
  826. //
  827. nTopIndex = nItemIndex + 1;
  828. do
  829. {
  830. nTopIndex--;
  831. pUserData = (LPUSERDATA_STRUCT_LABEL)
  832. GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)nTopIndex)),
  833. GWLP_USERDATA);
  834. if (pUserData != NULL)
  835. {
  836. rc.bottom -= (pUserData->nLabelHeight + m_nDefaultVerticalSpace);
  837. if (rc.bottom < 0)
  838. {
  839. nTopIndex++;
  840. }
  841. }
  842. else
  843. {
  844. //
  845. // Should not hit this, just added to make things safe
  846. //
  847. rc.bottom = 0;
  848. nTopIndex = 0;
  849. }
  850. } while (rc.bottom > 0);
  851. SetTopIndex(hWnd, nTopIndex);
  852. }
  853. }
  854. }
  855. }
  856. void
  857. CCheckList::DrawCheckFocusRect(HWND hWnd, HWND hwndCheck, BOOL fDraw)
  858. {
  859. RECT rcCheck;
  860. HDC hdc;
  861. TraceEnter(TRACE_CHECKLIST, "CCheckList::DrawCheckFocusRect");
  862. TraceAssert(hWnd != NULL);
  863. TraceAssert(hwndCheck != NULL);
  864. GetWindowRect(hwndCheck, &rcCheck);
  865. MapWindowPoints(NULL, hWnd, (LPPOINT)&rcCheck, 2);
  866. InflateRect(&rcCheck, 2, 2); // draw *outside* the checkbox
  867. hdc = GetDC(hWnd);
  868. if (hdc)
  869. {
  870. // Always erase before drawing, since we may already be
  871. // partially visible and drawing is an XOR operation.
  872. // (Don't want to leave any turds on the screen.)
  873. FrameRect(hdc, &rcCheck, GetSysColorBrush(BK_COLOR));
  874. if (fDraw)
  875. {
  876. SetTextColor(hdc, GetSysColor(TEXT_COLOR));
  877. SetBkColor(hdc, GetSysColor(BK_COLOR));
  878. DrawFocusRect(hdc, &rcCheck);
  879. }
  880. ReleaseDC(hWnd, hdc);
  881. }
  882. TraceLeaveVoid();
  883. }
  884. LRESULT
  885. CALLBACK
  886. CCheckList::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  887. {
  888. LRESULT lResult = 0;
  889. LPUSERDATA_STRUCT_LABEL pUserData = NULL;
  890. CCheckList *pThis = (CCheckList*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
  891. TraceEnter(TRACE_CHECKLIST, "CCheckList::WindowProc");
  892. TraceAssert(hWnd != NULL);
  893. switch (uMsg)
  894. {
  895. case WM_NCCREATE:
  896. pThis = new CCheckList(hWnd, (LPCREATESTRUCT)lParam);
  897. if (pThis != NULL)
  898. {
  899. SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
  900. lResult = TRUE;
  901. }
  902. break;
  903. case WM_DESTROY:
  904. pThis->ResetContent(hWnd);
  905. break;
  906. case WM_NCDESTROY:
  907. delete pThis;
  908. break;
  909. case WM_COMMAND:
  910. TraceAssert(pThis != NULL);
  911. lResult = pThis->MsgCommand(hWnd,
  912. GET_WM_COMMAND_ID(wParam, lParam),
  913. GET_WM_COMMAND_CMD(wParam, lParam),
  914. GET_WM_COMMAND_HWND(wParam, lParam));
  915. break;
  916. case WM_CTLCOLORSTATIC:
  917. TraceAssert(pThis != NULL);
  918. SetBkMode((HDC)wParam, TRANSPARENT);
  919. SetTextColor((HDC)wParam, GetSysColor(TEXT_COLOR));
  920. SetBkColor((HDC)wParam, GetSysColor(BK_COLOR));
  921. lResult = (LRESULT)GetSysColorBrush(BK_COLOR);
  922. break;
  923. case WM_PAINT:
  924. TraceAssert(pThis != NULL);
  925. pThis->MsgPaint(hWnd, (HDC)wParam);
  926. break;
  927. case WM_VSCROLL:
  928. TraceAssert(pThis != NULL);
  929. pThis->MsgVScroll(hWnd,
  930. (int)(short)GET_WM_VSCROLL_CODE(wParam, lParam),
  931. (int)(short)GET_WM_VSCROLL_POS(wParam, lParam));
  932. break;
  933. case WM_MOUSEWHEEL:
  934. TraceAssert(pThis != NULL);
  935. pThis->MsgMouseWheel(hWnd,
  936. LOWORD(wParam),
  937. (int)(short)HIWORD(wParam));
  938. break;
  939. case WM_LBUTTONDOWN:
  940. TraceAssert(pThis != NULL);
  941. pThis->MsgButtonDown(hWnd,
  942. wParam,
  943. (int)(short)LOWORD(lParam),
  944. (int)(short)HIWORD(lParam));
  945. break;
  946. case WM_ENABLE:
  947. TraceAssert(pThis != NULL);
  948. pThis->MsgEnable(hWnd, (BOOL)wParam);
  949. break;
  950. case WM_SETFONT:
  951. TraceAssert(pThis != NULL);
  952. {
  953. for (LONG i = 0; i < pThis->m_cItems; i++)
  954. SendDlgItemMessage(hWnd,
  955. MAKE_LABEL_ID(i),
  956. WM_SETFONT,
  957. wParam,
  958. lParam);
  959. }
  960. break;
  961. case WM_SIZE:
  962. TraceAssert(pThis != NULL);
  963. pThis->MsgSize(hWnd, (DWORD)wParam, LOWORD(lParam), HIWORD(lParam));
  964. break;
  965. case CLM_ADDITEM:
  966. TraceAssert(pThis != NULL);
  967. lResult = pThis->AddItem(hWnd, (LPCTSTR)wParam, lParam);
  968. break;
  969. case CLM_GETITEMCOUNT:
  970. TraceAssert(pThis != NULL);
  971. lResult = pThis->m_cItems;
  972. break;
  973. case CLM_SETSTATE:
  974. TraceAssert(pThis != NULL);
  975. pThis->SetState(hWnd, LOWORD(wParam), HIWORD(wParam), (LONG)lParam);
  976. break;
  977. case CLM_GETSTATE:
  978. TraceAssert(pThis != NULL);
  979. lResult = pThis->GetState(hWnd, LOWORD(wParam), HIWORD(wParam));
  980. break;
  981. case CLM_SETCOLUMNWIDTH:
  982. TraceAssert(pThis != NULL);
  983. {
  984. RECT rc;
  985. LONG cxDialog;
  986. GetClientRect(hWnd, &rc);
  987. cxDialog = rc.right;
  988. rc.right = (LONG)lParam;
  989. MapDialogRect(GetParent(hWnd), &rc);
  990. pThis->SetColumnWidth(hWnd, cxDialog, rc.right);
  991. }
  992. break;
  993. case CLM_SETITEMDATA:
  994. TraceAssert(GET_ITEM(wParam) < (ULONG)pThis->m_cItems);
  995. pUserData = (LPUSERDATA_STRUCT_LABEL)
  996. GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)wParam)),
  997. GWLP_USERDATA);
  998. if (pUserData != NULL)
  999. pUserData->lParam = lParam;
  1000. break;
  1001. case CLM_GETITEMDATA:
  1002. TraceAssert(GET_ITEM(wParam) < (ULONG)pThis->m_cItems);
  1003. pUserData = (LPUSERDATA_STRUCT_LABEL)
  1004. GetWindowLongPtr( GetDlgItem(hWnd, MAKE_LABEL_ID((int)wParam)),
  1005. GWLP_USERDATA);
  1006. if (pUserData != NULL)
  1007. lResult = pUserData->lParam;
  1008. break;
  1009. case CLM_RESETCONTENT:
  1010. TraceAssert(pThis != NULL);
  1011. pThis->ResetContent(hWnd);
  1012. break;
  1013. case CLM_GETVISIBLECOUNT:
  1014. TraceAssert(pThis != NULL);
  1015. lResult = pThis->GetVisibleCount(hWnd);
  1016. break;
  1017. case CLM_GETTOPINDEX:
  1018. TraceAssert(pThis != NULL);
  1019. lResult = pThis->GetTopIndex(hWnd);
  1020. break;
  1021. case CLM_SETTOPINDEX:
  1022. TraceAssert(pThis != NULL);
  1023. pThis->SetTopIndex(hWnd, (LONG)wParam);
  1024. break;
  1025. case CLM_ENSUREVISIBLE:
  1026. TraceAssert(pThis != NULL);
  1027. pThis->EnsureVisible(hWnd, (LONG)wParam);
  1028. break;
  1029. //
  1030. // Always refer to the chklist window for help. Don't pass
  1031. // one of the child window handles here.
  1032. //
  1033. case WM_HELP:
  1034. ((LPHELPINFO)lParam)->hItemHandle = hWnd;
  1035. lResult = SendMessage(GetParent(hWnd), uMsg, wParam, lParam);
  1036. break;
  1037. case WM_CONTEXTMENU:
  1038. lResult = SendMessage(GetParent(hWnd), uMsg, (WPARAM)hWnd, lParam);
  1039. break;
  1040. case WM_SETCURSOR:
  1041. if (LOWORD(lParam) == HTCLIENT)
  1042. {
  1043. SetCursor(LoadCursor(NULL, IDC_ARROW));
  1044. lResult = TRUE;
  1045. break;
  1046. }
  1047. // Fall Through
  1048. default:
  1049. lResult = DefWindowProc(hWnd, uMsg, wParam, lParam);
  1050. }
  1051. TraceLeaveValue(lResult);
  1052. }