/****************************** Module Header ******************************\
* Module Name: class.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* This module contains RegisterClass and the related window class management
* functions.
*
* History:
*  12-20-94  FritzS
*
\***************************************************************************/

#include "precomp.h"
#pragma hdrstop

BOOL VisWindow(PWND,DWORD);


/***************************************************************************\
*  xxxSetClassIconEnum
*
*
\***************************************************************************/

BOOL xxxSetClassIconEnum(
    PWND   pwnd,
    LPARAM lParam)
{
    CheckLock(pwnd);

    if (pwnd->pcls == (PCLS)lParam) {
        /*
         * If the window doesn't have a small icon or it comes from
         * WM_QUERYDRAGICON, redraw the title.  In the WM_QUERYDRAGICON
         * case, get rid of the small icon so redrawing the title will
         * create it if necessary.
         */
        if (TestWF(pwnd, WFSMQUERYDRAGICON))
            DestroyWindowSmIcon(pwnd);

        if (!_GetProp(pwnd, MAKEINTATOM(gpsi->atomIconSmProp),PROPF_INTERNAL))
            xxxRedrawTitle(pwnd, DC_ICON);
    }

    return TRUE;
}

/***************************************************************************\
*  SetClassIcon
*
*  Changes the big/small icon of a class.  Called from SetClassWord().
*
\***************************************************************************/

PCURSOR xxxSetClassIcon(
    PWND    pwnd,
    PCLS    pcls,
    PCURSOR pCursor,
    int     gcw)
{
    PTHREADINFO pti = PtiCurrent();
    PCURSOR     pCursorOld;
    HCURSOR     hCursorOld;
    TL          tlpwndChild;
    BOOL        fRedraw;

    CheckLock(pwnd);

    /*
     * Save old icon
     */
    pCursorOld = ((gcw == GCLP_HICON) ? pcls->spicn : pcls->spicnSm);

    if (pCursorOld != pCursor) {

        fRedraw = TRUE;

        hCursorOld = PtoH(pCursorOld);

        /*
         * Set new icon
         */
        if (gcw == GCLP_HICON) {

            /*
             * Destroy private cached small icon first.
             */
            if (pcls->spicnSm && !DestroyClassSmIcon(pcls))
                fRedraw = FALSE;

            Lock(&(pcls->spicn), pCursor);

        } else {

            /*
             * We don't allow apps to see the small icons we create from
             * their big icons.  They can see their own.  Saves memory
             * leak problems and is easier.
             */
            if (pcls->CSF_flags & CSF_CACHEDSMICON) {
                DestroyClassSmIcon(pcls);
                hCursorOld = NULL;
            }

            Lock(&(pcls->spicnSm), pCursor);
        }

        if (pcls->spicn && !pcls->spicnSm)
            xxxCreateClassSmIcon(pcls);

        if (fRedraw) {

            if (pcls->cWndReferenceCount > 1) {
                ThreadLock(pti->rpdesk->pDeskInfo->spwnd->spwndChild, &tlpwndChild);
                xxxInternalEnumWindow(pti->rpdesk->pDeskInfo->spwnd->spwndChild,
                                      xxxSetClassIconEnum,
                                      (LPARAM)pcls,
                                      BWL_ENUMLIST);
                ThreadUnlock(&tlpwndChild);
            } else {
                xxxSetClassIconEnum(pwnd, (LPARAM)pcls);
            }
        }

        /*
         * Revalidate the old cursor
         */
        if (hCursorOld != NULL) {
            pCursorOld = HMRevalidateHandleNoRip(hCursorOld);
        } else {
            pCursorOld = NULL;
        }
    }

    return(pCursorOld);
}

/***************************************************************************\
*  DestroyClassSmIcon()
*
*  Destroys the small icon of a class if we've created a cached one.
*
\***************************************************************************/

BOOL DestroyClassSmIcon(
    PCLS pcls)
{

    /*
     * If we don't have a cached icon, then no work.
     */
    if (pcls->CSF_flags & CSF_CACHEDSMICON) {
        if (pcls->spicnSm) {
            _DestroyCursor(pcls->spicnSm, CURSOR_ALWAYSDESTROY);
            Unlock(&pcls->spicnSm);
        }
        pcls->CSF_flags &= ~CSF_CACHEDSMICON;
        return TRUE;
    }

    return FALSE;
}

