#include "ctlspriv.h"
#pragma hdrstop
#include "usrctl32.h"
#include "combo.h"
#include "listbox.h"    // For LBIV struct


//---------------------------------------------------------------------------//
//
//  InitComboboxClass() - Registers the control's window class 
//
BOOL InitComboboxClass(HINSTANCE hInstance)
{
    WNDCLASS wc;

    wc.lpfnWndProc   = ComboBox_WndProc;
    wc.lpszClassName = WC_COMBOBOX;
    wc.style         = CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = sizeof(PCBOX);
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName  = NULL;

    if (!RegisterClass(&wc) && !GetClassInfo(hInstance, WC_COMBOBOX, &wc))
        return FALSE;

    return TRUE;
}


//---------------------------------------------------------------------------//
//
//  InitComboLBoxClass() - Registers the control's dropdown window class 
//
// The dropdown list is a specially registered version
// of the listbox control called ComboLBox. We need to
// register this dummy control since apps looking for a
// combobox's listbox look for the classname ComboLBox 
//
BOOL FAR PASCAL InitComboLBoxClass(HINSTANCE hinst)
{
    WNDCLASS wc;

    wc.lpfnWndProc     = ListBox_WndProc;
    wc.hCursor         = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon           = NULL;
    wc.lpszMenuName    = NULL;
    wc.hInstance       = hinst;
    wc.lpszClassName   = WC_COMBOLBOX;
    wc.hbrBackground   = (HBRUSH)(COLOR_WINDOW + 1); // NULL;
    wc.style           = CS_GLOBALCLASS | CS_SAVEBITS | CS_DBLCLKS;
    wc.cbWndExtra      = sizeof(PLBIV);
    wc.cbClsExtra      = 0;

    if (!RegisterClass(&wc) && !GetClassInfo(hinst, WC_COMBOLBOX, &wc))
        return FALSE;

    return TRUE;

}


//---------------------------------------------------------------------------//
//
// ComboBox_PressButton()
//
// Pops combobox button back up.
//
VOID ComboBox_PressButton(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 = pcbox->hwnd;

        pcbox->fButtonPressed = (fPress != 0);
        if (pcbox->f3DCombo)
        {
            InvalidateRect(hwnd, &pcbox->buttonrc, TRUE);
        }
        else
        {
            RECT rc;

            CopyRect(&rc, &pcbox->buttonrc);
            InflateRect(&rc, 0, GetSystemMetrics(SM_CYEDGE));
            InvalidateRect(hwnd, &rc, TRUE);
        }

        UpdateWindow(hwnd);

        NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEX_COMBOBOX_BUTTON);
    }
}


//---------------------------------------------------------------------------//
//
// ComboBox_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.
//
VOID ComboBox_HotTrack(PCBOX pcbox, POINT pt)
{
    if (!pcbox->fButtonHotTracked && !pcbox->fMouseDown) 
    {
        TRACKMOUSEEVENT tme; 

        tme.cbSize      = sizeof(TRACKMOUSEEVENT);
        tme.dwFlags     = TME_LEAVE;
        tme.hwndTrack   = pcbox->hwnd;
        tme.dwHoverTime = 0;
        if (TrackMouseEvent(&tme)) 
        {
            if ((pcbox->CBoxStyle == SDROPDOWN &&
                 PtInRect(&pcbox->buttonrc, pt)) ||
                 pcbox->CBoxStyle == SDROPDOWNLIST) 
            {
                pcbox->fButtonHotTracked = TRUE;
                InvalidateRect(pcbox->hwnd, NULL, TRUE);
            }
            else
            {
                pcbox->fButtonHotTracked = FALSE;
            }
        }
    }
}


//---------------------------------------------------------------------------//
//
// ComboBox_DBCharHandler
//
// Double Byte character handler for ANSI ComboBox
//
LRESULT ComboBox_DBCharHandler(PCBOX pcbox, HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    WORD w;
    HWND hwndSend;

    w = DbcsCombine(hwnd, (BYTE)wParam);
    if (w == 0) 
    {
        return CB_ERR;  // Failed to assemble DBCS
    }

    UserAssert(pcbox->hwndList);
    if (pcbox->fNoEdit) 
    {
        hwndSend = pcbox->hwndList;
    } 
    else if (pcbox->hwndEdit) 
    {
        TraceMsg(TF_STANDARD, "UxCombobox: ComboBoxWndProcWorker: WM_CHAR is posted to Combobox itself(%08x).",
                hwnd);
        hwndSend = pcbox->hwndEdit;
    } 
    else 
    {
        return CB_ERR;
    }

    TraceMsg(TF_STANDARD, "UxCombobox: ComboBoxWndProcWorker: sending WM_CHAR %04x", w);

    if (!TestWF(hwndSend, WFANSIPROC)) 
    {
        //
        // If receiver is not ANSI WndProc (may be subclassed?),
        // send a UNICODE message.
        //
        WCHAR wChar;
        LPWSTR lpwstr = &wChar;

        if (MBToWCSEx(CP_ACP, (LPCSTR)&w, 2, &lpwstr, 1, FALSE) == 0) 
        {
            TraceMsg(TF_STANDARD, "UxCombobox: ComboBoxWndProcWorker: cannot convert 0x%04x to UNICODE.", w);
            return CB_ERR;
        }

        return SendMessage(hwndSend, message, wChar, lParam);
    }

    //
    // 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(hwndSend, message, CrackCombinedDbcsTB(w), lParam);
    //
    return SendMessage(hwndSend, message, wParam, lParam);
}


