comparison foosdk/wtl/Include/atlddx.h @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
1 // Windows Template Library - WTL version 10.0
2 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.
3 //
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 Public License (http://opensource.org/licenses/MS-PL)
7 // which can be found in the file MS-PL.txt at the root folder.
8
9 #ifndef __ATLDDX_H__
10 #define __ATLDDX_H__
11
12 #pragma once
13
14 #ifndef __ATLAPP_H__
15 #error atlddx.h requires atlapp.h to be included first
16 #endif
17
18 #include <float.h>
19
20
21 ///////////////////////////////////////////////////////////////////////////////
22 // Classes in this file:
23 //
24 // CWinDataExchange<T>
25
26
27 namespace WTL
28 {
29
30 // Constants
31 #define DDX_LOAD FALSE
32 #define DDX_SAVE TRUE
33
34 // DDX map macros
35 #define BEGIN_DDX_MAP(thisClass) \
36 BOOL DoDataExchange(BOOL bSaveAndValidate = FALSE, UINT nCtlID = (UINT)-1) \
37 { \
38 (bSaveAndValidate); \
39 (nCtlID);
40
41 #define DDX_TEXT(nID, var) \
42 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
43 { \
44 if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate)) \
45 return FALSE; \
46 }
47
48 #define DDX_TEXT_LEN(nID, var, len) \
49 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
50 { \
51 if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate, TRUE, len)) \
52 return FALSE; \
53 }
54
55 #define DDX_INT(nID, var) \
56 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
57 { \
58 if(!DDX_Int(nID, var, TRUE, bSaveAndValidate)) \
59 return FALSE; \
60 }
61
62 #define DDX_INT_RANGE(nID, var, min, max) \
63 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
64 { \
65 if(!DDX_Int(nID, var, TRUE, bSaveAndValidate, TRUE, min, max)) \
66 return FALSE; \
67 }
68
69 #define DDX_UINT(nID, var) \
70 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
71 { \
72 if(!DDX_Int(nID, var, FALSE, bSaveAndValidate)) \
73 return FALSE; \
74 }
75
76 #define DDX_UINT_RANGE(nID, var, min, max) \
77 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
78 { \
79 if(!DDX_Int(nID, var, FALSE, bSaveAndValidate, TRUE, min, max)) \
80 return FALSE; \
81 }
82
83 #define DDX_FLOAT(nID, var) \
84 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
85 { \
86 if(!DDX_Float(nID, var, bSaveAndValidate)) \
87 return FALSE; \
88 }
89
90 #define DDX_FLOAT_RANGE(nID, var, min, max) \
91 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
92 { \
93 if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max)) \
94 return FALSE; \
95 }
96 #define DDX_FLOAT_P(nID, var, precision) \
97 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
98 { \
99 if(!DDX_Float(nID, var, bSaveAndValidate, FALSE, 0, 0, precision)) \
100 return FALSE; \
101 }
102
103 #define DDX_FLOAT_P_RANGE(nID, var, min, max, precision) \
104 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
105 { \
106 if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max, precision)) \
107 return FALSE; \
108 }
109
110 #define DDX_CONTROL(nID, obj) \
111 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
112 DDX_Control(nID, obj, bSaveAndValidate);
113
114 #define DDX_CONTROL_HANDLE(nID, obj) \
115 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
116 DDX_Control_Handle(nID, obj, bSaveAndValidate);
117
118 #define DDX_CHECK(nID, var) \
119 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
120 DDX_Check(nID, var, bSaveAndValidate);
121
122 #define DDX_RADIO(nID, var) \
123 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
124 DDX_Radio(nID, var, bSaveAndValidate);
125
126 #define END_DDX_MAP() \
127 return TRUE; \
128 }
129
130 // DDX support for Tab, Combo, ListBox and ListView selection index
131 // Note: Specialized versions require atlctrls.h to be included first
132
133 #define DDX_INDEX(CtrlClass, nID, var) \
134 if((nCtlID == (UINT)-1) || (nCtlID == nID)) \
135 DDX_Index<CtrlClass>(nID, var, bSaveAndValidate);
136
137 #ifdef __ATLCTRLS_H__
138 #define DDX_TAB_INDEX(nID, var) DDX_INDEX(WTL::CTabCtrl, nID, var)
139 #define DDX_COMBO_INDEX(nID, var) DDX_INDEX(WTL::CComboBox, nID, var)
140 #define DDX_LISTBOX_INDEX(nID, var) DDX_INDEX(WTL::CListBox, nID, var)
141 #define DDX_LISTVIEW_INDEX(nID, var) DDX_INDEX(WTL::CListViewCtrl, nID, var)
142 #endif // __ATLCTRLS_H__
143
144
145 ///////////////////////////////////////////////////////////////////////////////
146 // CWinDataExchange - provides support for DDX
147
148 template <class T>
149 class CWinDataExchange
150 {
151 public:
152 // Data exchange method - override in your derived class
153 BOOL DoDataExchange(BOOL /*bSaveAndValidate*/ = FALSE, UINT /*nCtlID*/ = (UINT)-1)
154 {
155 // this one should never be called, override it in
156 // your derived class by implementing DDX map
157 ATLASSERT(FALSE);
158 return FALSE;
159 }
160
161 // Helpers for validation error reporting
162 enum _XDataType
163 {
164 ddxDataNull = 0,
165 ddxDataText = 1,
166 ddxDataInt = 2,
167 ddxDataFloat = 3,
168 ddxDataDouble = 4
169 };
170
171 struct _XTextData
172 {
173 int nLength;
174 int nMaxLength;
175 };
176
177 struct _XIntData
178 {
179 long nVal;
180 long nMin;
181 long nMax;
182 };
183
184 struct _XFloatData
185 {
186 double nVal;
187 double nMin;
188 double nMax;
189 };
190
191 struct _XData
192 {
193 _XDataType nDataType;
194 union
195 {
196 _XTextData textData;
197 _XIntData intData;
198 _XFloatData floatData;
199 };
200 };
201
202 // Text exchange
203 BOOL DDX_Text(UINT nID, LPTSTR lpstrText, int cbSize, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)
204 {
205 T* pT = static_cast<T*>(this);
206 BOOL bSuccess = TRUE;
207
208 if(bSave)
209 {
210 HWND hWndCtrl = pT->GetDlgItem(nID);
211 int nRetLen = ::GetWindowText(hWndCtrl, lpstrText, cbSize / sizeof(TCHAR));
212 if(nRetLen < ::GetWindowTextLength(hWndCtrl))
213 bSuccess = FALSE;
214 }
215 else
216 {
217 ATLASSERT(!bValidate || (lstrlen(lpstrText) <= nLength));
218 bSuccess = pT->SetDlgItemText(nID, lpstrText);
219 }
220
221 if(!bSuccess)
222 {
223 pT->OnDataExchangeError(nID, bSave);
224 }
225 else if(bSave && bValidate) // validation
226 {
227 ATLASSERT(nLength > 0);
228 if(lstrlen(lpstrText) > nLength)
229 {
230 _XData data = { ddxDataText };
231 data.textData.nLength = lstrlen(lpstrText);
232 data.textData.nMaxLength = nLength;
233 pT->OnDataValidateError(nID, bSave, data);
234 bSuccess = FALSE;
235 }
236 }
237 return bSuccess;
238 }
239
240 BOOL DDX_Text(UINT nID, BSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)
241 {
242 T* pT = static_cast<T*>(this);
243 BOOL bSuccess = TRUE;
244
245 if(bSave)
246 {
247 bSuccess = pT->GetDlgItemText(nID, bstrText);
248 }
249 else
250 {
251 USES_CONVERSION;
252 LPTSTR lpstrText = OLE2T(bstrText);
253 ATLASSERT(!bValidate || (lstrlen(lpstrText) <= nLength));
254 bSuccess = pT->SetDlgItemText(nID, lpstrText);
255 }
256
257 if(!bSuccess)
258 {
259 pT->OnDataExchangeError(nID, bSave);
260 }
261 else if(bSave && bValidate) // validation
262 {
263 ATLASSERT(nLength > 0);
264 if((int)::SysStringLen(bstrText) > nLength)
265 {
266 _XData data = { ddxDataText };
267 data.textData.nLength = (int)::SysStringLen(bstrText);
268 data.textData.nMaxLength = nLength;
269 pT->OnDataValidateError(nID, bSave, data);
270 bSuccess = FALSE;
271 }
272 }
273 return bSuccess;
274 }
275
276 BOOL DDX_Text(UINT nID, ATL::CComBSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)
277 {
278 T* pT = static_cast<T*>(this);
279 BOOL bSuccess = TRUE;
280
281 if(bSave)
282 {
283 bSuccess = pT->GetDlgItemText(nID, (BSTR&)bstrText);
284 }
285 else
286 {
287 USES_CONVERSION;
288 LPTSTR lpstrText = OLE2T(bstrText);
289 ATLASSERT(!bValidate || (lstrlen(lpstrText) <= nLength));
290 bSuccess = pT->SetDlgItemText(nID, lpstrText);
291 }
292
293 if(!bSuccess)
294 {
295 pT->OnDataExchangeError(nID, bSave);
296 }
297 else if(bSave && bValidate) // validation
298 {
299 ATLASSERT(nLength > 0);
300 if((int)bstrText.Length() > nLength)
301 {
302 _XData data = { ddxDataText };
303 data.textData.nLength = (int)bstrText.Length();
304 data.textData.nMaxLength = nLength;
305 pT->OnDataValidateError(nID, bSave, data);
306 bSuccess = FALSE;
307 }
308 }
309 return bSuccess;
310 }
311
312 #ifdef __ATLSTR_H__
313 BOOL DDX_Text(UINT nID, ATL::CString& strText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0)
314 {
315 T* pT = static_cast<T*>(this);
316 BOOL bSuccess = TRUE;
317
318 if(bSave)
319 {
320 HWND hWndCtrl = pT->GetDlgItem(nID);
321 int nLen = ::GetWindowTextLength(hWndCtrl);
322 int nRetLen = -1;
323 LPTSTR lpstr = strText.GetBufferSetLength(nLen);
324 if(lpstr != NULL)
325 {
326 nRetLen = ::GetWindowText(hWndCtrl, lpstr, nLen + 1);
327 strText.ReleaseBuffer();
328 }
329 if(nRetLen < nLen)
330 bSuccess = FALSE;
331 }
332 else
333 {
334 bSuccess = pT->SetDlgItemText(nID, strText);
335 }
336
337 if(!bSuccess)
338 {
339 pT->OnDataExchangeError(nID, bSave);
340 }
341 else if(bSave && bValidate) // validation
342 {
343 ATLASSERT(nLength > 0);
344 if(strText.GetLength() > nLength)
345 {
346 _XData data = { ddxDataText };
347 data.textData.nLength = strText.GetLength();
348 data.textData.nMaxLength = nLength;
349 pT->OnDataValidateError(nID, bSave, data);
350 bSuccess = FALSE;
351 }
352 }
353 return bSuccess;
354 }
355 #endif // __ATLSTR_H__
356
357 // Numeric exchange
358 template <class Type>
359 BOOL DDX_Int(UINT nID, Type& nVal, BOOL bSigned, BOOL bSave, BOOL bValidate = FALSE, Type nMin = 0, Type nMax = 0)
360 {
361 T* pT = static_cast<T*>(this);
362 BOOL bSuccess = TRUE;
363
364 if(bSave)
365 {
366 nVal = (Type)pT->GetDlgItemInt(nID, &bSuccess, bSigned);
367 }
368 else
369 {
370 ATLASSERT(!bValidate || ((nVal >= nMin) && (nVal <= nMax)));
371 bSuccess = pT->SetDlgItemInt(nID, nVal, bSigned);
372 }
373
374 if(!bSuccess)
375 {
376 pT->OnDataExchangeError(nID, bSave);
377 }
378 else if(bSave && bValidate) // validation
379 {
380 ATLASSERT(nMin != nMax);
381 if((nVal < nMin) || (nVal > nMax))
382 {
383 _XData data = { ddxDataInt };
384 data.intData.nVal = (long)nVal;
385 data.intData.nMin = (long)nMin;
386 data.intData.nMax = (long)nMax;
387 pT->OnDataValidateError(nID, bSave, data);
388 bSuccess = FALSE;
389 }
390 }
391 return bSuccess;
392 }
393
394 // Float exchange
395 static BOOL _AtlSimpleFloatParse(LPCTSTR lpszText, double& d)
396 {
397 ATLASSERT(lpszText != NULL);
398 while ((*lpszText == _T(' ')) || (*lpszText == _T('\t')))
399 lpszText++;
400
401 TCHAR chFirst = lpszText[0];
402 d = _tcstod(lpszText, (LPTSTR*)&lpszText);
403 if ((d == 0.0) && (chFirst != _T('0')))
404 return FALSE; // could not convert
405 while ((*lpszText == _T(' ')) || (*lpszText == _T('\t')))
406 lpszText++;
407
408 if (*lpszText != _T('\0'))
409 return FALSE; // not terminated properly
410
411 return TRUE;
412 }
413
414 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 {
416 T* pT = static_cast<T*>(this);
417 BOOL bSuccess = TRUE;
418 const int cchBuff = 32;
419 TCHAR szBuff[cchBuff] = {};
420
421 if(bSave)
422 {
423 pT->GetDlgItemText(nID, szBuff, cchBuff);
424 double d = 0;
425 if(_AtlSimpleFloatParse(szBuff, d))
426 nVal = (float)d;
427 else
428 bSuccess = FALSE;
429 }
430 else
431 {
432 ATLASSERT(!bValidate || ((nVal >= nMin) && (nVal <= nMax)));
433 _stprintf_s(szBuff, cchBuff, _T("%.*g"), nPrecision, nVal);
434 bSuccess = pT->SetDlgItemText(nID, szBuff);
435 }
436
437 if(!bSuccess)
438 {
439 pT->OnDataExchangeError(nID, bSave);
440 }
441 else if(bSave && bValidate) // validation
442 {
443 ATLASSERT(nMin != nMax);
444 if((nVal < nMin) || (nVal > nMax))
445 {
446 _XData data = { ddxDataFloat };
447 data.floatData.nVal = (double)nVal;
448 data.floatData.nMin = (double)nMin;
449 data.floatData.nMax = (double)nMax;
450 pT->OnDataValidateError(nID, bSave, data);
451 bSuccess = FALSE;
452 }
453 }
454 return bSuccess;
455 }
456
457 BOOL DDX_Float(UINT nID, double& nVal, BOOL bSave, BOOL bValidate = FALSE, double nMin = 0., double nMax = 0., int nPrecision = DBL_DIG)
458 {
459 T* pT = static_cast<T*>(this);
460 BOOL bSuccess = TRUE;
461 const int cchBuff = 32;
462 TCHAR szBuff[cchBuff] = {};
463
464 if(bSave)
465 {
466 pT->GetDlgItemText(nID, szBuff, cchBuff);
467 double d = 0;
468 if(_AtlSimpleFloatParse(szBuff, d))
469 nVal = d;
470 else
471 bSuccess = FALSE;
472 }
473 else
474 {
475 ATLASSERT(!bValidate || ((nVal >= nMin) && (nVal <= nMax)));
476 _stprintf_s(szBuff, cchBuff, _T("%.*g"), nPrecision, nVal);
477 bSuccess = pT->SetDlgItemText(nID, szBuff);
478 }
479
480 if(!bSuccess)
481 {
482 pT->OnDataExchangeError(nID, bSave);
483 }
484 else if(bSave && bValidate) // validation
485 {
486 ATLASSERT(nMin != nMax);
487 if((nVal < nMin) || (nVal > nMax))
488 {
489 _XData data = { ddxDataFloat };
490 data.floatData.nVal = nVal;
491 data.floatData.nMin = nMin;
492 data.floatData.nMax = nMax;
493 pT->OnDataValidateError(nID, bSave, data);
494 bSuccess = FALSE;
495 }
496 }
497 return bSuccess;
498 }
499
500 // Full control subclassing (for CWindowImpl derived controls)
501 template <class TControl>
502 void DDX_Control(UINT nID, TControl& ctrl, BOOL bSave)
503 {
504 if(!bSave && (ctrl.m_hWnd == NULL))
505 {
506 T* pT = static_cast<T*>(this);
507 ctrl.SubclassWindow(pT->GetDlgItem(nID));
508 }
509 }
510
511 // Simple control attaching (for HWND wrapper controls)
512 template <class TControl>
513 void DDX_Control_Handle(UINT nID, TControl& ctrl, BOOL bSave)
514 {
515 if(!bSave && (ctrl.m_hWnd == NULL))
516 {
517 T* pT = static_cast<T*>(this);
518 ctrl = pT->GetDlgItem(nID);
519 }
520 }
521
522 // Control state
523 void DDX_Check(UINT nID, int& nValue, BOOL bSave)
524 {
525 T* pT = static_cast<T*>(this);
526 HWND hWndCtrl = pT->GetDlgItem(nID);
527 if(bSave)
528 {
529 nValue = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L);
530 ATLASSERT((nValue >= 0) && (nValue <= 2));
531 }
532 else
533 {
534 if((nValue < 0) || (nValue > 2))
535 {
536 ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - dialog data checkbox value (%d) out of range.\n"), nValue);
537 nValue = 0; // default to off
538 }
539 ::SendMessage(hWndCtrl, BM_SETCHECK, nValue, 0L);
540 }
541 }
542
543 // variant that supports bool (checked/not-checked, no intermediate state)
544 void DDX_Check(UINT nID, bool& bCheck, BOOL bSave)
545 {
546 int nValue = bCheck ? 1 : 0;
547 DDX_Check(nID, nValue, bSave);
548
549 if(bSave)
550 {
551 if(nValue == 2)
552 ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - checkbox state (%d) out of supported range.\n"), nValue);
553 bCheck = (nValue == 1);
554 }
555 }
556
557 void DDX_Radio(UINT nID, int& nValue, BOOL bSave)
558 {
559 T* pT = static_cast<T*>(this);
560 HWND hWndCtrl = pT->GetDlgItem(nID);
561 ATLASSERT(hWndCtrl != NULL);
562
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);
566
567 if(bSave)
568 nValue = -1; // value if none found
569
570 // walk all children in group
571 int nButton = 0;
572 do
573 {
574 if(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)
575 {
576 // control in group is a radio button
577 if(bSave)
578 {
579 if(::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0)
580 {
581 ATLASSERT(nValue == -1); // only set once
582 nValue = nButton;
583 }
584 }
585 else
586 {
587 // select button
588 ::SendMessage(hWndCtrl, BM_SETCHECK, (nButton == nValue), 0L);
589 }
590 nButton++;
591 }
592 else
593 {
594 ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - skipping non-radio button in group.\n"));
595 }
596 hWndCtrl = ::GetWindow(hWndCtrl, GW_HWNDNEXT);
597 }
598 while ((hWndCtrl != NULL) && !(GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP));
599 }
600
601 // DDX support for Tab, Combo, ListBox and ListView selection index
602 template <class TCtrl>
603 INT _getSel(TCtrl& tCtrl)
604 {
605 return tCtrl.GetCurSel();
606 }
607
608 template <class TCtrl>
609 void _setSel(TCtrl& tCtrl, INT iSel)
610 {
611 if(iSel < 0)
612 tCtrl.SetCurSel(-1);
613 else
614 tCtrl.SetCurSel(iSel);
615 }
616
617 #ifdef __ATLCTRLS_H__
618 // ListViewCtrl specialization
619 template <>
620 INT _getSel(WTL::CListViewCtrl& tCtrl)
621 {
622 return tCtrl.GetSelectedIndex();
623 }
624
625 template <>
626 void _setSel(WTL::CListViewCtrl& tCtrl, INT iSel)
627 {
628 if(iSel < 0)
629 tCtrl.SelectItem(-1);
630 else
631 tCtrl.SelectItem(iSel);
632 }
633 #endif // __ATLCTRLS_H__
634
635 template <class TCtrl>
636 void DDX_Index(UINT nID, INT& nVal, BOOL bSave)
637 {
638 T* pT = static_cast<T*>(this);
639 TCtrl ctrl(pT->GetDlgItem(nID));
640
641 if(bSave)
642 nVal = _getSel(ctrl);
643 else
644 _setSel(ctrl, nVal);
645 }
646
647 // Overrideables
648 void OnDataExchangeError(UINT nCtrlID, BOOL /*bSave*/)
649 {
650 // Override to display an error message
651 ::MessageBeep((UINT)-1);
652 T* pT = static_cast<T*>(this);
653 ::SetFocus(pT->GetDlgItem(nCtrlID));
654 }
655
656 void OnDataValidateError(UINT nCtrlID, BOOL /*bSave*/, _XData& /*data*/)
657 {
658 // Override to display an error message
659 ::MessageBeep((UINT)-1);
660 T* pT = static_cast<T*>(this);
661 ::SetFocus(pT->GetDlgItem(nCtrlID));
662 }
663 };
664
665 } // namespace WTL
666
667 #endif // __ATLDDX_H__