/*------------------------------------------------------------------------------*
 * File Name: LayoutHelper.h													*
 * Creation: Kenny 06/26/2009													*
 * Purpose: OriginC Source file													*
 * Copyright (c) OriginLab Corp. 2009											*
 * All Rights Reserved															*
 *																				*
 * Modification Log:															*
 *	Kenny 09/29/2009 IMPROVE_FIND_REPLACE_DLG_RESIZING_PROCESS					*
 *	Kenny 10/10/2009 QA81-14418 FIND_REPLACE_ALLOW_FLEXIBLE_SKIP_BLANK_TEXT		*
 *------------------------------------------------------------------------------*/

#ifndef __LAYOUTHELPER_H__
#define __LAYOUTHELPER_H__

/*----------------------------------------------------------------------------*/
/* Include files
/*----------------------------------------------------------------------------*/
#include <Origin.h>
#include <Array.h>

/*----------------------------------------------------------------------------*/
/* Macros/Enums/Consts
/*----------------------------------------------------------------------------*/
#define GetBits(pos)			( 1 << (pos) )
#define SetBit(bits, pos)		( (bits) |= GetBits(pos) )
#define ClrBit(bits, pos)		( (bits) &= ~GetBits(pos) )
//#define ToggleBit(bits, pos)	( (bits) ^= GetBits(pos) )
#define TestBit(bits, pos)		( (bits) & GetBits(pos) )

/*----------------------------------------------------------------------------*/
/* Utils function
/*----------------------------------------------------------------------------*/

enum
{
	MC_MOVE_X					= 0x01,
	MC_MOVE_Y					= 0x02,
	MC_SIZE_WIDTH				= 0x04,
	MC_SIZE_HEIGHT				= 0x08,

	MC_MOVE_X_Y					= MC_MOVE_X | MC_MOVE_Y,
	MC_SIZE_BOTH				= MC_SIZE_WIDTH | MC_SIZE_HEIGHT,
	MC_MOVE_AND_SIZE			= MC_MOVE_X_Y | MC_SIZE_BOTH,
};

void update_rect(RECT& rect, int nX, int nY, int nWidth = -1, int nHeight = -1, DWORD dwFlags = MC_MOVE_AND_SIZE )
{
	const int nHorizOffset = nX - rect.left;
	const int nVertiOffset = nY - rect.top;
	const bool bMoveX = dwFlags & MC_MOVE_X;
	const bool bMoveY = dwFlags & MC_MOVE_Y;
	const bool bSizeW = dwFlags & MC_SIZE_WIDTH;
	const bool bSizeH = dwFlags & MC_SIZE_HEIGHT;

	if (bMoveX)
		rect.left = nX;
	if (bSizeW)
	{
		if (nWidth >= 0)
			rect.right = rect.left + nWidth;
		else if (bMoveX)
			rect.right += nHorizOffset;
	}

	if (bMoveY)
		rect.top = nY;
	if (bSizeH)
	{
		if (nHeight >= 0)
			rect.bottom = rect.top + nHeight;
		else if (bMoveY)
			rect.bottom += nVertiOffset;
	}
}

void move_control(const Window& wndParent, Control& ctrl, int nX, int nY, int nWidth = -1, int nHeight = -1, DWORD dwFlags = MC_MOVE_AND_SIZE, BOOL bRepaint = FALSE)
{
	if ( ctrl )
	{
		RECT rect;
		ctrl.GetWindowRect( &rect );
		wndParent.ScreenToClient( &rect );
		update_rect(rect, nX, nY, nWidth, nHeight, dwFlags);
		ctrl.MoveWindow(&rect, bRepaint);
	}
}

void move_control_by_id(const Window& wndParent, UINT nCtrlID, int nX, int nY, int nWidth = -1, int nHeight = -1, DWORD dwFlags = MC_MOVE_AND_SIZE, BOOL bRepaint = FALSE)
{
	Control ctrl = wndParent.GetDlgItem(nCtrlID);
	move_control(wndParent, ctrl, nX, nY, nWidth, nHeight, dwFlags, bRepaint);
}

/*----------------------------------------------------------------------------*/
/* CLayoutTool
/*----------------------------------------------------------------------------*/

enum	// Flags for alignment
{
	ALIGN_BEGIN					= 0,

	ALIGN_LEFT					= ALIGN_BEGIN,
	ALIGN_RIGHT					= 1,
	ALIGN_TOP					= 2,
	ALIGN_BOTTOM				= 3,
	ALIGN_HORIZ_CENTER			= 4,
	ALIGN_VERTI_CENTER			= 5,

	ALIGN_FLAG_COUNT,
};

enum	// Flags for making same size
{
	MSS_SAME_WIDTH				= 0x01,
	MSS_SAME_HEIGHT				= 0x02,

	MSS_SAME_SIZE				= MSS_SAME_WIDTH | MSS_SAME_HEIGHT,
};

class CLayoutTool
{
public:
	CLayoutTool(const Window& wndParent, bool bSkipHiddenCtrl = true)
	{
		m_pWndParent			= &wndParent;
		m_bSkipHiddenCtrl		= bSkipHiddenCtrl;

		Reset();
	}
	~CLayoutTool()
	{
		;
	}
public:
	bool	m_bSkipHiddenCtrl;

	int		m_nAlignOption;
	int		m_nAlignReferencePos;
	POINT	m_ptBegin;

	int		m_nMakeSameSizeOption;
	SIZE	m_szSameSize;

	SIZE	m_szGap;
	// Available only when m_szGap.cx or m_szGap.cy is greather than -1
	// Set this flag to true to place each control at the right/bottom side of previous control,
	// else will place each control at the left/upper side of previous control 
	bool	m_FollowPreviousCtrlRightOrBottom;
public:
	void PerformLayout(const vector<UINT>& vnCtrlIDs, BOOL bRepaint = FALSE)
	{
		const int nCtrlSize = vnCtrlIDs.GetSize();
		if (   NULL == m_pWndParent 
			|| nCtrlSize <= 0
			|| (-1 == m_nAlignOption && -1 == m_nMakeSameSizeOption && -1 == m_szGap.cx && -1 == m_szGap.cy) )
		{
			ASSERT(FALSE);
			return;
		}

		Control ctrlFirst;
		int nCtrlIndex = GetNextValidCtrl(ctrlFirst, vnCtrlIDs);
		if (nCtrlIndex < 0)
		{
			return;
		}

		RECT rtPrevious;
		ctrlFirst.GetWindowRect( &rtPrevious );
		m_pWndParent->ScreenToClient( &rtPrevious );

		DWORD dwFlags = MC_SIZE_BOTH;
		if ( m_ptBegin.x >= 0 )
			dwFlags |= MC_MOVE_X;
		if ( m_ptBegin.y >= 0 )
			dwFlags |= MC_MOVE_Y;
		update_rect(rtPrevious, m_ptBegin.x, m_ptBegin.y, -1, -1, dwFlags);

		applyAlignOptionFromRect(rtPrevious);
		applyMakeSameSizeOptionFromRect(rtPrevious);

		if ( m_bAlign )
		{
			bool bVertAlign = (ALIGN_LEFT == m_nAlignOption || ALIGN_RIGHT == m_nAlignOption || ALIGN_HORIZ_CENTER == m_nAlignOption);
			if (bVertAlign)
			{
				if (m_szGap.cx >= 0)
					return;
			}
			else if (m_szGap.cy >= 0)
			{
				return;
			}
		}

		MakeControlRectSameSize(rtPrevious);
		AlignControlRect(rtPrevious);
		ctrlFirst.MoveWindow(&rtPrevious, bRepaint);

		Control ctrl;
		RECT rect;
		while ( (nCtrlIndex = GetNextValidCtrl(ctrl, vnCtrlIDs, nCtrlIndex)) >= 0 )
		{
			ctrl.GetWindowRect( &rect );
			m_pWndParent->ScreenToClient( &rect );

			MakeControlRectSameSize(rect);
			AlignControlRect(rect);
			SpaceControlRectEvenly(rect, rtPrevious);

			ctrl.MoveWindow(&rect, bRepaint);

			rtPrevious = rect;
		}
	}
	void Reset()
	{
		m_nAlignOption			= -1;
		m_nMakeSameSizeOption	= 0;

		m_nAlignReferencePos	= -1;
		m_ptBegin.x				= -1;
		m_ptBegin.y				= -1;
		m_FollowPreviousCtrlRightOrBottom = true;
		m_szGap.cx				= -1;
		m_szGap.cy				= -1;
		m_szSameSize.cx			= -1;
		m_szSameSize.cy			= -1;
	}
protected:
	int GetNextValidCtrl(Control& ctrl, const vector<UINT>& vnCtrlIDs, int nPreviousIndex = -1)
	{
		const int nCtrlSize = vnCtrlIDs.GetSize();
		for (int ii = nPreviousIndex+1; ii < nCtrlSize; ++ii)
		{
			ctrl = m_pWndParent->GetDlgItem( vnCtrlIDs[ii] );
			if ( !ctrl || (m_bSkipHiddenCtrl && !ctrl.Visible) )
			{
				continue;
			}
			return ii;
		}
		return -1;
	}
	void AlignControlRect(RECT& rect)
	{
		if (m_bAlign)
		{
			switch (m_nAlignOption)
			{
			case ALIGN_LEFT:
				update_rect(rect, m_nAlignReferencePos, -1, -1, -1, MC_MOVE_X|MC_SIZE_WIDTH );
				break;
			case ALIGN_RIGHT:
				rect.left += m_nAlignReferencePos - rect.right;
				rect.right = m_nAlignReferencePos;
				break;
			case ALIGN_HORIZ_CENTER:
				update_rect(rect, m_nAlignReferencePos - RECT_WIDTH(rect) / 2, -1, -1, -1, MC_MOVE_X|MC_SIZE_WIDTH );
				break;
			case ALIGN_TOP:
				update_rect(rect, -1, m_nAlignReferencePos, -1, -1, MC_MOVE_Y|MC_SIZE_HEIGHT );
				break;
			case ALIGN_BOTTOM:
				rect.top += m_nAlignReferencePos - rect.bottom;
				rect.bottom = m_nAlignReferencePos;
				break;
			case ALIGN_VERTI_CENTER:
				update_rect(rect, -1, m_nAlignReferencePos - RECT_HEIGHT(rect) / 2, -1, -1, MC_MOVE_Y|MC_SIZE_HEIGHT );
				break;
			}
		}
	}
	void MakeControlRectSameSize(RECT& rect)
	{
		if (m_bMakeSameSize)
		{
			if (m_szSameSize.cx > 0)
			{
				rect.right = rect.left + m_szSameSize.cx;
			}
			if (m_szSameSize.cy > 0)
			{
				rect.bottom = rect.top + m_szSameSize.cy;
			}
		}
	}
	void SpaceControlRectEvenly(RECT& rect, const RECT& rtPrevious)
	{
		int nX = rect.left;
		int nY = rect.top;
		if (m_szGap.cx > -1)
		{
			if ( m_FollowPreviousCtrlRightOrBottom )
				nX = rtPrevious.right + m_szGap.cx;
			else
				nX = rtPrevious.left - m_szGap.cx - RECT_WIDTH(rect);
		}
		if (m_szGap.cy > -1)
		{
			if ( m_FollowPreviousCtrlRightOrBottom )
				nY = rtPrevious.bottom + m_szGap.cy;
			else
				nY = rtPrevious.top - m_szGap.cy - RECT_HEIGHT(rect);
		}
		update_rect(rect, nX, nY);
	}
private:
	void applyAlignOptionFromRect(const RECT& rtReference)
	{
		if (m_nAlignOption >= ALIGN_BEGIN && m_nAlignOption < ALIGN_FLAG_COUNT)
		{
			if (m_nAlignReferencePos < 0)
			{
				switch (m_nAlignOption)
				{
				case ALIGN_LEFT:
					m_nAlignReferencePos = rtReference.left;
					break;
				case ALIGN_RIGHT:
					m_nAlignReferencePos = rtReference.right;
					break;
				case ALIGN_HORIZ_CENTER:
					m_nAlignReferencePos = rtReference.left + RECT_WIDTH(rtReference) / 2;
					break;
				case ALIGN_TOP:
					m_nAlignReferencePos = rtReference.top;
					break;
				case ALIGN_BOTTOM:
					m_nAlignReferencePos = rtReference.bottom;
					break;
				case ALIGN_VERTI_CENTER:
					m_nAlignReferencePos = rtReference.top + RECT_HEIGHT(rtReference) / 2;
					break;
				}
			}
			m_bAlign = true;
		}
		else
		{
			m_bAlign = false;
		}
	}
	void applyMakeSameSizeOptionFromRect(const RECT& rtReference)
	{
		bool bMakeSameWidth = m_nMakeSameSizeOption & MSS_SAME_WIDTH;
		bool bMakeSameHeight = m_nMakeSameSizeOption & MSS_SAME_HEIGHT;
		if ( bMakeSameWidth || bMakeSameHeight )
		{
			/// Kenny 10/10/2009 QA81-14418 FIND_REPLACE_ALLOW_FLEXIBLE_SKIP_BLANK_TEXT
// 			if (m_szSameSize.cx <= 0)
// 			{
// 				m_szSameSize.cx = RECT_WIDTH(rtReference);
// 			}
// 			if (m_szSameSize.cy <= 0)
// 			{
// 				m_szSameSize.cy = RECT_HEIGHT(rtReference);
// 			}
			if (m_szSameSize.cx <= 0 && bMakeSameWidth)
			{
				m_szSameSize.cx = RECT_WIDTH(rtReference);
			}
			if (m_szSameSize.cy <= 0 && bMakeSameHeight)
			{
				m_szSameSize.cy = RECT_HEIGHT(rtReference);
			}
			/// End QA81-14418 FIND_REPLACE_ALLOW_FLEXIBLE_SKIP_BLANK_TEXT
			m_bMakeSameSize = true;
		}
		else
		{
			m_bMakeSameSize = false;
		}
	}
private:
	Window* m_pWndParent;

