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.

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