Introduction
My other project is developing flexible windows frame based on flexible menu (see previous article RTSXMLParser here).

I chose to reuse RTSXML Parser.
How to Work
After seeing the pattern of window frame working, I decided to build my frames.xml look like this:
<frames>
<frame id="10000" name="stdframe" split="0" vk="ctrl+f1">
<view name="rtsview"></view>
</frame>
<frame id="10001" name="stdframe" split="1" vk="ctrl+f2">
<split id="1" cols="2" rows="1"></split>
<view name="rtsView " atid="1" atcol="0" atrow="0" size="50"></view>
<view name="rtsView " atid="1" atcol="1" atrow="0" size="50"></view>
</frame>
<frame id="10002" name="stdframe" split="1" vk="Ctrl+f3">
<split id="2" cols="1" rows="2"></split>
<split id="3" cols="2" rows="1" atid="2" atrow="0" atcol="0"></split>
<view name="rtsview " atid="2" atrow="1" atcol="0" size="50"></view>
<view name="rtsview " atid="3" atrow="0" atcol="0" size="50"></view>
<view name="rtsview " atid="3" atrow="0" atcol="1" size="50"></view>
</frame>
</frames>
frame id 10000 represents non-splitter window, 10001 represents single splitter window and last 10002 represents multiple splitter.
Implementation
And the next job becomes easier, I only need to create new class CXMLFrames which is inherited from CXMLWindow and implement Build() function which looks like this:
int CXMLFrames::Build(CRTSXMLNode *pNode)
{
CString szString;
CFrameItem *item;
CRTSXMLNode *child = pNode->GetFirstChild();
while (child)
{
if (child->m_sTitle == "frame")
{
item = new CFrameItem;
child->GetAttr("id", item->m_nMenuId);
child->GetAttr("name", item->m_szFrame);
child->GetAttr("split", item->m_nSplitFlag);
child->GetAttr("vk", item->m_szVirtualKey);
child->GetAttr("save", item->m_szSave);
m_pFrameItems.SetAt(item->m_nMenuId, item);
CRTSXMLNode *grandchild = child->GetFirstChild();
while (grandchild)
{
if (grandchild->m_sTitle == "split")
{
CSplitterItem *split = item->AddNewSplitter();
grandchild->GetAttr("id", split->m_nSplitId);
grandchild->GetAttr("cols", split->m_nCols);
grandchild->GetAttr("rows", split->m_nRows);
grandchild->GetAttr("atid", split->m_nParentId);
grandchild->GetAttr("atcol", split->m_nParentCol);
grandchild->GetAttr("atrow", split->m_nParentRow);
}
if (grandchild->m_sTitle == "view")
{
CViewItem *view = item->AddNewView();
grandchild->GetAttr("name", view->m_szName);
grandchild->GetAttr("param", view->m_szParam);
grandchild->GetAttr("atid", view->m_nSplitId);
grandchild->GetAttr("atcol", view->m_nCol);
grandchild->GetAttr("atrow", view->m_nRow);
grandchild->GetAttr("size", view->m_nSize);
}
dchild = child->GetNextChild();
}
}
id = pNode->GetNextChild();
}
return 1;
}
And develop frame class which looks like this:
BOOL CRTSFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
CViewItem *pViewItem;
CSplitterItem *pSplitItem;
CSplitterWnd *pWndSplitter, *pParentSplitter;
CRuntimeClass *pClass;
CString szError;
m_bActive = false;
CMainFrame *pParent = (CMainFrame *)m_pApp->m_pMainWnd;
item = m_pApp->m_pFrameManager.GetFrameItem(pParent->m_nMenuID);
if (!item)
{
szError.Format("Frame Not Available ID='%d'", pParent->m_nMenuID);
AfxMessageBox(szError, MB_OK|MB_ICONINFORMATION);
return false;
}
if (!item->m_nSplitFlag)
{
pViewItem = item->GetFirstView();
pClass = RUNTIME_CLASS(CRTSView);
pContext->m_pNewViewClass = pClass;
return CMDIChildWnd::OnCreateClient(lpcs, pContext);
}
pSplitItem = item->GetFirstSplitter();
while (pSplitItem)
{
pWndSplitter = new CSplitterWnd;
if (pSplitItem->m_nParentId == -1) pWndSplitter->CreateStatic(this,
pSplitItem->m_nRows, pSplitItem->m_nCols);
else
{
if (!m_pSplitters.Lookup(pSplitItem->m_nParentId,
(void *&)pParentSplitter))
{
pSplitItem = item->GetNextSplitter();
continue;
}
pWndSplitter->CreateStatic(pParentSplitter,
pSplitItem->m_nRows, pSplitItem->m_nCols,
WS_CHILD | WS_VISIBLE | WS_BORDER,
pParentSplitter->IdFromRowCol(pSplitItem->m_nParentRow,
pSplitItem->m_nParentCol));
}
m_pSplitters.SetAt(pSplitItem->m_nSplitId, pWndSplitter);_
pSplitItem = item->GetNextSplitter();
}
views pViewItem = item->GetFirstView();
while (pViewItem)
{
if (!m_pSplitters.Lookup(pViewItem->m_nSplitId,
(void *&)pWndSplitter))
{
pViewItem = item->GetNextView();
continue;
}
pWndSplitter->CreateView(pViewItem->m_nRow,
pViewItem->m_nCol, RUNTIME_CLASS(CRTSView),
CSize(0, 0), pContext);
pViewItem = item->GetNextView();
}
m_bActive = true;
return true;
}
Problem
Problem arises while I am developing WM_SIZE to resize all splitters and views; I can’t touch a part of splitter window which is used by another splitter. I am doing little tricks here:
void CRTSFrame::OnSize(UINT nType, int cx, int cy)
{
CSplitterWnd *pWndSplitter;
CViewItem *pViewItem;
CSplitterItem *pSplitItem;
int nHeight, nWidth;
CMDIChildWnd::OnSize(nType, cx, cy);
if (!m_bActive)
return;
if (!item)
return;
CRect pRect;
GetClientRect(pRect);
int nRectWidth = pRect.Width();
int nRectHeight = pRect.Height();
if (!nRectWidth || !nRectHeight)
return;
pViewItem = item->GetFirstView();
while (pViewItem)
{
if (!m_pSplitters.Lookup(pViewItem->m_nSplitId, (void *&)pWndSplitter))
{
pViewItem = item->GetNextView();
continue;
}
if (pViewItem->m_nSize == -1)
{
pViewItem = item->GetNextView();
continue;
}
if (pWndSplitter->GetRowCount() > 1)
{
nHeight = (int)((nRectHeight -
::GetSystemMetrics(SM_CYBORDER) * 2 - 8) * pViewItem->m_nSize/100);
pWndSplitter->SetRowInfo(pViewItem->m_nRow, nHeight, 1);
}
if (pWndSplitter->GetColumnCount() > 1)
{
nWidth = (int)((nRectWidth -
::GetSystemMetrics(SM_CXBORDER) * 2 - 8) * ewItem->m_nSize/100);
pWndSplitter->SetColumnInfo(pViewItem->m_nCol, nWidth, 1);
}
pViewItem = item->GetNextView();
}
int nSize;
pSplitItem = item->GetFirstSplitter();
while (pSplitItem)
{
if (pSplitItem->m_nParentId != -1)
{
nSize = 100;
pViewItem = item->GetFirstView();
while (pViewItem)
{
nSize -= pViewItem->m_nSize;
pViewItem = item->GetNextView();
}
if (!m_pSplitters.Lookup(pSplitItem->m_nParentId,
(void *&)pWndSplitter))
{
pSplitItem = item->GetNextSplitter();
continue;
}
if (pWndSplitter->GetRowCount() > 1)
{
nHeight = (int)((nRectHeight -
::GetSystemMetrics(SM_CYBORDER) * 2 - 8) * abs(nSize)/100);
pWndSplitter->SetRowInfo(pSplitItem->m_nParentRow, nHeight, 1);
}
if (pWndSplitter->GetColumnCount() > 1)
{
nWidth = (int)((nRectWidth -
::GetSystemMetrics(SM_CXBORDER) * 2 - 8) * abs(nSize)/100);
dSplitter->SetColumnInfo(pSplitItem->m_nParentCol, nWidth, 1);
}
pSplitItem = item->GetNextSplitter();
}
POSITION pos = m_pSplitters.GetStartPosition();
WORD nKey;
while (pos)
{
m_pSplitters.GetNextAssoc(pos, nKey, (void *&)pWndSplitter);
pWndSplitter->RecalcLayout();
}
}
}
In the first while block, I try to reach all created views and set size based on input.
In the next while block, I try to reach splitter panes which doesn’t have view. I am iterating all splitter windows which have a parent (so the parent must have no view in one of their panes).