/////////////////////////////////////////////////////////////////////////////////////////
// Project: SP ATL Extensions 1.3
//
// File: spDetailedMessageBox.cpp implementation of
// SP::CDetailedMessageWindow class,
// SP::CDetailedMessageBoxImpl and
// SP::CAxDetailedMessageBoxImpl
// class templates.
//
// Developer(s): Sergei Pavlovsky
// sergei_vp@hotmail.com
// Copyright (c) 2003-2005
//
// Description: In addition to message string extended message box provids an
// option to show/hide additional information or "details".
// CDetailedMessageWindow - implements common operations needed for
// message box functionality.
// CDetailedMessageBoxImpl - handles standard window messages.
// CAxDetailedMessageBoxImpl - handles standard window messages
// and also provides support for AcviveX controls.
//
// Platforms: Win32, ATL
//
// This code may be used in compiled form in any way you desire. This file may be
// redistributed unmodified by any means PROVIDING it is not sold for profit without
// the authors written consent, and providing that this notice and the authors name
// is included. If the source code in this file is used in any commercial application
// then acknowledgement must be made to the author of this file (in whatever form
// you wish).
//
// This file is provided "as is" with no expressed or implied warranty. The author
// accepts no liability for any damage/loss of business that this product may cause.
/////////////////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "spDetailedMessageBox.h"
namespace SP
{
/////////////////////////////////////////////////////////////////////////////////////////
// CDetailedMessageWindow class implementation
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// CDetailedMessageWindow::CDialogTempalte class implementation
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Initialization & uninitialization
BOOL CDetailedMessageWindow::CDialogTempalte::Initialize(const DTLMSGBOXPARAMS* pcDMBP)
{
ATLASSERT( !::IsBadReadPtr(pcDMBP, sizeof(DTLMSGBOXPARAMS)) );
ATLASSERT( !m_pcDMBP );
// Create measurment DC
HDC hdc = ::CreateIC(_T("DISPLAY"), NULL, NULL, NULL);
ATLASSERT( hdc );
if ( !hdc ) return FALSE;
m_pcDMBP = pcDMBP;
// Set font to device comtext
HFONT hFont = ( m_pcDMBP->hFont )
? m_pcDMBP->hFont
: (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
::SelectObject(hdc, hFont);
// Calculate elements sizes
BOOL bResult = Initialize(hdc);
ATLASSERT( bResult );
::DeleteDC(hdc);
if ( !bResult )
{
m_pcDMBP = NULL;
return FALSE;
}
return TRUE;
}
void CDetailedMessageWindow::CDialogTempalte::Uninitialize()
{
m_pcDMBP = NULL;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Helpers: Calculation of dimensions
void CDetailedMessageWindow::CDialogTempalte::CalculateAreaSize(const SIZE* pcSize,
BOOL bNotEmpty,
SIZE* pAreaSize) const
{
ATLASSERT( !::IsBadReadPtr(pcSize, sizeof(SIZE)) );
ATLASSERT( !::IsBadWritePtr(pAreaSize, sizeof(SIZE)) );
ATLASSERT( pcSize->cx >= 0 && pcSize->cy >= 0 );
ATLASSERT( m_sizeBaseUnit.cx > 0 && m_sizeBaseUnit.cy > 0 );
if ( bNotEmpty )
{
const LONG lPdnDbl = 2 * GetPadding();
pAreaSize->cx = pcSize->cx + lPdnDbl;
pAreaSize->cy = pcSize->cy + lPdnDbl;
}
else
{
pAreaSize->cx = pAreaSize->cy = 0;
}
}
BOOL CDetailedMessageWindow::CDialogTempalte::CalculateCaptionBarWidth(HDC hdc,
int* pcxCaptionBar) const
{
ATLASSERT( hdc );
ATLASSERT( !::IsBadWritePtr(pcxCaptionBar, sizeof(int)) );
ATLASSERT( m_pcDMBP );
int cButtons = ( IsContextHelpNeeded() ) ? 2 : 1;
int cxButton = IsToolWindowNeeded()
? ::GetSystemMetrics(SM_CXSMSIZE)
: ::GetSystemMetrics(SM_CXSIZE);
if ( m_pcDMBP->lpcszCaption )
{
// Set caption Font
HFONT hFont = (HFONT)::GetStockObject(SYSTEM_FONT);
HFONT hFontOld = (HFONT)::SelectObject(hdc, hFont);
SIZE size;
BOOL bResult = ::GetTextExtentPoint32(hdc, m_pcDMBP->lpcszCaption,
lstrlen(m_pcDMBP->lpcszCaption),
&size);
ATLASSERT( bResult );
::SelectObject(hdc, hFontOld);
if ( !bResult ) return FALSE;
*pcxCaptionBar = size.cx + cButtons * cxButton;
}
else
{
*pcxCaptionBar = cButtons * cxButton;
}
return TRUE;
}
int CDetailedMessageWindow::CDialogTempalte::CalculateButtonsWidth(int cButtons) const
{
ATLASSERT( cButtons >= 0 );
ATLASSERT( m_sizeButton.cx >= 0 && m_sizeButton.cy >= 0 );
return ( cButtons )
? (m_sizeButton.cx + GetSmallPadding()) * cButtons -
GetSmallPadding()
: 0;
}
int CDetailedMessageWindow::CDialogTempalte::CalculateButtonsHeight(int cButtons) const
{
ATLASSERT( cButtons >= 0 );
ATLASSERT( m_sizeButton.cx >= 0 && m_sizeButton.cy >= 0 );
return ( cButtons ) ? m_sizeButton.cy : 0;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Initialization: GUI
void CDetailedMessageWindow::CDialogTempalte::InitializeFontParameters(HDC hdc)
{
ATLASSERT( hdc );
ATLASSERT( m_pcDMBP );
HFONT hFont = (HFONT)::GetCurrentObject(hdc, OBJ_FONT);
LOGFONT lf; ::GetObject(hFont, sizeof(LOGFONT), &lf);
ATLASSERT( lf.lfHeight < 0 );
m_cyFontPointSize = MulDiv(-lf.lfHeight, 72, ::GetDeviceCaps(hdc, LOGPIXELSY));
m_wFontWeight = (WORD)lf.lfWeight;
m_bFontItalic = lf.lfItalic;
m_btFontCharset = lf.lfCharSet;
wcscpy(m_szFontFaceName, CT2W(lf.lfFaceName));
}
BOOL CDetailedMessageWindow::CDialogTempalte::InitializeBaseUnits(HDC hdc)
{
ATLASSERT( hdc );
// Define dialog base units
TEXTMETRIC tm;
BOOL bResult = ::GetTextMetrics(hdc, &tm);
ATLASSERT( bResult );
if ( !bResult ) return FALSE;
if ( tm.tmPitchAndFamily & TMPF_FIXED_PITCH )
{
const TCHAR szChars[] =
_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
const int cChars = sizeof(szChars)/sizeof(TCHAR) - 1;
SIZE size;
bResult = ::GetTextExtentPoint32(hdc, const_cast<LPTSTR>(szChars),
cChars, &size);
ATLASSERT( bResult );
if ( !bResult ) return FALSE;
// Round Up
m_sizeBaseUnit.cx = (size.cx / (cChars / 2) + 1) / 2;
}
else
{
m_sizeBaseUnit.cx = tm.tmAveCharWidth;
}
m_sizeBaseUnit.cy = tm.tmHeight;
return TRUE;
}
BOOL CDetailedMessageWindow::CDialogTempalte::InitializeIconSize(HDC hdc)
{
ATLASSERT( hdc );
ATLASSERT( m_sizeBaseUnit.cx > 0 && m_sizeBaseUnit.cy > 0 );
if ( IsIconNeeded() )
{
ICONINFO ii;
BOOL bResult = ::GetIconInfo(m_pcDMBP->hIcon, &ii);
ATLASSERT( bResult );
if ( !bResult ) return FALSE;
BITMAP bmp;
if ( ii.hbmColor )
::GetObject(ii.hbmColor, sizeof(BITMAP), &bmp);
else if ( ii.hbmMask )
::GetObject(ii.hbmMask, sizeof(BITMAP), &bmp);
else
bmp.bmWidth = bmp.bmHeight = 0;
m_sizeIcon.cx = bmp.bmWidth;
m_sizeIcon.cy = bmp.bmHeight;
if ( ii.hbmColor )
::DeleteObject(ii.hbmColor);
if ( ii.hbmMask )
::DeleteObject(ii.hbmMask);
}
else
{
m_sizeIcon.cx = m_sizeIcon.cy = 0;
}
return TRUE;
}
BOOL CDetailedMessageWindow::CDialogTempalte::InitializeButtonSize(HDC hdc,
const SIZE* pcClientMaxSize)
{
ATLASSERT( hdc );
ATLASSERT( !::IsBadReadPtr(pcClientMaxSize, sizeof(SIZE)) );
ATLASSERT( m_sizeBaseUnit.cx > 0 && m_sizeBaseUnit.cy > 0 );
const UINT unDTParams = DT_CALCRECT|DT_EDITCONTROL;
const LONG lPdnDbl = GetPadding() * 2;
// Maximum button size
SIZE sizeBtnMax = {
(pcClientMaxSize->cx - lPdnDbl) / (GetStdButtonsNumber() + 1) -
GetSmallPadding() * GetStdButtonsNumber(),
(pcClientMaxSize->cy - lPdnDbl) / 4
};
SIZE sizeBtnMin = {
(m_sizeBaseUnit.cx * 25) / 2,
(m_sizeBaseUnit.cy * 29) / 16
};
m_sizeButton.cx = 0;
m_sizeButton.cy = 0;
LPCTSTR lpcszText;
RECT rc;
// Define Detail button text bounds
if ( IsDetailButtonNeeded() )
{
ATLASSERT( m_pcDMBP->lpcszDetailOn && m_pcDMBP->lpcszDetailOff );
::SetRectEmpty(&rc);
lpcszText = m_pcDMBP->lpcszDetailOn;
int cyText = ::DrawTextEx(hdc, (LPTSTR)lpcszText, -1, &rc, unDTParams, NULL);
ATLASSERT( cyText );
if ( !cyText ) return FALSE;
if ( rc.right > m_sizeButton.cx )
m_sizeButton.cx = rc.right;
if ( rc.bottom > m_sizeButton.cy )
m_sizeButton.cy = rc.bottom;
::SetRectEmpty(&rc);
lpcszText = m_pcDMBP->lpcszDetailOff;
cyText = ::DrawTextEx(hdc, (LPTSTR)lpcszText, -1, &rc, unDTParams, NULL);
ATLASSERT( cyText );
if ( !cyText ) return FALSE;
if ( rc.right > m_sizeButton.cx )
m_sizeButton.cx = rc.right;
if ( rc.bottom > m_sizeButton.cy )
m_sizeButton.cy = rc.bottom;
rc.left = rc.top = rc.right = rc.bottom = 0;
}
// Define Help button text bounds
if ( IsHelpButtonNeeded() )
{
ATLASSERT( m_pcDMBP->lpcszHelp );
::SetRectEmpty(&rc);
lpcszText = m_pcDMBP->lpcszHelp;
int cyText = ::DrawTextEx(hdc, (LPTSTR)lpcszText, -1, &rc, unDTParams,
NULL);
ATLASSERT( cyText );
if ( !cyText ) return FALSE;
if ( rc.right > m_sizeButton.cx )
m_sizeButton.cx = rc.right;
if ( rc.bottom > m_sizeButton.cy )
m_sizeButton.cy = rc.bottom;
}
// Find maximum User button text bounds
for ( int i = 0; i < m_pcDMBP->cButtons && m_sizeButton.cx < sizeBtnMax.cx; i++ )
{
::SetRectEmpty(&rc);
lpcszText = m_pcDMBP->pacButtons[i].lpcszText;
if ( !lpcszText )
lpcszText = _T("");
int cyText = ::DrawTextEx(hdc, (LPTSTR)lpcszText, -1, &rc, unDTParams, NULL);
ATLASSERT( cyText );
if ( !cyText ) return FALSE;
if ( rc.right > m_sizeButton.cx )
m_sizeButton.cx = rc.right;
if ( rc.bottom > m_sizeButton.cy )
m_sizeButton.cy = rc.bottom;
}
// Adjust button size
m_sizeButton.cx += m_sizeBaseUnit.cx * 2; // 75
m_sizeButton.cy = (m_sizeButton.cy * 29) / 16; // 23
// Restrict button size
if ( m_sizeButton.cx < sizeBtnMin.cx )
m_sizeButton.cx = sizeBtnMin.cx;
if ( m_sizeButton.cy < sizeBtnMin.cy )
m_sizeButton.cy = sizeBtnMin.cy;
if ( m_sizeButton.cx > sizeBtnMax.cx )
m_sizeButton.cx = sizeBtnMax.cx;
if ( m_sizeButton.cy > sizeBtnMax.cy )
m_sizeButton.cy = sizeBtnMax.cy;
return TRUE;
}
BOOL CDetailedMessageWindow::CDialogTempalte::InitializeTextSize(HDC hdc,
const SIZE& sizeBounds)
{
ATLASSERT( hdc );
ATLASSERT( m_pcDMBP );
return CDetailedMessageWindow::CalcualteTextSize(hdc, m_pcDMBP->lpcszText,
sizeBounds, &m_sizeText);
}
BOOL CDetailedMessageWindow::CDialogTempalte::Initialize(HDC hdc)
{
ATLASSERT( hdc );
ATLASSERT( m_pcDMBP );
ATLASSERT( !::IsBadReadPtr(m_pcDMBP, sizeof(DTLMSGBOXPARAMS)) );
ATLASSERT( !::IsBadReadPtr(m_pcDMBP->pacButtons,
m_pcDMBP->cButtons * sizeof(DTLMSGBOXBUTTONPARAMS)) );
// Define owner window
HWND hwndOwner = CDetailedMessageWindow::GetActualOwnerWindow(m_pcDMBP->hwndOwner);
// Collect Font parameters
InitializeFontParameters(hdc);
// Collect display parameters
BOOL bResult = InitializeBaseUnits(hdc);
ATLASSERT( bResult );
if ( !bResult ) return FALSE;
// Monitor size
#if (_WIN32_WINNT >= 0x0500)
// HMONITOR hMonitor = ::MonitorFromWindow(hwndOwner, MONITOR_DEFAULTTONEAREST);
HMONITOR hMonitor = ::MonitorFromWindow(hwndOwner, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO mni = { sizeof(MONITORINFO) };
::GetMonitorInfo(hMonitor, &mni);
SIZE sizeMonitor = {
mni.rcWork.right - mni.rcWork.left,
mni.rcWork.bottom - mni.rcWork.top
};
#else // (_WIN32_WINNT >= 0x0500)
RECT rcMonitor;
::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcMonitor, 0);
SIZE sizeMonitor = {
rcMonitor.right - rcMonitor.left,
rcMonitor.bottom - rcMonitor.top
};
#endif // (_WIN32_WINNT >= 0x0500)
// Calculate size of dialog non-client parts
SIZE sizeNC = {
::GetSystemMetrics(SM_CXDLGFRAME) * 2,
::GetSystemMetrics(SM_CYDLGFRAME) * 2 + ::GetSystemMetrics(SM_CYCAPTION)
};
// Calculate maximum dialog size
SIZE sizeClientMax = {
sizeMonitor.cx * 14 / 16 - sizeNC.cx,
sizeMonitor.cy * 7 / 16 - sizeNC.cy
};
// Padding
const LONG lPdn = GetPadding();
const LONG lPdnDbl = 2 * lPdn;
const LONG lPdnHlf = GetSmallPadding();
// Calculate caption bar size
int cxCaptionBar;
bResult = CalculateCaptionBarWidth(hdc, &cxCaptionBar);
ATLASSERT( bResult );
if ( !bResult ) return FALSE;
// Calculate icon size and its area
bResult = InitializeIconSize(hdc);
ATLASSERT( bResult );
if ( !bResult ) return FALSE;
SIZE sizeIconArea;
CalculateAreaSize(&m_sizeIcon, IsIconNeeded(), &sizeIconArea);
// Calculate minimal text area
SIZE sizeMinTextArea;
if ( IsTextNeeded() )
{
sizeMinTextArea.cx = lPdnDbl;
sizeMinTextArea.cy = m_sizeBaseUnit.cy + lPdnDbl;
}
else
{
sizeMinTextArea.cx = sizeMinTextArea.cy = 0;
}
// Calculate button size
bResult = InitializeButtonSize(hdc, &sizeClientMax);
ATLASSERT( bResult );
if ( !bResult ) return FALSE;
// Calculate size required for all buttons
int cStdBtns = GetStdButtonsNumber();
m_cUsrBtns = m_pcDMBP->cButtons;
int cAllBtns = m_pcDMBP->cButtons + cStdBtns;
SIZE sizeButtons = {
CalculateButtonsWidth(cAllBtns),
CalculateButtonsHeight(cAllBtns)
};
SIZE sizeButtonArea;
CalculateAreaSize(&sizeButtons, (BOOL)cAllBtns, &sizeButtonArea);
// Calculate minimal dialog size required to place all the elements in.
SIZE sizeClientMin;
sizeClientMin.cx = sizeIconArea.cx + sizeMinTextArea.cx;
sizeClientMin.cx = max(cxCaptionBar, sizeClientMin.cx);
sizeClientMin.cx = max(sizeButtonArea.cx, sizeClientMin.cx);
sizeClientMin.cy = max(sizeMinTextArea.cy, sizeIconArea.cy);
sizeClientMin.cy += sizeButtonArea.cy;
// Restrict minimal size
if ( sizeClientMin.cx > sizeClientMax.cx )
{
int cxButtons = sizeClientMax.cx - lPdnDbl;
cAllBtns = (cxButtons + lPdnHlf)/(m_sizeButton.cx + lPdnHlf);
m_cUsrBtns = cAllBtns - cStdBtns;
if ( m_cUsrBtns > m_pcDMBP->cButtons )
{
m_cUsrBtns = m_pcDMBP->cButtons;
cAllBtns = m_cUsrBtns + cStdBtns;
}
sizeButtons.cx = CalculateButtonsWidth(cAllBtns);
sizeButtons.cy = CalculateButtonsHeight(cAllBtns);
CalculateAreaSize(&sizeButtons, (BOOL)cAllBtns, &sizeButtonArea);
sizeClientMin.cx = sizeIconArea.cx + sizeMinTextArea.cx;
sizeClientMin.cx = max(sizeClientMin.cx, cxCaptionBar);
sizeClientMin.cx = max(sizeClientMin.cx, sizeButtonArea.cx);
if ( sizeClientMin.cx > sizeClientMax.cx )
{
sizeClientMin.cx = sizeClientMax.cx;
}
}
if ( sizeClientMin.cy > sizeClientMax.cy )
{
sizeClientMin.cy = sizeClientMax.cy;
}
// Try to layout all child elements inside minimal portion of the screen.
for ( LONG lPortion = 8; lPortion < 15; lPortion++ )
{
m_sizeClient.cx = sizeMonitor.cx * lPortion / 16 - sizeNC.cx;
m_sizeClient.cy = sizeMonitor.cy * lPortion / 32 - sizeNC.cy;
if ( m_sizeClient.cx >= sizeClientMin.cx )
{
SIZE sizeBounds = {
m_sizeClient.cx - sizeIconArea.cx - lPdnDbl,
m_sizeClient.cy - sizeButtonArea.cy - lPdnDbl
};
bResult = InitializeTextSize(hdc, sizeBounds);
ATLASSERT( bResult );
if ( !bResult ) return FALSE;
SIZE sizeTextArea;
CalculateAreaSize(&m_sizeText, IsTextNeeded(), &sizeTextArea);
m_sizeClient.cx = sizeTextArea.cx + sizeIconArea.cx;
m_sizeClient.cy = GetIconAndTextAreaHeight() + sizeButtonArea.cy;
if ( m_sizeClient.cx <= sizeClientMax.cx &&
m_sizeClient.cy <= sizeClientMax.cy )
{
// All child elements fit to the dialog of current size.
break;
}
}
}
// Restrict client size
if ( m_sizeClient.cx < sizeClientMin.cx )
m_sizeClient.cx = sizeClientMin.cx;
if ( m_sizeClient.cy < sizeClientMin.cy )
m_sizeClient.cy = sizeClientMin.cy;
m_bScrollText = m_sizeText.cx && (m_sizeClient.cx > sizeClientMax.cx ||
m_sizeClient.cy > sizeClientMax.cy);
// Restrict text area to fit maximum dialog box size.
if ( m_bScrollText )
{
if ( m_sizeClient.cx > sizeClientMax.cx )
m_sizeClient.cx = sizeClientMax.cx;
if ( m_sizeClient.cy > sizeClientMax.cy )
m_sizeClient.cy = sizeClientMax.cy;
m_sizeText.cx = m_sizeClient.cx - sizeIconArea.cx - lPdnDbl;
m_sizeText.cy = m_sizeClient.cy - sizeButtonArea.cy - lPdnDbl;
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Helpers: Dialog template
int CDetailedMessageWindow::CDialogTempalte::GetControlsNumber() const
{
ATLASSERT( m_pcDMBP );
int cStdBtns = m_cUsrBtns;
if ( IsDetailButtonNeeded() )
cStdBtns++;
if ( IsHelpButtonNeeded() )
cStdBtns++;
if ( IsIconNeeded() )
cStdBtns++;
if ( IsTextNeeded() )
cStdBtns++;
return cStdBtns;
}
SIZE_T CDetailedMessageWindow::CDialogTempalte::CalculateDlgHdrSize() const
{
ATLASSERT( m_pcDMBP );
SIZE_T cbResult = sizeof(WORD) * 11 +
sizeof(DWORD) * 3 +
sizeof(BYTE) * 2 +
(wcslen(CT2W(m_pcDMBP->lpcszCaption)) + 1) * sizeof(WCHAR) +
(wcslen(m_szFontFaceName) + 1) * sizeof(WCHAR);
cbResult = reinterpret_cast<SIZE_T>(AlignByDWord((void*)cbResult));
return cbResult;
}
void* CDetailedMessageWindow::CDialogTempalte::AddDlgHdr(void* pBuffer,
const RECT* pcrc) const
{
ATLASSERT( !::IsBadReadPtr(pcrc, sizeof(RECT)) );
ATLASSERT( m_pcDMBP );
const DWORD dwDlgStyle = WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_VISIBLE|WS_CLIPCHILDREN|
DS_MODALFRAME|DS_SETFONT|DS_3DLOOK;
const DWORD dwDlgExStyle = (IsContextHelpNeeded() ? WS_EX_CONTEXTHELP : 0) |
#if (_WIN32_WINNT >= 0x0500)
(IsRTLLayout() ? WS_EX_LAYOUTRTL : 0) |
#endif //(_WIN32_WINNT >= 0x0500)
(IsToolWindowNeeded() ? WS_EX_TOOLWINDOW : 0);
MultyTypePtr ptr;
ptr.lpw = (WORD*)pBuffer;
// Fill the DLGTEMPLATEEX structure.
*ptr.lpw++ = 1; // dialog version
*ptr.lpw++ = 0xFFFF; // signature
*ptr.lpdw++ = m_pcDMBP->dwCtxHelpID; // help ID
*ptr.lpdw++ = dwDlgExStyle; // extended style
*ptr.lpdw++ = dwDlgStyle; // style
*ptr.lpw++ = GetControlsNumber(); // number of child items
*ptr.lpw++ = WORD(pcrc->left); // x
*ptr.lpw++ = WORD(pcrc->top); // y
*ptr.lpw++ = WORD(pcrc->right - pcrc->left); // cx
*ptr.lpw++ = WORD(pcrc->bottom - pcrc->top); // cy
*ptr.lpw++ = 0; // menu
*ptr.lpw++ = 0; // class
// Set Title
LPWSTR lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
wcscpy(lpwszText, m_pcDMBP->lpcszCaption ? CT2W(m_pcDMBP->lpcszCaption) : L"");
size_t cLength = wcslen(lpwszText);
ptr.lpw = reinterpret_cast<WORD*>(lpwszText + cLength + 1);
// Add font information if DS_SETFONT is used.
if ( dwDlgStyle & DS_SETFONT )
{
*ptr.lpw++ = m_cyFontPointSize; // Point size
*ptr.lpw++ = m_wFontWeight; // Weight
*ptr.lpb++ = m_bFontItalic; // italic flag
*ptr.lpb++ = m_btFontCharset; // charset
// Set Font name
lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
wcscpy(lpwszText, m_szFontFaceName);
cLength = wcslen(lpwszText);
ptr.lpw = reinterpret_cast<WORD*>(lpwszText + cLength + 1);
}
// Make sure the next item starts on a DWORD boundary.
ptr.lpw = reinterpret_cast<WORD*>(AlignByDWord(ptr.lpw));
return ptr.lpw;
}
SIZE_T CDetailedMessageWindow::CDialogTempalte::CalculateDlgIconSize() const
{
ATLASSERT( m_pcDMBP );
ATLASSERT( IsIconNeeded() );
WCHAR szWndClass[] = L"STATIC";
SIZE_T cbResult = sizeof(WORD) * 7 +
sizeof(DWORD) * 4 +
sizeof(szWndClass);
cbResult = reinterpret_cast<SIZE_T>(AlignByDWord((void*)cbResult));
return cbResult;
}
void* CDetailedMessageWindow::CDialogTempalte::AddDlgIcon(void* pBuffer,
const RECT* pcrc) const
{
ATLASSERT( pBuffer );
ATLASSERT( !::IsBadReadPtr(pcrc, sizeof(RECT)) );
ATLASSERT( m_pcDMBP );
ATLASSERT( IsIconNeeded() );
const DWORD dwStyle = WS_VISIBLE|WS_CHILD|SS_ICON;
WCHAR szWndClass[] = L"STATIC";
MultyTypePtr ptr;
ptr.lpw = (WORD*)pBuffer;
// Fill DLGITEMTEMPLATEEX structure
*ptr.lpdw++ = m_pcDMBP->dwCtxHelpID; // HelpID
*ptr.lpdw++ = 0; // ExtendedStyle
*ptr.lpdw++ = dwStyle; // Style;
*ptr.lpw++ = WORD(pcrc->left); // x
*ptr.lpw++ = WORD(pcrc->top); // y
*ptr.lpw++ = WORD(pcrc->right - pcrc->left); // cx
*ptr.lpw++ = WORD(pcrc->bottom - pcrc->top); // cy
*ptr.lpdw++ = -1; // IDC_STATIC // Control ID
// Fill in class i.d., this time by name.
LPWSTR lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
wcscpy(lpwszText, szWndClass);
ptr.lpw = reinterpret_cast<WORD*>(lpwszText) + sizeof(szWndClass)/sizeof(WCHAR);
// Resource ID
*ptr.lpw++ = 0xFFFF;
*ptr.lpw++ = 0;
// Advance pointer over nExtraStuff WORD.
*ptr.lpw++ = 0;
// Make sure the next item starts on a DWORD boundary.
ptr.lpw = reinterpret_cast<WORD*>(AlignByDWord(ptr.lpw));
return ptr.lpw;
}
SIZE_T CDetailedMessageWindow::CDialogTempalte::CalculateDlgTextSize() const
{
ATLASSERT( m_pcDMBP );
ATLASSERT( IsTextNeeded() && m_pcDMBP->lpcszText );
WCHAR szWndClass[] = L"STATIC";
SIZE_T cbResult = sizeof(WORD) * 5 +
sizeof(DWORD) * 4 +
sizeof(szWndClass) +
(wcslen(CT2W(m_pcDMBP->lpcszText)) + 1) * sizeof(WCHAR);
cbResult = reinterpret_cast<SIZE_T>(AlignByDWord((void*)cbResult));
return cbResult;
}
void* CDetailedMessageWindow::CDialogTempalte::AddDlgText(void* pBuffer,
const RECT* pcrc) const
{
ATLASSERT( pBuffer );
ATLASSERT( !::IsBadReadPtr(pcrc, sizeof(RECT)) );
ATLASSERT( m_pcDMBP );
ATLASSERT( IsTextNeeded() && m_pcDMBP->lpcszText );
const DWORD dwStyle = WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_READONLY|
(m_bScrollText ? WS_VSCROLL : 0);
WCHAR szWndClass[] = L"EDIT";
MultyTypePtr ptr;
ptr.lpw = (WORD*)pBuffer;
// Fill DLGITEMTEMPLATEEX structure
*ptr.lpdw++ = 0; // HelpID
*ptr.lpdw++ = 0; // ExtendedStyle
*ptr.lpdw++ = dwStyle; // Style;
*ptr.lpw++ = WORD(pcrc->left); // x
*ptr.lpw++ = WORD(pcrc->top); // y
*ptr.lpw++ = WORD(pcrc->right - pcrc->left); // cx
*ptr.lpw++ = WORD(pcrc->bottom - pcrc->top); // cy
*ptr.lpdw++ = -1; // IDC_STATIC // Control ID
// Fill in class i.d., this time by name.
LPWSTR lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
wcscpy(lpwszText, szWndClass);
ptr.lpw = reinterpret_cast<WORD*>(lpwszText) + sizeof(szWndClass)/sizeof(WCHAR);
// Copy the text of the first item.
lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
wcscpy(lpwszText, CT2W(m_pcDMBP->lpcszText));
size_t cLength = wcslen(lpwszText);
ptr.lpw = reinterpret_cast<WORD*>(lpwszText + cLength + 1);
// Advance pointer over nExtraStuff WORD.
*ptr.lpw++ = 0;
// Make sure the next item starts on a DWORD boundary.
ptr.lpw = reinterpret_cast<WORD*>(AlignByDWord(ptr.lpw));
return ptr.lpw;
}
SIZE_T CDetailedMessageWindow::CDialogTempalte::CalculateDlgButtonSize(
LPCTSTR lpcszText) const
{
ATLASSERT( m_pcDMBP );
WCHAR szWndClass[] = L"BUTTON";
SIZE_T cbResult = sizeof(WORD) * 5 +
sizeof(DWORD) * 4 +
sizeof(szWndClass) +
(wcslen(lpcszText ? CT2W(lpcszText) : L"") + 1) * sizeof(WCHAR);
cbResult = reinterpret_cast<SIZE_T>(AlignByDWord((void*)cbResult));
return cbResult;
}
void* CDetailedMessageWindow::CDialogTempalte::AddDlgButton(void* pBuffer,
const DTLMSGBOXBUTTONPARAMS* pcInfo,
BOOL bDefault, const RECT* pcrc) const
{
ATLASSERT( !::IsBadReadPtr(pcrc, sizeof(RECT)) );
ATLASSERT( !::IsBadReadPtr(pcInfo, sizeof(DTLMSGBOXBUTTONPARAMS)) );
ATLASSERT( m_pcDMBP );
WCHAR szWndClass[] = L"BUTTON";
MultyTypePtr ptr;
ptr.lpw = (WORD*)pBuffer;
// Fill DLGITEMTEMPLATEEX structure
const DWORD dwBtnStyle = (bDefault ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON)|
WS_VISIBLE|WS_CHILD|WS_TABSTOP;
*ptr.lpdw++ = pcInfo->dwCtxHelpID; // HelpID
*ptr.lpdw++ = 0; // ExtendedStyle
*ptr.lpdw++ = dwBtnStyle; // Style;
*ptr.lpw++ = WORD(pcrc->left); // x
*ptr.lpw++ = WORD(pcrc->top); // y
*ptr.lpw++ = WORD(pcrc->right - pcrc->left); // cx
*ptr.lpw++ = WORD(pcrc->bottom - pcrc->top); // cy
*ptr.lpdw++ = pcInfo->unID; // Control ID
// Fill in class i.d., this time by name.
LPWSTR lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
wcscpy(lpwszText, szWndClass);
ptr.lpw = reinterpret_cast<WORD*>(lpwszText) + sizeof(szWndClass)/sizeof(WCHAR);
// Copy the text of the first item.
lpwszText = reinterpret_cast<LPWSTR>(ptr.lpw);
wcscpy(lpwszText, pcInfo->lpcszText ? CT2W(pcInfo->lpcszText) : L"");
size_t cLength = wcslen(lpwszText);
ptr.lpw = reinterpret_cast<WORD*>(lpwszText + cLength + 1);
// Advance pointer over nExtraStuff WORD.
*ptr.lpw++ = 0;
// Make sure the next item starts on a DWORD boundary.
ptr.lpw = reinterpret_cast<WORD*>(AlignByDWord(ptr.lpw));
return ptr.lpw;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Operations
SIZE_T CDetailedMessageWindow::CDialogTempalte::CalculateDlgTmplSize() const
{
ATLASSERT( m_pcDMBP );
SIZE_T cResult = CalculateDlgHdrSize();
if ( IsIconNeeded() )
cResult += CalculateDlgIconSize();
if ( IsTextNeeded() )
cResult += CalculateDlgTextSize();
if ( m_cUsrBtns )
{
for ( int i = 0; i < m_cUsrBtns; i++ )
{
LPCTSTR lpcszText = m_pcDMBP->pacButtons[i].lpcszText;
cResult += CalculateDlgButtonSize(lpcszText);
}
}
if ( IsHelpButtonNeeded() )
{
LPCTSTR lpcszText = m_pcDMBP->lpcszHelp;
cResult += CalculateDlgButtonSize(lpcszText);
}
if ( IsDetailButtonNeeded() )
{
LPCTSTR lpcszText = m_pcDMBP->lpcszDetailOn;
cResult += CalculateDlgButtonSize(lpcszText);
}
return cResult;
}
BOOL CDetailedMessageWindow::CDialogTempalte::GenerateDlgTmpl(void* lpBuffer) const
{
ATLASSERT( lpBuffer );
ATLASSERT( m_pcDMBP );
// Add dialog template header
RECT rc;
rc.left = 0;
rc.top = 0;
rc.right = m_sizeClient.cx;
rc.bottom = m_sizeClient.cy;
RECTPix2DU(&rc);
void* lpTmpl = AddDlgHdr(lpBuffer, &rc);
// Add icon to dialog template
if ( IsIconNeeded() )
{
rc.left = GetPadding();
rc.top = GetPadding();
rc.right = rc.left + m_sizeIcon.cx;
rc.bottom = rc.top + m_sizeIcon.cy;
RECTPix2DU(&rc);
lpTmpl = AddDlgIcon(lpTmpl, &rc);
}
// Add text to dialog template
if ( IsTextNeeded() )
{
SIZE sizeIconArea;
CalculateAreaSize(&m_sizeIcon, TRUE, &sizeIconArea);
rc.left = sizeIconArea.cx + GetPadding();
rc.top = GetPadding();
rc.right = rc.left + m_sizeText.cx;
rc.bottom = rc.top + m_sizeText.cy;
RECTPix2DU(&rc);
lpTmpl = AddDlgText(lpTmpl, &rc);
}
// Add buttons
int cyIconAndTextArea = GetIconAndTextAreaHeight();
SIZE sizeButtons = { GetAllButtonsWidth(), GetAllButtonsHeight() };
// Add all user's buttons to dialog template
if ( m_cUsrBtns )
{
rc.right = m_sizeClient.cx - GetPadding();
rc.left = rc.right - sizeButtons.cx;
rc.top = cyIconAndTextArea + GetPadding();
rc.bottom = rc.top + sizeButtons.cy;
RECTPix2DU(&rc);
LONG cxUsrBtn = XPix2DU(m_sizeButton.cx);
LONG cxUsrBtnGap = XPix2DU(GetSmallPadding());
for ( int i = 0; i < m_cUsrBtns; i++ )
{
rc.right = rc.left + cxUsrBtn;
lpTmpl = AddDlgButton(lpTmpl, &m_pcDMBP->pacButtons[i],
i == m_pcDMBP->iDefaultButton, &rc);
rc.left = rc.right + cxUsrBtnGap;
}
}
// Add "Help" button to dialog template
if ( IsHelpButtonNeeded() )
{
rc.left = m_sizeClient.cx - GetPadding() - sizeButtons.cx +
(m_sizeButton.cx + GetSmallPadding()) * m_cUsrBtns;
rc.right = rc.left + m_sizeButton.cx;
rc.top = cyIconAndTextArea + GetPadding();
rc.bottom = rc.top + m_sizeButton.cy;
DTLMSGBOXBUTTONPARAMS dmbpDtl = {
m_pcDMBP->unHelpID,
m_pcDMBP->lpcszHelp,
m_pcDMBP->dwHelpCtxHelpID
};
RECTPix2DU(&rc);
lpTmpl = AddDlgButton(lpTmpl, &dmbpDtl, FALSE, &rc);
}
// Add "Detail" button to dialog template
if ( IsDetailButtonNeeded() )
{
rc.right = m_sizeClient.cx - GetPadding();
rc.left = rc.right - m_sizeButton.cx;
rc.top = cyIconAndTextArea + GetPadding();
rc.bottom = rc.top + m_sizeButton.cy;
DTLMSGBOXBUTTONPARAMS dmbpDtl = {
m_pcDMBP->unDetailID,
m_pcDMBP->lpcszDetailOn,
m_pcDMBP->dwDetailCtxHelpID
};
RECTPix2DU(&rc);
lpTmpl = AddDlgButton(lpTmpl, &dmbpDtl, FALSE, &rc);
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Utilities
BOOL CDetailedMessageWindow::CalcualteTextSize(HDC hdc, LPCTSTR lpcszText,
const SIZE& sizeBounds,
SIZE* pSize)
{
ATLASSERT( hdc );
ATLASSERT( !::IsBadWritePtr(pSize, sizeof(SIZE)) );
const UINT unDTParams = DT_CALCRECT|DT_WORDBREAK|DT_EXTERNALLEADING|
DT_EXPANDTABS|DT_NOPREFIX|DT_EDITCONTROL;
if ( lpcszText )
{
RECT rc = { 0, 0, sizeBounds.cx, sizeBounds.cy };
int cyText = ::DrawTextEx(hdc, (LPTSTR)lpcszText, -1, &rc, unDTParams,
NULL);
ATLASSERT( cyText );
if ( !cyText ) return FALSE;
pSize->cx = rc.right;
pSize->cy = rc.bottom;
}
else
{
pSize->cx = pSize->cy = 0;
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Initialization & uninitialization
void CDetailedMessageWindow::InitializeParams(const DTLMSGBOXPARAMS* pcDMBP)
{
ATLASSERT( !::IsBadReadPtr(pcDMBP, sizeof(DTLMSGBOXPARAMS)) );
ATLASSERT( !::IsBadReadPtr(pcDMBP->pacButtons,
sizeof(DTLMSGBOXBUTTONPARAMS) * pcDMBP->cButtons) );
ATLASSERT( pcDMBP->iDefaultButton < pcDMBP->cButtons );
m_sizeMinClient.cx = m_sizeMinClient.cx = 0;
m_cyMaxText = 0;
m_sizeButton.cx = m_sizeButton.cy = 0;
if ( pcDMBP->fStyle & DMB_HASHELP )
m_unHelpID = pcDMBP->unHelpID;
else
m_unHelpID = 0;
if ( pcDMBP->fStyle & DMB_HASDETAIL )
{
m_unDetailID = pcDMBP->unDetailID;
m_csDetailOn = pcDMBP->lpcszDetailOn ;
m_csDetailOff = pcDMBP->lpcszDetailOff;
}
else
m_unDetailID = 0;
m_hIcon = pcDMBP->hIcon;
}
DLGTEMPLATE* CDetailedMessageWindow::CreateDialogTemplate(const DTLMSGBOXPARAMS* pcDMBP)
{
ATLASSERT( !::IsBadReadPtr(pcDMBP, sizeof(DTLMSGBOXPARAMS)) );
CDialogTempalte templ;
BOOL bResult = templ.Initialize(pcDMBP);
ATLASSERT( bResult );
if ( !bResult ) return NULL;
m_sizeMinClient = *templ.GetClientSize();
m_lPadding = templ.GetPadding();
m_lSmallPadding = templ.GetSmallPadding();
m_sizeButton = *templ.GetButtonSize();
m_cyMaxText = templ.GetTextSize()->cy;
SIZE_T cbSize = templ.CalculateDlgTmplSize();
DLGTEMPLATE* lpdt = (DLGTEMPLATE*)malloc(cbSize);
ATLASSERT( lpdt );
if ( !lpdt ) return NULL;
bResult = templ.GenerateDlgTmpl(lpdt);
ATLASSERT( bResult );
if ( !bResult )
{
free(lpdt);
return NULL;
}
return lpdt;
}
void CDetailedMessageWindow::FreeDialogTemplate(DLGTEMPLATE* lpdt)
{
free(lpdt);
}
/////////////////////////////////////////////////////////////////////////////////////////
// Helpers: Child controls
BOOL CDetailedMessageWindow::InitializeChildControls()
{
ATLASSERT( IsWindow() );
ATLASSERT( !m_hwndIcon && !m_hwndText && m_vecButtons.empty() && !m_hwndDetail );
RECT rc; GetClientRect(&rc);
m_sizeMinClient.cx = rc.right - rc.left;
m_sizeMinClient.cy = rc.bottom - rc.top;
// Find Child controls
const SIZE_T cClassNameSize = 256;
LPCTSTR lpcszStaticClassName = _T("STATIC");
LPCTSTR lpcszEditClassName = _T("EDIT");
LPCTSTR lpcszButtonClassName = _T("BUTTON");
for ( HWND hWnd = GetTopWindow(); hWnd; hWnd = ::GetNextWindow(hWnd, GW_HWNDNEXT) )
{
TCHAR szClass[cClassNameSize];
::GetClassName(hWnd, szClass, cClassNameSize);
if ( lstrcmpi(szClass, lpcszStaticClassName) == 0 )
{
m_hwndIcon = hWnd;
}
else if ( lstrcmpi(szClass, lpcszEditClassName) == 0 )
{
m_hwndText = hWnd;
}
else
{
ATLASSERT( ( lstrcmpi(szClass, lpcszButtonClassName) == 0 ) );
m_vecButtons.push_back(hWnd);
}
}
// Set Icon
if ( m_hwndIcon && m_hIcon )
{
::SendMessage(m_hwndIcon, STM_SETICON, WPARAM(m_hIcon), 0);
}
// Create gripper
LONG cxGripper = ::GetSystemMetrics(SM_CXVSCROLL);
LONG cyGripper = ::GetSystemMetrics(SM_CYHSCROLL);
m_hwndGripper = ::CreateWindowEx(0, _T("SCROLLBAR"), NULL,
WS_CHILD|WS_CLIPSIBLINGS|SBS_SIZEGRIP|
SBS_SIZEBOX|SBS_BOTTOMALIGN|SBS_RIGHTALIGN,
m_sizeMinClient.cx - cxGripper,
m_sizeMinClient.cy - cyGripper,
cxGripper, cyGripper, m_hWnd, NULL,
_AtlBaseModule.GetResourceInstance(), NULL);
ATLASSERT( m_hwndGripper );
POINT apt[] = { {0, cyGripper}, {cxGripper, 0}, {cxGripper, cyGripper} };
HRGN hrgn = ::CreatePolygonRgn(apt, sizeof(apt)/sizeof(POINT), ALTERNATE);
::SetWindowRgn(m_hwndGripper, hrgn, FALSE);
::DeleteObject(hrgn);
// Create Detail control
RECT rcDetail = {
rc.left + m_lPadding,
rc.bottom,
rc.right + m_lPadding,
rc.bottom
};
m_hwndDetail = CreateDetailControl(&rcDetail);
return TRUE;
}
void CDetailedMessageWindow::AlignChildControls(LONG cx, LONG cy)
{
ATLASSERT( IsWindow() );
const LONG lDblPadding = 2 * m_lPadding;
const LONG xRight = cx - m_lPadding;
int cWindows = static_cast<int>(m_vecButtons.size()) +
(m_hwndText ? 1 : 0) +
(m_hwndDetail ? 1 : 0) +
(m_hwndGripper ? 1 : 0);
HDWP hdwp = ::BeginDeferWindowPos(cWindows);
RECT rc;
LONG yNext = 0;
if ( m_hwndIcon )
{
::GetWindowRect(m_hwndIcon, &rc);
ScreenToClient(&rc);
yNext += rc.bottom + m_lPadding;
}
// Align text
if ( m_hwndText )
{
::GetWindowRect(m_hwndText, &rc);
ScreenToClient(&rc);
SIZE sizeText = { xRight - rc.left, rc.bottom - rc.top };
if ( xRight != rc.right )
{
int cLength = ::GetWindowTextLength(m_hwndText);
LPTSTR lpszText = (LPTSTR)malloc((cLength + 1) * sizeof(TCHAR));
ATLASSERT( lpszText );
if ( lpszText )
{
SIZE sizeMax = { sizeText.cx, m_cyMaxText };
::GetWindowText(m_hwndText, lpszText, cLength + 1);
SIZE size;
HDC hdc = ::GetDC(m_hwndText);
CalcualteTextSize(hdc, lpszText, sizeMax, &size);
ReleaseDC(hdc);
if ( sizeText.cy != size.cy )
{
sizeText.cy = ( size.cy < m_cyMaxText ) ? size.cy : m_cyMaxText;
}
free(lpszText);
}
hdwp = ::DeferWindowPos(hdwp, m_hwndText, NULL, 0, 0,
sizeText.cx, sizeText.cy,
SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE);
}
LONG y = rc.top + sizeText.cy + m_lPadding;
if ( y > yNext )
yNext = y;
}
// Align buttons
if ( !m_vecButtons.empty() )
{
yNext += m_lPadding;
LONG x = xRight - m_sizeButton.cx;
for ( CHwndVector::reverse_iterator it = m_vecButtons.rbegin();
it != m_vecButtons.rend(); it++ )
{
HWND hwndButton = *it;
hdwp = ::DeferWindowPos(hdwp, hwndButton, NULL, x, yNext, 0, 0,
SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE);
x -= (m_sizeButton.cx + m_lSmallPadding);
}
yNext += m_sizeButton.cy + m_lPadding;
}
// Align Gripper control
if ( m_hwndGripper )
{
UINT fMode = IsDetailShown() ? SWP_NOZORDER|SWP_NOSIZE|SWP_SHOWWINDOW
: SWP_NOZORDER|SWP_NOSIZE|SWP_HIDEWINDOW;
LONG cxGripper = ::GetSystemMetrics(SM_CXVSCROLL);
LONG cyGripper = ::GetSystemMetrics(SM_CYHSCROLL);
hdwp = ::DeferWindowPos(hdwp, m_hwndGripper, NULL, cx - cxGripper,
cy - cyGripper, 0, 0, fMode);
}
// Align Detail control
if ( m_hwndDetail )
{
UINT fMode = IsDetailShown() ? SWP_NOZORDER|SWP_NOACTIVATE|SWP_SHOWWINDOW
: SWP_NOZORDER|SWP_NOACTIVATE|SWP_HIDEWINDOW;
hdwp = ::DeferWindowPos(hdwp, m_hwndDetail, NULL, m_lPadding, yNext,
cx - m_lPadding * 2, cy - yNext - m_lPadding,
fMode);
}
::EndDeferWindowPos(hdwp);
}
void CDetailedMessageWindow::CalculateMinWindowSize(SIZE* pSize) const
{
ATLASSERT( !IsBadWritePtr(pSize, sizeof(SIZE)) );
DWORD_PTR fExStyle = GetWindowLongPtr(GWL_EXSTYLE);
// Get Metrics
const LONG dyCaption = (fExStyle & WS_EX_TOOLWINDOW)
? ::GetSystemMetrics(SM_CYSMCAPTION)
: ::GetSystemMetrics(SM_CYCAPTION);
pSize->cx = m_sizeMinClient.cx +
2 * ::GetSystemMetrics(SM_CXSIZEFRAME);
pSize->cy = m_sizeMinClient.cy * 2 +
2 * ::GetSystemMetrics(SM_CYSIZEFRAME) + dyCaption;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Operations: User Commads
void CDetailedMessageWindow::ShowDetail(BOOL bShow)
{
ATLASSERT( IsWindow() );
DWORD_PTR fExStyle = GetWindowLongPtr(GWL_EXSTYLE);
// Get Metrics
const LONG dyCaption = (fExStyle & WS_EX_TOOLWINDOW)
? ::GetSystemMetrics(SM_CYSMCAPTION)
: ::GetSystemMetrics(SM_CYCAPTION);
const LONG lDblPadding = 2 * m_lPadding;
const LONG cxBaseBorder = ::GetSystemMetrics(SM_CXDLGFRAME);
const LONG cyBaseBorder = ::GetSystemMetrics(SM_CYDLGFRAME);
// Calculate differences of window border sizes
const LONG dxBorder = ::GetSystemMetrics(SM_CXSIZEFRAME) - cxBaseBorder;
const LONG dyBorder = ::GetSystemMetrics(SM_CYSIZEFRAME) - cyBaseBorder;
if ( bShow )
{
// Make window frame thick (sizeable)
ModifyStyle(DS_MODALFRAME, WS_THICKFRAME, 0);
// Restore default system menu for sizable window
GetSystemMenu(TRUE);
// Client size
SIZE sizeClient = { m_sizeMinClient.cx, m_sizeMinClient.cy * 2 };
RECT rc; GetWindowRect(&rc);
SetWindowPos(NULL, rc.left - dxBorder, rc.top - dyBorder,
sizeClient.cx + 2 * (dxBorder + cxBaseBorder),
sizeClient.cy + 2 * (dyBorder + cyBaseBorder) + dyCaption,
SWP_NOZORDER|SWP_NOACTIVATE|SWP_DRAWFRAME|SWP_FRAMECHANGED);
// Change "Detail" button caption
if ( m_unDetailID )
{
HWND hwndDetailButton = GetDlgItem(m_unDetailID);
::SetWindowText(hwndDetailButton, m_csDetailOff.c_str());
}
// Set child controls positions
AlignChildControls(sizeClient.cx, sizeClient.cy);
}
else
{
// Make window frame dialog
ModifyStyle(WS_THICKFRAME, DS_MODALFRAME, 0);
// Modify system menu. Remove all items except "Move" and "Close".
HMENU hMenu = GetSystemMenu(FALSE);
for ( int nPos = 0; nPos < ::GetMenuItemCount(hMenu); )
{
const UINT unID = ::GetMenuItemID(hMenu, nPos);
if ( unID == SC_CLOSE || unID == SC_MOVE )
nPos++;
else
::DeleteMenu(hMenu, nPos, MF_BYPOSITION);
}
// Client size
SIZE sizeClient = { m_sizeMinClient.cx, m_sizeMinClient.cy };
RECT rc; GetWindowRect(&rc);
SetWindowPos(NULL, rc.left + dxBorder, rc.top + dyBorder,
sizeClient.cx + 2 * cxBaseBorder,
sizeClient.cy + 2 * cyBaseBorder + dyCaption,
SWP_NOZORDER|SWP_NOACTIVATE|SWP_DRAWFRAME|SWP_FRAMECHANGED);
// Change "Detail" button caption
if ( m_unDetailID )
{
HWND hwndDetailButton = GetDlgItem(m_unDetailID);
::SetWindowText(hwndDetailButton, m_csDetailOn.c_str());
}
// Set child controls positions
AlignChildControls(sizeClient.cx, sizeClient.cy);
}
}
void CDetailedMessageWindow::QueryHelp()
{
HWND hwndOwner = GetWindow(GW_OWNER);
if ( hwndOwner != GetDesktopWindow() )
{
HELPINFO hi;
hi.cbSize = sizeof(HELPINFO);
hi.iContextType = HELPINFO_WINDOW;
hi.iCtrlId = IDHELP;
hi.hItemHandle = m_hWnd;
hi.dwContextId = GetWindowContextHelpId();
POINT pt; ::GetCursorPos(&pt);
hi.MousePos.x = pt.x;
hi.MousePos.y = pt.y;
::SendMessage(hwndOwner, WM_HELP, 0, LPARAM(&hi));
}
}
void CDetailedMessageWindow::QueryHelp(const HELPINFO* pcHI)
{
HWND hwndOwner = GetWindow(GW_OWNER);
if ( hwndOwner != GetDesktopWindow() )
{
::SendMessage(hwndOwner, WM_HELP, 0, LPARAM(pcHI));
}
}
}; // SP namespace