Link to: header | tables
directory
Copyright Turtle Creek Software 1996-2006. All Rights Reserved.
Comments
CDataTable
This class manages data tables for the Goldenseal
estimating software,
small business management software, construction
project management software and
construction estimating software.
It's a spreadsheet-style table, in which the date for each cell is stored in
a data holder.
We store a master array that contains one array for each column in the table.
Each of the column arrays contains one data pointer for each row in the column.
The data pointers reference a CHolder subclass which stores the actual data.
SUPERCLASS = CTCS_Table
Constructor
/*********************************************************************************
stream constructor
*********************************************************************************/
CDataTable::CDataTable(LStream *inStream)
: CTCS_Table(inStream)
{
mFillOnlyVisibleRows = false;
mTableDataSize = 0;
}/*********************************************************************************
struct constructor
*********************************************************************************/
CDataTable::CDataTable(const SPaneInfo &inPaneInfo, const SViewInfo &inViewInfo)
: CTCS_Table(inPaneInfo, inViewInfo)
{
mFillOnlyVisibleRows = false;
mTableDataSize = 0;
}
/*********************************************************************************
destructor TCS 6/5/01
*********************************************************************************/
CDataTable::~CDataTable()
{
// we remove all rows. This will automatically delete all of the
// data holders that were allocated for the table. TCS 6/5/01
RemoveRows(GetNumRows(), false);
}
Source Code
/*********************************************************************************
InsertRows
insert the given number of rows after this table. This requires us to
add entries to the data arrays
*********************************************************************************/
void CDataTable::InsertRows(const TableIndexT howMany,
const Boolean redraw /*= true*/)
{
// important to call the inherited method to take care of usual
THE_SUPERCLASS::InsertRows(howMany, redraw);
// now walk through the rows to be added, and add a data holder
// for each cell in that row
CDataHolderPtrArray *colDataArray = nil;
for (TableIndexT row = 1; row <= howMany; row++)
{
// walk through each column in this row
CArrayOfDataArraysIterator iterator(mDataArrays);
while (iterator.Next(colDataArray))
{
TCS_FailNILMsg(colDataArray, TCS_GetErrString(errID_BadArray));
// create a data holder for this cell
CHolder *holder = GetNewDataHolder(row, iterator.GetCurrentIndex());
TCS_FailNILMsg(holder, TCS_GetErrString(errID_BadCell));
SInt32 size = sizeof(holder);
mTableDataSize += size;
// add the data holder to the column array
colDataArray->Append(holder);
}
}
}
/*********************************************************************************
InsertCols
insert the given number of cols after this table. This requires us to
create a data array for the new cols
*********************************************************************************/
void CDataTable::InsertCols(const TableIndexT howMany, const SInt32 inColWidth,
const Boolean redraw)
{
// add data arrays to our array of arrays
for (SInt32 ct = 1; ct <= howMany; ct++)
{
mDataArrays.Append(NEW CDataHolderPtrArray);
}
// the inherited method will add columns to the table
THE_SUPERCLASS::InsertCols(howMany, inColWidth, redraw);
}
/*********************************************************************************
RemoveRows
remove the given number of rows. The rows are removed from the end
of the table
*********************************************************************************/
void CDataTable::RemoveRows(const TableIndexT howMany, const Boolean redraw)
{
// remove the rows from the data arrays
CDataHolderPtrArray *dataArray = nil;
SInt32 startRow = GetNumRows() - howMany + 1;
for (TableIndexT col = 1; col <= GetNumCols(); col++)
{
TCS_ASSERTMsg(mDataArrays.FetchItemAt(col, dataArray), TCS_GetErrString(errID_BadArray));
TCS_FailNILMsg(dataArray, TCS_GetErrString(errID_BadArray));
dataArray->RemoveItemsAt(howMany, startRow);
}
// the inherited method will remove the rows
THE_SUPERCLASS::RemoveRows(howMany, redraw);
}/*********************************************************************************
RemoveCols
remove the given number of cols from this table.
*********************************************************************************/
void CDataTable::RemoveCols(const TableIndexT howMany, const Boolean redraw)
{
// let's remove the data arrays for these columns
mDataArrays.RemoveItemsAt(howMany, GetNumCols() - howMany + 1);
// the inherited method will remove any remaining data
THE_SUPERCLASS::RemoveCols(howMany, redraw);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
GetDataCellText
get the given cell's text. This retrieves the data from the array
*********************************************************************************/
CTextString CDataTable::GetDataCellText(const TableIndexT row, const TableIndexT col) const
{
// get the holder
const CHolder *holder = GetCellDataHolder(row, col);
if (holder)
return holder->GetCString();
else
return cEmptyString;
}/*********************************************************************************
GetDataCellValue
get the given cell's value. This retrieves the data from the array
*********************************************************************************/
SInt32 CDataTable::GetDataCellValue(const TableIndexT row, const TableIndexT col) const
{
// get the holder
const CHolder *holder = GetCellDataHolder(row, col);
if (holder)
return holder->GetValue();
else
return 0;
}
/*********************************************************************************
GetCellDataHolder
return the data holder for the given cell. This is a protected routine
as it returns the actual data holder, not a copy. The returned holder
is marked const so that the caller can't do anything dangerous with it
*********************************************************************************/
const CHolder *CDataTable::GetCellDataHolder(const TableIndexT row, const TableIndexT col) const
{
CDataHolderPtrArray *dataArray = GetColDataArray(col);
if (!dataArray)
return nil;
// get the holder
CHolder *holder = nil;
if (!dataArray->FetchItemAt(row, holder))
return nil;
return holder;
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
SetDataCellText
set the given data cell's text. This just updates the array entry,
it doesn't do anything with any actual cell field
*********************************************************************************/
Boolean CDataTable::SetDataCellText(const TableIndexT row, const TableIndexT col,
const CTextString &inText)
{
return SetCellDataHolder(row, col,(CHolder *) NEW CStringHolder(inText));
}
/*********************************************************************************
SetDataCellValue
set the given data cell's value. This just updates the array entry,
it doesn't do anything with any actual cell field
*********************************************************************************/
Boolean CDataTable::SetDataCellValue(const TableIndexT row, const TableIndexT col,
SInt32 inValue)
{
return SetCellDataHolder(row, col,(CHolder *) NEW CInt32Holder(inValue));
}
/*********************************************************************************
SetCellDataHolder
set the data holder for the given cell. The passed holder is placed in
the data array, so the caller should not do anything further with the
holder
*********************************************************************************/
Boolean CDataTable::SetCellDataHolder(const TableIndexT row, const TableIndexT col,
CHolder *dataHolder)
{
TCS_FailNILMsg(dataHolder, TCS_GetErrString(errID_BadCell));
CDataHolderPtrArray *dataArray = GetColDataArray(col);
if (!dataArray)
{
TCS_Forget(dataHolder); // bugfix TCS 1/30/03
return false;
}
SInt32 dataSize = 0;
// if the cell already has a data holder, better delete it TCS 9/26/01
// this code fixes a serious memory leak.
CHolder *currentHolder = nil;
if (dataArray->FetchItemAt(row, currentHolder))
{
dataSize = sizeof(currentHolder);
mTableDataSize -= dataSize;
TCS_Forget(currentHolder);
}
dataSize = sizeof(dataHolder);
mTableDataSize += dataSize;
// now update the cell holder
if (dataArray->AssignItemAt(row, dataHolder) != CTCS_Array::index_Bad)
{
return true;
}
else
{
TCS_Forget(dataHolder); // bugfix TCS 1/30/03
return false;
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
ClearRow
clear the given row
*********************************************************************************/
void CDataTable::ClearRow(const TableIndexT row)
{
// go through each column in the row and clear that cell
for (TableIndexT col = 1; col <= GetNumCols(); col++)
{
ClearCell(row, col);
}
}
/******************************************************************************
HandleCellDrag TCS 11/27/01
Handle a cell drag from one location to another. We override, since
we can just move data in the data array, rather than mess with text swapping
*******************************************************************************/
void CDataTable::HandleCellDrag(const TableCellT &sourceCell, const TableCellT &targetCell)
{
TableIndexT sourceRow = ROW(sourceCell),
targetRow = ROW(targetCell);
// No need for action if the rows are the same, or if they move up by
// just one row (since we drop the row 'under' the hilited target row)
if (sourceRow == targetRow || sourceRow == targetRow + 1)
return;
// if we are moving up, adjust the target so it drops 'under' the selected row
if (targetRow < sourceRow)
targetRow++;
// adjust the data arrays
SwapRowData(sourceRow, targetRow);
// refresh the table
RefreshRows(targetRow, sourceRow);
Refresh();
}
/******************************************************************************
SwapRowData TCS split 2/17/03
move data from one row to another. Use this for row moves and row insertions.
*******************************************************************************/
void CDataTable::SwapRowData(const TableIndexT sourceRow, const TableIndexT targetRow)
{
// go through each column in the row and switch the array members
CDataHolderPtrArray *dataArray = nil;
for (TableIndexT col = 1; col <= GetNumCols(); col++)
{
TCS_ASSERTMsg(mDataArrays.FetchItemAt(col, dataArray), TCS_GetErrString(errID_BadArray));
TCS_FailNILMsg(dataArray, TCS_GetErrString(errID_BadArray));
dataArray->MoveItem(sourceRow, targetRow);
mDataArrays.AssignItemAt(col, dataArray);
}
}
/*********************************************************************************
SortTableRows TCS 9/27/01 rev 4/16/02
sort rows in a table. Note that the incoming breakdown array may be nil.
*********************************************************************************/
void CDataTable::SortTableRows(const TagType colNumber, const Boolean sortAscending,
TObjectIDArray *breakdownArray)
{
if ((LastDataRow() - FirstDataRow()) < 2)
return;
CDataHolderPtrArray *dataArray = nil;
// first get a simple array with the revised row order
TUInt32Array rowArray = GetSortedRowArray(colNumber, sortAscending);
// adjust each of the column data arrays into the revised order
for (TableIndexT col = 1; col <= GetNumCols(); col++)
{
TCS_ASSERTMsg(mDataArrays.FetchItemAt(col, dataArray), TCS_GetErrString(errID_BadArray));
TCS_FailNILMsg(dataArray, TCS_GetErrString(errID_BadArray));
// start with a copy of the row array, since we modify the array as we sort to it
TUInt32Array tempRowArray = rowArray;
TUInt32ArrayIterator rowIterator(tempRowArray);
SInt32 currentIndex = 0;
SInt32 lookAheadIndex;
TableIndexT row, zeroRow = 0;
// go through the row-order array, and swap the data rows into the same
// order. The algorithm: we walk the row and swap data column by column. We also
// reset the position of the original data in the latter part of the row-order
// array, so the revised position will be used when swapping gets that far.
while (rowIterator.Next(row))
{
currentIndex++;
if (row > currentIndex)
{
dataArray->SwapItems(currentIndex, row);
// if we have an incoming breakdown array, we also swap it TCS 9/28/01
if (col == 1 && breakdownArray)
{
breakdownArray->SwapItems(currentIndex, row);
}
// look ahead in the row array and adjust the current row's position.
// we also zap the current index so FetchIndexOf won't get confused
lookAheadIndex = tempRowArray.FetchIndexOf(currentIndex);
if (lookAheadIndex > currentIndex)
{
tempRowArray.AssignItemAt(lookAheadIndex, row);
tempRowArray.AssignItemAt(currentIndex, zeroRow);
}
}
}
mDataArrays.AssignItemAt(col, dataArray);
}
// redraw the entire table.
Refresh();
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
GetSortedRowArray TCS 1/20/00
get an array of row positions, sorted by values in the given column
*********************************************************************************/
TUInt32Array CDataTable::GetSortedRowArray(const TagType colNumber, const Boolean sortAscending)
{
TUInt32Array rowArray;
UInt8 colType = GetColType(colNumber);
switch (colType)
{
case coltype_cv:
case coltype_lookup:
case coltype_menucv:
case coltype_menulookup:
case coltype_mixcv:
case coltype_mixedit:
case coltype_checkmark:
case coltype_checklist:
GetSortedStringArray(colNumber, sortAscending, rowArray);
break;
case coltype_edit:
case coltype_caption:
switch (GetFieldType(colNumber))
{
case fieldtype_string:
case fieldtype_cv:
case fieldtype_mixcv:
case fieldtype_mixstring:
case fieldtype_checkbox:
GetSortedStringArray(colNumber, sortAscending, rowArray);
break;
case fieldtype_money:
case fieldtype_emoney:
case fieldtype_percent:
case fieldtype_number:
case fieldtype_integer:
case fieldtype_posinteger:
GetSortedMoneyArray(colNumber, sortAscending, rowArray);
break;
case fieldtype_date:
GetSortedDateArray(colNumber, sortAscending, rowArray);
break;
default:
break;
}
break;
default:
break;
}
return rowArray;
}
/*********************************************************************************
GetSortedStringArray TCS 1/20/00 rev 4/17/02
get an array of row positions, sorted alphabetically by the text in
the given column
*********************************************************************************/
void CDataTable::GetSortedStringArray(const TagType colNumber, const Boolean sortAscending,
TUInt32Array &rowArray)
{
TNameIDArray *tempArray = NEW TNameIDArray; // rev TCS 1/27/03
TCS_FailNILMsg(tempArray, TCS_GetErrString(errID_BadArray));
TCS_TRY
{
SNameIDInfo info;
CTextString textString;
TableIndexT row;
// fill in a temporary array of row numbers and text from the given col
for (row = FirstDataRow(); row <= LastDataRow(); row++) // rev TCS 4/17/02
{
textString = GetCellText(row, colNumber);
if (textString.Length() >= cMenuTextLen) // TCS bugfix 10/10/02
textString.Truncate(cMenuTextLen - 1);
info.id = row;
TCS_BufferFromText(info.itemName, textString);
tempArray->Append(info);
}
// sort the temporary array
tempArray->SortWithComparator(NEW CListNameComparator);
// add any unsorted rows to the beginning TCS 4/17/02
for (row = 1; row < FirstDataRow(); row++)
{
rowArray.Append(row);
}
// fill in an id array that stores the row order for ALL rows.
if (sortAscending)
{
TNameIDArrayIterator downIterator(*tempArray);
while (downIterator.Next(info))
{
row = info.id;
rowArray.Append(row);
}
}
else
{
TNameIDArrayIterator downIterator(*tempArray, iter_from_end);
while (downIterator.Previous(info))
{
row = info.id;
rowArray.Append(row);
}
}
// add any unsorted rows at the end TCS 4/17/02
for (row = LastDataRow() + 1; row < LastRow(); row++)
{
rowArray.Append(row);
}
}
TCS_CATCH {}
TCS_Forget(tempArray); // TCS 1/27/03
}
/*********************************************************************************
GetSortedMoneyArray TCS 8/27/01
get an array of row positions, sorted alphabetically by the money value
in the given column
*********************************************************************************/
void CDataTable::GetSortedMoneyArray(const TagType colNumber, const Boolean sortAscending,
TUInt32Array &rowArray)
{
TMoneyIDInfoArray *tempArray = NEW TMoneyIDInfoArray; // TCS 1/30/03
TCS_FailNILMsg(tempArray, TCS_GetErrString(errID_BadArray));
TCS_TRY
{
SMoneyIDInfo info;
CTextString textString;
TableIndexT row;
// fill in a temporary array of row numbers and text from the given col
for (row = FirstDataRow(); row <= LastDataRow(); row++) // rev TCS 4/17/02
{
textString = GetCellText(row, colNumber);
info.id = row;
info.amount = CMoney(textString);
tempArray->Append(info);
}
// sort the array
tempArray->SortWithComparator(NEW CMoneyIDValueComparator);
// add any unsorted rows to the beginning TCS 4/17/02
for (row = 1; row < FirstDataRow(); row++)
{
rowArray.Append(row);
}
// fill in an id array that stores the row order
if (sortAscending)
{
TMoneyIDInfoArrayIterator downIterator(*tempArray);
while (downIterator.Next(info))
{
row = info.id;
rowArray.Append(row);
}
}
else
{
TMoneyIDInfoArrayIterator downIterator(*tempArray, iter_from_end);
while (downIterator.Previous(info))
{
row = info.id;
rowArray.Append(row);
}
}
// add any unsorted rows at the end TCS 4/17/02
for (row = LastDataRow() + 1; row < LastRow(); row++)
{
rowArray.Append(row);
}
}
TCS_CATCH {}
TCS_Forget(tempArray); // TCS 1/30/03
}
/*********************************************************************************
GetSortedDateArray TCS 8/27/01
get an array of row positions, sorted alphabetically by the date value
in the given column
*********************************************************************************/
void CDataTable::GetSortedDateArray(const TagType colNumber, const Boolean sortAscending,
TUInt32Array &rowArray)
{
TDateIDInfoArray *tempArray = NEW TDateIDInfoArray; // TCS 1/30/03
TCS_FailNILMsg(tempArray, TCS_GetErrString(errID_BadArray));
TCS_TRY
{
SDateIDInfo info;
CTextString textString;
TableIndexT row;
// fill in a temporary array of row numbers and text from the given col
for (row = FirstDataRow(); row <= LastDataRow(); row++) // rev TCS 4/17/02
{
textString = GetCellText(row, colNumber);
info.id = row;
info.date = CDate(textString);
tempArray->Append(info);
}
// sort the array
tempArray->SortWithComparator(NEW CDateIDValueComparator);
// add any unsorted rows to the beginning TCS 4/17/02
for (row = 1; row < FirstDataRow(); row++)
{
rowArray.Append(row);
}
// fill in an id array that stores the row order
if (sortAscending)
{
TDateIDInfoArrayIterator downIterator(*tempArray);
while (downIterator.Next(info))
{
row = info.id;
rowArray.Append(row);
}
}
else
{
TDateIDInfoArrayIterator downIterator(*tempArray, iter_from_end);
while (downIterator.Previous(info))
{
row = info.id;
rowArray.Append(row);
}
}
// add any unsorted rows at the end TCS 4/17/02
for (row = LastDataRow() + 1; row < LastRow(); row++)
{
rowArray.Append(row);
}
}
TCS_CATCH {}
TCS_Forget(tempArray); // TCS 1/30/03
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
GetTableSize TCS 1/27/03
get the memory size of the table. Right now this is just an approximation.
*********************************************************************************/
SInt32 CDataTable::GetTableSize() const
{
return THE_SUPERCLASS::GetTableSize() +
ARRAY_FILE_SIZE(mDataArrays) +
mTableDataSize;
}
/*********************************************************************************
MakeSingleRowTable
make this table a single-row table. This just sets the number of rows
equal to 1 and clears the first row
*********************************************************************************/
void CDataTable::MakeSingleRowTable(const Boolean redraw, const Boolean findMode)
{
THE_SUPERCLASS::MakeSingleRowTable(redraw, findMode);
ClearRow(1);
}
|