// ===================================================================================
// 					Class Implementation : COXSizeDialogBar
// ===================================================================================

// Header file : OXSizeDlgBar.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>
#include <afxpriv.h>
#if _MFC_VER >= 0x0700
	#include <afxocc.h>
#endif
#include "OXSizeDlgBar.h"
#include "UTB64Bit.h"

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

IMPLEMENT_DYNAMIC(COXSizeDialogBar, COXSizeControlBar)

#define new DEBUG_NEW

GADGETRESIZEHANDLE CreateGadgetResizeHandle(CWnd* pWnd);
void DestroyGadgetResizeHandle(GADGETRESIZEHANDLE Handle);
void ResizeGadgetsOnWindow(GADGETRESIZEHANDLE Handle, int cx, int cy);

#ifndef _AFX_NO_OCC_SUPPORT
	// Following header file is supplied as part of MFC4.0, but is NOT in the standard include directory.
	// For convenience, a copy of occimpl.h is included along with these sources. If you upgrade MFc, you should
	// ensure this source file is upgraded also.
#ifndef __OX_OCCIMPL_H__
#if _MFC_VER < 0x0700
	#include <..\src\occimpl.h>
#else
//	#include <..\src\mfc\occimpl.h>
#endif
#define __OX_OCCIMPL_H__
#endif
#endif

#ifndef __OXMFCIMPL_H__
#if _MFC_VER < 0x0700
	#include <..\src\afximpl.h>
#else
	#include <..\src\mfc\afximpl.h>
#endif
#define __OXMFCIMPL_H__
#endif


typedef struct _Rect_And_Hwnd
	{
	CRect 	m_rect;
	HWND	m_hWnd;
	}	RECT_AND_HWND;

struct COXSizeDialogBar_ExtData
	{
	_AFX_OCC_DIALOG_INFO* m_pOccDialogInfo;
  	LPCTSTR m_lpszTemplateName;
	};


/////////////////////////////////////////////////////////////////////////////
// Definition of static members

// Data members -------------------------------------------------------------
// protected:

// private:
	
// Member functions ---------------------------------------------------------
// public:
#if defined(_AFXDLL) || (defined(_UNICODE) && _MFC_VER >= 0x0700)
#pragma message ( "oxsizedlgbar.cpp line 84 : VS 2003 may need this symbol defined" )
const TCHAR _afxWndControlBar[] = _T("AfxControlBar");  // class for control bars
#endif

COXSizeDialogBar::COXSizeDialogBar(int nStyle)
	: COXSizeControlBar(nStyle)
	{
	m_GadgetResizeHandle = NULL;

#ifndef _AFX_NO_OCC_SUPPORT
	m_lpszTemplateName = NULL;
	m_pOccDialogInfo = NULL;
#endif
	}

COXSizeDialogBar::~COXSizeDialogBar()
	{
	DestroyGadgetResizeHandle(m_GadgetResizeHandle);
	
	DestroyWindow();    // avoid PostNcDestroy problems  
	}


BEGIN_MESSAGE_MAP(COXSizeDialogBar, COXSizeControlBar)
	//{{AFX_MSG_MAP(COXSizeDialogBar)
	ON_WM_ERASEBKGND()
	//}}AFX_MSG_MAP
#ifndef _AFX_NO_OCC_SUPPORT
	ON_MESSAGE(WM_INITDIALOG, HandleInitDialog)
#endif
END_MESSAGE_MAP()


#ifdef _DEBUG

/////////////////////////////////////////////////////////////////////////////
// Following is taken from afximpl.h, to check that the dialog template
// for a dialog bars are indeed ok to use. Only present if debugging

// Extended dialog templates (new in Win95)
#pragma pack(push, 1)

#pragma pack(pop)

