#include "ctlspriv.h"
#pragma hdrstop
#include "usrctl32.h"
#include "static.h"
#include "image.h"

#define SS_TEXTMIN1         0x0000000BL
#define SS_TEXTMAX1         0x0000000DL
#define SS_EDITCONTROL      0x00002000L

#define ISSSTEXTOROD(bType)                 \
            (((bType) <= SS_TEXTMAX0)       \
            || (((bType) >= SS_TEXTMIN1)    \
            && ((bType) <= SS_TEXTMAX1)))

//  Common macros for image handling.
#define IsValidImage(imageType, realType, max)  \
            ((imageType < max) && (rgbType[imageType] == realType))


//---------------------------------------------------------------------------//
//
//  Type table.  This is used for validation of the
//  image-types.  For the PPC release we won't support
//  the metafile format, but others are OK.
#define IMAGE_STMMAX    IMAGE_ENHMETAFILE+1
static BYTE rgbType[IMAGE_STMMAX] = 
{
    SS_BITMAP,       // IMAGE_BITMAP
    SS_ICON,         // IMAGE_CURSOR
    SS_ICON,         // IMAGE_ICON
    SS_ENHMETAFILE   // IMAGE_ENHMETAFILE
};


//---------------------------------------------------------------------------//
//
//  LOBYTE of SS_ style is index into this array
#define STK_OWNER       0x00
#define STK_IMAGE       0x01
#define STK_TEXT        0x02
#define STK_GRAPHIC     0x03
#define STK_TYPE        0x03

#define STK_ERASE       0x04
#define STK_USEFONT     0x08
#define STK_USETEXT     0x10

BYTE rgstk[] = 
{
    STK_TEXT | STK_ERASE | STK_USEFONT | STK_USETEXT,       // SS_LEFT
    STK_TEXT | STK_ERASE | STK_USEFONT | STK_USETEXT,       // SS_CENTER
    STK_TEXT | STK_ERASE | STK_USEFONT | STK_USETEXT,       // SS_RIGHT
    STK_IMAGE | STK_ERASE,                                  // SS_ICON
    STK_GRAPHIC,                                            // SS_BLACKRECT
    STK_GRAPHIC,                                            // SS_GRAYRECT
    STK_GRAPHIC,                                            // SS_WHITERECT
    STK_GRAPHIC,                                            // SS_BLACKFRAME
    STK_GRAPHIC,                                            // SS_GRAYFRAME
    STK_GRAPHIC,                                            // SS_WHITEFRAME
    STK_OWNER,                                              // SS_USERITEM
    STK_TEXT | STK_USEFONT | STK_USETEXT,                   // SS_SIMPLE
    STK_TEXT | STK_ERASE | STK_USEFONT | STK_USETEXT,       // SS_LEFTNOWORDWRAP
    STK_OWNER | STK_USEFONT | STK_USETEXT,                  // SS_OWNERDRAW
    STK_IMAGE | STK_ERASE,                                  // SS_BITMAP
    STK_IMAGE | STK_ERASE,                                  // SS_ENHMETAFILE
    STK_GRAPHIC,                                            // SS_ETCHEDHORZ
    STK_GRAPHIC,                                            // SS_ETCHEDVERT
    STK_GRAPHIC                                             // SS_ETCHEDFRAME
};

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

    wc.lpfnWndProc   = Static_WndProc;
    wc.lpszClassName = WC_STATIC;
    wc.style         = CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = sizeof(PSTAT);
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName  = NULL;

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

    return TRUE;
}


//---------------------------------------------------------------------------//
void GetRectInParent(HWND hwnd, PRECT prc)
{
    HWND hwndParent = GetParent(hwnd);

    GetWindowRect(hwnd, prc);
    ScreenToClient(hwndParent, (PPOINT)prc);
    ScreenToClient(hwndParent, (PPOINT)&prc->right);
}


//---------------------------------------------------------------------------//
VOID GetIconSize(HICON hIcon, PSIZE pSize)
{
    ICONINFO iconInfo;
    BITMAP   bmp;

    pSize->cx = pSize->cy = 32;

    if (GetIconInfo(hIcon, &iconInfo))
    {
        if (GetObject(iconInfo.hbmColor, sizeof(bmp), &bmp)) 
        {
            pSize->cx = bmp.bmWidth;
            pSize->cy = bmp.bmHeight;
        }

        DeleteObject(iconInfo.hbmMask);
        DeleteObject(iconInfo.hbmColor);
    }
}


