Click here to Skip to main content
15,895,142 members
Articles / Desktop Programming / WTL

Resizable dialogs, form views and property sheets for WTL

Rate me:
Please Sign up or sign in to vote.
4.79/5 (9 votes)
7 Jul 20026 min read 173.6K   2.8K   38  
Implementation of resizable dialogs, form views and property sheets for WTL
// --------------------------------------------------------------------------
// Description : several WTL utility classes for managing the layout of window children
// Author	   : Serge Weinstock
//
//	You are free to use, distribute or modify this code
//	as long as this header is not removed or modified.
// --------------------------------------------------------------------------
#if !defined(__layoutmgr_h__)
#define __layoutmgr_h__

#include <map>

const int	N_MAX_ITERATIONS = 100;

//=====================================================================
// RegisteredWindowMessage
//=====================================================================
//! Wrapper class around RegisterWindowMessage
class ATL_NO_VTABLE RegisteredWindowMessage
{
public:
	//! Constructor
	RegisteredWindowMessage(LPCTSTR msgName);
	//! Returns the message ID
	operator UINT () const;
private:
	UINT	_msgID;	//!< the message number
};

//=============================================================================
// Private messages
//=============================================================================
extern const RegisteredWindowMessage	WMU_PS_ATTACH_PAGE;
extern const RegisteredWindowMessage	WMU_PS_INIT;
extern const RegisteredWindowMessage	WMU_PS_ATTACH_PS;

//=============================================================================
// SysInfo Helper
//=============================================================================
class SysInfo
{
public:
	//! Returns true if controls themes are supported
	static bool IsThemeSupported();
private:
	static int _isThemeSupported;
};

//=============================================================================
// LayoutMgr
//=============================================================================
//! This class is the base class for all resizable windows
/*!
	This class is inspired by the XmFormManager of Motif. 
	\p
	The layout of the children is based on relationships defined for every children and for some of their side.
	A children can have one side attached to another children or to one of the parent side.
	\p
	A constraint can be applied to one of the following side:
	<ul>
	<li>The left side (ATTACH_LEFT)</li>
	<li>The right side (ATTACH_RIGHT)</li>
	<li>The horizontal center(ATTACH_HCENTER)</li>
	<li>The top side (ATTACH_TOP)</li>
	<li>The bottom side (ATTACH_RIGHT)</li>
	<li>The vertical center(ATTACH_VCENTER)</li>
	</ul>
	The type of constraint can be:
	<ul>
	<li>No constraint (ATTACH_NONE)</li>
	<li>
	Attach the side to the opposited side of another children (ATTACH_WIDGET)
	\code
	 ---|    |---
	    |====|
	 ---|    |---
	\endcode
	</li>
	<li>
	Attach the side to the same side of another children (ATTACH_OPPOSITE_WIDGET)
	\code
	 ---|
	    |===
	 ---|   |
	        |
	        |
	 ---|   |
	    |===
	 ---|
	\endcode
	</li>
	<li>
	If the nb of position of the form has been set to N, the affected side will
	be at position/N th of the form (ATTACH_POSITION)
	\code
	 ---------------------
	 |   |   |   |   |   |
	 |   | |---  |   |   |
	 |   |=|     |   |   |
	 |   | |---  |   |   |
	 |   |   |   |   |   |
	 |--------------------
	\endcode
	</li>
	<li>
	Attach the side to the opposited side of the parent (ATTACH_FORM)
	\code
	 -------------
	 |    |---
	 |====|
	 |    |---
	 |------------
	\endcode
	</li>
	<li>
	Attach the side to the same side of the parent (ATTACH_OPPOSITE_FORM)
	\code
	 -------------
	 | ---|
	 |====|
	 | ---|
	 |------------
	\endcode
	</li>
	<li>
	Keeps the side to its initial position in the form (ATTACH_SELF)
	</li>
	</ul>
	
 */
template <class T>
class ATL_NO_VTABLE LayoutMgr {
public:
	//!
	enum Side {
		ATTACH_LEFT,		//!< left side
		ATTACH_RIGHT,		//!< right side
		ATTACH_HCENTER,		//!< horizontal center (works onky with ATTACH_OPPOSITE_WIDGET and ATTACH_FORM)
		ATTACH_TOP,			//!< top side
		ATTACH_BOTTOM,		//!< bottom side
		ATTACH_VCENTER,		//!< vertical center (works onky with ATTACH_OPPOSITE_WIDGET and ATTACH_FORM)
		ATTACH_MAX
		};

	enum ConstraintType {
		
		ATTACH_NONE = 0,		//!< no constraint
		ATTACH_WIDGET,			//!< the affected side is linked to the opposited side of the reference widget
		ATTACH_OPPOSITE_WIDGET,	//!< the affected side is linked to the same side of the reference widget
		ATTACH_POSITION,		//!< if the nb of position of the form has been set to N, the affected side will be at position/N th of the form
		ATTACH_FORM,			//!< the affected side will be attached to the corresponding border of the form
		ATTACH_OPPOSITE_FORM,	//!< the affected side will be attached to the opposite border of the form
		ATTACH_SELF				//!< the affected side will be attached to its initial position in the form
		};
public:
	//! Constructor
	LayoutMgr();
	//! Destructor
	~LayoutMgr();

	//! Sets the number of positions for the constraints of type ATTACH_POSITION
	void SetNPositions(unsigned nPos);

	//! Set the contraint to ATTACH_NONE
	void AttachNone(HWND child, Side side);
	//! Set the contraint to ATTACH_WIDGET
	void AttachWidget(HWND child, Side side, HWND reference);
	//! Set the contraint to ATTACH_OPPOSITE_WIDGET
	void AttachOppositeWidget(HWND child, Side side, HWND reference);
	//! Set the contraint to ATTACH_POSITION
	void AttachPosition(HWND child, Side side, unsigned pos);
	//! Set the contraint to ATTACH_FORM
	void AttachForm(HWND child, Side side);
	//! Set the contraint to ATTACH_OPPOSITE_FORM
	void AttachOppositeForm(HWND child, Side side);
	//! Set the contraint to ATTACH_SELF
	void AttachSelf(HWND child, Side side);

	//! Set the contraint to ATTACH_NONE
	void AttachNone(UINT childID, Side side);
	//! Set the contraint to ATTACH_WIDGET
	void AttachWidget(UINT childID, Side side, UINT refID);
	//! Set the contraint to ATTACH_OPPOSITE_WIDGET
	void AttachOppositeWidget(UINT childID, Side side, UINT refID);
	//! Set the contraint to ATTACH_POSITION
	void AttachPosition(UINT childID, Side side, unsigned pos);
	//! Set the contraint to ATTACH_FORM
	void AttachForm(UINT childID, Side side);
	//! Set the contraint to ATTACH_OPPOSITE_FORM
	void AttachOppositeForm(UINT childID, Side side);
	//! Set the contraint to ATTACH_SELF
	void AttachSelf(UINT childID, Side side);

	// show/hide the resizing handle
	bool ShowHandle(bool show);

	// unregister a window
	void Unattach(UINT childID);
	void Unattach(HWND hWnd);

	//!< All the information representing a constraint
	struct SideConstraint
		{
		ConstraintType	_type;		//!< Constraint type
		int				_offset;	//!< Offset used when applying the constraint
		HWND			_reference;	//!< the refrence widget used in constraint of type ATTACH_WIDGET
		int				_pos;		//!< the position used in contraint of type ATTACH_POSITION

		//!< Constructor
		SideConstraint()
			: _type(ATTACH_NONE), _offset(0), _reference(0), _pos(0) {}
		};
	//!< all the constraints applied to a children
	struct	Constraint
		{
		SideConstraint	_constraint[ATTACH_MAX];	//!< a map between a side and the corresponding constraint
		RECT			_pos;						//!< this rectangle is used during the computation of the layout for holding the temporary position of the children
		//!< Constructor
		Constraint()
			{ 
			_pos.left = _pos.top = _pos.right = _pos.bottom = 0; 
			::memset(_constraint, ATTACH_MAX*sizeof(SideConstraint), 0);
			}
		};

	// A map between children and their associated constraints
	typedef std::map<HWND , Constraint, std::less<HWND> >	ConstraintsMap;

	//! Compute the layout of the children without applying it to the children
	bool ComputeLayout();
	//! Moves and resizes the children according to the computed layout
	void DoLayout();

	// can be overridded by descendents
	//! Returns the client area of the container
	void GetDialogRect(LPRECT r);
	//! This method allows to define the layout constraints
	void DefineLayout();
	//! This methods returns the offset for positionning the controls in the client rect
	void GetOffset(LPPOINT offset);

