Click here to Skip to main content
15,891,316 members
Articles / Desktop Programming / ATL

Sharp Layout

Rate me:
Please Sign up or sign in to vote.
4.80/5 (11 votes)
31 Jan 20052 min read 42.6K   1.3K   33  
Sharp Layout manager for ATL/WTL.
// SharpLayout.h - ATL implementation of sharp layout manager
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2004 kliff
// All rights reserved.
// 11/03/2004 - Initial release.
//
// NO WARRANTIES ARE EXTENDED. USE AT YOUR OWN RISK.
//
// To contact the author with suggestions or comments, use hellokliff@yandex.ru
/////////////////////////////////////////////////////////////////////////////
#pragma once

#include <atlcoll.h>

#define SSP_DOCKTOP				0x00000001
#define SSP_DOCKBOTTOM			0x00000002
#define SSP_DOCKLEFT			0x00000004
#define SSP_DOCKRIGHT			0x00000008
#define SSP_DOCKMASK			0x0000000f
#define SSP_DOCKFILL			0x00000010
#define SSP_INTERACTIVE			0x00000020
#define SSP_SPLITTER			0x00000040
#define SSP_PROPORTIONAL		0x00000080
#define SSP_ALIGNLEFT			0x00000100
#define SSP_ALIGNRIGHT			0x00000200
#define SSP_ALIGNTOP			0x00000400
#define SSP_ALIGNBOTTOM			0x00000800
#define SSP_IGNORE				0x00001000

__declspec(selectany) struct
{
	enum { cxWidth = 32, cyHeight = 32 };
	int xHotSpot;
	int yHotSpot;
	unsigned char arrANDPlane[cxWidth * cyHeight / 8];
	unsigned char arrXORPlane[cxWidth * cyHeight / 8];
} _HorzSplitter_CursorData = 
{
	16, 16, 
	{
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
		0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 
		0xff, 0xd8, 0x1b, 0xff, 0xff, 0x88, 0x11, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x7f, 
		0xff, 0x00, 0x00, 0xff, 0xff, 0x88, 0x11, 0xff, 0xff, 0xd8, 0x1b, 0xff, 0xff, 0xf8, 0x1f, 0xff, 
		0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
	},
	{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x07, 0xe0, 0x00, 0x00, 0x05, 0xa0, 0x00, 0x00, 0x05, 0xa0, 0x00, 0x00, 0x05, 0xa0, 0x00, 
		0x00, 0x25, 0xa4, 0x00, 0x00, 0x55, 0xaa, 0x00, 0x00, 0x9d, 0xb9, 0x00, 0x01, 0x01, 0x80, 0x80, 
		0x00, 0x9d, 0xb9, 0x00, 0x00, 0x55, 0xaa, 0x00, 0x00, 0x25, 0xa4, 0x00, 0x00, 0x05, 0xa0, 0x00, 
		0x00, 0x05, 0xa0, 0x00, 0x00, 0x05, 0xa0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	}
};

__declspec(selectany) struct
{
	enum { cxWidth = 32, cyHeight = 32 };
	int xHotSpot;
	int yHotSpot;
	unsigned char arrANDPlane[cxWidth * cyHeight / 8];
	unsigned char arrXORPlane[cxWidth * cyHeight / 8];
} _VertSplitter_CursorData = 
{
	16, 16, 
	{
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 
		0xff, 0xfc, 0x7f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 
		0xfe, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 
		0xfe, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 
		0xff, 0xf8, 0x3f, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
	},
	{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 
		0x00, 0x02, 0x80, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x06, 0xc0, 0x00, 
		0x01, 0xfe, 0xff, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 
		0x01, 0x00, 0x01, 0x00, 0x01, 0xfe, 0xff, 0x00, 0x00, 0x06, 0xc0, 0x00, 0x00, 0x08, 0x20, 0x00, 
		0x00, 0x04, 0x40, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	}
};

template <typename T>
class CSharpLayoutImpl
{
public:
	struct PaneInfoStruct
	{
		int style;
		RECT bound;
		RECT align;
		SIZE size;

		PaneInfoStruct()
		{
			memset(this, 0, sizeof(*this));
		}
	};

	struct DragInfoStruct
	{
		CWindow wnd;
		int offset;
		int pos;
		int orig_pos;
		int size;

		DragInfoStruct() : offset(0), pos(0), orig_pos(0), size(0)
		{
		}
	};

	struct PosInfoStruct
	{
		CWindow wnd;
		int start;
		int size;
		int style;

		PosInfoStruct() : start(0), size(0), style(0)
		{
		}
	};

	struct GroupInfoStruct 
	{
		CSimpleArray<PosInfoStruct> posinfo;
		int size;
		int relative;
		int delta;

		GroupInfoStruct() : size(0), relative(0), delta(0)
		{
		}
	};

	CAtlMap<HWND, PaneInfoStruct> m_panes;
	DragInfoStruct* m_drag;

	HCURSOR m_cursor[2];
	int m_nSplitterSize;

	CSharpLayoutImpl() : m_drag(NULL)
	{
		m_nSplitterSize = 4;

		m_cursor[0] = ::CreateCursor(
#if (_ATL_VER >= 0x0700)
			ATL::_AtlBaseModule.GetModuleInstance(), 
#else //!(_ATL_VER >= 0x0700)
			_Module.GetModuleInstance(), 
#endif //!(_ATL_VER >= 0x0700)
			_HorzSplitter_CursorData.xHotSpot, 
			_HorzSplitter_CursorData.yHotSpot, 
			_HorzSplitter_CursorData.cxWidth, 
			_HorzSplitter_CursorData.cyHeight, 
			_HorzSplitter_CursorData.arrANDPlane, 
			_HorzSplitter_CursorData.arrXORPlane);

		m_cursor[1] = ::CreateCursor(
#if (_ATL_VER >= 0x0700)
			ATL::_AtlBaseModule.GetModuleInstance(), 
#else //!(_ATL_VER >= 0x0700)
			_Module.GetModuleInstance(), 
#endif //!(_ATL_VER >= 0x0700)
			_VertSplitter_CursorData.xHotSpot, 
			_VertSplitter_CursorData.yHotSpot, 
			_VertSplitter_CursorData.cxWidth, 
			_VertSplitter_CursorData.cyHeight, 
			_VertSplitter_CursorData.arrANDPlane, 
			_VertSplitter_CursorData.arrXORPlane);
	}

	~CSharpLayoutImpl()
	{
		if (NULL != m_drag)
			delete m_drag;

		if (NULL != m_cursor[0])
			DestroyCursor(m_cursor[0]);

		if (NULL != m_cursor[1])
			DestroyCursor(m_cursor[1]);
	}

	PaneInfoStruct* CreatePaneInfo(CWindow wnd)
	{
		CAtlMap<HWND, PaneInfoStruct>::CPair* p = m_panes.Lookup(wnd);
		if (NULL != p)
			return &p->m_value;
		if (!wnd.IsWindow())
			return NULL;
		T* pT = static_cast<T*>(this);
		CWindow self(pT->m_hWnd);

		RECT rc;
		ATLVERIFY(wnd.GetWindowRect(&rc));
		ATLVERIFY(self.ScreenToClient(&rc));
		PaneInfoStruct pi;
		ATLVERIFY(self.GetClientRect(&pi.bound));
		pi.size.cx = rc.right - rc.left;
		pi.size.cy = rc.bottom - rc.top;
		pi.align.left = rc.left - pi.bound.left;
		pi.align.right = pi.bound.right - rc.right;
		pi.align.top = rc.top - pi.bound.top;
		pi.align.bottom = pi.bound.bottom - rc.bottom;
		pi.style = SSP_IGNORE;
		POSITION pos = m_panes.SetAt(wnd, pi);
		ATLASSERT(NULL != pos);

		return &m_panes.GetValueAt(pos);
	}

	//overridable
	PaneInfoStruct* GetPaneInfo(CWindow wnd)
	{
		return CreatePaneInfo(wnd);
	}

