Click here to Skip to main content
15,896,111 members
Articles / Desktop Programming / WTL

CPPMessageBox v1.0

Rate me:
Please Sign up or sign in to vote.
4.89/5 (53 votes)
14 Feb 2005CPOL22 min read 175.5K   5.2K   128  
Extended MessageBox class
/* ��������� � ����������� ���������
	+ 1.	������� �������� checkbox �� ���������
	2.	������� ���������� �������, ��������� �������� DonAskAgain (�� ������� ���� 
		��� ��� ����� ���������� ������)
	+ 3.	�������������� �������� ���������������� ������ � ����������� �� ����� ������ ������
	+ 4.	�������� ���������� ������ �� �������� ����������
	5.  �������� ����������� ���������� checkbox, �������������� ���� WinXP
*/

#include "stdafx.h"

#ifdef _MFC_VER
	#pragma message("    compiling for MFC")
	#define CPPString	CString	//MFC program
#else
	#pragma message("    compiling for Win32")
	#include <windows.h>
	#include <stdio.h>
	#include <crtdbg.h>
	#include <tchar.h>
	#include "StdString.h"
	#ifdef _UNICODE
	#define CPPString	CStdStringW	//non-MFC program UNICODE
	#else
	#define CPPString	CStdStringA	//non-MFC program ANSI
	#endif
#endif

#include "PPMessageBox.h"
#include "PPHtmlDrawer.h"

//SetWindowLongPtr is available on _WIN64 platform
#ifndef SetWindowLongPtr
#define SetWindowLongPtr SetWindowLong
#endif

#pragma warning(disable : 4127)		// conditional expression is constant

//////////////////////////////////////////////////////////////
//
//
#define PPMSGBOX_DO_NOT_SAVE_CHECKBOX

//////////////////////////////////////////////////////////////
//Uncomments a following line if you want store "DoNotAsk" value to INI file
//
//#define PPMSGBOX_USE_PROFILE_FILE

//The sizes
#define PPMSGBOX_SIZE_SEPX		0
#define PPMSGBOX_SIZE_MSGX		1
#define PPMSGBOX_SIZE_MSGY		2
#define PPMSGBOX_SIZE_BTNX		3
#define PPMSGBOX_SIZE_BTNY		4
#define PPMSGBOX_SIZE_CHKX		5
#define PPMSGBOX_SIZE_CHKY		6
#define PPMSGBOX_SIZE_CTRLX		7
#define PPMSGBOX_SIZE_CTRLY		8


#define PPMSGBOX_BUTTON_DISABLED	_T("%s = %d")
#define PPMSGBOX_INI_FILE			_T("PPMessageBox.ini")
#define PPMSGBOX_REGISTRY_KEY		_T("PPMessageBox")

#define UDM_HTMLDRAWER_REPAINT	   (WM_APP + 100)

class CPPMessageBoxTemplate
{
public:
	CPPMessageBoxTemplate(const PPMSGBOXPARAMS *pMsgBox); //Constructor of CPPMessageBoxTemplate
	virtual ~CPPMessageBoxTemplate();
	int Display();

protected:
#pragma pack(1)
	typedef struct _STRUCT_BUTTONS
	{
		_STRUCT_BUTTONS()
		{
			hWnd = NULL;
			nTypeBtn = 0;
		}
		int nTypeBtn;		//The Type of the Button
		HWND hWnd;		    //HWND of the button
	} STRUCT_BUTTONS;
#pragma pack()

	typedef std::vector<STRUCT_BUTTONS> vecButton;	//Buttons
	vecButton m_vecButtons;

	STRUCT_BUTTONS m_stCheckBtn;
	STRUCT_BUTTONS m_stCheckText;

	DWORD m_dwSizes [PPMSGBOX_SIZE_CTRLY + 1]; //The sizes

	HICON m_hIcon;

	PPMSGBOXPARAMS m_MsgParam;
	UINT m_nDefBtn; //A default button index (0 - to max counts of the buttons)
	HFONT m_hFont;

	DWORD m_dwHeaderHeight;
	DWORD m_dwMessageHeight;
	DWORD m_dwControlHeight;
	DWORD m_dwMoreInfoHeight;

	BOOL m_bIsCancelExist;
	BOOL m_bMoreInfoOpened;

	DWORD m_dwCountdownTimer;

	CPPString m_sDisabledCaption;
	CPPString m_sHeaderSepText;
	CPPString m_sControlSepText;
	CPPString m_sMoreInfoSepText;
	CPPString m_sText;
	CPPString m_sCaption;
	CPPString m_sHeader;
	CPPString m_sMoreInfoOpened; //Text for the opened "MoreInfo" button
	CPPString m_sMoreInfoClosed; //Text for the closed "MoreInfo" button
	CPPString m_sTimeoutText; //Text for the timeout button or the timeout caption

	CPPHtmlDrawer m_pHeader;
	CPPHtmlDrawer m_pDrawer;
	CPPHtmlDrawer m_pMoreInfo;
	HBITMAP m_hBitmapBk;

	SIZE PrepareMessageBox(HWND hwnd);
	void RemoveAllButtons();
	CPPString GetString(LPCTSTR lpszText);
	CPPString GetLocalizedButtonText(DWORD dwBtnID, CPPString sDefText = _T(""));

	void OnInitDialog(HWND hwnd);
	void OnTimer(HWND hwnd);

	//Draw methods
	void DrawBox(HWND hwnd, HDC hDC);
	void DrawSeparator(HDC hDC, LPCRECT lpRect, int nTypeSeparator, LPCTSTR lpszText, int nTextAlign);

	static BOOL CALLBACK MsgBoxDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
};

int PPMessageBox(HWND hwnd,
				LPCTSTR lpszMessage,
				LPCTSTR lpszCaption /*= NULL*/,
				DWORD dwStyle /*= MB_OK | MB_ICONEXCLAMATION*/,
				const PPMSGBOXPARAMS * pMsgBox /*= NULL*/)
{
	PPMSGBOXPARAMS MsgBox;
	if (NULL != pMsgBox)
		MsgBox = *pMsgBox;

	MsgBox.lpszCaption = lpszCaption;
	MsgBox.lpszText = lpszMessage;
	MsgBox.dwStyle = dwStyle;
	MsgBox.hParentWnd = hwnd;

	return PPMessageBoxIndirect(&MsgBox);
} //End of PPMessageBox

int PPMessageBoxIndirect(const PPMSGBOXPARAMS * pMsgBox)
{
	_ASSERT(pMsgBox);
	_ASSERT((NULL != pMsgBox->lpszText) && (0 != pMsgBox->lpszText [0]));
	// cannot have both disabled and timeout seconds
//	_ASSERT((pMsgBox->nDisabledSeconds == 0) || (pMsgBox->nTimeoutSeconds == 0));

	PPMSGBOXPARAMS MsgBox;
	MsgBox = *pMsgBox;
	
	if (MsgBox.hParentWnd == NULL)
	{
		MsgBox.hParentWnd = ::GetActiveWindow();
		if (MsgBox.hParentWnd != NULL)
			MsgBox.hParentWnd = ::GetLastActivePopup(MsgBox.hParentWnd);
	} //if
	
	TCHAR tchCaption[MAX_PATH];
	if ((NULL == MsgBox.lpszCaption) || (0 == MsgBox.lpszCaption [0]))
	{
		if (NULL != MsgBox.hParentWnd)
		{
			::SendMessage(MsgBox.hParentWnd, WM_GETTEXT, MAX_PATH, (LPARAM)tchCaption);
			MsgBox.lpszCaption = tchCaption;
		} //if
		if ((NULL == MsgBox.lpszCaption) || (0 == MsgBox.lpszCaption [0]))
			MsgBox.lpszCaption = _T("Error");
	} //if

#ifdef PPMSGBOX_DO_NOT_SAVE_CHECKBOX
	//ENG: Checks a states of the "DonNotAskAgain" checkboxes
	//RUS: �������� ��������� ������ "�����������������"
	if ((MsgBox.dwStyle & MB_CHECKBOX))
	{
		if ((NULL != MsgBox.lpszModuleName) && (0 != MsgBox.lpszModuleName [0]))
		{
			//ENG: Default value of PPMessageBox return code (checkbox wasn't checked)
			//RUS: �������� ���� �������� PPMessageBox ��-��������� (������ �� ��� ����������)
			DWORD dwData = 0;

			//ENG: Get full path of the current module
			//RUS: �������� ������ ���� � ������������ ������
			TCHAR szModule[_MAX_PATH];
			::GetModuleFileName(NULL, szModule, _MAX_PATH);

			TCHAR szPath[MAX_PATH];
			LPTSTR lpFilePart;
			::GetFullPathName(szModule, _MAX_PATH, szPath, &lpFilePart);

			//ENG: Creates a key value
			//RUS: ������� �������� �����
			CPPString sDoNotAskKey;
			sDoNotAskKey.Format(_T("%s%d"), MsgBox.lpszModuleName, MsgBox.nLine);
#ifdef PPMSGBOX_USE_PROFILE_FILE
			//ENG: An info will be search in the INI file
			//RUS: ����� ���������� ����� ���������� � INI �����
			
			//ENG: Removes a module name from path
			//RUS: ������� ��� ������ �� ����
			if (NULL != lpFilePart)
				*(lpFilePart) = _T('\0');
			
			//ENG: Add an INI filename to path
			//RUS: ��������� ��� INI ����� � ����
			_tcscat(szPath, PPMSGBOX_INI_FILE);
			
			const nHexStringLength = 16;
			TCHAR szBuf[nHexStringLength];
			
			//ENG: Get a return code of PPMessageBox as HEX string
			//RUS: �������� ��� ������� ������� PPMessageBox � ������� HEX ������
			if (::GetPrivateProfileString(_T("DoNotAsk"),	// section name
				sDoNotAskKey,	// key name
				_T(""),			// default string
				szBuf,			// destination buffer
				nHexStringLength,// size of destination buffer
				szPath))		// initialization file name
			{
				//ENG: Convert from HEX string to DWORD value
				//RUS: �������������� HEX ������ � DWORD �����
				dwData = _tcstoul(szBuf, NULL, 16);
			} //if
#else
			//ENG: An info will be search in the system registry
			//RUS: ����� ���������� ����� ���������� � ��������� �������
			CPPString sSection;
			if ((NULL == MsgBox.lpszCompanyName) || (0 == MsgBox.lpszCompanyName [0]))
				sSection.Format(_T("Software\\%s\\%s"), lpFilePart, PPMSGBOX_REGISTRY_KEY);
			else
				sSection.Format(_T("Software\\%s\\%s\\%s"), MsgBox.lpszCompanyName, lpFilePart, PPMSGBOX_REGISTRY_KEY);

			//ENG: Read a value from registry
			//RUS: ���������� �������� �� �������
			HKEY hKey = NULL;
			LONG lRet = ::RegOpenKeyEx(HKEY_CURRENT_USER, sSection, 0, KEY_READ, &hKey);
			if (lRet == ERROR_SUCCESS)
			{
				DWORD dwType = 0;
				DWORD dwSize = sizeof(DWORD);
				lRet = ::RegQueryValueEx(hKey, sDoNotAskKey, 0, &dwType, (LPBYTE)&dwData, &dwSize);
				::RegCloseKey(hKey);
				if (lRet != ERROR_SUCCESS)
					dwData = 0;
			} //if
#endif //PPMSGBOX_USE_PROFILE_FILE
			//ENG: If "DoNotAsk" flag was checked
			//RUS: ���� ������ "������������" ����������
			if (dwData) 
				return (int)dwData;
		} //if
	} //if
#endif //PPMSGBOX_DO_NOT_SAVE_CHECKBOX

	if ((MsgBox.dwStyle & MB_NOSOUND) == 0)
		::MessageBeep(MsgBox.dwStyle & MB_ICONMASK);
	
	CPPMessageBoxTemplate Dlg(&MsgBox);
	
	int nResult = Dlg.Display();
	
	return nResult;
} //End of PPMessageBoxIndirect