	//! Common method called on WM_SIZE messages
	LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	//! Common method called on WM_GETMINMAXINFO messages
	LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	//! Common method called on WMU_PS_ATTACH_PS messages
	LRESULT OnAttachPropertySheet(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	//! Common method called on WMU_ERASEBKG messages
	/*!
	This method excludes the control window rectangles defined in _controlsToClip from the DC clipping region
	\warning _controlsToClip must be an array of control IDs where the last element must be 0
	*/
	LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

	ConstraintsMap	_constraints;		//!< all the constraints
	int				_nPos;				//!< the total nb of positions for ATTACH_POSITION constraints
	SIZE			_minClientSize;		//!< the minimum client size of the container
	SIZE			_prevClientSize;	//!< the client size before resizing
	SIZE			_minWindowSize;		//!< the minimum window size of the mgr
	SIZE			_maxWindowSize;		//!< the minimum window size of the mgr
	bool			_showHandle;		//!< show the resizing handle ?
	CScrollBar		_handle;			//!< the handle window
	const UINT		*_controlsToClip;	//!< the default _controlsToClip array
};

#ifdef __ATLWIN_H__
//=====================================================================
// CResizableDialogImplT
//=====================================================================
//! This class is the base class for resizable dialogs
template <class T, class TBase = CWindow, class DlgBase = CDialogImpl<T, TBase> >
class ATL_NO_VTABLE CResizableDialogImplT : public DlgBase, public LayoutMgr<T>
{
public:
	typedef CResizableDialogImplT<T, TBase, DlgBase> thisClass;

	//! Constructor
	/*!
	\param useHandle : if true a handle is shown on the bottom right corner of the dialog
	*/
	CResizableDialogImplT(bool useHandle = true);

	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_SIZE, LayoutMgr<T>::OnSize)
		MESSAGE_HANDLER(WM_ERASEBKGND, LayoutMgr<T>::OnEraseBackground)
		MESSAGE_HANDLER(WM_GETMINMAXINFO, LayoutMgr<T>::OnGetMinMaxInfo)
		MESSAGE_HANDLER(WMU_PS_ATTACH_PS, LayoutMgr<T>::OnAttachPropertySheet)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
	END_MSG_MAP()

protected:
	//! Initialisation of the dialog
	LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
};

//=====================================================================
// CResizableDialogImpl
//=====================================================================
template <class T, class TBase = CWindow>
class ATL_NO_VTABLE CResizableDialogImpl : public CResizableDialogImplT<T, TBase, CDialogImpl<T, TBase> >
{
public:
	typedef CResizableDialogImplT<T, TBase, CDialogImpl<T, TBase> > inherited;
	//! Constructor
	/*!
	\param useHandle : if true a handle is shown on the bottom right corner of the dialog
	*/
	CResizableDialogImpl(bool useHandle = true);
};

#ifndef _ATL_NO_HOSTING

//=====================================================================
// CAxResizableDialogImpl
//=====================================================================
template <class T, class TBase = CWindow>
class ATL_NO_VTABLE CAxResizableDialogImpl : public CResizableDialogImplT<T, TBase, CAxDialogImpl<T, TBase> >
{
public:
	typedef CResizableDialogImplT<T, TBase, CAxDialogImpl<T, TBase> > inherited;
	//! Constructor
	/*!
	\param useHandle : if true a handle is shown on the bottom right corner of the dialog
	*/
	CAxResizableDialogImpl(bool useHandle = true);
};

#endif // ifndef _ATL_NO_HOSTING

#endif // ifdef __ATLWIN_H__

#if defined(__ATLSCRL_H__) && defined(__ATLWIN_H__)
//=====================================================================
// CResizableFormViewImplT
//=====================================================================
//! This class is the base class for resizable form views
template <class T, class TBase = CWindow, class DlgBase = CDialogImpl<T, TBase> >
class ATL_NO_VTABLE CResizableFormViewImplT : 
	public DlgBase,
	public CScrollImpl<T>,
	public LayoutMgr<T>
{
public:

	typedef CResizableFormViewImplT<T, TBase, DlgBase>	thisClass;
	typedef CDialogImpl<T, TBase>						dialogBase;
	typedef CScrollImpl<T>								scrollBase;

	BEGIN_MSG_MAP(thisClass)
		CHAIN_MSG_MAP(scrollBase)
		MESSAGE_HANDLER(WM_SIZE, LayoutMgr<T>::OnSize)
		MESSAGE_HANDLER(WM_ERASEBKGND, LayoutMgr<T>::OnEraseBackground)
		MESSAGE_HANDLER(WMU_PS_ATTACH_PS, LayoutMgr<T>::OnAttachPropertySheet)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
	END_MSG_MAP()

	//! Paints the view
	void DoPaint(CDCHandle /*dc*/);
// LayoutMgr
	//! Returns the client area of the container
	void GetDialogRect(LPRECT r);
	//! This methods returns the offset for positionning the controls in the client rect
	void GetOffset(LPPOINT offset);
protected:

	//! Initialisation of the dialog
	LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
};

//=====================================================================
// CResizableFormViewImpl
//=====================================================================
template <class T, class TBase = CWindow>
class ATL_NO_VTABLE CResizableFormViewImpl : public CResizableFormViewImplT<T, TBase, CDialogImpl<T, TBase> >
{
};

#ifndef _ATL_NO_HOSTING

//=====================================================================
// CAxResizableFormViewImpl
//=====================================================================
template <class T, class TBase = CWindow>
class ATL_NO_VTABLE CAxResizableFormViewImpl : public CResizableFormViewImplT<T, TBase, CAxDialogImpl<T, TBase> >
{
};

#endif // ifndef _ATL_NO_HOSTING

#endif // ifdef defined(__ATLSCRL_H__) && defined(__ATLWIN_H__)

#if defined(__ATLDLGS_H__) && defined(__ATLWIN_H__)
//=====================================================================
// CResizablePropertySheetImpl
//=====================================================================
//! This the base class for resizable property sheet
template <bool childWnd, class T, class TBase = CPropertySheetWindow>
class ATL_NO_VTABLE CResizablePropertySheetImpl : 
	public CPropertySheetImpl<T, TBase>,
	public LayoutMgr<T>
{
public:

	typedef CResizablePropertySheetImpl<childWnd, T, TBase>	thisClass;
	typedef CPropertySheetImpl<T, TBase>					dialogBase;

	//! Constructor
	/*!
	\param useHandle : if true a handle is shown on the bottom right corner of the dialog
	*/
	CResizablePropertySheetImpl(WTL::_U_STRINGorID title = (LPCTSTR)NULL, UINT uStartPage = 0, HWND hWndParent = NULL, bool useHandle = true);
	//! Creates it
	HWND Create(HWND hWndParent = NULL);

	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_SIZE, LayoutMgr<T>::OnSize)
		MESSAGE_HANDLER(WM_GETMINMAXINFO, LayoutMgr<T>::OnGetMinMaxInfo)
		MESSAGE_HANDLER(WMU_PS_ATTACH_PS, LayoutMgr<T>::OnAttachPropertySheet)
		MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
		MESSAGE_HANDLER(WMU_PS_ATTACH_PAGE, OnAttachPage)
		MESSAGE_HANDLER(WMU_PS_INIT, OnInit)
		CHAIN_MSG_MAP(dialogBase)
	END_MSG_MAP()

	//! Property sheet callback function
	static int CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM lParam);
	//! Called at the intialisation of the property sheet
	void OnInitDialog();
	//! Common method called on WMU_ERASEBKG messages
	/*!
	This method excludes the tab control window from the area to refresh
	*/
	LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
protected:
	//! Attach a page
	LRESULT OnAttachPage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	//! Initialises the property sheet
	LRESULT OnInit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
};

//=============================================================================
// CChildResizablePropertySheetImpl
//=============================================================================
//! A resizable child property sheet
template <class T, class TBase = CPropertySheetWindow>
class ATL_NO_VTABLE CChildResizablePropertySheetImpl : 
	public CResizablePropertySheetImpl<true, T, TBase>
{
public:

	typedef CChildResizablePropertySheetImpl<T, TBase>	thisClass;
	typedef CResizablePropertySheetImpl<true, T, TBase>	dialogBase;

	BEGIN_MSG_MAP(thisClass)
		CHAIN_MSG_MAP(dialogBase)
	END_MSG_MAP()

	//! Constructor
	/*!
	\param useHandle : if true a handle is shown on the bottom right corner of the dialog
	\param placeHolderID : id of the control which will server as a reference for the position of the property sheet
	*/
	CChildResizablePropertySheetImpl(UINT placeHolderID, WTL::_U_STRINGorID title = (LPCTSTR)NULL, UINT uStartPage = 0, HWND hWndParent = NULL, bool useHandle = true);
	//! Specific intialisation depending on wether or not the property sheet is a child window
	void SpecificInit();
protected:

	UINT	m_ID_Place_Holder; // the ID of the placeHolder
};

