Fix typos
[TortoiseGit.git] / ext / ResizableLib / ResizableSheetEx.cpp
blob0f87557373bf519c5298f94093e1e88ef809731d
1 // ResizableSheetEx.cpp : implementation file
2 //
3 /////////////////////////////////////////////////////////////////////////////
4 //
5 // This file is part of ResizableLib
6 // http://sourceforge.net/projects/resizablelib
7 //
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 /////////////////////////////////////////////////////////////////////////////
20 #include "stdafx.h"
21 #include "ResizableSheetEx.h"
23 #ifdef _DEBUG
24 #define new DEBUG_NEW
25 #undef THIS_FILE
26 static char THIS_FILE[] = __FILE__;
27 #endif
29 /////////////////////////////////////////////////////////////////////////////
30 // CResizableSheetEx
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,
50 HBITMAP hbmHeader)
51 : CPropertySheetEx(nIDCaption, pParentWnd, iSelectPage,
52 hbmWatermark, hpalWatermark, hbmHeader)
56 CResizableSheetEx::CResizableSheetEx(LPCTSTR pszCaption, CWnd* pParentWnd,
57 UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
58 HBITMAP hbmHeader)
59 : CPropertySheetEx(pszCaption, pParentWnd, iSelectPage,
60 hbmWatermark, hpalWatermark, hbmHeader)
65 CResizableSheetEx::~CResizableSheetEx()
69 BEGIN_MESSAGE_MAP(CResizableSheetEx, CPropertySheetEx)
70 //{{AFX_MSG_MAP(CResizableSheetEx)
71 ON_WM_GETMINMAXINFO()
72 ON_WM_SIZE()
73 ON_WM_DESTROY()
74 ON_WM_ERASEBKGND()
75 ON_WM_NCCREATE()
76 //}}AFX_MSG_MAP
77 ON_NOTIFY_REFLECT_EX(PSN_SETACTIVE, OnPageChanging)
78 ON_REGISTERED_MESSAGE(WMU_RESIZESUPPORT, OnResizeSupport)
79 END_MESSAGE_MAP()
81 /////////////////////////////////////////////////////////////////////////////
82 // CResizableSheetEx message handlers
84 BOOL CResizableSheetEx::OnNcCreate(LPCREATESTRUCT lpCreateStruct)
86 if (!CPropertySheetEx::OnNcCreate(lpCreateStruct))
87 return FALSE;
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))
95 return FALSE;
97 MakeResizable(lpCreateStruct);
99 return TRUE;
102 BOOL CResizableSheetEx::OnInitDialog()
104 BOOL bResult = CPropertySheetEx::OnInitDialog();
106 // set the initial size as the min track size
107 CRect rc;
108 GetWindowRect(&rc);
109 SetMinTrackSize(rc.Size());
111 // initialize layout
112 PresetLayout();
113 m_bLayoutDone = TRUE;
115 return bResult;
118 LRESULT CResizableSheetEx::OnResizeSupport(WPARAM wParam, LPARAM lParam)
120 switch (wParam)
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);
128 if (pWnd == NULL)
129 return 0;
131 // suclass the window again and refresh page and sheet
132 pWnd->SubclassWindow(pWnd->Detach());
133 RefreshLayout();
134 pWnd->SendMessage(WM_SIZE);
135 Invalidate();
136 UnlockWindowUpdate();
138 if (pWnd->IsWindowVisible())
140 // send lost PSN_SETACTIVE notification message
141 CPropertyPage* pPage = DYNAMIC_DOWNCAST(CPropertyPage, pWnd);
142 if (pPage != NULL)
143 SetActivePage(pPage);
146 break;
148 default:
149 return FALSE;
151 return TRUE;
154 void CResizableSheetEx::OnDestroy()
156 if (m_bEnableSaveRestore)
158 SaveWindowRect(m_sSection, m_bRectOnly);
159 if (m_bSavePage)
160 SavePage(m_sSection);
163 RemoveAllAnchors();
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
184 // hide tab control
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);
192 else // tab mode
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))
227 return FALSE;
229 // set margins
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;
252 else // tab mode
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
265 CRect rectSave;
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);
274 // set margins
275 layout.marginTopLeft = rectPage.TopLeft() - rectSheet.TopLeft();
276 layout.marginBottomRight = rectPage.BottomRight() - rectSheet.BottomRight();
279 // set anchor types
280 layout.anchorTopLeft = TOP_LEFT;
281 layout.anchorBottomRight = BOTTOM_RIGHT;
283 // use this layout info
284 return TRUE;
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);
296 else
297 ShowSizeGrip(&m_dwGripTempState);
299 // update grip and layout
300 UpdateSizeGrip();
301 ArrangeLayout();
303 if (IsWizard97())
305 // refresh header area
306 CRect rect;
307 GetHeaderRect(rect);
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
326 if (IsWizard97())
328 // clip header area out
329 CRect rect;
330 GetHeaderRect(rect);
331 pDC->ExcludeClipRect(rect);
335 BOOL bRet = CPropertySheetEx::OnEraseBkgnd(pDC);
337 ClipChildren(pDC, TRUE);
339 return bRet;
342 BOOL CResizableSheetEx::CalcSizeExtra(HWND /*hWndChild*/, CSize sizeChild, CSize &sizeExtra)
344 CTabCtrl* pTab = GetTabControl();
345 if (!pTab)
346 return FALSE;
348 // get margins of tabcontrol
349 CRect rectMargins;
350 if (!GetAnchorMargins(pTab->m_hWnd, sizeChild, rectMargins))
351 return FALSE;
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
364 CRect rectSave;
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();
380 return TRUE;
383 void CResizableSheetEx::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
385 MinMaxInfo(lpMMI);
387 CTabCtrl* pTab = GetTabControl();
388 if (!pTab)
389 return;
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());
422 else // tab mode
424 ChainMinMaxInfoCB(lpMMI, *GetPage(idx));
429 // protected members
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;
442 else
443 ::SetRectEmpty(lpRect);
446 int CResizableSheetEx::GetMinWidth()
448 CWnd* pWnd = NULL;
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))
459 continue;
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;
468 if (left > max)
469 max = left;
470 if (right < min)
471 min = 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);
493 if (bWithPage)
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
511 LRESULT lResult = 0;
512 HandleNcCalcSize(FALSE, (LPNCCALCSIZE_PARAMS)lParam, lResult);
513 lResult = CPropertySheetEx::WindowProc(message, wParam, lParam);
514 HandleNcCalcSize(TRUE, (LPNCCALCSIZE_PARAMS)lParam, lResult);
515 return 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);
524 switch (message)
526 case PSCB_PRECREATE:
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);
532 break;
534 return nRes;
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();