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

interface

uses
  { Standard }
  Classes, Types, Windows, Graphics, SysUtils, Controls,
  {$IFDEF UI_TYPES}UITypes,{$ENDIF}
  { Next Standard 6 }
  NxIntf6, NxClasses6, NxConsts6, NxTypes6,
  { NextGrid 6 }
  NxCustomGrid6, NxColumns6, NxCells6;

type
  TNxAddRowEvent = procedure (Sender: TObject; Count: Integer) of object;
  TNxGetRowCountEvent = function (Sender: TObject): Integer of object;

  TNxCellEvent = procedure (Sender: TObject; ACol, ARow: Integer; Cell: INxCell) of object;

  TNxRowEvent = procedure (Sender: TObject; Index: Integer; Row: INxRow) of object;
  TNxRowShowingEvent = procedure (Sender: TObject; const Index: Integer;
    var Showing: Boolean) of object;

  { TNxVirtualCell }

  TNxVirtualCell = class(TNxCell6)
  private
    FBooleanValue: Boolean;
    FDateTimeValue: TDateTime;
    FFloatValue: Double;
    FIntegerValue: Integer;
    FStringValue: WideString;
  protected
    function GetAsBoolean: Boolean; override;
    function GetAsDateTime: TDateTime; override;
    function GetAsFloat: Double; override;
    function GetAsInteger: Integer; override;
    function GetAsString: WideString; override;
    procedure SetAsBoolean(const Value: Boolean); override;
    procedure SetAsDateTime(const Value: TDateTime); override;
    procedure SetAsFloat(const Value: Double); override;
    procedure SetAsInteger(const Value: Integer); override;
    procedure SetAsString(const Value: WideString); override;
  public
    procedure SetDummyValue; override;
  end;

  { TNxVirtualCellSource6 }

  TNxShowingState = (ssVisible, ssHidden, ssUnknown);

  TNxVirtualCellSource6 = class(TComponent, INxCells, INxSelectable, INxNotifyRowsChanged)
  private
    { Property Fields }
    FCell: INxCell;
    FFirstVisibleRow: Integer;
    FLastVisibleRow: Integer;
    FOnAddRow: TNxAddRowEvent;
    FOnClearRows: TNotifyEvent;
    FOnGetCell: TNxCellEvent;
    FOnGetRow: TNxRowEvent;
    FOnGetRowCount: TNxGetRowCountEvent;
    FOnGetRowShowing: TNxRowShowingEvent;
    FRow: INxRow;
    FSelCount: Integer;
    FSelected: array of Boolean;
    FShowingCount: Integer;
    FShowingStates: array of TNxShowingState;
    { Property Accessors }
    function GetAutoSort: Boolean;
    function GetCell(ACol, ARow: Integer): INxCell;
    function GetChildRow(const Index: Integer): INxRow;
    function GetChildRowCount: Integer;
    function GetControl: INxCellsControl;
    function GetFirstVisible: Integer;
    function GetLastVisible: Integer;
    function GetLocation: TPoint;
    function GetRow(const Index: Integer): INxRow;
    function GetRowCount: Integer;
    function GetSelected(const Index: Integer): Boolean;
    function GetSelectedCount: Integer;
    function GetShowing(const Index: Integer): Boolean;
    function GetShowingCount: Integer;
    function GetShowingState(const Index: Integer): TNxShowingState;
    function GetVisibleCount: Integer;
    procedure SetAutoSort(const Value: Boolean);
    procedure SetControl(const Value: INxCellsControl);
    procedure SetFirstVisible(const Value: Integer);
    procedure SetLocation(const Value: TPoint);
    procedure SetSelected(const Index: Integer; const Value: Boolean);
  protected
    procedure AddChild(AChildRow: INxRow);
    procedure CellChanged(Cell: INxCell);
    { Event Handlers }
    procedure DoClearRows; dynamic;
    procedure DoGetCell(ACol, ARow: Integer; Cell: INxCell); dynamic;
    procedure DoRowShowing(const Index: Integer; var Showing: Boolean); dynamic;
    function DoGetRowCount: Integer; dynamic;
    procedure DeleteChildRow(const Index: Integer);
    procedure IncBranchRowCount(Delta: Integer);
    procedure IncRowCount(const Delta: Integer);
    procedure RemoveRow(const Index: Integer);
    procedure UpdateShownCount(Row: INxRow; Delta: Integer);
    function IsDestroying: Boolean;
    function IsUpdating: Boolean;
    procedure FindFirstVisible;
    procedure FindLastVisible;
    { INxNotifyRowsChanged }
    procedure RowsChanged;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function AbsoluteIndexOf(Row: INxRow): Integer;
    { Methods }
    function AddChildRow(const Index, Count: Integer; IgnoreDefaults: Boolean): INxCellsRow; overload;
    function AddChildRow(ParentRow: INxRow; Count: Integer): INxCellsRow; overload;
    function AddRow(const Count: Integer; IgnoreDefaults: Boolean): INxCellsRow;
    function GetRowsBetween(FromPos, ToPos: Integer): Integer;
    function IndexOf(Row: INxRow): Integer;
    function InsertChildRow(ParentRow: INxRow; const Position, Count: Integer; IgnoreDefaults: Boolean): INxCellsRow;
    function InsertRow(const Index, Count: Integer; IgnoreDefaults: Boolean): INxCellsRow;
    procedure AddColumn;
    procedure Clear;
    procedure DeleteColumn(const Index: Integer);
    procedure DeleteRow(const Index: Integer);
    procedure FilterAll;
    function HasLocation: Boolean;
    procedure HideAll(Func: TNxHideAllFunc);
    procedure Move(CurIndex, NewIndex: Integer);
    procedure MoveRow(const FromPos, ToPos: Integer);
    procedure ProcessFilter(const Index: Integer);
    procedure Recalculate;
    procedure SelectNone;
    procedure Sort(const Index: Integer; SortKind: TNxSortKind; Resort: Boolean);
    procedure UnfilterAll;
    procedure UnhideAll;
    { Properties }
    property AutoSort: Boolean read GetAutoSort write SetAutoSort;
    property Cell[ACol, ARow: Integer]: INxCell read GetCell; default;
    property Control: INxCellsControl read GetControl write SetControl;
    property FirstVisible: Integer read GetFirstVisible;
    property LastVisible: Integer read GetLastVisible;
    property Location: TPoint read GetLocation write SetLocation;
    property Row[const Index: Integer]: INxRow read GetRow;
    property RowCount: Integer read GetRowCount;
    property Selected[const Index: Integer]: Boolean read GetSelected write SetSelected;
    property Showing[const Index: Integer]: Boolean read GetShowing;
    property ShowingState[const Index: Integer]: TNxShowingState read GetShowingState;
    property VisibleCount: Integer read GetVisibleCount;
  published
    { Events }
    property OnAddRow: TNxAddRowEvent read FOnAddRow write FOnAddRow;
    property OnClearRows: TNotifyEvent read FOnClearRows write FOnClearRows;
    property OnGetCell: TNxCellEvent read FOnGetCell write FOnGetCell;
    property OnGetRow: TNxRowEvent read FOnGetRow write FOnGetRow;
    property OnGetRowCount: TNxGetRowCountEvent read FOnGetRowCount write FOnGetRowCount;
    property OnGetRowShowing: TNxRowShowingEvent read FOnGetRowShowing write FOnGetRowShowing;
    property OnSetCell: TNxCellEvent read FOnGetCell write FOnGetCell;
  end;

  { TNxVirtualGrid6 }

  TNxVirtualGrid6 = class(TNxCustomGrid6)
  private
    { Property Accessors }
    function GetCellBy(ACol, ARow: Variant): INxCell;
    function GetSelectedCell: INxCell;
    procedure SetCellSource(const Value: INxCells);
  protected
    { INxRowIndices }
    function GetFirstVisibleRow: Integer; override;
    function GetLastVisibleRow: Integer; override;
    function GetShowingCount: Integer; override;
    procedure RowsChanged; override;
  protected
    FAutoCalculate: Boolean;
    FCellSource: INxCells;
    FLastAddedRow: Integer;
    { Virtual Methods }
    procedure AssignCells(Value: INxCells); virtual;
    function GetCell(ACol, ARow: Integer): INxCell; override;
    function GetCellSource: INxCells; override;
    function GetChildRow(const Index: Integer): INxRow; override;
    function GetChildRowCount: Integer; override;
    function GetRow(const Index: Integer): INxRow; override;
    function GetRowCount: Integer; override;
    function GetRowsBetween(FromPos, ToPos: Integer): Integer; override;
    function GetShowing(const Index: Integer): Boolean; override;
    procedure SetActiveCell(const Point: TPoint); override;
    procedure ScrollContentBy(DeltaX, DeltaY: Integer); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Methods }
    procedure AddCells(Values: array of WideString; Cols: array of Integer); virtual;
    function AddRow(const Count: Integer = 1; IgnoreDefaults: Boolean = True): INxCellsRow; virtual;
    procedure DeleteLastRow;
    procedure DeleteRow(const Index: Integer);
    function InsertChildRow(const Index, Position: Integer; const Count: Integer = 1;
      IgnoreDefaults: Boolean = True): INxCellsRow;
    function InsertRow(const Index: Integer; const Count: Integer = 1;
      IgnoreDefaults: Boolean = True): INxCellsRow; virtual;
    procedure SelectLastAddedRow;
    { Sorting Methods }
    procedure Sort(const Index: Integer; SortKind: TNxSortKind);
    { Properties }
    property CellBy[ACol, ARow: Variant]: INxCell read GetCellBy;
    property CellSource: INxCells read GetCellSource write SetCellSource;
    property LastAddedRow: Integer read FLastAddedRow;
    property SelectedCell: INxCell read GetSelectedCell;
  end;

  { TNextVirtualGrid6 }

  TNextVirtualGrid6 = class(TNxVirtualGrid6)
  private
    FSelStart: Integer;
    FEndStart: Integer;
    function GetFirstSelected: Integer;
    function GetLastSelected: Integer;
    function GetSelectable: INxSelectable;
  protected
    function GetSelected(Index: Integer): Boolean; override;
    function GetSelectedCount: Integer; override;
    procedure DeselectSelected; override;
    procedure SetSelected(Index: Integer; const Value: Boolean); override;
    { Properties }
    property Selectable: INxSelectable read GetSelectable;
  public
    constructor Create(AOwner: TComponent); override;
    { Methods }
    procedure DataChanged(Options: TNxUpdateOptions = [uoRecount, uoRepaint, uoScrollBar]); override;
    procedure RowsChanged; override;
    { Selection }
    procedure SelectNone(DelayedRefresh: Boolean = True); override;
    procedure SelectRange(ASelStart, ASelEnd: Integer; Value: Boolean); override;
    { Properties }
    property FirstSelected: Integer read GetFirstSelected;
    property LastSelected: Integer read GetLastSelected;
    property Selected[Index: Integer]: Boolean read GetSelected write SetSelected;
  end;

