//
// MyPrSht.cpp
//
//        Implementation of the extensions made to the PropertySheet API
//        in IE5, but which we need on all platforms.
//
// History:
//
//        10/11/1999  KenSh     Created
//

#include "stdafx.h"
#include "TheApp.h"
#include "MyPrSht.h"
#include "CWnd.h"
#include "unicwrap.h"

// Thunk up to propsheetpage v6 for XP
typedef struct _PROPSHEETPAGEV6W 
{
        DWORD           dwSize;
        DWORD           dwFlags;
        HINSTANCE       hInstance;
        union 
        {
            LPCWSTR     pszTemplate;
#ifdef _WIN32
            LPCDLGTEMPLATE  pResource;
#else
            const VOID *pResource;
#endif
        } DUMMYUNIONNAME;
        union 
        {
            HICON        hIcon;
            LPCWSTR      pszIcon;
        } DUMMYUNIONNAME2;
        LPCWSTR          pszTitle;
        DLGPROC          pfnDlgProc;
        LPARAM           lParam;
        LPFNPSPCALLBACKW pfnCallback;
        UINT             *pcRefParent;

#if (_WIN32_IE >= 0x0400)
        LPCWSTR          pszHeaderTitle;    // this is displayed in the header
        LPCWSTR          pszHeaderSubTitle; ///
#endif
        HANDLE           hActCtx;
} PROPSHEETPAGEV6W, *LPPROPSHEETPAGEV6W;




// Local data
//
static CMyPropSheet* g_pMyPropSheet;

static const TCHAR c_szProp_ClassPointer[] = _T("CP");


#define DEFAULTHEADERHEIGHT    58   // in pixels
#define DEFAULTTEXTDIVIDERGAP  5
#define DEFAULTCTRLWIDTH       501   // page list window in new wizard style
#define DEFAULTCTRLHEIGHT      253   // page list window in new wizard style
#define TITLEX                 22
#define TITLEY                 10
#define SUBTITLEX              44
#define SUBTITLEY              25

// fixed sizes for the bitmap painted in the header section
#define HEADERBITMAP_Y            5
#define HEADERBITMAP_WIDTH        49
#define HEADERBITMAP_CXBACK       (5 + HEADERBITMAP_WIDTH)
#define HEADERBITMAP_HEIGHT       49                
#define HEADERSUBTITLE_WRAPOFFSET 10

// Fixed sizes for the watermark bitmap (Wizard97IE5 style)
#define BITMAP_WIDTH  164
#define BITMAP_HEIGHT 312

#define DRAWTEXT_WIZARD97FLAGS (DT_LEFT | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL)

#define IDD_PAGELIST        0x3020
#define IDD_DIVIDER            0x3026
#define IDD_TOPDIVIDER        0x3027

#ifndef IS_INTRESOURCE
#define IS_INTRESOURCE(psz) (HIWORD((DWORD_PTR)(psz)) == 0)
#endif



/////////////////////////////////////////////////////////////////////////////
// MyPropertySheet

INT_PTR MyPropertySheet(LPCPROPSHEETHEADER pHeader)
{
    // If IE5 is present, use the built-in property sheet code
    // REVIEW: should we bother checking for IE5 on older OS's?
    
    if (theApp.IsWin98SEOrLater() && (! theApp.IsBiDiLocalized()) )
    {
        // BUGBUG THUNK THIS (but which way???)
        return PropertySheet(pHeader);
    }

    // BUGBUG: nobody destroys g_pMyPropSheet, nobody does g_pMyPropSheet->Release()
    ASSERT(g_pMyPropSheet == NULL);
    g_pMyPropSheet = new CMyPropSheet();
    if (g_pMyPropSheet)
        return g_pMyPropSheet->DoPropSheet(pHeader);
    else
        return NULL;
}

/////////////////////////////////////////////////////////////////////////////
// MyCreatePropertySheetPage

