ResizableSheetEx.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. // ResizableSheetEx.cpp : implementation file
  2. //
  3. /////////////////////////////////////////////////////////////////////////////
  4. //
  5. // Copyright (C) 2000-2002 by Paolo Messina
  6. // (http://www.geocities.com/ppescher - ppescher@yahoo.com)
  7. //
  8. // The contents of this file are subject to the Artistic License (the "License").
  9. // You may not use this file except in compliance with the License.
  10. // You may obtain a copy of the License at:
  11. // http://www.opensource.org/licenses/artistic-license.html
  12. //
  13. // If you find this code useful, credits would be nice!
  14. //
  15. /////////////////////////////////////////////////////////////////////////////
  16. #define _WIN32_IE 0x0400 // for CPropertyPageEx, CPropertySheetEx
  17. #define _WIN32_WINNT 0x0500 // for CPropertyPageEx, CPropertySheetEx
  18. #include <afxwin.h> // MFC core and standard components
  19. #include <afxext.h> // MFC extensions
  20. #include <afxcmn.h> // MFC support for Windows Common Controls
  21. #include "ResizableSheetEx.h"
  22. #ifdef _DEBUG
  23. #define new DEBUG_NEW
  24. #undef THIS_FILE
  25. static char THIS_FILE[] = __FILE__;
  26. #endif
  27. /////////////////////////////////////////////////////////////////////////////
  28. // CResizableSheetEx
  29. IMPLEMENT_DYNAMIC(CResizableSheetEx, CPropertySheetEx)
  30. inline void CResizableSheetEx::PrivateConstruct()
  31. {
  32. m_bEnableSaveRestore = FALSE;
  33. m_bSavePage = FALSE;
  34. m_dwGripTempState = 1;
  35. }
  36. CResizableSheetEx::CResizableSheetEx()
  37. {
  38. PrivateConstruct();
  39. }
  40. CResizableSheetEx::CResizableSheetEx(UINT nIDCaption, CWnd* pParentWnd,
  41. UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
  42. HBITMAP hbmHeader)
  43. : CPropertySheetEx(nIDCaption, pParentWnd, iSelectPage,
  44. hbmWatermark, hpalWatermark, hbmHeader)
  45. {
  46. PrivateConstruct();
  47. }
  48. CResizableSheetEx::CResizableSheetEx(LPCTSTR pszCaption, CWnd* pParentWnd,
  49. UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
  50. HBITMAP hbmHeader)
  51. : CPropertySheetEx(pszCaption, pParentWnd, iSelectPage,
  52. hbmWatermark, hpalWatermark, hbmHeader)
  53. {
  54. PrivateConstruct();
  55. }
  56. CResizableSheetEx::~CResizableSheetEx()
  57. {
  58. }
  59. BEGIN_MESSAGE_MAP(CResizableSheetEx, CPropertySheetEx)
  60. //{{AFX_MSG_MAP(CResizableSheetEx)
  61. ON_WM_GETMINMAXINFO()
  62. ON_WM_SIZE()
  63. ON_WM_DESTROY()
  64. ON_WM_CREATE()
  65. ON_WM_ERASEBKGND()
  66. //}}AFX_MSG_MAP
  67. ON_NOTIFY_REFLECT_EX(PSN_SETACTIVE, OnPageChanging)
  68. END_MESSAGE_MAP()
  69. /////////////////////////////////////////////////////////////////////////////
  70. // CResizableSheetEx message handlers
  71. int CResizableSheetEx::OnCreate(LPCREATESTRUCT lpCreateStruct)
  72. {
  73. if (CPropertySheetEx::OnCreate(lpCreateStruct) == -1)
  74. return -1;
  75. // keep client area
  76. CRect rect;
  77. GetClientRect(&rect);
  78. // set resizable style
  79. ModifyStyle(DS_MODALFRAME, WS_POPUP | WS_THICKFRAME);
  80. // adjust size to reflect new style
  81. ::AdjustWindowRectEx(&rect, GetStyle(),
  82. ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
  83. SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(), SWP_FRAMECHANGED|
  84. SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREPOSITION);
  85. // create and init the size-grip
  86. if (!CreateSizeGrip())
  87. return -1;
  88. return 0;
  89. }
  90. BOOL CResizableSheetEx::OnInitDialog()
  91. {
  92. BOOL bResult = CPropertySheetEx::OnInitDialog();
  93. // set the initial size as the min track size
  94. CRect rc;
  95. GetWindowRect(&rc);
  96. SetMinTrackSize(rc.Size());
  97. // initialize layout
  98. PresetLayout();
  99. // prevent flickering
  100. GetTabControl()->ModifyStyle(0, WS_CLIPSIBLINGS);
  101. return bResult;
  102. }
  103. void CResizableSheetEx::OnDestroy()
  104. {
  105. if (m_bEnableSaveRestore)
  106. {
  107. SaveWindowRect(m_sSection, m_bRectOnly);
  108. SavePage();
  109. }
  110. RemoveAllAnchors();
  111. CPropertySheetEx::OnDestroy();
  112. }
  113. // maps an index to a button ID and vice-versa
  114. static UINT _propButtons[] =
  115. {
  116. IDOK, IDCANCEL, ID_APPLY_NOW, IDHELP,
  117. ID_WIZBACK, ID_WIZNEXT, ID_WIZFINISH
  118. };
  119. // horizontal line in wizard mode
  120. #define ID_WIZLINE ID_WIZFINISH+1
  121. #define ID_WIZLINEHDR ID_WIZFINISH+2
  122. void CResizableSheetEx::PresetLayout()
  123. {
  124. if (IsWizard() || IsWizard97()) // wizard mode
  125. {
  126. // hide tab control
  127. GetTabControl()->ShowWindow(SW_HIDE);
  128. AddAnchor(ID_WIZLINE, BOTTOM_LEFT, BOTTOM_RIGHT);
  129. if (IsWizard97()) // add header line for wizard97 dialogs
  130. AddAnchor(ID_WIZLINEHDR, TOP_LEFT, TOP_RIGHT);
  131. }
  132. else // tab mode
  133. {
  134. AddAnchor(AFX_IDC_TAB_CONTROL, TOP_LEFT, BOTTOM_RIGHT);
  135. }
  136. // add a callback for active page (which can change at run-time)
  137. AddAnchorCallback(1);
  138. // use *total* parent size to have correct margins
  139. CRect rectPage, rectSheet;
  140. GetTotalClientRect(&rectSheet);
  141. GetActivePage()->GetWindowRect(&rectPage);
  142. ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectPage, 2);
  143. // pre-calculate margins
  144. m_sizePageTL = rectPage.TopLeft() - rectSheet.TopLeft();
  145. m_sizePageBR = rectPage.BottomRight() - rectSheet.BottomRight();
  146. // add all possible buttons, if they exist
  147. for (int i = 0; i < 7; i++)
  148. {
  149. if (NULL != GetDlgItem(_propButtons[i]))
  150. AddAnchor(_propButtons[i], BOTTOM_RIGHT);
  151. }
  152. }
  153. BOOL CResizableSheetEx::ArrangeLayoutCallback(LayoutInfo &layout)
  154. {
  155. if (layout.nCallbackID != 1) // we only added 1 callback
  156. return CResizableLayout::ArrangeLayoutCallback(layout);
  157. // set layout info for active page
  158. layout.hWnd = (HWND)::SendMessage(GetSafeHwnd(), PSM_GETCURRENTPAGEHWND, 0, 0);
  159. if (!::IsWindow(layout.hWnd))
  160. return FALSE;
  161. // set margins
  162. if (IsWizard()) // wizard mode
  163. {
  164. // use pre-calculated margins
  165. layout.sizeMarginTL = m_sizePageTL;
  166. layout.sizeMarginBR = m_sizePageBR;
  167. }
  168. else if (IsWizard97()) // wizard 97
  169. {
  170. // use pre-calculated margins
  171. layout.sizeMarginTL = m_sizePageTL;
  172. layout.sizeMarginBR = m_sizePageBR;
  173. if (!(GetActivePage()->m_psp.dwFlags & PSP_HIDEHEADER))
  174. {
  175. // add header vertical offset
  176. CRect rectLine;
  177. GetDlgItem(ID_WIZLINEHDR)->GetWindowRect(&rectLine);
  178. ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectLine, 2);
  179. layout.sizeMarginTL.cy = rectLine.bottom;
  180. }
  181. }
  182. else // tab mode
  183. {
  184. CTabCtrl* pTab = GetTabControl();
  185. ASSERT(pTab != NULL);
  186. // get tab position after resizing and calc page rect
  187. CRect rectPage, rectSheet;
  188. GetTotalClientRect(&rectSheet);
  189. VERIFY(GetAnchorPosition(pTab->m_hWnd, rectSheet, rectPage));
  190. pTab->AdjustRect(FALSE, &rectPage);
  191. // set margins
  192. layout.sizeMarginTL = rectPage.TopLeft() - rectSheet.TopLeft();
  193. layout.sizeMarginBR = rectPage.BottomRight() - rectSheet.BottomRight();
  194. }
  195. // set anchor types
  196. layout.sizeTypeTL = TOP_LEFT;
  197. layout.sizeTypeBR = BOTTOM_RIGHT;
  198. // use this layout info
  199. return TRUE;
  200. }
  201. void CResizableSheetEx::OnSize(UINT nType, int cx, int cy)
  202. {
  203. CWnd::OnSize(nType, cx, cy);
  204. if (nType == SIZE_MAXHIDE || nType == SIZE_MAXSHOW)
  205. return; // arrangement not needed
  206. if (nType == SIZE_MAXIMIZED)
  207. HideSizeGrip(&m_dwGripTempState);
  208. else
  209. ShowSizeGrip(&m_dwGripTempState);
  210. // update grip and layout
  211. UpdateSizeGrip();
  212. ArrangeLayout();
  213. }
  214. BOOL CResizableSheetEx::OnPageChanging(NMHDR* /*pNotifyStruct*/, LRESULT* /*pResult*/)
  215. {
  216. // update new wizard page
  217. // active page changes after this notification
  218. PostMessage(WM_SIZE);
  219. return FALSE; // continue routing
  220. }
  221. BOOL CResizableSheetEx::OnEraseBkgnd(CDC* pDC)
  222. {
  223. // Windows XP doesn't like clipping regions ...try this!
  224. EraseBackground(pDC);
  225. return TRUE;
  226. /* ClipChildren(pDC); // old-method (for safety)
  227. return CPropertySheetEx::OnEraseBkgnd(pDC);
  228. */
  229. }
  230. void CResizableSheetEx::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
  231. {
  232. MinMaxInfo(lpMMI);
  233. }
  234. // protected members
  235. int CResizableSheetEx::GetMinWidth()
  236. {
  237. CWnd* pWnd = NULL;
  238. CRect rectWnd, rectSheet;
  239. GetTotalClientRect(&rectSheet);
  240. int max = 0, min = rectSheet.Width();
  241. // search for leftmost and rightmost button margins
  242. for (int i = 0; i < 7; i++)
  243. {
  244. pWnd = GetDlgItem(_propButtons[i]);
  245. // exclude not present or hidden buttons
  246. if (pWnd == NULL || !(pWnd->GetStyle() & WS_VISIBLE))
  247. continue;
  248. // left position is relative to the right border
  249. // of the parent window (negative value)
  250. pWnd->GetWindowRect(&rectWnd);
  251. ::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rectWnd, 2);
  252. int left = rectSheet.right - rectWnd.left;
  253. int right = rectSheet.right - rectWnd.right;
  254. if (left > max)
  255. max = left;
  256. if (right < min)
  257. min = right;
  258. }
  259. // sizing border width
  260. int border = GetSystemMetrics(SM_CXSIZEFRAME);
  261. // compute total width
  262. return max + min + 2*border;
  263. }
  264. // NOTE: this must be called after all the other settings
  265. // to have the window and its controls displayed properly
  266. void CResizableSheetEx::EnableSaveRestore(LPCTSTR pszSection, BOOL bRectOnly, BOOL bWithPage)
  267. {
  268. m_sSection = pszSection;
  269. m_bSavePage = bWithPage;
  270. m_bEnableSaveRestore = TRUE;
  271. m_bRectOnly = bRectOnly;
  272. // restore immediately
  273. LoadWindowRect(pszSection, bRectOnly);
  274. LoadPage();
  275. }
  276. // private memebers
  277. // used to save/restore active page
  278. // either in the registry or a private .INI file
  279. // depending on your application settings
  280. #define ACTIVEPAGE _T("ActivePage")
  281. void CResizableSheetEx::SavePage()
  282. {
  283. if (!m_bSavePage)
  284. return;
  285. // saves active page index, zero (the first) if problems
  286. // cannot use GetActivePage, because it always fails
  287. CTabCtrl *pTab = GetTabControl();
  288. int page = 0;
  289. if (pTab != NULL)
  290. page = pTab->GetCurSel();
  291. if (page < 0)
  292. page = 0;
  293. AfxGetApp()->WriteProfileInt(m_sSection, ACTIVEPAGE, page);
  294. }
  295. void CResizableSheetEx::LoadPage()
  296. {
  297. // restore active page, zero (the first) if not found
  298. int page = AfxGetApp()->GetProfileInt(m_sSection, ACTIVEPAGE, 0);
  299. if (m_bSavePage)
  300. {
  301. SetActivePage(page);
  302. ArrangeLayout(); // needs refresh
  303. }
  304. }
  305. void CResizableSheetEx::RefreshLayout()
  306. {
  307. SendMessage(WM_SIZE);
  308. }