implementation

uses
  { Standard }
  Math, Dialogs, NxGridView6;

{ TNxVirtualCell }

function TNxVirtualCell.GetAsBoolean: Boolean;
begin
  Result := FBooleanValue;
end;

function TNxVirtualCell.GetAsDateTime: TDateTime;
begin
  Result := FDateTimeValue;
end;

function TNxVirtualCell.GetAsFloat: Double;
begin
  Result := FFloatValue;
end;

function TNxVirtualCell.GetAsInteger: Integer;
begin
  Result := FIntegerValue;
end;

function TNxVirtualCell.GetAsString: WideString;
begin
  Result := FStringValue;
end;

procedure TNxVirtualCell.SetAsBoolean(const Value: Boolean);
begin
  FBooleanValue := Value;
  FStringValue := BoolToStr(FBooleanValue, True);
  inherited;
end;

procedure TNxVirtualCell.SetAsDateTime(const Value: TDateTime);
begin
  FDateTimeValue := Value;
  FFloatValue := FDateTimeValue;
  FStringValue := DateTimeToStr(FDateTimeValue);
  inherited;
end;

procedure TNxVirtualCell.SetAsFloat(const Value: Double);
begin
  FFloatValue := Value;
  FStringValue := FloatToStr(FFloatValue);
  inherited;
