Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

566 lines
15 KiB

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