// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include <malloc.h>

#ifdef AFX_CORE3_SEG
#pragma code_seg(AFX_CORE3_SEG)
#endif

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define new DEBUG_NEW

/////////////////////////////////////////////////////////////////////////////
// CControlBar

// IMPLEMENT_DYNAMIC for CControlBar is in wincore.cpp for .OBJ granularity reasons

BEGIN_MESSAGE_MAP(CControlBar, CWnd)
	//{{AFX_MSG_MAP(CControlBar)
	ON_WM_TIMER()
	ON_WM_PAINT()
	ON_WM_CTLCOLOR()
	ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
	ON_MESSAGE(WM_SIZEPARENT, OnSizeParent)
	ON_WM_WINDOWPOSCHANGING()
	ON_WM_SHOWWINDOW()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_MOUSEACTIVATE()
	ON_WM_CANCELMODE()
	ON_WM_CREATE()
	ON_WM_DESTROY()
	ON_MESSAGE_VOID(WM_INITIALUPDATE, OnInitialUpdate)
	ON_MESSAGE(WM_HELPHITTEST, OnHelpHitTest)
	ON_WM_ERASEBKGND()
	//}}AFX_MSG_MAP
#ifndef _AFX_NO_CTL3D_SUPPORT
	ON_MESSAGE(WM_QUERY3DCONTROLS, OnQuery3dControls)
#endif
END_MESSAGE_MAP()

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

CControlBar::CControlBar()
{
	// no elements contained in the control bar yet
	m_nCount = 0;
	m_pData = NULL;

	// set up some default border spacings
	m_cxLeftBorder = m_cxRightBorder = 6;
	m_cxDefaultGap = 2;
	m_cyTopBorder = m_cyBottomBorder = 1;
	m_bAutoDelete = FALSE;
	m_hWndOwner = NULL;
	m_nStateFlags = 0;
	m_pDockSite = NULL;
	m_pDockBar = NULL;
	m_pDockContext = NULL;
	m_dwStyle = 0;
	m_dwDockStyle = 0;
	m_nMRUWidth = 32767;
}

void CControlBar::SetBorders(int cxLeft, int cyTop, int cxRight, int cyBottom)
{
	ASSERT(cxLeft >= 0);
	ASSERT(cyTop >= 0);
	ASSERT(cxRight >= 0);
	ASSERT(cyBottom >= 0);

	m_cxLeftBorder = cxLeft;
	m_cyTopBorder = cyTop;
	m_cxRightBorder = cxRight;
	m_cyBottomBorder = cyBottom;
}

BOOL CControlBar::PreCreateWindow(CREATESTRUCT& cs)
{
	if (!CWnd::PreCreateWindow(cs))
		return FALSE;

	// force clipsliblings (otherwise will cause repaint problems)
	cs.style |= WS_CLIPSIBLINGS;

	// default border style translation for Win4
	//  (you can turn off this translation by setting CBRS_BORDER_3D)
	if (afxData.bWin4 && (m_dwStyle & CBRS_BORDER_3D) == 0)
	{
		DWORD dwNewStyle = 0;
		switch (m_dwStyle & (CBRS_BORDER_ANY|CBRS_ALIGN_ANY))
		{
		case CBRS_LEFT:
			dwNewStyle = CBRS_BORDER_TOP|CBRS_BORDER_BOTTOM;
			break;
		case CBRS_TOP:
			dwNewStyle = CBRS_BORDER_TOP;
			break;
		case CBRS_RIGHT:
			dwNewStyle = CBRS_BORDER_TOP|CBRS_BORDER_BOTTOM;
			break;
		case CBRS_BOTTOM:
			dwNewStyle = CBRS_BORDER_BOTTOM;
			break;
		}

		// set new style if it matched one of the predefined border types
		if (dwNewStyle != 0)
		{
			m_dwStyle &= ~(CBRS_BORDER_ANY);
			m_dwStyle |= (dwNewStyle | CBRS_BORDER_3D);
		}
	}

	return TRUE;
}

