// ==========================================================================
// 					Class Implementation : COXRollup
// ==========================================================================

// Source file : oxrollup.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.
                         
// //////////////////////////////////////////////////////////////////////////


#include "stdafx.h"
#include "OXTleBar.h"
#include "OXRollup.h"

#include <stdlib.h>		
#include "UTB64Bit.h"

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

// internal struct for helping scrolling

#define IDC_CAPTION			1		// ID of caption

IMPLEMENT_DYNAMIC(COXRollup, CDialog);

#define new DEBUG_NEW		// track memory allocation with line numbers

/////////////////////////////////////////////////////////////////////////////
// Definition of static members
CPtrList COXRollup::m_RollupList;
CPtrList COXRollup::m_ArrangedRollups;

// Data members -------------------------------------------------------------
// protected:
	// COXTitleBar* m_pTitleBar;
	// --- Pointer to the Titlebar window

	// BOOL	m_bRolledUp;
	// --- rolled up?

	// BOOL	m_bResizingFrame;
	// --- do we have a resizing frame?
	
	// UINT	m_myTimerID;
	// --- timer id used for rolling up and down
	
	// int m_xWidth;
	// int m_yHeight;
	// --- stored heights and widths of rollup (for resizeable)

	// CMenu* m_pSysMenu;
	// --- pointer to system menu
	
	// WORD m_Rollup;
	// --- ID of our dialog, external
	
	// UINT	m_nTemplateID;
	// --- Ressource ID of dialog
	
	// HWND	m_hWndMR;
	// --- window getting messages (currently)

	// HWND	m_hWndDestroyRC;
	// --- window that gets call to destroy rollup	

	// CString m_sCaption;
	// --- caption of Rollup

// private:
	
// Member functions ---------------------------------------------------------
// public:

COXRollup::COXRollup(UINT nTemplate,CWnd* pParent /*=NULL*/)
	: CDialog(nTemplate, pParent),
	m_myTimerID(12345),	// timer id
	m_nTemplateID(nTemplate),
	m_bResizingFrame(FALSE),
	m_bRolledUp(FALSE),                   
	m_xWidth(-1),
	m_yHeight(-1),
	m_nRollup(0),
	m_nScrollSteps(ROLL_STATES),
	m_pSysMenu(NULL),
	m_pTitleBar(NULL),
	m_hWndMR(NULL),
	m_hWndDestroyRC(NULL),
	m_bIsArranged(FALSE),
	m_bRolling(FALSE)
	{
	// add entry to our rollup list
	m_RollupList.AddTail(this);
	}

COXRollup::~COXRollup()
	{
	if (m_pSysMenu)
		{
		m_pSysMenu->DestroyMenu();
		delete m_pSysMenu;
		}

    if (m_pTitleBar)
		{
		m_pTitleBar->DestroyWindow();
		delete m_pTitleBar;
		}
	// we have to remove ourselves from the rollup-list
	RemoveFromRUList();

	DestroyWindow();
	}

void COXRollup::RemoveFromRUList()
	{
	ASSERT_VALID(this);
	POSITION pos = m_RollupList.Find(this, NULL);

	// this is not to be expected an error (maybe you donīt want this rollup to arrange, so
	// you called this function before)
	if (pos != NULL)
		m_RollupList.RemoveAt(pos);
	}

void COXRollup::DoDataExchange(CDataExchange* pDX)
	{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(COXRollup)
	// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP  
	}

BOOL COXRollup::IsRolling()
	{
	return m_bRolling;
	}

BEGIN_MESSAGE_MAP(COXRollup, CDialog)
	//{{AFX_MSG_MAP(COXRollup)
	ON_COMMAND(IDM_OX_RU_ROLLUP, OnClickedRoll)
	ON_COMMAND(IDM_OX_RU_CLOSE, OnCloseRollup)
	ON_MESSAGE(WM_LMSUROLLUP,OnRollMessage)
	ON_COMMAND(IDM_OX_RU_ABOUT, OnRollUpAbout)
	ON_WM_SIZE()
	ON_WM_ACTIVATE()   
	ON_WM_TIMER()
	ON_WM_CREATE()
	ON_COMMAND(IDM_OX_RU_ARRANGE, Arrange)
	ON_COMMAND(IDM_OX_RU_ARRANGEALL, ArrangeAll)
	ON_COMMAND(IDM_OX_RU_ROLLDOWN, OnClickedRoll)
	ON_WM_DESTROY()
	ON_WM_NCPAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COXRollup message handlers

void COXRollup::OnNcPaint()
	{
	Default();

	if (!m_bRolledUp && m_bResizingFrame)
		{
		CWindowDC dc(this);
		COLORREF FrameColor = ::GetSysColor(COLOR_WINDOWFRAME);
		CPen FramePen(PS_SOLID, 1, FrameColor);
		CPen* pOldPen = dc.SelectObject(&FramePen);

		CRect WndRect;
		GetWindowRect(WndRect);
		dc.MoveTo (0, 0);
		dc.LineTo (WndRect.Width() - 1, 0);
		dc.LineTo (WndRect.Width() - 1, WndRect.Height() - 1);
		dc.LineTo (0, WndRect.Height() - 1);
		dc.LineTo (0, 0);

		dc.SelectObject(pOldPen);
		}
	}

void COXRollup::OnClickedRoll()
	{
	// donīt care about it if Iīm no window
	if (GetSafeHwnd() == NULL)
		return;
	
	if (m_bRolledUp)	// currently rolled up, do downrolling
		{
//====================================================================================
//  commented out (behaviour dependent on own needs)
//		UnArrange();	// get out of arranging (false heights otherwise)
//====================================================================================
		CRect fullRect;
		GetWindowRect(&fullRect);
		int	capHeight = fullRect.Height();
		
		if (m_bResizingFrame)
			{
			// memorize new width if border resizable
			m_xWidth = fullRect.Width();
			} 
		
		int decrement = m_yHeight - capHeight;
		decrement /= m_nScrollSteps;
		
		m_SCROLL_HELP.decrement	= decrement;
		m_SCROLL_HELP.width		= m_xWidth;
		m_SCROLL_HELP.height		= m_yHeight;
		m_SCROLL_HELP.capHeight	= capHeight;
		m_SCROLL_HELP.steps		= m_nScrollSteps;
		m_SCROLL_HELP.curstep		= 1;
		m_SCROLL_HELP.direction	= SCROLL_DOWN;
		if (SetTimer(m_myTimerID, DELAY_TIME,NULL) == 0)
			{
			CString sMessage;
			VERIFY(sMessage.LoadString(IDS_OX_ROLLUPCOULDNTALLOCTIMER));//"Could not alloc timer!"
			AfxMessageBox(sMessage);
			}
		m_bRolling = TRUE;
		}
	else	// roll it up again
		{
		// now roll it up
		if (m_bResizingFrame || m_xWidth == -1)
			{
			CRect fullRect;
			GetWindowRect(&fullRect);
			m_xWidth = fullRect.Width();
			m_yHeight = fullRect.Height();
			}
		
		CRect roll2;
		m_pTitleBar->GetClientRect(&roll2);
		
		int nFrameSize = 2 * ::GetSystemMetrics(SM_CYBORDER);
		int decrement = m_yHeight - roll2.Height() - nFrameSize +
			(m_bResizingFrame ? 2 * ::GetSystemMetrics(SM_CYDLGFRAME) : 0);
		
		decrement /= m_nScrollSteps;
		
		m_SCROLL_HELP.decrement	= decrement;
		m_SCROLL_HELP.width		= m_xWidth;
		m_SCROLL_HELP.height		= m_yHeight;
		m_SCROLL_HELP.capHeight	= roll2.Height() + nFrameSize;
		m_SCROLL_HELP.steps		= m_nScrollSteps;
		m_SCROLL_HELP.curstep		= 1;
		m_SCROLL_HELP.direction	= SCROLL_UP;
		if (SetTimer(m_myTimerID, DELAY_TIME,NULL) == 0)
			{
			CString sMessage;
			VERIFY(sMessage.LoadString(IDS_OX_ROLLUPCOULDNTALLOCTIMER));//"Could not alloc timer!"
			AfxMessageBox(sMessage);
			}
		m_bRolling = TRUE;
		}

	m_bRolledUp = !m_bRolledUp;

	// reflect the changes
	m_pTitleBar->SetRollupState(m_bRolledUp ? SCROLL_DOWN : SCROLL_UP);
	Invalidate();
	}

