/***************************************************************************\
*
*  DLGMGR.C -
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
*      Dialog Box Manager Routines
*
* ??-???-???? mikeke    Ported from Win 3.0 sources
* 12-Feb-1991 mikeke    Added Revalidation code
* 19-Feb-1991 JimA      Added access checks
\***************************************************************************/

#include "precomp.h"
#pragma hdrstop

#define UNICODE_MINUS_SIGN 0x2212


LOOKASIDE DialogLookaside;

BOOL ValidateCallback(HANDLE h);

#define IsInForegroundQueue(hwnd) \
    (NtUserQueryWindow(hwnd, WindowIsForegroundThread) != NULL)
#define IsCurrentThreadForeground() \
    ((BOOL)NtUserGetThreadState(UserThreadStateIsForeground))

/***************************************************************************\
*
* GetParentDialog()
*
* Gets top level window, not a control parent.  If not a dialog, then use
* "highest" control parent guy.
*
* BOGUS
* Need a way to mark a window as a dialog.  If it ever comes into
* DefDlgProc(), set an internal flag.  Will be used by thunking and
* CallDlgProc() optimizations also!
*
\***************************************************************************/

PWND GetParentDialog(PWND pwndDialog)
{
    PWND    pwndParent;

    pwndParent = pwndDialog;

    //
    // Walk up the parent chain.  We're looking for the top-most dialog
    // window.  Most cases, the window is a top level one.  But in case of
    // backup app, the window will be a child of some other window.
    //
    for (; pwndDialog; pwndDialog = REBASEPWND(pwndDialog, spwndParent))
    {
        if (TestWF(pwndDialog, WFDIALOGWINDOW))
        {
            //
            // For old guys:  If not DS_RECURSE, then stop here.
            // that way old apps which try to do the nested dialog
            // stuff in their old limited way don't die.
            //
            if (TestWF(pwndDialog, WEFCONTROLPARENT))
                pwndParent = pwndDialog;
            else if (!TestWF(pwndDialog, DFCONTROL))
                break;
        }

        if (!TestWF(pwndDialog, WFCHILD))
            break;
    }

    return(pwndParent);
}

/***************************************************************************\
* xxxSaveDlgFocus
*
* History:
* 02-18-92 JimA             Ported from Win31 sources
\***************************************************************************/

BOOL xxxSaveDlgFocus(
    PWND pwnd)
{
    HWND hwndFocus = GetFocus();

    CheckLock(pwnd);

    if (hwndFocus != NULL && IsChild(HWq(pwnd), hwndFocus) &&
            PDLG(pwnd)->hwndFocusSave == NULL) {
        PDLG(pwnd)->hwndFocusSave = hwndFocus;
        xxxRemoveDefaultButton(pwnd, ValidateHwnd(hwndFocus));
        return TRUE;
    }
    return FALSE;
}

/***************************************************************************\
* xxxRestoreDlgFocus
*
* History:
* 02-18-92 JimA             Ported from Win31 sources
* 01-01-2001 Mohamed        Need to re-validate the window before cleanup.
\***************************************************************************/

// LATER
// 21-Mar-1992 mikeke
// does pwndFocusSave need to be unlocked when the dialog is destroyed?

BOOL xxxRestoreDlgFocus(
    PWND pwnd)
{
    HWND hwndFocus;
    HWND hwndFocusSave;
    BOOL fRestored = FALSE;

    CheckLock(pwnd);
    

    if (PDLG(pwnd)->hwndFocusSave && !TestWF(pwnd, WFMINIMIZED)) {

        hwndFocus = GetFocus();
        hwndFocusSave = KHWND_TO_HWND(PDLG(pwnd)->hwndFocusSave);

        if (IsWindow(hwndFocusSave)) {
            xxxCheckDefPushButton(pwnd, hwndFocus, hwndFocusSave);
            fRestored = (NtUserSetFocus(hwndFocusSave) != NULL);
        }
            //
            // After calling SetFocus(), we need to re-validate
            // the window. PDLG(pwnd) might be NULL.
            //

        if (ValidateDialogPwnd(pwnd)) {
            PDLG(pwnd)->hwndFocusSave = NULL;
        }
    }

    return fRestored;
}


/***************************************************************************\
* DlgSetFocus
*
* History:
\***************************************************************************/

void DlgSetFocus(
    HWND hwnd)
{
    if (((UINT)SendMessage(hwnd, WM_GETDLGCODE, 0, 0)) & DLGC_HASSETSEL) {
        SendMessage(hwnd, EM_SETSEL, 0, MAXLONG);
    }

    NtUserSetFocus(hwnd);
}



FUNCLOG1(LOG_GENERAL, int, DUMMYCALLINGTYPE, GetDlgCtrlID, HWND, hwnd)
int GetDlgCtrlID(
    HWND hwnd)
{
    PWND pwnd;

    pwnd = ValidateHwnd(hwnd);
    if (pwnd == NULL)
        return 0;

    return PtrToLong(pwnd->spmenu);
}



/***************************************************************************\
* ValidateDialogPwnd
*
* Under Win3, DLGWINDOWEXTRA is 30 bytes. We cannot change that for 16 bit
* compatibility reasons. Problem is there is no way to tell if a given
* 16 bit window depends on byte count. If there was, this would be easy.
* The only way to tell is when a window is about to be used as a dialog
* window. This window may be of the class DIALOGCLASS, but again it may
* not!! So we keep dialog window words at 30 bytes, and allocate another
* structure for the real dialog structure fields. Problem is that this
* structure has to be created lazily! And that's what we're doing here.
*
* 05-21-91 ScottLu      Created.
\***************************************************************************/