//=============================================================================
// CPopupResizablePropertySheetImpl
//=============================================================================
//! A resizable popup property sheet
template <class T, class TBase = CPropertySheetWindow>
class ATL_NO_VTABLE CPopupResizablePropertySheetImpl : 
	public CResizablePropertySheetImpl<false, T, TBase>
{
public:

	typedef CPopupResizablePropertySheetImpl<T, TBase>		thisClass;
	typedef CResizablePropertySheetImpl<false, T, TBase>	dialogBase;

	//! Constructor
	/*!
	\param useHandle : if true a handle is shown on the bottom right corner of the dialog
	\param placeHolderID : id of the control which will server as a reference for the position of the property sheet
	*/
	CPopupResizablePropertySheetImpl(WTL::_U_STRINGorID title = (LPCTSTR)NULL, UINT uStartPage = 0, HWND hWndParent = NULL, bool useHandle = true);

	BEGIN_MSG_MAP(thisClass)
		CHAIN_MSG_MAP(dialogBase)
	END_MSG_MAP()

	//! Specific intialisation depending on wether or not the property sheet is a child window
	void SpecificInit();
};

//=====================================================================
// CResizablePropertyPageImpl
//=====================================================================
//! This class is the base class for resizable dialogs
template <class T, class TBase = CPropertyPageWindow, class dialogBase = CPropertyPageImpl<T, TBase> >
class ATL_NO_VTABLE CResizablePropertyPageImplT : public dialogBase, public LayoutMgr<T>
{
public:
	typedef CResizablePropertyPageImplT<T, TBase, dialogBase> thisClass;

	//! Constructor
	CResizablePropertyPageImplT(WTL::_U_STRINGorID title = (LPCTSTR)NULL);

	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_SIZE, LayoutMgr<T>::OnSize)
		MESSAGE_HANDLER(WM_ERASEBKGND, LayoutMgr<T>::OnEraseBackground)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		CHAIN_MSG_MAP(dialogBase)
	END_MSG_MAP()

	//! Page Activation;
	BOOL OnSetActive();
	//! Initialisation of the dialog
	LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

protected:
	bool	_init;
};

//=====================================================================
// CResizablePropertyPageImpl
//=====================================================================
template <class T, class TBase = CPropertyPageWindow>
class ATL_NO_VTABLE CResizablePropertyPageImpl : public CResizablePropertyPageImplT<T, TBase, CPropertyPageImpl<T, TBase> >
{
public:
	typedef CResizablePropertyPageImplT<T, TBase, CPropertyPageImpl<T, TBase> > inherited;

	//! Constructor
	CResizablePropertyPageImpl(WTL::_U_STRINGorID title = (LPCTSTR)NULL);
};

#ifndef _ATL_NO_HOSTING
//=====================================================================
// CAxResizablePropertyPageImpl
//=====================================================================
template <class T, class TBase = CPropertyPageWindow>
class ATL_NO_VTABLE CAxResizablePropertyPageImpl : public CResizablePropertyPageImplT<T, TBase, CAxPropertyPageImpl<T, TBase> >
{
public:
	typedef CAxResizablePropertyPageImpl<T, TBase> thisClass;
	typedef CResizablePropertyPageImplT<T, TBase, CAxPropertyPageImpl<T, TBase> > inherited;

	//! Constructor
	CAxResizablePropertyPageImpl(WTL::_U_STRINGorID title = (LPCTSTR)NULL);
#if (_ATL_VER >= 0x0700)
	// workaround an ATL7 bug, the initialisation code for ActiveX control is in CAxDialogImpl or CAxResizablePropertyPageImpl derives from CAxDialogImpl
	// so we must copy the code of CAxDialogImpl into this class and call it at the initalisation of the dialog
	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		CHAIN_MSG_MAP(inherited)
	END_MSG_MAP()

	virtual HRESULT CreateActiveXControls(UINT nID);
	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
#endif // (_ATL_VER >= 0x0700)
};

#endif // ifndef _ATL_NO_HOSTING

#endif // ifdef defined(__ATLDLGS_H__) && defined(__ATLWIN_H__)

//=============================================================================
//=============================================================================
// Inlines
//=============================================================================
//=============================================================================
//-----------------------------------------------------------------------------
// RegisteredWindowMessage
//-----------------------------------------------------------------------------
inline RegisteredWindowMessage::RegisteredWindowMessage(LPCTSTR msgName)
{
	ATLASSERT(msgName != 0);
	_msgID = ::RegisterWindowMessage(msgName);
}

inline RegisteredWindowMessage::operator UINT () const
{
	return _msgID;
}

//-----------------------------------------------------------------------------
// SysInfo Helper
//-----------------------------------------------------------------------------
inline bool SysInfo::IsThemeSupported()
{
	if (_isThemeSupported == -1)
		{
		HMODULE hThemeDLL = ::LoadLibrary(_T("uxtheme.dll"));
		_isThemeSupported = (hThemeDLL != NULL) ? 1 : 0;
		if(hThemeDLL != NULL)
			{
			::FreeLibrary(hThemeDLL);
			}
		}
	return (_isThemeSupported != 0);
}

//-----------------------------------------------------------------------------
// LayoutMgr
//-----------------------------------------------------------------------------
template <class T>
inline LayoutMgr<T>::LayoutMgr()
: _nPos(100), _showHandle(false), _controlsToClip(0)
{
	_minWindowSize.cx = _minWindowSize.cy = 0;
	_maxWindowSize.cx = _maxWindowSize.cy = -1;
	_minClientSize.cx = _minClientSize.cy = 0;
	_prevClientSize.cx = _prevClientSize.cy = 0;
}

template <class T>
inline LayoutMgr<T>::~LayoutMgr()
{
	if (_handle.IsWindow()) { _handle.DestroyWindow(); }
}

template <class T>
inline void LayoutMgr<T>::SetNPositions(unsigned nPos)
{
	ATLASSERT(_nPos>0);
	_nPos = nPos;
}

// Definitions of the constraints from IDs
template <class T>
inline void LayoutMgr<T>::AttachNone(UINT childID, Side side)
{
	T* pT = static_cast<T*>(this);
	HWND child = pT->GetDlgItem(childID);
	ATLASSERT(child != 0);
	AttachNone(child, side);
}


template <class T>
inline void LayoutMgr<T>::AttachWidget(UINT childID, Side side, UINT refID)
{
	T* pT = static_cast<T*>(this);
	HWND child = pT->GetDlgItem(childID);
	HWND ref = pT->GetDlgItem(refID);
	ATLASSERT(child != 0);
	ATLASSERT(ref != 0);
	AttachWidget(child, side, ref);
}

template <class T>
inline void LayoutMgr<T>::AttachOppositeWidget(UINT childID, Side side, UINT refID)
{
	T* pT = static_cast<T*>(this);
	HWND child = pT->GetDlgItem(childID);
	HWND ref = pT->GetDlgItem(refID);
	ATLASSERT(child != 0);
	ATLASSERT(ref != 0);
	AttachOppositeWidget(child, side, ref);
}

template <class T>
inline void LayoutMgr<T>::AttachPosition(UINT childID, Side side, unsigned pos)
{
	T* pT = static_cast<T*>(this);
	HWND child = pT->GetDlgItem(childID);
	ATLASSERT(child != 0);
	AttachPosition(child, side, pos);
}

template <class T>
inline void LayoutMgr<T>::AttachForm(UINT childID, Side side)
{
	T* pT = static_cast<T*>(this);
	HWND child = pT->GetDlgItem(childID);
	ATLASSERT(child != 0);
	AttachForm(child, side);
}

template <class T>
inline void LayoutMgr<T>::AttachOppositeForm(UINT childID, Side side)
{
	T* pT = static_cast<T*>(this);
	HWND child = pT->GetDlgItem(childID);
	ATLASSERT(child != 0);
	AttachOppositeForm(child, side);
}

template <class T>
inline void LayoutMgr<T>::AttachSelf(UINT childID, Side side)
{
	T* pT = static_cast<T*>(this);
	HWND child = pT->GetDlgItem(childID);
	ATLASSERT(child != 0);
	AttachSelf(child, side);
}

template <class T>
inline void LayoutMgr<T>::Unattach(UINT childID)
{
	T* pT = static_cast<T*>(this);
	HWND child = pT->GetDlgItem(childID);
	Unattach(child);
}

template <class T>
inline void LayoutMgr<T>::Unattach(HWND hwnd)
{
	_constraints.erase(hwnd);

	ConstraintsMap::iterator c;

	for (c = _constraints.begin(); c != _constraints.end(); c++)
		{
		for (int side = 0 ; side < ATTACH_MAX; side++)
			{
			if (c->second._constraint[side]._reference == hwnd)
				{
				AttachNone(hwnd, (Side)side);
				}
			}
		}
}

