|
|
1.1 root 1: // This is a part of the Microsoft Foundation Classes C++ library.
2: // Copyright (C) 1992 Microsoft Corporation
3: // All rights reserved.
4:
5: // This source code is only intended as a supplement to the
6: // Microsoft Foundation Classes Reference and Microsoft
7: // QuickHelp and/or WinHelp documentation provided with the library.
8: // See these sources for detailed information regarding the
9: // Microsoft Foundation Classes product.
10:
11: #include "stdafx.h"
12:
13: #ifdef AFX_CORE3_SEG
14: #pragma code_seg(AFX_CORE3_SEG)
15: #endif
16:
17: #ifdef _DEBUG
18: #undef THIS_FILE
19: static char BASED_CODE THIS_FILE[] = __FILE__;
20: #endif
21:
22: /////////////////////////////////////////////////////////////////////////////
23: // CDataExchange member functions (contructor is in wincore.cpp for swap tuning)
24:
25: HWND CDataExchange::PrepareEditCtrl(int nIDC)
26: {
27: HWND hWndCtrl = PrepareCtrl(nIDC);
28: ASSERT(hWndCtrl != NULL);
29: m_bEditLastControl = TRUE;
30: return hWndCtrl;
31: }
32:
33: HWND CDataExchange::PrepareCtrl(int nIDC)
34: {
35: ASSERT(nIDC != 0);
36: ASSERT(nIDC != -1); // not allowed
37: HWND hWndCtrl = ::GetDlgItem(m_pDlgWnd->m_hWnd, nIDC);
38: if (hWndCtrl == NULL)
39: {
40: TRACE1("Error: no data exchange control with ID 0x%04X\n", nIDC);
41: ASSERT(FALSE);
42: AfxThrowNotSupportedException();
43: }
44: m_hWndLastControl = hWndCtrl;
45: m_bEditLastControl = FALSE; // not an edit item by default
46: ASSERT(hWndCtrl != NULL); // never return NULL handle
47: return hWndCtrl;
48: }
49:
50: void CDataExchange::Fail()
51: {
52: if (!m_bSaveAndValidate)
53: {
54: TRACE0("Warning: CDataExchange::Fail called when not validating\n");
55: // throw the exception anyway
56: }
57: else if (m_hWndLastControl != NULL)
58: {
59: // restore focus and selection to offending field
60: ::SetFocus(m_hWndLastControl);
61: if (m_bEditLastControl) // select edit item
62: ::SendMessage(m_hWndLastControl, EM_SETSEL, 0, -1);
63: }
64: else
65: {
66: TRACE0("Error: fail validation with no control to restore focus to\n");
67: // do nothing more
68: }
69:
70: AfxThrowUserException();
71: }
72:
73: /////////////////////////////////////////////////////////////////////////////
74: // Notes for implementing dialog data exchange and validation procs:
75: // * always start with PrepareCtrl or PrepareEditCtrl
76: // * always start with 'pDX->m_bSaveAndValidate' check
77: // * pDX->Fail() will throw an exception - so be prepared
78: // * try to avoid creating temporary HWNDs for dialog controls - i.e.
79: // use HWNDs for child elements
80: // * validation procs should only act if 'm_bSaveAndValidate'
81: // * use the suffices:
82: // DDX_ = exchange proc
83: // DDV_ = validation proc
84: //
85: /////////////////////////////////////////////////////////////////////////////
86:
87: // only supports '%d', '%u', '%ld' and '%lu'
88: static BOOL PASCAL NEAR _AfxSimpleScanf(const char* pszText,
89: const char* pszFormat, va_list pData)
90: {
91: ASSERT(pszText != NULL);
92: ASSERT(pszFormat != NULL);
93:
94: ASSERT(*pszFormat == '%');
95: pszFormat++; // skip '%'
96:
97: BOOL bLong = FALSE;
98: if (*pszFormat == 'l')
99: {
100: bLong = TRUE;
101: pszFormat++;
102: }
103:
104: ASSERT(*pszFormat == 'd' || *pszFormat == 'u');
105: ASSERT(pszFormat[1] == '\0');
106:
107: while (*pszText == ' ' || *pszText == '\t')
108: pszText++;
109: char chFirst = pszText[0];
110: long l, l2;
111: if (*pszFormat == 'd')
112: {
113: // signed
114: l = strtol(pszText, (char**)&pszText, 10);
115: l2 = (int)l;
116: }
117: else
118: {
119: // unsigned
120: l = (long)strtoul(pszText, (char**)&pszText, 10);
121: l2 = (unsigned int)l;
122: }
123: if (l == 0 && chFirst != '0')
124: return FALSE; // could not convert
125:
126: while (*pszText == ' ' || *pszText == '\t')
127: pszText++;
128: if (*pszText != '\0')
129: return FALSE; // not terminated properly
130:
131: if (bLong)
132: *va_arg(pData, long*) = l;
133: else if (l == l2)
134: *va_arg(pData, int*) = (int)l;
135: else
136: return FALSE; // too big for int
137:
138: // all ok
139: return TRUE;
140: }
141:
142:
143: static void PASCAL NEAR DDX_TextWithFormat(CDataExchange* pDX, int nIDC,
144: const char* pszFormat, UINT nIDPrompt, ...)
145: // only supports windows output formats - no floating point
146: {
147: va_list pData;
148: va_start(pData, nIDPrompt);
149:
150: HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
151: char szT[64];
152: if (pDX->m_bSaveAndValidate)
153: {
154: // the following works for %d, %u, %ld, %lu
155: ::GetWindowText(hWndCtrl, szT, sizeof(szT));
156: if (!_AfxSimpleScanf(szT, pszFormat, pData))
157: {
158: AfxMessageBox(nIDPrompt);
159: pDX->Fail(); // throws exception
160: }
161: }
162: else
163: {
164: wvsprintf(szT, pszFormat, pData);
165: // does not support floating point numbers - see dlgfloat.cpp
166: _AfxSmartSetWindowText(hWndCtrl, szT);
167: }
168:
169: va_end(pData);
170: }
171:
172: /////////////////////////////////////////////////////////////////////////////
173: // Simple formatting to text item
174:
175: void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, int& value)
176: {
177: if (pDX->m_bSaveAndValidate)
178: DDX_TextWithFormat(pDX, nIDC, "%d", AFX_IDP_PARSE_INT, &value);
179: else
180: DDX_TextWithFormat(pDX, nIDC, "%d", AFX_IDP_PARSE_INT, value);
181: }
182:
183: void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, UINT& value)
184: {
185: if (pDX->m_bSaveAndValidate)
186: DDX_TextWithFormat(pDX, nIDC, "%u", AFX_IDP_PARSE_INT, &value);
187: else
188: DDX_TextWithFormat(pDX, nIDC, "%u", AFX_IDP_PARSE_INT, value);
189: }
190:
191: void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, long& value)
192: {
193: if (pDX->m_bSaveAndValidate)
194: DDX_TextWithFormat(pDX, nIDC, "%ld", AFX_IDP_PARSE_INT, &value);
195: else
196: DDX_TextWithFormat(pDX, nIDC, "%ld", AFX_IDP_PARSE_INT, value);
197: }
198:
199: void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, DWORD& value)
200: {
201: if (pDX->m_bSaveAndValidate)
202: DDX_TextWithFormat(pDX, nIDC, "%lu", AFX_IDP_PARSE_INT, &value);
203: else
204: DDX_TextWithFormat(pDX, nIDC, "%lu", AFX_IDP_PARSE_INT, value);
205: }
206:
207: void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value)
208: {
209: HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
210: if (pDX->m_bSaveAndValidate)
211: {
212: int nLen = ::GetWindowTextLength(hWndCtrl);
213: ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);
214: }
215: else
216: {
217: _AfxSmartSetWindowText(hWndCtrl, value);
218: }
219: }
220:
221: /////////////////////////////////////////////////////////////////////////////
222: // Data exchange for special control
223:
224: void AFXAPI DDX_Check(CDataExchange* pDX, int nIDC, int& value)
225: {
226: HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
227: if (pDX->m_bSaveAndValidate)
228: {
229: value = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L);
230: ASSERT(value >= 0 && value <= 2);
231: }
232: else
233: {
234: if (value < 0 || value > 2)
235: {
236: value = 0; // default to off
237: TRACE1("Warning: dialog data checkbox value out of range"
238: " %d\n", value);
239: }
240: ::SendMessage(hWndCtrl, BM_SETCHECK, (WPARAM)value, 0L);
241: }
242: }
243:
244: void AFXAPI DDX_Radio(CDataExchange* pDX, int nIDC, int& value)
245: // must be first in a group of auto radio buttons
246: {
247: HWND hWndFirstCtrl = pDX->PrepareCtrl(nIDC);
248:
249: ASSERT(::GetWindowLong(hWndFirstCtrl, GWL_STYLE) & WS_GROUP);
250: ASSERT((::GetWindowLong(hWndFirstCtrl, GWL_STYLE) & 0xf)
251: == BS_AUTORADIOBUTTON);
252:
253: if (pDX->m_bSaveAndValidate)
254: value = -1; // value if none found
255:
256: // walk all children in group
257: HWND hWndCtrl = hWndFirstCtrl;
258: int iButton = 0;
259: do
260: {
261: if (::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)
262: {
263: // control in group is a radio button
264: if (pDX->m_bSaveAndValidate)
265: {
266: if (::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0)
267: {
268: ASSERT(value == -1); // only set once
269: value = iButton;
270: }
271: }
272: else
273: {
274: // select button
275: ::SendMessage(hWndCtrl, BM_SETCHECK, (iButton == value), 0L);
276: }
277: iButton++;
278: }
279: else
280: {
281: TRACE0("Warning: skipping non-radio button in group\n");
282: }
283: hWndCtrl = ::GetNextDlgGroupItem(pDX->m_pDlgWnd->m_hWnd,
284: hWndCtrl, FALSE); // get next
285: } while (hWndCtrl != hWndFirstCtrl);
286: }
287:
288: /////////////////////////////////////////////////////////////////////////////
289: // Listboxes, comboboxes
290:
291: void AFXAPI DDX_LBString(CDataExchange* pDX, int nIDC, CString& value)
292: {
293: HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
294: if (pDX->m_bSaveAndValidate)
295: {
296: int nIndex = (int)::SendMessage(hWndCtrl, LB_GETCURSEL, 0, 0L);
297: if (nIndex != -1)
298: {
299: int nLen = (int)::SendMessage(hWndCtrl, LB_GETTEXTLEN, nIndex, 0L);
300: ::SendMessage(hWndCtrl, LB_GETTEXT, nIndex,
301: (LPARAM)(LPSTR)value.GetBufferSetLength(nLen));
302: }
303: else
304: {
305: // no selection
306: value.Empty();
307: }
308: }
309: else
310: {
311: // set current selection based on data string
312: if (::SendMessage(hWndCtrl, LB_SELECTSTRING, (WPARAM)-1,
313: (LPARAM)(LPCSTR)value) == LB_ERR)
314: {
315: // no selection match
316: TRACE0("Warning: no listbox item selected\n");
317: }
318: }
319: }
320:
321: // Win 3.1 only
322: void AFXAPI DDX_LBStringExact(CDataExchange* pDX, int nIDC, CString& value)
323: {
324: HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
325: if (pDX->m_bSaveAndValidate)
326: {
327: DDX_LBString(pDX, nIDC, value);
328: }
329: else
330: {
331: // set current selection based on data string
332: int i = (int)::SendMessage(hWndCtrl, LB_FINDSTRINGEXACT, (WPARAM)-1,
333: (LPARAM)(LPCSTR)value);
334: if (i < 0)
335: {
336: // no selection match
337: TRACE0("Warning: no listbox item selected\n");
338: }
339: else
340: {
341: // select it
342: SendMessage(hWndCtrl, LB_SETCURSEL, i, 0L);
343: }
344: }
345: }
346:
347: void AFXAPI DDX_CBString(CDataExchange* pDX, int nIDC, CString& value)
348: {
349: HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
350: if (pDX->m_bSaveAndValidate)
351: {
352: // just get current edit item text (or drop list static)
353: int nLen = ::GetWindowTextLength(hWndCtrl);
354: if (nLen != -1)
355: {
356: // get known length
357: ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);
358: }
359: else
360: {
361: // for drop lists GetWindowTextLength does not work - assume
362: // max of 255 characters
363: ::GetWindowText(hWndCtrl, value.GetBuffer(255), 255+1);
364: value.ReleaseBuffer();
365: }
366: }
367: else
368: {
369: // set current selection based on model string
370: if (::SendMessage(hWndCtrl, CB_SELECTSTRING, (WPARAM)-1,
371: (LPARAM)(LPCSTR)value) == CB_ERR)
372: {
373: // just set the edit text (will be ignored if DROPDOWNLIST)
374: _AfxSmartSetWindowText(hWndCtrl, value);
375: }
376: }
377: }
378:
379: // Win 3.1 only
380: void AFXAPI DDX_CBStringExact(CDataExchange* pDX, int nIDC, CString& value)
381: {
382: HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
383: if (pDX->m_bSaveAndValidate)
384: {
385: DDX_CBString(pDX, nIDC, value);
386: }
387: else
388: {
389: // set current selection based on data string
390: int i = (int)::SendMessage(hWndCtrl, CB_FINDSTRINGEXACT, (WPARAM)-1,
391: (LPARAM)(LPCSTR)value);
392: if (i < 0)
393: {
394: // no selection match
395: TRACE0("Warning: no combobox item selected\n");
396: }
397: else
398: {
399: // select it
400: SendMessage(hWndCtrl, CB_SETCURSEL, i, 0L);
401: }
402: }
403: }
404:
405: void AFXAPI DDX_LBIndex(CDataExchange* pDX, int nIDC, int& index)
406: {
407: HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
408: if (pDX->m_bSaveAndValidate)
409: index = (int)::SendMessage(hWndCtrl, LB_GETCURSEL, 0, 0L);
410: else
411: ::SendMessage(hWndCtrl, LB_SETCURSEL, (WPARAM)index, 0L);
412: }
413:
414: void AFXAPI DDX_CBIndex(CDataExchange* pDX, int nIDC, int& index)
415: {
416: HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
417: if (pDX->m_bSaveAndValidate)
418: index = (int)::SendMessage(hWndCtrl, CB_GETCURSEL, 0, 0L);
419: else
420: ::SendMessage(hWndCtrl, CB_SETCURSEL, (WPARAM)index, 0L);
421: }
422:
423: /////////////////////////////////////////////////////////////////////////////
424: // Range Dialog Data Validation
425:
426: static void NEAR PASCAL FailMinMaxWithFormat(CDataExchange* pDX,
427: long minVal, long maxVal, const char* pszFormat, UINT nIDPrompt)
428: // error string must have '%1' and '%2' strings for min and max values
429: // wsprintf formatting uses long values (format should be '%ld' or '%lu')
430: {
431: ASSERT(pszFormat != NULL);
432:
433: if (!pDX->m_bSaveAndValidate)
434: {
435: TRACE0("Warning: initial dialog data is out of range\n");
436: return; // don't stop now
437: }
438: char szMin[32];
439: char szMax[32];
440: wsprintf(szMin, pszFormat, minVal);
441: wsprintf(szMax, pszFormat, maxVal);
442: CString prompt;
443: AfxFormatString2(prompt, nIDPrompt, szMin, szMax);
444: AfxMessageBox(prompt, MB_ICONEXCLAMATION, nIDPrompt);
445: prompt.Empty(); // exception prep
446: pDX->Fail();
447: }
448:
449: //NOTE: don't use overloaded function names to avoid type ambiguities
450: void AFXAPI DDV_MinMaxInt(CDataExchange* pDX, int value, int minVal, int maxVal)
451: {
452: ASSERT(minVal <= maxVal);
453: if (value < minVal || value > maxVal)
454: FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%ld",
455: AFX_IDP_PARSE_INT_RANGE);
456: }
457:
458: void AFXAPI DDV_MinMaxLong(CDataExchange* pDX, long value, long minVal, long maxVal)
459: {
460: ASSERT(minVal <= maxVal);
461: if (value < minVal || value > maxVal)
462: FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%ld",
463: AFX_IDP_PARSE_INT_RANGE);
464: }
465:
466: void AFXAPI DDV_MinMaxUInt(CDataExchange* pDX, UINT value, UINT minVal, UINT maxVal)
467: {
468: ASSERT(minVal <= maxVal);
469: if (value < minVal || value > maxVal)
470: FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%lu",
471: AFX_IDP_PARSE_INT_RANGE);
472: }
473:
474: void AFXAPI DDV_MinMaxDWord(CDataExchange* pDX, DWORD value, DWORD minVal, DWORD maxVal)
475: {
476: ASSERT(minVal <= maxVal);
477: if (value < minVal || value > maxVal)
478: FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%lu",
479: AFX_IDP_PARSE_INT_RANGE);
480: }
481:
482: /////////////////////////////////////////////////////////////////////////////
483: // Max Chars Dialog Data Validation
484:
485: void AFXAPI DDV_MaxChars(CDataExchange* pDX, CString const& value, int nChars)
486: {
487: ASSERT(nChars >= 1); // allow them something
488: if (pDX->m_bSaveAndValidate && value.GetLength() > nChars)
489: {
490: char szT[32];
491: wsprintf(szT, "%d", nChars);
492: CString prompt;
493: AfxFormatString1(prompt, AFX_IDP_PARSE_STRING_SIZE, szT);
494: AfxMessageBox(prompt, MB_ICONEXCLAMATION, AFX_IDP_PARSE_STRING_SIZE);
495: prompt.Empty(); // exception prep
496: pDX->Fail();
497: }
498: }
499:
500: /////////////////////////////////////////////////////////////////////////////
501: // Special DDX_ proc for subclassing controls
502:
503: void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl)
504: {
505: if (rControl.m_hWnd == NULL) // not subclassed yet
506: {
507: ASSERT(!pDX->m_bSaveAndValidate);
508: HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
509: if (!rControl.SubclassWindow(hWndCtrl))
510: {
511: ASSERT(FALSE); // possibly trying to subclass twice ?
512: AfxThrowNotSupportedException();
513: }
514: }
515: }
516:
517: /////////////////////////////////////////////////////////////////////////////
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.