Click here to Skip to main content
15,886,830 members
Articles / Desktop Programming / MFC

The Ultimate Toolbox - Updates and User Contributions

Rate me:
Please Sign up or sign in to vote.
4.79/5 (26 votes)
12 Feb 2013CPOL8 min read 254.8K   23.7K   170  
Updates and User Contributions for the Ultimate Toolbox Libraries
// ===================================================================================
// 					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();
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
In January 2005, David Cunningham and Chris Maunder created TheUltimateToolbox.com, a new group dedicated to the continued development, support and growth of Dundas Software’s award winning line of MFC, C++ and ActiveX control products.

Ultimate Grid for MFC, Ultimate Toolbox for MFC, and Ultimate TCP/IP have been stalwarts of C++/MFC development for a decade. Thousands of developers have used these products to speed their time to market, improve the quality of their finished products, and enhance the reliability and flexibility of their software.
This is a Organisation

476 members

Comments and Discussions