end;

procedure TNxVirtualCell.SetAsInteger(const Value: Integer);
begin
  FIntegerValue := Value;
  FFloatValue := Value;
  FStringValue := FloatToStr(FFloatValue);
  inherited;
end;

procedure TNxVirtualCell.SetAsString(const Value: WideString);
begin
  FStringValue := Value;
  inherited;
end;

procedure TNxVirtualCell.SetDummyValue;
begin

end;

{ TNxVirtualCellSource6 }

function TNxVirtualCellSource6.AddChildRow(const Index, Count: Integer;
  IgnoreDefaults: Boolean): INxCellsRow;
begin

end;

procedure TNxVirtualCellSource6.AddChild(AChildRow: INxRow);
begin

end;

function TNxVirtualCellSource6.AddChildRow(ParentRow: INxRow;
  Count: Integer): INxCellsRow;
begin

end;

procedure TNxVirtualCellSource6.AddColumn;
begin

end;

function TNxVirtualCellSource6.AddRow(const Count: Integer;
  IgnoreDefaults: Boolean): INxCellsRow;
begin
  if Assigned(FOnAddRow) then FOnAddRow(Self, Count);
end;

procedure TNxVirtualCellSource6.CellChanged(Cell: INxCell);
begin

end;

procedure TNxVirtualCellSource6.Clear;
begin

