Click here to Skip to main content
15,892,746 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 Library 1.2 Copyright (c) 2003-2005
//
//	File:			spDetailedMessageBox.h	 interface for SP::CDetailedMessageWindow 
//											 class, SP::CDetailedMessageBoxImpl and
//											 SP::CAxDetailedMessageBoxImpl class 
//											 templates.
//
//	Developer(s):	Sergei Pavlovsky
//					sergei_vp@hotmail.com
//
//	Description:	Extended message box providing option to show additional details.
//					CDetailedMessageWindow - provides common operations required by
//					message box.
//					CDetailedMessageBoxImpl - handles standard windows' operations and 
//					messages.
//					window messages.
//					CAxDetailedMessageBoxImpl - handles standard windows' operations 
//					and messages; 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.
/////////////////////////////////////////////////////////////////////////////////////////


#if !defined(__SP_SPDETAILEDMESSAGEBOX_H__INCLUDED__)
#define __SP_SPDETAILEDMESSAGEBOX_H__INCLUDED__

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#include <vector>
#include <string>

namespace SP
{

/////////////////////////////////////////////////////////////////////////////////////////
// Structures

enum DTLMSGBOXSTYLES
{
	DMB_TOOLWINDOW		= 0x0001,
	DMB_CONTEXTHELP		= 0x0002,

#if (_WIN32_WINNT >= 0x0500)

	DMB_RTLREADING		= 0x0004,

#endif // (_WIN32_WINNT >= 0x0500)

	DMB_HASDETAIL		= 0x0008,	
	DMB_HASHELP			= 0x0010
};

typedef struct
{
	UINT	unID;
	LPCTSTR	lpcszText;
	DWORD	dwCtxHelpID;
} DTLMSGBOXBUTTONPARAMS, *LPDTLMSGBOXBUTTONPARAMS;

typedef struct
{
	UINT cbSize;
    HWND hwndOwner;
	DWORD fStyle;
	HFONT hFont;
    DWORD dwCtxHelpID;

	LPCTSTR lpcszCaption;
	HICON hIcon;
    LPCTSTR lpcszText;

	int cButtons; 
	const DTLMSGBOXBUTTONPARAMS* pacButtons;
	int iDefaultButton;

	UINT unHelpID;
	DWORD dwHelpCtxHelpID;
	LPCTSTR lpcszHelp;

	UINT unDetailID;
	DWORD dwDetailCtxHelpID;
	LPCTSTR lpcszDetailOn;
	LPCTSTR lpcszDetailOff;	

} DTLMSGBOXPARAMS, *LPDTLMSGBOXPARAMS;

/////////////////////////////////////////////////////////////////////////////////////////
// CDetailedMessageBoxBase class

class CDetailedMessageWindow : public CWindow
{
// Types
protected:
	typedef std::vector<HWND> CHwndVector;
	typedef std::basic_string<TCHAR> CTString;

// Helper classes	
private:
	// CDialogTempalte class
	friend class CDialogTempalte;

	class CDialogTempalte
	{
	// Types
	private:	
		union MultyTypePtr
		{
			BYTE*	lpb;
			WORD*	lpw;
			DWORD*	lpdw;
		};

	// Construction & destruction
	public:
		CDialogTempalte()
			: m_pcDMBP(NULL)
		{
		}

		CDialogTempalte(const DTLMSGBOXPARAMS* pcDMBP)
			: m_pcDMBP(NULL)
		{
			if ( !Initialize(pcDMBP) )
				throw false;
		}

	// Initialization & uninitialization
	public:
		BOOL 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 Uninitialize()
		{
			m_pcDMBP = NULL;
		}

	// Helpers: Content
	protected:

#if (_WIN32_WINNT >= 0x0500)

		BOOL IsRTLLayout() const
		{
			ATLASSERT( m_pcDMBP );

			return m_pcDMBP->fStyle & DMB_RTLREADING;
		};

#endif // (_WIN32_WINNT >= 0x0500)

		BOOL IsToolWindowNeeded() const
		{
			ATLASSERT( m_pcDMBP );

			return m_pcDMBP->fStyle & DMB_TOOLWINDOW;
		};

		BOOL IsContextHelpNeeded() const
		{
			ATLASSERT( m_pcDMBP );

			return m_pcDMBP->fStyle & DMB_CONTEXTHELP;
		};

		BOOL IsIconNeeded() const
		{
			ATLASSERT( m_pcDMBP );

			return m_pcDMBP->hIcon != NULL;
		};

		BOOL IsTextNeeded() const
		{
			ATLASSERT( m_pcDMBP );

			return m_pcDMBP->lpcszText != NULL;
		};

		BOOL IsDetailButtonNeeded() const
		{
			ATLASSERT( m_pcDMBP );
			ATLASSERT( (m_pcDMBP->fStyle & DMB_HASDETAIL) == 0 || 
					   (m_pcDMBP->lpcszDetailOn && m_pcDMBP->lpcszDetailOff) );

			return m_pcDMBP->fStyle & DMB_HASDETAIL;
		};

		BOOL IsHelpButtonNeeded() const
		{
			ATLASSERT( m_pcDMBP );
			ATLASSERT( (m_pcDMBP->fStyle & DMB_HASHELP) == 0 || m_pcDMBP->lpcszHelp );

			return m_pcDMBP->fStyle & DMB_HASHELP;
		};