//--------------------------------------------------------------
// Definitions of the constraints from HWND
//--------------------------------------------------------------
template <class T>
inline void LayoutMgr<T>::AttachNone(HWND child, Side side)
{
	_constraints[child]._constraint[side]._type = ATTACH_NONE;
	_constraints[child]._constraint[side]._reference = NULL;
	_constraints[child]._constraint[side]._pos = 0;
	_constraints[child]._constraint[side]._offset = 0;
	RECT rw;
	T	 *pT = static_cast<T*>(this);
	::GetWindowRect(child, &rw);
	pT->ScreenToClient(&rw);
	_constraints[child]._pos = rw;
}

template <class T>
inline void LayoutMgr<T>::AttachWidget(HWND child, Side side, HWND reference)
{
	_constraints[child]._constraint[side]._type = ATTACH_WIDGET;
	_constraints[child]._constraint[side]._reference = reference;
	_constraints[child]._constraint[side]._pos = 0;
	RECT rw, rr;
	T	 *pT = static_cast<T*>(this);
	::GetWindowRect(child, &rw);
	pT->ScreenToClient(&rw);
	_constraints[child]._pos = rw;
	::GetWindowRect(reference, &rr);
	pT->ScreenToClient(&rr);
	switch (side)
		{
		case ATTACH_LEFT:
			_constraints[child]._constraint[side]._offset = rw.left-rr.right;
			break;
		case ATTACH_RIGHT:
			_constraints[child]._constraint[side]._offset = rw.right-rr.left;
			break;
		case ATTACH_TOP:
			_constraints[child]._constraint[side]._offset = rw.top-rr.bottom;
			break;
		case ATTACH_BOTTOM:
			_constraints[child]._constraint[side]._offset = rw.bottom-rr.top;
			break;
		default:
			ATLASSERT(false);
		};
}

template <class T>
inline void LayoutMgr<T>::AttachOppositeWidget(HWND child, Side side, HWND reference)
{
	_constraints[child]._constraint[side]._type = ATTACH_OPPOSITE_WIDGET;
	_constraints[child]._constraint[side]._reference = reference;
	_constraints[child]._constraint[side]._pos = 0;
	_constraints[child]._constraint[side]._offset = 0;
	RECT rw, rr;
	T	 *pT = static_cast<T*>(this);
	::GetWindowRect(child, &rw);
	pT->ScreenToClient(&rw);
	_constraints[child]._pos = rw;
	::GetWindowRect(reference, &rr);
	pT->ScreenToClient(&rr);
	switch (side)
		{
		case ATTACH_LEFT:
			_constraints[child]._constraint[side]._offset = rw.left-rr.left;
			break;
		case ATTACH_RIGHT:
			_constraints[child]._constraint[side]._offset = rw.right-rr.right;
			break;
		case ATTACH_HCENTER:
			_constraints[child]._constraint[side]._offset = ((rw.left+rw.right)-(rr.left+rr.right))/2;
			break;
		case ATTACH_TOP:
			_constraints[child]._constraint[side]._offset = rw.top-rr.top;
			break;
		case ATTACH_BOTTOM:
			_constraints[child]._constraint[side]._offset = rw.bottom-rr.bottom;
			break;
		case ATTACH_VCENTER:
			_constraints[child]._constraint[side]._offset = ((rw.top+rw.bottom)-(rr.top+rr.bottom))/2;
			break;
		default:
			ATLASSERT(false);
		};
}

template <class T>
inline void LayoutMgr<T>::AttachPosition(HWND child, Side side, unsigned pos)
{
	_constraints[child]._constraint[side]._type = ATTACH_POSITION;
	_constraints[child]._constraint[side]._reference = NULL;
	_constraints[child]._constraint[side]._pos = pos;
	_constraints[child]._constraint[side]._offset = 0;
	RECT rw, rr;
	T	 *pT = static_cast<T*>(this);
	::GetWindowRect(child, &rw);
	pT->ScreenToClient(&rw);
	_constraints[child]._pos = rw;
	pT->GetDialogRect(&rr);
	switch (side)
		{
		case ATTACH_LEFT:
			_constraints[child]._constraint[side]._offset = rw.left-(pos*(rr.right-rr.left))/_nPos;
			break;
		case ATTACH_RIGHT:
			_constraints[child]._constraint[side]._offset = rw.right-(pos*(rr.right-rr.left))/_nPos;
			break;
		case ATTACH_TOP:
			_constraints[child]._constraint[side]._offset = rw.top-(pos*(rr.bottom-rr.top))/_nPos;
			break;
		case ATTACH_BOTTOM:
			_constraints[child]._constraint[side]._offset = rw.bottom-(pos*(rr.bottom-rr.top))/_nPos;
			break;
		default:
			ATLASSERT(false);
		};
}

template <class T>
inline void LayoutMgr<T>::AttachForm(HWND child, Side side)
{
	_constraints[child]._constraint[side]._type = ATTACH_FORM;
	_constraints[child]._constraint[side]._reference = NULL;
	_constraints[child]._constraint[side]._pos = 0;
	_constraints[child]._constraint[side]._offset = 0;
	RECT rw, rr;
	T	 *pT = static_cast<T*>(this);
	::GetWindowRect(child, &rw);
	pT->ScreenToClient(&rw);
	_constraints[child]._pos = rw;
	pT->GetDialogRect(&rr);
	switch (side)
		{
		case ATTACH_LEFT:
			_constraints[child]._constraint[side]._offset = rw.left-rr.left;
			break;
		case ATTACH_RIGHT:
			_constraints[child]._constraint[side]._offset = rw.right-rr.right;
			break;
		case ATTACH_HCENTER:
			_constraints[child]._constraint[side]._offset = ((rw.left+rw.right)-(rr.left+rr.right))/2;
			break;
		case ATTACH_TOP:
			_constraints[child]._constraint[side]._offset = rw.top-rr.top;
			break;
		case ATTACH_BOTTOM:
			_constraints[child]._constraint[side]._offset = rw.bottom-rr.bottom;
			break;
		case ATTACH_VCENTER:
			_constraints[child]._constraint[side]._offset = ((rw.top+rw.bottom)-(rr.top+rr.bottom))/2;
			break;
		default:
			ATLASSERT(false);
		};
}

template <class T>
inline void LayoutMgr<T>::AttachOppositeForm(HWND child, Side side)
{
	_constraints[child]._constraint[side]._type = ATTACH_OPPOSITE_FORM;
	_constraints[child]._constraint[side]._reference = NULL;
	_constraints[child]._constraint[side]._pos = 0;
	_constraints[child]._constraint[side]._offset = 0;
	RECT rw, rr;
	T	 *pT = static_cast<T*>(this);
	::GetWindowRect(child, &rw);
	pT->ScreenToClient(&rw);
	_constraints[child]._pos = rw;
	pT->GetDialogRect(&rr);
	switch (side)
		{
		case ATTACH_LEFT:
			_constraints[child]._constraint[side]._offset = rw.left-rr.right;
			break;
		case ATTACH_RIGHT:
			_constraints[child]._constraint[side]._offset = rw.right-rr.left;
			break;
		case ATTACH_TOP:
			_constraints[child]._constraint[side]._offset = rw.top-rr.bottom;
			break;
		case ATTACH_BOTTOM:
			_constraints[child]._constraint[side]._offset = rw.bottom-rr.top;
			break;
		default:
			ATLASSERT(false);
		};
}

template <class T>
inline void LayoutMgr<T>::AttachSelf(HWND child, Side side)
{
	_constraints[child]._constraint[side]._type = ATTACH_SELF;
	_constraints[child]._constraint[side]._reference = NULL;
	_constraints[child]._constraint[side]._pos = 0;
	_constraints[child]._constraint[side]._offset = 0;
	RECT rw, rr;
	T	 *pT = static_cast<T*>(this);
	::GetWindowRect(child, &rw);
	pT->ScreenToClient(&rw);
	_constraints[child]._pos = rw;
	pT->GetDialogRect(&rr);
	switch (side)
		{
		case ATTACH_LEFT:
			_constraints[child]._constraint[side]._offset = rw.left-rr.left;
			break;
		case ATTACH_RIGHT:
			_constraints[child]._constraint[side]._offset = rw.right-rr.left;
			break;
		case ATTACH_TOP:
			_constraints[child]._constraint[side]._offset = rw.top-rr.top;
			break;
		case ATTACH_BOTTOM:
			_constraints[child]._constraint[side]._offset = rw.bottom-rr.top;
			break;
		default:
			ATLASSERT(false);
		};
}

// Move all the windows to their definitive positions
template <class T>
inline void LayoutMgr<T>::DoLayout()
{
	ConstraintsMap::iterator	wgt;
	HDWP						hdwp;
	POINT						offset;
	T							*pT = static_cast<T*>(this);

	pT->GetOffset(&offset);
	hdwp = ::BeginDeferWindowPos(_constraints.size());
	for (wgt = _constraints.begin(); wgt != _constraints.end(); wgt++)
		{
		::DeferWindowPos(
			hdwp, wgt->first, NULL, 
			wgt->second._pos.left - offset.x, wgt->second._pos.top - offset.y,
			wgt->second._pos.right - wgt->second._pos.left,
			wgt->second._pos.bottom - wgt->second._pos.top,
			SWP_NOZORDER|SWP_NOREDRAW);
		}
	::EndDeferWindowPos(hdwp);
	pT->Invalidate();
}