void COXRollup::OnCloseRollup()
	{
	// called either from titlebar or from OnCloseRollup
	// override if you want to do something special in here (delete this, anything like that)
	ShowWindow(SW_HIDE);	// first we hide
	UnArrange();			// second get out of arrange orderings
	
	ReleaseRecipient(CWnd::FromHandle(m_hWndMR));	// release MR to send to parent
	Send2MR(IDCANCEL);
	}

void COXRollup::OnProcessSysMenu()
	{
	// dim the roll up and down commands via the boolean flag
	CMenu *pSysmenu = GetSysMenu();
	pSysmenu->EnableMenuItem(IDM_OX_RU_ROLLUP,
		MF_BYCOMMAND|(m_bRolledUp ? MF_DISABLED|MF_GRAYED : MF_ENABLED));
	pSysmenu->EnableMenuItem(IDM_OX_RU_ROLLDOWN,
		MF_BYCOMMAND|(m_bRolledUp ? MF_ENABLED : MF_DISABLED|MF_GRAYED));
	pSysmenu->EnableMenuItem(IDM_OX_RU_ARRANGE,
		MF_BYCOMMAND|(IsArranged() ? MF_DISABLED|MF_GRAYED : MF_ENABLED));
	
	// set the position of the system menu
	CRect rect;
	m_pTitleBar->GetWindowRect(&rect);
	pSysmenu->TrackPopupMenu(TPM_LEFTBUTTON|TPM_LEFTALIGN, rect.left, rect.bottom, this, NULL);
	}

BOOL COXRollup::CreateRollUp(CWnd* pParent, WORD nID, int nIDCaption)
{
	CString sCaption;
	VERIFY(sCaption.LoadString(nIDCaption));
	return CreateRollUp(pParent, nID, sCaption);
}

BOOL COXRollup::CreateRollUp(CWnd* pParent, WORD nID, LPCTSTR lpszCaption)
{
	ASSERT(pParent!=NULL && ::IsWindow(pParent->GetSafeHwnd()));
	ASSERT(lpszCaption!=NULL);

	BOOL bAlreadyCreated=::IsWindow(GetSafeHwnd());
	if(bAlreadyCreated)
	{
		DestroyWindow();
	}

	ASSERT(nID!=0);
	// set our roll up ID used later when dispatching messages
	m_nRollup=nID;

	m_sCaption=lpszCaption;

	// set up the window which is responsible for handling the close msg
	m_hWndDestroyRC = pParent->GetSafeHwnd();

	// register ourselves ( the parent ) as owner of the message dispatch
	SetRecipient(pParent);

	// now create us!
	m_bRolledUp = FALSE;	// full size
	if(!CDialog::Create(m_nTemplateID, pParent))
		return FALSE;
	
	ASSERT(::IsWindow(m_hWnd));
	DetermineFrameStyle();
	
	LPCTSTR lptstrBitmap=GetTitleBarBitmap();
	ASSERT(lptstrBitmap!=NULL);
	VERIFY(m_pTitleBar->LoadBitmap(lptstrBitmap));
	
	return TRUE;
}

// find our responsible window for acknowledging our messages
//
HWND COXRollup::GetCurMsgRecipient()
	{
	// if nobody is there except our parent..
	return m_hWndMR != NULL ? m_hWndMR : m_hWndDestroyRC;
	}

// SendMessage like
//
LRESULT COXRollup::Send2MR(WORD msg)
	{
	// we canīt send a message to a parent if we are no window!
	ASSERT(GetSafeHwnd() != NULL);
	
	HWND hWnd = GetCurMsgRecipient();

	// now route the message to the one who feels responsible	
	return ::SendMessage(hWnd, WM_LMSUROLLUP, (WPARAM)GetSafeHwnd(), MAKELPARAM(msg, m_nRollup));
	}