	bool SetPaneStyle(CWindow wnd, int style, bool update)
	{
		T* pT = static_cast<T*>(this);
		PaneInfoStruct* pane = pT->GetPaneInfo(wnd);
		if (NULL == pane)
			return false;
		if (pane->style == style)
			return true;
		pane->style = style;
		if (update)
			RecalcLayout();
		return true;
	}
	bool SetPaneWidth(CWindow wnd, int width, bool update)
	{
		T* pT = static_cast<T*>(this);
		PaneInfoStruct* pane = pT->GetPaneInfo(wnd);
		if (NULL == pane)
			return false;
		if (0 > width)
			width = 0;
		if (pane->size.cx == width)
			return true;
		pane->size.cx = width;
		if (update)
			RecalcLayout();
		return true;
	}
	bool SetPaneHeight(CWindow wnd, int height, bool update)
	{
		T* pT = static_cast<T*>(this);
		PaneInfoStruct* pane = pT->GetPaneInfo(wnd);
		if (NULL == pane)
			return false;
		if (0 > height)
			height = 0;
		if (pane->size.cy == height)
			return true;
		pane->size.cy = height;
		if (update)
			RecalcLayout();
		return true;
	}
	bool SetPaneDockSize(CWindow wnd, int size, bool update)
	{
		T* pT = static_cast<T*>(this);
		PaneInfoStruct* pane = pT->GetPaneInfo(wnd);
		if (NULL == pane)
			return false;
		if (0 > size)
			size = 0;
		bool vert = (0 == ((SSP_DOCKLEFT|SSP_DOCKRIGHT) & pane->style));
		if (vert)
			pane->size.cy = size;
		else
			pane->size.cx = size;
		if (SSP_PROPORTIONAL & pane->style)
		{
			//update proportinal info
			CWindow self(pT->m_hWnd);
			ATLVERIFY(self.GetClientRect(&pane->bound));
		}
		if (update)
			RecalcLayout();
		return true;
	}
	bool GetPaneDockSize(CWindow wnd, int& size)
	{
		T* pT = static_cast<T*>(this);
		PaneInfoStruct* pane = pT->GetPaneInfo(wnd);
		if (NULL == pane)
			return false;
		ATLASSERT(SSP_DOCKMASK & pane->style);
		bool vert = (0 == ((SSP_DOCKLEFT|SSP_DOCKRIGHT) & pane->style));
		size = vert ? pane->size.cy : pane->size.cx;
		if (SSP_PROPORTIONAL & pane->style)
		{
			CWindow self(pT->m_hWnd);

			RECT rc;
			ATLVERIFY(self.GetClientRect(&rc));

			int mul = vert ? (rc.bottom-rc.top) : (rc.right-rc.left);
			int div = vert ? 
				(pane->bound.bottom-pane->bound.top) : (pane->bound.right-pane->bound.left);
			size = int(size * double(mul)) / div;
		}
		return true;
	}
	bool GetPaneAlignInfo(CWindow wnd, RECT& align)
	{
		T* pT = static_cast<T*>(this);
		PaneInfoStruct* pane = pT->GetPaneInfo(wnd);
		if (NULL == pane)
			return false;
		align = pane->align;
		return true;
	}
	bool GetPaneWidth(CWindow wnd, int& width)
	{
		T* pT = static_cast<T*>(this);
		PaneInfoStruct* pane = pT->GetPaneInfo(wnd);
		if (NULL == pane)
			return false;
		width = pane->size.cx;
		return true;
	}
	bool GetPaneHeight(CWindow wnd, int& height)
	{
		T* pT = static_cast<T*>(this);
		PaneInfoStruct* pane = pT->GetPaneInfo(wnd);
		if (NULL == pane)
			return false;
		height = pane->size.cy;
		return true;
	}
	bool GetPaneStyle(CWindow wnd, int& style)
	{
		T* pT = static_cast<T*>(this);
		PaneInfoStruct* pane = pT->GetPaneInfo(wnd);
		if (NULL == pane)
			return false;
		style = pane->style;
		return true;
	}
	bool CalcPaneAlign(CWindow wnd, const RECT& rc, RECT& rc2)
	{
		int style;
		if (!GetPaneStyle(wnd, style))
			return false;

		RECT align;
		if (!GetPaneAlignInfo(wnd, align))
			return false;

		rc2.left = -1;
		rc2.right = -1;
		rc2.top = -1;
		rc2.bottom = -1;

		//process align
		if (SSP_ALIGNLEFT & style)
			rc2.left = rc.left + align.left;
		if (SSP_ALIGNRIGHT & style)
			rc2.right = rc.right - align.right;
		if (SSP_ALIGNTOP & style)
			rc2.top = rc.top + align.top;
		if (SSP_ALIGNBOTTOM & style)
			rc2.bottom = rc.bottom - align.bottom;

		//process width
		if (0 > rc2.left || 0 > rc2.right)
		{
			int width;
			if (!GetPaneWidth(wnd, width))
				return false;

			if (0 > rc2.left && 0 > rc2.right)
			{
				int center = (rc.right + rc.left) / 2;
				rc2.left = center - width;
				rc2.right = rc2.left + width;
			}
			else if (0 > rc2.left)
			{
				rc2.left = rc2.right - width;
			}
			else if (0 > rc2.right)
			{
				rc2.right = rc2.left + width;
			}
		}

		//process height
		if (0 > rc2.top || 0 > rc2.bottom)
		{
			int height;
			if (!GetPaneHeight(wnd, height))
				return false;

			if (0 > rc2.top && 0 > rc2.bottom)
			{
				int center = (rc.bottom + rc.top) / 2;
				rc2.top = center - height;
				rc2.bottom = rc2.top + height;
			}
			if (0 > rc2.top)
			{
				rc2.top = rc2.bottom - height;
			}
			else if (0 > rc2.bottom)
			{
				rc2.bottom = rc2.top + height;
			}
		}

		return true;
	}

