MFC类Qt的自动布局框架

发布于:2025-07-25 ⋅ 阅读:(15) ⋅ 点赞:(0)

由于作者习惯使用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_HELP
	typedef ETSCSHelpDialog		CBaseDialog;
	typedef ETSCSHelpFormView	CBaseFormView;
	typedef ETSCSHelpDlgBar		CBaseDialogBar;
	typedef ETSCSHelpPropPage	CBasePropertyPage;
#else
	typedef 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 can
		ABSOLUTE_HORZ		= 1 << 0,	// Horizontal size is absolute
		RELATIVE_HORZ		= 1 << 1,	// Horizontal size in percent
		ABSOLUTE_VERT		= 1 << 2,	// Vertical size is absolute
		RELATIVE_VERT		= 1 << 3,	// Vertical size in percent

		NORESIZE			= ABSOLUTE_HORZ | ABSOLUTE_VERT,

		SIZE_MASK			= NORESIZE,

		ALIGN_LEFT			= 1 << 4,   // following only for NORESIZE
		ALIGN_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 borders
		TRACKER_RIGHT		= 1 << 11,  // between items in the future
		TRACKER_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:
		// Standardconstructor
		CPaneBase( );
		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:
		// Standardconstructor
		CPane( );
		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 root
			m_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 root
			m_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_MSG
	DECLARE_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
{
// Construction
	DECLARE_DYNAMIC(ETSLayoutFormView)
public:
	ETSLayoutFormView(UINT nID, LPCTSTR strName = NULL);   // standard constructor
	virtual ~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_MSG
	DECLARE_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_HELP
	ETSLayoutDialogBar(UINT nID);
#else
	ETSLayoutDialogBar();
#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_MSG
	LRESULT 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; }
// Overrides
    virtual 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_MSG
	DECLARE_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_MSG
	DECLARE_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__;
#endif

