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.

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