// Layout management
template <class T>
inline bool LayoutMgr<T>::ComputeLayout()
{
	if (_constraints.empty())
		{
		return false;
		}
	
	// a very simple layout algorithm :
	// we apply the constraints until there's no more changes in the children layout
	int							i, ii;
	bool						change;
	ConstraintsMap::iterator	wgt;
	RECT						opos, formR;
	bool						doHLayout, doVLayout;
	T							*pT = static_cast<T *>(this);

	pT->GetDialogRect(&formR);

	doHLayout = ((formR.right - formR.left) > _minClientSize.cx) || (_prevClientSize.cx > _minClientSize.cx);
	doVLayout = ((formR.bottom - formR.top) > _minClientSize.cy) || (_prevClientSize.cy > _minClientSize.cy);

	if ((!doHLayout) && (!doVLayout))
		{
		return false;
		}

	for (i = 0; i < N_MAX_ITERATIONS; i++)
		{
		change = false;

		for (wgt = _constraints.begin(); wgt != _constraints.end(); wgt++)
			{
			RECT &pos = wgt->second._pos;
			opos = pos;
			if (doHLayout)
				//-----------------
				// left constraint
				//-----------------
				{
				SideConstraint &c = wgt->second._constraint[ATTACH_LEFT];
				switch (c._type)
					{
					case ATTACH_WIDGET:
						ATLASSERT(_constraints.find(c._reference) != _constraints.end());
						pos.left = _constraints[c._reference]._pos.right+c._offset;
						break;
					case ATTACH_OPPOSITE_WIDGET:
						ATLASSERT(_constraints.find(c._reference) != _constraints.end());
						pos.left = _constraints[c._reference]._pos.left+c._offset;
						break;
					case ATTACH_FORM:
						pos.left = formR.left+c._offset;
						break;
					case ATTACH_OPPOSITE_FORM:
						pos.left = formR.right+c._offset;
						break;
					case ATTACH_POSITION:
						pos.left = (c._pos*(formR.right-formR.left))/_nPos+c._offset;
						break;
					case ATTACH_SELF:
						pos.left = formR.left+c._offset;
						break;
					case ATTACH_NONE:
						break;
					default:
						ATLASSERT(false);
					}
				}
			if (doHLayout)
				//-----------------
				// right constraint
				//-----------------
				{
				SideConstraint &c = wgt->second._constraint[ATTACH_RIGHT];
				switch (c._type)
					{
					case ATTACH_WIDGET:
						ATLASSERT(_constraints.find(c._reference) != _constraints.end());
						pos.right = _constraints[c._reference]._pos.left+c._offset;
						break;
					case ATTACH_OPPOSITE_WIDGET:
						ATLASSERT(_constraints.find(c._reference) != _constraints.end());
						pos.right = _constraints[c._reference]._pos.right+c._offset;
						break;
					case ATTACH_FORM:
						pos.right = formR.right+c._offset;
						break;
					case ATTACH_OPPOSITE_FORM:
						pos.right = formR.left+c._offset;
						break;
					case ATTACH_POSITION:
						pos.right = (c._pos*(formR.right-formR.left))/_nPos+c._offset;
						break;
					case ATTACH_SELF:
						pos.right = formR.left+c._offset;
						break;
					case ATTACH_NONE:
						break;
					default:
						ATLASSERT(false);
					}
				}
			if (doVLayout)
				//-----------------
				// top constraint
				//-----------------
				{
				SideConstraint &c = wgt->second._constraint[ATTACH_TOP];
				switch (c._type)
					{
					case ATTACH_WIDGET:
						ATLASSERT(_constraints.find(c._reference) != _constraints.end());
						pos.top = _constraints[c._reference]._pos.bottom+c._offset;
						break;
					case ATTACH_OPPOSITE_WIDGET:
						ATLASSERT(_constraints.find(c._reference) != _constraints.end());
						pos.top = _constraints[c._reference]._pos.top+c._offset;
						break;
					case ATTACH_FORM:
						pos.top = formR.top+c._offset;
						break;
					case ATTACH_OPPOSITE_FORM:
						pos.top = formR.bottom+c._offset;
						break;
					case ATTACH_POSITION:
						pos.top = (c._pos*(formR.bottom-formR.top))/_nPos+c._offset;
						break;
					case ATTACH_SELF:
						pos.top = formR.top+c._offset;
						break;
					case ATTACH_NONE:
						break;
					default:
						ATLASSERT(false);
					}
				}
			if (doVLayout)
				//-----------------
				// bottom constraint
				//-----------------
				{
				SideConstraint &c = wgt->second._constraint[ATTACH_BOTTOM];
				switch (c._type)
					{
					case ATTACH_WIDGET:
						ATLASSERT(_constraints.find(c._reference) != _constraints.end());
						pos.bottom = _constraints[c._reference]._pos.top+c._offset;
						break;
					case ATTACH_OPPOSITE_WIDGET:
						ATLASSERT(_constraints.find(c._reference) != _constraints.end());
						pos.bottom = _constraints[c._reference]._pos.bottom+c._offset;
						break;
					case ATTACH_FORM:
						pos.bottom = formR.bottom+c._offset;
						break;
					case ATTACH_OPPOSITE_FORM:
						pos.bottom = formR.top+c._offset;
						break;
					case ATTACH_POSITION:
						pos.bottom = (c._pos*(formR.bottom-formR.top))/_nPos+c._offset;
						break;
					case ATTACH_SELF:
						pos.bottom = formR.top+c._offset;
						break;
					case ATTACH_NONE:
						break;
					default:
						ATLASSERT(false);
					}
				}
			if (doHLayout)
				//-----------------
				// horizontal center constraint
				//-----------------
				{
				SideConstraint &c = wgt->second._constraint[ATTACH_HCENTER];
				switch (c._type)
					{
					case ATTACH_OPPOSITE_WIDGET:
						ATLASSERT(_constraints.find(c._reference) != _constraints.end());
						ii = (pos.right-pos.left);
						pos.left = (_constraints[c._reference]._pos.left+_constraints[c._reference]._pos.right-(pos.right-pos.left))/2+c._offset;
						pos.right = pos.left + ii;
						break;
					case ATTACH_FORM:
						ii = (pos.right-pos.left);
						pos.left = (formR.left+formR.right-(pos.right-pos.left))/2+c._offset;
						pos.right = pos.left + ii;
						break;
						break;
					case ATTACH_NONE:
						break;
					default:
						ATLASSERT(false);
					}
				}
			if (doVLayout)
				//-----------------
				// vertical center constraint
				//-----------------
				{
				SideConstraint &c = wgt->second._constraint[ATTACH_VCENTER];
				switch (c._type)
					{
					case ATTACH_OPPOSITE_WIDGET:
						ATLASSERT(_constraints.find(c._reference) != _constraints.end());
						ii = (pos.bottom-pos.top);
						pos.top = (_constraints[c._reference]._pos.top+_constraints[c._reference]._pos.bottom-(pos.bottom-pos.top))/2+c._offset;
						pos.bottom = pos.top + ii;
						break;
					case ATTACH_FORM:
						ii = (pos.bottom-pos.top);
						pos.top = (formR.top+formR.bottom-(pos.bottom-pos.top))/2+c._offset;
						pos.bottom = pos.top + ii;
						break;
					case ATTACH_NONE:
						break;
					default:
						ATLASSERT(false);
					}
				}
			// now process the ATTACH_NONE constraints
			if (doHLayout)
				{
				if (wgt->second._constraint[ATTACH_LEFT]._type == ATTACH_NONE)
					pos.left = pos.right - (opos.right-opos.left);
				if (wgt->second._constraint[ATTACH_RIGHT]._type == ATTACH_NONE)
					pos.right = pos.left + (opos.right-opos.left);
				}
			if (doVLayout)
				{
				if (wgt->second._constraint[ATTACH_TOP]._type == ATTACH_NONE)
					pos.top = pos.bottom - (opos.bottom-opos.top);
				if (wgt->second._constraint[ATTACH_BOTTOM]._type == ATTACH_NONE)
					pos.bottom = pos.top + (opos.bottom-opos.top);
				}
			// made some changes ?
			change = change || (::memcmp(&opos, &pos, sizeof(pos)) != 0);
			}

		if (!change)
			return true;
		}
	ATLTRACE(_T("LayoutMgr<T>::DoLayout: no convergence after %d iterations !!!!!!!!\n"), i);
	return true;
}

