| // Windows Template Library - WTL version 8.0 |
| // Copyright (C) Microsoft Corporation. All rights reserved. |
| // |
| // This file is a part of the Windows Template Library. |
| // The use and distribution terms for this software are covered by the |
| // Microsoft Permissive License (Ms-PL) which can be found in the file |
| // Ms-PL.txt at the root of this distribution. |
| |
| #ifndef __ATLDDX_H__ |
| #define __ATLDDX_H__ |
| |
| #pragma once |
| |
| #ifndef __cplusplus |
| #error ATL requires C++ compilation (use a .cpp suffix) |
| #endif |
| |
| #ifndef __ATLAPP_H__ |
| #error atlddx.h requires atlapp.h to be included first |
| #endif |
| |
| #if defined(_ATL_USE_DDX_FLOAT) && defined(_ATL_MIN_CRT) |
| #error Cannot use floating point DDX with _ATL_MIN_CRT defined |
| #endif // defined(_ATL_USE_DDX_FLOAT) && defined(_ATL_MIN_CRT) |
| |
| #ifdef _ATL_USE_DDX_FLOAT |
| #include <float.h> |
| #endif // _ATL_USE_DDX_FLOAT |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Classes in this file: |
| // |
| // CWinDataExchange<T> |
| |
| |
| namespace WTL |
| { |
| |
| // Constants |
| #define DDX_LOAD FALSE |
| #define DDX_SAVE TRUE |
| |
| // DDX map macros |
| #define BEGIN_DDX_MAP(thisClass) \ |
| BOOL DoDataExchange(BOOL bSaveAndValidate = FALSE, UINT nCtlID = (UINT)-1) \ |
| { \ |
| bSaveAndValidate; \ |
| nCtlID; |
| |
| #define DDX_TEXT(nID, var) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| { \ |
| if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate)) \ |
| return FALSE; \ |
| } |
| |
| #define DDX_TEXT_LEN(nID, var, len) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| { \ |
| if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate, TRUE, len)) \ |
| return FALSE; \ |
| } |
| |
| #define DDX_INT(nID, var) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| { \ |
| if(!DDX_Int(nID, var, TRUE, bSaveAndValidate)) \ |
| return FALSE; \ |
| } |
| |
| #define DDX_INT_RANGE(nID, var, min, max) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| { \ |
| if(!DDX_Int(nID, var, TRUE, bSaveAndValidate, TRUE, min, max)) \ |
| return FALSE; \ |
| } |
| |
| #define DDX_UINT(nID, var) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| { \ |
| if(!DDX_Int(nID, var, FALSE, bSaveAndValidate)) \ |
| return FALSE; \ |
| } |
| |
| #define DDX_UINT_RANGE(nID, var, min, max) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| { \ |
| if(!DDX_Int(nID, var, FALSE, bSaveAndValidate, TRUE, min, max)) \ |
| return FALSE; \ |
| } |
| |
| #ifdef _ATL_USE_DDX_FLOAT |
| #define DDX_FLOAT(nID, var) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| { \ |
| if(!DDX_Float(nID, var, bSaveAndValidate)) \ |
| return FALSE; \ |
| } |
| |
| #define DDX_FLOAT_RANGE(nID, var, min, max) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| { \ |
| if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max)) \ |
| return FALSE; \ |
| } |
| #define DDX_FLOAT_P(nID, var, precision) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| { \ |
| if(!DDX_Float(nID, var, bSaveAndValidate, FALSE, 0, 0, precision)) \ |
| return FALSE; \ |
| } |
| |
| #define DDX_FLOAT_P_RANGE(nID, var, min, max, precision) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| { \ |
| if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max, precision)) \ |
| return FALSE; \ |
| } |
| #endif // _ATL_USE_DDX_FLOAT |
| |
| #define DDX_CONTROL(nID, obj) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| DDX_Control(nID, obj, bSaveAndValidate); |
| |
| #define DDX_CONTROL_HANDLE(nID, obj) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| DDX_Control_Handle(nID, obj, bSaveAndValidate); |
| |
| #define DDX_CHECK(nID, var) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| DDX_Check(nID, var, bSaveAndValidate); |
| |
| #define DDX_RADIO(nID, var) \ |
| if(nCtlID == (UINT)-1 || nCtlID == nID) \ |
| DDX_Radio(nID, var, bSaveAndValidate); |
| |
| #define END_DDX_MAP() \ |
| return TRUE; \ |
| } |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // CWinDataExchange - provides support for DDX |
| |
| template <class T> |
| class CWinDataExchange |
| { |
| public: |
| // Data exchange method - override in your derived class |
| BOOL DoDataExchange(BOOL /*bSaveAndValidate*/ = FALSE, UINT /*nCtlID*/ = (UINT)-1) |
| { |
| // this one should never be called, override it in |
| // your derived class by implementing DDX map |
| ATLASSERT(FALSE); |
| return FALSE; |
| } |
| |
| // Helpers for validation error reporting |
| enum _XDataType |
| { |
| ddxDataNull = 0, |
| ddxDataText = 1, |
| ddxDataInt = 2, |
| ddxDataFloat = 3, |
| ddxDataDouble = 4 |
| }; |
| |
| struct _XTextData |
| { |
| int nLength; |
| int nMaxLength; |
| }; |
| |
| struct _XIntData |
| { |
| long nVal; |
| long nMin; |
| long nMax; |
| }; |
| |
| struct _XFloatData |
| { |
| double nVal; |
| double nMin; |
| double nMax; |
| }; |
| |
| struct _XData |
| { |
| _XDataType nDataType; |
| union |
| { |
| _XTextData textData; |
| _XIntData intData; |
| _XFloatData floatData; |
| }; |
| }; |
| |
| // Text exchange |
| BOOL DDX_Text(UINT nID, LPTSTR lpstrText, int cbSize, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) |
| { |
| T* pT = static_cast<T*>(this); |
| BOOL bSuccess = TRUE; |
| |
| if(bSave) |
| { |
| HWND hWndCtrl = pT->GetDlgItem(nID); |
| int nRetLen = ::GetWindowText(hWndCtrl, lpstrText, cbSize / sizeof(TCHAR)); |
| if(nRetLen < ::GetWindowTextLength(hWndCtrl)) |
| bSuccess = FALSE; |
| } |
| else |
| { |
| ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength); |
| bSuccess = pT->SetDlgItemText(nID, lpstrText); |
| } |
| |
| if(!bSuccess) |
| { |
| pT->OnDataExchangeError(nID, bSave); |
| } |
| else if(bSave && bValidate) // validation |
| { |
| ATLASSERT(nLength > 0); |
| if(lstrlen(lpstrText) > nLength) |
| { |
| _XData data = { ddxDataText }; |
| data.textData.nLength = lstrlen(lpstrText); |
| data.textData.nMaxLength = nLength; |
| pT->OnDataValidateError(nID, bSave, data); |
| bSuccess = FALSE; |
| } |
| } |
| return bSuccess; |
| } |
| |
| BOOL DDX_Text(UINT nID, BSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) |
| { |
| T* pT = static_cast<T*>(this); |
| BOOL bSuccess = TRUE; |
| |
| if(bSave) |
| { |
| bSuccess = pT->GetDlgItemText(nID, bstrText); |
| } |
| else |
| { |
| USES_CONVERSION; |
| LPTSTR lpstrText = OLE2T(bstrText); |
| ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength); |
| bSuccess = pT->SetDlgItemText(nID, lpstrText); |
| } |
| |
| if(!bSuccess) |
| { |
| pT->OnDataExchangeError(nID, bSave); |
| } |
| else if(bSave && bValidate) // validation |
| { |
| ATLASSERT(nLength > 0); |
| if((int)::SysStringLen(bstrText) > nLength) |
| { |
| _XData data = { ddxDataText }; |
| data.textData.nLength = (int)::SysStringLen(bstrText); |
| data.textData.nMaxLength = nLength; |
| pT->OnDataValidateError(nID, bSave, data); |
| bSuccess = FALSE; |
| } |
| } |
| return bSuccess; |
| } |
| |
| BOOL DDX_Text(UINT nID, ATL::CComBSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) |
| { |
| T* pT = static_cast<T*>(this); |
| BOOL bSuccess = TRUE; |
| |
| if(bSave) |
| { |
| bSuccess = pT->GetDlgItemText(nID, (BSTR&)bstrText); |
| } |
| else |
| { |
| USES_CONVERSION; |
| LPTSTR lpstrText = OLE2T(bstrText); |
| ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength); |
| bSuccess = pT->SetDlgItemText(nID, lpstrText); |
| } |
| |
| if(!bSuccess) |
| { |
| pT->OnDataExchangeError(nID, bSave); |
| } |
| else if(bSave && bValidate) // validation |
| { |
| ATLASSERT(nLength > 0); |
| if((int)bstrText.Length() > nLength) |
| { |
| _XData data = { ddxDataText }; |
| data.textData.nLength = (int)bstrText.Length(); |
| data.textData.nMaxLength = nLength; |
| pT->OnDataValidateError(nID, bSave, data); |
| bSuccess = FALSE; |
| } |
| } |
| return bSuccess; |
| } |
| |
| #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) |
| BOOL DDX_Text(UINT nID, _CSTRING_NS::CString& strText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) |
| { |
| T* pT = static_cast<T*>(this); |
| BOOL bSuccess = TRUE; |
| |
| if(bSave) |
| { |
| HWND hWndCtrl = pT->GetDlgItem(nID); |
| int nLen = ::GetWindowTextLength(hWndCtrl); |
| int nRetLen = -1; |
| LPTSTR lpstr = strText.GetBufferSetLength(nLen); |
| if(lpstr != NULL) |
| { |
| nRetLen = ::GetWindowText(hWndCtrl, lpstr, nLen + 1); |
| strText.ReleaseBuffer(); |
| } |
| if(nRetLen < nLen) |
| bSuccess = FALSE; |
| } |
| else |
| { |
| bSuccess = pT->SetDlgItemText(nID, strText); |
| } |
| |
| if(!bSuccess) |
| { |
| pT->OnDataExchangeError(nID, bSave); |
| } |
| else if(bSave && bValidate) // validation |
| { |
| ATLASSERT(nLength > 0); |
| if(strText.GetLength() > nLength) |
| { |
| _XData data = { ddxDataText }; |
| data.textData.nLength = strText.GetLength(); |
| data.textData.nMaxLength = nLength; |
| pT->OnDataValidateError(nID, bSave, data); |
| bSuccess = FALSE; |
| } |
| } |
| return bSuccess; |
| } |
| #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) |
| |
| // Numeric exchange |
| template <class Type> |
| BOOL DDX_Int(UINT nID, Type& nVal, BOOL bSigned, BOOL bSave, BOOL bValidate = FALSE, Type nMin = 0, Type nMax = 0) |
| { |
| T* pT = static_cast<T*>(this); |
| BOOL bSuccess = TRUE; |
| |
| if(bSave) |
| { |
| nVal = (Type)pT->GetDlgItemInt(nID, &bSuccess, bSigned); |
| } |
| else |
| { |
| ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax); |
| bSuccess = pT->SetDlgItemInt(nID, nVal, bSigned); |
| } |
| |
| if(!bSuccess) |
| { |
| pT->OnDataExchangeError(nID, bSave); |
| } |
| else if(bSave && bValidate) // validation |
| { |
| ATLASSERT(nMin != nMax); |
| if(nVal < nMin || nVal > nMax) |
| { |
| _XData data = { ddxDataInt }; |
| data.intData.nVal = (long)nVal; |
| data.intData.nMin = (long)nMin; |
| data.intData.nMax = (long)nMax; |
| pT->OnDataValidateError(nID, bSave, data); |
| bSuccess = FALSE; |
| } |
| } |
| return bSuccess; |
| } |
| |
| // Float exchange |
| #ifdef _ATL_USE_DDX_FLOAT |
| static BOOL _AtlSimpleFloatParse(LPCTSTR lpszText, double& d) |
| { |
| ATLASSERT(lpszText != NULL); |
| while (*lpszText == _T(' ') || *lpszText == _T('\t')) |
| lpszText++; |
| |
| TCHAR chFirst = lpszText[0]; |
| d = _tcstod(lpszText, (LPTSTR*)&lpszText); |
| if (d == 0.0 && chFirst != _T('0')) |
| return FALSE; // could not convert |
| while (*lpszText == _T(' ') || *lpszText == _T('\t')) |
| lpszText++; |
| |
| if (*lpszText != _T('\0')) |
| return FALSE; // not terminated properly |
| |
| return TRUE; |
| } |
| |
| BOOL DDX_Float(UINT nID, float& nVal, BOOL bSave, BOOL bValidate = FALSE, float nMin = 0.F, float nMax = 0.F, int nPrecision = FLT_DIG) |
| { |
| T* pT = static_cast<T*>(this); |
| BOOL bSuccess = TRUE; |
| const int cchBuff = 32; |
| TCHAR szBuff[cchBuff] = { 0 }; |
| |
| if(bSave) |
| { |
| pT->GetDlgItemText(nID, szBuff, cchBuff); |
| double d = 0; |
| if(_AtlSimpleFloatParse(szBuff, d)) |
| nVal = (float)d; |
| else |
| bSuccess = FALSE; |
| } |
| else |
| { |
| ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax); |
| SecureHelper::sprintf_x(szBuff, cchBuff, _T("%.*g"), nPrecision, nVal); |
| bSuccess = pT->SetDlgItemText(nID, szBuff); |
| } |
| |
| if(!bSuccess) |
| { |
| pT->OnDataExchangeError(nID, bSave); |
| } |
| else if(bSave && bValidate) // validation |
| { |
| ATLASSERT(nMin != nMax); |
| if(nVal < nMin || nVal > nMax) |
| { |
| _XData data = { ddxDataFloat }; |
| data.floatData.nVal = (double)nVal; |
| data.floatData.nMin = (double)nMin; |
| data.floatData.nMax = (double)nMax; |
| pT->OnDataValidateError(nID, bSave, data); |
| bSuccess = FALSE; |
| } |
| } |
| return bSuccess; |
| } |
| |
| BOOL DDX_Float(UINT nID, double& nVal, BOOL bSave, BOOL bValidate = FALSE, double nMin = 0., double nMax = 0., int nPrecision = DBL_DIG) |
| { |
| T* pT = static_cast<T*>(this); |
| BOOL bSuccess = TRUE; |
| const int cchBuff = 32; |
| TCHAR szBuff[cchBuff] = { 0 }; |
| |
| if(bSave) |
| { |
| pT->GetDlgItemText(nID, szBuff, cchBuff); |
| double d = 0; |
| if(_AtlSimpleFloatParse(szBuff, d)) |
| nVal = d; |
| else |
| bSuccess = FALSE; |
| } |
| else |
| { |
| ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax); |
| SecureHelper::sprintf_x(szBuff, cchBuff, _T("%.*g"), nPrecision, nVal); |
| bSuccess = pT->SetDlgItemText(nID, szBuff); |
| } |
| |
| if(!bSuccess) |
| { |
| pT->OnDataExchangeError(nID, bSave); |
| } |
| else if(bSave && bValidate) // validation |
| { |
| ATLASSERT(nMin != nMax); |
| if(nVal < nMin || nVal > nMax) |
| { |
| _XData data = { ddxDataFloat }; |
| data.floatData.nVal = nVal; |
| data.floatData.nMin = nMin; |
| data.floatData.nMax = nMax; |
| pT->OnDataValidateError(nID, bSave, data); |
| bSuccess = FALSE; |
| } |
| } |
| return bSuccess; |
| } |
| #endif // _ATL_USE_DDX_FLOAT |
| |
| // Full control subclassing (for CWindowImpl derived controls) |
| template <class TControl> |
| void DDX_Control(UINT nID, TControl& ctrl, BOOL bSave) |
| { |
| if(!bSave && ctrl.m_hWnd == NULL) |
| { |
| T* pT = static_cast<T*>(this); |
| ctrl.SubclassWindow(pT->GetDlgItem(nID)); |
| } |
| } |
| |
| // Simple control attaching (for HWND wrapper controls) |
| template <class TControl> |
| void DDX_Control_Handle(UINT nID, TControl& ctrl, BOOL bSave) |
| { |
| if(!bSave && ctrl.m_hWnd == NULL) |
| { |
| T* pT = static_cast<T*>(this); |
| ctrl = pT->GetDlgItem(nID); |
| } |
| } |
| |
| // Control state |
| void DDX_Check(UINT nID, int& nValue, BOOL bSave) |
| { |
| T* pT = static_cast<T*>(this); |
| HWND hWndCtrl = pT->GetDlgItem(nID); |
| if(bSave) |
| { |
| nValue = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L); |
| ATLASSERT(nValue >= 0 && nValue <= 2); |
| } |
| else |
| { |
| if(nValue < 0 || nValue > 2) |
| { |
| ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - dialog data checkbox value (%d) out of range.\n"), nValue); |
| nValue = 0; // default to off |
| } |
| ::SendMessage(hWndCtrl, BM_SETCHECK, nValue, 0L); |
| } |
| } |
| |
| // variant that supports bool (checked/not-checked, no intermediate state) |
| void DDX_Check(UINT nID, bool& bCheck, BOOL bSave) |
| { |
| int nValue = bCheck ? 1 : 0; |
| DDX_Check(nID, nValue, bSave); |
| |
| if(bSave) |
| { |
| if(nValue == 2) |
| ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - checkbox state (%d) out of supported range.\n"), nValue); |
| bCheck = (nValue == 1); |
| } |
| } |
| |
| void DDX_Radio(UINT nID, int& nValue, BOOL bSave) |
| { |
| T* pT = static_cast<T*>(this); |
| HWND hWndCtrl = pT->GetDlgItem(nID); |
| ATLASSERT(hWndCtrl != NULL); |
| |
| // must be first in a group of auto radio buttons |
| ATLASSERT(::GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP); |
| ATLASSERT(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON); |
| |
| if(bSave) |
| nValue = -1; // value if none found |
| |
| // walk all children in group |
| int nButton = 0; |
| do |
| { |
| if(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON) |
| { |
| // control in group is a radio button |
| if(bSave) |
| { |
| if(::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0) |
| { |
| ATLASSERT(nValue == -1); // only set once |
| nValue = nButton; |
| } |
| } |
| else |
| { |
| // select button |
| ::SendMessage(hWndCtrl, BM_SETCHECK, (nButton == nValue), 0L); |
| } |
| nButton++; |
| } |
| else |
| { |
| ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - skipping non-radio button in group.\n")); |
| } |
| hWndCtrl = ::GetWindow(hWndCtrl, GW_HWNDNEXT); |
| } |
| while (hWndCtrl != NULL && !(GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP)); |
| } |
| |
| // Overrideables |
| void OnDataExchangeError(UINT nCtrlID, BOOL /*bSave*/) |
| { |
| // Override to display an error message |
| ::MessageBeep((UINT)-1); |
| T* pT = static_cast<T*>(this); |
| ::SetFocus(pT->GetDlgItem(nCtrlID)); |
| } |
| |
| void OnDataValidateError(UINT nCtrlID, BOOL /*bSave*/, _XData& /*data*/) |
| { |
| // Override to display an error message |
| ::MessageBeep((UINT)-1); |
| T* pT = static_cast<T*>(this); |
| ::SetFocus(pT->GetDlgItem(nCtrlID)); |
| } |
| }; |
| |
| }; // namespace WTL |
| |
| #endif // __ATLDDX_H__ |