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.

2588 lines
78 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: combo.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * The WndProc for combo boxes and other often used combo routines
  7. *
  8. * History:
  9. * ??-???-???? ?????? Ported from Win 3.0 sources
  10. * 01-Feb-1991 mikeke Added Revalidation code
  11. \***************************************************************************/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. LOOKASIDE ComboboxLookaside;
  15. BOOL NtUserTrackMouseEvent(TRACKMOUSEEVENT *ptme);
  16. LONG xxxCBGetTextLengthHelper(PCBOX pcbox, BOOL fAnsi);
  17. LONG xxxCBGetTextHelper(PCBOX pcbox, int len, LPWSTR lpstr, BOOL fAnsi);
  18. /***************************************************************************\
  19. *
  20. * PressButton()
  21. *
  22. * Pops combobox button back up.
  23. *
  24. \***************************************************************************/
  25. void xxxPressButton(PCBOX pcbox, BOOL fPress)
  26. {
  27. //
  28. // Publisher relies on getting a WM_PAINT message after the combo list
  29. // pops back up. On a WM_PAINT they change the focus, which causes
  30. // toolbar combos to send CBN_SELENDCANCEL notifications. On this
  31. // notification they apply the font/pt size change you made to the
  32. // selection.
  33. //
  34. // This happened in 3.1 because the dropdown list overlapped the button
  35. // on the bottom or top by a pixel. Since we'd end up painting under
  36. // the list SPB, when it went away USER would reinvalidate the dirty
  37. // area. This would cause a paint message.
  38. //
  39. // In 4.0, this doesn't happen because the dropdown doesn't overlap. So
  40. // we need to make sure Publisher gets a WM_PAINT anyway. We do this
  41. // by changing where the dropdown shows up for 3.x apps
  42. //
  43. //
  44. if ((pcbox->fButtonPressed != 0) != (fPress != 0)) {
  45. HWND hwnd = HWq(pcbox->spwnd);
  46. pcbox->fButtonPressed = (fPress != 0);
  47. if (pcbox->f3DCombo)
  48. NtUserInvalidateRect(hwnd, KPRECT_TO_PRECT(&pcbox->buttonrc), TRUE);
  49. else
  50. {
  51. RECT rc;
  52. CopyRect(&rc, KPRECT_TO_PRECT(&pcbox->buttonrc));
  53. InflateRect(&rc, 0, SYSMET(CYEDGE));
  54. NtUserInvalidateRect(hwnd, &rc, TRUE);
  55. }
  56. UpdateWindow(hwnd);
  57. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEX_COMBOBOX_BUTTON);
  58. }
  59. }
  60. /***************************************************************************\
  61. * HotTrack
  62. *
  63. * If we're not already hot-tracking and the mouse is over the combobox,
  64. * turn on hot-tracking and invalidate the drop-down button.
  65. *
  66. \***************************************************************************/
  67. #ifdef COLOR_HOTTRACKING
  68. void HotTrack(PCBOX pcbox)
  69. {
  70. if (!pcbox->fButtonHotTracked && !pcbox->fMouseDown) {
  71. HWND hwnd = HWq(pcbox->spwnd);
  72. TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT), TME_LEAVE, hwnd, 0};
  73. if (NtUserTrackMouseEvent(&tme)) {
  74. pcbox->fButtonHotTracked = TRUE;
  75. NtUserInvalidateRect(hwnd, &pcbox->buttonrc, TRUE);
  76. }
  77. }
  78. }
  79. #endif // COLOR_HOTTRACKING
  80. /***************************************************************************\
  81. * xxxComboBoxDBCharHandler
  82. *
  83. * Double Byte character handler for ANSI ComboBox
  84. *
  85. * History:
  86. \***************************************************************************/
  87. LRESULT ComboBoxDBCharHandler(
  88. PCBOX pcbox,
  89. HWND hwnd,
  90. UINT message,
  91. WPARAM wParam,
  92. LPARAM lParam)
  93. {
  94. WORD w;
  95. PWND pwndSend;
  96. w = DbcsCombine(hwnd, (BYTE)wParam);
  97. if (w == 0) {
  98. return CB_ERR; // Failed to assemble DBCS
  99. }
  100. UserAssert(pcbox->spwndList);
  101. if (pcbox->fNoEdit) {
  102. pwndSend = pcbox->spwndList;
  103. } else if (pcbox->spwndEdit) {
  104. RIPMSG1(RIP_WARNING, "ComboBoxWndProcWorker: WM_CHAR is posted to Combobox itself(%08x).",
  105. hwnd);
  106. pwndSend = pcbox->spwndEdit;
  107. } else {
  108. return CB_ERR;
  109. }
  110. RIPMSG1(RIP_VERBOSE, "ComboBoxWndProcWorker: sending WM_CHAR %04x", w);
  111. if (!TestWF(pwndSend, WFANSIPROC)) {
  112. //
  113. // If receiver is not ANSI WndProc (may be subclassed?),
  114. // send a UNICODE message.
  115. //
  116. WCHAR wChar;
  117. LPWSTR lpwstr = &wChar;
  118. if (MBToWCSEx(THREAD_CODEPAGE(), (LPCSTR)&w, 2, &lpwstr, 1, FALSE) == 0) {
  119. RIPMSG1(RIP_WARNING, "ComboBoxWndProcWorker: cannot convert 0x%04x to UNICODE.", w);
  120. return CB_ERR;
  121. }
  122. return SendMessageWorker(pwndSend, message, wChar, lParam, FALSE);
  123. }
  124. /*
  125. * Post the Trailing byte to the target
  126. * so that they can peek the second WM_CHAR
  127. * message later.
  128. * Note: it's safe since sender is A and receiver is A,
  129. * translation layer does not perform any DBCS combining and cracking.
  130. */
  131. PostMessageA(HWq(pwndSend), message, CrackCombinedDbcsTB(w), lParam);
  132. return SendMessageWorker(pwndSend, message, wParam, lParam, TRUE);
  133. }
  134. BOOL ComboBoxMsgOKInInit(UINT message, LRESULT* plRet)
  135. {
  136. switch (message) {
  137. default:
  138. break;
  139. case WM_SIZE:
  140. *plRet = 0;
  141. return FALSE;
  142. case WM_STYLECHANGED:
  143. case WM_GETTEXT:
  144. case WM_GETTEXTLENGTH:
  145. case WM_PRINT:
  146. case WM_COMMAND:
  147. case CBEC_KILLCOMBOFOCUS:
  148. case WM_PRINTCLIENT:
  149. case WM_SETFONT:
  150. case WM_SYSKEYDOWN:
  151. case WM_KEYDOWN:
  152. case WM_CHAR:
  153. case WM_LBUTTONDBLCLK:
  154. case WM_LBUTTONDOWN:
  155. case WM_MOUSEWHEEL:
  156. case WM_CAPTURECHANGED:
  157. case WM_LBUTTONUP:
  158. case WM_MOUSEMOVE:
  159. case WM_SETFOCUS:
  160. case WM_KILLFOCUS:
  161. case WM_SETREDRAW:
  162. case WM_ENABLE:
  163. case CB_SETDROPPEDWIDTH:
  164. case CB_DIR:
  165. case CB_ADDSTRING:
  166. /*
  167. * Cannot handle those messages yet. Bail out.
  168. */
  169. *plRet = CB_ERR;
  170. return FALSE;
  171. }
  172. return TRUE;
  173. }
  174. /***************************************************************************\
  175. * xxxComboBoxCtlWndProc
  176. *
  177. * Class procedure for all combo boxes
  178. *
  179. * History:
  180. \***************************************************************************/
  181. LRESULT APIENTRY ComboBoxWndProcWorker(
  182. PWND pwnd,
  183. UINT message,
  184. WPARAM wParam,
  185. LPARAM lParam,
  186. DWORD fAnsi)
  187. {
  188. HWND hwnd = HWq(pwnd);
  189. PCBOX pcbox;
  190. POINT pt;
  191. TL tlpwndEdit;
  192. TL tlpwndList;
  193. PAINTSTRUCT ps;
  194. LPWSTR lpwsz = NULL;
  195. LRESULT lReturn;
  196. static BOOL fInit = TRUE;
  197. int i;
  198. CheckLock(pwnd);
  199. VALIDATECLASSANDSIZE(pwnd, FNID_COMBOBOX);
  200. INITCONTROLLOOKASIDE(&ComboboxLookaside, CBOX, spwnd, 8);
  201. /*
  202. * Get the pcbox for the given window now since we will use it a lot in
  203. * various handlers. This is stored by NtUserSetWindowLongPtr() in the
  204. * INITCONTROLLOOKASIDE macro above.
  205. */
  206. pcbox = ((PCOMBOWND)pwnd)->pcbox;
  207. /*
  208. * Protect the combobox during the initialization.
  209. */
  210. if (pcbox->spwndList == NULL) {
  211. LRESULT lRet;
  212. if (!ComboBoxMsgOKInInit(message, &lRet)) {
  213. RIPMSG2(RIP_WARNING, "ComboBoxWndProcWorker: msg=%04x is sent to hwnd=%08x in the middle of initialization.",
  214. message, hwnd);
  215. return lRet;
  216. }
  217. }
  218. /*
  219. * Dispatch the various messages we can receive
  220. */
  221. switch (message) {
  222. case CBEC_KILLCOMBOFOCUS:
  223. /*
  224. * Private message coming from editcontrol informing us that the combo
  225. * box is losing the focus to a window which isn't in this combo box.
  226. */
  227. xxxCBKillFocusHelper(pcbox);
  228. break;
  229. case WM_COMMAND:
  230. /*
  231. * So that we can handle notification messages from the listbox and
  232. * edit control.
  233. */
  234. return xxxCBCommandHandler(pcbox, (DWORD)wParam, (HWND)lParam);
  235. case WM_STYLECHANGED:
  236. UserAssert(pcbox->spwndList != NULL);
  237. {
  238. LONG OldStyle;
  239. LONG NewStyle = 0;
  240. pcbox->fRtoLReading = (TestWF(pwnd, WEFRTLREADING) != 0);
  241. pcbox->fRightAlign = (TestWF(pwnd, WEFRIGHT) != 0);
  242. if (pcbox->fRtoLReading)
  243. NewStyle |= (WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
  244. if (pcbox->fRightAlign)
  245. NewStyle |= WS_EX_RIGHT;
  246. ThreadLock(pcbox->spwndList, &tlpwndList);
  247. OldStyle = GetWindowLong(HWq(pcbox->spwndList), GWL_EXSTYLE) & ~(WS_EX_RIGHT|WS_EX_RTLREADING|WS_EX_LEFTSCROLLBAR);
  248. SetWindowLong(HWq(pcbox->spwndList), GWL_EXSTYLE, OldStyle|NewStyle);
  249. ThreadUnlock(&tlpwndList);
  250. if (!pcbox->fNoEdit && pcbox->spwndEdit) {
  251. ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
  252. OldStyle = GetWindowLong(HWq(pcbox->spwndEdit), GWL_EXSTYLE) & ~(WS_EX_RIGHT|WS_EX_RTLREADING|WS_EX_LEFTSCROLLBAR);
  253. SetWindowLong(HWq(pcbox->spwndEdit), GWL_EXSTYLE, OldStyle|NewStyle);
  254. ThreadUnlock(&tlpwndEdit);
  255. }
  256. xxxCBPosition(pcbox);
  257. NtUserInvalidateRect(hwnd, NULL, FALSE);
  258. }
  259. break;
  260. case WM_CTLCOLORMSGBOX:
  261. case WM_CTLCOLOREDIT:
  262. case WM_CTLCOLORLISTBOX:
  263. case WM_CTLCOLORBTN:
  264. case WM_CTLCOLORDLG:
  265. case WM_CTLCOLORSCROLLBAR:
  266. case WM_CTLCOLORSTATIC:
  267. case WM_CTLCOLOR:
  268. //
  269. // Causes compatibility problems for 3.X apps. Forward only
  270. // for 4.0
  271. //
  272. if (TestWF(pwnd, WFWIN40COMPAT)) {
  273. TL tlpwndParent;
  274. LRESULT ret;
  275. PWND pwndParent;
  276. pwndParent = REBASEPWND(pwnd, spwndParent);
  277. ThreadLock(pwndParent, &tlpwndParent);
  278. ret = SendMessage(HW(pwndParent), message, wParam, lParam);
  279. ThreadUnlock(tlpwndParent);
  280. return ret;
  281. } else
  282. return(DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi));
  283. break;
  284. case WM_GETTEXT:
  285. if (pcbox->fNoEdit) {
  286. return xxxCBGetTextHelper(pcbox, (int)wParam, (LPWSTR)lParam, fAnsi);
  287. }
  288. goto CallEditSendMessage;
  289. break;
  290. case WM_GETTEXTLENGTH:
  291. /*
  292. * If the is not edit control, CBS_DROPDOWNLIST, then we have to
  293. * ask the list box for the size
  294. */
  295. if (pcbox->fNoEdit) {
  296. return xxxCBGetTextLengthHelper(pcbox, fAnsi);
  297. }
  298. // FALL THROUGH
  299. case WM_CLEAR:
  300. case WM_CUT:
  301. case WM_PASTE:
  302. case WM_COPY:
  303. case WM_SETTEXT:
  304. goto CallEditSendMessage;
  305. break;
  306. case WM_CREATE:
  307. /*
  308. * wParam - not used
  309. * lParam - Points to the CREATESTRUCT data structure for the window.
  310. */
  311. return xxxCBCreateHandler(pcbox, pwnd);
  312. case WM_ERASEBKGND:
  313. /*
  314. * Just return 1L so that the background isn't erased
  315. */
  316. return 1L;
  317. case WM_GETFONT:
  318. return (LRESULT)pcbox->hFont;
  319. case WM_PRINT:
  320. if (!DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi))
  321. return(FALSE);
  322. if ((lParam & PRF_OWNED) && (pcbox->CBoxStyle & SDROPPABLE) &&
  323. TestWF(pcbox->spwndList, WFVISIBLE)) {
  324. TL tpwndList;
  325. int iDC = SaveDC((HDC) wParam);
  326. OffsetWindowOrgEx((HDC) wParam, 0, pwnd->rcWindow.top - pcbox->spwndList->rcWindow.top, NULL);
  327. lParam &= ~PRF_CHECKVISIBLE;
  328. ThreadLock(pcbox->spwndList, &tpwndList);
  329. SendMessageWorker(pcbox->spwndList, WM_PRINT, wParam, lParam, FALSE);
  330. RestoreDC((HDC) wParam, iDC);
  331. }
  332. return TRUE;
  333. case WM_PRINTCLIENT:
  334. xxxCBPaint(pcbox, (HDC) wParam);
  335. break;
  336. case WM_PAINT: {
  337. HDC hdc;
  338. /*
  339. * wParam - perhaps a hdc
  340. */
  341. hdc = (wParam) ? (HDC) wParam : NtUserBeginPaint(hwnd, &ps);
  342. if (IsComboVisible(pcbox))
  343. xxxCBPaint(pcbox, hdc);
  344. if (!wParam)
  345. NtUserEndPaint(hwnd, &ps);
  346. break;
  347. }
  348. case WM_GETDLGCODE:
  349. /*
  350. * wParam - not used
  351. * lParam - not used
  352. */
  353. {
  354. LRESULT code = DLGC_WANTCHARS | DLGC_WANTARROWS;
  355. // If the listbox is dropped and the ENTER key is pressed,
  356. // we want this message so we can close up the listbox
  357. if ((lParam != 0) &&
  358. (((LPMSG)lParam)->message == WM_KEYDOWN) &&
  359. pcbox->fLBoxVisible &&
  360. ((wParam == VK_RETURN) || (wParam == VK_ESCAPE)))
  361. {
  362. code |= DLGC_WANTMESSAGE;
  363. }
  364. return code;
  365. }
  366. /*
  367. * No fall through
  368. */
  369. case WM_SETFONT:
  370. xxxCBSetFontHandler(pcbox, (HANDLE)wParam, LOWORD(lParam));
  371. break;
  372. case WM_SYSKEYDOWN:
  373. if (lParam & 0x20000000L) /* Check if the alt key is down */ {
  374. /*
  375. * Handle Combobox support. We want alt up or down arrow to behave
  376. * like F4 key which completes the combo box selection
  377. */
  378. if (lParam & 0x1000000) {
  379. /*
  380. * This is an extended key such as the arrow keys not on the
  381. * numeric keypad so just drop the combobox.
  382. */
  383. if (wParam == VK_DOWN || wParam == VK_UP)
  384. goto DropCombo;
  385. goto CallDWP;
  386. }
  387. if (GetKeyState(VK_NUMLOCK) & 0x1) {
  388. /*
  389. * If numlock down, just send all system keys to dwp
  390. */
  391. goto CallDWP;
  392. } else {
  393. /*
  394. * We just want to ignore keys on the number pad...
  395. */
  396. if (!(wParam == VK_DOWN || wParam == VK_UP))
  397. goto CallDWP;
  398. }
  399. DropCombo:
  400. if (!pcbox->fLBoxVisible) {
  401. /*
  402. * If the listbox isn't visible, just show it
  403. */
  404. xxxCBShowListBoxWindow(pcbox, TRUE);
  405. } else {
  406. /*
  407. * Ok, the listbox is visible. So hide the listbox window.
  408. */
  409. if (!xxxCBHideListBoxWindow(pcbox, TRUE, TRUE))
  410. return(0L);
  411. }
  412. }
  413. goto CallDWP;
  414. break;
  415. case WM_KEYDOWN:
  416. /*
  417. * If the listbox is dropped and the ENTER key is pressed,
  418. * close up the listbox successfully. If ESCAPE is pressed,
  419. * close it up like cancel.
  420. */
  421. if (pcbox->fLBoxVisible) {
  422. if ((wParam == VK_RETURN) || (wParam == VK_ESCAPE)) {
  423. xxxCBHideListBoxWindow(pcbox, TRUE, (wParam != VK_ESCAPE));
  424. break;
  425. }
  426. }
  427. // FALL THROUGH
  428. case WM_CHAR:
  429. if (fAnsi && IS_DBCS_ENABLED() && IsDBCSLeadByteEx(THREAD_CODEPAGE(), (BYTE)wParam)) {
  430. return ComboBoxDBCharHandler(pcbox, hwnd, message, wParam, lParam);
  431. }
  432. if (pcbox->fNoEdit) {
  433. goto CallListSendMessage;
  434. }
  435. else
  436. goto CallEditSendMessage;
  437. break;
  438. case WM_LBUTTONDBLCLK:
  439. case WM_LBUTTONDOWN:
  440. #ifdef COLOR_HOTTRACKING
  441. pcbox->fButtonHotTracked = FALSE;
  442. #endif // COLOR_HOTTRACKING
  443. /*
  444. * Set the focus to the combo box if we get a mouse click on it.
  445. */
  446. if (!pcbox->fFocus) {
  447. NtUserSetFocus(hwnd);
  448. if (!pcbox->fFocus) {
  449. /*
  450. * Don't do anything if we still don't have the focus.
  451. */
  452. break;
  453. }
  454. }
  455. /*
  456. * If user clicked in button rect and we are a combobox with edit, then
  457. * drop the listbox. (The button rect is 0 if there is no button so the
  458. * ptinrect will return false.) If a drop down list (no edit), clicking
  459. * anywhere on the face causes the list to drop.
  460. */
  461. POINTSTOPOINT(pt, lParam);
  462. if ((pcbox->CBoxStyle == SDROPDOWN &&
  463. PtInRect(KPRECT_TO_PRECT(&pcbox->buttonrc), pt)) ||
  464. pcbox->CBoxStyle == SDROPDOWNLIST) {
  465. /*
  466. * Set the fMouseDown flag so that we can handle clicking on
  467. * the popdown button and dragging into the listbox (when it just
  468. * dropped down) to make a selection.
  469. */
  470. pcbox->fButtonPressed = TRUE;
  471. if (pcbox->fLBoxVisible) {
  472. if (pcbox->fMouseDown) {
  473. pcbox->fMouseDown = FALSE;
  474. NtUserReleaseCapture();
  475. }
  476. xxxPressButton(pcbox, FALSE);
  477. if (!xxxCBHideListBoxWindow(pcbox, TRUE, TRUE))
  478. return(0L);
  479. } else {
  480. xxxCBShowListBoxWindow(pcbox, FALSE);
  481. // Setting and resetting this flag must always be followed
  482. // imediately by SetCapture or ReleaseCapture
  483. //
  484. pcbox->fMouseDown = TRUE;
  485. NtUserSetCapture(hwnd);
  486. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEX_COMBOBOX_BUTTON);
  487. }
  488. }
  489. break;
  490. case WM_MOUSEWHEEL:
  491. /*
  492. * Handle only scrolling.
  493. */
  494. if (wParam & (MK_CONTROL | MK_SHIFT))
  495. goto CallDWP;
  496. /*
  497. * If the listbox is visible, send it the message to scroll.
  498. */
  499. if (pcbox->fLBoxVisible)
  500. goto CallListSendMessage;
  501. /*
  502. * If we're in extended UI mode or the edit control isn't yet created,
  503. * bail.
  504. */
  505. if (pcbox->fExtendedUI || pcbox->spwndEdit == NULL)
  506. return TRUE;
  507. /*
  508. * Emulate arrow up/down messages to the edit control.
  509. */
  510. i = abs(((short)HIWORD(wParam))/WHEEL_DELTA);
  511. wParam = ((short)HIWORD(wParam) > 0) ? VK_UP : VK_DOWN;
  512. ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
  513. while (i-- > 0) {
  514. SendMessageWorker(
  515. pcbox->spwndEdit, WM_KEYDOWN, wParam, 0, fAnsi);
  516. }
  517. ThreadUnlock(&tlpwndEdit);
  518. return TRUE;
  519. case WM_CAPTURECHANGED:
  520. if (!(TestWF(pwnd, WFWIN40COMPAT)))
  521. return 0;
  522. if ((pcbox->fMouseDown)) {
  523. pcbox->fMouseDown = FALSE;
  524. xxxPressButton(pcbox, FALSE);
  525. //
  526. // Pop combo listbox back up, canceling.
  527. //
  528. if (pcbox->fLBoxVisible)
  529. xxxCBHideListBoxWindow(pcbox, TRUE, FALSE);
  530. }
  531. break;
  532. case WM_LBUTTONUP:
  533. xxxPressButton(pcbox, FALSE);
  534. /*
  535. * Clear this flag so that mouse moves aren't sent to the listbox
  536. */
  537. if (pcbox->fMouseDown) {
  538. pcbox->fMouseDown = FALSE;
  539. if (pcbox->CBoxStyle == SDROPDOWN) {
  540. // If an item in the listbox matches the text in the edit
  541. // control, scroll it to the top of the listbox. Select the
  542. // item only if the mouse button isn't down otherwise we
  543. // will select the item when the mouse button goes up.
  544. xxxCBUpdateListBoxWindow(pcbox, TRUE);
  545. xxxCBCompleteEditWindow(pcbox);
  546. }
  547. NtUserReleaseCapture();
  548. // Now, we want listbox to track mouse moves while mouse up
  549. // until mouse down, and select items as though they were
  550. // clicked on.
  551. if (TestWF(pwnd, WFWIN40COMPAT)) {
  552. ThreadLock(pcbox->spwndList, &tlpwndList);
  553. SendMessageWorker(pcbox->spwndList, LBCB_STARTTRACK, FALSE, 0, FALSE);
  554. ThreadUnlock(&tlpwndList);
  555. }
  556. }
  557. #ifdef COLOR_HOTTRACKING
  558. HotTrack(pcbox);
  559. break;
  560. case WM_MOUSELEAVE:
  561. pcbox->fButtonHotTracked = FALSE;
  562. NtUserInvalidateRect(hwnd, &pcbox->buttonrc, TRUE);
  563. #endif // COLOR_HOTTRACKING
  564. break;
  565. case WM_MOUSEMOVE:
  566. if (pcbox->fMouseDown) {
  567. POINTSTOPOINT(pt, lParam);
  568. // Note conversion of INT bit field to BOOL (1 or 0)
  569. if (PtInRect(KPRECT_TO_PRECT(&pcbox->buttonrc), pt) != !!pcbox->fButtonPressed) {
  570. xxxPressButton(pcbox, (pcbox->fButtonPressed == 0));
  571. }
  572. _ClientToScreen(pwnd, &pt);
  573. if (PtInRect(KPRECT_TO_PRECT(&pcbox->spwndList->rcClient), pt)) {
  574. /*
  575. * This handles dropdown comboboxes/listboxes so that clicking
  576. * on the dropdown button and dragging into the listbox window
  577. * will let the user make a listbox selection.
  578. */
  579. pcbox->fMouseDown = FALSE;
  580. NtUserReleaseCapture();
  581. if (pcbox->CBoxStyle & SEDITABLE) {
  582. /*
  583. * If an item in the listbox matches the text in the edit
  584. * control, scroll it to the top of the listbox. Select the
  585. * item only if the mouse button isn't down otherwise we
  586. * will select the item when the mouse button goes up.
  587. */
  588. /*
  589. * We need to select the item which matches the editcontrol
  590. * so that if the user drags out of the listbox, we don't
  591. * cancel back to his origonal selection
  592. */
  593. xxxCBUpdateListBoxWindow(pcbox, TRUE);
  594. }
  595. /*
  596. * Convert point to listbox coordinates and send a buttondown
  597. * message to the listbox window.
  598. */
  599. _ScreenToClient(pcbox->spwndList, &pt);
  600. lParam = POINTTOPOINTS(pt);
  601. message = WM_LBUTTONDOWN;
  602. goto CallListSendMessage;
  603. }
  604. }
  605. #ifdef COLOR_HOTTRACKING
  606. HotTrack(pcbox);
  607. #endif // COLOR_HOTTRACKING
  608. break;
  609. case WM_NCDESTROY:
  610. case WM_FINALDESTROY:
  611. xxxCBNcDestroyHandler(pwnd, pcbox);
  612. break;
  613. case WM_SETFOCUS:
  614. if (pcbox->fNoEdit) {
  615. /*
  616. * There is no editcontrol so set the focus to the combo box itself.
  617. */
  618. xxxCBGetFocusHelper(pcbox);
  619. } else if (pcbox->spwndEdit) {
  620. /*
  621. * Set the focus to the edit control window if there is one
  622. */
  623. ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
  624. NtUserSetFocus(HWq(pcbox->spwndEdit));
  625. ThreadUnlock(&tlpwndEdit);
  626. }
  627. break;
  628. case WM_KILLFOCUS:
  629. /*
  630. * wParam has the new focus hwnd
  631. */
  632. if (wParam != 0)
  633. wParam = (WPARAM)ValidateHwnd((HWND)wParam);
  634. if ((wParam == 0) || !_IsChild(pwnd, (PWND)wParam)) {
  635. /*
  636. * We only give up the focus if the new window getting the focus
  637. * doesn't belong to the combo box.
  638. */
  639. xxxCBKillFocusHelper(pcbox);
  640. }
  641. UserAssert(pcbox->spwndList);
  642. {
  643. PLBIV plb = ((PLBWND)pcbox->spwndList)->pLBIV;
  644. if ((plb != NULL) && (plb != (PLBIV)-1)) {
  645. plb->iTypeSearch = 0;
  646. if (plb->pszTypeSearch) {
  647. UserLocalFree(plb->pszTypeSearch);
  648. plb->pszTypeSearch = NULL;
  649. }
  650. }
  651. }
  652. break;
  653. case WM_SETREDRAW:
  654. /*
  655. * wParam - specifies state of the redraw flag. nonzero = redraw
  656. * lParam - not used
  657. */
  658. /*
  659. * effects: Sets the state of the redraw flag for this combo box
  660. * and its children.
  661. */
  662. pcbox->fNoRedraw = (UINT)!((BOOL)wParam);
  663. /*
  664. * Must check pcbox->spwnEdit in case we get this message before
  665. * WM_CREATE - PCBOX won't be initialized yet. (Eudora does this)
  666. */
  667. if (!pcbox->fNoEdit && pcbox->spwndEdit) {
  668. ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
  669. SendMessageWorker(pcbox->spwndEdit, message, wParam, lParam, FALSE);
  670. ThreadUnlock(&tlpwndEdit);
  671. }
  672. goto CallListSendMessage;
  673. break;
  674. case WM_ENABLE:
  675. /*
  676. * Invalidate the rect to cause it to be drawn in grey for its
  677. * disabled view or ungreyed for non-disabled view.
  678. */
  679. NtUserInvalidateRect(hwnd, NULL, FALSE);
  680. if ((pcbox->CBoxStyle & SEDITABLE) && pcbox->spwndEdit) {
  681. /*
  682. * Enable/disable the edit control window
  683. */
  684. ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
  685. NtUserEnableWindow(HWq(pcbox->spwndEdit), (TestWF(pwnd, WFDISABLED) == 0));
  686. ThreadUnlock(&tlpwndEdit);
  687. }
  688. /*
  689. * Enable/disable the listbox window
  690. */
  691. UserAssert(pcbox->spwndList);
  692. ThreadLock(pcbox->spwndList, &tlpwndList);
  693. NtUserEnableWindow(HWq(pcbox->spwndList), (TestWF(pwnd, WFDISABLED) == 0));
  694. ThreadUnlock(&tlpwndList);
  695. break;
  696. case WM_SIZE:
  697. /*
  698. * wParam - defines the type of resizing fullscreen, sizeiconic,
  699. * sizenormal etc.
  700. * lParam - new width in LOWORD, new height in HIGHUINT of client area
  701. */
  702. UserAssert(pcbox->spwndList);
  703. if (LOWORD(lParam) == 0 || HIWORD(lParam) == 0) {
  704. /*
  705. * If being sized to a zero width or to a zero height or we aren't
  706. * fully initialized, just return.
  707. */
  708. return 0;
  709. }
  710. // OPTIMIZATIONS -- first check if old and new widths are the same
  711. if (pcbox->cxCombo == pwnd->rcWindow.right - pwnd->rcWindow.left) {
  712. int iNewHeight = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
  713. // now check if new height is the dropped down height
  714. if (pcbox->fLBoxVisible) {
  715. // Check if new height is the full size height
  716. if (pcbox->cyDrop + pcbox->cyCombo == iNewHeight)
  717. return(0L);
  718. } else {
  719. // Check if new height is the closed up height
  720. if (pcbox->cyCombo == iNewHeight)
  721. return(0L);
  722. }
  723. }
  724. xxxCBSizeHandler(pcbox);
  725. break;
  726. case CB_GETDROPPEDSTATE:
  727. /*
  728. * returns 1 if combo is dropped down else 0
  729. * wParam - not used
  730. * lParam - not used
  731. */
  732. return pcbox->fLBoxVisible;
  733. case CB_GETDROPPEDCONTROLRECT:
  734. /*
  735. * wParam - not used
  736. * lParam - lpRect which will get the dropped down window rect in
  737. * screen coordinates.
  738. */
  739. ((LPRECT)lParam)->left = pwnd->rcWindow.left;
  740. ((LPRECT)lParam)->top = pwnd->rcWindow.top;
  741. ((LPRECT)lParam)->right = pwnd->rcWindow.left + max(pcbox->cxDrop, pcbox->cxCombo);
  742. ((LPRECT)lParam)->bottom = pwnd->rcWindow.top + pcbox->cyCombo + pcbox->cyDrop;
  743. break;
  744. case CB_SETDROPPEDWIDTH:
  745. if (pcbox->CBoxStyle & SDROPPABLE) {
  746. if (wParam) {
  747. wParam = max(wParam, (UINT)pcbox->cxCombo);
  748. if (wParam != (UINT) pcbox->cxDrop)
  749. {
  750. pcbox->cxDrop = (int)wParam;
  751. xxxCBPosition(pcbox);
  752. }
  753. }
  754. }
  755. // fall thru
  756. case CB_GETDROPPEDWIDTH:
  757. if (pcbox->CBoxStyle & SDROPPABLE)
  758. return((LRESULT) max(pcbox->cxDrop, pcbox->cxCombo));
  759. else
  760. return(CB_ERR);
  761. break;
  762. case CB_DIR:
  763. /*
  764. * wParam - Dos attribute value.
  765. * lParam - Points to a file specification string
  766. */
  767. if (fAnsi && lParam != 0) {
  768. if (MBToWCS((LPSTR)lParam, -1, &lpwsz, -1, TRUE) == 0)
  769. return CB_ERR;
  770. lParam = (LPARAM)lpwsz;
  771. }
  772. lReturn = xxxCBDir(pcbox, LOWORD(wParam), (LPWSTR)lParam);
  773. if (fAnsi && lParam != 0) {
  774. UserLocalFree(lpwsz);
  775. }
  776. return lReturn;
  777. case CB_SETEXTENDEDUI:
  778. /*
  779. * wParam - specifies state to set extendui flag to.
  780. * Currently only 1 is allowed. Return CB_ERR (-1) if
  781. * failure else 0 if success.
  782. */
  783. if (pcbox->CBoxStyle & SDROPPABLE) {
  784. if (!wParam) {
  785. pcbox->fExtendedUI = 0;
  786. return 0;
  787. }
  788. if (wParam == 1) {
  789. pcbox->fExtendedUI = 1;
  790. return 0;
  791. }
  792. RIPERR1(ERROR_INVALID_PARAMETER,
  793. RIP_WARNING,
  794. "Invalid parameter \"wParam\" (%ld) to ComboBoxWndProcWorker",
  795. wParam);
  796. } else {
  797. RIPERR1(ERROR_INVALID_MESSAGE,
  798. RIP_WARNING,
  799. "Invalid message (%ld) sent to ComboBoxWndProcWorker",
  800. message);
  801. }
  802. return CB_ERR;
  803. case CB_GETEXTENDEDUI:
  804. if (pcbox->CBoxStyle & SDROPPABLE) {
  805. if (pcbox->fExtendedUI)
  806. return TRUE;
  807. }
  808. return FALSE;
  809. case CB_GETEDITSEL:
  810. /*
  811. * wParam - not used
  812. * lParam - not used
  813. * effects: Gets the selection range for the given edit control. The
  814. * starting BYTE-position is in the low order word. It contains the
  815. * the BYTE-position of the first nonselected character after the end
  816. * of the selection in the high order word. Returns CB_ERR if no
  817. * editcontrol.
  818. */
  819. message = EM_GETSEL;
  820. goto CallEditSendMessage;
  821. break;
  822. case CB_LIMITTEXT:
  823. /*
  824. * wParam - max number of bytes that can be entered
  825. * lParam - not used
  826. * effects: Specifies the maximum number of bytes of text the user may
  827. * enter. If maxLength is 0, we may enter MAXINT number of BYTES.
  828. */
  829. message = EM_LIMITTEXT;
  830. goto CallEditSendMessage;
  831. break;
  832. case CB_SETEDITSEL:
  833. /*
  834. * wParam - ichStart
  835. * lParam - ichEnd
  836. *
  837. */
  838. message = EM_SETSEL;
  839. wParam = (int)(SHORT)LOWORD(lParam);
  840. lParam = (int)(SHORT)HIWORD(lParam);
  841. goto CallEditSendMessage;
  842. break;
  843. case CB_ADDSTRING:
  844. /*
  845. * wParam - not used
  846. * lParam - Points to null terminated string to be added to listbox
  847. */
  848. if (!pcbox->fCase)
  849. message = LB_ADDSTRING;
  850. else
  851. message = (pcbox->fCase & UPPERCASE) ? LB_ADDSTRINGUPPER : LB_ADDSTRINGLOWER;
  852. goto CallListSendMessage;
  853. break;
  854. case CB_DELETESTRING:
  855. /*
  856. * wParam - index to string to be deleted
  857. * lParam - not used
  858. */
  859. message = LB_DELETESTRING;
  860. goto CallListSendMessage;
  861. break;
  862. case CB_INITSTORAGE:
  863. // wParamLo - number of items
  864. // lParam - number of bytes of string space
  865. message = LB_INITSTORAGE;
  866. goto CallListSendMessage;
  867. case CB_SETTOPINDEX:
  868. // wParamLo - index to make top
  869. // lParam - not used
  870. message = LB_SETTOPINDEX;
  871. goto CallListSendMessage;
  872. case CB_GETTOPINDEX:
  873. // wParamLo / lParam - not used
  874. message = LB_GETTOPINDEX;
  875. goto CallListSendMessage;
  876. case CB_GETCOUNT:
  877. /*
  878. * wParam - not used
  879. * lParam - not used
  880. */
  881. message = LB_GETCOUNT;
  882. goto CallListSendMessage;
  883. break;
  884. case CB_GETCURSEL:
  885. /*
  886. * wParam - not used
  887. * lParam - not used
  888. */
  889. message = LB_GETCURSEL;
  890. goto CallListSendMessage;
  891. break;
  892. case CB_GETLBTEXT:
  893. /*
  894. * wParam - index of string to be copied
  895. * lParam - buffer that is to receive the string
  896. */
  897. message = LB_GETTEXT;
  898. goto CallListSendMessage;
  899. break;
  900. case CB_GETLBTEXTLEN:
  901. /*
  902. * wParam - index to string
  903. * lParam - now used for cbANSI
  904. */
  905. message = LB_GETTEXTLEN;
  906. goto CallListSendMessage;
  907. break;
  908. case CB_INSERTSTRING:
  909. /*
  910. * wParam - position to receive the string
  911. * lParam - points to the string
  912. */
  913. if (!pcbox->fCase)
  914. message = LB_INSERTSTRING;
  915. else
  916. message = (pcbox->fCase & UPPERCASE) ? LB_INSERTSTRINGUPPER : LB_INSERTSTRINGLOWER;
  917. goto CallListSendMessage;
  918. break;
  919. case CB_RESETCONTENT:
  920. /*
  921. * wParam - not used
  922. * lParam - not used
  923. * If we come here before WM_CREATE has been processed,
  924. * pcbox->spwndList will be NULL.
  925. */
  926. UserAssert(pcbox->spwndList);
  927. ThreadLock(pcbox->spwndList, &tlpwndList);
  928. SendMessageWorker(pcbox->spwndList, LB_RESETCONTENT, 0, 0, FALSE);
  929. ThreadUnlock(&tlpwndList);
  930. xxxCBInternalUpdateEditWindow(pcbox, NULL);
  931. break;
  932. case CB_GETHORIZONTALEXTENT:
  933. message = LB_GETHORIZONTALEXTENT;
  934. goto CallListSendMessage;
  935. case CB_SETHORIZONTALEXTENT:
  936. message = LB_SETHORIZONTALEXTENT;
  937. goto CallListSendMessage;
  938. case CB_FINDSTRING:
  939. /*
  940. * wParam - index of starting point for search
  941. * lParam - points to prefix string
  942. */
  943. message = LB_FINDSTRING;
  944. goto CallListSendMessage;
  945. break;
  946. case CB_FINDSTRINGEXACT:
  947. /*
  948. * wParam - index of starting point for search
  949. * lParam - points to a exact string
  950. */
  951. message = LB_FINDSTRINGEXACT;
  952. goto CallListSendMessage;
  953. break;
  954. case CB_SELECTSTRING:
  955. /*
  956. * wParam - index of starting point for search
  957. * lParam - points to prefix string
  958. */
  959. UserAssert(pcbox->spwndList);
  960. ThreadLock(pcbox->spwndList, &tlpwndList);
  961. lParam = SendMessageWorker(pcbox->spwndList, LB_SELECTSTRING,
  962. wParam, lParam, fAnsi);
  963. ThreadUnlock(&tlpwndList);
  964. xxxCBInternalUpdateEditWindow(pcbox, NULL);
  965. return lParam;
  966. case CB_SETCURSEL:
  967. /*
  968. * wParam - Contains index to be selected
  969. * lParam - not used
  970. * If we come here before WM_CREATE has been processed,
  971. * pcbox->spwndList will be NULL.
  972. */
  973. UserAssert(pcbox->spwndList);
  974. ThreadLock(pcbox->spwndList, &tlpwndList);
  975. lParam = SendMessageWorker(pcbox->spwndList, LB_SETCURSEL, wParam, lParam, FALSE);
  976. if (lParam != -1) {
  977. SendMessageWorker(pcbox->spwndList, LB_SETTOPINDEX, wParam, 0, FALSE);
  978. }
  979. ThreadUnlock(&tlpwndList);
  980. xxxCBInternalUpdateEditWindow(pcbox, NULL);
  981. return lParam;
  982. case CB_GETITEMDATA:
  983. message = LB_GETITEMDATA;
  984. goto CallListSendMessage;
  985. break;
  986. case CB_SETITEMDATA:
  987. message = LB_SETITEMDATA;
  988. goto CallListSendMessage;
  989. break;
  990. case CB_SETITEMHEIGHT:
  991. if (wParam == -1) {
  992. if (HIWORD(lParam) != 0)
  993. return CB_ERR;
  994. return xxxCBSetEditItemHeight(pcbox, LOWORD(lParam));
  995. }
  996. message = LB_SETITEMHEIGHT;
  997. goto CallListSendMessage;
  998. break;
  999. case CB_GETITEMHEIGHT:
  1000. if (wParam == -1)
  1001. return pcbox->editrc.bottom - pcbox->editrc.top;
  1002. message = LB_GETITEMHEIGHT;
  1003. goto CallListSendMessage;
  1004. break;
  1005. case CB_SHOWDROPDOWN:
  1006. /*
  1007. * wParam - True then drop down the listbox if possible else hide it
  1008. * lParam - not used
  1009. */
  1010. if (wParam && !pcbox->fLBoxVisible) {
  1011. xxxCBShowListBoxWindow(pcbox, TRUE);
  1012. } else {
  1013. if (!wParam && pcbox->fLBoxVisible) {
  1014. xxxCBHideListBoxWindow(pcbox, TRUE, FALSE);
  1015. }
  1016. }
  1017. break;
  1018. case CB_SETLOCALE:
  1019. /*
  1020. * wParam - locale id
  1021. * lParam - not used
  1022. */
  1023. message = LB_SETLOCALE;
  1024. goto CallListSendMessage;
  1025. break;
  1026. case CB_GETLOCALE:
  1027. /*
  1028. * wParam - not used
  1029. * lParam - not used
  1030. */
  1031. message = LB_GETLOCALE;
  1032. goto CallListSendMessage;
  1033. break;
  1034. case CB_GETCOMBOBOXINFO:
  1035. return NtUserGetComboBoxInfo(hwnd, (PCOMBOBOXINFO)lParam);
  1036. case WM_MEASUREITEM:
  1037. case WM_DELETEITEM:
  1038. case WM_DRAWITEM:
  1039. case WM_COMPAREITEM:
  1040. return xxxCBMessageItemHandler(pcbox, message, (LPVOID)lParam);
  1041. case WM_NCCREATE:
  1042. /*
  1043. * wParam - Contains a handle to the window being created
  1044. * lParam - Points to the CREATESTRUCT data structure for the window.
  1045. */
  1046. return CBNcCreateHandler(pcbox, pwnd);
  1047. case WM_PARENTNOTIFY:
  1048. if (LOWORD(wParam) == WM_DESTROY) {
  1049. if ((HWND)lParam == HW(pcbox->spwndEdit)) {
  1050. pcbox->CBoxStyle &= ~SEDITABLE;
  1051. pcbox->fNoEdit = TRUE;
  1052. pcbox->spwndEdit = pwnd;
  1053. } else if ((HWND)lParam == HW(pcbox->spwndList)) {
  1054. pcbox->CBoxStyle &= ~SDROPPABLE;
  1055. pcbox->spwndList = NULL;
  1056. }
  1057. }
  1058. break;
  1059. case WM_UPDATEUISTATE:
  1060. /*
  1061. * Propagate the change to the list control, if any
  1062. */
  1063. UserAssert(pcbox->spwndList);
  1064. ThreadLock(pcbox->spwndList, &tlpwndList);
  1065. SendMessageWorker(pcbox->spwndList, WM_UPDATEUISTATE,
  1066. wParam, lParam, fAnsi);
  1067. ThreadUnlock(&tlpwndList);
  1068. goto CallDWP;
  1069. case WM_HELP:
  1070. {
  1071. LPHELPINFO lpHelpInfo;
  1072. /*
  1073. * Check if this message is from a child of this combo
  1074. */
  1075. if ((lpHelpInfo = (LPHELPINFO)lParam) != NULL &&
  1076. ((pcbox->spwndEdit && lpHelpInfo->iCtrlId == (SHORT)(PTR_TO_ID(pcbox->spwndEdit->spmenu))) ||
  1077. lpHelpInfo->iCtrlId == (SHORT)(PTR_TO_ID(pcbox->spwndList->spmenu)) )) {
  1078. /*
  1079. * Make it look like the WM_HELP is coming form this combo.
  1080. * Then DefWindowProcWorker will pass it up to our parent,
  1081. * who can do whatever he wants with it.
  1082. */
  1083. lpHelpInfo->iCtrlId = (SHORT)(PTR_TO_ID(pwnd->spmenu));
  1084. lpHelpInfo->hItemHandle = hwnd;
  1085. lpHelpInfo->dwContextId = GetContextHelpId(pwnd);
  1086. }
  1087. }
  1088. /*
  1089. * Fall through to DefWindowProc
  1090. */
  1091. default:
  1092. if (SYSMET(PENWINDOWS) &&
  1093. (message >= WM_PENWINFIRST && message <= WM_PENWINLAST))
  1094. goto CallEditSendMessage;
  1095. CallDWP:
  1096. return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
  1097. } /* switch (message) */
  1098. return TRUE;
  1099. /*
  1100. * The following forward messages off to the child controls.
  1101. */
  1102. CallEditSendMessage:
  1103. if (!pcbox->fNoEdit && pcbox->spwndEdit) {
  1104. /*
  1105. * pcbox->spwndEdit will be NULL if we haven't done WM_CREATE yet!
  1106. */
  1107. ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
  1108. lReturn = SendMessageWorker(pcbox->spwndEdit, message,
  1109. wParam, lParam, fAnsi);
  1110. ThreadUnlock(&tlpwndEdit);
  1111. }
  1112. else {
  1113. RIPERR0(ERROR_INVALID_COMBOBOX_MESSAGE, RIP_VERBOSE, "");
  1114. lReturn = CB_ERR;
  1115. }
  1116. return lReturn;
  1117. CallListSendMessage:
  1118. /*
  1119. * pcbox->spwndList will be NULL if we haven't done WM_CREATE yet!
  1120. */
  1121. UserAssert(pcbox->spwndList);
  1122. ThreadLock(pcbox->spwndList, &tlpwndList);
  1123. lReturn = SendMessageWorker(pcbox->spwndList, message,
  1124. wParam, lParam, fAnsi);
  1125. ThreadUnlock(&tlpwndList);
  1126. return lReturn;
  1127. } /* ComboBoxWndProcWorker */
  1128. /***************************************************************************\
  1129. \***************************************************************************/
  1130. LRESULT WINAPI ComboBoxWndProcA(
  1131. HWND hwnd,
  1132. UINT message,
  1133. WPARAM wParam,
  1134. LPARAM lParam)
  1135. {
  1136. PWND pwnd;
  1137. if ((pwnd = ValidateHwnd(hwnd)) == NULL) {
  1138. return (0L);
  1139. }
  1140. /*
  1141. * If the control is not interested in this message,
  1142. * pass it to DefWindowProc.
  1143. */
  1144. if (!FWINDOWMSG(message, FNID_COMBOBOX) &&
  1145. !(SYSMET(PENWINDOWS) &&
  1146. (message >= WM_PENWINFIRST && message <= WM_PENWINLAST)))
  1147. return DefWindowProcWorker(pwnd, message, wParam, lParam, TRUE);
  1148. return ComboBoxWndProcWorker(pwnd, message, wParam, lParam, TRUE);
  1149. }
  1150. LRESULT WINAPI ComboBoxWndProcW(
  1151. HWND hwnd,
  1152. UINT message,
  1153. WPARAM wParam,
  1154. LPARAM lParam)
  1155. {
  1156. PWND pwnd;
  1157. if ((pwnd = ValidateHwnd(hwnd)) == NULL) {
  1158. return (0L);
  1159. }
  1160. /*
  1161. * If the control is not interested in this message,
  1162. * pass it to DefWindowProc.
  1163. */
  1164. if (!FWINDOWMSG(message, FNID_COMBOBOX) &&
  1165. !(SYSMET(PENWINDOWS) &&
  1166. (message >= WM_PENWINFIRST && message <= WM_PENWINLAST)))
  1167. return DefWindowProcWorker(pwnd, message, wParam, lParam, FALSE);
  1168. return ComboBoxWndProcWorker(pwnd, message, wParam, lParam, FALSE);
  1169. }
  1170. /***************************************************************************\
  1171. * xxxCBMessageItemHandler
  1172. *
  1173. * Handles WM_DRAWITEM,WM_MEASUREITEM,WM_DELETEITEM,WM_COMPAREITEM
  1174. * messages from the listbox.
  1175. *
  1176. * History:
  1177. \***************************************************************************/
  1178. LRESULT xxxCBMessageItemHandler(
  1179. PCBOX pcbox,
  1180. UINT message,
  1181. LPVOID lpfoo) /* Actually can be any of the structs below */
  1182. {
  1183. LRESULT lRet;
  1184. TL tlpwndParent;
  1185. CheckLock(pcbox->spwnd);
  1186. /*
  1187. * Send the <foo>item message back to the application after changing some
  1188. * parameters to their combo box specific versions.
  1189. */
  1190. ((LPMEASUREITEMSTRUCT)lpfoo)->CtlType = ODT_COMBOBOX;
  1191. ((LPMEASUREITEMSTRUCT)lpfoo)->CtlID = PtrToUlong(pcbox->spwnd->spmenu);
  1192. if (message == WM_DRAWITEM)
  1193. ((LPDRAWITEMSTRUCT)lpfoo)->hwndItem = HWq(pcbox->spwnd);
  1194. else if (message == WM_DELETEITEM)
  1195. ((LPDELETEITEMSTRUCT)lpfoo)->hwndItem = HWq(pcbox->spwnd);
  1196. else if (message == WM_COMPAREITEM)
  1197. ((LPCOMPAREITEMSTRUCT)lpfoo)->hwndItem = HWq(pcbox->spwnd);
  1198. ThreadLock(pcbox->spwndParent, &tlpwndParent);
  1199. lRet = SendMessage(HW(pcbox->spwndParent), message,
  1200. (WPARAM)pcbox->spwnd->spmenu, (LPARAM)lpfoo);
  1201. ThreadUnlock(&tlpwndParent);
  1202. return lRet;
  1203. }
  1204. /***************************************************************************\
  1205. * xxxCBPaint
  1206. *
  1207. * History:
  1208. \***************************************************************************/
  1209. void xxxCBPaint(
  1210. PCBOX pcbox,
  1211. HDC hdc)
  1212. {
  1213. RECT rc;
  1214. UINT msg;
  1215. HBRUSH hbr;
  1216. CheckLock(pcbox->spwnd);
  1217. rc.left = rc.top = 0;
  1218. rc.right = pcbox->cxCombo;
  1219. rc.bottom = pcbox->cyCombo;
  1220. if (pcbox->f3DCombo)
  1221. DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
  1222. else
  1223. DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST | BF_FLAT | BF_MONO);
  1224. if (pcbox->buttonrc.left != 0) {
  1225. // Draw in the dropdown arrow button
  1226. DrawFrameControl(hdc, KPRECT_TO_PRECT(&pcbox->buttonrc), DFC_SCROLL,
  1227. DFCS_SCROLLCOMBOBOX |
  1228. (pcbox->fButtonPressed ? DFCS_PUSHED | DFCS_FLAT : 0) |
  1229. (TestWF(pcbox->spwnd, WFDISABLED) ? DFCS_INACTIVE : 0));
  1230. #ifdef COLOR_HOTTRACKING
  1231. (pcbox->fButtonHotTracked ? DFCS_HOT: 0)));
  1232. #endif // COLOR_HOTTRACKING
  1233. if (pcbox->fRightAlign )
  1234. rc.left = pcbox->buttonrc.right;
  1235. else
  1236. rc.right = pcbox->buttonrc.left;
  1237. }
  1238. // Erase the background behind the edit/static item. Since a combo
  1239. // is an edit field/list box hybrid, we use the same coloring
  1240. // conventions.
  1241. msg = WM_CTLCOLOREDIT;
  1242. if (TestWF(pcbox->spwnd, WFWIN40COMPAT)) {
  1243. if (TestWF(pcbox->spwnd, WFDISABLED) ||
  1244. (!pcbox->fNoEdit && pcbox->spwndEdit && TestWF(pcbox->spwndEdit, EFREADONLY)))
  1245. msg = WM_CTLCOLORSTATIC;
  1246. } else
  1247. msg = WM_CTLCOLORLISTBOX;
  1248. hbr = GetControlBrush(HWq(pcbox->spwnd), hdc, msg);
  1249. if (pcbox->fNoEdit)
  1250. xxxCBInternalUpdateEditWindow(pcbox, hdc);
  1251. else
  1252. FillRect(hdc, &rc, hbr);
  1253. }
  1254. /***************************************************************************\
  1255. * xxxCBCommandHandler
  1256. *
  1257. * Check the various notification codes from the controls and do the
  1258. * proper thing.
  1259. * always returns 0L
  1260. *
  1261. * History:
  1262. \***************************************************************************/
  1263. long xxxCBCommandHandler(
  1264. PCBOX pcbox,
  1265. DWORD wParam,
  1266. HWND hwndControl)
  1267. {
  1268. CheckLock(pcbox->spwnd);
  1269. /*
  1270. * Check the edit control notification codes. Note that currently, edit
  1271. * controls don't send EN_KILLFOCUS messages to the parent.
  1272. */
  1273. if (!pcbox->fNoEdit &&
  1274. SAMEWOWHANDLE(hwndControl, HWq(pcbox->spwndEdit))) {
  1275. /*
  1276. * Edit control notification codes
  1277. */
  1278. switch (HIWORD(wParam)) {
  1279. case EN_SETFOCUS:
  1280. if (!pcbox->fFocus) {
  1281. /*
  1282. * The edit control has the focus for the first time which means
  1283. * this is the first time the combo box has received the focus
  1284. * and the parent must be notified that we have the focus.
  1285. */
  1286. xxxCBGetFocusHelper(pcbox);
  1287. }
  1288. break;
  1289. case EN_CHANGE:
  1290. xxxCBNotifyParent(pcbox, CBN_EDITCHANGE);
  1291. xxxCBUpdateListBoxWindow(pcbox, FALSE);
  1292. break;
  1293. case EN_UPDATE:
  1294. xxxCBNotifyParent(pcbox, CBN_EDITUPDATE);
  1295. break;
  1296. case EN_ERRSPACE:
  1297. xxxCBNotifyParent(pcbox, CBN_ERRSPACE);
  1298. break;
  1299. }
  1300. }
  1301. /*
  1302. * Check listbox control notification codes
  1303. */
  1304. if (SAMEWOWHANDLE(hwndControl, HWq(pcbox->spwndList))) {
  1305. /*
  1306. * Listbox control notification codes
  1307. */
  1308. switch ((int)HIWORD(wParam)) {
  1309. case LBN_DBLCLK:
  1310. xxxCBNotifyParent(pcbox, CBN_DBLCLK);
  1311. break;
  1312. case LBN_ERRSPACE:
  1313. xxxCBNotifyParent(pcbox, CBN_ERRSPACE);
  1314. break;
  1315. case LBN_SELCHANGE:
  1316. case LBN_SELCANCEL:
  1317. if (!pcbox->fKeyboardSelInListBox) {
  1318. /*
  1319. * If the selchange is caused by the user keyboarding through,
  1320. * we don't want to hide the listbox.
  1321. */
  1322. if (!xxxCBHideListBoxWindow(pcbox, TRUE, TRUE))
  1323. return(0L);
  1324. } else {
  1325. pcbox->fKeyboardSelInListBox = FALSE;
  1326. }
  1327. xxxCBNotifyParent(pcbox, CBN_SELCHANGE);
  1328. xxxCBInternalUpdateEditWindow(pcbox, NULL);
  1329. /*
  1330. * If this combobox doesn't have an edit control, it needs to send
  1331. * this notification itself when the user is cycling through the
  1332. * items with the keyboard.
  1333. * See bug #54766.
  1334. */
  1335. if (pcbox->fNoEdit) {
  1336. NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, HWq(pcbox->spwnd), OBJID_CLIENT, INDEX_COMBOBOX);
  1337. }
  1338. break;
  1339. }
  1340. }
  1341. return 0L;
  1342. }
  1343. /***************************************************************************\
  1344. * xxxCBNotifyParent
  1345. *
  1346. * Sends the notification code to the parent of the combo box control
  1347. *
  1348. * History:
  1349. \***************************************************************************/
  1350. void xxxCBNotifyParent(
  1351. PCBOX pcbox,
  1352. short notificationCode)
  1353. {
  1354. PWND pwndParent; // Parent if it exists
  1355. TL tlpwndParent;
  1356. CheckLock(pcbox->spwnd);
  1357. if (pcbox->spwndParent)
  1358. pwndParent = pcbox->spwndParent;
  1359. else
  1360. pwndParent = pcbox->spwnd;
  1361. /*
  1362. * wParam contains Control ID and notification code.
  1363. * lParam contains Handle to window
  1364. */
  1365. ThreadLock(pwndParent, &tlpwndParent);
  1366. SendMessageWorker(pwndParent, WM_COMMAND,
  1367. MAKELONG(PTR_TO_ID(pcbox->spwnd->spmenu), notificationCode),
  1368. (LPARAM)HWq(pcbox->spwnd), FALSE);
  1369. ThreadUnlock(&tlpwndParent);
  1370. }
  1371. /***************************************************************************\
  1372. *
  1373. *
  1374. * Completes the text in the edit box with the closest match from the
  1375. * listbox. If a prefix match can't be found, the edit control text isn't
  1376. * updated. Assume a DROPDOWN style combo box.
  1377. *
  1378. *
  1379. * History:
  1380. \***************************************************************************/
  1381. void xxxCBCompleteEditWindow(
  1382. PCBOX pcbox)
  1383. {
  1384. int cchText;
  1385. int cchItemText;
  1386. int itemNumber;
  1387. LPWSTR pText;
  1388. TL tlpwndEdit;
  1389. TL tlpwndList;
  1390. CheckLock(pcbox->spwnd);
  1391. /*
  1392. * Firstly check the edit control.
  1393. */
  1394. if (pcbox->spwndEdit == NULL) {
  1395. return;
  1396. }
  1397. ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
  1398. ThreadLock(pcbox->spwndList, &tlpwndList);
  1399. /*
  1400. * +1 for null terminator
  1401. */
  1402. cchText = (int)SendMessageWorker(pcbox->spwndEdit, WM_GETTEXTLENGTH, 0, 0, FALSE);
  1403. if (cchText) {
  1404. cchText++;
  1405. if (!(pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR))))
  1406. goto Unlock;
  1407. /*
  1408. * We want to be sure to free the above allocated memory even if
  1409. * the client dies during callback (xxx) or some of the following
  1410. * window revalidation fails.
  1411. */
  1412. try {
  1413. SendMessageWorker(pcbox->spwndEdit, WM_GETTEXT, cchText, (LPARAM)pText, FALSE);
  1414. itemNumber = (int)SendMessageWorker(pcbox->spwndList,
  1415. LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pText, FALSE);
  1416. if (itemNumber == -1)
  1417. itemNumber = (int)SendMessageWorker(pcbox->spwndList,
  1418. LB_FINDSTRING, (WPARAM)-1, (LPARAM)pText, FALSE);
  1419. } finally {
  1420. UserLocalFree((HANDLE)pText);
  1421. }
  1422. if (itemNumber == -1) {
  1423. /*
  1424. * No close match. Blow off.
  1425. */
  1426. goto Unlock;
  1427. }
  1428. cchItemText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN,
  1429. itemNumber, 0, FALSE);
  1430. if (cchItemText) {
  1431. cchItemText++;
  1432. if (!(pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchItemText*sizeof(WCHAR))))
  1433. goto Unlock;
  1434. /*
  1435. * We want to be sure to free the above allocated memory even if
  1436. * the client dies during callback (xxx) or some of the following
  1437. * window revalidation fails.
  1438. */
  1439. try {
  1440. SendMessageWorker(pcbox->spwndList, LB_GETTEXT,
  1441. itemNumber, (LPARAM)pText, FALSE);
  1442. SendMessageWorker(pcbox->spwndEdit, WM_SETTEXT,
  1443. 0, (LPARAM)pText, FALSE);
  1444. } finally {
  1445. UserLocalFree((HANDLE)pText);
  1446. }
  1447. SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, MAXLONG, !!TestWF(pcbox->spwnd, WFANSIPROC));
  1448. }
  1449. }
  1450. Unlock:
  1451. ThreadUnlock(&tlpwndList);
  1452. ThreadUnlock(&tlpwndEdit);
  1453. }
  1454. /***************************************************************************\
  1455. * xxxCBHideListBoxWindow
  1456. *
  1457. * Hides the dropdown listbox window if it is a dropdown style.
  1458. *
  1459. * History:
  1460. \***************************************************************************/
  1461. BOOL xxxCBHideListBoxWindow(
  1462. PCBOX pcbox,
  1463. BOOL fNotifyParent,
  1464. BOOL fSelEndOK)
  1465. {
  1466. HWND hwnd = HWq(pcbox->spwnd);
  1467. HWND hwndList = HWq(pcbox->spwndList);
  1468. TL tlpwndList;
  1469. CheckLock(pcbox->spwnd);
  1470. // For 3.1+ apps, send CBN_SELENDOK to all types of comboboxes but only
  1471. // allow CBN_SELENDCANCEL to be sent for droppable comboboxes
  1472. if (fNotifyParent && TestWF(pcbox->spwnd, WFWIN31COMPAT) &&
  1473. ((pcbox->CBoxStyle & SDROPPABLE) || fSelEndOK)) {
  1474. if (fSelEndOK)
  1475. {
  1476. xxxCBNotifyParent(pcbox, CBN_SELENDOK);
  1477. }
  1478. else
  1479. {
  1480. xxxCBNotifyParent(pcbox, CBN_SELENDCANCEL);
  1481. }
  1482. if (!IsWindow(hwnd))
  1483. return(FALSE);
  1484. }
  1485. /*
  1486. * return, we don't hide simple combo boxes.
  1487. */
  1488. if (!(pcbox->CBoxStyle & SDROPPABLE)) {
  1489. return TRUE;
  1490. }
  1491. /*
  1492. * Send a faked buttonup message to the listbox so that it can release
  1493. * the capture and all.
  1494. */
  1495. ThreadLock(pcbox->spwndList, &tlpwndList);
  1496. SendMessageWorker(pcbox->spwndList, LBCB_ENDTRACK, fSelEndOK, 0, FALSE);
  1497. if (pcbox->fLBoxVisible) {
  1498. WORD swpFlags = SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE;
  1499. if (!TestWF(pcbox->spwnd, WFWIN31COMPAT))
  1500. swpFlags |= SWP_FRAMECHANGED;
  1501. pcbox->fLBoxVisible = FALSE;
  1502. /*
  1503. * Hide the listbox window
  1504. */
  1505. NtUserShowWindow(hwndList, SW_HIDE);
  1506. //
  1507. // Invalidate the item area now since SWP() might update stuff.
  1508. // Since the combo is CS_VREDRAW/CS_HREDRAW, a size change will
  1509. // redraw the whole thing, including the item rect. But if it
  1510. // isn't changing size, we still want to redraw the item anyway
  1511. // to show focus/selection.
  1512. //
  1513. if (!(pcbox->CBoxStyle & SEDITABLE))
  1514. NtUserInvalidateRect(hwnd, KPRECT_TO_PRECT(&pcbox->editrc), TRUE);
  1515. NtUserSetWindowPos(hwnd, HWND_TOP, 0, 0,
  1516. pcbox->cxCombo, pcbox->cyCombo, swpFlags);
  1517. // In case size didn't change
  1518. UpdateWindow(hwnd);
  1519. if (pcbox->CBoxStyle & SEDITABLE) {
  1520. xxxCBCompleteEditWindow(pcbox);
  1521. }
  1522. if (fNotifyParent) {
  1523. /*
  1524. * Notify parent we will be popping up the combo box.
  1525. */
  1526. xxxCBNotifyParent(pcbox, CBN_CLOSEUP);
  1527. if (!IsWindow(hwnd))
  1528. return(FALSE);
  1529. }
  1530. }
  1531. ThreadUnlock(&tlpwndList);
  1532. return(TRUE);
  1533. }
  1534. /***************************************************************************\
  1535. * xxxCBShowListBoxWindow
  1536. *
  1537. * Lowers the dropdown listbox window.
  1538. *
  1539. * History:
  1540. \***************************************************************************/
  1541. void xxxCBShowListBoxWindow(
  1542. PCBOX pcbox, BOOL fTrack)
  1543. {
  1544. RECT editrc;
  1545. int itemNumber;
  1546. int iHeight;
  1547. int yTop;
  1548. DWORD dwMult;
  1549. int cyItem;
  1550. HWND hwnd = HWq(pcbox->spwnd);
  1551. HWND hwndList = HWq(pcbox->spwndList);
  1552. BOOL fAnimPos;
  1553. TL tlpwndList;
  1554. PMONITOR pMonitor;
  1555. //
  1556. // THIS FUNCTION IS ONLY CALLED FOR DROPPABLE LIST COMBOBOXES
  1557. //
  1558. UserAssert(pcbox->CBoxStyle & SDROPPABLE);
  1559. CheckLock(pcbox->spwnd);
  1560. ThreadLock(pcbox->spwndList, &tlpwndList);
  1561. /*
  1562. * Notify parent we will be dropping down the combo box.
  1563. */
  1564. xxxCBNotifyParent(pcbox, CBN_DROPDOWN);
  1565. /*
  1566. * Invalidate the button rect so that the depressed arrow is drawn.
  1567. */
  1568. NtUserInvalidateRect(hwnd, KPRECT_TO_PRECT(&pcbox->buttonrc), TRUE);
  1569. pcbox->fLBoxVisible = TRUE;
  1570. if (pcbox->CBoxStyle == SDROPDOWN) {
  1571. /*
  1572. * If an item in the listbox matches the text in the edit control,
  1573. * scroll it to the top of the listbox. Select the item only if the
  1574. * mouse button isn't down otherwise we will select the item when the
  1575. * mouse button goes up.
  1576. */
  1577. xxxCBUpdateListBoxWindow(pcbox, !pcbox->fMouseDown);
  1578. if (!pcbox->fMouseDown)
  1579. xxxCBCompleteEditWindow(pcbox);
  1580. } else {
  1581. /*
  1582. * Scroll the currently selected item to the top of the listbox.
  1583. */
  1584. itemNumber = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL,
  1585. 0, 0, FALSE);
  1586. if (itemNumber == -1) {
  1587. itemNumber = 0;
  1588. }
  1589. SendMessageWorker(pcbox->spwndList, LB_SETTOPINDEX, itemNumber, 0, FALSE);
  1590. SendMessageWorker(pcbox->spwndList, LBCB_CARETON, 0, 0, FALSE);
  1591. /*
  1592. * We need to invalidate the edit rect so that the focus frame/invert
  1593. * will be turned off when the listbox is visible. Tandy wants this for
  1594. * his typical reasons...
  1595. */
  1596. NtUserInvalidateRect(hwnd, KPRECT_TO_PRECT(&pcbox->editrc), TRUE);
  1597. }
  1598. //
  1599. // Figure out where to position the dropdown listbox. We want it just
  1600. // touching the edge around the edit rectangle. Note that since the
  1601. // listbox is a popup, we need the position in screen coordinates.
  1602. //
  1603. // We want the dropdown to pop below or above the combo
  1604. // Get screen coords
  1605. editrc.left = pcbox->spwnd->rcWindow.left;
  1606. editrc.top = pcbox->spwnd->rcWindow.top;
  1607. editrc.right = pcbox->spwnd->rcWindow.left + pcbox->cxCombo;
  1608. editrc.bottom = pcbox->spwnd->rcWindow.top + pcbox->cyCombo;
  1609. // List area
  1610. cyItem = (int)SendMessageWorker(pcbox->spwndList, LB_GETITEMHEIGHT, 0, 0, FALSE);
  1611. if (cyItem == 0) {
  1612. // Make sure that it's not 0
  1613. RIPMSG0( RIP_WARNING, "LB_GETITEMHEIGHT is returning 0\n" );
  1614. cyItem = gpsi->cySysFontChar;
  1615. }
  1616. // we shoulda' just been able to use cyDrop here, but thanks to VB's need
  1617. // to do things their OWN SPECIAL WAY, we have to keep monitoring the size
  1618. // of the listbox 'cause VB changes it directly (jeffbog 03/21/94)
  1619. iHeight = max(pcbox->cyDrop, pcbox->spwndList->rcWindow.bottom -
  1620. pcbox->spwndList->rcWindow.top);
  1621. if (dwMult = (DWORD)SendMessageWorker(pcbox->spwndList, LB_GETCOUNT, 0, 0, FALSE)) {
  1622. dwMult = (DWORD)(LOWORD(dwMult) * cyItem);
  1623. dwMult += SYSMET(CYEDGE);
  1624. if (dwMult < 0x7FFF)
  1625. iHeight = min(LOWORD(dwMult), iHeight);
  1626. }
  1627. if (!TestWF(pcbox->spwnd, CBFNOINTEGRALHEIGHT)) {
  1628. UserAssert(cyItem);
  1629. iHeight = ((iHeight - SYSMET(CYEDGE)) / cyItem) * cyItem + SYSMET(CYEDGE);
  1630. }
  1631. //
  1632. // Other 1/2 of old app combo fix. Make dropdown overlap combo window
  1633. // a little. That way we can have a chance of invalidating the overlap
  1634. // and causing a repaint to help out Publisher 2.0's toolbar combos.
  1635. // See comments for PressButton() above.
  1636. //
  1637. pMonitor = _MonitorFromWindow(pcbox->spwnd, MONITOR_DEFAULTTOPRIMARY);
  1638. if (editrc.bottom + iHeight <= pMonitor->rcMonitor.bottom) {
  1639. yTop = editrc.bottom;
  1640. if (!pcbox->f3DCombo)
  1641. yTop -= SYSMET(CYBORDER);
  1642. fAnimPos = TRUE;
  1643. } else {
  1644. yTop = max(editrc.top - iHeight, pMonitor->rcMonitor.top);
  1645. if (!pcbox->f3DCombo)
  1646. yTop += SYSMET(CYBORDER);
  1647. fAnimPos = FALSE;
  1648. }
  1649. if ( ! TestWF( pcbox->spwnd, WFWIN40COMPAT) )
  1650. {
  1651. // fix for Winword B#7504, Combo-ListBox text gets
  1652. // truncated by a small width, this is do to us
  1653. // now setting size here in SetWindowPos, rather than
  1654. // earlier where we did this in Win3.1
  1655. if ( (pcbox->spwndList->rcWindow.right - pcbox->spwndList->rcWindow.left ) >
  1656. pcbox->cxDrop )
  1657. pcbox->cxDrop = pcbox->spwndList->rcWindow.right - pcbox->spwndList->rcWindow.left;
  1658. }
  1659. NtUserSetWindowPos(hwndList, HWND_TOPMOST, editrc.left,
  1660. yTop, max(pcbox->cxDrop, pcbox->cxCombo), iHeight, SWP_NOACTIVATE);
  1661. /*
  1662. * Get any drawing in the combo box window out of the way so it doesn't
  1663. * invalidate any of the SPB underneath the list window.
  1664. */
  1665. UpdateWindow(hwnd);
  1666. if (!(TEST_EffectPUSIF(PUSIF_COMBOBOXANIMATION))
  1667. || (GetAppCompatFlags2(VER40) & GACF2_ANIMATIONOFF)) {
  1668. NtUserShowWindow(hwndList, SW_SHOWNA);
  1669. } else {
  1670. AnimateWindow(hwndList, CMS_QANIMATION, (fAnimPos ? AW_VER_POSITIVE :
  1671. AW_VER_NEGATIVE) | AW_SLIDE);
  1672. }
  1673. #ifdef LATER
  1674. //
  1675. // we don't have sys modal windows.
  1676. //
  1677. if (pwndSysModal) {
  1678. /*
  1679. * If this combo is in a system modal dialog box, we need to explicitly
  1680. * call update window otherwise we won't automatically send paint
  1681. * messages to the toplevel listbox window. This is especially
  1682. * noticeable in the File Open/Save sys modal dlgs which are put up at
  1683. * ExitWindows time.
  1684. */
  1685. UpdateWindow(hwndList);
  1686. }
  1687. #endif
  1688. /*
  1689. * Restart search buffer from first char
  1690. */
  1691. {
  1692. PLBIV plb = ((PLBWND)pcbox->spwndList)->pLBIV;
  1693. if ((plb != NULL) && (plb != (PLBIV)-1)) {
  1694. plb->iTypeSearch = 0;
  1695. }
  1696. }
  1697. if (fTrack && TestWF(pcbox->spwnd, WFWIN40COMPAT))
  1698. SendMessageWorker(pcbox->spwndList, LBCB_STARTTRACK, FALSE, 0, FALSE);
  1699. ThreadUnlock(&tlpwndList);
  1700. }
  1701. /***************************************************************************\
  1702. * xxxCBInternalUpdateEditWindow
  1703. *
  1704. * Updates the editcontrol/statictext window so that it contains the text
  1705. * given by the current selection in the listbox. If the listbox has no
  1706. * selection (ie. -1), then we erase all the text in the editcontrol.
  1707. *
  1708. * hdcPaint is from WM_PAINT messages Begin/End Paint hdc. If null, we should
  1709. * get our own dc.
  1710. *
  1711. * History:
  1712. \***************************************************************************/
  1713. void xxxCBInternalUpdateEditWindow(
  1714. PCBOX pcbox,
  1715. HDC hdcPaint)
  1716. {
  1717. int cchText = 0;
  1718. LPWSTR pText = NULL;
  1719. int sItem;
  1720. HDC hdc;
  1721. UINT msg;
  1722. HBRUSH hbrSave;
  1723. HBRUSH hbrControl;
  1724. HANDLE hOldFont;
  1725. DRAWITEMSTRUCT dis;
  1726. RECT rc;
  1727. HWND hwnd = HWq(pcbox->spwnd);
  1728. TL tlpwndList;
  1729. TL tlpwndEdit;
  1730. TL tlpwndParent;
  1731. CheckLock(pcbox->spwnd);
  1732. /* This check is also commented out in Win3.1 and Win95 */
  1733. // if (!TestWF(pcbox->spwnd, WFVISIBLE)) {
  1734. // return;
  1735. // }
  1736. ThreadLock(pcbox->spwndParent, &tlpwndParent);
  1737. ThreadLock(pcbox->spwndList, &tlpwndList);
  1738. ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
  1739. sItem = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL, 0, 0, FALSE);
  1740. /*
  1741. * This 'try-finally' block ensures that the allocated 'pText' will
  1742. * be freed no matter how this routine is exited.
  1743. */
  1744. try {
  1745. if (sItem != -1) {
  1746. cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN,
  1747. (DWORD)sItem, 0, FALSE);
  1748. if ((pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, (cchText+1) * sizeof(WCHAR)))) {
  1749. cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXT,
  1750. (DWORD)sItem, (LPARAM)pText, FALSE);
  1751. }
  1752. }
  1753. if (!pcbox->fNoEdit) {
  1754. if (pcbox->spwndEdit) {
  1755. if (TestWF(pcbox->spwnd, CBFHASSTRINGS))
  1756. SetWindowText(HWq(pcbox->spwndEdit), pText ? pText : TEXT(""));
  1757. if (pcbox->fFocus) {
  1758. /*
  1759. * Only hilite the text if we have the focus.
  1760. */
  1761. SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, MAXLONG, !!TestWF(pcbox->spwnd, WFANSIPROC));
  1762. }
  1763. }
  1764. } else if (IsComboVisible(pcbox)) {
  1765. if (hdcPaint) {
  1766. hdc = hdcPaint;
  1767. } else {
  1768. hdc = NtUserGetDC(hwnd);
  1769. }
  1770. SetBkMode(hdc, OPAQUE);
  1771. if (TestWF(pcbox->spwnd, WFWIN40COMPAT)) {
  1772. if (TestWF(pcbox->spwnd, WFDISABLED))
  1773. msg = WM_CTLCOLORSTATIC;
  1774. else
  1775. msg = WM_CTLCOLOREDIT;
  1776. } else
  1777. msg = WM_CTLCOLORLISTBOX;
  1778. hbrControl = GetControlBrush(hwnd, hdc, msg);
  1779. hbrSave = SelectObject(hdc, hbrControl);
  1780. CopyInflateRect(&rc, KPRECT_TO_PRECT(&pcbox->editrc), SYSMET(CXBORDER), SYSMET(CYBORDER));
  1781. PatBlt(hdc, rc.left, rc.top, rc.right - rc.left,
  1782. rc.bottom - rc.top, PATCOPY);
  1783. InflateRect(&rc, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
  1784. if (pcbox->fFocus && !pcbox->fLBoxVisible) {
  1785. //
  1786. // Fill in the selected area
  1787. //
  1788. // only do the FillRect if we know its not
  1789. // ownerdraw item, otherwise we mess up people up
  1790. // BUT: for Compat's sake we still do this for Win 3.1 guys
  1791. if (!TestWF( pcbox->spwnd, WFWIN40COMPAT) || !pcbox->OwnerDraw)
  1792. FillRect(hdc, &rc, SYSHBR(HIGHLIGHT));
  1793. SetBkColor(hdc, SYSRGB(HIGHLIGHT));
  1794. SetTextColor(hdc, SYSRGB(HIGHLIGHTTEXT));
  1795. } else if (TestWF(pcbox->spwnd, WFDISABLED) && !pcbox->OwnerDraw) {
  1796. if ((COLORREF)SYSRGB(GRAYTEXT) != GetBkColor(hdc))
  1797. SetTextColor(hdc, SYSRGB(GRAYTEXT));
  1798. }
  1799. if (pcbox->hFont != NULL)
  1800. hOldFont = SelectObject(hdc, pcbox->hFont);
  1801. if (pcbox->OwnerDraw) {
  1802. /*
  1803. * Let the app draw the stuff in the static text box.
  1804. */
  1805. dis.CtlType = ODT_COMBOBOX;
  1806. dis.CtlID = PtrToUlong(pcbox->spwnd->spmenu);
  1807. dis.itemID = sItem;
  1808. dis.itemAction = ODA_DRAWENTIRE;
  1809. dis.itemState = (UINT)
  1810. ((pcbox->fFocus && !pcbox->fLBoxVisible ? ODS_SELECTED : 0) |
  1811. (TestWF(pcbox->spwnd, WFDISABLED) ? ODS_DISABLED : 0) |
  1812. (pcbox->fFocus && !pcbox->fLBoxVisible ? ODS_FOCUS : 0) |
  1813. (TestWF(pcbox->spwnd, WFWIN40COMPAT) ? ODS_COMBOBOXEDIT : 0) |
  1814. (TestWF(pcbox->spwnd, WEFPUIFOCUSHIDDEN) ? ODS_NOFOCUSRECT : 0) |
  1815. (TestWF(pcbox->spwnd, WEFPUIACCELHIDDEN) ? ODS_NOACCEL : 0));
  1816. dis.hwndItem = hwnd;
  1817. dis.hDC = hdc;
  1818. CopyRect(&dis.rcItem, &rc);
  1819. // Don't let ownerdraw dudes draw outside of the combo client
  1820. // bounds.
  1821. IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
  1822. dis.itemData = (ULONG_PTR)SendMessageWorker(pcbox->spwndList,
  1823. LB_GETITEMDATA, (UINT)sItem, 0, FALSE);
  1824. SendMessage(HW(pcbox->spwndParent), WM_DRAWITEM, dis.CtlID,
  1825. (LPARAM)&dis);
  1826. } else {
  1827. /*
  1828. * Start the text one pixel within the rect so that we leave a
  1829. * nice hilite border around the text.
  1830. */
  1831. int x ;
  1832. UINT align ;
  1833. if (pcbox->fRightAlign ) {
  1834. align = TA_RIGHT;
  1835. x = rc.right - SYSMET(CXBORDER);
  1836. } else {
  1837. x = rc.left + SYSMET(CXBORDER);
  1838. align = 0;
  1839. }
  1840. if (pcbox->fRtoLReading )
  1841. align |= TA_RTLREADING;
  1842. if (align)
  1843. SetTextAlign(hdc, GetTextAlign(hdc) | align);
  1844. // Draw the text, leaving a gap on the left & top for selection.
  1845. ExtTextOut(hdc, x, rc.top + SYSMET(CYBORDER), ETO_CLIPPED | ETO_OPAQUE,
  1846. &rc, pText ? pText : TEXT(""), cchText, NULL);
  1847. if (pcbox->fFocus && !pcbox->fLBoxVisible) {
  1848. if (!TestWF(pcbox->spwnd, WEFPUIFOCUSHIDDEN)) {
  1849. DrawFocusRect(hdc, &rc);
  1850. }
  1851. }
  1852. }
  1853. if (pcbox->hFont && hOldFont) {
  1854. SelectObject(hdc, hOldFont);
  1855. }
  1856. if (hbrSave) {
  1857. SelectObject(hdc, hbrSave);
  1858. }
  1859. if (!hdcPaint) {
  1860. NtUserReleaseDC(hwnd, hdc);
  1861. }
  1862. }
  1863. } finally {
  1864. if (pText != NULL)
  1865. UserLocalFree((HANDLE)pText);
  1866. }
  1867. ThreadUnlock(&tlpwndEdit);
  1868. ThreadUnlock(&tlpwndList);
  1869. ThreadUnlock(&tlpwndParent);
  1870. }
  1871. /***************************************************************************\
  1872. * xxxCBInvertStaticWindow
  1873. *
  1874. * Inverts the static text/picture window associated with the combo
  1875. * box. Gets its own hdc, if the one given is null.
  1876. *
  1877. * History:
  1878. \***************************************************************************/
  1879. void xxxCBInvertStaticWindow(
  1880. PCBOX pcbox,
  1881. BOOL fNewSelectionState, /* True if inverted else false */
  1882. HDC hdc)
  1883. {
  1884. BOOL focusSave = pcbox->fFocus;
  1885. CheckLock(pcbox->spwnd);
  1886. pcbox->fFocus = (UINT)fNewSelectionState;
  1887. xxxCBInternalUpdateEditWindow(pcbox, hdc);
  1888. pcbox->fFocus = (UINT)focusSave;
  1889. }
  1890. /***************************************************************************\
  1891. * xxxCBUpdateListBoxWindow
  1892. *
  1893. * matches the text in the editcontrol. If fSelectionAlso is false, then we
  1894. * unselect the current listbox selection and just move the caret to the item
  1895. * which is the closest match to the text in the editcontrol.
  1896. *
  1897. * History:
  1898. \***************************************************************************/
  1899. void xxxCBUpdateListBoxWindow(
  1900. PCBOX pcbox,
  1901. BOOL fSelectionAlso)
  1902. {
  1903. int cchText;
  1904. int sItem, sSel;
  1905. LPWSTR pText = NULL;
  1906. TL tlpwndEdit;
  1907. TL tlpwndList;
  1908. if (pcbox->spwndEdit == NULL) {
  1909. return;
  1910. }
  1911. CheckLock(pcbox->spwnd);
  1912. ThreadLock(pcbox->spwndList, &tlpwndList);
  1913. ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
  1914. /*
  1915. * +1 for null terminator
  1916. */
  1917. cchText = (int)SendMessageWorker(pcbox->spwndEdit, WM_GETTEXTLENGTH, 0, 0, FALSE);
  1918. if (cchText) {
  1919. cchText++;
  1920. pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR));
  1921. if (pText != NULL) {
  1922. try {
  1923. SendMessageWorker(pcbox->spwndEdit, WM_GETTEXT, cchText, (LPARAM)pText, FALSE);
  1924. sItem = (int)SendMessageWorker(pcbox->spwndList, LB_FINDSTRING,
  1925. (WPARAM)-1L, (LPARAM)pText, FALSE);
  1926. } finally {
  1927. UserLocalFree((HANDLE)pText);
  1928. }
  1929. }
  1930. }
  1931. else
  1932. sItem = -1;
  1933. if (fSelectionAlso) {
  1934. sSel = sItem;
  1935. } else {
  1936. sSel = -1;
  1937. }
  1938. if (sItem == -1)
  1939. {
  1940. sItem = 0;
  1941. //
  1942. // Old apps: w/ editable combos, selected 1st item in list even if
  1943. // it didn't match text in edit field. This is not desirable
  1944. // behavior for 4.0 dudes esp. with cancel allowed. Reason:
  1945. // (1) User types in text that doesn't match list choices
  1946. // (2) User drops combo
  1947. // (3) User pops combo back up
  1948. // (4) User presses OK in dialog that does stuff w/ combo
  1949. // contents.
  1950. // In 3.1, when the combo dropped, we'd select the 1st item anyway.
  1951. // So the last CBN_SELCHANGE the owner got would be 0--which is
  1952. // bogus because it really should be -1. In fact if you type anything
  1953. // into the combo afterwards it will reset itself to -1.
  1954. //
  1955. // 4.0 dudes won't get this bogus 0 selection.
  1956. //
  1957. if (fSelectionAlso && !TestWF(pcbox->spwnd, WFWIN40COMPAT))
  1958. sSel = 0;
  1959. }
  1960. SendMessageWorker(pcbox->spwndList, LB_SETCURSEL, (DWORD)sSel, 0, FALSE);
  1961. SendMessageWorker(pcbox->spwndList, LB_SETCARETINDEX, (DWORD)sItem, 0, FALSE);
  1962. SendMessageWorker(pcbox->spwndList, LB_SETTOPINDEX, (DWORD)sItem, 0, FALSE);
  1963. ThreadUnlock(&tlpwndEdit);
  1964. ThreadUnlock(&tlpwndList);
  1965. }
  1966. /***************************************************************************\
  1967. * xxxCBGetFocusHelper
  1968. *
  1969. * Handles getting the focus for the combo box
  1970. *
  1971. * History:
  1972. \***************************************************************************/
  1973. void xxxCBGetFocusHelper(
  1974. PCBOX pcbox)
  1975. {
  1976. TL tlpwndList;
  1977. TL tlpwndEdit;
  1978. CheckLock(pcbox->spwnd);
  1979. if (pcbox->fFocus)
  1980. return;
  1981. ThreadLock(pcbox->spwndList, &tlpwndList);
  1982. ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
  1983. /*
  1984. * The combo box has gotten the focus for the first time.
  1985. */
  1986. /*
  1987. * First turn on the listbox caret
  1988. */
  1989. if (pcbox->CBoxStyle == SDROPDOWNLIST)
  1990. SendMessageWorker(pcbox->spwndList, LBCB_CARETON, 0, 0, FALSE);
  1991. /*
  1992. * and select all the text in the editcontrol or static text rectangle.
  1993. */
  1994. if (pcbox->fNoEdit) {
  1995. /*
  1996. * Invert the static text rectangle
  1997. */
  1998. xxxCBInvertStaticWindow(pcbox, TRUE, (HDC)NULL);
  1999. } else if (pcbox->spwndEdit) {
  2000. UserAssert(pcbox->spwnd);
  2001. SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, MAXLONG, !!TestWF(pcbox->spwnd, WFANSIPROC));
  2002. }
  2003. pcbox->fFocus = TRUE;
  2004. /*
  2005. * Notify the parent we have the focus
  2006. */
  2007. xxxCBNotifyParent(pcbox, CBN_SETFOCUS);
  2008. ThreadUnlock(&tlpwndEdit);
  2009. ThreadUnlock(&tlpwndList);
  2010. }
  2011. /***************************************************************************\
  2012. * xxxCBKillFocusHelper
  2013. *
  2014. * Handles losing the focus for the combo box.
  2015. *
  2016. * History:
  2017. \***************************************************************************/
  2018. void xxxCBKillFocusHelper(
  2019. PCBOX pcbox)
  2020. {
  2021. TL tlpwndList;
  2022. TL tlpwndEdit;
  2023. CheckLock(pcbox->spwnd);
  2024. if (!pcbox->fFocus || pcbox->spwndList == NULL)
  2025. return;
  2026. ThreadLock(pcbox->spwndList, &tlpwndList);
  2027. ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
  2028. /*
  2029. * The combo box is losing the focus. Send buttonup clicks so that
  2030. * things release the mouse capture if they have it... If the
  2031. * pwndListBox is null, don't do anything. This occurs if the combo box
  2032. * is destroyed while it has the focus.
  2033. */
  2034. SendMessageWorker(pcbox->spwnd, WM_LBUTTONUP, 0L, 0xFFFFFFFFL, FALSE);
  2035. if (!xxxCBHideListBoxWindow(pcbox, TRUE, FALSE))
  2036. return;
  2037. /*
  2038. * Turn off the listbox caret
  2039. */
  2040. if (pcbox->CBoxStyle == SDROPDOWNLIST)
  2041. SendMessageWorker(pcbox->spwndList, LBCB_CARETOFF, 0, 0, FALSE);
  2042. if (pcbox->fNoEdit) {
  2043. /*
  2044. * Invert the static text rectangle
  2045. */
  2046. xxxCBInvertStaticWindow(pcbox, FALSE, (HDC)NULL);
  2047. } else if (pcbox->spwndEdit) {
  2048. SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, 0, !!TestWF(pcbox->spwnd, WFANSIPROC));
  2049. }
  2050. pcbox->fFocus = FALSE;
  2051. xxxCBNotifyParent(pcbox, CBN_KILLFOCUS);
  2052. ThreadUnlock(&tlpwndEdit);
  2053. ThreadUnlock(&tlpwndList);
  2054. }
  2055. /***************************************************************************\
  2056. * xxxCBGetTextLengthHelper
  2057. *
  2058. * For the combo box without an edit control, returns size of current selected
  2059. * item
  2060. *
  2061. * History:
  2062. \***************************************************************************/
  2063. LONG xxxCBGetTextLengthHelper(
  2064. PCBOX pcbox,
  2065. BOOL fAnsi)
  2066. {
  2067. int item;
  2068. int cchText;
  2069. TL tlpwndList;
  2070. ThreadLock(pcbox->spwndList, &tlpwndList);
  2071. item = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL, 0, 0, fAnsi);
  2072. if (item == LB_ERR) {
  2073. /*
  2074. * No selection so no text.
  2075. */
  2076. cchText = 0;
  2077. } else {
  2078. cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN,
  2079. item, 0, fAnsi);
  2080. }
  2081. ThreadUnlock(&tlpwndList);
  2082. return cchText;
  2083. }
  2084. /***************************************************************************\
  2085. * xxxCBGetTextHelper
  2086. *
  2087. * For the combo box without an edit control, copies cbString bytes of the
  2088. * string in the static text box to the buffer given by pString.
  2089. *
  2090. * History:
  2091. \***************************************************************************/
  2092. LONG xxxCBGetTextHelper(
  2093. PCBOX pcbox,
  2094. int cchString,
  2095. LPWSTR pString,
  2096. BOOL fAnsi)
  2097. {
  2098. int item;
  2099. int cchText;
  2100. LPWSTR pText;
  2101. DWORD dw;
  2102. TL tlpwndList;
  2103. CheckLock(pcbox->spwnd);
  2104. if (!cchString || !pString)
  2105. return 0;
  2106. /*
  2107. * Null the buffer to be nice.
  2108. */
  2109. if (fAnsi) {
  2110. *((LPSTR)pString) = 0;
  2111. } else {
  2112. *((LPWSTR)pString) = 0;
  2113. }
  2114. ThreadLock(pcbox->spwndList, &tlpwndList);
  2115. item = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL, 0, 0, fAnsi);
  2116. if (item == LB_ERR) {
  2117. /*
  2118. * No selection so no text.
  2119. */
  2120. ThreadUnlock(&tlpwndList);
  2121. return 0;
  2122. }
  2123. cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN, item, 0, fAnsi);
  2124. cchText++;
  2125. if ((cchText <= cchString) ||
  2126. (!TestWF(pcbox->spwnd, WFWIN31COMPAT) && cchString == 2)) {
  2127. /*
  2128. * Just do the copy if the given buffer size is large enough to hold
  2129. * everything. Or if old 3.0 app. (Norton used to pass 2 & expect 3
  2130. * chars including the \0 in 3.0; Bug #7018 win31: vatsanp)
  2131. */
  2132. dw = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXT, item,
  2133. (LPARAM)pString, fAnsi);
  2134. ThreadUnlock(&tlpwndList);
  2135. return dw;
  2136. }
  2137. if (!(pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR)))) {
  2138. /*
  2139. * Bail. Not enough memory to chop up the text.
  2140. */
  2141. ThreadUnlock(&tlpwndList);
  2142. return 0;
  2143. }
  2144. try {
  2145. SendMessageWorker(pcbox->spwndList, LB_GETTEXT, item, (LPARAM)pText, fAnsi);
  2146. if (fAnsi) {
  2147. RtlCopyMemory((PBYTE)pString, (PBYTE)pText, cchString);
  2148. ((LPSTR)pString)[cchString - 1] = 0;
  2149. } else {
  2150. RtlCopyMemory((PBYTE)pString, (PBYTE)pText, cchString * sizeof(WCHAR));
  2151. ((LPWSTR)pString)[cchString - 1] = 0;
  2152. }
  2153. } finally {
  2154. UserLocalFree((HANDLE)pText);
  2155. }
  2156. ThreadUnlock(&tlpwndList);
  2157. return cchString;
  2158. }