// ===================================================================================
// Class Implementation : COXSizeDockBar
// ===================================================================================
// Header file : OXSizeDockBar.cpp
// Version: 9.3
// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office. For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
// Some portions Copyright (C)1994-5 Micro Focus Inc, 2465 East Bayshore Rd, Palo Alto, CA 94303.
// //////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <afxole.h>
#ifndef __OXMFCIMPL_H__
#if _MFC_VER >= 0x0700
#if _MFC_VER >= 1400
#include <afxtempl.h>
#endif
#include <..\src\mfc\afximpl.h>
#else
#include <..\src\afximpl.h>
#endif
#define __OXMFCIMPL_H__
#endif
#pragma warning(disable : 4355)
#include "OXSplitterRect.h" // used class specification
#include "OXRectTracker.h" // used class specification
#include "OXSizeCtrlBar.h" // used class specification
#include "OXSizeToolBar.h" // used class specification
#include "OXSizeDockBar.h" // this class specification
#include "OXSkins.h"
#include "OXMenuBar.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNAMIC(COXSizeDockBar, CDockBar)
#define new DEBUG_NEW
/////////////////////////////////////////////////////////////////////////////
// Definition of static members
// Data members -------------------------------------------------------------
// protected:
// private:
// Member functions ---------------------------------------------------------
// public:
/////////////////////////////////////////////////////////////////////////////
// CDockBar layout
const int IDC_DOCKTAB = 3000;
// helper which can acts on any array of windows
CControlBar* GetDockedControlBar(int nPos, const CPtrArray& arrBars)
{
CControlBar* pResult = (CControlBar*)arrBars[nPos];
if (HIWORD(pResult) == 0)
return NULL;
if(pResult!=NULL && !::IsWindow(pResult->m_hWnd))
return NULL;
return pResult;
}
COXSizeDockBar::COXSizeDockBar()
: m_hcurSizeNS(NULL),
m_hcurSizeWE(NULL),
m_pSplitCapture(NULL),
m_hcurLast(NULL),
m_LayoutSize(0xffff,0xffff), // some values to force automatic resize
m_CountBars(0),
m_pDockbarSkin(NULL),
m_wndDockTabCtrl(this)
{
}
COXSizeDockBar::~COXSizeDockBar()
{
// delete any outstanding splitter rects
DeleteSplitterRects();
// delete the classic skin
if (m_pDockbarSkin != NULL)
delete m_pDockbarSkin;
}
BEGIN_MESSAGE_MAP(COXSizeDockBar, CDockBar)
//{{AFX_MSG_MAP(COXSizeDockBar)
ON_WM_PAINT()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_SETCURSOR()
ON_WM_NCPAINT()
ON_WM_CREATE()
ON_WM_SIZE()
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_SIZEPARENT, OnSizeParent)
END_MESSAGE_MAP()
#ifdef _DEBUG
void DumpArrayBars(CDumpContext& dc, const CPtrArray& arrBars)
{
for (int nPos = 0; nPos < arrBars.GetSize(); nPos++)
{
LPVOID pVoid = arrBars[nPos];
dc << _T(" [") << nPos << _T("]") << pVoid;
CControlBar* pBar = ::GetDockedControlBar(nPos, arrBars);
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()))
{
CString strTitle;
pBar->GetWindowText(strTitle);
dc << _T(" ") << strTitle;
if (!pBar->IsVisible())
{
dc << _T(" hidden");
}
}
dc << _T("\n");
}
}
void COXSizeDockBar::Dump( CDumpContext& dc ) const
{
CDockBar::Dump(dc);
DumpArrayBars(dc, m_arrBars);
// now go through the splitter array.
int nDepth = dc.GetDepth();
dc.SetDepth(1);
m_SplitArr.Dump(dc);
dc.SetDepth(nDepth);
}
#endif
/////////////////////////////////////////////////////////////////////////////
// COXSizeDockBar message handlers
// most of this code is copied from MFC CDockBar - with additional comments to help
// my understanding of it. The basic idea is that our DockBar is being asked how big
// it is. To find out, it looks at the bars inside it
CSize COXSizeDockBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
{
ASSERT_VALID(this);
CSize sizeFixed = CControlBar::CalcFixedLayout(bStretch, bHorz);
if(m_arrBars.GetSize()<=1)
{
return sizeFixed;
}
// ID for this bar...used to set MRU docked position
UINT uDockBarID = PtrToUint(::GetWindowLongPtr(m_hWnd, GWL_ID));
// prepare for layout
AFX_SIZEPARENTPARAMS layout;
layout.hDWP= (m_bLayoutQuery)? NULL : ::BeginDeferWindowPos(PtrToInt(m_arrBars.GetSize()));
CPoint pt(-afxData.cxBorder2, -afxData.cyBorder2);
// get max size
CSize sizeMax;
if (!m_rectLayout.IsRectEmpty())
{
sizeMax = m_rectLayout.Size();
}
else
{
CRect rectFrame;
CFrameWnd* pFrame = GetParentFrame();
pFrame->GetClientRect(rectFrame);
pFrame->ClientToScreen(rectFrame);
for(int nID = AFX_IDW_CONTROLBAR_FIRST; nID<=AFX_IDW_CONTROLBAR_LAST; nID++)
{
CControlBar* pBar=m_pDockSite->GetControlBar(nID);
if(pBar!=NULL && pBar->IsDockBar() &&
pBar->IsVisible() && !pBar->IsFloating())
{
CRect rectBar;
pBar->GetWindowRect(rectBar);
if(pBar->GetStyle() & CBRS_TOP)
{
rectFrame.top=__max(rectFrame.top,rectBar.bottom);
}
else if(pBar->GetStyle() & CBRS_BOTTOM)
{
rectFrame.bottom=__min(rectFrame.bottom,rectBar.top);
}
}
}
sizeMax = rectFrame.Size();
}
// true if we should draw a bar for this row
BOOL bDrawBarForRow = FALSE;
DeleteSplitterRects(); // clear the splitter rects
int nWidth = 0;
int nFirstSplitterInRow = 0;
int nFirstPaneInRow = 0;
BOOL bFirstPaneInRow = TRUE;
int nCountSizeBars = 0;
BOOL bLastWasResizable=FALSE;
BOOL bWrapped = FALSE;
// layout all the control bars
int nLastVisSep = -1;
for (int nPos = 0; nPos < m_arrBars.GetSize(); nPos++)
{
void* pVoid = m_arrBars[nPos];
CControlBar* pBar = GetDockedControlBar(nPos);
if (pVoid != NULL)
{
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()) && pBar->IsVisible())
{
BOOL bSomeToolBar = (BOOL) (((CControlBar*)pBar)->
IsKindOf(RUNTIME_CLASS(COXSizeToolBar)));
if (bFirstPaneInRow)
{
bFirstPaneInRow = FALSE;
// remember where the first pane in the row is..
nFirstPaneInRow = nPos;
if (m_dwStyle & (CBRS_ALIGN_BOTTOM | CBRS_ALIGN_RIGHT))
{
bDrawBarForRow = IsRowSizeable(nPos) && !bSomeToolBar;
}
if (bDrawBarForRow)
{
if (bHorz)
{
AddSplitterRect(SPLITTER_HORZ, pt.x, pt.y, 0 ,
pt.y + CY_SPLIT, nPos); // width set at end
pt.y += CY_SPLIT;
}
else
{
AddSplitterRect(SPLITTER_VERT, pt.x, pt.y,
pt.x + CX_SPLIT, 0, nPos); // height set at end
pt.x += CX_SPLIT;
}
bDrawBarForRow = FALSE;
}
}
// draw a bar between 2 previous
if (bLastWasResizable && IsSizeable(pBar)&!bSomeToolBar)
{
if (bHorz)
{
// width set at end of row
AddSplitterRect(SPLITTER_VERT, pt.x, pt.y,
pt.x + CX_SPLIT, 0, nPos);
pt.x += CX_SPLIT;
}
else
{
// width set at end of row
AddSplitterRect(SPLITTER_HORZ, pt.x, pt.y, 0,
pt.y + CY_SPLIT, nPos);
pt.y += CY_SPLIT;
}
}
// if the bar is sizeable, then we'll need a sizer after it
if (IsSizeable(pBar))
{
bDrawBarForRow|=!bSomeToolBar;
}
// get ideal rect for bar to be docked
DWORD dwMode = 0;
if ((pBar->m_dwStyle & CBRS_SIZE_DYNAMIC) &&
(pBar->m_dwStyle & CBRS_FLOATING))
dwMode |= LM_HORZ | LM_MRUWIDTH;
else if (pBar->m_dwStyle & CBRS_ORIENT_HORZ)
dwMode |= LM_HORZ | LM_HORZDOCK;
else
dwMode |= LM_VERTDOCK;
CSize sizeBar = pBar->CalcDynamicLayout(-1, dwMode);
CRect rect(pt, sizeBar);
// get current rect for bar to be docked
CRect rectBar;
pBar->GetWindowRect(&rectBar);
ScreenToClient(&rectBar);
if (bHorz)
{
if(IsSizeable(pBar) && !bSomeToolBar)
{
// This is a control bar
// If ControlBar has been wrapped, then left justify
if (bWrapped)
{
bWrapped = FALSE;
rect.OffsetRect(-(rect.left + afxData.cxBorder2), 0);
}
else if(rect.left>=sizeMax.cx-afxData.cxBorder2 ||
rectBar.right<=rectBar.left || rectBar.right<=0)
{
if(nFirstPaneInRow==nPos)
{
m_arrBars.InsertAt(nPos+1, (CObject*)NULL);
AdjustRowSizes(nPos,sizeMax.cx,m_arrBars);
AdjustRowSizes(nPos+2,sizeMax.cx,m_arrBars);
pBar = NULL;
pVoid = NULL;
bWrapped = TRUE;
nPos--;
}
else
{
m_arrBars.InsertAt(nPos, (CObject*)NULL);
AdjustRowSizes(nPos+1,sizeMax.cx,m_arrBars);
pBar = NULL;
pVoid = NULL;
bWrapped = TRUE;
}
}
}
else // this is a toolbar
{
// Offset Calculated Rect out to Actual
if (rectBar.left > rect.left && !m_bFloating)
{
if(!bLastWasResizable && nCountSizeBars==0)
rect.OffsetRect(rectBar.left - rect.left, 0);
}
// If ControlBar goes off the right, then right justify
if (rect.right > sizeMax.cx && !m_bFloating)
{
int x = rect.Width() - afxData.cxBorder2;
x = max(sizeMax.cx - x, pt.x);
rect.OffsetRect(x - rect.left, 0);
}
// If ControlBar has been wrapped, then left justify
if (bWrapped)
{
bWrapped = FALSE;
rect.OffsetRect(-(rect.left + afxData.cxBorder2), 0);
}
// If ControlBar is completely invisible, then wrap it
else if ((rect.left >= (sizeMax.cx - afxData.cxBorder2)) &&
(nPos > 0) && (m_arrBars[nPos - 1] != NULL))
{
m_arrBars.InsertAt(nPos, (CObject*)NULL);
pBar = NULL; pVoid = NULL;
bWrapped = TRUE;
}
}
if (!bWrapped)
{
if (rect != rectBar)
{
if (!m_bLayoutQuery &&
!(pBar->m_dwStyle & CBRS_FLOATING))
{
pBar->m_pDockContext->m_rectMRUDockPos = rect;
}
AfxRepositionWindow(&layout, pBar->m_hWnd, &rect);
}
pt.x = rect.left + sizeBar.cx - afxData.cxBorder2;
nWidth = max(nWidth, sizeBar.cy);
}
}
else // vertical
{
if(IsSizeable(pBar) && !bSomeToolBar)
{
// If ControlBar has been wrapped, then top justify
if (bWrapped)
{
bWrapped = FALSE;
rect.OffsetRect(0, -(rect.top + afxData.cyBorder2));
}
else if(rect.top>=sizeMax.cy-afxData.cyBorder2 ||
rectBar.bottom<=rectBar.top || rectBar.bottom<=0)
{
if(nFirstPaneInRow==nPos)
{
m_arrBars.InsertAt(nPos+1, (CObject*)NULL);
AdjustRowSizes(nPos,sizeMax.cy,m_arrBars);
AdjustRowSizes(nPos+2,sizeMax.cy,m_arrBars);
pBar = NULL;
pVoid = NULL;
bWrapped = TRUE;
nPos--;
}
else
{
m_arrBars.InsertAt(nPos, (CObject*)NULL);
AdjustRowSizes(nPos+1,sizeMax.cy,m_arrBars);
pBar = NULL;
pVoid = NULL;
bWrapped = TRUE;
}
}
}
else
{
// Offset Calculated Rect out to Actual
if (rectBar.top > rect.top && !m_bFloating)
{
if(!bLastWasResizable && nCountSizeBars==0)
rect.OffsetRect(0, rectBar.top - rect.top);
}
// If ControlBar goes off the bottom, then bottom justify
if (rect.bottom > sizeMax.cy && !m_bFloating)
{
int y = rect.Height() - afxData.cyBorder2;
y = max(sizeMax.cy - y, pt.y);
rect.OffsetRect(0, y - rect.top);
}
// If ControlBar has been wrapped, then top justify
if (bWrapped)
{
bWrapped = FALSE;
rect.OffsetRect(0, -(rect.top + afxData.cyBorder2));
}
// If ControlBar is completely invisible, then wrap it
else if ((rect.top >= (sizeMax.cy - afxData.cyBorder2)) &&
(nPos > 0) && (m_arrBars[nPos - 1] != NULL))
{
m_arrBars.InsertAt(nPos, (CObject*)NULL);
pBar = NULL; pVoid = NULL;
bWrapped = TRUE;
}
}
if (!bWrapped)
{
if (rect != rectBar)
{
if (!m_bLayoutQuery &&
!(pBar->m_dwStyle & CBRS_FLOATING))
{
pBar->m_pDockContext->m_rectMRUDockPos = rect;
}
AfxRepositionWindow(&layout, pBar->m_hWnd, &rect);
}
pt.y = rect.top + sizeBar.cy - afxData.cyBorder2;
nWidth = max(nWidth, sizeBar.cx);
}
}
/////////////////////////
if(pBar!=NULL)
{
// repositioned the bar, so update the MRU dock position.
CDockContext* pDockContext = pBar->m_pDockContext;
ASSERT(pDockContext != NULL);
if (pDockContext != NULL)
{
pDockContext->m_rectMRUDockPos = rect;
pDockContext->m_uMRUDockID = uDockBarID;
}
// handle any delay/show hide for the bar
pBar->RecalcDelayShow(&layout);
bLastWasResizable=IsSizeable(pBar)&!bSomeToolBar;
}
}
}
else
{
nCountSizeBars=0;
// calculate the number of sizable bars in the row
for (int nPos2 = nPos + 1; nPos2 < m_arrBars.GetSize(); nPos2++)
{
void* pVoid = m_arrBars[nPos2];
CControlBar* pBar = GetDockedControlBar(nPos2);
if (pVoid == NULL)
break; // end of row
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()) &&
pBar->IsVisible())
{
if (IsSizeable(pBar) && !((CControlBar*)pBar)->
IsKindOf(RUNTIME_CLASS(COXSizeToolBar)))
{
nCountSizeBars++;
}
}
}
////////////////////////////////////
if (!bFirstPaneInRow) // FALSE if we've hit anything....
{
// end of row because pBar == NULL
if (bHorz)
{
pt.y += nWidth - afxData.cyBorder2;
sizeFixed.cx = __max(sizeFixed.cx, pt.x);
sizeFixed.cy = __max(sizeFixed.cy, pt.y);
pt.x = -afxData.cxBorder2;
SetSplitterSizeInRange(nFirstSplitterInRow, SPLITTER_VERT, pt.y);
}
else
{
pt.x += nWidth - afxData.cxBorder2;
sizeFixed.cx = __max(sizeFixed.cx, pt.x);
sizeFixed.cy = __max(sizeFixed.cy, pt.y);
pt.y = -afxData.cyBorder2;
SetSplitterSizeInRange(nFirstSplitterInRow, SPLITTER_HORZ, pt.x);
}
nLastVisSep = nPos; // record separator for last vis position
}
nFirstSplitterInRow = PtrToInt(__max(m_SplitArr.GetSize(), 0));
nWidth = 0;
bFirstPaneInRow = TRUE;
bLastWasResizable=FALSE;
}
}
// special case when bars are at top or left.
// use of nFirstPaneInRow (nPos where row started) so that sizing code can cope ok
if (nFirstPaneInRow != 0 && bDrawBarForRow &&
(m_dwStyle & (CBRS_ALIGN_TOP | CBRS_ALIGN_LEFT))) // there is at least one pane.
{
ASSERT(nLastVisSep != -1);
if (m_dwStyle & CBRS_ALIGN_TOP)
{
AddSplitterRect(SPLITTER_HORZ, pt.x, pt.y, 0 ,
pt.y + CY_SPLIT, nLastVisSep);
sizeFixed.cy += CY_SPLIT;
}
else
{
AddSplitterRect(SPLITTER_VERT, pt.x, pt.y,
pt.x + CX_SPLIT, 0, nLastVisSep);
sizeFixed.cx += CX_SPLIT - 1;
}
}
if (!m_bLayoutQuery)
{
// move and resize all the windows at once!
if (layout.hDWP == NULL || !::EndDeferWindowPos(layout.hDWP))
{
TRACE(_T("COXSizeDockBar::CalcFixedLayout: DeferWindowPos failed - low system resources\n"));
}
}
// Finally go back and set the size of the bars between the rows
if (bHorz)
{
// set widths of inter-rows
SetSplitterSizeInRange(0, SPLITTER_HORZ, sizeFixed.cx);
}
else
{
// set heights of inte-rcolumns
SetSplitterSizeInRange(0, SPLITTER_VERT, sizeFixed.cy);
}
// adjust size for borders on the dock bar itself
CRect rect;
rect.SetRectEmpty();
CalcInsideRect(rect, bHorz);
if ((!bStretch || !bHorz) && sizeFixed.cx != 0)
{
sizeFixed.cx += -rect.right + rect.left;
}
if ((!bStretch || bHorz) && sizeFixed.cy != 0)
{
sizeFixed.cy += -rect.bottom + rect.top;
}
// Allocate space for the tab container
sizeFixed.cy += GetTabHeight();
return sizeFixed;
}
void COXSizeDockBar::AddSplitterRect(int type, int x1, int y1, int x2, int y2, int nPos)
{
COXSplitterRect* pSplit = new COXSplitterRect(type, CRect(x1, y1, x2, y2));
pSplit->m_nPos = nPos;
ASSERT( pSplit != NULL);
m_SplitArr.Add(pSplit);
}
// helper function: Sets the length of all COXSplitterRects in the range
// (start->end of array) with the specified type. Used at the end of a row
// to set all the heights to the calculated width.
void COXSizeDockBar::SetSplitterSizeInRange(int start, int type, int length)
{
ASSERT(type == SPLITTER_VERT || type == SPLITTER_HORZ);
ASSERT(start >= 0 && start <= m_SplitArr.GetSize());
for (int i = PtrToInt(m_SplitArr.GetUpperBound()); i >= start; i--)
{
COXSplitterRect* pItem = (COXSplitterRect*)m_SplitArr[i];
if (pItem->m_type == type)
{
if (type == SPLITTER_VERT)
pItem->m_rect.bottom = length;
else
pItem->m_rect.right = length;
}
}
}
void COXSizeDockBar::DeleteSplitterRects()
{
for (int i = PtrToInt(m_SplitArr.GetUpperBound()); i >= 0 ; i--)
delete (m_SplitArr[i]);
m_SplitArr.RemoveAll();
}
void COXSizeDockBar::OnPaint()
{
CPaintDC dc(this); // device context for painting
for(int nIndex=PtrToInt(m_SplitArr.GetUpperBound()); nIndex>=0; nIndex--)
{
COXSplitterRect* pSplit=(COXSplitterRect*)m_SplitArr[nIndex];
GetDockbarSkin()->DrawSplitter(&dc, pSplit, this);
}
}
BOOL COXSizeDockBar::OnEraseBkgnd(CDC* pDC)
{
GetDockbarSkin()->DrawBackground(pDC, this);
return FALSE;
}
COXSplitterRect* COXSizeDockBar::HitTest(CPoint pt)
{
for (int i = PtrToInt(m_SplitArr.GetUpperBound()); i >= 0; i--)
{
COXSplitterRect* pSplit = GetSplitter(i);
if (pSplit->m_rect.PtInRect(pt))
{
return(pSplit);
}
}
return NULL;
}
BOOL COXSizeDockBar::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if(nHitTest==HTCLIENT && pWnd==this) // && !m_bTracking)
{
return TRUE; // we will handle it in the mouse move
}
return CDockBar::OnSetCursor(pWnd, nHitTest, message);
}
// if already positioned on NULL, go back one further
int COXSizeDockBar::StartPosOfRow(int nPos)
{
if (nPos > 0)
{
if (m_arrBars[nPos] == NULL)
nPos --;
while (nPos >= 0)
{
if (m_arrBars[nPos] == NULL)
return (nPos + 1);
nPos --;
}
}
return 0;
}
// returns strart of previous row.
// This function includes logic to cope with nPos beyond the end of the array.
int COXSizeDockBar::StartPosOfPreviousRow(int nPos)
{
ASSERT (nPos > 0);
if (nPos >= m_arrBars.GetUpperBound())
return (StartPosOfRow(nPos));
else
return StartPosOfRow(nPos - 1);
}
// Go through control bars in the row.
// returns the number of sizeable bars in the row, and the total space they currently
// take up
void COXSizeDockBar::GetRowSizeInfo(int nPos, ROWSIZEINFO* pRZI,
const CPtrArray& arrBars)
{
BOOL bHorz = IsBarHorizontal();
// zero all the fields
memset (pRZI, 0, sizeof (ROWSIZEINFO));
BOOL bLastWasResizable=FALSE;
while (nPos <= arrBars.GetUpperBound())
{
CRect rect;
void* pVoid = arrBars[nPos];
if (pVoid == NULL)
{
break; // end of the row
}
CControlBar* pBar=::GetDockedControlBar(nPos, arrBars);
if (pBar!= NULL && ::IsWindow(pBar->GetSafeHwnd()) && pBar->IsVisible())
{
// add a splitter size
if((IsSizeable(pBar) &&
!pBar->IsKindOf(RUNTIME_CLASS(COXSizeToolBar))))
{
if(bLastWasResizable)
pRZI->nFixedWidth += (bHorz ? CX_SPLIT : CY_SPLIT);
}
pBar->GetWindowRect(&rect);
int nWidth = __max(0, (bHorz ? rect.Width() : rect.Height()) );
int nHeight = __max(0, (bHorz ? rect.Height() : rect.Width()) );
pRZI->nTotalBars ++;
if(nHeight > pRZI->nMaxHeight)
{
pRZI->nMaxHeight = nHeight;
}
MINMAXINFO minmaxInfo={ 0 };
pBar->SendMessage(WM_GETMINMAXINFO,0,(LPARAM)&minmaxInfo);
if(pRZI->nMinWidth<minmaxInfo.ptMinTrackSize.x)
{
pRZI->nMinWidth=minmaxInfo.ptMinTrackSize.x;
}
if(pRZI->nMinHeight<minmaxInfo.ptMinTrackSize.y)
{
pRZI->nMinHeight=minmaxInfo.ptMinTrackSize.y;
}
if(IsSizeable(pBar))
{
pRZI->nFlexBars ++;
pRZI->nFlexWidth += nWidth;
}
else
{
pRZI->nFixedWidth += nWidth;
if (nHeight > pRZI->nMaxFixedHeight)
pRZI->nMaxFixedHeight = nHeight;
}
bLastWasResizable=IsSizeable(pBar) &&
!pBar->IsKindOf(RUNTIME_CLASS(COXSizeToolBar));
}
nPos++;
}
pRZI->nTotalWidth=pRZI->nFixedWidth+pRZI->nFlexWidth;
return;
}
// Adjusts the sizes of all the bars on a dockbar to fit a new size
BOOL COXSizeDockBar::AdjustAllRowSizes(int nNewSize)
{
BOOL bAdjusted = FALSE;
int nPos = 0;
while (nPos < m_arrBars.GetSize())
{
CControlBar* pBar = (CControlBar*)m_arrBars[nPos];
if (pBar == NULL)
{
// skip over NULLs
nPos++;
continue;
}
// adjust the sizes on a row
bAdjusted |= AdjustRowSizes(nPos, nNewSize, m_arrBars);
// skip to end of row
while (m_arrBars[nPos] != NULL)
nPos++;
}
return bAdjusted;
}
// Adjusts the size of a row - returns TRUE if any changes were made
BOOL COXSizeDockBar::AdjustRowSizes(int nPos, int nNewSize, CPtrArray& arrBars)
{
BOOL bHorz = IsBarHorizontal();
ROWSIZEINFO RZI;
GetRowSizeInfo(nPos, &RZI, arrBars);
if (RZI.nFlexBars == 0)
return FALSE; // no flexible bars - nothing to do !
// prepare for layout
int nTotalSize = (nNewSize - RZI.nFixedWidth)
+(bHorz ? afxData.cxBorder2 : afxData.cyBorder2)*RZI.nTotalBars;
int nSizeRemaining = nTotalSize;
int nSize=0;
// have to apply this size change to the bars on this row. Note: This will work
// by setting the docked size of the controls bars directly.
// Then RecalcLayout will do the rest.
int nCountFlexBars = 0;
while (TRUE)
{
void* pVoid = arrBars[nPos];
if (pVoid == NULL)
break; // end of the row, stop
// note:slight abuse of cast
COXSizeControlBar* pBar =
(COXSizeControlBar*)::GetDockedControlBar(nPos, arrBars);
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()) &&
pBar->IsVisible() && IsSizeable(pBar))
{
CRect rect;
pBar->GetWindowRect(&rect);
int nWidth = (bHorz ? rect.Width() : rect.Height());
nCountFlexBars ++;
if(RZI.nFlexWidth==0)
nSize = nSizeRemaining;
else
nSize = (int)((float)(((nWidth * nTotalSize)) / RZI.nFlexWidth) + (float) 0.5);
#ifdef _VERBOSE_TRACE
CString strTitle;
pBar->GetWindowText(strTitle);
TRACE2("New Size : %d on %s\n", nSize, strTitle);
#endif
nSizeRemaining -= nSize;
if (bHorz)
{
pBar->m_HorzDockSize.cx = nSize;
pBar->m_HorzDockSize.cy = RZI.nMaxHeight;
if(!m_bLayoutQuery)
SetWindowSize(pBar, pBar->m_HorzDockSize);
}
else
{
pBar->m_VertDockSize.cy = nSize;
pBar->m_VertDockSize.cx = RZI.nMaxHeight;
if(!m_bLayoutQuery)
SetWindowSize(pBar, pBar->m_VertDockSize);
}
}
nPos++;
}
return TRUE;
}
// Adjusts the sizes of all the bars on a dockbar to fit a new size
void COXSizeDockBar::TileDockedBars()
{
int nPos = 0;
while (nPos < m_arrBars.GetSize())
{
CControlBar* pBar = (CControlBar*)m_arrBars[nPos];
if (pBar == NULL)
{ // skip over NULLs
nPos ++;
continue;
}
TileDockedBarsRow(nPos); // adjust the sizes on a row
while (m_arrBars[nPos] != NULL) // skip to end of row
nPos++;
}
return;
}
// Tiles the docked bars:
void COXSizeDockBar::TileDockedBarsRow(int nPos)
{
BOOL bHorz = IsBarHorizontal();
ROWSIZEINFO RZI;
GetRowSizeInfo(nPos, &RZI, m_arrBars);
if (RZI.nFlexBars == 0)
return; // no flexible bars - nothing to do !
int nNewSize = (bHorz ? m_LayoutSize.cx : m_LayoutSize.cy);
int nTotalSize = __max(0, nNewSize - RZI.nFixedWidth);
int nNewWidth = nTotalSize / RZI.nFlexBars;
int nCountFlexBars = 0;
while(TRUE)
{
void* pVoid = m_arrBars[nPos];
if (pVoid == NULL)
break; // end of the row, stop
COXSizeControlBar* pBar = (COXSizeControlBar*)GetDockedControlBar(nPos); // note:slight abuse of cast
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()) &&
IsSizeable(pBar) && pBar->IsVisible())
{
nCountFlexBars ++;
if (nCountFlexBars == RZI.nFlexBars) // last bar adjust size change
{
nNewWidth = nTotalSize - ((RZI.nFlexBars - 1) * nNewWidth);
}
if (bHorz)
{
pBar->m_HorzDockSize.cx = nNewWidth;
pBar->m_HorzDockSize.cy = RZI.nMaxHeight;
}
else
{
pBar->m_VertDockSize.cy = nNewWidth;
pBar->m_VertDockSize.cx = RZI.nMaxHeight;
}
}
nPos++;
}
}
// Returns TRUE if BAR is in m_arrInvisibleBars
BOOL COXSizeDockBar::WasBarHidden(CControlBar* pBar)
{
for (int i= 0; i < m_arrHiddenBars.GetSize(); i++)
{
if (m_arrHiddenBars[i] == pBar)
return TRUE;
}
return FALSE;
}
// WM_SIZEPARENT message is sent from CFrameWnd::RepositionBars() to tell the
// dockbar to calculate it's size. The only reason for intercepting this was
// to actually find out the size the dockbar is taking up in the layout,
// so we can opt to re-layout a row to fit the desired size. There might well
// be a better way of doing this.
LRESULT COXSizeDockBar::OnSizeParent(WPARAM wParam, LPARAM lParam)
{
AFX_SIZEPARENTPARAMS* lpLayout = (AFX_SIZEPARENTPARAMS*)lParam;
BOOL bHorz = IsBarHorizontal();
CRect LayRect;
LayRect.CopyRect(&lpLayout->rect);
CSize LaySize = LayRect.Size(); // maximum size available
LaySize.cy -= GetTabHeight();
int nLayoutWidth = bHorz ? LaySize.cx : LaySize.cy;
BOOL bLayoutWidthChanged=
(nLayoutWidth != (bHorz ? m_LayoutSize.cx : m_LayoutSize.cy));
m_LayoutSize = LaySize;
// Attempt to detect bars that have changed state from Hidden->Visible.
// For these we attempt to adjust the other (previously visible) bars on
// the row so that the newly shown bars restore their previous size.
// Bars visible in the row (ones we can shrink)
CPtrArray arrVisibleBarsInRow;
int nWidthNeeded = 0;
int i = 0;
for (i = 0; i < m_arrBars.GetSize(); i++)
{
if (m_arrBars[i] == NULL)
{
ROWSIZEINFO RZI;
if (arrVisibleBarsInRow.GetSize() != 0 && nWidthNeeded != 0)
{
arrVisibleBarsInRow.Add(NULL);
GetRowSizeInfo(0, &RZI, arrVisibleBarsInRow);
int nNewWidth = __max(0, RZI.nTotalWidth - nWidthNeeded);
AdjustRowSizes(0, nNewWidth, arrVisibleBarsInRow);
}
nWidthNeeded = 0;
arrVisibleBarsInRow.RemoveAll();
}
else
{
CControlBar* pBar = GetDockedControlBar(i);
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()))
{
if (pBar->IsVisible())
{
if (WasBarHidden(pBar))
{
TRACE0("Bar hidden->visible\n");
CRect rect;
pBar->GetWindowRect(&rect);
nWidthNeeded += (bHorz ? rect.Width() : rect.Height());
}
else
{
arrVisibleBarsInRow.Add(pBar); // Track visible bars in this row that we can shrink
}
}
}
}
}
// construct new array of bars that are hidden in this dockbar
m_arrHiddenBars.RemoveAll();
for (i = 0; i < m_arrBars.GetSize(); i++)
{
CControlBar* pBar = GetDockedControlBar(i);
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()) && ! pBar->IsVisible())
m_arrHiddenBars.Add(pBar);
}
int nCheckSum = CheckSumBars();
// any other changes and we size the bars to fit the layout width
if (bLayoutWidthChanged || nCheckSum != m_CountBars)
{
AdjustAllRowSizes(nLayoutWidth);
m_CountBars = nCheckSum;
// force redraw of the dock bar - seems a bit of a sledgehammer
// InvalidateRect(NULL);
// force redraw of the dock bar - seems a bit of a sledgehammer
// RedrawWindow(NULL,NULL,RDW_FRAME|RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE);
// SetWindowPos(NULL,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_DRAWFRAME);
for(int i = PtrToInt(m_SplitArr.GetUpperBound()); i >= 0; i--)
InvalidateRect(((COXSplitterRect*)(m_SplitArr[i]))->m_rect);
}
// set m_bLayoutQuery to TRUE if lpLayout->hDWP == NULL
BOOL bLayoutQuery = m_bLayoutQuery;
m_bLayoutQuery = (lpLayout->hDWP == NULL);
LRESULT lResult = CDockBar::OnSizeParent(wParam, lParam);
// restore m_bLayoutQuery
m_bLayoutQuery = bLayoutQuery;
return lResult;
}
// Simple checksum for bars. Designed to spot the case when a bars moves within
// a dockrow.
int COXSizeDockBar::CheckSumBars() const
{
int nCount = 0; // total no of bars
int nCheckSum = 0; // XOR, power of 2 checksum
for (int i = 0; i < m_arrBars.GetSize(); i++)
{
if (m_arrBars[i] == NULL)
nCheckSum *= 2;
else
{
CControlBar* pBar = GetDockedControlBar(i);
ASSERT(pBar == NULL || pBar->IsKindOf(RUNTIME_CLASS(CControlBar)));
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()) && pBar->IsVisible())
{
nCheckSum++;
if(IsSizeable(pBar) && !pBar->IsKindOf(RUNTIME_CLASS(COXSizeToolBar)))
nCheckSum+=i;
nCount++;
}
}
}
// LSB = actual no of dockbars (limited to 256 !)
// bits 8-31 = checksum based on layout of rows.
return ((nCheckSum << 8) | (nCount & 0xff));
}
// Adjust sizes for specified newly added bar.
void COXSizeDockBar::AdjustForNewBar(CControlBar* pNewBar)
{
int nPos = FindBar(pNewBar);
ASSERT(nPos != -1); // bar should have been found.
// Go back to start of row.
while (m_arrBars[nPos] != NULL)
nPos--;
nPos++;
// create an array for the bars on the row, that aren't this one
CPtrArray arrOtherBarsInRow;
while (nPos < m_arrBars.GetSize() && m_arrBars[nPos] != NULL)
{
CControlBar* pBar = GetDockedControlBar(nPos);
if (pBar != pNewBar)
arrOtherBarsInRow.Add(pBar);
nPos++;
}
ROWSIZEINFO RZI;
arrOtherBarsInRow.Add(NULL);
GetRowSizeInfo(0, &RZI, arrOtherBarsInRow);
CRect rcNewBar;
pNewBar->GetWindowRect(&rcNewBar);
int nWidthNeeded = (IsBarHorizontal() ? rcNewBar.Width() : rcNewBar.Height());
if(IsSizeable(pNewBar) && RZI.nFlexBars>0)
{
nWidthNeeded+=(IsBarHorizontal() ? CX_SPLIT : CY_SPLIT);
}
int nNewWidth = __max(0, RZI.nTotalWidth - nWidthNeeded);
AdjustRowSizes(0, nNewWidth, arrOtherBarsInRow);
}
// Hit test the mouse position - and set cursor accordingly
COXSplitterRect* COXSizeDockBar::SetHitCursor(CPoint pt)
{
// Set up the split cursors here. This guarantees the app is around
if(m_hcurSizeWE==NULL)
{
m_hcurSizeWE=AfxGetApp()->LoadCursor(AFX_IDC_HSPLITBAR);
if(m_hcurSizeWE==NULL)
{
m_hcurSizeWE=::LoadCursor(NULL, IDC_SIZEWE);
}
}
if(m_hcurSizeNS==NULL)
{
m_hcurSizeNS=AfxGetApp()->LoadCursor(AFX_IDC_VSPLITBAR);
if(m_hcurSizeNS==NULL)
{
m_hcurSizeNS=::LoadCursor(NULL,IDC_SIZENS);
}
}
HCURSOR hcurNew=NULL;
COXSplitterRect* pSplit=HitTest(pt);
if(pSplit!=NULL)
{
hcurNew=(pSplit->m_type == SPLITTER_VERT ? m_hcurSizeWE : m_hcurSizeNS);
}
else
{
hcurNew=afxData.hcurArrow;
}
ASSERT(hcurNew!=NULL);
::SetCursor(hcurNew);
return pSplit;
}
void COXSizeDockBar::OnMouseMove(UINT nFlags, CPoint point)
{
SetHitCursor(point);
CDockBar::OnMouseMove(nFlags, point);
}
void COXSizeDockBar::OnLButtonDown(UINT /* nFlags */, CPoint point)
{
if (m_pSplitCapture == NULL)
{
m_pSplitCapture = SetHitCursor(point);
if (m_pSplitCapture != NULL)
{
StartTracking(point);
m_pSplitCapture = NULL;
}
}
}
void COXSizeDockBar::OnLButtonUp(UINT nFlags, CPoint point)
{
CDockBar::OnLButtonUp(nFlags, point);
}
void COXSizeDockBar::StartTracking(CPoint pt)
{
ASSERT(m_pSplitCapture != NULL);
// Some organizational flags: helps to cut down the cases
BOOL bHorz=IsBarHorizontal();
BOOL bVertSplitter=(m_pSplitCapture->m_type == SPLITTER_VERT);
BOOL bRowDivider=((!bVertSplitter) && bHorz) || (bVertSplitter && (!bHorz));
int nPos=m_pSplitCapture->m_nPos;
COXRectTracker MvRect;
// attempt to clip move rect by current layout size of the dockbar
CRect LayoutRect(CPoint(0,0), m_LayoutSize);
MvRect.m_rect=m_pSplitCapture->m_rect;
// Shrink the splitter rectangle to end up with a solid bar
if(bVertSplitter)
{
MvRect.m_rect.InflateRect(-2,0);
}
else
{
MvRect.m_rect.InflateRect(0,-2);
}
MvRect.m_rect.IntersectRect(MvRect.m_rect, LayoutRect);
ASSERT(!(MvRect.m_rect.IsRectEmpty()));
// get main window - all dragging is done relative to this window.
// this should be the frame window.....
CWnd* pClipWnd = GetParentFrame();
if (bVertSplitter)
{
MvRect.m_nStyle |= RectTracker_OnlyMoveHorz; // allow horizontal movement
}
else
{
MvRect.m_nStyle |= RectTracker_OnlyMoveVert; // allow vertical movement
}
// workout a limiting rectangle; - very dependent on orientation. Eventually may need to work
// out the fixed size of the windows beyond the current splitter, so it could get nasty.
// for now just use the client area of the window
ROWSIZEINFO RZI;
::ZeroMemory((void*)&RZI,sizeof(RZI));
CRect LimitRect;
pClipWnd->GetClientRect(&LimitRect);
pClipWnd->ClientToScreen(&LimitRect);
ScreenToClient(&LimitRect); // map to co-ords of pWnd
#ifdef _VERBOSE_TRACE
Dump(afxDump);
#endif
if(bRowDivider)
{
if (m_dwStyle & (CBRS_ALIGN_TOP | CBRS_ALIGN_LEFT)) // apply to previous row for top/bottom
{
nPos = StartPosOfPreviousRow(nPos);
ASSERT(nPos != 0);
}
GetRowSizeInfo(nPos,&RZI,m_arrBars); // get the row information:
switch (m_dwStyle & CBRS_ALIGN_ANY)
{
case CBRS_ALIGN_TOP:
LimitRect.top=__max(LimitRect.top,
MvRect.m_rect.top-(RZI.nMaxHeight-RZI.nMaxFixedHeight));
if(RZI.nMinHeight>0)
{
LimitRect.top=__max(LimitRect.top,
RZI.nMinHeight+MvRect.m_rect.top-RZI.nMaxHeight);
}
break;
case CBRS_ALIGN_BOTTOM:
LimitRect.bottom=__min(LimitRect.bottom,
MvRect.m_rect.top+(RZI.nMaxHeight-RZI.nMaxFixedHeight));
if(RZI.nMinHeight>0)
{
LimitRect.bottom=__min(LimitRect.bottom,
RZI.nMaxHeight-RZI.nMinHeight+CY_SPLIT+2*CY_BORDER);
}
break;
case CBRS_ALIGN_LEFT:
LimitRect.left=__max(LimitRect.left,
MvRect.m_rect.left-(RZI.nMaxHeight-RZI.nMaxFixedHeight));
if(RZI.nMinWidth>0)
{
LimitRect.left=__max(LimitRect.left,
RZI.nMinWidth+MvRect.m_rect.left-RZI.nMaxHeight);
}
break;
case CBRS_ALIGN_RIGHT:
LimitRect.right=__max(LimitRect.right,
MvRect.m_rect.left+(RZI.nMaxHeight-RZI.nMaxFixedHeight));
if(RZI.nMinWidth>0)
{
LimitRect.right=__min(LimitRect.right,RZI.nMaxHeight-RZI.nMinWidth);
}
break;
default:
ASSERT(FALSE);
break;
}
}
else
{
// How far can we go to down/right
int nFlexToRight, nFlexToLeft;
int nDownRight = ShrinkRowToRight(nPos, 16000, FALSE, &nFlexToRight);
int nUpLeft = ShrinkRowToLeft(nPos - 1, 16000, FALSE, &nFlexToLeft);
if ((nFlexToRight + nFlexToLeft) <= 1 ) // only 1 flex bar in the array - no movement !
{
nDownRight = 0;
nUpLeft = 0;
}
if (bHorz)
{
LimitRect.left = __max(LimitRect.left, MvRect.m_rect.left - nUpLeft);
LimitRect.right = __min(LimitRect.right, MvRect.m_rect.left + nDownRight);
}
else
{
LimitRect.top = __max(LimitRect.top , MvRect.m_rect.top - nUpLeft);
LimitRect.bottom = __min(LimitRect.bottom, MvRect.m_rect.top + nDownRight);
}
}
// Now enter the COXSplitterRect's modal track function
MvRect.m_LimitRect = LimitRect;
if (!MvRect.TrackFromHitTest (HTCAPTION, this, pt, pClipWnd))
return;
// Workout the size change cause by the drag:
int nSizeChange;
if (m_pSplitCapture->m_type == SPLITTER_VERT)
nSizeChange = MvRect.m_rect.left - MvRect.m_OrigRect.left;
else
nSizeChange = MvRect.m_rect.top - MvRect.m_OrigRect.top;
if (nSizeChange == 0)
return;
// COXSplitterRect::m_nPos is the pane position that the splitter was created at.
// For a row divider: this is the pane that immediately starts the next row
// For a column divider: this is the pane that is to the right of it.
// special case will be needed for the splitter used at the end of a left/top aligned
// dockbar.
int nSizeMoved;
if (bRowDivider)
{
if (m_dwStyle & (CBRS_ALIGN_TOP | CBRS_ALIGN_LEFT)) // apply to previous row for top/bottom
{
nSizeChange = -nSizeChange; // reverse polarity of change
}
int nNewHeight = __max(RZI.nMaxFixedHeight, RZI.nMaxHeight - nSizeChange);
// go along the rows applying size change to each bar in turn....
while (nPos < m_arrBars.GetSize()) // need to check size now
{
void* pVoid = m_arrBars[nPos];
if (pVoid == NULL)
break;
COXSizeControlBar* pBar = (COXSizeControlBar*)GetDockedControlBar(nPos);
// should check for visible ???
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()) &&
pBar->IsVisible() && IsSizeable(pBar))
{
if (bHorz)
pBar->m_HorzDockSize.cy = nNewHeight;
else
pBar->m_VertDockSize.cx = nNewHeight;
}
nPos ++;
}
}
else
{
if (nSizeChange < 0)
{
// move to left/up
nSizeMoved = ShrinkRowToLeft(nPos - 1, - nSizeChange, TRUE);
ShrinkRowToRight(nPos, - nSizeMoved, TRUE);
}
else
{
// move to right/down
nSizeMoved = ShrinkRowToRight(nPos, nSizeChange, TRUE);
ShrinkRowToLeft(nPos - 1, - nSizeMoved, TRUE);
}
}
// reposition the bars..
InvalidateRect(NULL);
ASSERT(pClipWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)));
((CFrameWnd*)pClipWnd)->RecalcLayout();
return;
}
// amount to shrink row to left.
// nPos = current pane: nPos -1 = pane to go for:
// return value = amount of space we actually sized
// bApply: if TRUE, apply changes to bar sizes
int COXSizeDockBar::ShrinkRowToLeft(int nPos, int nOrigAmount, BOOL bApply, int* pnFlexBars)
{
ASSERT(nPos >= 0 && nPos <= m_arrBars.GetSize());
int nAmount = nOrigAmount;
int nFlexBars = 0;
while (nPos >= 0)
{
if (m_arrBars[nPos] == NULL)
{
break;
}
COXSizeControlBar* pBar = (COXSizeControlBar*)GetDockedControlBar(nPos);
if (pBar!= NULL && ::IsWindow(pBar->GetSafeHwnd()) &&
IsSizeable(pBar) && pBar->IsVisible())
{
nFlexBars ++;
if (IsBarHorizontal())
{
if (pBar->m_HorzDockSize.cx >= nAmount)
{
if (bApply)
{
pBar->m_HorzDockSize.cx -= nAmount;
}
nAmount = 0;
break;
}
else
{
nAmount -= pBar->m_HorzDockSize.cx;
if (bApply)
{
pBar->m_HorzDockSize.cx = 0;
}
}
}
else
{
if (pBar->m_VertDockSize.cy >= nAmount)
{
if (bApply)
{
pBar->m_VertDockSize.cy -= nAmount;
}
nAmount = 0;
break;
}
else
{
if (bApply)
{
pBar->m_VertDockSize.cy = 0;
}
nAmount -= pBar->m_VertDockSize.cy;
}
}
}
nPos--;
}
// return no of flexible components encountered (if pointer supplied)
if (pnFlexBars != NULL)
{
*pnFlexBars = nFlexBars;
}
// reached left/top of row - return what size is still left to allocate
return (nOrigAmount - nAmount);
}
// amount to shrink row to right.
// nPos = current pane: nPos -1 = pane to go for:
// return value = amount of space we actually sized
int COXSizeDockBar::ShrinkRowToRight(int nPos, int nOrigAmount, BOOL bApply, int* pnFlexBars)
{
ASSERT(nPos >= 0 && nPos <= m_arrBars.GetSize());
int nAmount = nOrigAmount;
int nFlexBars = 0;
COXSizeControlBar* pLastBar = NULL;
while (nPos < m_arrBars.GetSize())
{
if (m_arrBars[nPos] == NULL)
break;
COXSizeControlBar* pBar = (COXSizeControlBar*)GetDockedControlBar(nPos);
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()))
{
pLastBar = pBar;
if (IsSizeable(pBar) && pBar->IsVisible())
{
nFlexBars ++;
if (IsBarHorizontal())
{
if (pBar->m_HorzDockSize.cx >= nAmount)
{
if (bApply)
pBar->m_HorzDockSize.cx -= nAmount;
nAmount = 0;
break;
}
else
{
nAmount -= pBar->m_HorzDockSize.cx;
if (bApply)
pBar->m_HorzDockSize.cx = 0;
}
}
else // Vertical
{
if (pBar->m_VertDockSize.cy >= nAmount)
{
if (bApply)
pBar->m_VertDockSize.cy -= nAmount;
nAmount = 0;
break;
}
else
{
nAmount -= pBar->m_VertDockSize.cy;
if (bApply)
pBar->m_VertDockSize.cy = 0;
}
}
}
}
nPos++;
}
// We've reached the end of the row. If we still have size left to find, the only way we can do it is if there
// is a flexble area at the end of the control bars..
if (nAmount > 0 && pLastBar != NULL)
{
int nSub;
CRect rect;
pLastBar->GetWindowRect(&rect);
ScreenToClient(&rect);
if (IsBarHorizontal())
nSub = m_LayoutSize.cx - rect.right;
else
nSub = m_LayoutSize.cy - rect.bottom;
nAmount -= __min(__max( 0, nSub), nAmount);
}
// return no of flexible components encountered (if pointer supplied)
if (pnFlexBars != NULL)
*pnFlexBars = nFlexBars;
// return amount allocated
return (nOrigAmount - nAmount);
}
// returns the first bar in the array - NULL if none
// used by the simplistic floating size routine
COXSizeControlBar* COXSizeDockBar::GetFirstControlBar()
{
// CMiniDockFrameWnd assumes that if there's only one bar, then it's at position 1
// in the array
// need to make a check for 0 sized array however
if (m_arrBars.GetSize() > 1)
return ((COXSizeControlBar*)GetDockedControlBar(1));
else
return NULL;
}
// returns TRUE if a CControlBar in the row is sizeable;
// intended to determine if the row should contain a splitter or not
BOOL COXSizeDockBar::IsRowSizeable(int nPos)
{
ASSERT(nPos >= 0 && nPos < m_arrBars.GetSize());
while (nPos < m_arrBars.GetSize())
{
if (m_arrBars[nPos] == NULL)
break;
CControlBar* pBar = GetDockedControlBar(nPos);
if (pBar!= NULL && ::IsWindow(pBar->GetSafeHwnd()) &&
IsSizeable(pBar) && pBar->IsVisible())
return TRUE;
nPos++;
}
return FALSE;
}
// Essentially the same as CDockBar::Insert(). Returns the position in the
// bar array that the object will be inserted.
// nPos = 0 => before first position... But will have to check if this dockbar
// is the same as the present one...(perhaps)
int COXSizeDockBar::TestInsertPosition(CControlBar* pBarIns, CRect rect)
{
CPoint ptMid(rect.left + rect.Width()/2, rect.top + rect.Height()/2);
// hang-on: Don't we want to work in client co-ords ???
ScreenToClient(&ptMid);
ASSERT_VALID(this);
ASSERT(pBarIns != NULL);
UNUSED(pBarIns);
int nPos = 0;
int nPosInsAfter = 0;
int nWidth = 0;
int nTotalWidth = 0;
BOOL bHorz = m_dwStyle & CBRS_ORIENT_HORZ ? TRUE : FALSE;
for (nPos = 0; nPos < m_arrBars.GetSize(); nPos++)
{
void* pVoid = m_arrBars[nPos];
CControlBar* pBar = GetDockedControlBar(nPos);
if (pVoid == NULL)
{
nTotalWidth += nWidth - afxData.cyBorder2;
nWidth = 0;
if ((bHorz ? ptMid.y : ptMid.x) < nTotalWidth)
{
if (nPos == 0) // ie in first section....
return 0; // indicate before first position....
return nPosInsAfter+1;
}
nPosInsAfter = nPos;
}
else
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()) && pBar->IsVisible())
{
CRect rectBar;
pBar->GetWindowRect(&rectBar);
ScreenToClient(&rectBar);
nWidth = __max(nWidth,
bHorz ? rectBar.Size().cy : rectBar.Size().cx - 1);
//if (bHorz ? rect.left > rectBar.left : rect.top > rectBar.top)
// don't need above test - only interested if it should go on the row or not...
nPosInsAfter = nPos;
}
}
return nPosInsAfter+1;
}
// returns no of bars that will be in the row (excluding the one to be inserted)
int COXSizeDockBar::BarsOnThisRow(CControlBar* pBarIns, CRect rect)
{
int nPos = TestInsertPosition(pBarIns, rect);
// if inserting before the first row, or after the last row, then return 0
// (there are no bars on this row).
if (nPos == 0 ||nPos > m_arrBars.GetUpperBound()) // case if inserting before first bar in the array.
return 0; // return 0 to use the full size
// go back to start of row.
while (nPos != 0 && m_arrBars[nPos - 1] != 0)
nPos --;
int nCount = 0;
while (TRUE)
{
void* pVoid = m_arrBars[nPos];
CControlBar* pBar = GetDockedControlBar(nPos);
if (pVoid == NULL)
break;
if (pBar!=NULL && ::IsWindow(pBar->GetSafeHwnd()) && pBar != pBarIns)
nCount++;
nPos++;
}
return nCount;
}
void COXSizeDockBar::ResizeBar(COXSizeControlBar* pBar, BOOL bMaximize)
{
ASSERT(pBar!=NULL);
ASSERT(pBar->CanResize());
CRect rect;
pBar->GetWindowRect(rect);
int nPos=TestInsertPosition(pBar,rect);
// go back to start of row.
while (nPos != 0 && m_arrBars[nPos - 1] != 0)
nPos --;
// Defines whether we should save the sizes of the other bars in the
// row/column or not
//
BOOL bSaveSize=TRUE;
if(bMaximize)
{
int nPosCopy=nPos;
while(TRUE)
{
void* pVoid = m_arrBars[nPos];
if (pVoid == NULL)
break; // end of the row, stop
CControlBar* pControlBar = GetDockedControlBar(nPos);
if(pBar!=pControlBar && pControlBar!=NULL &&
pControlBar->IsKindOf(RUNTIME_CLASS(COXSizeControlBar)) &&
::IsWindow(pControlBar->GetSafeHwnd()) &&
IsSizeable(pControlBar) && pControlBar->IsVisible())
{
COXSizeControlBar* pSizeControlBar=(COXSizeControlBar*)pControlBar;
if(pSizeControlBar->IsMaximized())
{
bSaveSize=FALSE;
break;
}
}
nPos++;
}
nPos=nPosCopy;
}
////////////////////////////////////////////////////////////////////////
BOOL bHorz = IsBarHorizontal();
int nMargin=0;
while(TRUE)
{
void* pVoid = m_arrBars[nPos];
if (pVoid == NULL)
break; // end of the row, stop
CControlBar* pControlBar = GetDockedControlBar(nPos);
if(pBar!=pControlBar && pControlBar!=NULL &&
pControlBar->IsKindOf(RUNTIME_CLASS(COXSizeControlBar)) &&
::IsWindow(pControlBar->GetSafeHwnd()) &&
IsSizeable(pControlBar) && pControlBar->IsVisible())
{
COXSizeControlBar* pSizeControlBar=(COXSizeControlBar*)pControlBar;
if(bHorz)
{
if(bMaximize)
{
if(bSaveSize)
{
pSizeControlBar->m_SavedDockSize=
pSizeControlBar->m_HorzDockSize;
}
nMargin+=pSizeControlBar->m_HorzDockSize.cx-
2*ID_CONTAINER_GAP-ID_BUTTON_SIDE;
pSizeControlBar->m_HorzDockSize.cx=2*ID_CONTAINER_GAP+
ID_BUTTON_SIDE;
}
else
{
pSizeControlBar->m_HorzDockSize=
pSizeControlBar->m_SavedDockSize;
}
}
else
{
if(bMaximize)
{
if(bSaveSize)
{
pSizeControlBar->m_SavedDockSize=
pSizeControlBar->m_VertDockSize;
}
nMargin+=pSizeControlBar->m_VertDockSize.cy-
2*ID_CONTAINER_GAP-ID_BUTTON_SIDE;
pSizeControlBar->m_VertDockSize.cy = 2*ID_CONTAINER_GAP+
ID_BUTTON_SIDE;
}
else
{
pSizeControlBar->m_VertDockSize=
pSizeControlBar->m_SavedDockSize;
}
}
pSizeControlBar->SetMaximized(FALSE);
}
nPos++;
}
if(bMaximize)
{
if(bHorz)
{
if(bSaveSize)
pBar->m_SavedDockSize=pBar->m_HorzDockSize;
pBar->m_HorzDockSize.cx+=nMargin;
}
else
{
if(bSaveSize)
pBar->m_SavedDockSize=pBar->m_VertDockSize;
pBar->m_VertDockSize.cy+=nMargin;
}
}
else
{
if(bHorz)
{
pBar->m_HorzDockSize=pBar->m_SavedDockSize;
}
else
{
pBar->m_VertDockSize=pBar->m_SavedDockSize;
}
}
GetParentFrame()->RecalcLayout();
pBar->SetMaximized(bMaximize);
}
COXDockbarSkin* COXSizeDockBar::GetDockbarSkin()
{
// Check if the app is derived from COXSkinnedApp
COXSkinnedApp* pSkinnedApp = DYNAMIC_DOWNCAST(COXSkinnedApp, AfxGetApp());
if (pSkinnedApp != NULL && pSkinnedApp->GetCurrentSkin() != NULL)
return pSkinnedApp->GetCurrentSkin()->GetDockbarSkin();
else
{
// Create a classic skin for this class if not created already
if (m_pDockbarSkin == NULL)
m_pDockbarSkin = new COXDockbarSkinClassic();
return m_pDockbarSkin;
}
}
// Helper functions
int FindInArray(const CPtrArray& arrBars, int nStartIndex, void* pFind)
{
while (nStartIndex < arrBars.GetUpperBound())
{
if (arrBars[nStartIndex] == pFind)
return nStartIndex;
if (arrBars[nStartIndex] == NULL)
break;
nStartIndex++;
}
return -1;
}
void* FindInArray(void* pFindId, void** pArray)
{
while (*pArray != NULL)
{
if (*pArray == pFindId)
return pArray;
pArray++;
}
return NULL;
}
#ifdef _DEBUG
// DEBUG only helper function
CString GetBarTitles(const CPtrArray& arrBars, int nPos)
{
CString strMsg, strTitle;
while (arrBars[nPos] != 0)
{
CControlBar* pBar = ::GetDockedControlBar(nPos, arrBars);
pBar->GetWindowText(strTitle);
strMsg += strTitle;
strMsg += ",";
nPos ++;
}
return strMsg;
}
#endif
void COXSizeDockBar::OnNcPaint()
{
CDockBar::OnNcPaint();
GetDockbarSkin()->OnNcPaintSizeDockBar(this);
}
void COXSizeDockBar::DockControlBar(CControlBar* pBar, LPCRECT lpRect)
{
ASSERT_VALID(this);
ASSERT_VALID(pBar);
ASSERT_KINDOF(CControlBar, pBar);
bool bNewDockBar = (pBar->m_pDockBar != this); // true if docking to a new dock bar
bool bInsertedToTab = false; // determines whether the control bar was just tabbed
if (bNewDockBar && pBar->m_pDockBar != NULL)
{
COXSizeDockBar* pOldDockBar = DYNAMIC_DOWNCAST(COXSizeDockBar, pBar->m_pDockBar);
if (pOldDockBar != NULL)
{
// Check to see if this control bar is tabbed to the old dock bar
if (pOldDockBar->m_wndDockTabCtrl.FindTab(pBar) != -1)
{
pOldDockBar->m_wndDockTabCtrl.RemoveTab(pBar);
}
}
}
if (lpRect != NULL && DYNAMIC_DOWNCAST(COXSizeControlBar, pBar))
{
// Determine if this control bar needs to be tabbed
int iOldIndex = m_wndDockTabCtrl.FindTab(pBar);
CPoint ptMouse(lpRect->left, lpRect->top);
int iNewTabIndex = m_wndDockTabCtrl.HitTestTabControl(ptMouse, pBar);
if (iNewTabIndex != -1)
{
// Yes, it does
if (iOldIndex != -1 && iOldIndex != iNewTabIndex)
{
// We need to reposition
m_wndDockTabCtrl.RepositionTabs(iOldIndex, iNewTabIndex, ptMouse);
}
else if (iOldIndex == -1)
{
// Not tabbed, so add it
if (GetTabHeight() > 0)
{
// The tab control is already visible
m_wndDockTabCtrl.InsertTab(pBar, iNewTabIndex);
bInsertedToTab = true;
}
else if (GetVisibleSizeControlBarCount(pBar) > 1)
{
// The rule is that it is okay to tab only if
// there is another control bar in this column for vertical
// dock bars. For horizontal dock bars there must be another
// control bar in the same row.
m_wndDockTabCtrl.InsertTab(pBar, iNewTabIndex);
bInsertedToTab = true;
}
}
}
else if (iOldIndex != -1)
{
m_wndDockTabCtrl.RemoveTab(pBar);
}
}
CRect rectBar;
pBar->GetWindowRect(&rectBar);
if (pBar->m_pDockBar == this && (lpRect == NULL || rectBar.EqualRect(lpRect)))
{
// already docked and no change in position
return;
}
// set CBRS_FLOAT_MULTI style if docking bar has it
if (m_bFloating && (pBar->m_dwDockStyle & CBRS_FLOAT_MULTI))
m_dwStyle |= CBRS_FLOAT_MULTI;
m_dwStyle &= ~(CBRS_SIZE_FIXED | CBRS_SIZE_DYNAMIC);
m_dwStyle |= pBar->m_dwStyle & (CBRS_SIZE_FIXED | CBRS_SIZE_DYNAMIC);
if (!(m_dwStyle & CBRS_FLOAT_MULTI))
{
TCHAR szTitle[_MAX_PATH];
pBar->GetWindowText(szTitle, _countof(szTitle));
AfxSetWindowText(m_hWnd, szTitle);
}
// align correctly and turn on all borders
DWORD dwStyle = pBar->GetBarStyle();
dwStyle &= ~(CBRS_ALIGN_ANY);
dwStyle |= (m_dwStyle & CBRS_ALIGN_ANY) | CBRS_BORDER_ANY;
if (m_bFloating)
dwStyle |= CBRS_FLOATING;
else
dwStyle &= ~CBRS_FLOATING;
pBar->SetBarStyle(dwStyle);
// hide first if changing to a new docking site to avoid flashing
BOOL bShow = FALSE;
if (pBar->m_pDockBar != this && pBar->IsWindowVisible())
{
pBar->SetWindowPos(NULL, 0, 0, 0, 0,
SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_HIDEWINDOW);
bShow = TRUE;
}
int nPos = -1;
if (lpRect != NULL)
{
// Get the rectangle of the dockbar
CRect rectThis;
GetWindowRect(rectThis);
ScreenToClient(&rectThis);
// insert into appropriate row
CRect rect(lpRect);
ScreenToClient(&rect);
CPoint ptInsertion = GetInsertionPoint(pBar, rect);
// To prevent flashing we need to stay away from a couple of coordinates
// For vertical dockbars
if (ptInsertion.x >= rectThis.left - 11 && ptInsertion.x <= rectThis.left - 3)
{
// Figure out the offset and shift
int iOffset = rectThis.left - 3 - ptInsertion.x + 1;
ptInsertion.x += iOffset;
}
// For horizontal dockbars
if (ptInsertion.y >= rectThis.top - 11 && ptInsertion.y <= rectThis.top - 3)
{
// Figure out the offset and shift
int iOffset = rectThis.top - 3 - ptInsertion.y + 1;
ptInsertion.y += iOffset;
}
nPos = Insert(pBar, rect, ptInsertion);
// position at requested position
CRect rectPrev;
pBar->GetWindowRect(rectPrev);
ScreenToClient(&rectPrev);
CRect rectNew(lpRect);
ScreenToClient(&rectNew);
// Determine if this is a horizontal or a vertical dockbar
if (m_dwStyle & CBRS_ORIENT_HORZ)
{
// Horizontal
rectNew.SetRect(rect.left, rectPrev.top, rect.Width(), rectPrev.Height());
COXSizeControlBar* pOXBar = DYNAMIC_DOWNCAST(COXSizeControlBar, pBar);
if (pOXBar != NULL && bNewDockBar && !bInsertedToTab)
rectNew.bottom = pOXBar->m_FloatSize.cx;
// Adjust the x cordinates so there would be no snapping
if (pOXBar != NULL)
rectNew.left = GetXCoord(pBar, rectNew.top, rect.left);
else // we have a COXCoolToolbar
{
// Make sure we are not going outside the boundaries
int iXMinimun = GetXCoord(pBar, rectNew.top, rect.left);
int iXMaximum = rectThis.right;
// Make sure we don't exceed the minimum
if (rectNew.left < iXMinimun)
rectNew.left = iXMinimun;
// Make sure we don't exceed the maximum
else if (rectNew.left + rectNew.right > iXMaximum)
rectNew.left = iXMaximum - rectNew.right;
// Make sure we don't exceed the minimum again
if (rectNew.left < iXMinimun)
rectNew.left = iXMinimun;
}
}
else
{
// Vertical
rectNew.SetRect(rectPrev.left, rect.top, rectPrev.Width(), rect.Height());
COXSizeControlBar* pOXBar = DYNAMIC_DOWNCAST(COXSizeControlBar, pBar);
if (pOXBar != NULL && bNewDockBar && !bInsertedToTab)
rectNew.right = pOXBar->m_FloatSize.cx;
// Adjust the x cordinates so there would be no snapping
if (pOXBar != NULL)
rectNew.top = GetYCoord(pBar, rectNew.left, rect.top);
else // we have a COXCoolToolbar
{
// Make sure we are not going outside the boundaries
int iYMinimun = GetYCoord(pBar, rectNew.left, rect.top);
int iYMaximum = rectThis.bottom - GetTabHeight();
// Make sure we don't exceed the minimum
if (rectNew.top < iYMinimun)
rectNew.top = iYMinimun;
// Make sure we don't exceed the maximum
else if (rectNew.top + rectNew.bottom > iYMaximum)
rectNew.top = iYMaximum - rectNew.bottom;
// Make sure we don't exceed the minimum again
if (rectNew.top < iYMinimun)
rectNew.top = iYMinimun;
}
}
pBar->SetWindowPos(NULL, rectNew.left, rectNew.top, rectNew.right, rectNew.bottom,
SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSENDCHANGING);
}
else
{
// always add on current row, then create new one
m_arrBars.Add(pBar);
m_arrBars.Add(NULL);
// align off the edge initially
pBar->SetWindowPos(NULL, -afxData.cxBorder2, -afxData.cyBorder2, 0, 0,
SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCOPYBITS);
}
// attach it to the docking site
if (pBar->GetParent() != this)
pBar->SetParent(this);
if (pBar->m_pDockBar == this)
pBar->m_pDockBar->RemoveControlBar(pBar, nPos);
else if (pBar->m_pDockBar != NULL)
pBar->m_pDockBar->RemoveControlBar(pBar, -1, m_bFloating && !pBar->m_pDockBar->m_bFloating);
pBar->m_pDockBar = this;
if (bShow)
{
ASSERT(!pBar->IsWindowVisible());
pBar->SetWindowPos(NULL, 0, 0, 0, 0,
SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_SHOWWINDOW);
}
// remove any place holder for pBar in this dockbar
RemovePlaceHolder(pBar);
// get parent frame for recalc layout
CFrameWnd* pFrameWnd = GetDockingFrame();
pFrameWnd->DelayRecalcLayout();
}
// This function returns the minimum X-coordinate that a control bar can take at a given
// Y-coordinate
int COXSizeDockBar::GetXCoord(CControlBar* pBar, int iYCoord, int iXCoord)
{
CRect rectDockBar;
GetWindowRect(rectDockBar);
ScreenToClient(&rectDockBar);
int iXMin = rectDockBar.left - 2;
// Loop through all control bars in m_arrBars
for (int i = 0; i < m_arrBars.GetSize(); i++)
{
CControlBar* pCurrentBar = GetDockedControlBar(i);
if (pCurrentBar == NULL || !IsWindow(pCurrentBar->m_hWnd))
continue;
else if (pCurrentBar == pBar)
continue; // skip the bar which we are trying to dock
else if (!pCurrentBar->IsVisible())
continue;
// Get the window rectable of the current control bar
CRect rectBar;
pCurrentBar->GetWindowRect(rectBar);
ScreenToClient(&rectBar);
if (rectBar.top != iYCoord)
continue; // this control bar is on a different column
// The current bar is on the same row
// If the iXCoord is to the left of the midpoint of the current
// bar stop and return whatever iXMin was accumulated so far
if (iXCoord < rectBar.left)
return iXMin;
if (DYNAMIC_DOWNCAST(COXSizeControlBar, pCurrentBar) &&
DYNAMIC_DOWNCAST(COXSizeControlBar, pBar))
rectBar.right += CX_SPLIT; // separator
if (iXMin < rectBar.right)
iXMin = rectBar.right - 2;
}
return iXMin;
}
// This function returns the minimum Y-coordinate that a control bar can take at a given
// X-coordinate
int COXSizeDockBar::GetYCoord(CControlBar *pBar, int iXCoord, int iYCoord)
{
CRect rectDockBar;
GetWindowRect(rectDockBar);
ScreenToClient(&rectDockBar);
int iYMin = rectDockBar.top;
// Loop through all control bars in m_arrBars in order to determine the control bar
// after which the new control bar will be docked
for (int i = 0; i < m_arrBars.GetSize(); i++)
{
CControlBar* pCurrentBar = GetDockedControlBar(i);
if (pCurrentBar == NULL || !IsWindow(pCurrentBar->m_hWnd))
continue;
else if (pCurrentBar == pBar)
continue; // skip the bar which we are trying to dock
else if (!pCurrentBar->IsVisible())
continue; // skin the hidden bars
// Get the window rectanle of the current control bar
CRect rectBar;
pCurrentBar->GetWindowRect(rectBar);
ScreenToClient(&rectBar);
if (rectBar.left != iXCoord)
continue; // this control bar is on a different column
// The current bar is on the same column
// If the iYCoord is higher than the midpoint of the current
// bar stop and return whatever iYMin was accumulated so far
if (iYCoord < rectBar.top)
return iYMin;
if (DYNAMIC_DOWNCAST(COXSizeControlBar, pCurrentBar) &&
DYNAMIC_DOWNCAST(COXSizeControlBar, pBar))
rectBar.bottom += CY_SPLIT; // separator
if (iYMin < rectBar.bottom)
iYMin = rectBar.bottom - 2;
}
return iYMin;
}
// Determines the point that should be passed to the Insert(...) method
CPoint COXSizeDockBar::GetInsertionPoint(CControlBar *pBar, CRect rect)
{
// Set the default insertion position
CPoint ptInsertion(rect.left - 5, rect.top);
// The purpose here is to examine the default insetion position and make sure it does
// not interfere with another control bar and cause an extra snap
// Get the dockbar rectangle
CRect rectDockBar;
GetWindowRect(rectDockBar);
ScreenToClient(&rectDockBar);
// Loop through all control bars in m_arrBars
for (int i = 0; i < m_arrBars.GetSize(); i++)
{
CControlBar* pCurrentBar = GetDockedControlBar(i);
if (pCurrentBar == NULL || !IsWindow(pCurrentBar->m_hWnd))
continue;
else if (pCurrentBar == pBar)
continue; // skip the bar which we are trying to dock
// Get the window rectable of the current control bar
CRect rectBar;
pCurrentBar->GetWindowRect(rectBar);
ScreenToClient(&rectBar);
// Determine if we have a vertical or a horizontal dockbar
if (m_dwStyle & CBRS_ORIENT_HORZ)
{
// Horizontal
if (ptInsertion.y >= rectBar.top && ptInsertion.y <= rectBar.bottom)
{
// If the current control bar is a menubar move one row down
if (DYNAMIC_DOWNCAST(COXMenuBar, pCurrentBar))
{
ptInsertion.y = rectBar.bottom + 2;
return ptInsertion;
}
// If the control bar we are trying to dock is a menu bar
if (DYNAMIC_DOWNCAST(COXMenuBar, pBar))
{
ptInsertion.y = rectBar.bottom + 2;
return ptInsertion;
}
}
}
else
{
// Vertical
if (ptInsertion.x >= rectBar.left && ptInsertion.x <= rectBar.right)
{
// If the current control bar is a menubar move one row to the right
if (DYNAMIC_DOWNCAST(COXMenuBar, pCurrentBar))
{
ptInsertion.x = rectBar.right + 2;
return ptInsertion;
}
// If the control bar we are trying to dock is a menu bar
if (DYNAMIC_DOWNCAST(COXMenuBar, pBar))
{
ptInsertion.x = rectBar.right + 2;
return ptInsertion;
}
}
}
}
return ptInsertion;
}
BOOL COXSizeDockBar::RemoveControlBar(CControlBar* pBar, int nPosExclude, int nAddPlaceHolder)
{
ASSERT(nAddPlaceHolder == 1 || nAddPlaceHolder == 0 || nAddPlaceHolder == -1);
ASSERT_VALID(this);
ASSERT(pBar != NULL);
int nPos = FindBar(pBar, nPosExclude);
ASSERT(nPos > 0);
if (nAddPlaceHolder == 1)
{
m_arrBars[nPos] = (void*)(UINT_PTR)_AfxGetDlgCtrlID(pBar->m_hWnd);
// check for already existing place holder
int nPosOld = FindBar((CControlBar*)m_arrBars[nPos], nPos);
if (nPosOld > 0)
{
m_arrBars.RemoveAt(nPos);
// remove section indicator (NULL) if nothing else in section
if (m_arrBars[nPos-1] == NULL && m_arrBars[nPos] == NULL)
m_arrBars.RemoveAt(nPos);
}
}
else
{
m_arrBars.RemoveAt(nPos);
if (m_arrBars[nPos-1] == NULL && m_arrBars[nPos] == NULL)
m_arrBars.RemoveAt(nPos);
// Remove any pre-existing place holders.
if (nAddPlaceHolder != -1)
RemovePlaceHolder(pBar);
}
// don't do anything more in the shutdown case!
if (pBar->m_pDockContext == NULL)
return FALSE;
// get parent frame for recalc layout/frame destroy
CFrameWnd* pFrameWnd = GetDockingFrame();
if (m_bFloating && GetDockedVisibleCount() == 0)
{
if (GetDockedCount() == 0)
{
pFrameWnd->DestroyWindow();
return TRUE; // Self-Destruct
}
else
pFrameWnd->ShowWindow(SW_HIDE);
}
else
pFrameWnd->DelayRecalcLayout();
return FALSE;
}
// This function returns the appropriate dockbar for the given point.
// If no dockbar is suitable then it returns NULL
COXSizeDockBar* COXSizeDockBar::GetAppropriateDockBar(CPoint point, CControlBar* pControlBar)
{
// Get a pointer to the main frame
CFrameWnd* pFrameWnd = DYNAMIC_DOWNCAST(CFrameWnd, ::AfxGetMainWnd());
if (pFrameWnd == NULL)
return NULL;
// Check the left, top, right and bottom dock bars
DWORD arDockPos[4] = {AFX_IDW_DOCKBAR_LEFT, AFX_IDW_DOCKBAR_RIGHT,
AFX_IDW_DOCKBAR_TOP, AFX_IDW_DOCKBAR_BOTTOM};
int i = 0;
for (i = 0; i < 4; i++)
{
COXSizeDockBar* pDockBar = (COXSizeDockBar*) pFrameWnd->GetControlBar(arDockPos[i]);
CRect rectDockBar;
pDockBar->GetWindowRect(rectDockBar);
if (rectDockBar.PtInRect(point))
{
// Make sure the appropriate flag is set
if (arDockPos[i] == AFX_IDW_DOCKBAR_LEFT &&
!(pControlBar->m_dwDockStyle & CBRS_ALIGN_LEFT))
{
continue;
}
if (arDockPos[i] == AFX_IDW_DOCKBAR_TOP &&
!(pControlBar->m_dwDockStyle & CBRS_ALIGN_TOP))
{
continue;
}
if (arDockPos[i] == AFX_IDW_DOCKBAR_RIGHT &&
!(pControlBar->m_dwDockStyle & CBRS_ALIGN_RIGHT))
{
continue;
}
if (arDockPos[i] == AFX_IDW_DOCKBAR_BOTTOM &&
!(pControlBar->m_dwDockStyle & CBRS_ALIGN_BOTTOM))
{
continue;
}
// Prevent a COXSizeControlBar to be docked to a dockbar which contains COXCoolToolBar
if (pControlBar->IsKindOf(RUNTIME_CLASS(COXSizeControlBar)) &&
pDockBar->GetFirstDockedCoolToolBar(NULL) != NULL)
{
continue;
}
// Prevent a COXCoolToolBar to be docked to a dockbar which contains COXSizeControlBar
if (pControlBar->IsKindOf(RUNTIME_CLASS(COXCoolToolBar)) &&
pDockBar->GetFirstDockedSizeControlBar(NULL) != NULL)
{
continue;
}
// If the tab control is visible do not allow docking except if the cursor is
// over the tab control
if (pDockBar->GetTabHeight() > 0)
{
if (pDockBar->m_wndDockTabCtrl.HitTestTabControl(point, pControlBar) == -1)
continue;
else
return pDockBar;
}
else
return pDockBar;
}
}
// Second pass - this time inflate the rectangle
for (i = 0; i < 4; i++)
{
COXSizeDockBar* pDockBar = (COXSizeDockBar*) pFrameWnd->GetControlBar(arDockPos[i]);
CRect rectDockBar;
pDockBar->GetWindowRect(rectDockBar);
if (arDockPos[i] == AFX_IDW_DOCKBAR_LEFT || arDockPos[i] == AFX_IDW_DOCKBAR_RIGHT)
{
rectDockBar.InflateRect(20, 20);
rectDockBar.bottom -= 28;
}
else if (arDockPos[i] == AFX_IDW_DOCKBAR_TOP || arDockPos[i] == AFX_IDW_DOCKBAR_BOTTOM)
rectDockBar.InflateRect(20, 20);
if (rectDockBar.PtInRect(point))
{
// Make sure the appropriate flag is set
if (arDockPos[i] == AFX_IDW_DOCKBAR_LEFT &&
!(pControlBar->m_dwDockStyle & CBRS_ALIGN_LEFT))
{
continue;
}
if (arDockPos[i] == AFX_IDW_DOCKBAR_TOP &&
!(pControlBar->m_dwDockStyle & CBRS_ALIGN_TOP))
{
continue;
}
if (arDockPos[i] == AFX_IDW_DOCKBAR_RIGHT &&
!(pControlBar->m_dwDockStyle & CBRS_ALIGN_RIGHT))
{
continue;
}
if (arDockPos[i] == AFX_IDW_DOCKBAR_BOTTOM &&
!(pControlBar->m_dwDockStyle & CBRS_ALIGN_BOTTOM))
{
continue;
}
// Prevent a COXSizeControlBar to be docked to a dockbar which contains COXCoolToolBar
if (pControlBar->IsKindOf(RUNTIME_CLASS(COXSizeControlBar)) &&
pDockBar->GetFirstDockedCoolToolBar(NULL) != NULL)
{
continue;
}
// Prevent a COXCoolToolBar to be docked to a dockbar which contains COXSizeControlBar
if (pControlBar->IsKindOf(RUNTIME_CLASS(COXCoolToolBar)) &&
pDockBar->GetFirstDockedSizeControlBar(NULL) != NULL)
{
continue;
}
// If the tab control is visible do not allow docking except if the cursor is
// over the tab control
if (pDockBar->GetTabHeight() > 0)
{
if (pDockBar->m_wndDockTabCtrl.HitTestTabControl(point, pControlBar) == -1)
continue;
else
return pDockBar;
}
else
return pDockBar;
}
}
return NULL;
}
// Returns the hight of the tab control at the bottom or 0 if not visible
int COXSizeDockBar::GetTabHeight()
{
if (m_wndDockTabCtrl.GetItemCount() > 0)
{
// Determine the tab height
return 28;
}
else
return 0;
}
int COXSizeDockBar::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDockBar::OnCreate(lpCreateStruct) == -1)
return -1;
// Create the tabs container
if (!m_wndDockTabCtrl.Create(WS_CHILD | WS_VISIBLE | TCS_BOTTOM | TCS_FOCUSNEVER,
CRect(0,0,0,0), this, IDC_DOCKTAB))
return -1;
return 0;
}
void COXSizeDockBar::OnSize(UINT nType, int cx, int cy)
{
CDockBar::OnSize(nType, cx, cy);
// Size the tabs container accordingly
CRect rectClient;
GetClientRect(rectClient);
rectClient.top = rectClient.bottom - GetTabHeight();
m_wndDockTabCtrl.MoveWindow(rectClient);
}
// This function returns the number of COXSizeControlBar items in this dock bar
int COXSizeDockBar::GetSizeControlBarCount(CControlBar* pExcludeFromCount)
{
int iCount = 0;
for (int i = 0; i < m_arrBars.GetSize(); i++)
{
COXSizeControlBar* pSizeBar = DYNAMIC_DOWNCAST(COXSizeControlBar, GetDockedControlBar(i));
if (pSizeBar != NULL && pSizeBar != pExcludeFromCount)
iCount++;
}
return iCount;
}
COXSizeControlBar* COXSizeDockBar::GetFirstDockedSizeControlBar(CControlBar* pExclude)
{
for (int i = 0; i < m_arrBars.GetSize(); i++)
{
COXSizeControlBar* pSizeBar = DYNAMIC_DOWNCAST(COXSizeControlBar, GetDockedControlBar(i));
if (pSizeBar != NULL && pSizeBar != pExclude)
return pSizeBar;
}
return NULL;
}
COXCoolToolBar* COXSizeDockBar::GetFirstDockedCoolToolBar(CControlBar* pExclude)
{
for (int i = 0; i < m_arrBars.GetSize(); i++)
{
COXCoolToolBar* pToolBar = DYNAMIC_DOWNCAST(COXCoolToolBar, GetDockedControlBar(i));
if (pToolBar != NULL && pToolBar != pExclude)
return pToolBar;
}
return NULL;
}
void COXSizeDockBar::PositionTabCtrl()
{
// Size the tabs container accordingly
CRect rectClient;
GetClientRect(rectClient);
rectClient.top = rectClient.bottom - GetTabHeight();
m_wndDockTabCtrl.MoveWindow(rectClient);
}
// This function returns the number of visible size control bars in this dockbar that are
// on the same row (for vertical dockbars) or on the same column (for horizontal dockbars)
// as the diven control bar
int COXSizeDockBar::GetVisibleSizeControlBarCount(CControlBar* pBar)
{
int iCount = 1;
// Get the client rectangle of the given control bar
CRect rectGiven;
pBar->GetWindowRect(rectGiven);
for (int i = 0; i < m_arrBars.GetSize(); i++)
{
COXSizeControlBar* pCurrentSizeBar = DYNAMIC_DOWNCAST(COXSizeControlBar, GetDockedControlBar(i));
if (pCurrentSizeBar != NULL && pCurrentSizeBar != pBar && pCurrentSizeBar->IsVisible())
{
CRect rectCurrent;
pCurrentSizeBar->GetWindowRect(rectCurrent);
if (IsBarHorizontal())
{
if (rectGiven.left == rectCurrent.left)
{
iCount++;
}
}
else // vertical
{
if (::abs(rectGiven.left - rectCurrent.left) < 5)
{
iCount++;
}
}
}
}
return iCount;
}
void COXSizeDockBar::TabAllDockedControlBars(COXSizeControlBar* pSelected)
{
// v9.3 update 01 modifications Manfred Drasch - at inittime in ShowSelectedTab() the Size is set very small
CRect rct;
int nMaxX = 0;
int nMaxY = 0;
for (int i = 0; i < m_arrBars.GetSize(); i++)
{
COXSizeControlBar* pSizeBar = DYNAMIC_DOWNCAST(COXSizeControlBar, GetDockedControlBar(i));
if (pSizeBar != NULL)
{
if (m_wndDockTabCtrl.FindTab(pSizeBar) < 0)
{
if (pSelected)
{
pSizeBar->GetWindowRect(&rct);
if (nMaxX < rct.Width()) nMaxX = rct.Width();
if (nMaxY < rct.Height()) nMaxY = rct.Height();
}
m_wndDockTabCtrl.InsertTab(pSizeBar, 0, FALSE);
}
}
}
if (pSelected)
{
m_wndDockTabCtrl.SetCurSel(m_wndDockTabCtrl.FindTab(pSelected));
pSelected->GetWindowRect(&rct);
rct.right = rct.left + nMaxX;
rct.bottom = rct.top + nMaxY;
m_wndDockTabCtrl.ShowSelectedTab();
pSelected->MoveWindow(&rct);
}
// end modifications Manfred Drasch
}
void COXSizeDockBar::TabAllDockedControlBars(int selectedIndex)
{
for (int i = 0; i < m_arrBars.GetSize(); i++)
{
COXSizeControlBar* pSizeBar = DYNAMIC_DOWNCAST(COXSizeControlBar, GetDockedControlBar(i));
if (pSizeBar != NULL)
{
if (m_wndDockTabCtrl.FindTab(pSizeBar) < 0)
m_wndDockTabCtrl.InsertTab(pSizeBar, 0, FALSE);
}
}
if (selectedIndex >= 0 && selectedIndex < m_CountBars)
{
m_wndDockTabCtrl.SetCurSel(m_wndDockTabCtrl.FindTab(GetDockedControlBar(selectedIndex)));
m_wndDockTabCtrl.ShowSelectedTab();
}
}