HPROPSHEETPAGE MyCreatePropertySheetPage(LPPROPSHEETPAGE ppsp)
{
    // If IE5 is present, use the built-in property sheet code
    // REVIEW: should we bother checking for IE5 on older OS's?

    if (theApp.IsWin98SEOrLater() && (! theApp.IsBiDiLocalized()) )
    {
        if (g_fRunningOnNT)
        {
            PROPSHEETPAGEV6W spv6;

            ASSERT(sizeof (spv6) >= sizeof (PROPSHEETPAGE));
            memcpy(&spv6, ppsp, sizeof (PROPSHEETPAGE));
            
            spv6.dwSize = sizeof (spv6);
            spv6.hActCtx = NULL;

            return CreatePropertySheetPage((PROPSHEETPAGE*) &spv6);
        }
        else
        {
            return CreatePropertySheetPage(ppsp);
        }
    }

    PROPSHEETPAGE psp;
    CopyMemory(&psp, ppsp, ppsp->dwSize);

    // REVIEW: this memory is never freed
    LPPROPSHEETPAGE ppspOriginal = (LPPROPSHEETPAGE)malloc(sizeof(PROPSHEETPAGE));
    if (ppspOriginal)
    {
        CopyMemory(ppspOriginal, ppsp, ppsp->dwSize);

        psp.dwSize = PROPSHEETPAGE_V1_SIZE;
        psp.dwFlags &= ~(PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE | PSP_HIDEHEADER);
        psp.lParam = (LPARAM)ppspOriginal;

        return ::CreatePropertySheetPage(&psp);
    }

    return NULL;
}

/////////////////////////////////////////////////////////////////////////////
// IsIMEWindow

BOOL IsIMEWindow(HWND hwnd, LPCREATESTRUCT lpcs)
{
    // check for cheap CS_IME style first...
    if (GetClassLong(hwnd, GCL_STYLE) & CS_IME)
        return TRUE;

    // get class name of the window that is being created
    LPCTSTR pszClassName;
    TCHAR szClassName[_countof("ime")+1];
    if (HIWORD(lpcs->lpszClass))
    {
        pszClassName = lpcs->lpszClass;
    }
    else
    {
        szClassName[0] = _T('\0');
        GlobalGetAtomName((ATOM)lpcs->lpszClass, szClassName, _countof(szClassName));
        pszClassName = szClassName;
    }

    // a little more expensive to test this way, but necessary...
    if (StrCmpI(pszClassName, _T("ime")) == 0)
        return TRUE;

    return FALSE; // not an IME window
}

void CMyPropSheet::SetHeaderFonts()
{
    if (m_hFontBold == NULL)
    {
        LOGFONT LogFont;
        GetObject(GetWindowFont(m_hWnd), sizeof(LogFont), &LogFont);

        LogFont.lfWeight = FW_BOLD;
        m_hFontBold = CreateFontIndirect(&LogFont);
    }
}

// Kensh: Copied and modified from _ComputeHeaderHeight in prsht.c (comctl32.dll)
//
// In Wizard97 only:
// The subtitles user passed in could be larger than the two line spaces we give
// them, especially in localization cases. So here we go through all subtitles and
// compute the max space they need and set the header height so that no text is clipped
int CMyPropSheet::ComputeHeaderHeight(int dxMax)
{
    SetHeaderFonts();

    int dyHeaderHeight;
    int dyTextDividerGap;
    HDC hdc;
    dyHeaderHeight = DEFAULTHEADERHEIGHT;
    hdc = ::GetDC(m_hWnd);

    // First, let's get the correct text height and spacing, this can be used
    // as the title height and the between-lastline-and-divider spacing.
    {
        HFONT hFont, hFontOld;
        TEXTMETRIC tm;
        if (m_hFontBold)
            hFont = m_hFontBold;
        else
            hFont = GetWindowFont(m_hWnd);

        hFontOld = (HFONT)SelectObject(hdc, hFont);
        if (GetTextMetrics(hdc, &tm))
        {
            dyTextDividerGap = tm.tmExternalLeading;
            m_ySubTitle = max ((tm.tmHeight + tm.tmExternalLeading + TITLEY), SUBTITLEY);
        }
        else
        {
            dyTextDividerGap = DEFAULTTEXTDIVIDERGAP;
            m_ySubTitle = SUBTITLEY;
        }

        if (hFontOld)
            SelectObject(hdc, hFontOld);
    }

    // Second, get the subtitle text block height
    // should make into a function if shared
    {
        RECT rcWrap;
//        UINT uPages;

        //
        //  WIZARD97IE5 subtracts out the space used by the header bitmap.
        //  WIZARD97IE4 uses the full width since the header bitmap
        //  in IE4 is a watermark and occupies no space.
        //
//        if (ppd->psh.dwFlags & PSH_WIZARD97IE4)
//            rcWrap.right = dxMax;
//        else
            rcWrap.right = dxMax - HEADERBITMAP_CXBACK - HEADERSUBTITLE_WRAPOFFSET;

        // Note (kensh): the "real" wizard code computes the max height across
        // all pages. Our cheap version only computes the current page's height
        LPPROPSHEETPAGE ppsp = GetCurrentPropSheetPage();
        if (ppsp != NULL)
        {
            if (!(ppsp->dwFlags & PSP_HIDEHEADER) &&
                 (ppsp->dwFlags & PSP_USEHEADERSUBTITLE))
            {
                int iSubHeaderHeight = WriteHeaderTitle(hdc, &rcWrap, ppsp->pszHeaderSubTitle,
                    FALSE, DT_CALCRECT | DRAWTEXT_WIZARD97FLAGS);
                if ((iSubHeaderHeight + m_ySubTitle) > dyHeaderHeight)
                    dyHeaderHeight = iSubHeaderHeight + m_ySubTitle;
            }
        }
    }

    // If the header height has been recomputed, set the correct gap between
    // the text and the divider.
    if (dyHeaderHeight != DEFAULTHEADERHEIGHT)
    {
        ASSERT(dyHeaderHeight > DEFAULTHEADERHEIGHT);
        dyHeaderHeight += dyTextDividerGap;
    }

    ::ReleaseDC(m_hWnd, hdc);
    return dyHeaderHeight;
}

