|
|
/**************************** Module Header ********************************\
* Module Name: combo.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * The WndProc for combo boxes and other often used combo routines * * History: * ??-???-???? ?????? Ported from Win 3.0 sources * 01-Feb-1991 mikeke Added Revalidation code \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
LOOKASIDE ComboboxLookaside;
BOOL NtUserTrackMouseEvent(TRACKMOUSEEVENT *ptme); LONG xxxCBGetTextLengthHelper(PCBOX pcbox, BOOL fAnsi); LONG xxxCBGetTextHelper(PCBOX pcbox, int len, LPWSTR lpstr, BOOL fAnsi);
/***************************************************************************\
* * PressButton() * * Pops combobox button back up. * \***************************************************************************/ void xxxPressButton(PCBOX pcbox, BOOL fPress) { //
// Publisher relies on getting a WM_PAINT message after the combo list
// pops back up. On a WM_PAINT they change the focus, which causes
// toolbar combos to send CBN_SELENDCANCEL notifications. On this
// notification they apply the font/pt size change you made to the
// selection.
//
// This happened in 3.1 because the dropdown list overlapped the button
// on the bottom or top by a pixel. Since we'd end up painting under
// the list SPB, when it went away USER would reinvalidate the dirty
// area. This would cause a paint message.
//
// In 4.0, this doesn't happen because the dropdown doesn't overlap. So
// we need to make sure Publisher gets a WM_PAINT anyway. We do this
// by changing where the dropdown shows up for 3.x apps
//
//
if ((pcbox->fButtonPressed != 0) != (fPress != 0)) {
HWND hwnd = HWq(pcbox->spwnd);
pcbox->fButtonPressed = (fPress != 0); if (pcbox->f3DCombo) NtUserInvalidateRect(hwnd, KPRECT_TO_PRECT(&pcbox->buttonrc), TRUE); else { RECT rc;
CopyRect(&rc, KPRECT_TO_PRECT(&pcbox->buttonrc)); InflateRect(&rc, 0, SYSMET(CYEDGE)); NtUserInvalidateRect(hwnd, &rc, TRUE); } UpdateWindow(hwnd);
NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEX_COMBOBOX_BUTTON); } }
/***************************************************************************\
* HotTrack * * If we're not already hot-tracking and the mouse is over the combobox, * turn on hot-tracking and invalidate the drop-down button. * \***************************************************************************/
#ifdef COLOR_HOTTRACKING
void HotTrack(PCBOX pcbox) { if (!pcbox->fButtonHotTracked && !pcbox->fMouseDown) { HWND hwnd = HWq(pcbox->spwnd); TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT), TME_LEAVE, hwnd, 0}; if (NtUserTrackMouseEvent(&tme)) { pcbox->fButtonHotTracked = TRUE; NtUserInvalidateRect(hwnd, &pcbox->buttonrc, TRUE); } } }
#endif // COLOR_HOTTRACKING
/***************************************************************************\
* xxxComboBoxDBCharHandler * * Double Byte character handler for ANSI ComboBox * * History: \***************************************************************************/
LRESULT ComboBoxDBCharHandler( PCBOX pcbox, HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { WORD w; PWND pwndSend;
w = DbcsCombine(hwnd, (BYTE)wParam); if (w == 0) { return CB_ERR; // Failed to assemble DBCS
}
UserAssert(pcbox->spwndList); if (pcbox->fNoEdit) { pwndSend = pcbox->spwndList; } else if (pcbox->spwndEdit) { RIPMSG1(RIP_WARNING, "ComboBoxWndProcWorker: WM_CHAR is posted to Combobox itself(%08x).", hwnd); pwndSend = pcbox->spwndEdit; } else { return CB_ERR; }
RIPMSG1(RIP_VERBOSE, "ComboBoxWndProcWorker: sending WM_CHAR %04x", w);
if (!TestWF(pwndSend, WFANSIPROC)) { //
// If receiver is not ANSI WndProc (may be subclassed?),
// send a UNICODE message.
//
WCHAR wChar; LPWSTR lpwstr = &wChar;
if (MBToWCSEx(THREAD_CODEPAGE(), (LPCSTR)&w, 2, &lpwstr, 1, FALSE) == 0) { RIPMSG1(RIP_WARNING, "ComboBoxWndProcWorker: cannot convert 0x%04x to UNICODE.", w); return CB_ERR; } return SendMessageWorker(pwndSend, message, wChar, lParam, FALSE); }
/*
* Post the Trailing byte to the target * so that they can peek the second WM_CHAR * message later. * Note: it's safe since sender is A and receiver is A, * translation layer does not perform any DBCS combining and cracking. */ PostMessageA(HWq(pwndSend), message, CrackCombinedDbcsTB(w), lParam); return SendMessageWorker(pwndSend, message, wParam, lParam, TRUE); }
BOOL ComboBoxMsgOKInInit(UINT message, LRESULT* plRet) { switch (message) { default: break; case WM_SIZE: *plRet = 0; return FALSE; case WM_STYLECHANGED: case WM_GETTEXT: case WM_GETTEXTLENGTH: case WM_PRINT: case WM_COMMAND: case CBEC_KILLCOMBOFOCUS: case WM_PRINTCLIENT: case WM_SETFONT: case WM_SYSKEYDOWN: case WM_KEYDOWN: case WM_CHAR: case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: case WM_MOUSEWHEEL: case WM_CAPTURECHANGED: case WM_LBUTTONUP: case WM_MOUSEMOVE: case WM_SETFOCUS: case WM_KILLFOCUS: case WM_SETREDRAW: case WM_ENABLE: case CB_SETDROPPEDWIDTH: case CB_DIR: case CB_ADDSTRING: /*
* Cannot handle those messages yet. Bail out. */ *plRet = CB_ERR; return FALSE; } return TRUE; }
/***************************************************************************\
* xxxComboBoxCtlWndProc * * Class procedure for all combo boxes * * History: \***************************************************************************/
LRESULT APIENTRY ComboBoxWndProcWorker( PWND pwnd, UINT message, WPARAM wParam, LPARAM lParam, DWORD fAnsi) { HWND hwnd = HWq(pwnd); PCBOX pcbox; POINT pt; TL tlpwndEdit; TL tlpwndList; PAINTSTRUCT ps; LPWSTR lpwsz = NULL; LRESULT lReturn; static BOOL fInit = TRUE; int i;
CheckLock(pwnd);
VALIDATECLASSANDSIZE(pwnd, FNID_COMBOBOX); INITCONTROLLOOKASIDE(&ComboboxLookaside, CBOX, spwnd, 8);
/*
* Get the pcbox for the given window now since we will use it a lot in * various handlers. This is stored by NtUserSetWindowLongPtr() in the * INITCONTROLLOOKASIDE macro above. */ pcbox = ((PCOMBOWND)pwnd)->pcbox;
/*
* Protect the combobox during the initialization. */ if (pcbox->spwndList == NULL) { LRESULT lRet;
if (!ComboBoxMsgOKInInit(message, &lRet)) { RIPMSG2(RIP_WARNING, "ComboBoxWndProcWorker: msg=%04x is sent to hwnd=%08x in the middle of initialization.", message, hwnd); return lRet; } }
/*
* Dispatch the various messages we can receive */ switch (message) { case CBEC_KILLCOMBOFOCUS:
/*
* Private message coming from editcontrol informing us that the combo * box is losing the focus to a window which isn't in this combo box. */ xxxCBKillFocusHelper(pcbox); break;
case WM_COMMAND:
/*
* So that we can handle notification messages from the listbox and * edit control. */ return xxxCBCommandHandler(pcbox, (DWORD)wParam, (HWND)lParam);
case WM_STYLECHANGED: UserAssert(pcbox->spwndList != NULL); { LONG OldStyle; LONG NewStyle = 0;
pcbox->fRtoLReading = (TestWF(pwnd, WEFRTLREADING) != 0); pcbox->fRightAlign = (TestWF(pwnd, WEFRIGHT) != 0); if (pcbox->fRtoLReading) NewStyle |= (WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR); if (pcbox->fRightAlign) NewStyle |= WS_EX_RIGHT;
ThreadLock(pcbox->spwndList, &tlpwndList); OldStyle = GetWindowLong(HWq(pcbox->spwndList), GWL_EXSTYLE) & ~(WS_EX_RIGHT|WS_EX_RTLREADING|WS_EX_LEFTSCROLLBAR); SetWindowLong(HWq(pcbox->spwndList), GWL_EXSTYLE, OldStyle|NewStyle); ThreadUnlock(&tlpwndList);
if (!pcbox->fNoEdit && pcbox->spwndEdit) { ThreadLock(pcbox->spwndEdit, &tlpwndEdit); OldStyle = GetWindowLong(HWq(pcbox->spwndEdit), GWL_EXSTYLE) & ~(WS_EX_RIGHT|WS_EX_RTLREADING|WS_EX_LEFTSCROLLBAR); SetWindowLong(HWq(pcbox->spwndEdit), GWL_EXSTYLE, OldStyle|NewStyle); ThreadUnlock(&tlpwndEdit); } xxxCBPosition(pcbox); NtUserInvalidateRect(hwnd, NULL, FALSE); } break;
case WM_CTLCOLORMSGBOX: case WM_CTLCOLOREDIT: case WM_CTLCOLORLISTBOX: case WM_CTLCOLORBTN: case WM_CTLCOLORDLG: case WM_CTLCOLORSCROLLBAR: case WM_CTLCOLORSTATIC: case WM_CTLCOLOR: //
// Causes compatibility problems for 3.X apps. Forward only
// for 4.0
//
if (TestWF(pwnd, WFWIN40COMPAT)) { TL tlpwndParent; LRESULT ret; PWND pwndParent;
pwndParent = REBASEPWND(pwnd, spwndParent); ThreadLock(pwndParent, &tlpwndParent); ret = SendMessage(HW(pwndParent), message, wParam, lParam); ThreadUnlock(tlpwndParent); return ret; } else return(DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi)); break;
case WM_GETTEXT: if (pcbox->fNoEdit) { return xxxCBGetTextHelper(pcbox, (int)wParam, (LPWSTR)lParam, fAnsi); } goto CallEditSendMessage; break;
case WM_GETTEXTLENGTH:
/*
* If the is not edit control, CBS_DROPDOWNLIST, then we have to * ask the list box for the size */
if (pcbox->fNoEdit) { return xxxCBGetTextLengthHelper(pcbox, fAnsi); }
// FALL THROUGH
case WM_CLEAR: case WM_CUT: case WM_PASTE: case WM_COPY: case WM_SETTEXT: goto CallEditSendMessage; break;
case WM_CREATE:
/*
* wParam - not used * lParam - Points to the CREATESTRUCT data structure for the window. */ return xxxCBCreateHandler(pcbox, pwnd);
case WM_ERASEBKGND:
/*
* Just return 1L so that the background isn't erased */ return 1L;
case WM_GETFONT: return (LRESULT)pcbox->hFont;
case WM_PRINT: if (!DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi)) return(FALSE);
if ((lParam & PRF_OWNED) && (pcbox->CBoxStyle & SDROPPABLE) && TestWF(pcbox->spwndList, WFVISIBLE)) { TL tpwndList; int iDC = SaveDC((HDC) wParam); OffsetWindowOrgEx((HDC) wParam, 0, pwnd->rcWindow.top - pcbox->spwndList->rcWindow.top, NULL); lParam &= ~PRF_CHECKVISIBLE; ThreadLock(pcbox->spwndList, &tpwndList); SendMessageWorker(pcbox->spwndList, WM_PRINT, wParam, lParam, FALSE); RestoreDC((HDC) wParam, iDC); } return TRUE;
case WM_PRINTCLIENT: xxxCBPaint(pcbox, (HDC) wParam); break;
case WM_PAINT: { HDC hdc;
/*
* wParam - perhaps a hdc */ hdc = (wParam) ? (HDC) wParam : NtUserBeginPaint(hwnd, &ps);
if (IsComboVisible(pcbox)) xxxCBPaint(pcbox, hdc);
if (!wParam) NtUserEndPaint(hwnd, &ps); break; } case WM_GETDLGCODE:
/*
* wParam - not used * lParam - not used */ { LRESULT code = DLGC_WANTCHARS | DLGC_WANTARROWS;
// If the listbox is dropped and the ENTER key is pressed,
// we want this message so we can close up the listbox
if ((lParam != 0) && (((LPMSG)lParam)->message == WM_KEYDOWN) && pcbox->fLBoxVisible && ((wParam == VK_RETURN) || (wParam == VK_ESCAPE))) { code |= DLGC_WANTMESSAGE; } return code; } /*
* No fall through */
case WM_SETFONT: xxxCBSetFontHandler(pcbox, (HANDLE)wParam, LOWORD(lParam)); break;
case WM_SYSKEYDOWN: if (lParam & 0x20000000L) /* Check if the alt key is down */ {
/*
* Handle Combobox support. We want alt up or down arrow to behave * like F4 key which completes the combo box selection */ if (lParam & 0x1000000) {
/*
* This is an extended key such as the arrow keys not on the * numeric keypad so just drop the combobox. */ if (wParam == VK_DOWN || wParam == VK_UP) goto DropCombo;
goto CallDWP; }
if (GetKeyState(VK_NUMLOCK) & 0x1) { /*
* If numlock down, just send all system keys to dwp */ goto CallDWP; } else {
/*
* We just want to ignore keys on the number pad... */ if (!(wParam == VK_DOWN || wParam == VK_UP)) goto CallDWP; } DropCombo: if (!pcbox->fLBoxVisible) {
/*
* If the listbox isn't visible, just show it */ xxxCBShowListBoxWindow(pcbox, TRUE); } else {
/*
* Ok, the listbox is visible. So hide the listbox window. */ if (!xxxCBHideListBoxWindow(pcbox, TRUE, TRUE)) return(0L); } } goto CallDWP; break;
case WM_KEYDOWN: /*
* If the listbox is dropped and the ENTER key is pressed, * close up the listbox successfully. If ESCAPE is pressed, * close it up like cancel. */ if (pcbox->fLBoxVisible) { if ((wParam == VK_RETURN) || (wParam == VK_ESCAPE)) { xxxCBHideListBoxWindow(pcbox, TRUE, (wParam != VK_ESCAPE)); break; } } // FALL THROUGH
case WM_CHAR: if (fAnsi && IS_DBCS_ENABLED() && IsDBCSLeadByteEx(THREAD_CODEPAGE(), (BYTE)wParam)) { return ComboBoxDBCharHandler(pcbox, hwnd, message, wParam, lParam); }
if (pcbox->fNoEdit) { goto CallListSendMessage; } else goto CallEditSendMessage; break;
case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN:
#ifdef COLOR_HOTTRACKING
pcbox->fButtonHotTracked = FALSE; #endif // COLOR_HOTTRACKING
/*
* Set the focus to the combo box if we get a mouse click on it. */ if (!pcbox->fFocus) { NtUserSetFocus(hwnd); if (!pcbox->fFocus) {
/*
* Don't do anything if we still don't have the focus. */ break; } }
/*
* If user clicked in button rect and we are a combobox with edit, then * drop the listbox. (The button rect is 0 if there is no button so the * ptinrect will return false.) If a drop down list (no edit), clicking * anywhere on the face causes the list to drop. */
POINTSTOPOINT(pt, lParam); if ((pcbox->CBoxStyle == SDROPDOWN && PtInRect(KPRECT_TO_PRECT(&pcbox->buttonrc), pt)) || pcbox->CBoxStyle == SDROPDOWNLIST) {
/*
* Set the fMouseDown flag so that we can handle clicking on * the popdown button and dragging into the listbox (when it just * dropped down) to make a selection. */ pcbox->fButtonPressed = TRUE; if (pcbox->fLBoxVisible) { if (pcbox->fMouseDown) { pcbox->fMouseDown = FALSE; NtUserReleaseCapture(); } xxxPressButton(pcbox, FALSE);
if (!xxxCBHideListBoxWindow(pcbox, TRUE, TRUE)) return(0L); } else { xxxCBShowListBoxWindow(pcbox, FALSE);
// Setting and resetting this flag must always be followed
// imediately by SetCapture or ReleaseCapture
//
pcbox->fMouseDown = TRUE; NtUserSetCapture(hwnd); NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEX_COMBOBOX_BUTTON); } } break;
case WM_MOUSEWHEEL: /*
* Handle only scrolling. */ if (wParam & (MK_CONTROL | MK_SHIFT)) goto CallDWP;
/*
* If the listbox is visible, send it the message to scroll. */ if (pcbox->fLBoxVisible) goto CallListSendMessage;
/*
* If we're in extended UI mode or the edit control isn't yet created, * bail. */ if (pcbox->fExtendedUI || pcbox->spwndEdit == NULL) return TRUE;
/*
* Emulate arrow up/down messages to the edit control. */ i = abs(((short)HIWORD(wParam))/WHEEL_DELTA); wParam = ((short)HIWORD(wParam) > 0) ? VK_UP : VK_DOWN;
ThreadLock(pcbox->spwndEdit, &tlpwndEdit); while (i-- > 0) { SendMessageWorker( pcbox->spwndEdit, WM_KEYDOWN, wParam, 0, fAnsi); } ThreadUnlock(&tlpwndEdit); return TRUE;
case WM_CAPTURECHANGED: if (!(TestWF(pwnd, WFWIN40COMPAT))) return 0;
if ((pcbox->fMouseDown)) { pcbox->fMouseDown = FALSE; xxxPressButton(pcbox, FALSE);
//
// Pop combo listbox back up, canceling.
//
if (pcbox->fLBoxVisible) xxxCBHideListBoxWindow(pcbox, TRUE, FALSE); } break;
case WM_LBUTTONUP: xxxPressButton(pcbox, FALSE);
/*
* Clear this flag so that mouse moves aren't sent to the listbox */ if (pcbox->fMouseDown) { pcbox->fMouseDown = FALSE;
if (pcbox->CBoxStyle == SDROPDOWN) { // If an item in the listbox matches the text in the edit
// control, scroll it to the top of the listbox. Select the
// item only if the mouse button isn't down otherwise we
// will select the item when the mouse button goes up.
xxxCBUpdateListBoxWindow(pcbox, TRUE); xxxCBCompleteEditWindow(pcbox); } NtUserReleaseCapture();
// Now, we want listbox to track mouse moves while mouse up
// until mouse down, and select items as though they were
// clicked on.
if (TestWF(pwnd, WFWIN40COMPAT)) {
ThreadLock(pcbox->spwndList, &tlpwndList); SendMessageWorker(pcbox->spwndList, LBCB_STARTTRACK, FALSE, 0, FALSE); ThreadUnlock(&tlpwndList); } } #ifdef COLOR_HOTTRACKING
HotTrack(pcbox); break;
case WM_MOUSELEAVE: pcbox->fButtonHotTracked = FALSE; NtUserInvalidateRect(hwnd, &pcbox->buttonrc, TRUE); #endif // COLOR_HOTTRACKING
break;
case WM_MOUSEMOVE: if (pcbox->fMouseDown) { POINTSTOPOINT(pt, lParam);
// Note conversion of INT bit field to BOOL (1 or 0)
if (PtInRect(KPRECT_TO_PRECT(&pcbox->buttonrc), pt) != !!pcbox->fButtonPressed) { xxxPressButton(pcbox, (pcbox->fButtonPressed == 0)); }
_ClientToScreen(pwnd, &pt); if (PtInRect(KPRECT_TO_PRECT(&pcbox->spwndList->rcClient), pt)) {
/*
* This handles dropdown comboboxes/listboxes so that clicking * on the dropdown button and dragging into the listbox window * will let the user make a listbox selection. */ pcbox->fMouseDown = FALSE; NtUserReleaseCapture();
if (pcbox->CBoxStyle & SEDITABLE) {
/*
* If an item in the listbox matches the text in the edit * control, scroll it to the top of the listbox. Select the * item only if the mouse button isn't down otherwise we * will select the item when the mouse button goes up. */
/*
* We need to select the item which matches the editcontrol * so that if the user drags out of the listbox, we don't * cancel back to his origonal selection */ xxxCBUpdateListBoxWindow(pcbox, TRUE); }
/*
* Convert point to listbox coordinates and send a buttondown * message to the listbox window. */ _ScreenToClient(pcbox->spwndList, &pt); lParam = POINTTOPOINTS(pt); message = WM_LBUTTONDOWN; goto CallListSendMessage; } } #ifdef COLOR_HOTTRACKING
HotTrack(pcbox); #endif // COLOR_HOTTRACKING
break;
case WM_NCDESTROY: case WM_FINALDESTROY: xxxCBNcDestroyHandler(pwnd, pcbox); break;
case WM_SETFOCUS: if (pcbox->fNoEdit) {
/*
* There is no editcontrol so set the focus to the combo box itself. */ xxxCBGetFocusHelper(pcbox); } else if (pcbox->spwndEdit) { /*
* Set the focus to the edit control window if there is one */ ThreadLock(pcbox->spwndEdit, &tlpwndEdit); NtUserSetFocus(HWq(pcbox->spwndEdit)); ThreadUnlock(&tlpwndEdit); } break;
case WM_KILLFOCUS:
/*
* wParam has the new focus hwnd */ if (wParam != 0) wParam = (WPARAM)ValidateHwnd((HWND)wParam); if ((wParam == 0) || !_IsChild(pwnd, (PWND)wParam)) {
/*
* We only give up the focus if the new window getting the focus * doesn't belong to the combo box. */ xxxCBKillFocusHelper(pcbox); }
UserAssert(pcbox->spwndList); { PLBIV plb = ((PLBWND)pcbox->spwndList)->pLBIV;
if ((plb != NULL) && (plb != (PLBIV)-1)) { plb->iTypeSearch = 0; if (plb->pszTypeSearch) { UserLocalFree(plb->pszTypeSearch); plb->pszTypeSearch = NULL; } } } break;
case WM_SETREDRAW:
/*
* wParam - specifies state of the redraw flag. nonzero = redraw * lParam - not used */
/*
* effects: Sets the state of the redraw flag for this combo box * and its children. */ pcbox->fNoRedraw = (UINT)!((BOOL)wParam);
/*
* Must check pcbox->spwnEdit in case we get this message before * WM_CREATE - PCBOX won't be initialized yet. (Eudora does this) */ if (!pcbox->fNoEdit && pcbox->spwndEdit) { ThreadLock(pcbox->spwndEdit, &tlpwndEdit); SendMessageWorker(pcbox->spwndEdit, message, wParam, lParam, FALSE); ThreadUnlock(&tlpwndEdit); } goto CallListSendMessage; break;
case WM_ENABLE:
/*
* Invalidate the rect to cause it to be drawn in grey for its * disabled view or ungreyed for non-disabled view. */ NtUserInvalidateRect(hwnd, NULL, FALSE); if ((pcbox->CBoxStyle & SEDITABLE) && pcbox->spwndEdit) {
/*
* Enable/disable the edit control window */ ThreadLock(pcbox->spwndEdit, &tlpwndEdit); NtUserEnableWindow(HWq(pcbox->spwndEdit), (TestWF(pwnd, WFDISABLED) == 0)); ThreadUnlock(&tlpwndEdit); }
/*
* Enable/disable the listbox window */ UserAssert(pcbox->spwndList); ThreadLock(pcbox->spwndList, &tlpwndList); NtUserEnableWindow(HWq(pcbox->spwndList), (TestWF(pwnd, WFDISABLED) == 0)); ThreadUnlock(&tlpwndList); break;
case WM_SIZE:
/*
* wParam - defines the type of resizing fullscreen, sizeiconic, * sizenormal etc. * lParam - new width in LOWORD, new height in HIGHUINT of client area */ UserAssert(pcbox->spwndList); if (LOWORD(lParam) == 0 || HIWORD(lParam) == 0) {
/*
* If being sized to a zero width or to a zero height or we aren't * fully initialized, just return. */ return 0; }
// OPTIMIZATIONS -- first check if old and new widths are the same
if (pcbox->cxCombo == pwnd->rcWindow.right - pwnd->rcWindow.left) { int iNewHeight = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
// now check if new height is the dropped down height
if (pcbox->fLBoxVisible) { // Check if new height is the full size height
if (pcbox->cyDrop + pcbox->cyCombo == iNewHeight) return(0L); } else { // Check if new height is the closed up height
if (pcbox->cyCombo == iNewHeight) return(0L); } }
xxxCBSizeHandler(pcbox); break;
case CB_GETDROPPEDSTATE:
/*
* returns 1 if combo is dropped down else 0 * wParam - not used * lParam - not used */ return pcbox->fLBoxVisible;
case CB_GETDROPPEDCONTROLRECT:
/*
* wParam - not used * lParam - lpRect which will get the dropped down window rect in * screen coordinates. */ ((LPRECT)lParam)->left = pwnd->rcWindow.left; ((LPRECT)lParam)->top = pwnd->rcWindow.top; ((LPRECT)lParam)->right = pwnd->rcWindow.left + max(pcbox->cxDrop, pcbox->cxCombo); ((LPRECT)lParam)->bottom = pwnd->rcWindow.top + pcbox->cyCombo + pcbox->cyDrop; break;
case CB_SETDROPPEDWIDTH: if (pcbox->CBoxStyle & SDROPPABLE) { if (wParam) { wParam = max(wParam, (UINT)pcbox->cxCombo);
if (wParam != (UINT) pcbox->cxDrop) { pcbox->cxDrop = (int)wParam; xxxCBPosition(pcbox); } } } // fall thru
case CB_GETDROPPEDWIDTH: if (pcbox->CBoxStyle & SDROPPABLE) return((LRESULT) max(pcbox->cxDrop, pcbox->cxCombo)); else return(CB_ERR); break;
case CB_DIR: /*
* wParam - Dos attribute value. * lParam - Points to a file specification string */ if (fAnsi && lParam != 0) { if (MBToWCS((LPSTR)lParam, -1, &lpwsz, -1, TRUE) == 0) return CB_ERR; lParam = (LPARAM)lpwsz; } lReturn = xxxCBDir(pcbox, LOWORD(wParam), (LPWSTR)lParam); if (fAnsi && lParam != 0) { UserLocalFree(lpwsz); } return lReturn;
case CB_SETEXTENDEDUI:
/*
* wParam - specifies state to set extendui flag to. * Currently only 1 is allowed. Return CB_ERR (-1) if * failure else 0 if success. */ if (pcbox->CBoxStyle & SDROPPABLE) { if (!wParam) { pcbox->fExtendedUI = 0; return 0; }
if (wParam == 1) { pcbox->fExtendedUI = 1; return 0; }
RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "Invalid parameter \"wParam\" (%ld) to ComboBoxWndProcWorker", wParam);
} else { RIPERR1(ERROR_INVALID_MESSAGE, RIP_WARNING, "Invalid message (%ld) sent to ComboBoxWndProcWorker", message); }
return CB_ERR;
case CB_GETEXTENDEDUI: if (pcbox->CBoxStyle & SDROPPABLE) { if (pcbox->fExtendedUI) return TRUE; } return FALSE;
case CB_GETEDITSEL:
/*
* wParam - not used * lParam - not used * effects: Gets the selection range for the given edit control. The * starting BYTE-position is in the low order word. It contains the * the BYTE-position of the first nonselected character after the end * of the selection in the high order word. Returns CB_ERR if no * editcontrol. */ message = EM_GETSEL; goto CallEditSendMessage; break;
case CB_LIMITTEXT:
/*
* wParam - max number of bytes that can be entered * lParam - not used * effects: Specifies the maximum number of bytes of text the user may * enter. If maxLength is 0, we may enter MAXINT number of BYTES. */ message = EM_LIMITTEXT; goto CallEditSendMessage; break;
case CB_SETEDITSEL:
/*
* wParam - ichStart * lParam - ichEnd * */ message = EM_SETSEL;
wParam = (int)(SHORT)LOWORD(lParam); lParam = (int)(SHORT)HIWORD(lParam); goto CallEditSendMessage; break;
case CB_ADDSTRING:
/*
* wParam - not used * lParam - Points to null terminated string to be added to listbox */ if (!pcbox->fCase) message = LB_ADDSTRING; else message = (pcbox->fCase & UPPERCASE) ? LB_ADDSTRINGUPPER : LB_ADDSTRINGLOWER; goto CallListSendMessage; break;
case CB_DELETESTRING:
/*
* wParam - index to string to be deleted * lParam - not used */ message = LB_DELETESTRING; goto CallListSendMessage; break;
case CB_INITSTORAGE: // wParamLo - number of items
// lParam - number of bytes of string space
message = LB_INITSTORAGE; goto CallListSendMessage;
case CB_SETTOPINDEX: // wParamLo - index to make top
// lParam - not used
message = LB_SETTOPINDEX; goto CallListSendMessage;
case CB_GETTOPINDEX: // wParamLo / lParam - not used
message = LB_GETTOPINDEX; goto CallListSendMessage;
case CB_GETCOUNT:
/*
* wParam - not used * lParam - not used */ message = LB_GETCOUNT; goto CallListSendMessage; break;
case CB_GETCURSEL:
/*
* wParam - not used * lParam - not used */ message = LB_GETCURSEL; goto CallListSendMessage; break;
case CB_GETLBTEXT:
/*
* wParam - index of string to be copied * lParam - buffer that is to receive the string */ message = LB_GETTEXT; goto CallListSendMessage; break;
case CB_GETLBTEXTLEN:
/*
* wParam - index to string * lParam - now used for cbANSI */ message = LB_GETTEXTLEN; goto CallListSendMessage; break;
case CB_INSERTSTRING:
/*
* wParam - position to receive the string * lParam - points to the string */ if (!pcbox->fCase) message = LB_INSERTSTRING; else message = (pcbox->fCase & UPPERCASE) ? LB_INSERTSTRINGUPPER : LB_INSERTSTRINGLOWER; goto CallListSendMessage; break;
case CB_RESETCONTENT:
/*
* wParam - not used * lParam - not used * If we come here before WM_CREATE has been processed, * pcbox->spwndList will be NULL. */ UserAssert(pcbox->spwndList); ThreadLock(pcbox->spwndList, &tlpwndList); SendMessageWorker(pcbox->spwndList, LB_RESETCONTENT, 0, 0, FALSE); ThreadUnlock(&tlpwndList); xxxCBInternalUpdateEditWindow(pcbox, NULL); break;
case CB_GETHORIZONTALEXTENT: message = LB_GETHORIZONTALEXTENT; goto CallListSendMessage;
case CB_SETHORIZONTALEXTENT: message = LB_SETHORIZONTALEXTENT; goto CallListSendMessage;
case CB_FINDSTRING:
/*
* wParam - index of starting point for search * lParam - points to prefix string */ message = LB_FINDSTRING; goto CallListSendMessage; break;
case CB_FINDSTRINGEXACT:
/*
* wParam - index of starting point for search * lParam - points to a exact string */ message = LB_FINDSTRINGEXACT; goto CallListSendMessage; break;
case CB_SELECTSTRING:
/*
* wParam - index of starting point for search * lParam - points to prefix string */ UserAssert(pcbox->spwndList); ThreadLock(pcbox->spwndList, &tlpwndList); lParam = SendMessageWorker(pcbox->spwndList, LB_SELECTSTRING, wParam, lParam, fAnsi); ThreadUnlock(&tlpwndList); xxxCBInternalUpdateEditWindow(pcbox, NULL); return lParam;
case CB_SETCURSEL:
/*
* wParam - Contains index to be selected * lParam - not used * If we come here before WM_CREATE has been processed, * pcbox->spwndList will be NULL. */
UserAssert(pcbox->spwndList);
ThreadLock(pcbox->spwndList, &tlpwndList); lParam = SendMessageWorker(pcbox->spwndList, LB_SETCURSEL, wParam, lParam, FALSE); if (lParam != -1) { SendMessageWorker(pcbox->spwndList, LB_SETTOPINDEX, wParam, 0, FALSE); } ThreadUnlock(&tlpwndList); xxxCBInternalUpdateEditWindow(pcbox, NULL); return lParam;
case CB_GETITEMDATA: message = LB_GETITEMDATA; goto CallListSendMessage; break;
case CB_SETITEMDATA: message = LB_SETITEMDATA; goto CallListSendMessage; break;
case CB_SETITEMHEIGHT: if (wParam == -1) { if (HIWORD(lParam) != 0) return CB_ERR; return xxxCBSetEditItemHeight(pcbox, LOWORD(lParam)); }
message = LB_SETITEMHEIGHT; goto CallListSendMessage; break;
case CB_GETITEMHEIGHT: if (wParam == -1) return pcbox->editrc.bottom - pcbox->editrc.top;
message = LB_GETITEMHEIGHT; goto CallListSendMessage; break;
case CB_SHOWDROPDOWN:
/*
* wParam - True then drop down the listbox if possible else hide it * lParam - not used */ if (wParam && !pcbox->fLBoxVisible) { xxxCBShowListBoxWindow(pcbox, TRUE); } else { if (!wParam && pcbox->fLBoxVisible) { xxxCBHideListBoxWindow(pcbox, TRUE, FALSE); } } break;
case CB_SETLOCALE:
/*
* wParam - locale id * lParam - not used */ message = LB_SETLOCALE; goto CallListSendMessage; break;
case CB_GETLOCALE:
/*
* wParam - not used * lParam - not used */ message = LB_GETLOCALE; goto CallListSendMessage; break;
case CB_GETCOMBOBOXINFO: return NtUserGetComboBoxInfo(hwnd, (PCOMBOBOXINFO)lParam);
case WM_MEASUREITEM: case WM_DELETEITEM: case WM_DRAWITEM: case WM_COMPAREITEM: return xxxCBMessageItemHandler(pcbox, message, (LPVOID)lParam);
case WM_NCCREATE:
/*
* wParam - Contains a handle to the window being created * lParam - Points to the CREATESTRUCT data structure for the window. */ return CBNcCreateHandler(pcbox, pwnd);
case WM_PARENTNOTIFY: if (LOWORD(wParam) == WM_DESTROY) { if ((HWND)lParam == HW(pcbox->spwndEdit)) { pcbox->CBoxStyle &= ~SEDITABLE; pcbox->fNoEdit = TRUE; pcbox->spwndEdit = pwnd; } else if ((HWND)lParam == HW(pcbox->spwndList)) { pcbox->CBoxStyle &= ~SDROPPABLE; pcbox->spwndList = NULL; } } break;
case WM_UPDATEUISTATE: /*
* Propagate the change to the list control, if any */ UserAssert(pcbox->spwndList); ThreadLock(pcbox->spwndList, &tlpwndList); SendMessageWorker(pcbox->spwndList, WM_UPDATEUISTATE, wParam, lParam, fAnsi); ThreadUnlock(&tlpwndList); goto CallDWP;
case WM_HELP: { LPHELPINFO lpHelpInfo;
/*
* Check if this message is from a child of this combo */ if ((lpHelpInfo = (LPHELPINFO)lParam) != NULL && ((pcbox->spwndEdit && lpHelpInfo->iCtrlId == (SHORT)(PTR_TO_ID(pcbox->spwndEdit->spmenu))) || lpHelpInfo->iCtrlId == (SHORT)(PTR_TO_ID(pcbox->spwndList->spmenu)) )) {
/*
* Make it look like the WM_HELP is coming form this combo. * Then DefWindowProcWorker will pass it up to our parent, * who can do whatever he wants with it. */ lpHelpInfo->iCtrlId = (SHORT)(PTR_TO_ID(pwnd->spmenu)); lpHelpInfo->hItemHandle = hwnd; lpHelpInfo->dwContextId = GetContextHelpId(pwnd); } } /*
* Fall through to DefWindowProc */
default:
if (SYSMET(PENWINDOWS) && (message >= WM_PENWINFIRST && message <= WM_PENWINLAST)) goto CallEditSendMessage;
CallDWP: return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi); } /* switch (message) */
return TRUE;
/*
* The following forward messages off to the child controls. */ CallEditSendMessage: if (!pcbox->fNoEdit && pcbox->spwndEdit) { /*
* pcbox->spwndEdit will be NULL if we haven't done WM_CREATE yet! */ ThreadLock(pcbox->spwndEdit, &tlpwndEdit); lReturn = SendMessageWorker(pcbox->spwndEdit, message, wParam, lParam, fAnsi); ThreadUnlock(&tlpwndEdit); } else { RIPERR0(ERROR_INVALID_COMBOBOX_MESSAGE, RIP_VERBOSE, ""); lReturn = CB_ERR; } return lReturn;
CallListSendMessage: /*
* pcbox->spwndList will be NULL if we haven't done WM_CREATE yet! */ UserAssert(pcbox->spwndList); ThreadLock(pcbox->spwndList, &tlpwndList); lReturn = SendMessageWorker(pcbox->spwndList, message, wParam, lParam, fAnsi); ThreadUnlock(&tlpwndList); return lReturn;
} /* ComboBoxWndProcWorker */
/***************************************************************************\
\***************************************************************************/
LRESULT WINAPI ComboBoxWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PWND pwnd;
if ((pwnd = ValidateHwnd(hwnd)) == NULL) { return (0L); }
/*
* If the control is not interested in this message, * pass it to DefWindowProc. */ if (!FWINDOWMSG(message, FNID_COMBOBOX) && !(SYSMET(PENWINDOWS) && (message >= WM_PENWINFIRST && message <= WM_PENWINLAST))) return DefWindowProcWorker(pwnd, message, wParam, lParam, TRUE);
return ComboBoxWndProcWorker(pwnd, message, wParam, lParam, TRUE); }
LRESULT WINAPI ComboBoxWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PWND pwnd;
if ((pwnd = ValidateHwnd(hwnd)) == NULL) { return (0L); }
/*
* If the control is not interested in this message, * pass it to DefWindowProc. */ if (!FWINDOWMSG(message, FNID_COMBOBOX) && !(SYSMET(PENWINDOWS) && (message >= WM_PENWINFIRST && message <= WM_PENWINLAST))) return DefWindowProcWorker(pwnd, message, wParam, lParam, FALSE);
return ComboBoxWndProcWorker(pwnd, message, wParam, lParam, FALSE); }
/***************************************************************************\
* xxxCBMessageItemHandler * * Handles WM_DRAWITEM,WM_MEASUREITEM,WM_DELETEITEM,WM_COMPAREITEM * messages from the listbox. * * History: \***************************************************************************/
LRESULT xxxCBMessageItemHandler( PCBOX pcbox, UINT message, LPVOID lpfoo) /* Actually can be any of the structs below */ { LRESULT lRet; TL tlpwndParent;
CheckLock(pcbox->spwnd);
/*
* Send the <foo>item message back to the application after changing some * parameters to their combo box specific versions. */ ((LPMEASUREITEMSTRUCT)lpfoo)->CtlType = ODT_COMBOBOX; ((LPMEASUREITEMSTRUCT)lpfoo)->CtlID = PtrToUlong(pcbox->spwnd->spmenu); if (message == WM_DRAWITEM) ((LPDRAWITEMSTRUCT)lpfoo)->hwndItem = HWq(pcbox->spwnd); else if (message == WM_DELETEITEM) ((LPDELETEITEMSTRUCT)lpfoo)->hwndItem = HWq(pcbox->spwnd); else if (message == WM_COMPAREITEM) ((LPCOMPAREITEMSTRUCT)lpfoo)->hwndItem = HWq(pcbox->spwnd);
ThreadLock(pcbox->spwndParent, &tlpwndParent); lRet = SendMessage(HW(pcbox->spwndParent), message, (WPARAM)pcbox->spwnd->spmenu, (LPARAM)lpfoo); ThreadUnlock(&tlpwndParent);
return lRet; }
/***************************************************************************\
* xxxCBPaint * * History: \***************************************************************************/
void xxxCBPaint( PCBOX pcbox, HDC hdc) { RECT rc; UINT msg; HBRUSH hbr;
CheckLock(pcbox->spwnd);
rc.left = rc.top = 0; rc.right = pcbox->cxCombo; rc.bottom = pcbox->cyCombo; if (pcbox->f3DCombo) DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST); else DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST | BF_FLAT | BF_MONO);
if (pcbox->buttonrc.left != 0) { // Draw in the dropdown arrow button
DrawFrameControl(hdc, KPRECT_TO_PRECT(&pcbox->buttonrc), DFC_SCROLL, DFCS_SCROLLCOMBOBOX | (pcbox->fButtonPressed ? DFCS_PUSHED | DFCS_FLAT : 0) | (TestWF(pcbox->spwnd, WFDISABLED) ? DFCS_INACTIVE : 0)); #ifdef COLOR_HOTTRACKING
(pcbox->fButtonHotTracked ? DFCS_HOT: 0))); #endif // COLOR_HOTTRACKING
if (pcbox->fRightAlign ) rc.left = pcbox->buttonrc.right; else rc.right = pcbox->buttonrc.left; }
// Erase the background behind the edit/static item. Since a combo
// is an edit field/list box hybrid, we use the same coloring
// conventions.
msg = WM_CTLCOLOREDIT; if (TestWF(pcbox->spwnd, WFWIN40COMPAT)) { if (TestWF(pcbox->spwnd, WFDISABLED) || (!pcbox->fNoEdit && pcbox->spwndEdit && TestWF(pcbox->spwndEdit, EFREADONLY))) msg = WM_CTLCOLORSTATIC; } else msg = WM_CTLCOLORLISTBOX;
hbr = GetControlBrush(HWq(pcbox->spwnd), hdc, msg);
if (pcbox->fNoEdit) xxxCBInternalUpdateEditWindow(pcbox, hdc); else FillRect(hdc, &rc, hbr); }
/***************************************************************************\
* xxxCBCommandHandler * * Check the various notification codes from the controls and do the * proper thing. * always returns 0L * * History: \***************************************************************************/
long xxxCBCommandHandler( PCBOX pcbox, DWORD wParam, HWND hwndControl) {
CheckLock(pcbox->spwnd);
/*
* Check the edit control notification codes. Note that currently, edit * controls don't send EN_KILLFOCUS messages to the parent. */ if (!pcbox->fNoEdit && SAMEWOWHANDLE(hwndControl, HWq(pcbox->spwndEdit))) {
/*
* Edit control notification codes */ switch (HIWORD(wParam)) { case EN_SETFOCUS: if (!pcbox->fFocus) {
/*
* The edit control has the focus for the first time which means * this is the first time the combo box has received the focus * and the parent must be notified that we have the focus. */ xxxCBGetFocusHelper(pcbox); } break;
case EN_CHANGE: xxxCBNotifyParent(pcbox, CBN_EDITCHANGE); xxxCBUpdateListBoxWindow(pcbox, FALSE); break;
case EN_UPDATE: xxxCBNotifyParent(pcbox, CBN_EDITUPDATE); break;
case EN_ERRSPACE: xxxCBNotifyParent(pcbox, CBN_ERRSPACE); break; } }
/*
* Check listbox control notification codes */ if (SAMEWOWHANDLE(hwndControl, HWq(pcbox->spwndList))) {
/*
* Listbox control notification codes */ switch ((int)HIWORD(wParam)) { case LBN_DBLCLK: xxxCBNotifyParent(pcbox, CBN_DBLCLK); break;
case LBN_ERRSPACE: xxxCBNotifyParent(pcbox, CBN_ERRSPACE); break;
case LBN_SELCHANGE: case LBN_SELCANCEL: if (!pcbox->fKeyboardSelInListBox) {
/*
* If the selchange is caused by the user keyboarding through, * we don't want to hide the listbox. */ if (!xxxCBHideListBoxWindow(pcbox, TRUE, TRUE)) return(0L); } else { pcbox->fKeyboardSelInListBox = FALSE; }
xxxCBNotifyParent(pcbox, CBN_SELCHANGE); xxxCBInternalUpdateEditWindow(pcbox, NULL); /*
* If this combobox doesn't have an edit control, it needs to send * this notification itself when the user is cycling through the * items with the keyboard. * See bug #54766. */ if (pcbox->fNoEdit) { NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, HWq(pcbox->spwnd), OBJID_CLIENT, INDEX_COMBOBOX); } break; } }
return 0L; }
/***************************************************************************\
* xxxCBNotifyParent * * Sends the notification code to the parent of the combo box control * * History: \***************************************************************************/
void xxxCBNotifyParent( PCBOX pcbox, short notificationCode) { PWND pwndParent; // Parent if it exists
TL tlpwndParent;
CheckLock(pcbox->spwnd);
if (pcbox->spwndParent) pwndParent = pcbox->spwndParent; else pwndParent = pcbox->spwnd;
/*
* wParam contains Control ID and notification code. * lParam contains Handle to window */ ThreadLock(pwndParent, &tlpwndParent); SendMessageWorker(pwndParent, WM_COMMAND, MAKELONG(PTR_TO_ID(pcbox->spwnd->spmenu), notificationCode), (LPARAM)HWq(pcbox->spwnd), FALSE); ThreadUnlock(&tlpwndParent); }
/***************************************************************************\
* * * Completes the text in the edit box with the closest match from the * listbox. If a prefix match can't be found, the edit control text isn't * updated. Assume a DROPDOWN style combo box. * * * History: \***************************************************************************/ void xxxCBCompleteEditWindow( PCBOX pcbox) { int cchText; int cchItemText; int itemNumber; LPWSTR pText; TL tlpwndEdit; TL tlpwndList;
CheckLock(pcbox->spwnd);
/*
* Firstly check the edit control. */ if (pcbox->spwndEdit == NULL) { return; }
ThreadLock(pcbox->spwndEdit, &tlpwndEdit); ThreadLock(pcbox->spwndList, &tlpwndList);
/*
* +1 for null terminator */ cchText = (int)SendMessageWorker(pcbox->spwndEdit, WM_GETTEXTLENGTH, 0, 0, FALSE);
if (cchText) { cchText++; if (!(pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR)))) goto Unlock;
/*
* We want to be sure to free the above allocated memory even if * the client dies during callback (xxx) or some of the following * window revalidation fails. */ try { SendMessageWorker(pcbox->spwndEdit, WM_GETTEXT, cchText, (LPARAM)pText, FALSE); itemNumber = (int)SendMessageWorker(pcbox->spwndList, LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pText, FALSE); if (itemNumber == -1) itemNumber = (int)SendMessageWorker(pcbox->spwndList, LB_FINDSTRING, (WPARAM)-1, (LPARAM)pText, FALSE); } finally { UserLocalFree((HANDLE)pText); }
if (itemNumber == -1) {
/*
* No close match. Blow off. */ goto Unlock; }
cchItemText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN, itemNumber, 0, FALSE); if (cchItemText) { cchItemText++; if (!(pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchItemText*sizeof(WCHAR)))) goto Unlock;
/*
* We want to be sure to free the above allocated memory even if * the client dies during callback (xxx) or some of the following * window revalidation fails. */ try { SendMessageWorker(pcbox->spwndList, LB_GETTEXT, itemNumber, (LPARAM)pText, FALSE); SendMessageWorker(pcbox->spwndEdit, WM_SETTEXT, 0, (LPARAM)pText, FALSE); } finally { UserLocalFree((HANDLE)pText); }
SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, MAXLONG, !!TestWF(pcbox->spwnd, WFANSIPROC)); } }
Unlock: ThreadUnlock(&tlpwndList); ThreadUnlock(&tlpwndEdit); }
/***************************************************************************\
* xxxCBHideListBoxWindow * * Hides the dropdown listbox window if it is a dropdown style. * * History: \***************************************************************************/
BOOL xxxCBHideListBoxWindow( PCBOX pcbox, BOOL fNotifyParent, BOOL fSelEndOK) { HWND hwnd = HWq(pcbox->spwnd); HWND hwndList = HWq(pcbox->spwndList); TL tlpwndList;
CheckLock(pcbox->spwnd);
// For 3.1+ apps, send CBN_SELENDOK to all types of comboboxes but only
// allow CBN_SELENDCANCEL to be sent for droppable comboboxes
if (fNotifyParent && TestWF(pcbox->spwnd, WFWIN31COMPAT) && ((pcbox->CBoxStyle & SDROPPABLE) || fSelEndOK)) { if (fSelEndOK) { xxxCBNotifyParent(pcbox, CBN_SELENDOK); } else { xxxCBNotifyParent(pcbox, CBN_SELENDCANCEL); } if (!IsWindow(hwnd)) return(FALSE); }
/*
* return, we don't hide simple combo boxes. */ if (!(pcbox->CBoxStyle & SDROPPABLE)) { return TRUE; }
/*
* Send a faked buttonup message to the listbox so that it can release * the capture and all. */ ThreadLock(pcbox->spwndList, &tlpwndList);
SendMessageWorker(pcbox->spwndList, LBCB_ENDTRACK, fSelEndOK, 0, FALSE);
if (pcbox->fLBoxVisible) { WORD swpFlags = SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE;
if (!TestWF(pcbox->spwnd, WFWIN31COMPAT)) swpFlags |= SWP_FRAMECHANGED;
pcbox->fLBoxVisible = FALSE;
/*
* Hide the listbox window */ NtUserShowWindow(hwndList, SW_HIDE);
//
// Invalidate the item area now since SWP() might update stuff.
// Since the combo is CS_VREDRAW/CS_HREDRAW, a size change will
// redraw the whole thing, including the item rect. But if it
// isn't changing size, we still want to redraw the item anyway
// to show focus/selection.
//
if (!(pcbox->CBoxStyle & SEDITABLE)) NtUserInvalidateRect(hwnd, KPRECT_TO_PRECT(&pcbox->editrc), TRUE);
NtUserSetWindowPos(hwnd, HWND_TOP, 0, 0, pcbox->cxCombo, pcbox->cyCombo, swpFlags);
// In case size didn't change
UpdateWindow(hwnd);
if (pcbox->CBoxStyle & SEDITABLE) { xxxCBCompleteEditWindow(pcbox); }
if (fNotifyParent) {
/*
* Notify parent we will be popping up the combo box. */ xxxCBNotifyParent(pcbox, CBN_CLOSEUP); if (!IsWindow(hwnd)) return(FALSE); } }
ThreadUnlock(&tlpwndList);
return(TRUE); }
/***************************************************************************\
* xxxCBShowListBoxWindow * * Lowers the dropdown listbox window. * * History: \***************************************************************************/
void xxxCBShowListBoxWindow( PCBOX pcbox, BOOL fTrack) { RECT editrc; int itemNumber; int iHeight; int yTop; DWORD dwMult; int cyItem; HWND hwnd = HWq(pcbox->spwnd); HWND hwndList = HWq(pcbox->spwndList); BOOL fAnimPos; TL tlpwndList; PMONITOR pMonitor;
//
// THIS FUNCTION IS ONLY CALLED FOR DROPPABLE LIST COMBOBOXES
//
UserAssert(pcbox->CBoxStyle & SDROPPABLE);
CheckLock(pcbox->spwnd);
ThreadLock(pcbox->spwndList, &tlpwndList);
/*
* Notify parent we will be dropping down the combo box. */
xxxCBNotifyParent(pcbox, CBN_DROPDOWN); /*
* Invalidate the button rect so that the depressed arrow is drawn. */ NtUserInvalidateRect(hwnd, KPRECT_TO_PRECT(&pcbox->buttonrc), TRUE);
pcbox->fLBoxVisible = TRUE;
if (pcbox->CBoxStyle == SDROPDOWN) {
/*
* If an item in the listbox matches the text in the edit control, * scroll it to the top of the listbox. Select the item only if the * mouse button isn't down otherwise we will select the item when the * mouse button goes up. */ xxxCBUpdateListBoxWindow(pcbox, !pcbox->fMouseDown); if (!pcbox->fMouseDown) xxxCBCompleteEditWindow(pcbox); } else {
/*
* Scroll the currently selected item to the top of the listbox. */ itemNumber = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL, 0, 0, FALSE); if (itemNumber == -1) { itemNumber = 0; } SendMessageWorker(pcbox->spwndList, LB_SETTOPINDEX, itemNumber, 0, FALSE); SendMessageWorker(pcbox->spwndList, LBCB_CARETON, 0, 0, FALSE);
/*
* We need to invalidate the edit rect so that the focus frame/invert * will be turned off when the listbox is visible. Tandy wants this for * his typical reasons... */ NtUserInvalidateRect(hwnd, KPRECT_TO_PRECT(&pcbox->editrc), TRUE); }
//
// Figure out where to position the dropdown listbox. We want it just
// touching the edge around the edit rectangle. Note that since the
// listbox is a popup, we need the position in screen coordinates.
//
// We want the dropdown to pop below or above the combo
// Get screen coords
editrc.left = pcbox->spwnd->rcWindow.left; editrc.top = pcbox->spwnd->rcWindow.top; editrc.right = pcbox->spwnd->rcWindow.left + pcbox->cxCombo; editrc.bottom = pcbox->spwnd->rcWindow.top + pcbox->cyCombo;
// List area
cyItem = (int)SendMessageWorker(pcbox->spwndList, LB_GETITEMHEIGHT, 0, 0, FALSE);
if (cyItem == 0) { // Make sure that it's not 0
RIPMSG0( RIP_WARNING, "LB_GETITEMHEIGHT is returning 0\n" );
cyItem = gpsi->cySysFontChar; }
// we shoulda' just been able to use cyDrop here, but thanks to VB's need
// to do things their OWN SPECIAL WAY, we have to keep monitoring the size
// of the listbox 'cause VB changes it directly (jeffbog 03/21/94)
iHeight = max(pcbox->cyDrop, pcbox->spwndList->rcWindow.bottom - pcbox->spwndList->rcWindow.top);
if (dwMult = (DWORD)SendMessageWorker(pcbox->spwndList, LB_GETCOUNT, 0, 0, FALSE)) { dwMult = (DWORD)(LOWORD(dwMult) * cyItem); dwMult += SYSMET(CYEDGE);
if (dwMult < 0x7FFF) iHeight = min(LOWORD(dwMult), iHeight); }
if (!TestWF(pcbox->spwnd, CBFNOINTEGRALHEIGHT)) { UserAssert(cyItem); iHeight = ((iHeight - SYSMET(CYEDGE)) / cyItem) * cyItem + SYSMET(CYEDGE); }
//
// Other 1/2 of old app combo fix. Make dropdown overlap combo window
// a little. That way we can have a chance of invalidating the overlap
// and causing a repaint to help out Publisher 2.0's toolbar combos.
// See comments for PressButton() above.
//
pMonitor = _MonitorFromWindow(pcbox->spwnd, MONITOR_DEFAULTTOPRIMARY); if (editrc.bottom + iHeight <= pMonitor->rcMonitor.bottom) { yTop = editrc.bottom; if (!pcbox->f3DCombo) yTop -= SYSMET(CYBORDER);
fAnimPos = TRUE; } else { yTop = max(editrc.top - iHeight, pMonitor->rcMonitor.top); if (!pcbox->f3DCombo) yTop += SYSMET(CYBORDER);
fAnimPos = FALSE; }
if ( ! TestWF( pcbox->spwnd, WFWIN40COMPAT) ) { // fix for Winword B#7504, Combo-ListBox text gets
// truncated by a small width, this is do to us
// now setting size here in SetWindowPos, rather than
// earlier where we did this in Win3.1
if ( (pcbox->spwndList->rcWindow.right - pcbox->spwndList->rcWindow.left ) > pcbox->cxDrop )
pcbox->cxDrop = pcbox->spwndList->rcWindow.right - pcbox->spwndList->rcWindow.left; }
NtUserSetWindowPos(hwndList, HWND_TOPMOST, editrc.left, yTop, max(pcbox->cxDrop, pcbox->cxCombo), iHeight, SWP_NOACTIVATE);
/*
* Get any drawing in the combo box window out of the way so it doesn't * invalidate any of the SPB underneath the list window. */ UpdateWindow(hwnd);
if (!(TEST_EffectPUSIF(PUSIF_COMBOBOXANIMATION)) || (GetAppCompatFlags2(VER40) & GACF2_ANIMATIONOFF)) { NtUserShowWindow(hwndList, SW_SHOWNA); } else { AnimateWindow(hwndList, CMS_QANIMATION, (fAnimPos ? AW_VER_POSITIVE : AW_VER_NEGATIVE) | AW_SLIDE); }
#ifdef LATER
//
// we don't have sys modal windows.
//
if (pwndSysModal) {
/*
* If this combo is in a system modal dialog box, we need to explicitly * call update window otherwise we won't automatically send paint * messages to the toplevel listbox window. This is especially * noticeable in the File Open/Save sys modal dlgs which are put up at * ExitWindows time. */ UpdateWindow(hwndList); } #endif
/*
* Restart search buffer from first char */ { PLBIV plb = ((PLBWND)pcbox->spwndList)->pLBIV;
if ((plb != NULL) && (plb != (PLBIV)-1)) { plb->iTypeSearch = 0; } }
if (fTrack && TestWF(pcbox->spwnd, WFWIN40COMPAT)) SendMessageWorker(pcbox->spwndList, LBCB_STARTTRACK, FALSE, 0, FALSE);
ThreadUnlock(&tlpwndList); }
/***************************************************************************\
* xxxCBInternalUpdateEditWindow * * Updates the editcontrol/statictext window so that it contains the text * given by the current selection in the listbox. If the listbox has no * selection (ie. -1), then we erase all the text in the editcontrol. * * hdcPaint is from WM_PAINT messages Begin/End Paint hdc. If null, we should * get our own dc. * * History: \***************************************************************************/
void xxxCBInternalUpdateEditWindow( PCBOX pcbox, HDC hdcPaint) { int cchText = 0; LPWSTR pText = NULL; int sItem; HDC hdc; UINT msg; HBRUSH hbrSave; HBRUSH hbrControl; HANDLE hOldFont; DRAWITEMSTRUCT dis; RECT rc; HWND hwnd = HWq(pcbox->spwnd); TL tlpwndList; TL tlpwndEdit; TL tlpwndParent;
CheckLock(pcbox->spwnd);
/* This check is also commented out in Win3.1 and Win95 */ // if (!TestWF(pcbox->spwnd, WFVISIBLE)) {
// return;
// }
ThreadLock(pcbox->spwndParent, &tlpwndParent); ThreadLock(pcbox->spwndList, &tlpwndList); ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
sItem = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL, 0, 0, FALSE);
/*
* This 'try-finally' block ensures that the allocated 'pText' will * be freed no matter how this routine is exited. */ try { if (sItem != -1) { cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN, (DWORD)sItem, 0, FALSE); if ((pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, (cchText+1) * sizeof(WCHAR)))) { cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXT, (DWORD)sItem, (LPARAM)pText, FALSE); } }
if (!pcbox->fNoEdit) {
if (pcbox->spwndEdit) { if (TestWF(pcbox->spwnd, CBFHASSTRINGS)) SetWindowText(HWq(pcbox->spwndEdit), pText ? pText : TEXT(""));
if (pcbox->fFocus) { /*
* Only hilite the text if we have the focus. */ SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, MAXLONG, !!TestWF(pcbox->spwnd, WFANSIPROC)); } } } else if (IsComboVisible(pcbox)) { if (hdcPaint) { hdc = hdcPaint; } else { hdc = NtUserGetDC(hwnd); }
SetBkMode(hdc, OPAQUE); if (TestWF(pcbox->spwnd, WFWIN40COMPAT)) { if (TestWF(pcbox->spwnd, WFDISABLED)) msg = WM_CTLCOLORSTATIC; else msg = WM_CTLCOLOREDIT; } else msg = WM_CTLCOLORLISTBOX;
hbrControl = GetControlBrush(hwnd, hdc, msg); hbrSave = SelectObject(hdc, hbrControl);
CopyInflateRect(&rc, KPRECT_TO_PRECT(&pcbox->editrc), SYSMET(CXBORDER), SYSMET(CYBORDER)); PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY); InflateRect(&rc, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
if (pcbox->fFocus && !pcbox->fLBoxVisible) { //
// Fill in the selected area
//
// only do the FillRect if we know its not
// ownerdraw item, otherwise we mess up people up
// BUT: for Compat's sake we still do this for Win 3.1 guys
if (!TestWF( pcbox->spwnd, WFWIN40COMPAT) || !pcbox->OwnerDraw) FillRect(hdc, &rc, SYSHBR(HIGHLIGHT));
SetBkColor(hdc, SYSRGB(HIGHLIGHT)); SetTextColor(hdc, SYSRGB(HIGHLIGHTTEXT)); } else if (TestWF(pcbox->spwnd, WFDISABLED) && !pcbox->OwnerDraw) { if ((COLORREF)SYSRGB(GRAYTEXT) != GetBkColor(hdc)) SetTextColor(hdc, SYSRGB(GRAYTEXT)); }
if (pcbox->hFont != NULL) hOldFont = SelectObject(hdc, pcbox->hFont);
if (pcbox->OwnerDraw) {
/*
* Let the app draw the stuff in the static text box. */ dis.CtlType = ODT_COMBOBOX; dis.CtlID = PtrToUlong(pcbox->spwnd->spmenu); dis.itemID = sItem; dis.itemAction = ODA_DRAWENTIRE; dis.itemState = (UINT) ((pcbox->fFocus && !pcbox->fLBoxVisible ? ODS_SELECTED : 0) | (TestWF(pcbox->spwnd, WFDISABLED) ? ODS_DISABLED : 0) | (pcbox->fFocus && !pcbox->fLBoxVisible ? ODS_FOCUS : 0) | (TestWF(pcbox->spwnd, WFWIN40COMPAT) ? ODS_COMBOBOXEDIT : 0) | (TestWF(pcbox->spwnd, WEFPUIFOCUSHIDDEN) ? ODS_NOFOCUSRECT : 0) | (TestWF(pcbox->spwnd, WEFPUIACCELHIDDEN) ? ODS_NOACCEL : 0));
dis.hwndItem = hwnd; dis.hDC = hdc; CopyRect(&dis.rcItem, &rc);
// Don't let ownerdraw dudes draw outside of the combo client
// bounds.
IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
dis.itemData = (ULONG_PTR)SendMessageWorker(pcbox->spwndList, LB_GETITEMDATA, (UINT)sItem, 0, FALSE);
SendMessage(HW(pcbox->spwndParent), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); } else {
/*
* Start the text one pixel within the rect so that we leave a * nice hilite border around the text. */
int x ; UINT align ;
if (pcbox->fRightAlign ) { align = TA_RIGHT; x = rc.right - SYSMET(CXBORDER); } else { x = rc.left + SYSMET(CXBORDER); align = 0; }
if (pcbox->fRtoLReading ) align |= TA_RTLREADING;
if (align) SetTextAlign(hdc, GetTextAlign(hdc) | align);
// Draw the text, leaving a gap on the left & top for selection.
ExtTextOut(hdc, x, rc.top + SYSMET(CYBORDER), ETO_CLIPPED | ETO_OPAQUE, &rc, pText ? pText : TEXT(""), cchText, NULL); if (pcbox->fFocus && !pcbox->fLBoxVisible) { if (!TestWF(pcbox->spwnd, WEFPUIFOCUSHIDDEN)) { DrawFocusRect(hdc, &rc); } } }
if (pcbox->hFont && hOldFont) { SelectObject(hdc, hOldFont); }
if (hbrSave) { SelectObject(hdc, hbrSave); }
if (!hdcPaint) { NtUserReleaseDC(hwnd, hdc); } }
} finally { if (pText != NULL) UserLocalFree((HANDLE)pText); }
ThreadUnlock(&tlpwndEdit); ThreadUnlock(&tlpwndList); ThreadUnlock(&tlpwndParent); }
/***************************************************************************\
* xxxCBInvertStaticWindow * * Inverts the static text/picture window associated with the combo * box. Gets its own hdc, if the one given is null. * * History: \***************************************************************************/
void xxxCBInvertStaticWindow( PCBOX pcbox, BOOL fNewSelectionState, /* True if inverted else false */ HDC hdc) { BOOL focusSave = pcbox->fFocus;
CheckLock(pcbox->spwnd);
pcbox->fFocus = (UINT)fNewSelectionState; xxxCBInternalUpdateEditWindow(pcbox, hdc);
pcbox->fFocus = (UINT)focusSave; }
/***************************************************************************\
* xxxCBUpdateListBoxWindow * * matches the text in the editcontrol. If fSelectionAlso is false, then we * unselect the current listbox selection and just move the caret to the item * which is the closest match to the text in the editcontrol. * * History: \***************************************************************************/
void xxxCBUpdateListBoxWindow( PCBOX pcbox, BOOL fSelectionAlso) { int cchText; int sItem, sSel; LPWSTR pText = NULL; TL tlpwndEdit; TL tlpwndList;
if (pcbox->spwndEdit == NULL) { return; }
CheckLock(pcbox->spwnd);
ThreadLock(pcbox->spwndList, &tlpwndList); ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
/*
* +1 for null terminator */
cchText = (int)SendMessageWorker(pcbox->spwndEdit, WM_GETTEXTLENGTH, 0, 0, FALSE);
if (cchText) { cchText++; pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR)); if (pText != NULL) { try { SendMessageWorker(pcbox->spwndEdit, WM_GETTEXT, cchText, (LPARAM)pText, FALSE); sItem = (int)SendMessageWorker(pcbox->spwndList, LB_FINDSTRING, (WPARAM)-1L, (LPARAM)pText, FALSE); } finally { UserLocalFree((HANDLE)pText); } } } else sItem = -1;
if (fSelectionAlso) { sSel = sItem; } else { sSel = -1; }
if (sItem == -1) { sItem = 0;
//
// Old apps: w/ editable combos, selected 1st item in list even if
// it didn't match text in edit field. This is not desirable
// behavior for 4.0 dudes esp. with cancel allowed. Reason:
// (1) User types in text that doesn't match list choices
// (2) User drops combo
// (3) User pops combo back up
// (4) User presses OK in dialog that does stuff w/ combo
// contents.
// In 3.1, when the combo dropped, we'd select the 1st item anyway.
// So the last CBN_SELCHANGE the owner got would be 0--which is
// bogus because it really should be -1. In fact if you type anything
// into the combo afterwards it will reset itself to -1.
//
// 4.0 dudes won't get this bogus 0 selection.
//
if (fSelectionAlso && !TestWF(pcbox->spwnd, WFWIN40COMPAT)) sSel = 0; }
SendMessageWorker(pcbox->spwndList, LB_SETCURSEL, (DWORD)sSel, 0, FALSE); SendMessageWorker(pcbox->spwndList, LB_SETCARETINDEX, (DWORD)sItem, 0, FALSE); SendMessageWorker(pcbox->spwndList, LB_SETTOPINDEX, (DWORD)sItem, 0, FALSE);
ThreadUnlock(&tlpwndEdit); ThreadUnlock(&tlpwndList); }
/***************************************************************************\
* xxxCBGetFocusHelper * * Handles getting the focus for the combo box * * History: \***************************************************************************/
void xxxCBGetFocusHelper( PCBOX pcbox) { TL tlpwndList; TL tlpwndEdit;
CheckLock(pcbox->spwnd);
if (pcbox->fFocus) return;
ThreadLock(pcbox->spwndList, &tlpwndList); ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
/*
* The combo box has gotten the focus for the first time. */
/*
* First turn on the listbox caret */
if (pcbox->CBoxStyle == SDROPDOWNLIST) SendMessageWorker(pcbox->spwndList, LBCB_CARETON, 0, 0, FALSE);
/*
* and select all the text in the editcontrol or static text rectangle. */
if (pcbox->fNoEdit) {
/*
* Invert the static text rectangle */ xxxCBInvertStaticWindow(pcbox, TRUE, (HDC)NULL); } else if (pcbox->spwndEdit) { UserAssert(pcbox->spwnd); SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, MAXLONG, !!TestWF(pcbox->spwnd, WFANSIPROC)); }
pcbox->fFocus = TRUE;
/*
* Notify the parent we have the focus */ xxxCBNotifyParent(pcbox, CBN_SETFOCUS);
ThreadUnlock(&tlpwndEdit); ThreadUnlock(&tlpwndList); }
/***************************************************************************\
* xxxCBKillFocusHelper * * Handles losing the focus for the combo box. * * History: \***************************************************************************/
void xxxCBKillFocusHelper( PCBOX pcbox) { TL tlpwndList; TL tlpwndEdit;
CheckLock(pcbox->spwnd);
if (!pcbox->fFocus || pcbox->spwndList == NULL) return;
ThreadLock(pcbox->spwndList, &tlpwndList); ThreadLock(pcbox->spwndEdit, &tlpwndEdit);
/*
* The combo box is losing the focus. Send buttonup clicks so that * things release the mouse capture if they have it... If the * pwndListBox is null, don't do anything. This occurs if the combo box * is destroyed while it has the focus. */ SendMessageWorker(pcbox->spwnd, WM_LBUTTONUP, 0L, 0xFFFFFFFFL, FALSE); if (!xxxCBHideListBoxWindow(pcbox, TRUE, FALSE)) return;
/*
* Turn off the listbox caret */
if (pcbox->CBoxStyle == SDROPDOWNLIST) SendMessageWorker(pcbox->spwndList, LBCB_CARETOFF, 0, 0, FALSE);
if (pcbox->fNoEdit) {
/*
* Invert the static text rectangle */ xxxCBInvertStaticWindow(pcbox, FALSE, (HDC)NULL); } else if (pcbox->spwndEdit) { SendMessageWorker(pcbox->spwndEdit, EM_SETSEL, 0, 0, !!TestWF(pcbox->spwnd, WFANSIPROC)); }
pcbox->fFocus = FALSE; xxxCBNotifyParent(pcbox, CBN_KILLFOCUS);
ThreadUnlock(&tlpwndEdit); ThreadUnlock(&tlpwndList); }
/***************************************************************************\
* xxxCBGetTextLengthHelper * * For the combo box without an edit control, returns size of current selected * item * * History: \***************************************************************************/
LONG xxxCBGetTextLengthHelper( PCBOX pcbox, BOOL fAnsi) { int item; int cchText; TL tlpwndList;
ThreadLock(pcbox->spwndList, &tlpwndList); item = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL, 0, 0, fAnsi);
if (item == LB_ERR) {
/*
* No selection so no text. */ cchText = 0; } else { cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN, item, 0, fAnsi); }
ThreadUnlock(&tlpwndList);
return cchText; }
/***************************************************************************\
* xxxCBGetTextHelper * * For the combo box without an edit control, copies cbString bytes of the * string in the static text box to the buffer given by pString. * * History: \***************************************************************************/
LONG xxxCBGetTextHelper( PCBOX pcbox, int cchString, LPWSTR pString, BOOL fAnsi) { int item; int cchText; LPWSTR pText; DWORD dw; TL tlpwndList;
CheckLock(pcbox->spwnd);
if (!cchString || !pString) return 0;
/*
* Null the buffer to be nice. */ if (fAnsi) { *((LPSTR)pString) = 0; } else { *((LPWSTR)pString) = 0; }
ThreadLock(pcbox->spwndList, &tlpwndList); item = (int)SendMessageWorker(pcbox->spwndList, LB_GETCURSEL, 0, 0, fAnsi);
if (item == LB_ERR) {
/*
* No selection so no text. */ ThreadUnlock(&tlpwndList); return 0; }
cchText = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXTLEN, item, 0, fAnsi);
cchText++; if ((cchText <= cchString) || (!TestWF(pcbox->spwnd, WFWIN31COMPAT) && cchString == 2)) { /*
* Just do the copy if the given buffer size is large enough to hold * everything. Or if old 3.0 app. (Norton used to pass 2 & expect 3 * chars including the \0 in 3.0; Bug #7018 win31: vatsanp) */ dw = (int)SendMessageWorker(pcbox->spwndList, LB_GETTEXT, item, (LPARAM)pString, fAnsi); ThreadUnlock(&tlpwndList); return dw; }
if (!(pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR)))) {
/*
* Bail. Not enough memory to chop up the text. */ ThreadUnlock(&tlpwndList); return 0; }
try { SendMessageWorker(pcbox->spwndList, LB_GETTEXT, item, (LPARAM)pText, fAnsi); if (fAnsi) { RtlCopyMemory((PBYTE)pString, (PBYTE)pText, cchString); ((LPSTR)pString)[cchString - 1] = 0; } else { RtlCopyMemory((PBYTE)pString, (PBYTE)pText, cchString * sizeof(WCHAR)); ((LPWSTR)pString)[cchString - 1] = 0; } } finally { UserLocalFree((HANDLE)pText); }
ThreadUnlock(&tlpwndList); return cchString; }
|