// diagnostic routine to check for and decode dialog templates
// return FALSE if a program error occurs (i.e. bad resource ID or
//   bad dialog styles).
BOOL AFXAPI CheckDialogTemplate(LPCTSTR lpszResource, BOOL bInvisibleChild)
	{
	ASSERT(lpszResource != NULL);
	HINSTANCE hInst = AfxFindResourceHandle(lpszResource, RT_DIALOG);
	HRSRC hResource = ::FindResource(hInst, lpszResource, RT_DIALOG);
	if (hResource == NULL)
		{
		if (HIWORD(lpszResource) != 0)
			TRACE1("ERROR: Cannot find dialog template named '%s'.\n",
				lpszResource);
		else
			TRACE1("ERROR: Cannot find dialog template with IDD 0x%04X.\n",
				LOWORD((DWORD_PTR)lpszResource));
		return FALSE;
		}

	if (!bInvisibleChild)
		return TRUE;        // that's all we need to check

	// we must check that the dialog template is for an invisible child
	//  window that can be used for a form-view or dialog-bar
	HGLOBAL hTemplate = LoadResource(hInst, hResource);
	if (hTemplate == NULL)
		{
		TRACE0("Warning: LoadResource failed for dialog template.\n");
		// this is only a warning, the real call to CreateDialog will fail
		return TRUE;        // not a program error - just out of memory
		}
	DLGTEMPLATEEX* pTemplate = (DLGTEMPLATEEX*)LockResource(hTemplate);
	DWORD dwStyle;
	if (pTemplate->signature == 0xFFFF)
		dwStyle = pTemplate->style;
	else
		dwStyle = ((DLGTEMPLATE*)pTemplate)->style;
	UnlockResource(hTemplate);
	FreeResource(hTemplate);

	if (dwStyle & WS_VISIBLE)
		{
		if (HIWORD(lpszResource) != 0)
			TRACE1("ERROR: Dialog named '%s' must be invisible.\n",
				lpszResource);
		else
			TRACE1("ERROR: Dialog with IDD 0x%04X must be invisible.\n",
				LOWORD((DWORD_PTR)lpszResource));
		return FALSE;
		}

	if (!(dwStyle & WS_CHILD))
		{
		if (HIWORD(lpszResource) != 0)
			TRACE1("ERROR: Dialog named '%s' must have the child style.\n",
				lpszResource);
		else
			TRACE1("ERROR: Dialog with IDD 0x%04X must have the child style.\n",
				LOWORD((DWORD_PTR)lpszResource));
		return FALSE;
		}

	return TRUE;
	}

#endif //_DEBUG

