857 lines
27 KiB
C++
857 lines
27 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1995 - 2000.
|
|
//
|
|
// File: categ.cxx
|
|
//
|
|
// Contents: Unique categorization class
|
|
//
|
|
// Classes: CCategorize
|
|
//
|
|
// History: 30 Mar 95 dlee Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <query.hxx>
|
|
|
|
#include "tabledbg.hxx"
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CCategorize, public
|
|
//
|
|
// Synopsis: Constructor for categorization class
|
|
//
|
|
// Arguments: [rCatSpec] -- categorization specification for this level
|
|
// [iSpec] -- 1-based categorization level, where smaller
|
|
// numbers are higher in the hierarchy.
|
|
// [pParent] -- categorization object that categorizes rows
|
|
// on this level (or 0 if none)
|
|
// [mutex] -- CAsyncQuery's mutex for serialization
|
|
//
|
|
// History: 6-1-95 dlee Created
|
|
//
|
|
// Notes: Category identifiers start at 0x40000000 plus 0x1000 times
|
|
// [iSpec]. The 0x40000000 is so it is obvious to the
|
|
// debugger that it is a category. The 0x1000*[iSpec] is
|
|
// so that it is obvious what level a category falls into.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CCategorize::CCategorize(
|
|
CCategorizationSpec & rCatSpec,
|
|
unsigned iSpec,
|
|
CCategorize * pParent,
|
|
CMutexSem & mutex)
|
|
: _iSpec( iSpec ),
|
|
_pParent( pParent ),
|
|
_pChild( 0 ),
|
|
_mutex( mutex ),
|
|
_iCategoryGen( 0x40000000 + ( 0x1000 * iSpec ) ),
|
|
_widCurrent( WORKID_TBLBEFOREFIRST ),
|
|
_fNotificationsEnabled( FALSE ),
|
|
_iFindHint( 0 ),
|
|
_aDynamicCategories( 0 ),
|
|
_aVisibleCategories( 16 )
|
|
{
|
|
if (rCatSpec.Type() != CATEGORIZE_UNIQUE)
|
|
THROW(CException( E_INVALIDARG ));
|
|
} //CCategorize
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CCategorize::GetRows, public
|
|
//
|
|
// Arguments: [widStart] - WORKID identifying first row to be
|
|
// transferred. If WORKID_TBLFIRST is
|
|
// used, the transfer will start at the first
|
|
// row in the segment.
|
|
// [chapt] - Chapter from which to fetch rows (if chaptered)
|
|
// [pOutColumns] - A CTableColumnSet that describes the
|
|
// output format of the data table.
|
|
// [rGetParams] - An CGetRowsParams structure which
|
|
// describes how many rows are to be fetched and
|
|
// other parameters of the operation.
|
|
// [rwidLastRowTransferred] - On return, the work ID of
|
|
// the last row to be transferred
|
|
// from this table. Can be used to
|
|
// initialize widStart on next call.
|
|
//
|
|
// Returns: SCODE - status of the operation. DB_S_ENDOFROWSET if
|
|
// widStart is WORKID_TBLAFTERLAST at start of
|
|
// transfer, or if rwidLastRowTransferred is the
|
|
// last row in the segment at the end of the transfer.
|
|
//
|
|
// STATUS_BUFFER_TOO_SMALL is returned if the available
|
|
// space in the out-of-line data was exhausted during
|
|
// the transfer.
|
|
//
|
|
// Notes: To transfer successive rows, as in GetNextRows, the
|
|
// rwidLastRowTransferred must be advanced by one prior
|
|
// to the next transfer.
|
|
//
|
|
// History: 6-1-95 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CCategorize::GetRows(
|
|
HWATCHREGION hRegion,
|
|
WORKID widStart,
|
|
CI_TBL_CHAPT chapt,
|
|
CTableColumnSet const & rOutColumns,
|
|
CGetRowsParams & rGetParams,
|
|
WORKID & rwidLastRowTransferred)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
TRY
|
|
{
|
|
if (WORKID_TBLAFTERLAST == widStart)
|
|
{
|
|
rwidLastRowTransferred = WORKID_TBLAFTERLAST;
|
|
return DB_S_ENDOFROWSET;
|
|
}
|
|
else if (widInvalid == widStart)
|
|
{
|
|
Win4Assert(! "CCategorize::GetRows widStart is widInvalid");
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
if ( 0 == _aVisibleCategories.Count() )
|
|
sc = DB_S_ENDOFROWSET;
|
|
else
|
|
{
|
|
unsigned iRow;
|
|
if ( widStart != WORKID_TBLFIRST &&
|
|
widStart != WORKID_TBLBEFOREFIRST )
|
|
iRow = _FindCategory( widStart );
|
|
else
|
|
iRow = 0;
|
|
|
|
rwidLastRowTransferred = 0;
|
|
|
|
while ( 0 != rGetParams.RowsToTransfer() &&
|
|
iRow < _aVisibleCategories.Count() )
|
|
{
|
|
// at the end of the chapter when this level is categorized?
|
|
|
|
if ( ( _isCategorized() ) &&
|
|
( DB_NULL_HCHAPTER != chapt ) &&
|
|
( _aVisibleCategories[ iRow ].catParent != chapt ) )
|
|
break; // code below will set sc = DB_S_ENDOFROWSET
|
|
|
|
BYTE* pbDst = (BYTE *) rGetParams.GetRowBuffer();
|
|
|
|
for ( unsigned col = 0; col < rOutColumns.Count(); col++ )
|
|
{
|
|
CTableColumn const & rDstColumn = *rOutColumns.Get(col);
|
|
PROPID pid = rDstColumn.GetPropId();
|
|
|
|
if ( pidChapter == pid || pidWorkId == pid )
|
|
{
|
|
if (rDstColumn.GetStoredType() == VT_VARIANT)
|
|
{
|
|
Win4Assert( rDstColumn.GetValueSize() == sizeof VARIANT );
|
|
CTableVariant * pVarnt = (CTableVariant *) ( pbDst +
|
|
rDstColumn.GetValueOffset() );
|
|
pVarnt->vt = VT_UI4;
|
|
pVarnt->ulVal = _aVisibleCategories[iRow].catID;
|
|
}
|
|
else
|
|
{
|
|
Win4Assert( rDstColumn.GetValueSize() == sizeof CI_TBL_CHAPT );
|
|
RtlCopyMemory( pbDst + rDstColumn.GetValueOffset(),
|
|
&( _aVisibleCategories[iRow].catID),
|
|
sizeof CI_TBL_CHAPT );
|
|
}
|
|
rDstColumn.SetStatus( pbDst, CTableColumn::StoreStatusOK );
|
|
rDstColumn.SetStatus( pbDst, CTableColumn::StoreStatusOK );
|
|
}
|
|
else
|
|
{
|
|
_pChild->LokGetOneColumn( _aVisibleCategories[iRow].widFirst,
|
|
rDstColumn,
|
|
pbDst,
|
|
rGetParams.GetVarAllocator() );
|
|
}
|
|
}
|
|
|
|
rwidLastRowTransferred = _aVisibleCategories[iRow].catID;
|
|
rGetParams.IncrementRowCount();
|
|
|
|
if ( rGetParams.GetFwdFetch() )
|
|
iRow++;
|
|
else
|
|
{
|
|
if (iRow == 0)
|
|
break;
|
|
iRow--;
|
|
}
|
|
}
|
|
|
|
// If we didn't transfer as many rows as requested, we must
|
|
// have run into the end of the table or chapter.
|
|
|
|
if ( rGetParams.RowsToTransfer() > 0 )
|
|
{
|
|
if ( 0 == rGetParams.RowsTransferred() )
|
|
sc = DB_E_BADSTARTPOSITION;
|
|
else
|
|
sc = DB_S_ENDOFROWSET;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
sc = e.GetErrorCode();
|
|
|
|
Win4Assert( E_OUTOFMEMORY == sc ||
|
|
STATUS_BUFFER_TOO_SMALL == sc ); // benign?
|
|
|
|
if ( E_OUTOFMEMORY == sc && rGetParams.RowsTransferred() > 0)
|
|
sc = DB_S_BLOCKLIMITEDROWS;
|
|
}
|
|
END_CATCH;
|
|
|
|
return sc;
|
|
} //GetRows
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCategorize::RestartPosition, public
|
|
//
|
|
// Synopsis: Set next fetch position for the chapter to the start
|
|
//
|
|
// Arguments: [chapt] - Chapter from which to fetch rows (if chaptered)
|
|
//
|
|
// Returns: SCODE - status of the operation.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CCategorize::RestartPosition(
|
|
CI_TBL_CHAPT chapt)
|
|
{
|
|
SetCurrentPosition( chapt, WORKID_TBLBEFOREFIRST );
|
|
CTableSource::RestartPosition( chapt );
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: LocateRelativeRow, public
|
|
//
|
|
// Synopsis: Finds a row in the category table. Since there is only one
|
|
// category segment, we are almost assured of finding the row.
|
|
//
|
|
// Arguments: [widStart] -- where to start the locate
|
|
// [chapt] -- parent chapter in which to do the locate
|
|
// [cRowsToMove] -- rows to skip after [widStart]
|
|
// [rwidRowOut] -- wid found after locate
|
|
// [rcRowsResidual] -- number of rows left over -- will be 0
|
|
//
|
|
// History: 6-1-95 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CCategorize::LocateRelativeRow(
|
|
WORKID widStart,
|
|
CI_TBL_CHAPT chapt,
|
|
DBROWOFFSET cRowsToMove,
|
|
WORKID & rwidRowOut,
|
|
DBROWOFFSET & rcRowsResidual)
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
if ( widStart == WORKID_TBLBEFOREFIRST && cRowsToMove > 0 )
|
|
{
|
|
widStart = WORKID_TBLFIRST;
|
|
cRowsToMove--;
|
|
}
|
|
else if ( widStart == WORKID_TBLAFTERLAST && cRowsToMove < 0 )
|
|
{
|
|
widStart = WORKID_TBLLAST;
|
|
cRowsToMove++;
|
|
}
|
|
else if ( WORKID_TBLAFTERLAST == widStart ||
|
|
WORKID_TBLBEFOREFIRST == widStart )
|
|
{
|
|
rwidRowOut = widStart;
|
|
rcRowsResidual = cRowsToMove;
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG iRow;
|
|
|
|
if ( WORKID_TBLFIRST == widStart )
|
|
{
|
|
if ( _isCategorized() && DB_NULL_HCHAPTER != chapt )
|
|
iRow = _FindCategory( _pParent->GetFirstWorkid( chapt ) );
|
|
else
|
|
iRow = 0;
|
|
}
|
|
else if ( WORKID_TBLLAST == widStart )
|
|
{
|
|
if ( _isCategorized() && DB_NULL_HCHAPTER != chapt )
|
|
{
|
|
iRow = _FindCategory( _pParent->GetFirstWorkid( chapt ) );
|
|
iRow += _pParent->GetRowCount( chapt );
|
|
}
|
|
else
|
|
{
|
|
iRow = _aVisibleCategories.Count();
|
|
}
|
|
|
|
iRow--;
|
|
}
|
|
else
|
|
{
|
|
iRow = _FindCategory( widStart );
|
|
}
|
|
|
|
rcRowsResidual = cRowsToMove + iRow;
|
|
|
|
if (rcRowsResidual < 0)
|
|
{
|
|
rwidRowOut = WORKID_TBLBEFOREFIRST;
|
|
tbDebugOut(( DEB_ITRACE,
|
|
"category table LocateRelativeRow off beginning of table\n" ));
|
|
}
|
|
else if ( (ULONG) rcRowsResidual >= _aVisibleCategories.Count() )
|
|
{
|
|
rwidRowOut = WORKID_TBLAFTERLAST;
|
|
rcRowsResidual -= _aVisibleCategories.Count();
|
|
|
|
tbDebugOut(( DEB_ITRACE,
|
|
"category table LocateRelativeRow off end of table\n" ));
|
|
}
|
|
else
|
|
{
|
|
rwidRowOut = (WORKID) _aVisibleCategories[ (unsigned) rcRowsResidual ].catID;
|
|
rcRowsResidual = 0;
|
|
}
|
|
|
|
return S_OK;
|
|
} //LocateRelativeRow
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CCategorize::LokAssignCategory, public
|
|
//
|
|
// Synopsis: Assigns a category to a row in a lower level, then calls
|
|
// the parent categorizer to re-categorize the row's category
|
|
// if there is a parent.
|
|
//
|
|
// Arguments: [prm] -- category parameters. See tblsink.hxx for more
|
|
// info about this object.
|
|
//
|
|
// History: 6-1-95 dlee Created
|
|
//
|
|
// Notes: No need to grab the CAsyncQuery lock -- bigtable grabs it
|
|
// up front in PutRow.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
unsigned CCategorize::LokAssignCategory(
|
|
CCategParams & prm)
|
|
{
|
|
unsigned category = chaptInvalid;
|
|
unsigned iEntry = 0xffffffff;
|
|
|
|
CDynArrayInPlace<CCategory> & array = _WritableArray();
|
|
|
|
if ( widInvalid != prm.widPrev &&
|
|
widInvalid != prm.widNext &&
|
|
prm.catPrev == prm.catNext )
|
|
{
|
|
// new row is between two rows of the same category
|
|
// no need to call parent categorizer -- just return
|
|
|
|
_IncrementRowCount( prm.catPrev );
|
|
return prm.catPrev;
|
|
}
|
|
else if ( widInvalid == prm.widPrev &&
|
|
widInvalid == prm.widNext )
|
|
{
|
|
// first row we've ever seen
|
|
|
|
Win4Assert( 0 == array.Count() );
|
|
CCategory cat( prm.widRow );
|
|
array[ 0 ] = cat;
|
|
category = _GenCategory();
|
|
array[ 0 ].catID = category;
|
|
iEntry = 0;
|
|
}
|
|
else if ( widInvalid == prm.widPrev )
|
|
{
|
|
// new first row in the next row's category or a new category
|
|
|
|
if ( prm.icmpNext > _iSpec )
|
|
{
|
|
// new first row in this (the next row's) category
|
|
|
|
category = prm.catNext;
|
|
iEntry = _FindWritableCategory( category );
|
|
array[ iEntry ].widFirst = prm.widRow;
|
|
array[ iEntry ].cRows++;
|
|
|
|
return category;
|
|
}
|
|
else
|
|
{
|
|
// insert new category before the next category
|
|
|
|
iEntry = _FindWritableCategory( prm.catNext );
|
|
category = _InsertNewCategory( prm.widRow, iEntry );
|
|
}
|
|
}
|
|
else if ( widInvalid == prm.widNext )
|
|
{
|
|
// new category OR
|
|
// new element in previous row's category
|
|
|
|
if ( prm.icmpPrev <= _iSpec )
|
|
{
|
|
// new category after previous (may be an insert operation).
|
|
// just because widNext is invalid doesn't mean it doesn't exist.
|
|
|
|
iEntry = 1 + _FindWritableCategory( prm.catPrev );
|
|
category = _InsertNewCategory( prm.widRow, iEntry );
|
|
}
|
|
else
|
|
{
|
|
// new element in previous row's category
|
|
|
|
_IncrementRowCount( prm.catPrev );
|
|
return prm.catPrev;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// good rows on either side in different categories, one of either:
|
|
// new member of previous row's category OR
|
|
// new first member of next rows's category OR
|
|
// new category
|
|
|
|
if ( prm.icmpPrev > _iSpec )
|
|
{
|
|
// new member of previous row's category
|
|
|
|
_IncrementRowCount( prm.catPrev );
|
|
return prm.catPrev;
|
|
}
|
|
else if ( prm.icmpNext > _iSpec )
|
|
{
|
|
// new first member of next rows's category
|
|
|
|
iEntry = _FindWritableCategory( prm.catNext );
|
|
|
|
array[ iEntry ].widFirst = prm.widRow;
|
|
array[ iEntry ].cRows++;
|
|
|
|
return prm.catNext;
|
|
}
|
|
else
|
|
{
|
|
// new category
|
|
|
|
iEntry = _FindWritableCategory( prm.catNext ) ;
|
|
category = _InsertNewCategory( prm.widRow, iEntry );
|
|
}
|
|
}
|
|
|
|
// Not all cases above get to this point. Several return early if
|
|
// there is no way a parent would care about the operation.
|
|
|
|
if ( _isCategorized() )
|
|
{
|
|
Win4Assert( category != chaptInvalid );
|
|
Win4Assert( iEntry != 0xffffffff );
|
|
|
|
// Get the parent category. Use a different CCategParams so original
|
|
// is intact.
|
|
|
|
CCategParams prnt = prm;
|
|
prnt.widRow = category;
|
|
|
|
if ( 0 == iEntry )
|
|
prnt.widPrev = widInvalid;
|
|
else
|
|
{
|
|
prnt.widPrev = array[ iEntry - 1 ].catID;
|
|
prnt.catPrev = array[ iEntry - 1 ].catParent;
|
|
}
|
|
|
|
if ( iEntry < ( array.Count() - 1 ) )
|
|
{
|
|
prnt.widNext = array[ iEntry + 1 ].catID;
|
|
prnt.catNext = array[ iEntry + 1 ].catParent;
|
|
}
|
|
else
|
|
prnt.widNext = widInvalid;
|
|
|
|
array[ iEntry ].catParent = _pParent->LokAssignCategory( prnt );
|
|
}
|
|
|
|
return category;
|
|
} //LokAssignCategory
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: _FindCategory, private
|
|
//
|
|
// Synopsis: Finds a category in the category array
|
|
//
|
|
// Arguments: [cat] -- category
|
|
//
|
|
// Returns: index into the category array
|
|
//
|
|
// History: 6-1-95 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
unsigned CCategorize::_FindCategory(
|
|
CI_TBL_CHAPT cat )
|
|
{
|
|
unsigned cCategories = _aVisibleCategories.Count();
|
|
|
|
// first try the hint and the hint + 1
|
|
|
|
if ( _iFindHint < cCategories )
|
|
{
|
|
if ( cat == _aVisibleCategories[ _iFindHint ].catID )
|
|
return _iFindHint;
|
|
|
|
unsigned iHintPlus = _iFindHint + 1;
|
|
|
|
if ( ( iHintPlus < cCategories ) &&
|
|
( cat == _aVisibleCategories[ iHintPlus ].catID ) )
|
|
{
|
|
_iFindHint++;
|
|
return _iFindHint;
|
|
}
|
|
}
|
|
|
|
// linear search for the category
|
|
|
|
for ( unsigned i = 0; i < cCategories; i++ )
|
|
{
|
|
if ( cat == _aVisibleCategories[ i ].catID )
|
|
{
|
|
_iFindHint = i;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
THROW( CException( DB_E_BADCHAPTER ) );
|
|
return 0;
|
|
} //_FindCategory
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: _FindWritableCategory, private
|
|
//
|
|
// Synopsis: Finds a category in the updatable category array
|
|
//
|
|
// Arguments: [cat] -- category
|
|
//
|
|
// Returns: index into the category array
|
|
//
|
|
// History: 6-1-95 dlee Created
|
|
//
|
|
// PERFPERF: Linear search -- we may want to put a hash table over this.
|
|
// How many categories do we expect?
|
|
// Another alternative is to cache the last array entry
|
|
// referenced and use it and the prev/next entries as first
|
|
// guesses.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
unsigned CCategorize::_FindWritableCategory(
|
|
CI_TBL_CHAPT cat )
|
|
{
|
|
CDynArrayInPlace<CCategory> & array = _WritableArray();
|
|
|
|
for ( unsigned i = 0; i < array.Count(); i++ )
|
|
if ( cat == array[ i ].catID )
|
|
return i;
|
|
|
|
Win4Assert( !"_FindWritableCategory failed" );
|
|
THROW( CException( DB_E_BADCHAPTER ) );
|
|
return 0;
|
|
} //_FindWritableCategory
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: GetRowsAt, public
|
|
//
|
|
// Synopsis: Retrieves rows at a specified location
|
|
//
|
|
// Arguments: [widStart] -- where to start retrieving rows
|
|
// [chapt] -- in which rows are retrieved
|
|
// [cRowsToMove] -- offset from widStart
|
|
// [rOutColumns] -- description of output columns
|
|
// [rGetParams] -- info about the get operation
|
|
// [rwidLastRowTransferred] -- last row retrieved
|
|
//
|
|
// Returns: SCODE
|
|
//
|
|
// History: 6-1-95 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CCategorize::GetRowsAt(
|
|
HWATCHREGION hRegion,
|
|
WORKID widStart,
|
|
CI_TBL_CHAPT chapt,
|
|
DBROWOFFSET cRowsToMove,
|
|
CTableColumnSet const & rOutColumns,
|
|
CGetRowsParams & rGetParams,
|
|
WORKID & rwidLastRowTransferred )
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
DBROWOFFSET cRowsResidual;
|
|
|
|
SCODE scRet = LocateRelativeRow( widStart,
|
|
chapt,
|
|
cRowsToMove,
|
|
widStart,
|
|
cRowsResidual);
|
|
|
|
Win4Assert( !FAILED( scRet ) );
|
|
|
|
if ( cRowsResidual )
|
|
{
|
|
Win4Assert ( WORKID_TBLAFTERLAST == widStart ||
|
|
WORKID_TBLBEFOREFIRST == widStart );
|
|
return DB_E_BADSTARTPOSITION;
|
|
}
|
|
|
|
scRet = GetRows( hRegion,
|
|
widStart,
|
|
chapt,
|
|
rOutColumns,
|
|
rGetParams,
|
|
rwidLastRowTransferred);
|
|
return scRet;
|
|
} //GetRowsAt
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: GetRowsAtRatio, public
|
|
//
|
|
// Synopsis: Retrieves rows at a specified location. Nothing fuzzy about
|
|
// this -- they're all in memory so be as exact as possible.
|
|
//
|
|
// Arguments: [num] -- numerator of starting point fraction
|
|
// [denom] -- denominator of starting point fraction
|
|
// [chapt] -- in which rows are retrieved
|
|
// [rOutColumns] -- description of output columns
|
|
// [rGetParams] -- info about the get operation
|
|
// [rwidLastRowTransferred] -- last row retrieved
|
|
//
|
|
// Returns: SCODE
|
|
//
|
|
// History: 6-1-95 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CCategorize::GetRowsAtRatio(
|
|
HWATCHREGION hRegion,
|
|
ULONG num,
|
|
ULONG denom,
|
|
CI_TBL_CHAPT chapt,
|
|
CTableColumnSet const & rOutColumns,
|
|
CGetRowsParams & rGetParams,
|
|
WORKID & rwidLastRowTransferred )
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
if ( 0 == denom || num > denom )
|
|
QUIETTHROW( CException(DB_E_BADRATIO) );
|
|
|
|
ULONG cRows;
|
|
if ( _isCategorized() && DB_NULL_HCHAPTER != chapt )
|
|
cRows = _pParent->GetRowCount( chapt );
|
|
else
|
|
cRows = _aVisibleCategories.Count();
|
|
|
|
ULONG cRowsFromFront = (ULONG) ( ( (unsigned _int64) num *
|
|
(unsigned _int64) cRows ) /
|
|
( unsigned _int64 ) denom );
|
|
|
|
if ( cRowsFromFront == cRows )
|
|
{
|
|
// The user is asking to retrieve past the end of table.
|
|
|
|
rwidLastRowTransferred = WORKID_TBLAFTERLAST;
|
|
return DB_S_ENDOFROWSET;
|
|
}
|
|
|
|
return GetRowsAt( hRegion, WORKID_TBLFIRST, chapt, (LONG) cRowsFromFront,
|
|
rOutColumns, rGetParams, rwidLastRowTransferred );
|
|
} //GetRowsAtRatio
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: RemoveRow, public
|
|
//
|
|
// Synopsis: Removes a row from the categorization
|
|
//
|
|
// Arguments: [chapt] -- of row being removed
|
|
// [wid] -- of removed row in categorized child table
|
|
// [widNext] -- of row following removed row
|
|
//
|
|
// History: 6-1-95 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CCategorize::RemoveRow(
|
|
CI_TBL_CHAPT chapt,
|
|
WORKID wid,
|
|
WORKID widNext )
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
CDynArrayInPlace<CCategory> & array = _WritableArray();
|
|
|
|
unsigned iEntry = _FindWritableCategory( chapt );
|
|
array[ iEntry ].cRows--;
|
|
|
|
if ( 0 == array[ iEntry ].cRows )
|
|
{
|
|
// in case we need parent category later, save it
|
|
|
|
unsigned catParent = array[ iEntry ].catParent;
|
|
|
|
// last member of category -- remove the category
|
|
|
|
array.Remove( iEntry );
|
|
|
|
if ( _isCategorized() )
|
|
{
|
|
// notify the parent that a row was deleted
|
|
|
|
WORKID widNextCategory;
|
|
if ( ( 0 == array.Count() ) ||
|
|
( iEntry >= ( array.Count() - 1) ) )
|
|
widNextCategory = widInvalid;
|
|
else
|
|
widNextCategory = array[ iEntry ].catID;
|
|
|
|
_pParent->RemoveRow( catParent,
|
|
chapt,
|
|
widNextCategory );
|
|
}
|
|
}
|
|
else if ( array[ iEntry ].widFirst == wid )
|
|
{
|
|
// new first member of the category
|
|
|
|
array[ iEntry ].widFirst = widNext;
|
|
|
|
// removed the GetNextRows() current position row -- fixup
|
|
|
|
if ( array[ iEntry ].widGetNextRowsPos == wid )
|
|
array[ iEntry ].widGetNextRowsPos = widNext;
|
|
}
|
|
} //RemoveRow
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: GetApproximatePosition, public
|
|
//
|
|
// Synopsis: Returns the offset of a bookmark in the table/chapter and
|
|
// the number of rows in that table/chapter
|
|
//
|
|
// Arguments: [chapt] -- of row being queried
|
|
// [bmk] -- of row being queried
|
|
// [piRow] -- returns index of row in table/chapter
|
|
// [pcRows] -- returns count of rows in table/chapter
|
|
//
|
|
// History: 6-29-95 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CCategorize::GetApproximatePosition(
|
|
CI_TBL_CHAPT chapt,
|
|
CI_TBL_BMK bmk,
|
|
DBCOUNTITEM * piRow,
|
|
DBCOUNTITEM * pcRows )
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
if (bmk == widInvalid)
|
|
return DB_E_BADBOOKMARK;
|
|
|
|
Win4Assert( bmk != WORKID_TBLBEFOREFIRST && bmk != WORKID_TBLAFTERLAST );
|
|
|
|
DBCOUNTITEM iBmkPos = ULONG_MAX, cRows = 0;
|
|
|
|
if ( _isCategorized() && DB_NULL_HCHAPTER != chapt )
|
|
cRows = _pParent->GetRowCount( chapt );
|
|
else
|
|
cRows = _aVisibleCategories.Count();
|
|
|
|
if ( WORKID_TBLFIRST == bmk )
|
|
iBmkPos = cRows ? 1 : 0;
|
|
else if ( WORKID_TBLLAST == bmk )
|
|
iBmkPos = cRows;
|
|
else
|
|
{
|
|
iBmkPos = _FindCategory( bmk ) + 1;
|
|
|
|
// position is relative to first member of the category (if any)
|
|
|
|
if ( _isCategorized() && DB_NULL_HCHAPTER != chapt )
|
|
iBmkPos -= _FindCategory( _pParent->GetFirstWorkid( chapt ) );
|
|
}
|
|
|
|
Win4Assert(iBmkPos <= cRows);
|
|
|
|
*piRow = iBmkPos;
|
|
*pcRows = cRows;
|
|
|
|
return S_OK;
|
|
} //GetApproximatePosition
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: LokGetOneColumn, public
|
|
//
|
|
// Synopsis: Returns column data for the first item in a category.
|
|
//
|
|
// Arguments: [wid] -- workid or chapter of the row to be queried
|
|
// [rOutColumn] -- layout of the output data
|
|
// [pbOut] -- where to write the column data
|
|
// [rVarAlloc] -- variable data allocator to use
|
|
//
|
|
// History: 22-Aug-95 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CCategorize::LokGetOneColumn(
|
|
WORKID wid,
|
|
CTableColumn const & rOutColumn,
|
|
BYTE * pbOut,
|
|
PVarAllocator & rVarAlloc )
|
|
{
|
|
unsigned iRow = _FindCategory( wid );
|
|
|
|
_pChild->LokGetOneColumn( _aVisibleCategories[iRow].widFirst,
|
|
rOutColumn,
|
|
pbOut,
|
|
rVarAlloc );
|
|
} //LokGetOneColumn
|
|
|
|
|