{ ------------------------------------------------------------- }
{ Purpose : NextGrid 6                                          }
{ By      : Bojan Nikolic                                       }
{ For     : BergSoft                                            }
{ ------------------------------------------------------------- }
{ Copyright (C) BergSoft.net 2012. All Rights Reserved.         }
{ ------------------------------------------------------------- }
unit NxGrid6;

interface

uses
  { Standard }
  Classes, Types, Windows, Graphics, SysUtils,
  { Next Standard 6 }
  NxIntf6, NxClasses6, NxConsts6, NxTypes6,
  { NextGrid 6 }
  NxVirtualGrid6, NxCustomGrid6, NxColumns6, NxCells6;

const
  exEvaluateError = 'Query evaluation error (%d): %s';

type
  TNxCellEvent = procedure (Sender: TObject; ACol, ARow: Integer) of object;
  TNxCompareEvent = procedure(Sender: TObject; Cell1, Cell2: INxCell; var Compare: Integer) of object;
  TNxRowNotifyEvent = procedure (Sender: TObject; ARow: Integer) of object;

type
  TNxFitOptions = set of (foColumn, foRow);

type
  EQueryEvaluateError = class(Exception);

type
  { TNextGrid6 }

  TNextGrid6 = class(TNxVirtualGrid6, INxCellsControl, INxRowsViewControl)
  private
    FAutoCalculate: Boolean;
    FAutoFit: Boolean;
    FAutoSort: Boolean;
    FCells: TNxCells6;
    FFitOptions: TNxFitOptions;
    FOnCellChange: TNxCellEvent;
    FOnCellCompare: TNxCompareEvent;
    FOnRowExpandedChange: TNxRowNotifyEvent;
    { Property Accessors }
    procedure SetAutoCalculate(const Value: Boolean);
    procedure SetAutoSort(const Value: Boolean);
    procedure SetLastAddedRow(const Value: Integer);
  protected
    procedure AssignCells(Value: INxCells); override;
    procedure CreateCells;

    function GetAutoCalculate: Boolean; override;

    function GetChildRowCount: Integer; override;

    { }
    procedure LocateCell(ACol, ARow: Integer); override;

    { INxCellsControl Methods }
    procedure ChildDeleted(ARow: INxRow; Hide: Boolean);
    procedure ChildsCleared(ARow: INxRow);
    procedure RowMoved(const CurIndex, NewIndex: Integer);

    procedure CellChanged(Cell: INxCell; UpdateSpread: TNxCellUpdateSpread); virtual;
    procedure ExpandedChanged(Row: INxRow; Expanded: Boolean);
    procedure HeightChanged(Index: Integer; Delta: Integer);
    procedure ShowChildRows(FirstChild: INxRow; Count: Integer; Hide: Boolean);
    procedure VisibilityChanged(Index: Integer; Show: Boolean);
    { Virtual Methods }
    function GetSelected(Index: Integer): Boolean; override;
    procedure SetCells(ACol, ARow: Integer; const Value: WideString); override;
    procedure SetFiltered(const Value: Boolean); override;
    procedure SetRowCount(const Value: Integer); override;
    procedure SetSelected(Index: Integer; const Value: Boolean); override;
    { Event Handlers }
    procedure DoCellChange(ACol, ARow: Integer); dynamic;
    procedure DoCellCompare(Cell1, Cell2: INxCell; var Compare: Integer); dynamic;
    procedure DoRowExpandedChange(ARow: Integer); dynamic;
    { External Event Handlers }
    procedure DoColumnAdd(Sender: TNxColumns6; Index: Integer = -1); override;
    procedure DoColumnRemove(Sender: TNxColumn6); override;
    procedure DoColumnSort(Sender: TNxColumn6); override;
    { Misc. }
    { Methods }
    procedure Filter; override;
    procedure Recount; override;
    procedure TrySelectLastAddedRow;

    function IsVirtual: Boolean; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Public Event Handlers }
    procedure DoAddRow(Sender: TObject); override;

    { Public Methods }
    function AddChildRow(const Index: Integer; const Count: Integer = 1;
      IgnoreDefaults: Boolean = True): INxCellsRow; overload;
    function AddChildRow(ParentRow: INxRow; Count: Integer = 1;
      IgnoreDefaults: Boolean = True): INxCellsRow; overload;
    function AddInsertRow: Boolean; override;
    function AddSiblingRow(const Index: Integer; const Count: Integer = 1;
      IgnoreDefaults: Boolean = True): INxCellsRow; overload;
    function CanEdit(const ACol, ARow: Integer): Boolean; override;
    procedure ChangeRowHeight(const Value: Integer); override;
    procedure Clear;
    procedure ClearRow(const Index: Integer);
    procedure ClearRows;
    procedure DeleteChildRow(const Index: Integer);
    procedure DeleteSelected;
    procedure FullCollapse;
    procedure FullExpand;
    function GetNextChild(const Index: Integer; VisibleOnly: Boolean = True): Integer; override;
    procedure HideAll(Func: TNxHideAllFunc = nil); overload;
    function IsUnique(const Index: Integer; Cell: INxCell): Boolean; override;
    procedure MoveRow(const FromPos, ToPos: Integer);
    function QueryCells(Query: WideString): TNxCells6;
    procedure SwapRows(const FromPos, ToPos: Integer);
    procedure UnhideAll;
    { Properties }
  published
    { Properties }
    property AutoCalculate: Boolean read GetAutoCalculate write SetAutoCalculate default False;
    property AutoFit: Boolean read FAutoFit write FAutoFit default False;
    property AutoSort: Boolean read FAutoSort write SetAutoSort default False;
    property FitOptions: TNxFitOptions read FFitOptions write FFitOptions default [foRow];
    property RowCount;
    { Events }
    property OnCellChange: TNxCellEvent read FOnCellChange write FOnCellChange;
    property OnCellCompare: TNxCompareEvent read FOnCellCompare write FOnCellCompare;
    property OnRowExpandedChange: TNxRowNotifyEvent read FOnRowExpandedChange write FOnRowExpandedChange;
  end;

  TNxRule = record
    Column: TNxColumn6;
    Value: WideString;
    Comparasion: TNxComparsion;
    Operand: TNxOperand;
    DataType: TNxDataType;
  end;

  TNxRules = array of TNxRule;

