ResizableSheet.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. // ResizableSheet.cpp : implementation file
  2. //
  3. /////////////////////////////////////////////////////////////////////////////
  4. //
  5. // This file is part of ResizableLib
  6. // https://github.com/ppescher/resizablelib
  7. //
  8. // Copyright (C) 2000-2015 by Paolo Messina
  9. // mailto:ppescher@hotmail.com
  10. //
  11. // The contents of this file are subject to the Artistic License 2.0
  12. // http://opensource.org/licenses/Artistic-2.0
  13. //
  14. // If you find this code useful, credits would be nice!
  15. //
  16. /////////////////////////////////////////////////////////////////////////////
  17. #include "stdafx.h"
  18. #include "ResizableSheet.h"
  19. #ifdef _DEBUG
  20. #define new DEBUG_NEW
  21. #undef THIS_FILE
  22. static char THIS_FILE[] = __FILE__;
  23. #endif
  24. /////////////////////////////////////////////////////////////////////////////
  25. // CResizableSheet
  26. IMPLEMENT_DYNAMIC(CResizableSheet, CPropertySheet)
  27. inline void CResizableSheet::PrivateConstruct()
  28. {
  29. m_bEnableSaveRestore = FALSE;
  30. m_bSavePage = FALSE;
  31. m_dwGripTempState = 1;
  32. m_bLayoutDone = FALSE;
  33. m_bRectOnly = FALSE;
  34. m_nCallbackID = 0;
  35. }
  36. inline BOOL CResizableSheet::IsWizard() const
  37. {
  38. return (m_psh.dwFlags & PSH_WIZARD);
  39. }
  40. CResizableSheet::CResizableSheet()
  41. {
  42. PrivateConstruct();
  43. }
  44. CResizableSheet::CResizableSheet(UINT nIDCaption, CWnd *pParentWnd, UINT iSelectPage)
  45. : CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
  46. {
  47. PrivateConstruct();
  48. }
  49. CResizableSheet::CResizableSheet(LPCTSTR pszCaption, CWnd *pParentWnd, UINT iSelectPage)
  50. : CPropertySheet(pszCaption, pParentWnd, iSelectPage)
  51. {
  52. PrivateConstruct();
  53. }
  54. CResizableSheet::~CResizableSheet()
  55. {
  56. }
  57. BEGIN_MESSAGE_MAP(CResizableSheet, CPropertySheet)
  58. //{{AFX_MSG_MAP(CResizableSheet)
  59. ON_WM_GETMINMAXINFO()
  60. ON_WM_SIZE()
  61. ON_WM_DESTROY()
  62. ON_WM_ERASEBKGND()
  63. ON_WM_NCCREATE()
  64. //}}AFX_MSG_MAP
  65. ON_NOTIFY_REFLECT_EX(PSN_SETACTIVE, OnPageChanging)
  66. END_MESSAGE_MAP()
  67. /////////////////////////////////////////////////////////////////////////////
  68. // CResizableSheet message handlers
  69. BOOL CResizableSheet::OnNcCreate(LPCREATESTRUCT lpCreateStruct)
  70. {
  71. if (!CPropertySheet::OnNcCreate(lpCreateStruct))
  72. return FALSE;
  73. // child dialogs don't want resizable border or size grip,
  74. // nor they can handle the min/max size constraints
  75. BOOL bChild = lpCreateStruct->style & WS_CHILD;
  76. // create and init the size-grip
  77. if (!CreateSizeGrip(!bChild))
  78. return FALSE;
  79. MakeResizable(lpCreateStruct);
  80. return TRUE;
  81. }
  82. BOOL CResizableSheet::OnInitDialog()
  83. {
  84. BOOL bResult = CPropertySheet::OnInitDialog();
  85. // initialize layout
  86. PresetLayout();
  87. m_bLayoutDone = TRUE;
  88. return bResult;
  89. }
  90. void CResizableSheet::OnDestroy()
  91. {
  92. if (m_bEnableSaveRestore)
  93. {
  94. SaveWindowRect(m_sSection, m_bRectOnly);
  95. if (m_bSavePage)
  96. SavePage(m_sSection);
  97. }
  98. // reset instance data
  99. RemoveAllAnchors();
  100. ResetAllRects();
  101. PrivateConstruct();
  102. CPropertySheet::OnDestroy();
  103. }
  104. // maps an index to a button ID and vice-versa
  105. static UINT _propButtons[] =
  106. {
  107. IDOK, IDCANCEL, ID_APPLY_NOW, IDHELP,
  108. ID_WIZBACK, ID_WIZNEXT, ID_WIZFINISH
  109. };
  110. const int _propButtonsCount = sizeof(_propButtons)/sizeof(UINT);
  111. // horizontal line in wizard mode
  112. #define ID_WIZLINE ID_WIZFINISH+1
  113. void CResizableSheet::PresetLayout()
  114. {
  115. // set the initial size as the min track size
  116. CRect rc;
  117. GetWindowRect(&rc);
  118. SetMinTrackSize(rc.Size());
  119. // use *total* parent size to have correct margins
  120. CRect rectPage, rectSheet;
  121. GetTotalClientRect(&rectSheet);
  122. // get page area
  123. if (IsWizard())
  124. {
  125. HWND hPage = PropSheet_GetCurrentPageHwnd(m_hWnd);
  126. ::GetWindowRect(hPage, &rectPage);
  127. }
  128. else
  129. {
  130. GetTabControl()->GetWindowRect(&rectPage);
  131. }
  132. ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectPage, 2);
  133. // calculate margins
  134. CRect rect;
  135. int cxDiff = rectSheet.right - rectPage.right;
  136. int cyDiff = 0;
  137. // try all possible buttons
  138. for (int i = 0; i < _propButtonsCount; i++)
  139. {
  140. CWnd* pWnd = GetDlgItem(_propButtons[i]);
  141. if (NULL != pWnd)
  142. {
  143. // move buttons if necessary
  144. if (GetStyle() & WS_CHILD)
  145. {
  146. pWnd->GetWindowRect(&rect);
  147. ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rect, 2);
  148. cyDiff = rectSheet.bottom - rect.bottom;
  149. rect.OffsetRect(cxDiff, cyDiff);
  150. pWnd->MoveWindow(&rect);
  151. }
  152. // add buttons to the layout manager
  153. AddAnchor(_propButtons[i], BOTTOM_RIGHT);
  154. }
  155. }
  156. // setup pages area
  157. if (IsWizard())
  158. {
  159. // move line and pages if necessary
  160. if (GetStyle() & WS_CHILD)
  161. {
  162. GetDlgItem(ID_WIZLINE)->GetWindowRect(&rect);
  163. ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rect, 2);
  164. rect.OffsetRect(0, cyDiff);
  165. rect.InflateRect(cxDiff, 0);
  166. GetDlgItem(ID_WIZLINE)->MoveWindow(&rect);
  167. rectPage.bottom += cyDiff;
  168. rectPage.left = 0;
  169. rectPage.top = 0;
  170. rectPage.right = rectSheet.right;
  171. }
  172. AddAnchor(ID_WIZLINE, BOTTOM_LEFT, BOTTOM_RIGHT);
  173. // hide tab control
  174. GetTabControl()->ShowWindow(SW_HIDE);
  175. // pre-calculate margins
  176. m_sizePageTL = rectPage.TopLeft() - rectSheet.TopLeft();
  177. m_sizePageBR = rectPage.BottomRight() - rectSheet.BottomRight();
  178. }
  179. else
  180. {
  181. // grow tab to the available sheet space
  182. if (cyDiff > 0)
  183. rectSheet.bottom = rectPage.bottom + cyDiff;
  184. if (GetStyle() & WS_CHILD)
  185. GetTabControl()->MoveWindow(&rectSheet);
  186. AddAnchor(AFX_IDC_TAB_CONTROL, TOP_LEFT, BOTTOM_RIGHT);
  187. }
  188. // add a callback for active page (which can change at run-time)
  189. m_nCallbackID = AddAnchorCallback();
  190. // prevent flickering
  191. GetTabControl()->ModifyStyle(0, WS_CLIPSIBLINGS);
  192. }
  193. BOOL CResizableSheet::ArrangeLayoutCallback(LAYOUTINFO &layout) const
  194. {
  195. if (layout.nCallbackID != m_nCallbackID) // we only added 1 callback
  196. return CResizableLayout::ArrangeLayoutCallback(layout);
  197. // set layout info for active page
  198. layout.hWnd = PropSheet_GetCurrentPageHwnd(m_hWnd);
  199. if (!::IsWindow(layout.hWnd))
  200. return FALSE;
  201. // set margins
  202. if (IsWizard()) // wizard mode
  203. {
  204. // use pre-calculated margins
  205. layout.marginTopLeft = m_sizePageTL;
  206. layout.marginBottomRight = m_sizePageBR;
  207. }
  208. else // tab mode
  209. {
  210. CTabCtrl* pTab = GetTabControl();
  211. ASSERT(pTab != NULL);
  212. // get tab position after resizing and calc page rect
  213. CRect rectPage, rectSheet;
  214. GetTotalClientRect(&rectSheet);
  215. if (!GetAnchorPosition(pTab->m_hWnd, rectSheet, rectPage))
  216. return FALSE; // no page yet
  217. // temporarily resize the tab control to calc page size
  218. CRect rectSave;
  219. pTab->GetWindowRect(rectSave);
  220. ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectSave, 2);
  221. pTab->SetRedraw(FALSE);
  222. pTab->MoveWindow(rectPage, FALSE);
  223. pTab->AdjustRect(FALSE, &rectPage);
  224. pTab->MoveWindow(rectSave, FALSE);
  225. pTab->SetRedraw(TRUE);
  226. // set margins
  227. layout.marginTopLeft = rectPage.TopLeft() - rectSheet.TopLeft();
  228. layout.marginBottomRight = rectPage.BottomRight() - rectSheet.BottomRight();
  229. }
  230. // set anchor types
  231. layout.anchorTopLeft = TOP_LEFT;
  232. layout.anchorBottomRight = BOTTOM_RIGHT;
  233. // use this layout info
  234. return TRUE;
  235. }
  236. void CResizableSheet::OnSize(UINT nType, int cx, int cy)
  237. {
  238. CWnd::OnSize(nType, cx, cy);
  239. if (nType == SIZE_MAXHIDE || nType == SIZE_MAXSHOW)
  240. return; // arrangement not needed
  241. if (nType == SIZE_MAXIMIZED)
  242. HideSizeGrip(&m_dwGripTempState);
  243. else
  244. ShowSizeGrip(&m_dwGripTempState);
  245. // update grip and layout
  246. UpdateSizeGrip();
  247. ArrangeLayout();
  248. }
  249. BOOL CResizableSheet::OnPageChanging(NMHDR* /*pNotifyStruct*/, LRESULT* /*pResult*/)
  250. {
  251. // update new wizard page
  252. // active page changes after this notification
  253. PostMessage(WM_SIZE);
  254. return FALSE; // continue routing
  255. }
  256. BOOL CResizableSheet::OnEraseBkgnd(CDC* pDC)
  257. {
  258. ClipChildren(pDC, FALSE);
  259. BOOL bRet = CPropertySheet::OnEraseBkgnd(pDC);
  260. ClipChildren(pDC, TRUE);
  261. return bRet;
  262. }
  263. BOOL CResizableSheet::CalcSizeExtra(HWND /*hWndChild*/, CSize sizeChild, CSize &sizeExtra)
  264. {
  265. CTabCtrl* pTab = GetTabControl();
  266. if (!pTab)
  267. return FALSE;
  268. // get margins of tabcontrol
  269. CRect rectMargins;
  270. if (!GetAnchorMargins(pTab->m_hWnd, sizeChild, rectMargins))
  271. return FALSE;
  272. // get margin caused by tabcontrol
  273. CRect rectTabMargins(0,0,0,0);
  274. // get tab position after resizing and calc page rect
  275. CRect rectPage, rectSheet;
  276. GetTotalClientRect(&rectSheet);
  277. if (!GetAnchorPosition(pTab->m_hWnd, rectSheet, rectPage))
  278. return FALSE; // no page yet
  279. // temporarily resize the tab control to calc page size
  280. CRect rectSave;
  281. pTab->GetWindowRect(rectSave);
  282. ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectSave, 2);
  283. pTab->SetRedraw(FALSE);
  284. pTab->MoveWindow(rectPage, FALSE);
  285. pTab->AdjustRect(TRUE, &rectTabMargins);
  286. pTab->MoveWindow(rectSave, FALSE);
  287. pTab->SetRedraw(TRUE);
  288. // add non-client size
  289. ::AdjustWindowRectEx(&rectTabMargins, GetStyle(), !(GetStyle() & WS_CHILD) &&
  290. ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
  291. // compute extra size
  292. sizeExtra = rectMargins.TopLeft() + rectMargins.BottomRight() +
  293. rectTabMargins.Size();
  294. return TRUE;
  295. }
  296. void CResizableSheet::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
  297. {
  298. MinMaxInfo(lpMMI);
  299. CTabCtrl* pTab = GetTabControl();
  300. if (!pTab)
  301. return;
  302. int nCount = GetPageCount();
  303. for (int idx = 0; idx < nCount; ++idx)
  304. {
  305. if (IsWizard()) // wizard mode
  306. {
  307. // use pre-calculated margins
  308. CRect rectExtra(-CPoint(m_sizePageTL), -CPoint(m_sizePageBR));
  309. // add non-client size
  310. ::AdjustWindowRectEx(&rectExtra, GetStyle(), !(GetStyle() & WS_CHILD) &&
  311. ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
  312. ChainMinMaxInfo(lpMMI, *GetPage(idx), rectExtra.Size());
  313. }
  314. else // tab mode
  315. {
  316. ChainMinMaxInfoCB(lpMMI, *GetPage(idx));
  317. }
  318. }
  319. }
  320. // protected members
  321. int CResizableSheet::GetMinWidth()
  322. {
  323. CRect rectWnd, rectSheet;
  324. GetTotalClientRect(&rectSheet);
  325. int max = 0, min = rectSheet.Width();
  326. // search for leftmost and rightmost button margins
  327. for (int i = 0; i < 7; i++)
  328. {
  329. CWnd* pWnd = GetDlgItem(_propButtons[i]);
  330. // exclude not present or hidden buttons
  331. if (pWnd == NULL || !(pWnd->GetStyle() & WS_VISIBLE))
  332. continue;
  333. // left position is relative to the right border
  334. // of the parent window (negative value)
  335. pWnd->GetWindowRect(&rectWnd);
  336. ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectWnd, 2);
  337. int left = rectSheet.right - rectWnd.left;
  338. int right = rectSheet.right - rectWnd.right;
  339. if (left > max)
  340. max = left;
  341. if (right < min)
  342. min = right;
  343. }
  344. // sizing border width
  345. int border = GetSystemMetrics(SM_CXSIZEFRAME);
  346. // compute total width
  347. return max + min + 2*border;
  348. }
  349. // NOTE: this must be called after all the other settings
  350. // to have the window and its controls displayed properly
  351. void CResizableSheet::EnableSaveRestore(LPCTSTR pszSection, BOOL bRectOnly, BOOL bWithPage)
  352. {
  353. m_sSection = pszSection;
  354. m_bSavePage = bWithPage;
  355. m_bEnableSaveRestore = TRUE;
  356. m_bRectOnly = bRectOnly;
  357. // restore immediately
  358. LoadWindowRect(pszSection, bRectOnly);
  359. {
  360. LoadPage(pszSection);
  361. ArrangeLayout(); // needs refresh
  362. }
  363. }
  364. void CResizableSheet::RefreshLayout()
  365. {
  366. SendMessage(WM_SIZE);
  367. }
  368. LRESULT CResizableSheet::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
  369. {
  370. if (message != WM_NCCALCSIZE || wParam == 0 || !m_bLayoutDone)
  371. return CPropertySheet::WindowProc(message, wParam, lParam);
  372. // specifying valid rects needs controls already anchored
  373. LRESULT lResult = 0;
  374. HandleNcCalcSize(FALSE, (LPNCCALCSIZE_PARAMS)lParam, lResult);
  375. lResult = CPropertySheet::WindowProc(message, wParam, lParam);
  376. HandleNcCalcSize(TRUE, (LPNCCALCSIZE_PARAMS)lParam, lResult);
  377. return lResult;
  378. }