	// Helpers: Calculation of dimensions
	private:
		void 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 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 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 CalculateButtonsHeight(int cButtons) const
		{			
			ATLASSERT( cButtons >= 0 );
			ATLASSERT( m_sizeButton.cx >= 0 && m_sizeButton.cy >= 0 );

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

	// Initialization
	private:
		void 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 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 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 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 InitializeTextSize(HDC hdc, const SIZE& sizeBounds)
		{
			ATLASSERT( hdc );
			ATLASSERT( m_pcDMBP );

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

		BOOL 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
	private:
		LONG XDU2Pix(LONG x) const
		{
			ATLASSERT( m_sizeBaseUnit.cx > 0 );

			return MulDiv(x, m_sizeBaseUnit.cx, 4);
		}

		LONG YDU2Pix(LONG y) const
		{
			ATLASSERT( m_sizeBaseUnit.cy > 0 );

			return MulDiv(y, m_sizeBaseUnit.cy, 8);
		}

		LONG XPix2DU(LONG x) const
		{
			ATLASSERT( m_sizeBaseUnit.cx > 0 );

			return MulDiv(x, 4, m_sizeBaseUnit.cx);
		}

		LONG YPix2DU(LONG y) const
		{
			ATLASSERT( m_sizeBaseUnit.cy > 0 );

			return MulDiv(y, 8, m_sizeBaseUnit.cy);
		}

		void RECTPix2DU(RECT* prc) const
		{
			ATLASSERT( !::IsBadWritePtr(prc, sizeof(RECT)) );
			ATLASSERT( m_sizeBaseUnit.cx > 0 );
			ATLASSERT( m_sizeBaseUnit.cy > 0 );

			prc->left = XPix2DU(prc->left);
			prc->right = XPix2DU(prc->right);
			prc->top = YPix2DU(prc->top);
			prc->bottom = YPix2DU(prc->bottom);
		}

		static void* AlignByDWord(void* lpSrc)
		{
			return (void*)(((reinterpret_cast<ULONG_PTR>(lpSrc) + 3) >> 2) << 2);
		}

		int 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 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* 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 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* 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 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* 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 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* 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
	public:
		SIZE_T 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 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;
		}

	// Accessors
	public:
		LONG GetPadding() const
		{
			ATLASSERT( m_pcDMBP );
			ATLASSERT( m_sizeBaseUnit.cx >= 0 );

			return GetSmallPadding() * 2;	
		}

		LONG GetSmallPadding() const
		{
			ATLASSERT( m_pcDMBP );
			ATLASSERT( m_sizeBaseUnit.cy >= 0 );

			return m_sizeBaseUnit.cx;	
		}

		const SIZE* GetIconSize() const
		{
			ATLASSERT( m_pcDMBP );
			ATLASSERT( m_sizeIcon.cy >= 0 && m_sizeIcon.cy >= 0 );

			return &m_sizeIcon;
		}

		const SIZE* GetTextSize() const
		{
			ATLASSERT( m_pcDMBP );
			ATLASSERT( m_sizeText.cy >= 0 && m_sizeText.cy >= 0 );

			return &m_sizeText;
		}

		LONG GetIconAndTextAreaHeight() const
		{
			ATLASSERT( m_pcDMBP );
			ATLASSERT( m_sizeIcon.cy >= 0 && m_sizeText.cy >= 0 );

			return ( IsIconNeeded() || IsTextNeeded() )
				? max(m_sizeIcon.cy, m_sizeText.cy) + 2 * GetPadding()
				: 0;
		}

		int GetStdButtonsNumber() const
		{
			ATLASSERT( m_pcDMBP );

			int cStdBtns = 0;
			if ( IsDetailButtonNeeded() )		
				cStdBtns++;

			if ( IsHelpButtonNeeded() )		
				cStdBtns++;

			return cStdBtns;
		}

		int GetUsrButtonsNumber() const
		{
			ATLASSERT( m_pcDMBP );

			return m_cUsrBtns;
		}

		const SIZE* GetButtonSize() const
		{
			ATLASSERT( m_pcDMBP );

			return &m_sizeButton;
		}

		LONG GetAllButtonsWidth() const
		{
			ATLASSERT( m_pcDMBP );

			return CalculateButtonsWidth(GetStdButtonsNumber() + GetUsrButtonsNumber());
		}

		LONG GetAllButtonsHeight() const
		{
			ATLASSERT( m_pcDMBP );

			return ( IsDetailButtonNeeded() || IsHelpButtonNeeded() || m_cUsrBtns )
						? m_sizeButton.cy : 0;
		}

		const SIZE* GetClientSize() const  
		{
			ATLASSERT( m_pcDMBP );

			return &m_sizeClient;
		}

	// Attributes
	private:
		const DTLMSGBOXPARAMS* m_pcDMBP;

		// Font parameters
		WORD m_cyFontPointSize;
		WORD m_wFontWeight;
		BOOL m_bFontItalic;
		BYTE m_btFontCharset;	
		WCHAR m_szFontFaceName[LF_FACESIZE];

		// Dialog base units
		SIZE m_sizeBaseUnit;

		// Child element dimensions
		SIZE m_sizeIcon;

		SIZE m_sizeText;
		BOOL m_bScrollText;

		SIZE m_sizeButton;
		int m_cUsrBtns;

		SIZE m_sizeClient;
	};

// Ooerations
protected:
	static HWND GetActualOwnerWindow(HWND hwndOwner)
	{
		return hwndOwner ? hwndOwner : ::GetDesktopWindow();
	}


	static BOOL 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;
	}

// Construction & destruction
public:
	CDetailedMessageWindow()
		: m_unHelpID(0),
		  m_unDetailID(0),
		  m_hIcon(NULL),
		  m_lPadding(0),
		  m_lSmallPadding(0),
		  m_hwndIcon(NULL),
		  m_hwndText(NULL),
		  m_hwndDetail(NULL),
		  m_cyMaxText(0)
	{	
		m_sizeButton.cx = m_sizeButton.cy = 0;
		m_sizeMinClient.cx = m_sizeMinClient.cy = 0;
	}

// Initialization & uninitialization
protected:
	void 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* 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 FreeDialogTemplate(DLGTEMPLATE* lpdt)
	{
		free(lpdt);
	}

// Helpers: Child controls
public:
	BOOL 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 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 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
public:
	BOOL IsDetailShown() const
	{
		DWORD_PTR fStyle = GetWindowLongPtr(GWL_STYLE);

		return (fStyle & WS_THICKFRAME) != NULL;
	}

	void 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 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 QueryHelp(const HELPINFO* pcHI)
	{
		HWND hwndOwner = GetWindow(GW_OWNER);

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

// Overridables
protected:
	virtual HWND CreateDetailControl(const RECT* pcrc) = 0;	

// Accessors
protected:
	UINT _GetHelpCmdID() const
	{
		return m_unHelpID;
	}

	UINT _GetDetailCmdID() const
	{
		return m_unDetailID;
	}

	LPCTSTR _GetDetailOnText() const
	{
		return m_csDetailOn.c_str();
	}

	LPCTSTR _GetDetailOffText() const
	{
		return m_csDetailOff.c_str();
	}


	HWND _GetIconHwnd() const
	{
		return m_hwndIcon;
	}

	HWND _GetTextHwnd() const
	{
		return m_hwndText;
	}

	const int _GetButtonsCount() const
	{
		return static_cast<int>(m_vecButtons.size());
	}

	HWND _GetButtonAt(int index) const
	{
		return m_vecButtons[index];
	}

	HWND _GetGripperHwnd() const
	{
		return m_hwndGripper;
	}

	HWND _GetDetailHwnd() const
	{
		return m_hwndDetail;
	}

	const SIZE& _GetMinClientSize() const
	{
		return m_sizeMinClient;
	}

	LONG _GetMaxTextHeight() const
	{
		return m_cyMaxText;
	}

	LONG _GetMinPadding() const
	{
		return m_lPadding;
	}

	LONG _GetSmallPadding() const
	{
		return m_lSmallPadding;
	}

	const SIZE& _GetButtonSize() const
	{
		return m_sizeButton;
	}

// Attributes
private:
	UINT			m_unHelpID;
	UINT			m_unDetailID;
	HICON			m_hIcon;
	CTString		m_csDetailOn;
	CTString		m_csDetailOff;

	HWND			m_hwndIcon;
	HWND			m_hwndText;
	CHwndVector		m_vecButtons;
	HWND			m_hwndGripper;
	HWND			m_hwndDetail;

	SIZE			m_sizeMinClient;
	LONG			m_cyMaxText;
	LONG			m_lPadding;
	LONG			m_lSmallPadding;
	SIZE			m_sizeButton;
};

/////////////////////////////////////////////////////////////////////////////////////////
// CDetailedMessageBoxImpl class template

template <class T, class TBase = CDetailedMessageWindow>
class ATL_NO_VTABLE CDetailedMessageBoxImpl 
	: public CDialogImpl< CDetailedMessageBoxImpl, TBase >
{
// Types
private:
	typedef CDialogImpl< CDetailedMessageBoxImpl, TBase > CBase;

// Message handling
public:
	BEGIN_MSG_MAP(CBase)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo);
		MESSAGE_HANDLER(WM_HELP, OnHelp)
		MESSAGE_HANDLER(WM_COMMAND, OnCommand)
	END_MSG_MAP()

// Supressed inherited operations
private:
	INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow(), LPARAM dwInitParam = NULL)
	{
		return -1;
	}

	HWND Create(HWND hWndParent, LPARAM dwInitParam = NULL)
	{
		return NULL;
	}

	HWND Create(HWND hWndParent, RECT&, LPARAM dwInitParam = NULL)
	{
		return NULL;
	}

// Operations: Modal dialogs
public:
	INT_PTR DoModal(const DTLMSGBOXPARAMS* pcDMBP, LPARAM dwInitParam = NULL)
	{
		ATLASSERT( !IsWindow() );

		InitializeParams(pcDMBP);	

		// Genrate dialog template
		DLGTEMPLATE* lpdt = CreateDialogTemplate(pcDMBP);
		ATLASSERT( lpdt );
		if ( !lpdt )	return -1;

		INT_PTR nResult;

		__try
		{
			HWND hwndOwner = GetActualOwnerWindow(pcDMBP->hwndOwner);

			_AtlWinModule.AddCreateWndData(&m_thunk.cd, 
							static_cast<CDialogImplBaseT<TBase>*>(this));

			nResult = ::DialogBoxIndirectParam(_AtlBaseModule.GetResourceInstance(), 
											   lpdt, hwndOwner, 
											   T::StartDialogProc, dwInitParam);
		}
		__finally
		{
			FreeDialogTemplate(lpdt);
		}

		return nResult;
	}