implementation

uses
  { Standard }
  Math, Dialogs;

{ TNextGrid6 }

function TNextGrid6.AddChildRow(const Index, Count: Integer;
  IgnoreDefaults: Boolean): INxCellsRow;
begin
  Result := Row[Index].AddChildRow(Count, IgnoreDefaults);
end;

function TNextGrid6.AddChildRow(ParentRow: INxRow; Count: Integer;
  IgnoreDefaults: Boolean): INxCellsRow;
begin
  Result := ParentRow.AddChildRow(Count, IgnoreDefaults);
end;

function TNextGrid6.AddInsertRow: Boolean;
var
  i: Integer;
begin
  { Default }
  Result := False;

  { Event inside }
  if EndEditing then
  begin
    { Should Add? }
    Result := inherited AddInsertRow;

    { Yes, Add }
    if Result then
      with AddRow.Cells do
      begin
        for i := 0 to Pred(Columns.Count) do
        begin
          FCells.Location := Point(i, Pred(RowCount));

          { Assign without convert }
          Item[i].AssignValue(Columns[i].InsertValue);

          { Set Default Value }
          Columns[i].InsertValue.Revert;
        end;

        { Trigger event }
        DoInsertRowAdd;
      end;

  end; { EndEditing? }

end;

function TNextGrid6.AddSiblingRow(const Index, Count: Integer; IgnoreDefaults: Boolean): INxCellsRow;
begin
  Result := Row[Index].AddSiblingRow(Count, IgnoreDefaults);
end;

procedure TNextGrid6.AssignCells(Value: INxCells);
begin
  FCells.Control := Self;
end;

function TNextGrid6.CanEdit(const ACol, ARow: Integer): Boolean;
begin
  { Cell to be enabled too }
  Result := inherited CanEdit(ACol, ARow)
    and Cell[ACol, ARow].Enabled;
end;

procedure TNextGrid6.CellChanged(Cell: INxCell; UpdateSpread: TNxCellUpdateSpread);
var
  Location: TPoint;
begin
  { Cell[] Prop. change location (!) }
  Location := Cell.Location;

  if Cell.Enabled then
  begin

    { Unique }
    if Columns[Location.X].Unique and
      not IsUnique(Location.X, Cell) then Exit;

    { Call event }
    if CanUpdate([uoEvents]) then
    begin
      DoCellChange(Location.X, Location.Y);
    end;

    { Apply filter, if set }
    if Filtered then
    begin
      FCells.ProcessFilter(Location.Y);
    end;

    { Note: Will change ColIndex of Cell (!) }
    if AutoFit then
    begin
      if foColumn in FitOptions then BestFitCol(Location.X);
      if foRow in FitOptions then BestFitRow(Location.Y);
    end;

    { Update InplaceEdit, if fisible }
    if Assigned(InplaceEdit) then
    begin
      InplaceEdit.AssignValue(Cell);
    end;

    { ToDo: Check if in view }
    if Assigned(ActiveView)
      and CanUpdate([uoRepaint]) then
    begin
      case UpdateSpread of
        cuCell: ActiveView.RefreshCell(Location, True);
        cuRow: ActiveView.RefreshRow(Location.Y);
        cuGrid: Invalidate;
      end;

    end;

  end;

  { Adjust Value Prop. }
  if CanUpdate([uoRecalculate])
    and AutoCalculate then
  begin
    Recalculate;
  end;
end;

procedure TNextGrid6.ChangeRowHeight(const Value: Integer);
var
  i: Integer;
begin
  inherited;
  for i := 0 to Pred(RowCount) do Row[i].Height := Value;
end;

procedure TNextGrid6.ChildDeleted(ARow: INxRow; Hide: Boolean);
var
  Selected, Shown: Boolean;
  Index, RowHeight: Integer;
begin
  Index := ARow.AbsoluteIndex;
  RowHeight := 0;

  if Assigned(ActiveView) then
  begin
    RowHeight := ActiveView.ItemHeight[Index];
  end;

  Selected := Index = SelectedRow;
  Shown := ARow.Showing;

  if Index = FLastAddedRow then FLastAddedRow := -1;

  { 10/15/13: SelectedRow can not stay the same (!) }
  if Index < SelectedRow then Dec(FSelectedRow);

  if Assigned(ActiveView) then
  begin

    { Row was Shown, before deleted? }
    if Shown then
    begin
      ActiveView.DoRowAppear(Index, RowHeight, False, True);

      if ARow.HasChildren and ARow.Expanded then
        ActiveView.ExpandRow(ARow, False);

    end;

    { It was selected }
    if CanUpdate([uoRepaint]) and Selected
      then ActiveView.RefreshSelection;

    if CanUpdate([uoScrollBar])
      then UpdateScrollBars;
  end;

  if CanUpdate([uoRecalculate])
    then Recalculate;

  if AutoSelect and (SelectedRow > LastVisibleRow)
    then SelectedRow := LastVisibleRow;
end;

procedure TNextGrid6.ChildsCleared(ARow: INxRow);
begin
  if not (csDestroying in ComponentState) then
    if Assigned(ActiveView) then
    begin
      if CanUpdate([uoRepaint]) then
      begin
        ActiveView.ExpandRow(ARow, False);
        ActiveView.RefreshRow(ARow.AbsoluteIndex);
      end;

      if CanUpdate([uoScrollBar])
        then UpdateScrollBars;
    end;
end;

procedure TNextGrid6.Clear;
begin
  Columns.Clear;
  ClearRows;
end;

procedure TNextGrid6.ClearRow(const Index: Integer);
var
  i: integer;
begin
  if RowExists(Index)
    then for i := 0 to Pred(ColCount) do Cell[i, Index].Revert;
end;

procedure TNextGrid6.ClearRows;
var
  i: Integer;
begin
  FCells.Clear;

  { Reset selection }
  Exclude(FState, gtMultiSelected);
  FSelectedRow := -1;

  for i := 0 to Pred(Columns.Count) do
    with Columns[i].Footer do
    begin
      Sum := 0;
      Value := 0;
    end;

  if Assigned(ActiveView) then
    with ActiveView do
    begin
      { Rel. CapturedView, FirstIndex etc. }
      Reset;

      if CanUpdate([uoRecount])
        then ActiveView.Recount;

      if CanUpdate([uoRepaint])
        then ActiveView.Invalidate(ActiveView.DataRect);

      VertScrollBar.Clear(True);
    end;
end;

constructor TNextGrid6.Create(AOwner: TComponent);
begin
  inherited;
  FAutoFit := False;
  FAutoSort := False;
  FFitOptions := [foRow];

  { Create cells struct. }
  CreateCells;
end;

procedure TNextGrid6.CreateCells;
begin
  FCells := TNxCells6.Create(nil, RowHeight);
  FCells.Control := Self;
  FCellSource := FCells;
end;

procedure TNextGrid6.DeleteChildRow(const Index: Integer);
begin
  Row[Index].ParentRow := nil;
end;

procedure TNextGrid6.DeleteSelected;
begin
  DeleteRow(SelectedRow);
end;

destructor TNextGrid6.Destroy;
begin
  FCells := nil;
  inherited; { Destroy FCellSource }
end;

procedure TNextGrid6.DoAddRow(Sender: TObject);
begin
  AddRow;
end;

procedure TNextGrid6.DoCellChange(ACol, ARow: Integer);
begin
  if Assigned(FOnCellChange) then FOnCellChange(Self, ACol, ARow);
end;

procedure TNextGrid6.DoColumnAdd(Sender: TNxColumns6; Index: Integer);
begin
  inherited;
  FCells.AddColumn;
end;

procedure TNextGrid6.DoColumnRemove(Sender: TNxColumn6);
begin
  inherited;

  { Grid may be destroying too! }
  if Assigned(FCells) then FCells.DeleteColumn(Sender);
end;

procedure TNextGrid6.DoColumnSort(Sender: TNxColumn6);
var
  SelectedRowIntf: INxRow;
begin
  if not (csLoading in ComponentState) then
  begin

    if Sender.Sorted then
    begin
      SelectedRowIntf := nil;

      if RowExists(SelectedRow)
        then SelectedRowIntf := Row[SelectedRow];

      FCells.Sort(Sender.Index, Sender.SortKind, False);

      { Set SelectedRow Back to prev. selected }
      if Assigned(SelectedRowIntf) then
      begin
        { 09/20/13: It is fine to set `FSelectedRow` only since SelectCell
                    events and etc. are not needed because same row is
                    selected anyway }
        FSelectedRow := FCells.IndexOf(SelectedRowIntf);
      end;

      if Assigned(ActiveView) then
      begin
        { Invalidate Data & Indicators }
        ActiveView.RefreshData;

        { ToDo: Update LastFixedRow }
        { Update FirstIndex }
        ActiveView.AfterSort;

        ScrollToSelected;
      end;

    end;

    { `OnAfterSort` call inside }
    inherited;
  end;
end;

procedure TNextGrid6.DoCellCompare(Cell1, Cell2: INxCell; var Compare: Integer);
begin
  if Assigned(FOnCellCompare) then FOnCellCompare(Self, Cell1, Cell2, Compare);
end;

procedure TNextGrid6.DoRowExpandedChange(ARow: Integer);
begin
  if Assigned(FOnRowExpandedChange) then FOnRowExpandedChange(Self, ARow);
end;

procedure TNextGrid6.ExpandedChanged(Row: INxRow; Expanded: Boolean);
var
  RowIndex: Integer;
begin
  if Assigned(ActiveView) then
  begin
    RowIndex := Row.AbsoluteIndex;
    DoRowExpandedChange(RowIndex);
    ActiveView.Invalidate;
//    ActiveView.ExpandRow(Row, Expanded);
    ActiveView.RefreshRow(RowIndex);
    UpdateScrollBars;
  end;
end;

procedure TNextGrid6.Filter;
begin
  EndEditing;

  try
    VertScrollBar.Lock;

    { Re-filter }
    FCells.FilterAll;

  finally
    VertScrollBar.Unlock(False);
  end;

  { Refresh & Update ScrollBar }
  inherited Filter;
end;

procedure TNextGrid6.FullCollapse;
var
  i: Integer;
begin
  for i := 0 to Pred(ChildRowCount) do
  begin
    ChildRow[i].Collapse(True);
    ChildRow[i].Expanded := False;
  end;
end;

procedure TNextGrid6.FullExpand;
var
  i: Integer;
begin
  for i := 0 to Pred(ChildRowCount) do ChildRow[i].Expand(True);
end;

function TNextGrid6.GetAutoCalculate: Boolean;
begin
  Result := FAutoCalculate;
end;

function TNextGrid6.GetChildRowCount: Integer;
begin
  Result := FCells.ChildRowCount;
end;

function TNextGrid6.GetNextChild(const Index: Integer;
  VisibleOnly: Boolean): Integer;
begin
  Result := Row[Index].Index;
  while Result < Pred(FCells.ChildRowCount) do
  begin
    Inc(Result);
    if not VisibleOnly or Row[Result].Visible
      then Break;
  end;
  Result := ChildRow[Result].AbsoluteIndex;
end;

function TNextGrid6.GetSelected(Index: Integer): Boolean;
begin
  if MultiSelect then Result := Row[Index].Selected
    else Result := SelectedRow = Index;
end;

procedure TNextGrid6.HeightChanged(Index, Delta: Integer);
begin
  { Must re-calc. }
  if CanUpdate([uoScrollBar])
    then UpdateScrollBars;

  if CanUpdate([uoRepaint]) then
  begin
    Invalidate;
  end;
end;

procedure TNextGrid6.HideAll(Func: TNxHideAllFunc);
begin
  EndEditing;

  try
    BeginUpdate([]);

    { Scroll to top, release Captured view etc. }
    ActiveView.Reset; { ToDo: Maybe to call it Clear? }

    { Scroll to Top, quietly }
    VertScrollBar.Clear;

    FCells.HideAll(Func);
  finally

    { Don't update instantly! }
    EndUpdate;
  end;

end;

function TNextGrid6.IsUnique(const Index: Integer; Cell: INxCell): Boolean;
begin
  //TODO: FCells.IsUnique()
  Result := False;
end;

function TNextGrid6.IsVirtual: Boolean;
begin
  Result := False;
end;

procedure TNextGrid6.LocateCell(ACol, ARow: Integer);
begin
  FCells.Location := Point(ACol, ARow);
end;

procedure TNextGrid6.MoveRow(const FromPos, ToPos: Integer);
var
  T, T2: INxRow;
begin
  if FromPos <> ToPos then
  begin
//    if Assigned(ActiveView)
//      then ActiveView.DoRowAppear(FromPos, ActiveView.ItemHeight[FromPos], False, False);

    T := Row[FromPos];
    T2 := Row[ToPos];

//    Self.BeginUpdate;

    { Delete self }
    T.Delete;

//   exit;

    if ToPos > FromPos then
    begin
      T2.ParentRow.InsertChildRow(T2.Index  + 1 , T);
    end else
    begin
      T2.ParentRow.InsertChildRow(T2.Index , T);
    end;


    //Self.EndUpdate(False);

//    Row[FromPos].MoveTo(Row[FromPos].ParentRow, ToPos);
//    FCells.MoveRow(FromPos, ToPos);

//    if Assigned(ActiveView)
//      then ActiveView.DoRowAppear(ToPos, ActiveView.ItemHeight[FromPos], True, True);
  end;
end;

procedure TNextGrid6.Recount;
begin
  FCells.Recalculate;
end;

procedure TNextGrid6.RowMoved(const CurIndex, NewIndex: Integer);
begin
  if Assigned(ActiveView) then
  begin
    ActiveView.DoRowAppear(CurIndex, Row[CurIndex].Height, False, True);
    ActiveView.DoRowAppear(NewIndex, Row[NewIndex].Height, True, True);
    Invalidate;
  end;
end;

function TNextGrid6.QueryCells(Query: WideString): TNxCells6;
const
  ArraySize = 8;
var
  i: Integer;
  Count: Integer;
  Rules: TNxRules;

  procedure AddRule(Rule: TNxRule);
  begin
    if Count = Length(Rules) then SetLength(Rules, Length(Rules) + Count);
    Rules[Count] := Rule;
    Inc(Count);
    Rule.Column := nil;
  end;

  procedure RaiseException(const Index: Integer);
  begin
    raise EQueryEvaluateError.CreateFmt(exEvaluateError, [Index, Copy(Query, 0, Index)]);
  end;

  function ParseRules: Boolean;
  var
    c: Integer;
    State: Integer;
    Buffer: WideString;
    Rule: TNxRule;
    QuoteCount: Integer;
  begin
    { Failed by default }
    Result := False;

    Rule.Column := nil;

    QuoteCount := 0;


    State := 0;

    { Evaluate string }
    c := 1;

    while c <= Length(Query) do
    begin

      if CharInSet(Char(Query[c]), [#13, #10]) then
      begin
        Inc(c);
        Continue;
      end;

//      if Query[c] = '(' then
//      begin
//        Result := Result and ParseRules;
//      end;
//
//      if Query[c] = ')' then
//      begin
//        Exit;
//      end;

      case State of

        0:  { Reading Spaces }
        begin
          if CharInSet(Char(Query[c]), ['a'..'z', 'A'..'Z']) then
          begin
            Buffer := Query[c];
            Inc(State);
          end
          else if Query[c] <> ' ' then
          begin
            RaiseException(c);
            Exit;
          end;

          Inc(c);
        end;

        1:  { Reading Cell's Col. }
        begin

          if CharInSet(Char(Query[c]), ['a'..'z', 'A'..'Z']) then
          begin
            Buffer := Buffer + Query[c];
            Inc(c);
          end
          else if CharInSet(Char(Query[c]), ['<', '>', '=', ' ']) then
          begin

            { Do we have a Col? }
            if Buffer <> '' then
            begin
              Rule.Column := Columns.ItemBy[Buffer];

              if Rule.Column = nil then
              begin
                RaiseException(c);

                Exit;
              end;

              { Stop reading }
              Buffer := '';
            end;

            if Query[c] <> ' ' then
            begin
              Buffer := Query[c];
              Inc(c);
            end else Buffer := '';

            { Go to next step }
            State := 2;

            Continue;
          end
          else
          begin
            RaiseException(c);
            Exit;
          end;

        end;

        2:  { Reading < > & = signs }
        begin

          if CharInSet(Char(Query[c]), ['<', '>', '=']) then
          begin
            Buffer := Buffer + Query[c];
            Inc(c);
          end else
          begin

            if (Query[c] = ' ') and (Buffer = '') then
            begin
              Inc(c);
              Continue;
            end;

            if StrToComparsion(Buffer) = coNone then
            begin
              RaiseException(c);

              Exit;
            end
            else Rule.Comparasion := StrToComparsion(Buffer);

            Buffer := '';

            { Stop reading }
            if Query[c] <> ' '
              then Buffer := Query[c] else Inc(c);

            { Go to next step }
            State := 3;
            QuoteCount := 0;

            Continue;
          end;

        end;

        3:  { Reading Value }
        begin

          if CharInSet(Char(Query[c]), ['0'..'9', LocalSettings.DecimalSeparator, '-', '+']) then
          begin
            if QuoteCount = 0 then
            Rule.DataType := dtFloat;

            Buffer := Buffer + Query[c];
            Inc(c);
          end
          else if Query[c] = '"' then
          begin
            Rule.DataType := dtString;

            Inc(QuoteCount);

            if QuoteCount = 1 then
            begin
              Inc(c);
            end
            else if QuoteCount = 2 then
            begin
              Inc(State);
              Rule.Value := Buffer;

              Buffer := '';
              Inc(c);

              Continue;
            end;


          end
          else if Query[c] = ' ' then
          begin
            Inc(State);

            if LowerCase(Buffer) = 'true' then Rule.DataType := dtBoolean
            else if LowerCase(Buffer) = 'false' then Rule.DataType := dtBoolean;

            Rule.Value := Buffer;

            Buffer := '';
          end
          else
          begin
            if QuoteCount < 3 then
            begin
              Buffer := Buffer + Query[c];
              Inc(c);
            end
            else
            begin
              RaiseException(c);
              Exit;
            end;
          end;

        end;

        4:  { Reading and/or operands }
        begin
          if CharInSet(Char(Query[c]), ['a'..'z', 'A'..'Z']) then
          begin
            Buffer := Buffer + Query[c];
            Inc(c);
          end else
          begin
            if (Query[c] = ' ') and (Buffer = '') then
            begin
              Inc(c);
              Continue;
            end;

            { is operand? }
            if StrToOperand(Buffer) = opUndefined then
            begin
              RaiseException(c);
              Exit;
            end
            else Rule.Operand := StrToOperand(Buffer);

            AddRule(Rule);

            { Reset to step 0 }
            State := 0;
            Buffer := '';
          end;

        end;

      end; { case }

    end; { while }

    { Last char is read, but still
      need to add rule into array
      & finalize things }

    { Value not fully added (in step 3) }
    if Rule.Column <> nil then
      if State >= 3 then
      begin
        if LowerCase(Buffer) = 'true' then Rule.DataType := dtBoolean
        else if LowerCase(Buffer) = 'false' then Rule.DataType := dtBoolean;

        if Buffer <> '' then
          Rule.Value := Buffer;

        AddRule(Rule);
      end;

    { Success }
    Result := True;
  end;

  function DoAdd(Index: Integer): Boolean;
  var
    i: Integer;
    Operand: TNxOperand;
    Match: Boolean;
    Cell: INxCell;
  begin
    Result := False;
    Match := False;

    Operand := opOr;

    for i := 0 to Pred(Count) do
    begin
      Cell := Self.Cell[Rules[i].Column.Index, Index];

      case Rules[i].DataType of
        dtBoolean:
          case Rules[i].Comparasion of
            coEqual: Match := Cell.AsBoolean = StrToBool(Rules[i].Value);
            coNotEqual: Match := Cell.AsBoolean <> StrToBool(Rules[i].Value);
          end;
        dtFloat:
          case Rules[i].Comparasion of
            coEqual: Match := Cell.AsFloat = StrToFloat(Rules[i].Value);
            coLessEqualThan: Match := Cell.AsFloat <= StrToFloat(Rules[i].Value);
            coLessThan: Match := Cell.AsFloat < StrToFloat(Rules[i].Value);
            coMoreEqualThan: Match := Cell.AsFloat >= StrToFloat(Rules[i].Value);
            coMoreThan: Match := Cell.AsFloat > StrToFloat(Rules[i].Value);
            coNotEqual: Match := Cell.AsFloat <> StrToFloat(Rules[i].Value);
          end;
        dtString:
          case Rules[i].Comparasion of
            coLessThan: Match := Cell.AsString < Rules[i].Value;
            coLessEqualThan: Match := Cell.AsString <= Rules[i].Value;
            coEqual: Match := Cell.AsString = Rules[i].Value;
            coNotEqual: Match := Cell.AsString <> Rules[i].Value;
          end;
      end;

      case Operand of
        opAnd: Result := Result and Match;
        opOr: Result := Result or Match;
      end;

      Operand := Rules[i].Operand;
    end;

  end;

begin
  Result := nil;

  { Default Size of Array }
  SetLength(Rules, ArraySize);

  Count := 0;

  { Valid query? }
  if ParseRules then
  begin

    { Has any rule? }
    if Count > 0 then
    begin
      Result := TNxCells6.Create(nil, RowHeight);
      Result.Control := Self as INxCellsControl;
      Result.ColCount := Columns.Count;

      { Applying rules to each Row }
      for i := 0 to Pred(RowCount) do
      begin

        if DoAdd(i) then
        begin
          with Result.AddRow(1, True) do
          begin
            AssignCells(Row[i] as INxCellsRow);
          end;
        end;

      end;

    end;

  end;

end;

procedure TNextGrid6.SetAutoCalculate(const Value: Boolean);
begin
  FAutoCalculate := Value;
end;

procedure TNextGrid6.SetAutoSort(const Value: Boolean);
begin
  FAutoSort := Value;
  FCells.AutoSort := FAutoSort;
end;

procedure TNextGrid6.SetCells(ACol, ARow: Integer; const Value: WideString);
begin
  FCells.Cell[ACol, ARow].AsString := Value;
end;

procedure TNextGrid6.SetFiltered(const Value: Boolean);
begin
  { Don't Filter if Reading Prop. }
  if not (csReading in ComponentState) then
  begin
    case Value of
      True: FCells.FilterAll;
      else FCells.UnfilterAll;
    end;
  end;

  { Set ScrollBar & Refresh }
  inherited;
end;

procedure TNextGrid6.SetLastAddedRow(const Value: Integer);
begin
  FLastAddedRow := Value;
  if AutoSelect then SelectedRow := FLastAddedRow;
end;

procedure TNextGrid6.SetRowCount(const Value: Integer);
var
  i: Integer;
begin
  if Value > -1 then
  begin
    if Value > RowCount then AddRow(Value - RowCount) else
    begin
      for i := 1 to RowCount - Value
        do DeleteRow(Pred(RowCount));
    end;
  end;
end;

procedure TNextGrid6.SetSelected(Index: Integer; const Value: Boolean);
begin
  Row[Index].Selected := Value;
end;

procedure TNextGrid6.ShowChildRows(FirstChild: INxRow; Count: Integer; Hide: Boolean);
var
  i: Integer;
  ChildRow: INxRow;
begin
  if FirstChild.Level = -1 then Exit;

  if Assigned(ActiveView) then
  begin

    { Invalidate & Recount }
    if CanUpdate([uoRepaint]) then
    begin

      ChildRow := FirstChild;

      for i := 0 to Count - 1 do
      begin

        ActiveView.DoRowAppear(ChildRow.AbsoluteIndex, ActiveView.ItemHeight[i], True, True);

        ChildRow := ChildRow.NextSibling;
      end;

      if FirstChild.HasChildren and FirstChild.Expanded then
        ActiveView.ExpandRow(FirstChild, True);



    end;

    { ToDo: Maybe to simply increase Max }
    if CanUpdate([uoScrollBar])
      then UpdateScrollBars;
  end;

end;

procedure TNextGrid6.SwapRows(const FromPos, ToPos: Integer);
begin
  FCells.SwapRows(FromPos, ToPos);
  if CanUpdate([uoRepaint]) then Invalidate;
end;

procedure TNextGrid6.TrySelectLastAddedRow;
begin

end;

procedure TNextGrid6.UnhideAll;
begin
  FCells.UnhideAll;
end;

procedure TNextGrid6.VisibilityChanged(Index: Integer; Show: Boolean);
begin
  if Assigned(ActiveView) then
  begin
    ActiveView.DoRowAppear(Index, ActiveView.ItemHeight[Index], Show, False);

    if not Show and (EditingCell.Y = Index) then
    begin
      EndEditing(False);
    end;

    if CanUpdate([uoScrollBar])
      then UpdateVertScrollBar;

    UpdateInplaceEditor;
  end;
end;

end.