BOOL ValidateDialogPwnd(
    PWND pwnd)
{
    static BOOL sfInit = TRUE;
    PDLG pdlg;

    /*
     * This bit is set if we've already run through this initialization and
     * have identified this window as a dialog window (able to withstand
     * peeks into window words at random moments in time).
     */
    if (TestWF(pwnd, WFDIALOGWINDOW))
        return TRUE;

    if (pwnd->cbwndExtra < DLGWINDOWEXTRA) {
        RIPERR0(ERROR_WINDOW_NOT_DIALOG, RIP_VERBOSE, "");
        return FALSE;
    }

    /*
     * See if the pdlg was destroyed and this is a rogue message to be ignored
     */
    if (pwnd->fnid & FNID_STATUS_BITS) {
        return FALSE;
    }

    /*
     * If the lookaside buffer has not been initialized, do it now.
     */
    if (sfInit) {
        if (!NT_SUCCESS(InitLookaside(&DialogLookaside, sizeof(DLG), 2))) {
            return FALSE;
        }
        sfInit = FALSE;
    }

    if ((pdlg = (PDLG)AllocLookasideEntry(&DialogLookaside)) == NULL) {
        return FALSE;
    }

    NtUserCallHwndParam(HWq(pwnd), (ULONG_PTR)pdlg, SFI_SETDIALOGPOINTER);

    return TRUE;
}


/***************************************************************************\
* CvtDec
*
* LATER!!! convert to itoa?
*
* History:
\***************************************************************************/

void CvtDec(
    int u,
    LPWSTR *lplpch)
{
    if (u >= 10) {
        CvtDec(u / 10, lplpch);
        u %= 10;
    }

    *(*lplpch)++ = (WCHAR)(u + '0');
}


/***************************************************************************\
* SetDlgItemInt
*
* History:
\***************************************************************************/


FUNCLOG4(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, SetDlgItemInt, HWND, hwnd, int, item, UINT, u, BOOL, fSigned)
BOOL SetDlgItemInt(
    HWND hwnd,
    int item,
    UINT u,
    BOOL fSigned)
{
    LPWSTR lpch;
    WCHAR rgch[16];

    lpch = rgch;
    if (fSigned) {
        if ((int)u < 0) {
            *lpch++ = TEXT('-');
            u = (UINT)(-(int)u);
        }
    } else {
        if (u & 0x80000000) {
            CvtDec(u / 10, (LPWSTR FAR *)&lpch);
            u = u % 10;
        }
    }

    CvtDec(u, (LPWSTR FAR *)&lpch);
    *lpch = 0;

    return SetDlgItemTextW(hwnd, item, rgch);
}


/***************************************************************************\
* CheckDlgButton
*
* History:
\***************************************************************************/


FUNCLOG3(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, CheckDlgButton, HWND, hwnd, int, id, UINT, cmdCheck)
BOOL CheckDlgButton(
    HWND hwnd,
    int id,
    UINT cmdCheck)
{
    if ((hwnd = GetDlgItem(hwnd, id)) == NULL) {
        return FALSE;
    }

    SendMessage(hwnd, BM_SETCHECK, cmdCheck, 0);

    return TRUE;
}

/***************************************************************************\
* GetDlgItemInt
*
* History:
\***************************************************************************/

UINT GetDlgItemInt(
    HWND hwnd,
    int item,
    BOOL FAR *lpfValOK,
    BOOL fSigned)
{
    int i, digit, ch;
    int maxTens, maxUnits;
    BOOL fOk, fNeg;
    LPWSTR lpch;
    WCHAR rgch[48];
    WCHAR rgchDigits[48];

    fOk = FALSE;
    if (lpfValOK != NULL)
        *lpfValOK = FALSE;

    if (!GetDlgItemTextW(hwnd, item, rgch, sizeof(rgch)/sizeof(WCHAR) - 1))
        return 0;

    lpch = rgch;

    /*
     * Skip leading white space.
     */
    while (*lpch == TEXT(' '))
        lpch++;

    fNeg = FALSE;
    while (fSigned && ((*lpch == L'-') || (*lpch == UNICODE_MINUS_SIGN))) {
        lpch++;
        fNeg ^= TRUE;
    }

    if (fSigned) {
        maxTens = INT_MAX/10;
        maxUnits = INT_MAX%10;
    } else {
        maxTens = UINT_MAX/10;
        maxUnits = UINT_MAX%10;
    }
    /*
     * Convert all decimal digits to ASCII Unicode digits 0x0030 - 0x0039
     */
    FoldStringW(MAP_FOLDDIGITS, lpch, -1, rgchDigits,
            sizeof(rgchDigits)/sizeof(rgchDigits[0]));
    lpch = rgchDigits;

    i = 0;
    while (ch = *lpch++) {
        digit = ch - TEXT('0');
        if (digit < 0 || digit > 9) {
            break;
        }
        if ((UINT)i >= (UINT)maxTens) {
            /*
             * We need to special case INT_MIN as the i = -i
             * would damage it
             */
            if (i == maxTens) {
                if (digit == maxUnits+1 && fNeg && (*lpch) == 0) {
                    i = INT_MIN;
                    goto HaveResult;
                } else if (digit > maxUnits) {
                    return 0;
                }
            } else {
                return 0;
            }
        }
        fOk = TRUE;
        i = ((UINT)i * 10) + digit;
    }

    if (fNeg)
        i = -i;
HaveResult:
    if (lpfValOK != NULL)
        *lpfValOK = ((ch == 0) && fOk);

    return (UINT)i;
}

/***************************************************************************\
* CheckRadioButton
*
* History:
\***************************************************************************/


