]> git.sesse.net Git - casparcg/blob - WTL80/include/atlddx.h
2.0.2: INFO TEMPLATE works on both compressed and uncompressed templates.
[casparcg] / WTL80 / include / atlddx.h
1 // Windows Template Library - WTL version 8.0\r
2 // Copyright (C) Microsoft Corporation. All rights reserved.\r
3 //\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
11 \r
12 #ifndef __ATLDDX_H__\r
13 #define __ATLDDX_H__\r
14 \r
15 #pragma once\r
16 \r
17 #ifndef __cplusplus\r
18         #error ATL requires C++ compilation (use a .cpp suffix)\r
19 #endif\r
20 \r
21 #ifndef __ATLAPP_H__\r
22         #error atlddx.h requires atlapp.h to be included first\r
23 #endif\r
24 \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
28 \r
29 #ifdef _ATL_USE_DDX_FLOAT\r
30   #include <float.h>\r
31 #endif // _ATL_USE_DDX_FLOAT\r
32 \r
33 \r
34 ///////////////////////////////////////////////////////////////////////////////\r
35 // Classes in this file:\r
36 //\r
37 // CWinDataExchange<T>\r
38 \r
39 \r
40 namespace WTL\r
41 {\r
42 \r
43 // Constants\r
44 #define DDX_LOAD        FALSE\r
45 #define DDX_SAVE        TRUE\r
46 \r
47 // DDX map macros\r
48 #define BEGIN_DDX_MAP(thisClass) \\r
49         BOOL DoDataExchange(BOOL bSaveAndValidate = FALSE, UINT nCtlID = (UINT)-1) \\r
50         { \\r
51                 bSaveAndValidate; \\r
52                 nCtlID;\r
53 \r
54 #define DDX_TEXT(nID, var) \\r
55                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
56                 { \\r
57                         if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate)) \\r
58                                 return FALSE; \\r
59                 }\r
60 \r
61 #define DDX_TEXT_LEN(nID, var, len) \\r
62                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
63                 { \\r
64                         if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate, TRUE, len)) \\r
65                                 return FALSE; \\r
66                 }\r
67 \r
68 #define DDX_INT(nID, var) \\r
69                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
70                 { \\r
71                         if(!DDX_Int(nID, var, TRUE, bSaveAndValidate)) \\r
72                                 return FALSE; \\r
73                 }\r
74 \r
75 #define DDX_INT_RANGE(nID, var, min, max) \\r
76                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
77                 { \\r
78                         if(!DDX_Int(nID, var, TRUE, bSaveAndValidate, TRUE, min, max)) \\r
79                                 return FALSE; \\r
80                 }\r
81 \r
82 #define DDX_UINT(nID, var) \\r
83                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
84                 { \\r
85                         if(!DDX_Int(nID, var, FALSE, bSaveAndValidate)) \\r
86                                 return FALSE; \\r
87                 }\r
88 \r
89 #define DDX_UINT_RANGE(nID, var, min, max) \\r
90                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
91                 { \\r
92                         if(!DDX_Int(nID, var, FALSE, bSaveAndValidate, TRUE, min, max)) \\r
93                                 return FALSE; \\r
94                 }\r
95 \r
96 #ifdef _ATL_USE_DDX_FLOAT\r
97 #define DDX_FLOAT(nID, var) \\r
98                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
99                 { \\r
100                         if(!DDX_Float(nID, var, bSaveAndValidate)) \\r
101                                 return FALSE; \\r
102                 }\r
103 \r
104 #define DDX_FLOAT_RANGE(nID, var, min, max) \\r
105                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
106                 { \\r
107                         if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max)) \\r
108                                 return FALSE; \\r
109                 }\r
110 #define DDX_FLOAT_P(nID, var, precision) \\r
111                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
112                 { \\r
113                         if(!DDX_Float(nID, var, bSaveAndValidate, FALSE, 0, 0, precision)) \\r
114                                 return FALSE; \\r
115                 }\r
116 \r
117 #define DDX_FLOAT_P_RANGE(nID, var, min, max, precision) \\r
118                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
119                 { \\r
120                         if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max, precision)) \\r
121                                 return FALSE; \\r
122                 }\r
123 #endif // _ATL_USE_DDX_FLOAT\r
124 \r
125 #define DDX_CONTROL(nID, obj) \\r
126                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
127                         DDX_Control(nID, obj, bSaveAndValidate);\r
128 \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
132 \r
133 #define DDX_CHECK(nID, var) \\r
134                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
135                         DDX_Check(nID, var, bSaveAndValidate);\r
136 \r
137 #define DDX_RADIO(nID, var) \\r
138                 if(nCtlID == (UINT)-1 || nCtlID == nID) \\r
139                         DDX_Radio(nID, var, bSaveAndValidate);\r
140 \r
141 #define END_DDX_MAP() \\r
142                 return TRUE; \\r
143         }\r
144 \r
145 \r
146 ///////////////////////////////////////////////////////////////////////////////\r
147 // CWinDataExchange - provides support for DDX\r
148 \r
149 template <class T>\r
150 class CWinDataExchange\r
151 {\r
152 public:\r
153 // Data exchange method - override in your derived class\r
154         BOOL DoDataExchange(BOOL /*bSaveAndValidate*/ = FALSE, UINT /*nCtlID*/ = (UINT)-1)\r
155         {\r
156                 // this one should never be called, override it in\r
157                 // your derived class by implementing DDX map\r
158                 ATLASSERT(FALSE);\r
159                 return FALSE;\r
160         }\r
161 \r
162 // Helpers for validation error reporting\r
163         enum _XDataType\r
164         {\r
165                 ddxDataNull = 0,\r
166                 ddxDataText = 1,\r
167                 ddxDataInt = 2,\r
168                 ddxDataFloat = 3,\r
169                 ddxDataDouble = 4\r
170         };\r
171 \r
172         struct _XTextData\r
173         {\r
174                 int nLength;\r
175                 int nMaxLength;\r
176         };\r
177 \r
178         struct _XIntData\r
179         {\r
180                 long nVal;\r
181                 long nMin;\r
182                 long nMax;\r
183         };\r
184 \r
185         struct _XFloatData\r
186         {\r
187                 double nVal;\r
188                 double nMin;\r
189                 double nMax;\r
190         };\r
191 \r
192         struct _XData\r
193         {\r
194                 _XDataType nDataType;\r
195                 union\r
196                 {\r
197                         _XTextData textData;\r
198                         _XIntData intData;\r
199                         _XFloatData floatData;\r
200                 };\r
201         };\r
202 \r
203 // Text exchange\r
204         BOOL DDX_Text(UINT nID, LPTSTR lpstrText, int cbSize, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)\r
205         {\r
206                 T* pT = static_cast<T*>(this);\r
207                 BOOL bSuccess = TRUE;\r
208 \r
209                 if(bSave)\r
210                 {\r
211                         HWND hWndCtrl = pT->GetDlgItem(nID);\r
212                         int nRetLen = ::GetWindowText(hWndCtrl, lpstrText, cbSize / sizeof(TCHAR));\r
213                         if(nRetLen < ::GetWindowTextLength(hWndCtrl))\r
214                                 bSuccess = FALSE;\r
215                 }\r
216                 else\r
217                 {\r
218                         ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength);\r
219                         bSuccess = pT->SetDlgItemText(nID, lpstrText);\r
220                 }\r
221 \r
222                 if(!bSuccess)\r
223                 {\r
224                         pT->OnDataExchangeError(nID, bSave);\r
225                 }\r
226                 else if(bSave && bValidate)   // validation\r
227                 {\r
228                         ATLASSERT(nLength > 0);\r
229                         if(lstrlen(lpstrText) > nLength)\r
230                         {\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
235                                 bSuccess = FALSE;\r
236                         }\r
237                 }\r
238                 return bSuccess;\r
239         }\r
240 \r
241         BOOL DDX_Text(UINT nID, BSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)\r
242         {\r
243                 T* pT = static_cast<T*>(this);\r
244                 BOOL bSuccess = TRUE;\r
245 \r
246                 if(bSave)\r
247                 {\r
248                         bSuccess = pT->GetDlgItemText(nID, bstrText);\r
249                 }\r
250                 else\r
251                 {\r
252                         USES_CONVERSION;\r
253                         LPTSTR lpstrText = OLE2T(bstrText);\r
254                         ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength);\r
255                         bSuccess = pT->SetDlgItemText(nID, lpstrText);\r
256                 }\r
257 \r
258                 if(!bSuccess)\r
259                 {\r
260                         pT->OnDataExchangeError(nID, bSave);\r
261                 }\r
262                 else if(bSave && bValidate)   // validation\r
263                 {\r
264                         ATLASSERT(nLength > 0);\r
265                         if((int)::SysStringLen(bstrText) > nLength)\r
266                         {\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
271                                 bSuccess = FALSE;\r
272                         }\r
273                 }\r
274                 return bSuccess;\r
275         }\r
276 \r
277         BOOL DDX_Text(UINT nID, ATL::CComBSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)\r
278         {\r
279                 T* pT = static_cast<T*>(this);\r
280                 BOOL bSuccess = TRUE;\r
281 \r
282                 if(bSave)\r
283                 {\r
284                         bSuccess = pT->GetDlgItemText(nID, (BSTR&)bstrText);\r
285                 }\r
286                 else\r
287                 {\r
288                         USES_CONVERSION;\r
289                         LPTSTR lpstrText = OLE2T(bstrText);\r
290                         ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength);\r
291                         bSuccess = pT->SetDlgItemText(nID, lpstrText);\r
292                 }\r
293 \r
294                 if(!bSuccess)\r
295                 {\r
296                         pT->OnDataExchangeError(nID, bSave);\r
297                 }\r
298                 else if(bSave && bValidate)   // validation\r
299                 {\r
300                         ATLASSERT(nLength > 0);\r
301                         if((int)bstrText.Length() > nLength)\r
302                         {\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
307                                 bSuccess = FALSE;\r
308                         }\r
309                 }\r
310                 return bSuccess;\r
311         }\r
312 \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
315         {\r
316                 T* pT = static_cast<T*>(this);\r
317                 BOOL bSuccess = TRUE;\r
318 \r
319                 if(bSave)\r
320                 {\r
321                         HWND hWndCtrl = pT->GetDlgItem(nID);\r
322                         int nLen = ::GetWindowTextLength(hWndCtrl);\r
323                         int nRetLen = -1;\r
324                         LPTSTR lpstr = strText.GetBufferSetLength(nLen);\r
325                         if(lpstr != NULL)\r
326                         {\r
327                                 nRetLen = ::GetWindowText(hWndCtrl, lpstr, nLen + 1);\r
328                                 strText.ReleaseBuffer();\r
329                         }\r
330                         if(nRetLen < nLen)\r
331                                 bSuccess = FALSE;\r
332                 }\r
333                 else\r
334                 {\r
335                         bSuccess = pT->SetDlgItemText(nID, strText);\r
336                 }\r
337 \r
338                 if(!bSuccess)\r
339                 {\r
340                         pT->OnDataExchangeError(nID, bSave);\r
341                 }\r
342                 else if(bSave && bValidate)   // validation\r
343                 {\r
344                         ATLASSERT(nLength > 0);\r
345                         if(strText.GetLength() > nLength)\r
346                         {\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
351                                 bSuccess = FALSE;\r
352                         }\r
353                 }\r
354                 return bSuccess;\r
355         }\r
356 #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)\r
357 \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
361         {\r
362                 T* pT = static_cast<T*>(this);\r
363                 BOOL bSuccess = TRUE;\r
364 \r
365                 if(bSave)\r
366                 {\r
367                         nVal = (Type)pT->GetDlgItemInt(nID, &bSuccess, bSigned);\r
368                 }\r
369                 else\r
370                 {\r
371                         ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax);\r
372                         bSuccess = pT->SetDlgItemInt(nID, nVal, bSigned);\r
373                 }\r
374 \r
375                 if(!bSuccess)\r
376                 {\r
377                         pT->OnDataExchangeError(nID, bSave);\r
378                 }\r
379                 else if(bSave && bValidate)   // validation\r
380                 {\r
381                         ATLASSERT(nMin != nMax);\r
382                         if(nVal < nMin || nVal > nMax)\r
383                         {\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
389                                 bSuccess = FALSE;\r
390                         }\r
391                 }\r
392                 return bSuccess;\r
393         }\r
394 \r
395 // Float exchange\r
396 #ifdef _ATL_USE_DDX_FLOAT\r
397         static BOOL _AtlSimpleFloatParse(LPCTSTR lpszText, double& d)\r
398         {\r
399                 ATLASSERT(lpszText != NULL);\r
400                 while (*lpszText == _T(' ') || *lpszText == _T('\t'))\r
401                         lpszText++;\r
402 \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
408                         lpszText++;\r
409 \r
410                 if (*lpszText != _T('\0'))\r
411                         return FALSE;   // not terminated properly\r
412 \r
413                 return TRUE;\r
414         }\r
415 \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
417         {\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
422 \r
423                 if(bSave)\r
424                 {\r
425                         pT->GetDlgItemText(nID, szBuff, cchBuff);\r
426                         double d = 0;\r
427                         if(_AtlSimpleFloatParse(szBuff, d))\r
428                                 nVal = (float)d;\r
429                         else\r
430                                 bSuccess = FALSE;\r
431                 }\r
432                 else\r
433                 {\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
437                 }\r
438 \r
439                 if(!bSuccess)\r
440                 {\r
441                         pT->OnDataExchangeError(nID, bSave);\r
442                 }\r
443                 else if(bSave && bValidate)   // validation\r
444                 {\r
445                         ATLASSERT(nMin != nMax);\r
446                         if(nVal < nMin || nVal > nMax)\r
447                         {\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
453                                 bSuccess = FALSE;\r
454                         }\r
455                 }\r
456                 return bSuccess;\r
457         }\r
458 \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
460         {\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
465 \r
466                 if(bSave)\r
467                 {\r
468                         pT->GetDlgItemText(nID, szBuff, cchBuff);\r
469                         double d = 0;\r
470                         if(_AtlSimpleFloatParse(szBuff, d))\r
471                                 nVal = d;\r
472                         else\r
473                                 bSuccess = FALSE;\r
474                 }\r
475                 else\r
476                 {\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
480                 }\r
481 \r
482                 if(!bSuccess)\r
483                 {\r
484                         pT->OnDataExchangeError(nID, bSave);\r
485                 }\r
486                 else if(bSave && bValidate)   // validation\r
487                 {\r
488                         ATLASSERT(nMin != nMax);\r
489                         if(nVal < nMin || nVal > nMax)\r
490                         {\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
496                                 bSuccess = FALSE;\r
497                         }\r
498                 }\r
499                 return bSuccess;\r
500         }\r
501 #endif // _ATL_USE_DDX_FLOAT\r
502 \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
506         {\r
507                 if(!bSave && ctrl.m_hWnd == NULL)\r
508                 {\r
509                         T* pT = static_cast<T*>(this);\r
510                         ctrl.SubclassWindow(pT->GetDlgItem(nID));\r
511                 }\r
512         }\r
513 \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
517         {\r
518                 if(!bSave && ctrl.m_hWnd == NULL)\r
519                 {\r
520                         T* pT = static_cast<T*>(this);\r
521                         ctrl = pT->GetDlgItem(nID);\r
522                 }\r
523         }\r
524 \r
525 // Control state\r
526         void DDX_Check(UINT nID, int& nValue, BOOL bSave)\r
527         {\r
528                 T* pT = static_cast<T*>(this);\r
529                 HWND hWndCtrl = pT->GetDlgItem(nID);\r
530                 if(bSave)\r
531                 {\r
532                         nValue = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L);\r
533                         ATLASSERT(nValue >= 0 && nValue <= 2);\r
534                 }\r
535                 else\r
536                 {\r
537                         if(nValue < 0 || nValue > 2)\r
538                         {\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
541                         }\r
542                         ::SendMessage(hWndCtrl, BM_SETCHECK, nValue, 0L);\r
543                 }\r
544         }\r
545 \r
546         // variant that supports bool (checked/not-checked, no intermediate state)\r
547         void DDX_Check(UINT nID, bool& bCheck, BOOL bSave)\r
548         {\r
549                 int nValue = bCheck ? 1 : 0;\r
550                 DDX_Check(nID, nValue, bSave);\r
551 \r
552                 if(bSave)\r
553                 {\r
554                         if(nValue == 2)\r
555                                 ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - checkbox state (%d) out of supported range.\n"), nValue);\r
556                         bCheck = (nValue == 1);\r
557                 }\r
558         }\r
559 \r
560         void DDX_Radio(UINT nID, int& nValue, BOOL bSave)\r
561         {\r
562                 T* pT = static_cast<T*>(this);\r
563                 HWND hWndCtrl = pT->GetDlgItem(nID);\r
564                 ATLASSERT(hWndCtrl != NULL);\r
565 \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
569 \r
570                 if(bSave)\r
571                         nValue = -1;     // value if none found\r
572 \r
573                 // walk all children in group\r
574                 int nButton = 0;\r
575                 do\r
576                 {\r
577                         if(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)\r
578                         {\r
579                                 // control in group is a radio button\r
580                                 if(bSave)\r
581                                 {\r
582                                         if(::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0)\r
583                                         {\r
584                                                 ATLASSERT(nValue == -1);    // only set once\r
585                                                 nValue = nButton;\r
586                                         }\r
587                                 }\r
588                                 else\r
589                                 {\r
590                                         // select button\r
591                                         ::SendMessage(hWndCtrl, BM_SETCHECK, (nButton == nValue), 0L);\r
592                                 }\r
593                                 nButton++;\r
594                         }\r
595                         else\r
596                         {\r
597                                 ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - skipping non-radio button in group.\n"));\r
598                         }\r
599                         hWndCtrl = ::GetWindow(hWndCtrl, GW_HWNDNEXT);\r
600                 }\r
601                 while (hWndCtrl != NULL && !(GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP));\r
602         }\r
603 \r
604 // Overrideables\r
605         void OnDataExchangeError(UINT nCtrlID, BOOL /*bSave*/)\r
606         {\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
611         }\r
612 \r
613         void OnDataValidateError(UINT nCtrlID, BOOL /*bSave*/, _XData& /*data*/)\r
614         {\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
619         }\r
620 };\r
621 \r
622 }; // namespace WTL\r
623 \r
624 #endif // __ATLDDX_H__\r