	BOOL EndDialog(int nRetCode)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		return ::EndDialog(m_hWnd, nRetCode);
	}

// Operations: Modeless dialogs
public:
	HWND Create(const DTLMSGBOXPARAMS* pcDMBP, LPARAM dwInitParam = NULL)
	{
		ATLASSERT( !IsWindow() );
		
		InitializeParams(pcDMBP);

		// Genrate dialog template
		DLGTEMPLATE* lpdt = CreateDialogTemplate(pcDMBP);
		ATLASSERT( lpdt );
		if ( !lpdt )	return -1;

		__try
		{
			HWND hwndOwner = GetActualOwnerWindow(pcDMBP->hwndOwner);

			_AtlWinModule.AddCreateWndData(&m_thunk.cd, 
							static_cast<CDialogImplBaseT<TBase>*>(this));

			HWND hwnd = ::CreateDialogParam(_AtlBaseModule.GetResourceInstance(), lpdt,
											hwndOwner, T::StartDialogProc, dwInitParam);
			ATLASSERT(m_hWnd == hwnd);
		}
		__finally
		{
			FreeDialogTemplate(lpdt);
		}

		return m_hWnd;
	}

	BOOL DestroyWindow()
	{
		ATLASSERT(::IsWindow(m_hWnd));

		return ::DestroyWindow(m_hWnd);
	}

// Message handlers
protected:
	LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;

		if ( !InitializeChildControls() )
		{
			EndDialog(-1);
			return FALSE;
		}

		ShowDetail(FALSE);

		return TRUE;
	}

	LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;	

		AlignChildControls(LOWORD(lParam), HIWORD(lParam));		

		return 0;
	}

	LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		MINMAXINFO* pMaxMinInfo = reinterpret_cast<MINMAXINFO*>(lParam);

		SIZE size;
		CalculateMinWindowSize(&size);

		pMaxMinInfo->ptMinTrackSize.x = size.cx;
		pMaxMinInfo->ptMinTrackSize.y = size.cy;

		return 0;
	}

	LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{		
		WORD wCmdID = LOWORD(wParam);

		if ( wCmdID == _GetHelpCmdID() && _GetHelpCmdID() )
			QueryHelp();
		else if ( wCmdID == _GetDetailCmdID() && _GetDetailCmdID() )
			ShowDetail(!IsDetailShown());
		else
			bHandled = FALSE;

		return 0;
	}

	LRESULT OnHelp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		QueryHelp(reinterpret_cast<const HELPINFO*>(lParam));

		return TRUE;
	}
};

/////////////////////////////////////////////////////////////////////////////////////////
// CAxDetailedMessageBoxImpl class template

template <class T, class TBase = CDetailedMessageWindow>
class ATL_NO_VTABLE CAxDetailedMessageBoxImpl 
	: public CAxDialogImpl< CAxDetailedMessageBoxImpl, TBase >
{
// Types
private:
	typedef CAxDialogImpl< CAxDetailedMessageBoxImpl, TBase > CBase;

// Message handling
public:
	BEGIN_MSG_MAP(CBase)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo);
		MESSAGE_HANDLER(WM_HELP, OnHelp)
		MESSAGE_HANDLER(WM_COMMAND, OnCommand)
	END_MSG_MAP()

// Supressed inherited operations
private:
	INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow(), LPARAM dwInitParam = NULL)
	{
		return -1;
	}

	HWND Create(HWND hWndParent, LPARAM dwInitParam = NULL)
	{
		return NULL;
	}

	HWND Create(HWND hWndParent, RECT&, LPARAM dwInitParam = NULL)
	{
		return NULL;
	}

// Operations: Modal dialogs
public:
	INT_PTR DoModal(const DTLMSGBOXPARAMS* pcDMBP, LPARAM dwInitParam = NULL)
	{
		ATLASSERT( !IsWindow() );

		InitializeParams(pcDMBP);	

		// Genrate dialog template
		DLGTEMPLATE* lpdt = CreateDialogTemplate(pcDMBP);
		ATLASSERT( lpdt );
		if ( !lpdt )	return -1;

		INT_PTR nResult;

		__try
		{
			HWND hwndOwner = GetActualOwnerWindow(pcDMBP->hwndOwner);

			_AtlWinModule.AddCreateWndData(&m_thunk.cd, 
							static_cast<CDialogImplBaseT<TBase>*>(this));

			nResult = ::DialogBoxIndirectParam(_AtlBaseModule.GetResourceInstance(), 
											   lpdt, hwndOwner, 
											   T::StartDialogProc, dwInitParam);
		}
		__finally
		{
			FreeDialogTemplate(lpdt);
		}

		return nResult;
	}

	BOOL EndDialog(int nRetCode)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		return ::EndDialog(m_hWnd, nRetCode);
	}

// Operations: Modeless dialogs
public:
	HWND Create(const DTLMSGBOXPARAMS* pcDMBP, LPARAM dwInitParam = NULL)
	{
		ATLASSERT( !IsWindow() );
		
		InitializeParams(pcDMBP);

		// Genrate dialog template
		DLGTEMPLATE* lpdt = CreateDialogTemplate(pcDMBP);
		ATLASSERT( lpdt );
		if ( !lpdt )	return -1;

		__try
		{
			HWND hwndOwner = GetActualOwnerWindow(pcDMBP->hwndOwner);

			_AtlWinModule.AddCreateWndData(&m_thunk.cd, 
							static_cast<CDialogImplBaseT<TBase>*>(this));

			HWND hwnd = ::CreateDialogParam(_AtlBaseModule.GetResourceInstance(), lpdt,
											hwndOwner, T::StartDialogProc, dwInitParam);
			ATLASSERT(m_hWnd == hwnd);
		}
		__finally
		{
			FreeDialogTemplate(lpdt);
		}

		return m_hWnd;
	}

	BOOL DestroyWindow()
	{
		ATLASSERT(::IsWindow(m_hWnd));

		return ::DestroyWindow(m_hWnd);
	}

// Message handlers
protected:
	LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;

		if ( !InitializeChildControls() )
		{
			EndDialog(-1);
			return FALSE;
		}

		ShowDetail(FALSE);

		return TRUE;
	}

	LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;	

		AlignChildControls(LOWORD(lParam), HIWORD(lParam));		

		return 0;
	}

	LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		MINMAXINFO* pMaxMinInfo = reinterpret_cast<MINMAXINFO*>(lParam);

		SIZE size;
		CalculateMinWindowSize(&size);

		pMaxMinInfo->ptMinTrackSize.x = size.cx;
		pMaxMinInfo->ptMinTrackSize.y = size.cy;

		return 0;
	}

	LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{		
		WORD wCmdID = LOWORD(wParam);

		if ( wCmdID == _GetHelpCmdID() && _GetHelpCmdID() )
			QueryHelp();
		else if ( wCmdID == _GetDetailCmdID() && _GetDetailCmdID() )
			ShowDetail(!IsDetailShown());
		else
			bHandled = FALSE;

		return 0;
	}

	LRESULT OnHelp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		QueryHelp(reinterpret_cast<const HELPINFO*>(lParam));

		return TRUE;
	}
};

}; // SP namespace

#endif // __SP_SPDETAILEDMESSAGEBOX_H__INCLUDED__

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