end;

constructor TNxVirtualCellSource6.Create(AOwner: TComponent);
begin
  inherited;
  FCell := TNxVirtualCell.Create;
  FRow := TNxCellsRow6.Create(Self as INxCells, 16);
  FSelCount := 0;
  FSelected := nil;
  FShowingCount := 0;
  FShowingStates := nil;
end;

procedure TNxVirtualCellSource6.DeleteChildRow(const Index: Integer);
begin

end;

procedure TNxVirtualCellSource6.DeleteColumn(const Index: Integer);
begin

end;

procedure TNxVirtualCellSource6.DeleteRow(const Index: Integer);
begin

end;

destructor TNxVirtualCellSource6.Destroy;
var
  Refr: INxCellsRow;
begin
  FCell := nil;

  { Note: Cells are handing reference
          back to row }
  if Supports(FRow, INxCellsRow, Refr) then
  begin
    Refr.DestroyCells;
  end;

  { RefCount should be 0 }
  FRow := nil;

  FShowingStates := nil; // Added: Eduard

  inherited;
end;

procedure TNxVirtualCellSource6.DoClearRows;
begin
  if Assigned(FOnClearRows) then FOnClearRows(Self);
end;

procedure TNxVirtualCellSource6.DoGetCell(ACol, ARow: Integer; Cell: INxCell);
begin
  if Assigned(FOnGetCell) then FOnGetCell(Self, ACol, ARow, Cell);
end;

function TNxVirtualCellSource6.DoGetRowCount: Integer;
begin
  Result := 0;
  if Assigned(FOnGetRowCount) then Result := FOnGetRowCount(Self);
end;

procedure TNxVirtualCellSource6.DoRowShowing(const Index: Integer; var Showing: Boolean);
begin
  if Assigned(FOnGetRowShowing) then FOnGetRowShowing(Self, Index, Showing);
end;

procedure TNxVirtualCellSource6.FilterAll;
begin

end;

procedure TNxVirtualCellSource6.FindFirstVisible;
begin

end;

procedure TNxVirtualCellSource6.FindLastVisible;
begin

end;

function TNxVirtualCellSource6.AbsoluteIndexOf(Row: INxRow): Integer;
begin
  Result := 0;
end;

function TNxVirtualCellSource6.GetAutoSort: Boolean;
begin
  Result := False;
end;

function TNxVirtualCellSource6.GetChildRow(const Index: Integer): INxRow;
begin
  Result := nil;
end;

function TNxVirtualCellSource6.GetChildRowCount: Integer;
begin
  Result := 0;
end;

function TNxVirtualCellSource6.GetControl: INxCellsControl;
begin
  Result := nil;
end;

///<author>Eduard</author>
function TNxVirtualCellSource6.GetFirstVisible: Integer;
begin
  Result := FFirstVisibleRow;
end;

function TNxVirtualCellSource6.GetCell(ACol, ARow: Integer): INxCell;
begin
  Result := FCell;

  { Call event }
  DoGetCell(ACol, ARow, Result);
end;

///<see>GetFirstVisible</see>
///<returns>Index of last visible row</returns>
///<author>Eduard</author>
function TNxVirtualCellSource6.GetLastVisible: Integer;
begin
  Result := FLastVisibleRow;
end;

function TNxVirtualCellSource6.GetLocation: TPoint;
begin

end;