/***************************************************************************\
*  xxxCreateClassSmIcon
*
*  Creates a cached class small icon from a class big icon.
*
\***************************************************************************/

VOID xxxCreateClassSmIcon(
    PCLS pcls)
{
    PCURSOR pcur;

    UserAssert(pcls->cWndReferenceCount > 0);
    UserAssert(pcls->spicn);
    UserAssert(!pcls->spicnSm);

    pcur = xxxClientCopyImage(PtoH(pcls->spicn),
            pcls->spicn->rt == PTR_TO_ID(RT_ICON) ? IMAGE_ICON : IMAGE_CURSOR,
            SYSMET(CXSMICON),
            SYSMET(CYSMICON),
            LR_DEFAULTCOLOR | LR_COPYFROMRESOURCE);

    Lock(&pcls->spicnSm, pcur);
    if (pcls->spicnSm)
        pcls->CSF_flags |= CSF_CACHEDSMICON;
}

/***************************************************************************\
*  SetWindowStyle
*
*  Changes the style bits of a window.  Called from SetWindowLong().  This
*  sends two messages, a changing and a changed.  Upon receipt of a
*  WM_STYLECHANGING message, a window can muck with the style bits for
*  validation purposes.  The WM_STYLECHANGED message is simply after the
*  fact.
*
\***************************************************************************/