// Kensh: Copied and modified from _WriteHeaderTitle in prsht.c (comctl32.dll)
//
int CMyPropSheet::WriteHeaderTitle(HDC hdc, LPRECT prc, LPCTSTR pszTitle, BOOL bTitle, DWORD dwDrawFlags)
{
    SetHeaderFonts();

    LPCTSTR pszOut;
    int cch;
    int cx, cy;
    UINT ETO_Flags=0; 
    SIZE Size;
    TCHAR szTitle[MAX_PATH*4];
    HFONT hFontOld = NULL;
    HFONT hFont;
    int yDrawHeight = 0;

    if (IS_INTRESOURCE(pszTitle))
    {
        LPPROPSHEETPAGE ppsp = GetCurrentPropSheetPage();
        if (NULL != ppsp)
        {
            LoadString(ppsp->hInstance, (UINT)LOWORD(pszTitle), szTitle, _countof(szTitle));
        }
        else
        {
            *szTitle = 0;
        }

        pszOut = szTitle;
    }
    else
        pszOut = pszTitle;

    cch = lstrlen(pszOut);

    if (bTitle && m_hFontBold)
        hFont = m_hFontBold;
    else
        hFont = GetWindowFont(m_hWnd);

    hFontOld = (HFONT)SelectObject(hdc, hFont);

    if (bTitle)
    {
          cx = TITLEX;
          cy = TITLEY;

        if (theApp.IsBiDiLocalized())
        {
        ETO_Flags |= ETO_RTLREADING;
        if (GetTextExtentPoint32(hdc, pszOut, lstrlen (pszOut), &Size))
            cx = prc->right - Size.cx;
        }          
        ExtTextOut(hdc, cx, cy, ETO_Flags, prc, pszOut, cch, NULL);
    }
    else
    {
        RECT rcWrap;
        CopyRect(&rcWrap, prc);

        rcWrap.left = SUBTITLEX;
        rcWrap.top = m_ySubTitle;
        if (theApp.IsBiDiLocalized())
        {
          dwDrawFlags |= DT_RTLREADING | DT_RIGHT;
          }
        yDrawHeight = DrawText(hdc, pszOut, cch, &rcWrap, dwDrawFlags);
    }

    if (hFontOld)
        SelectObject(hdc, hFontOld);

    return yDrawHeight;
}

/////////////////////////////////////////////////////////////////////////////
// CMyPropSheet

CMyPropSheet::CMyPropSheet()
{
    m_pRealHeader = NULL;
    m_hHook = NULL;
    m_hbrWindow = NULL;
    m_hbrDialog = NULL;
    m_hwndActive = NULL;
    m_hbmWatermark = NULL;
    m_hbmHeader = NULL;
    m_hpalWatermark = NULL;
    m_hFontBold = NULL;
}