static 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 spacing

		CRect rLeft, rRight;
		pWndFirst->GetWindowRect(&rLeft);
		pWndSecond->GetWindowRect(&rRight);

		int sizeX = rRight.left - rLeft.right;
	
		if( sizeX < 0 ) {
			// compare top to top
			sizeX = rRight.left - rLeft.left;
		}
		else {
			sizeX -= 2*nDefaultBorder;
		}

		return new PaneItem(paneNull, this, NORESIZE, sizeX, 0);
	}
	else {
		// I'm interested in vertical spacing
		CRect rTop, rBot;
		pWndFirst->GetWindowRect(&rTop);
		pWndSecond->GetWindowRect(&rBot);

		int sizeY = rBot.top - rTop.bottom;

		if( sizeY < 0 ) {
			// compare top to top
			sizeY = 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 spacing
		return new PaneItem(paneNull, this, NORESIZE, rRect.Width(), 0);
	}
	else {
		// I'm interested in vertical spacing
		return 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 constraints
	CRect rcClient = GetRect();

	if( m_pWnd->IsKindOf( RUNTIME_CLASS( CDialog ) ) && !(m_pWnd->GetStyle()&WS_CHILD) ) {
		CRect rcWindow;
		m_pWnd->GetWindowRect(rcWindow);

		// Added by Didier BULTIAUW
        CWnd* parentWnd = m_pWnd->GetParent();
        if( (parentWnd != 0) && parentWnd->IsKindOf(RUNTIME_CLASS(CView)) )
        {
			CRect rcParent;
            parentWnd->GetWindowRect(rcParent);
            rcWindow.OffsetRect(-rcParent.left,-rcParent.top);
        }
		// end add

		CRect rcBorder = rcWindow;
		rcBorder -= rcClient;

		// Min and Max info
		int 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 Layout
	rcClient = GetRect();

	// Add a Border around the rootPane
	rcClient.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);
	}
	else
		Layout(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 time
            if(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 children
		if( 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 rect
				static_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 implementation


ETSLayoutMgr::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 size
			m_sizeXMin		= rcControl.Width();
		}

		if(sizeY == 0) {
			m_sizeY			= rcControl.Height();
		}
		else {
			m_sizeY = sizeY;
		}
		if( m_sizeYMin == -1 ) {
			// do not make smaller than current size
			m_sizeYMin		= rcControl.Height();
		}

		TCHAR szClassName[ MAX_PATH ];
		::GetClassName( m_hwndCtrl, szClassName, MAX_PATH );

		// special treatment for combo-boxes
		if( _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 size
			m_sizeXMin		= rcControl.Width();
		}

		if(sizeY == 0) {
			m_sizeY			= rcControl.Height();
		}
		else {
			m_sizeY = sizeY;
		}
		if( m_sizeYMin == -1 ) {
			// do not make smaller than current size
			m_sizeYMin		= rcControl.Height();
		}

		TCHAR szClassName[ MAX_PATH ];
		::GetClassName( m_hwndCtrl, szClassName, MAX_PATH );

		// special treatment for combo-boxes
		if( _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-boxes
		if( 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 CB
			if( 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 implementation


ETSLayoutMgr::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 implementation


ETSLayoutMgr::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 implementation

ETSLayoutMgr::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 orientation
		CPane 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;
	}
	else
		return 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;
			else
				nMaxConstr = 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;
			else
				nMaxConstr = 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 returnvalue
	int nGreedy = 0;

	// first, subtract all absoulute items from available space
	for(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 space
			if(pItem->modeResize() & ABSOLUTE_HORZ) {
				availSpace -= (sizePrimary[i] = pItem->getConstrainHorz(0));
			}

			// count Greedy items for later
			if(!(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 space
			if(pItem->modeResize() & ABSOLUTE_VERT) {
				availSpace -= (sizePrimary[i] = pItem->getConstrainVert(0));
			}

			// count Greedy items for later
			if(!(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 subtracted

	int 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 availSpace

	int 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/max
	
	int relLeft = 0;			// The cumulated amout of space that can be saved by
								// shrinking the items/panes up to the minimum
	
	int 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/subpanes

	for(int i=0; i<m_paneItems.GetSize(); ++i) {
		CPaneBase pItem = m_paneItems[i];

		// For all relative items in primary direction
		if( (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 size
				sizePrimary[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 size
				relDiff += ( 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 size
				sizePrimary[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 pixels
				relLeft	+= ( nSizeRel - nSizeRelMin );

				// Set the primary size of this item/pane. Can later be modified by fixup
				sizePrimary[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 step
			availSpace	-= 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 iteration
		int 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 limited
			if( (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 iteration
				bool bLast = false;

				// the difference should be distributed amoung all non-limited items/subpanes equally.
				// nDiff is the amount for the current item/subpane
				int nDiff = (relDiff * sizePrimary[i]) / relCount;

				// if it's a too small value just add it to the current pane and break iteration
				if( abs(relDiff) <= FIXUP_CUTOFF ) {
					// take it all in this step
					nDiff = relDiff;

					// set break flag
					bLast = true;
				}

				// calculate the new size for the current item/subpane
				int 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 sum
					relDist += ( sizePrimary[i] - sizeMin[i] );

					// set it to the minimum possible size
					sizePrimary[i] = -sizeMin[i];

					// as this item/subpane is now limited it's occupied space doesn't count
					// for relCount anymore
					relCount-= ( sizePrimary[i] );
				}
				else {
					// account the difference of the sizes in relDist and set new size
					relDist += ( sizePrimary[i] - nNewSize );
					sizePrimary[i] = nNewSize;

					// if it's the last one break now
					if(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 value
	for(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 space
	int 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/max
	
	int greedyLeft = 0;			// The cumulated amount of space that can be saved by
								// shrinking the items/panes up to the minimum
	
	int 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/subpanes

	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)
			)
			||
			(m_Orientation==VERTICAL   
				&& !(pItem->modeResize()&ABSOLUTE_VERT) 
				&& !(pItem->modeResize()&RELATIVE_VERT)
			) 
		)
		{

			// All greedy items get an equal portion of the left space
			int 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 space
			if( 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 size
				sizePrimary[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 size
				greedyDiff		+= ( 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 size
				sizePrimary[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 pixels
				greedyLeft		+= ( nSize - nSizeMin );

				// Set the primary size of this item/pane. Can later be modified by fixup
				sizePrimary[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 step
			availSpace	-= 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 present
	bool 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 iteration
		int greedyDist = 0;

		// at least on not limited item present
		bAtLeastOne = 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 iteration
				bool bLast = false;

				// the difference should be distributed among all non-limited items/subpanes equally.
				// nDiff is the amount for the current item/subpane
				int nDiff = (greedyDiff * sizePrimary[i]) / greedyCount;

				// if it's a too small value just add it to the current pane and break iteration
				if( abs(greedyDiff) <= FIXUP_CUTOFF || nDiff == 0) {
					// take it all in this step
					nDiff = greedyDiff;

					// set break flag
					bLast = true;
				}

				// calculate the new size for the current item/subpane
				int 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 sum
					greedyDist += ( sizePrimary[i] - sizeMin[i] );

					// set it to the minimum possible size
					sizePrimary[i] = sizeMin[i];

					// as this item/subpane is now limited its occupied space doesn't count
					// for relCount anymore
					greedyCount -= ( sizePrimary[i] );
				}
				else {
					// yes, there is one
					bAtLeastOne = true;

					// account the difference of the sizes in relDist and set new size
					greedyDist += ( sizePrimary[i] - nNewSize );
					sizePrimary[i] = nNewSize;

					// if it's the last one break now
					if(bLast)
						break;
				}
			}
		}
		// Distributed some greedyDiff-space in every iteration
		ASSERT(!bAtLeastOne || greedyDist != 0 || greedyCount<=0);
		greedyDiff -= greedyDist;
	}


	// Fixup Greedy II
	if( 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 possible
					if( sizeMax[i] == -1) {
						// all fits in
						sizePrimary[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 value
	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
				&& 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 subpanes
	ASSERT(m_paneItems.GetSize());

	// This Array holds the size in primary direction for each item/subpane
	CArray<int,int>	sizePrimary;
	sizePrimary.SetSize(m_paneItems.GetSize());

	// This Array holds information about the minimum size in primary direction
	CArray<int,int>	sizeMin;
	sizeMin.SetSize(m_paneItems.GetSize());

	// This Array holds information about the maximum size in primary direction
	CArray<int,int>	sizeMax;
	sizeMax.SetSize(m_paneItems.GetSize());


	// How much space is actually available, subtract all borders between items
	int 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 too
	availSpace -= 2*m_sizeExtraBorder;

	// Add the extra Border to top/bottem resp. left/right
	if(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/subpanes
	int 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 them
	if( availSpace > 0 ) {
		// Count possible Items
		int 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 bigger
			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) 
				)
				{

					if( nFillItems == 1 ) {
						// the last one gets all the rest
						sizePrimary[i]	+= availSpace;
						availSpace		= 0;
						--nFillItems;
					}
					else {
						sizePrimary[i]	+= availSpace/nFillItems;
						availSpace		-= availSpace/nFillItems;
						--nFillItems;
					}

				}
			}
		}

	}

	// Now reposition all items:

	// starting offset
	int 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/subpane
		CRect 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_INIT
	m_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 handlers

BOOL 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 controls
		UpdateLayout();
		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 differ
			int nDiffHorz = rcWnd.Width() - rcClient.Width();
			int nDiffVert = rcWnd.Height() - rcClient.Height();

			// Take into account that there is a border around the rootPane
			lpMMI->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 resizable
    this->ModifyStyle(0, WS_THICKFRAME);

	if(!m_strRegStore.IsEmpty()) {
		Load(m_strRegStore);
	}	

#ifdef _AUTO_SET_ICON
	POSITION 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 icon
	if(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);
		}             
		else
			AfxMessageBox(_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/position
	if(!m_strRegStore.IsEmpty()) {
		Save(m_strRegStore);
	}	

	// manually delete layout definition if object is reused
	m_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_INIT
	m_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 handlers

LRESULT 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 differ
		CSize 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 differ
		CSize 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 controls
		UpdateLayout();
	}
	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 dialog

IMPLEMENT_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 handlers

BOOL 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 controls
		UpdateLayout();
	}
//	MoveWindow(0,0,cx,cy);
}

/*
void ETSLayoutFormView::UpdateLayout()
{
	ETSLayoutMgr::UpdateLayout();

	if(m_RootPane.IsValid()) {
		// Force MainFrame to re-layout
		CFrameWnd* 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.cpp

	if(m_RootPane.IsValid()) {
		CRect rcClient = GetRect();

		CRect rcWnd;
		GetParent()->GetWindowRect(rcWnd);
	
		// How much do Window and Client differ
		rcWnd-=rcClient;

		// Take into account that there is a border around the rootPane
		lpMMI->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_HELP
	IMPLEMENT_DYNCREATE(ETSLayoutPropertyPage, ETSCSHelpPropPage)
#else
	IMPLEMENT_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 again
		m_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 page
	if( 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 reused
	m_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 controls
		UpdateLayout();
	}	
}

void ETSLayoutPropertyPage::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{
	if(m_RootPane.IsValid()) {
		CRect rcClient = GetRect();

		CRect rcWnd;
		GetWindowRect(rcWnd);
		
		// How much do Window and Client differ
		int nDiffHorz = rcWnd.Width() - rcClient.Width();
		int nDiffVert = rcWnd.Height() - rcClient.Height();

		// Take into account that there is a border around the rootPane
		lpMMI->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 position
			m_bLockMove = false;
			MoveWindow(pSheet->m_rcPage);
			m_bLockMove = true;
		}
	}

	UpdateLayout();	

	return CBasePropertyPage::OnSetActive();
}

/////////////////////////////////////////////////////////////////////////////
// ETSLayoutPropertySheet

IMPLEMENT_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 button
			CWnd *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 Gripper
		if(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 tab
				GetTabControl()->AdjustRect(FALSE, &rcWnd);
			}
			else
			{
				rcWnd.bottom += 5;
			}

			// we need this size in WizardMode in order to 
			// reposition newly activated page correctly
			m_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 TabCtrl
    paneRoot << itemTab;
}

void ETSLayoutPropertySheet::AddButtons(CPane paneBottom)
{
	// first item greedy to keep others right
	paneBottom->addItem (paneNull, GREEDY);


	// add all Controls to the layouting
	bool 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 insert
				continue;
			}

			if((m_psh.dwFlags & PSH_NOAPPLYNOW) && _PropertySheetIDs[i] == ID_APPLY_NOW)
			{
				// don't insert
				continue;
			}

			// space before first one and between BACK & NEXT
			if( 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 rect
	GetWindowRect(&m_rcStart);

	CPropertyPage* pPage = CPropertySheet::GetActivePage();
	ASSERT(pPage);

	CRect rcPage;
	pPage->GetClientRect(&rcPage);

	CreateRoot(VERTICAL);
	//ASSERT(m_RootPane);

	// Add Tabcontrol to root pane
	m_ItemTab = item( GetTabControl(), GREEDY, 0, 0, 0, 0);
    AddMainArea(m_RootPane, m_ItemTab);

	// Tabcontrol is invisible in WizardMode
	if(IsWizard())
	{
		GetTabControl()->ShowWindow(false);
	}

	// add horizontal line in WizardMode
	if(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 together
		CPane bottomPane = pane(HORIZONTAL, ABSOLUTE_VERT, IsWizard() ? 0 : 5);

        AddButtons(bottomPane);
		// add bottom (button) pane if any controls were added
        if(bottomPane->m_paneItems.GetSize() > 0) {
    		m_RootPane << bottomPane;
        }
	}



	// some Space between Buttons und Gripper
	if(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 difference

	CRect 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 pages
		CSize 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-mode
		CRect 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 ETSLayoutMgr

		int 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/position
	if(!m_strRegStore.IsEmpty()) 
	{
		Save(m_strRegStore);
	}	
	m_RootPane = 0;

	CPropertySheet::OnDestroy();
}

void ETSLayoutPropertySheet::PostNcDestroy()
{
	if(m_bAutoDestroyPages)
	{
		// walk all pages and destry them
		for( 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 myself
		delete this;
	}
}

ETSLayoutMgr::CPaneBase::CPaneBase( )
{
	// MUST be initialized later
	m_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 can
		ABSOLUTE_HORZ		= 1 << 0,	// Horizontal size is absolute
		RELATIVE_HORZ		= 1 << 1,	// Horizontal size in percent
		ABSOLUTE_VERT		= 1 << 2,	// Vertical size is absolute
		RELATIVE_VERT		= 1 << 3,	// Vertical size in percent

		NORESIZE			= ABSOLUTE_HORZ | ABSOLUTE_VERT,

		SIZE_MASK			= NORESIZE,

		ALIGN_LEFT			= 1 << 4,   // following only for NORESIZE
		ALIGN_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 borders
		TRACKER_RIGHT		= 1 << 11,  // between items in the future
		TRACKER_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_MSG
	DECLARE_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 1
	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();

/*
	// Possibility 2
	CPane 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 3
	UpdateLayout (
		
		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
}


网站公告

今日签到

点亮在社区的每一天
去签到