Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

combo.c

Go to the documentation of this file.
00001 /**************************** Module Header ********************************\ 00002 * Module Name: combo.c 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * The WndProc for combo boxes and other often used combo routines 00007 * 00008 * History: 00009 * ??-???-???? ?????? Ported from Win 3.0 sources 00010 * 01-Feb-1991 mikeke Added Revalidation code 00011 \***************************************************************************/ 00012 00013 #include "precomp.h" 00014 #pragma hdrstop 00015 00016 LOOKASIDE ComboboxLookaside; 00017 00018 BOOL NtUserTrackMouseEvent(TRACKMOUSEEVENT *ptme); 00019 LONG xxxCBGetTextLengthHelper(PCBOX pcbox, BOOL fAnsi); 00020 LONG xxxCBGetTextHelper(PCBOX pcbox, int len, LPWSTR lpstr, BOOL fAnsi); 00021 00022 /***************************************************************************\ 00023 * 00024 * PressButton() 00025 * 00026 * Pops combobox button back up. 00027 * 00028 \***************************************************************************/ 00029 void xxxPressButton(PCBOX pcbox, BOOL fPress) 00030 { 00031 // 00032 // Publisher relies on getting a WM_PAINT message after the combo list 00033 // pops back up. On a WM_PAINT they change the focus, which causes 00034 // toolbar combos to send CBN_SELENDCANCEL notifications. On this 00035 // notification they apply the font/pt size change you made to the 00036 // selection. 00037 // 00038 // This happened in 3.1 because the dropdown list overlapped the button 00039 // on the bottom or top by a pixel. Since we'd end up painting under 00040 // the list SPB, when it went away USER would reinvalidate the dirty 00041 // area. This would cause a paint message. 00042 // 00043 // In 4.0, this doesn't happen because the dropdown doesn't overlap. So 00044 // we need to make sure Publisher gets a WM_PAINT anyway. We do this 00045 // by changing where the dropdown shows up for 3.x apps 00046 // 00047 // 00048 00049 if ((pcbox->fButtonPressed != 0) != (fPress != 0)) { 00050 00051 HWND hwnd = HWq(pcbox->spwnd); 00052 00053 pcbox->fButtonPressed = (fPress != 0); 00054 if (pcbox->f3DCombo) 00055 NtUserInvalidateRect(hwnd, &pcbox->buttonrc, TRUE); 00056 else 00057 { 00058 RECT rc; 00059 00060 CopyRect(&rc, &pcbox->buttonrc); 00061 InflateRect(&rc, 0, SYSMET(CYEDGE)); 00062 NtUserInvalidateRect(hwnd, &rc, TRUE); 00063 } 00064 UpdateWindow(hwnd); 00065 00066 if (FWINABLE()) { 00067 NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEX_COMBOBOX_BUTTON); 00068 } 00069 } 00070 } 00071 00072 /***************************************************************************\ 00073 * HotTrack 00074 * 00075 * If we're not already hot-tracking and the mouse is over the combobox, 00076 * turn on hot-tracking and invalidate the drop-down button. 00077 * 00078 \***************************************************************************/ 00079 00080 #ifdef COLOR_HOTTRACKING 00081 00082 void HotTrack(PCBOX pcbox) 00083 { 00084 if (!pcbox->fButtonHotTracked && !pcbox->fMouseDown) { 00085 HWND hwnd = HWq(pcbox->spwnd); 00086 TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT), TME_LEAVE, hwnd, 0}; 00087 if (NtUserTrackMouseEvent(&tme)) { 00088 pcbox->fButtonHotTracked = TRUE; 00089 NtUserInvalidateRect(hwnd, &pcbox->buttonrc, TRUE); 00090 } 00091 } 00092 } 00093 00094 #endif // COLOR_HOTTRACKING 00095 00096 /***************************************************************************\ 00097 * xxxComboBoxDBCharHandler 00098 * 00099 * Double Byte character handler for ANSI ComboBox 00100 * 00101 * History: 00102 \***************************************************************************/ 00103 00104 LRESULT ComboBoxDBCharHandler( 00105 PCBOX pcbox, 00106 HWND hwnd, 00107 UINT message, 00108 WPARAM wParam, 00109 LPARAM lParam) 00110 { 00111 WORD w; 00112 PWND pwndSend; 00113 00114 w = DbcsCombine(hwnd, (BYTE)wParam); 00115 if (w == 0) { 00116 return CB_ERR; // Failed to assemble DBCS 00117 } 00118 00119 UserAssert(pcbox->spwndList); 00120 if (pcbox->fNoEdit) { 00121 pwndSend = pcbox->spwndList; 00122 } else if (pcbox->spwndEdit) { 00123 RIPMSG1(RIP_WARNING, "ComboBoxWndProcWorker: WM_CHAR is posted to Combobox itself(%08x).", 00124 hwnd); 00125 pwndSend = pcbox->spwndEdit; 00126 } else { 00127 return CB_ERR; 00128 } 00129 00130 RIPMSG1(RIP_VERBOSE, "ComboBoxWndProcWorker: sending WM_CHAR %04x", w); 00131 00132 if (!TestWF(pwndSend, WFANSIPROC)) { 00133 // 00134 // If receiver is not ANSI WndProc (may be subclassed?), 00135 // send a UNICODE message. 00136 // 00137 WCHAR wChar; 00138 LPWSTR lpwstr = &wChar; 00139 00140 if (MBToWCSEx(THREAD_CODEPAGE(), (LPCSTR)&w, 2, &lpwstr, 1, FALSE) == 0) { 00141 RIPMSG1(RIP_WARNING, "ComboBoxWndProcWorker: cannot convert 0x%04x to UNICODE.", w); 00142 return CB_ERR; 00143 } 00144 return SendMessageWorker(pwndSend, message, wChar, lParam, FALSE); 00145 } 00146 00147 /* 00148 * Post the Trailing byte to the target 00149 * so that they can peek the second WM_CHAR 00150 * message later. 00151 * Note: it's safe since sender is A and receiver is A, 00152 * translation layer does not perform any DBCS combining and cracking. 00153 */ 00154 PostMessageA(HWq(pwndSend), message, CrackCombinedDbcsTB(w), lParam); 00155 return SendMessageWorker(pwndSend, message, wParam, lParam, TRUE); 00156 } 00157 00158 BOOL ComboBoxMsgOKInInit(UINT message, LRESULT* plRet) 00159 { 00160 switch (message) { 00161 default: 00162 break; 00163 case WM_SIZE: 00164 *plRet = 0; 00165 return FALSE; 00166 case WM_STYLECHANGED: 00167 case WM_GETTEXT: 00168 case WM_GETTEXTLENGTH: 00169 case WM_PRINT: 00170 case WM_COMMAND: 00171 case CBEC_KILLCOMBOFOCUS: 00172 case WM_PRINTCLIENT: 00173 case WM_SETFONT: 00174 case WM_SYSKEYDOWN: 00175 case WM_KEYDOWN: 00176 case WM_CHAR: 00177 case WM_LBUTTONDBLCLK: 00178 case WM_LBUTTONDOWN: 00179 case WM_MOUSEWHEEL: 00180 case WM_CAPTURECHANGED: 00181 case WM_LBUTTONUP: 00182 case WM_MOUSEMOVE: 00183 case WM_SETFOCUS: 00184 case WM_KILLFOCUS: 00185 case WM_SETREDRAW: 00186 case WM_ENABLE: 00187 case CB_SETDROPPEDWIDTH: 00188 case CB_DIR: 00189 case CB_ADDSTRING: 00190 /* 00191 * Cannot handle those messages yet. Bail out. 00192 */ 00193 *plRet = CB_ERR; 00194 return FALSE; 00195 } 00196 return TRUE; 00197 } 00198 00199 /***************************************************************************\ 00200 * xxxComboBoxCtlWndProc 00201 * 00202 * Class procedure for all combo boxes 00203 * 00204 * History: 00205 \***************************************************************************/ 00206 00207 LRESULT APIENTRY ComboBoxWndProcWorker( 00208 PWND pwnd, 00209 UINT message, 00210 WPARAM wParam, 00211 LPARAM lParam, 00212 DWORD fAnsi) 00213 { 00214 HWND hwnd = HWq(pwnd); 00215 PCBOX pcbox; 00216 POINT pt; 00217 TL tlpwndEdit; 00218 TL tlpwndList; 00219 PAINTSTRUCT ps; 00220 LPWSTR lpwsz = NULL; 00221 LRESULT lReturn; 00222 static BOOL fInit = TRUE; 00223 int i; 00224 00225 CheckLock(pwnd); 00226 00227 VALIDATECLASSANDSIZE(pwnd, FNID_COMBOBOX); 00228 INITCONTROLLOOKASIDE(&ComboboxLookaside, CBOX, spwnd, 8); 00229 00230 /* 00231 * Get the pcbox for the given window now since we will use it a lot in 00232 * various handlers. This is stored by NtUserSetWindowLongPtr() in the 00233 * INITCONTROLLOOKASIDE macro above. 00234 */ 00235 pcbox = ((PCOMBOWND)pwnd)->pcbox; 00236 00237 /* 00238 * Protect the combobox during the initialization. 00239 */ 00240 if (pcbox->spwndList == NULL) { 00241 LRESULT lRet; 00242 00243 if (!ComboBoxMsgOKInInit(message, &lRet)) { 00244 RIPMSG2(RIP_WARNING, "ComboBoxWndProcWorker: msg=%04x is sent to hwnd=%08x in the middle of initialization.", 00245 message, hwnd); 00246 return lRet; 00247 } 00248 } 00249 00250 /* 00251 * Dispatch the various messages we can receive 00252 */ 00253 switch (message) { 00254 case CBEC_KILLCOMBOFOCUS: 00255 00256 /* 00257 * Private message coming from editcontrol informing us that the combo 00258 * box is losing the focus to a window which isn't in this combo box. 00259 */ 00260 xxxCBKillFocusHelper(pcbox); 00261 break; 00262 00263 case WM_COMMAND: 00264 00265 /* 00266 * So that we can handle notification messages from the listbox and 00267 * edit control. 00268 */ 00269 return xxxCBCommandHandler(pcbox, (DWORD)wParam, (HWND)lParam); 00270 00271 case WM_STYLECHANGED: 00272 UserAssert(pcbox->spwndList != NULL); 00273 { 00274 LONG OldStyle; 00275 LONG NewStyle = 0; 00276 00277 pcbox->fRtoLReading = (TestWF(pwnd, WEFRTLREADING) != 0); 00278 pcbox->fRightAlign = (TestWF(pwnd, WEFRIGHT) != 0); 00279 if (pcbox->fRtoLReading) 00280 NewStyle |= (WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR); 00281 if (pcbox->fRightAlign) 00282 NewStyle |= WS_EX_RIGHT; 00283 00284 ThreadLock(pcbox->spwndList, &tlpwndList); 00285 OldStyle = GetWindowLong(HWq(pcbox->spwndList), GWL_EXSTYLE) & ~(WS_EX_RIGHT|WS_EX_RTLREADING|WS_EX_LEFTSCROLLBAR); 00286 SetWindowLong(HWq(pcbox->spwndList), GWL_EXSTYLE, OldStyle|NewStyle); 00287 ThreadUnlock(&tlpwndList); 00288 00289 if (!pcbox->fNoEdit && pcbox->spwndEdit) { 00290 ThreadLock(pcbox->spwndEdit, &tlpwndEdit); 00291 OldStyle = GetWindowLong(HWq(pcbox->spwndEdit), GWL_EXSTYLE) & ~(WS_EX_RIGHT|WS_EX_RTLREADING|WS_EX_LEFTSCROLLBAR); 00292 SetWindowLong(HWq(pcbox->spwndEdit), GWL_EXSTYLE, OldStyle|NewStyle); 00293 ThreadUnlock(&tlpwndEdit); 00294 } 00295 xxxCBPosition(pcbox); 00296 NtUserInvalidateRect(hwnd, NULL, FALSE); 00297 } 00298 break; 00299 00300 case WM_CTLCOLORMSGBOX: 00301 case WM_CTLCOLOREDIT: 00302 case WM_CTLCOLORLISTBOX: 00303 case WM_CTLCOLORBTN: 00304 case WM_CTLCOLORDLG: 00305 case WM_CTLCOLORSCROLLBAR: 00306 case WM_CTLCOLORSTATIC: 00307 case WM_CTLCOLOR: 00308 // 00309 // Causes compatibility problems for 3.X apps. Forward only 00310 // for 4.0 00311 // 00312 if (TestWF(pwnd, WFWIN40COMPAT)) { 00313 TL tlpwndParent; 00314 LRESULT ret; 00315 PWND pwndParent; 00316 00317 pwndParent = REBASEPWND(pwnd, spwndParent); 00318 ThreadLock(pwndParent, &tlpwndParent); 00319 ret = SendMessage(HW(pwndParent), message, wParam, lParam); 00320 ThreadUnlock(tlpwndParent); 00321 return ret; 00322 } else 00323 return(DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi)); 00324 break; 00325 00326 case WM_GETTEXT: 00327 if (pcbox->fNoEdit) { 00328 return xxxCBGetTextHelper(pcbox, (int)wParam, (LPWSTR)lParam, fAnsi); 00329 } 00330 goto CallEditSendMessage; 00331 break; 00332 00333 case WM_GETTEXTLENGTH: 00334 00335 /* 00336 * If the is not edit control, CBS_DROPDOWNLIST, then we have to 00337 * ask the list box for the size 00338 */ 00339 00340 if (pcbox->fNoEdit) { 00341 return xxxCBGetTextLengthHelper(pcbox, fAnsi); 00342 } 00343 00344 // FALL THROUGH 00345 00346 case WM_CLEAR: 00347 case WM_CUT: 00348 case WM_PASTE: 00349 case WM_COPY: 00350 case WM_SETTEXT: 00351 goto CallEditSendMessage; 00352 break; 00353 00354 case WM_CREATE: 00355 00356 /* 00357 * wParam - not used 00358 * lParam - Points to the CREATESTRUCT data structure for the window. 00359 */ 00360 return xxxCBCreateHandler(pcbox, pwnd); 00361 00362 case WM_ERASEBKGND: 00363 00364 /* 00365 * Just return 1L so that the background isn't erased 00366 */ 00367 return 1L; 00368 00369 case WM_GETFONT: 00370 return (LRESULT)pcbox->hFont; 00371 00372 case WM_PRINT: 00373 if (!DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi)) 00374 return(FALSE); 00375 00376 if ((lParam & PRF_OWNED) && (pcbox->CBoxStyle & SDROPPABLE) && 00377 TestWF(pcbox->spwndList, WFVISIBLE)) { 00378 TL tpwndList; 00379 int iDC = SaveDC((HDC) wParam); 00380 OffsetWindowOrgEx((HDC) wParam, 0, pwnd->rcWindow.top - pcbox->spwndList->rcWindow.top, NULL); 00381 lParam &= ~PRF_CHECKVISIBLE; 00382 ThreadLock(pcbox->spwndList, &tpwndList); 00383 SendMessageWorker(pcbox->spwndList, WM_PRINT, wParam, lParam, FALSE); 00384 RestoreDC((HDC) wParam, iDC); 00385 } 00386 return TRUE; 00387 00388 case WM_PRINTCLIENT: 00389 xxxCBPaint(pcbox, (HDC) wParam); 00390 break; 00391 00392 case WM_PAINT: { 00393 HDC hdc; 00394 00395 /* 00396 * wParam - perhaps a hdc 00397 */ 00398 hdc = (wParam) ? (HDC) wParam : NtUserBeginPaint(hwnd, &ps); 00399 00400 if (IsComboVisible(pcbox)) 00401 xxxCBPaint(pcbox, hdc); 00402 00403 if (!wParam) 00404 NtUserEndPaint(hwnd, &ps); 00405 break; 00406 } 00407 case WM_GETDLGCODE: 00408 00409 /* 00410 * wParam - not used 00411 * lParam - not used 00412 */ 00413 { 00414 LRESULT code = DLGC_WANTCHARS | DLGC_WANTARROWS; 00415 00416 // If the listbox is dropped and the ENTER key is pressed, 00417 // we want this message so we can close up the listbox 00418 if ((lParam != 0) && 00419 (((LPMSG)lParam)->message == WM_KEYDOWN) && 00420 pcbox->fLBoxVisible && 00421 ((wParam == VK_RETURN) || (wParam == VK_ESCAPE))) 00422 { 00423 code |= DLGC_WANTMESSAGE; 00424 } 00425 return code; 00426 } 00427 /* 00428 * No fall through 00429 */ 00430 00431 case WM_SETFONT: 00432 xxxCBSetFontHandler(pcbox, (HANDLE)wParam, LOWORD(lParam)); 00433 break; 00434 00435 case WM_SYSKEYDOWN: 00436 if (lParam & 0x20000000L) /* Check if the alt key is down */ { 00437 00438 /* 00439 * Handle Combobox support. We want alt up or down arrow to behave 00440 * like F4 key which completes the combo box selection 00441 */ 00442 if (lParam & 0x1000000) { 00443 00444 /* 00445 * This is an extended key such as the arrow keys not on the 00446 * numeric keypad so just drop the combobox. 00447 */ 00448 if (wParam == VK_DOWN || wParam == VK_UP) 00449 goto DropCombo; 00450 00451 goto CallDWP; 00452 } 00453 00454 if (GetKeyState(VK_NUMLOCK) & 0x1) { 00455 /* 00456 * If numlock down, just send all system keys to dwp 00457 */ 00458 goto CallDWP; 00459 } else { 00460 00461 /* 00462 * We just want to ignore keys on the number pad... 00463 */ 00464 if (!(wParam == VK_DOWN || wParam == VK_UP)) 00465 goto CallDWP; 00466 } 00467 DropCombo: 00468 if (!pcbox->fLBoxVisible) { 00469 00470 /* 00471 * If the listbox isn't visible, just show it 00472 */ 00473 xxxCBShowListBoxWindow(pcbox, TRUE); 00474 } else { 00475 00476 /* 00477 * Ok, the listbox is visible. So hide the listbox window. 00478 */ 00479 if (!xxxCBHideListBoxWindow(pcbox, TRUE, TRUE)) 00480 return(0L); 00481 } 00482 } 00483 goto CallDWP; 00484 break; 00485 00486 case WM_KEYDOWN: 00487 /* 00488 * If the listbox is dropped and the ENTER key is pressed, 00489 * close up the listbox successfully. If ESCAPE is pressed, 00490 * close it up like cancel. 00491 */ 00492 if (pcbox->fLBoxVisible) { 00493 if ((wParam == VK_RETURN) || (wParam == VK_ESCAPE)) { 00494 xxxCBHideListBoxWindow(pcbox, TRUE, (wParam != VK_ESCAPE)); 00495 break; 00496 } 00497 } 00498 // FALL THROUGH 00499 00500 case WM_CHAR: 00501 if (fAnsi && IS_DBCS_ENABLED() && IsDBCSLeadByteEx(THREAD_CODEPAGE(), (BYTE)wParam)) { 00502 return ComboBoxDBCharHandler(pcbox, hwnd, message, wParam, lParam); 00503 } 00504 00505 if (pcbox->fNoEdit) { 00506 goto CallListSendMessage; 00507 } 00508 else 00509 goto CallEditSendMessage; 00510 break; 00511 00512 case WM_LBUTTONDBLCLK: 00513 case WM_LBUTTONDOWN: 00514 00515 #ifdef COLOR_HOTTRACKING 00516 pcbox->fButtonHotTracked = FALSE; 00517 #endif // COLOR_HOTTRACKING 00518 00519 /* 00520 * Set the focus to the combo box if we get a mouse click on it. 00521 */ 00522 if (!pcbox->fFocus) { 00523 NtUserSetFocus(hwnd); 00524 if (!pcbox->fFocus) { 00525 00526 /* 00527 * Don't do anything if we still don't have the focus. 00528 */ 00529 break; 00530 } 00531 } 00532 00533 /* 00534 * If user clicked in button rect and we are a combobox with edit, then 00535 * drop the listbox. (The button rect is 0 if there is no button so the 00536 * ptinrect will return false.) If a drop down list (no edit), clicking 00537 * anywhere on the face causes the list to drop. 00538 */ 00539 00540 POINTSTOPOINT(pt, lParam); 00541 if ((pcbox->CBoxStyle == SDROPDOWN && 00542 PtInRect(&pcbox->buttonrc, pt)) || 00543 pcbox->CBoxStyle == SDROPDOWNLIST) { 00544 00545 /* 00546 * Set the fMouseDown flag so that we can handle clicking on 00547 * the popdown button and dragging into the listbox (when it just 00548 * dropped down) to make a selection. 00549 */ 00550 pcbox->fButtonPressed = TRUE; 00551 if (pcbox->fLBoxVisible) { 00552 if (pcbox->fMouseDown) { 00553 pcbox->fMouseDown = FALSE; 00554 NtUserReleaseCapture(); 00555 } 00556 xxxPressButton(pcbox, FALSE); 00557 00558 if (!xxxCBHideListBoxWindow(pcbox, TRUE, TRUE)) 00559 return(0L); 00560 } else { 00561 xxxCBShowListBoxWindow(pcbox, FALSE); 00562 00563 // Setting and resetting this flag must always be followed 00564 // imediately by SetCapture or ReleaseCapture 00565 // 00566 pcbox->fMouseDown = TRUE; 00567 NtUserSetCapture(hwnd); 00568 if (FWINABLE()) { 00569 NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEX_COMBOBOX_BUTTON); 00570 } 00571 } 00572 } 00573 break; 00574 00575 case WM_MOUSEWHEEL: 00576 /* 00577 * Handle only scrolling. 00578 */ 00579 if (wParam & (MK_CONTROL | MK_SHIFT)) 00580 goto CallDWP; 00581 00582 /* 00583 * If the listbox is visible, send it the message to scroll. 00584 */ 00585 if (pcbox->fLBoxVisible) 00586 goto CallListSendMessage; 00587 00588 /* 00589 * If we're in extended UI mode or the edit control isn't yet created, 00590 * bail. 00591 */ 00592 if (pcbox->fExtendedUI || pcbox->spwndEdit == NULL) 00593 return TRUE; 00594 00595 /* 00596 * Emulate arrow up/down messages to the edit control. 00597 */ 00598 i = abs(((short)HIWORD(wParam))/WHEEL_DELTA); 00599 wParam = ((short)HIWORD(wParam) > 0) ? VK_UP : VK_DOWN; 00600 00601 ThreadLock(pcbox->spwndEdit, &tlpwndEdit); 00602 while (i-- > 0) { 00603 SendMessageWorker( 00604 pcbox->spwndEdit, WM_KEYDOWN, wParam, 0, fAnsi); 00605 } 00606 ThreadUnlock(&tlpwndEdit); 00607 return TRUE; 00608 00609 case WM_CAPTURECHANGED: 00610 if (!(TestWF(pwnd, WFWIN40COMPAT))) 00611 return 0; 00612 00613 if ((pcbox->fMouseDown)) { 00614 pcbox->fMouseDown = FALSE; 00615 xxxPressButton(pcbox, FALSE); 00616 00617 // 00618 // Pop combo listbox back up, canceling. 00619 // 00620 if (pcbox->fLBoxVisible) 00621 xxxCBHideListBoxWindow(pcbox, TRUE, FALSE); 00622 } 00623 break; 00624 00625 case WM_LBUTTONUP: 00626 xxxPressButton(pcbox, FALSE); 00627 00628 /* 00629 * Clear this flag so that mouse moves aren't sent to the listbox 00630 */ 00631 if (pcbox->fMouseDown) { 00632 pcbox->fMouseDown = FALSE; 00633 00634 if (pcbox->CBoxStyle == SDROPDOWN) { 00635 // If an item in the listbox matches the text in the edit 00636 // control, scroll it to the top of the listbox. Select the 00637 // item only if the mouse button isn't down otherwise we 00638 // will select the item when the mouse button goes up. 00639 xxxCBUpdateListBoxWindow(pcbox, TRUE); 00640 xxxCBCompleteEditWindow(pcbox); 00641 } 00642 NtUserReleaseCapture(); 00643 00644 // Now, we want listbox to track mouse moves while mouse up 00645 // until mouse down, and select items as though they were 00646 // clicked on. 00647 if (TestWF(pwnd, WFWIN40COMPAT)) { 00648 00649 ThreadLock(pcbox->spwndList, &tlpwndList); 00650 SendMessageWorker(pcbox->spwndList, LBCB_STARTTRACK, FALSE, 0, FALSE); 00651 ThreadUnlock(&tlpwndList); 00652 } 00653 } 00654 #ifdef COLOR_HOTTRACKING 00655 HotTrack(pcbox); 00656 break; 00657 00658 case WM_MOUSELEAVE: 00659 pcbox->fButtonHotTracked = FALSE; 00660 NtUserInvalidateRect(hwnd, &pcbox->buttonrc, TRUE); 00661 #endif // COLOR_HOTTRACKING 00662 break; 00663 00664 case WM_MOUSEMOVE: 00665 if (pcbox->fMouseDown) { 00666 POINTSTOPOINT(pt, lParam); 00667 00668 // Note conversion of INT bit field to BOOL (1 or 0) 00669 00670 if (PtInRect(&pcbox->buttonrc, pt) != !!pcbox->fButtonPressed) { 00671 xxxPressButton(pcbox, (pcbox->fButtonPressed == 0)); 00672 } 00673 00674 _ClientToScreen(pwnd, &pt); 00675 if (PtInRect(&pcbox->spwndList->rcClient, pt)) { 00676 00677 /* 00678 * This handles dropdown comboboxes/listboxes so that clicking 00679 * on the dropdown button and dragging into the listbox window 00680 * will let the user make a listbox selection. 00681 */ 00682 pcbox->fMouseDown = FALSE; 00683 NtUserReleaseCapture(); 00684 00685 if (pcbox->CBoxStyle & SEDITABLE) { 00686 00687 /* 00688 * If an item in the listbox matches the text in the edit 00689 * control, scroll it to the top of the listbox. Select the 00690 * item only if the mouse button isn't down otherwise we 00691 * will select the item when the mouse button goes up. 00692 */ 00693 00694 /* 00695 * We need to select the item which matches the editcontrol 00696 * so that if the user drags out of the listbox, we don't 00697 * cancel back to his origonal selection 00698 */ 00699 xxxCBUpdateListBoxWindow(pcbox, TRUE); 00700 } 00701 00702 /* 00703 * Convert point to listbox coordinates and send a buttondown 00704 * message to the listbox window. 00705 */ 00706 _ScreenToClient(pcbox->spwndList, &pt); 00707 lParam = POINTTOPOINTS(pt); 00708 message = WM_LBUTTONDOWN; 00709 goto CallListSendMessage; 00710 } 00711 } 00712 #ifdef COLOR_HOTTRACKING 00713 HotTrack(pcbox); 00714 #endif // COLOR_HOTTRACKING 00715 break; 00716 00717 case WM_NCDESTROY: 00718 case WM_FINALDESTROY: 00719 xxxCBNcDestroyHandler(pwnd, pcbox); 00720 break; 00721 00722 case WM_SETFOCUS: 00723 if (pcbox->fNoEdit) { 00724 00725 /* 00726 * There is no editcontrol so set the focus to the combo box itself. 00727 */ 00728 xxxCBGetFocusHelper(pcbox); 00729 } else if (pcbox->spwndEdit) { 00730 /* 00731 * Set the focus to the edit control window if there is one 00732 */ 00733 ThreadLock(pcbox->spwndEdit, &tlpwndEdit); 00734 NtUserSetFocus(HWq(pcbox->spwndEdit)); 00735 ThreadUnlock(&tlpwndEdit); 00736 } 00737 break; 00738 00739 case WM_KILLFOCUS: 00740 00741 /* 00742 * wParam has the new focus hwnd 00743 */ 00744 if (wParam != 0) 00745 wParam = (WPARAM)ValidateHwnd((HWND)wParam); 00746 if ((wParam == 0) || !_IsChild(pwnd, (PWND)wParam)) { 00747 00748 /* 00749 * We only give up the focus if the new window getting the focus 00750 * doesn't belong to the combo box. 00751 */ 00752 xxxCBKillFocusHelper(pcbox); 00753 } 00754 00755 UserAssert(pcbox->spwndList); 00756 { 00757 PLBIV plb = ((PLBWND)pcbox->spwndList)->pLBIV; 00758 00759 if ((plb != NULL) && (plb != (PLBIV)-1)) { 00760 plb->iTypeSearch = 0; 00761 if (plb->pszTypeSearch) { 00762 UserLocalFree(plb->pszTypeSearch); 00763 plb->pszTypeSearch = NULL; 00764 } 00765 } 00766 } 00767 break; 00768 00769 case WM_SETREDRAW: 00770 00771 /* 00772 * wParam - specifies state of the redraw flag. nonzero = redraw 00773 * lParam - not used 00774 */ 00775 00776 /* 00777 * effects: Sets the state of the redraw flag for this combo box 00778 * and its children. 00779 */ 00780 pcbox->fNoRedraw = (UINT)!((BOOL)wParam); 00781 00782 /* 00783 * Must check pcbox->spwnEdit in case we get this message before 00784 * WM_CREATE - PCBOX won't be initialized yet. (Eudora does this) 00785 */ 00786 if (!pcbox->fNoEdit && pcbox->spwndEdit) { 00787 ThreadLock(pcbox->spwndEdit, &tlpwndEdit); 00788 SendMessageWorker(pcbox->spwndEdit, message, wParam, lParam, FALSE); 00789 ThreadUnlock(&tlpwndEdit); 00790 } 00791 goto CallListSendMessage; 00792 break; 00793 00794 case WM_ENABLE: 00795 00796 /* 00797 * Invalidate the rect to cause it to be drawn in grey for its 00798 * disabled view or ungreyed for non-disabled view. 00799 */ 00800 NtUserInvalidateRect(hwnd, NULL, FALSE); 00801 if ((pcbox->CBoxStyle & SEDITABLE) && pcbox->spwndEdit) { 00802 00803 /* 00804 * Enable/disable the edit control window 00805 */ 00806 ThreadLock(pcbox->spwndEdit, &tlpwndEdit); 00807 NtUserEnableWindow(HWq(pcbox->spwndEdit), (TestWF(pwnd, WFDISABLED) == 0)); 00808 ThreadUnlock(&tlpwndEdit); 00809 } 00810 00811 /* 00812 * Enable/disable the listbox window 00813 */ 00814 UserAssert(pcbox->spwndList); 00815 ThreadLock(pcbox->spwndList, &tlpwndList); 00816 NtUserEnableWindow(HWq(pcbox->spwndList), (TestWF(pwnd, WFDISABLED) == 0)); 00817 ThreadUnlock(&tlpwndList); 00818 break; 00819 00820 case WM_SIZE: 00821 00822 /* 00823 * wParam - defines the type of resizing fullscreen, sizeiconic, 00824 * sizenormal etc. 00825 * lParam - new width in LOWORD, new height in HIGHUINT of client area 00826 */ 00827 UserAssert(pcbox->spwndList); 00828 if (LOWORD(lParam) == 0 || HIWORD(lParam) == 0) { 00829 00830 /* 00831 * If being sized to a zero width or to a zero height or we aren't 00832 * fully initialized, just return. 00833 */ 00834 return 0; 00835 } 00836 00837 // OPTIMIZATIONS -- first check if old and new widths are the same 00838 if (pcbox->cxCombo == pwnd->rcWindow.right - pwnd->rcWindow.left) { 00839 int iNewHeight = pwnd->rcWindow.bottom - pwnd->rcWindow.top; 00840 00841 // now check if new height is the dropped down height 00842 if (pcbox->fLBoxVisible) { 00843 // Check if new height is the full size height 00844 if (pcbox->cyDrop + pcbox->cyCombo == iNewHeight) 00845 return(0L); 00846 } else { 00847 // Check if new height is the closed up height 00848 if (pcbox->cyCombo == iNewHeight) 00849 return(0L); 00850 } 00851 } 00852 00853 xxxCBSizeHandler(pcbox); 00854 break; 00855 00856 case CB_GETDROPPEDSTATE: 00857 00858 /* 00859 * returns 1 if combo is dropped down else 0 00860 * wParam - not used 00861 * lParam - not used 00862 */ 00863 return pcbox->fLBoxVisible; 00864 00865 case CB_GETDROPPEDCONTROLRECT: 00866 00867 /* 00868 * wParam - not used 00869 * lParam - lpRect which will get the dropped down window rect in 00870 * screen coordinates. 00871 */ 00872 ((LPRECT)lParam)->left = pwnd->rcWindow.left; 00873 ((LPRECT)lParam)->top = pwnd->rcWindow.top; 00874 ((LPRECT)lParam)->right = pwnd->rcWindow.left + max(pcbox->cxDrop, pcbox->cxCombo); 00875 ((LPRECT)lParam)->bottom = pwnd->rcWindow.top + pcbox->cyCombo + pcbox->cyDrop; 00876 break; 00877 00878 case CB_SETDROPPEDWIDTH: 00879 if (pcbox->CBoxStyle & SDROPPABLE) { 00880 if (wParam) { 00881 wParam = max(wParam, (UINT)pcbox->cxCombo); 00882 00883 if (wParam != (UINT) pcbox->cxDrop) 00884 { 00885 pcbox->cxDrop = (int)wParam; 00886 xxxCBPosition(pcbox); 00887 } 00888 } 00889 } 00890 // fall thru 00891 00892 case CB_GETDROPPEDWIDTH: 00893 if (pcbox->CBoxStyle & SDROPPABLE) 00894 return((LRESULT) max(pcbox->cxDrop, pcbox->cxCombo)); 00895 else 00896 return(CB_ERR); 00897 break; 00898 00899 case CB_DIR: 00900 /* 00901 * wParam - Dos attribute value. 00902 * lParam - Points to a file specification string 00903 */ 00904 if (fAnsi && lParam != 0) { 00905 if (MBToWCS((LPSTR)lParam, -1, &lpwsz, -1, TRUE) == 0) 00906 return CB_ERR; 00907 lParam = (LPARAM)lpwsz; 00908 } 00909 lReturn = xxxCBDir(pcbox, LOWORD(wParam), (LPWSTR)lParam); 00910 if (fAnsi && lParam != 0) { 00911 UserLocalFree(lpwsz); 00912 } 00913 return lReturn; 00914 00915 case CB_SETEXTENDEDUI: 00916 00917 /* 00918 * wParam - specifies state to set extendui flag to. 00919 * Currently only 1 is allowed. Return CB_ERR (-1) if 00920 * failure else 0 if success. 00921 */ 00922 if (pcbox->CBoxStyle & SDROPPABLE) { 00923 if (!wParam) { 00924 pcbox->fExtendedUI = 0; 00925 return 0; 00926 } 00927 00928 if (wParam == 1) { 00929 pcbox->fExtendedUI = 1; 00930 return 0; 00931 } 00932 00933 RIPERR1(ERROR_INVALID_PARAMETER, 00934 RIP_WARNING, 00935 "Invalid parameter \"wParam\" (%ld) to ComboBoxWndProcWorker", 00936 wParam); 00937 00938 } else { 00939 RIPERR1(ERROR_INVALID_MESSAGE, 00940 RIP_WARNING, 00941 "Invalid message (%ld) sent to ComboBoxWndProcWorker", 00942 message); 00943 } 00944 00945 return CB_ERR; 00946 00947 case CB_GETEXTENDEDUI: 00948 if (pcbox->CBoxStyle & SDROPPABLE) { 00949 if (pcbox->fExtendedUI) 00950 return TRUE; 00951 } 00952 return FALSE; 00953 00954 case CB_GETEDITSEL: 00955 00956 /* 00957 * wParam - not used 00958 * lParam - not used 00959 * effects: Gets the selection range for the given edit control. The 00960 * starting BYTE-position is in the low order word. It contains the 00961 * the BYTE-position of the first nonselected character after the end 00962 * of the selection in the high order word. Returns CB_ERR if no 00963 * editcontrol. 00964 */ 00965 message = EM_GETSEL; 00966 goto CallEditSendMessage; 00967 break; 00968 00969 case CB_LIMITTEXT: 00970 00971 /* 00972 * wParam - max number of bytes that can be entered 00973 * lParam - not used 00974 * effects: Specifies the maximum number of bytes of text the user may 00975 * enter. If maxLength is 0, we may enter MAXINT number of BYTES. 00976 */ 00977 message = EM_LIMITTEXT; 00978 goto CallEditSendMessage; 00979 break; 00980 00981 case CB_SETEDITSEL: 00982 00983 /* 00984 * wParam - ichStart 00985 * lParam - ichEnd 00986 * 00987 */ 00988 message = EM_SETSEL; 00989 00990 wParam = (int)(SHORT)LOWORD(lParam); 00991 lParam = (int)(SHORT)HIWORD(lParam); 00992 goto CallEditSendMessage; 00993 break; 00994 00995 case CB_ADDSTRING: 00996 00997 /* 00998 * wParam - not used 00999 * lParam - Points to null terminated string to be added to listbox 01000 */ 01001 if (!pcbox->fCase) 01002 message = LB_ADDSTRING; 01003 else 01004 message = (pcbox->fCase & UPPERCASE) ? LB_ADDSTRINGUPPER : LB_ADDSTRINGLOWER; 01005 goto CallListSendMessage; 01006 break; 01007 01008 case CB_DELETESTRING: 01009 01010 /* 01011 * wParam - index to string to be deleted 01012 * lParam - not used 01013 */ 01014 message = LB_DELETESTRING; 01015 goto CallListSendMessage; 01016 break; 01017 01018 case CB_INITSTORAGE: 01019 // wParamLo - number of items 01020 // lParam - number of bytes of string space 01021 message = LB_INITSTORAGE; 01022 goto CallListSendMessage; 01023 01024 case CB_SETTOPINDEX: 01025 // wParamLo - index to make top 01026 // lParam - not used 01027 message = LB_SETTOPINDEX; 01028 goto CallListSendMessage; 01029 01030 case CB_GETTOPINDEX: 01031 // wParamLo / lParam - not used 01032 message = LB_GETTOPINDEX; 01033 goto CallListSendMessage; 01034 01035 case CB_GETCOUNT: 01036 01037 /* 01038 * wParam - not used 01039 * lParam - not used 01040 */ 01041 message = LB_GETCOUNT; 01042 goto CallListSendMessage; 01043 break; 01044 01045 case CB_GETCURSEL: 01046 01047 /* 01048 * wParam - not used 01049 * lParam - not used 01050 */ 01051 message = LB_GETCURSEL; 01052 goto CallListSendMessage; 01053 break; 01054 01055 case CB_GETLBTEXT: 01056 01057 /* 01058 * wParam - index of string to be copied 01059 * lParam - buffer that is to receive the string 01060 */ 01061 message = LB_GETTEXT; 01062 goto CallListSendMessage; 01063 break; 01064 01065 case CB_GETLBTEXTLEN: 01066 01067 /* 01068 * wParam - index to string 01069 * lParam - now used for cbANSI 01070 */ 01071 message = LB_GETTEXTLEN; 01072 goto CallListSendMessage; 01073 break; 01074 01075 case CB_INSERTSTRING: 01076 01077 /* 01078 * wParam - position to receive the string 01079 * lParam - points to the string 01080 */ 01081 if (!pcbox->fCase) 01082 message = LB_INSERTSTRING; 01083 else 01084 message = (pcbox->fCase & UPPERCASE) ? LB_INSERTSTRINGUPPER : LB_INSERTSTRINGLOWER; 01085 goto CallListSendMessage; 01086 break; 01087 01088 case CB_RESETCONTENT: 01089 01090 /* 01091 * wParam - not used 01092 * lParam - not used 01093 * If we come here before WM_CREATE has been processed, 01094 * pcbox->spwndList will be NULL. 01095 */ 01096 UserAssert(pcbox->spwndList); 01097 ThreadLock(pcbox->spwndList, &tlpwndList); 01098 SendMessageWorker(pcbox->spwndList, LB_RESETCONTENT, 0, 0, FALSE); 01099 ThreadUnlock(&tlpwndList); 01100 xxxCBInternalUpdateEditWindow(pcbox, NULL); 01101 break; 01102 01103 case CB_GETHORIZONTALEXTENT: 01104 message = LB_GETHORIZONTALEXTENT; 01105 goto CallListSendMessage; 01106 01107 case CB_SETHORIZONTALEXTENT: 01108 message = LB_SETHORIZONTALEXTENT; 01109 goto CallListSendMessage; 01110 01111 case CB_FINDSTRING: 01112 01113 /* 01114 * wParam - index of starting point for search 01115 * lParam - points to prefix string 01116 */ 01117 message = LB_FINDSTRING; 01118 goto CallListSendMessage; 01119 break; 01120 01121 case CB_FINDSTRINGEXACT: 01122 01123 /* 01124 * wParam - index of starting point for search 01125 * lParam - points to a exact string 01126 */ 01127 message = LB_FINDSTRINGEXACT; 01128 goto CallListSendMessage; 01129 break; 01130 01131 case CB_SELECTSTRING: 01132 01133 /* 01134 * wParam - index of starting point for search 01135 * lParam - points to prefix string 01136 */ 01137 UserAssert(pcbox->spwndList); 01138 ThreadLock(pcbox->spwndList, &tlpwndList); 01139 lParam = SendMessageWorker(pcbox->spwndList, LB_SELECTSTRING, 01140 wParam, lParam, fAnsi); 01141 ThreadUnlock(&tlpwndList); 01142 xxxCBInternalUpdateEditWindow(pcbox, NULL); 01143 return lParam; 01144 01145 case CB_SETCURSEL: 01146 01147 /* 01148 * wParam - Contains index to be selected 01149 * lParam - not used 01150 * If we come here before WM_CREATE has been processed, 01151 * pcbox->spwndList will be NULL. 01152 */ 01153 01154 UserAssert(pcbox->spwndList); 01155 01156 ThreadLock(pcbox->spwndList, &tlpwndList); 01157 lParam = SendMessageWorker(pcbox->spwndList, LB_SETCURSEL, wParam, lParam, FALSE); 01158 if (lParam != -1) { 01159 SendMessageWorker(pcbox->spwndList, LB_SETTOPINDEX, wParam, 0, FALSE); 01160 } 01161 ThreadUnlock(&tlpwndList); 01162 xxxCBInternalUpdateEditWindow(pcbox, NULL); 01163 return lParam; 01164 01165 case CB_GETITEMDATA: 01166 message = LB_GETITEMDATA; 01167 goto CallListSendMessage; 01168 break; 01169 01170 case CB_SETITEMDATA: 01171 message = LB_SETITEMDATA; 01172 goto CallListSendMessage; 01173 break; 01174 01175 case CB_SETITEMHEIGHT: 01176 if (wParam == -1) { 01177 if (HIWORD(lParam) != 0) 01178 return CB_ERR; 01179 return xxxCBSetEditItemHeight(pcbox, LOWORD(lParam)); 01180 } 01181 01182 message = LB_SETITEMHEIGHT; 01183 goto CallListSendMessage; 01184 break; 01185 01186 case CB_GETITEMHEIGHT: 01187 if (wParam == -1) 01188 return pcbox->editrc.bottom - pcbox->editrc.top; 01189 01190 message = LB_GETITEMHEIGHT; 01191 goto CallListSendMessage; 01192 break; 01193 01194 case CB_SHOWDROPDOWN: 01195 01196 /* 01197 * wParam - True then drop down the listbox if possible else hide it 01198 * lParam - not used 01199 */ 01200 if (wParam && !pcbox->fLBoxVisible) { 01201 xxxCBShowListBoxWindow(pcbox, TRUE); 01202 } else { 01203 if (!wParam && pcbox->fLBoxVisible) { 01204 xxxCBHideListBoxWindow(pcbox, TRUE, FALSE); 01205 } 01206 } 01207 break; 01208 01209 case CB_SETLOCALE: 01210 01211 /* 01212 * wParam - locale id 01213 * lParam - not used 01214 */ 01215 message = LB_SETLOCALE; 01216 goto CallListSendMessage; 01217 break; 01218 01219 case CB_GETLOCALE: 01220 01221 /* 01222 * wParam - not used 01223 * lParam - not used 01224 */ 01225 message = LB_GETLOCALE; 01226 goto CallListSendMessage; 01227 break; 01228 01229 case WM_MEASUREITEM: 01230 case WM_DELETEITEM: 01231 case WM_DRAWITEM: 01232 case WM_COMPAREITEM: 01233 return xxxCBMessageItemHandler(pcbox, message, (LPVOID)lParam); 01234 01235 case WM_NCCREATE: 01236 01237 /* 01238 * wParam - Contains a handle to the window being created 01239 * lParam - Points to the CREATESTRUCT data structure for the window. 01240 */ 01241 return CBNcCreateHandler(pcbox, pwnd); 01242 01243 case WM_PARENTNOTIFY: 01244 if (LOWORD(wParam) == WM_DESTROY) { 01245 if ((HWND)lParam == HW(pcbox->spwndEdit)) { 01246 pcbox->CBoxStyle &= ~SEDITABLE; 01247 pcbox->fNoEdit = TRUE; 01248 pcbox->spwndEdit = pwnd; 01249 } else if ((HWND)lParam == HW(pcbox->spwndList)) { 01250 pcbox->CBoxStyle &= ~SDROPPABLE; 01251 pcbox->spwndList = NULL; 01252 } 01253 } 01254 break; 01255 01256 case WM_UPDATEUISTATE: 01257 /* 01258 * Propagate the change to the list control, if any 01259 */ 01260 UserAssert(pcbox->spwndList); 01261 ThreadLock(pcbox->spwndList, &tlpwndList); 01262 SendMessageWorker(pcbox->spwndList, WM_UPDATEUISTATE, 01263 wParam, lParam, fAnsi); 01264 ThreadUnlock(&tlpwndList); 01265 goto CallDWP; 01266 01267 case WM_HELP: 01268 { 01269 LPHELPINFO lpHelpInfo; 01270 01271 /* Check if this message is form a child of this combo 01272 */ 01273 if ((lpHelpInfo = (LPHELPINFO)lParam) != NULL && 01274 ((pcbox->spwndEdit && lpHelpInfo->iCtrlId == (SHORT)(PTR_TO_ID(pcbox->spwndEdit->spmenu))) || 01275 lpHelpInfo->iCtrlId == (SHORT)(PTR_TO_ID(pcbox->spwndList->spmenu)) )) { 01276 01277 // BUGBUG - What to do here? 01278 lpHelpInfo->iCtrlId = (SHORT)(PTR_TO_ID(pwnd->spmenu)); 01279 lpHelpInfo->hItemHandle = hwnd; 01280 lpHelpInfo->dwContextId = GetContextHelpId(pwnd); 01281 } 01282 } 01283 /* 01284 * Fall through to DefWindowProc 01285 */ 01286 01287 default: 01288 01289 if (SYSMET(PENWINDOWS) && 01290 (message >= WM_PENWINFIRST && message <= WM_PENWINLAST)) 01291 goto CallEditSendMessage; 01292 01293 CallDWP: 01294 return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi); 01295 } /* switch (message) */ 01296 01297 return TRUE; 01298 01299 /* 01300 * The following forward messages off to the child controls. 01301 */ 01302 CallEditSendMessage: 01303 if (!pcbox->fNoEdit && pcbox->spwndEdit) { 01304 /* 01305 * pcbox->spwndEdit will be NULL if we haven't done WM_CREATE yet! 01306 */ 01307 ThreadLock(pcbox->spwndEdit, &tlpwndEdit); 01308 lReturn = SendMessageWorker(pcbox->spwndEdit, message, 01309 wParam, lParam, fAnsi); 01310 ThreadUnlock(&tlpwndEdit); 01311 } 01312 else { 01313 RIPERR0(ERROR_INVALID_COMBOBOX_MESSAGE, RIP_VERBOSE, ""); 01314 lReturn = CB_ERR; 01315 } 01316 return lReturn; 01317 01318 CallListSendMessage: 01319 /* 01320 * pcbox->spwndList will be NULL if we haven't done WM_CREATE yet! 01321 */ 01322 UserAssert(pcbox->spwndList); 01323 ThreadLock(pcbox->spwndList, &tlpwndList); 01324 lReturn = SendMessageWorker(pcbox->spwndList, message, 01325 wParam, lParam, fAnsi); 01326 ThreadUnlock(&tlpwndList); 01327 return lReturn; 01328 01329 } /* ComboBoxWndProcWorker */ 01330 01331 01332 /***************************************************************************\ 01333 \***************************************************************************/ 01334 01335 LRESULT WINAPI ComboBoxWndProcA( 01336 HWND hwnd, 01337 UINT message, 01338 WPARAM wParam, 01339 LPARAM lParam) 01340 { 01341 PWND pwnd; 01342 01343 if ((pwnd = ValidateHwnd(hwnd)) == NULL) { 01344 return (0L); 01345 } 01346 01347 /* 01348 * If the control is not interested in this message, 01349 * pass it to DefWindowProc. 01350 */ 01351 if (!FWINDOWMSG(message, FNID_COMBOBOX) && 01352 !(SYSMET(PENWINDOWS) && 01353 (message >= WM_PENWINFIRST && message <= WM_PENWINLAST))) 01354 return DefWindowProcWorker(pwnd, message, wParam, lParam, TRUE); 01355 01356 return ComboBoxWndProcWorker(pwnd, message, wParam, lParam, TRUE); 01357 } 01358 01359 LRESULT WINAPI ComboBoxWndProcW( 01360 HWND hwnd, 01361 UINT message, 01362 WPARAM wParam, 01363 LPARAM lParam) 01364 { 01365 PWND pwnd; 01366 01367 if ((pwnd = ValidateHwnd(hwnd)) == NULL) { 01368 return (0L); 01369 } 01370 01371 /* 01372 * If the control is not interested in this message, 01373 * pass it to DefWindowProc. 01374 */ 01375 if (!FWINDOWMSG(message, FNID_COMBOBOX) && 01376 !(SYSMET(PENWINDOWS) && 01377 (message >= WM_PENWINFIRST && message <= WM_PENWINLAST))) 01378 return DefWindowProcWorker(pwnd, message, wParam, lParam, FALSE); 01379 01380 return ComboBoxWndProcWorker(pwnd, message, wParam, lParam, FALSE); 01381 } 01382 01383 01384 /***************************************************************************\ 01385 * xxxCBMessageItemHandler 01386 * 01387 * Handles WM_DRAWITEM,WM_MEASUREITEM,WM_DELETEITEM,WM_COMPAREITEM 01388 * messages from the listbox. 01389 * 01390 * History: 01391 \***************************************************************************/ 01392 01393 LRESULT xxxCBMessageItemHandler( 01394 PCBOX pcbox, 01395 UINT message, 01396 LPVOID lpfoo) /* Actually can be any of the structs below */ 01397 { 01398 LRESULT lRet; 01399 TL tlpwndParent; 01400 01401 CheckLock(pcbox->spwnd); 01402 01403 /* 01404 * Send the <foo>item message back to the application after changing some 01405 * parameters to their combo box specific versions. 01406 */ 01407 ((LPMEASUREITEMSTRUCT)lpfoo)->CtlType = ODT_COMBOBOX; 01408 ((LPMEASUREITEMSTRUCT)lpfoo)->CtlID = PtrToUlong(pcbox->spwnd->spmenu); 01409 if (message == WM_DRAWITEM) 01410 ((LPDRAWITEMSTRUCT)lpfoo)->hwndItem = HWq(pcbox->spwnd); 01411 else if (message == WM_DELETEITEM) 01412 ((LPDELETEITEMSTRUCT)lpfoo)->hwndItem = HWq(pcbox->spwnd); 01413 else if (message == WM_COMPAREITEM) 01414 ((LPCOMPAREITEMSTRUCT)lpfoo)->hwndItem = HWq(pcbox->spwnd); 01415 01416 ThreadLock(pcbox->spwndParent, &tlpwndParent); 01417 lRet = SendMessage(HW(pcbox->spwndParent), message, 01418 (WPARAM)pcbox->spwnd->spmenu, (LPARAM)lpfoo); 01419 ThreadUnlock(&tlpwndParent); 01420 01421 return lRet; 01422 } 01423 01424 01425 /***************************************************************************\ 01426 * xxxCBPaint 01427 * 01428 * History: 01429 \***************************************************************************/ 01430 01431 void xxxCBPaint( 01432 PCBOX pcbox, 01433 HDC hdc) 01434 { 01435 RECT rc; 01436 UINT msg; 01437 HBRUSH hbr; 01438 01439 CheckLock(pcbox->spwnd); 01440 01441 rc.left = rc.top = 0; 01442 rc.right = pcbox->cxCombo; 01443 rc.bottom = pcbox->cyCombo; 01444 if (pcbox->f3DCombo) 01445 DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST); 01446 else 01447 DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST | BF_FLAT | BF_MONO); 01448 01449 if (pcbox->buttonrc.left != 0) { 01450 // Draw in the dropdown arrow button 01451 DrawFrameControl(hdc, &pcbox->buttonrc, DFC_SCROLL, 01452 DFCS_SCROLLCOMBOBOX | 01453 (pcbox->fButtonPressed ? DFCS_PUSHED | DFCS_FLAT : 0) | 01454 (TestWF(pcbox->spwnd, WFDISABLED) ? DFCS_INACTIVE : 0)); 01455 #ifdef COLOR_HOTTRACKING 01456 (pcbox->fButtonHotTracked ? DFCS_HOT: 0))); 01457 #endif // COLOR_HOTTRACKING 01458 if (pcbox->fRightAlign ) 01459 rc.left = pcbox->buttonrc.right; 01460 else 01461 rc.right = pcbox->buttonrc.left; 01462 } 01463 01464 // Erase the background behind the edit/static item. Since a combo 01465 // is an edit field/list box hybrid, we use the same coloring 01466 // conventions. 01467 msg = WM_CTLCOLOREDIT; 01468 if (TestWF(pcbox->spwnd, WFWIN40COMPAT)) { 01469 if (TestWF(pcbox->spwnd, WFDISABLED) || 01470 (!pcbox->fNoEdit && pcbox->spwndEdit && TestWF(pcbox->spwndEdit, EFREADONLY))) 01471 msg = WM_CTLCOLORSTATIC; 01472 } else 01473 msg = WM_CTLCOLORLISTBOX; 01474 01475 hbr = GetControlBrush(HWq(pcbox->spwnd), hdc, msg); 01476 01477 if (pcbox->fNoEdit) 01478 xxxCBInternalUpdateEditWindow(pcbox, hdc); 01479 else 01480 FillRect(hdc, &rc, hbr); 01481 } 01482 01483 01484 /***************************************************************************\ 01485 * xxxCBCommandHandler 01486 * 01487 * Check the various notification codes from the controls and do the 01488 * proper thing. 01489 * always returns 0L 01490 * 01491 * History: 01492 \***************************************************************************/ 01493 01494 long xxxCBCommandHandler( 01495 PCBOX pcbox, 01496 DWORD wParam, 01497 HWND hwndControl) 01498 { 01499 01500 CheckLock(pcbox->spwnd); 01501 01502 /* 01503 * Check the edit control notification codes. Note that currently, edit 01504 * controls don't send EN_KILLFOCUS messages to the parent. 01505 */ 01506 if (!pcbox->fNoEdit && 01507 SAMEWOWHANDLE(hwndControl, HWq(pcbox->spwndEdit))) { 01508 01509 /* 01510 * Edit control notification codes 01511 */ 01512 switch (HIWORD(wParam)) { 01513 case EN_SETFOCUS: 01514 if (!pcbox->fFocus) { 01515 01516 /* 01517 * The edit control has the focus for the first time which means 01518 * this is the first time the combo box has received the focus 01519 * and the parent must be notified that we have the focus. 01520 */ 01521 xxxCBGetFocusHelper(pcbox); 01522 } 01523 break; 01524 01525 case EN_CHANGE: 01526 xxxCBNotifyParent(pcbox, CBN_EDITCHANGE); 01527 xxxCBUpdateListBoxWindow(pcbox, FALSE); 01528 break; 01529 01530 case EN_UPDATE: 01531 xxxCBNotifyParent(pcbox, CBN_EDITUPDATE); 01532 break; 01533 01534 case EN_ERRSPACE: 01535 xxxCBNotifyParent(pcbox, CBN_ERRSPACE); 01536 break; 01537 } 01538 } 01539 01540 /* 01541 * Check listbox control notification codes 01542 */ 01543 if (SAMEWOWHANDLE(hwndControl, HWq(pcbox->spwndList))) { 01544 01545 /* 01546 * Listbox control notification codes 01547 */ 01548 switch ((int)HIWORD(wParam)) { 01549 case LBN_DBLCLK: 01550 xxxCBNotifyParent(pcbox, CBN_DBLCLK); 01551 break; 01552 01553 case LBN_ERRSPACE: 01554 xxxCBNotifyParent(pcbox, CBN_ERRSPACE); 01555 break; 01556 01557 case LBN_SELCHANGE: 01558 case LBN_SELCANCEL: 01559 if (!pcbox->fKeyboardSelInListBox) { 01560 01561 /* 01562 * If the selchange is caused by the user keyboarding through, 01563 * we don't want to hide the listbox. 01564 */ 01565 if (!xxxCBHideListBoxWindow(pcbox, TRUE, TRUE)) 01566 return(0L); 01567 } else { 01568 pcbox->fKeyboardSelInListBox = FALSE; 01569 } 01570 01571 xxxCBNotifyParent(pcbox, CBN_SELCHANGE); 01572 xxxCBInternalUpdateEditWindow(pcbox, NULL); 01573 break; 01574 } 01575 } 01576 01577 return 0L; 01578 } 01579 01580 01581 /***************************************************************************\ 01582 * xxxCBNotifyParent 01583 * 01584 * Sends the notification code to the parent of the combo box control 01585 * 01586 * History: 01587 \***************************************************************************/ 01588 01589 void xxxCBNotifyParent( 01590 PCBOX pcbox, 01591 short notificationCode) 01592 { 01593 PWND pwndParent; // Parent if it exists 01594 TL tlpwndParent; 01595 01596 CheckLock(pcbox->spwnd); 01597 01598 if (pcbox->spwndParent) 01599 pwndParent = pcbox->spwndParent; 01600 else 01601 pwndParent = pcbox->spwnd; 01602 01603 /* 01604 * wParam contains Control ID and notification code. 01605 * lParam contains Handle to window 01606 */ 01607 ThreadLock(pwndParent, &tlpwndParent); 01608 SendMessageWorker(pwndParent, WM_COMMAND, 01609 MAKELONG(PTR_TO_ID(pcbox->spwnd->spmenu), notificationCode), 01610 (LPARAM)HWq(pcbox->spwnd), FALSE); 01611 ThreadUnlock(&tlpwndParent); 01612 } 01613 01614 /***************************************************************************\ 01615 * 01616 * 01617 * Completes the text in the edit box with the closest match from the 01618 * listbox. If a prefix match can't be found, the edit control text isn't 01619 * updated. Assume a DROPDOWN style combo box. 01620 * 01621 * 01622 * History: 01623 \***************************************************************************/ 01624 void xxxCBCompleteEditWindow( 01625 PCBOX pcbox) 01626 { 01627 int cchText; 01628 int cchItemText; 01629 int itemNumber; 01630 LPWSTR pText; 01631 TL tlpwndEdit; 01632 TL tlpwndList; 01633 01634 CheckLock(pcbox->spwnd); 01635 01636 /* 01637 * Firstly check the edit control. 01638 */ 01639 if (pcbox->spwndEdit == NULL) { 01640 return; 01641 } 01642 01643 ThreadLock(pcbox->spwndEdit, &tlpwndEdit); 01644 ThreadLock(pcbox->spwndList, &tlpwndList); 01645 01646 /* 01647 * +1 for null terminator 01648 */ 01649 cchText = (int)SendMessageWorker(pcbox->spwndEdit, WM_GETTEXTLENGTH, 0, 0, FALSE); 01650 01651 if (cchText) { 01652 cchText++; 01653 if (!(pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR)))) 01654 goto Unlock; 01655 01656 /* 01657 * We want to be sure to free the above allocated memory even if 01658 * the client dies during callback (xxx) or some of the following 01659 * window revalidation fails. 01660 */ 01661 try { 01662 SendMessageWorker(pcbox->spwndEdit, WM_GETTEXT, cchText, (LPARAM)pText, FALSE); 01663 itemNumber = (int)SendMessageWorker(pcbox->spwndList, 01664 LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pText, FALSE); 01665 if (itemNumber == -1) 01666 itemNumber = (int)SendMessageWorker(pcbox->spwndList, 01667 LB_FINDSTRING, (WPARAM)-1, (LPARAM)pText, FALSE); 01668 } finally { 01669 UserLocalFree((HANDLE)pText); 01670 } 01671 01672 if (itemNumber == -1) { 01673 01674 /* 01675 * No close match. Blow off. 01676 */ 01677 goto Unlock; 01678 } 01679 01680 cchItemText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN, 01681 itemNumber, 0, FALSE); 01682 if (cchItemText) { 01683 cchItemText++; 01684 if (!(pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchItemText*sizeof(WCHAR)))) 01685 goto Unlock; 01686 01687 /* 01688 * We want to be sure to free the above allocated memory even if 01689 * the client dies during callback (xxx) or some of the following 01690 * window revalidation fails. 01691 */ 01692 try { 01693 SendMessageWorker(pcbox->spwndList, LB_GETTEXT, 01694 itemNumber, (LPARAM)pText, FALSE); 01695 SendMessageWorker(pcbox->spwndEdit, WM_SETTEXT, 01696 0, (LPARAM)pText, FALSE); 01697 } finally { 01698 UserLocalFree((HANDLE)pText); 01699 } 01700 01701 SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, MAXLONG, !!TestWF(pcbox->spwnd, WFANSIPROC)); 01702 } 01703 } 01704 01705 Unlock: 01706 ThreadUnlock(&tlpwndList); 01707 ThreadUnlock(&tlpwndEdit); 01708 } 01709 01710 01711 /***************************************************************************\ 01712 * xxxCBHideListBoxWindow 01713 * 01714 * Hides the dropdown listbox window if it is a dropdown style. 01715 * 01716 * History: 01717 \***************************************************************************/ 01718 01719 BOOL xxxCBHideListBoxWindow( 01720 PCBOX pcbox, 01721 BOOL fNotifyParent, 01722 BOOL fSelEndOK) 01723 { 01724 HWND hwnd = HWq(pcbox->spwnd); 01725 HWND hwndList = HWq(pcbox->spwndList); 01726 TL tlpwndList; 01727 01728 01729 CheckLock(pcbox->spwnd); 01730 01731 // For 3.1+ apps, send CBN_SELENDOK to all types of comboboxes but only 01732 // allow CBN_SELENDCANCEL to be sent for droppable comboboxes 01733 if (fNotifyParent && TestWF(pcbox->spwnd, WFWIN31COMPAT) && 01734 ((pcbox->CBoxStyle & SDROPPABLE) || fSelEndOK)) { 01735 if (fSelEndOK) 01736 { 01737 xxxCBNotifyParent(pcbox, CBN_SELENDOK); 01738 } 01739 else 01740 { 01741 xxxCBNotifyParent(pcbox, CBN_SELENDCANCEL); 01742 } 01743 if (!IsWindow(hwnd)) 01744 return(FALSE); 01745 } 01746 01747 /* 01748 * return, we don't hide simple combo boxes. 01749 */ 01750 if (!(pcbox->CBoxStyle & SDROPPABLE)) { 01751 return TRUE; 01752 } 01753 01754 /* 01755 * Send a faked buttonup message to the listbox so that it can release 01756 * the capture and all. 01757 */ 01758 ThreadLock(pcbox->spwndList, &tlpwndList); 01759 01760 SendMessageWorker(pcbox->spwndList, LBCB_ENDTRACK, fSelEndOK, 0, FALSE); 01761 01762 if (pcbox->fLBoxVisible) { 01763 WORD swpFlags = SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE; 01764 01765 if (!TestWF(pcbox->spwnd, WFWIN31COMPAT)) 01766 swpFlags |= SWP_FRAMECHANGED; 01767 01768 pcbox->fLBoxVisible = FALSE; 01769 01770 /* 01771 * Hide the listbox window 01772 */ 01773 NtUserShowWindow(hwndList, SW_HIDE); 01774 01775 // 01776 // Invalidate the item area now since SWP() might update stuff. 01777 // Since the combo is CS_VREDRAW/CS_HREDRAW, a size change will 01778 // redraw the whole thing, including the item rect. But if it 01779 // isn't changing size, we still want to redraw the item anyway 01780 // to show focus/selection. 01781 // 01782 if (!(pcbox->CBoxStyle & SEDITABLE)) 01783 NtUserInvalidateRect(hwnd, &pcbox->editrc, TRUE); 01784 01785 NtUserSetWindowPos(hwnd, HWND_TOP, 0, 0, 01786 pcbox->cxCombo, pcbox->cyCombo, swpFlags); 01787 01788 // In case size didn't change 01789 UpdateWindow(hwnd); 01790 01791 if (pcbox->CBoxStyle & SEDITABLE) { 01792 xxxCBCompleteEditWindow(pcbox); 01793 } 01794 01795 if (fNotifyParent) { 01796 01797 /* 01798 * Notify parent we will be popping up the combo box. 01799 */ 01800 xxxCBNotifyParent(pcbox, CBN_CLOSEUP); 01801 if (!IsWindow(hwnd)) 01802 return(FALSE); 01803 } 01804 } 01805 01806 ThreadUnlock(&tlpwndList); 01807 01808 return(TRUE); 01809 } 01810 01811 /***************************************************************************\ 01812 * xxxCBShowListBoxWindow 01813 * 01814 * Lowers the dropdown listbox window. 01815 * 01816 * History: 01817 \***************************************************************************/ 01818 01819 void xxxCBShowListBoxWindow( 01820 PCBOX pcbox, BOOL fTrack) 01821 { 01822 RECT editrc; 01823 int itemNumber; 01824 int iHeight; 01825 int yTop; 01826 DWORD dwMult; 01827 int cyItem; 01828 HWND hwnd = HWq(pcbox->spwnd); 01829 HWND hwndList = HWq(pcbox->spwndList); 01830 BOOL fAnimPos; 01831 TL tlpwndList; 01832 PMONITOR pMonitor; 01833 01834 // 01835 // THIS FUNCTION IS ONLY CALLED FOR DROPPABLE LIST COMBOBOXES 01836 // 01837 UserAssert(pcbox->CBoxStyle & SDROPPABLE); 01838 01839 CheckLock(pcbox->spwnd); 01840 01841 ThreadLock(pcbox->spwndList, &tlpwndList); 01842 01843 /* 01844 * Notify parent we will be dropping down the combo box. 01845 */ 01846 01847 xxxCBNotifyParent(pcbox, CBN_DROPDOWN); 01848 /* 01849 * Invalidate the button rect so that the depressed arrow is drawn. 01850 */ 01851 NtUserInvalidateRect(hwnd, &pcbox->buttonrc, TRUE); 01852 01853 pcbox->fLBoxVisible = TRUE; 01854 01855 if (pcbox->CBoxStyle == SDROPDOWN) { 01856 01857 /* 01858 * If an item in the listbox matches the text in the edit control, 01859 * scroll it to the top of the listbox. Select the item only if the 01860 * mouse button isn't down otherwise we will select the item when the 01861 * mouse button goes up. 01862 */ 01863 xxxCBUpdateListBoxWindow(pcbox, !pcbox->fMouseDown); 01864 if (!pcbox->fMouseDown) 01865 xxxCBCompleteEditWindow(pcbox); 01866 } else { 01867 01868 /* 01869 * Scroll the currently selected item to the top of the listbox. 01870 */ 01871 itemNumber = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL, 01872 0, 0, FALSE); 01873 if (itemNumber == -1) { 01874 itemNumber = 0; 01875 } 01876 SendMessageWorker(pcbox->spwndList, LB_SETTOPINDEX, itemNumber, 0, FALSE); 01877 SendMessageWorker(pcbox->spwndList, LBCB_CARETON, 0, 0, FALSE); 01878 01879 /* 01880 * We need to invalidate the edit rect so that the focus frame/invert 01881 * will be turned off when the listbox is visible. Tandy wants this for 01882 * his typical reasons... 01883 */ 01884 NtUserInvalidateRect(hwnd, &pcbox->editrc, TRUE); 01885 } 01886 01887 // 01888 // Figure out where to position the dropdown listbox. We want it just 01889 // touching the edge around the edit rectangle. Note that since the 01890 // listbox is a popup, we need the position in screen coordinates. 01891 // 01892 01893 // We want the dropdown to pop below or above the combo 01894 01895 // Get screen coords 01896 editrc.left = pcbox->spwnd->rcWindow.left; 01897 editrc.top = pcbox->spwnd->rcWindow.top; 01898 editrc.right = pcbox->spwnd->rcWindow.left + pcbox->cxCombo; 01899 editrc.bottom = pcbox->spwnd->rcWindow.top + pcbox->cyCombo; 01900 01901 // List area 01902 cyItem = (int)SendMessageWorker(pcbox->spwndList, LB_GETITEMHEIGHT, 0, 0, FALSE); 01903 01904 if (cyItem == 0) { 01905 // Make sure that it's not 0 01906 RIPMSG0( RIP_WARNING, "LB_GETITEMHEIGHT is returning 0\n" ); 01907 01908 cyItem = gpsi->cySysFontChar; 01909 } 01910 01911 // we shoulda' just been able to use cyDrop here, but thanks to VB's need 01912 // to do things their OWN SPECIAL WAY, we have to keep monitoring the size 01913 // of the listbox 'cause VB changes it directly (jeffbog 03/21/94) 01914 iHeight = max(pcbox->cyDrop, pcbox->spwndList->rcWindow.bottom - 01915 pcbox->spwndList->rcWindow.top); 01916 01917 if (dwMult = (DWORD)SendMessageWorker(pcbox->spwndList, LB_GETCOUNT, 0, 0, FALSE)) { 01918 dwMult = (DWORD)(LOWORD(dwMult) * cyItem); 01919 dwMult += SYSMET(CYEDGE); 01920 01921 if (dwMult < 0x7FFF) 01922 iHeight = min(LOWORD(dwMult), iHeight); 01923 } 01924 01925 if (!TestWF(pcbox->spwnd, CBFNOINTEGRALHEIGHT)) { 01926 UserAssert(cyItem); 01927 iHeight = ((iHeight - SYSMET(CYEDGE)) / cyItem) * cyItem + SYSMET(CYEDGE); 01928 } 01929 01930 // 01931 // Other 1/2 of old app combo fix. Make dropdown overlap combo window 01932 // a little. That way we can have a chance of invalidating the overlap 01933 // and causing a repaint to help out Publisher 2.0's toolbar combos. 01934 // See comments for PressButton() above. 01935 // 01936 pMonitor = _MonitorFromWindow(pcbox->spwnd, MONITOR_DEFAULTTOPRIMARY); 01937 if (editrc.bottom + iHeight <= pMonitor->rcMonitor.bottom) { 01938 yTop = editrc.bottom; 01939 if (!pcbox->f3DCombo) 01940 yTop -= SYSMET(CYBORDER); 01941 01942 fAnimPos = TRUE; 01943 } else { 01944 yTop = max(editrc.top - iHeight, pMonitor->rcMonitor.top); 01945 if (!pcbox->f3DCombo) 01946 yTop += SYSMET(CYBORDER); 01947 01948 fAnimPos = FALSE; 01949 } 01950 01951 if ( ! TestWF( pcbox->spwnd, WFWIN40COMPAT) ) 01952 { 01953 // fix for Winword B#7504, Combo-ListBox text gets 01954 // truncated by a small width, this is do to us 01955 // now setting size here in SetWindowPos, rather than 01956 // earlier where we did this in Win3.1 01957 01958 if ( (pcbox->spwndList->rcWindow.right - pcbox->spwndList->rcWindow.left ) > 01959 pcbox->cxDrop ) 01960 01961 pcbox->cxDrop = pcbox->spwndList->rcWindow.right - pcbox->spwndList->rcWindow.left; 01962 } 01963 01964 NtUserSetWindowPos(hwndList, HWND_TOPMOST, editrc.left, 01965 yTop, max(pcbox->cxDrop, pcbox->cxCombo), iHeight, SWP_NOACTIVATE); 01966 01967 /* 01968 * Get any drawing in the combo box window out of the way so it doesn't 01969 * invalidate any of the SPB underneath the list window. 01970 */ 01971 UpdateWindow(hwnd); 01972 01973 if (!(TEST_EffectPUSIF(PUSIF_COMBOBOXANIMATION)) 01974 || (GetAppCompatFlags2(VER40) & GACF2_ANIMATIONOFF)) { 01975 NtUserShowWindow(hwndList, SW_SHOWNA); 01976 } else { 01977 AnimateWindow(hwndList, CMS_QANIMATION, (fAnimPos ? AW_VER_POSITIVE : 01978 AW_VER_NEGATIVE) | AW_SLIDE); 01979 } 01980 01981 #ifdef LATER 01982 // 01983 // we don't have sys modal windows. 01984 // 01985 if (pwndSysModal) { 01986 01987 /* 01988 * If this combo is in a system modal dialog box, we need to explicitly 01989 * call update window otherwise we won't automatically send paint 01990 * messages to the toplevel listbox window. This is especially 01991 * noticeable in the File Open/Save sys modal dlgs which are put up at 01992 * ExitWindows time. 01993 */ 01994 UpdateWindow(hwndList); 01995 } 01996 #endif 01997 01998 /* 01999 * Restart search buffer from first char 02000 */ 02001 { 02002 PLBIV plb = ((PLBWND)pcbox->spwndList)->pLBIV; 02003 02004 if ((plb != NULL) && (plb != (PLBIV)-1)) { 02005 plb->iTypeSearch = 0; 02006 } 02007 } 02008 02009 if (fTrack && TestWF(pcbox->spwnd, WFWIN40COMPAT)) 02010 SendMessageWorker(pcbox->spwndList, LBCB_STARTTRACK, FALSE, 0, FALSE); 02011 02012 ThreadUnlock(&tlpwndList); 02013 } 02014 02015 /***************************************************************************\ 02016 * xxxCBInternalUpdateEditWindow 02017 * 02018 * Updates the editcontrol/statictext window so that it contains the text 02019 * given by the current selection in the listbox. If the listbox has no 02020 * selection (ie. -1), then we erase all the text in the editcontrol. 02021 * 02022 * hdcPaint is from WM_PAINT messages Begin/End Paint hdc. If null, we should 02023 * get our own dc. 02024 * 02025 * History: 02026 \***************************************************************************/ 02027 02028 void xxxCBInternalUpdateEditWindow( 02029 PCBOX pcbox, 02030 HDC hdcPaint) 02031 { 02032 int cchText = 0; 02033 LPWSTR pText = NULL; 02034 int sItem; 02035 HDC hdc; 02036 UINT msg; 02037 HBRUSH hbrSave; 02038 HBRUSH hbrControl; 02039 HANDLE hOldFont; 02040 DRAWITEMSTRUCT dis; 02041 RECT rc; 02042 HWND hwnd = HWq(pcbox->spwnd); 02043 TL tlpwndList; 02044 TL tlpwndEdit; 02045 TL tlpwndParent; 02046 02047 CheckLock(pcbox->spwnd); 02048 02049 /* This check is also commented out in Win3.1 and Win95 */ 02050 // if (!TestWF(pcbox->spwnd, WFVISIBLE)) { 02051 // return; 02052 // } 02053 02054 ThreadLock(pcbox->spwndParent, &tlpwndParent); 02055 ThreadLock(pcbox->spwndList, &tlpwndList); 02056 ThreadLock(pcbox->spwndEdit, &tlpwndEdit); 02057 02058 sItem = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL, 0, 0, FALSE); 02059 02060 /* 02061 * This 'try-finally' block ensures that the allocated 'pText' will 02062 * be freed no matter how this routine is exited. 02063 */ 02064 try { 02065 if (sItem != -1) { 02066 cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN, 02067 (DWORD)sItem, 0, FALSE); 02068 if ((pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, (cchText+1) * sizeof(WCHAR)))) { 02069 cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXT, 02070 (DWORD)sItem, (LPARAM)pText, FALSE); 02071 } 02072 } 02073 02074 if (!pcbox->fNoEdit) { 02075 02076 if (pcbox->spwndEdit) { 02077 if (TestWF(pcbox->spwnd, CBFHASSTRINGS)) 02078 SetWindowText(HWq(pcbox->spwndEdit), pText ? pText : TEXT("")); 02079 02080 if (pcbox->fFocus) { 02081 /* 02082 * Only hilite the text if we have the focus. 02083 */ 02084 SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, MAXLONG, !!TestWF(pcbox->spwnd, WFANSIPROC)); 02085 } 02086 } 02087 } else if (IsComboVisible(pcbox)) { 02088 if (hdcPaint) { 02089 hdc = hdcPaint; 02090 } else { 02091 hdc = NtUserGetDC(hwnd); 02092 } 02093 02094 SetBkMode(hdc, OPAQUE); 02095 if (TestWF(pcbox->spwnd, WFWIN40COMPAT)) { 02096 if (TestWF(pcbox->spwnd, WFDISABLED)) 02097 msg = WM_CTLCOLORSTATIC; 02098 else 02099 msg = WM_CTLCOLOREDIT; 02100 } else 02101 msg = WM_CTLCOLORLISTBOX; 02102 02103 hbrControl = GetControlBrush(hwnd, hdc, msg); 02104 hbrSave = SelectObject(hdc, hbrControl); 02105 02106 CopyInflateRect(&rc, &pcbox->editrc, SYSMET(CXBORDER), SYSMET(CYBORDER)); 02107 PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, 02108 rc.bottom - rc.top, PATCOPY); 02109 InflateRect(&rc, -SYSMET(CXBORDER), -SYSMET(CYBORDER)); 02110 02111 if (pcbox->fFocus && !pcbox->fLBoxVisible) { 02112 // 02113 // Fill in the selected area 02114 // 02115 02116 02117 // only do the FillRect if we know its not 02118 // ownerdraw item, otherwise we mess up people up 02119 // BUT: for Compat's sake we still do this for Win 3.1 guys 02120 02121 if (!TestWF( pcbox->spwnd, WFWIN40COMPAT) || !pcbox->OwnerDraw) 02122 FillRect(hdc, &rc, SYSHBR(HIGHLIGHT)); 02123 02124 SetBkColor(hdc, SYSRGB(HIGHLIGHT)); 02125 SetTextColor(hdc, SYSRGB(HIGHLIGHTTEXT)); 02126 } else if (TestWF(pcbox->spwnd, WFDISABLED) && !pcbox->OwnerDraw) { 02127 if ((COLORREF)SYSRGB(GRAYTEXT) != GetBkColor(hdc)) 02128 SetTextColor(hdc, SYSRGB(GRAYTEXT)); 02129 } 02130 02131 if (pcbox->hFont != NULL) 02132 hOldFont = SelectObject(hdc, pcbox->hFont); 02133 02134 if (pcbox->OwnerDraw) { 02135 02136 /* 02137 * Let the app draw the stuff in the static text box. 02138 */ 02139 dis.CtlType = ODT_COMBOBOX; 02140 dis.CtlID = PtrToUlong(pcbox->spwnd->spmenu); 02141 dis.itemID = sItem; 02142 dis.itemAction = ODA_DRAWENTIRE; 02143 dis.itemState = (UINT) 02144 ((pcbox->fFocus && !pcbox->fLBoxVisible ? ODS_SELECTED : 0) | 02145 (TestWF(pcbox->spwnd, WFDISABLED) ? ODS_DISABLED : 0) | 02146 (pcbox->fFocus && !pcbox->fLBoxVisible ? ODS_FOCUS : 0) | 02147 (TestWF(pcbox->spwnd, WFWIN40COMPAT) ? ODS_COMBOBOXEDIT : 0) | 02148 (TestWF(pcbox->spwnd, WEFPUIFOCUSHIDDEN) ? ODS_NOFOCUSRECT : 0) | 02149 (TestWF(pcbox->spwnd, WEFPUIACCELHIDDEN) ? ODS_NOACCEL : 0)); 02150 02151 dis.hwndItem = hwnd; 02152 dis.hDC = hdc; 02153 CopyRect(&dis.rcItem, &rc); 02154 02155 // Don't let ownerdraw dudes draw outside of the combo client 02156 // bounds. 02157 IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom); 02158 02159 dis.itemData = (ULONG_PTR)SendMessageWorker(pcbox->spwndList, 02160 LB_GETITEMDATA, (UINT)sItem, 0, FALSE); 02161 02162 SendMessage(HW(pcbox->spwndParent), WM_DRAWITEM, dis.CtlID, 02163 (LPARAM)&dis); 02164 } else { 02165 02166 /* 02167 * Start the text one pixel within the rect so that we leave a 02168 * nice hilite border around the text. 02169 */ 02170 02171 int x ; 02172 UINT align ; 02173 02174 if (pcbox->fRightAlign ) { 02175 align = TA_RIGHT; 02176 x = rc.right - SYSMET(CXBORDER); 02177 } else { 02178 x = rc.left + SYSMET(CXBORDER); 02179 align = 0; 02180 } 02181 02182 if (pcbox->fRtoLReading ) 02183 align |= TA_RTLREADING; 02184 02185 if (align) 02186 SetTextAlign(hdc, GetTextAlign(hdc) | align); 02187 02188 // Draw the text, leaving a gap on the left & top for selection. 02189 ExtTextOut(hdc, x, rc.top + SYSMET(CYBORDER), ETO_CLIPPED | ETO_OPAQUE, 02190 &rc, pText ? pText : TEXT(""), cchText, NULL); 02191 if (pcbox->fFocus && !pcbox->fLBoxVisible) { 02192 if (!TestWF(pcbox->spwnd, WEFPUIFOCUSHIDDEN)) { 02193 DrawFocusRect(hdc, &rc); 02194 } 02195 } 02196 } 02197 02198 if (pcbox->hFont && hOldFont) { 02199 SelectObject(hdc, hOldFont); 02200 } 02201 02202 if (hbrSave) { 02203 SelectObject(hdc, hbrSave); 02204 } 02205 02206 if (!hdcPaint) { 02207 NtUserReleaseDC(hwnd, hdc); 02208 } 02209 } 02210 02211 } finally { 02212 if (pText != NULL) 02213 UserLocalFree((HANDLE)pText); 02214 } 02215 02216 ThreadUnlock(&tlpwndEdit); 02217 ThreadUnlock(&tlpwndList); 02218 ThreadUnlock(&tlpwndParent); 02219 } 02220 02221 /***************************************************************************\ 02222 * xxxCBInvertStaticWindow 02223 * 02224 * Inverts the static text/picture window associated with the combo 02225 * box. Gets its own hdc, if the one given is null. 02226 * 02227 * History: 02228 \***************************************************************************/ 02229 02230 void xxxCBInvertStaticWindow( 02231 PCBOX pcbox, 02232 BOOL fNewSelectionState, /* True if inverted else false */ 02233 HDC hdc) 02234 { 02235 BOOL focusSave = pcbox->fFocus; 02236 02237 CheckLock(pcbox->spwnd); 02238 02239 pcbox->fFocus = (UINT)fNewSelectionState; 02240 xxxCBInternalUpdateEditWindow(pcbox, hdc); 02241 02242 pcbox->fFocus = (UINT)focusSave; 02243 } 02244 02245 /***************************************************************************\ 02246 * xxxCBUpdateListBoxWindow 02247 * 02248 * matches the text in the editcontrol. If fSelectionAlso is false, then we 02249 * unselect the current listbox selection and just move the caret to the item 02250 * which is the closest match to the text in the editcontrol. 02251 * 02252 * History: 02253 \***************************************************************************/ 02254 02255 void xxxCBUpdateListBoxWindow( 02256 PCBOX pcbox, 02257 BOOL fSelectionAlso) 02258 { 02259 int cchText; 02260 int sItem, sSel; 02261 LPWSTR pText = NULL; 02262 TL tlpwndEdit; 02263 TL tlpwndList; 02264 02265 if (pcbox->spwndEdit == NULL) { 02266 return; 02267 } 02268 02269 CheckLock(pcbox->spwnd); 02270 02271 ThreadLock(pcbox->spwndList, &tlpwndList); 02272 ThreadLock(pcbox->spwndEdit, &tlpwndEdit); 02273 02274 /* 02275 * +1 for null terminator 02276 */ 02277 02278 cchText = (int)SendMessageWorker(pcbox->spwndEdit, WM_GETTEXTLENGTH, 0, 0, FALSE); 02279 02280 if (cchText) { 02281 cchText++; 02282 pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR)); 02283 if (pText != NULL) { 02284 try { 02285 SendMessageWorker(pcbox->spwndEdit, WM_GETTEXT, cchText, (LPARAM)pText, FALSE); 02286 sItem = (int)SendMessageWorker(pcbox->spwndList, LB_FINDSTRING, 02287 (WPARAM)-1L, (LPARAM)pText, FALSE); 02288 } finally { 02289 UserLocalFree((HANDLE)pText); 02290 } 02291 } 02292 } 02293 else 02294 sItem = -1; 02295 02296 if (fSelectionAlso) { 02297 sSel = sItem; 02298 } else { 02299 sSel = -1; 02300 } 02301 02302 if (sItem == -1) 02303 { 02304 sItem = 0; 02305 02306 // 02307 // Old apps: w/ editable combos, selected 1st item in list even if 02308 // it didn't match text in edit field. This is not desirable 02309 // behavior for 4.0 dudes esp. with cancel allowed. Reason: 02310 // (1) User types in text that doesn't match list choices 02311 // (2) User drops combo 02312 // (3) User pops combo back up 02313 // (4) User presses OK in dialog that does stuff w/ combo 02314 // contents. 02315 // In 3.1, when the combo dropped, we'd select the 1st item anyway. 02316 // So the last CBN_SELCHANGE the owner got would be 0--which is 02317 // bogus because it really should be -1. In fact if you type anything 02318 // into the combo afterwards it will reset itself to -1. 02319 // 02320 // 4.0 dudes won't get this bogus 0 selection. 02321 // 02322 if (fSelectionAlso && !TestWF(pcbox->spwnd, WFWIN40COMPAT)) 02323 sSel = 0; 02324 } 02325 02326 02327 SendMessageWorker(pcbox->spwndList, LB_SETCURSEL, (DWORD)sSel, 0, FALSE); 02328 SendMessageWorker(pcbox->spwndList, LB_SETCARETINDEX, (DWORD)sItem, 0, FALSE); 02329 SendMessageWorker(pcbox->spwndList, LB_SETTOPINDEX, (DWORD)sItem, 0, FALSE); 02330 02331 ThreadUnlock(&tlpwndEdit); 02332 ThreadUnlock(&tlpwndList); 02333 } 02334 02335 /***************************************************************************\ 02336 * xxxCBGetFocusHelper 02337 * 02338 * Handles getting the focus for the combo box 02339 * 02340 * History: 02341 \***************************************************************************/ 02342 02343 void xxxCBGetFocusHelper( 02344 PCBOX pcbox) 02345 { 02346 TL tlpwndList; 02347 TL tlpwndEdit; 02348 02349 CheckLock(pcbox->spwnd); 02350 02351 if (pcbox->fFocus) 02352 return; 02353 02354 ThreadLock(pcbox->spwndList, &tlpwndList); 02355 ThreadLock(pcbox->spwndEdit, &tlpwndEdit); 02356 02357 /* 02358 * The combo box has gotten the focus for the first time. 02359 */ 02360 02361 /* 02362 * First turn on the listbox caret 02363 */ 02364 02365 if (pcbox->CBoxStyle == SDROPDOWNLIST) 02366 SendMessageWorker(pcbox->spwndList, LBCB_CARETON, 0, 0, FALSE); 02367 02368 /* 02369 * and select all the text in the editcontrol or static text rectangle. 02370 */ 02371 02372 if (pcbox->fNoEdit) { 02373 02374 /* 02375 * Invert the static text rectangle 02376 */ 02377 xxxCBInvertStaticWindow(pcbox, TRUE, (HDC)NULL); 02378 } else if (pcbox->spwndEdit) { 02379 UserAssert(pcbox->spwnd); 02380 SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, MAXLONG, !!TestWF(pcbox->spwnd, WFANSIPROC)); 02381 } 02382 02383 pcbox->fFocus = TRUE; 02384 02385 /* 02386 * Notify the parent we have the focus 02387 */ 02388 xxxCBNotifyParent(pcbox, CBN_SETFOCUS); 02389 02390 ThreadUnlock(&tlpwndEdit); 02391 ThreadUnlock(&tlpwndList); 02392 } 02393 02394 /***************************************************************************\ 02395 * xxxCBKillFocusHelper 02396 * 02397 * Handles losing the focus for the combo box. 02398 * 02399 * History: 02400 \***************************************************************************/ 02401 02402 void xxxCBKillFocusHelper( 02403 PCBOX pcbox) 02404 { 02405 TL tlpwndList; 02406 TL tlpwndEdit; 02407 02408 CheckLock(pcbox->spwnd); 02409 02410 if (!pcbox->fFocus || pcbox->spwndList == NULL) 02411 return; 02412 02413 ThreadLock(pcbox->spwndList, &tlpwndList); 02414 ThreadLock(pcbox->spwndEdit, &tlpwndEdit); 02415 02416 /* 02417 * The combo box is losing the focus. Send buttonup clicks so that 02418 * things release the mouse capture if they have it... If the 02419 * pwndListBox is null, don't do anything. This occurs if the combo box 02420 * is destroyed while it has the focus. 02421 */ 02422 SendMessageWorker(pcbox->spwnd, WM_LBUTTONUP, 0L, 0xFFFFFFFFL, FALSE); 02423 if (!xxxCBHideListBoxWindow(pcbox, TRUE, FALSE)) 02424 return; 02425 02426 /* 02427 * Turn off the listbox caret 02428 */ 02429 02430 if (pcbox->CBoxStyle == SDROPDOWNLIST) 02431 SendMessageWorker(pcbox->spwndList, LBCB_CARETOFF, 0, 0, FALSE); 02432 02433 if (pcbox->fNoEdit) { 02434 02435 /* 02436 * Invert the static text rectangle 02437 */ 02438 xxxCBInvertStaticWindow(pcbox, FALSE, (HDC)NULL); 02439 } else if (pcbox->spwndEdit) { 02440 SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, 0, !!TestWF(pcbox->spwnd, WFANSIPROC)); 02441 } 02442 02443 pcbox->fFocus = FALSE; 02444 xxxCBNotifyParent(pcbox, CBN_KILLFOCUS); 02445 02446 ThreadUnlock(&tlpwndEdit); 02447 ThreadUnlock(&tlpwndList); 02448 } 02449 02450 02451 /***************************************************************************\ 02452 * xxxCBGetTextLengthHelper 02453 * 02454 * For the combo box without an edit control, returns size of current selected 02455 * item 02456 * 02457 * History: 02458 \***************************************************************************/ 02459 02460 LONG xxxCBGetTextLengthHelper( 02461 PCBOX pcbox, 02462 BOOL fAnsi) 02463 { 02464 int item; 02465 int cchText; 02466 TL tlpwndList; 02467 02468 ThreadLock(pcbox->spwndList, &tlpwndList); 02469 item = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL, 0, 0, fAnsi); 02470 02471 if (item == LB_ERR) { 02472 02473 /* 02474 * No selection so no text. 02475 */ 02476 cchText = 0; 02477 } else { 02478 cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN, 02479 item, 0, fAnsi); 02480 } 02481 02482 ThreadUnlock(&tlpwndList); 02483 02484 return cchText; 02485 } 02486 02487 /***************************************************************************\ 02488 * xxxCBGetTextHelper 02489 * 02490 * For the combo box without an edit control, copies cbString bytes of the 02491 * string in the static text box to the buffer given by pString. 02492 * 02493 * History: 02494 \***************************************************************************/ 02495 02496 LONG xxxCBGetTextHelper( 02497 PCBOX pcbox, 02498 int cchString, 02499 LPWSTR pString, 02500 BOOL fAnsi) 02501 { 02502 int item; 02503 int cchText; 02504 LPWSTR pText; 02505 DWORD dw; 02506 TL tlpwndList; 02507 02508 CheckLock(pcbox->spwnd); 02509 02510 if (!cchString || !pString) 02511 return 0; 02512 02513 /* 02514 * Null the buffer to be nice. 02515 */ 02516 if (fAnsi) { 02517 *((LPSTR)pString) = 0; 02518 } else { 02519 *((LPWSTR)pString) = 0; 02520 } 02521 02522 ThreadLock(pcbox->spwndList, &tlpwndList); 02523 item = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL, 0, 0, fAnsi); 02524 02525 if (item == LB_ERR) { 02526 02527 /* 02528 * No selection so no text. 02529 */ 02530 ThreadUnlock(&tlpwndList); 02531 return 0; 02532 } 02533 02534 cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN, item, 0, fAnsi); 02535 02536 cchText++; 02537 if ((cchText <= cchString) || 02538 (!TestWF(pcbox->spwnd, WFWIN31COMPAT) && cchString == 2)) { 02539 /* 02540 * Just do the copy if the given buffer size is large enough to hold 02541 * everything. Or if old 3.0 app. (Norton used to pass 2 & expect 3 02542 * chars including the \0 in 3.0; Bug #7018 win31: vatsanp) 02543 */ 02544 dw = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXT, item, 02545 (LPARAM)pString, fAnsi); 02546 ThreadUnlock(&tlpwndList); 02547 return dw; 02548 } 02549 02550 if (!(pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR)))) { 02551 02552 /* 02553 * Bail. Not enough memory to chop up the text. 02554 */ 02555 ThreadUnlock(&tlpwndList); 02556 return 0; 02557 } 02558 02559 try { 02560 SendMessageWorker(pcbox->spwndList, LB_GETTEXT, item, (LPARAM)pText, fAnsi); 02561 if (fAnsi) { 02562 RtlCopyMemory((PBYTE)pString, (PBYTE)pText, cchString); 02563 ((LPSTR)pString)[cchString - 1] = 0; 02564 } else { 02565 RtlCopyMemory((PBYTE)pString, (PBYTE)pText, cchString * sizeof(WCHAR)); 02566 ((LPWSTR)pString)[cchString - 1] = 0; 02567 } 02568 } finally { 02569 UserLocalFree((HANDLE)pText); 02570 } 02571 02572 ThreadUnlock(&tlpwndList); 02573 return cchString; 02574 }

Generated on Sat May 15 19:39:30 2004 for test by doxygen 1.3.7