function TNxVirtualCellSource6.GetRow(const Index: Integer): INxRow;
begin
  Result := FRow;
  Result.Height := 16;
  Result.Visible := True;
  if Assigned(FOnGetRow) then FOnGetRow(Self, Index, FRow);
end;

{ Changed: Eduard }
function TNxVirtualCellSource6.GetRowCount: Integer;
var
  CalcRowCount: Integer;
begin
  Result := DoGetRowCount;

  { Faster when Filtering }
  if (GetShowingCount > 0) then
  begin
    CalcRowCount := Succ(GetFirstVisible + GetLastVisible);
    if (CalcRowCount < Result) then Result := CalcRowCount;
  end;
end;

function TNxVirtualCellSource6.GetRowsBetween(FromPos, ToPos: Integer): Integer;
var
  i: Integer;
begin
  { Default }
  Result := 0;

  { Exchange, if needed }
  Arrange(FromPos, ToPos);

  i := FromPos;
  while i < ToPos do
  begin
    if Showing[i] then Inc(Result);
    Inc(i);
  end;
end;

{ Changed: Eduard }
function TNxVirtualCellSource6.GetSelected(const Index: Integer): Boolean;
begin
  if Assigned(FSelected) then Result := FSelected[Index] else Result := False;
end;

function TNxVirtualCellSource6.GetSelectedCount: Integer;
begin
  Result := FSelCount;
end;

function TNxVirtualCellSource6.GetShowing(const Index: Integer): Boolean;
begin
  { Default }
  Result := True;

  if (FShowingStates <> nil) then
  begin
    if (FShowingStates[Index] = ssUnknown) then
    begin

      { Trigger Event }
      DoRowShowing(Index, Result);

    end else
    begin
      Result := (FShowingStates[Index] = ssVisible);

      { Trigger Event }
      if Result then
      begin
        DoRowShowing(Index, Result);
      end;

    end;

  end else
    { Trigger Event }
    DoRowShowing(Index, Result);
end;

function TNxVirtualCellSource6.GetShowingCount: Integer;
begin
  Result := FShowingCount;
end;

function TNxVirtualCellSource6.GetShowingState(const Index: Integer): TNxShowingState;
begin
  Result := ssUnknown;
  if (FShowingStates <> nil) then
    Result := FShowingStates[Index];
end;

function TNxVirtualCellSource6.GetVisibleCount: Integer;
begin
  Result := RowCount;
end;

function TNxVirtualCellSource6.HasLocation: Boolean;
begin
  Result := True;
end;

procedure TNxVirtualCellSource6.HideAll(Func: TNxHideAllFunc);
begin

end;

procedure TNxVirtualCellSource6.IncBranchRowCount(Delta: Integer);
begin

end;

procedure TNxVirtualCellSource6.IncRowCount(const Delta: Integer);
begin

end;

function TNxVirtualCellSource6.IndexOf(Row: INxRow): Integer;
begin
  Result := -1;
end;

function TNxVirtualCellSource6.InsertChildRow(ParentRow: INxRow; const Position,
  Count: Integer; IgnoreDefaults: Boolean): INxCellsRow;
begin
  Result := nil;
end;

function TNxVirtualCellSource6.InsertRow(const Index, Count: Integer;
  IgnoreDefaults: Boolean): INxCellsRow;
begin
  Result := nil;
end;

function TNxVirtualCellSource6.IsDestroying: Boolean;
begin
  Result := False;
end;

function TNxVirtualCellSource6.IsUpdating: Boolean;
begin
  Result := True;
end;

procedure TNxVirtualCellSource6.Move(CurIndex, NewIndex: Integer);
begin

end;

procedure TNxVirtualCellSource6.MoveRow(const FromPos, ToPos: Integer);
begin

end;

procedure TNxVirtualCellSource6.ProcessFilter(const Index: Integer);
begin

end;

procedure TNxVirtualCellSource6.Recalculate;
begin

end;

procedure TNxVirtualCellSource6.SelectNone;
var
  i: Integer;
begin
  if Assigned(FSelected) then
  begin
    for i := Low(FSelected) to High(FSelected) do
      FSelected[i] := False;
  end;
  FSelCount := 0;
end;

procedure TNxVirtualCellSource6.RemoveRow(const Index: Integer);
begin

end;