CMyPropSheet::~CMyPropSheet()
{
    free(m_pRealHeader);
    ASSERT(m_hHook == NULL);

    if (m_hbrWindow != NULL)
        DeleteObject(m_hbrWindow);
    if (m_hbrDialog != NULL)
        DeleteObject(m_hbrDialog);
}

void CMyPropSheet::InitColorSettings()
{
    if (m_hbrWindow != NULL)
        DeleteObject(m_hbrWindow);
    m_hbrWindow = CreateSolidBrush(GetSysColor(COLOR_WINDOW));

    if (m_hbrDialog != NULL)
        DeleteObject(m_hbrDialog);
    m_hbrDialog = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
}

void CMyPropSheet::LoadBitmaps()
{
    LPPROPSHEETHEADER ppsh = m_pRealHeader;
    if (ppsh)
    {
        if (ppsh->dwFlags & PSH_USEHBMHEADER)
            m_hbmHeader = ppsh->hbmHeader;
        else
            m_hbmHeader = LoadBitmap(ppsh->hInstance, ppsh->pszbmHeader);

        if (ppsh->dwFlags & PSH_USEHBMWATERMARK)
            m_hbmWatermark = ppsh->hbmWatermark;
        else
            m_hbmWatermark = LoadBitmap(ppsh->hInstance, ppsh->pszbmWatermark);

        // Note: might need a palette later, but so far it hasn't been necessary
    }
}

INT_PTR CMyPropSheet::DoPropSheet(LPCPROPSHEETHEADER ppsh)
{
    INT_PTR nResult = 0;

    ASSERT(m_pRealHeader == NULL);
    m_pRealHeader = (LPPROPSHEETHEADER)malloc(ppsh->dwSize);
    if (m_pRealHeader)
    {
        CopyMemory(m_pRealHeader, ppsh, ppsh->dwSize);

        InitColorSettings();

        // Create header and watermark bitmaps
        LoadBitmaps();

        PROPSHEETHEADER psh;
        ASSERT(sizeof(psh) >= ppsh->dwSize);
        CopyMemory(&psh, ppsh, ppsh->dwSize);

        psh.dwSize = PROPSHEETHEADER_V1_SIZE;
        psh.dwFlags &= 0x00000fff; // W95 gold comctl32 prop sheet mask.
        psh.dwFlags |= PSH_WIZARD;

        ASSERT(m_hHook == NULL);
        m_hHook = SetWindowsHookEx(WH_CBT, HookProc, NULL, GetCurrentThreadId());

        nResult = ::PropertySheet(&psh);

        if (m_hHook != NULL)
        {
            UnhookWindowsHookEx(m_hHook);
            m_hHook = NULL;
        }
    }

    return nResult;
}

LRESULT CALLBACK CMyPropSheet::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    ASSERT(g_pMyPropSheet != NULL);

    LRESULT lResult = CallNextHookEx(g_pMyPropSheet->m_hHook, nCode, wParam, lParam);

    if (nCode == HCBT_CREATEWND)
    {
        HWND hwnd = (HWND)wParam;
        LPCBT_CREATEWND pCbt = (LPCBT_CREATEWND)lParam;

        // Make sure this isn't an IME window
        if (IsIMEWindow(hwnd, pCbt->lpcs))
            goto done;

        if (g_pMyPropSheet->m_hWnd == NULL) // The main wizard window
        {
            // Add the WS_EX_DLGMODALFRAME extended style to the window
            // Remove WS_EX_CONTEXTHELP extended style from the window
            SHSetWindowBits(hwnd, GWL_EXSTYLE, WS_EX_DLGMODALFRAME|WS_EX_CONTEXTHELP, WS_EX_DLGMODALFRAME);

            // Add the WS_SYSMENU style to the window
            SHSetWindowBits(hwnd, GWL_STYLE, WS_SYSMENU, WS_SYSMENU);

            // Subclass the window
            g_pMyPropSheet->Attach(hwnd);
        }
        else if (pCbt->lpcs->hwndParent == g_pMyPropSheet->m_hWnd && 
                 pCbt->lpcs->hMenu == NULL &&
                 (pCbt->lpcs->style & WS_CHILD) == WS_CHILD)
        {
            // It's a wizard page sub-dialog -- subclass it so we can 
            // draw its background
            CMyPropPage* pPropPage = new CMyPropPage;
            if (pPropPage)
            {
                pPropPage->Attach(hwnd);
                pPropPage->Release();
            }
        }
    }
    else if (nCode == HCBT_DESTROYWND)
    {
        HWND hwnd = (HWND)wParam;
        if (hwnd == g_pMyPropSheet->m_hWnd)
        {
            // Main window being destroyed -- stop hooking window creation
            UnhookWindowsHookEx(g_pMyPropSheet->m_hHook);
            g_pMyPropSheet->m_hHook = NULL;
        }
    }

done:
    return lResult;
}

LPPROPSHEETPAGE CMyPropSheet::GetCurrentPropSheetPage()
{
    CMyPropPage* pPage = CMyPropPage::FromHandle(GetActivePage());
    if (pPage)
    {
        return pPage->GetPropSheetPage();
    }
    return NULL;
}

LRESULT CMyPropSheet::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_SETTINGCHANGE:
        {
            InitColorSettings();
            return Default(message, wParam, lParam);
        }

    case PSM_SETCURSEL:
        {
            InvalidateRect(m_hWnd, NULL, TRUE);
            return Default(message, wParam, lParam);
        }
        break;

        /*
    case WM_ERASEBKGND:
        {
            TRACE("Main window - WM_ERASEBKGND - active page = %X\r\n", GetActivePage());
            LPPROPSHEETPAGE ppsp = GetCurrentPropSheetPage();

            HDC hdc = (HDC)wParam;
            RECT rcClient;
            GetClientRect(&rcClient);

            if (ppsp->dwFlags & PSP_HIDEHEADER)
            {
                RECT rcDivider;
                GetDlgItemRect(hwnd, IDD_DIVIDER, &rcDivider);
                rcClient.top = rcDivider.bottom;
                FillRect(hdc, &rcClient, m_hbrDialog);
                rcClient.bottom = rcClient.top;
                rcClient.top = 0;
                FillRect(hdc, &rcClient, m_hbrWindow);
                return TRUE;
            }
            else
            {
                RECT rcHeader;
                CopyRect(&rcHeader, &rcClient);
                rcHeader.bottom = DEFAULTHEADERHEIGHT;
                FillRect(hdc, &rcHeader, m_hbrWindow);
                rcClient.top = rcHeader.bottom;
                FillRect(hdc, &rcClient, m_hbrDialog);
            }
        }
        return FALSE;
        */

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            LPPROPSHEETPAGE ppsp = GetCurrentPropSheetPage();

            HDC hdc = ::BeginPaint(m_hWnd, &ps);

            if (ppsp != NULL)
            {
                if (ppsp->dwFlags & PSP_HIDEHEADER)
                {
                    // Draw the watermark
                    PaintWatermark(hdc, ppsp);
                }
                else
                {
                    // Draw the header
                    PaintHeader(hdc, ppsp);
                }
            }

            ::EndPaint(m_hWnd, &ps);
        }
        break;

    case WM_CTLCOLOREDIT:
    case WM_CTLCOLORDLG:
    case WM_CTLCOLOR:
    case WM_CTLCOLORMSGBOX:
    case WM_CTLCOLORLISTBOX:
    case WM_CTLCOLORBTN:
    case WM_CTLCOLORSCROLLBAR:
    case WM_CTLCOLORSTATIC:
        {
            return (LRESULT)OnCtlColor(message, (HDC)wParam, (HWND)lParam);
        }

    default:
        {
            return Default(message, wParam, lParam);
        }
    }

    return 0;
}

HBRUSH CMyPropSheet::OnCtlColor(UINT message, HDC hdc, HWND hwndControl)
{
    HBRUSH hbr = (HBRUSH)Default(message, (WPARAM)hdc, (LPARAM)hwndControl);

    if (message == WM_CTLCOLOREDIT || message == WM_CTLCOLORDLG)
        return hbr;

    SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
    SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
    return m_hbrWindow;
}

//
//  lprc is the target rectangle.
//  Use as much of the bitmap as will fit into the target rectangle.
//  If the bitmap is smaller than the target rectangle, then fill the rest with
//  the pixel in the upper left corner of the hbmpPaint.
//
void PaintWithPaletteBitmap(HDC hdc, LPRECT lprc, HPALETTE hplPaint, HBITMAP hbmpPaint)
{
    HDC hdcBmp = CreateCompatibleDC(hdc);
    if (hdcBmp)
    {
        BITMAP bm;
        int cxRect, cyRect, cxBmp, cyBmp;

        GetObject(hbmpPaint, sizeof(BITMAP), &bm);
        SelectObject(hdcBmp, hbmpPaint);

        if (hplPaint)
        {
            SelectPalette(hdc, hplPaint, FALSE);
            RealizePalette(hdc);
        }

        cxRect = RECTWIDTH(*lprc);
        cyRect = RECTHEIGHT(*lprc);

        //  Never use more pixels from the bmp as we have room in the rect.
        cxBmp = min(bm.bmWidth, cxRect);
        cyBmp = min(bm.bmHeight, cyRect);

        BitBlt(hdc, lprc->left, lprc->top, cxBmp, cyBmp, hdcBmp, 0, 0, SRCCOPY);

        // If bitmap is too narrow, then StretchBlt to fill the width.
        if (cxBmp < cxRect)
            StretchBlt(hdc, lprc->left + cxBmp, lprc->top,
                       cxRect - cxBmp, cyBmp,
                       hdcBmp, 0, 0, 1, 1, SRCCOPY);

        // If bitmap is to short, then StretchBlt to fill the height.
        if (cyBmp < cyRect)
            StretchBlt(hdc, lprc->left, cyBmp,
                       cxRect, cyRect - cyBmp,
                       hdcBmp, 0, 0, 1, 1, SRCCOPY);

        DeleteDC(hdcBmp);
    }
}

void CMyPropSheet::PaintWatermark(HDC hdc, LPPROPSHEETPAGE ppsp)
{
    RECT rcClient;
    RECT rcClient_Dlg;

    GetClientRect(m_hWnd, &rcClient);
    GetClientRect(m_hWnd, &rcClient_Dlg);

    RECT rcDivider;
    GetDlgItemRect(m_hWnd, IDD_DIVIDER, &rcDivider);

    if (m_hbmWatermark)
    {
        // Bottom gets gray
        rcClient.top = rcDivider.bottom;
        FillRect(hdc, &rcClient, m_hbrDialog);
        rcClient.bottom = rcClient.top;
        rcClient.top = 0;
        // Right-hand side gets m_hbrWindow.
        
        if (theApp.IsBiDiLocalized())
          rcClient.right = rcClient_Dlg.right - BITMAP_WIDTH;
        else
          rcClient.left = BITMAP_WIDTH;
        FillRect(hdc, &rcClient, m_hbrWindow);
        // Left-hand side gets watermark in top portion with autofill...
        if (theApp.IsBiDiLocalized())
            {
            rcClient.right = rcClient_Dlg.right;
            rcClient.left = rcClient_Dlg.right - BITMAP_WIDTH;
            }
        else    
            {
            rcClient.right = rcClient.left;
            rcClient.left = 0;
            }

        PaintWithPaletteBitmap(hdc, &rcClient, m_hpalWatermark, m_hbmWatermark);
    }
}

void CMyPropSheet::PaintHeader(HDC hdc, LPPROPSHEETPAGE ppsp)
{
    RECT rcClient, rcHeaderBitmap;
    GetClientRect(m_hWnd, &rcClient);
    int cyHeader = ComputeHeaderHeight(rcClient.right);

    // Bottom gets gray
    rcClient.top = cyHeader;
    FillRect(hdc, &rcClient, m_hbrDialog);

    // Top gets white
    rcClient.bottom = rcClient.top;
    rcClient.top = 0;
    FillRect(hdc, &rcClient, m_hbrWindow);

    // Draw the fixed-size header bitmap
    int bx= RECTWIDTH(rcClient) - HEADERBITMAP_CXBACK;
    ASSERT(bx > 0);
    SetRect(&rcHeaderBitmap, bx, HEADERBITMAP_Y, bx + HEADERBITMAP_WIDTH, HEADERBITMAP_Y + HEADERBITMAP_HEIGHT);
    PaintWithPaletteBitmap(hdc, &rcHeaderBitmap, m_hpalWatermark, m_hbmHeader);

    // Draw header title & subtitle
    rcClient.right = bx - HEADERSUBTITLE_WRAPOFFSET;
    WriteHeaderTitle(hdc, &rcClient, ppsp->pszHeaderTitle, TRUE, DRAWTEXT_WIZARD97FLAGS);
    WriteHeaderTitle(hdc, &rcClient, ppsp->pszHeaderSubTitle, FALSE, DRAWTEXT_WIZARD97FLAGS);
}


