Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

856 lines
27 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1995 - 2000.
  5. //
  6. // File: categ.cxx
  7. //
  8. // Contents: Unique categorization class
  9. //
  10. // Classes: CCategorize
  11. //
  12. // History: 30 Mar 95 dlee Created
  13. //
  14. //--------------------------------------------------------------------------
  15. #include <pch.cxx>
  16. #pragma hdrstop
  17. #include <query.hxx>
  18. #include "tabledbg.hxx"
  19. //+---------------------------------------------------------------------------
  20. //
  21. // Method: CCategorize, public
  22. //
  23. // Synopsis: Constructor for categorization class
  24. //
  25. // Arguments: [rCatSpec] -- categorization specification for this level
  26. // [iSpec] -- 1-based categorization level, where smaller
  27. // numbers are higher in the hierarchy.
  28. // [pParent] -- categorization object that categorizes rows
  29. // on this level (or 0 if none)
  30. // [mutex] -- CAsyncQuery's mutex for serialization
  31. //
  32. // History: 6-1-95 dlee Created
  33. //
  34. // Notes: Category identifiers start at 0x40000000 plus 0x1000 times
  35. // [iSpec]. The 0x40000000 is so it is obvious to the
  36. // debugger that it is a category. The 0x1000*[iSpec] is
  37. // so that it is obvious what level a category falls into.
  38. //
  39. //----------------------------------------------------------------------------
  40. CCategorize::CCategorize(
  41. CCategorizationSpec & rCatSpec,
  42. unsigned iSpec,
  43. CCategorize * pParent,
  44. CMutexSem & mutex)
  45. : _iSpec( iSpec ),
  46. _pParent( pParent ),
  47. _pChild( 0 ),
  48. _mutex( mutex ),
  49. _iCategoryGen( 0x40000000 + ( 0x1000 * iSpec ) ),
  50. _widCurrent( WORKID_TBLBEFOREFIRST ),
  51. _fNotificationsEnabled( FALSE ),
  52. _iFindHint( 0 ),
  53. _aDynamicCategories( 0 ),
  54. _aVisibleCategories( 16 )
  55. {
  56. if (rCatSpec.Type() != CATEGORIZE_UNIQUE)
  57. THROW(CException( E_INVALIDARG ));
  58. } //CCategorize
  59. //+---------------------------------------------------------------------------
  60. //
  61. // Method: CCategorize::GetRows, public
  62. //
  63. // Arguments: [widStart] - WORKID identifying first row to be
  64. // transferred. If WORKID_TBLFIRST is
  65. // used, the transfer will start at the first
  66. // row in the segment.
  67. // [chapt] - Chapter from which to fetch rows (if chaptered)
  68. // [pOutColumns] - A CTableColumnSet that describes the
  69. // output format of the data table.
  70. // [rGetParams] - An CGetRowsParams structure which
  71. // describes how many rows are to be fetched and
  72. // other parameters of the operation.
  73. // [rwidLastRowTransferred] - On return, the work ID of
  74. // the last row to be transferred
  75. // from this table. Can be used to
  76. // initialize widStart on next call.
  77. //
  78. // Returns: SCODE - status of the operation. DB_S_ENDOFROWSET if
  79. // widStart is WORKID_TBLAFTERLAST at start of
  80. // transfer, or if rwidLastRowTransferred is the
  81. // last row in the segment at the end of the transfer.
  82. //
  83. // STATUS_BUFFER_TOO_SMALL is returned if the available
  84. // space in the out-of-line data was exhausted during
  85. // the transfer.
  86. //
  87. // Notes: To transfer successive rows, as in GetNextRows, the
  88. // rwidLastRowTransferred must be advanced by one prior
  89. // to the next transfer.
  90. //
  91. // History: 6-1-95 dlee Created
  92. //
  93. //----------------------------------------------------------------------------
  94. SCODE CCategorize::GetRows(
  95. HWATCHREGION hRegion,
  96. WORKID widStart,
  97. CI_TBL_CHAPT chapt,
  98. CTableColumnSet const & rOutColumns,
  99. CGetRowsParams & rGetParams,
  100. WORKID & rwidLastRowTransferred)
  101. {
  102. SCODE sc = S_OK;
  103. TRY
  104. {
  105. if (WORKID_TBLAFTERLAST == widStart)
  106. {
  107. rwidLastRowTransferred = WORKID_TBLAFTERLAST;
  108. return DB_S_ENDOFROWSET;
  109. }
  110. else if (widInvalid == widStart)
  111. {
  112. Win4Assert(! "CCategorize::GetRows widStart is widInvalid");
  113. return E_FAIL;
  114. }
  115. else
  116. {
  117. CLock lock( _mutex );
  118. if ( 0 == _aVisibleCategories.Count() )
  119. sc = DB_S_ENDOFROWSET;
  120. else
  121. {
  122. unsigned iRow;
  123. if ( widStart != WORKID_TBLFIRST &&
  124. widStart != WORKID_TBLBEFOREFIRST )
  125. iRow = _FindCategory( widStart );
  126. else
  127. iRow = 0;
  128. rwidLastRowTransferred = 0;
  129. while ( 0 != rGetParams.RowsToTransfer() &&
  130. iRow < _aVisibleCategories.Count() )
  131. {
  132. // at the end of the chapter when this level is categorized?
  133. if ( ( _isCategorized() ) &&
  134. ( DB_NULL_HCHAPTER != chapt ) &&
  135. ( _aVisibleCategories[ iRow ].catParent != chapt ) )
  136. break; // code below will set sc = DB_S_ENDOFROWSET
  137. BYTE* pbDst = (BYTE *) rGetParams.GetRowBuffer();
  138. for ( unsigned col = 0; col < rOutColumns.Count(); col++ )
  139. {
  140. CTableColumn const & rDstColumn = *rOutColumns.Get(col);
  141. PROPID pid = rDstColumn.GetPropId();
  142. if ( pidChapter == pid || pidWorkId == pid )
  143. {
  144. if (rDstColumn.GetStoredType() == VT_VARIANT)
  145. {
  146. Win4Assert( rDstColumn.GetValueSize() == sizeof VARIANT );
  147. CTableVariant * pVarnt = (CTableVariant *) ( pbDst +
  148. rDstColumn.GetValueOffset() );
  149. pVarnt->vt = VT_UI4;
  150. pVarnt->ulVal = _aVisibleCategories[iRow].catID;
  151. }
  152. else
  153. {
  154. Win4Assert( rDstColumn.GetValueSize() == sizeof CI_TBL_CHAPT );
  155. RtlCopyMemory( pbDst + rDstColumn.GetValueOffset(),
  156. &( _aVisibleCategories[iRow].catID),
  157. sizeof CI_TBL_CHAPT );
  158. }
  159. rDstColumn.SetStatus( pbDst, CTableColumn::StoreStatusOK );
  160. rDstColumn.SetStatus( pbDst, CTableColumn::StoreStatusOK );
  161. }
  162. else
  163. {
  164. _pChild->LokGetOneColumn( _aVisibleCategories[iRow].widFirst,
  165. rDstColumn,
  166. pbDst,
  167. rGetParams.GetVarAllocator() );
  168. }
  169. }
  170. rwidLastRowTransferred = _aVisibleCategories[iRow].catID;
  171. rGetParams.IncrementRowCount();
  172. if ( rGetParams.GetFwdFetch() )
  173. iRow++;
  174. else
  175. {
  176. if (iRow == 0)
  177. break;
  178. iRow--;
  179. }
  180. }
  181. // If we didn't transfer as many rows as requested, we must
  182. // have run into the end of the table or chapter.
  183. if ( rGetParams.RowsToTransfer() > 0 )
  184. {
  185. if ( 0 == rGetParams.RowsTransferred() )
  186. sc = DB_E_BADSTARTPOSITION;
  187. else
  188. sc = DB_S_ENDOFROWSET;
  189. }
  190. }
  191. }
  192. }
  193. CATCH( CException, e )
  194. {
  195. sc = e.GetErrorCode();
  196. Win4Assert( E_OUTOFMEMORY == sc ||
  197. STATUS_BUFFER_TOO_SMALL == sc ); // benign?
  198. if ( E_OUTOFMEMORY == sc && rGetParams.RowsTransferred() > 0)
  199. sc = DB_S_BLOCKLIMITEDROWS;
  200. }
  201. END_CATCH;
  202. return sc;
  203. } //GetRows
  204. //+-------------------------------------------------------------------------
  205. //
  206. // Member: CCategorize::RestartPosition, public
  207. //
  208. // Synopsis: Set next fetch position for the chapter to the start
  209. //
  210. // Arguments: [chapt] - Chapter from which to fetch rows (if chaptered)
  211. //
  212. // Returns: SCODE - status of the operation.
  213. //
  214. //--------------------------------------------------------------------------
  215. void CCategorize::RestartPosition(
  216. CI_TBL_CHAPT chapt)
  217. {
  218. SetCurrentPosition( chapt, WORKID_TBLBEFOREFIRST );
  219. CTableSource::RestartPosition( chapt );
  220. }
  221. //+---------------------------------------------------------------------------
  222. //
  223. // Method: LocateRelativeRow, public
  224. //
  225. // Synopsis: Finds a row in the category table. Since there is only one
  226. // category segment, we are almost assured of finding the row.
  227. //
  228. // Arguments: [widStart] -- where to start the locate
  229. // [chapt] -- parent chapter in which to do the locate
  230. // [cRowsToMove] -- rows to skip after [widStart]
  231. // [rwidRowOut] -- wid found after locate
  232. // [rcRowsResidual] -- number of rows left over -- will be 0
  233. //
  234. // History: 6-1-95 dlee Created
  235. //
  236. //----------------------------------------------------------------------------
  237. SCODE CCategorize::LocateRelativeRow(
  238. WORKID widStart,
  239. CI_TBL_CHAPT chapt,
  240. DBROWOFFSET cRowsToMove,
  241. WORKID & rwidRowOut,
  242. DBROWOFFSET & rcRowsResidual)
  243. {
  244. CLock lock( _mutex );
  245. if ( widStart == WORKID_TBLBEFOREFIRST && cRowsToMove > 0 )
  246. {
  247. widStart = WORKID_TBLFIRST;
  248. cRowsToMove--;
  249. }
  250. else if ( widStart == WORKID_TBLAFTERLAST && cRowsToMove < 0 )
  251. {
  252. widStart = WORKID_TBLLAST;
  253. cRowsToMove++;
  254. }
  255. else if ( WORKID_TBLAFTERLAST == widStart ||
  256. WORKID_TBLBEFOREFIRST == widStart )
  257. {
  258. rwidRowOut = widStart;
  259. rcRowsResidual = cRowsToMove;
  260. return S_OK;
  261. }
  262. ULONG iRow;
  263. if ( WORKID_TBLFIRST == widStart )
  264. {
  265. if ( _isCategorized() && DB_NULL_HCHAPTER != chapt )
  266. iRow = _FindCategory( _pParent->GetFirstWorkid( chapt ) );
  267. else
  268. iRow = 0;
  269. }
  270. else if ( WORKID_TBLLAST == widStart )
  271. {
  272. if ( _isCategorized() && DB_NULL_HCHAPTER != chapt )
  273. {
  274. iRow = _FindCategory( _pParent->GetFirstWorkid( chapt ) );
  275. iRow += _pParent->GetRowCount( chapt );
  276. }
  277. else
  278. {
  279. iRow = _aVisibleCategories.Count();
  280. }
  281. iRow--;
  282. }
  283. else
  284. {
  285. iRow = _FindCategory( widStart );
  286. }
  287. rcRowsResidual = cRowsToMove + iRow;
  288. if (rcRowsResidual < 0)
  289. {
  290. rwidRowOut = WORKID_TBLBEFOREFIRST;
  291. tbDebugOut(( DEB_ITRACE,
  292. "category table LocateRelativeRow off beginning of table\n" ));
  293. }
  294. else if ( (ULONG) rcRowsResidual >= _aVisibleCategories.Count() )
  295. {
  296. rwidRowOut = WORKID_TBLAFTERLAST;
  297. rcRowsResidual -= _aVisibleCategories.Count();
  298. tbDebugOut(( DEB_ITRACE,
  299. "category table LocateRelativeRow off end of table\n" ));
  300. }
  301. else
  302. {
  303. rwidRowOut = (WORKID) _aVisibleCategories[ (unsigned) rcRowsResidual ].catID;
  304. rcRowsResidual = 0;
  305. }
  306. return S_OK;
  307. } //LocateRelativeRow
  308. //+---------------------------------------------------------------------------
  309. //
  310. // Method: CCategorize::LokAssignCategory, public
  311. //
  312. // Synopsis: Assigns a category to a row in a lower level, then calls
  313. // the parent categorizer to re-categorize the row's category
  314. // if there is a parent.
  315. //
  316. // Arguments: [prm] -- category parameters. See tblsink.hxx for more
  317. // info about this object.
  318. //
  319. // History: 6-1-95 dlee Created
  320. //
  321. // Notes: No need to grab the CAsyncQuery lock -- bigtable grabs it
  322. // up front in PutRow.
  323. //
  324. //----------------------------------------------------------------------------
  325. unsigned CCategorize::LokAssignCategory(
  326. CCategParams & prm)
  327. {
  328. unsigned category = chaptInvalid;
  329. unsigned iEntry = 0xffffffff;
  330. CDynArrayInPlace<CCategory> & array = _WritableArray();
  331. if ( widInvalid != prm.widPrev &&
  332. widInvalid != prm.widNext &&
  333. prm.catPrev == prm.catNext )
  334. {
  335. // new row is between two rows of the same category
  336. // no need to call parent categorizer -- just return
  337. _IncrementRowCount( prm.catPrev );
  338. return prm.catPrev;
  339. }
  340. else if ( widInvalid == prm.widPrev &&
  341. widInvalid == prm.widNext )
  342. {
  343. // first row we've ever seen
  344. Win4Assert( 0 == array.Count() );
  345. CCategory cat( prm.widRow );
  346. array[ 0 ] = cat;
  347. category = _GenCategory();
  348. array[ 0 ].catID = category;
  349. iEntry = 0;
  350. }
  351. else if ( widInvalid == prm.widPrev )
  352. {
  353. // new first row in the next row's category or a new category
  354. if ( prm.icmpNext > _iSpec )
  355. {
  356. // new first row in this (the next row's) category
  357. category = prm.catNext;
  358. iEntry = _FindWritableCategory( category );
  359. array[ iEntry ].widFirst = prm.widRow;
  360. array[ iEntry ].cRows++;
  361. return category;
  362. }
  363. else
  364. {
  365. // insert new category before the next category
  366. iEntry = _FindWritableCategory( prm.catNext );
  367. category = _InsertNewCategory( prm.widRow, iEntry );
  368. }
  369. }
  370. else if ( widInvalid == prm.widNext )
  371. {
  372. // new category OR
  373. // new element in previous row's category
  374. if ( prm.icmpPrev <= _iSpec )
  375. {
  376. // new category after previous (may be an insert operation).
  377. // just because widNext is invalid doesn't mean it doesn't exist.
  378. iEntry = 1 + _FindWritableCategory( prm.catPrev );
  379. category = _InsertNewCategory( prm.widRow, iEntry );
  380. }
  381. else
  382. {
  383. // new element in previous row's category
  384. _IncrementRowCount( prm.catPrev );
  385. return prm.catPrev;
  386. }
  387. }
  388. else
  389. {
  390. // good rows on either side in different categories, one of either:
  391. // new member of previous row's category OR
  392. // new first member of next rows's category OR
  393. // new category
  394. if ( prm.icmpPrev > _iSpec )
  395. {
  396. // new member of previous row's category
  397. _IncrementRowCount( prm.catPrev );
  398. return prm.catPrev;
  399. }
  400. else if ( prm.icmpNext > _iSpec )
  401. {
  402. // new first member of next rows's category
  403. iEntry = _FindWritableCategory( prm.catNext );
  404. array[ iEntry ].widFirst = prm.widRow;
  405. array[ iEntry ].cRows++;
  406. return prm.catNext;
  407. }
  408. else
  409. {
  410. // new category
  411. iEntry = _FindWritableCategory( prm.catNext ) ;
  412. category = _InsertNewCategory( prm.widRow, iEntry );
  413. }
  414. }
  415. // Not all cases above get to this point. Several return early if
  416. // there is no way a parent would care about the operation.
  417. if ( _isCategorized() )
  418. {
  419. Win4Assert( category != chaptInvalid );
  420. Win4Assert( iEntry != 0xffffffff );
  421. // Get the parent category. Use a different CCategParams so original
  422. // is intact.
  423. CCategParams prnt = prm;
  424. prnt.widRow = category;
  425. if ( 0 == iEntry )
  426. prnt.widPrev = widInvalid;
  427. else
  428. {
  429. prnt.widPrev = array[ iEntry - 1 ].catID;
  430. prnt.catPrev = array[ iEntry - 1 ].catParent;
  431. }
  432. if ( iEntry < ( array.Count() - 1 ) )
  433. {
  434. prnt.widNext = array[ iEntry + 1 ].catID;
  435. prnt.catNext = array[ iEntry + 1 ].catParent;
  436. }
  437. else
  438. prnt.widNext = widInvalid;
  439. array[ iEntry ].catParent = _pParent->LokAssignCategory( prnt );
  440. }
  441. return category;
  442. } //LokAssignCategory
  443. //+---------------------------------------------------------------------------
  444. //
  445. // Method: _FindCategory, private
  446. //
  447. // Synopsis: Finds a category in the category array
  448. //
  449. // Arguments: [cat] -- category
  450. //
  451. // Returns: index into the category array
  452. //
  453. // History: 6-1-95 dlee Created
  454. //
  455. //----------------------------------------------------------------------------
  456. unsigned CCategorize::_FindCategory(
  457. CI_TBL_CHAPT cat )
  458. {
  459. unsigned cCategories = _aVisibleCategories.Count();
  460. // first try the hint and the hint + 1
  461. if ( _iFindHint < cCategories )
  462. {
  463. if ( cat == _aVisibleCategories[ _iFindHint ].catID )
  464. return _iFindHint;
  465. unsigned iHintPlus = _iFindHint + 1;
  466. if ( ( iHintPlus < cCategories ) &&
  467. ( cat == _aVisibleCategories[ iHintPlus ].catID ) )
  468. {
  469. _iFindHint++;
  470. return _iFindHint;
  471. }
  472. }
  473. // linear search for the category
  474. for ( unsigned i = 0; i < cCategories; i++ )
  475. {
  476. if ( cat == _aVisibleCategories[ i ].catID )
  477. {
  478. _iFindHint = i;
  479. return i;
  480. }
  481. }
  482. THROW( CException( DB_E_BADCHAPTER ) );
  483. return 0;
  484. } //_FindCategory
  485. //+---------------------------------------------------------------------------
  486. //
  487. // Method: _FindWritableCategory, private
  488. //
  489. // Synopsis: Finds a category in the updatable category array
  490. //
  491. // Arguments: [cat] -- category
  492. //
  493. // Returns: index into the category array
  494. //
  495. // History: 6-1-95 dlee Created
  496. //
  497. // PERFPERF: Linear search -- we may want to put a hash table over this.
  498. // How many categories do we expect?
  499. // Another alternative is to cache the last array entry
  500. // referenced and use it and the prev/next entries as first
  501. // guesses.
  502. //
  503. //----------------------------------------------------------------------------
  504. unsigned CCategorize::_FindWritableCategory(
  505. CI_TBL_CHAPT cat )
  506. {
  507. CDynArrayInPlace<CCategory> & array = _WritableArray();
  508. for ( unsigned i = 0; i < array.Count(); i++ )
  509. if ( cat == array[ i ].catID )
  510. return i;
  511. Win4Assert( !"_FindWritableCategory failed" );
  512. THROW( CException( DB_E_BADCHAPTER ) );
  513. return 0;
  514. } //_FindWritableCategory
  515. //+---------------------------------------------------------------------------
  516. //
  517. // Method: GetRowsAt, public
  518. //
  519. // Synopsis: Retrieves rows at a specified location
  520. //
  521. // Arguments: [widStart] -- where to start retrieving rows
  522. // [chapt] -- in which rows are retrieved
  523. // [cRowsToMove] -- offset from widStart
  524. // [rOutColumns] -- description of output columns
  525. // [rGetParams] -- info about the get operation
  526. // [rwidLastRowTransferred] -- last row retrieved
  527. //
  528. // Returns: SCODE
  529. //
  530. // History: 6-1-95 dlee Created
  531. //
  532. //----------------------------------------------------------------------------
  533. SCODE CCategorize::GetRowsAt(
  534. HWATCHREGION hRegion,
  535. WORKID widStart,
  536. CI_TBL_CHAPT chapt,
  537. DBROWOFFSET cRowsToMove,
  538. CTableColumnSet const & rOutColumns,
  539. CGetRowsParams & rGetParams,
  540. WORKID & rwidLastRowTransferred )
  541. {
  542. CLock lock( _mutex );
  543. DBROWOFFSET cRowsResidual;
  544. SCODE scRet = LocateRelativeRow( widStart,
  545. chapt,
  546. cRowsToMove,
  547. widStart,
  548. cRowsResidual);
  549. Win4Assert( !FAILED( scRet ) );
  550. if ( cRowsResidual )
  551. {
  552. Win4Assert ( WORKID_TBLAFTERLAST == widStart ||
  553. WORKID_TBLBEFOREFIRST == widStart );
  554. return DB_E_BADSTARTPOSITION;
  555. }
  556. scRet = GetRows( hRegion,
  557. widStart,
  558. chapt,
  559. rOutColumns,
  560. rGetParams,
  561. rwidLastRowTransferred);
  562. return scRet;
  563. } //GetRowsAt
  564. //+---------------------------------------------------------------------------
  565. //
  566. // Method: GetRowsAtRatio, public
  567. //
  568. // Synopsis: Retrieves rows at a specified location. Nothing fuzzy about
  569. // this -- they're all in memory so be as exact as possible.
  570. //
  571. // Arguments: [num] -- numerator of starting point fraction
  572. // [denom] -- denominator of starting point fraction
  573. // [chapt] -- in which rows are retrieved
  574. // [rOutColumns] -- description of output columns
  575. // [rGetParams] -- info about the get operation
  576. // [rwidLastRowTransferred] -- last row retrieved
  577. //
  578. // Returns: SCODE
  579. //
  580. // History: 6-1-95 dlee Created
  581. //
  582. //----------------------------------------------------------------------------
  583. SCODE CCategorize::GetRowsAtRatio(
  584. HWATCHREGION hRegion,
  585. ULONG num,
  586. ULONG denom,
  587. CI_TBL_CHAPT chapt,
  588. CTableColumnSet const & rOutColumns,
  589. CGetRowsParams & rGetParams,
  590. WORKID & rwidLastRowTransferred )
  591. {
  592. CLock lock( _mutex );
  593. if ( 0 == denom || num > denom )
  594. QUIETTHROW( CException(DB_E_BADRATIO) );
  595. ULONG cRows;
  596. if ( _isCategorized() && DB_NULL_HCHAPTER != chapt )
  597. cRows = _pParent->GetRowCount( chapt );
  598. else
  599. cRows = _aVisibleCategories.Count();
  600. ULONG cRowsFromFront = (ULONG) ( ( (unsigned _int64) num *
  601. (unsigned _int64) cRows ) /
  602. ( unsigned _int64 ) denom );
  603. if ( cRowsFromFront == cRows )
  604. {
  605. // The user is asking to retrieve past the end of table.
  606. rwidLastRowTransferred = WORKID_TBLAFTERLAST;
  607. return DB_S_ENDOFROWSET;
  608. }
  609. return GetRowsAt( hRegion, WORKID_TBLFIRST, chapt, (LONG) cRowsFromFront,
  610. rOutColumns, rGetParams, rwidLastRowTransferred );
  611. } //GetRowsAtRatio
  612. //+---------------------------------------------------------------------------
  613. //
  614. // Method: RemoveRow, public
  615. //
  616. // Synopsis: Removes a row from the categorization
  617. //
  618. // Arguments: [chapt] -- of row being removed
  619. // [wid] -- of removed row in categorized child table
  620. // [widNext] -- of row following removed row
  621. //
  622. // History: 6-1-95 dlee Created
  623. //
  624. //----------------------------------------------------------------------------
  625. void CCategorize::RemoveRow(
  626. CI_TBL_CHAPT chapt,
  627. WORKID wid,
  628. WORKID widNext )
  629. {
  630. CLock lock( _mutex );
  631. CDynArrayInPlace<CCategory> & array = _WritableArray();
  632. unsigned iEntry = _FindWritableCategory( chapt );
  633. array[ iEntry ].cRows--;
  634. if ( 0 == array[ iEntry ].cRows )
  635. {
  636. // in case we need parent category later, save it
  637. unsigned catParent = array[ iEntry ].catParent;
  638. // last member of category -- remove the category
  639. array.Remove( iEntry );
  640. if ( _isCategorized() )
  641. {
  642. // notify the parent that a row was deleted
  643. WORKID widNextCategory;
  644. if ( ( 0 == array.Count() ) ||
  645. ( iEntry >= ( array.Count() - 1) ) )
  646. widNextCategory = widInvalid;
  647. else
  648. widNextCategory = array[ iEntry ].catID;
  649. _pParent->RemoveRow( catParent,
  650. chapt,
  651. widNextCategory );
  652. }
  653. }
  654. else if ( array[ iEntry ].widFirst == wid )
  655. {
  656. // new first member of the category
  657. array[ iEntry ].widFirst = widNext;
  658. // removed the GetNextRows() current position row -- fixup
  659. if ( array[ iEntry ].widGetNextRowsPos == wid )
  660. array[ iEntry ].widGetNextRowsPos = widNext;
  661. }
  662. } //RemoveRow
  663. //+---------------------------------------------------------------------------
  664. //
  665. // Method: GetApproximatePosition, public
  666. //
  667. // Synopsis: Returns the offset of a bookmark in the table/chapter and
  668. // the number of rows in that table/chapter
  669. //
  670. // Arguments: [chapt] -- of row being queried
  671. // [bmk] -- of row being queried
  672. // [piRow] -- returns index of row in table/chapter
  673. // [pcRows] -- returns count of rows in table/chapter
  674. //
  675. // History: 6-29-95 dlee Created
  676. //
  677. //----------------------------------------------------------------------------
  678. SCODE CCategorize::GetApproximatePosition(
  679. CI_TBL_CHAPT chapt,
  680. CI_TBL_BMK bmk,
  681. DBCOUNTITEM * piRow,
  682. DBCOUNTITEM * pcRows )
  683. {
  684. CLock lock( _mutex );
  685. if (bmk == widInvalid)
  686. return DB_E_BADBOOKMARK;
  687. Win4Assert( bmk != WORKID_TBLBEFOREFIRST && bmk != WORKID_TBLAFTERLAST );
  688. DBCOUNTITEM iBmkPos = ULONG_MAX, cRows = 0;
  689. if ( _isCategorized() && DB_NULL_HCHAPTER != chapt )
  690. cRows = _pParent->GetRowCount( chapt );
  691. else
  692. cRows = _aVisibleCategories.Count();
  693. if ( WORKID_TBLFIRST == bmk )
  694. iBmkPos = cRows ? 1 : 0;
  695. else if ( WORKID_TBLLAST == bmk )
  696. iBmkPos = cRows;
  697. else
  698. {
  699. iBmkPos = _FindCategory( bmk ) + 1;
  700. // position is relative to first member of the category (if any)
  701. if ( _isCategorized() && DB_NULL_HCHAPTER != chapt )
  702. iBmkPos -= _FindCategory( _pParent->GetFirstWorkid( chapt ) );
  703. }
  704. Win4Assert(iBmkPos <= cRows);
  705. *piRow = iBmkPos;
  706. *pcRows = cRows;
  707. return S_OK;
  708. } //GetApproximatePosition
  709. //+---------------------------------------------------------------------------
  710. //
  711. // Method: LokGetOneColumn, public
  712. //
  713. // Synopsis: Returns column data for the first item in a category.
  714. //
  715. // Arguments: [wid] -- workid or chapter of the row to be queried
  716. // [rOutColumn] -- layout of the output data
  717. // [pbOut] -- where to write the column data
  718. // [rVarAlloc] -- variable data allocator to use
  719. //
  720. // History: 22-Aug-95 dlee Created
  721. //
  722. //----------------------------------------------------------------------------
  723. void CCategorize::LokGetOneColumn(
  724. WORKID wid,
  725. CTableColumn const & rOutColumn,
  726. BYTE * pbOut,
  727. PVarAllocator & rVarAlloc )
  728. {
  729. unsigned iRow = _FindCategory( wid );
  730. _pChild->LokGetOneColumn( _aVisibleCategories[iRow].widFirst,
  731. rOutColumn,
  732. pbOut,
  733. rVarAlloc );
  734. } //LokGetOneColumn