//---------------------------------------------------------------------------//
BOOL ComboBox_MsgOKInit(UINT message, LRESULT* plRet)
{
    switch (message) 
    {
    default:
        break;
    case WM_SIZE:
    case CB_SETMINVISIBLE:
    case CB_GETMINVISIBLE:
        *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;
}


//---------------------------------------------------------------------------//
//
// ComboBox_MessageItemHandler
//
// Handles WM_DRAWITEM,WM_MEASUREITEM,WM_DELETEITEM,WM_COMPAREITEM
// messages from the listbox.
//
LRESULT ComboBox_MessageItemHandler(PCBOX pcbox, UINT uMsg, LPVOID lpv)
{
    LRESULT lRet;

    //
    // Send the <lpv>item message back to the application after changing some
    // parameters to their combo box specific versions.
    //
    ((LPMEASUREITEMSTRUCT)lpv)->CtlType = ODT_COMBOBOX;
    ((LPMEASUREITEMSTRUCT)lpv)->CtlID = GetWindowID(pcbox->hwnd);
 
    switch (uMsg)
    {
    case WM_DRAWITEM:
        ((LPDRAWITEMSTRUCT)lpv)->hwndItem = pcbox->hwnd;
        break;

    case WM_DELETEITEM:
        ((LPDELETEITEMSTRUCT)lpv)->hwndItem = pcbox->hwnd;
        break;

    case WM_COMPAREITEM:
        ((LPCOMPAREITEMSTRUCT)lpv)->hwndItem = pcbox->hwnd;
        break;
    }

    lRet = SendMessage(pcbox->hwndParent, uMsg, (WPARAM)GetWindowID(pcbox->hwnd), (LPARAM)lpv);

    return lRet;
}


//---------------------------------------------------------------------------//
VOID ComboBox_Paint(PCBOX pcbox, HDC hdc)
{
    RECT rc;
    UINT msg;
    HBRUSH hbr;
    INT iStateId;

    CCDBUFFER ccdb;

    if (pcbox->fButtonPressed)
    {
        iStateId = CBXS_PRESSED;
    }
    else if ( !IsWindowEnabled(pcbox->hwnd))
    {
        iStateId = CBXS_DISABLED;
    }
    else if (pcbox->fButtonHotTracked)
    {
        iStateId = CBXS_HOT;
    }
    else
    {
        iStateId = CBXS_NORMAL;
    }
        
    rc.left = rc.top = 0;
    rc.right = pcbox->cxCombo;
    rc.bottom = pcbox->cyCombo;

    hdc = CCBeginDoubleBuffer(hdc, &rc, &ccdb);

    if ( !pcbox->hTheme )
    {
        DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST | (!pcbox->f3DCombo ? BF_FLAT | BF_MONO : 0));
    }
    else
    {
        DrawThemeBackground(pcbox->hTheme, hdc, 0, iStateId, &rc, 0);
    }

    if ( !IsRectEmpty(&pcbox->buttonrc) )
    {
        //
        // Draw in the dropdown arrow button
        //
        if (!pcbox->hTheme)
        {
            DrawFrameControl(hdc, &pcbox->buttonrc, DFC_SCROLL,
                DFCS_SCROLLCOMBOBOX |
                (pcbox->fButtonPressed ? DFCS_PUSHED | DFCS_FLAT : 0) |
                (TESTFLAG(GET_STYLE(pcbox), WS_DISABLED) ? DFCS_INACTIVE : 0) |
                (pcbox->fButtonHotTracked ? DFCS_HOT: 0));
        }
        else
        {
            DrawThemeBackground(pcbox->hTheme, hdc, CP_DROPDOWNBUTTON, iStateId, &pcbox->buttonrc, 0);
        }

        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 (TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT)) 
    {
        ULONG ulStyle = pcbox->hwndEdit ? GetWindowStyle(pcbox->hwndEdit) : 0;
        if (TESTFLAG(GET_STYLE(pcbox), WS_DISABLED) ||
            (!pcbox->fNoEdit && pcbox->hwndEdit && (ulStyle & ES_READONLY)))
        {
            msg = WM_CTLCOLORSTATIC;
        }
    } 
    else
    {
        msg = WM_CTLCOLORLISTBOX;
    }

    //
    // GetControlBrush
    //
    hbr = (HBRUSH)SendMessage(GetParent(pcbox->hwnd), msg, (WPARAM)hdc, (LPARAM)pcbox->hwnd);

    if (pcbox->fNoEdit)
    {
        ComboBox_InternalUpdateEditWindow(pcbox, hdc);
    }
    else if (!pcbox->hTheme)
    {
        FillRect(hdc, &rc, hbr);
    }

    CCEndDoubleBuffer(&ccdb);
}


//---------------------------------------------------------------------------//
//
// ComboBox_NotifyParent
//
// Sends the notification code to the parent of the combo box control
//
VOID ComboBox_NotifyParent(PCBOX pcbox, short notificationCode)
{
    HWND hwndSend = (pcbox->hwndParent != 0) ? pcbox->hwndParent : pcbox->hwnd;

    //
    // wParam contains Control ID and notification code.
    // lParam contains Handle to window
    //
    SendMessage(hwndSend, WM_COMMAND,
            MAKELONG(GetWindowID(pcbox->hwnd), notificationCode),
            (LPARAM)pcbox->hwnd);
}


//---------------------------------------------------------------------------//
//
// ComboBox_UpdateListBoxWindow
//
// 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.
//
VOID ComboBox_UpdateListBoxWindow(PCBOX pcbox, BOOL fSelectionAlso)
{

    if (pcbox->hwndEdit) 
    {
        INT    cchText;
        INT    sItem, sSel;
        LPWSTR pText = NULL;

        sItem = CB_ERR;

        cchText = (int)SendMessage(pcbox->hwndEdit, WM_GETTEXTLENGTH, 0, 0);
        if (cchText) 
        {
            cchText++;
            pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR));
            if (pText != NULL) 
            {
                try 
                {
                    SendMessage(pcbox->hwndEdit, WM_GETTEXT, cchText, (LPARAM)pText);
                    sItem = (int)SendMessage(pcbox->hwndList, LB_FINDSTRING,
                            (WPARAM)-1L, (LPARAM)pText);
                } 
                finally 
                {
                    UserLocalFree((HANDLE)pText);
                }
            }
        }

        if (fSelectionAlso) 
        {
            sSel = sItem;
        } 
        else 
        {
            sSel = CB_ERR;
        }

        if (sItem == CB_ERR)
        {
            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 && !TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
            {
                sSel = 0;
            }
        }

        SendMessage(pcbox->hwndList, LB_SETCURSEL, (DWORD)sSel, 0);
        SendMessage(pcbox->hwndList, LB_SETCARETINDEX, (DWORD)sItem, 0);
        SendMessage(pcbox->hwndList, LB_SETTOPINDEX, (DWORD)sItem, 0);
    }
}


//---------------------------------------------------------------------------//
//
// ComboBox_InvertStaticWindow
//
// Inverts the static text/picture window associated with the combo
// box.  Gets its own hdc, if the one given is null.
//
VOID ComboBox_InvertStaticWindow(PCBOX pcbox, BOOL fNewSelectionState, HDC hdc)
{
    BOOL focusSave = pcbox->fFocus;

    pcbox->fFocus = (UINT)fNewSelectionState;
    ComboBox_InternalUpdateEditWindow(pcbox, hdc);

    pcbox->fFocus = (UINT)focusSave;
}


//---------------------------------------------------------------------------//
//
// ComboBox_GetFocusHandler
//
// Handles getting the focus for the combo box
//
VOID ComboBox_GetFocusHandler(PCBOX pcbox)
{
    if (pcbox->fFocus)
    {
        return;
    }

    //
    // The combo box has gotten the focus for the first time.
    //

    //
    // First turn on the listbox caret
    //

    if (pcbox->CBoxStyle == SDROPDOWNLIST)
    {
       SendMessage(pcbox->hwndList, LBCB_CARETON, 0, 0);
    }

    //
    // and select all the text in the editcontrol or static text rectangle.
    //

    if (pcbox->fNoEdit) 
    {
        //
        // Invert the static text rectangle
        //
        ComboBox_InvertStaticWindow(pcbox, TRUE, (HDC)NULL);
    } 
    else if (pcbox->hwndEdit) 
    {
        UserAssert(pcbox->hwnd);
        SendMessage(pcbox->hwndEdit, EM_SETSEL, 0, MAXLONG);
    }

    pcbox->fFocus = TRUE;

    //
    // Notify the parent we have the focus
    //
    ComboBox_NotifyParent(pcbox, CBN_SETFOCUS);
}


//---------------------------------------------------------------------------//
//
// ComboBox_KillFocusHandler
//
// Handles losing the focus for the combo box.
//
VOID ComboBox_KillFocusHandler(PCBOX pcbox)
{
    if (!pcbox->fFocus || pcbox->hwndList == NULL)
    {
        return;
    }

    //
    // 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.
    //
    SendMessage(pcbox->hwnd, WM_LBUTTONUP, 0L, 0xFFFFFFFFL);
    if (!ComboBox_HideListBoxWindow(pcbox, TRUE, FALSE))
    {
        return;
    }

    //
    // Turn off the listbox caret
    //
    if (pcbox->CBoxStyle == SDROPDOWNLIST)
    {
       SendMessage(pcbox->hwndList, LBCB_CARETOFF, 0, 0);
    }

    if (pcbox->fNoEdit) 
    {
        //
        // Invert the static text rectangle
        //
        ComboBox_InvertStaticWindow(pcbox, FALSE, (HDC)NULL);
    } 
    else if (pcbox->hwndEdit) 
    {
        SendMessage(pcbox->hwndEdit, EM_SETSEL, 0, 0);
    }

    pcbox->fFocus = FALSE;
    ComboBox_NotifyParent(pcbox, CBN_KILLFOCUS);
}


//---------------------------------------------------------------------------//
//
// ComboBox_CommandHandler
//
// Check the various notification codes from the controls and do the
// proper thing.
// always returns 0L
//
LONG ComboBox_CommandHandler(PCBOX pcbox, DWORD wParam, HWND hwndControl)
{
    //
    // Check the edit control notification codes.  Note that currently, edit
    // controls don't send EN_KILLFOCUS messages to the parent.
    //
    if (!pcbox->fNoEdit && (hwndControl == pcbox->hwndEdit)) 
    {
        //
        // 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.
                //
                ComboBox_GetFocusHandler(pcbox);
            }

            break;

        case EN_CHANGE:
            ComboBox_NotifyParent(pcbox, CBN_EDITCHANGE);
            ComboBox_UpdateListBoxWindow(pcbox, FALSE);
            break;

        case EN_UPDATE:
            ComboBox_NotifyParent(pcbox, CBN_EDITUPDATE);
            break;

        case EN_ERRSPACE:
            ComboBox_NotifyParent(pcbox, CBN_ERRSPACE);
            break;
        }
    }

    //
    // Check listbox control notification codes
    //
    if (hwndControl == pcbox->hwndList) 
    {
        //
        // Listbox control notification codes
        //
        switch ((int)HIWORD(wParam)) 
        {
        case LBN_DBLCLK:
            ComboBox_NotifyParent(pcbox, CBN_DBLCLK);
            break;

        case LBN_ERRSPACE:
            ComboBox_NotifyParent(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 (!ComboBox_HideListBoxWindow(pcbox, TRUE, TRUE))
                {
                    return 0;
                }
            } 
            else 
            {
                pcbox->fKeyboardSelInListBox = FALSE;
            }

            ComboBox_NotifyParent(pcbox, CBN_SELCHANGE);
            ComboBox_InternalUpdateEditWindow(pcbox, NULL);

            if (pcbox->fNoEdit)
            {
                NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, pcbox->hwnd, OBJID_CLIENT, INDEX_COMBOBOX);
            }

            break;
        }
    }

    return 0;
}


//---------------------------------------------------------------------------//
//
// ComboBox_CompleteEditWindow
//
//
// 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.
//
VOID ComboBox_CompleteEditWindow(PCBOX pcbox)
{
    int cchText;
    int cchItemText;
    int itemNumber;
    LPWSTR pText;

    //
    // Firstly check the edit control.
    // 
    if (pcbox->hwndEdit == NULL) 
    {
        return;
    }

    //
    // +1 for null terminator
    //
    cchText = (int)SendMessage(pcbox->hwndEdit, WM_GETTEXTLENGTH, 0, 0);

    if (cchText) 
    {
        cchText++;
        pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR));
        if (!pText)
        {
            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 
        {
            SendMessage(pcbox->hwndEdit, WM_GETTEXT, cchText, (LPARAM)pText);
            itemNumber = (int)SendMessage(pcbox->hwndList,
                    LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pText);

            if (itemNumber == -1)
            {
                itemNumber = (int)SendMessage(pcbox->hwndList,
                        LB_FINDSTRING, (WPARAM)-1, (LPARAM)pText);
            }
        } 
        finally 
        {
            UserLocalFree((HANDLE)pText);
        }

        if (itemNumber == -1) 
        {
            //
            // No close match.  Blow off.
            //
            goto Unlock;
        }

        cchItemText = (int)SendMessage(pcbox->hwndList, LB_GETTEXTLEN,
                itemNumber, 0);
        if (cchItemText) 
        {
            cchItemText++;
            pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchItemText*sizeof(WCHAR));
            if (!pText)
            {
                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 
            {
                SendMessage(pcbox->hwndList, LB_GETTEXT, itemNumber, (LPARAM)pText);
                SendMessage(pcbox->hwndEdit, WM_SETTEXT, 0, (LPARAM)pText);
            } 
            finally 
            {
                UserLocalFree((HANDLE)pText);
            }

            SendMessage(pcbox->hwndEdit, EM_SETSEL, 0, MAXLONG);
        }
    }

Unlock:
    return;
}


//---------------------------------------------------------------------------//
//
// ComboBox_HideListBoxWindow
//
// Hides the dropdown listbox window if it is a dropdown style.
//
BOOL ComboBox_HideListBoxWindow(PCBOX pcbox, BOOL fNotifyParent, BOOL fSelEndOK)
{
    HWND hwnd = pcbox->hwnd;
    HWND hwndList = pcbox->hwndList;

    //
    // 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 && TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN31COMPAT) &&
        ((pcbox->CBoxStyle & SDROPPABLE) || fSelEndOK)) 
    {
        if (fSelEndOK)
        {
            ComboBox_NotifyParent(pcbox, CBN_SELENDOK);
        }
        else
        {
            ComboBox_NotifyParent(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.
    //
    SendMessage(pcbox->hwndList, LBCB_ENDTRACK, fSelEndOK, 0);

    if (pcbox->fLBoxVisible) 
    {
        WORD swpFlags = SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE;

        if (!TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN31COMPAT))
        {
            swpFlags |= SWP_FRAMECHANGED;
        }

        pcbox->fLBoxVisible = FALSE;

        //
        // Hide the listbox window
        //
        ShowWindow(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))
        {
            InvalidateRect(hwnd, &pcbox->editrc, TRUE);
        }

        SetWindowPos(hwnd, HWND_TOP, 0, 0,
                pcbox->cxCombo, pcbox->cyCombo, swpFlags);

        //
        // In case size didn't change
        //
        UpdateWindow(hwnd);

        if (pcbox->CBoxStyle & SEDITABLE) 
        {
            ComboBox_CompleteEditWindow(pcbox);
        }

        if (fNotifyParent) 
        {
            //
            // Notify parent we will be popping up the combo box.
            //
            ComboBox_NotifyParent(pcbox, CBN_CLOSEUP);

            if (!IsWindow(hwnd))
            {
                return FALSE;
            }
        }
    }

    return TRUE;
}