CPPMessageBoxTemplate::CPPMessageBoxTemplate(const PPMSGBOXPARAMS *pMsgBox)
{
	m_MsgParam = *pMsgBox;

	if (NULL == m_MsgParam.hInstanceStrings)
		m_MsgParam.hInstanceStrings = ::GetModuleHandle(NULL);

	if (NULL == m_MsgParam.hInstanceIcons)
		m_MsgParam.hInstanceIcons = ::GetModuleHandle(NULL);

	m_hBitmapBk = NULL;

	m_bMoreInfoOpened = FALSE;
	m_dwMoreInfoHeight = 0;

	m_sDisabledCaption.Empty();
	m_sTimeoutText.Empty();
	m_sMoreInfoClosed.Empty();
	m_sMoreInfoOpened.Empty();
	m_sHeader.Empty();
	m_sCaption = GetString(m_MsgParam.lpszCaption);
	m_sText.Empty();

	m_vecButtons.clear();
//	m_mapBtnText.clear();

	NONCLIENTMETRICS ncm;
	memset(&ncm, 0, sizeof(ncm));
	ncm.cbSize = sizeof(ncm);
	::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
	m_hFont = ::CreateFontIndirect(&ncm.lfMessageFont);

	//Load a default icon
	m_hIcon = NULL;
	switch (m_MsgParam.dwStyle & MB_ICONMASK)
	{
	case MB_ICONERROR:
		m_hIcon = ::LoadIcon(NULL, IDI_ERROR);
		break;
	case MB_ICONQUESTION:
		m_hIcon = ::LoadIcon(NULL, IDI_QUESTION);
		break;
	case MB_ICONEXCLAMATION:
		m_hIcon = ::LoadIcon(NULL, IDI_EXCLAMATION);
		break;
	case MB_ICONASTERISK:
		m_hIcon = ::LoadIcon(NULL, IDI_ASTERISK);
		break;
	case MB_USERICON:
		if (NULL != m_MsgParam.lpszIcon)
		{
			m_hIcon = (HICON)::LoadImage(m_MsgParam.hInstanceIcons, m_MsgParam.lpszIcon, 
				IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
		} //if
		break;
	} //switch
	
	//Setup a sizes
	m_dwSizes [PPMSGBOX_SIZE_SEPX] = 5;
	m_dwSizes [PPMSGBOX_SIZE_MSGX] = 14;
	m_dwSizes [PPMSGBOX_SIZE_MSGY] = 14;
//	m_dwSizes [PPMSGBOX_SIZE_BTNX] = 90;
//	m_dwSizes [PPMSGBOX_SIZE_BTNY] = 23;
	m_dwSizes [PPMSGBOX_SIZE_CHKX] = 10;
	m_dwSizes [PPMSGBOX_SIZE_CHKY] = 13;
	m_dwSizes [PPMSGBOX_SIZE_CTRLX] = 11;
	m_dwSizes [PPMSGBOX_SIZE_CTRLY] = 11;

/*	//Customizes a button's text
	SetButtonText(IDOK, _T("OK"), IDS_OK);
	SetButtonText(IDCANCEL, _T("Cancel"), IDS_CANCEL);
	SetButtonText(IDABORT, _T("Abort"), IDS_ABORT);
	SetButtonText(IDRETRY, _T("Retry"), IDS_RETRY);
	SetButtonText(IDIGNORE, _T("Ignore"), IDS_IGNORE);
	SetButtonText(IDYES, _T("Yes"), IDS_YES);
	SetButtonText(IDNO, _T("No"), IDS_NO);
	SetButtonText(IDCLOSE, _T("Close"), 0);
	SetButtonText(IDHELP, _T("Help"), IDS_HELP);
	SetButtonText(IDTRYAGAIN, _T("Try Again"), IDS_TRYAGAIN);
	SetButtonText(IDCONTINUE, _T("Continue"), IDS_CONTINUE);
	SetButtonText(IDSKIP, _T("Skip"), IDS_SKIP);
	SetButtonText(IDSKIPALL, _T("Skip All"), IDS_SKIPALL);
	SetButtonText(IDIGNOREALL, _T("Ignore All"), IDS_IGNORE);
	SetButtonText(IDYESTOALL, _T("Yes to All"), IDS_YESTOALL);
	SetButtonText(IDNOTOALL, _T("No to All"), IDS_NOTOALL);
	if (m_MsgParam.sCheckBoxText.IsEmpty())
		SetButtonText(MB_CHECKBOX, _T("Do not show again"), IDS_DONOTSHOWAGAIN);
	else
		SetButtonText(MB_CHECKBOX, m_MsgParam.sCheckBoxText, IDS_DONOTSHOWAGAIN);*/
} //End of CPPMessageBoxTemplate

CPPMessageBoxTemplate::~CPPMessageBoxTemplate()
{
	if (NULL != m_hFont)
		::DeleteObject(m_hFont);

	if (NULL != m_hBitmapBk)
		::DeleteObject(m_hBitmapBk);

	if (NULL != m_hIcon)
		::DeleteObject(m_hIcon);

	RemoveAllButtons();
} //End of ~CPPMessageBoxTemplate

int CPPMessageBoxTemplate::Display()
{
	LPDLGTEMPLATE pdlg;

	HGLOBAL hgbl = GlobalAlloc(GMEM_ZEROINIT, sizeof(DLGTEMPLATE)*2);
	_ASSERT(hgbl);
	if (NULL == hgbl)
		return IDCANCEL;

    pdlg = (LPDLGTEMPLATE)GlobalLock(hgbl);
	_ASSERT(pdlg);
	if (NULL == hgbl)
	{
		GlobalFree(hgbl);
		return IDCANCEL;
	} //if

    pdlg->x = 0;
    pdlg->y = 0;
    pdlg->cx = 100;
    pdlg->cy = 100;
    pdlg->style = DS_MODALFRAME|WS_VISIBLE|WS_POPUP|WS_BORDER|WS_CAPTION|WS_SYSMENU|DS_CENTER;
    pdlg->dwExtendedStyle = 0;
    pdlg->cdit = 0;

	HINSTANCE hInstance = ::GetModuleHandle(NULL);
	int nResult = ::DialogBoxIndirectParam(hInstance, pdlg, m_MsgParam.hParentWnd, MsgBoxDlgProc, (LPARAM)this);

	GlobalUnlock(pdlg);
    GlobalFree(hgbl);

	return nResult;
} //End of PPMessageBox


//subclassing a checkbox
//LRESULT CALLBACK NewCheckBoxProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); // prototype for the combo box subclass proc
//WNDPROC lpfnOrgCheckboxProc;

///////////////////////////////////////////////////////////////////////////////
// MsgBoxDlgProc
BOOL CALLBACK CPPMessageBoxTemplate::MsgBoxDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	CPPMessageBoxTemplate * pDlg = (CPPMessageBoxTemplate*)::GetWindowLong(hwnd, GWL_USERDATA);

	switch (message)
	{
	case WM_INITDIALOG:
		{
			::SetWindowLong(hwnd, GWL_USERDATA, lParam);	// save it for the others
			pDlg = (CPPMessageBoxTemplate*)lParam;
			_ASSERTE(pDlg);
			pDlg->OnInitDialog(hwnd);
			if (NULL != pDlg->m_stCheckBtn.hWnd)
			{
				//Subclassing a checkbox
//				lpfnOrgCheckboxProc = (WNDPROC)::SetWindowLongPtr(
//					pDlg->m_stCheckBtn.hWnd,
//					GWL_WNDPROC, 
//					(LONG)NewCheckBoxProc);
			} //if
			return FALSE;
		} //End of case WM_INITDIALOG
	case WM_DESTROY:
		{
//			if (NULL != pDlg->m_stCheckBtn.hWnd)
//			{
//				//Unsubclassing a checkbox
//				::SetWindowLongPtr(pDlg->m_stCheckBtn.hWnd,
//					GWL_WNDPROC, (LONG)lpfnOrgCheckboxProc);
//			} //if
			pDlg->RemoveAllButtons();
			return FALSE;
		} //End of case WM_DESTROY
	case WM_PAINT:
		{
			PAINTSTRUCT ps; 
			BeginPaint(hwnd, &ps);
			pDlg->DrawBox(hwnd, ps.hdc);
			EndPaint(hwnd, &ps);
			return FALSE;
		} //End of case WM_PAINT
	case WM_COMMAND:
		{
			//ENG: Get a clicked button ID
			//RUS: �������� ID ������� ������
			DWORD dwBtnID = LOWORD(wParam);
			if(dwBtnID)
			{
				//ENG: Disable VK_ESCAPE if "Cancel" button is not present in messagebox
				//RUS: ��������� ������ VK_ESCAPE ���� ������ "������" �� ������������
				if ((IDCANCEL == dwBtnID) && !pDlg->m_bIsCancelExist)
					return FALSE;

				switch (dwBtnID)
				{
				case IDHELP:
					{
						TCHAR szBuf[_MAX_PATH];
						memset(&szBuf, 0, sizeof(szBuf));
						::GetModuleFileName(NULL, szBuf, _MAX_PATH);
						if (_tcslen(szBuf) > 0)
						{
							//searching a last dot char '.'
							TCHAR *cp = _tcsrchr(szBuf, _T('.'));
							if (cp)
							{
								_tcscpy(cp, _T(".hlp"));
								//Call a context help
								::WinHelp(hwnd, szBuf,
									(pDlg->m_MsgParam.dwContextHelpID == 0) ? HELP_PARTIALKEY : HELP_CONTEXT,
									pDlg->m_MsgParam.dwContextHelpID);
							} //if
						} //if
						return FALSE;
					}
				case IDMOREINFO_CLOSED:
				case IDMOREINFO_OPENED:
					{
						RECT rcWindow;
						::GetWindowRect(hwnd, &rcWindow);
						DWORD dwHeight = rcWindow.bottom - rcWindow.top;
						for (DWORD i = 0; i < pDlg->m_vecButtons.size(); ++i)
						{
							STRUCT_BUTTONS & sbTemp = pDlg->m_vecButtons [i];
							if (IDMOREINFO_CLOSED == sbTemp.nTypeBtn)
							{
								if (pDlg->m_bMoreInfoOpened)
								{
									pDlg->m_bMoreInfoOpened = FALSE;
									dwHeight -= pDlg->m_dwMoreInfoHeight;
									::SendMessage(sbTemp.hWnd, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)pDlg->m_sMoreInfoClosed);
								}
								else
								{
									pDlg->m_bMoreInfoOpened = TRUE;
									dwHeight += pDlg->m_dwMoreInfoHeight;
									::SendMessage(sbTemp.hWnd, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)pDlg->m_sMoreInfoOpened);
								} //if
								::MoveWindow(hwnd, rcWindow.left, rcWindow.top, rcWindow.right - rcWindow.left, dwHeight, TRUE);
							} //if
						} //for
						return FALSE;
					}
				case IDREPORT:
					{
						if ((pDlg->m_MsgParam.dwReportMsgID) && (NULL != pDlg->m_MsgParam.hParentWnd))
							::SendMessage(pDlg->m_MsgParam.hParentWnd, pDlg->m_MsgParam.dwReportMsgID, 0, 0);
						return FALSE;
					}
				default:
					{
						//ENG: Stop of timer
						//RUS: ������� �������
						::KillTimer(hwnd, 1);

						//ENG: Handles a checkbox state
						//RUS: ������������ ��������� checkbox
						if (NULL != pDlg->m_stCheckBtn.hWnd)
						{
							//ENG: Toggles a checkbox state
							//RUS: ����������� ��������� checkbox
							LRESULT nCheck = ::SendMessage(pDlg->m_stCheckBtn.hWnd, BM_GETCHECK, 0, 0);
							if (BST_CHECKED == nCheck)
							{
								dwBtnID |= pDlg->m_MsgParam.dwStyle & MB_CHECKBOXCHECKED;
#ifdef PPMSGBOX_DO_NOT_SAVE_CHECKBOX
								if ((NULL != pDlg->m_MsgParam.lpszModuleName) && (0 != pDlg->m_MsgParam.lpszModuleName [0]))
								{
									//ENG: Get full path of the current module
									//RUS: �������� ������ ���� � ������������ ������
									TCHAR szModule[_MAX_PATH];
									::GetModuleFileName(NULL, szModule, _MAX_PATH);
									
									TCHAR szPath[_MAX_PATH];
									LPTSTR lpFilePart;
									::GetFullPathName(szModule, _MAX_PATH, szPath, &lpFilePart);
									
									//ENG: Creates a key value
									//RUS: ������� �������� �����
									CPPString sDoNotAskKey;
									sDoNotAskKey.Format(_T("%s%d"), pDlg->m_MsgParam.lpszModuleName, pDlg->m_MsgParam.nLine);
									
#ifdef PPMSGBOX_USE_PROFILE_FILE
									//ENG: An info will be search in the INI file
									//RUS: ����� ���������� ����� ���������� � INI �����
									
									//ENG: Removes a module name from path
									//RUS: ������� ��� ������ �� ����
									if (NULL != lpFilePart)
										*(lpFilePart) = _T('\0');
									
									//ENG: Add an INI filename to path
									//RUS: ��������� ��� INI ����� � ����
									_tcscat(szPath, PPMSGBOX_INI_FILE);
									
									CPPString sData;
									sData.Format(_T("0x%08X"), dwBtnID);

									::WritePrivateProfileString(_T("DoNotAsk"),	// section name
															sDoNotAskKey,		// key name
															sData,				// string to add
															szPath);			// initialization file
#else
									//ENG: An info will be search in the system registry
									//RUS: ����� ���������� ����� ���������� � ��������� �������
									
									CPPString sSection;
									if ((NULL == pDlg->m_MsgParam.lpszCompanyName) || (0 == pDlg->m_MsgParam.lpszCompanyName [0]))
										sSection.Format(_T("Software\\%s\\%s"), lpFilePart, PPMSGBOX_REGISTRY_KEY);
									else
										sSection.Format(_T("Software\\%s\\%s\\%s"), pDlg->m_MsgParam.lpszCompanyName, lpFilePart, PPMSGBOX_REGISTRY_KEY);

									//ENG: Stores a checkbox state
									//RUS: ��������� ��������� checkbox
									DWORD dwResult = 0;
									HKEY hKey = NULL;
									LONG lRet = ::RegCreateKeyEx(HKEY_CURRENT_USER, sSection, 0, NULL,
										REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwResult);
									if (lRet == ERROR_SUCCESS)
									{
										::RegSetValueEx(hKey, sDoNotAskKey, 0, REG_DWORD,
											(const BYTE *) &dwBtnID, sizeof(DWORD));
										::RegCloseKey(hKey);
									} //if
#endif //PPMSGBOX_USE_PROFILE_FILE
								} //if
#endif //PPMSGBOX_DO_NOT_SAVE_CHECKBOX
							} //if
						} //if

						//ENG: Close a message box and return code
						//RUS: ��������� ������ � ���������� ���
						::EndDialog(hwnd, dwBtnID);
						return TRUE;
					}
				} //switch
			} //if
		} //End of case WM_COMMAND
		break;
	case WM_TIMER:		// used for timeout
		{
			if (wParam == 1)
			{
				pDlg->OnTimer(hwnd);
				return TRUE;
			} //if
			return FALSE;
		} //End of case WM_TIMER
		break;
	case WM_CTLCOLORSTATIC:
		{
			//ENG: For a transparent text of the checkbox
			//RUS: ��� ������������ ������ checkbox
			::SetBkMode((HDC)wParam, TRANSPARENT);
			return (BOOL)::GetStockObject(NULL_BRUSH);
		} //End of case WM_CTLCOLORSTATIC
		break;
	case WM_SETCURSOR:
		{
			POINT point;
			::GetCursorPos(&point);
			::ScreenToClient(hwnd, &point);

			if (pDlg->m_dwHeaderHeight)
			{
				if (pDlg->m_pHeader.OnSetCursor(&point))
					return TRUE; //The cursor over the hyperlink
			} //if

			if (pDlg->m_dwMessageHeight)
			{
				if (pDlg->m_pDrawer.OnSetCursor(&point))
					return TRUE; //The cursor over the hyperlink
			} //if

			if (pDlg->m_dwMoreInfoHeight)
			{
				if (pDlg->m_pMoreInfo.OnSetCursor(&point))
					return TRUE; //The cursor over the hyperlink
			} //if
		} //End of case WM_SETCURSOR
		break;
	case WM_LBUTTONDOWN:
		{
			POINT point = {LOWORD(lParam), HIWORD(lParam)};

			if (pDlg->m_dwHeaderHeight)
				pDlg->m_pHeader.OnLButtonDown(&point);
			if (pDlg->m_dwMessageHeight)
				pDlg->m_pDrawer.OnLButtonDown(&point);
			if (pDlg->m_dwMoreInfoHeight)
				pDlg->m_pMoreInfo.OnLButtonDown(&point);
		} //End of case WM_LBUTTONDOWN
		break;
	case WM_ERASEBKGND:
		{
			return TRUE;
		} //End of case WM_ERASEBKGND
	case UDM_HTMLDRAWER_REPAINT:
		{
			pDlg->DrawBox(hwnd, NULL);
			//::InvalidateRect(hwnd, NULL, FALSE);
			return TRUE;
		} //End of case UDM_HTMLDRAWER_REPAINT

//	case WM_LBUTTONDOWN:
//		{
//			//ENG: If a checkbox window is exist
//			//RUS: ���� checkbox ����������
//			if ((NULL != pDlg->m_stCheckText.hWnd) && (NULL != pDlg->m_stCheckBtn.hWnd))
//			{
//				//ENG: Get a child window
//				//RUS: �������� �������� ����
//				POINT pt;
//				pt.x = LOWORD(lParam); 
//				pt.y = HIWORD(lParam);
//				HWND hWndClient = ::ChildWindowFromPoint(hwnd, pt);
//				if (pDlg->m_stCheckText.hWnd == hWndClient)
//				{
//					//ENG: Toggles a checkbox state
//					//RUS: ����������� ��������� checkbox
//					LRESULT nCheck = ::SendMessage(pDlg->m_stCheckBtn.hWnd, BM_GETCHECK, 0, 0);
//					if (nCheck == BST_CHECKED)
//						::SendMessage(pDlg->m_stCheckBtn.hWnd, BM_SETCHECK, BST_UNCHECKED, 0);
//					else if (nCheck == BST_UNCHECKED)
//						::SendMessage(pDlg->m_stCheckBtn.hWnd, BM_SETCHECK, BST_CHECKED, 0);
//					return TRUE;
//				} //if
//			} //if
//		} //End of case WM_LBUTTONDOWN
//		break;
	} //switch

	return FALSE;
}
/*
BOOL m_bIsFocusedCheckBox(FALSE);

LRESULT CALLBACK NewCheckBoxProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_ERASEBKGND:
		{
			
			TRACE ("WM_ERASEBKGND\n");
			m_bIsFocusedCheckBox = FALSE;
		}
	case WM_SETFOCUS:
		break;
	case WM_KILLFOCUS:
//		::SendMessage(hwnd, WM_PAINT, 0, 0);
		break;
//	case WM_DRAWITEM:
//		{
//			LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam;
//
//			BOOL bIsPressed = (lpDIS->itemState & ODS_SELECTED);
//			BOOL bIsFocused  = (lpDIS->itemState & ODS_FOCUS);
//			
//			RECT rcClient = lpDIS->rcItem;
//			::SetBkMode(lpDIS->hDC, TRANSPARENT);
//
//			RECT rcBox;
//			rcBox.left = rcClient.left;
//			rcBox.top = rcClient.top;
//			rcBox.right = rcBox.left + 13;
//			rcBox.bottom = rcBox.top + 13;
//			::DrawFrameControl(lpDIS->hDC, &rcBox, DFC_BUTTON, DFCS_BUTTONCHECK);
//
//			return TRUE;
//		}
	case WM_PAINT:
		{
			TRACE ("WM_PAINT\n");
			RECT rect;
			::GetClientRect(hwnd, &rect);

			PAINTSTRUCT ps; 
			HDC hDC = BeginPaint(hwnd, &ps);

			UINT uState = ::SendMessage(hwnd, BM_GETSTATE, 0, 0);
			
			TCHAR szText [_MAX_PATH];
			int nCount = ::SendMessage(hwnd, WM_GETTEXT, _MAX_PATH, (WPARAM)szText);

			HFONT hFont = (HFONT)::SendMessage(hwnd, WM_GETFONT, 0, 0);
			HFONT hOldFont = (HFONT)::SelectObject(hDC, hFont);
			
			int nOldBkMode = ::SetBkMode(hDC, TRANSPARENT);

			RECT rcCheck = rect;
			rcCheck.right = rcCheck.left + rcCheck.bottom - rcCheck.top;
			if (uState & BST_CHECKED)
				::DrawFrameControl(hDC, &rcCheck, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_CHECKED | DFCS_HOT);
			else
				::DrawFrameControl(hDC, &rcCheck, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_HOT);

			rect.left = rcCheck.right + 6;
			::DrawText(hDC, szText, nCount, &rect, DT_SINGLELINE|DT_LEFT|DT_VCENTER);
//			::TextOut(hDC, 19, 0, szText, nCount);

			rect.left -= 2;
			rect.top -= 1;
			rect.right += 2;
			rect.bottom += 1;

//			if ((uState & BST_FOCUS) && !m_bIsFocusedCheckBox)
//			{
//				TRACE ("WM_FOCUS\n");
//				m_bIsFocusedCheckBox = TRUE;
//				::DrawFocusRect(hDC, &rect);
//			}
//			else if (!(uState & BST_FOCUS) && m_bIsFocusedCheckBox)
//			{
//				TRACE ("WM_UNFOCUS\n");
//				m_bIsFocusedCheckBox = FALSE;
//				::DrawFocusRect(hDC, &rect);
//			}

//			::Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);
			
			::SetBkMode(hDC, nOldBkMode);
			::SelectObject(hDC, hOldFont);
			EndPaint(hwnd, &ps);
			return TRUE;
		}
	}
	return CallWindowProc(lpfnOrgCheckboxProc, hwnd, message, wParam, lParam);

} //End of NewCheckBoxProc
*/
void CPPMessageBoxTemplate::OnTimer(HWND hwnd)
{
	//Gets info about default button
	STRUCT_BUTTONS & sb = m_vecButtons [m_nDefBtn];
	if (m_dwCountdownTimer > 0)
	{
		m_dwCountdownTimer--;
		if (m_dwCountdownTimer > 0) 
		{
			//Time was not complete
			CPPString str;
			if ((m_MsgParam.nDisabledSeconds > 0) && (m_MsgParam.bDisableAllCtrls))
			{
				str.Format(m_sDisabledCaption, m_sCaption, m_dwCountdownTimer);
				::SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)str);
				return;
			}
			else
			{
				str.Format(PPMSGBOX_BUTTON_DISABLED, m_sTimeoutText, m_dwCountdownTimer);
				::SendMessage(sb.hWnd, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)str);
				return;
			} //if
		} //if
	} //if

	//ENG: Stop timer
	//RUS: ������������� ������
	::KillTimer(hwnd, 1);

	if (m_MsgParam.nDisabledSeconds > 0)
	{
		//ENG: Enable a system menu if Cancel button exist
		//RUS: ��������� ��������� ���� ���� ������ ������ ����������
		if (m_bIsCancelExist)
			::EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_ENABLED);

		if (m_MsgParam.bDisableAllCtrls)
		{
			//ENG: Set a caption text
			//RUS: ������������� ����� ���������
			::SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)m_sCaption);
			
			//ENG: Enables all buttons except default button
			//RUS: ��������� ��� ������ ����� ������ �� ���������
			for (UINT i = 0; i < m_vecButtons.size(); ++i) 
			{
				if (i != m_nDefBtn)
				{
					STRUCT_BUTTONS & sbTemp = m_vecButtons [i];
					LONG nStyle = ::GetWindowLong(sbTemp.hWnd, GWL_STYLE);
					nStyle &= ~WS_DISABLED;
					::SetWindowLong(sbTemp.hWnd, GWL_STYLE, nStyle);
					::SendMessage(sbTemp.hWnd, WM_ENABLE, 1, 0);
				} //if
			} //for
		}
		else
		{
			//ENG: Set a real text on the default button only
			//RUS: ������������� �������� ����� ������ ������ �� ���������
			::SendMessage(sb.hWnd, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)m_sTimeoutText);
		} //if

		//ENG: Enable a default button and set it to default
		//RUS: ��������� ������ �� ��������� � ������������� �� �� ���������
		LONG nStyle = ::GetWindowLong(sb.hWnd, GWL_STYLE);
		nStyle &= ~WS_DISABLED;
		nStyle |= BS_DEFPUSHBUTTON;
		::SetWindowLong(sb.hWnd, GWL_STYLE, nStyle);
		::SendMessage(sb.hWnd, WM_ENABLE, 1, 0);
	}
	else if (m_MsgParam.nTimeoutSeconds > 0)
	{
		//ENG: Set a real text on the default button only
		//RUS: ������������� �������� ����� ������ ������ �� ���������
		::SendMessage(sb.hWnd, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)m_sTimeoutText);
		
		//ENG: Imitation to click on default button
		//RUS: �������� ������� ������ �� ���������
		::SendMessage(hwnd, WM_COMMAND, sb.nTypeBtn | MB_TIMEOUT, 0);
	} //if
} //End of OnTimer

void CPPMessageBoxTemplate::OnInitDialog(HWND hwnd)
{
	//ENG: Setup a timer
	//RUS: ��������� �������
	m_dwCountdownTimer = 0;
	if (m_MsgParam.nDisabledSeconds > 0)
		m_dwCountdownTimer = m_MsgParam.nDisabledSeconds;
	if (m_MsgParam.nTimeoutSeconds > 0)
		m_dwCountdownTimer = m_MsgParam.nTimeoutSeconds;

	m_pHeader.SetCallbackRepaint(hwnd, UDM_HTMLDRAWER_REPAINT);
	m_pDrawer.SetCallbackRepaint(hwnd, UDM_HTMLDRAWER_REPAINT);
	m_pMoreInfo.SetCallbackRepaint(hwnd, UDM_HTMLDRAWER_REPAINT);
	
	CPPString str;
	m_sText = GetString(m_MsgParam.lpszText);
	if (m_MsgParam.dwStyle & MB_RIGHT)
		str.Format(_T("<right>%s</right>"), m_sText);
	else
		str = m_sText;

	int nSysIconWidth = ::GetSystemMetrics(SM_CXICON);
	if (NULL != m_hIcon)
		m_sText.Format(_T("<table cellpadding=%d><tr><td width=%d><icon handle=0x%X></td><td>%s</td></tr></table>"), m_dwSizes [PPMSGBOX_SIZE_SEPX], nSysIconWidth, m_hIcon, str);
	else
		m_sText = str;//.Format(_T("<table><tr><td>%s</td></tr></table>"), str);

	SIZE sz = PrepareMessageBox(hwnd);

	//Set a message box caption
	BOOL bGrayedClose = m_bIsCancelExist ? FALSE : TRUE;
	str = m_sCaption;
	if (m_MsgParam.nDisabledSeconds > 0)
	{
		if (m_MsgParam.bDisableAllCtrls)
		{
			m_sDisabledCaption = GetLocalizedButtonText(MB_TIMEOUT);
			str.Format(m_sDisabledCaption, m_sCaption, m_dwCountdownTimer);
			bGrayedClose = TRUE;
		}
		else
		{
			STRUCT_BUTTONS & sb = m_vecButtons [m_nDefBtn];
			if (IDCANCEL == sb.nTypeBtn)
				bGrayedClose = TRUE;
		} //if
	} //if
	::SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)str);

	//Disable a menu
	if (bGrayedClose)
		::EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_GRAYED);


	//Centering of the dialog
	RECT rcParent;
	if (NULL != GetParent(hwnd))
		::GetWindowRect(::GetParent(hwnd), &rcParent);
	else
	{
			rcParent.left    = 0;
			rcParent.top     = 0;
			rcParent.right   = ::GetSystemMetrics(SM_CXSCREEN);
			rcParent.bottom  = ::GetSystemMetrics(SM_CYSCREEN);
	} //if
	::MoveWindow(hwnd, rcParent.left + (rcParent.right - rcParent.left - sz.cx) / 2, 
						rcParent.top + (rcParent.bottom - rcParent.top - sz.cy) / 2, 
						sz.cx, sz.cy, TRUE);

	//ENG: Gets a size of the client area of the messagebox
	//RUS: �������� ������� ���������� ������� messagebox
	RECT rcClient;
	::GetClientRect(hwnd, &rcClient);

	//ENG: Calculates a full width of all buttons
	//RUS: ��������� ������, ���������� ����� ��������
	int nWidth = m_vecButtons.size() * (m_dwSizes [PPMSGBOX_SIZE_BTNX] + m_dwSizes [PPMSGBOX_SIZE_CTRLX]) - m_dwSizes [PPMSGBOX_SIZE_CTRLX];

	RECT rcBtn = rcClient;

	rcBtn.bottom -= m_dwSizes [PPMSGBOX_SIZE_CTRLY];
	if ((m_MsgParam.dwStyle & MB_CHECKBOX) && (m_MsgParam.dwStyle & MB_CHECKBOXUNDERBUTTONS))
		rcBtn.bottom -= m_dwSizes [PPMSGBOX_SIZE_CTRLY] + m_dwSizes [PPMSGBOX_SIZE_CHKY];
	rcBtn.top = rcBtn.bottom - m_dwSizes [PPMSGBOX_SIZE_BTNY];
	switch (m_MsgParam.nControlsAlign) 
	{
	case PPMSGBOX_ALIGN_LEFT:
		rcBtn.left += m_dwSizes [PPMSGBOX_SIZE_CTRLX];
		break;
	case PPMSGBOX_ALIGN_RIGHT:
		rcBtn.left = rcBtn.right - m_dwSizes [PPMSGBOX_SIZE_CTRLX] - nWidth;
		break;
	case PPMSGBOX_ALIGN_CENTER:
	default:
		rcBtn.left += (rcBtn.right - rcBtn.left - nWidth) / 2;
		break;
	} //switch

	//Create buttons
	for (UINT i = 0; i < m_vecButtons.size(); ++i) 
	{
		//ENG: Get a right edge of the button
		//RUS: ��������� ���������� ������ ������� ������
		rcBtn.right = rcBtn.left + m_dwSizes [PPMSGBOX_SIZE_BTNX];

		STRUCT_BUTTONS & sb = m_vecButtons [i];
	
		//ENG: Create a button
		//RUS: ������� ������
		::MoveWindow(sb.hWnd, rcBtn.left, rcBtn.top, m_dwSizes [PPMSGBOX_SIZE_BTNX], m_dwSizes [PPMSGBOX_SIZE_BTNY], TRUE);

		//ENG: Jump to the left edge of the next button
		//RUS: ������������ � ������� ������ ���� ��������� ������
		rcBtn.left += m_dwSizes [PPMSGBOX_SIZE_BTNX] + m_dwSizes [PPMSGBOX_SIZE_CTRLX];
	} //for

	//ENG: Create a checkbox
	//RUS: ������� checkbox
	if (m_stCheckBtn.nTypeBtn)
	{
		//ENG: Gets a text of the checkbox
		//RUS: �������� ����� checkbox
//		CPPString sCheckText = GetButtonText(m_stCheckBtn.nTypeBtn);
		
		//Creates a checkbox control
		rcBtn = rcClient;
		rcBtn.bottom -= m_dwSizes [PPMSGBOX_SIZE_CTRLY];
		if (!(m_MsgParam.dwStyle & MB_CHECKBOXUNDERBUTTONS))
			rcBtn.bottom -= m_dwSizes [PPMSGBOX_SIZE_CTRLY] + m_dwSizes [PPMSGBOX_SIZE_BTNY];
		rcBtn.top = rcBtn.bottom - m_dwSizes [PPMSGBOX_SIZE_CHKY];
		rcBtn.left += m_dwSizes [PPMSGBOX_SIZE_CHKX];

		::MoveWindow(m_stCheckBtn.hWnd, rcBtn.left, rcBtn.top, m_dwSizes [PPMSGBOX_SIZE_CHKY], // + m_dwSizes [PPMSGBOX_SIZE_CHKY] / 2 + sz.cx, 
			m_dwSizes [PPMSGBOX_SIZE_CHKY], TRUE);

		//ENG: To the transparency output of the text of the checkbox
		//RUS: ����� ��� checkbox ������� �������� ��� ����������� ������
		rcBtn.left += m_dwSizes [PPMSGBOX_SIZE_CHKY] + m_dwSizes [PPMSGBOX_SIZE_CHKY] / 2;
		RECT rcCheck;
		::GetWindowRect(m_stCheckText.hWnd, &rcCheck);
		::MoveWindow(m_stCheckText.hWnd, rcBtn.left, rcBtn.top, rcCheck.right - rcCheck.left, 
			rcCheck.bottom - rcCheck.top, TRUE);
	} //if

	//ENG: The message box is created with the WS_EX_TOPMOST window style
	if (m_MsgParam.dwStyle & MB_TOPMOST)
		::SetWindowPos(hwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);

	//ENG: The message box becomes the foreground window
	if (m_MsgParam.dwStyle & MB_SETFOREGROUND)
		::SetForegroundWindow(hwnd);

	//ENG: The countdown timer is enabled
	//RUS: ������ ��������� ������� ������������
	if (m_dwCountdownTimer > 0)
		::SetTimer(hwnd, 1, 1000, NULL);
} //End of OnInitDialog

CPPString CPPMessageBoxTemplate::GetString(LPCTSTR lpszText)
{
	if ((NULL == lpszText) || (0 == lpszText [0]))
		return _T("");

	CPPString sText = _T("");
	if (0 == HIWORD(lpszText)) 
	{
		//lpszText is a MAKEINTRESOURCE(ID) of the text from the resources
		if ((NULL != m_MsgParam.hInstanceStrings) && !(m_MsgParam.dwStyle & MB_NORESOURCE))
		{
			TCHAR tchText [_MAX_PATH];
			int nCount = ::LoadString(m_MsgParam.hInstanceStrings, (UINT)lpszText, tchText, _MAX_PATH);
			if (nCount)
				sText = (CPPString)tchText;
		} //if
	}
	else
	{
		//lpszText is a pointer to the button text
		sText = (CPPString)lpszText;
	} //if
	return sText;
} //End of GetString

CPPString CPPMessageBoxTemplate::GetLocalizedButtonText(DWORD dwBtnID, CPPString sDefText /*=_T("")*/)
{
	DWORD dwResID = 0;
	CPPString sText = _T("");
	switch (dwBtnID) 
	{
	case IDOK:
		dwResID = IDS_OK;
		sText = _T("OK");
		break;
	case IDCANCEL:
		dwResID = IDS_CANCEL;
		sText = _T("Cancel");
		break;
	case IDIGNORE:
		dwResID = IDS_IGNORE;
		sText = _T("Ignore");
		break;
	case IDRETRY:
		dwResID = IDS_RETRY;
		sText = _T("Retry");
		break;
	case IDABORT:
		dwResID = IDS_ABORT;
		sText = _T("Abort");
		break;
	case IDHELP:
		dwResID = IDS_HELP;
		sText = _T("Help");
		break;
	case IDYES:
		dwResID = IDS_YES;
		sText = _T("Yes");
		break;
	case IDNO:
		dwResID = IDS_NO;
		sText = _T("No");
		break;
	case IDCONTINUE:
		dwResID = IDS_CONTINUE;
		sText = _T("Continue");
		break;
	case IDYESTOALL:
		dwResID = IDS_YESTOALL;
		sText = _T("Yes to all");
		break;
	case IDNOTOALL:
		dwResID = IDS_NOTOALL;
		sText = _T("No to all");
		break;
	case IDTRYAGAIN:
		dwResID = IDS_TRYAGAIN;
		sText = _T("Try again");
		break;
	case IDREPORT:
		dwResID = IDS_REPORT;
		sText = _T("Report");
		break;
	case IDIGNOREALL:
		dwResID = IDS_IGNOREALL;
		sText = _T("Ignore all");
		break;
	case IDSKIP:
		dwResID = IDS_SKIP;
		sText = _T("Skip");
		break;
	case IDSKIPALL:
		dwResID = IDS_SKIPALL;
		sText = _T("Skip all");
		break;
	case MB_CHECKBOX:
		dwResID = IDS_DONOTSHOWAGAIN;
		sText = _T("Do not show again");
		break;
	case IDMOREINFO_OPENED:
		dwResID = IDS_MOREINFO_OPENED;
		sText = _T("<< More");
		break;
	case IDMOREINFO_CLOSED:
		dwResID = IDS_MOREINFO_CLOSED;
		sText = _T("More >>");
		break;
	case MB_TIMEOUT:
		dwResID = IDS_GLOBALDISABLE;
		sText = _T("%s (%d seconds remaining)");
		break;
	} //switch

	if (sDefText.IsEmpty())
		sDefText = sText;
	sText.Empty();

	//Try to get a localized button text from the specified resources or from the pointer
	//to real text
	if (NULL != m_MsgParam.pLocalBtnText)
	{
		typedef mapLocalBtnText::iterator iterLocalBtnText;
		iterLocalBtnText iterMap = m_MsgParam.pLocalBtnText->find(dwBtnID);
		
		if (iterMap != m_MsgParam.pLocalBtnText->end())
		{
			LPCTSTR lpszText = iterMap->second;
			sText = GetString(lpszText);
/*			if (0 == HIWORD(lpszText)) 
			{
				//lpszText is a MAKEINTRESOURCE() of the buton text from the resources
				if ((NULL != m_MsgParam.hInstanceStrings) && !(m_MsgParam.dwStyle & MB_NORESOURCE))
				{
					TCHAR tchText [_MAX_PATH];
					int nCount = ::LoadString(m_MsgParam.hInstanceStrings, (UINT)lpszText, tchText, _MAX_PATH);
					if (nCount)
						sText = (CPPString)tchText;
				} //if
			}
			else
			{
				//lpszText is a pointer to the button text
				sText = (CPPString)lpszText;
			} //if
*/		} //if
	} //if

	//Try to get a button text from resources
	if (sText.IsEmpty() && (NULL != m_MsgParam.hInstanceStrings) && !(m_MsgParam.dwStyle & MB_NORESOURCE) && dwResID)
	{
		TCHAR tchText [_MAX_PATH];
		int nCount = ::LoadString(m_MsgParam.hInstanceStrings, dwResID, tchText, _MAX_PATH);
		if (nCount)
			sText = (CPPString)tchText;
	} //if

	if (sText.IsEmpty())
		sText = sDefText;

	return sText;
} //End of GetLocalizedButtonText

void CPPMessageBoxTemplate::RemoveAllButtons()
{
	//ENG: Removes an existing control buttons
	//RUS: ������� ������������ ������ ����������
	for (UINT i = 0; i < m_vecButtons.size(); ++i)
	{
		STRUCT_BUTTONS & sb = m_vecButtons [i];
		if (NULL != sb.hWnd)
		{
			::DestroyWindow(sb.hWnd);
			sb.hWnd = NULL;
		}
	} //for
	m_vecButtons.clear();

	//ENG: Removes a checkbox
	//RUS: ������� checkbox
	if (NULL != m_stCheckBtn.hWnd)
	{
		::DestroyWindow(m_stCheckBtn.hWnd);
		m_stCheckBtn.hWnd = NULL;
	} //if

	//ENG: Removes a text of the checkbox
	//RUS: ������� ����� checkbox
	if (NULL != m_stCheckText.hWnd)
	{
		::DestroyWindow(m_stCheckText.hWnd);
		m_stCheckText.hWnd = NULL;
	} //if
}

