/////////////////////////////////////////////////////////////////////////////////////////
// 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__