//---------------------------------------------------------------------------//
//
// ComboBox_ShowListBoxWindow
//
// Lowers the dropdown listbox window.
//
VOID ComboBox_ShowListBoxWindow(PCBOX pcbox, BOOL fTrack)
{
    RECT        editrc;
    RECT        rcWindow;
    RECT        rcList;
    int         itemNumber;
    int         iHeight;
    int         yTop;
    DWORD       dwMult;
    int         cyItem;
    HWND        hwnd = pcbox->hwnd;
    HWND        hwndList = pcbox->hwndList;
    BOOL        fAnimPos;
    HMONITOR    hMonitor;
    MONITORINFO mi = {0};
    BOOL        bCBAnim = FALSE;

    //
    // This function is only called for droppable list comboboxes
    //
    UserAssert(pcbox->CBoxStyle & SDROPPABLE);

    //
    // Notify parent we will be dropping down the combo box.
    //
    ComboBox_NotifyParent(pcbox, CBN_DROPDOWN);

    //
    // Invalidate the button rect so that the depressed arrow is drawn.
    //
    InvalidateRect(hwnd, &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.
        //
        ComboBox_UpdateListBoxWindow(pcbox, !pcbox->fMouseDown);

        if (!pcbox->fMouseDown)
        {
            ComboBox_CompleteEditWindow(pcbox);
        }
    } 
    else 
    {
        //
        // Scroll the currently selected item to the top of the listbox.
        //
        itemNumber = (int)SendMessage(pcbox->hwndList, LB_GETCURSEL, 0, 0);
        if (itemNumber == -1) 
        {
            itemNumber = 0;
        }

        SendMessage(pcbox->hwndList, LB_SETTOPINDEX, itemNumber, 0);
        SendMessage(pcbox->hwndList, LBCB_CARETON, 0, 0);

        //
        // 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...
        //
        InvalidateRect(hwnd, &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
    //
    GetWindowRect(pcbox->hwnd, &rcWindow);
    editrc.left   = rcWindow.left;
    editrc.top    = rcWindow.top;
    editrc.right  = rcWindow.left + pcbox->cxCombo;
    editrc.bottom = rcWindow.top  + pcbox->cyCombo;

    //
    // List area
    //
    cyItem = (int)SendMessage(pcbox->hwndList, LB_GETITEMHEIGHT, 0, 0);

    if (cyItem == 0) 
    {
        //
        // Make sure that it's not 0
        //
        TraceMsg(TF_STANDARD, "UxCombobox: LB_GETITEMHEIGHT is returning 0" );
        cyItem = SYSFONT_CYCHAR;
    }

    //
    //  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)
    //
    GetWindowRect(pcbox->hwndList, &rcList);
    iHeight = max(pcbox->cyDrop, rcList.bottom - rcList.top);

    if (dwMult = (DWORD)SendMessage(pcbox->hwndList, LB_GETCOUNT, 0, 0)) 
    {
        dwMult = (DWORD)(LOWORD(dwMult) * cyItem);
        dwMult += GetSystemMetrics(SM_CYEDGE);

        if (dwMult < 0x7FFF)
        {
            iHeight = min(LOWORD(dwMult), iHeight);
        }
    }

    if (!GET_STYLE(pcbox) & CBS_NOINTEGRALHEIGHT) 
    {
        UserAssert(cyItem);
        iHeight = ((iHeight - GetSystemMetrics(SM_CYEDGE)) / cyItem) * cyItem + GetSystemMetrics(SM_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.
    //
    hMonitor = MonitorFromWindow(pcbox->hwnd, MONITOR_DEFAULTTOPRIMARY);
    mi.cbSize = sizeof(mi);
    GetMonitorInfo(hMonitor, &mi);
    if (editrc.bottom + iHeight <= mi.rcMonitor.bottom) 
    {
        yTop = editrc.bottom;
        if (!pcbox->f3DCombo)
        {
            yTop -= GetSystemMetrics(SM_CYBORDER);
        }

        fAnimPos = TRUE;
    } 
    else 
    {
        yTop = max(editrc.top - iHeight, mi.rcMonitor.top);
        if (!pcbox->f3DCombo)
        {
            yTop += GetSystemMetrics(SM_CYBORDER);
        }

        fAnimPos = FALSE;
    }

    if (!TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
    {
        //
        // 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
        //

        GetWindowRect(pcbox->hwndList, &rcList);
        if ((rcList.right - rcList.left ) > pcbox->cxDrop)
        {
            pcbox->cxDrop = rcList.right - rcList.left;
        }
    }

    if (!TESTFLAG(GET_EXSTYLE(pcbox), WS_EX_LAYOUTRTL))
    {
        SetWindowPos(hwndList, HWND_TOPMOST, editrc.left,
            yTop, max(pcbox->cxDrop, pcbox->cxCombo), iHeight, SWP_NOACTIVATE);
    }
    else
    {
        int cx = max(pcbox->cxDrop, pcbox->cxCombo);

        SetWindowPos(hwndList, HWND_TOPMOST, editrc.right - cx,
            yTop, cx, 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);

    SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, (LPVOID)&bCBAnim, 0);
    if (!bCBAnim)
    {
        ShowWindow(hwndList, SW_SHOWNA);
    } 
    else 
    {
        AnimateWindow(hwndList, CMS_QANIMATION, (fAnimPos ? AW_VER_POSITIVE :
                AW_VER_NEGATIVE) | AW_SLIDE);
    }

    //
    // Restart search buffer from first char
    //
    {
        PLBIV plb = ListBox_GetPtr(pcbox->hwndList);

        if ((plb != NULL) && (plb != (PLBIV)-1)) 
        {
            plb->iTypeSearch = 0;
        }
    }

    if (fTrack && TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
    {
        SendMessage(pcbox->hwndList, LBCB_STARTTRACK, FALSE, 0);
    }
}


//---------------------------------------------------------------------------//
//
// ComboBox_InternalUpdateEditWindow
//
// 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.
//
VOID ComboBox_InternalUpdateEditWindow(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 = pcbox->hwnd;

    sItem = (int)SendMessage(pcbox->hwndList, LB_GETCURSEL, 0, 0);

    //
    // 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)SendMessage(pcbox->hwndList, LB_GETTEXTLEN, (DWORD)sItem, 0);
            pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, (cchText+1) * sizeof(WCHAR));
            if (pText) 
            {
                cchText = (int)SendMessage(pcbox->hwndList, LB_GETTEXT,
                        (DWORD)sItem, (LPARAM)pText);
            }
            else
            {
                cchText = 0;
            }
        }

        if (!pcbox->fNoEdit) 
        {
            if (pcbox->hwndEdit) 
            {
                if (GET_STYLE(pcbox) & CBS_HASSTRINGS)
                {
                    SetWindowText(pcbox->hwndEdit, pText ? pText : TEXT(""));
                }

                if (pcbox->fFocus) 
                {
                    //
                    // Only hilite the text if we have the focus.
                    //
                    SendMessage(pcbox->hwndEdit, EM_SETSEL, 0, MAXLONG);
                }
            }
        } 
        else if (IsComboVisible(pcbox)) 
        {
            if (hdcPaint) 
            {
                hdc = hdcPaint;
            } 
            else 
            {
                hdc = GetDC(hwnd);
            }

            SetBkMode(hdc, OPAQUE);
            if (TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT)) 
            {
                if (TESTFLAG(GET_STYLE(pcbox), WS_DISABLED))
                {
                    msg = WM_CTLCOLORSTATIC;
                }
                else
                {
                    msg = WM_CTLCOLOREDIT;
                }
            } 
            else
            {
                msg = WM_CTLCOLORLISTBOX;
            }

            hbrControl = (HBRUSH)SendMessage(GetParent(hwnd), msg, (WPARAM)hdc, (LPARAM)hwnd);
            hbrSave = SelectObject(hdc, hbrControl);

            CopyRect(&rc, &pcbox->editrc);
            InflateRect(&rc, GetSystemMetrics(SM_CXBORDER), GetSystemMetrics(SM_CYBORDER));
            PatBlt(hdc, rc.left, rc.top, rc.right - rc.left,
                rc.bottom - rc.top, PATCOPY);
            InflateRect(&rc, -GetSystemMetrics(SM_CXBORDER), -GetSystemMetrics(SM_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 (!TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT) || !pcbox->OwnerDraw)
                {
                    FillRect(hdc, &rc, GetSysColorBrush(COLOR_HIGHLIGHT));
                }

                SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
                SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
            } 
            else if (TESTFLAG(GET_STYLE(pcbox), WS_DISABLED) && !pcbox->OwnerDraw) 
            {
                if ((COLORREF)GetSysColor(COLOR_GRAYTEXT) != GetBkColor(hdc))
                {
                    SetTextColor(hdc, GetSysColor(COLOR_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 = GetWindowID(pcbox->hwnd);
                dis.itemID = sItem;
                dis.itemAction = ODA_DRAWENTIRE;
                dis.itemState = (UINT)
                    ((pcbox->fFocus && !pcbox->fLBoxVisible ? ODS_SELECTED : 0) |
                    (TESTFLAG(GET_STYLE(pcbox), WS_DISABLED) ? ODS_DISABLED : 0) |
                    (pcbox->fFocus && !pcbox->fLBoxVisible ? ODS_FOCUS : 0) |
                    (TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT) ? ODS_COMBOBOXEDIT : 0) |
                    (TESTFLAG(GET_EXSTYLE(pcbox), WS_EXP_UIFOCUSHIDDEN) ? ODS_NOFOCUSRECT : 0) |
                    (TESTFLAG(GET_EXSTYLE(pcbox), WS_EXP_UIACCELHIDDEN) ? 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)SendMessage(pcbox->hwndList,
                        LB_GETITEMDATA, (UINT)sItem, 0);

                SendMessage(pcbox->hwndParent, 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 - GetSystemMetrics(SM_CXBORDER);
                } 
                else 
                {
                    x = rc.left + GetSystemMetrics(SM_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 + GetSystemMetrics(SM_CYBORDER), ETO_CLIPPED | ETO_OPAQUE,
                       &rc, pText ? pText : TEXT(""), cchText, NULL);
                if (pcbox->fFocus && !pcbox->fLBoxVisible) 
                {
                    if (!TESTFLAG(GET_EXSTYLE(pcbox), WS_EXP_UIFOCUSHIDDEN)) 
                    {
                        DrawFocusRect(hdc, &rc);
                    }
                }
            }

            if (pcbox->hFont && hOldFont) 
            {
                SelectObject(hdc, hOldFont);
            }

            if (hbrSave) 
            {
                SelectObject(hdc, hbrSave);
            }

            if (!hdcPaint) 
            {
                ReleaseDC(hwnd, hdc);
            }
        }

    } 
    finally 
    {
        if (pText != NULL)
        {
            UserLocalFree((HANDLE)pText);
        }
    }
}


//---------------------------------------------------------------------------//
//
// ComboBox_GetTextLengthHandler
//
// For the combo box without an edit control, returns size of current selected
// item
//
LONG ComboBox_GetTextLengthHandler(PCBOX pcbox)
{
    int item;
    int cchText;

    item = (int)SendMessage(pcbox->hwndList, LB_GETCURSEL, 0, 0);

    if (item == LB_ERR) 
    {
        //
        // No selection so no text.
        //
        cchText = 0;
    } 
    else 
    {
        cchText = (int)SendMessage(pcbox->hwndList, LB_GETTEXTLEN, item, 0);
    }

    return cchText;
}


//---------------------------------------------------------------------------//
//
// ComboBox_GetTextHandler
//
// For the combo box without an edit control, copies cbString bytes of the
// string in the static text box to the buffer given by lpszString.
//
LONG ComboBox_GetTextHandler(PCBOX pcbox, int cchString, LPWSTR lpszString)
{
    int    item;
    int    cchText;
    LPWSTR lpszBuffer;
    DWORD  dw;

    if (!cchString || !lpszString)
    {
        return 0;
    }

    //
    // Null the buffer to be nice.
    //
    *lpszString = 0;

    item = (int)SendMessage(pcbox->hwndList, LB_GETCURSEL, 0, 0);

    if (item == LB_ERR) 
    {
        //
        // No selection so no text.
        //
        return 0;
    }

    cchText = (int)SendMessage(pcbox->hwndList, LB_GETTEXTLEN, item, 0);

    cchText++;
    if ((cchText <= cchString) ||
            (!TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN31COMPAT) && 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)SendMessage(pcbox->hwndList, LB_GETTEXT, item,
                (LPARAM)lpszString);
        return dw;
    }

    lpszBuffer = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR));
    if (!lpszBuffer) 
    {
        //
        // Bail.  Not enough memory to chop up the text.
        //
        return 0;
    }

    try 
    {
        SendMessage(pcbox->hwndList, LB_GETTEXT, item, (LPARAM)lpszBuffer);
        RtlCopyMemory((PBYTE)lpszString, (PBYTE)lpszBuffer, cchString * sizeof(WCHAR));
        lpszString[cchString - 1] = 0;
    } 
    finally 
    {
        UserLocalFree((HANDLE)lpszBuffer);
    }

    return cchString;
}


//---------------------------------------------------------------------------//
// ComboBox_GetInfo 
//
// return information about this combobox to the caller
// in the ComboBoxInfo struct
//
BOOL ComboBox_GetInfo(PCBOX pcbox, PCOMBOBOXINFO pcbi)
{
    BOOL bRet = FALSE;

    if (!pcbi || pcbi->cbSize != sizeof(COMBOBOXINFO))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
    }
    else
    {
        //
        // populate the structure
        //
        pcbi->hwndCombo = pcbox->hwnd;
        pcbi->hwndItem  = pcbox->hwndEdit;
        pcbi->hwndList  = pcbox->hwndList;

        pcbi->rcItem   = pcbox->editrc;
        pcbi->rcButton = pcbox->buttonrc;

        pcbi->stateButton = 0;
        if (pcbox->CBoxStyle == CBS_SIMPLE)
        {
            pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
        }
        if (pcbox->fButtonPressed)
        {
            pcbi->stateButton |= STATE_SYSTEM_PRESSED;
        }

        bRet = TRUE;
    }

    return bRet;
}


//---------------------------------------------------------------------------//
//
// ComboBox_WndProc
//
// WndProc for comboboxes.
//
LRESULT WINAPI ComboBox_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PCBOX       pcbox;
    POINT       pt;
    LPWSTR      lpwsz = NULL;
    LRESULT     lReturn = TRUE;
    static BOOL fInit = TRUE;
    INT         i;
    RECT        rcCombo;
    RECT        rcList;
    RECT        rcWindow;

    //
    // Get the instance data for this combobox control
    //
    pcbox = ComboBox_GetPtr(hwnd);
    if (!pcbox && uMsg != WM_NCCREATE)
    {
        goto CallDWP;
    }

    //
    // Protect the combobox during the initialization.
    //
    if (!pcbox || pcbox->hwndList == NULL) 
    {
        if (!ComboBox_MsgOKInit(uMsg, &lReturn)) 
        {
            TraceMsg(TF_STANDARD, "UxCombobox: ComboBoxWndProcWorker: msg=%04x is sent to hwnd=%08x in the middle of initialization.",
                    uMsg, hwnd);
            return lReturn;
        }
    }

    //
    // Dispatch the various messages we can receive
    //
    switch (uMsg) 
    {
    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.
        //
        ComboBox_KillFocusHandler(pcbox);
        break;

    case WM_COMMAND:

        //
        // So that we can handle notification messages from the listbox and
        // edit control.
        //
        return ComboBox_CommandHandler(pcbox, (DWORD)wParam, (HWND)lParam);

    case WM_STYLECHANGED:
    {
        LONG OldStyle;
        LONG NewStyle = 0;

        UserAssert(pcbox->hwndList != NULL);

        pcbox->fRtoLReading = TESTFLAG(GET_EXSTYLE(pcbox), WS_EX_RTLREADING);
        pcbox->fRightAlign  = TESTFLAG(GET_EXSTYLE(pcbox), WS_EX_RIGHT);

        if (pcbox->fRtoLReading)
        {
            NewStyle |= (WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
        }

        if (pcbox->fRightAlign)
        {
            NewStyle |= WS_EX_RIGHT;
        }

        OldStyle = GetWindowExStyle(pcbox->hwndList) & ~(WS_EX_RIGHT|WS_EX_RTLREADING|WS_EX_LEFTSCROLLBAR);
        SetWindowLong(pcbox->hwndList, GWL_EXSTYLE, OldStyle|NewStyle);

        if (!pcbox->fNoEdit && pcbox->hwndEdit) 
        {
            OldStyle = GetWindowExStyle(pcbox->hwndEdit) & ~(WS_EX_RIGHT|WS_EX_RTLREADING|WS_EX_LEFTSCROLLBAR);
            SetWindowLong(pcbox->hwndEdit, GWL_EXSTYLE, OldStyle|NewStyle);
        }

        ComboBox_Position(pcbox);
        InvalidateRect(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 (TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT)) 
        {
            LRESULT ret;

            ret = SendMessage(pcbox->hwndParent, uMsg, wParam, lParam);
            return ret;
        } 
        else
        {
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }

        break;

    case WM_GETTEXT:
        if (pcbox->fNoEdit) 
        {
            return ComboBox_GetTextHandler(pcbox, (int)wParam, (LPWSTR)lParam);
        }

        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 ComboBox_GetTextLengthHandler(pcbox);
        }

        // 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 ComboBox_CreateHandler(pcbox, hwnd);

    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 (!DefWindowProc(hwnd, uMsg, wParam, lParam))
            return FALSE;

        if ( (lParam & PRF_OWNED) && 
             (pcbox->CBoxStyle & SDROPPABLE) &&
             IsWindowVisible(pcbox->hwndList) ) 
        {
            INT iDC = SaveDC((HDC) wParam);

            GetWindowRect(hwnd, &rcCombo);
            GetWindowRect(pcbox->hwndList, &rcList);

            OffsetWindowOrgEx((HDC) wParam, 0, rcCombo.top - rcList.top, NULL);

            lParam &= ~PRF_CHECKVISIBLE;
            SendMessage(pcbox->hwndList, WM_PRINT, wParam, lParam);
            RestoreDC((HDC) wParam, iDC);
        }

        return TRUE;

    case WM_PRINTCLIENT:
        ComboBox_Paint(pcbox, (HDC) wParam);
        break;

    case WM_PAINT: 
    {
        HDC hdc;
        PAINTSTRUCT ps;

        //
        // wParam - perhaps a hdc
        //
        hdc = (wParam) ? (HDC) wParam : BeginPaint(hwnd, &ps);

        if (IsComboVisible(pcbox))
        {
            ComboBox_Paint(pcbox, hdc);
        }

        if (!wParam)
        {
            EndPaint(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;
    }

    case WM_SETFONT:
        ComboBox_SetFontHandler(pcbox, (HANDLE)wParam, LOWORD(lParam));
        break;

    case WM_SYSKEYDOWN:
        //
        // Check if the alt key is down
        //
        if (lParam & 0x20000000L)
        {
            //
            // 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
                //
                ComboBox_ShowListBoxWindow(pcbox, TRUE);
            } 
            else 
            {
                //
                // Ok, the listbox is visible.  So hide the listbox window.
                //
                if (!ComboBox_HideListBoxWindow(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)) 
            {
                ComboBox_HideListBoxWindow(pcbox, TRUE, (wParam != VK_ESCAPE));
                break;
            }
        }

        //
        // FALL THROUGH
        //

    case WM_CHAR:
        if (g_fDBCSEnabled && IsDBCSLeadByte((BYTE)wParam)) 
        {
            return ComboBox_DBCharHandler(pcbox, hwnd, uMsg, wParam, lParam);
        }

        if (pcbox->fNoEdit) 
        {
            goto CallListSendMessage;
        }
        else
        {
            goto CallEditSendMessage;
        }
        break;

    case WM_LBUTTONDBLCLK:
    case WM_LBUTTONDOWN:

        pcbox->fButtonHotTracked = FALSE;
        //
        // Set the focus to the combo box if we get a mouse click on it.
        //
        if (!pcbox->fFocus) 
        {
            SetFocus(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(&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;
                    ReleaseCapture();
                }
                ComboBox_PressButton(pcbox, FALSE);

                if (!ComboBox_HideListBoxWindow(pcbox, TRUE, TRUE))
                {
                    return 0L;
                }
            } 
            else 
            {
                ComboBox_ShowListBoxWindow(pcbox, FALSE);

                // Setting and resetting this flag must always be followed
                // imediately by SetCapture or ReleaseCapture
                //
                pcbox->fMouseDown = TRUE;
                SetCapture(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->hwndEdit == 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;

        while (i-- > 0) 
        {
            SendMessage(pcbox->hwndEdit, WM_KEYDOWN, wParam, 0);
        }

        return TRUE;

    case WM_CAPTURECHANGED:
        if (!TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
        {
            return 0;
        }

        if ((pcbox->fMouseDown)) 
        {
            pcbox->fMouseDown = FALSE;
            ComboBox_PressButton(pcbox, FALSE);

            //
            // Pop combo listbox back up, canceling.
            //
            if (pcbox->fLBoxVisible)
            {
                ComboBox_HideListBoxWindow(pcbox, TRUE, FALSE);
            }
        }
        break;

    case WM_LBUTTONUP:
        ComboBox_PressButton(pcbox, FALSE);

        //
        // Clear this flag so that mouse moves aren't sent to the listbox
        //
        if (pcbox->fMouseDown || ((pcbox->CBoxStyle & SDROPPABLE) && pcbox->fLBoxVisible))  
        {
            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.
                    //
                    ComboBox_UpdateListBoxWindow(pcbox, TRUE);
                    ComboBox_CompleteEditWindow(pcbox);
                }

                ReleaseCapture();
            }

            //
            // Now, we want listbox to track mouse moves while mouse up
            // until mouse down, and select items as though they were
            // clicked on.
            //
            if (TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT)) 
            {
                SendMessage(pcbox->hwndList, LBCB_STARTTRACK, FALSE, 0);
            }
        }

        if (pcbox->hTheme)
        {
            POINTSTOPOINT(pt, lParam);
            ComboBox_HotTrack(pcbox, pt);
        }
        
        break;

    case WM_MOUSELEAVE:
        pcbox->fButtonHotTracked = FALSE;
        InvalidateRect(hwnd, NULL, TRUE);
        break;

    case WM_MOUSEMOVE:
        if (pcbox->fMouseDown) 
        {
            POINTSTOPOINT(pt, lParam);

            ClientToScreen(hwnd, &pt);
            GetWindowRect(pcbox->hwndList, &rcList);
            if (PtInRect(&rcList, 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;
                ReleaseCapture();

                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
                    //
                    ComboBox_UpdateListBoxWindow(pcbox, TRUE);
                }

                //
                // Convert point to listbox coordinates and send a buttondown
                // message to the listbox window.
                //
                ScreenToClient(pcbox->hwndList, &pt);
                lParam = POINTTOPOINTS(pt);
                uMsg = WM_LBUTTONDOWN;

                goto CallListSendMessage;
            }
        }

        if (pcbox->hTheme)
        {
            POINTSTOPOINT(pt, lParam);
            ComboBox_HotTrack(pcbox, pt);
        }

        break;

    case WM_NCDESTROY:
    case WM_FINALDESTROY:
        ComboBox_NcDestroyHandler(hwnd, pcbox);

        break;

    case WM_SETFOCUS:
        if (pcbox->fNoEdit) 
        {
            //
            // There is no editcontrol so set the focus to the combo box itself.
            //
            ComboBox_GetFocusHandler(pcbox);
        } 
        else if (pcbox->hwndEdit) 
        {
            //
            // Set the focus to the edit control window if there is one
            //
            SetFocus(pcbox->hwndEdit);
        }
        break;

    case WM_KILLFOCUS:

        //
        // wParam has the new focus hwnd
        //
        if ((wParam == 0) || !IsChild(hwnd, (HWND)wParam)) 
        {
            //
            // We only give up the focus if the new window getting the focus
            // doesn't belong to the combo box.
            //
            ComboBox_KillFocusHandler(pcbox);
        }

        if ( IsWindow(hwnd) )
        {
            PLBIV plb = ListBox_GetPtr(pcbox->hwndList);

            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->hwndEdit) 
        {
            SendMessage(pcbox->hwndEdit, uMsg, wParam, lParam);
        }

        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.
        //
        InvalidateRect(hwnd, NULL, FALSE);
        if ((pcbox->CBoxStyle & SEDITABLE) && pcbox->hwndEdit) 
        {
            //
            // Enable/disable the edit control window
            //
            EnableWindow(pcbox->hwndEdit, !TESTFLAG(GET_STYLE(pcbox), WS_DISABLED));
        }

        //
        // Enable/disable the listbox window
        //
        UserAssert(pcbox->hwndList);
        EnableWindow(pcbox->hwndList, !TESTFLAG(GET_STYLE(pcbox), WS_DISABLED));
      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->hwndList);
        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
        //
        GetWindowRect(hwnd, &rcWindow);
        if (pcbox->cxCombo == rcWindow.right - rcWindow.left) 
        {
            int iNewHeight = rcWindow.bottom - 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 0;
                }
            } 
            else 
            {
                //
                // Check if new height is the closed up height
                //
                if (pcbox->cyCombo == iNewHeight)
                {
                    return 0;
                }
            }
        }

        ComboBox_SizeHandler(pcbox);

        break;

    case WM_WINDOWPOSCHANGING:
        if (lParam)
        {
            ((LPWINDOWPOS)lParam)->flags |= SWP_NOCOPYBITS;
        }

        break;

    case WM_WININICHANGE:
        InitGlobalMetrics(wParam);
        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.
        //
        if ( lParam )
        {
            GetWindowRect(hwnd, &rcWindow);
            ((LPRECT)lParam)->left      = rcWindow.left;
            ((LPRECT)lParam)->top       = rcWindow.top;
            ((LPRECT)lParam)->right     = rcWindow.left + max(pcbox->cxDrop, pcbox->cxCombo);
            ((LPRECT)lParam)->bottom    = rcWindow.top + pcbox->cyCombo + pcbox->cyDrop;
        }
        else
        {
            lReturn = 0;
        }

        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;
                    ComboBox_Position(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
        //
        return lParam ? CBDir(pcbox, LOWORD(wParam), (LPWSTR)lParam) : CB_ERR;

    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;
            }

            TraceMsg(TF_STANDARD,
                    "UxCombobox: Invalid parameter \"wParam\" (%ld) to ComboBoxWndProcWorker",
                    wParam);

        } 
        else 
        {
            TraceMsg(TF_STANDARD,
                    "UxCombobox: Invalid message (%ld) sent to ComboBoxWndProcWorker",
                    uMsg);
        }

        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.
        //
        uMsg = 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.
        //
        uMsg = EM_LIMITTEXT;

        goto CallEditSendMessage;
        break;

    case CB_SETEDITSEL:

        //
        // wParam - ichStart
        // lParam - ichEnd
        //
        uMsg = 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)
        {
            uMsg = LB_ADDSTRING;
        }
        else
        {
            uMsg = (pcbox->fCase & UPPERCASE) ? LB_ADDSTRINGUPPER : LB_ADDSTRINGLOWER;
        }

        goto CallListSendMessage;
        break;

    case CB_DELETESTRING:

        //
        // wParam - index to string to be deleted
        // lParam - not used
        //
        uMsg = LB_DELETESTRING;

        goto CallListSendMessage;
        break;

    case CB_INITSTORAGE:
        //
        // wParamLo - number of items
        // lParam - number of bytes of string space
        //
        uMsg = LB_INITSTORAGE;

        goto CallListSendMessage;

    case CB_SETTOPINDEX:
        //
        // wParamLo - index to make top
        // lParam - not used
        //
        uMsg = LB_SETTOPINDEX;

        goto CallListSendMessage;

    case CB_GETTOPINDEX:
        //
        // wParamLo / lParam - not used
        //
        uMsg = LB_GETTOPINDEX;

        goto CallListSendMessage;

    case CB_GETCOUNT:
        //
        // wParam - not used
        // lParam - not used
        //
        uMsg = LB_GETCOUNT;

        goto CallListSendMessage;
        break;

    case CB_GETCURSEL:
        //
        // wParam - not used
        // lParam - not used
        //
        uMsg = LB_GETCURSEL;

        goto CallListSendMessage;
        break;

    case CB_GETLBTEXT:
        //
        // wParam - index of string to be copied
        // lParam - buffer that is to receive the string
        //
        uMsg = LB_GETTEXT;

        goto CallListSendMessage;
        break;

    case CB_GETLBTEXTLEN:
        //
        // wParam - index to string
        // lParam - now used for cbANSI
        //
        uMsg = LB_GETTEXTLEN;

        goto CallListSendMessage;
        break;

    case CB_INSERTSTRING:
        //
        // wParam - position to receive the string
        // lParam - points to the string
        //
        if (!pcbox->fCase)
        {
            uMsg = LB_INSERTSTRING;
        }
        else
        {
            uMsg = (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->hwndList);
        SendMessage(pcbox->hwndList, LB_RESETCONTENT, 0, 0);
        ComboBox_InternalUpdateEditWindow(pcbox, NULL);

        break;

    case CB_GETHORIZONTALEXTENT:
        uMsg = LB_GETHORIZONTALEXTENT;

        goto CallListSendMessage;

    case CB_SETHORIZONTALEXTENT:
        uMsg = LB_SETHORIZONTALEXTENT;

        goto CallListSendMessage;

    case CB_FINDSTRING:
        //
        // wParam - index of starting point for search
        // lParam - points to prefix string
        //
        uMsg = LB_FINDSTRING;

        goto CallListSendMessage;
        break;

    case CB_FINDSTRINGEXACT:
        //
        // wParam - index of starting point for search
        // lParam - points to a exact string
        //
        uMsg = LB_FINDSTRINGEXACT;

        goto CallListSendMessage;
        break;

    case CB_SELECTSTRING:
        //
        // wParam - index of starting point for search
        // lParam - points to prefix string
        //
        UserAssert(pcbox->hwndList);
        lParam = SendMessage(pcbox->hwndList, LB_SELECTSTRING, wParam, lParam);
        ComboBox_InternalUpdateEditWindow(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->hwndList);

        lParam = SendMessage(pcbox->hwndList, LB_SETCURSEL, wParam, lParam);
        if (lParam != -1) 
        {
            SendMessage(pcbox->hwndList, LB_SETTOPINDEX, wParam, 0);
        }
        ComboBox_InternalUpdateEditWindow(pcbox, NULL);

        return lParam;

    case CB_GETITEMDATA:
        uMsg = LB_GETITEMDATA;

        goto CallListSendMessage;
        break;

    case CB_SETITEMDATA:
        uMsg = LB_SETITEMDATA;

        goto CallListSendMessage;
        break;

    case CB_SETITEMHEIGHT:
        if (wParam == -1) 
        {
            if (HIWORD(lParam) != 0)
            {
                return CB_ERR;
            }

            return ComboBox_SetEditItemHeight(pcbox, LOWORD(lParam));
        }

        uMsg = LB_SETITEMHEIGHT;
        goto CallListSendMessage;

        break;

    case CB_GETITEMHEIGHT:
        if (wParam == -1)
        {
            return pcbox->editrc.bottom - pcbox->editrc.top;
        }

        uMsg = 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) 
        {
            ComboBox_ShowListBoxWindow(pcbox, TRUE);
        } 
        else 
        {
            if (!wParam && pcbox->fLBoxVisible) 
            {
                ComboBox_HideListBoxWindow(pcbox, TRUE, FALSE);
            }
        }

        break;

    case CB_SETLOCALE:
        //
        // wParam - locale id
        // lParam - not used
        //
        uMsg = LB_SETLOCALE;
        goto CallListSendMessage;

        break;

    case CB_GETLOCALE:
        //
        // wParam - not used
        // lParam - not used
        //
        uMsg = LB_GETLOCALE;
        goto CallListSendMessage;
        break;

    case CB_GETCOMBOBOXINFO:
        //
        // wParam - not used
        // lParam - pointer to COMBOBOXINFO struct
        //
        lReturn = ComboBox_GetInfo(pcbox, (PCOMBOBOXINFO)lParam);
        break;

    case CB_SETMINVISIBLE:
        if (wParam > 0)
        {
            PLBIV plb = ListBox_GetPtr(pcbox->hwndList);

            pcbox->iMinVisible = (int)wParam;
            if (plb && !plb->fNoIntegralHeight)
            {
                // forward through to the listbox to let him adjust
                // his size if necessary
                SendMessage(pcbox->hwndList, uMsg, wParam, 0L);
            }

            lReturn = TRUE;
        }
        else
        {
            lReturn = FALSE;
        }

        break;

    case CB_GETMINVISIBLE:

        return pcbox->iMinVisible;

    case WM_MEASUREITEM:
    case WM_DELETEITEM:
    case WM_DRAWITEM:
    case WM_COMPAREITEM:
        return ComboBox_MessageItemHandler(pcbox, uMsg, (LPVOID)lParam);

    case WM_NCCREATE:
        //
        // wParam - Contains a handle to the window being created
        // lParam - Points to the CREATESTRUCT data structure for the window.
        //

        //
        // Allocate the combobox instance stucture
        //
        pcbox = (PCBOX)UserLocalAlloc(HEAP_ZERO_MEMORY, sizeof(CBOX));
        if (pcbox)
        {
            //
            // Success... store the instance pointer.
            //
            TraceMsg(TF_STANDARD, "COMBOBOX: Setting combobox instance pointer.");
            ComboBox_SetPtr(hwnd, pcbox);

            return ComboBox_NcCreateHandler(pcbox, hwnd);
        }
        else
        {
            //
            // Failed... return FALSE.
            //
            // From a WM_NCCREATE msg, this will cause the
            // CreateWindow call to fail.
            //
            TraceMsg(TF_STANDARD, "COMBOBOX: Unable to allocate combobox instance structure.");
            lReturn = FALSE;
        }

        break;

    case WM_PARENTNOTIFY:
        if (LOWORD(wParam) == WM_DESTROY) 
        {
            if ((HWND)lParam == pcbox->hwndEdit) 
            {
                pcbox->CBoxStyle &= ~SEDITABLE;
                pcbox->fNoEdit = TRUE;
                pcbox->hwndEdit = hwnd;
            } 
            else if ((HWND)lParam == pcbox->hwndList) 
            {
                pcbox->CBoxStyle &= ~SDROPPABLE;
                pcbox->hwndList = NULL;
            }
        }
        break;

    case WM_UPDATEUISTATE:
        //
        // Propagate the change to the list control, if any
        //
        UserAssert(pcbox->hwndList);
        SendMessage(pcbox->hwndList, WM_UPDATEUISTATE, wParam, lParam);

        goto CallDWP;

    case WM_GETOBJECT:

        if(lParam == OBJID_QUERYCLASSNAMEIDX)
        {
            lReturn = MSAA_CLASSNAMEIDX_COMBOBOX;
        }
        else
        {
            lReturn = FALSE;
        }

        break;

    case WM_THEMECHANGED:

        if ( pcbox->hTheme )
        {
            CloseThemeData(pcbox->hTheme);
        }

        pcbox->hTheme = OpenThemeData(pcbox->hwnd, L"Combobox");

        ComboBox_Position(pcbox);
        InvalidateRect(pcbox->hwnd, NULL, TRUE);

        lReturn = TRUE;

        break;

    case WM_HELP:
    {
        LPHELPINFO lpHelpInfo;

        //
        // Check if this message is from a child of this combo
        //
        if ((lpHelpInfo = (LPHELPINFO)lParam) != NULL &&
            ((pcbox->hwndEdit && lpHelpInfo->iCtrlId == (SHORT)GetWindowID(pcbox->hwndEdit)) ||
             lpHelpInfo->iCtrlId == (SHORT)GetWindowID(pcbox->hwndList) )) 
        {
            //
            // 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)GetWindowID(hwnd);
            lpHelpInfo->hItemHandle = hwnd;
#if 0   // PORTPORT: GetContextHelpId
            lpHelpInfo->dwContextId = GetContextHelpId(hwnd);
#endif
        }
        //
        // Fall through to DefWindowProc
        //
    }

    default:

        if ( (GetSystemMetrics(SM_PENWINDOWS)) &&
                  (uMsg >= WM_PENWINFIRST && uMsg <= WM_PENWINLAST))
        {
            goto CallEditSendMessage;
        }
        else
        {

CallDWP:
            lReturn = DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
    }

    return lReturn;

//
// The following forward messages off to the child controls.
//
CallEditSendMessage:
    if (!pcbox->fNoEdit && pcbox->hwndEdit) 
    {
        lReturn = SendMessage(pcbox->hwndEdit, uMsg, wParam, lParam);
    }
    else 
    {
        TraceMsg(TF_STANDARD, "COMBOBOX: Invalid combobox message %#.4x", uMsg);
        lReturn = CB_ERR;
    }
    return lReturn;

CallListSendMessage:
    UserAssert(pcbox->hwndList);
    lReturn = SendMessage(pcbox->hwndList, uMsg, wParam, lParam);

    return lReturn;
}