SIZE CPPMessageBoxTemplate::PrepareMessageBox(HWND hwnd)
{
	RemoveAllButtons();

	//ENG: Gets a device context
	//RUS: �������� �������� ����������
	HDC hDC = ::GetDC(NULL);
	HFONT hOldFont = (HFONT)::SelectObject(hDC, m_hFont);

	SIZE sz = {0, 0};
	SIZE szTemp = {0, 0};

	//---------------- Header area -----------------------

	m_dwHeaderHeight = 0;
	if (m_MsgParam.nHeaderHeight)
	{
		//ENG: Stores a minimal height of the header area
		//RUS: ��������� ����������� ������ ������� ���������
		m_dwHeaderHeight = m_MsgParam.nHeaderHeight;
		szTemp.cx = 0;
		m_sHeader = GetString(m_MsgParam.lpszHeaderText);
		if (!m_sHeader.IsEmpty())
		{
			//ENG: If header text is available then prepare him to output
			//RUS: ���� ����� ��������� ����������, �� �������������� ��� � ������
			m_pHeader.PrepareOutput(hDC, m_sHeader, &szTemp);
			szTemp.cx += m_dwSizes[PPMSGBOX_SIZE_MSGX] * 2;
			
			//ENG: Updates a height of the header area
			//RUS: ��������� ������ ������� ���������
			m_dwHeaderHeight = max(m_dwHeaderHeight, szTemp.cy + m_dwSizes[PPMSGBOX_SIZE_MSGX] * 2);
		} //if
		sz.cx = max(sz.cx, szTemp.cx);

		//ENG: Header separator will be drawn
		//RUS: ����������� ��������� ����� �������
		m_sHeaderSepText = GetString(m_MsgParam.pHeaderBk.lpszSepText);
		if (m_MsgParam.pHeaderBk.nSepType != PPMSGBOX_SEP_NONE)
		{
			::GetTextExtentPoint32(hDC, m_sHeaderSepText, m_sHeaderSepText.GetLength(), &szTemp);
			sz.cx = max(sz.cx, (int)(szTemp.cx + 2 * m_dwSizes [PPMSGBOX_SIZE_SEPX]));
		} //if
	} //if
	sz.cy += m_dwHeaderHeight;

	//--------------- Message area -----------------------

	m_dwMessageHeight = 0;
	if (!m_sText.IsEmpty())
	{
		//ENG: If message text is available then prepare him to output
		//RUS: ���� ����� ��������� ����������, �� �������������� ��� � ������
		m_pDrawer.PrepareOutput(hDC, m_sText, &szTemp);
		
		//ENG: Updates a height of the message area
		//RUS: ��������� ������ ������� ���������
		m_dwMessageHeight = szTemp.cy + 2 * m_dwSizes [PPMSGBOX_SIZE_MSGY];
		sz.cx = max(sz.cx, (int)(szTemp.cx + 2 * m_dwSizes [PPMSGBOX_SIZE_MSGX]));
	} //if
	sz.cy += m_dwMessageHeight;

	//--------------- Moreinfo area -----------------------
	
	m_dwMoreInfoHeight = 0;
	CPPString sMoreInfo = GetString(m_MsgParam.lpszMoreInfo);
	if (!sMoreInfo.IsEmpty())
	{
		//ENG: If message text is available then prepare him to output
		//RUS: ���� ����� ��������� ����������, �� �������������� ��� � ������
		m_pMoreInfo.PrepareOutput(hDC, sMoreInfo, &szTemp);
		
		//ENG: Updates a height of the message area
		//RUS: ��������� ������ ������� ���������
		m_dwMoreInfoHeight = szTemp.cy + 2 * m_dwSizes [PPMSGBOX_SIZE_MSGY];
		sz.cx = max(sz.cx, (int)(szTemp.cx + 2 * m_dwSizes [PPMSGBOX_SIZE_MSGX]));
	} //if
	
	//---------------- Control area ----------------------

	m_dwControlHeight = m_dwSizes [PPMSGBOX_SIZE_CTRLY];
	if ((m_MsgParam.pControlBk.nEffectBk >= 0) || (m_MsgParam.pControlBk.nSepType != PPMSGBOX_SEP_NONE))
	{
		m_dwControlHeight += m_dwSizes [PPMSGBOX_SIZE_CTRLY];
		
		//ENG: Comtrol separator will be drawn
		//RUS: ����������� ������� ���������� ����� �������
		m_sControlSepText = GetString(m_MsgParam.pControlBk.lpszSepText);
		if (m_MsgParam.pControlBk.nSepType != PPMSGBOX_SEP_NONE)
		{
			::GetTextExtentPoint32(hDC, m_sControlSepText, m_sControlSepText.GetLength(), &szTemp);
			sz.cx = max(sz.cx, (int)(szTemp.cx + 2 * m_dwSizes [PPMSGBOX_SIZE_SEPX]));
		} //if
	} //if

	//ENG: Get a checkbox type
	//RUS: �������� ��� checkbox
	m_stCheckBtn.hWnd = NULL;
	m_stCheckBtn.nTypeBtn = (m_MsgParam.dwStyle & MB_CHECKBOX);
	m_stCheckText = m_stCheckBtn;
	if (m_stCheckBtn.nTypeBtn)
	{
		//ENG: Checkbox will drawn
		//RUS: ������ ����������� � �������
		m_dwControlHeight += m_dwSizes [PPMSGBOX_SIZE_CTRLY] + m_dwSizes [PPMSGBOX_SIZE_CHKY];

		//ENG: Gets the sizes of the text of the checkbox
		//RUS: �������� ������� ������ ������
		CPPString sCheckBoxText = GetString(m_MsgParam.lpszCheckBoxText);
		if (sCheckBoxText.IsEmpty()) 
			sCheckBoxText = _T("Do not show again");
		CPPString str = GetLocalizedButtonText(MB_CHECKBOX, sCheckBoxText);
		::GetTextExtentPoint32(hDC, str, str.GetLength(), &szTemp);

		//ENG: Check a max width of the dialog
		//RUS: ��������� ������������ ������ �������
		sz.cx = max(sz.cx, (int)(szTemp.cx + 2 * m_dwSizes [PPMSGBOX_SIZE_CHKX] + 20));

		m_stCheckBtn.hWnd = ::CreateWindow(_T("BUTTON"), NULL /*sCheckText*/, 
			WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_AUTOCHECKBOX, 
			0, 0, m_dwSizes [PPMSGBOX_SIZE_CHKY], // + m_dwSizes [PPMSGBOX_SIZE_CHKY] / 2 + sz.cx, 
			m_dwSizes [PPMSGBOX_SIZE_CHKY], hwnd, NULL, NULL, NULL);
		if (m_MsgParam.dwStyle & MB_CHECKBOXCHECKED)
			::SendMessage(m_stCheckBtn.hWnd, BM_SETCHECK, (WPARAM)BST_CHECKED, FALSE);

		//ENG: To the transparency output of the text of the checkbox
		//RUS: ����� ��� checkbox ������� �������� ��� ����������� ������
		m_stCheckText.hWnd = ::CreateWindow(_T("STATIC"), str, 
			WS_VISIBLE | WS_CHILD | SS_LEFT,
			0, 0, szTemp.cx, m_dwSizes [PPMSGBOX_SIZE_CHKY],
			hwnd, NULL, NULL, NULL);
		::SendMessage(m_stCheckText.hWnd, WM_SETFONT, (WPARAM)m_hFont, FALSE);
	} //if

	//ENG: Creates a list of the buttons 
	//RUS: ������� ������ ������
	STRUCT_BUTTONS sb;
	sb.hWnd = NULL;

	CPPString sCustomButtons = GetString(m_MsgParam.lpszCustomButtons);
	if (((m_MsgParam.dwStyle & MB_TYPEMASK) == MB_OK) && sCustomButtons.IsEmpty())
		m_bIsCancelExist = TRUE;
	else
		m_bIsCancelExist = FALSE;
	
	//ENG: Try to load a custom buttons
	//RUS: ������� ��������� ���������������� ������
	if (!sCustomButtons.IsEmpty())
	{
		//ENG: Will be use the custom buttons
		//RUS: ����� ������������ ���������������� ������
		sCustomButtons.Remove(_T('\r'));
		DWORD dwButton = IDCUSTOM1;
		sCustomButtons += _T("\n");
		int nLength = sCustomButtons.GetLength();
		for (int i = 0; (i < nLength) && (dwButton <= IDCUSTOM4); ++i)
		{
			int nIndex = sCustomButtons.Find(_T('\n'), i);
			if (i != nIndex)
			{
				//ENG: A custom button was found
				//RUS: ���������������� ������ �������
//				SetButtonText(dwButton, m_MsgParam.sCustomButtons.Mid(i, nIndex - i));
				sb.nTypeBtn = dwButton;
				m_vecButtons.push_back(sb);
				dwButton ++;
				i = nIndex;
			} //if
		} //for
	} //if

	if (0 == m_vecButtons.size())
	{
		//ENG: Custom buttons not used
		//RUS: ���������������� ������ �� ������������
		switch (m_MsgParam.dwStyle & MB_TYPEMASK) 
		{
		case MB_OK:
			sb.nTypeBtn = IDOK;
			m_vecButtons.push_back(sb);
			break;
		case MB_OKCANCEL:
			sb.nTypeBtn = IDOK;
			m_vecButtons.push_back(sb);
			sb.nTypeBtn = IDCANCEL;
			m_vecButtons.push_back(sb);
			m_bIsCancelExist = TRUE;
			break;
		case MB_RETRYCANCEL:
			sb.nTypeBtn = IDRETRY;
			m_vecButtons.push_back(sb);
			sb.nTypeBtn = IDCANCEL;
			m_vecButtons.push_back(sb);
			m_bIsCancelExist = TRUE;
			break;
		case MB_YESNO:
			sb.nTypeBtn = IDYES;
			m_vecButtons.push_back(sb);
			if (m_MsgParam.dwStyle & MB_YESTOALL)
			{
				sb.nTypeBtn = IDYESTOALL;
				m_vecButtons.push_back(sb);
			} //if
			sb.nTypeBtn = IDNO;
			m_vecButtons.push_back(sb);
			if (m_MsgParam.dwStyle & MB_NOTOALL)
			{
				sb.nTypeBtn = IDNOTOALL;
				m_vecButtons.push_back(sb);
			} //if
			break;
		case MB_CONTINUEABORT:
			sb.nTypeBtn = IDCONTINUE;
			m_vecButtons.push_back(sb);
			sb.nTypeBtn = IDABORT;
			m_vecButtons.push_back(sb);
			break;
		case MB_YESNOCANCEL:
			sb.nTypeBtn = IDYES;
			m_vecButtons.push_back(sb);
			if (m_MsgParam.dwStyle & MB_YESTOALL)
			{
				sb.nTypeBtn = IDYESTOALL;
				m_vecButtons.push_back(sb);
			} //if
			sb.nTypeBtn = IDNO;
			m_vecButtons.push_back(sb);
			if (m_MsgParam.dwStyle & MB_NOTOALL)
			{
				sb.nTypeBtn = IDNOTOALL;
				m_vecButtons.push_back(sb);
			} //if
			sb.nTypeBtn = IDCANCEL;
			m_vecButtons.push_back(sb);
			m_bIsCancelExist = TRUE;
			break;
		case MB_ABORTRETRYIGNORE:
			sb.nTypeBtn = IDABORT;
			m_vecButtons.push_back(sb);
			sb.nTypeBtn = IDRETRY;
			m_vecButtons.push_back(sb);
			sb.nTypeBtn = IDIGNORE;
			m_vecButtons.push_back(sb);
			break;
		case MB_CANCELTRYCONTINUE:
			sb.nTypeBtn = IDCANCEL;
			m_vecButtons.push_back(sb);
			sb.nTypeBtn = IDRETRY;
			m_vecButtons.push_back(sb);
			sb.nTypeBtn = IDCONTINUE;
			m_vecButtons.push_back(sb);
			m_bIsCancelExist = TRUE;
			break;
		case MB_SKIPSKIPALLCANCEL:
			sb.nTypeBtn = IDSKIP;
			m_vecButtons.push_back(sb);
			sb.nTypeBtn = IDSKIPALL;
			m_vecButtons.push_back(sb);
			sb.nTypeBtn = IDCANCEL;
			m_vecButtons.push_back(sb);
			m_bIsCancelExist = TRUE;
			break;
		case MB_IGNOREIGNOREALLCANCEL:
			sb.nTypeBtn = IDIGNORE;
			m_vecButtons.push_back(sb);
			sb.nTypeBtn = IDIGNOREALL;
			m_vecButtons.push_back(sb);
			sb.nTypeBtn = IDCANCEL;
			m_vecButtons.push_back(sb);
			m_bIsCancelExist = TRUE;
			break;
		} //switch
	} //if

	//ENG: Adds a Help button to the message box.
	//RUS: ��������� ������ Help
	if (m_MsgParam.dwStyle & MB_HELP) 
	{
		sb.nTypeBtn = IDHELP;
		m_vecButtons.push_back(sb);
	} //if

	//ENG: Adds a Report button to the message box.
	//RUS: ��������� ������ Report
	if (m_MsgParam.dwReportMsgID && (NULL != m_MsgParam.hParentWnd)) 
	{
		sb.nTypeBtn = IDREPORT;
		m_vecButtons.push_back(sb);
	} //if

	//ENG: Adds a More Info button to message box.
	//RUS: ��������� ������ "�������������"
	m_sMoreInfoSepText = GetString(m_MsgParam.pMoreInfoBk.lpszSepText);
	m_sMoreInfoClosed = m_sMoreInfoOpened = _T("");
	if (!sMoreInfo.IsEmpty()) 
	{
		sb.nTypeBtn = IDMOREINFO_CLOSED;
		m_vecButtons.push_back(sb);

		//Load a "MoreInfo" button text
		m_sMoreInfoOpened = GetLocalizedButtonText(IDMOREINFO_OPENED);
		m_sMoreInfoClosed = GetLocalizedButtonText(IDMOREINFO_CLOSED);
	} //if

	//ENG: Remembers a default button
	//RUS: ���������� ������ �� ���������
	m_nDefBtn = (m_MsgParam.dwStyle >> 8) & 0x0f;
	if (m_nDefBtn >= m_vecButtons.size())
		m_nDefBtn = 0;

	//ENG:
	m_dwSizes [PPMSGBOX_SIZE_BTNY] = 0;
	m_dwSizes [PPMSGBOX_SIZE_BTNX] = 0;
	int nCustomIndex = 0;
	for (UINT i = 0; i < m_vecButtons.size(); ++i)
	{
		STRUCT_BUTTONS & sbTemp = m_vecButtons [i];

		CPPString sBtnText; 
		if (IDMOREINFO_CLOSED == sbTemp.nTypeBtn)
		{
			::GetTextExtentPoint32(hDC, m_sMoreInfoOpened, m_sMoreInfoOpened.GetLength(), &szTemp);
			SIZE szTemp2;
			::GetTextExtentPoint32(hDC, m_sMoreInfoClosed, m_sMoreInfoClosed.GetLength(), &szTemp2);
			szTemp.cx = max (szTemp.cx, szTemp2.cx);
			szTemp.cy = max (szTemp.cy, szTemp2.cy);
			sBtnText = m_sMoreInfoClosed;
		}
		else 
		{
			if ((sbTemp.nTypeBtn >= IDCUSTOM1) && (sbTemp.nTypeBtn <= IDCUSTOM4))
			{
				int nIndex = sCustomButtons.Find(_T('\n'), nCustomIndex);
				sBtnText = sCustomButtons.Mid(nCustomIndex, nIndex - nCustomIndex);
				nCustomIndex = nIndex + 1;
			}
			else
				sBtnText = GetLocalizedButtonText(sbTemp.nTypeBtn);
			::GetTextExtentPoint32(hDC, sBtnText, sBtnText.GetLength(), &szTemp);
		} //if
		szTemp.cx = ::MulDiv(szTemp.cx, 80, 45);//40);
		szTemp.cy = ::MulDiv(szTemp.cy, 23, 13);
		m_dwSizes [PPMSGBOX_SIZE_BTNY] = max ((DWORD)szTemp.cy, m_dwSizes [PPMSGBOX_SIZE_BTNY]);
		m_dwSizes [PPMSGBOX_SIZE_BTNX] = max ((DWORD)szTemp.cx, m_dwSizes [PPMSGBOX_SIZE_BTNX]);

		//ENG: Common styles for all buttons
		//RUS: ����� ����� ��� ���� ������
		DWORD dwStyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP;

		if (sbTemp.nTypeBtn == IDHELP)
		{
			//RUS: ���������� ������������ ������ Help, ��� ��� ��� �� ������ ������� �����������
			if (i != m_nDefBtn)
				dwStyle |= BS_PUSHBUTTON;
			else
			{
				dwStyle |= BS_DEFPUSHBUTTON;
				m_sTimeoutText = sBtnText;
				if ((m_MsgParam.nTimeoutSeconds > 0) || ((m_MsgParam.nDisabledSeconds > 0) && !m_MsgParam.bDisableAllCtrls))
					sBtnText.Format(PPMSGBOX_BUTTON_DISABLED, m_sTimeoutText, m_dwCountdownTimer);
			} //if
		}
		else
		{
			if ((m_MsgParam.nDisabledSeconds > 0) && m_MsgParam.bDisableAllCtrls)
			{
				dwStyle |= WS_DISABLED | BS_PUSHBUTTON;
			}
			else
			{
				if (i != m_nDefBtn)
				{
					dwStyle |= BS_PUSHBUTTON;
				}
				else
				{
					m_sTimeoutText = sBtnText;
					if (m_MsgParam.nDisabledSeconds > 0)
						dwStyle |= BS_PUSHBUTTON;
					else
						dwStyle |= BS_DEFPUSHBUTTON;
					
					if (m_dwCountdownTimer > 0)
					{
						sBtnText.Format(PPMSGBOX_BUTTON_DISABLED, m_sTimeoutText, m_dwCountdownTimer);
						if (m_MsgParam.nDisabledSeconds > 0)
							dwStyle |= WS_DISABLED;
					} //if
				} //if
			} //if
		} //if

		//ENG: Create a button
		//RUS: ������� ������
		sbTemp.hWnd = ::CreateWindow(_T("BUTTON"), sBtnText, dwStyle, 0, 0, 0, 0, hwnd, (HMENU)sbTemp.nTypeBtn, NULL, NULL);
		::SendMessage(sbTemp.hWnd, WM_SETFONT, (WPARAM)m_hFont, FALSE);

		//ENG: Set a focus for default button
		//RUS: ��� ������ �� ���������, �������� �� �����
		if (i == m_nDefBtn)
			::SetFocus(sbTemp.hWnd);
	} //if

	if (m_dwSizes [PPMSGBOX_SIZE_BTNX] < 80)
		m_dwSizes [PPMSGBOX_SIZE_BTNX] = 80;

	if (m_dwSizes [PPMSGBOX_SIZE_BTNY] < 23)
		m_dwSizes [PPMSGBOX_SIZE_BTNY] = 23;
	
	//ENG: Calculates a width of the all buttons area
	//RUS: ������������ ������ ������� ���� ������
	szTemp.cx = m_dwSizes [PPMSGBOX_SIZE_CTRLX] + m_vecButtons.size() * (m_dwSizes [PPMSGBOX_SIZE_CTRLX] + m_dwSizes [PPMSGBOX_SIZE_BTNX]);
	sz.cx = max(sz.cx, szTemp.cx);

	m_dwControlHeight += m_dwSizes [PPMSGBOX_SIZE_BTNY];
	
	//ENG: Add a height of the control area
	//RUS: ��������� ������ ������� ���������
	sz.cy += m_dwControlHeight;

	//----------------------------------------------------
	//ENG: Add the edges and the caption
	//RUS: ��������� ������ � ��������� ������� 
	sz.cx += 2 * ::GetSystemMetrics(SM_CXFIXEDFRAME);
	sz.cy += ::GetSystemMetrics(SM_CYCAPTION) + 2 * ::GetSystemMetrics(SM_CYFIXEDFRAME);

	//ENG: Release a device context
	//RUS: ����������� �������� ���������
	::SelectObject(hDC, hOldFont);
	if (NULL != hDC)
		::ReleaseDC(NULL, hDC);

	return sz;
} //End of GetClientSize

