home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c480 / 18.ddi / MFC / SRC / DLGDATA.CP_ / DLGDATA.CP
Encoding:
Text File  |  1993-02-08  |  13.5 KB  |  502 lines

  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, MAKELPARAM(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, void* pData)
  90. {
  91.     ASSERT(pszText != NULL);
  92.     ASSERT(pszFormat != NULL);
  93.     ASSERT(pData != NULL);
  94.  
  95.     ASSERT(*pszFormat == '%');
  96.     pszFormat++;        // skip '%'
  97.  
  98.     BOOL bLong = FALSE;
  99.     if (*pszFormat == 'l')
  100.     {
  101.         bLong = TRUE;
  102.         pszFormat++;
  103.     }
  104.  
  105.     ASSERT(*pszFormat == 'd' || *pszFormat == 'u');
  106.     ASSERT(pszFormat[1] == '\0');
  107.  
  108.     while (*pszText == ' ' || *pszText == '\t')
  109.         pszText++;
  110.     char chFirst = pszText[0];
  111.     long l, l2;
  112.     if (*pszFormat == 'd')
  113.     {
  114.         // signed
  115.         l = strtol(pszText, (char**)&pszText, 10);
  116.         l2 = (int)l;
  117.     }
  118.     else
  119.     {
  120.         // unsigned
  121.         l = (long)strtoul(pszText, (char**)&pszText, 10);
  122.         l2 = (unsigned int)l;
  123.     }
  124.     if (l == 0 && chFirst != '0')
  125.         return FALSE;   // could not convert
  126.  
  127.     while (*pszText == ' ' || *pszText == '\t')
  128.         pszText++;
  129.     if (*pszText != '\0')
  130.         return FALSE;   // not terminated properly
  131.  
  132.     if (bLong)
  133.         *((long*)pData) = l;
  134.     else if (l == l2)
  135.         *((int*)pData) = (int)l;
  136.     else
  137.         return FALSE;       // too big for int
  138.  
  139.     // all ok
  140.     return TRUE;
  141. }
  142.  
  143.  
  144. static void PASCAL NEAR DDX_TextWithFormat(CDataExchange* pDX, int nIDC,
  145.     void* pData, const char* pszFormat, UINT nIDPrompt)
  146.     // only supports windows output formats - no floating point
  147. {
  148.     HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
  149.     char szT[64];
  150.     if (pDX->m_bSaveAndValidate)
  151.     {
  152.         // the following works for %d, %u, %ld, %lu
  153.         ::GetWindowText(hWndCtrl, szT, sizeof(szT));
  154.         if (!_AfxSimpleScanf(szT, pszFormat, pData))
  155.         {
  156.             AfxMessageBox(nIDPrompt);
  157.             pDX->Fail();        // throws exception
  158.         }
  159.     }
  160.     else
  161.     {
  162.         wvsprintf(szT, pszFormat, pData);
  163.             // does not support floating point numbers - see dlgfloat.cpp
  164.         _AfxSmartSetWindowText(hWndCtrl, szT);
  165.     }
  166. }
  167.  
  168. /////////////////////////////////////////////////////////////////////////////
  169. // Simple formatting to text item
  170.  
  171. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, int& value)
  172. {
  173.     DDX_TextWithFormat(pDX, nIDC, &value, "%d", AFX_IDP_PARSE_INT);
  174. }
  175.  
  176. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, UINT& value)
  177. {
  178.     DDX_TextWithFormat(pDX, nIDC, &value, "%u", AFX_IDP_PARSE_INT);
  179. }
  180.  
  181. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, long& value)
  182. {
  183.     DDX_TextWithFormat(pDX, nIDC, &value, "%ld", AFX_IDP_PARSE_INT);
  184. }
  185.  
  186. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, DWORD& value)
  187. {
  188.     DDX_TextWithFormat(pDX, nIDC, &value, "%lu", AFX_IDP_PARSE_INT);
  189. }
  190.  
  191. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value)
  192. {
  193.     HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
  194.     if (pDX->m_bSaveAndValidate)
  195.     {
  196.         int nLen = ::GetWindowTextLength(hWndCtrl);
  197.         ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);
  198.     }
  199.     else
  200.     {
  201.         _AfxSmartSetWindowText(hWndCtrl, value);
  202.     }
  203. }
  204.  
  205. /////////////////////////////////////////////////////////////////////////////
  206. // Data exchange for special control
  207.  
  208. void AFXAPI DDX_Check(CDataExchange* pDX, int nIDC, int& value)
  209. {
  210.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  211.     if (pDX->m_bSaveAndValidate)
  212.     {
  213.         value = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L);
  214.         ASSERT(value >= 0 && value <= 2);
  215.     }
  216.     else
  217.     {
  218.         if (value < 0 || value > 2)
  219.         {
  220.             value = 0;      // default to off
  221.             TRACE1("Warning: dialog data checkbox value out of range"
  222.                     " %d\n", value);
  223.         }
  224.         ::SendMessage(hWndCtrl, BM_SETCHECK, (WPARAM)value, 0L);
  225.     }
  226. }
  227.  
  228. void AFXAPI DDX_Radio(CDataExchange* pDX, int nIDC, int& value)
  229.     // must be first in a group of auto radio buttons
  230. {
  231.     HWND hWndFirstCtrl = pDX->PrepareCtrl(nIDC);
  232.  
  233.     ASSERT(::GetWindowLong(hWndFirstCtrl, GWL_STYLE) & WS_GROUP);
  234.     ASSERT((::GetWindowLong(hWndFirstCtrl, GWL_STYLE) & 0xf)
  235.             == BS_AUTORADIOBUTTON);
  236.  
  237.     if (pDX->m_bSaveAndValidate)
  238.         value = -1;         // value if none found
  239.  
  240.     // walk all children in group
  241.     HWND hWndCtrl = hWndFirstCtrl;
  242.     int iButton = 0;
  243.     do
  244.     {
  245.         if (::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)
  246.         {
  247.             // control in group is a radio button
  248.             if (pDX->m_bSaveAndValidate)
  249.             {
  250.                 if (::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0)
  251.                 {
  252.                     ASSERT(value == -1);    // only set once
  253.                     value = iButton;
  254.                 }
  255.             }
  256.             else
  257.             {
  258.                 // select button
  259.                 ::SendMessage(hWndCtrl, BM_SETCHECK, (iButton == value), 0L);
  260.             }
  261.             iButton++;
  262.         }
  263.         else
  264.         {
  265.             TRACE0("Warning: skipping non-radio button in group\n");
  266.         }
  267.         hWndCtrl = ::GetNextDlgGroupItem(pDX->m_pDlgWnd->m_hWnd,
  268.             hWndCtrl, FALSE);       // get next
  269.     } while (hWndCtrl != hWndFirstCtrl);
  270. }
  271.  
  272. /////////////////////////////////////////////////////////////////////////////
  273. // Listboxes, comboboxes
  274.  
  275. void AFXAPI DDX_LBString(CDataExchange* pDX, int nIDC, CString& value)
  276. {
  277.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  278.     if (pDX->m_bSaveAndValidate)
  279.     {
  280.         int nIndex = (int)::SendMessage(hWndCtrl, LB_GETCURSEL, 0, 0L);
  281.         if (nIndex != -1)
  282.         {
  283.             int nLen = (int)::SendMessage(hWndCtrl, LB_GETTEXTLEN, nIndex, 0L);
  284.             ::SendMessage(hWndCtrl, LB_GETTEXT, nIndex,
  285.                     (LPARAM)(LPSTR)value.GetBufferSetLength(nLen));
  286.         }
  287.         else
  288.         {
  289.             // no selection
  290.             value.Empty();
  291.         }
  292.     }
  293.     else
  294.     {
  295.         // set current selection based on data string
  296.         if (::SendMessage(hWndCtrl, LB_SELECTSTRING, (WPARAM)-1,
  297.           (LPARAM)(LPCSTR)value) == LB_ERR)
  298.         {
  299.             // no selection match
  300.             TRACE0("Warning: no listbox item selected\n");
  301.         }
  302.     }
  303. }
  304.  
  305. // Win 3.1 only
  306. void AFXAPI DDX_LBStringExact(CDataExchange* pDX, int nIDC, CString& value)
  307. {
  308.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  309.     if (pDX->m_bSaveAndValidate)
  310.     {
  311.         DDX_LBString(pDX, nIDC, value);
  312.     }
  313.     else
  314.     {
  315.         // set current selection based on data string
  316.         int i = (int)::SendMessage(hWndCtrl, LB_FINDSTRINGEXACT, (WPARAM)-1,
  317.           (LPARAM)(LPCSTR)value);
  318.         if (i < 0)
  319.         {
  320.             // no selection match
  321.             TRACE0("Warning: no listbox item selected\n");
  322.         }
  323.         else
  324.         {
  325.             // select it
  326.             SendMessage(hWndCtrl, LB_SETCURSEL, i, 0L);
  327.         }
  328.     }
  329. }
  330.  
  331. void AFXAPI DDX_CBString(CDataExchange* pDX, int nIDC, CString& value)
  332. {
  333.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  334.     if (pDX->m_bSaveAndValidate)
  335.     {
  336.         // just get current edit item text (or drop list static)
  337.         int nLen = ::GetWindowTextLength(hWndCtrl);
  338.         if (nLen != -1)
  339.         {
  340.             // get known length
  341.             ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);
  342.         }
  343.         else
  344.         {
  345.             // for drop lists GetWindowTextLength does not work - assume
  346.             //  max of 255 characters
  347.             ::GetWindowText(hWndCtrl, value.GetBuffer(255), 255+1);
  348.             value.ReleaseBuffer();
  349.         }
  350.     }
  351.     else
  352.     {
  353.         // set current selection based on model string
  354.         if (::SendMessage(hWndCtrl, CB_SELECTSTRING, (WPARAM)-1,
  355.             (LPARAM)(LPCSTR)value) == CB_ERR)
  356.         {
  357.             // just set the edit text (will be ignored if DROPDOWNLIST)
  358.             _AfxSmartSetWindowText(hWndCtrl, value);
  359.         }
  360.     }
  361. }
  362.  
  363. // Win 3.1 only
  364. void AFXAPI DDX_CBStringExact(CDataExchange* pDX, int nIDC, CString& value)
  365. {
  366.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  367.     if (pDX->m_bSaveAndValidate)
  368.     {
  369.         DDX_CBString(pDX, nIDC, value);
  370.     }
  371.     else
  372.     {
  373.         // set current selection based on data string
  374.         int i = (int)::SendMessage(hWndCtrl, CB_FINDSTRINGEXACT, (WPARAM)-1,
  375.           (LPARAM)(LPCSTR)value);
  376.         if (i < 0)
  377.         {
  378.             // no selection match
  379.             TRACE0("Warning: no combobox item selected\n");
  380.         }
  381.         else
  382.         {
  383.             // select it
  384.             SendMessage(hWndCtrl, CB_SETCURSEL, i, 0L);
  385.         }
  386.     }
  387. }
  388.  
  389. void AFXAPI DDX_LBIndex(CDataExchange* pDX, int nIDC, int& index)
  390. {
  391.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  392.     if (pDX->m_bSaveAndValidate)
  393.         index = (int)::SendMessage(hWndCtrl, LB_GETCURSEL, 0, 0L);
  394.     else
  395.         ::SendMessage(hWndCtrl, LB_SETCURSEL, (WPARAM)index, 0L);
  396. }
  397.  
  398. void AFXAPI DDX_CBIndex(CDataExchange* pDX, int nIDC, int& index)
  399. {
  400.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  401.     if (pDX->m_bSaveAndValidate)
  402.         index = (int)::SendMessage(hWndCtrl, CB_GETCURSEL, 0, 0L);
  403.     else
  404.         ::SendMessage(hWndCtrl, CB_SETCURSEL, (WPARAM)index, 0L);
  405. }
  406.  
  407. /////////////////////////////////////////////////////////////////////////////
  408. // Range Dialog Data Validation
  409.  
  410. static void NEAR PASCAL FailMinMaxWithFormat(CDataExchange* pDX,
  411.      long minVal, long maxVal, const char* pszFormat, UINT nIDPrompt)
  412.     // error string must have '%1' and '%2' strings for min and max values
  413.     // wsprintf formatting uses long values (format should be '%ld' or '%lu')
  414. {
  415.     ASSERT(pszFormat != NULL);
  416.  
  417.     if (!pDX->m_bSaveAndValidate)
  418.     {
  419.         TRACE0("Warning: initial dialog data is out of range\n");
  420.         return;     // don't stop now
  421.     }
  422.     char szMin[32];
  423.     char szMax[32];
  424.     wsprintf(szMin, pszFormat, minVal);
  425.     wsprintf(szMax, pszFormat, maxVal);
  426.     CString prompt;
  427.     AfxFormatString2(prompt, nIDPrompt, szMin, szMax);
  428.     AfxMessageBox(prompt, MB_ICONEXCLAMATION, nIDPrompt);
  429.     prompt.Empty(); // exception prep
  430.     pDX->Fail();
  431. }
  432.  
  433. //NOTE: don't use overloaded function names to avoid type ambiguities
  434. void AFXAPI DDV_MinMaxInt(CDataExchange* pDX, int value, int minVal, int maxVal)
  435. {
  436.     ASSERT(minVal <= maxVal);
  437.     if (value < minVal || value > maxVal)
  438.         FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%ld",
  439.             AFX_IDP_PARSE_INT_RANGE);
  440. }
  441.  
  442. void AFXAPI DDV_MinMaxLong(CDataExchange* pDX, long value, long minVal, long maxVal)
  443. {
  444.     ASSERT(minVal <= maxVal);
  445.     if (value < minVal || value > maxVal)
  446.         FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%ld",
  447.             AFX_IDP_PARSE_INT_RANGE);
  448. }
  449.  
  450. void AFXAPI DDV_MinMaxUInt(CDataExchange* pDX, UINT value, UINT minVal, UINT maxVal)
  451. {
  452.     ASSERT(minVal <= maxVal);
  453.     if (value < minVal || value > maxVal)
  454.         FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%lu",
  455.             AFX_IDP_PARSE_INT_RANGE);
  456. }
  457.  
  458. void AFXAPI DDV_MinMaxDWord(CDataExchange* pDX, DWORD value, DWORD minVal, DWORD maxVal)
  459. {
  460.     ASSERT(minVal <= maxVal);
  461.     if (value < minVal || value > maxVal)
  462.         FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%lu",
  463.             AFX_IDP_PARSE_INT_RANGE);
  464. }
  465.  
  466. /////////////////////////////////////////////////////////////////////////////
  467. // Max Chars Dialog Data Validation
  468.  
  469. void AFXAPI DDV_MaxChars(CDataExchange* pDX, CString const& value, int nChars)
  470. {
  471.     ASSERT(nChars >= 1);        // allow them something
  472.     if (pDX->m_bSaveAndValidate && value.GetLength() > nChars)
  473.     {
  474.         char szT[32];
  475.         wsprintf(szT, "%d", nChars);
  476.         CString prompt;
  477.         AfxFormatString1(prompt, AFX_IDP_PARSE_STRING_SIZE, szT);
  478.         AfxMessageBox(prompt, MB_ICONEXCLAMATION, AFX_IDP_PARSE_STRING_SIZE);
  479.         prompt.Empty(); // exception prep
  480.         pDX->Fail();
  481.     }
  482. }
  483.  
  484. /////////////////////////////////////////////////////////////////////////////
  485. // Special DDX_ proc for subclassing controls
  486.  
  487. void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl)
  488. {
  489.     if (rControl.m_hWnd == NULL)    // not subclassed yet
  490.     {
  491.         ASSERT(!pDX->m_bSaveAndValidate);
  492.         HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  493.         if (!rControl.SubclassWindow(hWndCtrl))
  494.         {
  495.             ASSERT(FALSE);      // possibly trying to subclass twice ?
  496.             AfxThrowNotSupportedException();
  497.         }
  498.     }
  499. }
  500.  
  501. /////////////////////////////////////////////////////////////////////////////
  502.