void CControlBar::SetBarStyle(DWORD dwStyle)
{
	ASSERT((dwStyle & CBRS_ALL) == dwStyle);

	EnableToolTips(dwStyle & CBRS_TOOLTIPS);

	if (m_dwStyle != dwStyle)
	{
		DWORD dwOldStyle = m_dwStyle;
		m_dwStyle = dwStyle;
		OnBarStyleChange(dwOldStyle, dwStyle);
	}
}

void CControlBar::OnBarStyleChange(DWORD, DWORD)
{
	// can be overridden in derived classes
}

BOOL CControlBar::AllocElements(int nElements, int cbElement)
{
	ASSERT_VALID(this);
	ASSERT(nElements >= 0 && cbElement >= 0);
	ASSERT(m_pData != NULL || m_nCount == 0);

	// allocate new data if necessary
	void* pData = NULL;
	if (nElements > 0)
	{
		ASSERT(cbElement > 0);
		if ((pData = calloc(nElements, cbElement)) == NULL)
			return FALSE;
	}
	free(m_pData);      // free old data

	// set new data and elements
	m_pData = pData;
	m_nCount = nElements;

	return TRUE;
}

#ifdef AFX_CORE3_SEG
#pragma code_seg(AFX_CORE3_SEG)
#endif

CControlBar::~CControlBar()
{
	ASSERT_VALID(this);

	DestroyWindow();    // avoid PostNcDestroy problems

	// also done in OnDestroy, but done here just in case
	if (m_pDockSite != NULL)
		m_pDockSite->RemoveControlBar(this);

	// free docking context
	CDockContext* pDockContext = m_pDockContext;
	m_pDockContext = NULL;
	delete pDockContext;

	// free array
	if (m_pData != NULL)
	{
		ASSERT(m_nCount != 0);
		free(m_pData);
	}

	_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
	if (pThreadState->m_pLastStatus == this)
	{
		pThreadState->m_pLastStatus = NULL;
		pThreadState->m_nLastStatus = -1;
	}
}

void CControlBar::PostNcDestroy()
{
	if (m_bAutoDelete)      // Automatic cleanup?
		delete this;
}

/////////////////////////////////////////////////////////////////////////////
// Attributes

CSize CControlBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
{
	CSize size;
	size.cx = (bStretch && bHorz ? 32767 : 0);
	size.cy = (bStretch && !bHorz ? 32767 : 0);
	return size;
}

CSize CControlBar::CalcDynamicLayout(int, DWORD nMode)
{
	return CalcFixedLayout(nMode & LM_STRETCH, nMode & LM_HORZ);
}