FUNCLOG4(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, CheckRadioButton, HWND, hwnd, int, idFirst, int, idLast, int, id)
BOOL CheckRadioButton(
    HWND hwnd,
    int idFirst,
    int idLast,
    int id)
{
    PWND pwnd, pwndDialog;
    BOOL    fCheckOn;

    pwndDialog = ValidateHwnd(hwnd);
    if (pwndDialog == NULL)
        return 0;

    for (pwnd = REBASE(pwndDialog, spwndChild); pwnd; pwnd = REBASE(pwnd, spwndNext)) {

        if ((PtrToLong(pwnd->spmenu) >= idFirst) &&
            (PtrToLong(pwnd->spmenu) <= idLast)) {

            fCheckOn = (PtrToLong(pwnd->spmenu) == id);
            SendMessage(PtoHq(pwnd), BM_SETCHECK, fCheckOn, 0L);
        }
    }

    return TRUE;
}


/***************************************************************************\
* IsDlgButtonChecked
*
* History:
\***************************************************************************/


FUNCLOG2(LOG_GENERAL, UINT, DUMMYCALLINGTYPE, IsDlgButtonChecked, HWND, hwnd, int, id)
UINT IsDlgButtonChecked(
    HWND hwnd,
    int id)
{
    if ((hwnd = GetDlgItem(hwnd, id)) != NULL) {
        return (UINT)SendMessage(hwnd, BM_GETCHECK, 0, 0);
    }

    return FALSE;
}


/***************************************************************************\
* DefDlgProc
*
* History:
\***************************************************************************/