	void GetPanesByStyle(CSimpleArray<CWindow>& sa, int mask, bool visibleonly = true)
	{
		T* pT = static_cast<T*>(this);
		CWindow self(pT->m_hWnd);

		sa.RemoveAll();
		for (CWindow wnd = self.GetWindow(GW_CHILD); 
			wnd.IsWindow(); wnd = wnd.GetWindow(GW_HWNDNEXT))
		{
			if (visibleonly && !(WS_VISIBLE & wnd.GetStyle()))
				continue;

			int style;
			ATLVERIFY(GetPaneStyle(wnd, style));

			if (mask & style || -1 == style)
				ATLVERIFY(sa.Add(wnd));
		}
	}
	bool GetPaneDockBound(RECT& rc)
	{
		T* pT = static_cast<T*>(this);
		CWindow self(pT->m_hWnd);

		if (!self.GetClientRect(&rc))
			return false;

		CSimpleArray<CWindow> docked;
		GetPanesByStyle(docked, SSP_DOCKMASK);

		ProcessDocked(docked, rc, CDockDummy());
		return true;
	}
	CWindow SplitterFromPoint(int x, int y)
	{
		T* pT = static_cast<T*>(this);
		CWindow self(pT->m_hWnd);

		CSimpleArray<CWindow> docked;
		GetPanesByStyle(docked, SSP_DOCKMASK);

		RECT rc;
		ATLVERIFY(self.GetClientRect(&rc));

		CDockFromPoint dfp(x, y);
		ProcessDocked(docked, rc, dfp);

		return dfp.m_wnd;
	}
	void DrawGhostBar()
	{
		T* pT = static_cast<T*>(this);
		CWindow self(pT->m_hWnd);

		ATLASSERT(NULL != m_drag);
		CWindow wnd = m_drag->wnd;
		int pos = m_drag->pos;

		RECT rc;
		ATLVERIFY(wnd.GetWindowRect(&rc));
		ATLVERIFY(self.ScreenToClient(&rc));

		int style;
		ATLVERIFY(GetPaneStyle(wnd, style));

		int split = m_nSplitterSize;

		if (SSP_DOCKLEFT & style)
		{
			rc.left = pos;
			rc.right = rc.left + split;
		}
		else if (SSP_DOCKRIGHT & style)
		{
			rc.left = pos - split;
			rc.right = rc.left + split;
		}
		else if (SSP_DOCKTOP & style)
		{
			rc.top = pos;
			rc.bottom = rc.top + split;
		}
		else if (SSP_DOCKBOTTOM & style)
		{
			rc.top = pos - split;
			rc.bottom = rc.top + split;
		}

		// invert the brush pattern (looks just like frame window sizing)
		HBRUSH halftoneBrush = NULL;
		WORD grayPattern[8];
		for (int i = 0; i < 8; i++)
			grayPattern[i] = (WORD)(0x5555 << (i & 1));
		HBITMAP grayBitmap = ::CreateBitmap(8, 8, 1, 1, &grayPattern);
		if (grayBitmap != NULL)
		{
			halftoneBrush = ::CreatePatternBrush(grayBitmap);
			::DeleteObject(grayBitmap);
		}
		if(halftoneBrush != NULL)
		{
			//draw in window area (not client), so need to convert
			RECT bound = {0,0,0,0};
			ATLVERIFY(::AdjustWindowRectEx(&bound, 
				self.GetStyle(), FALSE, self.GetExStyle()));
			::OffsetRect(&rc, -bound.left, -bound.top);

			HDC hdc = self.GetWindowDC();
			HGDIOBJ brushOld = SelectObject(hdc, halftoneBrush);
			::PatBlt(
				hdc,
				rc.left, rc.top, 
				rc.right - rc.left, rc.bottom - rc.top, 
				PATINVERT);
			::SelectObject(hdc, brushOld);
			::DeleteObject(halftoneBrush);
			self.ReleaseDC(hdc);
		}
	}

	BOOL IsFullDrag() const
	{
		BOOL bFullDrag = TRUE;
		::SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &bFullDrag, 0);
		return bFullDrag;
	}

public:
	BEGIN_MSG_MAP(CSharpLayoutImpl)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
		MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
		MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
		MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
	END_MSG_MAP()

	LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
		CWindow wnd = SplitterFromPoint(pt.x, pt.y);
		int style = 0;
		if(GetPaneStyle(wnd, style) && (SSP_INTERACTIVE & style))
		{
			T* pT = static_cast<T*>(this);
			CWindow self(pT->m_hWnd);

			self.SetCapture();

			bool vert = (0 == ((SSP_DOCKLEFT|SSP_DOCKRIGHT) & style));

			::SetCursor(vert ? m_cursor[1] : m_cursor[0]);

			RECT rc;
			ATLVERIFY(wnd.GetWindowRect(&rc));
			ATLVERIFY(self.ScreenToClient(&rc));

			m_drag = new DragInfoStruct();

			m_drag->wnd = wnd;
			m_drag->size = vert ? 
				rc.bottom - rc.top : rc.right - rc.left;

			if (SSP_DOCKLEFT & style)
				m_drag->pos = rc.right;
			else if (SSP_DOCKRIGHT & style) 
				m_drag->pos = rc.left;
			else if (SSP_DOCKTOP & style)
				m_drag->pos = rc.bottom;
			else if (SSP_DOCKBOTTOM & style)
				m_drag->pos = rc.top;

			m_drag->orig_pos = m_drag->pos;
			int pos = vert ? pt.y : pt.x;
			m_drag->offset = pos - m_drag->pos;

			if(!pT->IsFullDrag())
				DrawGhostBar();
		}
		bHandled = FALSE;
		return 0;
	}
	LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		::ReleaseCapture();
		bHandled = FALSE;
		return 0;
	}
	LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
		if((wParam & MK_LBUTTON) && NULL != m_drag)
		{
			T* pT = static_cast<T*>(this);
			CWindow self(pT->m_hWnd);

			int style;
			ATLVERIFY(GetPaneStyle(m_drag->wnd, style));

			//calculate bound rectange
			RECT bound;
			ATLVERIFY(GetPaneDockBound(bound));

			int split = m_nSplitterSize;

			int pos = -1;
			if (SSP_DOCKLEFT & style)
				pos = __min(pt.x-m_drag->offset, bound.right-split);
			else if (SSP_DOCKRIGHT & style)
				pos = __max(pt.x-m_drag->offset, bound.left+split);
			else if (SSP_DOCKTOP & style)
				pos = __min(pt.y-m_drag->offset, bound.bottom-split);
			else if (SSP_DOCKBOTTOM & style)
				pos = __max(pt.y-m_drag->offset, bound.top+split);

			int delta = pos - m_drag->orig_pos;

			bool inverse = (0 == ((SSP_DOCKLEFT|SSP_DOCKTOP) & style));
			if (inverse)
				delta = -delta;

			int size = m_drag->size + delta;

			//limit size smaller then zero
			if (0 > size)
				pos = m_drag->orig_pos - (inverse ? -m_drag->size : m_drag->size);

			if(m_drag->pos != pos)
			{
				if(pT->IsFullDrag())
				{
					m_drag->pos = pos;
					SetPaneDockSize(m_drag->wnd, size, true);
				}
				else
				{
					DrawGhostBar();
					m_drag->pos = pos;
					DrawGhostBar();
				}
			}
		}
		else		// not dragging, just set cursor
		{
			CWindow wnd = SplitterFromPoint(pt.x, pt.y);
			int style;
			if(GetPaneStyle(wnd, style) && (SSP_INTERACTIVE & style))
			{
				bool vert = (0 == ((SSP_DOCKLEFT|SSP_DOCKRIGHT) & style));
				::SetCursor(vert ? m_cursor[1] : m_cursor[0]);
			}
			bHandled = FALSE;
		}
		return 0;
	}
	LRESULT OnCaptureChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		T* pT = static_cast<T*>(this);
		if (NULL != m_drag && !pT->IsFullDrag())
		{
			DrawGhostBar();

			int style;
			ATLVERIFY(GetPaneStyle(m_drag->wnd, style));

			int delta = m_drag->pos - m_drag->orig_pos;

			bool inverse = (0 == ((SSP_DOCKLEFT|SSP_DOCKTOP) & style));
			if (inverse)
				delta = -delta;

			int size = m_drag->size + delta;

			ATLVERIFY(SetPaneDockSize(m_drag->wnd, size, true));
		}
		if (NULL != m_drag)
		{
			delete m_drag;
			m_drag = NULL;
		}
		return 0;
	}
	LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		RecalcLayout();		
		bHandled = FALSE;
		return 0;
	}

	class CDockDummy
	{
	public:
		bool operator()(CWindow& wnd, POINT& ptwin, SIZE& sizewin, POINT& ptsplit, SIZE& sizesplit)
		{
			return true;
		}
	};

	class CDockFromPoint
	{
	public:
		POINT m_pt;
		CWindow m_wnd;
		CDockFromPoint(int x, int y)
		{
			m_pt.x = x;
			m_pt.y = y;
		}
		bool operator()(CWindow& wnd, POINT& ptwin, SIZE& sizewin, POINT& ptsplit, SIZE& sizesplit)
		{
			RECT rc = {ptsplit.x, ptsplit.y, ptsplit.x + sizesplit.cx, ptsplit.y + sizesplit.cy};
			if (!::PtInRect(&rc, m_pt))
				return true;
			m_wnd = wnd;
			return false;
		}
	};

	class CDockResize
	{
	public:
		HDWP& m_hdwp;
		T* m_pT;
		CDockResize(T* pT, HDWP& hdwp) : m_pT(pT), m_hdwp(hdwp)
		{
		}
		bool operator()(CWindow& wnd, POINT& ptwin, SIZE& sizewin, POINT& ptsplit, SIZE& sizesplit)
		{
			RECT rc = {ptwin.x, ptwin.y, ptwin.x+sizewin.cx,ptwin.y+sizewin.cy};
			m_hdwp = m_pT->SetPanePos(m_hdwp, wnd, rc);
			return true;
		}
	};

	template <typename TAction>
	void ProcessDocked(CSimpleArray<CWindow>& docked, RECT& rc, TAction& action)
	{
		for (int i=0; i<docked.GetSize(); i++)
		{
			CWindow wnd = docked[i];

			int style;
			ATLVERIFY(GetPaneStyle(wnd, style));

			int size;
			ATLVERIFY(GetPaneDockSize(wnd, size));

			int split = (SSP_SPLITTER & style) ? m_nSplitterSize : 0;
			int sizeall = ((SSP_DOCKLEFT|SSP_DOCKRIGHT) & style) ? 
				rc.right-rc.left : rc.bottom-rc.top;

			if (size + split > sizeall)
				size = sizeall - split;

			POINT ptwin, ptsplit;
			SIZE sizewin, sizesplit;
			if (SSP_DOCKLEFT & style)
			{
				ptwin.x = rc.left;
				ptwin.y = rc.top;
				sizewin.cx = size;
				sizewin.cy = rc.bottom-rc.top;
				ptsplit.x = ptwin.x + sizewin.cx;
				ptsplit.y = ptwin.y;
				sizesplit.cx = split;
				sizesplit.cy = sizewin.cy;

				rc.left += size + split;
			}
			else if (SSP_DOCKRIGHT & style)
			{
				ptwin.x = rc.right - size;
				ptwin.y = rc.top;
				sizewin.cx = size;
				sizewin.cy = rc.bottom-rc.top;
				ptsplit.x = ptwin.x - split;
				ptsplit.y = ptwin.y;
				sizesplit.cx = split;
				sizesplit.cy = sizewin.cy;

				rc.right -= size + split;
			}
			else if (SSP_DOCKTOP & style)
			{
				ptwin.x = rc.left;
				ptwin.y = rc.top;
				sizewin.cx = rc.right-rc.left;
				sizewin.cy = size;
				ptsplit.x = ptwin.x;
				ptsplit.y = ptwin.y + sizewin.cy;
				sizesplit.cx = sizewin.cx;
				sizesplit.cy = split;

				rc.top += size + split;
			}
			else if (SSP_DOCKBOTTOM & style)
			{
				ptwin.x = rc.left;
				ptwin.y = rc.bottom - size;
				sizewin.cx = rc.right-rc.left;
				sizewin.cy = size;
				ptsplit.x = ptwin.x;
				ptsplit.y = ptwin.y - split;
				sizesplit.cx = sizewin.cx;
				sizesplit.cy = split;

				rc.bottom -= size + split;
			}

			if (!action(wnd, ptwin, sizewin, ptsplit, sizesplit))
				break;
		}
	}
	void RecalcLayout()
	{
		T* pT = static_cast<T*>(this);
		CWindow self(pT->m_hWnd);

		RECT rc;
		ATLVERIFY(self.GetClientRect(&rc));

		CSimpleArray<CWindow> docked;
		CSimpleArray<CWindow> aligned;
		CSimpleArray<CWindow> filled;

		CWindow wnd = self.GetWindow(GW_CHILD);
		while (wnd.IsWindow())
		{
			if (WS_VISIBLE & wnd.GetStyle())
			{
				int style;
				ATLVERIFY(GetPaneStyle(wnd, style));

				if (!(SSP_IGNORE & style))
				{
					if (SSP_DOCKMASK & style)
						ATLVERIFY(docked.Add(wnd));
					else if (SSP_DOCKFILL & style)
						ATLVERIFY(filled.Add(wnd));
					else
						ATLVERIFY(aligned.Add(wnd));
				}
			}

			wnd = wnd.GetWindow(GW_HWNDNEXT);
		}

		HDWP hdwp = ::BeginDeferWindowPos(docked.GetSize() + 
			filled.GetSize() + aligned.GetSize());

		ProcessDocked(docked, rc, CDockResize(pT, hdwp));

		for (int i=0; i<filled.GetSize(); i++)
		{
			CWindow wnd = filled[i];
			hdwp = pT->SetPanePos(hdwp, wnd, rc);
		}

		for (int i=0; i<aligned.GetSize(); i++)
		{
			CWindow wnd = aligned[i];
			RECT rc2;
			if (!CalcPaneAlign(wnd, rc, rc2))
				continue;
			hdwp = pT->SetPanePos(hdwp, wnd, rc2);
		}

		::EndDeferWindowPos(hdwp);

		pT->OnRecalcLayout();
	}
	HDWP SetPanePos(HDWP hdwp, CWindow wnd, const RECT& rc)
	{
		return wnd.DeferWindowPos(hdwp, NULL, rc.left, rc.top, 
			rc.right-rc.left, rc.bottom-rc.top, SWP_NOZORDER|SWP_NOACTIVATE);
	}
	void OnRecalcLayout()
	{
	}
