Click here to Skip to main content
15,891,828 members
Articles / Desktop Programming / ATL

SP Numeric Edit Control

Rate me:
Please Sign up or sign in to vote.
4.78/5 (11 votes)
16 Nov 200511 min read 74.9K   3.6K   36  
Masked numeric edit ActiveX control.
/////////////////////////////////////////////////////////////////////////////////////////
//	Project:		SP ATL Extensions 1.3
//
//	File:			spDetailedMessageBox.cpp implementation of 
//											 SP::CDetailedMessageWindow class, 
//											 SP::CDetailedMessageBoxImpl and
//											 SP::CAxDetailedMessageBoxImpl  
//											 class templates.
//
//	Developer(s):	Sergei Pavlovsky
//					sergei_vp@hotmail.com
//					Copyright (c) 2003-2005
//
//	Description:	In addition to message string extended message box provids an 
//					option to show/hide additional information or "details".
//					CDetailedMessageWindow - implements common operations needed for
//					message box functionality.
//					CDetailedMessageBoxImpl - handles standard window messages.
//					CAxDetailedMessageBoxImpl - handles standard window messages 
//					and also provides support for AcviveX controls.
//
//	Platforms:		Win32, ATL
//
//	This code may be used in compiled form in any way you desire. This file may be 
//	redistributed unmodified by any means PROVIDING it is not sold for profit without 
//	the authors written consent, and providing that this notice and the authors name 
//	is included. If the source code in this file is used in any commercial application 
//	then acknowledgement must be made to the author of this file (in whatever form 
//	you wish).
//
//	This file is provided "as is" with no expressed or implied warranty. The author 
//	accepts no liability for any damage/loss of business that this product may cause.
/////////////////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "spDetailedMessageBox.h"