//
// "message posting" implemented; intended use is simple notification
// like "we are rolling up now"
//
BOOL COXRollup::Post2MR(WORD msg)
	{
	// we canīt send a message to a parent if we are no window!
	ASSERT(GetSafeHwnd() != NULL);
	
	HWND hWnd = GetCurMsgRecipient();

	// now route the message to the one who feels responsible	
	return ::PostMessage(hWnd, WM_LMSUROLLUP, (WPARAM)GetSafeHwnd(), MAKELPARAM(msg, m_nRollup));
	}

HWND COXRollup::SetRecipient(CWnd* pWnd)
	{
	ASSERT(pWnd != NULL && ::IsWindow(pWnd->GetSafeHwnd()));
	
	// store old value for return
	HWND hOld = m_hWndMR;
	// set recipient
	m_hWndMR = pWnd->GetSafeHwnd();

	TRACE1("[RU]New recipientīs HWND : %ld\n", m_hWndMR);
	return hOld;
	} 

void COXRollup::ReleaseRecipient(const CWnd* pRCRequesting, BOOL bCallNotifyFunction)
	{
	// do this only when he is the current owner of the message dispatch system
	if (IsOwner(pRCRequesting)) 
		{
		TRACE1("[RU]Released the owner of the mds. Handle : %ld\n", m_hWndMR);
		
		// if he chose to do some cleanup, do it
		if (bCallNotifyFunction)
			OnRecipientRelease();
		
		m_hWndMR = NULL;
		}
	// else do nothing (somebody else has registered as current user
	}

// Intent: Parent can inform us to roll up or down
//
LRESULT COXRollup::OnRollMessage(WPARAM /* wParam */, LPARAM lParam)
	{
	if (HIWORD(lParam) == m_nRollup)
		{
		switch (LOWORD(lParam))
			{
			case ID_ROLLDOWN:
				( m_bRolledUp ? OnClickedRoll() : NULL);
				break;
			case ID_ROLLUP:
				( m_bRolledUp ? NULL : OnClickedRoll());
				break;
			}
		}

	return 0L;
	}

// Intent: Display About information 
//
void COXRollup::OnRollUpAbout()
	{
	// just a very simple about box
	AfxMessageBox(IDS_OX_RU_ABOUT, MB_OK | MB_ICONINFORMATION);
	}

// Intent: donīt allow wrong resources the class doesnīt allow
//
void  COXRollup::DetermineFrameStyle()
	{
	long style = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
#ifdef _DEBUG
	if (style & DS_MODALFRAME)
		{
		TRACE0("Create a rollup with DS_MODALFRAME? Uh,no!\n");
		ASSERT(FALSE);
		} 
	
	if (style & WS_CHILD)
		{
		TRACE0("Create a rollup with WS_CHILD property? Uh,no!\n");
		ASSERT(FALSE);
		}
#endif 
	
	if (style & WS_BORDER)
		m_bResizingFrame = FALSE;
	if (style & WS_THICKFRAME)
		m_bResizingFrame = TRUE;
	}

void COXRollup::OnSize(UINT nType, int cx, int cy)
	{
	CDialog::OnSize(nType, cx, cy);
	// if no resizing style and I donīt exist
	if ((!m_bResizingFrame) || (GetSafeHwnd() == NULL))
		return;
	
	CRect crCaption, crClient;
	GetClientRect(&crClient);
	
	// if dialog (Titlebar) doesnīt exist, skip sizing
	// this will happen at the first call in this routine
	if((m_pTitleBar == NULL) || (m_pTitleBar->GetSafeHwnd() == NULL))
		return;

	// if its allowed, now I will resize
	m_pTitleBar->GetClientRect(&crCaption);
    m_pTitleBar->MoveWindow(0, 0, crClient.Width(), crCaption.Height());
	}

void COXRollup::OnActivate(UINT nState, CWnd* pOther, BOOL bMinimized)
{
	CDialog::OnActivate(nState,pOther,bMinimized);

	// now draw the caption bars
	if((WA_ACTIVE == nState) || (WA_CLICKACTIVE == nState))
	{
		DrawCaption(COLOR_ACTIVECAPTION);
	}

	if(WA_INACTIVE==nState)
	{
		DrawCaption(COLOR_INACTIVECAPTION);
	}
}


void COXRollup::DrawCaption(int nDisplayElement)
	{
	// activation automatically draws menu bar
	m_pTitleBar->SetActivation(nDisplayElement);
	}

void COXRollup::OnTimer(UINT nIDEvent)
	{
	// this is not my timer event...
	if ((m_myTimerID)!= nIDEvent)
		return;

	if (SCROLL_DOWN == m_SCROLL_HELP.direction)
		{
		if (m_SCROLL_HELP.curstep != m_SCROLL_HELP.steps)
			{
			SetWindowPos(NULL, 0, 0, m_SCROLL_HELP.width,
				m_SCROLL_HELP.capHeight + m_SCROLL_HELP.decrement * m_SCROLL_HELP.curstep,
				SWP_NOMOVE | SWP_NOZORDER);
			m_SCROLL_HELP.curstep++;
			}
		else
			{
			SetWindowPos(NULL, 0, 0, m_SCROLL_HELP.width, m_SCROLL_HELP.height,
				SWP_NOMOVE | SWP_NOZORDER);	

			if (m_bResizingFrame)
				{
				long style = ::GetWindowLongPtr(m_hWnd,GWL_STYLE);
				style |= WS_THICKFRAME;
				style &= ~WS_BORDER;
				::SetWindowLongPtr(m_hWnd, GWL_STYLE, style); 

				// Now make Windows redraw the window.
				SetWindowPos(NULL, 0, 0, 0, 0,
							 SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_FRAMECHANGED);
				}

			// kill the timer
			KillTimer(m_myTimerID);
			m_bRolling = FALSE;
			}
		} // end scroll down
	else if (SCROLL_UP == m_SCROLL_HELP.direction)
		{	
		if (m_SCROLL_HELP.curstep != m_SCROLL_HELP.steps)
			{
			SetWindowPos(NULL, 0, 0, m_SCROLL_HELP.width,
				m_SCROLL_HELP.height - m_SCROLL_HELP.decrement * m_SCROLL_HELP.curstep,
				SWP_NOMOVE | SWP_NOZORDER);
			m_SCROLL_HELP.curstep++;			
			}
		else
			{
			// last final positioning
			SetWindowPos(NULL, 0, 0, m_SCROLL_HELP.width, m_SCROLL_HELP.capHeight,
				SWP_NOMOVE | SWP_NOZORDER);			
			
			// set back to thinframe
			if (m_bResizingFrame)
				{
				long style = ::GetWindowLongPtr(m_hWnd,GWL_STYLE);
				style &= ~WS_THICKFRAME;
				style |= WS_BORDER;
				::SetWindowLongPtr(m_hWnd,GWL_STYLE,style);

				// Now make Windows redraw the window.
				SetWindowPos(NULL, 0, 0, 0, 0,
					 SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_FRAMECHANGED);
				}

			KillTimer(m_myTimerID);
			m_bRolling = FALSE;
			}
		} // end scroll up
	}

// Intent: Works like COleServerDoc::GetEmbeddedItem
//		   One time construction per menu needed	
CMenu* COXRollup::GetSysMenu()
	{
	if (NULL == m_pSysMenu)
		{
		m_pSysMenu = OnGetSysMenu();
		}

	ASSERT_VALID(m_pSysMenu);	// this check is for your override of OnGet...
	return m_pSysMenu;
	}