// show/hide the grip handle
template <class T>
inline bool LayoutMgr<T>::ShowHandle(bool show)
{
	bool oshow = _showHandle;

	if (_handle == 0)
		{
		T* pT = static_cast<T*>(this);

		// Creation of the size grip (see MS KB Q102485)
		RECT	rc;
		int		l = ::GetSystemMetrics(SM_CXHSCROLL);
		int		h = ::GetSystemMetrics(SM_CYHSCROLL);

		::GetClientRect(*pT, &rc);
		rc.left = rc.right - l;
		rc.top = rc.bottom - h;

		_handle.Create(*pT, rc, _T("SizeGrip"), WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|SBS_SIZEBOX|SBS_SIZEGRIP, WS_EX_TRANSPARENT);
		
		AttachForm(_handle, ATTACH_RIGHT);
		AttachForm(_handle, ATTACH_BOTTOM);
		}
	_showHandle = show;
	_handle.ShowWindow(_showHandle ? SW_SHOW : SW_HIDE);

	return oshow;
}

// Common method called on WM_SIZE messages
template <class T>
inline LRESULT LayoutMgr<T>::OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
{
	bHandled = false;
	if ((GET_X_LPARAM(lParam) != 0) && (GET_Y_LPARAM(lParam) != 0))
		{
		if (ComputeLayout())
			{
			DoLayout();
			}
		}
	_prevClientSize.cx = GET_X_LPARAM(lParam);
	_prevClientSize.cy = GET_Y_LPARAM(lParam);
	return 0;
}

// Common method called on WM_GETMINMAXINFO messages
template <class T>
inline LRESULT LayoutMgr<T>::OnGetMinMaxInfo(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
{
	MINMAXINFO	*lpMMI = (MINMAXINFO *)lParam;
    lpMMI->ptMinTrackSize.x = _minWindowSize.cx;
    lpMMI->ptMinTrackSize.y = _minWindowSize.cy;
	if (_maxWindowSize.cx > 0)
		{
		lpMMI->ptMaxTrackSize.x = _maxWindowSize.cx;
		}
	if (_maxWindowSize.cy > 0)
		{
		lpMMI->ptMaxTrackSize.y = _maxWindowSize.cy;
		}
	return 0;
}

// Common method called on WMU_PS_ATTACH_PS messages
template <class T>
inline LRESULT LayoutMgr<T>::OnAttachPropertySheet(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	T* pT = static_cast<T*>(this);

	pT->AttachOppositeWidget((HWND)wParam, ATTACH_LEFT, (HWND)lParam);
	pT->AttachOppositeWidget((HWND)wParam, ATTACH_RIGHT, (HWND)lParam);
	pT->AttachOppositeWidget((HWND)wParam, ATTACH_TOP, (HWND)lParam);
	pT->AttachOppositeWidget((HWND)wParam, ATTACH_BOTTOM, (HWND)lParam);
	return 0;
}

template <class T>
inline void LayoutMgr<T>::DefineLayout()
{
}

// Returns the client area of the container
template <class T>
inline void LayoutMgr<T>::GetDialogRect(LPRECT r)
{
	T* pT = static_cast<T*>(this);
    pT->GetClientRect(r);
}

// This methods returns the offset for positioning the controls in the client rect
template <class T>
inline void LayoutMgr<T>::GetOffset(LPPOINT offset)
{
	offset->x = offset->y = 0;
}

// Excludes the control window rectangles from the DC clipping region
template <class T>
inline LRESULT LayoutMgr<T>::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	bHandled = false;
	T* pT = static_cast<T*>(this);
	if (!SysInfo::IsThemeSupported() && pT->_controlsToClip != 0)
		{
		HDC			dc = (HDC)wParam;
		const UINT	*c = _controlsToClip;
		RECT		rc;
		POINT		dcOffset;

		int mapmode = ::SetMapMode(dc, MM_TEXT);
		::GetViewportOrgEx(dc, &dcOffset);
		for(c = pT->_controlsToClip; *c > 0; c++)
			{
			HWND chwnd = pT->GetDlgItem(*c);
			if (chwnd != 0 && ::IsWindowVisible(chwnd))
				{
				::GetWindowRect(chwnd, &rc);
				pT->ScreenToClient(&rc);
				::OffsetRect(&rc, -dcOffset.x, -dcOffset.y);
				::ExcludeClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
				}
			}
		::SetMapMode(dc, mapmode);
		}
	return 0;
}

#ifdef __ATLWIN_H__
//-----------------------------------------------------------------------------
// CResizableDialogImplT
//-----------------------------------------------------------------------------
template <class T, class TBase, class DlgBase>
inline CResizableDialogImplT<T, TBase, DlgBase>::CResizableDialogImplT(bool useHandle)
{
	_showHandle = useHandle;
}

// Initalisation of the dialog
template <class T, class TBase, class DlgBase>
inline LRESULT CResizableDialogImplT<T, TBase, DlgBase>::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	T* pT = static_cast<T*>(this);
	if ((pT->GetStyle() & WS_CHILD) != 0)
		{
		// no min size
		_minWindowSize.cx = _minWindowSize.cy = 0;
		_minClientSize.cx = _minClientSize.cy = 0;
		}
	else
		{
		// make sure we have a resizable border
		pT->ModifyStyle(DS_MODALFRAME, WS_THICKFRAME);
		// add the handle
		if (_showHandle)
			{
			ShowHandle(true);
			}
		RECT r;
		bHandled = false;
		pT->GetClientRect(&r);
		_minClientSize.cx = (r.right-r.left);
		_minClientSize.cy = (r.bottom-r.top);
		_prevClientSize = _minClientSize;
		pT->GetWindowRect(&r);
		_minWindowSize.cx = (r.right-r.left);
		_minWindowSize.cy = (r.bottom-r.top);
		}
	pT->DefineLayout();
	bHandled = false;

	return 0;
}

//-----------------------------------------------------------------------------
// CResizableDialogImpl
//-----------------------------------------------------------------------------
template <class T, class TBase>
inline CResizableDialogImpl<T, TBase>::CResizableDialogImpl(bool useHandle)
: inherited(useHandle) {}

#ifndef _ATL_NO_HOSTING
//-----------------------------------------------------------------------------
// CAxResizableDialogImpl
//-----------------------------------------------------------------------------
template <class T, class TBase>
inline CAxResizableDialogImpl<T, TBase>::CAxResizableDialogImpl(bool useHandle)
: inherited(useHandle) {}

#endif // ifndef _ATL_NO_HOSTING

#endif // __ATLWIN_H__

#if defined(__ATLSCRL_H__) && defined(__ATLWIN_H__)
//-----------------------------------------------------------------------------
// CResizableFormViewImpl
//-----------------------------------------------------------------------------
template <class T, class TBase, class DlgBase>
inline void CResizableFormViewImplT<T, TBase, DlgBase>::DoPaint(CDCHandle /*dc*/)
{
	// Just avoid the assertion
}

// Initalisation of the dialog
template <class T, class TBase, class DlgBase>
inline LRESULT CResizableFormViewImplT<T, TBase, DlgBase>::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	T* pT = static_cast<T*>(this);
    RECT r;
	bHandled = false;
    pT->GetClientRect(&r);
    _minClientSize.cx = (r.right-r.left);
    _minClientSize.cy = (r.bottom-r.top);
	_prevClientSize = _minClientSize;
    pT->GetWindowRect(&r);
    _minWindowSize.cx = (r.right-r.left);
    _minWindowSize.cy = (r.bottom-r.top);
	pT->DefineLayout();
	pT->SetScrollSize(_minClientSize);
	bHandled = false;

	return 0;
}

// This methods returns the offset for positioning the controls in the client rect
template <class T, class TBase, class DlgBase>
inline void CResizableFormViewImplT<T, TBase, DlgBase>::GetOffset(LPPOINT offset)
{
	T* pT = static_cast<T*>(this);
	*offset = pT->m_ptOffset;
}

// Returns the client area of the container
template <class T, class TBase, class DlgBase>
inline void CResizableFormViewImplT<T, TBase, DlgBase>::GetDialogRect(LPRECT r)
{
	T* pT = static_cast<T*>(this);
	// NB : GetClientRect returns always a rectangle with left an top are set to 0
    pT->GetClientRect(r);	
	if (r->right < pT->m_sizeAll.cx)
		{
		r->right = pT->m_sizeAll.cx;
		}
	if (r->bottom < pT->m_sizeAll.cy)
		{
		r->bottom = pT->m_sizeAll.cy;
		}
}

#endif // defined(__ATLSCRL_H__) && defined(__ATLWIN_H__)