void CPPMessageBoxTemplate::DrawBox(HWND hwnd, HDC hDC)
{
	BOOL bAutoReleaseDC = FALSE;
	if (NULL == hDC)
	{
		hDC = ::GetDC(hwnd);
		bAutoReleaseDC = TRUE;
	} //if

	RECT rcClient;
	::GetClientRect(hwnd, &rcClient);

	RECT rcFull = rcClient;
	if (!m_bMoreInfoOpened)
		rcFull.bottom += m_dwMoreInfoHeight;

	int nWidth = rcFull.right - rcFull.left;
	int nHeight = rcFull.bottom - rcFull.top;

	HDC hMemDC = ::CreateCompatibleDC(hDC);
	HDC hSrcDC = ::CreateCompatibleDC(hDC);
	HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, nWidth, nHeight);
	
	HBITMAP hOldSrcBitmap = NULL;
	HFONT hOldFont = NULL;

	if (NULL == m_hBitmapBk)
	{
		hOldFont = (HFONT)::SelectObject(hSrcDC, m_hFont);
		m_hBitmapBk = ::CreateCompatibleBitmap(hDC, nWidth, nHeight);
		hOldSrcBitmap = (HBITMAP)::SelectObject(hSrcDC, m_hBitmapBk);

		RECT rect = rcClient;
		
		//Fill a message box window
		if (m_MsgParam.pMsgBoxBk.nEffectBk >= 0)
			m_pDrawer.GetDrawManager()->FillEffect(hSrcDC, m_MsgParam.pMsgBoxBk.nEffectBk, &rcFull, m_MsgParam.pMsgBoxBk.crStartBk, m_MsgParam.pMsgBoxBk.crMidBk, m_MsgParam.pMsgBoxBk.crEndBk);
		else
		{
			HBRUSH hBrush = ::CreateSolidBrush(::GetSysColor(COLOR_3DFACE));
			::FillRect(hSrcDC, &rcFull, hBrush);
			::DeleteObject(hBrush);
		} //if

		//Draw a header area
		if (0 != m_dwHeaderHeight) 
		{
			//Fill a header background
			rcClient.bottom = rcClient.top + m_dwHeaderHeight;
			if (m_MsgParam.pHeaderBk.nEffectBk >= 0)
				m_pDrawer.GetDrawManager()->FillEffect(hSrcDC, m_MsgParam.pHeaderBk.nEffectBk, &rcClient, m_MsgParam.pHeaderBk.crStartBk, m_MsgParam.pHeaderBk.crMidBk, m_MsgParam.pHeaderBk.crEndBk);
			//Draw a Separator
			rcClient.top = rcClient.bottom;
			if (m_MsgParam.pHeaderBk.nSepType != PPMSGBOX_SEP_NONE)
				DrawSeparator(hSrcDC, &rcClient, m_MsgParam.pHeaderBk.nSepType, m_sHeaderSepText, m_MsgParam.pHeaderBk.nSepAlign);
		} //if

		rcClient.top += m_dwMessageHeight;

		//Draw a control area
		if (m_MsgParam.pControlBk.nEffectBk >= 0)
		{
			rcClient.bottom = rcClient.top + m_dwControlHeight;
			m_pDrawer.GetDrawManager()->FillEffect(hSrcDC, m_MsgParam.pControlBk.nEffectBk, &rcClient, m_MsgParam.pControlBk.crStartBk, m_MsgParam.pControlBk.crMidBk, m_MsgParam.pControlBk.crEndBk);
		} //if
		if (m_MsgParam.pControlBk.nSepType != PPMSGBOX_SEP_NONE)
			DrawSeparator(hSrcDC, &rcClient, m_MsgParam.pControlBk.nSepType, m_sControlSepText, m_MsgParam.pControlBk.nSepAlign);

		//Draw a MoreInfo area background
		if (m_dwMoreInfoHeight)
		{
			rcClient.top += m_dwControlHeight;
			//Draw a control's area
			if (m_MsgParam.pMoreInfoBk.nEffectBk >= 0)
			{
				rcClient.bottom = rcFull.bottom;
				m_pDrawer.GetDrawManager()->FillEffect(hSrcDC, m_MsgParam.pMoreInfoBk.nEffectBk, &rcClient, m_MsgParam.pMoreInfoBk.crStartBk, m_MsgParam.pMoreInfoBk.crMidBk, m_MsgParam.pMoreInfoBk.crEndBk);
			} //if
			if (m_MsgParam.pMoreInfoBk.nSepType != PPMSGBOX_SEP_NONE)
				DrawSeparator(hSrcDC, &rcClient, m_MsgParam.pMoreInfoBk.nSepType, m_sMoreInfoSepText, m_MsgParam.pMoreInfoBk.nSepAlign);
		} //if

		::SelectObject(hSrcDC, hOldFont);
		rcClient = rect;
	}
	else
	{
		hOldSrcBitmap = (HBITMAP)::SelectObject(hSrcDC, m_hBitmapBk);
	} //if

	hOldFont = (HFONT)::SelectObject(hMemDC, m_hFont);
	HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hMemDC, hBitmap);
	::BitBlt(hMemDC, 0, 0, nWidth, nHeight, hSrcDC, 0, 0, SRCCOPY);

	//Draw a header's area
	if (0 != m_dwHeaderHeight) 
	{
		//Fill a header background
		rcClient.bottom = rcClient.top + m_dwHeaderHeight;
		//Draw a header text
		if (!m_sHeader.IsEmpty())
		{
			RECT rcHeader;
			rcHeader.left = rcClient.left + m_dwSizes[PPMSGBOX_SIZE_MSGX];
			rcHeader.right = rcClient.right - m_dwSizes[PPMSGBOX_SIZE_MSGX];
			rcHeader.top = rcClient.top + m_dwSizes[PPMSGBOX_SIZE_MSGY];
			rcHeader.bottom = rcClient.bottom - m_dwSizes[PPMSGBOX_SIZE_MSGY];
			m_pHeader.DrawPreparedOutput(hMemDC, &rcHeader);
		} //if
		//Draw a Separator
		rcClient.top = rcClient.bottom;
	} //if

	//Draw a message's area
	if (!m_sText.IsEmpty()) 
	{
		//Margins from the top
		RECT rcMsg = rcClient;
		rcMsg.bottom = rcClient.top + m_dwMessageHeight - m_dwSizes [PPMSGBOX_SIZE_MSGY];
		rcMsg.top += m_dwSizes [PPMSGBOX_SIZE_MSGY];
		rcMsg.left += m_dwSizes [PPMSGBOX_SIZE_MSGX];
		rcMsg.right -= m_dwSizes [PPMSGBOX_SIZE_MSGX];
		m_pDrawer.DrawPreparedOutput(hMemDC, &rcMsg);
	} //if

	//Draw a moreinfo's area
	if (m_bMoreInfoOpened && m_dwMoreInfoHeight)
	{
		//Margins from the top
		RECT rcMoreInfo = rcFull;
		rcMoreInfo.top += rcMoreInfo.bottom - m_dwMoreInfoHeight + m_dwSizes [PPMSGBOX_SIZE_MSGY];
		rcMoreInfo.left += m_dwSizes [PPMSGBOX_SIZE_MSGX];
		rcMoreInfo.right -= m_dwSizes [PPMSGBOX_SIZE_MSGX];
		m_pMoreInfo.DrawPreparedOutput(hMemDC, &rcMoreInfo);
	} //if
	::SelectObject(hMemDC, hOldFont);

	if (!bAutoReleaseDC)
		::BitBlt(hDC, 0, 0, nWidth, nHeight, hMemDC, 0, 0, SRCCOPY);
	else
	{
		::BitBlt(hDC, 0, 0, nWidth, m_dwHeaderHeight + m_dwMessageHeight, hMemDC, 0, 0, SRCCOPY);
		if (m_bMoreInfoOpened)
			::BitBlt(hDC, 0, rcFull.bottom - m_dwMoreInfoHeight, nWidth, m_dwMoreInfoHeight, hMemDC, 0, nHeight - m_dwMoreInfoHeight, SRCCOPY);
	}

	::SelectObject(hMemDC, hOldBitmap);
	::SelectObject(hSrcDC, hOldSrcBitmap);
	::DeleteObject(hBitmap);
	::DeleteDC(hMemDC);
	::DeleteDC(hSrcDC);

	if (bAutoReleaseDC)
		::ReleaseDC(hwnd, hDC);
} //End of DrawBox

void CPPMessageBoxTemplate::DrawSeparator(HDC hDC, LPCRECT lpRect, int nTypeSeparator, LPCTSTR lpszText, int nTextAlign)
{
	RECT rect = {lpRect->left + m_dwSizes [PPMSGBOX_SIZE_SEPX], lpRect->top, lpRect->right - m_dwSizes [PPMSGBOX_SIZE_SEPX], lpRect->bottom};
	RECT rcTemp = rect;
	if ((NULL != lpszText) && (0 != lpszText [0]))
	{
		CPPString sText = lpszText;
		SIZE sz = {0, 0};
		::GetTextExtentPoint32(hDC, lpszText, sText.GetLength(), &sz);
		rcTemp.top = rect.top - sz.cy / 2;
		rcTemp.bottom = rcTemp.top + sz.cy;
		switch (nTextAlign)
		{
		case PPMSGBOX_ALIGN_LEFT:
			::DrawState(hDC, NULL, NULL, (LPARAM)lpszText, 0, rcTemp.left, rcTemp.top, 0, 0, DST_TEXT | DSS_DISABLED);
			rcTemp.left += sz.cx + m_dwSizes [PPMSGBOX_SIZE_SEPX];
			break;
		case PPMSGBOX_ALIGN_RIGHT:
			::DrawState(hDC, NULL, NULL, (LPARAM)lpszText, 0, rcTemp.right - sz.cx, rcTemp.top, 0, 0, DST_TEXT | DSS_DISABLED);
			rcTemp.right -= sz.cx + m_dwSizes [PPMSGBOX_SIZE_SEPX];
			break;
		case PPMSGBOX_ALIGN_CENTER:
			rcTemp.right = rcTemp.left + (rcTemp.right - rcTemp.left - sz.cx) / 2;
			::DrawState(hDC, NULL, NULL, (LPARAM)lpszText, 0, rcTemp.right, rcTemp.top, 0, 0, DST_TEXT | DSS_DISABLED);
			rcTemp.right -= m_dwSizes [PPMSGBOX_SIZE_SEPX];
			if (rcTemp.left < rcTemp.right)
			{
				rcTemp.top = rcTemp.bottom = rect.top;
				switch (nTypeSeparator)
				{
				case PPMSGBOX_SEP_ETCHED:
					::DrawEdge(hDC, &rcTemp, EDGE_ETCHED, BF_TOP);
					break;
				case PPMSGBOX_SEP_BLACK:
					m_pDrawer.GetDrawManager()->DrawLine(hDC, rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.top, RGB (0, 0, 0));
					break;
				case PPMSGBOX_SEP_WHITE:
					m_pDrawer.GetDrawManager()->DrawLine(hDC, rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.top, RGB (255, 255, 255));
					break;
				} //switch
			} //if
			rcTemp.left = rcTemp.right + 2 * m_dwSizes [PPMSGBOX_SIZE_SEPX] + sz.cx;
			rcTemp.right = rect.right;

			break;
		} //switch
		rcTemp.top = rcTemp.bottom = rect.top;
	} //if
	
	if (rcTemp.left < rcTemp.right)
	{
		switch (nTypeSeparator)
		{
		case PPMSGBOX_SEP_ETCHED:
			::DrawEdge(hDC, &rcTemp, EDGE_ETCHED, BF_TOP);
			break;
		case PPMSGBOX_SEP_BLACK:
			m_pDrawer.GetDrawManager()->DrawLine(hDC, rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.top, RGB (0, 0, 0));
			break;
		case PPMSGBOX_SEP_WHITE:
			m_pDrawer.GetDrawManager()->DrawLine(hDC, rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.top, RGB (255, 255, 255));
			break;
		} //switch
	} //if
} //End of DrawSeparator

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


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

Comments and Discussions