// AddTdDlg.cpp : implementation file for dialog that allows user to // specify fields that constitute the tabledef's structure // // This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1998 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #include "daotable.h" #include "tabledef.h" #include "field.h" #include "AddTbDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CDlgAddTable dialog // default constructor CDlgAddTable::CDlgAddTable(CWnd* pParent /*=NULL*/) : CDialog(CDlgAddTable::IDD, pParent) { // call centralized initialization function initializer(); } // the constructor that will generally be called to create this dialog // // IN: pDatabase--pointer to an open database object // IN: strTableName--name of table whose fields will be added, deleted, or viewed // IN: pParent--pointer to parent of the dialog // CDlgAddTable::CDlgAddTable(CDaoDatabase *pDatabase, CString strTableName, CWnd * pParent) : CDialog(CDlgAddTable::IDD, pParent) { // the incoming database pointer that is used in creating the tabledef m_pDatabase = pDatabase; // transfer name of table (if any) to member m_strTableName = strTableName; // call centralized function to initialize dialog data and other // member data initializer(); } // set up the members of this class void CDlgAddTable::initializer() { // initialize the field info members (including ordinal position) fieldInitializer(TRUE); // no fields added to table yet m_bFirstFieldAdded = FALSE; // when DoDataExchange is executed, check validity of transferred data m_bCheckDDV = TRUE; // no tabledef until it is created with the user specified name m_pTableDef = NULL; // the index into the collection of fields for the current field m_nFieldIndex = 0; // initialize type map. The combobox list is sequential, the internal // constants for DAO are not. m_nTypeMap[Boolean] = dbBoolean; m_nTypeMap[Byte] = dbByte; m_nTypeMap[Currency] = dbCurrency; m_nTypeMap[Date] = dbDate; m_nTypeMap[Double] = dbDouble; m_nTypeMap[Long] = dbLong; m_nTypeMap[LongBinary] = dbLongBinary; m_nTypeMap[Memo] = dbMemo; m_nTypeMap[Short] = dbInteger; // it is really a short integer m_nTypeMap[Single] = dbSingle; m_nTypeMap[Text] = dbText; } // initialize the field information to a default state. // IN: bInitOrdPos -- The ordinal position is conditionally initialized // to allow for it autoincrementing with each addition of a field to the // tabledef--TRUE by default void CDlgAddTable::fieldInitializer(BOOL bInitOrdPos /* = TRUE */) { // conditionally initialize the ordinal position if (bInitOrdPos) m_FI.m_nOrdinalPosition = 0; // always initialize the rest m_FI.m_strName = _T(""); m_FI.m_nType = -1; // initialized for the benefit of the combox m_FI.m_lSize = 0; m_FI.m_strDefaultValue = _T(""); m_FI.m_bRequired = FALSE; m_FI.m_lAttributes = dbUpdatableField; #if _MFC_VER == 0x400 m_FI.m_bAllowZeroLength = TRUE; #else m_FI.m_bAllowZeroLength = FALSE; #endif; m_FI.m_strValidationRule = _T(""); m_FI.m_strValidationText = _T(""); } void CDlgAddTable::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CDlgAddTable) DDX_Control(pDX, IDC_VALIDATION_TEXT, m_ValidationTextControl); DDX_Control(pDX, IDC_VALIDATION_RULE, m_ValidationRuleControl); DDX_Control(pDX, IDC_REQUIRED, m_RequireControl); DDX_Control(pDX, IDC_POSITION, m_OrdinalPositionControl); DDX_Control(pDX, IDC_NAME, m_NameControl); DDX_Control(pDX, IDC_THE_SIZE, m_SizeControl); DDX_Control(pDX, IDC_DEFAULT_VALUE, m_DefaultValueControl); DDX_Control(pDX, IDC_AUTO_INCR_FIELD, m_AutoIncrFieldControl); DDX_Control(pDX, IDC_FIXED_FIELD, m_FieldVariabilityControl); DDX_Text(pDX, IDC_TABLE_NAME, m_strTableName); //}}AFX_DATA_MAP // move those DDX calls that use DDV or map directly to members // of the field info struct, or require special processing outside // of the wizard block DDX_Control(pDX, IDC_TYPE, m_TypeControl); // only check validity if so instructed--avoids unnecessary warnings if (m_bCheckDDV) // user must select a type for the field DDV_NoSel(pDX, &m_TypeControl); // direct mapping to field info struct DDX_Text(pDX, IDC_POSITION, m_FI.m_nOrdinalPosition); DDX_Text(pDX, IDC_NAME, m_FI.m_strName); DDX_Text(pDX, IDC_VALIDATION_RULE, m_FI.m_strValidationRule); DDX_Text(pDX, IDC_VALIDATION_TEXT, m_FI.m_strValidationText); DDX_Text(pDX, IDC_THE_SIZE, m_FI.m_lSize); DDX_Text(pDX, IDC_DEFAULT_VALUE, m_FI.m_strDefaultValue); DDX_Check(pDX, IDC_REQUIRED, m_FI.m_bRequired); // manage the type control directly since we need to use the type // map (combobox uses sequential index, DAO types are non-sequential) if (pDX->m_bSaveAndValidate) { // when transferring from dialog control to member, use map the // easy way--just index into it theTypes selectedType = (theTypes) m_TypeControl.GetCurSel(); m_FI.m_nType = m_nTypeMap[selectedType]; } else { // if the type is -1, then no type selected if (m_FI.m_nType == -1) { // update control appropriately m_TypeControl.SetCurSel(m_FI.m_nType); } else { // must search the map looking for a match-- // search for actual type to determine internally used type for (int i = 0; (i < MAX_TYPES) && (m_nTypeMap[i] != m_FI.m_nType); i++) ; // update control appropriately m_TypeControl.SetCurSel(i); } } // do attribute specific processing -- member values are encoded in // the m_lAttributes of CDaoFieldInfo struct if (pDX->m_bSaveAndValidate) { // first do transfer DDX_Radio(pDX, IDC_FIXED_FIELD, m_FieldVariability); DDX_Check(pDX, IDC_AUTO_INCR_FIELD, m_bAutoIncrField); // if transferring to member from control, determine the // attributes value to store for field if (m_bAutoIncrField) m_FI.m_lAttributes |= dbAutoIncrField; else m_FI.m_lAttributes &= ~dbAutoIncrField; // for text fields only, set the variability if (m_FI.m_nType == dbText) if (m_FieldVariability == 0) m_FI.m_lAttributes |= dbFixedField; else m_FI.m_lAttributes |= dbVariableField; } else { // transferring from field attributes to controls m_bAutoIncrField = (m_FI.m_lAttributes & dbAutoIncrField) == dbAutoIncrField; if ((m_FI.m_lAttributes & dbFixedField) == dbFixedField) m_FieldVariability = 0; else m_FieldVariability = 1; // now transfer the information to the controls DDX_Radio(pDX, IDC_FIXED_FIELD, m_FieldVariability); DDX_Check(pDX, IDC_AUTO_INCR_FIELD, m_bAutoIncrField); } } // check if a type selection was made--one must be made void CDlgAddTable::DDV_NoSel(CDataExchange* pDX, CComboBox *theControl) { // only process if transferring to member from control. If // no selection has been made, this constitutes an error condition if (pDX->m_bSaveAndValidate && (CB_ERR == theControl->GetCurSel())) { AfxMessageBox(_T("You must select a type."), MB_ICONEXCLAMATION); pDX->m_hWndLastControl = theControl->m_hWnd; pDX->Fail(); } } BEGIN_MESSAGE_MAP(CDlgAddTable, CDialog) //{{AFX_MSG_MAP(CDlgAddTable) ON_BN_CLICKED(IDC_NEXT_FIELD, OnNextField) ON_CBN_SELENDOK(IDC_TYPE, OnSelendokType) ON_BN_CLICKED(IDOK, OnDone) ON_WM_CLOSE() ON_BN_CLICKED(IDC_PREVIOUS_FIELD, OnPreviousField) ON_BN_CLICKED(IDC_DELETE_FIELD, OnDeleteField) ON_BN_CLICKED(IDC_ADD_FIELD, OnAddField) ON_CBN_CLOSEUP(IDC_TYPE, OnCloseupType) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CDlgAddTable message handlers BOOL CDlgAddTable::OnInitDialog() { CDialog::OnInitDialog(); // user either specified new table or existing table if(!IsExistentTable(m_pDatabase, m_strTableName)) { // initialize size control to disabled until we know field type // since that is what defines size (unless variable field type) m_SizeControl.EnableWindow(FALSE); // until a field has been added, can not be "done" since a tabledef // must contain at least one field CButton *pButton = (CButton *)GetDlgItem(IDOK); pButton->EnableWindow(FALSE); } else { // user specified existing table, so populate // dialog with information from first field // // open the table rather than creating it if (!openTableDef(m_pDatabase, &m_pTableDef, m_strTableName)) return TRUE; // get the information for the first field (no error reporting) if (getFieldInfo(m_pTableDef, &m_FI, m_nFieldIndex, FALSE)) { // there is a first field so set controls appropriate setTypeDependentStatesAndExisting(m_FI.m_nType, TRUE); } else { // no first field (user must have delete all of them), // so come up initialized and updatable first time fieldInitializer(FALSE); setTypeDependentStatesAndExisting(m_FI.m_nType, FALSE); } // the first field has already been added long ago m_bFirstFieldAdded = TRUE; UpdateData(FALSE); } // set focus to name of field edit box CEdit *pEdit = (CEdit *)GetDlgItem(IDC_NAME); pEdit->SetFocus(); // set the range of the spin control CSpinButtonCtrl* pOrdPosSpin = (CSpinButtonCtrl*) GetDlgItem(IDC_SPIN_ORDINAL_POSITION); pOrdPosSpin->SetRange(0,100); return FALSE; // return TRUE unless you set the focus to a control } // user selected to go to next existing field--prompt for acceptance // since we lose the currently specified field if it is not added void CDlgAddTable::OnNextField() { // get values from control -- no validity checking m_bCheckDDV = FALSE; // turn validity checking off UpdateData(TRUE); m_bCheckDDV = TRUE; // turn checking back on--don't forget this! // action depends on if this is an existing field or not // if this is a new field see if user wants to save it // or not--if not, then try to move to next field if ((m_FI.m_strName != _T("")) && (!IsExistentField(m_pTableDef, m_FI.m_strName))) if (IDYES != AfxMessageBox(_T("Current field will be ignored unless added. Continue anyway?"), MB_YESNO)) return; // can only move next if there is a item at the current index in the // collection--else you keep indexing into unused parts of the index. // So, check for existence of current indexed item and only increment // the index if there is an item (no error reporting) if (getFieldInfo(m_pTableDef, &m_FI, m_nFieldIndex, FALSE)) // move to next field index m_nFieldIndex += 1; // get the information for the next field if possible-- // last parameter indicates no failure reporting if (getFieldInfo(m_pTableDef, &m_FI, m_nFieldIndex, FALSE)) { // some settings are type dependent setTypeDependentStatesAndExisting(m_FI.m_nType, TRUE); } else { // initialize field property values for new field fieldInitializer(FALSE); // set ordinal position to this index just for neatness m_FI.m_nOrdinalPosition = m_nFieldIndex; // some settings are type dependent setTypeDependentStatesAndExisting(m_FI.m_nType, FALSE); } UpdateData(FALSE); // set focus to name of field edit box CEdit *pEdit = (CEdit *)GetDlgItem(IDC_NAME); pEdit->SetFocus(); } // user selected a type for the field from the type combobox void CDlgAddTable::OnSelendokType() { // need to transfer values into member variables // otherwize the UpdateData(FALSE) at the end will // wipe out all the values the user entered. // Do not do any validity checks m_bCheckDDV = FALSE; // turn validity checking off UpdateData(TRUE); m_bCheckDDV = TRUE; // turn checking back on--don't forget this! // once a type is selected, it is time to enable the size edit control m_SizeControl.EnableWindow(); // now set the states that depend on the type setTypeDependentStatesAndExisting(m_FI.m_nType, FALSE); // update the controls with new info UpdateData(FALSE); } // user can scroll in dropdown--must handle this possibility. Just pretend // it is a selection that ended OK void CDlgAddTable::OnCloseupType() { OnSelendokType(); } // user is done adding/viewing/deleting fields and is ready to finalize // the table structure void CDlgAddTable::OnDone() { // by default, we are ready to exit the dialog int retCode = IDYES; // if user has entered a field name, then warn them they will lose // it if it is not explicitly added--we don't want to do an auto-add // since user may have entered name without meaning to add another field // // see if there is a field name specified CString name; CEdit *pEdit = (CEdit *)GetDlgItem(IDC_NAME); pEdit->GetWindowText(name); // if there is a name, then warn the user if (name.GetLength() != 0) { // only an issue if this is not an existing field if (!IsExistentField(m_pTableDef, name)) retCode = AfxMessageBox(_T("Current field will be ignored unless added. Continue anyway?"), MB_YESNO); } // either there never was a field name specified or the user has // chosen not to add the field if (retCode == IDYES) { // done with tabledef object delete m_pTableDef; // end the dialog CDialog::EndDialog(0); } } // user cancelled tabledef creation process void CDlgAddTable::OnClose() { // if we have already allocated a tabledef object, better // clean it up if (m_pTableDef != NULL) delete m_pTableDef; // let the base class cleanup the rest CDialog::OnClose(); } // user selected to move to previous existing field, prompt for acceptance since // we lose any currently specified field unless is is added to the collection void CDlgAddTable::OnPreviousField() { // can't go previous if at 0th index if (m_nFieldIndex >= 1) { // get values from control -- no validity checking m_bCheckDDV = FALSE; // turn validity checking off UpdateData(TRUE); m_bCheckDDV = TRUE; // turn checking back on--don't forget this! // by default, move previous int retCode = IDYES; // if user has entered a field name, then warn them they will lose // it if it is not explicitly added--we don't want to do an auto-add // since user may have entered name without meaning to add another field // // if there is a name, then warn the user if (m_FI.m_strName != _T("")) { // only an issue if this is not an existing field if (!IsExistentField(m_pTableDef, m_FI.m_strName)) retCode = AfxMessageBox(_T("Current field will be ignored unless added. Continue anyway?"), MB_YESNO); } // either there never was a field name specified or the user has // chosen not to add the field if (retCode == IDYES) { // move previous m_nFieldIndex -= 1; // get the information for the previous field (no error reporting) // if there is info, display appropriate to existing field // if no info, treat this as a new field the user can set if(getFieldInfo(m_pTableDef, &m_FI, m_nFieldIndex, FALSE)) { // some settings are type dependent setTypeDependentStatesAndExisting(m_FI.m_nType, TRUE); } else { // initialize field property values for new field fieldInitializer(FALSE); // set ordinal position to this index just for neatness m_FI.m_nOrdinalPosition = m_nFieldIndex; // some settings are type dependent setTypeDependentStatesAndExisting(m_FI.m_nType, FALSE); } // update the dialog controls with new values UpdateData(FALSE); } } } // user has selected to delete the current field, prompt for acceptance void CDlgAddTable::OnDeleteField() { // get values from control -- don't continue if failure if (!UpdateData(TRUE)) return; // can only delete existing fields if (IsExistentField(m_pTableDef, m_FI.m_strName)) { // is user sure? if (IDYES == AfxMessageBox(_T("Delete current field?"), MB_YESNO)) { // only react if field is deleted! if (deleteField(m_pTableDef, m_FI.m_strName)) { // index into collection is unchanged, so // get the information for this field if there is one // (no error reporting) if (getFieldInfo(m_pTableDef, &m_FI, m_nFieldIndex, FALSE)) { // there is a field, so set type depenedent properties // and existing field properties of controls setTypeDependentStatesAndExisting(m_FI.m_nType, TRUE); } else { // there is no field in collection following the // deletion at this index, so // set the field info to initial state fieldInitializer(FALSE); // clear read-only status of control setTypeDependentStatesAndExisting(m_FI.m_nType, FALSE); } // update the dialog controls to erase deleted field UpdateData(FALSE); } } } } // control availability depends on two things--the type of the field and // whether this is an existing field or new. Some controls only make // sense for certain types (like autoincrement for longs only). Existing // fields have primarily read-only properties. This function takes both // into account when setting control states // // Note: since disabled is acceptable for existing field controls, you will // not see the bExisting taken into consideration if we are disabling // a control based on type // // IN: theType--the type of the current field (long, text, etc.) // IN: bExistingField--TRUE if this is an existing field, else false // void CDlgAddTable::setTypeDependentStatesAndExisting(int theType, BOOL bExistingField) { // only longs can be auto increment if (theType == dbLong) m_AutoIncrFieldControl.EnableWindow(TRUE && (!bExistingField)); else m_AutoIncrFieldControl.EnableWindow(FALSE); // except for text, setting variability of // field size is not an option if (theType == dbText) { // the state of controls depends on if this is an existing field or new CButton *pButton = (CButton *)GetDlgItem(IDC_FIXED_FIELD); pButton->EnableWindow(!bExistingField); pButton = (CButton *)GetDlgItem(IDC_VARIABLE_FIELD); pButton->EnableWindow(!bExistingField); } else { // the controls are disabled for all but non-existing text fields CButton *pButton = (CButton *)GetDlgItem(IDC_FIXED_FIELD); pButton->EnableWindow(FALSE); pButton = (CButton *)GetDlgItem(IDC_VARIABLE_FIELD); pButton->EnableWindow(FALSE); } // by default, the default value depends only on field existence m_DefaultValueControl.EnableWindow(TRUE && (!bExistingField)); // by default the validation depends only on field existence m_ValidationTextControl.SetReadOnly(bExistingField); m_ValidationRuleControl.SetReadOnly(bExistingField); // In general, the selected type determines the following: // * the size of the field (in bytes) // * whether the size is user modifiable or not // * what the setting for field variability is by default switch(theType) { case dbBoolean: case dbByte: m_SizeControl.SetReadOnly(); m_FI.m_lSize = 1; // set to fixed field type m_FI.m_lAttributes &= ~dbVariableField; m_FI.m_lAttributes |= dbFixedField; break; case dbInteger: m_SizeControl.SetReadOnly(); m_FI.m_lSize = 2; // set to fixed field type m_FI.m_lAttributes &= ~dbVariableField; m_FI.m_lAttributes |= dbFixedField; break; case dbLong: case dbSingle: m_SizeControl.SetReadOnly(); m_FI.m_lSize = 4; // set to fixed field type m_FI.m_lAttributes &= ~dbVariableField; m_FI.m_lAttributes |= dbFixedField; break; case dbCurrency: case dbDate: case dbDouble: m_SizeControl.SetReadOnly(); m_FI.m_lSize = 8; // set to fixed field type m_FI.m_lAttributes &= ~dbVariableField; m_FI.m_lAttributes |= dbFixedField; break; case dbText: m_SizeControl.SetReadOnly(FALSE | bExistingField); // set to variable field type m_FI.m_lAttributes &= ~dbFixedField; m_FI.m_lAttributes |= dbVariableField; break; case dbLongBinary: m_SizeControl.SetReadOnly(FALSE | bExistingField); // set to variable field type m_FI.m_lAttributes &= ~dbFixedField; m_FI.m_lAttributes |= dbVariableField; break; case dbMemo: m_SizeControl.SetReadOnly(FALSE | bExistingField); // set to variable field type m_FI.m_lAttributes &= ~dbFixedField; m_FI.m_lAttributes |= dbVariableField; break; } // end of switch // now handle setting availability for controls that are not type // dependent m_NameControl.SetReadOnly(bExistingField); m_OrdinalPositionControl.SetReadOnly(bExistingField); m_TypeControl.EnableWindow(!bExistingField); m_RequireControl.EnableWindow(!bExistingField); UpdateData(FALSE); } // user selected to add a new field to the collection // NOTE: here is also where the tabledef is created and // appended to the tabledef collection. The tabledef // is created before the first field is created and appended // after which it is appended to the tabledef collection void CDlgAddTable::OnAddField() { // get values from control -- don't continue if failure if (!UpdateData(TRUE)) return; // don't do anything if this is an existing field (except // say so) if (!IsExistentField(m_pTableDef, m_FI.m_strName)) { // if no fields added yet, then this is first so create the tabledef if (!m_bFirstFieldAdded) { // try to create the tabledef, if failure, stop processing if (!createNewTableDef(m_pDatabase, &m_pTableDef, m_strTableName)) return; } // try to create the field with error checking--may fail if a // duplicate named field already exists. Note: creating a field // also appends it to the tabledef's field collection if (!createNewField(m_pTableDef, &m_FI)) { // if this is the first field, then failure means we delete the // table we just created if (!m_bFirstFieldAdded) deleteTable(m_pDatabase, m_strTableName); // done return; } // field created successfully, so continue doing required // initialization if (!m_bFirstFieldAdded) { // append the tabldef to the database collection of tabledefs // this must be done after at least the first first field // has been added if (!appendTableDef(m_pDatabase, m_pTableDef)) { // if append fails, then we should delete the first field // since it is probably the culprit and we should allow // user to set new properties deleteField(m_pTableDef, m_FI.m_strName); // delete the table too if since this is the first field // that failed--else we will have a created table that is // not appended deleteTable(m_pDatabase, m_strTableName); // done return; } else // now first field has been added successfully m_bFirstFieldAdded = TRUE; // now that first field added, can possbily be "done" CButton *pButton = (CButton *)GetDlgItem(IDOK); pButton->EnableWindow(TRUE); } // clean out all fields except ordinal position to prepare // user to enter next field's properties fieldInitializer(FALSE); // manage the ordinal position -- autoincrement // Note: it is OK for fields to have the same ordinal position-- // see the DAO docs for this property. // also increment the index into the collection m_FI.m_nOrdinalPosition += 1; m_nFieldIndex += 1; // performs visible clearing of controls that are initialized UpdateData(FALSE); } else { AfxMessageBox(_T("Can't add field--it already exists in the TableDef.")); } // set focus to name of field edit box CEdit *pEdit = (CEdit *)GetDlgItem(IDC_NAME); pEdit->SetFocus(); }