LONG xxxSetWindowStyle(
    PWND  pwnd,
    int   gwl,
    DWORD styleNew)
{
    STYLESTRUCT sty;
    BOOL        fWasChild;
    BOOL        fIsChild;
    BOOL        fBefore;
    BOOL        fAfter;

    CheckLock(pwnd);
    UserAssert(IsWinEventNotifyDeferredOK());

    /*
     * HACK-O-RAMA
     * A STYLESTRUCT currently has just one field: a DWORD for the style.
     * Therefore, conveniently, we can pass a pointer into the stack for
     * LPARAM.  But, if we add stuff, we'll have to change this.
     */
    sty.styleOld = ((gwl == GWL_STYLE) ? pwnd->style : pwnd->ExStyle);
    sty.styleNew = styleNew;

    /*
     * Note that we don't do validation before _and_ after.  It is sufficient
     * to do our stuff at the end.
     */

    /*
     * We break Quicken 2.0 if we send the messages.  That's why we version
     * switch them.
     */

    /*
     * Send a WM_STYLECHANGING message to the window, so it can muck with
     * the style bits.  Like validate some stuff.
     */
    if (TestWF(pwnd, WFWIN40COMPAT)) {
        xxxSendMessage(pwnd, WM_STYLECHANGING, gwl, (LPARAM)(LPSTYLESTRUCT)&sty);
    }

    /*
     * Now do our own validation.
     */
    if (gwl == GWL_STYLE) {

        BOOL fWasVisWindow;

        /*
         * If this is an edit control that has ES_PASSWORD set and
         * the caller does not own it and is trying to reset it,
         * fail the call.
         */
        if (PpiCurrent() != GETPTI(pwnd)->ppi && IS_EDIT(pwnd) &&
            (sty.styleOld & ES_PASSWORD) && !(sty.styleNew & ES_PASSWORD)) {
            RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "Access denied in xxxSetWindowStyle");
            return 0;
        }

        /* Listbox ownerdraw style check was moved to the client side (client\ntstubs.c) */

        /*
         * Do proper validation on style bits
         */
        if (pwnd->spwndParent == PWNDDESKTOP(pwnd))
            sty.styleNew |= WS_CLIPSIBLINGS;

        /*
         * If the clipping-ness is changing, invalidate the dc cache.
         */
        if ((sty.styleNew & (WS_CLIPCHILDREN | WS_CLIPSIBLINGS)) !=
            (sty.styleOld & (WS_CLIPCHILDREN | WS_CLIPSIBLINGS))) {

            /*
             * No need to DeferWinEventNotify() - pwnd is locked
             */
            zzzInvalidateDCCache(pwnd, IDC_DEFAULT);
        }

        /*
         * This breaks all Paradox dialogs 1.0-5.0 that have combos.  They
         * enumerate all child windows, add on minimized, then sit in a peek
         * loop.  After that they enumerate all child windows and remove
         * WS_MINIMIZE--except the code below won't let them.
         *
         * Result is weird painting and an inability to use the dialog any
         * more short of dismissing it
         *
         * Temp fix:  Check for child window first.
         */

        /*
         * if this window is REALLY minimized (minimized bit is set and caption
         * present bit is removed), then don't allow app to remove the minimize
         * bit -- this fixes FoxBlow's attempt at being the OS -- jeffbog
         */
        if (!TestWF(pwnd, WFCHILD) &&
                TestWF(pwnd, WFMINIMIZED) &&
                !TestWF(pwnd, WFCPRESENT) &&
                !(sty.styleNew & WS_MINIMIZE)) {

            sty.styleNew |= WS_MINIMIZE;
        }

        /*
         * If we're changing the child bit, deal with spmenu appropriately.
         * If we're turning into a child, change spmenu to an id. If we're
         * turning into a top level window, turn spmenu into a menu.
         */
        fWasChild = TestwndChild(pwnd);

        pwnd->style = sty.styleNew;

        fIsChild = TestwndChild(pwnd);

        /*
         * If we turned into a top level window, change spmenu to NULL.
         * If we turned into a child from a top level window, unlock spmenu.
         */
        if (fWasChild && !fIsChild)
            pwnd->spmenu = NULL;

        if (!fWasChild && fIsChild) {
            ClrWF(pwnd, WFMPRESENT);
            UnlockWndMenu(pwnd, &pwnd->spmenu);
        }

        /*
         * If the visible, child, or minimized style is changing,
         * then update the cVisWindows count
         */
        fWasVisWindow = VisWindow(pwnd, sty.styleOld);
        if (fWasVisWindow != VisWindow(pwnd, sty.styleNew))
        {

#if DBG
            if (TestWF(pwnd, WFINDESTROY)) {
                RIPMSG1(RIP_ERROR, "xxxSetWindowStyle: window INDESTROY 0x%08X", pwnd);
            }
#endif

//            IncDecVisWindows(pwnd, !fWasVisWindow);
            if (fWasVisWindow)
                DecVisWindows(pwnd);
            else
                IncVisWindows(pwnd);
        }
    } else {
        /*
         * First, see if the app might be setting bits that it really
         * doesn't know about.  If so, replace those bits with the
         * current values.
         */
        if (GetAppCompatFlags2(VER40) & GACF2_NO50EXSTYLEBITS) {
            sty.styleNew &= WS_EX_VALID40;
        } else {
            /*
             * Don't let aplications set unused extended bits
             * Mcostea #237449
             */
#if DBG
            if (sty.styleNew & ~WS_EX_ALLVALID) {
                RIPMSG0(RIP_WARNING, "Trying to set reserved exStyle bits in SetWindowLong");
            }
#endif
            sty.styleNew &= WS_EX_ALLVALID;
        }
        /*
         * Is someone trying to toggle the WS_EX_TOPMOST style bit?
         */
        if ((sty.styleOld & WS_EX_TOPMOST) != (sty.styleNew & WS_EX_TOPMOST)) {

#if DBG
            /*
             * Rip in debug about this
             */
            RIPMSG0(RIP_WARNING, "Can't change WS_EX_TOPMOST with SetWindowLong");
#endif

            /*
             * BACKWARDS COMPATIBILITY HACK
             * If stuff is getting stored in the high word, then it must be
             * Lotus 123-W sticking a FAR pointer in this field.  So don't
             * modify it.
             */
            if (TestWF(pwnd, WFWIN40COMPAT) || !HIWORD(sty.styleNew)) {

                /*
                 * Don't let the bit be flipped
                 */
                sty.styleNew &= ~WS_EX_TOPMOST;
                sty.styleNew |= (sty.styleOld & WS_EX_TOPMOST);
            }
        }

        /*
         * Check pwnd->ExStyle directly since sty.styleOld can now be
         * different from the real state of the window, because of the
         * callbacks in this function from the time sty.styleOld was
         * remembered and up to now. We must call the layering functions
         * based on the real state of the layering bit.
         */
        fBefore = (pwnd->ExStyle & WS_EX_LAYERED);
        fAfter = (sty.styleNew & WS_EX_LAYERED);

        if (fBefore && !fAfter) {
            UnsetLayeredWindow(pwnd);
        } else if (!fBefore && fAfter) {
            if (!xxxSetLayeredWindow(pwnd, TRUE)) {
                return 0;
            }
        }

        fBefore = (pwnd->ExStyle & WS_EX_COMPOSITED);
        fAfter = (sty.styleNew & WS_EX_COMPOSITED);

        if (!fBefore && fAfter) {
            /*
             * If we are turning WS_EX_COMPOSITED on, none of our parents
             * should already have WS_EX_COMPOSITED turned on.  If any do,
             * since we were explicitely trying to turn this style on, fail
             * the call.
             */

            if (GetStyleWindow(pwnd->spwndParent, WEFCOMPOSITED) != NULL) {
                return 0;
            }
        }

        if (fBefore && !fAfter) {
            UnsetRedirectedWindow(pwnd, REDIRECT_COMPOSITED);
        } else if (!fBefore && fAfter) {
            if (!SetRedirectedWindow(pwnd, REDIRECT_COMPOSITED)) {
                return 0;
            }

            /*
             * We have successfully turned WS_EX_COMPOSITED on for ourself, so
             * need to ensure that none of our child have WS_EX_COMPOSITED also
             * turned on.
             */

            xxxTurnOffCompositing(pwnd, TRUE);
        }

#ifdef REDIRECTION
        {

            BOOL fVisrgnChange = FALSE;
            fBefore = (pwnd->ExStyle & WS_EX_EXTREDIRECTED);
            fAfter = (sty.styleNew & WS_EX_EXTREDIRECTED);

            if (fBefore && !fAfter) {
                UnsetRedirectedWindow(pwnd, REDIRECT_EXTREDIRECTED);
                fVisrgnChange = TRUE;
            } else if (!fBefore && fAfter) {
                if (!SetRedirectedWindow(pwnd, REDIRECT_EXTREDIRECTED)) {
                    return 0;
                }
                fVisrgnChange = TRUE;
            }

            if (fVisrgnChange) {
                BEGINATOMICCHECK();
                zzzInvalidateDCCache(pwnd, IDC_DEFAULT | IDC_NOMOUSE);
                ENDATOMICCHECK();
            }
        }
#endif // REDIRECTION

        /*
         * The bits we use internally should be preserved
         */
        pwnd->ExStyle = sty.styleNew | (pwnd->ExStyle & ~WS_EX_ALLVALID);
        if ((sty.styleOld ^ sty.styleNew)
            & (WS_EX_LEFTSCROLLBAR | WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LAYOUTRTL)) {
            xxxRedrawFrame(pwnd);
        }
    }

    /*
     * See if we still need the 3D edge since the window styles changed.
     */
    if (NeedsWindowEdge(pwnd->style, pwnd->ExStyle, TestWF(pwnd, WFWIN40COMPAT)))
        SetWF(pwnd, WEFWINDOWEDGE);
    else
        ClrWF(pwnd, WEFWINDOWEDGE);

    /*
     * Send a WM_STYLECHANGED message
     */
    if (TestWF(pwnd, WFWIN40COMPAT))
        xxxSendMessage(pwnd, WM_STYLECHANGED, gwl, (LPARAM)(LPSTYLESTRUCT)&sty);

    return(sty.styleOld);
}

/***************************************************************************\
* VisWindow
*
*  Based on style, determines if this is considered to be "visible" by
*  queue foreground styles.
*
\***************************************************************************/

BOOL VisWindow(
    PWND pwnd,
    DWORD style)
{
    return(FTopLevel(pwnd) &&
            !(style & WS_MINIMIZE) &&
            (style & WS_VISIBLE));
}