Accounting Software
Small Business Software Estimating Software
Time Tracking SoftwareTime Management SoftwareTime Billing SoftwareContact Management SoftwareCustomer Management SoftwareProject Management SoftwareBusiness Management Software

Object Data Tables (Source Code)

Link to: header | source 2 | tables directory

Copyright Turtle Creek Software 1996-2006. All Rights Reserved.

This class manages object data tables for the Goldenseal estimating software,
small business management software, construction project management software and
construction estimating software.

Comments

CObjectDataTable

a table showing a list of objects and data for them. The columns of the table
are members of a particular class. This class is used for database utility
reports.

CObjectDataTables only store an array of object id's, and retrieves data
from each object only when the row for that object is drawn. This cuts down
on the overall memory requirement for the table. A CObjectDataTable can
display many thousands of rows, while a CReportTable or CSpreadsheetTable
runs out of memory if too many rows, since they store all cell contents in
RAM-- even those not currently in view.

On the other hand, the objects in a CObjectDataTables need to be accessed
whenever the table is redrawn, which may be multiple times. Scrolling the
table results in a ton of disk access since we're accessing many objects
for just a brief period of display.

NOTE: For similar tables used in reports, see CReportTable. That used
to be a subclass of this class, but it is now very different.

CRegisterBreakdownTable will probably use a similar memory storage scheme.

SUPERCLASS = CTCS_Table

Constructor

/*********************************************************************************
struct constructor
*********************************************************************************/
CObjectDataTable::CObjectDataTable(const SPaneInfo &inPaneInfo,
const SViewInfo &inViewInfo)
: CTCS_Table(inPaneInfo, inViewInfo)
{
InitObjectDataTable();
mTitleBarHeight = 0;
mTableType = 0;
}
/*********************************************************************************
PP stream constructor, called when being created from a Constructor
resource
*********************************************************************************/
CObjectDataTable::CObjectDataTable(LStream *inStream)
: CTCS_Table(inStream)
{
InitObjectDataTable();
mTitleBarHeight = mRowHeight;
}/*********************************************************************************
destructor
*********************************************************************************/
CObjectDataTable::~CObjectDataTable()
{
TCS_Forget(mObjectInfoArray); // TCS 7/16/02

gShowOnlyOneError = false; // TCS 7/22/03
}

Source Code

/*********************************************************************************

InitObjectDataTable

private initializer

*********************************************************************************/
void CObjectDataTable::InitObjectDataTable()
{
mObjectInfoArray = nil;
mDescriptor = nil;

// we want to erase the grayscale background TCS 12/27/01
mEraseFirst = true;
}/*********************************************************************************

CreateTitleCaptions

create the captions for the column titles

*********************************************************************************/
void CObjectDataTable::CreateTitleCaptions()
{
const SInt32 cTitleBarGap = 4;

// first let's figure out the top-left corner of this
// table, in superview coords. We start by getting the
// local frame rect
TCS_Rect frameR;
CalcPortFrameRect(frameR);
SInt32 colLeft = frameR.left,
colTop = frameR.top;

// ok, we have the topleft of the first caption. Let's now loop
// through all the columns and add the captions
SPaneInfo paneInfo = CTCS_Pane::sDefaultPaneInfo;
SLETableColInfo colInfo;
CTCS_Caption *titleCaption;
SInt32 colWidth = 0;
for (SInt32 i = 1; i <= GetNumCols(); i++)
{
// get the column information for this column
TCS_ASSERTMsg(GetColInfo(i, colInfo), TCS_GetErrString(errID_BadColumn));
if (i > 1)
{ // increment the left coord of the caption by
// colWidth, which is the width of the prev column
colLeft += colWidth;
}
// get the new column width
colWidth = GetColWidth(i);

// fill in the pane info with the information for the title caption
paneInfo.left = colLeft;
paneInfo.top = colTop;
paneInfo.width = colWidth;
paneInfo.height = GetRowHeight();
paneInfo.superView = GetSuperView();

// got the pane info, now create the caption, which is
// bold by default
titleCaption =
NEW CTCS_Caption(paneInfo, colInfo.titleTextInfo, GetColTitle(i));
TCS_FailNILMsg(titleCaption, TCS_GetErrString(errID_BadName));
titleCaption->FinishCreate();
}
// now we are done creating the captions, we need to move
// the table itself down the by the title bar height
MoveBy(0, GetTitleBarHeight(), cRedraw);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

SetColInfo

update column info for the given column

*********************************************************************************/
void CObjectDataTable::SetColInfo(const TableIndexT column, const SLETableColInfo &colInfo,
Boolean redraw)
{
// update the array element
mColumns.AssignItemAt(column, colInfo);

if (redraw)
Refresh();
}
/*********************************************************************************

SetColTag

update column tag for the given column

*********************************************************************************/
void CObjectDataTable::SetColTag(const TableIndexT col, const TagType tag)
{
SLETableColInfo colInfo;
if (GetColInfo(col, colInfo))
{
colInfo.tag = tag;
SetColInfo(col, colInfo);
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

CheckCache

return an array containing the ids of objects in the cache
*********************************************************************************/
static void *Func_CheckCache(const CNeoCollection *aNode, const short aOffset,
const NeoLockType /*aLock*/, TFullObjectInfoArray *array,
CProgressBar *progressBar)
{
TCS_FailNILMsg(array, TCS_GetErrString(errID_BadArray));
TCS_FailNILMsg(progressBar, TCS_GetErrString(errID_BadWindow));

TCS_TRY // TCS 7/5/01
{
// sanity check
const CNeoIDIndex *node = (const CNeoIDIndex *)aNode;
TCS_FailNILMsg(node, TCS_GetErrString(errID_BadArray));
TCS_ASSERTMsg(aOffset >= 0 && aOffset < node->getCount(), // rev TCS 1/16/00
TCS_GetErrString(errID_BadValidateAt) + "CCacheReportWindow::CheckCache");

SFullObjectInfo info;

// get the object
CNeoPersist *object = node->fEntry[aOffset].fObject;

if (object)
{ // it's in the cache, add it to the array
info.itemID = object->getID();
info.classID = object->getClassID(); // bugfix TCS 12/11/01
array->Append(info);
}

if (progressBar)
{
// if we have a progress bar, we allow users to stop TCS 10/10/02
if (progressBar->Increment() != progress_continue)
{
return progressBar; // we need to return a pointer to halt it. OK.
}
}
}
TCS_CATCH {}

// return nil so we continue to the next object in the cache
return nil;
}
/*********************************************************************************

FillObjectsInCache rev TCS 7/16/02

walk the cache and get a list of objects in it
*********************************************************************************/
void CObjectDataTable::FillObjectsInCache()
{
NeoTestFunc1 iterFunc = NeoNewTestFunc1(Func_CheckCache);

// sanity check
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
TCS_FailNILMsg(iterFunc, TCS_GetErrString(errID_BadIterator));

// create a new array
if (mObjectInfoArray)
{
mObjectInfoArray->RemoveAllItems();
}
else
{
mObjectInfoArray = NEW TFullObjectInfoArray;
TCS_FailNILMsg(mObjectInfoArray, TCS_GetErrString(errID_BadArray));
}

SInt32 objectCount = gDBFile->GetTotalObjectCount();

// set up a progress bar, since it may take a while to look
// at every record.
CProgressBar *progressBar = CProgressBar::CreateBar(TCS_GetStockString(stockID_Records),
DLOG_ProgressWithStop); // rev TCS 10/10/02
TCS_FailNILMsg(progressBar, TCS_GetErrString(errID_BadWindow));

CProgressBarWatcher watcher (progressBar);

progressBar->SetMaxValue(objectCount);
progressBar->SetWindowTitle(TCS_GetStockString(stockID_FetchRecords)); // TCS 12/11/02
progressBar->SetMessage(TCS_GetStockString(stockID_TestRecords));
progressBar->SetActionType(TCS_GetStockString(stockID_Records));

// got the array, use the db's doUntilObject method to
// walk all the indices, so we can see which objects are now cached
gDBFile->doUntilObject(nil, // no start object
id_NeoPersist, // start with the base class
kNeoPrimaryIndex, // index to start with
cIncludeSubclasses,
iterFunc,
mObjectInfoArray,
progressBar);

SetNumRows(mObjectInfoArray->GetCount());

// tidy up TCS 12/6/00
TCS_DisposeRoutine(iterFunc);
}

/*********************************************************************************

FillClassesInDB

fill in a list of classes from the database
*********************************************************************************/
void CObjectDataTable::FillClassesInDB(const Boolean includeNeoObjects)
{
// sanity check
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

// create a new array
if (mObjectInfoArray)
{
mObjectInfoArray->RemoveAllItems();
}
else
{
mObjectInfoArray = NEW TFullObjectInfoArray;
TCS_FailNILMsg(mObjectInfoArray, TCS_GetErrString(errID_BadArray));
}

DB_PersistentObject *object = nil;
SFullObjectInfo info;

CCursorSpinner spinner; // TCS 12/31/02
++spinner;

// if we're including Neo objects, first include them
if (includeNeoObjects)
{
DB_FileManager::FillNeoClassesList(mObjectInfoArray);
++spinner;
}

// loop thru our class ID list
for (DBid classID = id_FirstClass; classID < id_LastClass; classID++)
{
++spinner;

if (CLASS_DESC::HasDescriptor(classID, false)) // rev TCS 7/31/01
{
TCS_TRY // TCS 7/5/01
{
SInt32 objectCount = gDBFile->GetObjectCount(classID);
if (objectCount)
{
object = gDBFile->GetFirstObject(classID);
TCS_FailNILMsg(object, TCS_GetErrString(errID_BadObject));

DB_ObjectWatcher watcher(object);

info.classID = classID;
info.itemID = object->GetDBID();

mObjectInfoArray->Append(info);
}
}
TCS_CATCH {} // catch here so we keep going even if a bad object
}
}

SetNumRows(mObjectInfoArray->GetCount());
}

/*********************************************************************************

FillObjectOrder

fill in a list of record objects, in the order they appear in the file
*********************************************************************************/
void CObjectDataTable::FillObjectOrder(const Boolean /*includeNeoObjects*/)
{
// sanity check
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

CCursorSpinner spinner; // TCS 12/29/02
++spinner;

// this is memory-intensive, so clear some space TCS 12/29/02
gDBFile->SaveAllAndPurge();

++spinner;

// create a new array
if (mObjectInfoArray)
{
mObjectInfoArray->RemoveAllItems();
}
else
{
mObjectInfoArray = NEW TFullObjectInfoArray;
TCS_FailNILMsg(mObjectInfoArray, TCS_GetErrString(errID_BadArray));
}

++spinner;

{
DB_FileManager *fileManager = TCS_SAFE_CAST(gDBFile->GetFirstObject(id_FileManager),
DB_FileManager);
TCS_FailNILMsg(fileManager, TCS_GetErrString(errID_BadFileManager));
DB_ObjectWatcher watcher (fileManager);
fileManager->FetchObjectsAndSpaces(*mObjectInfoArray);
}

SetNumRows(mObjectInfoArray->GetCount());
}

/*********************************************************************************

FillObjectsInClass TCS 9/29/98, rewrite 7/16/02

fetch a list of objects in the given class. We use this for the class details
when you double-click on a row in the Database Report.
*********************************************************************************/
void CObjectDataTable::FillObjectsInClass(const DBid classID)
{
// sanity check
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

// create a new array
if (mObjectInfoArray)
{
mObjectInfoArray->RemoveAllItems();
}
else
{
mObjectInfoArray = NEW TFullObjectInfoArray;
TCS_FailNILMsg(mObjectInfoArray, TCS_GetErrString(errID_BadArray));
}

if (IS_NEO_CLASS_ID(classID))
{
DB_FileManager::FillNeoClassList(classID, mObjectInfoArray);
}
else
{
TObjectIDArray idArray;

// fill in an array of object ID's. We special case for reports
// since the list manager only stores custom report id's. We also
// allow the option to bypass the list manager, so it's possible
// to view database contents even if the list mgr is corrupt TCS bugfix 1/19/04
if (classID == id_ReportLayout || TCS_ModifierKeyDown())
DB_ListManager::FillObjectIDArray(classID, idArray, true, false, false);
else
DB_ListManager::FillObjectIDArray(classID, idArray);

TObjectIDArrayIterator iterator (idArray);
DBid id;
SFullObjectInfo info;
info.classID = classID;

while (iterator.Next(id))
{
info.itemID = id;
mObjectInfoArray->Append(info);
}
}

SetNumRows(mObjectInfoArray->GetCount());
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

GetColTitle

return the title of the given column in a report table

*********************************************************************************/
CTextString CObjectDataTable::GetColTitle(const TableIndexT col) const
{
// we don't have user customzied titles
return GetStockColTitle(col);
}
/*********************************************************************************

GetStockColTitle

return the default title of the given column in a report table

*********************************************************************************/
CTextString CObjectDataTable::GetStockColTitle(const TableIndexT col) const
{
// sanity check
TCS_FailNILMsg(mDescriptor, TCS_GetErrString(errID_BadDescriptor));
// get the column information for the requested column
SLETableColInfo colInfo;
TCS_ASSERTMsg(mColumns.FetchItemAt(col, colInfo),
TCS_GetErrString(errID_BadColumn));
if (colInfo.tag == tag_totalcolumn)
return TCS_GetStockString(stockID_Total);
else
{
TCS_TRY
{
// get the member information for the column tag
// and extract the name from it
SMemberInfo memberInfo;
TCS_ASSERTMsg(mDescriptor->GetMemberInfo(colInfo.tag, &memberInfo),
TCS_GetErrString(errID_BadDescriptor));
return CTextString(memberInfo.memberName);
}
TCS_CATCH
{
}
return TCS_GetStockString(stockID_BadTitle);
}
}
/*********************************************************************************

SetObjectInfoArray

set the object id array used for the report contents

*********************************************************************************/
void CObjectDataTable::SetObjectInfoArray(TFullObjectInfoArray *array)
{
// take over the new one
mObjectInfoArray = array;

// we should have as many rows as there are
// objects in the array
SetNumRows(mObjectInfoArray->GetCount());
}
/*********************************************************************************

GetRowObjectInfo TCS 9/29/98

return the object info for the given row

*********************************************************************************/
SFullObjectInfo CObjectDataTable::GetRowObjectInfo(const TableIndexT row)
{
SFullObjectInfo info;

if (mObjectInfoArray->GetCount() >= row)
mObjectInfoArray->FetchItemAt(row, info);
else
{ // couldn't find, so we return nil info
info.itemID = 0;
info.classID = 0;
}
return info;
}