//---------------------------------------------------------------------------//
//
// SetStaticImage()
//
// Sets bitmap/icon of static guy, either in response to a STM_SETxxxx
// message, or at create time.
//
HANDLE Static_SetImage(PSTAT pstat, HANDLE hImage, BOOL fDeleteIt)
{
    UINT   bType;
    RECT   rc;
    RECT   rcWindow;
    RECT   rcClient;
    HANDLE hImageOld;
    DWORD  dwRate;
    UINT   cicur;
    BOOL   fAnimated = FALSE;
    HWND   hwnd = pstat->hwnd;
    LONG   dwStyle = GET_STYLE(pstat);

    bType =  dwStyle & SS_TYPEMASK;

    GetClientRect(hwnd, &rcClient);

    //
    // If this is an old-ani-icon, then delete its timer.
    //
    if ((bType == SS_ICON) && pstat->cicur > 1) 
    {
        //
        // Old cursor was an animated cursor, so kill
        // the timer that is used to animate it.
        //
        KillTimer(hwnd, IDSYS_STANIMATE);
    }

    //
    // Initialize the old-image return value.
    //
    hImageOld = pstat->hImage;

    rc.right = rc.bottom = 0;

    if (hImage != NULL) 
    {

        switch (bType) 
        {
        case SS_ENHMETAFILE: 
        {
            //
            // We do NOT resize the window.
            //
            rc.right  = rcClient.right  - rcClient.left;
            rc.bottom = rcClient.bottom - rcClient.top;

            break;
        }

        case SS_BITMAP: 
        {
            BITMAP bmp;

            pstat->fAlphaImage = FALSE;

            if (GetObject(hImage, sizeof(BITMAP), &bmp)) 
            {
                rc.right  = bmp.bmWidth;
                rc.bottom = bmp.bmHeight;

                if (bmp.bmBitsPixel == 32)
                {
                    HDC hdc = CreateCompatibleDC(NULL);
                    if (hdc)
                    {
                        RGBQUAD* prgb;
                        HBITMAP hbmpImage32 = CreateDIB(hdc, bmp.bmWidth, bmp.bmHeight, &prgb);
                        if (hbmpImage32)
                        {
                            HDC hdc32 = CreateCompatibleDC(hdc);
                            if (hdc32)
                            {
                                HBITMAP hbmpOld = (HBITMAP)SelectObject(hdc32, hbmpImage32);
                                HBITMAP hbmpTemp = (HBITMAP)SelectObject(hdc, hImage);
                                BitBlt(hdc32, 0, 0, bmp.bmWidth, bmp.bmHeight, hdc, 0, 0, SRCCOPY);

                                SelectObject(hdc, hbmpTemp);
                                SelectObject(hdc32, hbmpOld);
                                DeleteDC(hdc32);

                                if (DIBHasAlpha(bmp.bmWidth, bmp.bmHeight, prgb))
                                {
                                    PreProcessDIB(bmp.bmWidth, bmp.bmHeight, prgb);

                                    if (fDeleteIt)
                                        DeleteObject(hImage);

                                    pstat->fAlphaImage = TRUE;
                                    hImage = hbmpImage32;
                                    hbmpImage32 = NULL;
                                    fDeleteIt = TRUE;
                                }
                            }

                            if (hbmpImage32)
                                DeleteObject(hbmpImage32);
                        }

                        DeleteDC(hdc);
                    }
                }
            }

            break;
        }

        case SS_ICON: 
        {
            SIZE size;

            GetIconSize((HICON)hImage, &size);
            rc.right  = size.cx;
            rc.bottom = size.cy;

            pstat->cicur = 0;
            pstat->iicur = 0;

            //
            // Perhaps we can do something like shell\cpl\main\mouseptr.c
            // here, and make GetCursorFrameInfo obsolete.
            //
            if (GetCursorFrameInfo(hImage, NULL, 0, &dwRate, &cicur)) 
            {
                fAnimated = (cicur > 1);
                pstat->cicur = cicur;
            }
            break;
        }

        }
    }

    pstat->hImage = hImage;
    pstat->fDeleteIt = fDeleteIt;

    //
    // Resize static to fit.
    // Do NOT do this for SS_CENTERIMAGE or SS_REALSIZECONTROL
    //
    if (!(dwStyle & SS_CENTERIMAGE) && !(dwStyle & SS_REALSIZECONTROL))
    {
        //
        // Get current window rect in parent's client coordinates.
        //
        GetRectInParent(hwnd, &rcWindow);

        //
        // Get new window dimensions
        //
        rc.left = 0;
        rc.top = 0;

        if (rc.right && rc.bottom) 
        {
            AdjustWindowRectEx(&rc, dwStyle, FALSE, GET_EXSTYLE(pstat));
            rc.right  -= rc.left;
            rc.bottom -= rc.top;
        }

        SetWindowPos(hwnd, HWND_TOP,
                    0, 0, rc.right, rc.bottom,
                    SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
    }

    if (IsWindowVisible(hwnd)) 
    {
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
    }


    //
    // If this is an aimated-icon, then start the timer for
    // the animation sequence.
    //
    if(fAnimated) 
    {
        //
        // Perhaps we can do something like shell\cpl\main\mouseptr.c
        // here, and make GetCursorFrameInfo obsolete.
        //
        GetCursorFrameInfo(pstat->hImage, NULL, pstat->iicur, &dwRate, &cicur);
        dwRate = max(200, dwRate * 100 / 6);
        SetTimer(hwnd, IDSYS_STANIMATE, dwRate, NULL);
    }

    return hImageOld;
}


//---------------------------------------------------------------------------//
//
// Static_LoadImage()
//
// Loads the icon or bitmap from the app's resource file if a name was
// specified in the dialog template.  We assume that the name is the name
// of the resource to load.
//
VOID Static_LoadImage(PSTAT pstat, LPTSTR lpszName)
{
    HANDLE hImage = NULL;
    HWND hwnd = pstat->hwnd;
    ULONG ulStyle = GET_STYLE(pstat);
    HINSTANCE hInstance = (HINSTANCE) GetWindowInstance(hwnd);

    if (lpszName && *lpszName) 
    {
        //
        // Only try to load the icon/bitmap if the string is non null.
        //
        if (*(BYTE *)lpszName == 0xFF)
        {
            lpszName = (TCHAR*)MAKEINTRESOURCE(((LPWORD)lpszName)[1]);
        }
   
        //
        // Load the image.  If it can't be found in the app, try the
        // display driver.
        //
        if (lpszName)
        {
            switch ((ulStyle & SS_TYPEMASK)) 
            {
            case SS_BITMAP:

                hImage = LoadBitmap(hInstance, lpszName);

                //
                // If the above didn't load it, try loading it from the
                // display driver (hmod == NULL).
                //
                if (hImage == NULL)
                {
                    hImage = LoadBitmap(NULL, lpszName);
                }

                break;

            case SS_ICON:
                if ((ulStyle & SS_REALSIZEIMAGE)) 
                {
                    hImage = LoadImage(hInstance, lpszName, IMAGE_ICON, 0, 0, 0);
                } 
                else 
                {
                    hImage = LoadIcon(hInstance, lpszName);

                    //
                    // We will also try to load a cursor-format if the
                    // window is a 4.0 compatible.  Icons/Cursors are really
                    // the same.  We don't do this for 3.x apps for the
                    // usual compatibility reasons.
                    //
                    if ((hImage == NULL))
                    {
                        hImage = LoadCursor(hInstance, lpszName);
                    }

                    //
                    // If the above didn't load it, try loading it from the
                    // display driver (hmod == NULL).
                    //
                    if (hImage == NULL) 
                    {
                        hImage = LoadIcon(NULL, lpszName);
                    }
                }

                break;
            }

            //
            // Set the image if it was loaded.
            //
            if (hImage)
            {
                Static_SetImage(pstat, hImage, TRUE);
            }
        }
    }
}


//---------------------------------------------------------------------------//
//
// Static_DrawStateCB()
//
// Draws text statics, called by DrawState.
//
BOOL CALLBACK Static_DrawStateCB(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy)
{
    PSTAT pstat  = (PSTAT)lp;
    if (pstat)
    {
        UINT   style;
        RECT   rc;
        BYTE   bType;
        LONG   lTextLength;
        ULONG  ulStyle   = GET_STYLE(pstat);
        ULONG  ulExStyle = GET_EXSTYLE(pstat);
        HWND   hwnd = pstat->hwnd;


        bType = (BYTE)(ulStyle & SS_TYPEMASK);
        
        if ((lTextLength = GetWindowTextLength(hwnd))) 
        {
            PTCHAR lpszName = (TCHAR *) _alloca((lTextLength + 1) * sizeof(wchar_t));

            if (lpszName)
            {
                GetWindowText(pstat->hwnd, lpszName, lTextLength + 1);
                style = DT_NOCLIP | DT_EXPANDTABS;

                if (bType != LOBYTE(SS_LEFTNOWORDWRAP)) 
                {
                    style |= DT_WORDBREAK;
                    style |= (UINT)(bType - LOBYTE(SS_LEFT));

                    if (ulStyle &  SS_EDITCONTROL)
                    {
                        style |= DT_EDITCONTROL;
                    }
                }

                switch (ulStyle & SS_ELLIPSISMASK) 
                {
                case SS_WORDELLIPSIS:
                    style |= DT_WORD_ELLIPSIS | DT_SINGLELINE;
                    break;

                case SS_PATHELLIPSIS:
                    style |= DT_PATH_ELLIPSIS | DT_SINGLELINE;
                    break;

                case SS_ENDELLIPSIS:
                    style |= DT_END_ELLIPSIS | DT_SINGLELINE;
                    break;
                }

                if (ulStyle &  SS_NOPREFIX)
                {
                    style |= DT_NOPREFIX;
                }

                if (ulStyle & SS_CENTERIMAGE)
                {
                    style |= DT_VCENTER | DT_SINGLELINE;
                }

                rc.left     = 0;
                rc.top      = 0;
                rc.right    = cx;
                rc.bottom   = cy;

                if (TESTFLAG(GET_EXSTYLE(pstat), WS_EXP_UIACCELHIDDEN))
                {
                    style |= DT_HIDEPREFIX;
                } 
                else if (pstat->fPaintKbdCuesOnly) 
                {
                    style |= DT_PREFIXONLY;
                }

                if (!pstat->hTheme)
                {
                    DrawText(hdc, lpszName, -1, &rc, (DWORD)style);
                }
                else
                {
                    DrawThemeText(pstat->hTheme, 
                                  hdc, 
                                  0, 
                                  0, 
                                  lpszName, 
                                  lTextLength, 
                                  style, 
                                  0, 
                                  &rc);
                }
            }
        }

        return TRUE;
    }

    return FALSE;
}


//---------------------------------------------------------------------------//
void Static_Paint(PSTAT pstat, HDC hdc, BOOL fClip)
{
    HWND   hwndParent;
    RECT   rc;
    UINT   cmd;
    BYTE   bType;
    BOOL   fFont;
    HBRUSH hbrControl;
    UINT   oldAlign;
    DWORD  dwOldLayout=0;
    HANDLE hfontOld = NULL;
    HWND hwnd = pstat->hwnd;
    ULONG ulStyle = GET_STYLE(pstat);
    ULONG ulExStyle = GET_EXSTYLE(pstat);

    if (ulExStyle & WS_EX_RTLREADING)
    {
        oldAlign = GetTextAlign(hdc);
        SetTextAlign(hdc, oldAlign | TA_RTLREADING);
    }

    bType = (BYTE)(ulStyle & SS_TYPEMASK);
    GetClientRect(hwnd, &rc);

    if (fClip) 
    {
        IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
    }

    fFont = (rgstk[bType] & STK_USEFONT) && (pstat->hFont != NULL);

    if (fFont)
    {
        hfontOld = SelectObject(hdc, pstat->hFont);
    }

    //
    // Send WM_CTLCOLORSTATIC to all statics (even frames) for 1.03
    // compatibility.
    //
    SetBkMode(hdc, OPAQUE);
    hbrControl = (HBRUSH)SendMessage(GetParent(hwnd), WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd);

    //
    // Do we erase the background?  We don't for SS_OWNERDRAW
    // and STK_GRAPHIC kind of things.
    //
    hwndParent = GetParent(hwnd);
    
    if ((rgstk[bType] & STK_ERASE) && 
         !pstat->fPaintKbdCuesOnly &&
         !pstat->hTheme) 
    {
        FillRect(hdc, &rc, hbrControl);
    }

    switch (LOBYTE(bType))
    {
    case SS_ICON:

        if (pstat->hImage)
        {

            int     cx;
            int     cy;

            if (ulExStyle & WS_EX_LAYOUTRTL) 
            {
                dwOldLayout = SetLayoutWidth(hdc, -1, 0);
            }

            //
            // Perform the correct rect-setup.
            //
            if (ulStyle & SS_CENTERIMAGE)
            {
                SIZE size;

                GetIconSize((HICON)pstat->hImage, &size);
                cx = size.cx;
                cy = size.cy;

                rc.left   = (rc.right  - cx) / 2;
                rc.right  = (rc.left   + cx);
                rc.top    = (rc.bottom - cy) / 2;
                rc.bottom = (rc.top    + cy);
            }
            else
            {
                cx = rc.right  - rc.left;
                cy = rc.bottom - rc.top;
            }

            DrawIconEx(hdc, rc.left, rc.top, (HICON)pstat->hImage, cx, cy,
                       pstat->iicur, pstat->hTheme ? NULL : hbrControl, DI_NORMAL);

            if (ulExStyle & WS_EX_LAYOUTRTL) 
            {
                SetLayoutWidth(hdc, -1, dwOldLayout);
            }
        }
        else 
        {
            // Empty!  Need to erase.
            FillRect(hdc, &rc, hbrControl);
        }
        break;

    case SS_BITMAP:

        if (pstat->hImage)
        {

            BITMAP  bmp;
            //
            // Get the bitmap information.  If this fails, then we
            // can assume somethings wrong with its format...don't
            // draw in this case.
            //
            if (GetObject(pstat->hImage, sizeof(BITMAP), &bmp))
            {
                HDC hdcT;

                if (ulStyle & SS_CENTERIMAGE) 
                {
                    rc.left   = (rc.right  - bmp.bmWidth)  >> 1;
                    rc.right  = (rc.left   + bmp.bmWidth);
                    rc.top    = (rc.bottom - bmp.bmHeight) >> 1;
                    rc.bottom = (rc.top    + bmp.bmHeight);
                } 

                //
                // Select in the bitmap and blt it to the client-surface.
                //
                hdcT = CreateCompatibleDC(hdc);
                if (hdcT)
                {
                    HBITMAP hbmpT = (HBITMAP)SelectObject(hdcT, pstat->hImage);

                    if (pstat->fAlphaImage)
                    {
                        BLENDFUNCTION bf = {0};
                        bf.BlendOp = AC_SRC_OVER;
                        bf.SourceConstantAlpha = 255;
                        bf.AlphaFormat = AC_SRC_ALPHA;
                        bf.BlendFlags = 0;
                        GdiAlphaBlend(hdc,  rc.left, rc.top, rc.right-rc.left,
                               rc.bottom-rc.top, hdcT, 0, 0, bmp.bmWidth, bmp.bmHeight, bf);
                    }
                    else
                    {
                        // I'm assuming people try to match the color to the dialog
                        GdiTransparentBlt(hdc, rc.left, rc.top, rc.right-rc.left,
                               rc.bottom-rc.top, hdcT, 0, 0, bmp.bmWidth,
                               bmp.bmHeight, GetSysColor(COLOR_BTNFACE));       
                    }

                    if (hbmpT)
                    {
                        SelectObject(hdcT, hbmpT);
                    }

                    DeleteDC(hdcT);
                }                
            }
        }
        break;

    case SS_ENHMETAFILE:

        if (pstat->hImage) 
        {
            RECT rcl;

            rcl.left   = rc.left;
            rcl.top    = rc.top;
            rcl.right  = rc.right;
            rcl.bottom = rc.bottom;

            PlayEnhMetaFile(hdc, (HENHMETAFILE)pstat->hImage, &rcl);
        }
        break;

    case SS_OWNERDRAW: 
        {
            DRAWITEMSTRUCT dis;

            dis.CtlType    = ODT_STATIC;
            dis.CtlID      = GetDlgCtrlID(hwnd);
            dis.itemAction = ODA_DRAWENTIRE;
            dis.itemState  = IsWindowVisible(hwnd) ? ODS_DISABLED : 0;
            dis.hwndItem   = hwnd;
            dis.hDC        = hdc;
            dis.itemData   = 0L;
            dis.rcItem     = rc;

            if (TESTFLAG(GET_EXSTYLE(pstat), WS_EXP_UIACCELHIDDEN))
            {
                dis.itemState |= ODS_NOACCEL;
            }

            //
            // Send a WM_DRAWITEM message to the parent.
            //
            SendMessage(hwndParent, WM_DRAWITEM, (WPARAM)dis.CtlID, (LPARAM)&dis);
        }
        break;

    case SS_LEFT:
    case SS_CENTER:
    case SS_RIGHT:
    case SS_LEFTNOWORDWRAP:

        if (GetWindowTextLength(hwnd)) 
        {
            UINT dstFlags;

            dstFlags = DST_COMPLEX;

            if (!IsWindowEnabled(hwnd)) 
            {
                dstFlags |= DSS_DISABLED;
            }

            DrawState(hdc, GetSysColorBrush(COLOR_WINDOWTEXT),
                (DRAWSTATEPROC)Static_DrawStateCB,(LPARAM)pstat, (WPARAM)TRUE,
                rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
                dstFlags);
        }
        break;

    case SS_SIMPLE: 
        {
            LPWSTR pszText = NULL;
            INT    cchText;

            //
            // The "Simple" bType assumes everything, including the following:
            // 1. The Text exists and fits on one line.
            // 2. The Static item is always enabled.
            // 3. The Static item is never changed to be a shorter string.
            // 4. The Parent never responds to the CTLCOLOR message
            //
            cchText = GetWindowTextLength(hwnd);
            if (cchText > 0) 
            {
                pszText = (LPWSTR)_alloca((cchText + 1) * sizeof(WCHAR));
                cchText = GetWindowText(hwnd, pszText, cchText + 1);
            }
            else
            {
                pszText = TEXT("");
                cchText = 0;
            }

            if (pszText)
            {
                if (ulStyle & SS_NOPREFIX && !pstat->hTheme) 
                {
                    ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE | ETO_CLIPPED, &rc, pszText, cchText, 0L);
                } 
                else 
                {
                    //
                    // Use OPAQUE for speed.
                    //
                    DWORD dwFlags;
                    if (TESTFLAG(GET_EXSTYLE(pstat), WS_EXP_UIACCELHIDDEN))
                    {
                        dwFlags = DT_HIDEPREFIX;
                    } 
                    else if (pstat->fPaintKbdCuesOnly) 
                    {
                        dwFlags = DT_PREFIXONLY;
                    } 
                    else if (ulStyle & SS_NOPREFIX)
                    {
                        dwFlags = DT_NOPREFIX;
                    }
                    else
                    {
                        dwFlags = 0;
                    }

                    if (!pstat->hTheme)
                    {
                        TextOut(hdc, rc.left, rc.top, pszText, cchText);
                    }
                    else
                    {
                        DrawThemeText(pstat->hTheme, hdc, 0, 0, pszText, cchText, dwFlags, 0, &rc);
                    }
                }
            }
        }

        break;

    case SS_BLACKFRAME:
        cmd = (COLOR_3DDKSHADOW << 3);
        goto StatFrame;

    case SS_GRAYFRAME:
        cmd = (COLOR_3DSHADOW << 3);
        goto StatFrame;

    case SS_WHITEFRAME:
        cmd = (COLOR_3DHILIGHT << 3);
StatFrame:
        DrawFrame(hdc, &rc, 1, cmd);
        break;

    case SS_BLACKRECT:
        hbrControl = GetSysColorBrush(COLOR_3DDKSHADOW);
        goto StatRect;

    case SS_GRAYRECT:
        hbrControl = GetSysColorBrush(COLOR_3DSHADOW);
        goto StatRect;

    case SS_WHITERECT:
        hbrControl = GetSysColorBrush(COLOR_3DHILIGHT);
StatRect:
        FillRect(hdc, &rc, hbrControl);
        break;

    case SS_ETCHEDFRAME:
        DrawEdge(hdc, &rc, EDGE_ETCHED, BF_RECT);
        break;
    }

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

    if (ulExStyle & WS_EX_RTLREADING) 
    {
        SetTextAlign(hdc, oldAlign);
    }
}


