Source code of Windows XP (NT5)
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.

2309 lines
56 KiB

  1. #include "ctlspriv.h"
  2. #pragma hdrstop
  3. #include "usrctl32.h"
  4. #include "listbox.h"
  5. //---------------------------------------------------------------------------//
  6. //
  7. // Forwards
  8. //
  9. VOID ListBox_CalcItemRowsAndColumns(PLBIV);
  10. LONG ListBox_Create(PLBIV, HWND, LPCREATESTRUCT);
  11. VOID ListBox_Destroy(PLBIV, HWND);
  12. VOID ListBox_SetFont(PLBIV, HANDLE, BOOL);
  13. VOID ListBox_Size(PLBIV, INT, INT, BOOL);
  14. BOOL ListBox_SetTabStopsHandler(PLBIV, INT, LPINT);
  15. VOID ListBox_DropObjectHandler(PLBIV, PDROPSTRUCT);
  16. int ListBox_GetSetItemHeightHandler(PLBIV, UINT, int, UINT);
  17. //---------------------------------------------------------------------------//
  18. //
  19. // InitListBoxClass() - Registers the control's window class
  20. //
  21. BOOL InitListBoxClass(HINSTANCE hinst)
  22. {
  23. WNDCLASS wc;
  24. wc.lpfnWndProc = ListBox_WndProc;
  25. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  26. wc.hIcon = NULL;
  27. wc.lpszMenuName = NULL;
  28. wc.hInstance = hinst;
  29. wc.lpszClassName = WC_LISTBOX;
  30. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // NULL;
  31. wc.style = CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS;
  32. wc.cbWndExtra = sizeof(PLBIV);
  33. wc.cbClsExtra = 0;
  34. if (!RegisterClass(&wc) && !GetClassInfo(hinst, WC_LISTBOX, &wc))
  35. return FALSE;
  36. return TRUE;
  37. }
  38. //---------------------------------------------------------------------------//
  39. //
  40. // ListBox_WndProc
  41. //
  42. // Window Procedure for ListBox AND ComboLBox controls.
  43. //
  44. LRESULT APIENTRY ListBox_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  45. {
  46. PLBIV plb;
  47. UINT wFlags;
  48. LRESULT lReturn = FALSE;
  49. //
  50. // Get the instance data for this listbox control
  51. //
  52. plb = ListBox_GetPtr(hwnd);
  53. if (!plb && uMsg != WM_NCCREATE)
  54. {
  55. goto CallDWP;
  56. }
  57. switch (uMsg)
  58. {
  59. case LB_GETTOPINDEX:
  60. //
  61. // Return index of top item displayed.
  62. //
  63. return plb->iTop;
  64. case LB_SETTOPINDEX:
  65. if (wParam && ((INT)wParam < 0 || (INT)wParam >= plb->cMac))
  66. {
  67. TraceMsg(TF_STANDARD, "Invalid index");
  68. return LB_ERR;
  69. }
  70. if (plb->cMac)
  71. {
  72. ListBox_NewITop(plb, (INT)wParam);
  73. }
  74. break;
  75. case WM_STYLECHANGED:
  76. plb->fRtoLReading = ((GET_EXSTYLE(plb) & WS_EX_RTLREADING) != 0);
  77. plb->fRightAlign = ((GET_EXSTYLE(plb) & WS_EX_RIGHT) != 0);
  78. ListBox_CheckRedraw(plb, FALSE, 0);
  79. break;
  80. case WM_WINDOWPOSCHANGED:
  81. //
  82. // If we are in the middle of creation, ignore this
  83. // message because it will generate a WM_SIZE message.
  84. // See ListBox_Create().
  85. //
  86. if (!plb->fIgnoreSizeMsg)
  87. {
  88. goto CallDWP;
  89. }
  90. break;
  91. case WM_SIZE:
  92. //
  93. // If we are in the middle of creation, ignore size
  94. // messages. See ListBox_Create().
  95. //
  96. if (!plb->fIgnoreSizeMsg)
  97. {
  98. ListBox_Size(plb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), FALSE);
  99. }
  100. break;
  101. case WM_ERASEBKGND:
  102. {
  103. HDC hdcSave = plb->hdc;
  104. HBRUSH hbr;
  105. plb->hdc = (HDC)wParam;
  106. hbr = ListBox_GetBrush(plb, NULL);
  107. if (hbr)
  108. {
  109. RECT rcClient;
  110. GetClientRect(hwnd, &rcClient);
  111. FillRect(plb->hdc, &rcClient, hbr);
  112. lReturn = TRUE;
  113. }
  114. plb->hdc = hdcSave;
  115. break;
  116. }
  117. case LB_RESETCONTENT:
  118. ListBox_ResetContentHandler(plb);
  119. break;
  120. case WM_TIMER:
  121. if (wParam == IDSYS_LBSEARCH)
  122. {
  123. plb->iTypeSearch = 0;
  124. KillTimer(hwnd, IDSYS_LBSEARCH);
  125. ListBox_InvertItem(plb, plb->iSel, TRUE);
  126. break;
  127. }
  128. uMsg = WM_MOUSEMOVE;
  129. ListBox_TrackMouse(plb, uMsg, plb->ptPrev);
  130. break;
  131. case WM_LBUTTONUP:
  132. //
  133. // 295135: if the combobox dropdown button is pressed and the listbox
  134. // covers the combobox, the ensuing buttonup message gets sent to
  135. // list instead of the combobox, which causes the dropdown to be
  136. // closed immediately.
  137. //
  138. //
  139. // send this to the combo if it hasn't processed buttonup yet after
  140. // dropping the list.
  141. //
  142. if (plb->pcbox && plb->pcbox->hwnd && plb->pcbox->fButtonPressed)
  143. {
  144. return SendMessage(plb->pcbox->hwnd, uMsg, wParam, lParam);
  145. }
  146. // fall through
  147. case WM_MOUSEMOVE:
  148. case WM_LBUTTONDOWN:
  149. case WM_LBUTTONDBLCLK:
  150. {
  151. POINT pt;
  152. POINTSTOPOINT(pt, lParam);
  153. ListBox_TrackMouse(plb, uMsg, pt);
  154. break;
  155. }
  156. case WM_MBUTTONDOWN:
  157. EnterReaderMode(hwnd);
  158. break;
  159. case WM_CAPTURECHANGED:
  160. //
  161. // Note that this message should be handled only on unexpected
  162. // capture changes currently.
  163. //
  164. ASSERT(TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT));
  165. if (plb->fCaptured)
  166. {
  167. ListBox_ButtonUp(plb, LBUP_NOTIFY);
  168. }
  169. break;
  170. case LBCB_STARTTRACK:
  171. //
  172. // Start tracking mouse moves in the listbox, setting capture
  173. //
  174. if (!plb->pcbox)
  175. {
  176. break;
  177. }
  178. plb->fCaptured = FALSE;
  179. if (wParam)
  180. {
  181. POINT pt;
  182. POINTSTOPOINT(pt, lParam);
  183. ScreenToClient(hwnd, &pt);
  184. ListBox_TrackMouse(plb, WM_LBUTTONDOWN, pt);
  185. }
  186. else
  187. {
  188. SetCapture(hwnd);
  189. plb->fCaptured = TRUE;
  190. plb->iLastSelection = plb->iSel;
  191. }
  192. break;
  193. case LBCB_ENDTRACK:
  194. //
  195. // Kill capture, tracking, etc.
  196. //
  197. if ( plb->fCaptured || (GetCapture() == plb->hwndParent) )
  198. {
  199. ListBox_ButtonUp(plb, LBUP_RELEASECAPTURE | (wParam ? LBUP_SELCHANGE :
  200. LBUP_RESETSELECTION));
  201. }
  202. break;
  203. case WM_PRINTCLIENT:
  204. ListBox_Paint(plb, (HDC)wParam, NULL);
  205. break;
  206. case WM_NCPAINT:
  207. if (plb->hTheme && (GET_EXSTYLE(plb) & WS_EX_CLIENTEDGE))
  208. {
  209. HRGN hrgn = (wParam != 1) ? (HRGN)wParam : NULL;
  210. HBRUSH hbr = (HBRUSH)GetClassLongPtr(hwnd, GCLP_HBRBACKGROUND);
  211. if (CCDrawNonClientTheme(plb->hTheme, hwnd, hrgn, hbr, 0, 0))
  212. {
  213. break;
  214. }
  215. }
  216. goto CallDWP;
  217. case WM_PAINT:
  218. {
  219. PAINTSTRUCT ps;
  220. HDC hdc;
  221. LPRECT lprc;
  222. if (wParam)
  223. {
  224. hdc = (HDC) wParam;
  225. lprc = NULL;
  226. }
  227. else
  228. {
  229. hdc = BeginPaint(hwnd, &ps);
  230. lprc = &(ps.rcPaint);
  231. }
  232. if (IsLBoxVisible(plb))
  233. {
  234. ListBox_Paint(plb, hdc, lprc);
  235. }
  236. if (!wParam)
  237. {
  238. EndPaint(hwnd, &ps);
  239. }
  240. break;
  241. }
  242. case WM_NCDESTROY:
  243. case WM_FINALDESTROY:
  244. ListBox_Destroy(plb, hwnd);
  245. break;
  246. case WM_SETFOCUS:
  247. CaretCreate(plb);
  248. ListBox_SetCaret(plb, TRUE);
  249. ListBox_NotifyOwner(plb, LBN_SETFOCUS);
  250. ListBox_Event(plb, EVENT_OBJECT_FOCUS, plb->iSelBase);
  251. break;
  252. case WM_KILLFOCUS:
  253. //
  254. // Reset the wheel delta count.
  255. //
  256. gcWheelDelta = 0;
  257. ListBox_SetCaret(plb, FALSE);
  258. ListBox_CaretDestroy(plb);
  259. ListBox_NotifyOwner(plb, LBN_KILLFOCUS);
  260. if (plb->iTypeSearch)
  261. {
  262. plb->iTypeSearch = 0;
  263. KillTimer(hwnd, IDSYS_LBSEARCH);
  264. }
  265. if (plb->pszTypeSearch)
  266. {
  267. ControlFree(GetProcessHeap(), plb->pszTypeSearch);
  268. plb->pszTypeSearch = NULL;
  269. }
  270. break;
  271. case WM_MOUSEWHEEL:
  272. {
  273. int cDetants;
  274. int cPage;
  275. int cLines;
  276. RECT rc;
  277. int windowWidth;
  278. int cPos;
  279. UINT ucWheelScrollLines;
  280. //
  281. // Don't handle zoom and datazoom.
  282. //
  283. if (wParam & (MK_SHIFT | MK_CONTROL))
  284. {
  285. goto CallDWP;
  286. }
  287. lReturn = 1;
  288. gcWheelDelta -= (short) HIWORD(wParam);
  289. cDetants = gcWheelDelta / WHEEL_DELTA;
  290. SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &ucWheelScrollLines, 0);
  291. if ( cDetants != 0 &&
  292. ucWheelScrollLines > 0 &&
  293. (GET_STYLE(plb) & (WS_VSCROLL | WS_HSCROLL)))
  294. {
  295. gcWheelDelta = gcWheelDelta % WHEEL_DELTA;
  296. if (GET_STYLE(plb) & WS_VSCROLL)
  297. {
  298. cPage = max(1, (plb->cItemFullMax - 1));
  299. cLines = cDetants *
  300. (int) min((UINT) cPage, ucWheelScrollLines);
  301. cPos = max(0, min(plb->iTop + cLines, plb->cMac - 1));
  302. if (cPos != plb->iTop)
  303. {
  304. ListBox_VScroll(plb, SB_THUMBPOSITION, cPos);
  305. ListBox_VScroll(plb, SB_ENDSCROLL, 0);
  306. }
  307. }
  308. else if (plb->fMultiColumn)
  309. {
  310. cPage = max(1, plb->numberOfColumns);
  311. cLines = cDetants * (int) min((UINT) cPage, ucWheelScrollLines);
  312. cPos = max(
  313. 0,
  314. min((plb->iTop / plb->itemsPerColumn) + cLines,
  315. plb->cMac - 1 - ((plb->cMac - 1) % plb->itemsPerColumn)));
  316. if (cPos != plb->iTop)
  317. {
  318. ListBox_HSrollMultiColumn(plb, SB_THUMBPOSITION, cPos);
  319. ListBox_HSrollMultiColumn(plb, SB_ENDSCROLL, 0);
  320. }
  321. }
  322. else
  323. {
  324. GetClientRect(plb->hwnd, &rc);
  325. windowWidth = rc.right;
  326. cPage = max(plb->cxChar, (windowWidth / 3) * 2) /
  327. plb->cxChar;
  328. cLines = cDetants *
  329. (int) min((UINT) cPage, ucWheelScrollLines);
  330. cPos = max(
  331. 0,
  332. min(plb->xOrigin + (cLines * plb->cxChar),
  333. plb->maxWidth));
  334. if (cPos != plb->xOrigin) {
  335. ListBox_HScroll(plb, SB_THUMBPOSITION, cPos);
  336. ListBox_HScroll(plb, SB_ENDSCROLL, 0);
  337. }
  338. }
  339. }
  340. break;
  341. }
  342. case WM_VSCROLL:
  343. ListBox_VScroll(plb, LOWORD(wParam), HIWORD(wParam));
  344. break;
  345. case WM_HSCROLL:
  346. ListBox_HScroll(plb, LOWORD(wParam), HIWORD(wParam));
  347. break;
  348. case WM_GETDLGCODE:
  349. return DLGC_WANTARROWS | DLGC_WANTCHARS;
  350. case WM_CREATE:
  351. return ListBox_Create(plb, hwnd, (LPCREATESTRUCT)lParam);
  352. case WM_SETREDRAW:
  353. //
  354. // If wParam is nonzero, the redraw flag is set
  355. // If wParam is zero, the flag is cleared
  356. //
  357. ListBox_SetRedraw(plb, (wParam != 0));
  358. break;
  359. case WM_ENABLE:
  360. ListBox_InvalidateRect(plb, NULL, !plb->OwnerDraw);
  361. break;
  362. case WM_SETFONT:
  363. ListBox_SetFont(plb, (HANDLE)wParam, LOWORD(lParam));
  364. break;
  365. case WM_GETFONT:
  366. return (LRESULT)plb->hFont;
  367. case WM_DRAGSELECT:
  368. case WM_DRAGLOOP:
  369. case WM_DRAGMOVE:
  370. case WM_DROPFILES:
  371. return SendMessage(plb->hwndParent, uMsg, wParam, lParam);
  372. case WM_QUERYDROPOBJECT:
  373. case WM_DROPOBJECT:
  374. //
  375. // fix up control data, then pass message to parent
  376. //
  377. ListBox_DropObjectHandler(plb, (PDROPSTRUCT)lParam);
  378. return SendMessage(plb->hwndParent, uMsg, wParam, lParam);
  379. case LB_GETITEMRECT:
  380. return ListBox_GetItemRectHandler(plb, (INT)wParam, (LPRECT)lParam);
  381. case LB_GETITEMDATA:
  382. //
  383. // wParam = item index
  384. //
  385. return ListBox_GetItemDataHandler(plb, (INT)wParam);
  386. case LB_SETITEMDATA:
  387. //
  388. // wParam is item index
  389. //
  390. return ListBox_SetItemDataHandler(plb, (INT)wParam, lParam);
  391. case LB_ADDSTRINGUPPER:
  392. wFlags = UPPERCASE | LBI_ADD;
  393. goto CallInsertItem;
  394. case LB_ADDSTRINGLOWER:
  395. wFlags = LOWERCASE | LBI_ADD;
  396. goto CallInsertItem;
  397. case LB_ADDSTRING:
  398. wFlags = LBI_ADD;
  399. goto CallInsertItem;
  400. case LB_INSERTSTRINGUPPER:
  401. wFlags = UPPERCASE;
  402. goto CallInsertItem;
  403. case LB_INSERTSTRINGLOWER:
  404. wFlags = LOWERCASE;
  405. goto CallInsertItem;
  406. case LB_INSERTSTRING:
  407. wFlags = 0;
  408. CallInsertItem:
  409. // Validate the lParam. If the listbox does not have HASSTRINGS,
  410. // the lParam is a data value. Otherwise, it is a string
  411. // pointer, fail if NULL.
  412. if ( !TESTFLAG(GET_STYLE(plb), LBS_HASSTRINGS) || lParam )
  413. {
  414. lReturn = (LRESULT)ListBox_InsertItem(plb, (LPWSTR) lParam, (int) wParam, wFlags);
  415. if (!plb->fNoIntegralHeight)
  416. {
  417. ListBox_Size(plb, 0, 0, TRUE);
  418. }
  419. }
  420. else
  421. {
  422. lReturn = LB_ERR;
  423. }
  424. break;
  425. case LB_INITSTORAGE:
  426. return ListBox_InitStorage(plb, FALSE, (INT)wParam, (INT)lParam);
  427. case LB_DELETESTRING:
  428. return ListBox_DeleteStringHandler(plb, (INT)wParam);
  429. case LB_DIR:
  430. //
  431. // wParam - Dos attribute value.
  432. // lParam - Points to a file specification string
  433. //
  434. lReturn = ListBox_DirHandler(plb, (INT)wParam, (LPWSTR)lParam);
  435. break;
  436. case LB_ADDFILE:
  437. lReturn = ListBox_InsertFile(plb, (LPWSTR)lParam);
  438. break;
  439. case LB_SETSEL:
  440. return ListBox_SetSelHandler(plb, (wParam != 0), (INT)lParam);
  441. case LB_SETCURSEL:
  442. //
  443. // If window obscured, update so invert will work correctly
  444. //
  445. return ListBox_SetCurSelHandler(plb, (INT)wParam);
  446. case LB_GETSEL:
  447. if (wParam >= (UINT)plb->cMac)
  448. {
  449. return (LRESULT)LB_ERR;
  450. }
  451. return ListBox_IsSelected(plb, (INT)wParam, SELONLY);
  452. case LB_GETCURSEL:
  453. if (plb->wMultiple == SINGLESEL)
  454. {
  455. return plb->iSel;
  456. }
  457. return plb->iSelBase;
  458. case LB_SELITEMRANGE:
  459. if (plb->wMultiple == SINGLESEL)
  460. {
  461. //
  462. // Can't select a range if only single selections are enabled
  463. //
  464. TraceMsg(TF_STANDARD, "Invalid index passed to LB_SELITEMRANGE");
  465. return LB_ERR;
  466. }
  467. ListBox_SetRange(plb, LOWORD(lParam), HIWORD(lParam), (wParam != 0));
  468. break;
  469. case LB_SELITEMRANGEEX:
  470. if (plb->wMultiple == SINGLESEL)
  471. {
  472. //
  473. // Can't select a range if only single selections are enabled
  474. //
  475. TraceMsg(TF_STANDARD, "LB_SELITEMRANGEEX:Can't select a range if only single selections are enabled");
  476. return LB_ERR;
  477. }
  478. else
  479. {
  480. BOOL fHighlight = ((DWORD)lParam > (DWORD)wParam);
  481. if (fHighlight == FALSE)
  482. {
  483. ULONG_PTR temp = lParam;
  484. lParam = wParam;
  485. wParam = temp;
  486. }
  487. ListBox_SetRange(plb, (INT)wParam, (INT)lParam, fHighlight);
  488. }
  489. break;
  490. case LB_GETTEXTLEN:
  491. if (lParam != 0)
  492. {
  493. TraceMsg(TF_WARNING, "LB_GETTEXTLEN with lParam = %lx\n", lParam);
  494. }
  495. lReturn = ListBox_GetTextHandler(plb, TRUE, FALSE, (INT)wParam, NULL);
  496. break;
  497. case LB_GETTEXT:
  498. lReturn = ListBox_GetTextHandler(plb, FALSE, FALSE, (INT)wParam, (LPWSTR)lParam);
  499. break;
  500. case LB_GETCOUNT:
  501. return (LRESULT)plb->cMac;
  502. case LB_SETCOUNT:
  503. return ListBox_SetCount(plb, (INT)wParam);
  504. case LB_SELECTSTRING:
  505. case LB_FINDSTRING:
  506. {
  507. int iSel = Listbox_FindStringHandler(plb, (LPWSTR)lParam, (INT)wParam, PREFIX, TRUE);
  508. if (uMsg == LB_FINDSTRING || iSel == LB_ERR)
  509. {
  510. lReturn = iSel;
  511. }
  512. else
  513. {
  514. lReturn = ListBox_SetCurSelHandler(plb, iSel);
  515. }
  516. break;
  517. }
  518. case LB_GETLOCALE:
  519. return plb->dwLocaleId;
  520. case LB_SETLOCALE:
  521. {
  522. DWORD dwRet;
  523. //
  524. // Validate locale
  525. //
  526. wParam = ConvertDefaultLocale((LCID)wParam);
  527. if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
  528. {
  529. return LB_ERR;
  530. }
  531. dwRet = plb->dwLocaleId;
  532. plb->dwLocaleId = (DWORD)wParam;
  533. return dwRet;
  534. }
  535. case LB_GETLISTBOXINFO:
  536. //
  537. // wParam - not used
  538. // lParam - not used
  539. //
  540. if (plb->fMultiColumn)
  541. {
  542. lReturn = (LRESULT)plb->itemsPerColumn;
  543. }
  544. else
  545. {
  546. lReturn = (LRESULT)plb->cMac;
  547. }
  548. break;
  549. case CB_GETCOMBOBOXINFO:
  550. //
  551. // wParam - not used
  552. // lParam - pointer to COMBOBOXINFO struct
  553. //
  554. if (plb->pcbox && plb->pcbox->hwnd && IsWindow(plb->pcbox->hwnd))
  555. {
  556. lReturn = SendMessage(plb->pcbox->hwnd, uMsg, wParam, lParam);
  557. }
  558. break;
  559. case CB_SETMINVISIBLE:
  560. if (!plb->fNoIntegralHeight)
  561. {
  562. ListBox_Size(plb, 0, 0, TRUE);
  563. }
  564. break;
  565. case WM_KEYDOWN:
  566. //
  567. // IanJa: Use LOWORD() to get low 16-bits of wParam - this should
  568. // work for Win16 & Win32. The value obtained is the virtual key
  569. //
  570. ListBox_KeyInput(plb, uMsg, LOWORD(wParam));
  571. break;
  572. case WM_CHAR:
  573. ListBox_CharHandler(plb, LOWORD(wParam), FALSE);
  574. break;
  575. case LB_GETSELITEMS:
  576. case LB_GETSELCOUNT:
  577. //
  578. // IanJa/Win32 should this be LPWORD now?
  579. //
  580. return ListBox_GetSelItemsHandler(plb, (uMsg == LB_GETSELCOUNT), (INT)wParam, (LPINT)lParam);
  581. case LB_SETTABSTOPS:
  582. //
  583. // IanJa/Win32: Tabs given by array of INT for backwards compatability
  584. //
  585. return ListBox_SetTabStopsHandler(plb, (INT)wParam, (LPINT)lParam);
  586. case LB_GETHORIZONTALEXTENT:
  587. //
  588. // Return the max width of the listbox used for horizontal scrolling
  589. //
  590. return plb->maxWidth;
  591. case LB_SETHORIZONTALEXTENT:
  592. //
  593. // Set the max width of the listbox used for horizontal scrolling
  594. //
  595. if (plb->maxWidth != (INT)wParam)
  596. {
  597. plb->maxWidth = (INT)wParam;
  598. //
  599. // When horizontal extent is set, Show/hide the scroll bars.
  600. // NOTE: ListBox_ShowHideScrollBars() takes care if Redraw is OFF.
  601. // Fix for Bug #2477 -- 01/14/91 -- SANKAR --
  602. //
  603. //
  604. // Try to show or hide scroll bars
  605. //
  606. ListBox_ShowHideScrollBars(plb);
  607. if (plb->fHorzBar && plb->fRightAlign && !(plb->fMultiColumn || plb->OwnerDraw))
  608. {
  609. //
  610. // origin to right
  611. //
  612. ListBox_HScroll(plb, SB_BOTTOM, 0);
  613. }
  614. }
  615. break;
  616. case LB_SETCOLUMNWIDTH:
  617. //
  618. // Set the width of a column in a multicolumn listbox
  619. //
  620. plb->cxColumn = (INT)wParam;
  621. ListBox_CalcItemRowsAndColumns(plb);
  622. if (IsLBoxVisible(plb))
  623. {
  624. InvalidateRect(hwnd, NULL, TRUE);
  625. }
  626. ListBox_ShowHideScrollBars(plb);
  627. break;
  628. case LB_SETANCHORINDEX:
  629. if ((INT)wParam >= plb->cMac)
  630. {
  631. TraceMsg(TF_ERROR, "Invalid index passed to LB_SETANCHORINDEX");
  632. return LB_ERR;
  633. }
  634. plb->iMouseDown = (INT)wParam;
  635. plb->iLastMouseMove = (INT)wParam;
  636. ListBox_InsureVisible(plb, (int) wParam, (BOOL)(lParam != 0));
  637. break;
  638. case LB_GETANCHORINDEX:
  639. return plb->iMouseDown;
  640. case LB_SETCARETINDEX:
  641. if ( (plb->iSel == -1) || ((plb->wMultiple != SINGLESEL) &&
  642. (plb->cMac > (INT)wParam)))
  643. {
  644. //
  645. // Set's the iSelBase to the wParam
  646. // if lParam, then don't scroll if partially visible
  647. // else scroll into view if not fully visible
  648. //
  649. ListBox_InsureVisible(plb, (INT)wParam, (BOOL)LOWORD(lParam));
  650. ListBox_SetISelBase(plb, (INT)wParam);
  651. break;
  652. }
  653. else
  654. {
  655. if ((INT)wParam >= plb->cMac)
  656. {
  657. TraceMsg(TF_ERROR, "Invalid index passed to LB_SETCARETINDEX");
  658. }
  659. return LB_ERR;
  660. }
  661. break;
  662. case LB_GETCARETINDEX:
  663. return plb->iSelBase;
  664. case LB_SETITEMHEIGHT:
  665. case LB_GETITEMHEIGHT:
  666. return ListBox_GetSetItemHeightHandler(plb, uMsg, (INT)wParam, LOWORD(lParam));
  667. case LB_FINDSTRINGEXACT:
  668. return Listbox_FindStringHandler(plb, (LPWSTR)lParam, (INT)wParam, EQ, TRUE);
  669. case LB_ITEMFROMPOINT:
  670. {
  671. POINT pt;
  672. BOOL bOutside;
  673. DWORD dwItem;
  674. POINTSTOPOINT(pt, lParam);
  675. bOutside = ListBox_ISelFromPt(plb, pt, &dwItem);
  676. ASSERT(bOutside == 1 || bOutside == 0);
  677. return (LRESULT)MAKELONG(dwItem, bOutside);
  678. }
  679. case LBCB_CARETON:
  680. //
  681. // Internal message for combo box support
  682. //
  683. CaretCreate(plb);
  684. //
  685. // Set up the caret in the proper location for drop downs.
  686. //
  687. plb->iSelBase = plb->iSel;
  688. ListBox_SetCaret(plb, TRUE);
  689. if (IsWindowVisible(hwnd) || (GetFocus() == hwnd))
  690. {
  691. ListBox_Event(plb, EVENT_OBJECT_FOCUS, plb->iSelBase);
  692. }
  693. return plb->iSel;
  694. case LBCB_CARETOFF:
  695. //
  696. // Internal message for combo box support
  697. //
  698. ListBox_SetCaret(plb, FALSE);
  699. ListBox_CaretDestroy(plb);
  700. break;
  701. case WM_NCCREATE:
  702. //
  703. // Allocate the listbox instance stucture
  704. //
  705. plb = (PLBIV)UserLocalAlloc(HEAP_ZERO_MEMORY, sizeof(LBIV));
  706. if(plb)
  707. {
  708. ULONG ulStyle;
  709. //
  710. // Success... store the instance pointer.
  711. //
  712. TraceMsg(TF_STANDARD, "LISTBOX: Setting listbox instance pointer.");
  713. ListBox_SetPtr(hwnd, plb);
  714. plb->hwnd = hwnd;
  715. plb->pww = (PWW)GetWindowLongPtr(hwnd, GWLP_WOWWORDS);
  716. ulStyle = GET_STYLE(plb);
  717. if ( (ulStyle & LBS_MULTICOLUMN) &&
  718. (ulStyle & WS_VSCROLL))
  719. {
  720. DWORD dwMask = WS_VSCROLL;
  721. DWORD dwFlags = 0;
  722. if (!TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT))
  723. {
  724. dwMask |= WS_HSCROLL;
  725. dwFlags = WS_HSCROLL;
  726. }
  727. AlterWindowStyle(hwnd, dwMask, dwFlags);
  728. }
  729. goto CallDWP;
  730. }
  731. else
  732. {
  733. //
  734. // Failed... return FALSE.
  735. //
  736. // From a WM_NCCREATE msg, this will cause the
  737. // CreateWindow call to fail.
  738. //
  739. TraceMsg(TF_STANDARD, "LISTBOX: Unable to allocate listbox instance structure.");
  740. lReturn = FALSE;
  741. }
  742. break;
  743. case WM_GETOBJECT:
  744. if(lParam == OBJID_QUERYCLASSNAMEIDX)
  745. {
  746. lReturn = MSAA_CLASSNAMEIDX_LISTBOX;
  747. }
  748. else
  749. {
  750. lReturn = FALSE;
  751. }
  752. break;
  753. case WM_THEMECHANGED:
  754. if ( plb->hTheme )
  755. {
  756. CloseThemeData(plb->hTheme);
  757. }
  758. plb->hTheme = OpenThemeData(plb->hwnd, L"Listbox");
  759. InvalidateRect(plb->hwnd, NULL, TRUE);
  760. lReturn = TRUE;
  761. break;
  762. default:
  763. CallDWP:
  764. lReturn = DefWindowProcW(hwnd, uMsg, wParam, lParam);
  765. }
  766. return lReturn;
  767. }
  768. //---------------------------------------------------------------------------//
  769. //
  770. // Function: GetWindowBorders
  771. //
  772. // Synopsis: Calculates # of borders around window
  773. //
  774. // Algorithm: Calculate # of window borders and # of client borders
  775. //
  776. int GetWindowBorders(LONG lStyle, DWORD dwExStyle, BOOL fWindow, BOOL fClient)
  777. {
  778. int cBorders = 0;
  779. DWORD dwTemp;
  780. if (fWindow)
  781. {
  782. //
  783. // Is there a 3D border around the window?
  784. //
  785. if (dwExStyle & WS_EX_WINDOWEDGE)
  786. {
  787. cBorders += 2;
  788. }
  789. else if (dwExStyle & WS_EX_STATICEDGE)
  790. {
  791. ++cBorders;
  792. }
  793. //
  794. // Is there a single flat border around the window? This is true for
  795. // WS_BORDER, WS_DLGFRAME, and WS_EX_DLGMODALFRAME windows.
  796. //
  797. if ( (lStyle & WS_CAPTION) || (dwExStyle & WS_EX_DLGMODALFRAME) )
  798. {
  799. ++cBorders;
  800. }
  801. //
  802. // Is there a sizing flat border around the window?
  803. //
  804. if (lStyle & WS_SIZEBOX)
  805. {
  806. if(SystemParametersInfo(SPI_GETBORDER, 0, &dwTemp, 0))
  807. {
  808. cBorders += dwTemp;
  809. }
  810. else
  811. {
  812. ASSERT(0);
  813. }
  814. }
  815. }
  816. if (fClient)
  817. {
  818. //
  819. // Is there a 3D border around the client?
  820. //
  821. if (dwExStyle & WS_EX_CLIENTEDGE)
  822. {
  823. cBorders += 2;
  824. }
  825. }
  826. return cBorders;
  827. }
  828. //---------------------------------------------------------------------------//
  829. //
  830. // GetLpszItem
  831. //
  832. // Returns a far pointer to the string belonging to item sItem
  833. // ONLY for Listboxes maintaining their own strings (pLBIV->fHasStrings == TRUE)
  834. //
  835. LPWSTR GetLpszItem(PLBIV pLBIV, INT sItem)
  836. {
  837. LONG offsz;
  838. lpLBItem plbi;
  839. if (sItem < 0 || sItem >= pLBIV->cMac)
  840. {
  841. TraceMsg(TF_ERROR, "Invalid parameter \"sItem\" (%ld) to GetLpszItem", sItem);
  842. return NULL;
  843. }
  844. //
  845. // get pointer to item index array
  846. // NOTE: NOT OWNERDRAW
  847. //
  848. plbi = (lpLBItem)(pLBIV->rgpch);
  849. offsz = plbi[sItem].offsz;
  850. return (LPWSTR)((PBYTE)(pLBIV->hStrings) + offsz);
  851. }
  852. //---------------------------------------------------------------------------//
  853. //
  854. // Multi column Listbox functions
  855. //
  856. //---------------------------------------------------------------------------//
  857. //
  858. // ListBox_CalcItemRowsAndColumns
  859. //
  860. // Calculates the number of columns (including partially visible)
  861. // in the listbox and calculates the number of items per column
  862. //
  863. void ListBox_CalcItemRowsAndColumns(PLBIV plb)
  864. {
  865. RECT rc;
  866. GetClientRect(plb->hwnd, &rc);
  867. //
  868. // B#4155
  869. // We need to check if plb->cyChar has been initialized. This is because
  870. // we remove WS_BORDER from old listboxes and add on WS_EX_CLIENTEDGE.
  871. // Since listboxes are always inflated by CXBORDER and CYBORDER, a
  872. // listbox that was created empty always ends up 2 x 2. Since this isn't
  873. // big enough to fit the entire client border, we don't mark it as
  874. // present. Thus the client isn't empty in VER40, although it was in
  875. // VER31 and before. It is possible to get to this spot without
  876. // plb->cyChar having been initialized yet if the listbox is
  877. // multicolumn && ownerdraw variable.
  878. //
  879. if (rc.bottom && rc.right && plb->cyChar)
  880. {
  881. //
  882. // Only make these calculations if the width & height are positive
  883. //
  884. plb->itemsPerColumn = (INT)max(rc.bottom / plb->cyChar, 1);
  885. plb->numberOfColumns = (INT)max(rc.right / plb->cxColumn, 1);
  886. plb->cItemFullMax = plb->itemsPerColumn * plb->numberOfColumns;
  887. //
  888. // Adjust iTop so it's at the top of a column
  889. //
  890. ListBox_NewITop(plb, plb->iTop);
  891. }
  892. }
  893. //---------------------------------------------------------------------------//
  894. //
  895. // ListBox_HSrollMultiColumn
  896. //
  897. // Supports horizontal scrolling of multicolumn listboxes
  898. //
  899. void ListBox_HSrollMultiColumn(PLBIV plb, INT cmd, INT xAmt)
  900. {
  901. INT iTop = plb->iTop;
  902. if (!plb->cMac)
  903. {
  904. return;
  905. }
  906. switch (cmd)
  907. {
  908. case SB_LINEUP:
  909. if (plb->fRightAlign)
  910. {
  911. goto ReallyLineDown;
  912. }
  913. ReallyLineUp:
  914. iTop -= plb->itemsPerColumn;
  915. break;
  916. case SB_LINEDOWN:
  917. if (plb->fRightAlign)
  918. {
  919. goto ReallyLineUp;
  920. }
  921. ReallyLineDown:
  922. iTop += plb->itemsPerColumn;
  923. break;
  924. case SB_PAGEUP:
  925. if (plb->fRightAlign)
  926. {
  927. goto ReallyPageDown;
  928. }
  929. ReallyPageUp:
  930. iTop -= plb->itemsPerColumn * plb->numberOfColumns;
  931. break;
  932. case SB_PAGEDOWN:
  933. if (plb->fRightAlign)
  934. {
  935. goto ReallyPageUp;
  936. }
  937. ReallyPageDown:
  938. iTop += plb->itemsPerColumn * plb->numberOfColumns;
  939. break;
  940. case SB_THUMBTRACK:
  941. case SB_THUMBPOSITION:
  942. if (plb->fRightAlign)
  943. {
  944. int iCols = plb->cMac ? ((plb->cMac-1) / plb->itemsPerColumn) + 1 : 0;
  945. xAmt = iCols - (xAmt + plb->numberOfColumns);
  946. if (xAmt < 0)
  947. {
  948. xAmt=0;
  949. }
  950. }
  951. iTop = xAmt * plb->itemsPerColumn;
  952. break;
  953. case SB_TOP:
  954. if (plb->fRightAlign)
  955. {
  956. goto ReallyBottom;
  957. }
  958. ReallyTop:
  959. iTop = 0;
  960. break;
  961. case SB_BOTTOM:
  962. if (plb->fRightAlign)
  963. {
  964. goto ReallyTop;
  965. }
  966. ReallyBottom:
  967. iTop = plb->cMac - 1 - ((plb->cMac - 1) % plb->itemsPerColumn);
  968. break;
  969. case SB_ENDSCROLL:
  970. plb->fSmoothScroll = TRUE;
  971. ListBox_ShowHideScrollBars(plb);
  972. break;
  973. }
  974. ListBox_NewITop(plb, iTop);
  975. }
  976. //---------------------------------------------------------------------------//
  977. //
  978. // ListBox variable height owner draw functions
  979. //
  980. //---------------------------------------------------------------------------//
  981. //
  982. // ListBox_GetVarHeightItemHeight
  983. //
  984. // Returns the height of the given item number. Assumes variable
  985. // height owner draw.
  986. //
  987. INT ListBox_GetVarHeightItemHeight(PLBIV plb, INT itemNumber)
  988. {
  989. BYTE itemHeight;
  990. UINT offsetHeight;
  991. if (plb->cMac)
  992. {
  993. if (plb->fHasStrings)
  994. {
  995. offsetHeight = plb->cMac * sizeof(LBItem);
  996. }
  997. else
  998. {
  999. offsetHeight = plb->cMac * sizeof(LBODItem);
  1000. }
  1001. if (plb->wMultiple)
  1002. {
  1003. offsetHeight += plb->cMac;
  1004. }
  1005. offsetHeight += itemNumber;
  1006. itemHeight = *(plb->rgpch+(UINT)offsetHeight);
  1007. return (INT)itemHeight;
  1008. }
  1009. //
  1010. // Default, we return the height of the system font. This is so we can draw
  1011. // the focus rect even though there are no items in the listbox.
  1012. //
  1013. return SYSFONT_CYCHAR;
  1014. }
  1015. //---------------------------------------------------------------------------//
  1016. //
  1017. // ListBox_SetVarHeightItemHeight
  1018. //
  1019. // Sets the height of the given item number. Assumes variable height
  1020. // owner draw, a valid item number and valid height.
  1021. //
  1022. void ListBox_SetVarHeightItemHeight(PLBIV plb, INT itemNumber, INT itemHeight)
  1023. {
  1024. int offsetHeight;
  1025. if (plb->fHasStrings)
  1026. offsetHeight = plb->cMac * sizeof(LBItem);
  1027. else
  1028. offsetHeight = plb->cMac * sizeof(LBODItem);
  1029. if (plb->wMultiple)
  1030. offsetHeight += plb->cMac;
  1031. offsetHeight += itemNumber;
  1032. *(plb->rgpch + (UINT)offsetHeight) = (BYTE)itemHeight;
  1033. }
  1034. //---------------------------------------------------------------------------//
  1035. //
  1036. // ListBox_VisibleItemsVarOwnerDraw
  1037. //
  1038. // Returns the number of items which can fit in a variable height OWNERDRAW
  1039. // list box. If fDirection, then we return the number of items which
  1040. // fit starting at sTop and going forward (for page down), otherwise, we are
  1041. // going backwards (for page up). (Assumes var height ownerdraw) If fPartial,
  1042. // then include the partially visible item at the bottom of the listbox.
  1043. //
  1044. INT ListBox_VisibleItemsVarOwnerDraw(PLBIV plb, BOOL fPartial)
  1045. {
  1046. RECT rect;
  1047. INT sItem;
  1048. INT clientbottom;
  1049. GetClientRect(plb->hwnd, (LPRECT)&rect);
  1050. clientbottom = rect.bottom;
  1051. //
  1052. // Find the number of var height ownerdraw items which are visible starting
  1053. // from plb->iTop.
  1054. //
  1055. for (sItem = plb->iTop; sItem < plb->cMac; sItem++)
  1056. {
  1057. //
  1058. // Find out if the item is visible or not
  1059. //
  1060. if (!ListBox_GetItemRectHandler(plb, sItem, (LPRECT)&rect))
  1061. {
  1062. //
  1063. // This is the first item which is completely invisible, so return
  1064. // how many items are visible.
  1065. //
  1066. return (sItem - plb->iTop);
  1067. }
  1068. if (!fPartial && rect.bottom > clientbottom)
  1069. {
  1070. //
  1071. // If we only want fully visible items, then if this item is
  1072. // visible, we check if the bottom of the item is below the client
  1073. // rect, so we return how many are fully visible.
  1074. //
  1075. return (sItem - plb->iTop - 1);
  1076. }
  1077. }
  1078. //
  1079. // All the items are visible
  1080. //
  1081. return (plb->cMac - plb->iTop);
  1082. }
  1083. //---------------------------------------------------------------------------//
  1084. //
  1085. // ListBox_Page
  1086. //
  1087. // For variable height ownerdraw listboxes, calaculates the new iTop we must
  1088. // move to when paging (page up/down) through variable height listboxes.
  1089. //
  1090. INT ListBox_Page(PLBIV plb, INT startItem, BOOL fPageForwardDirection)
  1091. {
  1092. INT i;
  1093. INT height;
  1094. RECT rc;
  1095. if (plb->cMac == 1)
  1096. {
  1097. return 0;
  1098. }
  1099. GetClientRect(plb->hwnd, &rc);
  1100. height = rc.bottom;
  1101. i = startItem;
  1102. if (fPageForwardDirection)
  1103. {
  1104. while ((height >= 0) && (i < plb->cMac))
  1105. {
  1106. height -= ListBox_GetVarHeightItemHeight(plb, i++);
  1107. }
  1108. return (height >= 0) ? (plb->cMac - 1) : max(i - 2, startItem + 1);
  1109. }
  1110. else
  1111. {
  1112. while ((height >= 0) && (i >= 0))
  1113. {
  1114. height -= ListBox_GetVarHeightItemHeight(plb, i--);
  1115. }
  1116. return (height >= 0) ? 0 : min(i + 2, startItem - 1);
  1117. }
  1118. }
  1119. //---------------------------------------------------------------------------//
  1120. //
  1121. // ListBox_CalcVarITopScrollAmt
  1122. //
  1123. // Changing the top most item in the listbox from iTopOld to iTopNew we
  1124. // want to calculate the number of pixels to scroll so that we minimize the
  1125. // number of items we will redraw.
  1126. //
  1127. INT ListBox_CalcVarITopScrollAmt(PLBIV plb, INT iTopOld, INT iTopNew)
  1128. {
  1129. RECT rc;
  1130. RECT rcClient;
  1131. GetClientRect(plb->hwnd, (LPRECT)&rcClient);
  1132. //
  1133. // Just optimize redrawing when move +/- 1 item. We will redraw all items
  1134. // if moving more than 1 item ahead or back. This is good enough for now.
  1135. //
  1136. if (iTopOld + 1 == iTopNew)
  1137. {
  1138. //
  1139. // We are scrolling the current iTop up off the top off the listbox so
  1140. // return a negative number.
  1141. //
  1142. ListBox_GetItemRectHandler(plb, iTopOld, (LPRECT)&rc);
  1143. return (rcClient.top - rc.bottom);
  1144. }
  1145. if (iTopOld - 1 == iTopNew)
  1146. {
  1147. //
  1148. // We are scrolling the current iTop down and the previous item is
  1149. // becoming the new iTop so return a positive number.
  1150. //
  1151. ListBox_GetItemRectHandler(plb, iTopNew, (LPRECT)&rc);
  1152. return -rc.top;
  1153. }
  1154. return rcClient.bottom - rcClient.top;
  1155. }
  1156. //---------------------------------------------------------------------------//
  1157. //
  1158. // (supposedly) Rarely called Listbox functions
  1159. //
  1160. //---------------------------------------------------------------------------//
  1161. void ListBox_SetCItemFullMax(PLBIV plb)
  1162. {
  1163. if (plb->OwnerDraw != OWNERDRAWVAR)
  1164. {
  1165. plb->cItemFullMax = ListBox_CItemInWindow(plb, FALSE);
  1166. }
  1167. else if (plb->cMac < 2)
  1168. {
  1169. plb->cItemFullMax = 1;
  1170. }
  1171. else
  1172. {
  1173. int height;
  1174. RECT rect;
  1175. int i;
  1176. int j = 0;
  1177. GetClientRect(plb->hwnd, &rect);
  1178. height = rect.bottom;
  1179. plb->cItemFullMax = 0;
  1180. for (i = plb->cMac - 1; i >= 0; i--, j++)
  1181. {
  1182. height -= ListBox_GetVarHeightItemHeight(plb, i);
  1183. if (height < 0)
  1184. {
  1185. plb->cItemFullMax = j;
  1186. break;
  1187. }
  1188. }
  1189. if (!plb->cItemFullMax)
  1190. {
  1191. plb->cItemFullMax = j;
  1192. }
  1193. }
  1194. }
  1195. //---------------------------------------------------------------------------//
  1196. LONG ListBox_Create(PLBIV plb, HWND hwnd, LPCREATESTRUCT lpcs)
  1197. {
  1198. UINT style;
  1199. DWORD ExStyle;
  1200. MEASUREITEMSTRUCT measureItemStruct;
  1201. HDC hdc;
  1202. HWND hwndParent;
  1203. SIZE size;
  1204. //
  1205. // Once we make it here, nobody can change the ownerdraw style bits
  1206. // by calling SetWindowLong. The window style must match the flags in plb
  1207. //
  1208. plb->fInitialized = TRUE;
  1209. style = lpcs->style;
  1210. ExStyle = lpcs->dwExStyle;
  1211. hwndParent = lpcs->hwndParent;
  1212. plb->hwndParent = hwndParent;
  1213. plb->hTheme = OpenThemeData(plb->hwnd, L"Listbox");
  1214. //
  1215. // Break out the style bits
  1216. //
  1217. plb->fRedraw = ((style & LBS_NOREDRAW) == 0);
  1218. plb->fDeferUpdate = FALSE;
  1219. plb->fNotify = (UINT)((style & LBS_NOTIFY) != 0);
  1220. plb->fVertBar = ((style & WS_VSCROLL) != 0);
  1221. plb->fHorzBar = ((style & WS_HSCROLL) != 0);
  1222. if (!TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT))
  1223. {
  1224. //
  1225. // for 3.x apps, if either scroll bar was specified, the app got BOTH
  1226. //
  1227. if (plb->fVertBar || plb->fHorzBar)
  1228. {
  1229. plb->fVertBar = plb->fHorzBar = TRUE;
  1230. }
  1231. }
  1232. plb->fRtoLReading = (ExStyle & WS_EX_RTLREADING)!= 0;
  1233. plb->fRightAlign = (ExStyle & WS_EX_RIGHT) != 0;
  1234. plb->fDisableNoScroll = ((style & LBS_DISABLENOSCROLL) != 0);
  1235. plb->fSmoothScroll = TRUE;
  1236. //
  1237. // LBS_NOSEL gets priority over any other selection style. Next highest
  1238. // priority goes to LBS_EXTENDEDSEL. Then LBS_MULTIPLESEL.
  1239. //
  1240. if (TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT) && (style & LBS_NOSEL))
  1241. {
  1242. plb->wMultiple = SINGLESEL;
  1243. plb->fNoSel = TRUE;
  1244. }
  1245. else if (style & LBS_EXTENDEDSEL)
  1246. {
  1247. plb->wMultiple = EXTENDEDSEL;
  1248. }
  1249. else
  1250. {
  1251. plb->wMultiple = (UINT)((style & LBS_MULTIPLESEL) ? MULTIPLESEL : SINGLESEL);
  1252. }
  1253. plb->fNoIntegralHeight = ((style & LBS_NOINTEGRALHEIGHT) != 0);
  1254. plb->fWantKeyboardInput = ((style & LBS_WANTKEYBOARDINPUT) != 0);
  1255. plb->fUseTabStops = ((style & LBS_USETABSTOPS) != 0);
  1256. if (plb->fUseTabStops)
  1257. {
  1258. //
  1259. // Set tab stops every <default> dialog units.
  1260. //
  1261. ListBox_SetTabStopsHandler(plb, 0, NULL);
  1262. }
  1263. plb->fMultiColumn = ((style & LBS_MULTICOLUMN) != 0);
  1264. plb->fHasStrings = TRUE;
  1265. plb->iLastSelection = -1;
  1266. //
  1267. // Anchor point for multi selection
  1268. //
  1269. plb->iMouseDown = -1;
  1270. plb->iLastMouseMove = -1;
  1271. //
  1272. // Get ownerdraw style bits
  1273. //
  1274. if ((style & LBS_OWNERDRAWFIXED))
  1275. {
  1276. plb->OwnerDraw = OWNERDRAWFIXED;
  1277. }
  1278. else if ((style & LBS_OWNERDRAWVARIABLE) && !plb->fMultiColumn)
  1279. {
  1280. plb->OwnerDraw = OWNERDRAWVAR;
  1281. //
  1282. // Integral height makes no sense with var height owner draw
  1283. //
  1284. plb->fNoIntegralHeight = TRUE;
  1285. }
  1286. if (plb->OwnerDraw && !(style & LBS_HASSTRINGS))
  1287. {
  1288. //
  1289. // If owner draw, do they want the listbox to maintain strings?
  1290. //
  1291. plb->fHasStrings = FALSE;
  1292. }
  1293. //
  1294. // If user specifies sort and not hasstrings, then we will send
  1295. // WM_COMPAREITEM messages to the parent.
  1296. //
  1297. plb->fSort = ((style & LBS_SORT) != 0);
  1298. //
  1299. // "No data" lazy-eval listbox mandates certain other style settings
  1300. //
  1301. plb->fHasData = TRUE;
  1302. if (style & LBS_NODATA)
  1303. {
  1304. if (plb->OwnerDraw != OWNERDRAWFIXED || plb->fSort || plb->fHasStrings)
  1305. {
  1306. TraceMsg(TF_STANDARD, "NODATA listbox must be OWNERDRAWFIXED, w/o SORT or HASSTRINGS");
  1307. }
  1308. else
  1309. {
  1310. plb->fHasData = FALSE;
  1311. }
  1312. }
  1313. plb->dwLocaleId = GetThreadLocale();
  1314. //
  1315. // Check if this is part of a combo box
  1316. //
  1317. if ((style & LBS_COMBOBOX) != 0)
  1318. {
  1319. //
  1320. // Get the pcbox structure contained in the parent window's extra data
  1321. // pointer. Check cbwndExtra to ensure compatibility with SQL windows.
  1322. //
  1323. plb->pcbox = ComboBox_GetPtr(hwndParent);
  1324. }
  1325. plb->iSel = -1;
  1326. plb->hdc = NULL;
  1327. //
  1328. // Set the keyboard state so that when the user keyboard clicks he selects
  1329. // an item.
  1330. //
  1331. plb->fNewItemState = TRUE;
  1332. ListBox_InitHStrings(plb);
  1333. if (plb->fHasStrings && plb->hStrings == NULL)
  1334. {
  1335. return -1L;
  1336. }
  1337. hdc = GetDC(hwnd);
  1338. GetCharDimensions(hdc, &size);
  1339. plb->cxChar = size.cx;
  1340. plb->cyChar = size.cy;
  1341. ReleaseDC(hwnd, hdc);
  1342. if ((plb->cxChar == 0) || (plb->cyChar == 0))
  1343. {
  1344. TraceMsg(TF_STANDARD, "LISTBOX: GetCharDimensions failed.");
  1345. plb->cxChar = SYSFONT_CXCHAR;
  1346. plb->cyChar = SYSFONT_CYCHAR;
  1347. }
  1348. if (plb->OwnerDraw == OWNERDRAWFIXED)
  1349. {
  1350. //
  1351. // Query for item height only if we are fixed height owner draw. Note
  1352. // that we don't care about an item's width for listboxes.
  1353. //
  1354. measureItemStruct.CtlType = ODT_LISTBOX;
  1355. measureItemStruct.CtlID = GetDlgCtrlID(hwnd);
  1356. //
  1357. // System font height is default height
  1358. //
  1359. measureItemStruct.itemHeight = plb->cyChar;
  1360. measureItemStruct.itemWidth = 0;
  1361. measureItemStruct.itemData = 0;
  1362. //
  1363. // IanJa: #ifndef WIN16 (32-bit Windows), plb->id gets extended
  1364. // to LONG wParam automatically by the compiler
  1365. //
  1366. SendMessage(plb->hwndParent, WM_MEASUREITEM,
  1367. measureItemStruct.CtlID,
  1368. (LPARAM)&measureItemStruct);
  1369. //
  1370. // Use default height if given 0. This prevents any possible future
  1371. // div-by-zero errors.
  1372. //
  1373. if (measureItemStruct.itemHeight)
  1374. {
  1375. plb->cyChar = measureItemStruct.itemHeight;
  1376. }
  1377. if (plb->fMultiColumn)
  1378. {
  1379. //
  1380. // Get default column width from measure items struct if we are a
  1381. // multicolumn listbox.
  1382. //
  1383. plb->cxColumn = measureItemStruct.itemWidth;
  1384. }
  1385. }
  1386. else if (plb->OwnerDraw == OWNERDRAWVAR)
  1387. {
  1388. plb->cyChar = 0;
  1389. }
  1390. if (plb->fMultiColumn)
  1391. {
  1392. //
  1393. // Set these default values till we get the WM_SIZE message and we
  1394. // calculate them properly. This is because some people create a
  1395. // 0 width/height listbox and size it later. We don't want to have
  1396. // problems with invalid values in these fields
  1397. //
  1398. if (plb->cxColumn <= 0)
  1399. {
  1400. plb->cxColumn = 15 * plb->cxChar;
  1401. }
  1402. plb->numberOfColumns = plb->itemsPerColumn = 1;
  1403. }
  1404. ListBox_SetCItemFullMax(plb);
  1405. //
  1406. // Don't do this for 4.0 apps. It'll make everyone's lives easier and
  1407. // fix the anomaly that a combo & list created the same width end up
  1408. // different when all is done.
  1409. // B#1520
  1410. //
  1411. if (!TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT))
  1412. {
  1413. plb->fIgnoreSizeMsg = TRUE;
  1414. MoveWindow(hwnd,
  1415. lpcs->x - SYSMET(CXBORDER),
  1416. lpcs->y - SYSMET(CYBORDER),
  1417. lpcs->cx + SYSMET(CXEDGE),
  1418. lpcs->cy + SYSMET(CYEDGE),
  1419. FALSE);
  1420. plb->fIgnoreSizeMsg = FALSE;
  1421. }
  1422. if (!plb->fNoIntegralHeight)
  1423. {
  1424. //
  1425. // Send a message to ourselves to resize the listbox to an integral
  1426. // height. We need to do it this way because at create time we are all
  1427. // mucked up with window rects etc...
  1428. // IanJa: #ifndef WIN16 (32-bit Windows), wParam 0 gets extended
  1429. // to wParam 0L automatically by the compiler.
  1430. //
  1431. PostMessage(hwnd, WM_SIZE, 0, 0L);
  1432. }
  1433. return 1L;
  1434. }
  1435. //---------------------------------------------------------------------------//
  1436. //
  1437. // ListBox_DoDeleteItems
  1438. //
  1439. // Send DELETEITEM message for all the items in the ownerdraw listbox.
  1440. //
  1441. void ListBox_DoDeleteItems(PLBIV plb)
  1442. {
  1443. INT sItem;
  1444. //
  1445. // Send WM_DELETEITEM message for ownerdraw listboxes which are
  1446. // being deleted. (NODATA listboxes don't send such, though.)
  1447. //
  1448. if (plb->OwnerDraw && plb->cMac && plb->fHasData)
  1449. {
  1450. for (sItem = plb->cMac - 1; sItem >= 0; sItem--)
  1451. {
  1452. ListBox_DeleteItem(plb, sItem);
  1453. }
  1454. }
  1455. }
  1456. //---------------------------------------------------------------------------//
  1457. VOID ListBox_Destroy(PLBIV plv, HWND hwnd)
  1458. {
  1459. if (plv != NULL)
  1460. {
  1461. //
  1462. // If ownerdraw, send deleteitem messages to parent
  1463. //
  1464. ListBox_DoDeleteItems(plv);
  1465. if (plv->rgpch != NULL)
  1466. {
  1467. ControlFree(GetProcessHeap(), plv->rgpch);
  1468. plv->rgpch = NULL;
  1469. }
  1470. if (plv->hStrings != NULL)
  1471. {
  1472. ControlFree(GetProcessHeap(), plv->hStrings);
  1473. plv->hStrings = NULL;
  1474. }
  1475. if (plv->iTabPixelPositions != NULL)
  1476. {
  1477. ControlFree(GetProcessHeap(), (HANDLE)plv->iTabPixelPositions);
  1478. plv->iTabPixelPositions = NULL;
  1479. }
  1480. if (plv->pszTypeSearch)
  1481. {
  1482. ControlFree(GetProcessHeap(), plv->pszTypeSearch);
  1483. }
  1484. if (plv->hTheme != NULL)
  1485. {
  1486. CloseThemeData(plv->hTheme);
  1487. }
  1488. //
  1489. // If we're part of a combo box, let it know we're gone
  1490. //
  1491. if (plv->hwndParent && plv->pcbox)
  1492. {
  1493. ComboBox_WndProc(plv->hwndParent, WM_PARENTNOTIFY,
  1494. MAKEWPARAM(WM_DESTROY, GetWindowID(hwnd)), (LPARAM)hwnd);
  1495. }
  1496. UserLocalFree(plv);
  1497. }
  1498. TraceMsg(TF_STANDARD, "LISTBOX: Clearing listbox instance pointer.");
  1499. ListBox_SetPtr(hwnd, NULL);
  1500. }
  1501. //---------------------------------------------------------------------------//
  1502. void ListBox_SetFont(PLBIV plb, HANDLE hFont, BOOL fRedraw)
  1503. {
  1504. HDC hdc;
  1505. HANDLE hOldFont = NULL;
  1506. SIZE size;
  1507. plb->hFont = hFont;
  1508. hdc = GetDC(plb->hwnd);
  1509. if (hFont)
  1510. {
  1511. hOldFont = SelectObject(hdc, hFont);
  1512. if (!hOldFont)
  1513. {
  1514. plb->hFont = NULL;
  1515. }
  1516. }
  1517. GetCharDimensions(hdc, &size);
  1518. if ((size.cx == 0) || (size.cy == 0))
  1519. {
  1520. TraceMsg(TF_STANDARD, "LISTBOX: GetCharDimensions failed.");
  1521. size.cx = SYSFONT_CXCHAR;
  1522. size.cy = SYSFONT_CYCHAR;
  1523. }
  1524. plb->cxChar = size.cx;
  1525. if (!plb->OwnerDraw && (plb->cyChar != size.cy))
  1526. {
  1527. //
  1528. // We don't want to mess up the cyChar height for owner draw listboxes
  1529. // so don't do this.
  1530. //
  1531. plb->cyChar = size.cy;
  1532. //
  1533. // Only resize the listbox for 4.0 dudes, or combo dropdowns.
  1534. // Macromedia Director 4.0 GP-faults otherwise.
  1535. //
  1536. if (!plb->fNoIntegralHeight &&
  1537. (plb->pcbox || TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT)))
  1538. {
  1539. RECT rcClient;
  1540. GetClientRect(plb->hwnd, &rcClient);
  1541. ListBox_Size(plb, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, FALSE);
  1542. }
  1543. }
  1544. if (hOldFont)
  1545. {
  1546. SelectObject(hdc, hOldFont);
  1547. }
  1548. ReleaseDC(plb->hwnd, hdc);
  1549. if (plb->fMultiColumn)
  1550. {
  1551. ListBox_CalcItemRowsAndColumns(plb);
  1552. }
  1553. ListBox_SetCItemFullMax(plb);
  1554. if (fRedraw)
  1555. {
  1556. ListBox_CheckRedraw(plb, FALSE, 0);
  1557. }
  1558. }
  1559. //---------------------------------------------------------------------------//
  1560. void ListBox_Size(PLBIV plb, INT cx, INT cy, BOOL fSizeMinVisible)
  1561. {
  1562. RECT rc, rcWindow;
  1563. int iTopOld;
  1564. int cBorder;
  1565. BOOL fSizedSave;
  1566. if (!plb->fNoIntegralHeight)
  1567. {
  1568. int cBdrs = GetWindowBorders(GET_STYLE(plb), GET_EXSTYLE(plb), TRUE, TRUE);
  1569. GetWindowRect(plb->hwnd, &rcWindow);
  1570. cBorder = SYSMET(CYBORDER);
  1571. CopyRect(&rc, &rcWindow);
  1572. InflateRect(&rc, 0, -cBdrs * cBorder);
  1573. //
  1574. // Size the listbox to fit an integral # of items in its client
  1575. //
  1576. if ((plb->cyChar && ((rc.bottom - rc.top) % plb->cyChar)) || fSizeMinVisible)
  1577. {
  1578. int iItems = (rc.bottom - rc.top);
  1579. //
  1580. // B#2285 - If its a 3.1 app its SetWindowPos needs
  1581. // to be window based dimensions not Client !
  1582. // this crunches Money into using a scroll bar
  1583. //
  1584. if (!TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT))
  1585. {
  1586. //
  1587. // so add it back in
  1588. //
  1589. iItems += (cBdrs * SYSMET(CYEDGE));
  1590. }
  1591. iItems /= plb->cyChar;
  1592. //
  1593. // If we're in a dropdown list, size the listbox to accomodate
  1594. // a minimum number of items before needing to show scrolls.
  1595. //
  1596. if (plb->pcbox &&
  1597. (plb->pcbox->CBoxStyle & SDROPPABLE) &&
  1598. (((iItems < plb->pcbox->iMinVisible) &&
  1599. (iItems < plb->cMac)) || fSizeMinVisible))
  1600. {
  1601. iItems = min(plb->pcbox->iMinVisible, plb->cMac);
  1602. }
  1603. SetWindowPos(plb->hwnd, HWND_TOP, 0, 0, rc.right - rc.left,
  1604. iItems * plb->cyChar + (SYSMET(CYEDGE) * cBdrs),
  1605. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  1606. //
  1607. // Changing the size causes us to recurse. Upon return
  1608. // the state is where it should be and nothing further
  1609. // needs to be done.
  1610. //
  1611. return;
  1612. }
  1613. }
  1614. if (plb->fMultiColumn)
  1615. {
  1616. //
  1617. // Compute the number of DISPLAYABLE rows and columns in the listbox
  1618. //
  1619. ListBox_CalcItemRowsAndColumns(plb);
  1620. }
  1621. else
  1622. {
  1623. //
  1624. // Adjust the current horizontal position to eliminate as much
  1625. // empty space as possible from the right side of the items.
  1626. //
  1627. GetClientRect(plb->hwnd, &rc);
  1628. if ((plb->maxWidth - plb->xOrigin) < (rc.right - rc.left))
  1629. {
  1630. plb->xOrigin = max(0, plb->maxWidth - (rc.right - rc.left));
  1631. }
  1632. }
  1633. ListBox_SetCItemFullMax(plb);
  1634. //
  1635. // Adjust the top item in the listbox to eliminate as much empty space
  1636. // after the last item as possible
  1637. // (fix for bugs #8490 & #3836)
  1638. //
  1639. iTopOld = plb->iTop;
  1640. fSizedSave = plb->fSized;
  1641. plb->fSized = FALSE;
  1642. ListBox_NewITop(plb, plb->iTop);
  1643. //
  1644. // If changing the top item index caused a resize, there is no
  1645. // more work to be done here.
  1646. //
  1647. if (plb->fSized)
  1648. {
  1649. return;
  1650. }
  1651. plb->fSized = fSizedSave;
  1652. if (IsLBoxVisible(plb))
  1653. {
  1654. //
  1655. // This code no longer blows because it's fixed right!!! We could
  1656. // optimize the fMultiColumn case with some more code to figure out
  1657. // if we really need to invalidate the whole thing but note that some
  1658. // 3.0 apps depend on this extra invalidation (AMIPRO 2.0, bug 14620)
  1659. //
  1660. // For 3.1 apps, we blow off the invalidaterect in the case where
  1661. // cx and cy are 0 because this happens during the processing of
  1662. // the posted WM_SIZE message when we are created which would otherwise
  1663. // cause us to flash.
  1664. //
  1665. if ((plb->fMultiColumn && !(cx == 0 && cy == 0)) || plb->iTop != iTopOld)
  1666. {
  1667. InvalidateRect(plb->hwnd, NULL, TRUE);
  1668. }
  1669. else if (plb->iSelBase >= 0)
  1670. {
  1671. //
  1672. // Invalidate the item with the caret so that if the listbox
  1673. // grows horizontally, we redraw it properly.
  1674. //
  1675. ListBox_GetItemRectHandler(plb, plb->iSelBase, &rc);
  1676. InvalidateRect(plb->hwnd, &rc, FALSE);
  1677. }
  1678. }
  1679. else if (!plb->fRedraw)
  1680. {
  1681. plb->fDeferUpdate = TRUE;
  1682. }
  1683. //
  1684. // Send "fake" scroll bar messages to update the scroll positions since we
  1685. // changed size.
  1686. //
  1687. if (TESTFLAG(GET_STYLE(plb), WS_VSCROLL))
  1688. {
  1689. ListBox_VScroll(plb, SB_ENDSCROLL, 0);
  1690. }
  1691. //
  1692. // We count on this to call ListBox_ShowHideScrollBars except when plb->cMac == 0!
  1693. //
  1694. ListBox_HScroll(plb, SB_ENDSCROLL, 0);
  1695. //
  1696. // Show/hide scroll bars depending on how much stuff is visible...
  1697. //
  1698. // Note: Now we only call this guy when cMac == 0, because it is
  1699. // called inside the ListBox_HScroll with SB_ENDSCROLL otherwise.
  1700. //
  1701. if (plb->cMac == 0)
  1702. {
  1703. ListBox_ShowHideScrollBars(plb);
  1704. }
  1705. }
  1706. //---------------------------------------------------------------------------//
  1707. //
  1708. // ListBox_SetTabStopsHandler
  1709. //
  1710. // Sets the tab stops for this listbox. Returns TRUE if successful else FALSE.
  1711. //
  1712. BOOL ListBox_SetTabStopsHandler(PLBIV plb, INT count, LPINT lptabstops)
  1713. {
  1714. PINT ptabs;
  1715. if (!plb->fUseTabStops)
  1716. {
  1717. TraceMsg(TF_STANDARD, "Calling SetTabStops without the LBS_TABSTOPS style set");
  1718. return FALSE;
  1719. }
  1720. if (count)
  1721. {
  1722. //
  1723. // Allocate memory for the tab stops. The first byte in the
  1724. // plb->iTabPixelPositions array will contain a count of the number
  1725. // of tab stop positions we have.
  1726. //
  1727. ptabs = (LPINT)ControlAlloc(GetProcessHeap(), (count + 1) * sizeof(int));
  1728. if (ptabs == NULL)
  1729. {
  1730. return FALSE;
  1731. }
  1732. if (plb->iTabPixelPositions != NULL)
  1733. {
  1734. ControlFree(GetProcessHeap(), plb->iTabPixelPositions);
  1735. }
  1736. plb->iTabPixelPositions = ptabs;
  1737. //
  1738. // Set the count of tab stops
  1739. //
  1740. *ptabs++ = count;
  1741. for (; count > 0; count--)
  1742. {
  1743. //
  1744. // Convert the dialog unit tabstops into pixel position tab stops.
  1745. //
  1746. *ptabs++ = MultDiv(*lptabstops, plb->cxChar, 4);
  1747. lptabstops++;
  1748. }
  1749. }
  1750. else
  1751. {
  1752. //
  1753. // Set default 8 system font ave char width tabs. So free the memory
  1754. // associated with the tab stop list.
  1755. //
  1756. if (plb->iTabPixelPositions != NULL)
  1757. {
  1758. ControlFree(GetProcessHeap(), (HANDLE)plb->iTabPixelPositions);
  1759. plb->iTabPixelPositions = NULL;
  1760. }
  1761. }
  1762. return TRUE;
  1763. }
  1764. //---------------------------------------------------------------------------//
  1765. void ListBox_InitHStrings(PLBIV plb)
  1766. {
  1767. if (plb->fHasStrings)
  1768. {
  1769. plb->ichAlloc = 0;
  1770. plb->cchStrings = 0;
  1771. plb->hStrings = ControlAlloc(GetProcessHeap(), 0);
  1772. }
  1773. }
  1774. //---------------------------------------------------------------------------//
  1775. //
  1776. // ListBox_DropObjectHandler
  1777. //
  1778. // Handles a WM_DROPITEM message on this listbox
  1779. //
  1780. void ListBox_DropObjectHandler(PLBIV plb, PDROPSTRUCT pds)
  1781. {
  1782. LONG mouseSel;
  1783. if (ListBox_ISelFromPt(plb, pds->ptDrop, &mouseSel))
  1784. {
  1785. //
  1786. // User dropped in empty space at bottom of listbox
  1787. //
  1788. pds->dwControlData = (DWORD)-1L;
  1789. }
  1790. else
  1791. {
  1792. pds->dwControlData = mouseSel;
  1793. }
  1794. }
  1795. //---------------------------------------------------------------------------//
  1796. //
  1797. // ListBox_GetSetItemHeightHandler()
  1798. //
  1799. // Sets/Gets the height associated with each item. For non ownerdraw
  1800. // and fixed height ownerdraw, the item number is ignored.
  1801. //
  1802. int ListBox_GetSetItemHeightHandler(PLBIV plb, UINT message, int item, UINT height)
  1803. {
  1804. if (message == LB_GETITEMHEIGHT)
  1805. {
  1806. //
  1807. // All items are same height for non ownerdraw and for fixed height
  1808. // ownerdraw.
  1809. //
  1810. if (plb->OwnerDraw != OWNERDRAWVAR)
  1811. {
  1812. return plb->cyChar;
  1813. }
  1814. if (plb->cMac && item >= plb->cMac)
  1815. {
  1816. TraceMsg(TF_STANDARD,
  1817. "Invalid parameter \"item\" (%ld) to ListBox_GetSetItemHeightHandler", item);
  1818. return LB_ERR;
  1819. }
  1820. return (int)ListBox_GetVarHeightItemHeight(plb, (INT)item);
  1821. }
  1822. if (!height || height > 255)
  1823. {
  1824. TraceMsg(TF_STANDARD,
  1825. "Invalid parameter \"height\" (%ld) to ListBox_GetSetItemHeightHandler", height);
  1826. return LB_ERR;
  1827. }
  1828. if (plb->OwnerDraw != OWNERDRAWVAR)
  1829. {
  1830. plb->cyChar = height;
  1831. }
  1832. else
  1833. {
  1834. if (item < 0 || item >= plb->cMac)
  1835. {
  1836. TraceMsg(TF_STANDARD,
  1837. "Invalid parameter \"item\" (%ld) to ListBox_GetSetItemHeightHandler", item);
  1838. return LB_ERR;
  1839. }
  1840. ListBox_SetVarHeightItemHeight(plb, (INT)item, (INT)height);
  1841. }
  1842. if (plb->fMultiColumn)
  1843. {
  1844. ListBox_CalcItemRowsAndColumns(plb);
  1845. }
  1846. ListBox_SetCItemFullMax(plb);
  1847. return 0;
  1848. }
  1849. //---------------------------------------------------------------------------//
  1850. //
  1851. // ListBox_Event()
  1852. //
  1853. // This is for item focus & selection events in listboxes.
  1854. //
  1855. void ListBox_Event(PLBIV plb, UINT uEvent, int iItem)
  1856. {
  1857. switch (uEvent)
  1858. {
  1859. case EVENT_OBJECT_SELECTIONREMOVE:
  1860. if (plb->wMultiple != SINGLESEL)
  1861. {
  1862. break;
  1863. }
  1864. iItem = -1;
  1865. //
  1866. // FALL THRU
  1867. //
  1868. case EVENT_OBJECT_SELECTIONADD:
  1869. if (plb->wMultiple == MULTIPLESEL)
  1870. {
  1871. uEvent = EVENT_OBJECT_SELECTION;
  1872. }
  1873. break;
  1874. case EVENT_OBJECT_SELECTIONWITHIN:
  1875. iItem = -1;
  1876. break;
  1877. }
  1878. NotifyWinEvent(uEvent, plb->hwnd, OBJID_CLIENT, iItem+1);
  1879. }