// Intent: Works like COleServerDoc::OnGetEmbeddedItem
//		   Constructs a menu once to be used further
CMenu* COXRollup::OnGetSysMenu()
	{
	// this is our base implementation; you can override it if you want to
	CMenu *pSysMenu = new CMenu();
	VERIFY(pSysMenu->CreatePopupMenu());

	// add all menu items; for strings see string table in OXRollup.rc
	CString sMenuText;
	sMenuText.LoadString(IDS_OX_RUM_ROLLUP);
	pSysMenu->AppendMenu(MF_STRING | MF_ENABLED, IDM_OX_RU_ROLLUP, sMenuText);
	sMenuText.LoadString(IDS_OX_RUM_ROLLDOWN);
	pSysMenu->AppendMenu(MF_STRING | MF_ENABLED, IDM_OX_RU_ROLLDOWN, sMenuText);
	pSysMenu->AppendMenu(MF_SEPARATOR);
	sMenuText.LoadString(IDS_OX_RUM_ARRANGE);
	pSysMenu->AppendMenu(MF_STRING | MF_ENABLED, IDM_OX_RU_ARRANGE, sMenuText);
	sMenuText.LoadString(IDS_OX_RUM_ARRANGEALL);
	pSysMenu->AppendMenu(MF_STRING | MF_ENABLED, IDM_OX_RU_ARRANGEALL, sMenuText);
	pSysMenu->AppendMenu(MF_SEPARATOR);
	sMenuText.LoadString(IDS_OX_RUM_ABOUT);
	pSysMenu->AppendMenu(MF_STRING | MF_ENABLED, IDM_OX_RU_ABOUT, sMenuText);
	sMenuText.LoadString(IDS_OX_RUM_CLOSE);
	pSysMenu->AppendMenu(MF_STRING | MF_ENABLED, IDM_OX_RU_CLOSE, sMenuText);
	
	ASSERT_VALID(pSysMenu);
	return pSysMenu;
	}

