Merge branch 'scintilla-357'
[TortoiseGit.git] / ext / ResizableLib / ResizableSheetEx.cpp
blob435b5d4d1b25563962f6dfd427731fb375db2d5f
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;
40 m_bRectOnly = FALSE;
41 m_nCallbackID = 0;
44 inline BOOL CResizableSheetEx::IsWizard() const
46 return (m_psh.dwFlags & PSH_WIZARD);
49 inline BOOL CResizableSheetEx::IsWizard97() const
51 return (m_psh.dwFlags & (PSH_IE4WIZARD97 | PSH_IE5WIZARD97));
54 CResizableSheetEx::CResizableSheetEx()
56 PrivateConstruct();
59 CResizableSheetEx::CResizableSheetEx(UINT nIDCaption, CWnd* pParentWnd,
60 UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
61 HBITMAP hbmHeader)
62 : CPropertySheetEx(nIDCaption, pParentWnd, iSelectPage,
63 hbmWatermark, hpalWatermark, hbmHeader)
65 PrivateConstruct();
68 CResizableSheetEx::CResizableSheetEx(LPCTSTR pszCaption, CWnd* pParentWnd,
69 UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
70 HBITMAP hbmHeader)
71 : CPropertySheetEx(pszCaption, pParentWnd, iSelectPage,
72 hbmWatermark, hpalWatermark, hbmHeader)
74 PrivateConstruct();
78 CResizableSheetEx::~CResizableSheetEx()
82 BEGIN_MESSAGE_MAP(CResizableSheetEx, CPropertySheetEx)
83 //{{AFX_MSG_MAP(CResizableSheetEx)
84 ON_WM_GETMINMAXINFO()
85 ON_WM_SIZE()
86 ON_WM_DESTROY()
87 ON_WM_ERASEBKGND()
88 ON_WM_NCCREATE()
89 //}}AFX_MSG_MAP
90 ON_NOTIFY_REFLECT_EX(PSN_SETACTIVE, OnPageChanging)
91 ON_REGISTERED_MESSAGE(WMU_RESIZESUPPORT, OnResizeSupport)
92 END_MESSAGE_MAP()
94 /////////////////////////////////////////////////////////////////////////////
95 // CResizableSheetEx message handlers
97 BOOL CResizableSheetEx::OnNcCreate(LPCREATESTRUCT lpCreateStruct)
99 if (!CPropertySheetEx::OnNcCreate(lpCreateStruct))
100 return FALSE;
102 // child dialogs don't want resizable border or size grip,
103 // nor they can handle the min/max size constraints
104 BOOL bChild = lpCreateStruct->style & WS_CHILD;
106 // create and init the size-grip
107 if (!CreateSizeGrip(!bChild))
108 return FALSE;
110 MakeResizable(lpCreateStruct);
112 return TRUE;
115 BOOL CResizableSheetEx::OnInitDialog()
117 BOOL bResult = CPropertySheetEx::OnInitDialog();
119 // set the initial size as the min track size
120 CRect rc;
121 GetWindowRect(&rc);
122 SetMinTrackSize(rc.Size());
124 // initialize layout
125 PresetLayout();
126 m_bLayoutDone = TRUE;
128 return bResult;
131 LRESULT CResizableSheetEx::OnResizeSupport(WPARAM wParam, LPARAM lParam)
133 switch (wParam)
135 case RSZSUP_SHEETPAGEEXHACK:
137 // a window object must be still associated to the page handle
138 // but MFC subclassing has been turned off to allow the system
139 // to subclass it first, so we can catch all the messages
140 CWnd* pWnd = CWnd::FromHandlePermanent((HWND)lParam);
141 if (pWnd == NULL)
142 return 0;
144 // suclass the window again and refresh page and sheet
145 pWnd->SubclassWindow(pWnd->Detach());
146 RefreshLayout();
147 pWnd->SendMessage(WM_SIZE);
148 Invalidate();
149 UnlockWindowUpdate();
151 if (pWnd->IsWindowVisible())
153 // send lost PSN_SETACTIVE notification message
154 CPropertyPage* pPage = DYNAMIC_DOWNCAST(CPropertyPage, pWnd);
155 if (pPage != NULL)
156 SetActivePage(pPage);
159 break;
161 default:
162 return FALSE;
164 return TRUE;
167 void CResizableSheetEx::OnDestroy()
169 if (m_bEnableSaveRestore)
171 SaveWindowRect(m_sSection, m_bRectOnly);
172 if (m_bSavePage)
173 SavePage(m_sSection);
176 RemoveAllAnchors();
178 CPropertySheetEx::OnDestroy();
181 // maps an index to a button ID and vice-versa
182 static UINT _propButtons[] =
184 IDOK, IDCANCEL, ID_APPLY_NOW, IDHELP,
185 ID_WIZBACK, ID_WIZNEXT, ID_WIZFINISH
187 const int _propButtonsCount = sizeof(_propButtons)/sizeof(UINT);
189 // horizontal line in wizard mode
190 #define ID_WIZLINE ID_WIZFINISH+1
191 #define ID_WIZLINEHDR ID_WIZFINISH+2
193 void CResizableSheetEx::PresetLayout()
195 if (IsWizard() || IsWizard97()) // wizard mode
197 // hide tab control
198 GetTabControl()->ShowWindow(SW_HIDE);
200 AddAnchor(ID_WIZLINE, BOTTOM_LEFT, BOTTOM_RIGHT);
202 if (IsWizard97()) // add header line for wizard97 dialogs
203 AddAnchor(ID_WIZLINEHDR, TOP_LEFT, TOP_RIGHT);
205 else // tab mode
207 AddAnchor(AFX_IDC_TAB_CONTROL, TOP_LEFT, BOTTOM_RIGHT);
210 // add a callback for active page (which can change at run-time)
211 m_nCallbackID = AddAnchorCallback();
213 // use *total* parent size to have correct margins
214 CRect rectPage, rectSheet;
215 GetTotalClientRect(&rectSheet);
217 GetActivePage()->GetWindowRect(&rectPage);
218 ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectPage, 2);
220 // pre-calculate margins
221 m_sizePageTL = rectPage.TopLeft() - rectSheet.TopLeft();
222 m_sizePageBR = rectPage.BottomRight() - rectSheet.BottomRight();
224 // add all possible buttons, if they exist
225 for (int i = 0; i < _propButtonsCount; i++)
227 if (NULL != GetDlgItem(_propButtons[i]))
228 AddAnchor(_propButtons[i], BOTTOM_RIGHT);
232 BOOL CResizableSheetEx::ArrangeLayoutCallback(LAYOUTINFO &layout) const
234 if (layout.nCallbackID != m_nCallbackID) // we only added 1 callback
235 return CResizableLayout::ArrangeLayoutCallback(layout);
237 // set layout info for active page
238 layout.hWnd = (HWND)::SendMessage(GetSafeHwnd(), PSM_GETCURRENTPAGEHWND, 0, 0);
239 if (!::IsWindow(layout.hWnd))
240 return FALSE;
242 // set margins
243 if (IsWizard()) // wizard mode
245 // use pre-calculated margins
246 layout.marginTopLeft = m_sizePageTL;
247 layout.marginBottomRight = m_sizePageBR;
249 else if (IsWizard97()) // wizard 97
251 // use pre-calculated margins
252 layout.marginTopLeft = m_sizePageTL;
253 layout.marginBottomRight = m_sizePageBR;
255 if (!(GetActivePage()->m_psp.dwFlags & PSP_HIDEHEADER))
257 // add header vertical offset
258 CRect rectLine, rectSheet;
259 GetTotalClientRect(&rectSheet);
260 GetAnchorPosition(ID_WIZLINEHDR, rectSheet, rectLine);
262 layout.marginTopLeft.cy = rectLine.bottom;
265 else // tab mode
267 CTabCtrl* pTab = GetTabControl();
268 ASSERT(pTab != NULL);
270 // get tab position after resizing and calc page rect
271 CRect rectPage, rectSheet;
272 GetTotalClientRect(&rectSheet);
274 if (!GetAnchorPosition(pTab->m_hWnd, rectSheet, rectPage))
275 return FALSE; // no page yet
277 // temporarily resize the tab control to calc page size
278 CRect rectSave;
279 pTab->GetWindowRect(rectSave);
280 ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectSave, 2);
281 pTab->SetRedraw(FALSE);
282 pTab->MoveWindow(rectPage, FALSE);
283 pTab->AdjustRect(FALSE, &rectPage);
284 pTab->MoveWindow(rectSave, FALSE);
285 pTab->SetRedraw(TRUE);
287 // set margins
288 layout.marginTopLeft = rectPage.TopLeft() - rectSheet.TopLeft();
289 layout.marginBottomRight = rectPage.BottomRight() - rectSheet.BottomRight();
292 // set anchor types
293 layout.anchorTopLeft = TOP_LEFT;
294 layout.anchorBottomRight = BOTTOM_RIGHT;
296 // use this layout info
297 return TRUE;
300 void CResizableSheetEx::OnSize(UINT nType, int cx, int cy)
302 CWnd::OnSize(nType, cx, cy);
304 if (nType == SIZE_MAXHIDE || nType == SIZE_MAXSHOW)
305 return; // arrangement not needed
307 if (nType == SIZE_MAXIMIZED)
308 HideSizeGrip(&m_dwGripTempState);
309 else
310 ShowSizeGrip(&m_dwGripTempState);
312 // update grip and layout
313 UpdateSizeGrip();
314 ArrangeLayout();
316 if (IsWizard97())
318 // refresh header area
319 CRect rect;
320 GetHeaderRect(rect);
321 InvalidateRect(rect, FALSE);
325 BOOL CResizableSheetEx::OnPageChanging(NMHDR* /*pNotifyStruct*/, LRESULT* /*pResult*/)
327 // update new wizard page
328 // active page changes after this notification
329 PostMessage(WM_SIZE);
331 return FALSE; // continue routing
334 BOOL CResizableSheetEx::OnEraseBkgnd(CDC* pDC)
336 if (ClipChildren(pDC, FALSE))
338 // when clipping, remove header from clipping area
339 if (IsWizard97())
341 // clip header area out
342 CRect rect;
343 GetHeaderRect(rect);
344 pDC->ExcludeClipRect(rect);
348 BOOL bRet = CPropertySheetEx::OnEraseBkgnd(pDC);
350 ClipChildren(pDC, TRUE);
352 return bRet;
355 BOOL CResizableSheetEx::CalcSizeExtra(HWND /*hWndChild*/, CSize sizeChild, CSize &sizeExtra)
357 CTabCtrl* pTab = GetTabControl();
358 if (!pTab)
359 return FALSE;
361 // get margins of tabcontrol
362 CRect rectMargins;
363 if (!GetAnchorMargins(pTab->m_hWnd, sizeChild, rectMargins))
364 return FALSE;
366 // get margin caused by tabcontrol
367 CRect rectTabMargins(0,0,0,0);
369 // get tab position after resizing and calc page rect
370 CRect rectPage, rectSheet;
371 GetTotalClientRect(&rectSheet);
373 if (!GetAnchorPosition(pTab->m_hWnd, rectSheet, rectPage))
374 return FALSE; // no page yet
376 // temporarily resize the tab control to calc page size
377 CRect rectSave;
378 pTab->GetWindowRect(rectSave);
379 ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectSave, 2);
380 pTab->SetRedraw(FALSE);
381 pTab->MoveWindow(rectPage, FALSE);
382 pTab->AdjustRect(TRUE, &rectTabMargins);
383 pTab->MoveWindow(rectSave, FALSE);
384 pTab->SetRedraw(TRUE);
386 // add non-client size
387 ::AdjustWindowRectEx(&rectTabMargins, GetStyle(), !(GetStyle() & WS_CHILD) &&
388 ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
390 // compute extra size
391 sizeExtra = rectMargins.TopLeft() + rectMargins.BottomRight() +
392 rectTabMargins.Size();
393 return TRUE;
396 void CResizableSheetEx::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
398 MinMaxInfo(lpMMI);
400 CTabCtrl* pTab = GetTabControl();
401 if (!pTab)
402 return;
404 int nCount = GetPageCount();
405 for (int idx = 0; idx < nCount; ++idx)
407 if (IsWizard()) // wizard mode
409 // use pre-calculated margins
410 CRect rectExtra(-CPoint(m_sizePageTL), -CPoint(m_sizePageBR));
411 // add non-client size
412 ::AdjustWindowRectEx(&rectExtra, GetStyle(), !(GetStyle() & WS_CHILD) &&
413 ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
414 ChainMinMaxInfo(lpMMI, *GetPage(idx), rectExtra.Size());
416 else if (IsWizard97()) // wizard 97
418 // use pre-calculated margins
419 CRect rectExtra(-CPoint(m_sizePageTL), -CPoint(m_sizePageBR));
421 if (!(GetPage(idx)->m_psp.dwFlags & PSP_HIDEHEADER))
423 // add header vertical offset
424 CRect rectLine, rectSheet;
425 GetTotalClientRect(&rectSheet);
426 GetAnchorPosition(ID_WIZLINEHDR, rectSheet, rectLine);
428 rectExtra.top = -rectLine.bottom;
430 // add non-client size
431 ::AdjustWindowRectEx(&rectExtra, GetStyle(), !(GetStyle() & WS_CHILD) &&
432 ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
433 ChainMinMaxInfo(lpMMI, *GetPage(idx), rectExtra.Size());
435 else // tab mode
437 ChainMinMaxInfoCB(lpMMI, *GetPage(idx));
442 // protected members
444 void CResizableSheetEx::GetHeaderRect(LPRECT lpRect)
446 CWnd* pWizLineHdr = GetDlgItem(ID_WIZLINEHDR);
447 if (pWizLineHdr != NULL && pWizLineHdr->IsWindowVisible())
449 pWizLineHdr->GetWindowRect(lpRect);
450 ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)lpRect, 2);
451 LONG bottom = lpRect->top;
452 GetClientRect(lpRect);
453 lpRect->bottom = bottom;
455 else
456 ::SetRectEmpty(lpRect);
459 int CResizableSheetEx::GetMinWidth()
461 CWnd* pWnd = NULL;
462 CRect rectWnd, rectSheet;
463 GetTotalClientRect(&rectSheet);
465 int max = 0, min = rectSheet.Width();
466 // search for leftmost and rightmost button margins
467 for (int i = 0; i < 7; i++)
469 pWnd = GetDlgItem(_propButtons[i]);
470 // exclude not present or hidden buttons
471 if (pWnd == NULL || !(pWnd->GetStyle() & WS_VISIBLE))
472 continue;
474 // left position is relative to the right border
475 // of the parent window (negative value)
476 pWnd->GetWindowRect(&rectWnd);
477 ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectWnd, 2);
478 int left = rectSheet.right - rectWnd.left;
479 int right = rectSheet.right - rectWnd.right;
481 if (left > max)
482 max = left;
483 if (right < min)
484 min = right;
487 // sizing border width
488 int border = GetSystemMetrics(SM_CXSIZEFRAME);
490 // compute total width
491 return max + min + 2*border;
494 // NOTE: this must be called after all the other settings
495 // to have the window and its controls displayed properly
496 void CResizableSheetEx::EnableSaveRestore(LPCTSTR pszSection, BOOL bRectOnly, BOOL bWithPage)
498 m_sSection = pszSection;
499 m_bSavePage = bWithPage;
501 m_bEnableSaveRestore = TRUE;
502 m_bRectOnly = bRectOnly;
504 // restore immediately
505 LoadWindowRect(pszSection, bRectOnly);
506 if (bWithPage)
508 LoadPage(pszSection);
509 ArrangeLayout(); // needs refresh
513 void CResizableSheetEx::RefreshLayout()
515 SendMessage(WM_SIZE);
518 LRESULT CResizableSheetEx::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
520 if (message != WM_NCCALCSIZE || wParam == 0 || !m_bLayoutDone)
521 return CPropertySheetEx::WindowProc(message, wParam, lParam);
523 // specifying valid rects needs controls already anchored
524 LRESULT lResult = 0;
525 HandleNcCalcSize(FALSE, (LPNCCALCSIZE_PARAMS)lParam, lResult);
526 lResult = CPropertySheetEx::WindowProc(message, wParam, lParam);
527 HandleNcCalcSize(TRUE, (LPNCCALCSIZE_PARAMS)lParam, lResult);
528 return lResult;