1 // ResizableSheetEx.cpp : implementation file
3 /////////////////////////////////////////////////////////////////////////////
5 // This file is part of ResizableLib
6 // http://sourceforge.net/projects/resizablelib
8 // Copyright (C) 2000-2004 by Paolo Messina
9 // http://www.geocities.com/ppescher - mailto:ppescher@hotmail.com
11 // The contents of this file are subject to the Artistic License (the "License").
12 // You may not use this file except in compliance with the License.
13 // You may obtain a copy of the License at:
14 // http://www.opensource.org/licenses/artistic-license.html
16 // If you find this code useful, credits would be nice!
18 /////////////////////////////////////////////////////////////////////////////
21 #include "ResizableSheetEx.h"
26 static char THIS_FILE
[] = __FILE__
;
29 /////////////////////////////////////////////////////////////////////////////
32 IMPLEMENT_DYNAMIC(CResizableSheetEx
, CPropertySheetEx
)
34 inline BOOL
CResizableSheetEx::IsWizard() const
36 return (m_psh
.dwFlags
& PSH_WIZARD
);
39 inline BOOL
CResizableSheetEx::IsWizard97() const
41 return (m_psh
.dwFlags
& (PSH_IE4WIZARD97
| PSH_IE5WIZARD97
));
44 CResizableSheetEx::CResizableSheetEx()
48 CResizableSheetEx::CResizableSheetEx(UINT nIDCaption
, CWnd
* pParentWnd
,
49 UINT iSelectPage
, HBITMAP hbmWatermark
, HPALETTE hpalWatermark
,
51 : CPropertySheetEx(nIDCaption
, pParentWnd
, iSelectPage
,
52 hbmWatermark
, hpalWatermark
, hbmHeader
)
56 CResizableSheetEx::CResizableSheetEx(LPCTSTR pszCaption
, CWnd
* pParentWnd
,
57 UINT iSelectPage
, HBITMAP hbmWatermark
, HPALETTE hpalWatermark
,
59 : CPropertySheetEx(pszCaption
, pParentWnd
, iSelectPage
,
60 hbmWatermark
, hpalWatermark
, hbmHeader
)
65 CResizableSheetEx::~CResizableSheetEx()
69 BEGIN_MESSAGE_MAP(CResizableSheetEx
, CPropertySheetEx
)
70 //{{AFX_MSG_MAP(CResizableSheetEx)
77 ON_NOTIFY_REFLECT_EX(PSN_SETACTIVE
, OnPageChanging
)
78 ON_REGISTERED_MESSAGE(WMU_RESIZESUPPORT
, OnResizeSupport
)
81 /////////////////////////////////////////////////////////////////////////////
82 // CResizableSheetEx message handlers
84 BOOL
CResizableSheetEx::OnNcCreate(LPCREATESTRUCT lpCreateStruct
)
86 if (!CPropertySheetEx::OnNcCreate(lpCreateStruct
))
89 // child dialogs don't want resizable border or size grip,
90 // nor they can handle the min/max size constraints
91 BOOL bChild
= lpCreateStruct
->style
& WS_CHILD
;
93 // create and init the size-grip
94 if (!CreateSizeGrip(!bChild
))
97 MakeResizable(lpCreateStruct
);
102 BOOL
CResizableSheetEx::OnInitDialog()
104 BOOL bResult
= CPropertySheetEx::OnInitDialog();
106 // set the initial size as the min track size
109 SetMinTrackSize(rc
.Size());
113 m_bLayoutDone
= TRUE
;
118 LRESULT
CResizableSheetEx::OnResizeSupport(WPARAM wParam
, LPARAM lParam
)
122 case RSZSUP_SHEETPAGEEXHACK
:
124 // a window object must be still associated to the page handle
125 // but MFC subclassing has been turned off to allow the system
126 // to subclass it first, so we can catch all the messages
127 CWnd
* pWnd
= CWnd::FromHandlePermanent((HWND
)lParam
);
131 // suclass the window again and refresh page and sheet
132 pWnd
->SubclassWindow(pWnd
->Detach());
134 pWnd
->SendMessage(WM_SIZE
);
136 UnlockWindowUpdate();
138 if (pWnd
->IsWindowVisible())
140 // send lost PSN_SETACTIVE notification message
141 CPropertyPage
* pPage
= DYNAMIC_DOWNCAST(CPropertyPage
, pWnd
);
143 SetActivePage(pPage
);
154 void CResizableSheetEx::OnDestroy()
156 if (m_bEnableSaveRestore
)
158 SaveWindowRect(m_sSection
, m_bRectOnly
);
160 SavePage(m_sSection
);
165 CPropertySheetEx::OnDestroy();
168 // maps an index to a button ID and vice-versa
169 static UINT _propButtons
[] =
171 IDOK
, IDCANCEL
, ID_APPLY_NOW
, IDHELP
,
172 ID_WIZBACK
, ID_WIZNEXT
, ID_WIZFINISH
174 const int _propButtonsCount
= sizeof(_propButtons
)/sizeof(UINT
);
176 // horizontal line in wizard mode
177 #define ID_WIZLINE ID_WIZFINISH+1
178 #define ID_WIZLINEHDR ID_WIZFINISH+2
180 void CResizableSheetEx::PresetLayout()
182 if (IsWizard() || IsWizard97()) // wizard mode
185 GetTabControl()->ShowWindow(SW_HIDE
);
187 AddAnchor(ID_WIZLINE
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
189 if (IsWizard97()) // add header line for wizard97 dialogs
190 AddAnchor(ID_WIZLINEHDR
, TOP_LEFT
, TOP_RIGHT
);
194 AddAnchor(AFX_IDC_TAB_CONTROL
, TOP_LEFT
, BOTTOM_RIGHT
);
197 // add a callback for active page (which can change at run-time)
198 m_nCallbackID
= AddAnchorCallback();
200 // use *total* parent size to have correct margins
201 CRect rectPage
, rectSheet
;
202 GetTotalClientRect(&rectSheet
);
204 GetActivePage()->GetWindowRect(&rectPage
);
205 ::MapWindowPoints(NULL
, m_hWnd
, (LPPOINT
)&rectPage
, 2);
207 // pre-calculate margins
208 m_sizePageTL
= rectPage
.TopLeft() - rectSheet
.TopLeft();
209 m_sizePageBR
= rectPage
.BottomRight() - rectSheet
.BottomRight();
211 // add all possible buttons, if they exist
212 for (int i
= 0; i
< _propButtonsCount
; i
++)
214 if (NULL
!= GetDlgItem(_propButtons
[i
]))
215 AddAnchor(_propButtons
[i
], BOTTOM_RIGHT
);
219 BOOL
CResizableSheetEx::ArrangeLayoutCallback(LAYOUTINFO
&layout
) const
221 if (layout
.nCallbackID
!= m_nCallbackID
) // we only added 1 callback
222 return CResizableLayout::ArrangeLayoutCallback(layout
);
224 // set layout info for active page
225 layout
.hWnd
= (HWND
)::SendMessage(GetSafeHwnd(), PSM_GETCURRENTPAGEHWND
, 0, 0);
226 if (!::IsWindow(layout
.hWnd
))
230 if (IsWizard()) // wizard mode
232 // use pre-calculated margins
233 layout
.marginTopLeft
= m_sizePageTL
;
234 layout
.marginBottomRight
= m_sizePageBR
;
236 else if (IsWizard97()) // wizard 97
238 // use pre-calculated margins
239 layout
.marginTopLeft
= m_sizePageTL
;
240 layout
.marginBottomRight
= m_sizePageBR
;
242 if (!(GetActivePage()->m_psp
.dwFlags
& PSP_HIDEHEADER
))
244 // add header vertical offset
245 CRect rectLine
, rectSheet
;
246 GetTotalClientRect(&rectSheet
);
247 GetAnchorPosition(ID_WIZLINEHDR
, rectSheet
, rectLine
);
249 layout
.marginTopLeft
.cy
= rectLine
.bottom
;
254 CTabCtrl
* pTab
= GetTabControl();
255 ASSERT(pTab
!= NULL
);
257 // get tab position after resizing and calc page rect
258 CRect rectPage
, rectSheet
;
259 GetTotalClientRect(&rectSheet
);
261 if (!GetAnchorPosition(pTab
->m_hWnd
, rectSheet
, rectPage
))
262 return FALSE
; // no page yet
264 // temporarily resize the tab control to calc page size
266 pTab
->GetWindowRect(rectSave
);
267 ::MapWindowPoints(NULL
, m_hWnd
, (LPPOINT
)&rectSave
, 2);
268 pTab
->SetRedraw(FALSE
);
269 pTab
->MoveWindow(rectPage
, FALSE
);
270 pTab
->AdjustRect(FALSE
, &rectPage
);
271 pTab
->MoveWindow(rectSave
, FALSE
);
272 pTab
->SetRedraw(TRUE
);
275 layout
.marginTopLeft
= rectPage
.TopLeft() - rectSheet
.TopLeft();
276 layout
.marginBottomRight
= rectPage
.BottomRight() - rectSheet
.BottomRight();
280 layout
.anchorTopLeft
= TOP_LEFT
;
281 layout
.anchorBottomRight
= BOTTOM_RIGHT
;
283 // use this layout info
287 void CResizableSheetEx::OnSize(UINT nType
, int cx
, int cy
)
289 CWnd::OnSize(nType
, cx
, cy
);
291 if (nType
== SIZE_MAXHIDE
|| nType
== SIZE_MAXSHOW
)
292 return; // arrangement not needed
294 if (nType
== SIZE_MAXIMIZED
)
295 HideSizeGrip(&m_dwGripTempState
);
297 ShowSizeGrip(&m_dwGripTempState
);
299 // update grip and layout
305 // refresh header area
308 InvalidateRect(rect
, FALSE
);
312 BOOL
CResizableSheetEx::OnPageChanging(NMHDR
* /*pNotifyStruct*/, LRESULT
* /*pResult*/)
314 // update new wizard page
315 // active page changes after this notification
316 PostMessage(WM_SIZE
);
318 return FALSE
; // continue routing
321 BOOL
CResizableSheetEx::OnEraseBkgnd(CDC
* pDC
)
323 if (ClipChildren(pDC
, FALSE
))
325 // when clipping, remove header from clipping area
328 // clip header area out
331 pDC
->ExcludeClipRect(rect
);
335 BOOL bRet
= CPropertySheetEx::OnEraseBkgnd(pDC
);
337 ClipChildren(pDC
, TRUE
);
342 BOOL
CResizableSheetEx::CalcSizeExtra(HWND
/*hWndChild*/, CSize sizeChild
, CSize
&sizeExtra
)
344 CTabCtrl
* pTab
= GetTabControl();
348 // get margins of tabcontrol
350 if (!GetAnchorMargins(pTab
->m_hWnd
, sizeChild
, rectMargins
))
353 // get margin caused by tabcontrol
354 CRect
rectTabMargins(0,0,0,0);
356 // get tab position after resizing and calc page rect
357 CRect rectPage
, rectSheet
;
358 GetTotalClientRect(&rectSheet
);
360 if (!GetAnchorPosition(pTab
->m_hWnd
, rectSheet
, rectPage
))
361 return FALSE
; // no page yet
363 // temporarily resize the tab control to calc page size
365 pTab
->GetWindowRect(rectSave
);
366 ::MapWindowPoints(NULL
, m_hWnd
, (LPPOINT
)&rectSave
, 2);
367 pTab
->SetRedraw(FALSE
);
368 pTab
->MoveWindow(rectPage
, FALSE
);
369 pTab
->AdjustRect(TRUE
, &rectTabMargins
);
370 pTab
->MoveWindow(rectSave
, FALSE
);
371 pTab
->SetRedraw(TRUE
);
373 // add non-client size
374 ::AdjustWindowRectEx(&rectTabMargins
, GetStyle(), !(GetStyle() & WS_CHILD
) &&
375 ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
377 // compute extra size
378 sizeExtra
= rectMargins
.TopLeft() + rectMargins
.BottomRight() +
379 rectTabMargins
.Size();
383 void CResizableSheetEx::OnGetMinMaxInfo(MINMAXINFO FAR
* lpMMI
)
387 CTabCtrl
* pTab
= GetTabControl();
391 int nCount
= GetPageCount();
392 for (int idx
= 0; idx
< nCount
; ++idx
)
394 if (IsWizard()) // wizard mode
396 // use pre-calculated margins
397 CRect
rectExtra(-CPoint(m_sizePageTL
), -CPoint(m_sizePageBR
));
398 // add non-client size
399 ::AdjustWindowRectEx(&rectExtra
, GetStyle(), !(GetStyle() & WS_CHILD
) &&
400 ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
401 ChainMinMaxInfo(lpMMI
, *GetPage(idx
), rectExtra
.Size());
403 else if (IsWizard97()) // wizard 97
405 // use pre-calculated margins
406 CRect
rectExtra(-CPoint(m_sizePageTL
), -CPoint(m_sizePageBR
));
408 if (!(GetPage(idx
)->m_psp
.dwFlags
& PSP_HIDEHEADER
))
410 // add header vertical offset
411 CRect rectLine
, rectSheet
;
412 GetTotalClientRect(&rectSheet
);
413 GetAnchorPosition(ID_WIZLINEHDR
, rectSheet
, rectLine
);
415 rectExtra
.top
= -rectLine
.bottom
;
417 // add non-client size
418 ::AdjustWindowRectEx(&rectExtra
, GetStyle(), !(GetStyle() & WS_CHILD
) &&
419 ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
420 ChainMinMaxInfo(lpMMI
, *GetPage(idx
), rectExtra
.Size());
424 ChainMinMaxInfoCB(lpMMI
, *GetPage(idx
));
431 void CResizableSheetEx::GetHeaderRect(LPRECT lpRect
)
433 CWnd
* pWizLineHdr
= GetDlgItem(ID_WIZLINEHDR
);
434 if (pWizLineHdr
!= NULL
&& pWizLineHdr
->IsWindowVisible())
436 pWizLineHdr
->GetWindowRect(lpRect
);
437 ::MapWindowPoints(NULL
, m_hWnd
, (LPPOINT
)lpRect
, 2);
438 LONG bottom
= lpRect
->top
;
439 GetClientRect(lpRect
);
440 lpRect
->bottom
= bottom
;
443 ::SetRectEmpty(lpRect
);
446 int CResizableSheetEx::GetMinWidth()
449 CRect rectWnd
, rectSheet
;
450 GetTotalClientRect(&rectSheet
);
452 int max
= 0, min
= rectSheet
.Width();
453 // search for leftmost and rightmost button margins
454 for (int i
= 0; i
< 7; i
++)
456 pWnd
= GetDlgItem(_propButtons
[i
]);
457 // exclude not present or hidden buttons
458 if (pWnd
== NULL
|| !(pWnd
->GetStyle() & WS_VISIBLE
))
461 // left position is relative to the right border
462 // of the parent window (negative value)
463 pWnd
->GetWindowRect(&rectWnd
);
464 ::MapWindowPoints(NULL
, m_hWnd
, (LPPOINT
)&rectWnd
, 2);
465 int left
= rectSheet
.right
- rectWnd
.left
;
466 int right
= rectSheet
.right
- rectWnd
.right
;
474 // sizing border width
475 int border
= GetSystemMetrics(SM_CXSIZEFRAME
);
477 // compute total width
478 return max
+ min
+ 2*border
;
481 // NOTE: this must be called after all the other settings
482 // to have the window and its controls displayed properly
483 void CResizableSheetEx::EnableSaveRestore(LPCTSTR pszSection
, BOOL bRectOnly
, BOOL bWithPage
, BOOL bHorzResize
, BOOL bVertResize
)
485 m_sSection
= pszSection
;
486 m_bSavePage
= bWithPage
;
488 m_bEnableSaveRestore
= TRUE
;
489 m_bRectOnly
= bRectOnly
;
491 // restore immediately
492 LoadWindowRect(pszSection
, bRectOnly
, bHorzResize
, bVertResize
);
495 LoadPage(pszSection
);
496 ArrangeLayout(); // needs refresh
500 void CResizableSheetEx::RefreshLayout()
502 SendMessage(WM_SIZE
);
505 LRESULT
CResizableSheetEx::WindowProc(UINT message
, WPARAM wParam
, LPARAM lParam
)
507 if (message
!= WM_NCCALCSIZE
|| wParam
== 0 || !m_bLayoutDone
)
508 return CPropertySheetEx::WindowProc(message
, wParam
, lParam
);
510 // specifying valid rects needs controls already anchored
512 HandleNcCalcSize(FALSE
, (LPNCCALCSIZE_PARAMS
)lParam
, lResult
);
513 lResult
= CPropertySheetEx::WindowProc(message
, wParam
, lParam
);
514 HandleNcCalcSize(TRUE
, (LPNCCALCSIZE_PARAMS
)lParam
, lResult
);
518 int CALLBACK
CResizableSheetEx::XmnPropSheetCallback(HWND hWnd
, UINT message
, LPARAM lParam
)
520 extern int CALLBACK
AfxPropSheetCallback(HWND
, UINT message
, LPARAM lParam
);
521 // XMN: Call MFC's callback
522 int nRes
= AfxPropSheetCallback(hWnd
, message
, lParam
);
527 // Change the font and font size
528 auto pResource
= reinterpret_cast<LPDLGTEMPLATE
>(lParam
);
529 CDialogTemplate
dlgTemplate(pResource
);
530 dlgTemplate
.SetFont(L
"Segoe UI", 9);
531 memmove((void*)lParam
, dlgTemplate
.m_hTemplate
, dlgTemplate
.m_dwTemplateSize
);
537 // Overriding DoModal() allows us to hook our callback into
538 // the prop sheet creation
539 INT_PTR
CResizableSheetEx::DoModal()
541 // Hook into property sheet creation code
542 m_psh
.dwFlags
|= PSH_USECALLBACK
;
543 m_psh
.pfnCallback
= XmnPropSheetCallback
;
544 return CPropertySheet::DoModal();