{*********************************************************}
{*                  OVCTCARY.PAS 4.06                    *}
{*********************************************************}

{* ***** BEGIN LICENSE BLOCK *****                                            *}
{* Version: MPL 1.1                                                           *}
{*                                                                            *}
{* The contents of this file are subject to the Mozilla Public License        *}
{* Version 1.1 (the "License"); you may not use this file except in           *}
{* compliance with the License. You may obtain a copy of the License at       *}
{* http://www.mozilla.org/MPL/                                                *}
{*                                                                            *}
{* Software distributed under the License is distributed on an "AS IS" basis, *}
{* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License   *}
{* for the specific language governing rights and limitations under the       *}
{* License.                                                                   *}
{*                                                                            *}
{* The Original Code is TurboPower Orpheus                                    *}
{*                                                                            *}
{* The Initial Developer of the Original Code is TurboPower Software          *}
{*                                                                            *}
{* Portions created by TurboPower Software Inc. are Copyright (C)1995-2002    *}
{* TurboPower Software Inc. All Rights Reserved.                              *}
{*                                                                            *}
{* Contributor(s):                                                            *}
{*                                                                            *}
{* ***** END LICENSE BLOCK *****                                              *}

{$I OVC.INC}

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

unit ovctcary;
  {-Orpheus Table - Cell Array class}

interface

{Note: this class exists for the *sole* purpose of providing a sorted
       list of cell addresses that need repainting. It has no other
       possible application. It also stores a flag that states that
       the unused bit of the table window needs painting}

uses
  SysUtils, Classes, OvcTCmmn;

type
  TOvcCellAddress = packed record
    Row : TRowNum;
    Col : TColNum;
  end;

  TOvcCellArray = class
    protected {private}
      {.Z+}
      FArray : pointer;
      FLimit : Integer;
      FCount : Integer;

      DoUnusedBit : boolean;
      {.Z-}

    protected
      {.Z+}
      function GetEmpty : boolean;
      {.Z-}

    public
      destructor Destroy; override;

      procedure AddCell(RowNum : TRowNum; ColNum : TColNum);
      procedure AddUnusedBit;
      procedure Clear;
      function DeleteCell(RowNum : TRowNum; ColNum : TColNum) : boolean;
      procedure GetCellAddr(Inx : Integer; var CellAddr : TOvcCellAddress);
      procedure Merge(CA : TOvcCellArray);
      function MustDoUnusedBit : boolean;

      property Count : Integer
         read FCount;

      property Empty : boolean
         read GetEmpty;
    end;

implementation


type
  POvcCellArrayPrim = ^TOvcCellArrayPrim;
  TOvcCellArrayPrim = array [0..9999] of TOvcCellAddress;


{===TOvcCellArray====================================================}
destructor TOvcCellArray.Destroy;
  begin
    if Assigned(FArray) then
      FreeMem(FArray, sizeof(TOvcCellAddress) * FLimit);
  end;
{--------}
procedure TOvcCellArray.AddCell(RowNum : TRowNum; ColNum : TColNum);
  var
    NewLimit : Integer;
    L, R, M  : Integer;
    NewArray : pointer;
    MCell    : TOvcCellAddress;
  begin
    {grow array if required}
    if (FCount = FLimit) then
      begin
        NewLimit := FLimit + 128;
        GetMem(NewArray, sizeof(TOvcCellAddress) * NewLimit);
        if Assigned(FArray) then
          begin
            Move(FArray^, NewArray^, sizeof(TOvcCellAddress) * FLimit);
            FreeMem(FArray, sizeof(TOvcCellAddress) * FLimit);
          end;
        FLimit := NewLimit;
        FArray := NewArray;
      end;
    {do special case, er, specially}
    if (FCount = 0) then
      begin
        with POvcCellArrayPrim(FArray)^[0] do
          begin
            Row := RowNum;
            Col := ColNum;
          end;
        FCount := 1;
        Exit;
      end;
    {binary search through array, insert in order}
    L := 0;
    R := pred(FCount);
    repeat
      M := (L + R) div 2;
      MCell := POvcCellArrayPrim(FArray)^[M];
      if (RowNum = MCell.Row) then
        if (ColNum = MCell.Col) then
          Exit {nothing to do-already present}
        else if (ColNum < MCell.Col) then
          R := M - 1
        else
          L := M + 1
      else if (RowNum < MCell.Row) then
        R := M - 1
      else
        L := M + 1;
    until (L > R);
    {insert at L}
    if (L < FCount) then
      Move(POvcCellArrayPrim(FArray)^[L],
           POvcCellArrayPrim(FArray)^[L+1],
           sizeof(TOvcCellAddress)*(FCount-L));
    with POvcCellArrayPrim(FArray)^[L] do
      begin
        Row := RowNum;
        Col := ColNum;
      end;
    inc(FCount);
  end;
{--------}
procedure TOvcCellArray.AddUnusedBit;
  begin
    DoUnusedBit := true;
  end;
{--------}
procedure TOvcCellArray.Clear;
  begin
    FCount := 0;
    DoUnusedBit := false;
  end;
{--------}
function TOvcCellArray.DeleteCell(RowNum : TRowNum; ColNum : TColNum) : boolean;
  var
    L, R, M  : Integer;
    MCell    : TOvcCellAddress;
  begin
    Result := false;
    {do special case, er, specially}
    if (FCount = 0) then
      Exit;
    {binary search through array}
    L := 0;
    R := pred(FCount);
    repeat
      M := (L + R) div 2;
      MCell := POvcCellArrayPrim(FArray)^[M];
      if (RowNum = MCell.Row) then
        if (ColNum = MCell.Col) then
          begin
            {got it}
            dec(FCount);
            if (FCount > M) then
              Move(POvcCellArrayPrim(FArray)^[M+1],
                   POvcCellArrayPrim(FArray)^[M],
                   sizeof(TOvcCellAddress)*(FCount-M));
            Result := true;
            Exit;
          end
        else if (ColNum < MCell.Col) then
          R := M - 1
        else
          L := M + 1
      else if (RowNum < MCell.Row) then
        R := M - 1
      else
        L := M + 1;
    until (L > R);
  end;
{--------}
procedure TOvcCellArray.GetCellAddr(Inx : Integer; var CellAddr : TOvcCellAddress);
  begin
    if (0 <= Inx) and (Inx < FCount) then
      CellAddr := POvcCellArrayPrim(FArray)^[Inx]
    else
      FillChar(CellAddr, sizeof(CellAddr), 0);
  end;
{--------}
function TOvcCellArray.GetEmpty : boolean;
  begin
    Result := (Count = 0) and (not DoUnusedBit);
  end;
{--------}
procedure TOvcCellArray.Merge(CA : TOvcCellArray);
  var
    NewA     : POvcCellArrayPrim;
    InxMerge : integer;
    InxSelf  : integer;
    InxCA    : integer;
    CellSelf : TOvcCellAddress;
    CellCA   : TOvcCellAddress;
    NewLimit : integer;
    MergeNum : integer;
    i        : integer;
  begin
    {if both cell arrays are empty, there's nothing to do}
    if (Count = 0) and (CA.Count = 0) then
      Exit;
    {make a new array at least as large as both arrays put together}
    NewLimit := ((Count + CA.Count) + 127) and $7FFFFF80;
    GetMem(NewA, sizeof(TOvcCellAddress) * NewLimit);
    {prepare for the loop}
    InxMerge := 0;
    InxSelf := 0;
    InxCA := 0;
    if (Count > 0) then
      CellSelf := POvcCellArrayPrim(FArray)^[0];
    if (CA.Count > 0) then
      CellCA := POvcCellArrayPrim(CA.FArray)^[0];
    {loop until one (or both) of the arrays is exhausted}
    while (InxSelf < Count) and (InxCA < CA.Count) do
      begin
        if (CellSelf.Row < CellCA.Row) then
          MergeNum := 1
        else if (CellSelf.Row > CellCA.Row) then
          MergeNum := 2
        else {CellSelf.Row = CellCA.Row}
          if (CellSelf.Col < CellCA.Col) then
            MergeNum := 1
          else if (CellSelf.Col > CellCA.Col) then
            MergeNum := 2
          else {both rows & cols equal}
            MergeNum := 0;
        case MergeNum of
          0 : begin {equal}
                NewA^[InxMerge] := CellSelf;
                inc(InxMerge);
                inc(InxSelf);
                if (InxSelf < Count) then
                  CellSelf := POvcCellArrayPrim(FArray)^[InxSelf];
                inc(InxCA);
                if (InxCA < CA.Count) then
                  CellCA := POvcCellArrayPrim(CA.FArray)^[InxCA];
              end;
          1 : begin
                NewA^[InxMerge] := CellSelf;
                inc(InxMerge);
                inc(InxSelf);
                if (InxSelf < Count) then
                  CellSelf := POvcCellArrayPrim(FArray)^[InxSelf];
              end;
          2 : begin
                NewA^[InxMerge] := CellCA;
                inc(InxMerge);
                inc(InxCA);
                if (InxCA < CA.Count) then
                  CellCA := POvcCellArrayPrim(CA.FArray)^[InxCA];
              end;
        end;{case}
      end;
    {after this loop one (or both) of the input merge streams has been
     exhausted; copy the remaining elements from the other}
    if (InxSelf = Count) then {self array exhausted}
      for i := InxCA to pred(CA.Count) do
        begin
          NewA^[InxMerge] := POvcCellArrayPrim(CA.FArray)^[i];
          inc(InxMerge);
        end
    else if (InxCA = CA.Count) then {CA array exhausted}
      for i := InxSelf to pred(Count) do
        begin
          NewA^[InxMerge] := POvcCellArrayPrim(FArray)^[i];
          inc(InxMerge);
        end;
    {all merged, replace the current array with the merged array}
    FreeMem(FArray, sizeof(TOvcCellAddress) * FLimit);
    FArray := NewA;
    FLimit := NewLimit;
    FCount := InxMerge;
  end;
{--------}
function TOvcCellArray.MustDoUnusedBit : boolean;
  begin
    Result := DoUnusedBit;
  end;
{====================================================================}


end.
