Merge branch 'logdlg-showtags' of git://github.com/ch3cooli/TortoiseGit
[TortoiseGit.git] / ext / ResizableLib / ResizableSheetEx.cpp
blob7acf76ec5498253c6b40e21caf332612fc78bc43
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 void CResizableSheetEx::PrivateConstruct()
36 m_bEnableSaveRestore = FALSE;
37 m_bSavePage = FALSE;
38 m_dwGripTempState = 1;
39 m_bLayoutDone = FALSE;
42 inline BOOL CResizableSheetEx::IsWizard() const
44 return (m_psh.dwFlags & PSH_WIZARD);
47 inline BOOL CResizableSheetEx::IsWizard97() const
49 return (m_psh.dwFlags & (PSH_IE4WIZARD97 | PSH_IE5WIZARD97));
52 CResizableSheetEx::CResizableSheetEx()
54 PrivateConstruct();
57 CResizableSheetEx::CResizableSheetEx(UINT nIDCaption, CWnd* pParentWnd,
58 UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
59 HBITMAP hbmHeader)
60 : CPropertySheetEx(nIDCaption, pParentWnd, iSelectPage,
61 hbmWatermark, hpalWatermark, hbmHeader)
63 PrivateConstruct();
66 CResizableSheetEx::CResizableSheetEx(LPCTSTR pszCaption, CWnd* pParentWnd,
67 UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
68 HBITMAP hbmHeader)
69 : CPropertySheetEx(pszCaption, pParentWnd, iSelectPage,
70 hbmWatermark, hpalWatermark, hbmHeader)
72 PrivateConstruct();
76 CResizableSheetEx::~CResizableSheetEx()
80 BEGIN_MESSAGE_MAP(CResizableSheetEx, CPropertySheetEx)
81 //{{AFX_MSG_MAP(CResizableSheetEx)
82 ON_WM_GETMINMAXINFO()
83 ON_WM_SIZE()
84 ON_WM_DESTROY()
85 ON_WM_ERASEBKGND()
86 ON_WM_NCCREATE()
87 //}}AFX_MSG_MAP
88 ON_NOTIFY_REFLECT_EX(PSN_SETACTIVE, OnPageChanging)
89 ON_REGISTERED_MESSAGE(WMU_RESIZESUPPORT, OnResizeSupport)
90 END_MESSAGE_MAP()
92 /////////////////////////////////////////////////////////////////////////////
93 // CResizableSheetEx message handlers
95 BOOL CResizableSheetEx::OnNcCreate(LPCREATESTRUCT lpCreateStruct)
97 if (!CPropertySheetEx::OnNcCreate(lpCreateStruct))
98 return FALSE;
100 // child dialogs don't want resizable border or size grip,
101 // nor they can handle the min/max size constraints
102 BOOL bChild = lpCreateStruct->style & WS_CHILD;
104 // create and init the size-grip
105 if (!CreateSizeGrip(!bChild))
106 return FALSE;
108 MakeResizable(lpCreateStruct);
110 return TRUE;
113 BOOL CResizableSheetEx::OnInitDialog()
115 BOOL bResult = CPropertySheetEx::OnInitDialog();
117 // set the initial size as the min track size
118 CRect rc;
119 GetWindowRect(&rc);
120 SetMinTrackSize(rc.Size());
122 // initialize layout
123 PresetLayout();
124 m_bLayoutDone = TRUE;
126 return bResult;
129 LRESULT CResizableSheetEx::OnResizeSupport(WPARAM wParam, LPARAM lParam)
131 switch (wParam)
133 case RSZSUP_SHEETPAGEEXHACK:
135 // a window object must be still associated to the page handle
136 // but MFC subclassing has been turned off to allow the system
137 // to subclass it first, so we can catch all the messages
138 CWnd* pWnd = CWnd::FromHandlePermanent((HWND)lParam);
139 if (pWnd == NULL)
140 return 0;
142 // suclass the window again and refresh page and sheet
143 pWnd->SubclassWindow(pWnd->Detach());
144 RefreshLayout();
145 pWnd->SendMessage(WM_SIZE);
146 Invalidate();
147 UnlockWindowUpdate();
149 if (pWnd->IsWindowVisible())
151 // send lost PSN_SETACTIVE notification message
152 CPropertyPage* pPage = DYNAMIC_DOWNCAST(CPropertyPage, pWnd);
153 if (pPage != NULL)
154 SetActivePage(pPage);
157 break;
159 default:
160 return FALSE;
162 return TRUE;
165 void CResizableSheetEx::OnDestroy()
167 if (m_bEnableSaveRestore)
169 SaveWindowRect(m_sSection, m_bRectOnly);
170 if (m_bSavePage)
171 SavePage(m_sSection);
174 RemoveAllAnchors();
176 CPropertySheetEx::OnDestroy();
179 // maps an index to a button ID and vice-versa
180 static UINT _propButtons[] =
182 IDOK, IDCANCEL, ID_APPLY_NOW, IDHELP,
183 ID_WIZBACK, ID_WIZNEXT, ID_WIZFINISH
185 const int _propButtonsCount = sizeof(_propButtons)/sizeof(UINT);
187 // horizontal line in wizard mode
188 #define ID_WIZLINE ID_WIZFINISH+1
189 #define ID_WIZLINEHDR ID_WIZFINISH+2
191 void CResizableSheetEx::PresetLayout()
193 if (IsWizard() || IsWizard97()) // wizard mode
195 // hide tab control
196 GetTabControl()->ShowWindow(SW_HIDE);
198 AddAnchor(ID_WIZLINE, BOTTOM_LEFT, BOTTOM_RIGHT);
200 if (IsWizard97()) // add header line for wizard97 dialogs
201 AddAnchor(ID_WIZLINEHDR, TOP_LEFT, TOP_RIGHT);
203 else // tab mode
205 AddAnchor(AFX_IDC_TAB_CONTROL, TOP_LEFT, BOTTOM_RIGHT);
208 // add a callback for active page (which can change at run-time)
209 m_nCallbackID = AddAnchorCallback();
211 // use *total* parent size to have correct margins
212 CRect rectPage, rectSheet;
213 GetTotalClientRect(&rectSheet);
215 GetActivePage()->GetWindowRect(&rectPage);
216 ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectPage, 2);
218 // pre-calculate margins
219 m_sizePageTL = rectPage.TopLeft() - rectSheet.TopLeft();
220 m_sizePageBR = rectPage.BottomRight() - rectSheet.BottomRight();
222 // add all possible buttons, if they exist
223 for (int i = 0; i < _propButtonsCount; i++)
225 if (NULL != GetDlgItem(_propButtons[i]))
226 AddAnchor(_propButtons[i], BOTTOM_RIGHT);
230 BOOL CResizableSheetEx::ArrangeLayoutCallback(LAYOUTINFO &layout) const
232 if (layout.nCallbackID != m_nCallbackID) // we only added 1 callback
233 return CResizableLayout::ArrangeLayoutCallback(layout);
235 // set layout info for active page
236 layout.hWnd = (HWND)::SendMessage(GetSafeHwnd(), PSM_GETCURRENTPAGEHWND, 0, 0);
237 if (!::IsWindow(layout.hWnd))
238 return FALSE;
240 // set margins
241 if (IsWizard()) // wizard mode
243 // use pre-calculated margins
244 layout.marginTopLeft = m_sizePageTL;
245 layout.marginBottomRight = m_sizePageBR;
247 else if (IsWizard97()) // wizard 97
249 // use pre-calculated margins
250 layout.marginTopLeft = m_sizePageTL;
251 layout.marginBottomRight = m_sizePageBR;
253 if (!(GetActivePage()->m_psp.dwFlags & PSP_HIDEHEADER))
255 // add header vertical offset
256 CRect rectLine, rectSheet;
257 GetTotalClientRect(&rectSheet);
258 GetAnchorPosition(ID_WIZLINEHDR, rectSheet, rectLine);
260 layout.marginTopLeft.cy = rectLine.bottom;
263 else // tab mode
265 CTabCtrl* pTab = GetTabControl();
266 ASSERT(pTab != NULL);
268 // get tab position after resizing and calc page rect
269 CRect rectPage, rectSheet;
270 GetTotalClientRect(&rectSheet);
272 if (!GetAnchorPosition(pTab->m_hWnd, rectSheet, rectPage))
273 return FALSE; // no page yet
275 // temporarily resize the tab control to calc page size
276 CRect rectSave;
277 pTab->GetWindowRect(rectSave);
278 ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectSave, 2);
279 pTab->SetRedraw(FALSE);
280 pTab->MoveWindow(rectPage, FALSE);
281 pTab->AdjustRect(FALSE, &rectPage);
282 pTab->MoveWindow(rectSave, FALSE);
283 pTab->SetRedraw(TRUE);
285 // set margins
286 layout.marginTopLeft = rectPage.TopLeft() - rectSheet.TopLeft();
287 layout.marginBottomRight = rectPage.BottomRight() - rectSheet.BottomRight();
290 // set anchor types
291 layout.anchorTopLeft = TOP_LEFT;
292 layout.anchorBottomRight = BOTTOM_RIGHT;
294 // use this layout info
295 return TRUE;
298 void CResizableSheetEx::OnSize(UINT nType, int cx, int cy)
300 CWnd::OnSize(nType, cx, cy);
302 if (nType == SIZE_MAXHIDE || nType == SIZE_MAXSHOW)
303 return; // arrangement not needed
305 if (nType == SIZE_MAXIMIZED)
306 HideSizeGrip(&m_dwGripTempState);
307 else
308 ShowSizeGrip(&m_dwGripTempState);
310 // update grip and layout
311 UpdateSizeGrip();
312 ArrangeLayout();
314 if (IsWizard97())
316 // refresh header area
317 CRect rect;
318 GetHeaderRect(rect);
319 InvalidateRect(rect, FALSE);
323 BOOL CResizableSheetEx::OnPageChanging(NMHDR* /*pNotifyStruct*/, LRESULT* /*pResult*/)
325 // update new wizard page
326 // active page changes after this notification
327 PostMessage(WM_SIZE);
329 return FALSE; // continue routing
332 BOOL CResizableSheetEx::OnEraseBkgnd(CDC* pDC)
334 if (ClipChildren(pDC, FALSE))
336 // when clipping, remove header from clipping area
337 if (IsWizard97())
339 // clip header area out
340 CRect rect;
341 GetHeaderRect(rect);
342 pDC->ExcludeClipRect(rect);
346 BOOL bRet = CPropertySheetEx::OnEraseBkgnd(pDC);
348 ClipChildren(pDC, TRUE);
350 return bRet;
353 BOOL CResizableSheetEx::CalcSizeExtra(HWND /*hWndChild*/, CSize sizeChild, CSize &sizeExtra)
355 CTabCtrl* pTab = GetTabControl();
356 if (!pTab)
357 return FALSE;
359 // get margins of tabcontrol
360 CRect rectMargins;
361 if (!GetAnchorMargins(pTab->m_hWnd, sizeChild, rectMargins))
362 return FALSE;
364 // get margin caused by tabcontrol
365 CRect rectTabMargins(0,0,0,0);
367 // get tab position after resizing and calc page rect
368 CRect rectPage, rectSheet;
369 GetTotalClientRect(&rectSheet);
371 if (!GetAnchorPosition(pTab->m_hWnd, rectSheet, rectPage))
372 return FALSE; // no page yet
374 // temporarily resize the tab control to calc page size
375 CRect rectSave;
376 pTab->GetWindowRect(rectSave);
377 ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectSave, 2);
378 pTab->SetRedraw(FALSE);
379 pTab->MoveWindow(rectPage, FALSE);
380 pTab->AdjustRect(TRUE, &rectTabMargins);
381 pTab->MoveWindow(rectSave, FALSE);
382 pTab->SetRedraw(TRUE);
384 // add non-client size
385 ::AdjustWindowRectEx(&rectTabMargins, GetStyle(), !(GetStyle() & WS_CHILD) &&
386 ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
388 // compute extra size
389 sizeExtra = rectMargins.TopLeft() + rectMargins.BottomRight() +
390 rectTabMargins.Size();
391 return TRUE;
394 void CResizableSheetEx::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
396 MinMaxInfo(lpMMI);
398 CTabCtrl* pTab = GetTabControl();
399 if (!pTab)
400 return;
402 int nCount = GetPageCount();
403 for (int idx = 0; idx < nCount; ++idx)
405 if (IsWizard()) // wizard mode
407 // use pre-calculated margins
408 CRect rectExtra(-CPoint(m_sizePageTL), -CPoint(m_sizePageBR));
409 // add non-client size
410 ::AdjustWindowRectEx(&rectExtra, GetStyle(), !(GetStyle() & WS_CHILD) &&
411 ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
412 ChainMinMaxInfo(lpMMI, *GetPage(idx), rectExtra.Size());
414 else if (IsWizard97()) // wizard 97
416 // use pre-calculated margins
417 CRect rectExtra(-CPoint(m_sizePageTL), -CPoint(m_sizePageBR));
419 if (!(GetPage(idx)->m_psp.dwFlags & PSP_HIDEHEADER))
421 // add header vertical offset
422 CRect rectLine, rectSheet;
423 GetTotalClientRect(&rectSheet);
424 GetAnchorPosition(ID_WIZLINEHDR, rectSheet, rectLine);
426 rectExtra.top = -rectLine.bottom;
428 // add non-client size
429 ::AdjustWindowRectEx(&rectExtra, GetStyle(), !(GetStyle() & WS_CHILD) &&
430 ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
431 ChainMinMaxInfo(lpMMI, *GetPage(idx), rectExtra.Size());
433 else // tab mode
435 ChainMinMaxInfoCB(lpMMI, *GetPage(idx));
440 // protected members
442 void CResizableSheetEx::GetHeaderRect(LPRECT lpRect)
444 CWnd* pWizLineHdr = GetDlgItem(ID_WIZLINEHDR);
445 if (pWizLineHdr != NULL && pWizLineHdr->IsWindowVisible())
447 pWizLineHdr->GetWindowRect(lpRect);
448 ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)lpRect, 2);
449 LONG bottom = lpRect->top;
450 GetClientRect(lpRect);
451 lpRect->bottom = bottom;
453 else
454 ::SetRectEmpty(lpRect);
457 int CResizableSheetEx::GetMinWidth()
459 CWnd* pWnd = NULL;
460 CRect rectWnd, rectSheet;
461 GetTotalClientRect(&rectSheet);
463 int max = 0, min = rectSheet.Width();
464 // search for leftmost and rightmost button margins
465 for (int i = 0; i < 7; i++)
467 pWnd = GetDlgItem(_propButtons[i]);
468 // exclude not present or hidden buttons
469 if (pWnd == NULL || !(pWnd->GetStyle() & WS_VISIBLE))
470 continue;
472 // left position is relative to the right border
473 // of the parent window (negative value)
474 pWnd->GetWindowRect(&rectWnd);
475 ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectWnd, 2);
476 int left = rectSheet.right - rectWnd.left;
477 int right = rectSheet.right - rectWnd.right;
479 if (left > max)
480 max = left;
481 if (right < min)
482 min = right;
485 // sizing border width
486 int border = GetSystemMetrics(SM_CXSIZEFRAME);
488 // compute total width
489 return max + min + 2*border;
492 // NOTE: this must be called after all the other settings
493 // to have the window and its controls displayed properly
494 void CResizableSheetEx::EnableSaveRestore(LPCTSTR pszSection, BOOL bRectOnly, BOOL bWithPage)
496 m_sSection = pszSection;
497 m_bSavePage = bWithPage;
499 m_bEnableSaveRestore = TRUE;
500 m_bRectOnly = bRectOnly;
502 // restore immediately
503 LoadWindowRect(pszSection, bRectOnly);
504 if (bWithPage)
506 LoadPage(pszSection);
507 ArrangeLayout(); // needs refresh
511 void CResizableSheetEx::RefreshLayout()
513 SendMessage(WM_SIZE);
516 LRESULT CResizableSheetEx::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
518 if (message != WM_NCCALCSIZE || wParam == 0 || !m_bLayoutDone)
519 return CPropertySheetEx::WindowProc(message, wParam, lParam);
521 // specifying valid rects needs controls already anchored
522 LRESULT lResult = 0;
523 HandleNcCalcSize(FALSE, (LPNCCALCSIZE_PARAMS)lParam, lResult);
524 lResult = CPropertySheetEx::WindowProc(message, wParam, lParam);
525 HandleNcCalcSize(TRUE, (LPNCCALCSIZE_PARAMS)lParam, lResult);
526 return lResult;