BOOL COXSizeDialogBar::Create(CWnd* pParentWnd, LPCTSTR lpszTemplateName,
							  UINT nStyle, UINT nID)
{
	ASSERT(pParentWnd != NULL);
	ASSERT(lpszTemplateName != NULL);
	ASSERT((nStyle & WS_THICKFRAME)==NULL);
	
// Alas, MFC doesn't export many of the debugg-only functions, but I've added
// the code for this safety net as it helps track down errors faster, and
// only has an overhead in debug code
#ifdef _DEBUG
// dialog template must exist and be invisible with WS_CHILD set
	if (!CheckDialogTemplate(lpszTemplateName, TRUE))
	{
		ASSERT(FALSE);          // invalid dialog template name
		PostNcDestroy();        // cleanup if Create fails too soon
		return FALSE;
	}
#endif //_DEBUG

	// allow chance to modify styles
#if _MFC_VER <= 0x0421
	m_dwStyle = nStyle;
#else
    m_dwStyle = nStyle&CBRS_ALL;
#endif
	CREATESTRUCT cs;
	memset(&cs, 0, sizeof(cs));
	cs.lpszClass = _afxWndControlBar;
	cs.style = (DWORD)nStyle | WS_CHILD;
	cs.hMenu = (HMENU)(UINT_PTR)nID;
	cs.hInstance = AfxGetInstanceHandle();
	cs.hwndParent = pParentWnd->GetSafeHwnd();
	if (!PreCreateWindow(cs))
		return FALSE;
	
	// create a modeless dialog
#ifndef _AFX_NO_OCC_SUPPORT
	m_lpszTemplateName = lpszTemplateName;
#endif

	// initialize common controls
	//VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));

	BOOL bSuccess = CreateDlg(lpszTemplateName, pParentWnd);

#ifndef _AFX_NO_OCC_SUPPORT
	m_lpszTemplateName = NULL;
#endif

	if (!bSuccess)
		return FALSE;
	
	// dialog template MUST specify that the dialog
	//  is an invisible child window
	SetDlgCtrlID(nID);
	CRect rect;
	
	
	// force WS_CLIPSIBLINGS
	// I also remove the titlebar. This means the resource can include a caption, which
	// will be used when the frame is floating.
	ModifyStyle(WS_CAPTION, WS_CLIPSIBLINGS);		
	if (!ExecuteDlgInit(lpszTemplateName))
		return FALSE;
	
	// m_sizeDefault isn't actually used by the COXSizeDialogBar, but since it's public, it
	// seemed sensible to keep it available. Otherwise it might be difficult to get hold
	// of the original size of the dialog (as specified in the resource file). 
	
	//GetWindowRect(&rect);
	GetClientRect(&rect);
	m_sizeDefault = rect.Size();    // set fixed size
	
	// Move to position 0,0
	// NB: size not forced to zero, as this can affect resizing if bar is 
	// immediately docked
	SetWindowPos(NULL, 0, 0, 0, 0, 
		SWP_NOZORDER|SWP_NOACTIVATE|SWP_SHOWWINDOW|SWP_NOSIZE|SWP_FRAMECHANGED);
	
	// set all the sizes to be the default after the positioning done above
	m_VertDockSize 	= m_sizeDefault;
	m_HorzDockSize  = m_sizeDefault;
	m_FloatSize 	= m_sizeDefault;
	
	
	// if auto-sizing, store the rectangles of all the child windows.
	if(m_Style & SZBARF_DLGAUTOSIZE)	
	{
		m_GadgetResizeHandle = CreateGadgetResizeHandle(this);
	}

	return TRUE;
}


// need to paint the background...
BOOL COXSizeDialogBar::OnEraseBkgnd(CDC* pDC)
{
	return COXSizeControlBar::OnEraseBkgnd(pDC);
}


void COXSizeDialogBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
{
	COXSizeControlBar::OnUpdateCmdUI(pTarget,bDisableIfNoHndler);
	UpdateDialogControls(pTarget, bDisableIfNoHndler);
}


void COXSizeDialogBar::OnSizedOrDocked(int cx, int cy, BOOL bFloating, int flags)
{
	COXSizeControlBar::OnSizedOrDocked(cx, cy, bFloating, flags);
	if (cx == 0 || cy == 0)
		return;
	
	// if auto-sizing, go thru and reposition all the controls.
	if (m_Style & SZBARF_DLGAUTOSIZE)	
	{
		CRect rectClient;
		GetClientRect(&rectClient);
		ResizeGadgetsOnWindow(m_GadgetResizeHandle, rectClient.Width(), 
			rectClient.Height());
	}
}

/////////////////////////////////////////////////////////////////////////////
// Gadget resizing functions - used to resize all the child windows of a dialog
// bar above. Routines extracted as they may be more used in general

// For a given window, creates an array of the child windows, that can be used
// for resizing later on.
GADGETRESIZEHANDLE CreateGadgetResizeHandle(CWnd* pWnd)
	{
	CPtrArray* pArray = new CPtrArray;
	
	// 1st element of the array stores the client rectangle of the given window
	RECT_AND_HWND* pRectWnd = new RECT_AND_HWND;
	pWnd->GetClientRect(&(pRectWnd->m_rect));
	pRectWnd->m_hWnd = NULL;
	pArray->Add(pRectWnd);
	TCHAR szClassName[64];
	
	for (HWND hWndChild = ::GetTopWindow(pWnd->m_hWnd); hWndChild != NULL;
	hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT))
		{
		RECT_AND_HWND* pRectWnd = new RECT_AND_HWND;
		pRectWnd->m_hWnd = hWndChild;
		GetClassName(pRectWnd->m_hWnd, szClassName, 64); 
		::GetWindowRect(hWndChild, &(pRectWnd->m_rect));
		pWnd->ScreenToClient(&(pRectWnd->m_rect));

		// if it's a drop down combo box, then we set bottom -ve to indicate
		// we should keep the height fixed. Otherwise we might find that the drop
		// down size gets stupidly small.
		if (_tcscmp(szClassName, _T("ComboBox")) == 0 &&
			(GetWindowLongPtr(hWndChild, GWL_STYLE) & (CBS_DROPDOWN | CBS_DROPDOWNLIST)) )
			{
			pRectWnd->m_rect.bottom = - (pRectWnd->m_rect.bottom - pRectWnd->m_rect.top);
			}

		pArray->Add(pRectWnd);
		}
	
	return (GADGETRESIZEHANDLE) pArray;
	}


// deletes the underlying array and it's elements
void DestroyGadgetResizeHandle(GADGETRESIZEHANDLE Handle)
	{
	CPtrArray* pArray = (CPtrArray*)Handle;		
	if (pArray != NULL)
		{
		for (int i = PtrToInt(pArray->GetUpperBound()); i >= 0; i--)
			delete ((RECT_AND_HWND*)pArray->GetAt(i));
		delete pArray;
		}
	}


// Resizes all the gadgets in an array to match the new size.
// Generally called from an OnSize routine (or the like)
void ResizeGadgetsOnWindow(GADGETRESIZEHANDLE Handle, int cx, int cy)
	{
	CPtrArray* pArray = (CPtrArray*)Handle;		
	
	if (pArray == NULL || cx == 0 || cy == 0)
		return;
	
	ASSERT(pArray->GetSize() > 0);	// must be at least one element in the array
	
	// if auto-sizing, go thru and reposition all the controls.
	CSize sizeOrig = ((RECT_AND_HWND*)pArray->GetAt(0))->m_rect.Size();
	if(sizeOrig.cx==0)
		sizeOrig.cx=1;
	if(sizeOrig.cy==0)
		sizeOrig.cy=1;
	for (int i = 1; i < pArray->GetSize(); i++)
		{
		RECT_AND_HWND* pRectWnd = (RECT_AND_HWND*)pArray->GetAt(i);
		CRect newRect = pRectWnd->m_rect;
		newRect.top    = newRect.top    * cy / sizeOrig.cy;
		newRect.left   = newRect.left   * cx / sizeOrig.cx;
		newRect.right  = newRect.right  * cx / sizeOrig.cx;
		// if bottom is < 0, then keep it fixed height - used for combo boxes
		// where we don't really want to resize most of them...
		if (newRect.bottom < 0)
			newRect.bottom = newRect.top - newRect.bottom;
		else 
			newRect.bottom = newRect.bottom * cy / sizeOrig.cy;

		::SetWindowPos(pRectWnd->m_hWnd, NULL,
			newRect.left, newRect.top, newRect.Width(), newRect.Height(),
			SWP_NOZORDER);
		}
	}

#ifndef _AFX_NO_OCC_SUPPORT

LRESULT COXSizeDialogBar::HandleInitDialog(WPARAM, LPARAM)
	{
	Default();  // allow default to initialize first (common dialogs/etc)


	// get extension data - for backward compatibility with existing MRCEXT,
	// this can't be in the base class itself.
	// create OLE controls
	COccManager* pOccManager = afxOccManager;
	if ((pOccManager != NULL) && (m_pOccDialogInfo != NULL))
		{
		if (!pOccManager->CreateDlgControls(this, m_lpszTemplateName, m_pOccDialogInfo))
			{
			TRACE0("Warning: CreateDlgControls failed during dialog bar init.\n");
			return FALSE;
			}
		}

	BOOL bResult=OnInitDialog();
	return bResult;
	}

BOOL COXSizeDialogBar::SetOccDialogInfo(_AFX_OCC_DIALOG_INFO* pOccDialogInfo)
	{
	m_pOccDialogInfo = pOccDialogInfo;
	return TRUE;
	}

#endif