//---------------------------------------------------------------------------//
void Static_Repaint(PSTAT pstat)
{
    HWND hwnd = pstat->hwnd;

    if (IsWindowVisible(hwnd)) 
    {
        HDC hdc;

        if (hdc = GetDC(hwnd)) 
        {
            Static_Paint(pstat, hdc, TRUE);
            ReleaseDC(hwnd, hdc);
        }
    }
}



//---------------------------------------------------------------------------//
//
// Static_NotifyParent()
// 
// Sends WM_COMMAND notification messages.
//
LRESULT Static_NotifyParent(HWND hwnd, HWND hwndParent, int  nCode)
{
    LRESULT lret;

    if (!hwndParent) 
    {
        hwndParent = GetParent(hwnd);
    }

    lret = SendMessage(hwndParent, WM_COMMAND,
                       MAKELONG(GetDlgCtrlID(hwnd), nCode), (LPARAM)hwnd);

    return lret;
}


//---------------------------------------------------------------------------//
//
// Static_AniIconStep
//
// Advances to the next step in an animaged icon.
//
VOID Static_AniIconStep(PSTAT pstat)
{
    DWORD dwRate;
    HWND hwnd = pstat->hwnd;

    dwRate = 0;

    //
    // Stop the timer for the next animation step.
    //
    KillTimer(hwnd, IDSYS_STANIMATE);

    if (++(pstat->iicur) >= pstat->cicur) 
    {
        pstat->iicur = 0;
    }

    //
    // Perhaps we can do something like shell\cpl\main\mouseptr.c
    // here, and make GetCursorFrameInfo obsolete.
    //
    GetCursorFrameInfo(pstat->hImage, NULL, pstat->iicur, &dwRate, &pstat->cicur);
    dwRate = max(200, dwRate * 100 / 6);

    InvalidateRect(hwnd, NULL, FALSE);
    UpdateWindow(hwnd);

    SetTimer(hwnd, IDSYS_STANIMATE, dwRate, NULL);
}