#if defined(__ATLDLGS_H__) && defined(__ATLWIN_H__)
//-----------------------------------------------------------------------------
// CResizablePropertySheetImpl
//-----------------------------------------------------------------------------
template <bool childWnd, class T, class TBase>
inline CResizablePropertySheetImpl<childWnd, T, TBase>::CResizablePropertySheetImpl(WTL::_U_STRINGorID title , UINT uStartPage, HWND hWndParent, bool useHandle)
: CPropertySheetImpl<T, TBase>(title, uStartPage, hWndParent)
{
	_showHandle = useHandle;
}

// Creation of the property sheet
template <bool childWnd, class T, class TBase>
inline void CResizablePropertySheetImpl<childWnd, T, TBase>::OnInitDialog()
{
}

// Creation of the property sheet
template <bool childWnd, class T, class TBase>
inline HWND CResizablePropertySheetImpl<childWnd, T, TBase>::Create(HWND hWndParent)
{
	if (CPropertySheetImpl< T, TBase >::Create(hWndParent) == 0)
		{
		return 0;
		}

	return m_hWnd;
}

// Property sheet callback function
template <bool childWnd, class T, class TBase>
inline int CALLBACK CResizablePropertySheetImpl<childWnd, T, TBase>::PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM lParam)
{
	if(uMsg == PSCB_PRECREATE)
		{
		// Set the right window styles
		DLGTEMPLATE* dlg = (DLGTEMPLATE*) lParam;
		if (childWnd)
			{
			dlg->style &= ~(WS_POPUP|WS_CAPTION|WS_SYSMENU|DS_MODALFRAME);
			dlg->style |= (WS_CHILD|WS_VISIBLE|WS_TABSTOP|DS_CONTROL|DS_3DLOOK);
			dlg->dwExtendedStyle |= WS_EX_CONTROLPARENT;
			}
		else
			{
			dlg->style &= ~(WS_CHILD|DS_MODALFRAME|WS_CLIPCHILDREN);
			dlg->style |= (WS_THICKFRAME);
			}
		}
	else if (uMsg == PSCB_INITIALIZED)
		{
		::PostMessage(hWnd, WMU_PS_INIT, 0, 0);
		}
	return CPropertySheetImpl< T, TBase >::PropSheetCallback(hWnd, uMsg, lParam);
}

// Attach a page
template <bool childWnd, class T, class TBase>
inline LRESULT CResizablePropertySheetImpl<childWnd, T, TBase>::OnAttachPage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	HWND page = (HWND)wParam;
	ATLASSERT(::IsWindow(page));
	// set the constraints for the page
	AttachForm(page, ATTACH_LEFT);
	AttachForm(page, ATTACH_RIGHT);
	AttachForm(page, ATTACH_TOP);
	AttachForm(page, ATTACH_BOTTOM);
	return 0;
}

// Init the property sheet
template <bool childWnd, class T, class TBase>
inline LRESULT CResizablePropertySheetImpl<childWnd, T, TBase>::OnInit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    RECT r;
	T* pT = static_cast<T*>(this);

	bHandled = false;
	if (!childWnd)
		{
		pT->GetClientRect(&r);
		_minClientSize.cx = (r.right-r.left);
		_minClientSize.cy = (r.bottom-r.top);
		_prevClientSize = _minClientSize;
		pT->GetWindowRect(&r);
		_minWindowSize.cx = (r.right-r.left);
		_minWindowSize.cy = (r.bottom-r.top);
		}
	
	pT->SpecificInit();

	// allow specific initialisation by the descendants
	pT->OnInitDialog();
	return 0;
}

// Excludes the control window rectangles and the tab control from the DC clipping region
template <bool childWnd, class T, class TBase>
inline LRESULT CResizablePropertySheetImpl<childWnd, T, TBase>::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	bHandled = false;
	if (!SysInfo::IsThemeSupported())
		{
		T			*pT = static_cast<T*>(this);
		HWND		tabwnd;
		HDC			dc = (HDC)wParam;
		RECT		rc;
		POINT		dcOffset;
		int			mapmode = ::SetMapMode(dc, MM_TEXT);

		::GetViewportOrgEx(dc, &dcOffset);
		tabwnd = pT->GetTabControl();
		ATLASSERT(tabwnd != 0);
		::GetWindowRect(tabwnd, &rc);
		pT->ScreenToClient(&rc);
		::OffsetRect(&rc, -dcOffset.x, -dcOffset.y);
		::ExcludeClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
		::SetMapMode(dc, mapmode);
		}

	return 0;
}

//=============================================================================
// CChildResizablePropertySheetImpl
//=============================================================================
template <class T, class TBase>
CChildResizablePropertySheetImpl<T, TBase>::CChildResizablePropertySheetImpl(UINT placeHolderID, WTL::_U_STRINGorID title, UINT uStartPage, HWND hWndParent, bool useHandle)
: CResizablePropertySheetImpl<true, T, TBase>(title, uStartPage, hWndParent, useHandle), m_ID_Place_Holder(placeHolderID)
{
}

// Specific intialisation depending on whether or not the property sheet is a child window
template <class T, class TBase>
inline void CChildResizablePropertySheetImpl<T, TBase>::SpecificInit()
{
	const UINT stdIDs[] = {
		ID_WIZNEXT, ID_WIZFINISH, ID_WIZBACK, ID_WIZNEXT, ID_WIZFINISH+1,
		IDOK, IDCANCEL,	ID_APPLY_NOW, IDHELP,
		-1
		};

	const UINT	*id;
	T			*pT = static_cast<T*>(this);
	CWindow parent = pT->GetParent();
	CWindow tab = pT->GetTabControl();
	CWindow actPg = pT->GetActivePage();
	ATLASSERT(parent != 0);
	ATLASSERT(tab != 0);
	ATLASSERT(actPg != 0);
	// Allows tab navigation in the parent
	parent.ModifyStyleEx(0, WS_EX_CONTROLPARENT);
	// Destroy the standard buttons
	for(id = stdIDs; *id != -1; id++)
		{
		CWindow btn = pT->GetDlgItem(*id);
		if (btn != 0) { btn.DestroyWindow(); }
		}
	// Get relative position of the tab control and of the active page
	RECT rTC, rAP;
	tab.GetWindowRect(&rTC);
	actPg.GetWindowRect(&rAP);
	// Move and resize the window and the tab control
	CWindow ph = parent.GetDlgItem(m_ID_Place_Holder);
	ATLASSERT(ph.IsWindow());
	RECT cr;
	ph.GetWindowRect(&cr);
	parent.ScreenToClient(&cr);
	pT->SetWindowPos(0, &cr, SWP_NOZORDER|SWP_NOACTIVATE);
	// resize the tab control
	tab.SetWindowPos(0, 0, 0, cr.right-cr.left, cr.bottom-cr.top, SWP_NOZORDER|SWP_NOACTIVATE);
	// we can now set the constraints for the tabControl
	pT->AttachForm(tab, ATTACH_LEFT);
	pT->AttachForm(tab, ATTACH_RIGHT);
	pT->AttachForm(tab, ATTACH_TOP);
	pT->AttachForm(tab, ATTACH_BOTTOM);
	// and finally resize the active page
	actPg.SetWindowPos(0, 
		rAP.left-rTC.left,
		rAP.top-rTC.top,
		cr.right-cr.left-(rTC.right-rAP.right)-(rAP.left-rTC.left),
		cr.bottom-cr.top-(rTC.bottom-rAP.bottom)-(rAP.top-rTC.top),
		SWP_NOZORDER|SWP_NOACTIVATE);
	// we must reset the constraints
	pT->AttachForm(actPg, ATTACH_LEFT);
	pT->AttachForm(actPg, ATTACH_RIGHT);
	pT->AttachForm(actPg, ATTACH_TOP);
	pT->AttachForm(actPg, ATTACH_BOTTOM);
	// attach the PS to the prent
	parent.SendMessage(WMU_PS_ATTACH_PS, (WPARAM)pT->m_hWnd, (LPARAM)ph.m_hWnd);

}

//=============================================================================
// CPopupResizablePropertySheetImpl
//=============================================================================
template <class T, class TBase>
CPopupResizablePropertySheetImpl<T, TBase>::CPopupResizablePropertySheetImpl(WTL::_U_STRINGorID title, UINT uStartPage, HWND hWndParent, bool useHandle)
: CResizablePropertySheetImpl<false, T, TBase>(title, uStartPage, hWndParent, useHandle)
{
}

