/*
Dialog Layout Manager for MFC
Copyright 2009 Adam Sawicki
E-mail: sawickiap(at)poczta(dot)onet(dot)pl
Homepage: http://regedit.gamedev.pl/
License: Public Domain
*/

////////////////////////////////////////////////////////////////////////////////
// Code for HPP file

class DialogLayoutManager
{
public:
	static const unsigned ANCHOR_LEFT   = 0x01;
	static const unsigned ANCHOR_TOP    = 0x02;
	static const unsigned ANCHOR_RIGHT  = 0x04;
	static const unsigned ANCHOR_BOTTOM = 0x08;

	DialogLayoutManager();
	~DialogLayoutManager();

	// Dodaje lub zmienia rejestrację kontrolki
	void SetControl(CWnd *wnd, unsigned anchorFlags);
	// Usuwa rejestrację kontrolki
	void RemoveControl(CWnd *wnd);

	bool IsSaved() { return SavedClientSizeX != -1; }
	void Save(CWnd *parentWindow);
	void Restore(CWnd *parentWindow);

private:
	struct SControlDesc
	{
		CWnd *Wnd;
		unsigned AnchorFlags;
		RECT Rect;
	};

	std::vector<SControlDesc> ControlDescs;
	int SavedClientSizeX, SavedClientSizeY;
};


////////////////////////////////////////////////////////////////////////////////
// Code for CPP file

DialogLayoutManager::DialogLayoutManager()
: SavedClientSizeX(-1)
, SavedClientSizeY(-1)
{
}

DialogLayoutManager::~DialogLayoutManager()
{
}

void DialogLayoutManager::SetControl(CWnd *wnd, unsigned anchorFlags)
{
	assert( (anchorFlags & ANCHOR_LEFT) != 0 || (anchorFlags & ANCHOR_RIGHT ) != 0);
	assert( (anchorFlags & ANCHOR_TOP ) != 0 || (anchorFlags & ANCHOR_BOTTOM) != 0);
	assert(wnd != NULL);

	size_t count = ControlDescs.size();
	size_t index;
	for (index = 0; index < count; index++)
		if (ControlDescs[index].Wnd == wnd)
			break;
	if (index == count)
		index = ControlDescs.push_back(SControlDesc());

	ControlDescs[index].Wnd = wnd;
	ControlDescs[index].AnchorFlags = anchorFlags;
	ControlDescs[index].Rect.left   = -1;
	ControlDescs[index].Rect.top    = -1;
	ControlDescs[index].Rect.right  = -1;
	ControlDescs[index].Rect.bottom = -1;
}

void DialogLayoutManager::RemoveControl(CWnd *wnd)
{
	if (wnd == NULL) return;

	size_t count = ControlDescs.size();
	for (size_t index = 0; index < count; index++)
	{
		if (ControlDescs[index].Wnd == wnd)
		{
			ControlDescs.erase(ControlDescs.begin() + index);
			break;
		}
	}
}

void DialogLayoutManager::Save(CWnd *parentWindow)
{
	assert(parentWindow);

	RECT wndClientRect;
	parentWindow->GetClientRect(&wndClientRect);
	SavedClientSizeX = wndClientRect.right  - wndClientRect.left;
	SavedClientSizeY = wndClientRect.bottom - wndClientRect.top;

	size_t count = ControlDescs.size();
	for (size_t i = 0; i < count; i++)
	{
		ControlDescs[i].Wnd->GetWindowRect(&ControlDescs[i].Rect);
		parentWindow->ScreenToClient(&ControlDescs[i].Rect);
	}
}

void DialogLayoutManager::Restore(CWnd *parentWindow)
{
	assert(parentWindow);
	assert(SavedClientSizeX != -1);

	RECT wndClientRect;
	parentWindow->GetClientRect(&wndClientRect);
	int newClientSizeX = wndClientRect.right  - wndClientRect.left;
	int newClientSizeY = wndClientRect.bottom - wndClientRect.top;

	size_t count = ControlDescs.size();
	RECT newRect;
	size_t savedControlSizeX, savedControlSizeY;
	
	for (size_t i = 0; i < count; i++)
	{
		const SControlDesc &desc = ControlDescs[i];
		savedControlSizeX = desc.Rect.right  - desc.Rect.left;
		savedControlSizeY = desc.Rect.bottom - desc.Rect.top;

		if ( (desc.AnchorFlags & ANCHOR_LEFT) != 0 && (desc.AnchorFlags & ANCHOR_RIGHT) != 0 )
		{
			newRect.left = desc.Rect.left;
			newRect.right = newClientSizeX - (SavedClientSizeX - desc.Rect.right);
		}
		else if ( (desc.AnchorFlags & ANCHOR_RIGHT) != 0 )
		{
			newRect.right = newClientSizeX - (SavedClientSizeX - desc.Rect.right);
			newRect.left = newRect.right - savedControlSizeX;
		}
		else
		{
			newRect.left = desc.Rect.left;
			newRect.right = newRect.left + savedControlSizeX;
		}

		if ( (desc.AnchorFlags & ANCHOR_TOP) != 0 && (desc.AnchorFlags & ANCHOR_BOTTOM) != 0 )
		{
			newRect.top = desc.Rect.top;
			newRect.bottom = newClientSizeY - (SavedClientSizeY - desc.Rect.bottom);
		}
		else if ( (desc.AnchorFlags & ANCHOR_BOTTOM) != 0 )
		{
			newRect.bottom = newClientSizeY - (SavedClientSizeY - desc.Rect.bottom);
			newRect.top = newRect.bottom - savedControlSizeY;
		}
		else
		{
			newRect.top = desc.Rect.top;
			newRect.bottom = newRect.top + savedControlSizeY;
		}

		desc.Wnd->SetWindowPos(
			NULL,
			newRect.left, newRect.top,
			newRect.right - newRect.left, newRect.bottom - newRect.top,
			SWP_NOOWNERZORDER | SWP_NOACTIVATE);
		desc.Wnd->RedrawWindow(); // Bo powstają jakieś buraki że się nie odrysowuje.
	}
}


////////////////////////////////////////////////////////////////////////////////
// Usage example in dialog window

////// HPP file of dialog window

class CDialog01 : public CDialog
{
	...
	
	CDialog01(CWnd* pParent = NULL);
	~CDialog01();
	virtual BOOL OnInitDialog();
	afx_msg void OnSize(UINT nType, int cx, int cy);
	
	DialogLayoutManager *m_LayoutManager;
};

////// CPP file of dialog window

CDialog01::CDialog01(CWnd* pParent)
: m_LayoutManager(new DialogLayoutManager)
{
}

CDialog01::~CDialog01()
{
	delete m_LayoutManager;
}

BOOL CDialog01::OnInitDialog()
{
	...
	
	m_LayoutManager->SetControl(&Control01,
		DialogLayoutManager::ANCHOR_LEFT | DialogLayoutManager::ANCHOR_TOP |
		DialogLayoutManager::ANCHOR_RIGHT | DialogLayoutManager::ANCHOR_BOTTOM);
	m_LayoutManager->SetControl(GetDlgItem(ID_CONTROL02),
		DialogLayoutManager::ANCHOR_RIGHT | DialogLayoutManager::ANCHOR_BOTTOM);

	m_LayoutManager->Save(this);
}

void CDialog01::OnSize(UINT nType, int cx, int cy)
{
	...

	if (LayoutManager && LayoutManager->IsSaved())
		m_LayoutManager->Restore(this);
}

BEGIN_MESSAGE_MAP(CDialog01, CDialog)
	...
	ON_WM_SIZE()
END_MESSAGE_MAP()