procedure TNxVirtualCellSource6.RowsChanged;

  ///<author>Eduard</author>
  procedure InitShowingArray;
  var
    i: Integer;
  begin
    FShowingStates := nil;
    SetLength(FShowingStates, DoGetRowCount);
    for i := Low(FShowingStates) to High(FShowingStates) do
      FShowingStates[i] := ssUnknown;

    { MultiSelect }
    FSelected := nil;
    SetLength(FSelected, DoGetRowCount);
    for i := Low(FSelected) to High(FSelected) do FSelected[i] := False;
  end;

  ///<returns>True if row is Showing</returns>
  ///<remarks>Default <c>True</c></remarks>
  ///<author>Eduard</author>
  function LocalRowShowing(const Index: Integer): Boolean;
  begin
    Result := True;
    DoRowShowing(Index, Result);
  end;

  ///<author>Eduard</author>
  function RecountShowingCount: Integer;
  var
    i: Integer;
  begin
    Result := 0;
    for i := 0 to Pred(DoGetRowCount) do
    begin
      if LocalRowShowing(i) then
      begin
        FShowingStates[i] := ssVisible;
        Inc(Result);
      end
      else FShowingStates[i] := ssHidden;
    end;
  end;

  ///<author>Eduard</author>
  function RecountLastVisibleRow: Integer;
  begin
    Result := DoGetRowCount;
    while Result > 0 do
    begin
      Dec(Result);
      if LocalRowShowing(Result)
        then Break;
    end;
  end;

  ///<author>Eduard</author>
  function RecountFirstVisibleRow: Integer;
  begin
    Result := 0;
    while Result < DoGetRowCount do
    begin
      if LocalRowShowing(Result)
        then Break;
      Inc(Result);
    end;
  end;

begin
  InitShowingArray;
  FFirstVisibleRow := RecountFirstVisibleRow;
  FLastVisibleRow := RecountLastVisibleRow;
  FShowingCount := RecountShowingCount;
end;

procedure TNxVirtualCellSource6.SetAutoSort(const Value: Boolean);
begin

end;

procedure TNxVirtualCellSource6.SetControl(const Value: INxCellsControl);
begin

end;

procedure TNxVirtualCellSource6.SetFirstVisible(const Value: Integer);
begin

end;

procedure TNxVirtualCellSource6.SetLocation(const Value: TPoint);
begin

end;

procedure TNxVirtualCellSource6.SetSelected(const Index: Integer; const Value: Boolean);
var
  AValue: Boolean;
begin
  { Added by Eduard }
  if not Assigned(FSelected) then
    Exit;

  if Index > -1 then
  begin
    AValue := FSelected[Index];
    if AValue <> Value then
    begin
      FSelected[Index] := Value;
      if Value then Inc(FSelCount) else Dec(FSelCount);
      if FSelCount < 0 then FSelCount := 0;
    end;
  end;
end;

procedure TNxVirtualCellSource6.Sort(const Index: Integer;
  SortKind: TNxSortKind; Resort: Boolean);
begin

end;

procedure TNxVirtualCellSource6.UnfilterAll;
begin

end;

procedure TNxVirtualCellSource6.UnhideAll;
begin

end;

procedure TNxVirtualCellSource6.UpdateShownCount(Row: INxRow; Delta: Integer);
begin

end;

{ TNextVirtualGrid6 }

procedure TNxVirtualGrid6.AddCells(Values: array of WideString;
  Cols: array of Integer);
var
  i, j: Integer;
begin
  if Columns.HasItems then
  begin
    i := 0;

    { Add first }
    AddRow;

    while i < Length(Values) do
    begin
      for j := Low(Cols) to High(Cols) do
      begin
        Cells[Cols[j], LastAddedRow] := Values[i];

        Inc(i);

        { Done adding all }
        if i >= Length(Values)
          then Exit;
      end;

      { Add another }
      if i < Length(Values)
        then AddRow;
    end;
  end;
end;

function TNxVirtualGrid6.AddRow(const Count: Integer;
  IgnoreDefaults: Boolean): INxCellsRow;
var
  PrevCount, VisibleCount: Integer;
  OldUpdate: TNxUpdateOptions;