BOOL CControlBar::IsDockBar() const
{
	return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Fly-by status bar help

#define ID_TIMER_WAIT   0xE000  // timer while waiting to show status
#define ID_TIMER_CHECK  0xE001  // timer to check for removal of status

void CControlBar::ResetTimer(UINT nEvent, UINT nTime)
{
	KillTimer(ID_TIMER_WAIT);
	KillTimer(ID_TIMER_CHECK);
	VERIFY(SetTimer(nEvent, nTime, NULL));
}

void CControlBar::OnTimer(UINT nIDEvent)
{
	if (GetKeyState(VK_LBUTTON) < 0)
		return;

	_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();

	// get current mouse position for hit test
	CPoint point; GetCursorPos(&point);
	ScreenToClient(&point);
	int nHit = OnToolHitTest(point, NULL);
	if (nHit >= 0)
	{
		// determine if status bar help should go away
		CWnd* pParent = GetTopLevelParent();
		if (!IsTopParentActive() || !pParent->IsWindowEnabled())
			nHit = -1;

		// remove status help if capture is set
		HWND hWndTip = pThreadState->m_pToolTip->GetSafeHwnd();
		CWnd* pCapture = GetCapture();
		if (pCapture != this && pCapture->GetSafeHwnd() != hWndTip &&
			pCapture->GetTopLevelParent() == pParent)
		{
			nHit = -1;
		}
	}
	else
	{
		pThreadState->m_nLastStatus = -1;
	}

	// make sure it isn't over some other app's window
	if (nHit >= 0)
	{
		ClientToScreen(&point);
		HWND hWnd = ::WindowFromPoint(point);
		if (hWnd == NULL || (hWnd != m_hWnd && !::IsChild(m_hWnd, hWnd) &&
			pThreadState->m_pToolTip->GetSafeHwnd() != hWnd))
		{
			nHit = -1;
			pThreadState->m_nLastStatus = -1;
		}
	}

	// handle the result
	if (nHit < 0)
	{
		if (pThreadState->m_nLastStatus == -1)
			KillTimer(ID_TIMER_CHECK);
		SetStatusText(-1);
	}

	// set status text after initial timeout
	if (nIDEvent == ID_TIMER_WAIT)
	{
		KillTimer(ID_TIMER_WAIT);
		if (nHit >= 0)
			SetStatusText(nHit);
	}
}

BOOL CControlBar::SetStatusText(int nHit)
{
	CWnd* pOwner = GetOwner();

	_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
	if (nHit == -1)
	{
		// handle reset case
		pThreadState->m_pLastStatus = NULL;
		if (m_nStateFlags & statusSet)
		{
			pOwner->SendMessage(WM_POPMESSAGESTRING, AFX_IDS_IDLEMESSAGE);
			m_nStateFlags &= ~statusSet;
			return TRUE;
		}
		KillTimer(ID_TIMER_WAIT);
	}
	else
	{
		// handle setnew case
		if (!(m_nStateFlags & statusSet) || pThreadState->m_nLastStatus != nHit)
		{
			pThreadState->m_pLastStatus = this;
			pOwner->SendMessage(WM_SETMESSAGESTRING, nHit);
			m_nStateFlags |= statusSet;
			ResetTimer(ID_TIMER_CHECK, 200);
			return TRUE;
		}
	}
	return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Default control bar processing

BOOL CControlBar::PreTranslateMessage(MSG* pMsg)
{
	ASSERT_VALID(this);
	ASSERT(m_hWnd != NULL);

	// allow tooltip messages to be filtered
	if (CWnd::PreTranslateMessage(pMsg))
		return TRUE;

	UINT message = pMsg->message;
	CWnd* pOwner = GetOwner();

	// handle CBRS_FLYBY style (status bar flyby help)
	if (((m_dwStyle & CBRS_FLYBY) ||
		message == WM_LBUTTONDOWN || message == WM_LBUTTONUP) &&
		((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) ||
		 (message >= WM_NCMOUSEFIRST && message <= WM_NCMOUSELAST)))
	{
		_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();

		// gather information about current mouse position
		CPoint point = pMsg->pt;
		ScreenToClient(&point);
		TOOLINFO ti; memset(&ti, 0, sizeof(TOOLINFO));
		ti.cbSize = sizeof(AFX_OLDTOOLINFO);
		int nHit = OnToolHitTest(point, &ti);
		if (ti.lpszText != LPSTR_TEXTCALLBACK)
			free(ti.lpszText);
		BOOL bNotButton =
			message == WM_LBUTTONDOWN && (ti.uFlags & TTF_NOTBUTTON);
		if (message != WM_LBUTTONDOWN && GetKeyState(VK_LBUTTON) < 0)
			nHit = pThreadState->m_nLastStatus;

		// update state of status bar
		if (nHit < 0 || bNotButton)
		{
			if (GetKeyState(VK_LBUTTON) >= 0 || bNotButton)
			{
				SetStatusText(-1);
				KillTimer(ID_TIMER_CHECK);
			}
		}
		else
		{
			if (message == WM_LBUTTONUP)
			{
				SetStatusText(-1);
				ResetTimer(ID_TIMER_CHECK, 200);
			}
			else
			{
				if ((m_nStateFlags & statusSet) || GetKeyState(VK_LBUTTON) < 0)
					SetStatusText(nHit);
				else if (nHit != pThreadState->m_nLastStatus)
					ResetTimer(ID_TIMER_WAIT, 300);
			}
		}
		pThreadState->m_nLastStatus = nHit;
	}

	// don't translate dialog messages when in Shift+F1 help mode
	CFrameWnd* pFrameWnd = GetTopLevelFrame();
	if (pFrameWnd != NULL && pFrameWnd->m_bHelpMode)
		return FALSE;

	// since 'IsDialogMessage' will eat frame window accelerators,
	//   we call all frame windows' PreTranslateMessage first
	while (pOwner != NULL)
	{
		// allow owner & frames to translate before IsDialogMessage does
		if (pOwner->PreTranslateMessage(pMsg))
			return TRUE;

		// try parent frames until there are no parent frames
		pOwner = pOwner->GetParentFrame();
	}

	// filter both messages to dialog and from children
	return PreTranslateInput(pMsg);
}

LRESULT CControlBar::WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	ASSERT_VALID(this);

	LRESULT lResult;
	switch (nMsg)
	{
	case WM_NOTIFY:
	case WM_COMMAND:
	case WM_DRAWITEM:
	case WM_MEASUREITEM:
	case WM_DELETEITEM:
	case WM_COMPAREITEM:
	case WM_VKEYTOITEM:
	case WM_CHARTOITEM:
		// send these messages to the owner if not handled
		if (OnWndMsg(nMsg, wParam, lParam, &lResult))
			return lResult;
		else
		{
			// try owner next
			lResult = GetOwner()->SendMessage(nMsg, wParam, lParam);

			// special case for TTN_NEEDTEXTA and TTN_NEEDTEXTW
			if(nMsg == WM_NOTIFY)
			{
				NMHDR* pNMHDR = (NMHDR*)lParam;
				if (pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW)
				{
					TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
					TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
					if ((pNMHDR->code == TTN_NEEDTEXTA && (!pTTTA->lpszText || !*pTTTA->lpszText)) ||
						(pNMHDR->code == TTN_NEEDTEXTW && (!pTTTW->lpszText || !*pTTTW->lpszText)))
					{
						// not handled by owner, so let bar itself handle it
						lResult = CWnd::WindowProc(nMsg, wParam, lParam);
					}
				}
			}
			return lResult;
		}
	}

	// otherwise, just handle in default way
	lResult = CWnd::WindowProc(nMsg, wParam, lParam);
	return lResult;
}

LRESULT CControlBar::OnHelpHitTest(WPARAM, LPARAM lParam)
{
	ASSERT_VALID(this);

	int nID = OnToolHitTest((DWORD)lParam, NULL);
	if (nID != -1)
		return HID_BASE_COMMAND+nID;

	nID = _AfxGetDlgCtrlID(m_hWnd);
	return nID != 0 ? HID_BASE_CONTROL+nID : 0;
}

void CControlBar::OnWindowPosChanging(LPWINDOWPOS lpWndPos)
{
	// WINBUG: We call DefWindowProc here instead of CWnd::OnWindowPosChanging
	//  (which calls CWnd::Default, which calls through the super wndproc)
	//  because certain control bars that are system implemented (such as
	//  CToolBar with TBSTYLE_FLAT) do not implement WM_WINDOWPOSCHANGING
	//  correctly, causing repaint problems.  This code bypasses that whole
	//  mess.
	::DefWindowProc(m_hWnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)lpWndPos);

	if (lpWndPos->flags & SWP_NOSIZE)
		return;

	// invalidate borders on the right
	CRect rect;
	GetWindowRect(&rect);
	CSize sizePrev = rect.Size();
	int cx = lpWndPos->cx;
	int cy = lpWndPos->cy;
	if (cx != sizePrev.cx && (m_dwStyle & CBRS_BORDER_RIGHT))
	{
		rect.SetRect(cx-afxData.cxBorder2, 0, cx, cy);
		InvalidateRect(&rect);
		rect.SetRect(sizePrev.cx-afxData.cxBorder2, 0, sizePrev.cx, cy);
		InvalidateRect(&rect);
	}

	// invalidate borders on the bottom
	if (cy != sizePrev.cy && (m_dwStyle & CBRS_BORDER_BOTTOM))
	{
		rect.SetRect(0, cy-afxData.cyBorder2, cx, cy);
		InvalidateRect(&rect);
		rect.SetRect(0, sizePrev.cy-afxData.cyBorder2, cx, sizePrev.cy);
		InvalidateRect(&rect);
	}
}