namespace SP
{

/////////////////////////////////////////////////////////////////////////////////////////
// CDetailedMessageWindow class implementation
/////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////
// CDetailedMessageWindow::CDialogTempalte class implementation
/////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////
// Initialization & uninitialization

BOOL CDetailedMessageWindow::CDialogTempalte::Initialize(const DTLMSGBOXPARAMS* pcDMBP)
{	
	ATLASSERT( !::IsBadReadPtr(pcDMBP, sizeof(DTLMSGBOXPARAMS)) );
	ATLASSERT( !m_pcDMBP );	

	// Create measurment DC
	HDC hdc = ::CreateIC(_T("DISPLAY"), NULL, NULL, NULL);
	ATLASSERT( hdc );
	if ( !hdc )   return FALSE;

	m_pcDMBP = pcDMBP;

	// Set font to device comtext
	HFONT hFont = ( m_pcDMBP->hFont ) 
						? m_pcDMBP->hFont
						: (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
	
	::SelectObject(hdc, hFont);

	// Calculate elements sizes			
	BOOL bResult = Initialize(hdc);
	ATLASSERT( bResult );
	::DeleteDC(hdc);
	if ( !bResult )
	{
		m_pcDMBP = NULL;
		return FALSE;
	}
	
	return TRUE;
}

void CDetailedMessageWindow::CDialogTempalte::Uninitialize()
{
	m_pcDMBP = NULL;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Helpers: Calculation of dimensions

void CDetailedMessageWindow::CDialogTempalte::CalculateAreaSize(const SIZE* pcSize, 
																BOOL bNotEmpty, 
																SIZE* pAreaSize) const
{			
	ATLASSERT( !::IsBadReadPtr(pcSize, sizeof(SIZE)) );
	ATLASSERT( !::IsBadWritePtr(pAreaSize, sizeof(SIZE)) );
	ATLASSERT( pcSize->cx >= 0 && pcSize->cy >= 0 );
	ATLASSERT( m_sizeBaseUnit.cx > 0 && m_sizeBaseUnit.cy > 0 );

	if ( bNotEmpty )
	{
		const LONG lPdnDbl = 2 * GetPadding();

		pAreaSize->cx = pcSize->cx + lPdnDbl;
		pAreaSize->cy = pcSize->cy + lPdnDbl;
	}
	else
	{
		pAreaSize->cx = pAreaSize->cy = 0;
	}
}

BOOL CDetailedMessageWindow::CDialogTempalte::CalculateCaptionBarWidth(HDC hdc, 
															int* pcxCaptionBar) const
{
	ATLASSERT( hdc );
	ATLASSERT( !::IsBadWritePtr(pcxCaptionBar, sizeof(int)) );
	ATLASSERT( m_pcDMBP );

	int cButtons = ( IsContextHelpNeeded() ) ? 2 : 1;
	int cxButton = IsToolWindowNeeded() 
						? ::GetSystemMetrics(SM_CXSMSIZE)
						: ::GetSystemMetrics(SM_CXSIZE);

	if ( m_pcDMBP->lpcszCaption )
	{
		// Set caption Font
		HFONT hFont = (HFONT)::GetStockObject(SYSTEM_FONT);
		HFONT hFontOld = (HFONT)::SelectObject(hdc, hFont);

		SIZE size;
		BOOL bResult = ::GetTextExtentPoint32(hdc, m_pcDMBP->lpcszCaption, 
												lstrlen(m_pcDMBP->lpcszCaption), 
												&size);
		ATLASSERT( bResult );
		::SelectObject(hdc, hFontOld);
		if ( !bResult )   return FALSE;

		*pcxCaptionBar = size.cx + cButtons * cxButton;
	}
	else
	{
		*pcxCaptionBar = cButtons * cxButton;
	}

	return TRUE;
}

int CDetailedMessageWindow::CDialogTempalte::CalculateButtonsWidth(int cButtons) const
{			
	ATLASSERT( cButtons >= 0 );
	ATLASSERT( m_sizeButton.cx >= 0 && m_sizeButton.cy >= 0 );

	return ( cButtons )
				? (m_sizeButton.cx + GetSmallPadding()) * cButtons - 
					GetSmallPadding()
				: 0;		
}

int CDetailedMessageWindow::CDialogTempalte::CalculateButtonsHeight(int cButtons) const
{			
	ATLASSERT( cButtons >= 0 );
	ATLASSERT( m_sizeButton.cx >= 0 && m_sizeButton.cy >= 0 );

	return ( cButtons )	? m_sizeButton.cy : 0;		
}

/////////////////////////////////////////////////////////////////////////////////////////
// Initialization: GUI

void CDetailedMessageWindow::CDialogTempalte::InitializeFontParameters(HDC hdc)
{
	ATLASSERT( hdc );
	ATLASSERT( m_pcDMBP );

	HFONT hFont = (HFONT)::GetCurrentObject(hdc, OBJ_FONT);					
	LOGFONT lf;  ::GetObject(hFont, sizeof(LOGFONT), &lf);
	
	ATLASSERT( lf.lfHeight < 0 );
	m_cyFontPointSize = MulDiv(-lf.lfHeight, 72, ::GetDeviceCaps(hdc, LOGPIXELSY));
	m_wFontWeight = (WORD)lf.lfWeight;
	m_bFontItalic = lf.lfItalic;
	m_btFontCharset = lf.lfCharSet;
	wcscpy(m_szFontFaceName, CT2W(lf.lfFaceName));
}

BOOL CDetailedMessageWindow::CDialogTempalte::InitializeBaseUnits(HDC hdc)
{
	ATLASSERT( hdc );

	// Define dialog base units
	TEXTMETRIC tm;
	BOOL bResult = ::GetTextMetrics(hdc, &tm);
	ATLASSERT( bResult );
	if ( !bResult )   return FALSE;

	if ( tm.tmPitchAndFamily & TMPF_FIXED_PITCH )
	{
		const TCHAR szChars[] =	
			_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");

		const int cChars = sizeof(szChars)/sizeof(TCHAR) - 1;

		SIZE size;
		bResult = ::GetTextExtentPoint32(hdc, const_cast<LPTSTR>(szChars), 
										cChars, &size);
		ATLASSERT( bResult );
		if ( !bResult )   return FALSE;

		// Round Up
		m_sizeBaseUnit.cx = (size.cx / (cChars / 2) + 1) / 2;
	}
	else
	{
		m_sizeBaseUnit.cx = tm.tmAveCharWidth;
	}

	m_sizeBaseUnit.cy = tm.tmHeight;

	return TRUE;
}

BOOL CDetailedMessageWindow::CDialogTempalte::InitializeIconSize(HDC hdc)
{
	ATLASSERT( hdc );
	ATLASSERT( m_sizeBaseUnit.cx > 0 && m_sizeBaseUnit.cy > 0 );

	if ( IsIconNeeded() )
	{		
		ICONINFO ii;
		BOOL bResult = ::GetIconInfo(m_pcDMBP->hIcon, &ii);
		ATLASSERT( bResult );
		if ( !bResult )   return FALSE;

		BITMAP bmp;
		if ( ii.hbmColor )
			::GetObject(ii.hbmColor, sizeof(BITMAP), &bmp);
		else if ( ii.hbmMask )
			::GetObject(ii.hbmMask, sizeof(BITMAP), &bmp);
		else
			bmp.bmWidth = bmp.bmHeight = 0;

		m_sizeIcon.cx = bmp.bmWidth;
		m_sizeIcon.cy = bmp.bmHeight;

		if ( ii.hbmColor )
			::DeleteObject(ii.hbmColor);

		if ( ii.hbmMask )
			::DeleteObject(ii.hbmMask);
	}
	else
	{
		m_sizeIcon.cx = m_sizeIcon.cy = 0;
	}

	return TRUE;
}

BOOL CDetailedMessageWindow::CDialogTempalte::InitializeButtonSize(HDC hdc, 
														const SIZE* pcClientMaxSize) 
{
	ATLASSERT( hdc );
	ATLASSERT( !::IsBadReadPtr(pcClientMaxSize, sizeof(SIZE)) );
	ATLASSERT( m_sizeBaseUnit.cx > 0 && m_sizeBaseUnit.cy > 0 );

	const UINT unDTParams = DT_CALCRECT|DT_EDITCONTROL;
	const LONG lPdnDbl = GetPadding() * 2;

	// Maximum button size
	SIZE sizeBtnMax = { 
		(pcClientMaxSize->cx - lPdnDbl) / (GetStdButtonsNumber() + 1) - 
			GetSmallPadding() * GetStdButtonsNumber(),
		(pcClientMaxSize->cy - lPdnDbl) / 4 
	};

	SIZE sizeBtnMin = {	
		(m_sizeBaseUnit.cx * 25) / 2,
		(m_sizeBaseUnit.cy * 29) / 16 
	};

	m_sizeButton.cx = 0;
	m_sizeButton.cy = 0;

	LPCTSTR lpcszText;
	RECT rc;

	// Define Detail button text bounds
	if ( IsDetailButtonNeeded() )
	{	
		ATLASSERT( m_pcDMBP->lpcszDetailOn && m_pcDMBP->lpcszDetailOff );

		::SetRectEmpty(&rc);
		lpcszText = m_pcDMBP->lpcszDetailOn;

		int cyText = ::DrawTextEx(hdc, (LPTSTR)lpcszText, -1, &rc, unDTParams, NULL);
		ATLASSERT( cyText );
		if ( !cyText )  return FALSE;

		if ( rc.right > m_sizeButton.cx )
			m_sizeButton.cx = rc.right;
		if ( rc.bottom > m_sizeButton.cy )
			m_sizeButton.cy = rc.bottom;

		::SetRectEmpty(&rc);
		lpcszText = m_pcDMBP->lpcszDetailOff;

		cyText = ::DrawTextEx(hdc, (LPTSTR)lpcszText, -1, &rc, unDTParams, NULL);
		ATLASSERT( cyText );
		if ( !cyText )  return FALSE;

		if ( rc.right > m_sizeButton.cx )
			m_sizeButton.cx = rc.right;
		if ( rc.bottom > m_sizeButton.cy )
			m_sizeButton.cy = rc.bottom;

		rc.left = rc.top = rc.right = rc.bottom = 0;
	}

	// Define Help button text bounds
	if ( IsHelpButtonNeeded() )
	{
		ATLASSERT( m_pcDMBP->lpcszHelp );

		::SetRectEmpty(&rc);
		lpcszText = m_pcDMBP->lpcszHelp;

		int cyText = ::DrawTextEx(hdc, (LPTSTR)lpcszText, -1, &rc, unDTParams, 
									NULL);
		ATLASSERT( cyText );
		if ( !cyText )  return FALSE;

		if ( rc.right > m_sizeButton.cx )
			m_sizeButton.cx = rc.right;
		if ( rc.bottom > m_sizeButton.cy )
			m_sizeButton.cy = rc.bottom;
	}

	// Find maximum User button text bounds
	for ( int i = 0; i < m_pcDMBP->cButtons && m_sizeButton.cx < sizeBtnMax.cx; i++ )
	{
		::SetRectEmpty(&rc);
		lpcszText = m_pcDMBP->pacButtons[i].lpcszText;
		if ( !lpcszText )
			lpcszText = _T("");

		int cyText = ::DrawTextEx(hdc, (LPTSTR)lpcszText, -1, &rc, unDTParams, NULL);
		ATLASSERT( cyText );
		if ( !cyText )  return FALSE;

		if ( rc.right > m_sizeButton.cx )
			m_sizeButton.cx = rc.right;
		if ( rc.bottom > m_sizeButton.cy )
			m_sizeButton.cy = rc.bottom;
	}

	// Adjust button size
	m_sizeButton.cx += m_sizeBaseUnit.cx * 2;		// 75
	m_sizeButton.cy = (m_sizeButton.cy * 29) / 16;	// 23

	// Restrict button size
	if ( m_sizeButton.cx < sizeBtnMin.cx )
		m_sizeButton.cx = sizeBtnMin.cx;
	if ( m_sizeButton.cy < sizeBtnMin.cy )
		m_sizeButton.cy = sizeBtnMin.cy;
	
	if ( m_sizeButton.cx > sizeBtnMax.cx )
		m_sizeButton.cx = sizeBtnMax.cx;
	if ( m_sizeButton.cy > sizeBtnMax.cy )
		m_sizeButton.cy = sizeBtnMax.cy;

	return TRUE;
}

BOOL CDetailedMessageWindow::CDialogTempalte::InitializeTextSize(HDC hdc, 
														const SIZE& sizeBounds)
{
	ATLASSERT( hdc );
	ATLASSERT( m_pcDMBP );

	return CDetailedMessageWindow::CalcualteTextSize(hdc, m_pcDMBP->lpcszText, 
													 sizeBounds, &m_sizeText);
}

BOOL CDetailedMessageWindow::CDialogTempalte::Initialize(HDC hdc)
{
	ATLASSERT( hdc );
	ATLASSERT( m_pcDMBP );
	ATLASSERT( !::IsBadReadPtr(m_pcDMBP, sizeof(DTLMSGBOXPARAMS)) );		
	ATLASSERT( !::IsBadReadPtr(m_pcDMBP->pacButtons, 
					m_pcDMBP->cButtons * sizeof(DTLMSGBOXBUTTONPARAMS)) );

	// Define owner window
	HWND hwndOwner = CDetailedMessageWindow::GetActualOwnerWindow(m_pcDMBP->hwndOwner);

	// Collect Font parameters
	InitializeFontParameters(hdc);

	// Collect display parameters
	BOOL bResult = InitializeBaseUnits(hdc);
	ATLASSERT( bResult );
	if ( !bResult )   return FALSE;

	// Monitor size
#if (_WIN32_WINNT >= 0x0500)
//		HMONITOR hMonitor = ::MonitorFromWindow(hwndOwner, MONITOR_DEFAULTTONEAREST);
	HMONITOR hMonitor = ::MonitorFromWindow(hwndOwner, MONITOR_DEFAULTTOPRIMARY);

	MONITORINFO mni = { sizeof(MONITORINFO) };
	::GetMonitorInfo(hMonitor, &mni);

	SIZE sizeMonitor = {
		mni.rcWork.right - mni.rcWork.left,
		mni.rcWork.bottom - mni.rcWork.top
	};
#else // (_WIN32_WINNT >= 0x0500)

	RECT rcMonitor;
	::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcMonitor, 0);

	SIZE sizeMonitor = {
		rcMonitor.right - rcMonitor.left,
		rcMonitor.bottom - rcMonitor.top
	};

#endif // (_WIN32_WINNT >= 0x0500)

	// Calculate size of dialog non-client parts
	SIZE sizeNC = {
		::GetSystemMetrics(SM_CXDLGFRAME) * 2,
		::GetSystemMetrics(SM_CYDLGFRAME) * 2 + ::GetSystemMetrics(SM_CYCAPTION)
	};

	// Calculate maximum dialog size
	SIZE sizeClientMax = { 
		sizeMonitor.cx * 14 / 16 - sizeNC.cx,
		sizeMonitor.cy *  7 / 16 - sizeNC.cy
	};

	// Padding
	const LONG lPdn = GetPadding();	
	const LONG lPdnDbl = 2 * lPdn;
	const LONG lPdnHlf = GetSmallPadding();

	// Calculate caption bar size
	int cxCaptionBar;
	bResult = CalculateCaptionBarWidth(hdc, &cxCaptionBar);
	ATLASSERT( bResult );
	if ( !bResult )   return FALSE;

	// Calculate icon size and its area
	bResult = InitializeIconSize(hdc);
	ATLASSERT( bResult );
	if ( !bResult )   return FALSE;

	SIZE sizeIconArea;
	CalculateAreaSize(&m_sizeIcon, IsIconNeeded(), &sizeIconArea);

	// Calculate minimal text area
	SIZE sizeMinTextArea;
	if ( IsTextNeeded() )
	{
		sizeMinTextArea.cx = lPdnDbl;
		sizeMinTextArea.cy = m_sizeBaseUnit.cy + lPdnDbl;
	}
	else
	{
		sizeMinTextArea.cx = sizeMinTextArea.cy = 0;
	}

	// Calculate button size
	bResult = InitializeButtonSize(hdc,  &sizeClientMax);
	ATLASSERT( bResult );
	if ( !bResult )   return FALSE;

	// Calculate size required for all buttons
	int cStdBtns = GetStdButtonsNumber();
	m_cUsrBtns = m_pcDMBP->cButtons;
	int cAllBtns = m_pcDMBP->cButtons + cStdBtns;

	SIZE sizeButtons = {
		CalculateButtonsWidth(cAllBtns),
		CalculateButtonsHeight(cAllBtns)
	};

	SIZE sizeButtonArea;
	CalculateAreaSize(&sizeButtons, (BOOL)cAllBtns, &sizeButtonArea);

	// Calculate minimal dialog size required to place all the elements in.
	SIZE sizeClientMin;
	sizeClientMin.cx = sizeIconArea.cx + sizeMinTextArea.cx;
	sizeClientMin.cx = max(cxCaptionBar, sizeClientMin.cx);
	sizeClientMin.cx = max(sizeButtonArea.cx, sizeClientMin.cx);
				
	sizeClientMin.cy = max(sizeMinTextArea.cy, sizeIconArea.cy);
	sizeClientMin.cy += sizeButtonArea.cy;

	// Restrict minimal size
	if ( sizeClientMin.cx > sizeClientMax.cx )
	{		
		int cxButtons = sizeClientMax.cx - lPdnDbl; 
		cAllBtns = (cxButtons + lPdnHlf)/(m_sizeButton.cx + lPdnHlf);
		
		m_cUsrBtns = cAllBtns - cStdBtns;
		if ( m_cUsrBtns > m_pcDMBP->cButtons )
		{
			m_cUsrBtns = m_pcDMBP->cButtons;
			cAllBtns = m_cUsrBtns + cStdBtns;
		}

		sizeButtons.cx = CalculateButtonsWidth(cAllBtns);
		sizeButtons.cy = CalculateButtonsHeight(cAllBtns);
		CalculateAreaSize(&sizeButtons, (BOOL)cAllBtns, &sizeButtonArea);

		sizeClientMin.cx = sizeIconArea.cx + sizeMinTextArea.cx;
		sizeClientMin.cx = max(sizeClientMin.cx, cxCaptionBar);
		sizeClientMin.cx = max(sizeClientMin.cx, sizeButtonArea.cx);
		
		if ( sizeClientMin.cx > sizeClientMax.cx )
		{
			sizeClientMin.cx = sizeClientMax.cx;
		}
	}

	if ( sizeClientMin.cy > sizeClientMax.cy )
	{
		sizeClientMin.cy = sizeClientMax.cy;
	}

	// Try to layout all child elements inside minimal portion of the screen.
	for ( LONG lPortion = 8; lPortion < 15; lPortion++ )
	{
		m_sizeClient.cx = sizeMonitor.cx * lPortion / 16 - sizeNC.cx;
		m_sizeClient.cy = sizeMonitor.cy * lPortion / 32 - sizeNC.cy;				

		if ( m_sizeClient.cx >= sizeClientMin.cx )
		{
			SIZE sizeBounds = { 
				m_sizeClient.cx - sizeIconArea.cx - lPdnDbl,
				m_sizeClient.cy - sizeButtonArea.cy - lPdnDbl
			};
			
			bResult = InitializeTextSize(hdc, sizeBounds);
			ATLASSERT( bResult );
			if ( !bResult )   return FALSE;

			SIZE sizeTextArea;
			CalculateAreaSize(&m_sizeText, IsTextNeeded(), &sizeTextArea);

			m_sizeClient.cx = sizeTextArea.cx + sizeIconArea.cx;
			m_sizeClient.cy = GetIconAndTextAreaHeight() + sizeButtonArea.cy;

			if ( m_sizeClient.cx <= sizeClientMax.cx && 
				m_sizeClient.cy <= sizeClientMax.cy )
			{
				// All child elements fit to the dialog of current size.
				break;
			}
		}
	}

	// Restrict client size
	if ( m_sizeClient.cx < sizeClientMin.cx )
		m_sizeClient.cx = sizeClientMin.cx;
	if ( m_sizeClient.cy < sizeClientMin.cy )
		m_sizeClient.cy = sizeClientMin.cy;

	m_bScrollText = m_sizeText.cx && (m_sizeClient.cx > sizeClientMax.cx || 
									  m_sizeClient.cy > sizeClientMax.cy);

	// Restrict text area to fit maximum dialog box size.
	if ( m_bScrollText )
	{
		if ( m_sizeClient.cx > sizeClientMax.cx )
			m_sizeClient.cx = sizeClientMax.cx;
		if ( m_sizeClient.cy > sizeClientMax.cy )
			m_sizeClient.cy = sizeClientMax.cy;

		m_sizeText.cx = m_sizeClient.cx - sizeIconArea.cx - lPdnDbl;
		m_sizeText.cy = m_sizeClient.cy - sizeButtonArea.cy - lPdnDbl;
	}

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Helpers: Dialog template

int CDetailedMessageWindow::CDialogTempalte::GetControlsNumber() const
{
	ATLASSERT( m_pcDMBP );

	int cStdBtns = m_cUsrBtns;

	if ( IsDetailButtonNeeded() )		
		cStdBtns++;

	if ( IsHelpButtonNeeded() )
		cStdBtns++;

	if ( IsIconNeeded() )
		cStdBtns++;

	if ( IsTextNeeded() )
		cStdBtns++;

	return cStdBtns;
}

SIZE_T CDetailedMessageWindow::CDialogTempalte::CalculateDlgHdrSize() const
{
	ATLASSERT( m_pcDMBP );

	SIZE_T cbResult = sizeof(WORD) * 11 + 
					  sizeof(DWORD) * 3 +
					  sizeof(BYTE) * 2 +
					  (wcslen(CT2W(m_pcDMBP->lpcszCaption)) + 1) * sizeof(WCHAR) +
					  (wcslen(m_szFontFaceName) + 1) * sizeof(WCHAR);

	cbResult = reinterpret_cast<SIZE_T>(AlignByDWord((void*)cbResult));

	return cbResult;
}

void* CDetailedMessageWindow::CDialogTempalte::AddDlgHdr(void* pBuffer, 
														  const RECT* pcrc) const
{
	ATLASSERT( !::IsBadReadPtr(pcrc, sizeof(RECT)) );
	ATLASSERT( m_pcDMBP );

	const DWORD dwDlgStyle = WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_VISIBLE|WS_CLIPCHILDREN|
							 DS_MODALFRAME|DS_SETFONT|DS_3DLOOK;

	const DWORD dwDlgExStyle = (IsContextHelpNeeded() ? WS_EX_CONTEXTHELP : 0) |
#if (_WIN32_WINNT >= 0x0500)
							   (IsRTLLayout() ? WS_EX_LAYOUTRTL : 0) |
#endif //(_WIN32_WINNT >= 0x0500)
							   (IsToolWindowNeeded() ? WS_EX_TOOLWINDOW : 0);
	
	MultyTypePtr ptr;
	ptr.lpw = (WORD*)pBuffer;						 

	// Fill the DLGTEMPLATEEX structure.
	*ptr.lpw++ = 1;									// dialog version
	*ptr.lpw++ = 0xFFFF;							// signature
	*ptr.lpdw++ = m_pcDMBP->dwCtxHelpID;			// help ID
	*ptr.lpdw++ = dwDlgExStyle;						// extended style
	*ptr.lpdw++ = dwDlgStyle;						// style
	*ptr.lpw++ = GetControlsNumber();				// number of child items
	*ptr.lpw++ = WORD(pcrc->left);					// x
	*ptr.lpw++ = WORD(pcrc->top);					// y
	*ptr.lpw++ = WORD(pcrc->right - pcrc->left);	// cx
	*ptr.lpw++ = WORD(pcrc->bottom - pcrc->top);	// cy
	*ptr.lpw++ = 0;									// menu
	*ptr.lpw++ = 0;									// class

	// Set Title
	LPWSTR lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
	wcscpy(lpwszText, m_pcDMBP->lpcszCaption ? CT2W(m_pcDMBP->lpcszCaption) : L"");
	size_t cLength = wcslen(lpwszText);
	ptr.lpw = reinterpret_cast<WORD*>(lpwszText + cLength + 1);

	// Add font information if DS_SETFONT is used. 
	if ( dwDlgStyle & DS_SETFONT )
	{
		*ptr.lpw++ = m_cyFontPointSize;		// Point size 
		*ptr.lpw++ = m_wFontWeight;			// Weight
		*ptr.lpb++ = m_bFontItalic;			// italic flag
		*ptr.lpb++ = m_btFontCharset;		// charset

		// Set Font name
		lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
		wcscpy(lpwszText, m_szFontFaceName);
		cLength = wcslen(lpwszText);
		ptr.lpw = reinterpret_cast<WORD*>(lpwszText + cLength + 1);
	}
	
	// Make sure the next item starts on a DWORD boundary. 
	ptr.lpw = reinterpret_cast<WORD*>(AlignByDWord(ptr.lpw));

	return ptr.lpw;
}

SIZE_T CDetailedMessageWindow::CDialogTempalte::CalculateDlgIconSize() const
{
	ATLASSERT( m_pcDMBP );
	ATLASSERT( IsIconNeeded() );

	WCHAR szWndClass[] = L"STATIC";

	SIZE_T cbResult = sizeof(WORD) * 7 + 
					  sizeof(DWORD) * 4 +
					  sizeof(szWndClass);

	cbResult = reinterpret_cast<SIZE_T>(AlignByDWord((void*)cbResult));

	return cbResult;
}

void* CDetailedMessageWindow::CDialogTempalte::AddDlgIcon(void* pBuffer, 
														   const RECT* pcrc) const
{
	ATLASSERT( pBuffer );
	ATLASSERT( !::IsBadReadPtr(pcrc, sizeof(RECT)) );
	ATLASSERT( m_pcDMBP );
	ATLASSERT( IsIconNeeded() );

	const DWORD dwStyle = WS_VISIBLE|WS_CHILD|SS_ICON;
	WCHAR szWndClass[] = L"STATIC";

	MultyTypePtr ptr;
	ptr.lpw = (WORD*)pBuffer;

	// Fill DLGITEMTEMPLATEEX structure	
	*ptr.lpdw++ = m_pcDMBP->dwCtxHelpID;			// HelpID
	*ptr.lpdw++ = 0;								// ExtendedStyle
	*ptr.lpdw++ = dwStyle;							// Style;
	*ptr.lpw++ = WORD(pcrc->left);					// x
	*ptr.lpw++ = WORD(pcrc->top);					// y
	*ptr.lpw++ = WORD(pcrc->right - pcrc->left);	// cx
	*ptr.lpw++ = WORD(pcrc->bottom - pcrc->top);	// cy
	*ptr.lpdw++ = -1; // IDC_STATIC					// Control ID

	// Fill in class i.d., this time by name.
	LPWSTR lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
	wcscpy(lpwszText, szWndClass);
	ptr.lpw = reinterpret_cast<WORD*>(lpwszText) + sizeof(szWndClass)/sizeof(WCHAR);

	// Resource ID
	*ptr.lpw++ = 0xFFFF;
	*ptr.lpw++ = 0;

	// Advance pointer over nExtraStuff WORD.
	*ptr.lpw++ = 0;

	// Make sure the next item starts on a DWORD boundary. 
	ptr.lpw = reinterpret_cast<WORD*>(AlignByDWord(ptr.lpw));

	return ptr.lpw;
}

SIZE_T CDetailedMessageWindow::CDialogTempalte::CalculateDlgTextSize() const
{
	ATLASSERT( m_pcDMBP );
	ATLASSERT( IsTextNeeded() && m_pcDMBP->lpcszText );

	WCHAR szWndClass[] = L"STATIC";

	SIZE_T cbResult = sizeof(WORD) * 5 + 
					  sizeof(DWORD) * 4 +
					  sizeof(szWndClass) + 
					  (wcslen(CT2W(m_pcDMBP->lpcszText)) + 1) * sizeof(WCHAR);

	cbResult = reinterpret_cast<SIZE_T>(AlignByDWord((void*)cbResult));

	return cbResult;
}

void* CDetailedMessageWindow::CDialogTempalte::AddDlgText(void* pBuffer, 
														   const RECT* pcrc) const
{
	ATLASSERT( pBuffer );
	ATLASSERT( !::IsBadReadPtr(pcrc, sizeof(RECT)) );
	ATLASSERT( m_pcDMBP );
	ATLASSERT( IsTextNeeded() && m_pcDMBP->lpcszText );

	const DWORD dwStyle = WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_READONLY|
						  (m_bScrollText ? WS_VSCROLL : 0);
	WCHAR szWndClass[] = L"EDIT";

	MultyTypePtr ptr;
	ptr.lpw = (WORD*)pBuffer;

	// Fill DLGITEMTEMPLATEEX structure		
	*ptr.lpdw++ = 0;								// HelpID
	*ptr.lpdw++ = 0;								// ExtendedStyle
	*ptr.lpdw++ = dwStyle;							// Style;
	*ptr.lpw++ = WORD(pcrc->left);					// x
	*ptr.lpw++ = WORD(pcrc->top);					// y
	*ptr.lpw++ = WORD(pcrc->right - pcrc->left);	// cx
	*ptr.lpw++ = WORD(pcrc->bottom - pcrc->top);	// cy
	*ptr.lpdw++ = -1; // IDC_STATIC					// Control ID

	// Fill in class i.d., this time by name.
	LPWSTR lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
	wcscpy(lpwszText, szWndClass);

	ptr.lpw = reinterpret_cast<WORD*>(lpwszText) + sizeof(szWndClass)/sizeof(WCHAR);

	// Copy the text of the first item.
	lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
	wcscpy(lpwszText, CT2W(m_pcDMBP->lpcszText));
	size_t cLength = wcslen(lpwszText);
	ptr.lpw = reinterpret_cast<WORD*>(lpwszText + cLength + 1);

	// Advance pointer over nExtraStuff WORD.
	*ptr.lpw++ = 0;

	// Make sure the next item starts on a DWORD boundary. 
	ptr.lpw = reinterpret_cast<WORD*>(AlignByDWord(ptr.lpw));

	return ptr.lpw;
}

SIZE_T CDetailedMessageWindow::CDialogTempalte::CalculateDlgButtonSize(
														LPCTSTR lpcszText) const
{
	ATLASSERT( m_pcDMBP );

	WCHAR szWndClass[] = L"BUTTON";

	SIZE_T cbResult = sizeof(WORD) * 5 + 
					  sizeof(DWORD) * 4 +
					  sizeof(szWndClass) + 
				  	  (wcslen(lpcszText ? CT2W(lpcszText) : L"") + 1) * sizeof(WCHAR);

	cbResult = reinterpret_cast<SIZE_T>(AlignByDWord((void*)cbResult));

	return cbResult;
}

void* CDetailedMessageWindow::CDialogTempalte::AddDlgButton(void* pBuffer, 
												const DTLMSGBOXBUTTONPARAMS* pcInfo, 
												BOOL bDefault, const RECT* pcrc) const
{
	ATLASSERT( !::IsBadReadPtr(pcrc, sizeof(RECT)) );
	ATLASSERT( !::IsBadReadPtr(pcInfo, sizeof(DTLMSGBOXBUTTONPARAMS)) );
	ATLASSERT( m_pcDMBP );

	WCHAR szWndClass[] = L"BUTTON";		

	MultyTypePtr ptr;
	ptr.lpw = (WORD*)pBuffer;

	// Fill DLGITEMTEMPLATEEX structure	
	const DWORD dwBtnStyle = (bDefault ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON)|
								WS_VISIBLE|WS_CHILD|WS_TABSTOP;
	*ptr.lpdw++ = pcInfo->dwCtxHelpID;				// HelpID
	*ptr.lpdw++ = 0;								// ExtendedStyle
	*ptr.lpdw++ = dwBtnStyle;						// Style;
	*ptr.lpw++ = WORD(pcrc->left);					// x
	*ptr.lpw++ = WORD(pcrc->top);					// y
	*ptr.lpw++ = WORD(pcrc->right - pcrc->left);	// cx
	*ptr.lpw++ = WORD(pcrc->bottom - pcrc->top);	// cy
	*ptr.lpdw++ = pcInfo->unID;						// Control ID

	// Fill in class i.d., this time by name.
	LPWSTR lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
	wcscpy(lpwszText, szWndClass);

	ptr.lpw = reinterpret_cast<WORD*>(lpwszText) + sizeof(szWndClass)/sizeof(WCHAR);

	// Copy the text of the first item.
	lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
	wcscpy(lpwszText, pcInfo->lpcszText ? CT2W(pcInfo->lpcszText) : L"");
	size_t cLength = wcslen(lpwszText);
	ptr.lpw = reinterpret_cast<WORD*>(lpwszText + cLength + 1);
	
	// Advance pointer over nExtraStuff WORD.
	*ptr.lpw++ = 0;

	// Make sure the next item starts on a DWORD boundary. 
	ptr.lpw = reinterpret_cast<WORD*>(AlignByDWord(ptr.lpw));

	return ptr.lpw;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Operations

SIZE_T CDetailedMessageWindow::CDialogTempalte::CalculateDlgTmplSize() const
{
	ATLASSERT( m_pcDMBP );

	SIZE_T cResult = CalculateDlgHdrSize();

	if ( IsIconNeeded() )
		cResult += CalculateDlgIconSize();

	if ( IsTextNeeded() )
		cResult += CalculateDlgTextSize();

	if ( m_cUsrBtns )
	{
		for ( int i = 0; i < m_cUsrBtns; i++ )
		{	
			LPCTSTR lpcszText = m_pcDMBP->pacButtons[i].lpcszText;

			cResult += CalculateDlgButtonSize(lpcszText);
		}
	}

	if ( IsHelpButtonNeeded() )
	{
		LPCTSTR lpcszText = m_pcDMBP->lpcszHelp;

		cResult += CalculateDlgButtonSize(lpcszText);
	}

	if ( IsDetailButtonNeeded() )
	{
		LPCTSTR lpcszText = m_pcDMBP->lpcszDetailOn;

		cResult += CalculateDlgButtonSize(lpcszText);
	}

	return cResult;
}

BOOL CDetailedMessageWindow::CDialogTempalte::GenerateDlgTmpl(void* lpBuffer) const
{		
	ATLASSERT( lpBuffer );
	ATLASSERT( m_pcDMBP );

	// Add dialog template header 
	RECT rc;
	rc.left = 0; 
	rc.top = 0; 
	rc.right = m_sizeClient.cx; 
	rc.bottom = m_sizeClient.cy;

	RECTPix2DU(&rc);	
	void* lpTmpl = AddDlgHdr(lpBuffer, &rc);

	// Add icon to dialog template
	if ( IsIconNeeded() )
	{
		rc.left = GetPadding();
		rc.top = GetPadding();
		rc.right = rc.left + m_sizeIcon.cx;
		rc.bottom = rc.top + m_sizeIcon.cy;

		RECTPix2DU(&rc);
		lpTmpl = AddDlgIcon(lpTmpl, &rc);
	}

	// Add text to dialog template
	if ( IsTextNeeded() )
	{
		SIZE sizeIconArea;
		CalculateAreaSize(&m_sizeIcon, TRUE, &sizeIconArea);

		rc.left = sizeIconArea.cx + GetPadding();
		rc.top = GetPadding();
		rc.right = rc.left + m_sizeText.cx;
		rc.bottom = rc.top + m_sizeText.cy;

		RECTPix2DU(&rc);
		lpTmpl = AddDlgText(lpTmpl, &rc);
	}

	// Add buttons
	int cyIconAndTextArea = GetIconAndTextAreaHeight();
	SIZE sizeButtons = { GetAllButtonsWidth(),	GetAllButtonsHeight() };

	// Add all user's buttons to dialog template
	if ( m_cUsrBtns )
	{
		rc.right = m_sizeClient.cx - GetPadding();
		rc.left = rc.right - sizeButtons.cx;
		rc.top = cyIconAndTextArea + GetPadding();				
		rc.bottom = rc.top + sizeButtons.cy;

		RECTPix2DU(&rc);
		LONG cxUsrBtn = XPix2DU(m_sizeButton.cx);
		LONG cxUsrBtnGap = XPix2DU(GetSmallPadding());

		for ( int i = 0; i < m_cUsrBtns; i++ )
		{		
			rc.right = rc.left + cxUsrBtn;

			lpTmpl = AddDlgButton(lpTmpl, &m_pcDMBP->pacButtons[i], 
								i == m_pcDMBP->iDefaultButton, &rc);

			rc.left = rc.right + cxUsrBtnGap;
		}
	}

	// Add "Help" button to dialog template
	if ( IsHelpButtonNeeded() )
	{
		rc.left = m_sizeClient.cx - GetPadding() - sizeButtons.cx +
				(m_sizeButton.cx + GetSmallPadding()) * m_cUsrBtns;
		rc.right = rc.left + m_sizeButton.cx;		
		rc.top = cyIconAndTextArea + GetPadding();
		rc.bottom = rc.top + m_sizeButton.cy;

		DTLMSGBOXBUTTONPARAMS dmbpDtl = { 
			m_pcDMBP->unHelpID, 
			m_pcDMBP->lpcszHelp, 
			m_pcDMBP->dwHelpCtxHelpID
		}; 

		RECTPix2DU(&rc);
		lpTmpl = AddDlgButton(lpTmpl, &dmbpDtl, FALSE, &rc);
	}

	// Add "Detail" button to dialog template
	if ( IsDetailButtonNeeded() )
	{
		rc.right = m_sizeClient.cx - GetPadding();
		rc.left = rc.right - m_sizeButton.cx;
		rc.top = cyIconAndTextArea + GetPadding();
		rc.bottom = rc.top + m_sizeButton.cy;

		DTLMSGBOXBUTTONPARAMS dmbpDtl = { 
			m_pcDMBP->unDetailID, 
			m_pcDMBP->lpcszDetailOn, 
			m_pcDMBP->dwDetailCtxHelpID
		}; 

		RECTPix2DU(&rc);
		lpTmpl = AddDlgButton(lpTmpl, &dmbpDtl, FALSE, &rc);
	}

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Utilities

BOOL CDetailedMessageWindow::CalcualteTextSize(HDC hdc, LPCTSTR lpcszText, 
											   const SIZE& sizeBounds,
											   SIZE* pSize)
{
	ATLASSERT( hdc );
	ATLASSERT( !::IsBadWritePtr(pSize, sizeof(SIZE)) );

	const UINT unDTParams = DT_CALCRECT|DT_WORDBREAK|DT_EXTERNALLEADING|
							DT_EXPANDTABS|DT_NOPREFIX|DT_EDITCONTROL;

	if ( lpcszText )
	{
		RECT rc = { 0, 0, sizeBounds.cx, sizeBounds.cy };

		int cyText = ::DrawTextEx(hdc, (LPTSTR)lpcszText, -1, &rc, unDTParams, 
									NULL);
		ATLASSERT( cyText );
		if ( !cyText )  return FALSE;

		pSize->cx = rc.right;
		pSize->cy = rc.bottom;
	}
	else
	{
		pSize->cx = pSize->cy = 0;
	}

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Initialization & uninitialization

void CDetailedMessageWindow::InitializeParams(const DTLMSGBOXPARAMS* pcDMBP)
{
	ATLASSERT( !::IsBadReadPtr(pcDMBP, sizeof(DTLMSGBOXPARAMS)) );
	ATLASSERT( !::IsBadReadPtr(pcDMBP->pacButtons, 
								sizeof(DTLMSGBOXBUTTONPARAMS) * pcDMBP->cButtons) );
	ATLASSERT( pcDMBP->iDefaultButton < pcDMBP->cButtons );
	
	m_sizeMinClient.cx = m_sizeMinClient.cx = 0;
	m_cyMaxText = 0;
	m_sizeButton.cx = m_sizeButton.cy = 0;

	if ( pcDMBP->fStyle & DMB_HASHELP ) 
		m_unHelpID = pcDMBP->unHelpID;
	else
		m_unHelpID = 0;

	if ( pcDMBP->fStyle & DMB_HASDETAIL )
	{
		m_unDetailID = pcDMBP->unDetailID;
		m_csDetailOn = pcDMBP->lpcszDetailOn ;
		m_csDetailOff = pcDMBP->lpcszDetailOff;
	}
	else
		m_unDetailID = 0;

	m_hIcon = pcDMBP->hIcon;
}

DLGTEMPLATE* CDetailedMessageWindow::CreateDialogTemplate(const DTLMSGBOXPARAMS* pcDMBP)
{
	ATLASSERT( !::IsBadReadPtr(pcDMBP, sizeof(DTLMSGBOXPARAMS)) );

	CDialogTempalte templ;

	BOOL bResult = templ.Initialize(pcDMBP);
	ATLASSERT( bResult );
	if ( !bResult )   return NULL;

	m_sizeMinClient = *templ.GetClientSize();
	m_lPadding = templ.GetPadding(); 
	m_lSmallPadding = templ.GetSmallPadding();
	m_sizeButton = *templ.GetButtonSize();
	m_cyMaxText = templ.GetTextSize()->cy;

	SIZE_T cbSize = templ.CalculateDlgTmplSize();
	
	DLGTEMPLATE* lpdt = (DLGTEMPLATE*)malloc(cbSize);
	ATLASSERT( lpdt );
	if ( !lpdt )	return NULL;

	bResult = templ.GenerateDlgTmpl(lpdt);
	ATLASSERT( bResult );		
	if ( !bResult )   
	{
		free(lpdt);
		return NULL;
	}

	return lpdt;
}

void CDetailedMessageWindow::FreeDialogTemplate(DLGTEMPLATE* lpdt)
{
	free(lpdt);
}

/////////////////////////////////////////////////////////////////////////////////////////
// Helpers: Child controls

BOOL CDetailedMessageWindow::InitializeChildControls()
{
	ATLASSERT( IsWindow() );
	ATLASSERT( !m_hwndIcon && !m_hwndText && m_vecButtons.empty() && !m_hwndDetail );

	RECT rc; GetClientRect(&rc);
	m_sizeMinClient.cx = rc.right - rc.left;
	m_sizeMinClient.cy = rc.bottom - rc.top;		

	// Find Child controls
	const SIZE_T cClassNameSize = 256;
	LPCTSTR lpcszStaticClassName = _T("STATIC");
	LPCTSTR lpcszEditClassName = _T("EDIT");
	LPCTSTR lpcszButtonClassName = _T("BUTTON");

	for ( HWND hWnd = GetTopWindow(); hWnd; hWnd = ::GetNextWindow(hWnd, GW_HWNDNEXT) )
	{
		TCHAR szClass[cClassNameSize];

		::GetClassName(hWnd, szClass, cClassNameSize);

		if ( lstrcmpi(szClass, lpcszStaticClassName) == 0 )
		{
			m_hwndIcon = hWnd;
		}
		else if ( lstrcmpi(szClass, lpcszEditClassName) == 0 )
		{
			m_hwndText = hWnd;
		}
		else
		{
			ATLASSERT( ( lstrcmpi(szClass, lpcszButtonClassName) == 0 ) );

			m_vecButtons.push_back(hWnd);
		}
	}

	// Set Icon
	if ( m_hwndIcon && m_hIcon )
	{
		::SendMessage(m_hwndIcon, STM_SETICON, WPARAM(m_hIcon), 0);
	}

	// Create gripper
	LONG cxGripper = ::GetSystemMetrics(SM_CXVSCROLL); 
	LONG cyGripper = ::GetSystemMetrics(SM_CYHSCROLL);

	m_hwndGripper = ::CreateWindowEx(0, _T("SCROLLBAR"), NULL, 
									 WS_CHILD|WS_CLIPSIBLINGS|SBS_SIZEGRIP|
									 SBS_SIZEBOX|SBS_BOTTOMALIGN|SBS_RIGHTALIGN, 
									 m_sizeMinClient.cx - cxGripper, 
									 m_sizeMinClient.cy - cyGripper, 
									 cxGripper, cyGripper, m_hWnd, NULL,
									 _AtlBaseModule.GetResourceInstance(), NULL);
	ATLASSERT( m_hwndGripper );
	POINT apt[] = { {0, cyGripper}, {cxGripper, 0}, {cxGripper, cyGripper} };

	HRGN hrgn = ::CreatePolygonRgn(apt, sizeof(apt)/sizeof(POINT), ALTERNATE);
	::SetWindowRgn(m_hwndGripper, hrgn, FALSE);
	::DeleteObject(hrgn);

	// Create Detail control
	RECT rcDetail = { 
		rc.left + m_lPadding, 
		rc.bottom,
		rc.right + m_lPadding,
		rc.bottom
	};

	m_hwndDetail = CreateDetailControl(&rcDetail);

	return TRUE;
}

void CDetailedMessageWindow::AlignChildControls(LONG cx, LONG cy)
{
	ATLASSERT( IsWindow() );
		
	const LONG lDblPadding = 2 * m_lPadding;
	const LONG xRight = cx - m_lPadding;
	
	int cWindows = static_cast<int>(m_vecButtons.size()) + 
									(m_hwndText ? 1 : 0) + 
									(m_hwndDetail ? 1 : 0) +
									(m_hwndGripper ? 1 : 0);

	HDWP hdwp = ::BeginDeferWindowPos(cWindows);

	RECT rc;		

	LONG yNext = 0;

	if ( m_hwndIcon )
	{
		::GetWindowRect(m_hwndIcon, &rc);
		ScreenToClient(&rc);

		yNext += rc.bottom + m_lPadding;
	}

	// Align text
	if ( m_hwndText )
	{
		::GetWindowRect(m_hwndText, &rc);
		ScreenToClient(&rc);

		SIZE sizeText = { xRight - rc.left, rc.bottom - rc.top };

		if ( xRight != rc.right	)
		{
			int cLength = ::GetWindowTextLength(m_hwndText);
			LPTSTR lpszText = (LPTSTR)malloc((cLength + 1) * sizeof(TCHAR));
			ATLASSERT( lpszText );
			if ( lpszText )
			{
				SIZE sizeMax = { sizeText.cx, m_cyMaxText };

				::GetWindowText(m_hwndText, lpszText, cLength + 1);

				SIZE size;

				HDC hdc = ::GetDC(m_hwndText);
				CalcualteTextSize(hdc, lpszText, sizeMax, &size);
				ReleaseDC(hdc);

				if ( sizeText.cy != size.cy )
				{
					sizeText.cy = ( size.cy < m_cyMaxText ) ? size.cy : m_cyMaxText;
				}

				free(lpszText);
			}

			hdwp = ::DeferWindowPos(hdwp, m_hwndText, NULL, 0, 0,
									sizeText.cx, sizeText.cy, 
									SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE);
		}

		LONG y = rc.top + sizeText.cy + m_lPadding;

		if ( y > yNext )
			yNext = y;
	}

	// Align buttons
	if ( !m_vecButtons.empty() )
	{
		yNext += m_lPadding;

		LONG x = xRight - m_sizeButton.cx;

		for ( CHwndVector::reverse_iterator it = m_vecButtons.rbegin();
				it != m_vecButtons.rend(); it++ )
		{
			HWND hwndButton = *it;

			hdwp = ::DeferWindowPos(hdwp, hwndButton, NULL, x, yNext, 0, 0, 
									SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE);

			x -= (m_sizeButton.cx + m_lSmallPadding);
		}

		yNext += m_sizeButton.cy + m_lPadding;
	}

	// Align Gripper control
	if ( m_hwndGripper )
	{
		UINT fMode = IsDetailShown() ? SWP_NOZORDER|SWP_NOSIZE|SWP_SHOWWINDOW 
									 : SWP_NOZORDER|SWP_NOSIZE|SWP_HIDEWINDOW;


		LONG cxGripper = ::GetSystemMetrics(SM_CXVSCROLL);
		LONG cyGripper = ::GetSystemMetrics(SM_CYHSCROLL);

		hdwp = ::DeferWindowPos(hdwp, m_hwndGripper, NULL, cx - cxGripper, 
								cy - cyGripper, 0, 0, fMode);
	}

	// Align Detail control
	if ( m_hwndDetail )
	{
		UINT fMode = IsDetailShown() ? SWP_NOZORDER|SWP_NOACTIVATE|SWP_SHOWWINDOW 
									 : SWP_NOZORDER|SWP_NOACTIVATE|SWP_HIDEWINDOW;

		hdwp = ::DeferWindowPos(hdwp, m_hwndDetail, NULL, m_lPadding, yNext, 
								cx - m_lPadding * 2, cy - yNext - m_lPadding, 
								fMode);
	}

	::EndDeferWindowPos(hdwp);
}

void CDetailedMessageWindow::CalculateMinWindowSize(SIZE* pSize) const
{
	ATLASSERT( !IsBadWritePtr(pSize, sizeof(SIZE)) );

	DWORD_PTR fExStyle = GetWindowLongPtr(GWL_EXSTYLE);

	// Get Metrics
	const LONG dyCaption = (fExStyle & WS_EX_TOOLWINDOW)
								? ::GetSystemMetrics(SM_CYSMCAPTION)
								: ::GetSystemMetrics(SM_CYCAPTION);

	pSize->cx = m_sizeMinClient.cx + 
		2 * ::GetSystemMetrics(SM_CXSIZEFRAME);
	pSize->cy = m_sizeMinClient.cy * 2 + 
		2 * ::GetSystemMetrics(SM_CYSIZEFRAME) + dyCaption;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Operations: User Commads

void CDetailedMessageWindow::ShowDetail(BOOL bShow)
{
	ATLASSERT( IsWindow() );

	DWORD_PTR fExStyle = GetWindowLongPtr(GWL_EXSTYLE);

	// Get Metrics
	const LONG dyCaption = (fExStyle & WS_EX_TOOLWINDOW)
								? ::GetSystemMetrics(SM_CYSMCAPTION)
								: ::GetSystemMetrics(SM_CYCAPTION);

	const LONG lDblPadding = 2 * m_lPadding;
	
	const LONG cxBaseBorder = ::GetSystemMetrics(SM_CXDLGFRAME);
	const LONG cyBaseBorder = ::GetSystemMetrics(SM_CYDLGFRAME);

	// Calculate differences of window border sizes
	const LONG dxBorder = ::GetSystemMetrics(SM_CXSIZEFRAME) - cxBaseBorder;
	const LONG dyBorder = ::GetSystemMetrics(SM_CYSIZEFRAME) - cyBaseBorder;

	if ( bShow )
	{
		// Make window frame thick (sizeable)
		ModifyStyle(DS_MODALFRAME, WS_THICKFRAME, 0);

		// Restore default system menu for sizable window
		GetSystemMenu(TRUE);

		// Client size
		SIZE sizeClient = { m_sizeMinClient.cx, m_sizeMinClient.cy * 2 };

		RECT rc;  GetWindowRect(&rc);
		SetWindowPos(NULL, rc.left - dxBorder, rc.top - dyBorder, 
					 sizeClient.cx + 2 * (dxBorder + cxBaseBorder), 
					 sizeClient.cy + 2 * (dyBorder + cyBaseBorder) + dyCaption, 
					 SWP_NOZORDER|SWP_NOACTIVATE|SWP_DRAWFRAME|SWP_FRAMECHANGED);

		// Change "Detail" button caption
		if ( m_unDetailID )
		{
			HWND hwndDetailButton = GetDlgItem(m_unDetailID);
			::SetWindowText(hwndDetailButton, m_csDetailOff.c_str());
		}

		// Set child controls positions
		AlignChildControls(sizeClient.cx, sizeClient.cy);
	}
	else
	{
		// Make window frame dialog
		ModifyStyle(WS_THICKFRAME, DS_MODALFRAME, 0);

		// Modify system menu. Remove all items except "Move" and "Close".
		HMENU hMenu = GetSystemMenu(FALSE);
		
		for ( int nPos = 0; nPos < ::GetMenuItemCount(hMenu); )
		{
			const UINT unID = ::GetMenuItemID(hMenu, nPos);

			if ( unID == SC_CLOSE || unID == SC_MOVE )
				nPos++;
			else
				::DeleteMenu(hMenu, nPos, MF_BYPOSITION);
		}

		// Client size
		SIZE sizeClient = { m_sizeMinClient.cx, m_sizeMinClient.cy };

		RECT rc;  GetWindowRect(&rc);
		SetWindowPos(NULL, rc.left + dxBorder, rc.top + dyBorder, 
					 sizeClient.cx + 2 * cxBaseBorder, 
					 sizeClient.cy + 2 * cyBaseBorder + dyCaption, 
					 SWP_NOZORDER|SWP_NOACTIVATE|SWP_DRAWFRAME|SWP_FRAMECHANGED);

		// Change "Detail" button caption
		if ( m_unDetailID )
		{
			HWND hwndDetailButton = GetDlgItem(m_unDetailID);
			::SetWindowText(hwndDetailButton, m_csDetailOn.c_str());
		}

		// Set child controls positions
		AlignChildControls(sizeClient.cx, sizeClient.cy);			
	}
}

void CDetailedMessageWindow::QueryHelp()
{
	HWND hwndOwner = GetWindow(GW_OWNER);

	if ( hwndOwner != GetDesktopWindow() )
	{
		HELPINFO hi;
		hi.cbSize = sizeof(HELPINFO);
		hi.iContextType = HELPINFO_WINDOW;
		hi.iCtrlId = IDHELP;
		hi.hItemHandle = m_hWnd;
		hi.dwContextId = GetWindowContextHelpId();

		POINT pt; ::GetCursorPos(&pt);
		hi.MousePos.x = pt.x;
		hi.MousePos.y = pt.y;

		::SendMessage(hwndOwner, WM_HELP, 0, LPARAM(&hi));
	}
}

void CDetailedMessageWindow::QueryHelp(const HELPINFO* pcHI)
{
	HWND hwndOwner = GetWindow(GW_OWNER);

	if ( hwndOwner != GetDesktopWindow() )
	{
		::SendMessage(hwndOwner, WM_HELP, 0, LPARAM(pcHI));
	}
}

}; // SP namespace

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions