test: combo.c Source File

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 }