// Specific intialisation depending on whether or not the property sheet is a child window
template <class T, class TBase>
inline void CPopupResizablePropertySheetImpl<T, TBase>::SpecificInit()
{
	const UINT stdIDs[] = {
		ID_WIZNEXT, ID_WIZFINISH, ID_WIZBACK, ID_WIZNEXT, ID_WIZFINISH+1,
		IDOK, IDCANCEL,	ID_APPLY_NOW, IDHELP,
		-1
		};
	T			*pT = static_cast<T *>(this);
	CWindow		tab = pT->GetTabControl();
	// we can now set the constraints for the tabControl
	pT->AttachForm(tab, ATTACH_LEFT);
	pT->AttachForm(tab, ATTACH_RIGHT);
	pT->AttachForm(tab, ATTACH_TOP);
	pT->AttachForm(tab, ATTACH_BOTTOM);
	// set the constraints for the standard buttons
	const UINT	*id;
	HWND		lmW = 0;
	int			xmax = 0;
	RECT		r;
	for (id = stdIDs; *id != -1; id++)
		{
		HWND hwnd = pT->GetDlgItem(*id);
		// some buttons as the help button are created even if they are not used
		// to know if they are used, we look at he WS_VISIBLE style.
		if ((hwnd != 0) && ((::GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE) != 0))
			{
			pT->AttachForm(hwnd, ATTACH_BOTTOM);
			pT->AttachForm(hwnd, ATTACH_RIGHT);
			::GetWindowRect(hwnd, &r);
			if (r.right >= xmax)
				{
				xmax = r.right;
				lmW = hwnd;
				}
			}
		}
	if (_showHandle)
		{
		// create the handle
		pT->ShowHandle(true);
		// and place the left most button over the handle
		if (lmW)
			{
			::SetWindowPos(pT->_handle, lmW, 0, 0, 0, 0, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE);
			}
		}
}

//-----------------------------------------------------------------------------
// CResizablePropertyPageImplT
//-----------------------------------------------------------------------------
template <class T, class TBase, class dialogBase>
inline CResizablePropertyPageImplT<T, TBase, dialogBase>::CResizablePropertyPageImplT(WTL::_U_STRINGorID title)
: dialogBase(title), _init(false)
{
}

// Initalisation of the dialog
template <class T, class TBase, class dialogBase>
inline LRESULT CResizablePropertyPageImplT<T, TBase, dialogBase>::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	bHandled = false;
	T* pT = static_cast<T*>(this);
	pT->DefineLayout();
	bHandled = false;
	return 0;
}

// Page Activation
template <class T, class TBase, class dialogBase>
inline BOOL CResizablePropertyPageImplT<T, TBase, dialogBase>::OnSetActive()
{
	if (!_init)
		{
		::SendMessage(GetParent(), WMU_PS_ATTACH_PAGE, (WPARAM)m_hWnd, 0);
		_init = true;
		}
	return true;
}

//-----------------------------------------------------------------------------
// CResizablePropertyPageImpl
//-----------------------------------------------------------------------------
template <class T, class TBase>
inline CResizablePropertyPageImpl<T, TBase>::CResizablePropertyPageImpl(WTL::_U_STRINGorID title)
: inherited(title)
{
}

#ifndef _ATL_NO_HOSTING
//-----------------------------------------------------------------------------
// CAxResizablePropertyPageImpl
//-----------------------------------------------------------------------------
template <class T, class TBase>
inline CAxResizablePropertyPageImpl<T, TBase>::CAxResizablePropertyPageImpl(WTL::_U_STRINGorID title)
: inherited(title)
{
}

#if (_ATL_VER >= 0x0700)
// workaround an ATL7 bug, the initialisation code for ActiveX control is in CAxDialogImpl or CAxResizablePropertyPageImpl derives from CAxDialogImpl
	// so we must copy the code of CAxDialogImpl into this class and call it at the initalisation of the dialog

// simple copy of CAxDialogImpl::CreateActiveXControls
template <class T, class TBase>
inline HRESULT CAxResizablePropertyPageImpl<T, TBase>::CreateActiveXControls(UINT nID)
{
		// Load dialog template and InitData
		HRSRC hDlgInit = ::FindResource(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCE(nID), (LPTSTR)_ATL_RT_DLGINIT);
		BYTE* pInitData = NULL;
		HGLOBAL hData = NULL;
		HRESULT hr = S_OK;
		if (hDlgInit != NULL)
		{
			hData = ::LoadResource(_AtlBaseModule.GetResourceInstance(), hDlgInit);
			if (hData != NULL)
				pInitData = (BYTE*) ::LockResource(hData);
		}

		HRSRC hDlg = ::FindResource(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCE(nID), (LPTSTR)RT_DIALOG);
		if (hDlg != NULL)
		{
			HGLOBAL hResource = ::LoadResource(_AtlBaseModule.GetResourceInstance(), hDlg);
			DLGTEMPLATE* pDlg = NULL;
			if (hResource != NULL)
			{
				pDlg = (DLGTEMPLATE*) ::LockResource(hResource);
				if (pDlg != NULL)
				{
					// Get first control on the template
					BOOL bDialogEx = _DialogSplitHelper::IsDialogEx(pDlg);
					WORD nItems = _DialogSplitHelper::DlgTemplateItemCount(pDlg);

					// Get first control on the dialog
					DLGITEMTEMPLATE* pItem = _DialogSplitHelper::FindFirstDlgItem(pDlg);
					HWND hWndPrev = GetWindow(GW_CHILD);

					// Create all ActiveX cotnrols in the dialog template and place them in the correct tab order (z-order)
					for (WORD nItem = 0; nItem < nItems; nItem++)
					{
						DWORD wID = bDialogEx ? ((_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->id : pItem->id;
						if (_DialogSplitHelper::IsActiveXControl(pItem, bDialogEx))
						{
							BYTE* pData = NULL;
							DWORD dwLen = _DialogSplitHelper::FindCreateData(wID, pInitData, &pData);
							CComPtr<IStream> spStream;
							if (dwLen != 0)
							{
								HGLOBAL h = GlobalAlloc(GHND, dwLen);
								if (h != NULL)
								{
									BYTE* pBytes = (BYTE*) GlobalLock(h);
									BYTE* pSource = pData; 
									memcpy(pBytes, pSource, dwLen);
									GlobalUnlock(h);
									CreateStreamOnHGlobal(h, TRUE, &spStream);
								}
								else
								{
									hr = E_OUTOFMEMORY;
									break;
								}
							}

							CComBSTR bstrLicKey;
							hr = _DialogSplitHelper::ParseInitData(spStream, &bstrLicKey.m_str);
							if (SUCCEEDED(hr))
							{
								CAxWindow2 wnd;
								// Get control caption.
								LPWSTR pszClassName = 
									bDialogEx ? 
										(LPWSTR)(((_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem) + 1) :
										(LPWSTR)(pItem + 1);
								// Get control rect.
								RECT rect;
								rect.left = 
									bDialogEx ? 
										((_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->x : 
										pItem->x;
								rect.top = 
									bDialogEx ? 
										((_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->y : 
										pItem->y;
								rect.right = rect.left + 
									(bDialogEx ? 
										((_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->cx : 
										pItem->cx);
								rect.bottom = rect.top + 
									(bDialogEx ? 
										((_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->cy : 
										pItem->cy);

								// Convert from dialog units to screen units
								MapDialogRect(&rect);

								// Create AxWindow with a NULL caption.
								wnd.Create(m_hWnd, 
									&rect, 
									NULL, 
									(bDialogEx ? 
										((_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->style : 
										pItem->style) | WS_TABSTOP, 
									bDialogEx ? 
										((_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->exStyle : 
										0,
									bDialogEx ? 
										((_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->id : 
										pItem->id,
									NULL);

								if (wnd != NULL)
								{
									// Set the Help ID
									if (bDialogEx && ((_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->helpID != 0)
										wnd.SetWindowContextHelpId(((_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->helpID);
									// Try to create the ActiveX control.
									hr = wnd.CreateControlLic(pszClassName, spStream, NULL, bstrLicKey);
									if (FAILED(hr))
										break;
									// Set the correct tab position.
									if (nItem == 0)
										hWndPrev = HWND_TOP;
									wnd.SetWindowPos(hWndPrev, 0,0,0,0,SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
									hWndPrev = wnd;
								}
								else
								{
									hr = AtlHresultFromLastError();
								}
							}
						}
						else
						{
							if (nItem != 0)
								hWndPrev = ::GetWindow(hWndPrev, GW_HWNDNEXT);
						}
						pItem = _DialogSplitHelper::FindNextDlgItem(pItem, bDialogEx);
					}
				}
				else
					hr = AtlHresultFromLastError();
			}
			else
				hr = AtlHresultFromLastError();
		}
		return hr;
}

template <class T, class TBase>
inline LRESULT CAxResizablePropertyPageImpl<T, TBase>::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
	HRESULT hr;
	T		*pT = static_cast<T*>(this);

	// missing initialisation
	if (FAILED(hr = pT->CreateActiveXControls(pT->IDD)))
		{
		pT->DestroyWindow();
		SetLastError(hr & 0x0000FFFF);
		return FALSE;
		}
	bHandled = FALSE;
	return 1;
}

#endif // (_ATL_VER >= 0x0700)

#endif // ifndef _ATL_NO_HOSTING

#endif // defined(__ATLDLGS_H__) && defined(__ATLWIN_H__)

#endif // !defined(__layoutmgr_h__)

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions