Created
August 3, 2015 21:00
-
-
Save davidruhmann/d6e837e75311e8f9f9a4 to your computer and use it in GitHub Desktop.
Visual C++ MFC Message Dialog Class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "stdafx.h" | |
#include "MessageDlg.h" | |
#include <dcp_colorscheme.h> // GetSchemeBrush | |
#include <Nursing_MsgAPI_Macros.h> // MSGWRITE* | |
// Static Value Definitions | |
#define MIN_HEIGHT 50 | |
#define MIN_WIDTH 50 | |
#define MAX_HEIGHT 500 | |
#define MAX_WIDTH 500 | |
#define MAX_MESSAGE_LINES 5 | |
#define DEF_BUTTON_STYLE WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | |
#define DEF_WINDOW_STYLE DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | |
// Resource Handle | |
#ifdef SET_RESOURCE_HANDLE | |
#undef SET_RESOURCE_HANDLE | |
#endif | |
#define SET_RESOURCE_HANDLE AFX_MANAGE_STATE(AfxGetStaticModuleState()); AfxSetResourceHandle(AfxGetInstanceHandle()); | |
BOOL CCCButton::Create(_In_ LPCTSTR sText, _In_ DWORD nStyle, _In_ const RECT& rect, _In_ CWnd* pParent, _In_ UINT nID) | |
{ | |
SetText(sText); | |
return CCButton::Create(sText, nStyle, rect, pParent, nID); | |
} | |
int CCCButton::GetWindowText(_Out_writes_(nMaxCount) LPTSTR sBuffer, _In_ int nMaxCount) const | |
{ | |
_tcsncpy_s(sBuffer, nMaxCount, m_sText, _TRUNCATE); | |
return _tcsnlen(sBuffer, nMaxCount); | |
} | |
void CCCButton::GetWindowText(_Out_ CString& sString) const | |
{ | |
sString = m_sText; | |
} | |
void CCCButton::SetText(_In_ LPCTSTR sText) | |
{ | |
m_sText = sText; | |
} | |
void CCCButton::SetWindowText(_In_ LPCTSTR sText) { | |
SetText(sText); | |
CCButton::SetWindowText(sText); | |
} | |
IMPLEMENT_DYNAMIC(CMessageDlg, CCDialog) // RTTI support | |
// Message Map Declaration | |
BEGIN_MESSAGE_MAP(CMessageDlg, CCDialog) | |
ON_WM_CREATE() | |
ON_WM_DESTROY() | |
ON_WM_SIZE() | |
ON_BN_CLICKED(EControl::eButtonOK, OnOK) | |
ON_BN_CLICKED(EControl::eButtonCancel, OnCancel) | |
ON_BN_CLICKED(EControl::eButtonAbort, OnAbort) | |
ON_BN_CLICKED(EControl::eButtonRetry, OnRetry) | |
ON_BN_CLICKED(EControl::eButtonIgnore, OnIgnore) | |
ON_BN_CLICKED(EControl::eButtonYes, OnYes) | |
ON_BN_CLICKED(EControl::eButtonNo, OnNo) | |
ON_BN_CLICKED(EControl::eButtonClose, OnClose) | |
ON_BN_CLICKED(EControl::eButtonHelp, OnHelp) | |
ON_BN_CLICKED(EControl::eButtonTryAgain, OnTryAgain) | |
ON_BN_CLICKED(EControl::eButtonContinue, OnContinue) | |
ON_CONTROL_RANGE(BN_CLICKED, EControl::eButtonCustom, EControl::eButtonCustomEnd, OnCustom) | |
ON_CONTROL_RANGE(BN_CLICKED, EControl::eButtonOK, EControl::eButtonContinue, OnClickButton) | |
ON_CONTROL_RANGE(BN_CLICKED, EControl::eButtonCustom, EControl::eButtonCustomEnd, OnClickCustom) | |
ON_WM_PAINT() | |
ON_WM_CTLCOLOR() | |
ON_WM_GETMINMAXINFO() | |
END_MESSAGE_MAP() | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Default CMessageDlg Constructor | |
/// \details Create the Reason Dialog | |
/// \param[in] sTitle - title to display on the reason dialog (default = NULL) | |
/// \param[in] sMessage - message to display on the reason dialog (default = NULL) | |
/// \param[in] eButtons - Reason dialog button type (default = eOKCancel) | |
/// \param[in] eIcon - Reason dialog message icons (default = eNone) | |
/// \param[in] pParent - CWnd pointer to parent window (default = NULL) | |
/// \pre none | |
/// \post Reason Dialog object is created. | |
//////////////////////////////////////////////////////////////////////////////// | |
CMessageDlg::CMessageDlg(_In_opt_ LPCTSTR sTitle /*= NULL*/, _In_opt_ LPCTSTR sMessage /*= NULL*/, _In_ EButtons eButtons /*= eOKCancel*/, _In_ EIcon eIcon /*= eNone*/, _In_opt_ CWnd* pParent /*= NULL*/) | |
: CCDialog(), | |
m_pParent(pParent), | |
m_sTitle(sTitle), | |
m_sMessage(sMessage), | |
m_MinSize(0, 0), | |
m_MaxSize(0, 0), | |
m_eButtons(eButtons), | |
m_eIcon(eIcon), | |
m_eNextButtonID(eButtonCustom), | |
m_eNextControlID(eReservedID), // TODO Test with derived class | |
m_nWindowStyle(DEF_WINDOW_STYLE), | |
m_nButtonStyle(DEF_BUTTON_STYLE), | |
m_ButtonList(), | |
m_ButtonMap() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Default CMessageDlg Destructor | |
/// \details Deletes all allocated memory. | |
/// \pre none | |
/// \post Memory is freed | |
//////////////////////////////////////////////////////////////////////////////// | |
CMessageDlg::~CMessageDlg() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Add a button to the message dialog. | |
/// \return CButton* = pointer to the newly added button. | |
/// \param[in] sText | |
/// \param[in] eButtonID | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
CButton* CMessageDlg::AddButton(_In_opt_ LPCTSTR sText /*= NULL*/, _In_ EControl eButtonID /*= EControl::eButonCustom*/) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
SetLastError(ERROR_SUCCESS); | |
// Validate Button ID | |
if (eInvalidID == eButtonID) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Invalid Control ID encountered. Task aborted.")); | |
SetLastError(ERROR_INVALID_PARAMETER); | |
return NULL; | |
} | |
// Create New Button | |
CCCButton* pButton = new CCCButton(); | |
if (NULL == pButton) | |
{ | |
ASSERT(pButton); | |
MSGWRITEERRORF(__FUNCTION__, _T("Failed to create new button: %u. Task aborted."), eButtonID); | |
SetLastError(ERROR_FUNCTION_FAILED); | |
return NULL; | |
} | |
// Get next Control ID | |
if (eButtonCustom == eButtonID) | |
{ | |
eButtonID = GetNextButtonID(); | |
MSGWRITEDEBUGF(__FUNCTION__, _T("Custom control, using next available ID: %u"), eButtonID); | |
} | |
// Remove Existing Button | |
if (RemoveButton(eButtonID)) | |
{ | |
MSGWRITEDEBUGF(__FUNCTION__, _T("Existing button with same ID %u was removed."), eButtonID); | |
} | |
// Create Window | |
if (false == CreateButton(*pButton, CString(sText), eButtonID)) | |
{ | |
MSGWRITEERRORF(__FUNCTION__, _T("Failed to add button control: %u. Task aborted."), eButtonID); | |
SetLastError(ERROR_CREATE_FAILED); | |
delete pButton; | |
return NULL; | |
} | |
// Add Button | |
m_ButtonMap.SetAt(eButtonID, pButton); | |
m_ButtonList.AddTail(eButtonID); | |
MSGWRITEDEBUGF(__FUNCTION__, _T("Created button: %u"), eButtonID); | |
return pButton; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Calculate the window growth needed for the control's contents. | |
/// \remarks The growth is a positive or negative amount from the current size. | |
/// example: cx = +10 because my control's contents need 10 more units to fit. | |
/// \return void | |
/// \param[in,out] size - input current growth needed, update with max growth. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::CalculateContentsGrowth(_Inout_ CSize& size) | |
{ | |
// Calculate the Message text box width needed vs what it actually has (offset) | |
CRect rect(0, 0, 0, 0); | |
m_edtMessage.GetClientRect(rect); | |
LONG nOffset = (CalculateWidestLineSize(m_edtMessage.GetSafeHwnd()).cx - (rect.Width() - (2 * GetSystemMetrics(SM_CXEDGE)))); | |
size.cx = max(nOffset, size.cx); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Determine the height and width of a string of text. | |
/// \return CSize = size of the string. Upon failure size of 0 is returned. | |
/// For extended error details check GetLastError() | |
/// ERROR_SUCCESS means success | |
/// ERROR_NO_DATA means the string is empty. (0) | |
/// ERROR_INVALID_PARAMETER means the hWnd is NULL. (0) | |
/// ERROR_INVALID_HANDLE means no window handles could be retrieved. (0) | |
/// ERROR_INVALID_OPERATION means the device context failed. (0) | |
/// ERROR_FUNCTION_FAILED means the calculation failed on one of the lines. (?) | |
/// \param[in] hWnd - Handle of the window for calculation context. | |
/// \param[in] sText = NULL (window text) - String to measure. | |
/// \param[in] nWidth = 0 (actual text) - Width to measure against. If < 0, use | |
/// the window's client width. Value only used if > 0. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
CSize CMessageDlg::CalculateTextSize(_In_ HWND hWnd, _In_opt_ LPCTSTR sText /*= NULL*/, _In_opt_ int nWidth /*= 0*/) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
SetLastError(ERROR_SUCCESS); | |
// Verify Parameters | |
if (NULL == hWnd) | |
{ | |
ASSERT(hWnd); | |
MSGWRITEERROR(__FUNCTION__, _T("Invalid handle encountered. Task aborted.")); | |
SetLastError(ERROR_INVALID_PARAMETER); | |
return CSize(0, 0); | |
} | |
// Convert to CWnd | |
CWnd* pWnd = CWnd::FromHandle(hWnd); | |
if (NULL == pWnd) | |
{ | |
ASSERT(pWnd); | |
MSGWRITEERROR(__FUNCTION__, _T("Invalid handle encountered. Task aborted.")); | |
SetLastError(ERROR_INVALID_HANDLE); | |
return CSize(0, 0); | |
} | |
// Retrieve Text | |
CString sString(sText); | |
if (NULL == sText || sString.IsEmpty()) | |
{ | |
pWnd->GetWindowText(sString); | |
if (sString.IsEmpty()) | |
{ | |
MSGWRITEWARN(__FUNCTION__, _T("Nothing to calculate, string is empty. Task aborted.")); | |
SetLastError(ERROR_NO_DATA); | |
return CSize(0, 0); | |
} | |
} | |
// Retrieve Device Context | |
CDC* pDC = pWnd->GetDC(); | |
ASSERT(pDC); | |
if (NULL == pDC) | |
{ | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to get device context. Task aborted.")); | |
SetLastError(ERROR_INVALID_OPERATION); | |
return CSize(0, 0); | |
} | |
// Save Current Context | |
int nSavedDC = pDC->SaveDC(); | |
if (0 == nSavedDC) | |
{ | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to save device context.")); | |
} | |
// Switch to the control's Font | |
CFont* myFont = pWnd->GetFont(); | |
CFont* exFont = pDC->SelectObject(myFont); | |
// Determine the Width to use: Given if > 0, Client if < 0, Text if == 0. | |
CRect rect(0, 0, nWidth, 0); | |
if (nWidth < 0) | |
{ | |
CRect client; | |
pWnd->GetClientRect(client); | |
rect.right = client.right; | |
} | |
else if (0 == nWidth) | |
{ | |
rect.right = CalculateWidestLineSize(hWnd, sString).cx; | |
} | |
// Calculate the Height | |
pDC->DrawText(sString, rect, DT_CALCRECT | DT_WORDBREAK); | |
// Convert to Size | |
CSize size; | |
size.cx = rect.Width(); | |
size.cy = rect.Height(); | |
// Retrieve the Text Metrics | |
TEXTMETRIC tm; | |
if (0 == pDC->GetTextMetrics(&tm)) | |
{ | |
tm = {0}; | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to retrieve text metrics.")); | |
} | |
// Add the average width to prevent clipping | |
size.cx += tm.tmAveCharWidth; | |
// Add the external leading to prevent clipping | |
//size.cy += tm.tmExternalLeading; | |
// Convert Measurement | |
pDC->LPtoDP(&size); | |
// Restore the previous Font | |
pDC->SelectObject(exFont); | |
// Restore the saved device context | |
if (0 != nSavedDC) | |
{ | |
if (0 == pDC->RestoreDC(nSavedDC)) | |
{ | |
MSGWRITEERRORF(__FUNCTION__, _T("Failed to restore the saved device context: %d. May exhibit drawing issues."), nSavedDC); | |
} | |
} | |
// Release the device context | |
if (0 == pWnd->ReleaseDC(pDC)) | |
{ | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to release device context. Memory leaked.")); | |
} | |
return size; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Determine the length of the widest line of text. | |
/// \remarks The height returned is just the height of a single line. | |
/// \return CSize = size of the widest line. Upon failure size of 0 is returned. | |
/// For extended error details check GetLastError() | |
/// ERROR_SUCCESS means success | |
/// ERROR_NO_DATA means the string is empty. (0) | |
/// ERROR_INVALID_PARAMETER means the hWnd is NULL. (0) | |
/// ERROR_INVALID_HANDLE means no window handles could be retrieved. (0) | |
/// ERROR_INVALID_OPERATION means the device context failed. (0) | |
/// ERROR_FUNCTION_FAILED means the calculation failed on one of the lines. (?) | |
/// \param[in] hWnd - Handle of the window for calculation context. | |
/// \param[in] sText = NULL (window text) - String to measure. | |
/// \param[in] bSingle = false - Specify if the string is single line (strip CRLF). | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
CSize CMessageDlg::CalculateWidestLineSize(_In_ HWND hWnd, _In_opt_ LPCTSTR sText /*= NULL*/, _In_ bool bSingle /*= false*/) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
SetLastError(ERROR_SUCCESS); | |
// Verify Parameters | |
if (NULL == hWnd) | |
{ | |
ASSERT(hWnd); | |
MSGWRITEERROR(__FUNCTION__, _T("Invalid handle encountered. Task aborted.")); | |
SetLastError(ERROR_INVALID_PARAMETER); | |
return CSize(0, 0); | |
} | |
// Convert to CWnd | |
CWnd* pWnd = CWnd::FromHandle(hWnd); | |
if (NULL == pWnd) | |
{ | |
ASSERT(pWnd); | |
MSGWRITEERROR(__FUNCTION__, _T("Invalid handle encountered. Task aborted.")); | |
SetLastError(ERROR_INVALID_HANDLE); | |
return CSize(0, 0); | |
} | |
// Retrieve Text | |
CString sString(sText); | |
if (NULL == sText || sString.IsEmpty()) | |
{ | |
pWnd->GetWindowText(sString); | |
if (sString.IsEmpty()) | |
{ | |
MSGWRITEWARN(__FUNCTION__, _T("Nothing to calculate, string is empty. Task aborted.")); | |
SetLastError(ERROR_NO_DATA); | |
return CSize(0, 0); | |
} | |
} | |
// Retrieve Device Context | |
CDC* pDC = pWnd->GetDC(); | |
ASSERT(pDC); | |
if (NULL == pDC) | |
{ | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to get device context. Task aborted.")); | |
SetLastError(ERROR_INVALID_OPERATION); | |
return CSize(0, 0); | |
} | |
// Remove CR LF to get accurate width for non multi line controls | |
if (bSingle) | |
{ | |
// CR - Carriage Return (\r) | |
if (sString.Remove(_T('\r'))) | |
{ | |
MSGWRITEDEBUG(__FUNCTION__, _T("Removed CR from the string.")); | |
} | |
// LF - Line Feed (\n) | |
if (sString.Remove(_T('\n'))) | |
{ | |
MSGWRITEDEBUG(__FUNCTION__, _T("Removed LF from the string.")); | |
} | |
} | |
// Switch to the control's Font | |
CFont* myFont = pWnd->GetFont(); | |
CFont* exFont = pDC->SelectObject(myFont); | |
// Loop Setup | |
CSize size; | |
CSize widest; | |
CString sLine; | |
// Check the Width of each Line | |
for (int i = 0; AfxExtractSubString(sLine, sString, i); i++) | |
{ | |
// Calculate the Text Width | |
if (0 == GetTextExtentPoint32(pDC->GetSafeHdc(), sLine, sLine.GetLength(), &size)) | |
{ | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to calculate size.")); | |
SetLastError(ERROR_FUNCTION_FAILED); | |
size = CSize(0, 0); | |
} | |
// Remember the Widest | |
if (size.cx > widest.cx) | |
{ | |
widest = size; | |
} | |
} | |
// Retrieve the Text Metrics | |
TEXTMETRIC tm; | |
if (0 == pDC->GetTextMetrics(&tm)) | |
{ | |
tm = {0}; | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to retrieve text metrics.")); | |
} | |
// Add the average width to prevent clipping | |
widest.cx += tm.tmAveCharWidth; | |
// Verify the Height is large enough according to the metrics | |
widest.cy = widest.cy < (tm.tmHeight + tm.tmExternalLeading) ? (tm.tmHeight + tm.tmExternalLeading) : widest.cy; | |
// Convert Measurement | |
pDC->LPtoDP(&widest); | |
// Restore the previous Font | |
pDC->SelectObject(exFont); | |
if (0 == pWnd->ReleaseDC(pDC)) | |
{ | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to release device context. Memory leaked.")); | |
} | |
return widest; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Remove added buttons. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::ClearButtons() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
// Remove Buttons by List | |
POSITION pos = m_ButtonList.GetHeadPosition(); | |
while (pos) | |
{ | |
if (false == RemoveButton(m_ButtonList.GetNext(pos))) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to remove button. Possible memory leak.")); | |
} | |
} | |
// Remove Buttons by Map (Double Check) | |
CButton* pValue = NULL; | |
EControl eKey = EControl::eInvalidID; | |
pos = m_ButtonMap.GetStartPosition(); | |
while (pos) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Button list and map are misaligned. Could cause memory leaks!")); | |
m_ButtonMap.GetNextAssoc(pos, eKey, pValue); | |
if (false == RemoveButton(eKey)) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to remove button. Possible memory leak.")); | |
} | |
} | |
} | |
bool CMessageDlg::CreateButton(_In_ CCCButton& button, _In_ const CString& sText, _In_ EControl eButtonID) | |
{ | |
SetLastError(ERROR_SUCCESS); | |
// Validate Button ID | |
if (eInvalidID == eButtonID) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Invalid Control ID encountered. Task aborted.")); | |
SetLastError(ERROR_INVALID_PARAMETER); | |
return false; | |
} | |
// Validate Parent | |
if (FALSE == IsWindow(GetSafeHwnd())) | |
{ | |
// TODO ENSURE vs ASSERT | |
button.SetWindowText(sText); | |
MSGWRITEDEBUG(__FUNCTION__, _T("Dialog not yet initialized. Button not created.")); | |
SetLastError(ERROR_NOT_READY); | |
return true; | |
} | |
// Validate Text | |
if (sText.IsEmpty()) | |
{ | |
MSGWRITEWARNF(__FUNCTION__, _T("Empty button text encountered for ID: %u"), eButtonID); | |
} | |
// Create Window | |
CRect rect(0, 0, 0, 0); | |
if (FALSE == button.Create(sText, m_nButtonStyle, rect, this, eButtonID)) | |
{ | |
MSGWRITEERRORF(__FUNCTION__, _T("Failed to create button control: %u. Task aborted."), eButtonID); | |
SetLastError(ERROR_CREATE_FAILED); | |
return false; | |
} | |
return true; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Create the added buttons. | |
/// \return bool | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
bool CMessageDlg::CreateButtons() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
// Create Buttons in the Map | |
CButton* pValue = NULL; | |
CCCButton* pButton = NULL; | |
EControl eKey = EControl::eInvalidID; | |
POSITION pos = m_ButtonMap.GetStartPosition(); | |
while (pos) | |
{ | |
m_ButtonMap.GetNextAssoc(pos, eKey, pValue); | |
pButton = dynamic_cast<CCCButton*>(pValue); | |
if (NULL == pValue) | |
{ | |
ASSERT(NULL); | |
MSGWRITEERROR(__FUNCTION__, _T("Invalid button encountered. Attempting to continue.")); | |
continue; | |
} | |
if (IsWindow(pValue->GetSafeHwnd())) | |
{ | |
MSGWRITEDEBUG(__FUNCTION__, _T("Skipping button since it has already been created.")); | |
continue; | |
} | |
CString sText; | |
pButton->GetWindowText(sText); | |
CreateButton(*pButton, sText, eKey); | |
} | |
return true; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Called by the framework to exchange and validate dialog data. | |
/// \remarks Never call this function directly. It is called by the UpdateData | |
/// member function. Call UpdateData to initialize a dialog box's controls or | |
/// retrieve data from a dialog box. | |
/// \return void | |
/// \param[in] pDX - A pointer to a CDataExchange object. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::DoDataExchange(CDataExchange* pDX) | |
{ | |
// Inherited Exchange | |
CCDialog::DoDataExchange(pDX); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \details This member function handles all interaction with the user while the | |
/// dialog box is active. This is what makes the dialog box modal; that is, the | |
/// user cannot interact with other windows until the dialog box is closed. | |
/// \remarks https://msdn.microsoft.com/en-us/library/619z63f5.aspx | |
/// \return int = An int value that specifies the value of the nResult parameter | |
/// that was passed to the CDialog::EndDialog member function, which is used to | |
/// close the dialog box. The return value is -1 if the function could not create | |
/// the dialog box, or IDABORT if some other error occurred, in which case the | |
/// Output window will contain error information from GetLastError. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
INT_PTR CMessageDlg::DoModal() | |
{ | |
// Set Limits | |
SetSizeLimits(MIN_WIDTH, MIN_HEIGHT, MAX_WIDTH, MAX_HEIGHT, false); | |
// Create Window | |
DWORD dwExStyle = WS_EX_CONTROLPARENT; | |
DWORD dwStyle = DS_MODALFRAME | m_nWindowStyle; | |
if (FALSE == CreateModal(dwExStyle, m_sTitle, dwStyle, 0, 0, GetSystemMetrics(SM_CXMIN), GetSystemMetrics(SM_CYMIN), m_pParent)) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to create window. Task aborted.")); | |
return EResult::eFailed; | |
} | |
// Display Dialog | |
return CCDialog::DoModal(); | |
} | |
// TODO Modeless | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Set the control to focus upon. | |
/// \remarks Make sure to call this in OnInitDialog and return this result. | |
/// \return bool = true on success, false on failure. | |
/// \param[in] hWnd - handle to the control to put focus. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
bool CMessageDlg::FocusOn(_In_ HWND hWnd) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
CWnd* pWnd = CWnd::FromHandle(hWnd); | |
if (NULL == pWnd) | |
{ | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to retrieve window handle. Task aborted")); | |
return false; | |
} | |
GotoDlgCtrl(pWnd); | |
pWnd->SetFocus(); | |
return true; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Retrieve the next available button ID. | |
/// \return EControl = button ID upon success, eInvalidID on failure. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
CMessageDlg::EControl CMessageDlg::GetNextButtonID() | |
{ | |
m_eNextButtonID = m_eNextButtonID >= eButtonCustomEnd ? eInvalidID : static_cast<EControl>(m_eNextButtonID + 1); | |
MSGWRITEDEBUGF(__FUNCTION__, _T("Next Button ID: %u"), m_eNextButtonID); | |
return m_eNextButtonID; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Retrieve the next available control ID. | |
/// \return EControl = control ID upon success, eInvalidID on failure. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
CMessageDlg::EControl CMessageDlg::GetNextControlID() | |
{ | |
m_eNextControlID = m_eNextControlID >= MAXUINT ? eInvalidID : static_cast<EControl>(m_eNextControlID + 1); | |
MSGWRITEDEBUGF(__FUNCTION__, _T("Next Control ID: %u"), m_eNextControlID); | |
return m_eNextControlID; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Retrieves the text metrics for a given window. | |
/// \return bool = true on success, false on failure. | |
/// For extended error details check GetLastError() | |
/// ERROR_SUCCESS means success (true) | |
/// ERROR_INVALID_PARAMETER means the hWnd is NULL. (false) | |
/// ERROR_INVALID_HANDLE means no window handles could be retrieved. (false) | |
/// ERROR_INVALID_OPERATION means the device context failed. (false) | |
/// ERROR_FUNCTION_FAILED means that the TEXTMETRIC was not retrieved. (false) | |
/// ERROR_OBJECT_NOT_FOUND means the device context was leaked. (true) | |
/// \param[in] hWnd - Handle of the window for context. If NULL, use window. | |
/// \param[in] tm - TEXTMETRIC to store the results. Values set to 0 upon failure. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
bool CMessageDlg::GetTextMetrics(_In_ HWND hWnd, _Out_ TEXTMETRIC& tm) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
SetLastError(ERROR_SUCCESS); | |
// Initialize | |
tm = {0}; | |
// Verify Parameters | |
if (NULL == hWnd) | |
{ | |
ASSERT(hWnd); | |
MSGWRITEERROR(__FUNCTION__, _T("Invalid handle encountered. Task aborted.")); | |
SetLastError(ERROR_INVALID_PARAMETER); | |
return false; | |
} | |
// Convert to CWnd | |
CWnd* pWnd = CWnd::FromHandle(hWnd); | |
if (NULL == pWnd) | |
{ | |
ASSERT(pWnd); | |
MSGWRITEERROR(__FUNCTION__, _T("Invalid handle encountered. Task aborted.")); | |
SetLastError(ERROR_INVALID_HANDLE); | |
return false; | |
} | |
// Retrieve Device Context | |
CDC* pDC = pWnd->GetDC(); | |
ASSERT(pDC); | |
if (NULL == pDC) | |
{ | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to get device context. Task aborted.")); | |
SetLastError(ERROR_INVALID_OPERATION); | |
return false; | |
} | |
// Switch to the control's Font | |
CFont* myFont = pWnd->GetFont(); | |
CFont* exFont = pDC->SelectObject(myFont); | |
// Retrieve the Text Metrics | |
bool bResult(true); | |
if (0 == pDC->GetTextMetrics(&tm)) | |
{ | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to retrieve text metrics.")); | |
SetLastError(ERROR_FUNCTION_FAILED); | |
bResult = false; | |
} | |
// Restore the previous Font | |
pDC->SelectObject(exFont); | |
// Release the device context | |
if (0 == pWnd->ReleaseDC(pDC)) | |
{ | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to release device context. Memory leaked.")); | |
SetLastError(ERROR_OBJECT_NOT_FOUND); | |
} | |
return bResult; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Load the Message Icon based upon the type given. | |
/// \remarks Attempts to load system icon upon failure of custom icon. | |
/// \return HICON = handle to the icon if successfully loaded, else NULL. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
HICON CMessageDlg::LoadMessageIcon() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
// Load Resource Location | |
SET_RESOURCE_HANDLE; | |
// Identify Icon | |
UINT nID = 0U; | |
LPTSTR sID = NULL; | |
switch (m_eIcon) | |
{ | |
case eWarning: | |
nID = IDI_WARNING_4017; | |
sID = IDI_WARNING; | |
break; | |
case eInfo: | |
nID = IDI_INFORMATION_5104; | |
sID = IDI_INFORMATION; | |
break; | |
case eError: | |
nID = IDI_ERROR_6275; | |
sID = IDI_ERROR; | |
break; | |
case eAlert: | |
nID = IDI_ALERT_6047; | |
break; | |
case eQuestion: | |
sID = IDI_QUESTION; | |
break; | |
case eNone: | |
default: | |
break; | |
} | |
// Load Icon | |
HICON hIcon = NULL; | |
if (nID > 0U) | |
{ | |
hIcon = static_cast<HICON>(LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(nID), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED)); | |
} | |
if (NULL == hIcon) | |
{ | |
MSGWRITEWARN(__FUNCTION__, _T("Failed to load resource icon. Attempting to continue.")); | |
// Load System Icon | |
if (NULL != sID) | |
{ | |
hIcon = static_cast<HICON>(LoadImage(NULL, sID, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED)); | |
} | |
if (NULL == hIcon) | |
{ | |
MSGWRITEWARN(__FUNCTION__, _T("Failed to load system icon. Attempting to continue.")); | |
} | |
} | |
return hIcon; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Abort button on click event handler ON_BN_CLICKED | |
/// \remarks Ends the dialog with EResult::eAbort result. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnAbort() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(EResult::eAbort); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Cancel button on click event handler ON_BN_CLICKED, Alt + F4, Escape. | |
/// \remarks Ends the dialog with EResult::eCancel result. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnCancel() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(EResult::eCancel); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Button on click event handler. BN_CLICKED for control range. | |
/// \remarks Ends the dialog with nID result. | |
/// \return void | |
/// \param[in] nID - Control ID for the button clicked. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnClickButton(_In_ UINT nID) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(nID); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Custom button on click event handler. BN_CLICKED for control range. | |
/// \remarks Ends the dialog with nID result. | |
/// \return void | |
/// \param[in] nID - Control ID for the button clicked. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnClickCustom(_In_ UINT nID) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(nID); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Close button on click event handler. ON_BN_CLICKED | |
/// \remarks Ends the dialog with EResult::eClose result. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnClose() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(EResult::eClose); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Continue button on click event handler. ON_BN_CLICKED | |
/// \remarks Ends the dialog with EResult::eContinue result. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnContinue() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(EResult::eContinue); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief The framework calls this member function when a child control is about to be drawn. | |
/// \details Powerchart UI Refresh Color scheme API's are defined in <dcp_colorscheme.h>. | |
/// To paint the control background as White, call the GetSchemeBrush(gBodyBackgroundBrush) API | |
/// which returns the Brush to be used to paint the control background with the predetermined color. | |
/// \remarks ON_WM_CTLCOLOR | |
/// \return HBRUSH = a handle to the brush that is to be used for painting the control background. | |
/// \param[in] pDC - Contains a pointer to the display context for the child window. May be temporary. | |
/// \param[in] pWnd - Contains a pointer to the control asking for the color. May be temporary. | |
/// \param[in] nCtlColor - Contains a value which specifies the type of control. | |
/// \pre none | |
/// \post Color the static controls | |
//////////////////////////////////////////////////////////////////////////////// | |
HBRUSH CMessageDlg::OnCtlColor(_In_ CDC* pDC, _In_ CWnd* pWnd, _In_ UINT nCtlColor) | |
{ | |
// Inherited Control Coloring | |
HBRUSH hbr = CCDialog::OnCtlColor(pDC, pWnd, nCtlColor); | |
// Control Coloring Overrides | |
switch (nCtlColor) | |
{ | |
// Static Controls | |
case CTLCOLOR_STATIC: | |
if (NULL != pDC) | |
{ | |
pDC->SetBkMode(TRANSPARENT); | |
} | |
else | |
{ | |
ASSERT(pDC); | |
MSGWRITEERRORF(__FUNCTION__, _T("Invalid device context encountered for static control.")); | |
} | |
hbr = ::GetSchemeBrush(gBodyBackgroundBrush); | |
break; | |
default: | |
break; | |
} | |
return hbr; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Dialog on creation event handler. | |
/// \details The framework calls this member function when an application | |
/// requests that the window be created by calling the Create member function. | |
/// \remarks https://msdn.microsoft.com/en-us/library/384x0633.aspx | |
/// \return int = OnCreate must return 0 to continue the creation of the CWnd object. | |
/// If the application returns -1, the window will be destroyed. | |
/// \param[in] lpCreateStruct - Points to a CREATESTRUCT structure that contains | |
/// information about the CWnd object being created. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
int CMessageDlg::OnCreate(_In_ LPCREATESTRUCT lpCreateStruct) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
// Inherited Creation | |
if (CCDialog::OnCreate(lpCreateStruct) == -1) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to create window from parent. Task aborted.")); | |
EndDialog(EResult::eFailed); | |
return -1; | |
} | |
// Load Resource Location | |
SET_RESOURCE_HANDLE; | |
// Invisible Rectangle | |
CRect rect(0, 0, 0, 0); | |
// Icon image | |
if (FALSE == m_imgIcon.Create(NULL, WS_CHILD | WS_VISIBLE | SS_ICON, rect, this, EControl::eIcon)) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to create icon control. Task aborted.")); | |
EndDialog(EResult::eFailed); | |
return -1; | |
} | |
// Load Icon | |
HICON hIcon = LoadMessageIcon(); | |
if (NULL != hIcon) | |
{ | |
SetMessageIcon(hIcon); | |
} | |
// Message text box | |
if (FALSE == m_edtMessage.Create(WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | ES_WANTRETURN, rect, this, EControl::eMessage)) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to create message control. Task aborted.")); | |
EndDialog(EResult::eFailed); | |
return -1; | |
} | |
m_edtMessage.SetWindowText(m_sMessage); | |
m_edtMessage.HideCaret(); | |
// Remove border from message text box | |
if (0 == m_edtMessage.ModifyStyleEx(WS_EX_CLIENTEDGE, 0UL)) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to modify message control extended style.")); | |
} | |
// Create buttons added before creation | |
CreateButtons(); | |
// Load First button text | |
UINT nStringID = IDS_OK; | |
EControl nControlID = EControl::eInvalidID; | |
switch (m_eButtons) | |
{ | |
case EButtons::eYesNo: | |
case EButtons::eYesNoCancel: | |
nStringID = IDS_YES; | |
nControlID = EControl::eButtonYes; | |
break; | |
case EButtons::eRetryCancel: | |
nStringID = IDS_RETRY; | |
nControlID = EControl::eButtonRetry; | |
break; | |
case EButtons::eCancelTryContinue: | |
nStringID = IDS_CANCEL; | |
nControlID = EControl::eButtonCancel; | |
break; | |
case EButtons::eAbortRetryIgnore: | |
nStringID = IDS_ABORT; | |
nControlID = EControl::eButtonAbort; | |
break; | |
case EButtons::eOKCancel: | |
case EButtons::eOKOnly: | |
default: | |
nStringID = IDS_OK; | |
nControlID = EControl::eButtonOK; | |
break; | |
} | |
CString sText; | |
if (FALSE == sText.LoadString(nStringID)) | |
{ | |
sText.Empty(); | |
MSGWRITEERRORF(__FUNCTION__, _T("Failed to load first button resource string: %u. Attempting to continue."), nStringID); | |
} | |
// First button | |
if (eInvalidID != nControlID | |
&& NULL == AddButton(sText, nControlID)) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to create first button control. Task aborted.")); | |
EndDialog(EResult::eFailed); | |
return -1; | |
} | |
// Load Second button text | |
switch (m_eButtons) | |
{ | |
case EButtons::eYesNo: | |
case EButtons::eYesNoCancel: | |
nStringID = IDS_NO; | |
nControlID = EControl::eButtonNo; | |
break; | |
case EButtons::eOKCancel: | |
case EButtons::eRetryCancel: | |
nStringID = IDS_CANCEL; | |
nControlID = EControl::eButtonCancel; | |
break; | |
case EButtons::eCancelTryContinue: | |
nStringID = IDS_TRYAGAIN; | |
nControlID = EControl::eButtonTryAgain; | |
break; | |
case EButtons::eAbortRetryIgnore: | |
nStringID = IDS_RETRY; | |
nControlID = EControl::eButtonRetry; | |
break; | |
case EButtons::eOKOnly: | |
default: | |
nStringID = 0U; | |
nControlID = EControl::eInvalidID; | |
break; | |
} | |
if (FALSE == sText.LoadString(nStringID)) | |
{ | |
sText.Empty(); | |
MSGWRITEERRORF(__FUNCTION__, _T("Failed to load second button resource string: %u. Attempting to continue."), nStringID); | |
} | |
// Second button | |
if (eInvalidID != nControlID | |
&& NULL == AddButton(sText, nControlID)) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to create second button control. Task aborted.")); | |
EndDialog(EResult::eFailed); | |
return -1; | |
} | |
// Load Third button text | |
switch (m_eButtons) | |
{ | |
case EButtons::eYesNoCancel: | |
nStringID = IDS_CANCEL; | |
nControlID = EControl::eButtonCancel; | |
break; | |
case EButtons::eCancelTryContinue: | |
nStringID = IDS_CONTINUE; | |
nControlID = EControl::eButtonContinue; | |
break; | |
case EButtons::eAbortRetryIgnore: | |
nStringID = IDS_IGNORE; | |
nControlID = EControl::eButtonIgnore; | |
break; | |
case EButtons::eOKCancel: | |
case EButtons::eRetryCancel: | |
case EButtons::eYesNo: | |
case EButtons::eOKOnly: | |
default: | |
nStringID = 0U; | |
nControlID = EControl::eInvalidID; | |
break; | |
} | |
if (FALSE == sText.LoadString(nStringID)) | |
{ | |
sText.Empty(); | |
MSGWRITEERRORF(__FUNCTION__, _T("Failed to load third button resource string: %u. Attempting to continue."), nStringID); | |
} | |
// Third button | |
if (eInvalidID != nControlID | |
&& NULL == AddButton(sText, nControlID)) | |
{ | |
ASSERT(false); | |
MSGWRITEERROR(__FUNCTION__, _T("Failed to create third button control. Task aborted.")); | |
EndDialog(EResult::eFailed); | |
return -1; | |
} | |
return 0; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Custom button on click event handler. BN_CLICKED for control range. | |
/// \remarks Ends the dialog with nID result. | |
/// \return void | |
/// \param[in] nID - Control ID for the button clicked. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnCustom(_In_ UINT nID) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(nID); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Dialog on destroy event handler. | |
/// \details The framework calls this member function to inform the CWnd object | |
/// that it is being destroyed. | |
/// \remarks https://msdn.microsoft.com/en-us/library/2eahe3wf.aspx | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnDestroy() | |
{ | |
// Clean up dynamically created controls | |
m_imgIcon.DestroyWindow(); | |
m_edtMessage.DestroyWindow(); | |
ClearButtons(); | |
CCDialog::OnDestroy(); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Handler for the WM_GETMINMAXINFO message. | |
/// \details Sent to a window when the size or position of the window is about to | |
/// change. An application can use this message to override the window's default | |
/// maximized size and position, or its default minimum or maximum tracking size. | |
/// \remarks https://msdn.microsoft.com/en-us/library/windows/desktop/ms632626.aspx | |
/// \remarks Override the Minimum and Maximum tracking size and the Maximum size | |
/// when the window does not have a maximize button. | |
/// \param[in,out] lpMMI - A pointer to a MINMAXINFO structure that contains the | |
/// default maximized position and dimensions, and the default minimum and maximum | |
/// tracking sizes. An application can override the defaults by setting the members | |
/// of this structure. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnGetMinMaxInfo(_Inout_ MINMAXINFO* lpMMI) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
// Inherited Limits | |
CCDialog::OnGetMinMaxInfo(lpMMI); | |
// Validate Parameters | |
if (NULL == lpMMI) | |
{ | |
ASSERT(lpMMI); | |
MSGWRITEERROR(__FUNCTION__, _T("Invalid MINMAXINFO reference encountered.")); | |
return; | |
} | |
// Override Min and Max Tracking (Drag) Sizes | |
lpMMI->ptMinTrackSize = m_MinSize; | |
lpMMI->ptMaxTrackSize = m_MaxSize; | |
// Override Max Size when no Maximize button shown | |
if (false == (GetStyle() & WS_MAXIMIZEBOX)) | |
{ | |
lpMMI->ptMaxSize = m_MaxSize; | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Help button on click event handler. ON_BN_CLICKED | |
/// \remarks Ends the dialog with EResult::eHelp result. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnHelp() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(EResult::eHelp); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Ignore button on click event handler ON_BN_CLICKED | |
/// \remarks Ends the dialog with EResult::eIgnore result. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnIgnore() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(EResult::eIgnore); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief This method is called in response to the WM_INITDIALOG message. | |
/// \remarks https://msdn.microsoft.com/en-us/library/fwz35s59.aspx | |
/// \return BOOL = Specifies whether the application has set the input focus to | |
/// one of the controls in the dialog box. If OnInitDialog returns nonzero, | |
/// Windows sets the input focus to the default location, the first control in | |
/// the dialog box. The application can return 0 only if it has explicitly set | |
/// the input focus to one of the controls in the dialog box. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
BOOL CMessageDlg::OnInitDialog() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
// Inherited Initialization | |
CCDialog::OnInitDialog(); | |
// Make first button the default | |
SetDefID(m_ButtonList.GetHead()); // ASSERTs if list is empty. | |
// Clear Previous Data | |
ResetDialog(); | |
// Size Window Appropriately | |
SizeWindowToContents(); | |
// Beep | |
UINT nBeep = m_eIcon; | |
if (nBeep & eAlert) | |
{ | |
nBeep = MB_ICONWARNING; | |
} | |
if (0 == MessageBeep(nBeep)) | |
{ | |
Beep(700, 600); | |
} | |
//BOOL bFocus = FocusOn(.GetSafeHwnd()) ? FALSE : TRUE; // TODO update control | |
return TRUE; // return TRUE unless you set the focus to a control | |
// EXCEPTION: OCX Property Pages should return FALSE | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief No button on click event handler. ON_BN_CLICKED. | |
/// \remarks Ends the dialog with EResult::eNo result. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnNo() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(EResult::eNo); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief OK button on click event handler. ON_BN_CLICKED or Enter if default. | |
/// \remarks Ends the dialog with EResult::eOK result. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnOK() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(EResult::eOK); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Dialog on paint event handler. ON_WM_PAINT | |
/// \details The framework calls this member function when Windows or an | |
/// application makes a request to repaint a portion of an application's window. | |
/// \remarks Powerchart UI Refresh Color scheme API's are defined in <dcp_colorscheme.h>. | |
/// To paint the Window background as White, call the GetSchemeBrush(gBodyBackgroundBrush) API | |
/// which returns the Brush to be used to fill the window rectangle with the predetermined color. | |
/// \remarks The coloring is only done when the window is not iconic "minimized". | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnPaint() | |
{ | |
// Minimized Window = Iconic | |
if (FALSE == IsIconic()) | |
{ | |
// Color the Window | |
CPaintDC dc(this); | |
::FillRect(dc.GetSafeHdc(), &dc.m_ps.rcPaint, ::GetSchemeBrush(gBodyBackgroundBrush)); | |
// Inherited Painting | |
CCDialog::OnPaint(); | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Retry button on click event handler ON_BN_CLICKED | |
/// \remarks Ends the dialog with EResult::eRetry result. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnRetry() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(EResult::eRetry); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Dialog on size and move event handler. ON_WM_SIZE | |
/// \details The framework calls this member function after the window's size has changed. | |
/// \remarks https://msdn.microsoft.com/en-US/library/h464d4f3.aspx | |
/// \return void | |
/// \param[in] nType - Specifies the type of resizing requested. | |
/// \param[in] cx - Specifies the new width of the client area. | |
/// \param[in] cy - Specifies the new height of the client area. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnSize(_In_ UINT nType, _In_ int cx, _In_ int cy) | |
{ | |
// Inherited Sizing | |
CCDialog::OnSize(nType, cx, cy); | |
// TODO Honor RTL | |
// Dimensions: | |
// | |
// 0 y Position | |
// x +--------------------------------------------+ -- | |
// | | | t | |
// | 0 h Sizing | | | |
// | w +--------------+ +--------------+ | -- | |
// | | | | | | | | |
// | | Control | | Control | | | | |
// | | | | | | | | |
// | | | | | | | | |
// | +--------------+ +--------------+ | | -- | |
// | | | | SECTION_YSPACE | |
// | | | | | |
// | +--------------+ | | -- | |
// | | | | | | |
// | | Control | | | | |
// | | | | | | |
// | | | | | | |
// | +--------------+ | | -- | |
// | | | | CONTROL_YSPACE | |
// | +--------------+ | | -- | |
// | | | | | | |
// | | Control | | | | |
// | | | | | | |
// | | | | | | |
// | +--------------+ | -- | |
// | | | b | |
// +--------------------------------------------+ cx -- | |
// cy | |
// | |
// |------|---------------------------------|---| | |
// l r | |
// | |
// |---| | |
// CONTROL_XSPACE | |
//// Initialize Dimensions | |
int l = LEFT_SPACE * 2; // Left Padding | |
int r = RIGHT_SPACE * 2; // Right Padding | |
int t = TOP_SPACE; // Top Padding | |
int b = BOTTOM_SPACE; // Bottom Padding | |
// Position | |
int x = 0; | |
int y = 0; | |
// Size | |
int w = 0; | |
int h = 0; | |
// Minimum | |
int mx = 0; | |
int my = 0; | |
// Temp | |
int temp = 0; | |
// TODO Adjust initial x,y based upon scroll bar positions | |
// TODO Establish Minimums for everything | |
////////////////////////////// | |
// Icon and Message Section // | |
////////////////////////////// | |
// Icon image | |
x = l; | |
y = t; | |
CRect rect(0, 0, 0, 0); | |
m_imgIcon.GetWindowRect(rect); | |
w = rect.Width(); | |
h = rect.Height(); | |
m_imgIcon.MoveWindow(x, y, w, h); | |
// Horizontal Control Spacer | |
if (w > 0) | |
{ | |
x += CONTROL_XSPACE; | |
} | |
// Message text box | |
x += w; | |
w = cx - r - x; // Use available width | |
m_edtMessage.GetWindowRect(rect); | |
m_edtMessage.MoveWindow(x, y, w, rect.Height()); // Update control width for accurate line count | |
temp = h; // Remember icon height | |
if (m_edtMessage.GetLineCount() > MAX_MESSAGE_LINES) | |
{ | |
h = CalculateWidestLineSize(m_edtMessage.GetSafeHwnd()).cy * MAX_MESSAGE_LINES; | |
m_edtMessage.ModifyStyle(0UL, WS_VSCROLL); | |
} | |
else | |
{ | |
h = CalculateTextSize(m_edtMessage.GetSafeHwnd(), NULL, w).cy; | |
m_edtMessage.ModifyStyle(WS_VSCROLL, 0UL); | |
} | |
h += h > 0 ? (2 * GetSystemMetrics(SM_CYBORDER)) : 0; | |
m_edtMessage.MoveWindow(x, y, w, h); | |
// Vertical Section Spacer only if a message exists | |
if (h > 0) | |
{ | |
y += SECTION_YSPACE; | |
// Actual Section Height | |
h = max(h, temp); | |
} | |
// Minimum Height for Message Section | |
my = y + h; | |
///////////////////// | |
// Buttons Section // | |
///////////////////// | |
// Size Buttons (Right to Left) | |
x = cx; | |
temp = 0; | |
CSize size(0, 0); | |
bool bFirst(true); | |
CButton* pButton = NULL; | |
POSITION pos = m_ButtonList.GetTailPosition(); | |
while (pos) | |
{ | |
// Retrieve Button pointer | |
if (FALSE == m_ButtonMap.Lookup(m_ButtonList.GetPrev(pos), pButton) || NULL == pButton) | |
{ | |
ASSERT(pButton); | |
MSGWRITEERROR(__FUNCTION__, _T("Button list and map are misaligned. Could cause memory leaks!")); | |
continue; | |
} | |
// Size Button | |
size = CalculateWidestLineSize(pButton->GetSafeHwnd(), NULL, true); | |
w = size.cx + (2 * GetSystemMetrics(SM_CXEDGE)); | |
w = max(BUTTON_WIDTH, w); | |
h = max(BUTTON_HEIGHT, size.cy); | |
x -= ((bFirst ? RIGHT_SPACE : CONTROL_XSPACE) + w); | |
y = cy - b - h; | |
pButton->MoveWindow(x, y, w, h); | |
temp = max(cy - y, temp); | |
bFirst = false; | |
} | |
// Vertical Section Spacer only if a button exists | |
if (false == bFirst) | |
{ | |
temp += SECTION_YSPACE; | |
} | |
// Add Minimums for Buttons | |
mx = cx - (x - LEFT_SPACE); | |
////////////////////// | |
// Children Section // | |
////////////////////// | |
// Calculate Child Safe Rectangle | |
CRect child(l, my, cx - r, cy - temp); | |
child.right = max(child.left, child.right); | |
child.bottom = max(child.top, child.bottom); | |
my += temp; // Add Button Section to Minimum Height | |
// Size Children | |
CSize sz = SizeChild(child); | |
temp = cx - abs(sz.cx - (l + r)); // Calculate Minimum Child Width | |
mx = max(mx, temp); // Update Minimum Width if greater | |
my += sz.cy; // Add Used Child Section to Minimum Height | |
//////////////////////// | |
// Minimum Adjustment // | |
//////////////////////// | |
// Client to Window | |
GetWindowRect(rect); | |
mx += rect.Width() - cx; | |
my += rect.Height() - cy; | |
// Save the adjusted minimums | |
SetSizeLimits(mx, my); | |
// Update Window with Changes | |
RedrawWindow(); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Try Again button on click event handler. ON_BN_CLICKED | |
/// \remarks Ends the dialog with EResult::eTryAgain result. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnTryAgain() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(EResult::eTryAgain); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Yes button on click event handler. ON_BN_CLICKED or if default Enter. | |
/// \remarks Ends the dialog with EResult::eYes result. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::OnYes() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
EndDialog(EResult::eYes); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Display a dialog to obtain. | |
/// \return int = EResult of the dialog. | |
/// \param[in] dialog - Dialog to display. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
int CMessageDlg::Prompt(_In_ CMessageDlg& dialog) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
// Prompt with the Dialog | |
int nResult = dialog.DoModal(); | |
MSGWRITEINFOF(__FUNCTION__, _T("Result = %d"), nResult); | |
return nResult; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Remove a button by the control ID. | |
/// \return bool = true on success, false on failure. | |
/// \param[in] eControlID - button control ID to remove. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
bool CMessageDlg::RemoveButton(_In_ EControl eControlID) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
SetLastError(ERROR_SUCCESS); | |
CButton* pButton = NULL; | |
if (FALSE == m_ButtonMap.Lookup(eControlID, pButton) || NULL == pButton) | |
{ | |
MSGWRITEDEBUGF(__FUNCTION__, _T("Button does not exist with ID: %u"), eControlID); | |
SetLastError(ERROR_NOT_FOUND); | |
return false; | |
} | |
// Remove References | |
POSITION pos = m_ButtonList.Find(eControlID); | |
if (NULL == pos) | |
{ | |
MSGWRITEERRORF(__FUNCTION__, _T("Button was not found in the list: %u"), eControlID); | |
SetLastError(ERROR_NOT_FOUND); | |
} | |
m_ButtonList.RemoveAt(pos); // ASSERTs if position is invalid hence why it is not in an else. | |
m_ButtonMap.SetAt(eControlID, NULL); | |
if (FALSE == m_ButtonMap.RemoveKey(eControlID)) | |
{ | |
MSGWRITEERRORF(__FUNCTION__, _T("Failed to remove button: %u. Attempting to continue."), eControlID); | |
} | |
// Destroy Window | |
if (FALSE == pButton->DestroyWindow()) | |
{ | |
MSGWRITEERRORF(__FUNCTION__, _T("Failed to destroy button: %u. Attempting to continue."), eControlID); | |
} | |
// Delete Button | |
MSGWRITEDEBUGF(__FUNCTION__, _T("Button deleted: %u"), eControlID); | |
delete(pButton); | |
return true; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Reset the dialog components. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::ResetDialog() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
//FocusOn(m_cboReason.GetSafeHwnd()); // TODO Update control | |
} | |
void CMessageDlg::SetButtonStyle(_In_ DWORD nStyle) | |
{ | |
if (MAXDWORD == nStyle) | |
{ | |
m_nButtonStyle = DEF_BUTTON_STYLE; | |
} | |
else | |
{ | |
m_nButtonStyle = nStyle; | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Set the message icon to use. | |
/// \remarks Do not destroy the icon given until done with the dialog else the | |
/// icon will disappear from the dialog. | |
/// \return void | |
/// \param[in] hIcon - Handle to the icon to show. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::SetMessageIcon(_In_opt_ HICON hIcon) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
if (NULL == hIcon) | |
{ | |
MSGWRITEWARN(__FUNCTION__, _T("NULL icon detected.")); | |
} | |
m_imgIcon.SetIcon(hIcon); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Set the dimension limits for the dialog. | |
/// \remarks Only positive values will be used a limits. | |
/// \remarks Negative values will set the bound to the system limit or limitless max(0). | |
/// \remarks Zero will be ignored and keep the existing limit. | |
/// \return void | |
/// \param[in] nMinWidth = 0 - Minimum Suggested Width | |
/// \param[in] nMinHeight = 0 - Minimum Suggested Height | |
/// \param[in] nMaxWidth = 0 - Maximum Suggested Width | |
/// \param[in] nMaxHeight = 0 - Maximum Suggested Height | |
/// \param[in] bResize = true - Resize the dialog to the contents given the limits. | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::SetSizeLimits(_In_opt_ LONG nMinWidth /*= 0*/, _In_opt_ LONG nMinHeight /*= 0*/, _In_opt_ LONG nMaxWidth /*= 0*/, _In_opt_ LONG nMaxHeight /*= 0*/, bool bResize /*= true*/) | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
// Retrieve System Limits | |
int nCXMIN = GetSystemMetrics(SM_CXMINTRACK); | |
int nCXMAX = GetSystemMetrics(SM_CXMAXTRACK); | |
int nCYMIN = GetSystemMetrics(SM_CYMINTRACK); | |
int nCYMAX = GetSystemMetrics(SM_CYMAXTRACK); | |
//// TODO If Minimums are greater than the System Limits, Show Scroll Bars as last resort to fit everything | |
//nMinWidth > nCXMAX ? ModifyStyle(NULL, WS_HSCROLL) : ModifyStyle(WS_HSCROLL, NULL); | |
//nMinWidth > nCXMAX ? ModifyStyle(NULL, WS_VSCROLL) : ModifyStyle(WS_VSCROLL, NULL); | |
// 1. Keep if > 0 and within system limits | |
// 2. Use System or on system fail, max limitless(0), if < 0 or outside system limits | |
// 3. Discard(No Change) if == 0 | |
nMinWidth = nMinWidth > 0 ? (nMinWidth < nCXMIN ? nCXMIN : (nCXMAX > 0 ? min(nCXMAX, nMinWidth) : nMinWidth)) : (nMinWidth < 0 ? nCXMIN : m_MinSize.x); | |
nMaxWidth = nMaxWidth > 0 ? (nMaxWidth < nCXMIN ? nCXMIN : (nCXMAX > 0 ? min(nCXMAX, nMaxWidth) : nMaxWidth)) : (nMaxWidth < 0 ? nCXMAX : m_MaxSize.x); | |
nMinHeight = nMinHeight > 0 ? (nMinHeight < nCYMIN ? nCYMIN : (nCYMAX > 0 ? min(nCYMAX, nMinHeight) : nMinHeight)) : (nMinHeight < 0 ? nCYMIN : m_MinSize.y); | |
nMaxHeight = nMaxHeight > 0 ? (nMaxHeight < nCYMIN ? nCYMIN : (nCYMAX > 0 ? min(nCYMAX, nMaxHeight) : nMaxHeight)) : (nMaxHeight < 0 ? nCYMAX : m_MaxSize.y); | |
// Verify Positive Plane, if negative and not max limitless(0), use minimum. | |
nMaxWidth = nMaxWidth > 0 ? max(nMinWidth, nMaxWidth) : 0; | |
nMaxHeight = nMaxHeight > 0 ? max(nMinHeight, nMaxHeight) : 0; | |
// Save Changes | |
const CPoint oldMin = m_MinSize; | |
const CPoint oldMax = m_MaxSize; | |
m_MinSize.x = nMinWidth; | |
m_MinSize.y = nMinHeight; | |
m_MaxSize.x = nMaxWidth; | |
m_MaxSize.y = nMaxHeight; | |
// Check for Changes | |
bool bChanged = false; | |
if (oldMin.x != m_MinSize.x | |
|| oldMin.y != m_MinSize.y | |
|| oldMax.x != m_MaxSize.x | |
|| oldMax.y != m_MaxSize.y) | |
{ | |
bChanged = true; | |
} | |
// Resize to contents with limits | |
if (bChanged && bResize) | |
{ | |
SizeWindowToContents(); | |
} | |
} | |
void CMessageDlg::SetWindowStyle(_In_ DWORD nStyle) | |
{ | |
if (MAXDWORD == nStyle) | |
{ | |
m_nWindowStyle = DEF_WINDOW_STYLE; | |
} | |
else | |
{ | |
m_nWindowStyle = nStyle; | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Size the Child class elements. Called by OnSize() | |
/// \remarks The parent method should be called and added for return. | |
/// \return CSize = the actual width and height used from the rect top left. | |
/// \param[in] rect - Rectangle where it is safe to draw my elements | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
CSize CMessageDlg::SizeChild(_In_ const CRect& rect) | |
{ | |
return CSize(0, 0); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// \brief Resize the Window to fit the contents within the defined limits. | |
/// \details Adjust the width based upon the message and reasons and adjust the | |
/// height based upon the message and comment field. | |
/// \remarks OnSize must be run first to establish initial window sizes. | |
/// Just run this no earlier than the OnInitDialog() function. | |
/// \remarks The window is also repositioned to maintain on screen center. | |
/// \return void | |
/// \pre none | |
/// \post none | |
//////////////////////////////////////////////////////////////////////////////// | |
void CMessageDlg::SizeWindowToContents() | |
{ | |
MSGWRITETRACE(__FUNCTION__); | |
// Calculate Growth Needed | |
CSize size(INT_MIN, INT_MIN); | |
CalculateContentsGrowth(size); | |
// Adjust the Window Size to fit the content | |
CRect rect(0, 0, 0, 0); | |
GetWindowRect(rect); | |
int w = rect.Width() + size.cx; | |
int h = rect.Height() + size.cy; | |
// Retrieve limits | |
int xMin = m_MinSize.x; | |
int yMin = m_MinSize.y; | |
int xMax = m_MaxSize.x; | |
int yMax = m_MaxSize.y; | |
// Adjust to remain within the space limits | |
w = xMin > w ? xMin : xMax < xMin ? w : xMax > w ? w : xMax > 0 ? xMax : w; | |
h = yMin > h ? yMin : yMax < yMin ? h : yMax > h ? h : yMax > 0 ? yMax : h; | |
// Save as Limits | |
SetSizeLimits(w, h, 0, 0, false); | |
// Determine the actual adjustment | |
size.cx = w - rect.Width(); | |
size.cy = h - rect.Height(); | |
// Adjust the Window Location to maintain center | |
int x = rect.left - (size.cx / 2); | |
int y = rect.top - (size.cy / 2); | |
// Adjust to remain within the location limits | |
//x = max(0, x); | |
//y = max(0, y); | |
// Move the Window | |
MoveWindow(x, y, w, h); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//////////////////////////////////////////////////////////////////////////////// | |
/// Project PVDocMgmt1 | |
/// | |
/// \file MessageDlg.h | |
/// | |
/// \class CMessageDlg | |
/// | |
/// \brief Class to create a message dialog. | |
/// | |
/// \details Creates a message dialog and its controls. | |
/// | |
//////////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include "Resource.h" | |
#include <dcp_dialog.h> // CCDialog | |
#include <dcp_edit.h> // CCEdit | |
#include <dcp_button.h> // CCButton | |
#include <dcp_static.h> // CCStatic | |
#include <Wtsapi32.h> // IDTIMEOUT, IDASYNC | |
// TODO Options | |
// TODO Honor NOMB | |
class CCCButton : public CCButton | |
{ | |
private: | |
CString m_sText; | |
public: | |
virtual BOOL Create(_In_ LPCTSTR sText, _In_ DWORD nStyle, _In_ const RECT& rect, _In_ CWnd* pParent, _In_ UINT nID) override; | |
int GetWindowText(_Out_writes_(nMaxCount) LPTSTR lpszStringBuf, _In_ int nMaxCount) const; | |
void GetWindowText(_Out_ CString& rString) const; | |
void SetWindowText(_In_ LPCTSTR sText); | |
protected: | |
void SetText(_In_ LPCTSTR sText); | |
}; | |
class CMessageDlg : public CCDialog | |
{ | |
protected: | |
//! Dialog Control IDs | |
enum EControl : UINT | |
{ | |
eInvalidID = 0U, //!< Control ID must be > 0 | |
eButtonOK = IDOK, //!< OK | |
eButtonCancel = IDCANCEL, //!< Cancel | |
eButtonAbort = IDABORT, //!< Abort | |
eButtonRetry = IDRETRY, //!< Retry | |
eButtonIgnore = IDIGNORE, //!< Ignore | |
eButtonYes = IDYES, //!< Yes | |
eButtonNo = IDNO, //!< No | |
eButtonClose = IDCLOSE, //!< Close | |
eButtonHelp = IDHELP, //!< Help | |
eButtonTryAgain = IDTRYAGAIN, //!< Try Again | |
eButtonContinue = IDCONTINUE, //!< Continue | |
eTimerTimeout = IDTIMEOUT, //!< Timeout Timer | |
eThreadAsync = IDASYNC, //!< Asynchronous Thread | |
eButtonCustom, //!< Beginning of Custom Button ID Range | |
eButtonCustomEnd = eButtonCustom + 100U, //!< End of Custom Button ID Range (Limit 100) | |
eIcon, //!< Message Static Icon | |
eMessage, //!< Message Text Box | |
// eReservedID must be listed last | |
eReservedID //!< Next Control ID | |
}; | |
public: | |
//! Dialog Result Values | |
enum EResult : int | |
{ | |
eFailed = -1, //!< Dialog Creation Failed | |
eOK = EControl::eButtonOK, //!< OK | |
eCancel = EControl::eButtonCancel, //!< Cancel | |
eAbort = EControl::eButtonAbort, //!< Abort | |
eRetry = EControl::eButtonRetry, //!< Retry | |
eIgnore = EControl::eButtonIgnore, //!< Ignore | |
eYes = EControl::eButtonYes, //!< Yes | |
eNo = EControl::eButtonNo, //!< No | |
eClose = EControl::eButtonClose, //!< Close | |
eHelp = EControl::eButtonHelp, //!< Help | |
eTryAgain = EControl::eButtonTryAgain, //!< Try Again | |
eContinue = EControl::eButtonContinue, //!< Continue | |
eTimeout = IDTIMEOUT, //!< Timeout TODO add timeout functionality | |
eAsync = IDASYNC, //!< Asynchronous, No Immediate Result TODO add threaded functionality | |
eReservedCustom = EControl::eButtonCustom, //!< First Possible Custom Button | |
eReservedCustomEnd = EControl::eButtonCustomEnd, //!< Last Possible Custom Button | |
// eReservedResult must be listed last | |
eReservedResult //!< Next Result for Derived Classes | |
}; | |
//! Dialog Button Types | |
enum EButtons : DWORD | |
{ | |
eOKOnly = MB_OK, //!< OK button | |
eOKCancel = MB_OKCANCEL, //!< OK and Cancel buttons | |
eAbortRetryIgnore = MB_ABORTRETRYIGNORE, //!< Abort, Retry, and Ignore buttons | |
eYesNoCancel = MB_YESNOCANCEL, //!< Yes, No, and Cancel buttons | |
eYesNo = MB_YESNO, //!< Yes and No buttons | |
eRetryCancel = MB_RETRYCANCEL, //!< Retry and Cancel buttons | |
eCancelTryContinue = MB_CANCELTRYCONTINUE, //!< Cancel, Try Again, and Continue buttons | |
eCustom //!< User Defined Buttons | |
}; | |
//! Dialog Message Icons | |
enum EIcon : DWORD | |
{ | |
eInfo = MB_ICONINFORMATION, //!< Blue Circle with White Exclamation Mark | |
eError = MB_ICONERROR, //!< Red Circle with White X | |
eWarning = MB_ICONWARNING, //!< Yellow Triangle with Black Exclamation Point | |
eQuestion = MB_ICONQUESTION, //!< Blue Circle with White Question Mark | |
eAlert, //!< Red Triangle with White Exclamation Point | |
eUser = MB_USERICON, //!< User Defined Icon | |
eNone //!< No Icon | |
}; | |
private: | |
DECLARE_DYNAMIC(CMessageDlg) // RTTI support | |
// Attributes | |
CWnd* m_pParent; //!< Pointer to Parent Window | |
CString m_sTitle; //!< Dialog caption | |
CString m_sMessage; //!< Message to display | |
CPoint m_MinSize; //!< Dialog Minimum Dimension Limits | |
CPoint m_MaxSize; //!< Dialog Maximum Dimension Limits | |
EButtons m_eButtons; //!< Dialog Buttons Type | |
EIcon m_eIcon; //!< Dialog Message Icon | |
EControl m_eNextButtonID; //!< Next Available Button ID | |
EControl m_eNextControlID; //!< Next Available Control ID | |
DWORD m_nWindowStyle; //!< Window Style to Apply | |
DWORD m_nButtonStyle; //!< Button Style to Apply | |
// Controls | |
CCStatic m_imgIcon; //!< Message Icon Image Control Member | |
CCEdit m_edtMessage; //!< Message Control Member | |
// Dynamic Controls | |
CList<EControl> m_ButtonList; //!< Ordered List of the Buttons | |
CMap<EControl, EControl, CButton*, CButton*> m_ButtonMap; //!< Map of Buttons | |
public: | |
// Constructors | |
CMessageDlg(_In_opt_ LPCTSTR sTitle = NULL, _In_opt_ LPCTSTR sMessage = NULL, _In_ EButtons eButtons = eOKCancel, _In_ EIcon eIcon = eNone, _In_opt_ CWnd* pParent = NULL); | |
virtual ~CMessageDlg() override; | |
// Accessors | |
void SetMessageIcon(_In_opt_ HICON hIcon); | |
void SetSizeLimits(_In_opt_ LONG nMinWidth = 0, _In_opt_ LONG nMinHeight = 0, _In_opt_ LONG nMaxWidth = 0, _In_opt_ LONG nMaxHeight = 0, bool bResize = true); | |
// Public Methods | |
CButton* AddButton(_In_opt_ LPCTSTR sText = NULL, _In_ EControl eButtonID = EControl::eButtonCustom); | |
bool RemoveButton(_In_ EControl eControlID); | |
void SetButtonStyle(_In_ DWORD nStyle = MAXDWORD); | |
void SetWindowStyle(_In_ DWORD nStyle = MAXDWORD); | |
// Public Overrides | |
virtual INT_PTR DoModal() override; | |
virtual EControl GetNextButtonID(); | |
virtual EControl GetNextControlID(); | |
virtual void ResetDialog(); | |
// Public Static Methods | |
static int Prompt(_In_ CMessageDlg& dialog); | |
protected: | |
// Internal Methods | |
void ClearButtons(); | |
bool CreateButton(_In_ CCCButton& button, _In_ const CString& sText, _In_ EControl eButtonID); | |
bool CreateButtons(); | |
bool FocusOn(_In_ HWND hWnd); | |
HICON LoadMessageIcon(); | |
void SizeWindowToContents(); | |
// Static Methods | |
static CSize CalculateTextSize(_In_ HWND hWnd, _In_opt_ LPCTSTR sText = NULL, _In_opt_ int nWidth = 0); | |
static CSize CalculateWidestLineSize(_In_ HWND hWnd, _In_opt_ LPCTSTR sText = NULL, _In_ bool bSingle = false); | |
static bool GetTextMetrics(_In_ HWND hWnd, _Out_ TEXTMETRIC& tm); | |
// Internal Overrides | |
virtual void CalculateContentsGrowth(_Inout_ CSize& size); | |
virtual void DoDataExchange(CDataExchange* pDX) override; // DDX/DDV support | |
virtual BOOL OnInitDialog() override; | |
virtual CSize SizeChild(_In_ const CRect& rect); | |
//virtual void OnTimeout(); | |
// Message Map Functions | |
afx_msg int OnCreate(_In_ LPCREATESTRUCT lpCreateStruct); | |
afx_msg void OnDestroy(); | |
afx_msg void OnSize(_In_ UINT nType, _In_ int cx, _In_ int cy); | |
afx_msg void OnOK() override; | |
afx_msg void OnCancel() override; | |
afx_msg void OnYes(); | |
afx_msg void OnNo(); | |
afx_msg void OnAbort(); | |
afx_msg void OnIgnore(); | |
afx_msg void OnRetry(); | |
afx_msg void OnClose(); | |
afx_msg void OnHelp(); | |
afx_msg void OnTryAgain(); | |
afx_msg void OnContinue(); | |
afx_msg void OnClickButton(_In_ UINT nID); | |
afx_msg void OnClickCustom(_In_ UINT nID); | |
afx_msg void OnCustom(_In_ UINT nID); | |
afx_msg void OnPaint(); | |
afx_msg HBRUSH OnCtlColor(_In_ CDC* pDC, _In_ CWnd* pWnd, _In_ UINT nCtlColor); | |
afx_msg void OnGetMinMaxInfo(_Inout_ MINMAXINFO* lpMMI); | |
DECLARE_MESSAGE_MAP() | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment