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 >
Wrap
C/C++ Source or Header
|
1998-03-26
|
24KB
|
733 lines
// 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();
}