	bool	m_bAlign;
	bool	m_bMakeSameSize;
};

/*----------------------------------------------------------------------------*/
/* Class CLayoutOption
/*----------------------------------------------------------------------------*/

enum
{
	LOT_BEGIN			= 0,

	// The offset to the sides of the parent/container window.
	// If the related value is set to -1, that means we should keep the original offset.
	LOT_LEFT_OFFSET		= LOT_BEGIN,
	LOT_TOP_OFFSET		= 1,
	LOT_RIGHT_OFFSET	= 2,
	LOT_BOTTOM_OFFSET	= 3,

// 	LOT_MIN_WIDTH		= 4,
// 	LOT_MAX_WIDTH		= 5,
// 	LOT_MIN_HEIGHT		= 6,
// 	LOT_MAX_HEIGHT		= 7,
//	LOT_ASPECT_RATIO	= 8,

	LOT_OPTION_COUNT,
};

class CLayoutOption
{
public:
	CLayoutOption()
	{
		m_dwOptionBits = 0;
	}
	~CLayoutOption()
	{
		;
	}
public:
	bool	SetOption(int nOption, int nValue)
	{
		if ( IsValidOption(nOption) )
		{
			SetBit(m_dwOptionBits, nOption);
			m_arrOptionValues[nOption] = nValue;
			return true;
		}
		return false;
	}
	bool	RemoveOption(int nOption)
	{
		if ( IsValidOption(nOption) )
		{
			ClrBit(m_dwOptionBits, nOption);
			return true;
		}
		return false;
	}
	bool	HasOption(int nOption)
	{
		if ( IsValidOption(nOption) )
		{
			return TestBit(m_dwOptionBits, nOption);
		}
		return false;
	}
	bool	GetOption(int nOption, int& nValue)
	{
		if ( HasOption(nOption) )
		{
			nValue = m_arrOptionValues[nOption];
			return true;
		}
		return false;
	}
	void	Reset()
	{
		m_dwOptionBits = 0;
	}
protected:
	bool	IsValidOption(int nOption)
	{
		return (LOT_BEGIN <= nOption && nOption < LOT_OPTION_COUNT);
	}
private:
	DWORD	m_dwOptionBits;
	int		m_arrOptionValues[LOT_OPTION_COUNT];
};

/*----------------------------------------------------------------------------*/
/* CLayoutInfo
/*----------------------------------------------------------------------------*/

class CLayoutInfo
{
public:
	CLayoutInfo(const CLayoutInfo& infoCopy)
	{
		*this = infoCopy;
	}
	CLayoutInfo(int nCtrlID, const CLayoutOption& lOption = NULL)
	{
		m_nCtrlID = nCtrlID;
		if (lOption)
		{
			m_layoutOption = lOption;
		}
	}
public:
	int		GetCtrlID() { ASSERT(m_nCtrlID > 0); return m_nCtrlID; }
	Control	GetControl(const Window& wndParent) { return wndParent.GetDlgItem( GetCtrlID() ) }
	void	UpdateRect(const Window& wndParent)
	{
		Control ctrl = GetControl(wndParent);
		ctrl.GetWindowRect( &m_rect );
		wndParent.ScreenToClient( &m_rect );
	}
public:
	RECT			m_rect;
	CLayoutOption	m_layoutOption;
private:
	int				m_nCtrlID;
};

/*----------------------------------------------------------------------------*/
/* CLayoutHelper
/*----------------------------------------------------------------------------*/

class CLayoutHelper
{
public:
	CLayoutHelper()
	{
		m_pWndParent = NULL;
		m_arrLayoutInfos.SetAsOwner(TRUE);
		m_nParentWndWidth = -1;
		m_nParentWndHeight = -1;

		m_bSkipHiddenCtrl = true;
		m_szMinLayoutSize.cx = -1;
		m_szMinLayoutSize.cy = -1;
	}
public:
	bool	m_bSkipHiddenCtrl;
	SIZE	m_szMinLayoutSize;	// Stop performing layout when the size of parent window is small this size
public:
	BOOL Init(const Window& wndParent)
	{
		m_pWndParent = &wndParent;
		RECT rtDlg;
		m_pWndParent->GetClientRect(&rtDlg);
		m_nParentWndWidth = RECT_WIDTH(rtDlg);
		m_nParentWndHeight = RECT_HEIGHT(rtDlg);
		return m_pWndParent != NULL;
	}

	BOOL Add(int nCtrlId, const CLayoutOption& option)
	{
		ASSERT(NULL != m_pWndParent);
		CLayoutInfo* pNewAnchorInfo = new CLayoutInfo(nCtrlId, option);
		return Add(pNewAnchorInfo);
	}

	/// Kenny 09/29/2009 IMPROVE_FIND_REPLACE_DLG_RESIZING_PROCESS
	BOOL Add(const vector<int>& vnCtrlIDs, const CLayoutOption& option)
	{
		BOOL bRet = TRUE;
		for (int ii = 0; ii < vnCtrlIDs.GetSize(); ++ii)
		{
			bRet &= Add( vnCtrlIDs[ii], option);
		}
		return bRet;
	}
	/// End IMPROVE_FIND_REPLACE_DLG_RESIZING_PROCESS

	BOOL Add(const CLayoutInfo& info)
	{
		ASSERT(NULL != m_pWndParent);
		CLayoutInfo* pNewAnchorInfo = new CLayoutInfo(info);
		return Add(pNewAnchorInfo);
	}

	CLayoutInfo* GetCtrlLayoutInfoPointer(const int nCtrlId)
	{
		int nIndex = FindCtrl(nCtrlId);
		if (nIndex >= 0)
		{
			CLayoutInfo& info = m_arrLayoutInfos.GetAt(nIndex);
			return &info;
		}
		return NULL;
	}

	void UpdateAllCtrlRect()
	{
		const int nAnchorInfoSize = m_arrLayoutInfos.GetSize();
		for (int ii = 0; ii < nAnchorInfoSize; ++ii)
		{
			m_arrLayoutInfos.GetAt(ii).UpdateRect(*m_pWndParent);
		}
	}

	BOOL Remove(const int nCtrlId)
	{
		int nIndex = FindCtrl(nCtrlId);
		if (nIndex >= 0)
		{
			return m_arrLayoutInfos.RemoveAt(nIndex);
		}
		return FALSE;
	}

	BOOL RemoveAll()
	{
		BOOL bAllCleared = TRUE;
		for (int ii = m_arrLayoutInfos.GetSize()-1; ii >= 0; --ii)
		{
			bAllCleared &= m_arrLayoutInfos.RemoveAt(ii);
		}
		return bAllCleared;
	}

	void PerformLayout(const int nParentWndWidth, const int nParentWndHeight, BOOL bRepaint = TRUE)
	{
		if ( !m_pWndParent )
			return;
		if (   m_szMinLayoutSize.cx > -1 && nParentWndWidth >= m_szMinLayoutSize.cx
			|| m_szMinLayoutSize.cy > -1 && nParentWndHeight >= m_szMinLayoutSize.cy )
		{
			return;
		}

		const int nWidthDelta = nParentWndWidth - m_nParentWndWidth;
		const int nHeightDelta = nParentWndHeight - m_nParentWndHeight;

		const int nAnchorInfoSize = m_arrLayoutInfos.GetSize();
		for (int ii = 0; ii < nAnchorInfoSize; ++ii)
		{
			CLayoutInfo& info = m_arrLayoutInfos.GetAt(ii);

			Control ctrl = info.GetControl(*m_pWndParent);
			if ( m_bSkipHiddenCtrl && !ctrl.Visible )
				continue;

			UpdateLayoutInfo(info, nParentWndWidth, nParentWndHeight, nWidthDelta, nHeightDelta);
			ctrl.MoveWindow(&info.m_rect, bRepaint);
		}

		RECT rtDlg;
		m_pWndParent->GetClientRect(&rtDlg);
		m_nParentWndWidth = RECT_WIDTH(rtDlg);
		m_nParentWndHeight = RECT_HEIGHT(rtDlg);
	}
protected:
	BOOL Add(CLayoutInfo* pNewAnchorInfo)
	{
		if ( !pNewAnchorInfo || !m_pWndParent )
		{
			ASSERT(FALSE);
			return FALSE;
		}
		pNewAnchorInfo->UpdateRect(*m_pWndParent);
		if ( m_arrLayoutInfos.Add(*pNewAnchorInfo) < 0 )
		{
			ASSERT(FALSE);
			delete pNewAnchorInfo;
			return FALSE;
		}

		return TRUE;
	}
	int FindCtrl(const int nCtrlId)
	{
		const int nInfoSize = m_arrLayoutInfos.GetSize();
		for (int ii = 0; ii < nInfoSize; ++ii)
		{
			const CLayoutInfo& info = m_arrLayoutInfos.GetAt(ii);
			if ( nCtrlId == info.GetCtrlID() )
			{
				return ii;
			}
		}
		return -1;
	}
	void UpdateLayoutInfo(CLayoutInfo& info, const int cx, const int cy, const int nWidthDelta, const int nHeightDelta)
	{
		updateLayoutInfoX(info, cx, nWidthDelta);
		updateLayoutInfoY(info, cy, nHeightDelta);
	}
private:
	void updateLayoutInfoX(CLayoutInfo& info, const int cx, const int nWidthDelta)
	{
		const CLayoutOption& option = info.m_layoutOption;

		int nLeftOffset = -1;
		int nRightOffset = -1;

		const bool bLeftOffset = option.GetOption(LOT_LEFT_OFFSET, nLeftOffset);
		const bool bRightOffset = option.GetOption(LOT_RIGHT_OFFSET, nRightOffset);

		if ( bLeftOffset && nLeftOffset >= 0 )
			info.m_rect.left = nLeftOffset;
		if ( bRightOffset )
		{
			int nNewRight;
			if ( nRightOffset >= 0 )
			{
				nNewRight = cx - nRightOffset;
			}
			else
			{
				nNewRight = info.m_rect.right + nWidthDelta;
			}

			if ( !bLeftOffset )
				info.m_rect.left += nNewRight - info.m_rect.right;
			info.m_rect.right = nNewRight;
		}
	}
	void updateLayoutInfoY(CLayoutInfo& info, const int cy, const int nHeightDelta)
	{
		const CLayoutOption& option = info.m_layoutOption;

		int nTopOffset = -1;
		int nBottomOffset = -1;

		const bool bTopOffset = option.GetOption(LOT_TOP_OFFSET, nTopOffset);
		const bool bBottomOffset = option.GetOption(LOT_BOTTOM_OFFSET, nBottomOffset);

		if ( bTopOffset && nTopOffset >= 0 )
			info.m_rect.top = nTopOffset;
		if ( bBottomOffset )
		{
			int nNewBottom;
			if ( nBottomOffset >= 0 )
			{
				nNewBottom = cy - nBottomOffset;
			}
			else
			{
				nNewBottom = info.m_rect.bottom + nHeightDelta;
			}

			if ( !bTopOffset )
				info.m_rect.top += nNewBottom - info.m_rect.bottom;
			info.m_rect.bottom = nNewBottom;
		}
	}
private:
	Array<CLayoutInfo&>		m_arrLayoutInfos;
	Window*					m_pWndParent;
	int						m_nParentWndWidth;
	int						m_nParentWndHeight;
};

#endif // __LAYOUTHELPER_H__