public:
	bool SaveLayout(HKEY hkey, LPCTSTR szkey)
	{
		//delete old layout record
		CRegKey key_tmp;
		if (ERROR_SUCCESS != key_tmp.Open(hkey, NULL, KEY_WRITE))
			return false;

		key_tmp.RecurseDeleteKey(szkey);

		CRegKey root;
		if (ERROR_SUCCESS != root.Create(hkey, szkey))
			return false;

		CSimpleArray<CWindow> sa;
		GetPanesByStyle(sa, -1, false);

		root.SetDWORDValue(_T("count"), sa.GetSize());
		for (int i=0; i<sa.GetSize(); i++)
		{
			CWindow wnd = sa[i];

			T* pT = static_cast<T*>(this);
			PaneInfoStruct* pane = pT->GetPaneInfo(wnd);
			ATLASSERT(NULL != pane);
			if (NULL == pane)
				return false;

			TCHAR name[32];
			wsprintf(name, "%d", wnd.GetDlgCtrlID());

			CRegKey key;
			if (ERROR_SUCCESS != key.Create(root, name))
				return false;

			key.SetDWORDValue(_T("style"), pane->style);
			key.SetDWORDValue(_T("width"), pane->size.cx);
			key.SetDWORDValue(_T("height"), pane->size.cy);
			key.SetDWORDValue(_T("align.left"), pane->align.left);
			key.SetDWORDValue(_T("align.right"), pane->align.right);
			key.SetDWORDValue(_T("align.top"), pane->align.top);
			key.SetDWORDValue(_T("align.bottom"), pane->align.bottom);
			key.SetDWORDValue(_T("bound.left"), pane->bound.left);
			key.SetDWORDValue(_T("bound.right"), pane->bound.right);
			key.SetDWORDValue(_T("bound.top"), pane->bound.top);
			key.SetDWORDValue(_T("bound.bottom"), pane->bound.bottom);

			DWORD visible = (WS_VISIBLE & wnd.GetStyle()) ? TRUE : FALSE;
			key.SetDWORDValue(_T("visible"), visible);
		}
		return true;
	}
	bool LoadLayout(HKEY hkey, LPCTSTR szkey, bool update)
	{
		CRegKey root;
		if (ERROR_SUCCESS != root.Open(hkey, szkey, KEY_READ))
			return false;

//		DWORD count;
//		if (ERROR_SUCCESS != root.QueryDWORDValue(_T("count"), count))
//			return false;
//
//		CSimpleArray<CWindow> sa;
//		GetPanesByStyle(sa, -1, false);
//
//		ATLASSERT(count == sa.GetSize());
//		if (count != sa.GetSize())
//			return false;

		T* pT = static_cast<T*>(this);
		CWindow self(pT->m_hWnd);

		TCHAR name[32]; DWORD cb = sizeof(name)/sizeof(name[0]);
		for (int i=0; ERROR_SUCCESS == root.EnumKey(i, name, &cb); 
			i++, cb = sizeof(name)/sizeof(name[0]))
		{
			CWindow wnd = self.GetDlgItem(StrToInt(name));
			if (!wnd.IsWindow())
				continue;

			T* pT = static_cast<T*>(this);
			PaneInfoStruct* pane = pT->GetPaneInfo(wnd);
			ATLASSERT(NULL != pane);
			if (NULL == pane)
				continue;

			CRegKey key;
			if (ERROR_SUCCESS != key.Open(root, name, KEY_READ))
				continue;

			key.QueryDWORDValue(_T("style"), (DWORD&)pane->style);
			key.QueryDWORDValue(_T("width"), (DWORD&)pane->size.cx);
			key.QueryDWORDValue(_T("height"), (DWORD&)pane->size.cy);
			key.QueryDWORDValue(_T("align.left"), (DWORD&)pane->align.left);
			key.QueryDWORDValue(_T("align.right"), (DWORD&)pane->align.right);
			key.QueryDWORDValue(_T("align.top"), (DWORD&)pane->align.top);
			key.QueryDWORDValue(_T("align.bottom"), (DWORD&)pane->align.bottom);
			key.QueryDWORDValue(_T("bound.left"), (DWORD&)pane->bound.left);
			key.QueryDWORDValue(_T("bound.right"), (DWORD&)pane->bound.right);
			key.QueryDWORDValue(_T("bound.top"), (DWORD&)pane->bound.top);
			key.QueryDWORDValue(_T("bound.bottom"), (DWORD&)pane->bound.bottom);

			DWORD visible = true;
			key.QueryDWORDValue(_T("visible"), visible);

			wnd.ShowWindow(visible ? SW_SHOWNA : SW_HIDE);
		}
		if (update)
			RecalcLayout();
		return true;
	}
};

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
Russian Federation Russian Federation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions