1 // Windows Template Library - WTL version 8.0
\r
2 // Copyright (C) Microsoft Corporation. All rights reserved.
\r
4 // This file is a part of the Windows Template Library.
\r
5 // The use and distribution terms for this software are covered by the
\r
6 // Common Public License 1.0 (http://opensource.org/osi3.0/licenses/cpl1.0.php)
\r
7 // which can be found in the file CPL.TXT at the root of this distribution.
\r
8 // By using this software in any fashion, you are agreeing to be bound by
\r
9 // the terms of this license. You must not remove this notice, or
\r
10 // any other, from this software.
\r
12 #ifndef __ATLDDX_H__
\r
13 #define __ATLDDX_H__
\r
18 #error ATL requires C++ compilation (use a .cpp suffix)
\r
21 #ifndef __ATLAPP_H__
\r
22 #error atlddx.h requires atlapp.h to be included first
\r
25 #if defined(_ATL_USE_DDX_FLOAT) && defined(_ATL_MIN_CRT)
\r
26 #error Cannot use floating point DDX with _ATL_MIN_CRT defined
\r
27 #endif // defined(_ATL_USE_DDX_FLOAT) && defined(_ATL_MIN_CRT)
\r
29 #ifdef _ATL_USE_DDX_FLOAT
\r
31 #endif // _ATL_USE_DDX_FLOAT
\r
34 ///////////////////////////////////////////////////////////////////////////////
\r
35 // Classes in this file:
\r
37 // CWinDataExchange<T>
\r
44 #define DDX_LOAD FALSE
\r
45 #define DDX_SAVE TRUE
\r
48 #define BEGIN_DDX_MAP(thisClass) \
\r
49 BOOL DoDataExchange(BOOL bSaveAndValidate = FALSE, UINT nCtlID = (UINT)-1) \
\r
54 #define DDX_TEXT(nID, var) \
\r
55 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
57 if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate)) \
\r
61 #define DDX_TEXT_LEN(nID, var, len) \
\r
62 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
64 if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate, TRUE, len)) \
\r
68 #define DDX_INT(nID, var) \
\r
69 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
71 if(!DDX_Int(nID, var, TRUE, bSaveAndValidate)) \
\r
75 #define DDX_INT_RANGE(nID, var, min, max) \
\r
76 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
78 if(!DDX_Int(nID, var, TRUE, bSaveAndValidate, TRUE, min, max)) \
\r
82 #define DDX_UINT(nID, var) \
\r
83 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
85 if(!DDX_Int(nID, var, FALSE, bSaveAndValidate)) \
\r
89 #define DDX_UINT_RANGE(nID, var, min, max) \
\r
90 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
92 if(!DDX_Int(nID, var, FALSE, bSaveAndValidate, TRUE, min, max)) \
\r
96 #ifdef _ATL_USE_DDX_FLOAT
\r
97 #define DDX_FLOAT(nID, var) \
\r
98 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
100 if(!DDX_Float(nID, var, bSaveAndValidate)) \
\r
104 #define DDX_FLOAT_RANGE(nID, var, min, max) \
\r
105 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
107 if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max)) \
\r
110 #define DDX_FLOAT_P(nID, var, precision) \
\r
111 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
113 if(!DDX_Float(nID, var, bSaveAndValidate, FALSE, 0, 0, precision)) \
\r
117 #define DDX_FLOAT_P_RANGE(nID, var, min, max, precision) \
\r
118 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
120 if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max, precision)) \
\r
123 #endif // _ATL_USE_DDX_FLOAT
\r
125 #define DDX_CONTROL(nID, obj) \
\r
126 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
127 DDX_Control(nID, obj, bSaveAndValidate);
\r
129 #define DDX_CONTROL_HANDLE(nID, obj) \
\r
130 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
131 DDX_Control_Handle(nID, obj, bSaveAndValidate);
\r
133 #define DDX_CHECK(nID, var) \
\r
134 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
135 DDX_Check(nID, var, bSaveAndValidate);
\r
137 #define DDX_RADIO(nID, var) \
\r
138 if(nCtlID == (UINT)-1 || nCtlID == nID) \
\r
139 DDX_Radio(nID, var, bSaveAndValidate);
\r
141 #define END_DDX_MAP() \
\r
146 ///////////////////////////////////////////////////////////////////////////////
\r
147 // CWinDataExchange - provides support for DDX
\r
150 class CWinDataExchange
\r
153 // Data exchange method - override in your derived class
\r
154 BOOL DoDataExchange(BOOL /*bSaveAndValidate*/ = FALSE, UINT /*nCtlID*/ = (UINT)-1)
\r
156 // this one should never be called, override it in
\r
157 // your derived class by implementing DDX map
\r
162 // Helpers for validation error reporting
\r
194 _XDataType nDataType;
\r
197 _XTextData textData;
\r
199 _XFloatData floatData;
\r
204 BOOL DDX_Text(UINT nID, LPTSTR lpstrText, int cbSize, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)
\r
206 T* pT = static_cast<T*>(this);
\r
207 BOOL bSuccess = TRUE;
\r
211 HWND hWndCtrl = pT->GetDlgItem(nID);
\r
212 int nRetLen = ::GetWindowText(hWndCtrl, lpstrText, cbSize / sizeof(TCHAR));
\r
213 if(nRetLen < ::GetWindowTextLength(hWndCtrl))
\r
218 ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength);
\r
219 bSuccess = pT->SetDlgItemText(nID, lpstrText);
\r
224 pT->OnDataExchangeError(nID, bSave);
\r
226 else if(bSave && bValidate) // validation
\r
228 ATLASSERT(nLength > 0);
\r
229 if(lstrlen(lpstrText) > nLength)
\r
231 _XData data = { ddxDataText };
\r
232 data.textData.nLength = lstrlen(lpstrText);
\r
233 data.textData.nMaxLength = nLength;
\r
234 pT->OnDataValidateError(nID, bSave, data);
\r
241 BOOL DDX_Text(UINT nID, BSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)
\r
243 T* pT = static_cast<T*>(this);
\r
244 BOOL bSuccess = TRUE;
\r
248 bSuccess = pT->GetDlgItemText(nID, bstrText);
\r
253 LPTSTR lpstrText = OLE2T(bstrText);
\r
254 ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength);
\r
255 bSuccess = pT->SetDlgItemText(nID, lpstrText);
\r
260 pT->OnDataExchangeError(nID, bSave);
\r
262 else if(bSave && bValidate) // validation
\r
264 ATLASSERT(nLength > 0);
\r
265 if((int)::SysStringLen(bstrText) > nLength)
\r
267 _XData data = { ddxDataText };
\r
268 data.textData.nLength = (int)::SysStringLen(bstrText);
\r
269 data.textData.nMaxLength = nLength;
\r
270 pT->OnDataValidateError(nID, bSave, data);
\r
277 BOOL DDX_Text(UINT nID, ATL::CComBSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)
\r
279 T* pT = static_cast<T*>(this);
\r
280 BOOL bSuccess = TRUE;
\r
284 bSuccess = pT->GetDlgItemText(nID, (BSTR&)bstrText);
\r
289 LPTSTR lpstrText = OLE2T(bstrText);
\r
290 ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength);
\r
291 bSuccess = pT->SetDlgItemText(nID, lpstrText);
\r
296 pT->OnDataExchangeError(nID, bSave);
\r
298 else if(bSave && bValidate) // validation
\r
300 ATLASSERT(nLength > 0);
\r
301 if((int)bstrText.Length() > nLength)
\r
303 _XData data = { ddxDataText };
\r
304 data.textData.nLength = (int)bstrText.Length();
\r
305 data.textData.nMaxLength = nLength;
\r
306 pT->OnDataValidateError(nID, bSave, data);
\r
313 #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
\r
314 BOOL DDX_Text(UINT nID, _CSTRING_NS::CString& strText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)
\r
316 T* pT = static_cast<T*>(this);
\r
317 BOOL bSuccess = TRUE;
\r
321 HWND hWndCtrl = pT->GetDlgItem(nID);
\r
322 int nLen = ::GetWindowTextLength(hWndCtrl);
\r
324 LPTSTR lpstr = strText.GetBufferSetLength(nLen);
\r
327 nRetLen = ::GetWindowText(hWndCtrl, lpstr, nLen + 1);
\r
328 strText.ReleaseBuffer();
\r
335 bSuccess = pT->SetDlgItemText(nID, strText);
\r
340 pT->OnDataExchangeError(nID, bSave);
\r
342 else if(bSave && bValidate) // validation
\r
344 ATLASSERT(nLength > 0);
\r
345 if(strText.GetLength() > nLength)
\r
347 _XData data = { ddxDataText };
\r
348 data.textData.nLength = strText.GetLength();
\r
349 data.textData.nMaxLength = nLength;
\r
350 pT->OnDataValidateError(nID, bSave, data);
\r
356 #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
\r
358 // Numeric exchange
\r
359 template <class Type>
\r
360 BOOL DDX_Int(UINT nID, Type& nVal, BOOL bSigned, BOOL bSave, BOOL bValidate = FALSE, Type nMin = 0, Type nMax = 0)
\r
362 T* pT = static_cast<T*>(this);
\r
363 BOOL bSuccess = TRUE;
\r
367 nVal = (Type)pT->GetDlgItemInt(nID, &bSuccess, bSigned);
\r
371 ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax);
\r
372 bSuccess = pT->SetDlgItemInt(nID, nVal, bSigned);
\r
377 pT->OnDataExchangeError(nID, bSave);
\r
379 else if(bSave && bValidate) // validation
\r
381 ATLASSERT(nMin != nMax);
\r
382 if(nVal < nMin || nVal > nMax)
\r
384 _XData data = { ddxDataInt };
\r
385 data.intData.nVal = (long)nVal;
\r
386 data.intData.nMin = (long)nMin;
\r
387 data.intData.nMax = (long)nMax;
\r
388 pT->OnDataValidateError(nID, bSave, data);
\r
396 #ifdef _ATL_USE_DDX_FLOAT
\r
397 static BOOL _AtlSimpleFloatParse(LPCTSTR lpszText, double& d)
\r
399 ATLASSERT(lpszText != NULL);
\r
400 while (*lpszText == _T(' ') || *lpszText == _T('\t'))
\r
403 TCHAR chFirst = lpszText[0];
\r
404 d = _tcstod(lpszText, (LPTSTR*)&lpszText);
\r
405 if (d == 0.0 && chFirst != _T('0'))
\r
406 return FALSE; // could not convert
\r
407 while (*lpszText == _T(' ') || *lpszText == _T('\t'))
\r
410 if (*lpszText != _T('\0'))
\r
411 return FALSE; // not terminated properly
\r
416 BOOL DDX_Float(UINT nID, float& nVal, BOOL bSave, BOOL bValidate = FALSE, float nMin = 0.F, float nMax = 0.F, int nPrecision = FLT_DIG)
\r
418 T* pT = static_cast<T*>(this);
\r
419 BOOL bSuccess = TRUE;
\r
420 const int cchBuff = 32;
\r
421 TCHAR szBuff[cchBuff] = { 0 };
\r
425 pT->GetDlgItemText(nID, szBuff, cchBuff);
\r
427 if(_AtlSimpleFloatParse(szBuff, d))
\r
434 ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax);
\r
435 SecureHelper::sprintf_x(szBuff, cchBuff, _T("%.*g"), nPrecision, nVal);
\r
436 bSuccess = pT->SetDlgItemText(nID, szBuff);
\r
441 pT->OnDataExchangeError(nID, bSave);
\r
443 else if(bSave && bValidate) // validation
\r
445 ATLASSERT(nMin != nMax);
\r
446 if(nVal < nMin || nVal > nMax)
\r
448 _XData data = { ddxDataFloat };
\r
449 data.floatData.nVal = (double)nVal;
\r
450 data.floatData.nMin = (double)nMin;
\r
451 data.floatData.nMax = (double)nMax;
\r
452 pT->OnDataValidateError(nID, bSave, data);
\r
459 BOOL DDX_Float(UINT nID, double& nVal, BOOL bSave, BOOL bValidate = FALSE, double nMin = 0., double nMax = 0., int nPrecision = DBL_DIG)
\r
461 T* pT = static_cast<T*>(this);
\r
462 BOOL bSuccess = TRUE;
\r
463 const int cchBuff = 32;
\r
464 TCHAR szBuff[cchBuff] = { 0 };
\r
468 pT->GetDlgItemText(nID, szBuff, cchBuff);
\r
470 if(_AtlSimpleFloatParse(szBuff, d))
\r
477 ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax);
\r
478 SecureHelper::sprintf_x(szBuff, cchBuff, _T("%.*g"), nPrecision, nVal);
\r
479 bSuccess = pT->SetDlgItemText(nID, szBuff);
\r
484 pT->OnDataExchangeError(nID, bSave);
\r
486 else if(bSave && bValidate) // validation
\r
488 ATLASSERT(nMin != nMax);
\r
489 if(nVal < nMin || nVal > nMax)
\r
491 _XData data = { ddxDataFloat };
\r
492 data.floatData.nVal = nVal;
\r
493 data.floatData.nMin = nMin;
\r
494 data.floatData.nMax = nMax;
\r
495 pT->OnDataValidateError(nID, bSave, data);
\r
501 #endif // _ATL_USE_DDX_FLOAT
\r
503 // Full control subclassing (for CWindowImpl derived controls)
\r
504 template <class TControl>
\r
505 void DDX_Control(UINT nID, TControl& ctrl, BOOL bSave)
\r
507 if(!bSave && ctrl.m_hWnd == NULL)
\r
509 T* pT = static_cast<T*>(this);
\r
510 ctrl.SubclassWindow(pT->GetDlgItem(nID));
\r
514 // Simple control attaching (for HWND wrapper controls)
\r
515 template <class TControl>
\r
516 void DDX_Control_Handle(UINT nID, TControl& ctrl, BOOL bSave)
\r
518 if(!bSave && ctrl.m_hWnd == NULL)
\r
520 T* pT = static_cast<T*>(this);
\r
521 ctrl = pT->GetDlgItem(nID);
\r
526 void DDX_Check(UINT nID, int& nValue, BOOL bSave)
\r
528 T* pT = static_cast<T*>(this);
\r
529 HWND hWndCtrl = pT->GetDlgItem(nID);
\r
532 nValue = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L);
\r
533 ATLASSERT(nValue >= 0 && nValue <= 2);
\r
537 if(nValue < 0 || nValue > 2)
\r
539 ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - dialog data checkbox value (%d) out of range.\n"), nValue);
\r
540 nValue = 0; // default to off
\r
542 ::SendMessage(hWndCtrl, BM_SETCHECK, nValue, 0L);
\r
546 // variant that supports bool (checked/not-checked, no intermediate state)
\r
547 void DDX_Check(UINT nID, bool& bCheck, BOOL bSave)
\r
549 int nValue = bCheck ? 1 : 0;
\r
550 DDX_Check(nID, nValue, bSave);
\r
555 ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - checkbox state (%d) out of supported range.\n"), nValue);
\r
556 bCheck = (nValue == 1);
\r
560 void DDX_Radio(UINT nID, int& nValue, BOOL bSave)
\r
562 T* pT = static_cast<T*>(this);
\r
563 HWND hWndCtrl = pT->GetDlgItem(nID);
\r
564 ATLASSERT(hWndCtrl != NULL);
\r
566 // must be first in a group of auto radio buttons
\r
567 ATLASSERT(::GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP);
\r
568 ATLASSERT(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON);
\r
571 nValue = -1; // value if none found
\r
573 // walk all children in group
\r
577 if(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)
\r
579 // control in group is a radio button
\r
582 if(::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0)
\r
584 ATLASSERT(nValue == -1); // only set once
\r
591 ::SendMessage(hWndCtrl, BM_SETCHECK, (nButton == nValue), 0L);
\r
597 ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - skipping non-radio button in group.\n"));
\r
599 hWndCtrl = ::GetWindow(hWndCtrl, GW_HWNDNEXT);
\r
601 while (hWndCtrl != NULL && !(GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP));
\r
605 void OnDataExchangeError(UINT nCtrlID, BOOL /*bSave*/)
\r
607 // Override to display an error message
\r
608 ::MessageBeep((UINT)-1);
\r
609 T* pT = static_cast<T*>(this);
\r
610 ::SetFocus(pT->GetDlgItem(nCtrlID));
\r
613 void OnDataValidateError(UINT nCtrlID, BOOL /*bSave*/, _XData& /*data*/)
\r
615 // Override to display an error message
\r
616 ::MessageBeep((UINT)-1);
\r
617 T* pT = static_cast<T*>(this);
\r
618 ::SetFocus(pT->GetDlgItem(nCtrlID));
\r
622 }; // namespace WTL
\r
624 #endif // __ATLDDX_H__
\r