当前位置: 首页 > news >正文

MFC类Qt的自动布局框架

由于作者习惯使用Qt,习惯了其框架下的水平和垂直布局。但在使用MFC时,却发现并没有十分好用的布局框架,检索了部分资料,发现要么不提供源码,要么方案不理想。搜索了很多资料,最终发现一个可用方案,记录在此。

一、背景

本方案主要参考如下链接:Layout Manager for Dialogs, Formviews, DialogBars and PropertyPages - CodeProject   

其布局效果图如下:

 

二、基类源码

ETSLayout.h

////////////////////////////////////////////
//         ___ ____ _________________     //
//        / _/_  _// _______________/     //
//       / _/ / / / /  ___ ___ ____       //
//      /__/ /_/ / / /   // _/_  _/       //
//     _________/ / / / // _/ / /         //
// (c) 1998-2000_/ /___//_/  /_/          //
//                                        //
////////////////////////////////////////////
//          all rights reserved           //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ETSLayoutDialog
//
// A class for smart layouting of Dialogs and such
//
// USAGE: See LayoutMgr.html
//
// AUTHOR: Erwin Tratar <tr@et-soft.de>
//
// DISCLAIMER:
//
// This Sourcecode and all accompaning material is ?998-1999 Erwin Tratar. 
// All rights reserved.
//
// The source code may be used in compiled form in any way you desire 
// (including usage in commercial applications), providing that your 
// application adds essential code (i.e. it is not only a wrapper) to the 
// functionality found here
//
// Redistribution of the sourcecode itself, publication in any media or 
// inclusion in a library requires the authors expressed written consent.
// You may not sale this code for profit.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. USE IT 
// AT YOUR OWN RISK! THE AUTHOR ACCEPTS NO LIABILITY FOR ANY DAMAGE/LOSS OF 
// BUSINESS THAT THIS PRODUCT MAY CAUSE.#if !defined(ETS_LAYOUTMGR_INCLUDED_)
#define ETS_LAYOUTMGR_INCLUDED_#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// DialogMgr.h : header file
//namespace ETSLayout
{#ifdef CS_HELPtypedef ETSCSHelpDialog		CBaseDialog;typedef ETSCSHelpFormView	CBaseFormView;typedef ETSCSHelpDlgBar		CBaseDialogBar;typedef ETSCSHelpPropPage	CBasePropertyPage;
#elsetypedef CDialog				CBaseDialog;typedef CFormView			CBaseFormView;typedef CDialogBar			CBaseDialogBar;typedef CPropertyPage		CBasePropertyPage;
#endif
}// Support for CBCGDialogBar instead of CDialogBar available:
// you just have to change the typedef to CBaseDialogBar#ifndef ETSGUI_EXT_CLASS
#define ETSGUI_EXT_CLASS
#endif#include <afxtempl.h>// Support for CBCGDialogBar instead of CDialogBar/*** Controls whether the Icon is automatically set to IDR_MAINFRAME*/
#define _AUTO_SET_ICON/*** Forward class declarations*/
class ETSLayoutDialog;
class ETSLayoutDialogBar;
class ETSLayoutFormView;
class ETSLayoutMgr;
class ETSLayoutPropertyPage;
class ETSLayoutPropertySheet;/*** These are NOOPs now*/
#define DECLARE_LAYOUT()
#define IMPLEMENT_LAYOUT()/*** This is the default border size between the panes. You* may override it in Pane constructor, but it is the* fixed border around the root pane*/
const int nDefaultBorder	= 5;/*** The minimum size for not ABSOLUTE_XXX items*/
const int nMinConstrain = 5;class ETSGUI_EXT_CLASS ETSLayoutMgr
{
public:enum layResizeMode {GREEDY				= 0,		// Will eat up as much as it canABSOLUTE_HORZ		= 1 << 0,	// Horizontal size is absoluteRELATIVE_HORZ		= 1 << 1,	// Horizontal size in percentABSOLUTE_VERT		= 1 << 2,	// Vertical size is absoluteRELATIVE_VERT		= 1 << 3,	// Vertical size in percentNORESIZE			= ABSOLUTE_HORZ | ABSOLUTE_VERT,SIZE_MASK			= NORESIZE,ALIGN_LEFT			= 1 << 4,   // following only for NORESIZEALIGN_RIGHT			= 1 << 5,ALIGN_TOP			= 1 << 6,ALIGN_BOTTOM		= 1 << 7,ALIGN_HCENTER		= ALIGN_LEFT    | ALIGN_RIGHT,	ALIGN_VCENTER		= ALIGN_TOP     | ALIGN_BOTTOM,ALIGN_CENTER		= ALIGN_HCENTER | ALIGN_VCENTER,ALIGN_FILL_HORZ		= 1 << 8,ALIGN_FILL_VERT		= 1 << 9,ALIGN_FILL			= ALIGN_FILL_HORZ | ALIGN_FILL_VERT,/*		TRACKER_LEFT		= 1 << 10,	// not yet. May allow tracking of bordersTRACKER_RIGHT		= 1 << 11,  // between items in the futureTRACKER_TOP			= 1 << 12,TRACKER_BOTTOM		= 1 << 13,
*/};enum layOrientation {HORIZONTAL,VERTICAL};/*** This is the base class for all kind of panes. */class ETSGUI_EXT_CLASS PaneBase {friend class ETSLayoutMgr;friend class CPaneBase;friend class CPane;public:/*** Informs the caller how much of the given space this pane would* like to receive in horizontal direction*/virtual int		getConstrainHorz(int sizeParent) = 0;/*** Informs the caller how much of the given space this pane would* like to receive in vertical direction*/virtual int		getConstrainVert(int sizeParent) = 0;/*** Informs the caller how much of the given space this pane* minimally need. This would be an absolute Value if * the mode contains ABSOLUTE_HORZ or an explicit minimum* value, else nMinConstrain*/virtual int		getMinConstrainHorz() = 0;/*** Informs the caller if there is an restriction for maximum* space this pane needs. Return -1 for unrestricted (GREEDY* or RELATIVE)*/virtual int		getMaxConstrainHorz() = 0;/*** Informs the caller how much of the given space this pane* minimally need. This would be an absolute Value if * the mode contains ABSOLUTE_VERT or an explicit minimum* value, else nMinConstrain*/virtual int		getMinConstrainVert() = 0;/*** Informs the caller if there is an restriction for maximum* space this pane needs. Return -1 for unrestricted (GREEDY* or RELATIVE)*/virtual int		getMaxConstrainVert() = 0;/*** This will do the actual resize operation after the* caller computed a new area for this pane*/virtual bool	resizeTo(CRect& rcNewArea) = 0;/*** Constructor needed pointer to LayoutManager*/PaneBase( ETSLayoutMgr* pMgr )		{ m_pMgr = pMgr; };/*** Virtual destructor needed in Container operations*/virtual ~PaneBase() {};/*** Returs the Resize Mode of this pane*/DWORD	modeResize() { return m_modeResize; };protected:/*** How this Item will be resized, a combination of the flags above*/DWORD	m_modeResize;/*** A pointer to the holding LayoutManager derivate*/ETSLayoutMgr*		m_pMgr;};/*** CPaneBase represents an autopointer to a PaneBase. Use this and you won't have to worry* about cleaning up any Panes. Also this autopointer lets you return Pane objects* from function without using pointers (at least you won't see them :) )*/struct ETSGUI_EXT_CLASS PaneHolder{PaneHolder(PaneBase* pPane );~PaneHolder();void	AddRef();void	Release();PaneBase*	m_pPane;long		m_nRefCount;};class ETSGUI_EXT_CLASS CPaneBase{protected:PaneHolder*		m_pPaneHolder;public:// StandardconstructorCPaneBase( );CPaneBase( PaneBase* pPane );CPaneBase( const CPaneBase& other );~CPaneBase();void operator=( PaneBase* pPane );void operator=( const CPaneBase& other );PaneBase* operator->() const;PaneBase* GetPaneBase()	{ return operator->(); }bool IsValid()			{ return (m_pPaneHolder != 0); }bool operator !()			{ return (m_pPaneHolder == 0); }};class Pane;class ETSGUI_EXT_CLASS CPane : public CPaneBase{public:// StandardconstructorCPane( );CPane( Pane* pPane );CPane( const CPane& other );~CPane();void operator=( Pane* pPane );void operator=( const CPane& other );Pane* operator->() const;Pane* GetPane()			{ return operator->(); }CPaneBase ConvertBase() const;CPane& operator<< ( const CPane pPane );CPane& operator<< ( const CPaneBase pItem );};/*** PaneItem represents a single control*/class ETSGUI_EXT_CLASS PaneItem : public PaneBase {friend class ETSLayoutMgr;friend class Pane;protected:/*** Creates a new PaneItem from an Control. If sizeX or sizeY are 0* and modeResize is ABSOLUTE will copy the current dimensions of* the control to m_sizeX/Y. So the appearance does not change* from the Dialog Editor*/PaneItem( CWnd* pWnd, ETSLayoutMgr* pMgr, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=0, int sizeYMin=0);/*** If your control is not mapped you can name it by its ChildID. Pass* the pMgr to receive the CWnd* of nID. * The rest as stated above*/PaneItem( UINT nID, ETSLayoutMgr* pMgr, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=0, int sizeYMin=0);public:/*** see PaneBase*/virtual int getConstrainHorz(int sizeParent);virtual int getConstrainVert(int sizeParent);virtual int getMinConstrainHorz();virtual int getMinConstrainVert();virtual int	getMaxConstrainHorz();virtual int	getMaxConstrainVert();virtual bool resizeTo(CRect& rcNewArea);bool	isDummy()				{ return (m_hwndCtrl == 0);	}protected:friend class ETSLayoutPropertySheet;/*** The horizontal size of the control (see m_modeResize)*/int				m_sizeX;int				m_sizeXMin;/*** The vertical size of the control (see m_modeResize)*/int				m_sizeY;int				m_sizeYMin;/*** Child Control pointer*/HWND			m_hwndCtrl;/*** Combo box needs special treatment*/bool			m_bComboSpecial;};/*** This class encapsulates a Subpane (and indeed the root Pane too)* it is a container of PaneBase* which it will recursivly resize*/class ETSGUI_EXT_CLASS Pane : public PaneBase {friend class ETSLayoutMgr;friend class CPaneBase;friend class CPane;friend class ETSLayoutPropertySheet;protected:/*** Tell the pane in which direction it is positioned. A HORIZONTAL pane* arranges it's subpanes from left to right, a VERTICAL from top to bottom*/Pane( ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0 );public:/*** If your control is not mapped you can name it by its ChildID. Pass* the pMgr to receive the CWnd* of nID. * The rest as stated above*/bool addItem( UINT nID, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=-1, int sizeYMin=-1);/*** Creates a new PaneItem from an Control. If sizeX or sizeY are 0* and modeResize is ABSOLUTE will copy the current dimensions of* the control to m_sizeX/Y. So the appearance does not change* from the Dialog Editor*/bool addItem( CWnd* pWnd, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=-1, int sizeYMin=-1);/*** Add a whitespace Item (paneNull) of variable size with* a minimum size of 0*/bool addItemGrowing();/*** Add a whitespace Item (paneNull) with fixed size*/bool addItemFixed(int size);/*** Add a whitespace Item (paneNull) of fixed size based on the* current layout (as in the dialog template). Based on the layout* of the pane vertical or horizontal spacing is considered** First argument is the left (top) item for a HORIZONTAL (VERTICAL) pane*/bool addItemSpaceBetween( CWnd* pWndFirst, CWnd* pWndSecond );bool addItemSpaceBetween( UINT nIDFirst, UINT nIDSecond );/*** Add a whitespace Item (paneNull) of fixed size based on the* size of another item*/bool addItemSpaceLike( CWnd* pWnd );bool addItemSpaceLike( UINT nID );/*** Add an item to the pane, appending at the end. This may be either obtained* by a call to ETSLayoutMgr::item() or one of the ETSLayoutMgr::paneXXX() calls*/bool addPane( CPaneBase pItem );bool addPane( CPane pSubpane, layResizeMode modeResize, int sizeSecondary /* = 0 */);virtual int		getConstrainHorz(int sizeParent);virtual int		getConstrainVert(int sizeParent);virtual int		getMinConstrainHorz();virtual int		getMinConstrainVert();virtual int		getMaxConstrainHorz();virtual int		getMaxConstrainVert();virtual bool	resizeTo(CRect& rcNewArea);/*** The destructor takes care of destroying all Subpanes and items*/virtual ~Pane();/*** Access to the orientation of this pane*/layOrientation	getOrientation() { return m_Orientation; };protected:int		resizeToAbsolute(int& availSpace, CArray<int,int>& sizePrimary, CArray<int,int>& sizeMin, CArray<int,int>& sizeMax);bool	resizeToRelative(int& availSpace, CArray<int,int>& sizePrimary, CArray<int,int>& sizeMin, CArray<int,int>& sizeMax);bool	resizeToGreedy(  int& availSpace, int nGreedy, CArray<int,int>& sizePrimary, CArray<int,int>& sizeMin, CArray<int,int>& sizeMax);/*** The orientation of the pane. Keep in mind that all subpanes* must have the complementary orientation, i.e. a VERTICAL* pane must have all HORIZONTAL SubPanes (or normal Items* of course)*/layOrientation					m_Orientation;/*** This array holds the pointers to the Items/SubPanes*/CArray<CPaneBase, CPaneBase>	m_paneItems;/*** The secondary constrain*/int				m_sizeSecondary;/** * Size of gap between childs*/int				m_sizeBorder;int				m_sizeExtraBorder;};/*** This class encapsulates a Subpane which is a Tab* it will use calls to AdjustRect to position it's* childs*/class ETSGUI_EXT_CLASS PaneTab : public Pane{friend class ETSLayoutMgr;protected:/*** Tell the pane in which direction it is positioned. A HORIZONTAL pane* arranges it's subpanes from left to right, a VERTICAL from top to bottom*/PaneTab( CTabCtrl* pTab, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0 );public:virtual int		getConstrainHorz(int sizeParent);virtual int		getConstrainVert(int sizeParent);virtual int		getMinConstrainHorz();virtual int		getMinConstrainVert();virtual int		getMaxConstrainHorz();virtual int		getMaxConstrainVert();virtual bool	resizeTo(CRect& rcNewArea);private:CTabCtrl* m_pTab;};/*** This class encapsulates a Subpane which is a Static* it will use calls to AdjustRect to position it's* childs*/class ETSGUI_EXT_CLASS PaneCtrl : public Pane{friend class ETSLayoutMgr;protected:/*** Tell the pane in which direction it is positioned. A HORIZONTAL pane* arranges it's subpanes from left to right, a VERTICAL from top to bottom*/PaneCtrl( CWnd* pCtrl, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0);PaneCtrl( UINT nID, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0 );public:virtual int		getConstrainHorz(int sizeParent);virtual int		getConstrainVert(int sizeParent);virtual int		getMinConstrainHorz();virtual int		getMinConstrainVert();virtual int		getMaxConstrainHorz();virtual int		getMaxConstrainVert();virtual bool	resizeTo(CRect& rcNewArea);private:HWND			m_hwndCtrl;int				m_sizeTopExtra;};ETSLayoutMgr(CWnd* pWnd)	{ m_pWnd = pWnd; m_sizeRootBorders = CSize(5,5); };virtual ~ETSLayoutMgr();virtual CRect GetRect() { CRect r; m_pWnd->GetClientRect(r); return r; };CWnd*	m_pWnd;CWnd*	GetWnd()		{ return m_pWnd; };void	setRootBorders(int cx, int cy)	{ m_sizeRootBorders = CSize(cx,cy); };/*** Pass this for a pseudo Pane with no content*/static CWnd*	paneNull;/*** Loads the current position and size from the registry using a supplied* key. Will be loaded with AfxGetApp()->WriteProfileXXX(). You may* specify a subfolder (e.g. Load( _T("MyDialog\\Layout") ); ). Will* load the following keys:** - lpstrRegKey+"SizeX";* - lpstrRegKey+"SizeY";* - lpstrRegKey+"PosX";* - lpstrRegKey+"PosY";** Is automatically called during OnActivate() if key specified in* constructor.*/bool Load(LPCTSTR lpstrRegKey);/*** Store the current position and size to the registry using a supplied* key. Will be stored with AfxGetApp()->WriteProfileXXX(). You may* specify a subfolder (e.g. Save( _T("MyDialog\\Layout") ); ). Will* create the following keys:** - lpstrRegKey+"SizeX";* - lpstrRegKey+"SizeY";* - lpstrRegKey+"PosX";* - lpstrRegKey+"PosY";** Is automatically called during DestroyWindow() if key specified in* constructor.*/bool Save(LPCTSTR lpstrRegKey);/*** Updates the layout after you specify the new* layout*/virtual void UpdateLayout();virtual void UpdateLayout(CPane p) {if(m_RootPane.IsValid()){// free old rootm_RootPane = 0;}m_RootPane = p;UpdateLayout();}/*** Does the actual Layout, called from OnSize()* Default implementation does nothing, use* IMPLEMENT_LAYOUT in your derived class (see above)*/virtual void Layout(CRect& rcClient);/*** Erasing only the these parts of the client area where* there is no child window. Extra-code for group-boxes * included!*/void EraseBkgnd(CDC* pDC);/*** Helperfunctions for the stream-interface. For usage see sample Application* and/or documentation.*//*** Create a new Pane. You may specify the resize* mode for both directions. If you add modes for the secondary direction* (i.e. *_VERT for a HORIZONTAL pane) then sizeSecondary is used as it's* size. If you do not specify sizeSecondary and the mode is ABSOLUTE_VERT* it will be computed as the maximum Height of all SubPanes (the same is* true for VERTICAL panes and subpanes with *_HORZ)*/CPane pane( layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeSecondary = 0);/*** Create one of the special control panes. Parameter are like pane(). For* additional information see documentation*/CPane paneTab( CTabCtrl* pTab, layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeSecondary = 0);CPane paneCtrl( UINT nID, layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0, int sizeSecondary = 0);CPane paneCtrl( CWnd* pCtrl, layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0, int sizeSecondary = 0);/*** Creates a new PaneItem for an Control. If sizeX or sizeY are 0* and modeResize is ABSOLUTE will copy the current dimensions of* the control to m_sizeX/Y. So the appearance does not change* from the Dialog Editor. size*Min = -1 means: do not make smaller* than in Dialog Template.*/CPaneBase item(UINT nID, layResizeMode modeResize = GREEDY, int sizeX =0, int sizeY =0, int sizeXMin =-1, int sizeYMin =-1);CPaneBase item(CWnd* pWnd, layResizeMode modeResize = GREEDY, int sizeX =0, int sizeY =0, int sizeXMin =-1, int sizeYMin =-1);/*** Add a whitespace Item (paneNull) of variable size with* a minimum size of 0*/CPaneBase itemGrowing(layOrientation orientation);/*** Add a whitespace Item (paneNull) with fixed size*/CPaneBase itemFixed(layOrientation orientation, int sizePrimary);/*** Add a whitespace Item (paneNull) of fixed size based on the* current layout (as in the dialog template). Based on the layout* of the pane vertical or horizontal spacing is considered** First argument is the left (top) item for a HORIZONTAL (VERTICAL) pane*/CPaneBase itemSpaceBetween( layOrientation orientation, CWnd* pWndFirst, CWnd* pWndSecond );CPaneBase itemSpaceBetween( layOrientation orientation, UINT nIDFirst, UINT nIDSecond );/*** Add a whitespace Item (paneNull) of fixed size based on the* size of another item*/CPaneBase itemSpaceLike( layOrientation orientation, CWnd* pWnd );CPaneBase itemSpaceLike( layOrientation orientation, UINT nID );protected:/*** This holds the root pane. Fill in InitDialog()*/CPane m_RootPane;/*** Create a root pane*/CPane CreateRoot(layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0 ){if(m_RootPane.IsValid()){// free old rootm_RootPane = 0;}m_RootPane = new Pane( this, orientation, sizeBorder, sizeExtraBorder);return m_RootPane;}/*** Key in Registry where to store Size*/CString m_strRegStore;/*** Borders around root*/CSize	m_sizeRootBorders;
};inline ETSLayoutMgr::layResizeMode operator|(const ETSLayoutMgr::layResizeMode m1, const ETSLayoutMgr::layResizeMode m2){ return (ETSLayoutMgr::layResizeMode)( (DWORD)m1|(DWORD)m2); }/*** Base class for the Layout function. Derive your own class* from this or derive it from CDialog and modify _all_* references to CDialog to ETSLayoutDialog*/
class ETSGUI_EXT_CLASS ETSLayoutDialog : public ETSLayout::CBaseDialog, protected ETSLayoutMgr
{
// Construction
public:ETSLayoutDialog(UINT nID, CWnd* pParent = NULL, LPCTSTR strName = NULL, bool bGripper = true);   // standard constructor// Dialog Data//{{AFX_DATA(ETSLayoutDialog)//}}AFX_DATA// Overrides// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(ETSLayoutDialog)//}}AFX_VIRTUAL// Implementation
protected:// Generated message map functions//{{AFX_MSG(ETSLayoutDialog)afx_msg void OnSize(UINT nType, int cx, int cy);afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);afx_msg BOOL OnEraseBkgnd(CDC* pDC);virtual BOOL OnInitDialog();afx_msg void OnDestroy();//}}AFX_MSGDECLARE_MESSAGE_MAP()virtual CRect GetRect();bool		m_bGripper;CStatusBar	m_StatusBar;
};/*** Base class for the Layout function. Derive your own class* from this or derive it from CDialog and modify _all_* references to CFormView to ETSLayoutFormView*/
class ETSGUI_EXT_CLASS ETSLayoutFormView : public ETSLayout::CBaseFormView, public ETSLayoutMgr
{
// ConstructionDECLARE_DYNAMIC(ETSLayoutFormView)
public:ETSLayoutFormView(UINT nID, LPCTSTR strName = NULL);   // standard constructorvirtual ~ETSLayoutFormView();//	virtual void UpdateLayout();// Overrides// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(ETSLayoutDialog)//}}AFX_VIRTUAL// Implementation
protected:// Generated message map functions//{{AFX_MSG(ETSLayoutDialog)afx_msg void OnSize(UINT nType, int cx, int cy);afx_msg BOOL OnEraseBkgnd(CDC* pDC);afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);//}}AFX_MSGDECLARE_MESSAGE_MAP()
};/*** Base class for the Layout function. Derive your own class* from this or derive it from CBCGDialogBar/CDialogBar and * modify _all_  references to CBCGDialogBar/CDialogBar to * ETSLayoutDialogBar*/
class ETSGUI_EXT_CLASS ETSLayoutDialogBar : public ETSLayout::CBaseDialogBar, protected ETSLayoutMgr
{
// Construction
public:
#ifdef CS_HELPETSLayoutDialogBar(UINT nID);
#elseETSLayoutDialogBar();
#endif// Overrides// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(ETSLayoutDialogBar)virtual CSize CalcDynamicLayout(int nLength, DWORD dwMode);//}}AFX_VIRTUAL/*** Override this to define Layout*/virtual BOOL Initialize() { return false; };virtual void UpdateLayout();// Implementation
protected:// Generated message map functions//{{AFX_MSG(ETSLayoutDialogBar)afx_msg void OnSize(UINT nType, int cx, int cy);afx_msg void OnDestroy();afx_msg BOOL OnEraseBkgnd(CDC* pDC);//}}AFX_MSGLRESULT OnInitDialog(WPARAM, LPARAM);DECLARE_MESSAGE_MAP()virtual CRect GetRect();bool	m_bInitialized;
};/**************************************************** ! the code is only tested for modal sheets ! ****************************************************//*** Resizable PropertySheet. Use this class standalone* or as your base class (instead CProptertySheet)*/
class ETSGUI_EXT_CLASS ETSLayoutPropertySheet : public CPropertySheet, protected ETSLayoutMgr
{DECLARE_DYNAMIC(ETSLayoutPropertySheet)// Construction
public:ETSLayoutPropertySheet(UINT nIDCaption, CWnd *pParentWnd = NULL, UINT iSelectPage = 0, LPCTSTR strName=NULL, bool bGripper=true);ETSLayoutPropertySheet(LPCTSTR pszCaption, CWnd *pParentWnd = NULL, UINT iSelectPage = 0, LPCTSTR strName=NULL, bool bGripper=true);// Operationen
public:void	SetAutoDestroy()		{ m_bAutoDestroy = true; }void	SetAutoDestroyPages()	{ m_bAutoDestroyPages = true; }void	ModelessWithButtons()	{ m_bModelessButtons = true; }
// Overridesvirtual void AddMainArea(CPane paneRoot, CPaneBase itemTab);virtual void AddButtons(CPane paneBottom);// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(ETSLayoutPropertySheet)public:virtual BOOL OnInitDialog();virtual void PostNcDestroy();//}}AFX_VIRTUAL// Implementation
public:virtual ~ETSLayoutPropertySheet();// Generated message map functions
protected://{{AFX_MSG(ETSLayoutPropertySheet)afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);afx_msg void OnSize(UINT nType, int cx, int cy);afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);afx_msg void OnDestroy();afx_msg BOOL OnEraseBkgnd(CDC* pDC);//}}AFX_MSGDECLARE_MESSAGE_MAP()void Resize(int cx, int cy);friend class ETSLayoutPropertyPage;void		Init(LPCTSTR strName, bool bGripper);CRect		m_rcStart;CRect		m_rcPage;bool		m_bGripper;CStatusBar	m_StatusBar;CPaneBase	m_ItemTab;bool		m_bAutoDestroy;bool		m_bAutoDestroyPages;bool		m_bModelessButtons;
};/*** Base class for the Layout function. Derive your own class* from this or derive it from CPropertyPage and * modify _all_  references to CPropertyPage to * ETSLayoutPropertyPage*/
class ETSGUI_EXT_CLASS ETSLayoutPropertyPage : public ETSLayout::CBasePropertyPage, protected ETSLayoutMgr
{
friend class ETSLayoutPropertySheet;DECLARE_DYNCREATE(ETSLayoutPropertyPage)// Konstruktion
public:ETSLayoutPropertyPage( );ETSLayoutPropertyPage( UINT nIDTemplate, UINT nIDCaption = 0 );ETSLayoutPropertyPage( LPCTSTR lpszTemplateName, UINT nIDCaption = 0 );~ETSLayoutPropertyPage();// Overrides// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(ETSLayoutPropertyPage)public:virtual BOOL OnSetActive();//}}AFX_VIRTUAL// Implementation
protected:// Generated message map functions//{{AFX_MSG(ETSLayoutPropertyPage)afx_msg void OnSize(UINT nType, int cx, int cy);afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);virtual BOOL OnInitDialog();afx_msg BOOL OnEraseBkgnd(CDC* pDC);afx_msg void OnWindowPosChanging( WINDOWPOS* lpwndpos );afx_msg void OnDestroy();afx_msg void OnWindowPosChanged(WINDOWPOS FAR* lpwndpos);//}}AFX_MSGDECLARE_MESSAGE_MAP()virtual CRect GetRect();bool m_bLockMove;bool m_bResetBuddyOnNextTimeVisible;
};//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.#endif // !defined(ETS_LAYOUTMGR_INCLUDED_)

ETSLayout.cpp

////////////////////////////////////////////
//         ___ ____ _________________     //
//        / _/_  _// _______________/     //
//       / _/ / / / /  ___ ___ ____       //
//      /__/ /_/ / / /   // _/_  _/       //
//     _________/ / / / // _/ / /         //
// (c) 1998-2000_/ /___//_/  /_/          //
//                                        //
////////////////////////////////////////////
//          all rights reserved           //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ETSLayoutDialog
//
// A class for smart layouting of Dialogs and such
//
// USAGE: See LayoutMgr.html
//
// AUTHOR: Erwin Tratar <tr@et-soft.de>
//
// DISCLAIMER:
//
// This Sourcecode and all accompaning material is ?998-1999 Erwin Tratar. 
// All rights reserved.
//
// The source code may be used in compiled form in any way you desire 
// (including usage in commercial applications), providing that your 
// application adds essential code (i.e. it is not only a wrapper) to the 
// functionality found here
//
// Redistribution of the sourcecode itself, publication in any media or 
// inclusion in a library requires the authors expressed written consent.
// You may not sale this code for profit.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. USE IT 
// AT YOUR OWN RISK! THE AUTHOR ACCEPTS NO LIABILITY FOR ANY DAMAGE/LOSS OF 
// BUSINESS THAT THIS PRODUCT MAY CAUSE.
//
//
// HISTORY: 
// 1998/05/1	Initial Release
// 1998/05/13	Added ability to have a Pane with a control
// 1998/05/13	Added better support for TabControls
// 1998/05/14	automatically set Icon to IDR_MAINFRAME
// 1998/05/19	no flicker on restoring position in OnInitialUpdate
//				Changed procedure for load/save, see constructor
// 1998/10/02	Added support for Maximum (tracking) size
// 1998/10/02	Much improved handling regarding RELATIVE/GREEDY
//              /w critical minimum size
// 1998/10/02	turn on/off gripper at lower right corner
// 1998/10/05   Support for user defined minimum size for items
//              (was hardcoded 5 before)
// 1998/10/07   Fix for FormViews
// 1998/10/31	Support for SECDialogBar/CDialogBar
// 1998/10/31	simplified interface
// 1998/10/31	Advanced positioning options
// 1998/10/31	Added paneNull for empty Pane (former: NULL)
// 1998/11/20	Swapped ETSLayoutDialog constructor parameters
// 1998/11/20	Added Pane::addItemSpaceBetween 
//				[Leo Zelevinsky]
// 1998/11/24	Added fixup for greedy panes
// 1998/11/24	addItemSpaceBetween now subtracts 2*nDefaultBorder
// 1998/11/24	addGrowing() added as a shortcut for a paneNull
// 1998/11/24	simplified interface: no more PaneBase:: / Pane:: 
//				needed
// 1998/11/24	added FILL_* Modes
// 1998/11/24	improved maximum size handling for greedy panes
// 1998/11/25	Fixup of greedy panes caused infinite loop in some 
//				cases
// 1999/01/07	addItemSpaceLike() added
// 1999/04/03   Fixed ETSLayoutFormView memory leak
// 1999/04/07   Fixed ALIGN_xCENTER
// 1999/04/08   New simple stream-interface added
// 1999/04/09   Added support for an empty Status-Bar for resizing 
//              instead of a gripper in the lower right corner
//              [Andreas Kapust]
// 1999/04/11   New code for much less flickering, OnEraseBkgnd()
//              overidden for this task
// 1999/05/12   Split Layout code into understandable pieces and adding
//              a lot of comments
// 1999/06/20   ABSOLUTE_X + ALIGN_FILL_X expands item if there is any
//              left space (after all Abs/Rel/Greedy processing is done)
// 1999/10/06   Changed Load() and Save() to use WINDOWPLACEMENT
//              [Keith Bussell]
// 1999/11/18   Added possibility to add panes of the same orientation
//              to another pane. This merges both panes in one big
//              pane with the same orientation
// 1999/11/18   Added support for BCGDialogBar (only with BCG > 4.52!)
// 1999/11/25   Addes support for PropertyPages/Sheets. Uses some code
//              of a code submission from Anreas Kapust
// 1999/11/25   Renamed classes to ETSLayoutXXX
// 1999/11/25   Use CreateRoot() and Root() instead of m_pRootPane in
//              derived class.
// 1999/11/26   Added autopointer support. No need to use normal pointers
//              when defining layout anymore. Changed m_pRootPane to 
//              m_RootPane
// 1999/11/26   Bug in Fixup Greedy II with multiple GREEDY panes and one
//              of them min/max limited
// 1999/11/28   Fixed PaneTab::getConstrainVert() for ABSOLUTE_VERT
// 1999/11/28   Fixed itemFixed()
// 1999/11/28   Changed DWORD modeResize Arguments to layModeResize for 
//              better type safety. Added typesafe operator|
// 1999/12/04   Don't reposition window in UpdateLayout if it's a child
//              (as a child Dialog or PropertyPage)
// 1999/12/04   Erase Backgroung with GCL_HBRBACKGROUND (if available) 
// 1999/12/04   itemSpaceXXX() adds a NORESIZE item instead of ABSOLUTE_XXX
//              this will fix unwanted growing in secondary direction
//
// Version: 1.0 [1999/12/04] Initial Article on CodeProject
//
// 1999/12/10   Erase Backgroung within TabCtrl was 'fixed' badly. Reverted to
//              old working code
// 2000/02/02   When the Dialog is child of a View the class works correctly
//              now [Didier BULTIAUW]
// 2000/02/15   Combo-Boxes were not working correctly (in all modes!)
// 2000/02/17   aligned SpinButton Controls (with buddy) now handled 
//              automatically
//              !! do not add such a control to the layout !! it is always
//              reattached to its buddy.
// 2000/02/17   changed some cotrol class names to the defined constants
//
// Version: 1.1 [2000/02/17]
//
// 2000/02/25   Fixed auto alignment of SpinButton Controls to only affect 
//              visible ones
// 2000/02/27   Put all the classes into the namespace 'ETSLayout'
// 2000/03/07   Fixed growing Dialog after minimizing and restoring
// 2000/05/22   Whole Statusbar (Gripper) is not excluded anymore in EraseBkgnd()
//              instead only the triangular Gripper is excluded
// 2000/05/31   Fix for PropertySheets with PSH_WIZARDHASFINISH [Th鰉mi]
// 2000/05/31   Fix for UpDown-Controls with EditCtrl Buddy in PropertyPages.
//              These were not repositioned every time the page is being show
//              until the first resize
// 2000/07/28   Problems with resizing ActiveX Controls fixed [Micheal Chapman]
// 2000/07/28   Some strings were not properly wrapped with _T()
// 2000/08/03   Check for BS_GROUPBOX was not correct as BS_GROUPBOX is more than one Bit
// 2000/08/03   New override AddMainArea added to ETSLayoutPropertySheet in order to 
//              have a hook for additional controls in a PropertySheet (besides the Tab)
// 2000/08/03   New override AddButtons added to ETSLayoutPropertySheet in order to 
//              have a hook for additional controls in the bottem pane of a PropertySheet
// 2000/08/03   Removed the need for DECLARE_LAYOUT
//
// Version: 1.2 [2000/08/05]#define OEMRESOURCE#include "stdafx.h"
#include "ETSLayout.h"using namespace ETSLayout;
#pragma warning(disable: 4097 4610 4510 4100)#ifndef OBM_SIZE
#define	OBM_SIZE		32766
// taken from WinresRc.h
// if not used for any reason
#endif#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endifstatic UINT auIDStatusBar[] = 
{ ID_SEPARATOR
};const int ERASE_GROUP_BORDER	= 10;
const int FIXUP_CUTOFF	= 5;
const int TAB_SPACE = 5;// the _NULL-Pane
CWnd* ETSLayoutMgr::paneNull = 0;void ETSLayoutMgr::Layout(CRect& rcClient)
{if(rcClient.Height() && rcClient.Width()  && m_RootPane.IsValid())	\m_RootPane->resizeTo(rcClient);									\
}ETSLayoutMgr::CPane ETSLayoutMgr::pane( layOrientation orientation, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeBorder /*=nDefaultBorder*/, int sizeExtraBorder /*=0*/, int sizeSecondary /*=0*/)
{Pane* pPane = new Pane ( this, orientation, sizeBorder, sizeExtraBorder );pPane->m_sizeSecondary = sizeSecondary;pPane->m_modeResize    = modeResize;return CPane(pPane);
}ETSLayoutMgr::CPane ETSLayoutMgr::paneTab( CTabCtrl* pTab, layOrientation orientation, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeBorder /*=nDefaultBorder*/, int sizeExtraBorder /*=0*/, int sizeSecondary /*=0*/)
{Pane* pPane = new PaneTab ( pTab, this, orientation, sizeBorder, sizeExtraBorder );pPane->m_sizeSecondary = sizeSecondary;pPane->m_modeResize    = modeResize;return CPane(pPane);
}ETSLayoutMgr::CPane ETSLayoutMgr::paneCtrl( CWnd* pCtrl, layOrientation orientation, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeBorder /*=nDefaultBorder*/, int sizeExtraBorder /*=0*/, int sizeTopExtra /*=0*/, int sizeSecondary /*=0*/)
{Pane* pPane = new PaneCtrl ( pCtrl, this, orientation, sizeBorder, sizeExtraBorder, sizeTopExtra );pPane->m_sizeSecondary = sizeSecondary;pPane->m_modeResize    = modeResize;return CPane(pPane);
}ETSLayoutMgr::CPane ETSLayoutMgr::paneCtrl( UINT nID, layOrientation orientation, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeBorder /*=nDefaultBorder*/, int sizeExtraBorder /*=0*/,int sizeTopExtra /*=0*/, int sizeSecondary /*=0*/)
{Pane* pPane = new PaneCtrl ( nID, this, orientation, sizeBorder, sizeExtraBorder, sizeTopExtra );pPane->m_sizeSecondary = sizeSecondary;pPane->m_modeResize    = modeResize;return CPane(pPane);
}ETSLayoutMgr::CPaneBase ETSLayoutMgr::item(UINT nID, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeX /*=0*/, int sizeY /*=0*/, int sizeXMin /*=-1*/, int sizeYMin /*=-1*/)
{return new PaneItem( nID, this, modeResize, sizeX, sizeY, sizeXMin, sizeYMin);
}ETSLayoutMgr::CPaneBase ETSLayoutMgr::item(CWnd* pWnd, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/,int sizeX /*=0*/, int sizeY /*=0*/, int sizeXMin /*=-1*/, int sizeYMin /*=-1*/)
{return new PaneItem( pWnd, this, modeResize, sizeX, sizeY, sizeXMin, sizeYMin);
}ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemFixed(layOrientation orientation, int sizePrimary)
{CPaneBase p = new PaneItem(paneNull, this, NORESIZE, (orientation==HORIZONTAL)?sizePrimary:0, (orientation==VERTICAL)?sizePrimary:0);return p;
}ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemGrowing(layOrientation orientation)
{return new PaneItem(paneNull, this, (orientation==HORIZONTAL)?ABSOLUTE_VERT:ABSOLUTE_HORZ, 0, 0, -nDefaultBorder, -nDefaultBorder);
}ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceBetween( layOrientation orientation, CWnd* pWndFirst, CWnd* pWndSecond )
{if( orientation == HORIZONTAL ) {// I'm interested in horizontal spacingCRect rLeft, rRight;pWndFirst->GetWindowRect(&rLeft);pWndSecond->GetWindowRect(&rRight);int sizeX = rRight.left - rLeft.right;if( sizeX < 0 ) {// compare top to topsizeX = rRight.left - rLeft.left;}else {sizeX -= 2*nDefaultBorder;}return new PaneItem(paneNull, this, NORESIZE, sizeX, 0);}else {// I'm interested in vertical spacingCRect rTop, rBot;pWndFirst->GetWindowRect(&rTop);pWndSecond->GetWindowRect(&rBot);int sizeY = rBot.top - rTop.bottom;if( sizeY < 0 ) {// compare top to topsizeY = sizeY = rBot.top - rTop.top;}else {sizeY -= 2*nDefaultBorder;}return new PaneItem(paneNull, this, NORESIZE, 0, sizeY);}
}ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceBetween( layOrientation orientation, UINT nIDFirst, UINT nIDSecond )
{CWnd *pFirst	= GetWnd()->GetDlgItem(nIDFirst);CWnd *pSecond	= GetWnd()->GetDlgItem(nIDSecond);ASSERT( pFirst && pSecond );return itemSpaceBetween( orientation, pFirst, pSecond );
}ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceLike( layOrientation orientation, CWnd* pWnd )
{CRect rRect;pWnd->GetWindowRect(&rRect);if( orientation == HORIZONTAL ) {// I'm interested in horizontal spacingreturn new PaneItem(paneNull, this, NORESIZE, rRect.Width(), 0);}else {// I'm interested in vertical spacingreturn new PaneItem(paneNull, this, NORESIZE, 0, rRect.Height() );}}ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceLike( layOrientation orientation, UINT nID )
{CWnd *pWnd	= GetWnd()->GetDlgItem(nID);ASSERT( pWnd );return itemSpaceLike( orientation, pWnd );
}ETSLayoutMgr::~ETSLayoutMgr()
{
}void ETSLayoutMgr::UpdateLayout()
{if(!m_RootPane)return;// Check constraintsCRect rcClient = GetRect();if( m_pWnd->IsKindOf( RUNTIME_CLASS( CDialog ) ) && !(m_pWnd->GetStyle()&WS_CHILD) ) {CRect rcWindow;m_pWnd->GetWindowRect(rcWindow);// Added by Didier BULTIAUWCWnd* parentWnd = m_pWnd->GetParent();if( (parentWnd != 0) && parentWnd->IsKindOf(RUNTIME_CLASS(CView)) ){CRect rcParent;parentWnd->GetWindowRect(rcParent);rcWindow.OffsetRect(-rcParent.left,-rcParent.top);}// end addCRect rcBorder = rcWindow;rcBorder -= rcClient;// Min and Max infoint minWidth	= m_RootPane->getMinConstrainHorz() + rcBorder.Width()  + 2*m_sizeRootBorders.cx;int minHeight	= m_RootPane->getMinConstrainVert() + rcBorder.Height() + 2*m_sizeRootBorders.cy;int maxWidth	= m_RootPane->getMaxConstrainHorz();if(maxWidth != -1) {maxWidth += rcBorder.Width()  + 2*m_sizeRootBorders.cx;maxWidth = max(maxWidth, minWidth);}int maxHeight	= m_RootPane->getMaxConstrainVert();if(maxHeight != -1) {maxHeight += rcBorder.Height() + 2*m_sizeRootBorders.cy;maxHeight = max(maxHeight, minHeight);}if(rcWindow.Width() < minWidth)rcWindow.right = rcWindow.left + minWidth;if(rcWindow.Height() < minHeight)rcWindow.bottom = rcWindow.top + minHeight;if(maxWidth != -1  && rcWindow.Width() > maxWidth)rcWindow.right = rcWindow.left + maxWidth;if(maxHeight != -1 && rcWindow.Height() > maxHeight)rcWindow.bottom = rcWindow.top + maxHeight;m_pWnd->MoveWindow(rcWindow);}// Do the LayoutrcClient = GetRect();// Add a Border around the rootPanercClient.top	+= m_sizeRootBorders.cy;rcClient.bottom -= m_sizeRootBorders.cy;rcClient.left	+= m_sizeRootBorders.cx;rcClient.right	-= m_sizeRootBorders.cx;if(GetWnd()->IsWindowVisible()) {// Avoid ugly artifacts//GetWnd()->SetRedraw(FALSE);Layout(rcClient);//GetWnd()->SetRedraw(TRUE);}elseLayout(rcClient);// Take special care of SpinButtons (Up-Down Controls) with Buddy set, enumerate// all childs:CWnd* pWndChild = GetWnd()->GetWindow(GW_CHILD);TCHAR szClassName[ MAX_PATH ];while(pWndChild){::GetClassName( pWndChild->GetSafeHwnd(), szClassName, MAX_PATH );DWORD dwStyle = pWndChild->GetStyle();// is it a SpinButton?if( _tcscmp(szClassName, UPDOWN_CLASS)==0 && ::IsWindowVisible(pWndChild->GetSafeHwnd()) ) {HWND hwndBuddy = (HWND)::SendMessage( pWndChild->GetSafeHwnd(), UDM_GETBUDDY, 0, 0);if( hwndBuddy != 0 && (dwStyle&(UDS_ALIGNRIGHT|UDS_ALIGNLEFT)) != 0 ){// reset Buddy::SendMessage( pWndChild->GetSafeHwnd(), UDM_SETBUDDY, (WPARAM)hwndBuddy, 0);}}pWndChild = pWndChild->GetWindow(GW_HWNDNEXT);}GetWnd()->Invalidate();
}bool ETSLayoutMgr::Save(LPCTSTR lpstrRegKey)
{CRect rcWnd;if(IsWindow(GetWnd()->m_hWnd)){WINDOWPLACEMENT wp;if(GetWnd()->GetWindowPlacement(&wp)){// Make sure we don't pop up // minimized the next timeif(wp.showCmd != SW_SHOWMAXIMIZED)wp.showCmd = SW_SHOWNORMAL;AfxGetApp()->WriteProfileBinary(lpstrRegKey, _T("WindowPlacement"), reinterpret_cast<LPBYTE>(&wp), sizeof(wp));}}return true;
}bool ETSLayoutMgr::Load(LPCTSTR lpstrRegKey)
{LPBYTE pbtData = 0;UINT nSize = 0;if(AfxGetApp()->GetProfileBinary(lpstrRegKey,_T("WindowPlacement"), &pbtData, &nSize)){WINDOWPLACEMENT* pwp = reinterpret_cast<WINDOWPLACEMENT*>(pbtData);ASSERT(nSize == sizeof(WINDOWPLACEMENT));if(nSize == sizeof(WINDOWPLACEMENT))GetWnd()->SetWindowPlacement(reinterpret_cast<WINDOWPLACEMENT*>(pbtData));delete [] pbtData;}return true;
}void ETSLayoutMgr::EraseBkgnd(CDC* pDC)
{CRect	rcClient;GetWnd()->GetClientRect( rcClient );CRgn	rgn;rgn.CreateRectRgnIndirect(rcClient);TRACE("CreateRgn (%d,%d,%d,%d)\n", rcClient.left, rcClient.top, rcClient.right, rcClient.bottom );CRgn    rgnRect;rgnRect.CreateRectRgn(0,0,0,0);CRect	rcChild;CWnd* pWndChild = GetWnd()->GetWindow( GW_CHILD );TCHAR szClassName[ MAX_PATH ];pDC->SelectClipRgn(NULL);while( pWndChild ) {pWndChild->GetWindowRect(rcChild);GetWnd()->ScreenToClient( rcChild );rgnRect.SetRectRgn( rcChild );::GetClassName( pWndChild->GetSafeHwnd(), szClassName, MAX_PATH );DWORD dwStyle = pWndChild->GetStyle();// doesn't make sense for hidden childrenif( dwStyle & WS_VISIBLE ) {// Fix: BS_GROUPBOX is more than one Bit, extend check to (dwStyle & BS_GROUPBOX)==BS_GROUPBOX [ET]if( _tcscmp(szClassName,_T("Button"))==0 && (dwStyle & BS_GROUPBOX)==BS_GROUPBOX ) {// it is a group-box, ignore completely}else if( _tcscmp(szClassName,WC_TABCONTROL )==0 ) {// ignore Tab-Control's inside rectstatic_cast<CTabCtrl*>(pWndChild)->AdjustRect(FALSE,rcChild);CRgn rgnContent;rgnContent.CreateRectRgnIndirect(rcChild);rgnRect.CombineRgn( &rgnRect, &rgnContent, RGN_DIFF );rgn.CombineRgn( &rgn, &rgnRect, RGN_DIFF );}else if( _tcscmp(szClassName,STATUSCLASSNAME)==0 ) {CPoint ptTriangleGrip[3];ptTriangleGrip[0] = CPoint(rcChild.right,rcChild.top);ptTriangleGrip[1] = CPoint(rcChild.right,rcChild.bottom);ptTriangleGrip[2] = CPoint(rcChild.right-rcChild.Height(),rcChild.bottom);CRgn rgnGripper;rgnGripper.CreatePolygonRgn(ptTriangleGrip,3, WINDING);rgn.CombineRgn( &rgn, &rgnGripper, RGN_DIFF );}else {rgn.CombineRgn( &rgn, &rgnRect, RGN_DIFF );}}pWndChild = pWndChild->GetNextWindow();}HBRUSH hBrBack = (HBRUSH) ::GetClassLong(GetWnd()->GetSafeHwnd(), GCL_HBRBACKGROUND) ;if( hBrBack == 0 )hBrBack = ::GetSysColorBrush(COLOR_BTNFACE);pDC->FillRgn( &rgn, CBrush::FromHandle( hBrBack ));}/////////////////////////////////////////////////////////////////////////////
// ETSLayoutMgr::PaneItem implementationETSLayoutMgr::PaneItem::PaneItem(CWnd* pWnd, ETSLayoutMgr* pMgr, ETSLayoutMgr::layResizeMode modeResize/*=GREEDY*/, int sizeX/*=0*/, int sizeY/*=0*/, int sizeXMin/*=-1*/, int sizeYMin/*=-1*/ ) : PaneBase( pMgr )
{m_modeResize	= modeResize;m_hwndCtrl		= pWnd->GetSafeHwnd();m_sizeX			= 0;m_sizeY			= 0;m_bComboSpecial = false;m_sizeXMin		= sizeXMin;m_sizeYMin		= sizeYMin;if(!m_hwndCtrl) {			// only Dummy!m_sizeX = sizeX;m_sizeY = sizeY;}else {CRect rcControl;::GetWindowRect(m_hwndCtrl, &rcControl);if(sizeX == 0) {m_sizeX			= rcControl.Width();}else {m_sizeX = sizeX;}if( m_sizeXMin == -1 ) {// do not make smaller than current sizem_sizeXMin		= rcControl.Width();}if(sizeY == 0) {m_sizeY			= rcControl.Height();}else {m_sizeY = sizeY;}if( m_sizeYMin == -1 ) {// do not make smaller than current sizem_sizeYMin		= rcControl.Height();}TCHAR szClassName[ MAX_PATH ];::GetClassName( m_hwndCtrl, szClassName, MAX_PATH );// special treatment for combo-boxesif( _tcscmp(szClassName,_T("ComboBox"))==0 || _tcscmp(szClassName,WC_COMBOBOXEX)==0) {m_bComboSpecial = true;}}
}ETSLayoutMgr::PaneItem::PaneItem( UINT nID, ETSLayoutMgr* pMgr, ETSLayoutMgr::layResizeMode modeResize/*=GREEDY*/, int sizeX/*=0*/, int sizeY/*=0*/, int sizeXMin/*=-1*/, int sizeYMin/*=-1*/ ) : PaneBase( pMgr )
{CWnd* pWnd		= pMgr->GetWnd()->GetDlgItem(nID);m_hwndCtrl		= pWnd->GetSafeHwnd();m_sizeX			= 0;m_sizeY			= 0;m_bComboSpecial = false;m_modeResize	= modeResize;m_sizeXMin = sizeXMin;m_sizeYMin = sizeYMin;if(!m_hwndCtrl) {			// only Dummy!m_sizeX = sizeX;m_sizeY = sizeY;}else {CRect rcControl;::GetWindowRect(m_hwndCtrl, &rcControl);if(sizeX == 0) {m_sizeX			= rcControl.Width();}else {m_sizeX = sizeX;}if( m_sizeXMin == -1 ) {// do not make smaller than current sizem_sizeXMin		= rcControl.Width();}if(sizeY == 0) {m_sizeY			= rcControl.Height();}else {m_sizeY = sizeY;}if( m_sizeYMin == -1 ) {// do not make smaller than current sizem_sizeYMin		= rcControl.Height();}TCHAR szClassName[ MAX_PATH ];::GetClassName( m_hwndCtrl, szClassName, MAX_PATH );// special treatment for combo-boxesif( _tcscmp(szClassName,_T("ComboBox"))==0 || _tcscmp(szClassName,WC_COMBOBOXEX)==0) {m_bComboSpecial = true;}}
}int ETSLayoutMgr::PaneItem::getConstrainHorz(int sizeParent) 
{if( m_modeResize & ABSOLUTE_HORZ) {return m_sizeX;	}if(m_modeResize & RELATIVE_HORZ) {return (sizeParent * m_sizeX) / 100;	}return -1;
}int ETSLayoutMgr::PaneItem::getConstrainVert(int sizeParent) 
{if(m_modeResize & ABSOLUTE_VERT) {return m_sizeY;	}if(m_modeResize & RELATIVE_VERT) {return (sizeParent * m_sizeY) / 100;	}return -1;
}int ETSLayoutMgr::PaneItem::getMinConstrainHorz() 
{if(m_modeResize & ABSOLUTE_HORZ) {return m_sizeX;	}return max(nMinConstrain,m_sizeXMin);
}int ETSLayoutMgr::PaneItem::getMinConstrainVert() 
{if(m_modeResize & ABSOLUTE_VERT) {return m_sizeY;	}return max(nMinConstrain,m_sizeYMin);
}int ETSLayoutMgr::PaneItem::getMaxConstrainHorz() 
{if(m_modeResize & ABSOLUTE_HORZ) {return m_sizeX;	}return -1;
}int ETSLayoutMgr::PaneItem::getMaxConstrainVert() 
{if(m_modeResize & ABSOLUTE_VERT) {return m_sizeY;	}return -1;	
}bool ETSLayoutMgr::PaneItem::resizeTo(CRect& rcNewArea) 
{if(m_hwndCtrl) {CRect rcWnd;::GetWindowRect( m_hwndCtrl, rcWnd );if( !(m_modeResize & ALIGN_FILL_HORZ) && m_modeResize & ABSOLUTE_HORZ ) {if( (m_modeResize & ALIGN_HCENTER) == ALIGN_HCENTER ) {rcNewArea.OffsetRect( (rcNewArea.Width() - rcWnd.Width())/2, 0 ); }else if( m_modeResize & ALIGN_RIGHT ) {rcNewArea.OffsetRect( rcNewArea.Width() - rcWnd.Width(), 0 ); }rcNewArea.right = rcNewArea.left + rcWnd.Width();}if( !(m_modeResize & ALIGN_FILL_VERT) && m_modeResize & ABSOLUTE_VERT ) {if( (m_modeResize & ALIGN_VCENTER) == ALIGN_VCENTER ) {rcNewArea.OffsetRect( 0, (rcNewArea.Height()-rcWnd.Height())/2 ); }else if( m_modeResize & ALIGN_BOTTOM ) {rcNewArea.OffsetRect( 0, rcNewArea.Height() - rcWnd.Height()); }rcNewArea.bottom = rcNewArea.top + rcWnd.Height();}DWORD dwStyle = ::GetWindowLong( m_hwndCtrl, GWL_STYLE );// special treatment for combo-boxesif( m_bComboSpecial && (dwStyle & CBS_DROPDOWN) ) {// keep height (though only fully visible when dropped down)rcNewArea.bottom = rcNewArea.top + rcWnd.Height();}// FIX: ::MoveWindow would case problems with some ActiveX Controls [Micheal Chapman]CWnd* pTempWnd = CWnd::FromHandle( m_hwndCtrl );pTempWnd->MoveWindow( rcNewArea.left, rcNewArea.top, rcNewArea.Width(), rcNewArea.Height() );if( m_bComboSpecial && !(dwStyle & CBS_DROPDOWN) && !(dwStyle & CBS_NOINTEGRALHEIGHT) ) {// Keep CB Size = Edit + LB ( if not CBS_NOINTEGRALHEIGHT)::GetWindowRect( m_hwndCtrl, rcWnd );CRect rcListBox;HWND hwndListBox = ::GetDlgItem(m_hwndCtrl, 1000); // ListBox of CBif( hwndListBox != 0 ){::GetWindowRect( hwndListBox, rcListBox );rcWnd.bottom = rcListBox.bottom;rcNewArea.bottom = rcNewArea.top + rcWnd.Height();// FIX: ::MoveWindow would case problems with some ActiveX Controls [Micheal Chapman]CWnd* pTempWnd = CWnd::FromHandle( m_hwndCtrl );pTempWnd->MoveWindow( rcNewArea.left, rcNewArea.top, rcNewArea.Width(), rcNewArea.Height(), true );}}::RedrawWindow(m_hwndCtrl,0,0, RDW_INVALIDATE | RDW_UPDATENOW ); }return true;
}/////////////////////////////////////////////////////////////////////////////
// ETSLayoutMgr::PaneTab implementationETSLayoutMgr::PaneTab::PaneTab( CTabCtrl* pTab, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /*= nDefaultBorder*/, int sizeExtraBorder /*= 0*/ )
: ETSLayoutMgr::Pane(pMgr, orientation, sizeBorder, sizeExtraBorder) 
{ASSERT(pTab);m_pTab = pTab;
}int ETSLayoutMgr::PaneTab::getConstrainHorz(int sizeParent)
{CRect rcTab;m_pTab->AdjustRect(TRUE, &rcTab);if(rcTab.Width() > sizeParent)return rcTab.Width();return Pane::getConstrainHorz(sizeParent /*- rcTab.Width()*/);
}int ETSLayoutMgr::PaneTab::getConstrainVert(int sizeParent)
{CRect rcTab;m_pTab->AdjustRect(TRUE, &rcTab);if( m_modeResize & ABSOLUTE_VERT ) {return m_sizeSecondary + rcTab.Height();}if(rcTab.Height() > sizeParent)return rcTab.Height();return Pane::getConstrainVert(sizeParent /*- rcTab.Height()*/);
}int ETSLayoutMgr::PaneTab::getMinConstrainHorz()
{CRect rcTab(0,0,0,0);m_pTab->AdjustRect(TRUE, &rcTab);return Pane::getMinConstrainHorz() + rcTab.Width() ;
}int ETSLayoutMgr::PaneTab::getMinConstrainVert()
{CRect rcTab(0,0,0,0);m_pTab->AdjustRect(TRUE, &rcTab);return Pane::getMinConstrainVert() + rcTab.Height();
}int ETSLayoutMgr::PaneTab::getMaxConstrainHorz()
{CRect rcTab(0,0,0,0);m_pTab->AdjustRect(TRUE, &rcTab);int paneMax = Pane::getMaxConstrainHorz();return (paneMax != -1) ? paneMax + rcTab.Width() : -1;
}int ETSLayoutMgr::PaneTab::getMaxConstrainVert()
{CRect rcTab(0,0,0,0);m_pTab->AdjustRect(TRUE, &rcTab);int paneMax = Pane::getMaxConstrainVert();return (paneMax != -1) ? paneMax + rcTab.Height() : -1;
}bool ETSLayoutMgr::PaneTab::resizeTo(CRect& rcNewArea)
{m_pTab->MoveWindow(rcNewArea);m_pTab->AdjustRect(FALSE,rcNewArea);return Pane::resizeTo(rcNewArea);
}/////////////////////////////////////////////////////////////////////////////
// ETSLayoutMgr::PaneCtrl implementationETSLayoutMgr::PaneCtrl::PaneCtrl( CWnd* pCtrl, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /*= nDefaultBorder*/, int sizeExtraBorder /*= 0*/, int sizeTopExtra /*= 0*/ )
: ETSLayoutMgr::Pane(pMgr, orientation, sizeBorder, sizeExtraBorder)
{m_sizeTopExtra = sizeTopExtra;ASSERT(pCtrl);m_hwndCtrl = pCtrl->GetSafeHwnd();
}ETSLayoutMgr::PaneCtrl::PaneCtrl( UINT nID, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /*= nDefaultBorder*/, int sizeExtraBorder /*= 0*/, int sizeTopExtra /*= 0*/ )
: ETSLayoutMgr::Pane(pMgr, orientation, sizeBorder, sizeExtraBorder)
{m_sizeTopExtra = sizeTopExtra;m_hwndCtrl = ::GetDlgItem(pMgr->GetWnd()->GetSafeHwnd(), nID);ASSERT(m_hwndCtrl);
}int ETSLayoutMgr::PaneCtrl::getConstrainHorz(int sizeParent)
{return Pane::getConstrainHorz(sizeParent) ;
}int ETSLayoutMgr::PaneCtrl::getConstrainVert(int sizeParent)
{return Pane::getConstrainVert(sizeParent);
}int ETSLayoutMgr::PaneCtrl::getMinConstrainHorz()
{return Pane::getMinConstrainHorz();
}int ETSLayoutMgr::PaneCtrl::getMinConstrainVert()
{return Pane::getMinConstrainVert() + m_sizeTopExtra;
}int ETSLayoutMgr::PaneCtrl::getMaxConstrainHorz()
{int paneMax = Pane::getMaxConstrainHorz();return ( paneMax == -1) ? -1 : paneMax ;
}int ETSLayoutMgr::PaneCtrl::getMaxConstrainVert()
{int paneMax = Pane::getMaxConstrainVert();return ( paneMax == -1) ? -1 : paneMax + m_sizeTopExtra;
}bool ETSLayoutMgr::PaneCtrl::resizeTo(CRect& rcNewArea)
{// FIX: ::MoveWindow would case problems with some ActiveX Controls [Micheal Chapman]CWnd* pTempWnd = CWnd::FromHandle( m_hwndCtrl );pTempWnd->MoveWindow( rcNewArea.left, rcNewArea.top, rcNewArea.Width(), rcNewArea.Height(), true );::RedrawWindow(m_hwndCtrl,0,0, RDW_INVALIDATE | RDW_UPDATENOW |RDW_ERASE); rcNewArea.top	+= m_sizeTopExtra;return Pane::resizeTo(rcNewArea);
}/////////////////////////////////////////////////////////////////////////////
// ETSLayoutMgr::Pane implementationETSLayoutMgr::Pane::Pane( ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /* = nDefaultBorder */, int sizeExtraBorder /*= 0*/) 
: PaneBase(pMgr)
{m_Orientation	= orientation;m_sizeBorder	= sizeBorder;m_sizeSecondary	= 0;m_modeResize	= 0;m_sizeExtraBorder= sizeExtraBorder;
}ETSLayoutMgr::Pane::~Pane() 
{
}bool ETSLayoutMgr::Pane::addItem( CWnd* pWnd, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeX /*=0*/, int sizeY /*=0*/, int sizeXMin /*=0*/, int sizeYMin /*=0*/)
{CPaneBase pItem = new PaneItem( pWnd, m_pMgr, modeResize, sizeX, sizeY, sizeXMin, sizeYMin);return addPane( pItem );
}bool ETSLayoutMgr::Pane::addItem( UINT nID, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeX /*=0*/, int sizeY /*=0*/, int sizeXMin /*=0*/, int sizeYMin /*=0*/)
{CPaneBase pItem = new PaneItem( nID, m_pMgr, modeResize, sizeX, sizeY, sizeXMin, sizeYMin);return addPane( pItem );
}bool ETSLayoutMgr::Pane::addItemFixed(int size)
{CPaneBase pNewItem = m_pMgr->itemFixed(m_Orientation, size);return addPane( pNewItem );
}bool ETSLayoutMgr::Pane::addItemGrowing()
{CPaneBase pNewItem = m_pMgr->itemGrowing(m_Orientation);return addPane( pNewItem );
}bool ETSLayoutMgr::Pane::addItemSpaceBetween( CWnd* pWndFirst, CWnd* pWndSecond )
{CPaneBase pNewItem = m_pMgr->itemSpaceBetween(m_Orientation, pWndFirst, pWndSecond);return addPane( pNewItem );
}bool ETSLayoutMgr::Pane::addItemSpaceBetween( UINT nIDFirst, UINT nIDSecond )
{CPaneBase pNewItem = m_pMgr->itemSpaceBetween(m_Orientation, nIDFirst, nIDSecond);return addPane( pNewItem );
}bool ETSLayoutMgr::Pane::addItemSpaceLike( CWnd* pWnd )
{CPaneBase pNewItem = m_pMgr->itemSpaceLike(m_Orientation, pWnd);return addPane( pNewItem );
}bool ETSLayoutMgr::Pane::addItemSpaceLike( UINT nID )
{CPaneBase pNewItem = m_pMgr->itemSpaceLike(m_Orientation, nID);return addPane( pNewItem );
}bool ETSLayoutMgr::Pane::addPane( CPane pSubpane, ETSLayoutMgr::layResizeMode modeResize, int sizeSecondary /* = 0 */) 
{if( pSubpane->getOrientation() == m_Orientation){// wrap in subpane of opposite orientationCPane pPaneWrap = new Pane(m_pMgr, m_Orientation==HORIZONTAL?VERTICAL:HORIZONTAL,0,0);pPaneWrap->addPane( pSubpane  );addPane( pPaneWrap, modeResize, sizeSecondary );}else{pSubpane->m_modeResize = modeResize;if(m_Orientation==HORIZONTAL && (modeResize & ABSOLUTE_HORZ) ) {if(sizeSecondary == 0) {pSubpane->m_sizeSecondary = pSubpane->getMinConstrainHorz();}}else if(m_Orientation==HORIZONTAL && (modeResize & RELATIVE_HORZ) ) {pSubpane->m_sizeSecondary = sizeSecondary;}else if(m_Orientation==VERTICAL && (modeResize & ABSOLUTE_VERT) ) {if(sizeSecondary == 0) {pSubpane->m_sizeSecondary = pSubpane->getMinConstrainVert();}}else if(m_Orientation==VERTICAL && (modeResize & RELATIVE_VERT) ) {pSubpane->m_sizeSecondary = sizeSecondary;}m_paneItems.Add(pSubpane);}return true;
}bool ETSLayoutMgr::Pane::addPane( CPaneBase pItem ) 
{m_paneItems.Add(pItem);return true;
}int ETSLayoutMgr::Pane::getConstrainHorz(int sizeParent) 
{ASSERT( m_Orientation == VERTICAL);if( m_modeResize & RELATIVE_HORZ ) {return (sizeParent * m_sizeSecondary) / 100;}else if( m_modeResize & ABSOLUTE_HORZ ){return m_sizeSecondary;}elsereturn 0;
}int ETSLayoutMgr::Pane::getConstrainVert(int sizeParent) 
{ASSERT( m_Orientation == HORIZONTAL);if( m_modeResize & RELATIVE_VERT ) {return (sizeParent * m_sizeSecondary) / 100;}else if( m_modeResize & ABSOLUTE_VERT ) {return m_sizeSecondary;}else {return 0;}
}int ETSLayoutMgr::Pane::getMaxConstrainHorz() 
{if(m_Orientation == HORIZONTAL) {int nMaxConstr = -1;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];int nConstrain = pItem->getMaxConstrainHorz();if(nConstrain == -1)return -1;nMaxConstr += nConstrain;}return (nMaxConstr == -1) ? -1 : nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder;}else if( m_modeResize & ABSOLUTE_HORZ && m_sizeSecondary!=0) {return m_sizeSecondary; // + 2*m_sizeExtraBorder;}else {int nMaxConstr = -1;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];int nConstrain = pItem->getMaxConstrainHorz();if( nConstrain == -1)return -1;elsenMaxConstr = max(nMaxConstr, nConstrain);}return (nMaxConstr == -1) ? -1 : nMaxConstr + 2*m_sizeExtraBorder;}
}int ETSLayoutMgr::Pane::getMaxConstrainVert() 
{if(m_Orientation == VERTICAL) {int nMaxConstr = -1;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];int nConstrain = pItem->getMaxConstrainVert();if(nConstrain == -1)return -1;nMaxConstr += nConstrain;}return (nMaxConstr == -1) ? -1 : nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder;}else if( m_modeResize & ABSOLUTE_VERT && m_sizeSecondary!=0) {return m_sizeSecondary; // + 2*m_sizeExtraBorder;}else {int nMaxConstr = -1;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];int nConstrain = pItem->getMaxConstrainVert();if( nConstrain == -1)return -1;elsenMaxConstr = max(nMaxConstr, nConstrain);}return (nMaxConstr == -1) ? -1 : nMaxConstr + 2*m_sizeExtraBorder;}
}int ETSLayoutMgr::Pane::getMinConstrainHorz() 
{if(m_Orientation == HORIZONTAL) {int nMaxConstr = 0;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];nMaxConstr += max(nMinConstrain, pItem->getMinConstrainHorz());}return nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder;}else if( m_modeResize & ABSOLUTE_HORZ && m_sizeSecondary!=0) {return m_sizeSecondary; // + 2*m_sizeExtraBorder;}else {int nMaxConstr = 0;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];int nConstrain = pItem->getMinConstrainHorz();nMaxConstr = max(nMaxConstr, nConstrain);}return nMaxConstr + 2*m_sizeExtraBorder;}
}int ETSLayoutMgr::Pane::getMinConstrainVert() 
{if(m_Orientation == VERTICAL) {int nMaxConstr = 0;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];nMaxConstr += max(nMinConstrain, pItem->getMinConstrainVert());}return nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder;}else if( m_modeResize & ABSOLUTE_VERT && m_sizeSecondary!=0) {return m_sizeSecondary; // + 2*m_sizeExtraBorder;}else {int nMaxConstr = 0;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];int nConstrain = pItem->getMinConstrainVert();nMaxConstr = max(nMaxConstr, nConstrain);}return nMaxConstr + 2*m_sizeExtraBorder;}
}int ETSLayoutMgr::Pane::resizeToAbsolute(int& availSpace, CArray<int,int>& sizePrimary, CArray<int,int>& sizeMin, CArray<int,int>& sizeMax)
{// count all greedy items as returnvalueint nGreedy = 0;// first, subtract all absoulute items from available spacefor(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];if( m_Orientation == HORIZONTAL ) {// for absolute items subtract their size from available spaceif(pItem->modeResize() & ABSOLUTE_HORZ) {availSpace -= (sizePrimary[i] = pItem->getConstrainHorz(0));}// count Greedy items for laterif(!(pItem->modeResize() & ABSOLUTE_HORZ) && !(pItem->modeResize() & RELATIVE_HORZ)) {nGreedy++;}sizeMin[i] = pItem->getMinConstrainHorz();sizeMax[i] = pItem->getMaxConstrainHorz();}else {// for absolute items subtract their size from available spaceif(pItem->modeResize() & ABSOLUTE_VERT) {availSpace -= (sizePrimary[i] = pItem->getConstrainVert(0));}// count Greedy items for laterif(!(pItem->modeResize() & ABSOLUTE_VERT) && !(pItem->modeResize() & RELATIVE_VERT)) {nGreedy++;}sizeMin[i] = pItem->getMinConstrainVert();sizeMax[i] = pItem->getMaxConstrainVert();}}// Must not be negative !!availSpace = max(availSpace, 0);return nGreedy;
}bool ETSLayoutMgr::Pane::resizeToRelative(int& availSpace, CArray<int,int>& sizePrimary,CArray<int,int>& sizeMin, CArray<int,int>& sizeMax)
{// Then all relative items as percentage of left space (as of now after// all absolute items are subtractedint availRel = availSpace;	// At the beginning all of remaining space is available. We want all// operation to be relative to the left space at this moment, so we// save this amount here. Then we safly can lower availSpaceint relDiff = 0;			// The cumulated difference between first proposed size and// eventual maximum/minimum size. This amount has to be// saved in some other place (i.e. where relativ items/subpane// are not limited by min/maxint relLeft = 0;			// The cumulated amout of space that can be saved by// shrinking the items/panes up to the minimumint relCount = 0;			// Actually allocated item/subpane's cumulated primary sizes // of non-limited items/subpanes (these can be modified in fixup)// needed for equally distribution of differences amoung non-limited// relative items/subpanesfor(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];// For all relative items in primary directionif( (m_Orientation==HORIZONTAL && pItem->modeResize() & RELATIVE_HORZ)||(m_Orientation==VERTICAL   && pItem->modeResize() & RELATIVE_VERT) ){// minimum item/subpane size in primary direction (pixels)int nSizeRelMin = sizeMin[i];// maximum item/subpane size in primary direction (pixels)int nSizeRelMax = sizeMax[i];// Relative size in primary direction (pixels)int nSizeRel	= (m_Orientation==HORIZONTAL) ? (pItem->getConstrainHorz(availRel)) :(pItem->getConstrainVert(availRel));if( nSizeRel < nSizeRelMin) {// The item/pane is shrinked too small!// We will grow it to it's minimum-size. In order not to modify// this item later when fixing up set the size to the negative// minimum sizesizePrimary[i]	= -nSizeRelMin;// As we grew one item/subpane we have to shrink another one.// We keep count on how much space we needed to grow the item// to it's minimum sizerelDiff += ( nSizeRelMin - nSizeRel );}else if(  nSizeRelMax != -1 && nSizeRel > nSizeRelMax) {// if there's a maximum size (nSizeRelMax != -1) and our item/subpane// is to be resized over that amount correct it.  In order not to modify// this item later when fixing up set the size to the negative// maximum sizesizePrimary[i]	= -nSizeRelMax;// As we shrinked one item/subpane we have to grow another one.// We keep count on how much space we needed to grow the item// to it's maximum size.relDiff += ( nSizeRelMax - nSizeRel );}else {// this is the normal case: neither are we minimum limited nor maximum// limited// As this item/subpane is larger that it's minimum we could later (if// necessary for fixup) shrink it for the difference amount of pixelsrelLeft	+= ( nSizeRel - nSizeRelMin );// Set the primary size of this item/pane. Can later be modified by fixupsizePrimary[i]	= nSizeRel;// Add this item/subpane's primary size to the count of already allocated// cumulated size of non-limited items/subpanes (these can be modified in fixup)relCount	+= nSizeRel;}// decrease available space by used space in this stepavailSpace	-= nSizeRel;}}// We now have the situation that some items/subpanes had to be adjusted for cumulated// relDiff pixels (positive value means more space taken than indicated by percentage of// left space). On the other hand we have some items/subpanes which were not limited (in // their current dimensions) but could be if necessary up to relLeft pixels. if(relLeft < relDiff && availSpace >= (relDiff-relLeft) ){		// If it's not possible to shrink other (relative) panes in order to distribute the// difference because the left for shrinking (relLeft) is too small we need to aquire// more space from the globally left space (if available at all)availSpace -= (relDiff-relLeft);relDiff = relLeft;}// At this point we should have some space left (at least not be negative with the leftover// space) and on the other hand there's enough space for the limit-difference to be distributed
//	ASSERT( availSpace >= 0 && relLeft >= relDiff);// Fixup Relative:// Distribute (if anecessary) relDiff on other (not limited) relative items/subpanes // (if available - if not later just grow the limited panes)while( relDiff != 0 && relCount >= 0 ) {// in every iteration there must be some space distributed (of the difference) or it could // come to endless looping. Save the amount of space actually distributed in this iterationint relDist = 0;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];// For all relative items in primary direction which were NOT limitedif( (m_Orientation==HORIZONTAL && (pItem->modeResize() & RELATIVE_HORZ) && sizePrimary[i] > 0)||(m_Orientation==VERTICAL   && (pItem->modeResize() & RELATIVE_VERT) && sizePrimary[i] > 0) ){// keep a flag for termination of this iterationbool bLast = false;// the difference should be distributed amoung all non-limited items/subpanes equally.// nDiff is the amount for the current item/subpaneint nDiff = (relDiff * sizePrimary[i]) / relCount;// if it's a too small value just add it to the current pane and break iterationif( abs(relDiff) <= FIXUP_CUTOFF ) {// take it all in this stepnDiff = relDiff;// set break flagbLast = true;}// calculate the new size for the current item/subpaneint nNewSize = sizePrimary[i] - nDiff;if( nNewSize < sizeMin[i] ) {// oh, we are limited here. Revise our plan:// Not all of the space could be saved, add the actually possible space// to the sumrelDist += ( sizePrimary[i] - sizeMin[i] );// set it to the minimum possible sizesizePrimary[i] = -sizeMin[i];// as this item/subpane is now limited it's occupied space doesn't count// for relCount anymorerelCount-= ( sizePrimary[i] );}else {// account the difference of the sizes in relDist and set new sizerelDist += ( sizePrimary[i] - nNewSize );sizePrimary[i] = nNewSize;// if it's the last one break nowif(bLast)break;}}}// Distributed some relDiff-space in every iteration
//		ASSERT(relDist != 0);	relDiff -= relDist;if( relDist == 0 )break;}// Fixup Relative: invert all negative (limited) sized to correct valuefor(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];if( (m_Orientation==HORIZONTAL && (pItem->modeResize() & RELATIVE_HORZ) && sizePrimary[i] < 0)||(m_Orientation==VERTICAL   && (pItem->modeResize() & RELATIVE_VERT) && sizePrimary[i] < 0) ){sizePrimary[i] *= -1;}}return true;
}bool ETSLayoutMgr::Pane::resizeToGreedy(int& availSpace, int nGreedy, CArray<int,int>& sizePrimary, CArray<int,int>& sizeMin, CArray<int,int>& sizeMax)
{// Now resize all Greedy items/subpanes equally among the remaining spaceint greedyDiff = 0;			// The cumulated difference between first proposed size and// eventual maximum/minimum size. This amount has to be// saved in some other place (i.e. where items/subpane// are not limited by min/maxint greedyLeft = 0;			// The cumulated amount of space that can be saved by// shrinking the items/panes up to the minimumint greedyCount = 0;		// Actually allocated item/subpane's cumulated primary sizes // of non-limited items/subpanes (these can be modified in fixup)// needed for equally distribution of differences amoung non-limited// items/subpanesfor(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];if( (m_Orientation==HORIZONTAL && !(pItem->modeResize()&ABSOLUTE_HORZ) && !(pItem->modeResize()&RELATIVE_HORZ))||(m_Orientation==VERTICAL   && !(pItem->modeResize()&ABSOLUTE_VERT) && !(pItem->modeResize()&RELATIVE_VERT)) ){// All greedy items get an equal portion of the left spaceint nSize		= availSpace / nGreedy;// minimum item/subpane size in primary direction (pixels)int nSizeMin	= sizeMin[i];// maximum item/subpane size in primary direction (pixels)int nSizeMax	= sizeMax[i];// the last gets the all of the remaining spaceif( nGreedy == 1 )nSize = availSpace;						if( nSize < nSizeMin) {// The item/pane is shrinked too small!// We will grow it to it's minimum-size. In order not to modify// this item later when fixing up set the size to the negative// minimum sizesizePrimary[i]	= -nSizeMin;// As we grew one item/subpane we have to shrink another one.// We keep count on how much space we needed to grow the item// to it's minimum sizegreedyDiff		+= ( nSizeMin - nSize );}else if( nSizeMax != -1 && nSize > nSizeMax) {// if there's a maximum size (nSizeRelMax != -1) and our item/subpane// is to be resized over that amount correct it.  In order not to modify// this item later when fixing up set the size to the negative// maximum sizesizePrimary[i]	= -nSizeMax;// As we shrinked one item/subpane we have to grow another one.// We keep count on how much space we needed to grow the item// to it's maximum size.greedyDiff		+= ( nSizeMax - nSize );}else {// this is the normal case: neither are we minimum limited nor maximum// limited// As this item/subpane is larger that it's minimum we could later (if// necessary for fixup) shrink it for the difference amount of pixelsgreedyLeft		+= ( nSize - nSizeMin );// Set the primary size of this item/pane. Can later be modified by fixupsizePrimary[i]	= nSize;// Add this item/subpane's primary size to the count of already allocated// cumulated size of non-limited items/subpanes (these can be modified in fixup)greedyCount		+= nSize;}// decrease available space by used space in this stepavailSpace	-= nSize;// one greedy item/subpane complete--nGreedy;}}// Fixup Greedy I// Distribute (if anecessary) greedyDiff on other (not limited) greedy items/subpanes // (if available - if not later just grow the limited panes)// at least on not limited item presentbool bAtLeastOne = true;while( bAtLeastOne && greedyDiff != 0 && greedyCount > 0) {// in every iteration there must be some space distributed (of the difference) or it could // come to endless looping. Save the amount of space actually distributed in this iterationint greedyDist = 0;// at least on not limited item presentbAtLeastOne = false;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];if( (m_Orientation==HORIZONTAL && !(pItem->modeResize()&ABSOLUTE_HORZ) && !(pItem->modeResize()&RELATIVE_HORZ)&& sizePrimary[i] > 0)	||(m_Orientation==VERTICAL   && !(pItem->modeResize()&ABSOLUTE_VERT) && !(pItem->modeResize()&RELATIVE_VERT)&& sizePrimary[i] > 0 )){// keep a flag for termination of this iterationbool bLast = false;// the difference should be distributed among all non-limited items/subpanes equally.// nDiff is the amount for the current item/subpaneint nDiff = (greedyDiff * sizePrimary[i]) / greedyCount;// if it's a too small value just add it to the current pane and break iterationif( abs(greedyDiff) <= FIXUP_CUTOFF || nDiff == 0) {// take it all in this stepnDiff = greedyDiff;// set break flagbLast = true;}// calculate the new size for the current item/subpaneint nNewSize = sizePrimary[i] - nDiff;if( nNewSize < sizeMin[i] ) {// oh, we are limited here. Revise our plan:if( sizePrimary[i] != sizeMin[i] )bAtLeastOne = true;// Not all of the space could be saved, add the actually possible space// to the sumgreedyDist += ( sizePrimary[i] - sizeMin[i] );// set it to the minimum possible sizesizePrimary[i] = sizeMin[i];// as this item/subpane is now limited its occupied space doesn't count// for relCount anymoregreedyCount -= ( sizePrimary[i] );}else {// yes, there is onebAtLeastOne = true;// account the difference of the sizes in relDist and set new sizegreedyDist += ( sizePrimary[i] - nNewSize );sizePrimary[i] = nNewSize;// if it's the last one break nowif(bLast)break;}}}// Distributed some greedyDiff-space in every iterationASSERT(!bAtLeastOne || greedyDist != 0 || greedyCount<=0);greedyDiff -= greedyDist;}// Fixup Greedy IIif( greedyDiff < 0 ) {// still difference, some space left// are there any items which are minimum-limited where we can give more space?for(int i=0; i<m_paneItems.GetSize() && greedyDiff!=0; ++i) {CPaneBase pItem = m_paneItems[i];if( (m_Orientation==HORIZONTAL && !(pItem->modeResize()&ABSOLUTE_HORZ) && !(pItem->modeResize()&RELATIVE_HORZ))	||(m_Orientation==VERTICAL   && !(pItem->modeResize()&ABSOLUTE_VERT) && !(pItem->modeResize()&RELATIVE_VERT))){if( sizePrimary[i] == -sizeMin[i] ) {// fill this one up as much as possibleif( sizeMax[i] == -1) {// all fits insizePrimary[i] += greedyDiff;greedyDiff = 0;}else {sizePrimary[i] += -min( -greedyDiff, sizeMax[i]-sizeMin[i]);greedyDiff     -= -min( -greedyDiff, sizeMax[i]-sizeMin[i]);}}}}}// Fixup Greedy III: invert all negative (limited) sized to correct valuefor(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];if( (m_Orientation==HORIZONTAL && !(pItem->modeResize() & ABSOLUTE_HORZ) && !(pItem->modeResize() & RELATIVE_HORZ) && sizePrimary[i] < 0&& sizeMin[i] >= 0)||(m_Orientation==VERTICAL   && !(pItem->modeResize() & ABSOLUTE_VERT) && !(pItem->modeResize() & RELATIVE_VERT) && sizePrimary[i] < 0&& sizeMin[i] >= 0) ){if(sizePrimary[i] < 0)sizePrimary[i] *= -1;}}return true;
}bool ETSLayoutMgr::Pane::resizeTo(CRect& rcNewArea) 
{// There must be some items or subpanesASSERT(m_paneItems.GetSize());// This Array holds the size in primary direction for each item/subpaneCArray<int,int>	sizePrimary;sizePrimary.SetSize(m_paneItems.GetSize());// This Array holds information about the minimum size in primary directionCArray<int,int>	sizeMin;sizeMin.SetSize(m_paneItems.GetSize());// This Array holds information about the maximum size in primary directionCArray<int,int>	sizeMax;sizeMax.SetSize(m_paneItems.GetSize());// How much space is actually available, subtract all borders between itemsint availSpace = (m_Orientation == HORIZONTAL ? rcNewArea.Width() : rcNewArea.Height() ) - (m_paneItems.GetUpperBound()*m_sizeBorder);// If there is some Extra border (on top/bottem resp. left/right) subtract it tooavailSpace -= 2*m_sizeExtraBorder;// Add the extra Border to top/bottem resp. left/rightif(m_Orientation == HORIZONTAL) {rcNewArea.top		+= m_sizeExtraBorder;rcNewArea.bottom	-= m_sizeExtraBorder;}else {rcNewArea.left		+= m_sizeExtraBorder;rcNewArea.right		-= m_sizeExtraBorder;}// Counts the number of greedy items/subpanesint nGreedy = resizeToAbsolute(availSpace, sizePrimary, sizeMin, sizeMax );if(nGreedy == -1)return false;if(! resizeToRelative(availSpace, sizePrimary, sizeMin, sizeMax ) )return false;if(! resizeToGreedy(availSpace, nGreedy, sizePrimary, sizeMin, sizeMax ) )return false;// If there is any left space and there are ALIGN_FILL_* Items to assign it// equally among themif( availSpace > 0 ) {// Count possible Itemsint nFillItems = 0;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];if( m_Orientation == HORIZONTAL && (pItem->modeResize() & ABSOLUTE_HORZ ) && (pItem->modeResize() & ALIGN_FILL_HORZ)||(pItem->modeResize() & ABSOLUTE_VERT ) && (pItem->modeResize() & ALIGN_FILL_VERT) ){++nFillItems;}}if( nFillItems > 0 ) {// okay, there are nFillItems, make them all availSpace/nFillItems biggerfor(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];if( m_Orientation == HORIZONTAL && (pItem->modeResize() & ABSOLUTE_HORZ ) && (pItem->modeResize() & ALIGN_FILL_HORZ)||(pItem->modeResize() & ABSOLUTE_VERT ) && (pItem->modeResize() & ALIGN_FILL_VERT) ){if( nFillItems == 1 ) {// the last one gets all the restsizePrimary[i]	+= availSpace;availSpace		= 0;--nFillItems;}else {sizePrimary[i]	+= availSpace/nFillItems;availSpace		-= availSpace/nFillItems;--nFillItems;}}}}}// Now reposition all items:// starting offsetint nOffset = (m_Orientation==HORIZONTAL ? rcNewArea.left : rcNewArea.top ) + m_sizeExtraBorder;for(int i=0; i<m_paneItems.GetSize(); ++i) {CPaneBase pItem = m_paneItems[i];// Calculate rect of item/subpaneCRect rcPane;if( m_Orientation==HORIZONTAL ) {rcPane.SetRect(nOffset, rcNewArea.top, nOffset+sizePrimary[i], rcNewArea.bottom);}else {rcPane.SetRect(rcNewArea.left, nOffset, rcNewArea.right, nOffset+sizePrimary[i]);}// do the resizing!pItem->resizeTo( rcPane );// go to the next position (old pos + size + border)ASSERT(sizePrimary[i] >= 0);nOffset += m_sizeBorder + sizePrimary[i];}				return true;			
}/////////////////////////////////////////////////////////////////////////////
// ETSLayoutDialog dialog#pragma warning(disable: 4355)
ETSLayoutDialog::ETSLayoutDialog(UINT nID, CWnd* pParent /*=NULL*/, LPCTSTR strName /*=NULL*/, bool bGripper /*=true*/): CBaseDialog(nID, pParent), ETSLayoutMgr( this )
{//{{AFX_DATA_INIT(ETSLayoutDialog)// NOTE: the ClassWizard will add member initialization here//}}AFX_DATA_INITm_bGripper	= bGripper;if(strName)m_strRegStore = strName;
}
#pragma warning(default: 4355)BEGIN_MESSAGE_MAP(ETSLayoutDialog, CBaseDialog)//{{AFX_MSG_MAP(ETSLayoutDialog)ON_WM_SIZE()ON_WM_GETMINMAXINFO()ON_WM_ERASEBKGND()ON_WM_DESTROY()//}}AFX_MSG_MAP
END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////
// ETSLayoutDialog message handlersBOOL ETSLayoutDialog::OnEraseBkgnd(CDC* pDC) 
{EraseBkgnd(pDC);return true;
}void ETSLayoutDialog::OnSize(UINT nType, int cx, int cy) 
{CBaseDialog::OnSize(nType, cx, cy);if( abs(cx) + abs(cy) > 0) {// Reposition Size Marker// Re-Layout all controlsUpdateLayout();RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);}}void ETSLayoutDialog::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{if(m_RootPane.IsValid()) {CRect rcClient = GetRect();if( rcClient.Height() > 0 || rcClient.Width() > 0 ){CRect rcWnd;GetWindowRect(rcWnd);// How much do Window and Client differint nDiffHorz = rcWnd.Width() - rcClient.Width();int nDiffVert = rcWnd.Height() - rcClient.Height();// Take into account that there is a border around the rootPanelpMMI->ptMinTrackSize = CPoint(m_RootPane->getMinConstrainHorz() + nDiffHorz + 2*m_sizeRootBorders.cx,m_RootPane->getMinConstrainVert() + nDiffVert + 2*m_sizeRootBorders.cy);int maxWidth = m_RootPane->getMaxConstrainHorz();int maxHeight = m_RootPane->getMaxConstrainVert();if( maxWidth != -1 ) {lpMMI->ptMaxTrackSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx;lpMMI->ptMaxSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx;}if( maxHeight != -1 ) {lpMMI->ptMaxTrackSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy;lpMMI->ptMaxSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy;}}}
}CRect ETSLayoutDialog::GetRect() 
{ CRect r; GetClientRect(r);if( m_bGripper ) {if( ::IsWindow(m_StatusBar.GetSafeHwnd()) ) {CRect rcSizeIcon;m_StatusBar.GetWindowRect( rcSizeIcon);r.bottom -= (rcSizeIcon.Height() - m_sizeRootBorders.cy - 5);}}return r; 
}BOOL ETSLayoutDialog::OnInitDialog() 
{CBaseDialog::OnInitDialog();// Ensure that the dialog is resizablethis->ModifyStyle(0, WS_THICKFRAME);if(!m_strRegStore.IsEmpty()) {Load(m_strRegStore);}	#ifdef _AUTO_SET_ICONPOSITION pos = AfxGetApp()->GetFirstDocTemplatePosition();if(pos) {class ETSPseudoDocTemplate : public CDocTemplate{friend class ETSLayoutDialog;};ETSPseudoDocTemplate* pDocT = (ETSPseudoDocTemplate*) AfxGetApp()->GetNextDocTemplate(pos);SetIcon( AfxGetApp()->LoadIcon(pDocT->m_nIDResource) ,FALSE);}
#endif// Sizing iconif(m_bGripper){if(m_StatusBar.Create(m_pWnd)){                           m_StatusBar.SetIndicators(auIDStatusBar, sizeof(auIDStatusBar) / sizeof(UINT));m_StatusBar.SetWindowText(_T(""));		m_StatusBar.SetPaneStyle( 0, SBPS_STRETCH | SBPS_NOBORDERS );m_pWnd -> RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);}             elseAfxMessageBox(_T("Error - Statusbar"));}return TRUE;  // return TRUE unless you set the focus to a control// EXCEPTION: OCX Property Pages should return FALSE
}void ETSLayoutDialog::OnDestroy() 
{// Store size/positionif(!m_strRegStore.IsEmpty()) {Save(m_strRegStore);}	// manually delete layout definition if object is reusedm_RootPane = 0;CBaseDialog::OnDestroy();
}/////////////////////////////////////////////////////////////////////////////
// ETSLayoutDialog dialog#pragma warning(disable: 4355)
#ifdef CS_HELP
ETSLayoutDialogBar::ETSLayoutDialogBar(UINT nID ): CBaseDialogBar( nID ), ETSLayoutMgr( this )
#else
ETSLayoutDialogBar::ETSLayoutDialogBar(): ETSLayoutMgr( this )
#endif
{//{{AFX_DATA_INIT(ETSLayoutDialogBar)// NOTE: the ClassWizard will add member initialization here//}}AFX_DATA_INITm_bInitialized = false;setRootBorders(0,0);
}
#pragma warning(default: 4355)BEGIN_MESSAGE_MAP(ETSLayoutDialogBar, CBaseDialogBar)//{{AFX_MSG_MAP(ETSLayoutDialogBar)ON_WM_SIZE()ON_WM_GETMINMAXINFO()ON_WM_DESTROY()ON_WM_ERASEBKGND()ON_MESSAGE(WM_INITDIALOG, OnInitDialog)//}}AFX_MSG_MAP
END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////
// ETSLayoutDialogBar message handlersLRESULT ETSLayoutDialogBar::OnInitDialog(WPARAM, LPARAM)
{Default();Initialize();return TRUE;
}void ETSLayoutDialogBar::UpdateLayout()
{ETSLayoutMgr::UpdateLayout();if(m_RootPane.IsValid()) {CRect rcClient = GetRect();CRect rcWnd;GetWindowRect(rcWnd);// How much do Window and Client differCSize sizeDiff( rcWnd.Width() - rcClient.Width(), rcWnd.Height() - rcClient.Height());// Take into account that there is a border around the rootPane
//		m_szMin = CSize(m_RootPane->getMinConstrainHorz() + sizeDiff.cx + 2*m_sizeRootBorders.cx,
//			m_RootPane->getMinConstrainVert() + sizeDiff.cy + 2*m_sizeRootBorders.cy);}
}CSize ETSLayoutDialogBar::CalcDynamicLayout(int nLength, DWORD dwMode)
{CSize sizeRet =  CBaseDialogBar::CalcDynamicLayout(nLength, dwMode);CSize sizeMin = sizeRet;CSize sizeMax = sizeRet;if(m_RootPane.IsValid()) {CRect rcClient = GetRect();CRect rcWnd;GetWindowRect(rcWnd);// How much do Window and Client differCSize sizeDiff( rcWnd.Width() - rcClient.Width(), rcWnd.Height() - rcClient.Height());// Take into account that there is a border around the rootPane
//		sizeMin = CSize(m_RootPane->getMinConstrainHorz() + sizeDiff.cx + 2*m_sizeRootBorders.cx,
//			m_RootPane->getMinConstrainVert() + sizeDiff.cy + 2*m_sizeRootBorders.cy);int maxWidth = m_RootPane->getMaxConstrainHorz();int maxHeight = m_RootPane->getMaxConstrainVert();if( maxWidth != -1 ) {sizeMax.cx = maxWidth + sizeDiff.cy + 2*m_sizeRootBorders.cx;}if( maxHeight != -1 ) {sizeMax.cy = maxHeight + sizeDiff.cy + 2*m_sizeRootBorders.cy;}}if( IsFloating() || !(dwMode&LM_HORZ)){sizeRet.cx = min( sizeRet.cx, sizeMax.cx );}if( IsFloating() || (dwMode&LM_HORZ)){sizeRet.cy = min( sizeRet.cy, sizeMax.cy );}sizeRet.cx = max( sizeRet.cx, sizeMin.cx );sizeRet.cy = max( sizeRet.cy, sizeMin.cy );return sizeRet;
}BOOL ETSLayoutDialogBar::OnEraseBkgnd(CDC* pDC) 
{EraseBkgnd(pDC);return true;
}void ETSLayoutDialogBar::OnSize(UINT nType, int cx, int cy) 
{CBaseDialogBar::OnSize(nType, cx, cy);if( abs(cx) + abs(cy) > 0){// Re-Layout all controlsUpdateLayout();}RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);}CRect ETSLayoutDialogBar::GetRect() 
{ CRect r; GetClientRect(r);if( IsFloating() )r.DeflateRect(4,4);return r; 
}void ETSLayoutDialogBar::OnDestroy() 
{// Store size/position on your own!CBaseDialogBar::OnDestroy();
}/////////////////////////////////////////////////////////////////////////////
// ETSLayoutFormView dialogIMPLEMENT_DYNAMIC(ETSLayoutFormView, CFormView)#pragma warning(disable: 4355)
ETSLayoutFormView::ETSLayoutFormView(UINT nID, LPCTSTR strName /*=NULL*/): CBaseFormView(nID), ETSLayoutMgr( this )
{if(strName)m_strRegStore = strName;
}
#pragma warning(default: 4355)BEGIN_MESSAGE_MAP(ETSLayoutFormView, CBaseFormView)//{{AFX_MSG_MAP(ETSLayoutFormView)ON_WM_SIZE()ON_WM_GETMINMAXINFO()ON_WM_ERASEBKGND()//}}AFX_MSG_MAP
END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////
// ETSLayoutFormView message handlersBOOL ETSLayoutFormView::OnEraseBkgnd(CDC* pDC) 
{EraseBkgnd(pDC);return true;
}void ETSLayoutFormView::OnSize(UINT nType, int cx, int cy) 
{
//	CBaseFormView::OnSize(nType, cx, cy);SetScrollSizes(MM_TEXT, CSize(cx,cy));if( abs(cx) + abs(cy) > 0) {// Re-Layout all controlsUpdateLayout();}
//	MoveWindow(0,0,cx,cy);
}/*
void ETSLayoutFormView::UpdateLayout()
{ETSLayoutMgr::UpdateLayout();if(m_RootPane.IsValid()) {// Force MainFrame to re-layoutCFrameWnd* pFrame = static_cast<CFrameWnd*>(GetParent());if(pFrame) {CRect rcWnd;pFrame->GetWindowRect(rcWnd);pFrame->MoveWindow(rcWnd);pFrame->RecalcLayout();}return;}
}
*/void ETSLayoutFormView::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{// To use this you'll have to modify your CMainFrame://// 1) Add a handler for WM_GETMINMAXINFO()// 2) Let this handler be:// void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) // {// 	CFrameWnd::OnGetMinMaxInfo(lpMMI);// // 	if( GetActiveView() && GetActiveView()->IsKindOf( RUNTIME_CLASS(ETSLayoutFormView) ) ) {// 		GetActiveView()->SendMessage( WM_GETMINMAXINFO, 0, (LPARAM) lpMMI );// 	}// }// 3) Add "#include "dialogmgr.h" to MainFrm.cppif(m_RootPane.IsValid()) {CRect rcClient = GetRect();CRect rcWnd;GetParent()->GetWindowRect(rcWnd);// How much do Window and Client differrcWnd-=rcClient;// Take into account that there is a border around the rootPanelpMMI->ptMinTrackSize = CPoint(m_RootPane->getMinConstrainHorz() + rcWnd.Width() + 2*m_sizeRootBorders.cx,m_RootPane->getMinConstrainVert() + rcWnd.Height() + 2*m_sizeRootBorders.cy);int maxWidth = m_RootPane->getMaxConstrainHorz();int maxHeight = m_RootPane->getMaxConstrainVert();if( maxWidth != -1 ) {lpMMI->ptMaxTrackSize.x = maxWidth + rcWnd.Width()+ 2*m_sizeRootBorders.cx;lpMMI->ptMaxSize.x = maxWidth + rcWnd.Width()+ 2*m_sizeRootBorders.cx;}if( maxHeight != -1 ) {lpMMI->ptMaxTrackSize.y = maxHeight + rcWnd.Height() + 2*m_sizeRootBorders.cy;lpMMI->ptMaxSize.y = maxHeight + rcWnd.Height() + 2*m_sizeRootBorders.cy;}}
}ETSLayoutFormView::~ETSLayoutFormView() 
{// Cleanup
}/////////////////////////////////////////////////////////////////////////////
// ETSLayoutPropertyPage#ifdef CS_HELPIMPLEMENT_DYNCREATE(ETSLayoutPropertyPage, ETSCSHelpPropPage)
#elseIMPLEMENT_DYNCREATE(ETSLayoutPropertyPage, CPropertyPage)
#endif#pragma warning(disable: 4355)
ETSLayoutPropertyPage::ETSLayoutPropertyPage( ) : ETSLayoutMgr( this )
{m_bLockMove = false;m_bResetBuddyOnNextTimeVisible = true;
}ETSLayoutPropertyPage::ETSLayoutPropertyPage( UINT nIDTemplate, UINT nIDCaption /*= 0*/ ): CBasePropertyPage(nIDTemplate, nIDCaption), ETSLayoutMgr( this )
{m_bLockMove = false;m_bResetBuddyOnNextTimeVisible = true;
}ETSLayoutPropertyPage::ETSLayoutPropertyPage( LPCTSTR lpszTemplateName, UINT nIDCaption /*= 0*/ ): CBasePropertyPage(lpszTemplateName, nIDCaption), ETSLayoutMgr( this )
{m_bLockMove = false;m_bResetBuddyOnNextTimeVisible = true;
}
#pragma warning(default: 4355)ETSLayoutPropertyPage::~ETSLayoutPropertyPage()
{
}BEGIN_MESSAGE_MAP(ETSLayoutPropertyPage, CBasePropertyPage)//{{AFX_MSG_MAP(ETSLayoutPropertyPage)ON_WM_SIZE()ON_WM_GETMINMAXINFO()ON_WM_ERASEBKGND()ON_WM_WINDOWPOSCHANGING()ON_WM_DESTROY()ON_WM_WINDOWPOSCHANGED()//}}AFX_MSG_MAP
END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////
// Behandlungsroutinen f黵 Nachrichten ETSLayoutPropertyPage void ETSLayoutPropertyPage::OnWindowPosChanged(WINDOWPOS FAR* lpwndpos) 
{CBasePropertyPage::OnWindowPosChanged(lpwndpos);// This code is needed in order to reset the buddy after this page has// been activated. At least on Win2k this is not done thru normal resizing,// as the page is not visible when first layouted. And without the page// being visible it's not possible to tell if the attached buddy is visible// or not (at least I don't know any way to do so)if( ::IsWindowVisible( GetWnd()->GetSafeHwnd() ) ){if( m_bResetBuddyOnNextTimeVisible ) {// Take special care of SpinButtons (Up-Down Controls) with Buddy set, enumerate// all childs:CWnd* pWndChild = GetWnd()->GetWindow(GW_CHILD);TCHAR szClassName[ MAX_PATH ];while(pWndChild){::GetClassName( pWndChild->GetSafeHwnd(), szClassName, MAX_PATH );DWORD dwStyle = pWndChild->GetStyle();// is it a SpinButton?if( _tcscmp(szClassName, UPDOWN_CLASS)==0 && ::IsWindowVisible(pWndChild->GetSafeHwnd()) ) {HWND hwndBuddy = (HWND)::SendMessage( pWndChild->GetSafeHwnd(), UDM_GETBUDDY, 0, 0);if( hwndBuddy != 0 && (dwStyle&(UDS_ALIGNRIGHT|UDS_ALIGNLEFT)) != 0 ){// reset Buddy::SendMessage( pWndChild->GetSafeHwnd(), UDM_SETBUDDY, (WPARAM)hwndBuddy, 0);}}pWndChild = pWndChild->GetWindow(GW_HWNDNEXT);}m_bResetBuddyOnNextTimeVisible = false;}}	else{// has been hidden againm_bResetBuddyOnNextTimeVisible = true;}
}void ETSLayoutPropertyPage::OnWindowPosChanging( WINDOWPOS* lpwndpos )
{// In WizardMode the System calls SetWindowPos with the // original size at every activation. This could cause// some flicker in certain circumstances. Therefore we lock// moving the page and unlock it only if _we_ move the pageif( m_bLockMove){lpwndpos->flags |= SWP_NOMOVE | SWP_NOSIZE;}CBasePropertyPage::OnWindowPosChanging( lpwndpos );
}BOOL ETSLayoutPropertyPage::OnEraseBkgnd(CDC* pDC) 
{EraseBkgnd(pDC);return true;
}void ETSLayoutPropertyPage::OnDestroy() 
{// manually delete layout definition if object is reusedm_RootPane = 0;CBasePropertyPage::OnDestroy();
}void ETSLayoutPropertyPage::OnSize(UINT nType, int cx, int cy) 
{CBasePropertyPage::OnSize(nType, cx, cy);if( abs(cx) + abs(cy) > 0) {// Re-Layout all controlsUpdateLayout();}	
}void ETSLayoutPropertyPage::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{if(m_RootPane.IsValid()) {CRect rcClient = GetRect();CRect rcWnd;GetWindowRect(rcWnd);// How much do Window and Client differint nDiffHorz = rcWnd.Width() - rcClient.Width();int nDiffVert = rcWnd.Height() - rcClient.Height();// Take into account that there is a border around the rootPanelpMMI->ptMinTrackSize = CPoint(m_RootPane->getMinConstrainHorz() + nDiffHorz + 2*m_sizeRootBorders.cx,m_RootPane->getMinConstrainVert() + nDiffVert + 2*m_sizeRootBorders.cy);int maxWidth = m_RootPane->getMaxConstrainHorz();int maxHeight = m_RootPane->getMaxConstrainVert();if( maxWidth != -1 ) {lpMMI->ptMaxTrackSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx;lpMMI->ptMaxSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx;}if( maxHeight != -1 ) {lpMMI->ptMaxTrackSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy;lpMMI->ptMaxSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy;}}
}CRect ETSLayoutPropertyPage::GetRect() 
{ CRect r; GetClientRect(r);return r; 
}BOOL ETSLayoutPropertyPage::OnInitDialog() 
{CBasePropertyPage::OnInitDialog();UpdateLayout();ETSLayoutPropertySheet* pSheet = (ETSLayoutPropertySheet*) GetParent();ASSERT_KINDOF( ETSLayoutPropertySheet, pSheet);if(pSheet){if(pSheet->IsWizard()){m_bLockMove = true;}}return TRUE;
}BOOL ETSLayoutPropertyPage::OnSetActive() 
{ETSLayoutPropertySheet* pSheet = (ETSLayoutPropertySheet*) GetParent();ASSERT_KINDOF( ETSLayoutPropertySheet, pSheet);if(pSheet){if(pSheet->IsWizard()){// In WizardMode the System calls SetWindowPos with the // original size on Page Activation. This will position the// page at the correct positionm_bLockMove = false;MoveWindow(pSheet->m_rcPage);m_bLockMove = true;}}UpdateLayout();	return CBasePropertyPage::OnSetActive();
}/////////////////////////////////////////////////////////////////////////////
// ETSLayoutPropertySheetIMPLEMENT_DYNAMIC(ETSLayoutPropertySheet, CPropertySheet)#pragma warning(disable: 4355)
ETSLayoutPropertySheet::ETSLayoutPropertySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage, LPCTSTR strName /*=NULL*/, bool bGripper/*=true*/): CPropertySheet(nIDCaption, pParentWnd, iSelectPage), ETSLayoutMgr( this )
{Init(strName, bGripper);
}ETSLayoutPropertySheet::ETSLayoutPropertySheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage, LPCTSTR strName /*=NULL*/, bool bGripper/*=true*/): CPropertySheet(pszCaption, pParentWnd, iSelectPage), ETSLayoutMgr( this )
{Init(strName, bGripper);
}
#pragma warning(default: 4355)void ETSLayoutPropertySheet::Init(LPCTSTR strName, bool bGripper)
{m_bGripper	= bGripper;if(strName)m_strRegStore = strName;m_bAutoDestroy	= false;m_bAutoDestroyPages	= false;m_bModelessButtons = false;
}ETSLayoutPropertySheet::~ETSLayoutPropertySheet()
{
}BEGIN_MESSAGE_MAP(ETSLayoutPropertySheet, CPropertySheet)//{{AFX_MSG_MAP(ETSLayoutPropertySheet)ON_WM_CREATE()ON_WM_SIZE()ON_WM_GETMINMAXINFO()ON_WM_DESTROY()ON_WM_ERASEBKGND()//}}AFX_MSG_MAP
END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////
// Behandlungsroutinen f黵 Nachrichten ETSLayoutPropertySheet BOOL ETSLayoutPropertySheet::OnEraseBkgnd(CDC* pDC) 
{EraseBkgnd(pDC);return true;
}int ETSLayoutPropertySheet::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{if (CPropertySheet::OnCreate(lpCreateStruct) == -1)return -1;ModifyStyle(0,WS_THICKFRAME| WS_SYSMENU);return 0;
}void ETSLayoutPropertySheet::Resize(int cx, int cy)
{if( abs(cx) + abs(cy) > 0 && m_RootPane.IsValid() ) {UpdateLayout();// Fix for PSH_WIZARDHASFINISH [Th鰉mi]if (IsWizard() && !(m_psh.dwFlags & PSH_WIZARDHASFINISH) ){// manual reposition of the FINISH button// can not be done with normaly layouting because it// shares position with the NEXT buttonCWnd *pWndFinish;pWndFinish=GetDlgItem(ID_WIZFINISH);if(pWndFinish){CRect rcWnd;GetDlgItem(ID_WIZNEXT)->GetWindowRect(&rcWnd);ScreenToClient(&rcWnd);pWndFinish->MoveWindow(rcWnd);pWndFinish->RedrawWindow(0,0, RDW_INVALIDATE | RDW_UPDATENOW );}}// reposition Gripperif(m_bGripper)RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);CPropertyPage* pPage = (CPropertyPage*)GetActivePage();if(pPage){CRect rcWnd;GetTabControl()->GetWindowRect(&rcWnd);ScreenToClient(&rcWnd);if(!IsWizard()) {// get inside of tabGetTabControl()->AdjustRect(FALSE, &rcWnd);}else{rcWnd.bottom += 5;}// we need this size in WizardMode in order to // reposition newly activated page correctlym_rcPage = rcWnd;if( IsWizard() && pPage->IsKindOf(RUNTIME_CLASS(ETSLayoutPropertyPage)) ){ETSLayoutPropertyPage* pEtsPage = reinterpret_cast<ETSLayoutPropertyPage*>(pPage);pEtsPage->m_bLockMove = false;pEtsPage->MoveWindow(m_rcPage);pEtsPage->m_bLockMove = true;}else {pPage->MoveWindow(m_rcPage);}}if(IsWindowVisible()){RedrawWindow(0,0, RDW_INVALIDATE|RDW_UPDATENOW );if(!IsWizard())GetTabControl()->RedrawWindow(0,0, RDW_INVALIDATE|RDW_UPDATENOW );}}
}void ETSLayoutPropertySheet::OnSize(UINT nType, int cx, int cy) 
{CPropertySheet::OnSize(nType, cx, cy);Resize(cx,cy);
}// IDs of all PropertySheet controls
long _PropertySheetIDs[] =
{ID_WIZBACK,ID_WIZNEXT, ID_WIZFINISH,IDOK, IDCANCEL,ID_APPLY_NOW, IDHELP
};void ETSLayoutPropertySheet::AddMainArea(CPane paneRoot, CPaneBase itemTab)
{// the default is: Whole main Area is covered by the TabCtrlpaneRoot << itemTab;
}void ETSLayoutPropertySheet::AddButtons(CPane paneBottom)
{// first item greedy to keep others rightpaneBottom->addItem (paneNull, GREEDY);// add all Controls to the layoutingbool bFirst = true;for(int i = 0; i < (sizeof(_PropertySheetIDs) / sizeof(long)) ; i++){// Prevent movement of finish button, if it is not shown explicitly [Th鰉mi]if( IsWizard() && _PropertySheetIDs[i] == ID_WIZFINISH && !(m_psh.dwFlags & PSH_WIZARDHASFINISH) ) {continue;}CWnd* pWnd = GetDlgItem(_PropertySheetIDs[i]);if(pWnd){if(!(m_psh.dwFlags & PSH_HASHELP) && _PropertySheetIDs[i] == IDHELP){// don't insertcontinue;}if((m_psh.dwFlags & PSH_NOAPPLYNOW) && _PropertySheetIDs[i] == ID_APPLY_NOW){// don't insertcontinue;}// space before first one and between BACK & NEXTif( IsWizard() ){if( !bFirst && !(_PropertySheetIDs[i]==ID_WIZNEXT) ){paneBottom->addItem(paneNull, NORESIZE,12,0,0,0);}}pWnd->ShowWindow(true);paneBottom->addItem(_PropertySheetIDs[i], NORESIZE);			bFirst = false;}}}BOOL ETSLayoutPropertySheet::OnInitDialog() 
{BOOL bRet = CPropertySheet::OnInitDialog();ASSERT(!m_RootPane);// Save initial rectGetWindowRect(&m_rcStart);CPropertyPage* pPage = CPropertySheet::GetActivePage();ASSERT(pPage);CRect rcPage;pPage->GetClientRect(&rcPage);CreateRoot(VERTICAL);//ASSERT(m_RootPane);// Add Tabcontrol to root panem_ItemTab = item( GetTabControl(), GREEDY, 0, 0, 0, 0);AddMainArea(m_RootPane, m_ItemTab);// Tabcontrol is invisible in WizardModeif(IsWizard()){GetTabControl()->ShowWindow(false);}// add horizontal line in WizardModeif(IsWizard() && GetDlgItem(ID_WIZFINISH+1)){m_RootPane << item(ID_WIZFINISH+1, ABSOLUTE_VERT, 0, 0, 0, 0);}if( IsWizard() || !m_bModeless || m_bModelessButtons ){// No spaces in WizardMode in order to keep BACK & NEXT togetherCPane bottomPane = pane(HORIZONTAL, ABSOLUTE_VERT, IsWizard() ? 0 : 5);AddButtons(bottomPane);// add bottom (button) pane if any controls were addedif(bottomPane->m_paneItems.GetSize() > 0) {m_RootPane << bottomPane;}}// some Space between Buttons und Gripperif(m_bGripper){m_RootPane->addItem(paneNull, ABSOLUTE_VERT,0,2);if(m_StatusBar.Create(m_pWnd)){                           m_StatusBar.SetIndicators(auIDStatusBar,sizeof(auIDStatusBar) / sizeof(UINT));m_StatusBar.SetWindowText(_T(""));		RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);}             else{AfxMessageBox(_T("Error - Statusbar"));}}if(!m_strRegStore.IsEmpty()){Load(m_strRegStore);}	Resize(1,1); // Fix. for 95/98/NT differenceCRect rcWnd;GetWindowRect( & rcWnd );MoveWindow( rcWnd );return bRet;
}void ETSLayoutPropertySheet::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{if(m_RootPane.IsValid() && GetTabControl() != 0 ) {CRect rcWnd;GetWindowRect(rcWnd);		CRect rcClient = GetRect();rcWnd-=rcClient;// ask for MinMax of all pagesCSize sizePageMax(0,0);CSize sizePageMin(0,0);for( int nPage=0; nPage<GetPageCount(); ++nPage){CPropertyPage* pPage = GetPage(nPage);ASSERT(pPage);if( pPage ){MINMAXINFO mmi;memset(&mmi, 0, sizeof(mmi));if( IsWindow(pPage->GetSafeHwnd()) ){pPage->SendMessage(WM_GETMINMAXINFO, 0, (LPARAM) &mmi);if(mmi.ptMaxTrackSize.x != 0){sizePageMax.cx = min(sizePageMax.cx, mmi.ptMaxTrackSize.x);}if(mmi.ptMaxTrackSize.y != 0){sizePageMax.cy = min(sizePageMax.cy, mmi.ptMaxTrackSize.y);}if(mmi.ptMinTrackSize.x != 0){sizePageMin.cx = max(sizePageMin.cx, mmi.ptMinTrackSize.x);}if(mmi.ptMinTrackSize.y != 0){sizePageMin.cy = max(sizePageMin.cy, mmi.ptMinTrackSize.y);}}}}static_cast<PaneItem*>( m_ItemTab.GetPaneBase() )->m_sizeXMin = sizePageMin.cx;static_cast<PaneItem*>( m_ItemTab.GetPaneBase() )->m_sizeYMin = sizePageMin.cy;// calculate the needed size of the tabctrl in non-wizard-modeCRect rcItem(0,0,0,0);if(!IsWizard()){GetTabControl()->AdjustRect( TRUE, rcItem );}lpMMI->ptMinTrackSize.x = m_RootPane->getMinConstrainHorz() + rcWnd.Width() + 2*m_sizeRootBorders.cx+ rcItem.Width();lpMMI->ptMinTrackSize.y = m_RootPane->getMinConstrainVert() + rcWnd.Height() + 2*m_sizeRootBorders.cy + rcItem.Height();// never smaller than inital size!lpMMI->ptMinTrackSize.x = max(lpMMI->ptMinTrackSize.x, m_rcStart.Width() );lpMMI->ptMinTrackSize.y = max(lpMMI->ptMinTrackSize.y, m_rcStart.Height() );// Rest like ETSLayoutMgrint maxWidth = m_RootPane->getMaxConstrainHorz();int maxHeight = m_RootPane->getMaxConstrainVert();if( maxWidth != -1 ) {lpMMI->ptMaxSize.x = sizePageMax.cx + rcWnd.Width()+ 2*m_sizeRootBorders.cx + rcItem.Width() ;}if( maxHeight != -1 ) {lpMMI->ptMaxSize.y = sizePageMax.cy + rcWnd.Height() + 2*m_sizeRootBorders.cy + rcItem.Width() ;}lpMMI->ptMaxTrackSize = lpMMI->ptMaxSize;}
}void ETSLayoutPropertySheet::OnDestroy() 
{// Store size/positionif(!m_strRegStore.IsEmpty()) {Save(m_strRegStore);}	m_RootPane = 0;CPropertySheet::OnDestroy();
}void ETSLayoutPropertySheet::PostNcDestroy()
{if(m_bAutoDestroyPages){// walk all pages and destry themfor( int nPage=0; nPage<GetPageCount(); ++nPage){CPropertyPage* pPage = GetPage(nPage);ASSERT(pPage);if( pPage ){delete pPage;}}}if(m_bAutoDestroy)delete this;
}/*** CPane represents an autopointer to a PaneBase. Use this and you won't have to worry* about cleaning up any Panes. Also this autopointer lets you return Pane objects* from function without using pointers (at least you won't see them :) )*/
ETSLayoutMgr::PaneHolder::PaneHolder(PaneBase* pPane )
{ASSERT( pPane );m_pPane = pPane;// Implicitly AddRef()m_nRefCount = 1;
}ETSLayoutMgr::PaneHolder::~PaneHolder()
{ASSERT( m_pPane );ASSERT( m_nRefCount == 0 );delete m_pPane;
}void ETSLayoutMgr::PaneHolder::AddRef()
{InterlockedIncrement( &m_nRefCount );
}void ETSLayoutMgr::PaneHolder::Release()
{if( InterlockedDecrement( &m_nRefCount ) <= 0 ){// no more references on me, so destroy myselfdelete this;}
}ETSLayoutMgr::CPaneBase::CPaneBase( )
{// MUST be initialized laterm_pPaneHolder = 0;
}ETSLayoutMgr::CPaneBase::CPaneBase( PaneBase* pPane )
{m_pPaneHolder = 0;if( pPane != 0)operator=( pPane );
}ETSLayoutMgr::CPaneBase::CPaneBase( const CPaneBase& other )
{m_pPaneHolder = 0;operator=(other);
}ETSLayoutMgr::CPaneBase::~CPaneBase()
{if(m_pPaneHolder)m_pPaneHolder->Release();
}void ETSLayoutMgr::CPaneBase::operator=( PaneBase* pPane )
{if(m_pPaneHolder){m_pPaneHolder->Release();m_pPaneHolder = 0;}if( pPane != 0 )m_pPaneHolder = new PaneHolder( pPane );
}void ETSLayoutMgr::CPaneBase::operator=( const CPaneBase& other )
{ASSERT( other.m_pPaneHolder );if(m_pPaneHolder){m_pPaneHolder->Release();m_pPaneHolder = 0;}other.m_pPaneHolder->AddRef();m_pPaneHolder = other.m_pPaneHolder;
}ETSLayoutMgr::PaneBase* ETSLayoutMgr::CPaneBase::operator->() const
{ASSERT(m_pPaneHolder);if(!m_pPaneHolder)return 0;return (m_pPaneHolder->m_pPane);
}ETSLayoutMgr::CPane::CPane( )
{
}ETSLayoutMgr::CPane::CPane( Pane* pPane ) : ETSLayoutMgr::CPaneBase( static_cast<PaneBase*>(pPane) )
{
}ETSLayoutMgr::CPane::CPane( const CPane& other )
{operator=(other);
}ETSLayoutMgr::CPane::~CPane()
{
}void ETSLayoutMgr::CPane::operator=( Pane* pPane )
{CPaneBase::operator=(pPane);
}void ETSLayoutMgr::CPane::operator=( const ETSLayoutMgr::CPane& other )
{ASSERT( other.m_pPaneHolder );if(m_pPaneHolder){m_pPaneHolder->Release();m_pPaneHolder = 0;}other.m_pPaneHolder->AddRef();m_pPaneHolder = other.m_pPaneHolder;
}ETSLayoutMgr::Pane* ETSLayoutMgr::CPane::operator->() const
{ASSERT(m_pPaneHolder);if(!m_pPaneHolder)return 0;return reinterpret_cast<Pane*>(m_pPaneHolder->m_pPane);
}ETSLayoutMgr::CPaneBase ETSLayoutMgr::CPane::ConvertBase() const
{ASSERT(m_pPaneHolder);return CPaneBase( m_pPaneHolder->m_pPane );
}ETSLayoutMgr::CPane& ETSLayoutMgr::CPane::operator<< ( const ETSLayoutMgr::CPane pPane )
{GetPane()->addPane( pPane, (ETSLayoutMgr::layResizeMode)pPane->m_modeResize, pPane->m_sizeSecondary);return (*this);
}ETSLayoutMgr::CPane& ETSLayoutMgr::CPane::operator<< ( const ETSLayoutMgr::CPaneBase pItem )
{GetPane()->addPane( pItem );return (*this);
}

三、工作原理

3.1 布局尺寸基础定义

	enum layResizeMode {GREEDY				= 0,		// Will eat up as much as it canABSOLUTE_HORZ		= 1 << 0,	// Horizontal size is absoluteRELATIVE_HORZ		= 1 << 1,	// Horizontal size in percentABSOLUTE_VERT		= 1 << 2,	// Vertical size is absoluteRELATIVE_VERT		= 1 << 3,	// Vertical size in percentNORESIZE			= ABSOLUTE_HORZ | ABSOLUTE_VERT,SIZE_MASK			= NORESIZE,ALIGN_LEFT			= 1 << 4,   // following only for NORESIZEALIGN_RIGHT			= 1 << 5,ALIGN_TOP			= 1 << 6,ALIGN_BOTTOM		= 1 << 7,ALIGN_HCENTER		= ALIGN_LEFT    | ALIGN_RIGHT,	ALIGN_VCENTER		= ALIGN_TOP     | ALIGN_BOTTOM,ALIGN_CENTER		= ALIGN_HCENTER | ALIGN_VCENTER,ALIGN_FILL_HORZ		= 1 << 8,ALIGN_FILL_VERT		= 1 << 9,ALIGN_FILL			= ALIGN_FILL_HORZ | ALIGN_FILL_VERT,/*		TRACKER_LEFT		= 1 << 10,	// not yet. May allow tracking of bordersTRACKER_RIGHT		= 1 << 11,  // between items in the futureTRACKER_TOP			= 1 << 12,TRACKER_BOTTOM		= 1 << 13,
*/};

3.2 基础布局

	enum layOrientation {HORIZONTAL,VERTICAL};

3.3 实际操作

通过阅读源码及示例,总结一句话:布局由panel和item组成。panel决定水平和垂直布局,item设置自身的尺寸调整(如固定尺寸(NORESIZE),充满剩余空间(GREEDY),百分比拉伸(RELATIVE_HORZ,70)表示水平百分之70的尺寸,(RELATIVE_VERT,20)垂直百分之20的尺寸。下图中的红色表示垂直布局中的panel 蓝色表示水平布局中的panel。

参考如下示例:

	CreateRoot(VERTICAL)  << item ( IDC_NEW_ITEM_STATIC, NORESIZE )<< 	( pane(HORIZONTAL, ABSOLUTE_VERT )<< item( IDC_NEW_ITEM, GREEDY )<< item( IDC_ADD_ITEM, NORESIZE ))<< item ( IDC_ITEM_LIST_STATIC, NORESIZE )<< item ( IDC_ITEM_LIST, GREEDY )<<	( pane(HORIZONTAL, ABSOLUTE_VERT )<< item( IDOK, RELATIVE_HORZ, 70 )<< item( IDCANCEL, RELATIVE_HORZ, 30 ));UpdateLayout();

CreateRoot 返回根布局panel。

panel中可添加item和子panel,从而实现不同方向和尺寸的布局。效果图如下:

有时候我们会需要一些空的占位,以实现靠左或靠右的显示效果,这时我们需要使用itemGrowing,具体示例如下(示例中OK和Cancel会在布局中靠右显示):

	CPane bottomPane = pane( HORIZONTAL, ABSOLUTE_VERT )<< itemGrowing(HORIZONTAL)<< item( IDOK, NORESIZE )<< item( IDCANCEL, NORESIZE );

四、示例解析

创建MFC dialog,从ETSLayoutDialog继承:

头文件:

class CMyDialog : public ETSLayoutDialog
{
// Konstruktion
public:CMyDialog(CWnd* pParent = NULL);   // Standardkonstruktor/* DialogMgr: Add this: */DECLARE_LAYOUT();/************************/// Dialogfelddaten//{{AFX_DATA(CMyDialog)enum { IDD = IDD_DIALOG1 };// HINWEIS: Der Klassen-Assistent f黦t hier Datenelemente ein//}}AFX_DATA// 躡erschreibungen// Vom Klassen-Assistenten generierte virtuelle Funktions黚erschreibungen//{{AFX_VIRTUAL(CMyDialog)protected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV-Unterst黷zung//}}AFX_VIRTUAL// Implementierung
protected:// Generierte Nachrichtenzuordnungsfunktionen//{{AFX_MSG(CMyDialog)virtual BOOL OnInitDialog();//}}AFX_MSGDECLARE_MESSAGE_MAP()
};

实现:

// MyDialog.cpp: Implementierungsdatei
//#include "stdafx.h"
#include "LayoutManager.h"
#include "MyDialog.h"#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif/////////////////////////////////////////////////////////////////////////////
// Dialogfeld CMyDialog CMyDialog::CMyDialog(CWnd* pParent /*=NULL*/): ETSLayoutDialog(CMyDialog::IDD, pParent)
{//{{AFX_DATA_INIT(CMyDialog)// HINWEIS: Der Klassen-Assistent f黦t hier Elementinitialisierung ein//}}AFX_DATA_INIT
}void CMyDialog::DoDataExchange(CDataExchange* pDX)
{ETSLayoutDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CMyDialog)//}}AFX_DATA_MAP
}BEGIN_MESSAGE_MAP(CMyDialog, ETSLayoutDialog)//{{AFX_MSG_MAP(CMyDialog)//}}AFX_MSG_MAP
END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////
// Behandlungsroutinen f黵 Nachrichten CMyDialog BOOL CMyDialog::OnInitDialog() 
{ETSLayoutDialog::OnInitDialog();/* DialogMgr: Add this: */// See article for comments// define the Layout// Possibility 1CreateRoot(VERTICAL)<< item ( IDC_NEW_ITEM_STATIC, NORESIZE )<< 	( pane(HORIZONTAL, ABSOLUTE_VERT )<< item( IDC_NEW_ITEM, GREEDY )<< item( IDC_ADD_ITEM, NORESIZE ))<< item ( IDC_ITEM_LIST_STATIC, NORESIZE )<< item ( IDC_ITEM_LIST, GREEDY )<<	( pane(HORIZONTAL, ABSOLUTE_VERT )<< item( IDOK, RELATIVE_HORZ, 70 )<< item( IDCANCEL, RELATIVE_HORZ, 30 ));UpdateLayout();/*// Possibility 2CPane newItemPane=new Pane ( this, HORIZONTAL );newItemPane->addItem ( IDC_NEW_ITEM, GREEDY );newItemPane->addItem ( IDC_ADD_ITEM, NORESIZE );CPane bottomPane=new Pane ( this, HORIZONTAL );bottomPane->addItem ( paneNull, GREEDY );bottomPane->addItem ( IDOK, NORESIZE );bottomPane->addItem ( IDCANCEL, NORESIZE );CreateRoot( VERTICAL );m_RootPane->addItem ( IDC_NEW_ITEM_STATIC, NORESIZE );m_RootPane->addPane ( newItemPane, ABSOLUTE_VERT );m_RootPane->addItem ( IDC_ITEM_LIST_STATIC, NORESIZE );m_RootPane->addItem ( IDC_ITEM_LIST, GREEDY );m_RootPane->addPane ( bottomPane, ABSOLUTE_VERT );UpdateLayout();
/*/*// Possibility 3UpdateLayout (paneVert()<< item ( IDC_NEW_ITEM_STATIC, NORESIZE )<< 	( pane(HORIZONTAL, ABSOLUTE_VERT )<< item( IDC_NEW_ITEM, GREEDY )<< item( IDC_ADD_ITEM, NORESIZE ))<< item ( IDC_ITEM_LIST_STATIC, NORESIZE )<< item ( IDC_ITEM_LIST, GREEDY )<<	( pane(HORIZONTAL, ABSOLUTE_VERT )<< item( paneNull, GREEDY )<< item( IDOK, NORESIZE )<< item( IDCANCEL, NORESIZE )););
*//************************/return TRUE;  // return TRUE unless you set the focus to a control// EXCEPTION: OCX-Eigenschaftenseiten sollten FALSE zur點kgeben
}

http://www.xdnf.cn/news/1168075.html

相关文章:

  • 力扣-链表相关题 持续更新中。。。。。。
  • UE5 UI ScrollBox 滚动框
  • 欧拉系统二进制部署Docker
  • Linux_Ext系列文件系统基本认识(一)
  • Fluent许可与网络安全策略
  • 【洛谷】用两个数组实现静态单链表、静态双向链表,排队顺序
  • 【C语言进阶】动态内存管理(1)
  • 赋能未来数学课堂——基于Qwen3、LangChain与Agent架构的个性化教辅系统研究
  • Vue + WebSocket 实时数据可视化实战:多源融合与模拟数据双模式设计
  • vscode目录,右键菜单加入用VSCode打开文件和文件夹(快速解决)(含删除)(脚本)
  • 华为服务器操作系统openEuler介绍与安装
  • 信息学奥赛一本通 1553:【例 2】暗的连锁
  • C++_Hello算法_队列
  • Grails(Groovy)框架抛出NoHandlerFoundException而不是返回404 Not Found
  • Android-API调用学习总结
  • 专题 前端面试知识梳理大全
  • Leetcode题解:209长度最小的子数组,掌握滑动窗口从此开始!!!
  • Android13重置锁屏(1)
  • 碰一碰发视频源码搭建:支持OEM
  • 现在希望用git将本地文件crawler目录下的文件更新到远程仓库指定crawler目录下,命名相同的文件本地文件将其覆盖
  • 【Tomcat】Tomcat线程池深度调优手册(终极版)
  • 用USBi仿真器的SPI模式和IIC模式来调试DSP应该怎么做?
  • Vue项目中的AJAX请求与跨域问题解析
  • Linux CentOS 虚拟机升级内核至4.x以上版本
  • 异构融合 4A:重构高性能计算与复杂场景分析的安全与效率边界
  • Go 的第一类对象与闭包
  • Vercel AI SDK 3.0 学习入门指南
  • 厚铜板载流革命与精密压合工艺——高可靠性PCB批量制造的新锚点
  • 容器化部署 Tomcat + MySQL 实战指南:从入门到进阶
  • 分布式高可用ELK平台搭建及使用保姆级教程指南