int CControlBar::OnCreate(LPCREATESTRUCT lpcs)
{
	if (CWnd::OnCreate(lpcs) == -1)
		return -1;

	if (m_dwStyle & CBRS_TOOLTIPS)
		EnableToolTips();

	CFrameWnd *pFrameWnd = (CFrameWnd*)GetParent();
	if (pFrameWnd->IsFrameWnd())
	{
		m_pDockSite = pFrameWnd;
		m_pDockSite->AddControlBar(this);
	}
	return 0;
}

void CControlBar::OnDestroy()
{
	_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
	if (pThreadState->m_pLastStatus == this)
	{
		SetStatusText(-1);
		ASSERT(pThreadState->m_pLastStatus == NULL);
	}

	if (m_pDockSite != NULL)
	{
		m_pDockSite->RemoveControlBar(this);
		m_pDockSite = NULL;
	}

	CWnd::OnDestroy();
}

BOOL CControlBar::DestroyWindow()
{
	if (m_hWnd != NULL && IsFloating())
		return GetDockingFrame()->DestroyWindow();
	else
		return CWnd::DestroyWindow();
}

int CControlBar::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT nMsg)
{
	// call default when toolbar is not floating
	if (!IsFloating())
		return CWnd::OnMouseActivate(pDesktopWnd, nHitTest, nMsg);

	// special behavior when floating
	ActivateTopParent();

	return MA_NOACTIVATE;   // activation already done
}

void CControlBar::OnPaint()
{
	// background is already filled in gray
	CPaintDC dc(this);

	// erase background now
	if (IsVisible())
		DoPaint(&dc);       // delegate to paint helper
}

void CControlBar::EraseNonClient()
{
	// get window DC that is clipped to the non-client area
	CWindowDC dc(this);
	CRect rectClient;
	GetClientRect(rectClient);
	CRect rectWindow;
	GetWindowRect(rectWindow);
	ScreenToClient(rectWindow);
	rectClient.OffsetRect(-rectWindow.left, -rectWindow.top);
	dc.ExcludeClipRect(rectClient);

	// draw borders in non-client area
	rectWindow.OffsetRect(-rectWindow.left, -rectWindow.top);
	DrawBorders(&dc, rectWindow);

	// erase parts not drawn
	dc.IntersectClipRect(rectWindow);
	SendMessage(WM_ERASEBKGND, (WPARAM)dc.m_hDC);

	// draw gripper in non-client area
	DrawGripper(&dc, rectWindow);
}

HBRUSH CControlBar::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	LRESULT lResult;
	if (pWnd->SendChildNotifyLastMsg(&lResult))
		return (HBRUSH)lResult;     // eat it

	// force black text on gray background all the time
	if (!GrayCtlColor(pDC->m_hDC, pWnd->GetSafeHwnd(), nCtlColor,
	   afxData.hbrBtnFace, afxData.clrBtnText))
		return (HBRUSH)Default();
	return afxData.hbrBtnFace;
}

void CControlBar::OnLButtonDown(UINT nFlags, CPoint pt)
{
	// only start dragging if clicked in "void" space
	if (m_pDockBar != NULL && OnToolHitTest(pt, NULL) == -1)
	{
		// start the drag
		ASSERT(m_pDockContext != NULL);
		ClientToScreen(&pt);
		m_pDockContext->StartDrag(pt);
	}
	else
	{
		CWnd::OnLButtonDown(nFlags, pt);
	}
}

void CControlBar::OnLButtonDblClk(UINT nFlags, CPoint pt)
{
	// only toggle docking if clicked in "void" space
	if (m_pDockBar != NULL && OnToolHitTest(pt, NULL) == -1)
	{
		// start the drag
		ASSERT(m_pDockContext != NULL);
		m_pDockContext->ToggleDocking();
	}
	else
	{
		CWnd::OnLButtonDblClk(nFlags, pt);
	}
}

