// 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;
}
};