// ResizableSheetEx.cpp : implementation file
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ResizableSheetEx.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CResizableSheetEx
IMPLEMENT_DYNAMIC(CResizableSheetEx, CPropertySheetEx)
inline void CResizableSheetEx::Construct()
{
m_bInitDone = FALSE;
m_bEnableSaveRestore = FALSE;
m_bSavePage = FALSE;
}
CResizableSheetEx::CResizableSheetEx()
{
Construct();
}
CResizableSheetEx::CResizableSheetEx(UINT nIDCaption, CWnd* pParentWnd,
UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
HBITMAP hbmHeader)
: CPropertySheetEx(nIDCaption, pParentWnd, iSelectPage,
hbmWatermark, hpalWatermark, hbmHeader)
{
Construct();
}
CResizableSheetEx::CResizableSheetEx(LPCTSTR pszCaption, CWnd* pParentWnd,
UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
HBITMAP hbmHeader)
: CPropertySheetEx(pszCaption, pParentWnd, iSelectPage,
hbmWatermark, hpalWatermark, hbmHeader)
{
Construct();
}
CResizableSheetEx::~CResizableSheetEx()
{
}
BEGIN_MESSAGE_MAP(CResizableSheetEx, CPropertySheetEx)
//{{AFX_MSG_MAP(CResizableSheetEx)
ON_WM_GETMINMAXINFO()
ON_WM_SIZE()
ON_WM_DESTROY()
ON_WM_CREATE()
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
ON_NOTIFY_REFLECT_EX(PSN_SETACTIVE, OnPageChanging)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CResizableSheetEx message handlers
int CResizableSheetEx::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CPropertySheetEx::OnCreate(lpCreateStruct) == -1)
return -1;
// keep client area
CRect rect;
GetClientRect(&rect);
// set resizable style
ModifyStyle(DS_MODALFRAME, WS_POPUP | WS_THICKFRAME);
// adjust size to reflect new style
::AdjustWindowRectEx(&rect, GetStyle(),
::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(), SWP_FRAMECHANGED|
SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREPOSITION);
if (!InitGrip())
return -1;
return 0;
}
BOOL CResizableSheetEx::OnInitDialog()
{
BOOL bResult = CPropertySheetEx::OnInitDialog();
// set the initial size as the min track size
CRect rc;
GetWindowRect(&rc);
SetMinTrackSize(rc.Size());
// init
UpdateGripPos();
PresetLayout();
// prevent flickering
GetTabControl()->ModifyStyle(0, WS_CLIPSIBLINGS);
m_bInitDone = TRUE;
ArrangeLayout();
return bResult;
}
void CResizableSheetEx::OnDestroy()
{
if (m_bEnableSaveRestore)
{
SaveWindowRect(m_sSection, m_bRectOnly);
SavePage();
}
RemoveAllAnchors();
CPropertySheetEx::OnDestroy();
}
// maps an index to a button ID and vice-versa
static UINT _propButtons[] =
{
IDOK, IDCANCEL, ID_APPLY_NOW, IDHELP,
ID_WIZBACK, ID_WIZNEXT, ID_WIZFINISH
};
// horizontal line in wizard mode
#define ID_WIZLINE ID_WIZFINISH+1
#define ID_WIZLINEHDR ID_WIZFINISH+2
void CResizableSheetEx::PresetLayout()
{
if (IsWizard() || IsWizard97()) // wizard mode
{
// hide tab control
GetTabControl()->ShowWindow(SW_HIDE);
AddAnchor(ID_WIZLINE, BOTTOM_LEFT, BOTTOM_RIGHT);
if (IsWizard97()) // add header line for wizard97 dialogs
AddAnchor(ID_WIZLINEHDR, TOP_LEFT, TOP_RIGHT);
}
else // tab mode
{
AddAnchor(AFX_IDC_TAB_CONTROL, TOP_LEFT, BOTTOM_RIGHT);
}
// add a callback for active page (which can change at run-time)
AddAnchorCallback(1);
// use *total* parent size to have correct margins
CRect rectPage, rectSheet;
GetTotalClientRect(&rectSheet);
GetActivePage()->GetWindowRect(&rectPage);
ScreenToClient(&rectPage);
// pre-calculate margins
m_sizePageTL = rectPage.TopLeft() - rectSheet.TopLeft();
m_sizePageBR = rectPage.BottomRight() - rectSheet.BottomRight();
// add all possible buttons, if they exist
for (int i = 0; i < 7; i++)
{
if (NULL != GetDlgItem(_propButtons[i]))
AddAnchor(_propButtons[i], BOTTOM_RIGHT);
}
}
BOOL CResizableSheetEx::ArrangeLayoutCallback(LayoutInfo &layout)
{
if (layout.nCallbackID != 1) // we only added 1 callback
return CResizableLayout::ArrangeLayoutCallback(layout);
// set layout info for active page
layout.hWnd = GetActivePage()->GetSafeHwnd();
// set margins
if (IsWizard()) // wizard mode
{
// use pre-calculated margins
layout.sizeMarginTL = m_sizePageTL;
layout.sizeMarginBR = m_sizePageBR;
}
else if (IsWizard97()) // wizard 97
{
// use pre-calculated margins
layout.sizeMarginTL = m_sizePageTL;
layout.sizeMarginBR = m_sizePageBR;
if (!(GetActivePage()->m_psp.dwFlags & PSP_HIDEHEADER))
{
// add header vertical offset
CRect rectLine;
GetDlgItem(ID_WIZLINEHDR)->GetWindowRect(&rectLine);
ScreenToClient(&rectLine);
layout.sizeMarginTL.cy = rectLine.bottom;
}
}
else // tab mode
{
CRect rectPage, rectSheet;
GetTotalClientRect(&rectSheet);
CTabCtrl* pTab = GetTabControl();
pTab->GetWindowRect(&rectPage);
pTab->AdjustRect(FALSE, &rectPage);
ScreenToClient(&rectPage);
// use tab control
layout.sizeMarginTL = rectPage.TopLeft() - rectSheet.TopLeft();
layout.sizeMarginBR = rectPage.BottomRight() - rectSheet.BottomRight();
}
// set anchor types
layout.sizeTypeTL = TOP_LEFT;
layout.sizeTypeBR = BOTTOM_RIGHT;
// use this layout info
return TRUE;
}
void CResizableSheetEx::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
if (nType == SIZE_MAXHIDE || nType == SIZE_MAXSHOW)
return; // arrangement not needed
if (m_bInitDone)
{
// update size-grip
UpdateGripPos();
ArrangeLayout();
}
}
BOOL CResizableSheetEx::OnPageChanging(NMHDR* /*pNotifyStruct*/, LRESULT* /*pResult*/)
{
// update new wizard page
// active page changes after this notification
PostMessage(WM_SIZE);
return FALSE; // continue routing
}
BOOL CResizableSheetEx::OnEraseBkgnd(CDC* pDC)
{
ClipChildren(pDC);
return CPropertySheetEx::OnEraseBkgnd(pDC);
}
void CResizableSheetEx::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
if (!m_bInitDone)
return;
MinMaxInfo(lpMMI);
}
// protected members
int CResizableSheetEx::GetMinWidth()
{
CWnd* pWnd = NULL;
CRect rectWnd, rectSheet;
GetTotalClientRect(&rectSheet);
int max = 0, min = rectSheet.Width();
// search for leftmost and rightmost button margins
for (int i = 0; i < 7; i++)
{
pWnd = GetDlgItem(_propButtons[i]);
// exclude not present or hidden buttons
if (pWnd == NULL || !(pWnd->GetStyle() & WS_VISIBLE))
continue;
// left position is relative to the right border
// of the parent window (negative value)
pWnd->GetWindowRect(&rectWnd);
ScreenToClient(&rectWnd);
int left = rectSheet.right - rectWnd.left;
int right = rectSheet.right - rectWnd.right;
if (left > max)
max = left;
if (right < min)
min = right;
}
// sizing border width
int border = GetSystemMetrics(SM_CXSIZEFRAME);
// compute total width
return max + min + 2*border;
}
// NOTE: this must be called after all the other settings
// to have the window and its controls displayed properly
void CResizableSheetEx::EnableSaveRestore(LPCTSTR pszSection, BOOL bRectOnly, BOOL bWithPage)
{
m_sSection = pszSection;
m_bSavePage = bWithPage;
m_bEnableSaveRestore = TRUE;
m_bRectOnly = bRectOnly;
// restore immediately
LoadWindowRect(pszSection, bRectOnly);
LoadPage();
}
// private memebers
// used to save/restore active page
// either in the registry or a private .INI file
// depending on your application settings
#define ACTIVEPAGE _T("ActivePage")
void CResizableSheetEx::SavePage()
{
if (!m_bSavePage)
return;
// saves active page index, zero (the first) if problems
// cannot use GetActivePage, because it always fails
CTabCtrl *pTab = GetTabControl();
int page = 0;
if (pTab != NULL)
page = pTab->GetCurSel();
if (page < 0)
page = 0;
AfxGetApp()->WriteProfileInt(m_sSection, ACTIVEPAGE, page);
}
void CResizableSheetEx::LoadPage()
{
// restore active page, zero (the first) if not found
int page = AfxGetApp()->GetProfileInt(m_sSection, ACTIVEPAGE, 0);
if (m_bSavePage)
{
SetActivePage(page);
ArrangeLayout(); // needs refresh
}
}