home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / mfc / database / daotable / addtbdlg.cpp < prev    next >
C/C++ Source or Header  |  1998-03-26  |  24KB  |  733 lines

  1. // AddTdDlg.cpp : implementation file for dialog that allows user to
  2. // specify fields that constitute the tabledef's structure
  3. //
  4. // This is a part of the Microsoft Foundation Classes C++ library.
  5. // Copyright (C) 1992-1998 Microsoft Corporation
  6. // All rights reserved.
  7. //
  8. // This source code is only intended as a supplement to the
  9. // Microsoft Foundation Classes Reference and related
  10. // electronic documentation provided with the library.
  11. // See these sources for detailed information regarding the
  12. // Microsoft Foundation Classes product.
  13.  
  14. #include "stdafx.h"
  15. #include "daotable.h"
  16. #include "tabledef.h"
  17. #include "field.h"
  18. #include "AddTbDlg.h"
  19.  
  20. #ifdef _DEBUG
  21. #define new DEBUG_NEW
  22. #undef THIS_FILE
  23. static char THIS_FILE[] = __FILE__;
  24. #endif
  25.  
  26. /////////////////////////////////////////////////////////////////////////////
  27. // CDlgAddTable dialog
  28.  
  29. // default constructor
  30. CDlgAddTable::CDlgAddTable(CWnd* pParent /*=NULL*/)
  31.     : CDialog(CDlgAddTable::IDD, pParent)
  32. {
  33.     // call centralized initialization function
  34.     initializer();
  35. }
  36.  
  37. // the constructor that will generally be called to create this dialog
  38. //
  39. // IN: pDatabase--pointer to an open database object
  40. // IN: strTableName--name of table whose fields will be added, deleted, or viewed
  41. // IN: pParent--pointer to parent of the dialog
  42. //
  43. CDlgAddTable::CDlgAddTable(CDaoDatabase *pDatabase, CString strTableName,
  44.                            CWnd * pParent)
  45.     : CDialog(CDlgAddTable::IDD, pParent)
  46. {
  47.     // the incoming database pointer that is used in creating the tabledef
  48.     m_pDatabase = pDatabase;
  49.  
  50.     // transfer name of table (if any) to member
  51.     m_strTableName = strTableName;
  52.  
  53.     // call centralized function to initialize dialog data and other
  54.     // member data
  55.     initializer();
  56. }
  57.  
  58. // set up the members of this class
  59. void CDlgAddTable::initializer()
  60. {
  61.     // initialize the field info members (including ordinal position)
  62.     fieldInitializer(TRUE);
  63.  
  64.     // no fields added to table yet
  65.     m_bFirstFieldAdded = FALSE;
  66.     // when DoDataExchange is executed, check validity of transferred data
  67.     m_bCheckDDV = TRUE;
  68.     // no tabledef until it is created with the user specified name
  69.     m_pTableDef = NULL;
  70.  
  71.     // the index into the collection of fields for the current field
  72.     m_nFieldIndex = 0;
  73.  
  74.     // initialize type map.  The combobox list is sequential, the internal
  75.     // constants for DAO are not.
  76.     m_nTypeMap[Boolean] = dbBoolean;
  77.     m_nTypeMap[Byte] = dbByte;
  78.     m_nTypeMap[Currency] = dbCurrency;
  79.     m_nTypeMap[Date] = dbDate;
  80.     m_nTypeMap[Double] = dbDouble;
  81.     m_nTypeMap[Long] = dbLong;
  82.     m_nTypeMap[LongBinary] = dbLongBinary;
  83.     m_nTypeMap[Memo] = dbMemo;
  84.     m_nTypeMap[Short] = dbInteger;  // it is really a short integer
  85.     m_nTypeMap[Single] = dbSingle;
  86.     m_nTypeMap[Text] = dbText;
  87. }
  88.  
  89. // initialize the field information to a default state.
  90. // IN: bInitOrdPos -- The ordinal position is conditionally initialized
  91. // to allow for it autoincrementing with each addition of a field to the
  92. // tabledef--TRUE by default
  93. void CDlgAddTable::fieldInitializer(BOOL bInitOrdPos /* = TRUE */)
  94. {
  95.     // conditionally initialize the ordinal position
  96.     if (bInitOrdPos)
  97.         m_FI.m_nOrdinalPosition = 0;
  98.  
  99.     // always initialize the rest
  100.     m_FI.m_strName = _T("");
  101.     m_FI.m_nType = -1;      // initialized for the benefit of the combox
  102.     m_FI.m_lSize = 0;
  103.     m_FI.m_strDefaultValue = _T("");
  104.     m_FI.m_bRequired = FALSE;
  105.     m_FI.m_lAttributes = dbUpdatableField;
  106. #if _MFC_VER == 0x400
  107.     m_FI.m_bAllowZeroLength = TRUE;
  108. #else
  109.     m_FI.m_bAllowZeroLength = FALSE;
  110. #endif;
  111.     m_FI.m_strValidationRule = _T("");
  112.     m_FI.m_strValidationText = _T("");
  113. }
  114.  
  115. void CDlgAddTable::DoDataExchange(CDataExchange* pDX)
  116. {
  117.     CDialog::DoDataExchange(pDX);
  118.     //{{AFX_DATA_MAP(CDlgAddTable)
  119.     DDX_Control(pDX, IDC_VALIDATION_TEXT, m_ValidationTextControl);
  120.     DDX_Control(pDX, IDC_VALIDATION_RULE, m_ValidationRuleControl);
  121.     DDX_Control(pDX, IDC_REQUIRED, m_RequireControl);
  122.     DDX_Control(pDX, IDC_POSITION, m_OrdinalPositionControl);
  123.     DDX_Control(pDX, IDC_NAME, m_NameControl);
  124.     DDX_Control(pDX, IDC_THE_SIZE, m_SizeControl);
  125.     DDX_Control(pDX, IDC_DEFAULT_VALUE, m_DefaultValueControl);
  126.     DDX_Control(pDX, IDC_AUTO_INCR_FIELD, m_AutoIncrFieldControl);
  127.     DDX_Control(pDX, IDC_FIXED_FIELD, m_FieldVariabilityControl);
  128.     DDX_Text(pDX, IDC_TABLE_NAME, m_strTableName);
  129.     //}}AFX_DATA_MAP
  130.  
  131.     // move those DDX calls that use DDV or map directly to members
  132.     // of the field info struct, or require special processing outside
  133.     // of the wizard block
  134.  
  135.     DDX_Control(pDX, IDC_TYPE, m_TypeControl);
  136.     // only check validity if so instructed--avoids unnecessary warnings
  137.     if (m_bCheckDDV)
  138.         // user must select a type for the field
  139.         DDV_NoSel(pDX, &m_TypeControl);
  140.  
  141.     // direct mapping to field info struct
  142.     DDX_Text(pDX, IDC_POSITION, m_FI.m_nOrdinalPosition);
  143.     DDX_Text(pDX, IDC_NAME, m_FI.m_strName);
  144.     DDX_Text(pDX, IDC_VALIDATION_RULE, m_FI.m_strValidationRule);
  145.     DDX_Text(pDX, IDC_VALIDATION_TEXT, m_FI.m_strValidationText);
  146.     DDX_Text(pDX, IDC_THE_SIZE, m_FI.m_lSize);
  147.     DDX_Text(pDX, IDC_DEFAULT_VALUE, m_FI.m_strDefaultValue);
  148.     DDX_Check(pDX, IDC_REQUIRED, m_FI.m_bRequired);
  149.  
  150.     // manage the type control directly since we need to use the type
  151.     // map (combobox uses sequential index, DAO types are non-sequential)
  152.     if (pDX->m_bSaveAndValidate)
  153.     {
  154.         // when transferring from dialog control to member, use map the
  155.         // easy way--just index into it
  156.         theTypes selectedType = (theTypes) m_TypeControl.GetCurSel();
  157.         m_FI.m_nType = m_nTypeMap[selectedType];
  158.     }
  159.     else
  160.     {
  161.         // if the type is -1, then no type selected
  162.         if (m_FI.m_nType == -1)
  163.         {
  164.             // update control appropriately
  165.             m_TypeControl.SetCurSel(m_FI.m_nType);
  166.         }
  167.         else
  168.         {
  169.             // must search the map looking for a match--
  170.             // search for actual type to determine internally used type
  171.             for (int i = 0; (i < MAX_TYPES) && (m_nTypeMap[i] != m_FI.m_nType); i++)
  172.                 ;
  173.  
  174.             // update control appropriately
  175.             m_TypeControl.SetCurSel(i);
  176.         }
  177.     }
  178.  
  179.     // do attribute specific processing -- member values are encoded in
  180.     // the m_lAttributes of CDaoFieldInfo struct
  181.     if (pDX->m_bSaveAndValidate)
  182.     {
  183.         // first do transfer
  184.         DDX_Radio(pDX, IDC_FIXED_FIELD, m_FieldVariability);
  185.         DDX_Check(pDX, IDC_AUTO_INCR_FIELD, m_bAutoIncrField);
  186.  
  187.         // if transferring to member from control, determine the
  188.         // attributes value to store for field
  189.         if (m_bAutoIncrField)
  190.             m_FI.m_lAttributes |= dbAutoIncrField;
  191.         else
  192.             m_FI.m_lAttributes &= ~dbAutoIncrField;
  193.  
  194.         // for text fields only, set the variability
  195.         if (m_FI.m_nType == dbText)
  196.             if (m_FieldVariability == 0)
  197.                 m_FI.m_lAttributes |= dbFixedField;
  198.             else
  199.                 m_FI.m_lAttributes |= dbVariableField;
  200.     }
  201.     else
  202.     {
  203.         // transferring from field attributes to controls
  204.         m_bAutoIncrField = (m_FI.m_lAttributes & dbAutoIncrField) == dbAutoIncrField;
  205.  
  206.         if ((m_FI.m_lAttributes & dbFixedField) == dbFixedField)
  207.             m_FieldVariability = 0;
  208.         else
  209.             m_FieldVariability = 1;
  210.  
  211.         // now transfer the information to the controls
  212.         DDX_Radio(pDX, IDC_FIXED_FIELD, m_FieldVariability);
  213.         DDX_Check(pDX, IDC_AUTO_INCR_FIELD, m_bAutoIncrField);
  214.     }
  215. }
  216.  
  217. // check if a type selection was made--one must be made
  218. void CDlgAddTable::DDV_NoSel(CDataExchange* pDX, CComboBox *theControl)
  219. {
  220.     // only process if transferring to member from control.  If
  221.     // no selection has been made, this constitutes an error condition
  222.     if (pDX->m_bSaveAndValidate && (CB_ERR == theControl->GetCurSel()))
  223.     {
  224.         AfxMessageBox(_T("You must select a type."), MB_ICONEXCLAMATION);
  225.         pDX->m_hWndLastControl = theControl->m_hWnd;
  226.         pDX->Fail();
  227.     }
  228. }
  229.  
  230. BEGIN_MESSAGE_MAP(CDlgAddTable, CDialog)
  231.     //{{AFX_MSG_MAP(CDlgAddTable)
  232.     ON_BN_CLICKED(IDC_NEXT_FIELD, OnNextField)
  233.     ON_CBN_SELENDOK(IDC_TYPE, OnSelendokType)
  234.     ON_BN_CLICKED(IDOK, OnDone)
  235.     ON_WM_CLOSE()
  236.     ON_BN_CLICKED(IDC_PREVIOUS_FIELD, OnPreviousField)
  237.     ON_BN_CLICKED(IDC_DELETE_FIELD, OnDeleteField)
  238.     ON_BN_CLICKED(IDC_ADD_FIELD, OnAddField)
  239.     ON_CBN_CLOSEUP(IDC_TYPE, OnCloseupType)
  240.     //}}AFX_MSG_MAP
  241. END_MESSAGE_MAP()
  242.  
  243. /////////////////////////////////////////////////////////////////////////////
  244. // CDlgAddTable message handlers
  245.  
  246. BOOL CDlgAddTable::OnInitDialog()
  247. {
  248.     CDialog::OnInitDialog();
  249.  
  250.     // user either specified new table or existing table
  251.     if(!IsExistentTable(m_pDatabase, m_strTableName))
  252.     {
  253.         // initialize size control to disabled until we know field type
  254.         // since that is what defines size (unless variable field type)
  255.         m_SizeControl.EnableWindow(FALSE);
  256.  
  257.         // until a field has been added, can not be "done" since a tabledef
  258.         // must contain at least one field
  259.         CButton *pButton = (CButton *)GetDlgItem(IDOK);
  260.         pButton->EnableWindow(FALSE);
  261.     }
  262.     else
  263.     {
  264.         // user specified existing table, so populate
  265.         // dialog with information from first field
  266.         //
  267.         // open the table rather than creating it
  268.         if (!openTableDef(m_pDatabase, &m_pTableDef, m_strTableName))
  269.             return TRUE;
  270.  
  271.         // get the information for the first field (no error reporting)
  272.         if (getFieldInfo(m_pTableDef, &m_FI, m_nFieldIndex, FALSE))
  273.         {
  274.             // there is a first field so set controls appropriate
  275.             setTypeDependentStatesAndExisting(m_FI.m_nType, TRUE);
  276.         }
  277.         else
  278.         {
  279.             // no first field (user must have delete all of them),
  280.             // so come up initialized and updatable first time
  281.             fieldInitializer(FALSE);
  282.             setTypeDependentStatesAndExisting(m_FI.m_nType, FALSE);
  283.         }
  284.  
  285.         // the first field has already been added long ago
  286.         m_bFirstFieldAdded = TRUE;
  287.  
  288.         UpdateData(FALSE);
  289.     }
  290.  
  291.     // set focus to name of field edit box
  292.     CEdit *pEdit = (CEdit *)GetDlgItem(IDC_NAME);
  293.     pEdit->SetFocus();
  294.  
  295.     // set the range of the spin control
  296.     CSpinButtonCtrl* pOrdPosSpin =
  297.         (CSpinButtonCtrl*) GetDlgItem(IDC_SPIN_ORDINAL_POSITION);
  298.     pOrdPosSpin->SetRange(0,100);
  299.  
  300.     return FALSE;  // return TRUE unless you set the focus to a control
  301. }
  302.  
  303. // user selected to go to next existing field--prompt for acceptance
  304. // since we lose the currently specified field if it is not added
  305. void CDlgAddTable::OnNextField()
  306. {
  307.     // get values from control -- no validity checking
  308.     m_bCheckDDV = FALSE;    // turn validity checking off
  309.     UpdateData(TRUE);
  310.     m_bCheckDDV = TRUE;     // turn checking back on--don't forget this!
  311.  
  312.     // action depends on if this is an existing field or not
  313.     // if this is a new field see if user wants to save it
  314.     // or not--if not, then try to move to next field
  315.     if ((m_FI.m_strName != _T("")) && (!IsExistentField(m_pTableDef, m_FI.m_strName)))
  316.         if (IDYES != AfxMessageBox(_T("Current field will be ignored unless added.  Continue anyway?"),
  317.                                 MB_YESNO))
  318.             return;
  319.  
  320.     // can only move next if there is a item at the current index in the
  321.     // collection--else you keep indexing into unused parts of the index.
  322.     // So, check for existence of current indexed item and only increment
  323.     // the index if there is an item (no error reporting)
  324.     if (getFieldInfo(m_pTableDef, &m_FI, m_nFieldIndex, FALSE))
  325.         // move to next field index
  326.         m_nFieldIndex += 1;
  327.  
  328.     // get the information for the next field if possible--
  329.     // last parameter indicates no failure reporting
  330.     if (getFieldInfo(m_pTableDef, &m_FI, m_nFieldIndex, FALSE))
  331.     {
  332.         // some settings are type dependent
  333.         setTypeDependentStatesAndExisting(m_FI.m_nType, TRUE);
  334.     }
  335.     else
  336.     {
  337.         // initialize field property values for new field
  338.         fieldInitializer(FALSE);
  339.  
  340.         // set ordinal position to this index just for neatness
  341.         m_FI.m_nOrdinalPosition = m_nFieldIndex;
  342.  
  343.         // some settings are type dependent
  344.         setTypeDependentStatesAndExisting(m_FI.m_nType, FALSE);
  345.     }
  346.  
  347.     UpdateData(FALSE);
  348.  
  349.     // set focus to name of field edit box
  350.     CEdit *pEdit = (CEdit *)GetDlgItem(IDC_NAME);
  351.     pEdit->SetFocus();
  352. }
  353.  
  354. // user selected a type for the field from the type combobox
  355. void CDlgAddTable::OnSelendokType()
  356. {
  357.     // need to transfer values into member variables
  358.     // otherwize the UpdateData(FALSE) at the end will
  359.     // wipe out all the values the user entered.
  360.     // Do not do any validity checks
  361.     m_bCheckDDV = FALSE;    // turn validity checking off
  362.     UpdateData(TRUE);
  363.     m_bCheckDDV = TRUE;     // turn checking back on--don't forget this!
  364.  
  365.     // once a type is selected, it is time to enable the size edit control
  366.     m_SizeControl.EnableWindow();
  367.  
  368.     // now set the states that depend on the type
  369.     setTypeDependentStatesAndExisting(m_FI.m_nType, FALSE);
  370.  
  371.     // update the controls with new info
  372.     UpdateData(FALSE);
  373. }
  374.  
  375. // user can scroll in dropdown--must handle this possibility.  Just pretend
  376. // it is a selection that ended OK
  377. void CDlgAddTable::OnCloseupType()
  378. {
  379.     OnSelendokType();
  380. }
  381.  
  382.  
  383. // user is done adding/viewing/deleting fields and is ready to finalize
  384. // the table structure
  385. void CDlgAddTable::OnDone()
  386. {
  387.     // by default, we are ready to exit the dialog
  388.     int retCode = IDYES;
  389.  
  390.     // if user has entered a field name, then warn them they will lose
  391.     // it if it is not explicitly added--we don't want to do an auto-add
  392.     // since user may have entered name without meaning to add another field
  393.     //
  394.     // see if there is a field name specified
  395.     CString name;
  396.     CEdit *pEdit = (CEdit *)GetDlgItem(IDC_NAME);
  397.     pEdit->GetWindowText(name);
  398.     // if there is a name, then warn the user
  399.     if (name.GetLength() != 0)
  400.     {
  401.         // only an issue if this is not an existing field
  402.         if (!IsExistentField(m_pTableDef, name))
  403.             retCode = AfxMessageBox(_T("Current field will be ignored unless added.  Continue anyway?"),
  404.                                 MB_YESNO);
  405.     }
  406.     // either there never was a field name specified or the user has
  407.     // chosen not to add the field
  408.     if (retCode == IDYES)
  409.     {
  410.         // done with tabledef object
  411.         delete m_pTableDef;
  412.  
  413.         // end the dialog
  414.         CDialog::EndDialog(0);
  415.     }
  416. }
  417.  
  418. // user cancelled tabledef creation process
  419. void CDlgAddTable::OnClose()
  420. {
  421.     // if we have already allocated a tabledef object, better
  422.     // clean it up
  423.     if (m_pTableDef != NULL)
  424.         delete m_pTableDef;
  425.  
  426.     // let the base class cleanup the rest
  427.     CDialog::OnClose();
  428. }
  429.  
  430. // user selected to move to previous existing field, prompt for acceptance since
  431. // we lose any currently specified field unless is is added to the collection
  432. void CDlgAddTable::OnPreviousField()
  433. {
  434.     // can't go previous if at 0th index
  435.     if (m_nFieldIndex >= 1)
  436.     {
  437.         // get values from control -- no validity checking
  438.         m_bCheckDDV = FALSE;    // turn validity checking off
  439.         UpdateData(TRUE);
  440.         m_bCheckDDV = TRUE;     // turn checking back on--don't forget this!
  441.  
  442.         // by default, move previous
  443.         int retCode = IDYES;
  444.  
  445.         // if user has entered a field name, then warn them they will lose
  446.         // it if it is not explicitly added--we don't want to do an auto-add
  447.         // since user may have entered name without meaning to add another field
  448.         //
  449.         // if there is a name, then warn the user
  450.         if (m_FI.m_strName != _T(""))
  451.         {
  452.             // only an issue if this is not an existing field
  453.             if (!IsExistentField(m_pTableDef, m_FI.m_strName))
  454.                 retCode = AfxMessageBox(_T("Current field will be ignored unless added.  Continue anyway?"),
  455.                                     MB_YESNO);
  456.         }
  457.  
  458.         // either there never was a field name specified or the user has
  459.         // chosen not to add the field
  460.         if (retCode == IDYES)
  461.         {
  462.             // move previous
  463.             m_nFieldIndex -= 1;
  464.  
  465.             // get the information for the previous field (no error reporting)
  466.             // if there is info, display appropriate to existing field
  467.             // if no info, treat this as a new field the user can set
  468.             if(getFieldInfo(m_pTableDef, &m_FI, m_nFieldIndex, FALSE))
  469.             {
  470.                 // some settings are type dependent
  471.                 setTypeDependentStatesAndExisting(m_FI.m_nType, TRUE);
  472.             }
  473.             else
  474.             {
  475.                 // initialize field property values for new field
  476.                 fieldInitializer(FALSE);
  477.  
  478.                 // set ordinal position to this index just for neatness
  479.                 m_FI.m_nOrdinalPosition = m_nFieldIndex;
  480.  
  481.                 // some settings are type dependent
  482.                 setTypeDependentStatesAndExisting(m_FI.m_nType, FALSE);
  483.             }
  484.  
  485.             // update the dialog controls with new values
  486.             UpdateData(FALSE);
  487.         }
  488.     }
  489. }
  490.  
  491. // user has selected to delete the current field, prompt for acceptance
  492. void CDlgAddTable::OnDeleteField()
  493. {
  494.     // get values from control -- don't continue if failure
  495.     if (!UpdateData(TRUE))
  496.         return;
  497.  
  498.     // can only delete existing fields
  499.     if (IsExistentField(m_pTableDef, m_FI.m_strName))
  500.     {
  501.         // is user sure?
  502.         if (IDYES == AfxMessageBox(_T("Delete current field?"), MB_YESNO))
  503.         {
  504.             // only react if field is deleted!
  505.             if (deleteField(m_pTableDef, m_FI.m_strName))
  506.             {
  507.                 // index into collection is unchanged, so
  508.                 // get the information for this field if there is one
  509.                 // (no error reporting)
  510.                 if (getFieldInfo(m_pTableDef, &m_FI, m_nFieldIndex, FALSE))
  511.                 {
  512.                     // there is a field, so set type depenedent properties
  513.                     // and existing field properties of controls
  514.                     setTypeDependentStatesAndExisting(m_FI.m_nType, TRUE);
  515.                 }
  516.                 else
  517.                 {
  518.                     // there is no field in collection following the
  519.                     // deletion at this index, so
  520.                     // set the field info to initial state
  521.                     fieldInitializer(FALSE);
  522.  
  523.                     // clear read-only status of control
  524.                     setTypeDependentStatesAndExisting(m_FI.m_nType, FALSE);
  525.                 }
  526.  
  527.                 // update the dialog controls to erase deleted field
  528.                 UpdateData(FALSE);
  529.             }
  530.         }
  531.     }
  532. }
  533.  
  534. // control availability depends on two things--the type of the field and
  535. // whether this is an existing field or new.  Some controls only make
  536. // sense for certain types (like autoincrement for longs only).  Existing
  537. // fields have primarily read-only properties.  This function takes both
  538. // into account when setting control states
  539. //
  540. // Note: since disabled is acceptable for existing field controls, you will
  541. //       not see the bExisting taken into consideration if we are disabling
  542. //       a control based on type
  543. //
  544. // IN: theType--the type of the current field (long, text, etc.)
  545. // IN: bExistingField--TRUE if this is an existing field, else false
  546. //
  547. void CDlgAddTable::setTypeDependentStatesAndExisting(int theType, BOOL bExistingField)
  548. {
  549.     // only longs can be auto increment
  550.     if (theType == dbLong)
  551.         m_AutoIncrFieldControl.EnableWindow(TRUE && (!bExistingField));
  552.     else
  553.         m_AutoIncrFieldControl.EnableWindow(FALSE);
  554.  
  555.     // except for text, setting variability of
  556.     // field size is not an option
  557.     if (theType == dbText)
  558.     {
  559.         // the state of controls depends on if this is an existing field or new
  560.         CButton *pButton = (CButton *)GetDlgItem(IDC_FIXED_FIELD);
  561.         pButton->EnableWindow(!bExistingField);
  562.         pButton = (CButton *)GetDlgItem(IDC_VARIABLE_FIELD);
  563.         pButton->EnableWindow(!bExistingField);
  564.     }
  565.     else
  566.     {
  567.         // the controls are disabled for all but non-existing text fields
  568.         CButton *pButton = (CButton *)GetDlgItem(IDC_FIXED_FIELD);
  569.         pButton->EnableWindow(FALSE);
  570.         pButton = (CButton *)GetDlgItem(IDC_VARIABLE_FIELD);
  571.         pButton->EnableWindow(FALSE);
  572.     }
  573.  
  574.     // by default, the default value depends only on field existence
  575.     m_DefaultValueControl.EnableWindow(TRUE && (!bExistingField));
  576.  
  577.     // by default the validation depends only on field existence
  578.     m_ValidationTextControl.SetReadOnly(bExistingField);
  579.     m_ValidationRuleControl.SetReadOnly(bExistingField);
  580.  
  581.     // In general, the selected type determines the following:
  582.     //   * the size of the field (in bytes)
  583.     //   * whether the size is user modifiable or not
  584.     //   * what the setting for field variability is by default
  585.     switch(theType)
  586.     {
  587.         case dbBoolean:
  588.         case dbByte:    m_SizeControl.SetReadOnly();
  589.                         m_FI.m_lSize = 1;
  590.                         // set to fixed field type
  591.                         m_FI.m_lAttributes &= ~dbVariableField;
  592.                         m_FI.m_lAttributes |= dbFixedField;
  593.                         break;
  594.         case dbInteger: m_SizeControl.SetReadOnly();
  595.                         m_FI.m_lSize = 2;
  596.                         // set to fixed field type
  597.                         m_FI.m_lAttributes &= ~dbVariableField;
  598.                         m_FI.m_lAttributes |= dbFixedField;
  599.                         break;
  600.         case dbLong:
  601.         case dbSingle:  m_SizeControl.SetReadOnly();
  602.                         m_FI.m_lSize = 4;
  603.                         // set to fixed field type
  604.                         m_FI.m_lAttributes &= ~dbVariableField;
  605.                         m_FI.m_lAttributes |= dbFixedField;
  606.                         break;
  607.         case dbCurrency:
  608.         case dbDate:
  609.         case dbDouble:  m_SizeControl.SetReadOnly();
  610.                         m_FI.m_lSize = 8;
  611.                         // set to fixed field type
  612.                         m_FI.m_lAttributes &= ~dbVariableField;
  613.                         m_FI.m_lAttributes |= dbFixedField;
  614.                         break;
  615.         case dbText:    m_SizeControl.SetReadOnly(FALSE | bExistingField);
  616.                         // set to variable field type
  617.                         m_FI.m_lAttributes &= ~dbFixedField;
  618.                         m_FI.m_lAttributes |= dbVariableField;
  619.                         break;
  620.         case dbLongBinary:
  621.                         m_SizeControl.SetReadOnly(FALSE | bExistingField);
  622.                         // set to variable field type
  623.                         m_FI.m_lAttributes &= ~dbFixedField;
  624.                         m_FI.m_lAttributes |= dbVariableField;
  625.                         break;
  626.         case dbMemo:    m_SizeControl.SetReadOnly(FALSE | bExistingField);
  627.                         // set to variable field type
  628.                         m_FI.m_lAttributes &= ~dbFixedField;
  629.                         m_FI.m_lAttributes |= dbVariableField;
  630.                         break;
  631.     } // end of switch
  632.  
  633.     // now handle setting availability for controls that are not type
  634.     // dependent
  635.     m_NameControl.SetReadOnly(bExistingField);
  636.     m_OrdinalPositionControl.SetReadOnly(bExistingField);
  637.     m_TypeControl.EnableWindow(!bExistingField);
  638.     m_RequireControl.EnableWindow(!bExistingField);
  639.  
  640.     UpdateData(FALSE);
  641. }
  642.  
  643. // user selected to add a new field to the collection
  644. // NOTE: here is also where the tabledef is created and
  645. //       appended to the tabledef collection.  The tabledef
  646. //       is created before the first field is created and appended
  647. //       after which it is appended to the tabledef collection
  648. void CDlgAddTable::OnAddField()
  649. {
  650.     // get values from control -- don't continue if failure
  651.     if (!UpdateData(TRUE))
  652.         return;
  653.  
  654.     // don't do anything if this is an existing field (except
  655.     // say so)
  656.     if (!IsExistentField(m_pTableDef, m_FI.m_strName))
  657.     {
  658.         // if no fields added yet, then this is first so create the tabledef
  659.         if (!m_bFirstFieldAdded)
  660.         {
  661.             // try to create the tabledef, if failure, stop processing
  662.             if (!createNewTableDef(m_pDatabase, &m_pTableDef, m_strTableName))
  663.                 return;
  664.         }
  665.  
  666.         // try to create the field with error checking--may fail if a
  667.         // duplicate named field already exists. Note: creating a field
  668.         // also appends it to the tabledef's field collection
  669.         if (!createNewField(m_pTableDef, &m_FI))
  670.         {
  671.             // if this is the first field, then failure means we delete the
  672.             // table we just created
  673.             if (!m_bFirstFieldAdded)
  674.                 deleteTable(m_pDatabase, m_strTableName);
  675.             // done
  676.             return;
  677.         }
  678.  
  679.         // field created successfully, so continue doing required
  680.         // initialization
  681.         if (!m_bFirstFieldAdded)
  682.         {
  683.             // append the tabldef to the database collection of tabledefs
  684.             // this must be done after at least the first first field
  685.             // has been added
  686.             if (!appendTableDef(m_pDatabase, m_pTableDef))
  687.             {
  688.                 // if append fails, then we should delete the first field
  689.                 // since it is probably the culprit and we should allow
  690.                 // user to set new properties
  691.                 deleteField(m_pTableDef, m_FI.m_strName);
  692.  
  693.                 // delete the table too if since this is the first field
  694.                 // that failed--else we will have a created table that is
  695.                 // not appended
  696.                 deleteTable(m_pDatabase, m_strTableName);
  697.  
  698.                 // done
  699.                 return;
  700.             }
  701.             else
  702.                 // now first field has been added successfully
  703.                 m_bFirstFieldAdded = TRUE;
  704.  
  705.             // now that first field added, can possbily be "done"
  706.             CButton *pButton = (CButton *)GetDlgItem(IDOK);
  707.             pButton->EnableWindow(TRUE);
  708.         }
  709.  
  710.         // clean out all fields except ordinal position to prepare
  711.         // user to enter next field's properties
  712.         fieldInitializer(FALSE);
  713.  
  714.         // manage the ordinal position -- autoincrement
  715.         // Note: it is OK for fields to have the same ordinal position--
  716.         // see the DAO docs for this property.
  717.         // also increment the index into the collection
  718.         m_FI.m_nOrdinalPosition += 1;
  719.         m_nFieldIndex += 1;
  720.  
  721.         // performs visible clearing of controls that are initialized
  722.         UpdateData(FALSE);
  723.     }
  724.     else
  725.     {
  726.         AfxMessageBox(_T("Can't add field--it already exists in the TableDef."));
  727.     }
  728.  
  729.     // set focus to name of field edit box
  730.     CEdit *pEdit = (CEdit *)GetDlgItem(IDC_NAME);
  731.     pEdit->SetFocus();
  732. }
  733.