1 // Windows Template Library - WTL version 8.0
2 // Copyright (C) Microsoft Corporation. All rights reserved.
4 // This file is a part of the Windows Template Library.
5 // The use and distribution terms for this software are covered by the
6 // Microsoft Permissive License (Ms-PL) which can be found in the file
7 // Ms-PL.txt at the root of this distribution.
15 #error ATL requires C++ compilation (use a .cpp suffix)
19 #error atlddx.h requires atlapp.h to be included first
22 #if defined(_ATL_USE_DDX_FLOAT) && defined(_ATL_MIN_CRT)
23 #error Cannot use floating point DDX with _ATL_MIN_CRT defined
24 #endif // defined(_ATL_USE_DDX_FLOAT) && defined(_ATL_MIN_CRT)
26 #ifdef _ATL_USE_DDX_FLOAT
28 #endif // _ATL_USE_DDX_FLOAT
31 ///////////////////////////////////////////////////////////////////////////////
32 // Classes in this file:
34 // CWinDataExchange<T>
41 #define DDX_LOAD FALSE
45 #define BEGIN_DDX_MAP(thisClass) \
46 BOOL DoDataExchange(BOOL bSaveAndValidate = FALSE, UINT nCtlID = (UINT)-1) \
51 #define DDX_TEXT(nID, var) \
52 if(nCtlID == (UINT)-1 || nCtlID == nID) \
54 if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate)) \
58 #define DDX_TEXT_LEN(nID, var, len) \
59 if(nCtlID == (UINT)-1 || nCtlID == nID) \
61 if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate, TRUE, len)) \
65 #define DDX_INT(nID, var) \
66 if(nCtlID == (UINT)-1 || nCtlID == nID) \
68 if(!DDX_Int(nID, var, TRUE, bSaveAndValidate)) \
72 #define DDX_INT_RANGE(nID, var, min, max) \
73 if(nCtlID == (UINT)-1 || nCtlID == nID) \
75 if(!DDX_Int(nID, var, TRUE, bSaveAndValidate, TRUE, min, max)) \
79 #define DDX_UINT(nID, var) \
80 if(nCtlID == (UINT)-1 || nCtlID == nID) \
82 if(!DDX_Int(nID, var, FALSE, bSaveAndValidate)) \
86 #define DDX_UINT_RANGE(nID, var, min, max) \
87 if(nCtlID == (UINT)-1 || nCtlID == nID) \
89 if(!DDX_Int(nID, var, FALSE, bSaveAndValidate, TRUE, min, max)) \
93 #ifdef _ATL_USE_DDX_FLOAT
94 #define DDX_FLOAT(nID, var) \
95 if(nCtlID == (UINT)-1 || nCtlID == nID) \
97 if(!DDX_Float(nID, var, bSaveAndValidate)) \
101 #define DDX_FLOAT_RANGE(nID, var, min, max) \
102 if(nCtlID == (UINT)-1 || nCtlID == nID) \
104 if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max)) \
107 #define DDX_FLOAT_P(nID, var, precision) \
108 if(nCtlID == (UINT)-1 || nCtlID == nID) \
110 if(!DDX_Float(nID, var, bSaveAndValidate, FALSE, 0, 0, precision)) \
114 #define DDX_FLOAT_P_RANGE(nID, var, min, max, precision) \
115 if(nCtlID == (UINT)-1 || nCtlID == nID) \
117 if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max, precision)) \
120 #endif // _ATL_USE_DDX_FLOAT
122 #define DDX_CONTROL(nID, obj) \
123 if(nCtlID == (UINT)-1 || nCtlID == nID) \
124 DDX_Control(nID, obj, bSaveAndValidate);
126 #define DDX_CONTROL_HANDLE(nID, obj) \
127 if(nCtlID == (UINT)-1 || nCtlID == nID) \
128 DDX_Control_Handle(nID, obj, bSaveAndValidate);
130 #define DDX_CHECK(nID, var) \
131 if(nCtlID == (UINT)-1 || nCtlID == nID) \
132 DDX_Check(nID, var, bSaveAndValidate);
134 #define DDX_RADIO(nID, var) \
135 if(nCtlID == (UINT)-1 || nCtlID == nID) \
136 DDX_Radio(nID, var, bSaveAndValidate);
138 #define END_DDX_MAP() \
143 ///////////////////////////////////////////////////////////////////////////////
144 // CWinDataExchange - provides support for DDX
147 class CWinDataExchange
150 // Data exchange method - override in your derived class
151 BOOL
DoDataExchange(BOOL
/*bSaveAndValidate*/ = FALSE
, UINT
/*nCtlID*/ = (UINT
)-1)
153 // this one should never be called, override it in
154 // your derived class by implementing DDX map
159 // Helpers for validation error reporting
191 _XDataType nDataType
;
196 _XFloatData floatData
;
201 BOOL
DDX_Text(UINT nID
, LPTSTR lpstrText
, int cbSize
, BOOL bSave
, BOOL bValidate
= FALSE
, int nLength
= 0)
203 T
* pT
= static_cast<T
*>(this);
204 BOOL bSuccess
= TRUE
;
208 HWND hWndCtrl
= pT
->GetDlgItem(nID
);
209 int nRetLen
= ::GetWindowText(hWndCtrl
, lpstrText
, cbSize
/ sizeof(TCHAR
));
210 if(nRetLen
< ::GetWindowTextLength(hWndCtrl
))
215 ATLASSERT(!bValidate
|| lstrlen(lpstrText
) <= nLength
);
216 bSuccess
= pT
->SetDlgItemText(nID
, lpstrText
);
221 pT
->OnDataExchangeError(nID
, bSave
);
223 else if(bSave
&& bValidate
) // validation
225 ATLASSERT(nLength
> 0);
226 if(lstrlen(lpstrText
) > nLength
)
228 _XData data
= { ddxDataText
};
229 data
.textData
.nLength
= lstrlen(lpstrText
);
230 data
.textData
.nMaxLength
= nLength
;
231 pT
->OnDataValidateError(nID
, bSave
, data
);
238 BOOL
DDX_Text(UINT nID
, BSTR
& bstrText
, int /*cbSize*/, BOOL bSave
, BOOL bValidate
= FALSE
, int nLength
= 0)
240 T
* pT
= static_cast<T
*>(this);
241 BOOL bSuccess
= TRUE
;
245 bSuccess
= pT
->GetDlgItemText(nID
, bstrText
);
250 LPTSTR lpstrText
= OLE2T(bstrText
);
251 ATLASSERT(!bValidate
|| lstrlen(lpstrText
) <= nLength
);
252 bSuccess
= pT
->SetDlgItemText(nID
, lpstrText
);
257 pT
->OnDataExchangeError(nID
, bSave
);
259 else if(bSave
&& bValidate
) // validation
261 ATLASSERT(nLength
> 0);
262 if((int)::SysStringLen(bstrText
) > nLength
)
264 _XData data
= { ddxDataText
};
265 data
.textData
.nLength
= (int)::SysStringLen(bstrText
);
266 data
.textData
.nMaxLength
= nLength
;
267 pT
->OnDataValidateError(nID
, bSave
, data
);
274 BOOL
DDX_Text(UINT nID
, ATL::CComBSTR
& bstrText
, int /*cbSize*/, BOOL bSave
, BOOL bValidate
= FALSE
, int nLength
= 0)
276 T
* pT
= static_cast<T
*>(this);
277 BOOL bSuccess
= TRUE
;
281 bSuccess
= pT
->GetDlgItemText(nID
, (BSTR
&)bstrText
);
286 LPTSTR lpstrText
= OLE2T(bstrText
);
287 ATLASSERT(!bValidate
|| lstrlen(lpstrText
) <= nLength
);
288 bSuccess
= pT
->SetDlgItemText(nID
, lpstrText
);
293 pT
->OnDataExchangeError(nID
, bSave
);
295 else if(bSave
&& bValidate
) // validation
297 ATLASSERT(nLength
> 0);
298 if((int)bstrText
.Length() > nLength
)
300 _XData data
= { ddxDataText
};
301 data
.textData
.nLength
= (int)bstrText
.Length();
302 data
.textData
.nMaxLength
= nLength
;
303 pT
->OnDataValidateError(nID
, bSave
, data
);
310 #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
311 BOOL
DDX_Text(UINT nID
, _CSTRING_NS::CString
& strText
, int /*cbSize*/, BOOL bSave
, BOOL bValidate
= FALSE
, int nLength
= 0)
313 T
* pT
= static_cast<T
*>(this);
314 BOOL bSuccess
= TRUE
;
318 HWND hWndCtrl
= pT
->GetDlgItem(nID
);
319 int nLen
= ::GetWindowTextLength(hWndCtrl
);
321 LPTSTR lpstr
= strText
.GetBufferSetLength(nLen
);
324 nRetLen
= ::GetWindowText(hWndCtrl
, lpstr
, nLen
+ 1);
325 strText
.ReleaseBuffer();
332 bSuccess
= pT
->SetDlgItemText(nID
, strText
);
337 pT
->OnDataExchangeError(nID
, bSave
);
339 else if(bSave
&& bValidate
) // validation
341 ATLASSERT(nLength
> 0);
342 if(strText
.GetLength() > nLength
)
344 _XData data
= { ddxDataText
};
345 data
.textData
.nLength
= strText
.GetLength();
346 data
.textData
.nMaxLength
= nLength
;
347 pT
->OnDataValidateError(nID
, bSave
, data
);
353 #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
356 template <class Type
>
357 BOOL
DDX_Int(UINT nID
, Type
& nVal
, BOOL bSigned
, BOOL bSave
, BOOL bValidate
= FALSE
, Type nMin
= 0, Type nMax
= 0)
359 T
* pT
= static_cast<T
*>(this);
360 BOOL bSuccess
= TRUE
;
364 nVal
= (Type
)pT
->GetDlgItemInt(nID
, &bSuccess
, bSigned
);
368 ATLASSERT(!bValidate
|| nVal
>= nMin
&& nVal
<= nMax
);
369 bSuccess
= pT
->SetDlgItemInt(nID
, nVal
, bSigned
);
374 pT
->OnDataExchangeError(nID
, bSave
);
376 else if(bSave
&& bValidate
) // validation
378 ATLASSERT(nMin
!= nMax
);
379 if(nVal
< nMin
|| nVal
> nMax
)
381 _XData data
= { ddxDataInt
};
382 data
.intData
.nVal
= (long)nVal
;
383 data
.intData
.nMin
= (long)nMin
;
384 data
.intData
.nMax
= (long)nMax
;
385 pT
->OnDataValidateError(nID
, bSave
, data
);
393 #ifdef _ATL_USE_DDX_FLOAT
394 static BOOL
_AtlSimpleFloatParse(LPCTSTR lpszText
, double& d
)
396 ATLASSERT(lpszText
!= NULL
);
397 while (*lpszText
== _T(' ') || *lpszText
== _T('\t'))
400 TCHAR chFirst
= lpszText
[0];
401 d
= _tcstod(lpszText
, (LPTSTR
*)&lpszText
);
402 if (d
== 0.0 && chFirst
!= _T('0'))
403 return FALSE
; // could not convert
404 while (*lpszText
== _T(' ') || *lpszText
== _T('\t'))
407 if (*lpszText
!= _T('\0'))
408 return FALSE
; // not terminated properly
413 BOOL
DDX_Float(UINT nID
, float& nVal
, BOOL bSave
, BOOL bValidate
= FALSE
, float nMin
= 0.F
, float nMax
= 0.F
, int nPrecision
= FLT_DIG
)
415 T
* pT
= static_cast<T
*>(this);
416 BOOL bSuccess
= TRUE
;
417 const int cchBuff
= 32;
418 TCHAR szBuff
[cchBuff
] = { 0 };
422 pT
->GetDlgItemText(nID
, szBuff
, cchBuff
);
424 if(_AtlSimpleFloatParse(szBuff
, d
))
431 ATLASSERT(!bValidate
|| nVal
>= nMin
&& nVal
<= nMax
);
432 SecureHelper::sprintf_x(szBuff
, cchBuff
, _T("%.*g"), nPrecision
, nVal
);
433 bSuccess
= pT
->SetDlgItemText(nID
, szBuff
);
438 pT
->OnDataExchangeError(nID
, bSave
);
440 else if(bSave
&& bValidate
) // validation
442 ATLASSERT(nMin
!= nMax
);
443 if(nVal
< nMin
|| nVal
> nMax
)
445 _XData data
= { ddxDataFloat
};
446 data
.floatData
.nVal
= (double)nVal
;
447 data
.floatData
.nMin
= (double)nMin
;
448 data
.floatData
.nMax
= (double)nMax
;
449 pT
->OnDataValidateError(nID
, bSave
, data
);
456 BOOL
DDX_Float(UINT nID
, double& nVal
, BOOL bSave
, BOOL bValidate
= FALSE
, double nMin
= 0., double nMax
= 0., int nPrecision
= DBL_DIG
)
458 T
* pT
= static_cast<T
*>(this);
459 BOOL bSuccess
= TRUE
;
460 const int cchBuff
= 32;
461 TCHAR szBuff
[cchBuff
] = { 0 };
465 pT
->GetDlgItemText(nID
, szBuff
, cchBuff
);
467 if(_AtlSimpleFloatParse(szBuff
, d
))
474 ATLASSERT(!bValidate
|| nVal
>= nMin
&& nVal
<= nMax
);
475 SecureHelper::sprintf_x(szBuff
, cchBuff
, _T("%.*g"), nPrecision
, nVal
);
476 bSuccess
= pT
->SetDlgItemText(nID
, szBuff
);
481 pT
->OnDataExchangeError(nID
, bSave
);
483 else if(bSave
&& bValidate
) // validation
485 ATLASSERT(nMin
!= nMax
);
486 if(nVal
< nMin
|| nVal
> nMax
)
488 _XData data
= { ddxDataFloat
};
489 data
.floatData
.nVal
= nVal
;
490 data
.floatData
.nMin
= nMin
;
491 data
.floatData
.nMax
= nMax
;
492 pT
->OnDataValidateError(nID
, bSave
, data
);
498 #endif // _ATL_USE_DDX_FLOAT
500 // Full control subclassing (for CWindowImpl derived controls)
501 template <class TControl
>
502 void DDX_Control(UINT nID
, TControl
& ctrl
, BOOL bSave
)
504 if(!bSave
&& ctrl
.m_hWnd
== NULL
)
506 T
* pT
= static_cast<T
*>(this);
507 ctrl
.SubclassWindow(pT
->GetDlgItem(nID
));
511 // Simple control attaching (for HWND wrapper controls)
512 template <class TControl
>
513 void DDX_Control_Handle(UINT nID
, TControl
& ctrl
, BOOL bSave
)
515 if(!bSave
&& ctrl
.m_hWnd
== NULL
)
517 T
* pT
= static_cast<T
*>(this);
518 ctrl
= pT
->GetDlgItem(nID
);
523 void DDX_Check(UINT nID
, int& nValue
, BOOL bSave
)
525 T
* pT
= static_cast<T
*>(this);
526 HWND hWndCtrl
= pT
->GetDlgItem(nID
);
529 nValue
= (int)::SendMessage(hWndCtrl
, BM_GETCHECK
, 0, 0L);
530 ATLASSERT(nValue
>= 0 && nValue
<= 2);
534 if(nValue
< 0 || nValue
> 2)
536 ATLTRACE2(atlTraceUI
, 0, _T("ATL: Warning - dialog data checkbox value (%d) out of range.\n"), nValue
);
537 nValue
= 0; // default to off
539 ::SendMessage(hWndCtrl
, BM_SETCHECK
, nValue
, 0L);
543 // variant that supports bool (checked/not-checked, no intermediate state)
544 void DDX_Check(UINT nID
, bool& bCheck
, BOOL bSave
)
546 int nValue
= bCheck
? 1 : 0;
547 DDX_Check(nID
, nValue
, bSave
);
552 ATLTRACE2(atlTraceUI
, 0, _T("ATL: Warning - checkbox state (%d) out of supported range.\n"), nValue
);
553 bCheck
= (nValue
== 1);
557 void DDX_Radio(UINT nID
, int& nValue
, BOOL bSave
)
559 T
* pT
= static_cast<T
*>(this);
560 HWND hWndCtrl
= pT
->GetDlgItem(nID
);
561 ATLASSERT(hWndCtrl
!= NULL
);
563 // must be first in a group of auto radio buttons
564 ATLASSERT(::GetWindowLong(hWndCtrl
, GWL_STYLE
) & WS_GROUP
);
565 ATLASSERT(::SendMessage(hWndCtrl
, WM_GETDLGCODE
, 0, 0L) & DLGC_RADIOBUTTON
);
568 nValue
= -1; // value if none found
570 // walk all children in group
574 if(::SendMessage(hWndCtrl
, WM_GETDLGCODE
, 0, 0L) & DLGC_RADIOBUTTON
)
576 // control in group is a radio button
579 if(::SendMessage(hWndCtrl
, BM_GETCHECK
, 0, 0L) != 0)
581 ATLASSERT(nValue
== -1); // only set once
588 ::SendMessage(hWndCtrl
, BM_SETCHECK
, (nButton
== nValue
), 0L);
594 ATLTRACE2(atlTraceUI
, 0, _T("ATL: Warning - skipping non-radio button in group.\n"));
596 hWndCtrl
= ::GetWindow(hWndCtrl
, GW_HWNDNEXT
);
598 while (hWndCtrl
!= NULL
&& !(GetWindowLong(hWndCtrl
, GWL_STYLE
) & WS_GROUP
));
602 void OnDataExchangeError(UINT nCtrlID
, BOOL
/*bSave*/)
604 // Override to display an error message
605 ::MessageBeep((UINT
)-1);
606 T
* pT
= static_cast<T
*>(this);
607 ::SetFocus(pT
->GetDlgItem(nCtrlID
));
610 void OnDataValidateError(UINT nCtrlID
, BOOL
/*bSave*/, _XData
& /*data*/)
612 // Override to display an error message
613 ::MessageBeep((UINT
)-1);
614 T
* pT
= static_cast<T
*>(this);
615 ::SetFocus(pT
->GetDlgItem(nCtrlID
));
621 #endif // __ATLDDX_H__