/////////////////////////////////////////////////////////////////////////////
// CMyPropPage

CMyPropPage* CMyPropPage::FromHandle(HWND hwnd)
{
    return (CMyPropPage*)(CWnd::FromHandle(hwnd));
}

LPPROPSHEETPAGE CMyPropPage::GetPropSheetPage()
{
    return m_ppspOriginal;
}

LRESULT CMyPropPage::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_INITDIALOG:
        {
            LPPROPSHEETPAGE ppspBogus = (LPPROPSHEETPAGE)lParam;
            LPPROPSHEETPAGE ppspOriginal = (LPPROPSHEETPAGE) ppspBogus->lParam;
            m_ppspOriginal = ppspOriginal;
            lParam = (LPARAM)ppspOriginal;
        }
        break;

    case WM_ERASEBKGND:
        {
            if ((m_ppspOriginal->dwFlags & PSP_HIDEHEADER) != 0)
            {
                // Let the parent window bleed through
                return FALSE;
            }
        }
        break;

    case WM_CTLCOLORSTATIC:
        {
            if ((m_ppspOriginal->dwFlags & PSP_HIDEHEADER) != 0)
            {
                return (LRESULT)g_pMyPropSheet->OnCtlColor(message, (HDC)wParam, (HWND)lParam);
            }
        }
        break;

    case WM_NOTIFY:
        {
            NMHDR* pHdr = (NMHDR*)lParam;
            switch (pHdr->code)
            {
            case PSN_KILLACTIVE:
                {
//                    TRACE("PSN_KILLACTIVE - hwnd = %X\r\n", hwnd);
                }
                break;

            case PSN_SETACTIVE:
                {
//                    TRACE("PSN_SETACTIVE - hwnd = %X\r\n", hwnd);

                    HWND hwndParent = GetParent(m_hWnd);

                    RECT rcParent;
                    ::GetClientRect(hwndParent, &rcParent);

                    RECT rcTopDivider;
                    HWND hwndTopDivider = GetDlgItemRect(hwndParent, IDD_TOPDIVIDER, &rcTopDivider);


                    // Hide the tab control (not sure why it's showing up, but it shouldn't)
                    ShowWindow(::GetDlgItem(hwndParent, IDD_PAGELIST), SW_HIDE);

                    RECT rcDivider;
                    HWND hwndDivider = GetDlgItemRect(hwndParent, IDD_DIVIDER, &rcDivider);
                    
                    // Set the proper size and position for the dialog
                    if ((m_ppspOriginal->dwFlags & PSP_HIDEHEADER) != 0)
                    {
                        // Reposition the divider
                        SetWindowPos(hwndDivider, NULL, 0, rcDivider.top, rcParent.right, RECTHEIGHT(rcDivider),
                                     SWP_NOZORDER | SWP_NOACTIVATE);

                        // Hide the top divider
                        if (hwndTopDivider != NULL)
                            ShowWindow(hwndTopDivider, SW_HIDE);

                        // Reposition the dialog

                        SetWindowPos(m_hWnd, NULL, rcParent.left, rcParent.top, RECTWIDTH(rcParent), rcDivider.top - rcParent.top,
                                     SWP_NOZORDER | SWP_NOACTIVATE);
                        

                    }
                    else
                    {
                        int cyHeader = g_pMyPropSheet->ComputeHeaderHeight(rcParent.right);


                        // Reposition and show the top divider
                        if (hwndTopDivider != NULL)
                        {
                            SetWindowPos(hwndTopDivider, NULL, 0, cyHeader, rcParent.right, RECTHEIGHT(rcTopDivider),
                                         SWP_NOZORDER | SWP_NOACTIVATE);
                            ShowWindow(hwndTopDivider, SW_SHOW);
                        }

                        // Reposition the dialog
                        SetWindowPos(m_hWnd, NULL, rcParent.left + 7, cyHeader + 7, RECTWIDTH(rcParent) - 14, rcDivider.top - cyHeader - 14,
                                     SWP_NOZORDER | SWP_NOACTIVATE);
                    }
                    g_pMyPropSheet->OnSetActivePage(m_hWnd);
                }
                break;
            }
        }
        break;

    default:
        break;
    }

    return CWnd::Default(message, wParam, lParam);
}