begin
  { Default }
  Result := nil;

  if Count > 0 then
  begin
    PrevCount := RowCount;
    VisibleCount := ShowingCount;

    { Create, Add Rows & Cells }
    OldUpdate := FUpdateOptions;
    BeginUpdate;

    Result := CellSource.AddRow(Count, IgnoreDefaults);

    EndUpdate;
    FUpdateOptions := OldUpdate;

    { LastAddedRow Prop. }
    FLastAddedRow := Pred(RowCount);

    if Assigned(ActiveView) then
    begin
      { Invalidate }
      if CanUpdate([uoRepaint]) then
        ActiveView.RefreshRow(PrevCount, Pred(RowCount));

      { ScrollBar }
      if CanUpdate([uoScrollBar])
        then UpdateScrollBars;

      { ToDo: Set FirstIndex visible }
      if VisibleCount = 0 then
        ActiveView.DoAddRow(Count);
    end;

    if CanUpdate([uoRecalculate]) and FAutoCalculate
      then Recalculate;

    { Select last added }
    if AutoSelect then SelectedRow := FLastAddedRow;
  end;
end;

procedure TNxVirtualGrid6.AssignCells(Value: INxCells);
begin
  // do nothing
end;

constructor TNxVirtualGrid6.Create(AOwner: TComponent);
begin
  inherited;
  FAutoCalculate := False;
  FCellSource := nil;
  FLastAddedRow := -1;
end;

procedure TNxVirtualGrid6.DeleteLastRow;
begin
  DeleteRow(Pred(RowCount));
end;

procedure TNxVirtualGrid6.DeleteRow(const Index: Integer);
begin
  Row[Index].Delete;
end;

destructor TNxVirtualGrid6.Destroy;
begin
  if Assigned(FCellSource) then
  begin
    FCellSource.Clear;
    FCellSource := nil;
  end;

  inherited Destroy;
end;

function TNxVirtualGrid6.GetCell(ACol, ARow: Integer): INxCell;
begin
  { Default Value in case of AV }
  Result := nil;

  { Check if Cell exists, or raise Exception }
  if CheckCell(ACol, ARow) then
  begin
    Result := CellSource[ACol, ARow];
  end;
end;

function TNxVirtualGrid6.GetCellBy(ACol, ARow: Variant): INxCell;
begin
  with GetCellLocation(ACol, ARow) do Result := Cell[X, Y];
end;

function TNxVirtualGrid6.GetCellSource: INxCells;
begin
  Result := FCellSource;
end;

function TNxVirtualGrid6.GetChildRow(const Index: Integer): INxRow;
begin
  Result := CellSource.ChildRow[Index];
end;

function TNxVirtualGrid6.GetChildRowCount: Integer;
begin
  if CellSource <> nil then Result := CellSource.ChildRowCount
    else Result := 0;
end;

function TNxVirtualGrid6.GetFirstVisibleRow: Integer;
begin
  Result := CellSource.FirstVisible;
end;

function TNxVirtualGrid6.GetLastVisibleRow: Integer;
begin
  Result := CellSource.LastVisible;
end;

function TNxVirtualGrid6.GetRow(const Index: Integer): INxRow;
begin
  Result := CellSource.Row[Index];
end;

function TNxVirtualGrid6.GetRowCount: Integer;
begin
  if Assigned(CellSource) then Result := CellSource.RowCount else Result := 0;
end;

function TNxVirtualGrid6.GetRowsBetween(FromPos, ToPos: Integer): Integer;
begin
  Result := CellSource.GetRowsBetween(FromPos, ToPos);
end;

function TNxVirtualGrid6.GetSelectedCell: INxCell;
begin
  Result := Cell[SelectedCol, SelectedRow];
end;

function TNxVirtualGrid6.GetShowing(const Index: Integer): Boolean;
begin
  Result := CellSource.Showing[Index];
end;

function TNxVirtualGrid6.GetShowingCount: Integer;
begin
  if Assigned(CellSource) then Result := CellSource.ShowingCount else Result := 0;
end;

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

function TNxVirtualGrid6.InsertRow(const Index, Count: Integer;
  IgnoreDefaults: Boolean): INxCellsRow;
begin
  Result := nil;

  { Create, add Rows & Cells. Re-paint & re-calc.
    will be triggered inside. }

  if Count > 0 then
  begin
    Result := (CellSource as INxRow).InsertChildRow(Index, Count, IgnoreDefaults);
  end;
end;

procedure TNxVirtualGrid6.RowsChanged;
begin

end;

procedure TNxVirtualGrid6.ScrollContentBy(DeltaX, DeltaY: Integer);
begin
  inherited;
  if Assigned(ActiveView) then
  begin
    ActiveView.ScrollContentBy(DeltaX, DeltaY);
  end;
end;

procedure TNxVirtualGrid6.SelectLastAddedRow;
begin
  SelectedRow := FLastAddedRow;
end;

procedure TNxVirtualGrid6.SetActiveCell(const Point: TPoint);
begin
  inherited;
  CellSource.Location := Point;
end;

procedure TNxVirtualGrid6.SetCellSource(const Value: INxCells);
begin
  { Free first }
  FCellSource := nil;

  { Set another }
  FCellSource := Value;

  if Assigned(Value) then
  begin
    { Set additional properties }
    AssignCells(FCellSource);
  end;

  Invalidate;
end;

procedure TNxVirtualGrid6.Sort(const Index: Integer; SortKind: TNxSortKind);
var
  Column: TNxColumn6;
begin
  Column := Columns[Index];
  Column.SetSort(True, Column.SortType, SortKind);
end;

{ TNextVirtualGrid6 }

procedure TNextVirtualGrid6.RowsChanged;
var
  Intf: INxNotifyRowsChanged;
begin
  if Supports(CellSource, INxNotifyRowsChanged, Intf) then
  begin
    Intf.RowsChanged;
  end;
end;

constructor TNextVirtualGrid6.Create(AOwner: TComponent);
begin
  inherited;
  FSelStart := 0;
  FEndStart := -1;
end;

{ Added: Eduard (Need to be Public) }
function TNextVirtualGrid6.GetFirstSelected: Integer;
var
  i: Integer;
begin
  i := 0;
  Result := -1;
  while i < LastVisibleRow do
  begin
    if Selectable.Selected[i] then
    begin
      Result := i;
      Break;
    end else Inc(i);
  end;
end;

{ Added: Eduard (Need to be Public) }
function TNextVirtualGrid6.GetLastSelected: Integer;
var
  i: Integer;
begin
  i := LastVisibleRow;
  Result := -1;
  while i > 0 do
  begin
    Dec(i);
    if Selectable.Selected[i] then
    begin
      Result := i;
      Break;
    end;
  end;
end;

function TNextVirtualGrid6.GetSelectable: INxSelectable;
begin
  Supports(CellSource, INxSelectable, Result);
  Assert(Assigned(Result), 'CellSource does not support INxSelectable');
end;

function TNextVirtualGrid6.GetSelected(Index: Integer): Boolean;
begin
  Result := Selectable.Selected[Index];
end;

function TNextVirtualGrid6.GetSelectedCount: Integer;
begin
  Result := Selectable.SelectedCount;
end;

procedure TNextVirtualGrid6.SelectNone(DelayedRefresh: Boolean);
begin
  Selectable.SelectNone;
  if Assigned(ActiveView) then
  begin
    ActiveView.Invalidate;
  end;
end;

procedure TNextVirtualGrid6.SelectRange(ASelStart, ASelEnd: Integer; Value: Boolean);
var
  i: Integer;
begin
  if ASelEnd = -1 then Exit;
  i := ASelStart;

  while i <= ASelEnd do
  begin
    if Selectable.Selected[i] <> Value then
    begin
      if CellSource.Showing[i] then Selectable.Selected[i] := Value;
    end;
    Inc(i);
  end;
  if Assigned(ActiveView) then ActiveView.RefreshRow(ASelStart, ASelEnd);
end;

procedure TNextVirtualGrid6.SetSelected(Index: Integer; const Value: Boolean);
begin
  if Selectable.Selected[Index] <> Value then
  begin
    Selectable.Selected[Index] := Value;
    RefreshRow(Index);
  end;
end;

procedure TNextVirtualGrid6.DataChanged(Options: TNxUpdateOptions);
begin
  inherited DataChanged(Options);
  FSelStart := SelectedRow;
  FEndStart := -1;
end;

procedure TNextVirtualGrid6.DeselectSelected;
begin
  if gtMultiSelected in FState then
  begin
    SelectNone;
    Exclude(FState, gtMultiSelected);
  end;
end;

end.