LRESULT DefDlgProcWorker(
    PWND pwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam,
    DWORD fAnsi)
{
    HWND hwnd = HWq(pwnd);
    TL tlpwndT1, tlpwndT2, tlpwndT3, tlpwndTop;
    PWND pwndT;
    PWND pwndT1, pwndT2, pwndT3, pwndTop;
    HWND hwndT1;
    LRESULT result;
    BOOL fSetBit;
    DLGPROC pfn;

    CheckLock(pwnd);

    /*
     * use the Win 3.1 documented size
     */
    VALIDATECLASSANDSIZE(pwnd, FNID_DIALOG);

    /*
     * Must do special validation here to make sure pwnd is a dialog window.
     */
    if (!ValidateDialogPwnd(pwnd))
        return 0;

    if (((PDIALOG)pwnd)->resultWP != 0)
        NtUserSetWindowLongPtr(hwnd, DWLP_MSGRESULT, 0, FALSE);
    result = 0;   // no dialog proc

    if (message == WM_FINALDESTROY) {
        goto DoCleanup;
    }

    if ((pfn = PDLG(pwnd)->lpfnDlg) != NULL) {
        /* Bug 234292 - joejo
         * Since the called window/dialog proc may have a different calling
         * convention, we must wrap the call and, check esp and replace with
         * a good esp when the call returns. This is what UserCallWinProc* does.
        */
        if (UserCallDlgProcCheckWow(pwnd->pActCtx, pfn, hwnd, message, wParam, lParam, &(pwnd->state), &result)) {
            return result;
        }

        /*
         * Get out if the window was destroyed in the dialog proc.
         */
        if ((RevalidateHwnd(hwnd)==NULL) || (pwnd->fnid & FNID_STATUS_BITS))
            return result;
    }

    /*
     * SPECIAL CASED ... and DOCUMENTED that way !!!
     * These 6, and ONLY these 6, should be hacked in this fashion.
     * Anybody who needs the REAL return value to a message should
     * use SetDlgMsgResult in WINDOWSX.H
     */

    switch (message)
    {
        case WM_COMPAREITEM:
        case WM_VKEYTOITEM:
        case WM_CHARTOITEM:
        case WM_INITDIALOG:
        case WM_QUERYDRAGICON:
            return ((LRESULT)(DWORD)result);

        case WM_CTLCOLOR:
        case WM_CTLCOLORMSGBOX:
        case WM_CTLCOLOREDIT:
        case WM_CTLCOLORLISTBOX:
        case WM_CTLCOLORBTN:
        case WM_CTLCOLORDLG:
        case WM_CTLCOLORSCROLLBAR:
        case WM_CTLCOLORSTATIC:
            // QuarkXPress doesn't like finding the WM_CTLCOLOR result in
            // resultWP -- we should never be setting resultWP -- that's meant
            // as a pass-thru return value -- so let's go back to doing it the
            // old way -- Win95B B#21269 -- 03/13/95 -- tracysh (cr: jeffbog)
            if (result)
                return result;
            break;
    }

    if (!result) {

        /*
         * Save the result value in case our private memory is freed
         * before we return
         */
//        result = PDLG(pwnd)->resultWP;

        switch (message) {
        case WM_CTLCOLOR:
        case WM_CTLCOLORMSGBOX:
        case WM_CTLCOLOREDIT:
        case WM_CTLCOLORLISTBOX:
        case WM_CTLCOLORBTN:
        case WM_CTLCOLORDLG:
        case WM_CTLCOLORSCROLLBAR:
        case WM_CTLCOLORSTATIC:
        {
            //
            // HACK OF DEATH:
            // To get 3D colors for non 4.0 apps who use 3DLOOK,
            // we temporarily add on the 4.0 compat bit, pass this
            // down to DWP, and clear it.
            //
            // Use "result" var for bool saying we have to add/clear 4.0
            // compat bit.

            fSetBit = (TestWF(pwnd, DF3DLOOK)!= 0) &&
                     (TestWF(pwnd, WFWIN40COMPAT) == 0);

            if (fSetBit)
                SetWindowState(pwnd, WFWIN40COMPAT);

            result = DefWindowProcWorker(pwnd, message,
                    wParam, lParam, fAnsi);

            if (fSetBit)
                ClearWindowState(pwnd, WFWIN40COMPAT);
            return result;
        }

        case WM_ERASEBKGND:
            FillWindow(hwnd, hwnd, (HDC)wParam, (HBRUSH)CTLCOLOR_DLG);
            return TRUE;

        case WM_SHOWWINDOW:

            /*
             * If hiding the window, save the focus.  If showing the window
             * by means of a SW_* command and the fEnd bit is set, do not
             * pass to DWP so it won't get shown.
             */
            if (GetParentDialog(pwnd) == pwnd) {
                if (!wParam) {
                    xxxSaveDlgFocus(pwnd);
                } else {

                    if (LOWORD(lParam) != 0 && PDLG(pwnd)->fEnd)
                        break;

                    /*
                     * Snap the cursor to the center of the default button.
                     * Only do this if the current thread is in the foreground.
                     * The _ShowCursor() code is added to work around a
                     * problem with hardware cursors.  If change is done
                     * in the same refresh cycle, the display of the cursor
                     * would not reflect the new position.
                     */
                    if (TEST_PUSIF(PUSIF_SNAPTO) &&
                            IsInForegroundQueue(hwnd)) {
                        hwndT1 = GetDlgItem(hwnd, (int)PDLG(pwnd)->result);
                        if (hwndT1) {
                            RECT rc;

                            NtUserShowCursor(FALSE);

                            GetWindowRect(hwndT1, &rc);
                            NtUserSetCursorPos(rc.left + ((rc.right - rc.left)/2),
                                         rc.top + ((rc.bottom - rc.top)/2));

                            NtUserShowCursor(TRUE);
                        }
                    }
                }
            }
            goto CallDWP;

        case WM_SYSCOMMAND:
            if (GetParentDialog(pwnd) == pwnd) {
                /*
                 * If hiding the window, save the focus.  If showing the window
                 * by means of a SW_* command and the fEnd bit is set, do not
                 * pass to DWP so it won't get shown.
                 */
                if ((int)wParam == SC_MINIMIZE)
                    xxxSaveDlgFocus(pwnd);
            }
            goto CallDWP;

        case WM_ACTIVATE:
            pwndT1 = GetParentDialog(pwnd);
            if ( pwndT1 != pwnd) {

                /*
                 * This random bit is used during key processing - bit
                 * 08000000 of WM_CHAR messages is set if a dialog is currently
                 * active.
                 */
                NtUserSetThreadState(wParam ? QF_DIALOGACTIVE : 0, QF_DIALOGACTIVE);
            }

            ThreadLock(pwndT1, &tlpwndT1);
            if (wParam != 0)
                xxxRestoreDlgFocus(pwndT1);
            else
                xxxSaveDlgFocus(pwndT1);

            ThreadUnlock(&tlpwndT1);
            break;

        case WM_SETFOCUS:
            pwndT1 = GetParentDialog(pwnd);
            if (!PDLG(pwndT1)->fEnd && !xxxRestoreDlgFocus(pwndT1)) {

                pwndT = _GetNextDlgTabItem(pwndT1, NULL, FALSE);
                DlgSetFocus(HW(pwndT));
            }
            break;

        case WM_CLOSE:
            /*
             * Make sure cancel button is not disabled before sending the
             * IDCANCEL.  Note that we need to do this as a message instead
             * of directly calling the dlg proc so that any dialog box
             * filters get this.
             */
            pwndT1 = _GetDlgItem(pwnd, IDCANCEL);
            if (pwndT1 && TestWF(pwndT1, WFDISABLED))
                NtUserMessageBeep(0);
            else
                PostMessage(hwnd, WM_COMMAND, MAKELONG(IDCANCEL, BN_CLICKED),
                        (LPARAM)HW(pwndT1));
            break;

        case WM_NCDESTROY:
        case WM_FINALDESTROY:
DoCleanup:
            NtUserSetThreadState(0, QF_DIALOGACTIVE);
            if (!(pwnd->style & DS_LOCALEDIT)) {
                if (PDLG(pwnd)->hData) {
                    ReleaseEditDS(KHANDLE_TO_HANDLE(PDLG(pwnd)->hData));
                    PDLG(pwnd)->hData = NULL;
                }
            }

            /*
             * Delete the user defined font if any
             */
            if (PDLG(pwnd)->hUserFont) {
                DeleteObject(KHFONT_TO_HFONT(PDLG(pwnd)->hUserFont));
                PDLG(pwnd)->hUserFont = NULL;
            }

            /*
             * Free the dialog memory and mark this as a non-dialog window
             */
            FreeLookasideEntry(&DialogLookaside, KPVOID_TO_PVOID(PDLG(pwnd)));
            NtUserCallHwndParam(hwnd, 0, SFI_SETDIALOGPOINTER);
            break;

        case DM_REPOSITION:
            {
                RECT        rc;
                PMONITOR    pMonitor;

                // DAT recorder APP sends it's own private message 0x402
                // through and we mistake it to be DM_REPOSITION. To avoid
                // this confusion, we do the following check.
                // Fix for Bug#25747 -- 9/29/94 --
                if (!TestWF(pwnd, WEFCONTROLPARENT) ||
                    (GETFNID(pwnd) != FNID_DESKTOP &&
                     GETFNID(REBASEPWND(pwnd, spwndParent)) != FNID_DESKTOP)) {

                    goto CallDWP;
                }

                CopyRect(&rc, KPRECT_TO_PRECT(&pwnd->rcWindow));
                pMonitor = _MonitorFromRect(&rc, MONITOR_DEFAULTTOPRIMARY);
                RepositionRect(pMonitor, &rc, pwnd->style, pwnd->ExStyle);
                NtUserSetWindowPos(hwnd, HWND_TOP, rc.left, rc.top,
                             rc.right-rc.left, rc.bottom-rc.top,
                             SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
            }
            break;

        case DM_SETDEFID:
            pwndT1 = GetParentDialog(pwnd);
            ThreadLock(pwndT1, &tlpwndT1);

            if (!(PDLG(pwndT1)->fEnd)) {

                pwndT2 = NULL;
                if (PDLG(pwndT1)->result != 0)
                    pwndT2 = _FindDlgItem(pwndT1, (int)PDLG(pwndT1)->result);

                pwndT3 = NULL;
                if (wParam != 0) {
                    pwndT3 = _GetDlgItem(pwnd, (UINT)wParam);
                }

                ThreadLock(pwndT2, &tlpwndT2);
                ThreadLock(pwndT3, &tlpwndT3);

                xxxCheckDefPushButton(pwndT1, HW(pwndT2), HW(pwndT3));

                ThreadUnlock(&tlpwndT3);
                ThreadUnlock(&tlpwndT2);

                PDLG(pwndT1)->result = (UINT)wParam;
//                if (PDLG(pwnd)->spwndFocusSave) {
//                    Lock(&(PDLG(pwnd)->spwndFocusSave), pwndT2);
//                }

                NotifyWinEvent(EVENT_OBJECT_DEFACTIONCHANGE, HW(pwndT1), OBJID_CLIENT, INDEXID_CONTAINER);
            }
            ThreadUnlock(&tlpwndT1);
            return TRUE;

        case DM_GETDEFID:
            pwndT1 = GetParentDialog(pwnd);

            if (!PDLG(pwndT1)->fEnd && PDLG(pwndT1)->result)
                return(MAKELONG(PDLG(pwndT1)->result, DC_HASDEFID));
            else
                return 0;
            break;

        /*
         * This message was added so that user defined controls that want
         * tab keys can pass the tab off to the next/previous control in the
         * dialog box.  Without this, all they could do was set the focus
         * which didn't do the default button stuff.
         */
        case WM_NEXTDLGCTL:
            pwndTop = GetParentDialog(pwnd);
            ThreadLock(pwndTop, &tlpwndTop);

            hwndT1 = GetFocus();
            pwndT2 = ValidateHwndNoRip(hwndT1);
            if (LOWORD(lParam)) {
                if (pwndT2 == NULL)
                    pwndT2 = pwndTop;

                /*
                 * wParam contains the pwnd of the ctl to set focus to.
                 */
                if ((pwndT1 = ValidateHwnd((HWND)wParam)) == NULL) {
                    ThreadUnlock(&tlpwndTop);
                    return TRUE;
                }
            } else {
                if (pwndT2 == NULL) {

                    /*
                     * Set focus to the first tab item.
                     */
                    pwndT1 = _GetNextDlgTabItem(pwndTop, NULL, FALSE);
                    pwndT2 = pwndTop;
                } else {

                    /*
                     * If window with focus not a dlg ctl, ignore message.
                     */
                    if (!_IsChild(pwndTop, pwndT2)) {
                        ThreadUnlock(&tlpwndTop);
                        return TRUE;
                    }
                    /*
                     * wParam = TRUE for previous, FALSE for next
                     */
                    pwndT1 = _GetNextDlgTabItem(pwndTop, pwndT2, (wParam != 0));

                    /*
                     * If there is no next item, ignore the message.
                     */
                    if (pwndT1 == NULL) {
                        ThreadUnlock(&tlpwndTop);
                        return TRUE;
                    }
                }
            }

            ThreadLock(pwndT1, &tlpwndT1);
            ThreadLock(pwndT2, &tlpwndT2);

            DlgSetFocus(HW(pwndT1));
            xxxCheckDefPushButton(pwndTop, HW(pwndT2), HW(pwndT1));

            ThreadUnlock(&tlpwndT2);
            ThreadUnlock(&tlpwndT1);
            ThreadUnlock(&tlpwndTop);

            return TRUE;

        case WM_ENTERMENULOOP:

            /*
             * We need to pop up the combo box window if the user brings
             * down a menu.
             *
             * ...  FALL THROUGH...
             */

        case WM_LBUTTONDOWN:
        case WM_NCLBUTTONDOWN:
            hwndT1 = GetFocus();
            if (hwndT1 != NULL) {
                pwndT1 = ValidateHwndNoRip(hwndT1);

                if (GETFNID(pwndT1) == FNID_COMBOBOX) {

                    /*
                     * If user clicks anywhere in dialog box and a combo box (or
                     * the editcontrol of a combo box) has the focus, then hide
                     * it's listbox.
                     */
                    ThreadLockAlways(pwndT1, &tlpwndT1);
                    SendMessage(HWq(pwndT1), CB_SHOWDROPDOWN, FALSE, 0);
                    ThreadUnlock(&tlpwndT1);

                } else {
                    PWND pwndParent;

                    /*
                     * It's a subclassed combo box.  See if the listbox and edit
                     * boxes exist (this is a very cheezy evaluation - what if
                     * these controls are subclassed too? NOTE: Not checking
                     * for EditWndProc: it's a client proc address.
                     */
                    pwndParent = REBASEPWND(pwndT1, spwndParent);
                    if (GETFNID(pwndParent) == FNID_COMBOBOX) {
                        pwndT1 = pwndParent;
                        ThreadLock(pwndT1, &tlpwndT1);
                        SendMessage(HWq(pwndT1), CB_SHOWDROPDOWN, FALSE, 0);
                        ThreadUnlock(&tlpwndT1);
                    }
                }
            }

            /*
             * Always send the message off to DefWndProc
             */
            goto CallDWP;

        case WM_GETFONT:
            return (LRESULT)PDLG(pwnd)->hUserFont;

        case WM_VKEYTOITEM:
        case WM_COMPAREITEM:
        case WM_CHARTOITEM:
        case WM_INITDIALOG:

            /*
             * We need to return the 0 the app may have returned for these
             * items instead of calling defwindow proc.
             */
            return result;

        case WM_NOTIFYFORMAT:
            if (lParam == NF_QUERY)
                return((PDLG(pwnd)->flags & DLGF_ANSI ) ? NFR_ANSI : NFR_UNICODE);
            return result;

        case WM_INPUTLANGCHANGEREQUEST:
            if (IS_IME_ENABLED()) {
                /*
                 * #115190
                 * For dialogbox itself, buttons/static controls on top of
                 * dialogbox, we'll simply discard this message. B#3843-win95c
                 */
                break;
            }
            if (PDLG(pwnd)->lpfnDlg == MB_DlgProc) {
                break;
            }
            goto CallDWP;

        default:
CallDWP:
            return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
        }
    } else if ((message == WM_SHOWWINDOW) && result) {

        /*
         * For a visible-case we want to snap the cursor regardless of
         * what was returned from the dialog-handler on the client.  If
         * we're going visible, snap the cursor to the dialog-button.
         */
        if (GetParentDialog(pwnd) == pwnd) {

            if (wParam && ((LOWORD(lParam) == 0) || !PDLG(pwnd)->fEnd)) {

                /*
                 * Snap the cursor to the center of the default button.
                 * Only do this if the current thread is in the foreground.
                 * The _ShowCursor() code is added to work around a
                 * problem with hardware cursors.  If change is done
                 * in the same refresh cycle, the display of the cursor
                 * would not reflect the new position.
                 */
                if (TEST_PUSIF(PUSIF_SNAPTO) &&
                        IsInForegroundQueue(hwnd)) {
                    hwndT1 = GetDlgItem(hwnd, (int)PDLG(pwnd)->result);
                    if (hwndT1) {
                        RECT rc;

                        NtUserShowCursor(FALSE);

                        GetWindowRect(hwndT1, &rc);
                        NtUserSetCursorPos(rc.left + ((rc.right - rc.left)/2),
                                     rc.top + ((rc.bottom - rc.top)/2));

                        NtUserShowCursor(TRUE);
                    }
                }
            }
        }
    }


    /*
     * If this is still marked as a dialog window then return the real
     * result. Otherwise, we've already processed the WM_NCDESTROY message
     * and freed our private memory so return the stored value.
     */
    if (TestWF(pwnd, WFDIALOGWINDOW))
        return KERNEL_LRESULT_TO_LRESULT(((PDIALOG)pwnd)->resultWP);
    else
        return result;
}


/***************************************************************************\
* DefDlgProc
*
* Translates the message, calls DefDlgProc on server side.  DefDlgProc
* is the default WindowProc for dialogs (NOT the dialog's dialog proc)
*
* 04-11-91 ScottLu Created.
\***************************************************************************/


FUNCLOG4(LOG_GENERAL, LRESULT, WINAPI, DefDlgProcW, HWND, hwnd, UINT, message, WPARAM, wParam, LPARAM, lParam)
LRESULT WINAPI DefDlgProcW(
    HWND hwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
    PWND pwnd;

    if ((pwnd = ValidateHwnd(hwnd)) == NULL) {
        return (0L);
    }

    return DefDlgProcWorker(pwnd, message, wParam, lParam, FALSE);
}


FUNCLOG4(LOG_GENERAL, LRESULT, WINAPI, DefDlgProcA, HWND, hwnd, UINT, message, WPARAM, wParam, LPARAM, lParam)
LRESULT WINAPI DefDlgProcA(
    HWND hwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
    PWND pwnd;

    if ((pwnd = ValidateHwnd(hwnd)) == NULL) {
        return (0L);
    }

    return DefDlgProcWorker(pwnd, message, wParam, lParam, TRUE);
}


/***************************************************************************\
* DialogBox2
*
* History:
\***************************************************************************/

INT_PTR DialogBox2(
    HWND hwnd,
    HWND hwndOwner,
    BOOL fDisabled,
    BOOL fOwnerIsActiveWindow)
{
    MSG msg;
    INT_PTR result;
    BOOL fShown;
    BOOL fWantIdleMsgs;
    BOOL fSentIdleMessage = FALSE;
    HWND hwndCapture;
    PWND pwnd;

    if (hwnd) {
        pwnd = ValidateHwnd(hwnd);
    } else {
        pwnd = NULL;
    }

    CheckLock(pwnd);

    if (pwnd == NULL) {
        if ((hwndOwner != NULL) && !fDisabled && IsWindow(hwndOwner)) {
            NtUserEnableWindow(hwndOwner, TRUE);
            if (fOwnerIsActiveWindow) {

                /*
                 * The dialog box failed but we disabled the owner in
                 * xxxDialogBoxIndirectParam and if it had the focus, the
                 * focus was set to NULL.  Now, when we enable the window, it
                 * doesn't get the focus back if it had it previously so we
                 * need to correct this.
                 */
                NtUserSetFocus(hwndOwner);
            }
        }
        return -1;
    }

    hwndCapture = GetCapture();
    if (hwndCapture != NULL) {
        SendMessage(hwndCapture, WM_CANCELMODE, 0, 0);
    }

    /*
     * Set the 'parent disabled' flag for EndDialog().
     * convert BOOL to definite bit 0 or 1
     */
    PDLG(pwnd)->fDisabled = !!fDisabled;

    fShown = TestWF(pwnd, WFVISIBLE);

    /*
     * Should the WM_ENTERIDLE messages be sent?
     */
    fWantIdleMsgs = !(pwnd->style & DS_NOIDLEMSG);

    if ((SYSMET(SLOWMACHINE) & 1) && !fShown && !PDLG(pwnd)->fEnd)
        goto ShowIt;

    while (PDLG(pwnd) && (!PDLG(pwnd)->fEnd)) {
        if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
ShowIt:
            if (!fShown) {
                fShown = TRUE;

#ifdef SYSMODALWINDOWS
                if (pwnd == gspwndSysModal) {
                    /*
                     * Make this a topmost window
                     */
                    NtUserSetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
                               SWP_NOSIZE | SWP_NOMOVE |
                               SWP_NOREDRAW | SWP_NOACTIVATE);
                }
#endif

                NtUserShowWindow(hwnd, SHOW_OPENWINDOW);
                UpdateWindow(hwnd);

                NotifyWinEvent(EVENT_SYSTEM_DIALOGSTART, hwnd, OBJID_WINDOW, INDEXID_CONTAINER);
            } else {
                /*
                 * Make sure window still exists
                 */
                if (hwndOwner && !IsWindow(hwndOwner))
                    hwndOwner = NULL;

                if (hwndOwner && fWantIdleMsgs && !fSentIdleMessage) {
                    fSentIdleMessage = TRUE;

                    SendMessage(hwndOwner, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)hwnd);
                } else {
                    if ((RevalidateHwnd(hwnd)==NULL) || (pwnd->fnid & FNID_STATUS_BITS))
                        break;

                    NtUserWaitMessage();
                }
            }

        } else {
            /*
             * We got a real message.  Reset fSentIdleMessage so that we send
             * one next time things are calm.
             */
            fSentIdleMessage = FALSE;

            if (msg.message == WM_QUIT) {
                PostQuitMessage((int)msg.wParam);
                break;
            }

            /*
             * If pwnd is a message box, allow Ctrl-C and Ctrl-Ins
             * to copy its content to the clipboard.
             * Fall through in case hooking apps look for these keys.
             */
            if (TestWF(pwnd, WFMSGBOX)) {
                if ( (msg.message == WM_CHAR && LOBYTE(msg.wParam) == 3) ||
                     (msg.message == WM_KEYDOWN && LOBYTE(msg.wParam) == VK_INSERT && GetKeyState(VK_CONTROL) < 0)) {
                        /*
                         * Send the WM_COPY message and let the original message fall through
                         * as some apps might want it
                         */
                        SendMessage(hwnd, WM_COPY, 0, 0);
                }
            }

            /*
             * Moved the msg filter hook call to IsDialogMessage to allow
             * messages to be hooked for both modal and modeless dialog
             * boxes.
             */
            if (!IsDialogMessage(hwnd, &msg)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }

            /*
             * If we get a timer message, go ahead and show the window.
             * We may continuously get timer msgs if there are zillions of
             * apps running.
             *
             * If we get a syskeydown message, show the dialog box because
             * the user may be bringing down a menu and we want the dialog
             * box to become visible.
             */
            if (!fShown && (msg.message == WM_TIMER ||
                    msg.message == WM_SYSTIMER || msg.message == WM_SYSKEYDOWN))
                goto ShowIt;
        }

        if (!RevalidateHwnd(hwnd)) {
            /*
             * Bogus case - we've already been destroyed somehow (by app,
             * GP, etc.)
             */
            RIPMSG0(RIP_WARNING,
               "Dialog should be dismissed with EndDialog, not DestroyWindow");
            break;
        }
    }

    NotifyWinEvent(EVENT_SYSTEM_DIALOGEND, hwnd, OBJID_WINDOW, INDEXID_CONTAINER);

    /*
     * Make sure the window still exists
     */
    if (!RevalidateHwnd(hwnd)) {
        return 0;
    }

    if (PDLG(pwnd))
        result = KERNEL_INT_PTR_TO_INT_PTR(PDLG(pwnd)->result);
    else
        result = 0;

    NtUserDestroyWindow(hwnd);

    /*
     * If the owner window belongs to another thread, the reactivation
     * of the owner may have failed within DestroyWindow().  Therefore,
     * if the current thread is in the foreground and the owner is not
     * in the foreground we can safely set the foreground back
     * to the owner.
     */
    if (hwndOwner != NULL) {
        if (IsCurrentThreadForeground() &&
            !IsInForegroundQueue(hwndOwner)) {
            NtUserSetForegroundWindow(hwndOwner);
        }
    }

    return result;
}


/***************************************************************************\
* InternalDialogBox
*
* Server portion of DialogBoxIndirectParam.
*
* 04-05-91 ScottLu      Created.
\***************************************************************************/

INT_PTR InternalDialogBox(
    HANDLE hModule,
    LPDLGTEMPLATE lpdt,
    HWND hwndOwner,
    DLGPROC pfnDialog,
    LPARAM lParam,
    UINT fSCDLGFlags)
{
    INT_PTR i;
    BOOL fDisabled = FALSE;
    HWND hwnd;
    PWND pwndOwner;
    BOOL fOwnerIsActiveWindow = FALSE;
    TL tlpwndOwner;
    BOOL fUnlockOwner;

    UserAssert(!(fSCDLGFlags & ~(SCDLG_CLIENT|SCDLG_ANSI|SCDLG_16BIT)));    // These are the only valid flags

    /*
     * If hwndOwner == HWNDESKTOP, change it to NULL.  This way the desktop
     * (and all its children) won't be disabled if the dialog is modal.
     */
    if (hwndOwner && SAMEWOWHANDLE(hwndOwner, GetDesktopWindow()))
        hwndOwner = NULL;

    /*
     * We return 0 if the ValidateHwnd fails in order to match Win 3.1
     * validation layer which always returns 0 for invalid hwnds even
     * if the function is spec'ed to return -1.  Autocad setup bug #3615
     */
    if (hwndOwner) {
        if ((pwndOwner = ValidateHwnd(hwndOwner)) == NULL) {
            return (0L);
        }
    } else {
        pwndOwner = NULL;
    }

    CheckLock(pwndOwner);

    fUnlockOwner = FALSE;
    if (pwndOwner != NULL) {

        /* The following fixes an AV in Corel Photo-Paint 6.0.  It passes a
         * 16-bit HWND in, and croaks at some point when it gets 16-bit hwnds
         * back in send messages. FritzS -- fixing bug 12531
         */
        hwndOwner = PtoHq(pwndOwner);

        /*
         * Make sure the owner is a top level window.
         */
        if (TestwndChild(pwndOwner)) {
            pwndOwner = GetTopLevelWindow(pwndOwner);
            hwndOwner = HWq(pwndOwner);
            ThreadLock(pwndOwner, &tlpwndOwner);
            fUnlockOwner = TRUE;
        }

        /*
         * Remember if window was originally disabled (so we can set
         * the correct state when the dialog goes away.
         */
        fDisabled = TestWF(pwndOwner, WFDISABLED);
        fOwnerIsActiveWindow = (SAMEWOWHANDLE(hwndOwner, GetActiveWindow()));

        /*
         * Disable the window.
         */
        NtUserEnableWindow(hwndOwner, FALSE);
    }

    /*
     * Don't show cursors on a mouseless system. Put up an hour glass while
     * the dialog comes up.
     */
    if (SYSMET(MOUSEPRESENT)) {
        NtUserSetCursor(LoadCursor(NULL, IDC_WAIT));
    }

    /*
     * Creates the dialog.  Frees the menu if this routine fails.
     */
    hwnd = InternalCreateDialog(hModule, lpdt, 0, hwndOwner,
            pfnDialog, lParam, fSCDLGFlags);

    if (hwnd == NULL) {

        /*
         * The dialog creation failed.  Re-enable the window, destroy the
         * menu, ie., fail gracefully.
         */
        if (!fDisabled && hwndOwner != NULL)
            NtUserEnableWindow(hwndOwner, TRUE);

        if (fUnlockOwner)
            ThreadUnlock(&tlpwndOwner);
        return -1;
    }

    i = DialogBox2(hwnd, hwndOwner, fDisabled, fOwnerIsActiveWindow);

    if (fUnlockOwner)
        ThreadUnlock(&tlpwndOwner);
    return i;
}

/***************************************************************************\
**
**  RepositionRect()
**
**  Used to ensure that toplevel dialogs are still visible within the
**  desktop area after they've resized.
**
\***************************************************************************/

void
RepositionRect(
        PMONITOR    pMonitor,
        LPRECT      lprc,
        DWORD       dwStyle,
        DWORD       dwExStyle)
{
    LPRECT      lprcClip;
    int         y;

    UserAssert(lprc);
    UserAssert(pMonitor);

    if (dwStyle & WS_CHILD) {
        if (dwExStyle & WS_EX_CONTROLPARENT)
            return;

        /*
         * Old style 3.1 child dialogs--do this nonsense anyway.  Keeps
         * FedEx happy.
         */
        pMonitor = GetPrimaryMonitor();
        lprcClip = KPRECT_TO_PRECT(&pMonitor->rcMonitor);
    } else if (dwExStyle & WS_EX_TOOLWINDOW) {
        lprcClip = KPRECT_TO_PRECT(&pMonitor->rcMonitor);
    } else {
        lprcClip = KPRECT_TO_PRECT(&pMonitor->rcWork);
    }

    UserAssert(lprc);

    y = lprcClip->bottom - (SYSMET(CYEDGE) * 2 + SYSMET(CYKANJIWINDOW));

    if (lprc->bottom > y) {
        OffsetRect(lprc, 0, y - lprc->bottom);
    }

    if (lprc->top < lprcClip->top) {
        OffsetRect(lprc, 0, lprcClip->top - lprc->top);
    }

    if (lprc->right > lprcClip->right) {
        OffsetRect(lprc, lprcClip->right - lprc->right, 0);
    }

    if (lprc->left < lprcClip->left) {
        OffsetRect(lprc, lprcClip->left - lprc->left, 0);
    }
}

/***************************************************************************\
* MapDialogRect
*
* History:
\***************************************************************************/


FUNCLOG2(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, MapDialogRect, HWND, hwnd, LPRECT, lprc)
BOOL MapDialogRect(
    HWND hwnd,
    LPRECT lprc)
{
    PWND pwnd;

    if ((pwnd = ValidateHwnd(hwnd)) == NULL) {
        return FALSE;
    }

    /*
     * Must do special validation here to make sure pwnd is a dialog window.
     */
    if (!ValidateDialogPwnd(pwnd))
        return FALSE;

    lprc->left = XPixFromXDU(lprc->left, PDLG(pwnd)->cxChar);
    lprc->right = XPixFromXDU(lprc->right, PDLG(pwnd)->cxChar);
    lprc->top = YPixFromYDU(lprc->top, PDLG(pwnd)->cyChar);
    lprc->bottom = YPixFromYDU(lprc->bottom, PDLG(pwnd)->cyChar);

    return TRUE;
}