{$I OVC.INC}

{$B-} {Complete Boolean Evaluation}
{$I+} {Input/Output-Checking}
{$P+} {Open Parameters}
{$T-} {Typed @ Operator}
{$W-} {Windows Stack Frame}
{$X+} {Extended Syntax}

{$IFNDEF Win32}
{$G+} {286 Instructions}
{$N+} {Numeric Coprocessor}

{$C MOVEABLE,DEMANDLOAD,DISCARDABLE}
{$ENDIF}

{*********************************************************}
{*                  OVCTSELL.PAS 2.00                    *}
{*     Copyright (c) TurboPower Software Co 1995-96      *}
{*                 All rights reserved.                  *}
{*********************************************************}

unit OvcTSelL;
  {-Table cell selection list class}


{Notes:

  The TOvcSelectionList class implements a data structure that stores which
  cells in a table are selected. The structure is implemented as an array
  of (sub)arrays, each element of the outer array pertaining to a single
  column. The items in each subarray are ranges of rows that are selected.
  Thus if the cells (in (row,col) format) (1,1)..(2,3) are all selected,
  the structure will look like this:
             Column   Value
             0        nil
             1        array with 1 element: range row 1 to row 2
             2        array with 1 element: range row 1 to row 2
             3        array with 1 element: range row 1 to row 2
             4        nil
             ...
  Adding a new selected cell at (4,2) would add an element to the third
  column's subarray: a range from row 4 to row 4.

  To check whether a cell is selected or not, get the element of the array
  pertaining to the column. If nil, the cell is not selected. If not,
  search through the ranges sequentially until the cell's row falls into a
  range.

  Adding a selected cell (or cells) will generally add a new row range
  element to the relevant column array, but it might cause row range
  mergings. Deselecting a cell (or cells) might cause range splittings and
  mergings.

  To aid in dynamic selection of cells, the class remembers the 'current
  range'. The table using this class will set the anchor cell address and
  then periodically set the new active cell; the difference between these
  is the current range. The class will move the current range to the above
  data structure when the anchor cell is set. IsCellSelected and
  HaveSelection will both look at this current range as well as the data
  structure. Note that the current range could be for selecting as well as
  deselecting cells: there's a flag to define which.

  One important assumption has been made. This is that generally there are
  'few' selections (obviously one can imagine the fanatical user who is
  determined to see 1000 separate disjoint selections, but normally there
  will be one selection to maybe half a dozen). In other words it will be
  more efficient to code a sequential search internally rather than a
  binary one, and other similar types of speed improvements have not been
  used.
}

interface

uses
  {$IFDEF Win32}
  Windows,
  {$ELSE}
  WinProcs,
  {$ENDIF}
  SysUtils, OvcExcpt, OvcConst, OvcTCmmn;

type
  TOvcSelRowRange = record            {a row range}
    L, H : TRowNum;
  end;

  POvcSelRRArray = ^TOvcSelRRArray;   {an array of row ranges}
  TOvcSelRRArray = record
    RRCount : integer;
    RRTotal : integer;
    RRs     : array [0..(MaxInt div sizeof(TOvcSelRowRange))-2] of TOvcSelRowRange;
  end;

  POvcSelColArray = ^TOvcSelColArray; {an array of arrays of row ranges}
  TOvcSelColArray = array [0..(MaxInt div sizeof(POvcSelRRArray))-1] of POvcSelRRArray;

  TOvcSelectionList = class           {class to manage list of selected cells}
    protected {private}
      {.Z+}
      {even sized}
      slArray : POvcSelColArray;      {array of arrays of row ranges}
      slColCount : TColNum;           {number of columns in slArray}
      slColWithSelCount : TColNum;    {num of columns with at least 1 selected cell}
      slActiveCol : TColNum;          {current active cell-column}
      slActiveRow : TColNum;          {                   -row}
      slAnchorCol : TColNum;          {current anchor-column}
      slAnchorRow : TColNum;          {              -row}
      slRowCount : TRowNum;           {number of rows in slArray}
      slColMin : TColNum;             {current range-minimum column}
      slColMax : TColNum;             {             -maximum column}
      slRowMin : TRowNum;             {             -minimum row}
      slRowMax : TRowNum;             {             -maximum row}
      {odd sized}
      slSelecting : boolean;          {current range is for selection}
      slEmptyRange : boolean;         {current range is empty}
      {.Z-}
    protected
      {.Z+}
      procedure slDeselectCellRangeInCol(Row1, Row2 : TRowNum; ColNum : TColNum);
      procedure slSelectCellRangeInCol(Row1, Row2 : TRowNum; ColNum : TColNum);
      {.Z-}
    public
      constructor Create(RowCount : TRowNum; ColCount : TColNum);
        {-Create a new instance for RowCount rows & ColCount columns}
      destructor Destroy; override;
        {-Destroy the instance}

      procedure DeselectAll;
        {-Deselect all cells}
      procedure DeselectCell(RowNum : TRowNum; ColNum : TColNum);
        {-Deselect a single cell}
      procedure DeselectCellRange(FromRow : TRowNum; FromCol : TColNum;
                                  ToRow : TRowNum; ToCol : TColNum);
        {-Deselect a range of cells}
      procedure ExtendRange(RowNum : TRowNum; ColNum : TColNum;
                            IsSelecting : boolean);
        {-Extend/shrink the current range to RowNum, ColNum}
      function HaveSelection : boolean;
        {-Return true if at least one cell is selected}
      function IsCellSelected(RowNum : TRowNum; ColNum : TColNum) : boolean;
        {-Return true if specified cell is selected}
      procedure Iterate(SI : TSelectionIterator; ExtraData : pointer);
        {-Iterate through all the selection ranges calling SI for each}
      procedure SelectAll;
        {-Select all cells}
      procedure SelectCell(RowNum : TRowNum; ColNum : TColNum);
        {-Mark a single cell as selected}
      procedure SelectCellRange(FromRow : TRowNum; FromCol : TColNum;
                                ToRow : TRowNum; ToCol : TColNum);
        {-Mark a range of cells as selected}
      procedure SetColCount(ColCount : TColNum);
        {-Change the number of columns}
      procedure SetRangeAnchor(RowNum : TRowNum; ColNum : TColNum;
                               Action : TOvcTblSelectionType);
        {-Set the anchor cell; if Action is tstAdditional the current
          selection is stored, if not all DeselectAll is called}
      procedure SetRowCount(RowCount : TRowNum);
        {-Change the number of rows}
  end;

implementation
