Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2909 lines
76 KiB

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