//---------------------------------------------------------------------------//
//
// Static_WndProc
//
// WndProc for Static controls
//
LRESULT APIENTRY Static_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PSTAT   pstat;
    LRESULT lReturn = FALSE;

    //
    // Get the instance data for this static control
    //
    pstat = Static_GetPtr(hwnd);
    if (!pstat && uMsg != WM_NCCREATE)
    {
        goto CallDWP;
    }

    switch (uMsg) 
    {
    case STM_GETICON:
        wParam = IMAGE_ICON;

    case STM_GETIMAGE:
        if (IsValidImage(wParam, (GET_STYLE(pstat) & SS_TYPEMASK), IMAGE_STMMAX)) 
        {
            return (LRESULT)pstat->hImage;
        }
        break;

    case STM_SETICON:
        lParam = (LPARAM)wParam;
        wParam = IMAGE_ICON;

    case STM_SETIMAGE:
        if (IsValidImage(wParam, (GET_STYLE(pstat) & SS_TYPEMASK), IMAGE_STMMAX)) 
        {
            return (LRESULT)Static_SetImage(pstat, (HANDLE)lParam, FALSE);
        }
        break;

    case WM_ERASEBKGND:

        //
        // The control will be erased in Static_Paint().
        //
        return TRUE;

    case WM_PRINTCLIENT:
        Static_Paint(pstat, (HDC)wParam, FALSE);
        break;

    case WM_PAINT:
    {
        HDC         hdc;
        PAINTSTRUCT ps;

        hdc = (HDC)wParam;
        if (hdc == NULL) 
        {
            hdc = BeginPaint(hwnd, &ps);
        }

        if (IsWindowVisible(hwnd)) 
        {
            Static_Paint(pstat, hdc, !wParam);
        }

        //
        // If hwnd was destroyed, BeginPaint was automatically undone.
        //
        if (!wParam) 
        {
            EndPaint(hwnd, &ps);
        }

        break;
    }

    case WM_CREATE:
    {
        BYTE bType = (BYTE)(GET_STYLE(pstat) & SS_TYPEMASK);
        pstat->hTheme = OpenThemeData(pstat->hwnd, L"Static");
        EnableThemeDialogTexture(GetParent(pstat->hwnd), ETDT_ENABLE);

        if ((rgstk[bType] & STK_TYPE) == STK_IMAGE) 
        {
            LPTSTR  lpszName;

            lpszName = (LPTSTR)(((LPCREATESTRUCT)lParam)->lpszName);

            //
            // Load the image
            //
            Static_LoadImage(pstat, lpszName);
        } 
        else if (bType == SS_ETCHEDHORZ || bType == SS_ETCHEDVERT) 
        {
            //
            // Resize static window to fit edge.  Horizontal dudes
            // make bottom one edge from top, vertical dudes make
            // right edge one edge from left.
            //
            RECT    rcClient;

            GetClientRect(hwnd, &rcClient);
            if (bType == SS_ETCHEDHORZ)
            {
                rcClient.bottom = GetSystemMetrics(SM_CYEDGE);
            }
            else
            {
                rcClient.right = GetSystemMetrics(SM_CXEDGE);
            }

            SetWindowPos(hwnd, HWND_TOP, 0, 0, rcClient.right,
                rcClient.bottom, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
        }

        break;
    }

    case WM_DESTROY:
    {
        BYTE bType = (BYTE)(GET_STYLE(pstat) & SS_TYPEMASK);

        if (((rgstk[bType] & STK_TYPE) == STK_IMAGE) &&
            (pstat->hImage != NULL)                  &&
            (pstat->fDeleteIt)) 
        {

            if (bType == SS_BITMAP) 
            {
                DeleteObject(pstat->hImage);
            } 
            else if (bType == SS_ICON) 
            {
                if (pstat->cicur > 1) 
                {
                    //  Kill the animated cursor timer
                    KillTimer(hwnd, IDSYS_STANIMATE);
                }
                DestroyIcon((HICON)(pstat->hImage));
            }
        }

        break;

    }
    case WM_NCCREATE:

        //
        // Allocate the static instance stucture
        //
        pstat = (PSTAT)UserLocalAlloc(HEAP_ZERO_MEMORY, sizeof(STAT));
        if (pstat)
        {
            DWORD dwStyle;
            DWORD dwExStyle;
            BYTE  bType;
        
            //
            // Success... store the instance pointer.
            //
            TraceMsg(TF_STANDARD, "STATIC: Setting static instance pointer.");
            Static_SetPtr(hwnd, pstat);

            pstat->hwnd = hwnd;
            pstat->pww  = (PWW)GetWindowLongPtr(hwnd, GWLP_WOWWORDS);

            dwStyle = GET_STYLE(pstat);
            dwExStyle = GET_EXSTYLE(pstat);
            bType = (BYTE)(dwStyle & SS_TYPEMASK);

            if ((dwExStyle & WS_EX_RIGHT) != 0) 
            {
                AlterWindowStyle(hwnd, SS_TYPEMASK, SS_RIGHT);
            }

            if (dwStyle & SS_SUNKEN ||
                ((bType == LOBYTE(SS_ETCHEDHORZ)) || (bType == LOBYTE(SS_ETCHEDVERT)))) 
            {
                dwExStyle |= WS_EX_STATICEDGE;
                SetWindowLong(hwnd, GWL_EXSTYLE, dwExStyle);
            }

            
            goto CallDWP;
        }
        else
        {
            //
            // Failed... return FALSE.
            //
            // From a WM_NCCREATE msg, this will cause the
            // CreateWindow call to fail.
            //
            TraceMsg(TF_STANDARD, "STATIC: Unable to allocate static instance structure.");
            lReturn = FALSE;
        }

        break;

    case WM_NCDESTROY:
        if ( pstat->hTheme )
        {
            CloseThemeData(pstat->hTheme);
        }

        UserLocalFree(pstat);
        TraceMsg(TF_STANDARD, "STATIC: Clearing static instance pointer.");
        Static_SetPtr(hwnd, NULL);

        break;
    
    case WM_NCHITTEST:
        return (GET_STYLE(pstat) &  SS_NOTIFY) ? HTCLIENT : HTTRANSPARENT;

    case WM_LBUTTONDOWN:
    case WM_NCLBUTTONDOWN:
        if (GET_STYLE(pstat) & SS_NOTIFY) 
        {
            //
            // It is acceptable for an app to destroy a static label
            // in response to a STN_CLICKED notification.
            //
            Static_NotifyParent(hwnd, NULL, STN_CLICKED);
        }
        break;

    case WM_LBUTTONDBLCLK:
    case WM_NCLBUTTONDBLCLK:
        if (GET_STYLE(pstat) & SS_NOTIFY) 
        {
            //
            // It is acceptable for an app to destroy a static label in
            // response to a STN_DBLCLK notification.
            //
            Static_NotifyParent(hwnd, NULL, STN_DBLCLK);
        }
        break;

    case WM_SETTEXT:
    {
        BYTE bType = (BYTE)(GET_STYLE(pstat) & SS_TYPEMASK);

        //
        // No more hack to set icon/bitmap via WM_SETTEXT!
        //
        if (rgstk[bType] & STK_USETEXT) 
        {
            if (DefWindowProc(hwnd, WM_SETTEXT, wParam, lParam)) 
            {
                Static_Repaint(pstat);
                return TRUE;
            }
        }
        break;

    }
    case WM_ENABLE:
        Static_Repaint(pstat);
        if (GET_STYLE(pstat) & SS_NOTIFY) 
        {
            Static_NotifyParent(hwnd, NULL, (wParam ? STN_ENABLE : STN_DISABLE));
        }
        break;

    case WM_GETDLGCODE:
        return (LONG)DLGC_STATIC;

    case WM_SETFONT:
    {
        BYTE bType = (BYTE)(GET_STYLE(pstat) & SS_TYPEMASK);

        //
        // wParam - handle to the font
        // lParam - if true, redraw else don't
        //
        if (rgstk[bType] & STK_USEFONT) 
        {
            pstat->hFont = (HANDLE)wParam;

            if (lParam && IsWindowVisible(hwnd)) 
            {
                InvalidateRect(hwnd, NULL, TRUE);
                UpdateWindow(hwnd);
            }
        }
        break;

    }
    case WM_GETFONT:
    {
        BYTE bType = (BYTE)(GET_STYLE(pstat) & SS_TYPEMASK);

        if (rgstk[bType] & STK_USEFONT) 
        {
            return (LRESULT)pstat->hFont;
        }

        break;

    }
    case WM_TIMER:
        if (wParam == IDSYS_STANIMATE) 
        {
            Static_AniIconStep(pstat);
        }
        break;

    case WM_UPDATEUISTATE:
        {
            DefWindowProc(hwnd, uMsg, wParam, lParam);

            if (HIWORD(wParam) & UISF_HIDEACCEL) 
            {
                BYTE bType = (BYTE)(GET_STYLE(pstat) & SS_TYPEMASK);

                if (ISSSTEXTOROD(bType)) 
                {
                    pstat->fPaintKbdCuesOnly = TRUE;
                    Static_Repaint(pstat);
                    pstat->fPaintKbdCuesOnly = FALSE;
                }
            }
        }
        break;

    case WM_GETOBJECT:

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

        break;

    case WM_THEMECHANGED:

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

        pstat->hTheme = OpenThemeData(pstat->hwnd, L"Static");

        InvalidateRect(pstat->hwnd, NULL, TRUE);

        lReturn = TRUE;

        break;

    default:

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

    return lReturn;
}