LRESULT CControlBar::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM)
{
	// handle delay hide/show
	BOOL bVis = GetStyle() & WS_VISIBLE;
	UINT swpFlags = 0;
	if ((m_nStateFlags & delayHide) && bVis)
		swpFlags = SWP_HIDEWINDOW;
	else if ((m_nStateFlags & delayShow) && !bVis)
		swpFlags = SWP_SHOWWINDOW;
	m_nStateFlags &= ~(delayShow|delayHide);
	if (swpFlags != 0)
	{
		SetWindowPos(NULL, 0, 0, 0, 0, swpFlags|
			SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
	}

	// the style must be visible and if it is docked
	// the dockbar style must also be visible
	if ((GetStyle() & WS_VISIBLE) &&
		(m_pDockBar == NULL || (m_pDockBar->GetStyle() & WS_VISIBLE)))
	{
		CFrameWnd* pTarget = (CFrameWnd*)GetOwner();
		if (pTarget == NULL || !pTarget->IsFrameWnd())
			pTarget = GetParentFrame();
		if (pTarget != NULL)
			OnUpdateCmdUI(pTarget, (BOOL)wParam);
	}
	return 0L;
}

void CControlBar::OnInitialUpdate()
{
	// update the indicators before becoming visible
	OnIdleUpdateCmdUI(TRUE, 0L);
}

DWORD CControlBar::RecalcDelayShow(AFX_SIZEPARENTPARAMS* lpLayout)
{
	ASSERT(lpLayout != NULL);

	// resize and reposition this control bar based on styles
	DWORD dwStyle = (m_dwStyle & (CBRS_ALIGN_ANY|CBRS_BORDER_ANY)) |
		(GetStyle() & WS_VISIBLE);

	// handle delay hide/show
	if (m_nStateFlags & (delayHide|delayShow))
	{
		UINT swpFlags = 0;
		if (m_nStateFlags & delayHide)
		{
			ASSERT((m_nStateFlags & delayShow) == 0);
			if (dwStyle & WS_VISIBLE)
				swpFlags = SWP_HIDEWINDOW;
		}
		else
		{
			ASSERT(m_nStateFlags & delayShow);
			if ((dwStyle & WS_VISIBLE) == 0)
				swpFlags = SWP_SHOWWINDOW;
		}
		if (swpFlags != 0)
		{
			// make the window seem visible/hidden
			dwStyle ^= WS_VISIBLE;
			if (lpLayout->hDWP != NULL)
			{
				// clear delay flags
				m_nStateFlags &= ~(delayShow|delayHide);
				// hide/show the window if actually doing layout
				lpLayout->hDWP = ::DeferWindowPos(lpLayout->hDWP, m_hWnd, NULL,
					0, 0, 0, 0, swpFlags|
					SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
			}
		}
		else
		{
			// clear delay flags -- window is already in correct state
			m_nStateFlags &= ~(delayShow|delayHide);
		}
	}
	return dwStyle; // return new style
}

LRESULT CControlBar::OnSizeParent(WPARAM, LPARAM lParam)
{
	AFX_SIZEPARENTPARAMS* lpLayout = (AFX_SIZEPARENTPARAMS*)lParam;
	DWORD dwStyle = RecalcDelayShow(lpLayout);

	if ((dwStyle & WS_VISIBLE) && (dwStyle & CBRS_ALIGN_ANY) != 0)
	{
		// align the control bar
		CRect rect;
		rect.CopyRect(&lpLayout->rect);

		CSize sizeAvail = rect.Size();  // maximum size available

		// get maximum requested size
		DWORD dwMode = lpLayout->bStretch ? LM_STRETCH : 0;
		if ((m_dwStyle & CBRS_SIZE_DYNAMIC) && m_dwStyle & CBRS_FLOATING)
			dwMode |= LM_HORZ | LM_MRUWIDTH;
		else if (dwStyle & CBRS_ORIENT_HORZ)
			dwMode |= LM_HORZ | LM_HORZDOCK;
		else
			dwMode |=  LM_VERTDOCK;

		CSize size = CalcDynamicLayout(-1, dwMode);

		size.cx = min(size.cx, sizeAvail.cx);
		size.cy = min(size.cy, sizeAvail.cy);

		if (dwStyle & CBRS_ORIENT_HORZ)
		{
			lpLayout->sizeTotal.cy += size.cy;
			lpLayout->sizeTotal.cx = max(lpLayout->sizeTotal.cx, size.cx);
			if (dwStyle & CBRS_ALIGN_TOP)
				lpLayout->rect.top += size.cy;
			else if (dwStyle & CBRS_ALIGN_BOTTOM)
			{
				rect.top = rect.bottom - size.cy;
				lpLayout->rect.bottom -= size.cy;
			}
		}
		else if (dwStyle & CBRS_ORIENT_VERT)
		{
			lpLayout->sizeTotal.cx += size.cx;
			lpLayout->sizeTotal.cy = max(lpLayout->sizeTotal.cy, size.cy);
			if (dwStyle & CBRS_ALIGN_LEFT)
				lpLayout->rect.left += size.cx;
			else if (dwStyle & CBRS_ALIGN_RIGHT)
			{
				rect.left = rect.right - size.cx;
				lpLayout->rect.right -= size.cx;
			}
		}
		else
		{
			ASSERT(FALSE);      // can never happen
		}

		rect.right = rect.left + size.cx;
		rect.bottom = rect.top + size.cy;

		// only resize the window if doing layout and not just rect query
		if (lpLayout->hDWP != NULL)
			AfxRepositionWindow(lpLayout, m_hWnd, &rect);
	}
	return 0;
}

void CControlBar::DelayShow(BOOL bShow)
{
	m_nStateFlags &= ~(delayHide|delayShow);
	if (bShow && (GetStyle() & WS_VISIBLE) == 0)
		m_nStateFlags |= delayShow;
	else if (!bShow && (GetStyle() & WS_VISIBLE) != 0)
		m_nStateFlags |= delayHide;
}

BOOL CControlBar::IsVisible() const
{
	if (m_nStateFlags & delayHide)
		return FALSE;

	if ((m_nStateFlags & delayShow) || ((GetStyle() & WS_VISIBLE) != 0))
		return TRUE;

	return FALSE;
}

void CControlBar::DoPaint(CDC* pDC)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);

	// paint inside client area
	CRect rect;
	GetClientRect(rect);
	DrawBorders(pDC, rect);
	DrawGripper(pDC, rect);
}

void CControlBar::DrawBorders(CDC* pDC, CRect& rect)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);

	DWORD dwStyle = m_dwStyle;
	if (!(dwStyle & CBRS_BORDER_ANY))
		return;

	// prepare for dark lines
	ASSERT(rect.top == 0 && rect.left == 0);
	CRect rect1, rect2;
	rect1 = rect;
	rect2 = rect;
	COLORREF clr = afxData.bWin4 ? afxData.clrBtnShadow : afxData.clrWindowFrame;

	// draw dark line one pixel back/up
	if (dwStyle & CBRS_BORDER_3D)
	{
		rect1.right -= CX_BORDER;
		rect1.bottom -= CY_BORDER;
	}
	if (dwStyle & CBRS_BORDER_TOP)
		rect2.top += afxData.cyBorder2;
	if (dwStyle & CBRS_BORDER_BOTTOM)
		rect2.bottom -= afxData.cyBorder2;

	// draw left and top
	if (dwStyle & CBRS_BORDER_LEFT)
		pDC->FillSolidRect(0, rect2.top, CX_BORDER, rect2.Height(), clr);
	if (dwStyle & CBRS_BORDER_TOP)
		pDC->FillSolidRect(0, 0, rect.right, CY_BORDER, clr);

	// draw right and bottom
	if (dwStyle & CBRS_BORDER_RIGHT)
		pDC->FillSolidRect(rect1.right, rect2.top, -CX_BORDER, rect2.Height(), clr);
	if (dwStyle & CBRS_BORDER_BOTTOM)
		pDC->FillSolidRect(0, rect1.bottom, rect.right, -CY_BORDER, clr);

	if (dwStyle & CBRS_BORDER_3D)
	{
		// prepare for hilite lines
		clr = afxData.clrBtnHilite;

		// draw left and top
		if (dwStyle & CBRS_BORDER_LEFT)
			pDC->FillSolidRect(1, rect2.top, CX_BORDER, rect2.Height(), clr);
		if (dwStyle & CBRS_BORDER_TOP)
			pDC->FillSolidRect(0, 1, rect.right, CY_BORDER, clr);

		// draw right and bottom
		if (dwStyle & CBRS_BORDER_RIGHT)
			pDC->FillSolidRect(rect.right, rect2.top, -CX_BORDER, rect2.Height(), clr);
		if (dwStyle & CBRS_BORDER_BOTTOM)
			pDC->FillSolidRect(0, rect.bottom, rect.right, -CY_BORDER, clr);
	}

	if (dwStyle & CBRS_BORDER_LEFT)
		rect.left += afxData.cxBorder2;
	if (dwStyle & CBRS_BORDER_TOP)
		rect.top += afxData.cyBorder2;
	if (dwStyle & CBRS_BORDER_RIGHT)
		rect.right -= afxData.cxBorder2;
	if (dwStyle & CBRS_BORDER_BOTTOM)
		rect.bottom -= afxData.cyBorder2;
}

#define CX_GRIPPER  3
#define CY_GRIPPER  3
#define CX_BORDER_GRIPPER 2
#define CY_BORDER_GRIPPER 2

void CControlBar::DrawGripper(CDC* pDC, const CRect& rect)
{
	// only draw the gripper if not floating and gripper is specified
	if ((m_dwStyle & (CBRS_GRIPPER|CBRS_FLOATING)) == CBRS_GRIPPER)
	{
		// draw the gripper in the border
		if (m_dwStyle & CBRS_ORIENT_HORZ)
		{
			pDC->Draw3dRect(rect.left+CX_BORDER_GRIPPER,
				rect.top+m_cyTopBorder,
				CX_GRIPPER, rect.Height()-m_cyTopBorder-m_cyBottomBorder,
				afxData.clrBtnHilite, afxData.clrBtnShadow);
		}
		else
		{
			pDC->Draw3dRect(rect.left+m_cyTopBorder,
				rect.top+CY_BORDER_GRIPPER,
				rect.Width()-m_cyTopBorder-m_cyBottomBorder, CY_GRIPPER,
				afxData.clrBtnHilite, afxData.clrBtnShadow);
		}
	}
}

// input CRect should be client rectangle size
void CControlBar::CalcInsideRect(CRect& rect, BOOL bHorz) const
{
	ASSERT_VALID(this);
	DWORD dwStyle = m_dwStyle;

	if (dwStyle & CBRS_BORDER_LEFT)
		rect.left += afxData.cxBorder2;
	if (dwStyle & CBRS_BORDER_TOP)
		rect.top += afxData.cyBorder2;
	if (dwStyle & CBRS_BORDER_RIGHT)
		rect.right -= afxData.cxBorder2;
	if (dwStyle & CBRS_BORDER_BOTTOM)
		rect.bottom -= afxData.cyBorder2;

	// inset the top and bottom.
	if (bHorz)
	{
		rect.left += m_cxLeftBorder;
		rect.top += m_cyTopBorder;
		rect.right -= m_cxRightBorder;
		rect.bottom -= m_cyBottomBorder;
		if ((m_dwStyle & (CBRS_GRIPPER|CBRS_FLOATING)) == CBRS_GRIPPER)
			rect.left += CX_BORDER_GRIPPER+CX_GRIPPER+CX_BORDER_GRIPPER;
	}
	else
	{
		rect.left += m_cyTopBorder;
		rect.top += m_cxLeftBorder;
		rect.right -= m_cyBottomBorder;
		rect.bottom -= m_cxRightBorder;
		if ((m_dwStyle & (CBRS_GRIPPER|CBRS_FLOATING)) == CBRS_GRIPPER)
			rect.top += CY_BORDER_GRIPPER+CY_GRIPPER+CY_BORDER_GRIPPER;
	}
}

/////////////////////////////////////////////////////////////////////////////
// CControlBar diagnostics

#ifdef _DEBUG
void CControlBar::AssertValid() const
{
	CWnd::AssertValid();

	ASSERT(m_nCount == 0 || m_pData != NULL);
	ASSERT((m_dwStyle & CBRS_ALL) == m_dwStyle);
}

void CControlBar::Dump(CDumpContext& dc) const
{
	CWnd::Dump(dc);

	dc << "\nm_cxLeftBorder = " << m_cxLeftBorder;
	dc << "\nm_cxRightBorder = " << m_cxRightBorder;
	dc << "\nm_cyTopBorder = " << m_cyTopBorder;
	dc << "\nm_cyBottomBorder = " << m_cyBottomBorder;
	dc << "\nm_cxDefaultGap = " << m_cxDefaultGap;
	dc << "\nm_nCount = " << m_nCount;
	dc << "\nm_bAutoDelete = " << m_bAutoDelete;

	dc << "\n";
}
#endif

/////////////////////////////////////////////////////////////////////////////