int COXRollup::OnCreate(LPCREATESTRUCT lpCreateStruct) 
	{
	if (CDialog::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// this happens if: called CreateRollUp, closed and 
	// reopened via CreateRollUp
	if (m_pTitleBar != NULL)
		{
		ASSERT(m_pTitleBar->GetSafeHwnd() == NULL);
		}
	else
		{
		if ((m_pTitleBar = OnGetTitleBar()) == NULL)
			return -1;
		}
	// you have to derive from COXTitleBar!!!
	ASSERT_VALID(m_pTitleBar);
	ASSERT(m_pTitleBar->IsKindOf(RUNTIME_CLASS(COXTitleBar)));
	
	CRect rcTBar;
	GetTitleBarRect(rcTBar);
	if (! m_pTitleBar->Create(m_sCaption, rcTBar, this, IDC_CAPTION))
		return -1;
	
	return 0;
	}

void COXRollup::OnDestroy() 
	{
	CDialog::OnDestroy();
	}

COXTitleBar* COXRollup::OnGetTitleBar()
	{
	ASSERT_VALID(this);
	
	// here your override should create an own window
	COXTitleBar* pTitleBar = new COXTitleBar();
	return pTitleBar;
	}

void COXRollup::GetTitleBarRect(CRect& rcTBarRect)
	{
	GetClientRect(rcTBarRect);
	rcTBarRect.bottom = TBAR_HEIGHT;
	}


/////////////////////////////////////////////////////////////////////////////
// COXRollup::Arrange - arrange this rollup

void COXRollup::Arrange()
	{
	ASSERT_VALID(this);
	if (IsArranged()) return;
	
	CWnd* pMainWnd = AfxGetMainWnd();
	ASSERT((pMainWnd != NULL) && ::IsWindow(pMainWnd->m_hWnd));		// donīt care about OLE
	if (!pMainWnd->IsWindowEnabled() && pMainWnd->IsIconic())
		return;
	
	InternalRollUp();
	
	COXRollup* pRollup = NULL;
	if (!m_ArrangedRollups.IsEmpty())
		pRollup = (COXRollup*)m_ArrangedRollups.GetTail();	// get last rolled up element
	
	// we have got a rollup (or not...)
	CPoint topLeft(0,0);
	if (pRollup == NULL)
		{
		// we found no college that was rolled up, so we arrange to the left of the screen
		CRect rcLeftOver;
		CWnd* pWndLeftOverPane = pMainWnd->GetDlgItem(AFX_IDW_PANE_FIRST);
		ASSERT(pWndLeftOverPane != NULL);
		pWndLeftOverPane->GetWindowRect(&rcLeftOver);
		topLeft = CPoint(rcLeftOver.left, rcLeftOver.top);
		}
	else
		{
		ASSERT(pRollup != this);		// canīt happen, otherwise we would have been arranged
		ASSERT(pRollup->IsRolledUp());	// must be up if arranged
		CRect rcLastRollup;
		pRollup->GetWindowRect(&rcLastRollup);
		topLeft = CPoint(rcLastRollup.left, rcLastRollup.bottom - 1);
		}
	
	SetWindowPos(NULL, topLeft.x, topLeft.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
	m_ArrangedRollups.AddTail(this);
	m_bIsArranged = TRUE;			
	}

/////////////////////////////////////////////////////////////////////////////
// COXRollup::ArrangeAll - all open rollups are arranged

void COXRollup::ArrangeAll()
	{
	ASSERT_VALID(this);
	
	CWnd* pMainWnd = AfxGetMainWnd();
	ASSERT((pMainWnd != NULL) && ::IsWindow(pMainWnd->m_hWnd));		// donīt care about OLE
	if (!pMainWnd->IsWindowEnabled() && pMainWnd->IsIconic())
		return;
	
	// set out new top point; because we have to move to first position
	CWnd* pWndLeftOverPane = pMainWnd->GetDlgItem(AFX_IDW_PANE_FIRST);
	ASSERT(pWndLeftOverPane != NULL);
	
	CRect rcWndNewPos;
	pWndLeftOverPane->GetWindowRect(&rcWndNewPos);
	int nLeft = rcWndNewPos.left;
	int nNewTop = rcWndNewPos.top;
	
	POSITION pos = m_ArrangedRollups.GetHeadPosition();
	COXRollup* pRoll = NULL;
	while (pos != NULL)
		{
		pRoll = (COXRollup*)m_ArrangedRollups.GetNext(pos);
		pRoll->GetWindowRect(&rcWndNewPos);
		pRoll->SetWindowPos(NULL, nLeft, nNewTop, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
		nNewTop += rcWndNewPos.Height() - 1;
		}
	// now add the ones that havenīt been rolled up before
	POSITION posNotArranged = m_RollupList.GetHeadPosition();
	while (posNotArranged != NULL)
		{
		pRoll = (COXRollup*)m_ArrangedRollups.GetNext(posNotArranged);
		if (m_ArrangedRollups.Find(pRoll, NULL) == NULL)
			{
			if ((::IsWindow(pRoll->m_hWnd)) && pRoll->IsWindowVisible())
				{
				pRoll->InternalRollUp();
				pRoll->SetArrangeFlagTrue();
				pRoll->GetWindowRect(&rcWndNewPos);
				pRoll->SetWindowPos(NULL, nLeft, nNewTop, 0, 0,SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
				nNewTop += rcWndNewPos.Height() - 1;
				m_ArrangedRollups.AddTail(pRoll);	// add it to list
				}
			}
		}
	}

/////////////////////////////////////////////////////////////////////////////
// COXRollup::ReArrangeArranged - call this function if your application moves

void COXRollup::ReArrangeArranged()
	{
	POSITION pos = m_ArrangedRollups.GetHeadPosition();
	if (pos == NULL)
		return;	// this function has to be as fast as possible
	
	CWnd* pMainWnd = AfxGetMainWnd();
	ASSERT((pMainWnd != NULL) && ::IsWindow(pMainWnd->m_hWnd));		// donīt care about OLE
	if (!pMainWnd->IsWindowEnabled() && pMainWnd->IsIconic())
		return;
	
	// set out new top point; because we have to move to first position
	CWnd* pWndLeftOverPane = pMainWnd->GetDlgItem(AFX_IDW_PANE_FIRST);
	ASSERT(pWndLeftOverPane != NULL);
	
	CRect rcWndNewPos;
	pWndLeftOverPane->GetWindowRect(&rcWndNewPos);
	int nLeft = rcWndNewPos.left;
	int nNewTop = rcWndNewPos.top;
	
	COXRollup* pRoll = NULL;
	while (pos != NULL)
		{
		pRoll = (COXRollup*)m_ArrangedRollups.GetNext(pos);
		pRoll->GetWindowRect(&rcWndNewPos);
		pRoll->SetWindowPos(NULL, nLeft, nNewTop, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
		nNewTop += rcWndNewPos.Height() - 1;
		}
	}

/////////////////////////////////////////////////////////////////////////////
// COXRollup::InternalRollUp - roll up quickly

void COXRollup::InternalRollUp()
	{
	ASSERT_VALID(this);
	ASSERT(::IsWindow(m_hWnd));
	ASSERT(m_pTitleBar != NULL);
	
	if (m_bRolledUp)
		return;	// we are up, get out
	
	CRect rcWnd;
	if (m_bResizingFrame || m_xWidth == -1)
		{
		GetWindowRect(&rcWnd);
		m_xWidth = rcWnd.Width();
		m_yHeight = rcWnd.Height();
		// set back to thickframe
		if (m_bResizingFrame)
			{
			long style = ::GetWindowLongPtr(m_hWnd,GWL_STYLE);
			style &= ~WS_THICKFRAME;
			style |= WS_BORDER;
			::SetWindowLongPtr(m_hWnd,GWL_STYLE,style);
			}
		}
	
	m_pTitleBar->GetClientRect(&rcWnd);
	int nFrameSize = 2 * ::GetSystemMetrics(SM_CYBORDER);
	
	SetWindowPos(NULL, 0, 0, m_xWidth, rcWnd.Height() + nFrameSize,	SWP_NOMOVE | SWP_NOZORDER);			
	m_bRolledUp = TRUE;
	m_pTitleBar->SetRollupState(SCROLL_DOWN);
	Invalidate();
	}

void COXRollup::PostNcDestroy()
	{
	// why this unarrange here and not in the destructor?
	// You would get a GP-fault when referencing the main wnd if you have arranged rollups
	// at application termination time, so this is the last valid time to do it safely
	UnArrange();
	CDialog::PostNcDestroy();
	}

void COXRollup::UnArrange()
	{
	ASSERT_VALID(this);
	m_bIsArranged = FALSE;

	// we have to find ourselves in the list
	POSITION pos = m_ArrangedRollups.Find(this, NULL);
	if (pos != NULL )
		{
		// first: all other rollups (arranged ones) move up one step
		COXRollup* pNext = (COXRollup*)m_ArrangedRollups.GetAt(pos);
		POSITION posCur = pos;
		CRect rcWndNewPos;
		int nLeft, nNewTop;
		
		if (m_ArrangedRollups.GetHeadPosition() == posCur)
			{
			// set out new top point; because we have to move to first position
			CWnd* pWndLeftOverPane = AfxGetMainWnd()->GetDlgItem(AFX_IDW_PANE_FIRST);
			ASSERT(pWndLeftOverPane != NULL);
			pWndLeftOverPane->GetWindowRect(&rcWndNewPos);
			nLeft = rcWndNewPos.left;
			nNewTop = rcWndNewPos.top;
			}
		else											   
			{
			POSITION posPrev = posCur;
			m_ArrangedRollups.GetPrev(posPrev);	// we skip this element
			COXRollup* pPrev = (COXRollup*)m_ArrangedRollups.GetPrev(posPrev);
			pPrev->GetWindowRect(&rcWndNewPos);
			nLeft = rcWndNewPos.left;
			nNewTop = rcWndNewPos.bottom - 1;
			}
		
		m_ArrangedRollups.GetNext(posCur);	// we skip this element
		while (posCur != NULL)	// only if we are not last element
			{
			pNext = (COXRollup*)m_ArrangedRollups.GetNext(posCur);
			pNext->GetWindowRect(&rcWndNewPos);
			pNext->SetWindowPos(NULL, nLeft, nNewTop, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
			nNewTop += rcWndNewPos.Height() - 1;
			}
		
		m_ArrangedRollups.RemoveAt(pos);		// now we delete the element from the list
		}
	}
