{ ------------------------------------------------------------- }
{ Purpose : NextGrid 6                                          }
{ By      : Bojan Nikolic                                       }
{ For     : BergSoft                                            }
{ ------------------------------------------------------------- }
{ Copyright (C) BergSoft.net 2018. All Rights Reserved.         }
{ ------------------------------------------------------------- }
{$I '..\NxSuite6.inc'}
unit NxCells6;

interface

uses
  { Standard }
  Classes, Types, Graphics, {$IFDEF UI_TYPES}UITypes, {$ELSE}ImgList,{$ENDIF}
  { Next Standard 6 }
  NxIntf6, NxClasses6, NxTypes6, NxConsts6, NxColumns6;

const
  szDefaultRowHeight = 16;

type
  TNxHideAllFunc = function(Index: Integer): Boolean of object;

  TNxCellStyle = set of (ctDisabled, ctEmpty, ctNull, ctLocked, ctParentColor, ctHidden);
  TNxCellUpdateSpread = (cuCell, cuRow, cuGrid);

  INxCell = interface;
  INxCells = interface;
  INxCellsRow = interface;
  INxRow = interface;

  TNxCells6 = class;
  TNxCellsList = class;

  { INxRowsIndices }

  INxRowsControl = interface
    ['{9296ADB2-7412-4F86-AFBB-9048CE4E3646}']
    function GetRow(const Index: Integer): INxRow;
    function GetRowCount: Integer;
    function GetShowing(const Indext: Integer): Boolean;
    function GetShowingCount: Integer;
    procedure SetActiveRow(const Index: Integer);
    property Row[const Index: Integer]: INxRow read GetRow;
    property RowCount: Integer read GetRowCount;
    property Showing[const Index: Integer]: Boolean read GetShowing;
    property ShowingCount: Integer read GetShowingCount;
  end;

  INxNotifyRowsChanged = interface
    ['{B044493C-E7AB-40F0-ABD1-F04AA05D833F}']
    procedure RowsChanged;
  end;

  INxCellsControl = interface
    ['{B0A5B213-132E-4588-8475-14A193336A33}']
    function GetColumns: TNxColumns6;
    function GetRowHeight: Integer;
    function CanUpdate(Options: TNxUpdateOptions): Boolean;
    { Event Handlers }
    procedure DoCellCompare(Cell1, Cell2: INxCell; var Compare: Integer);
    { Methods }
    procedure CellChanged(Cell: INxCell; UpdateSpread: TNxCellUpdateSpread);
    procedure ChildDeleted(ARow: INxRow; Hide: Boolean);
    procedure ChildsCleared(ARow: INxRow);
    procedure RowMoved(const CurIndex, NewIndex: Integer);
//    procedure DeleteRow(const Index: Integer);
    procedure ExpandedChanged(Row: INxRow; Expanded: Boolean);
    function GetDisplayText(Cell: INxCell): WideString;
    procedure HeightChanged(Index: Integer; Delta: Integer);
    procedure RefreshRow(const Index: Integer);
    procedure ShowChildRows(ARow: INxRow; Count: Integer; Hide: Boolean);
    procedure VisibilityChanged(Index: Integer; Show: Boolean);

    procedure SetLastAddedRow(const Value: Integer);
    { Properties }
    property Columns: TNxColumns6 read GetColumns;
    property RowHeight: Integer read GetRowHeight;
  end;

  { TNxCell }

  INxCell = interface(INxBaseCell)
    ['{5EF81C3C-7BDE-4A04-A1C8-6DB27506C2DE}']
    { Property Accessors }
    function GetColIndex: Integer;
    function GetComputedLocation: TPoint;
    function GetData: Pointer;
    function GetEnabled: Boolean;
    function GetFont: TFont;
    function GetHint: WideString;
    function GetList: TNxCellsList;
    function GetLocation: TPoint;
    function GetRowIndex: Integer;
    function GetState: TNxCellStyle;
    function GetVisible: Boolean;
    procedure SetData(const Value: Pointer);
    procedure SetEnabled(const Value: Boolean);
    procedure SetFont(const Value: TFont);
    procedure SetHint(const Value: WideString);
    procedure SetList(const Value: TNxCellsList);
    procedure SetState(const Value: TNxCellStyle);
    procedure SetVisible(const Value: Boolean);
    { Methods }
    procedure Assign(Source: TObject);
    function HasLocation: Boolean;
    procedure Hide;
    procedure InvertValue;
    function IsEqual(Value: INxCell): Boolean;
    function IsFontSet: Boolean;
    procedure RevertStyle;
    procedure SetDummyValue;
    { Properties }
    property ColIndex: Integer read GetColIndex;
    property ComputedLocation: TPoint read GetComputedLocation;
    property Data: Pointer read GetData write SetData;
    property Enabled: Boolean read GetEnabled write SetEnabled;
    property Font: TFont read GetFont write SetFont;
    property Hint: WideString read GetHint write SetHint;
    property List: TNxCellsList read GetList write SetList;
    property Location: TPoint read GetLocation;
    property RowIndex: Integer read GetRowIndex;
    property State: TNxCellStyle read GetState write SetState;
    property Visible: Boolean read GetVisible write SetVisible;
  end;

  {$M+}
  TNxCell6 = class(TInterfacedObject, INxCell)
  private
    FColor: TColor;
    FData: Pointer;
    FFont: TFont;
    FHint: WideString;
    FList: TNxCellsList;
    FState: TNxCellStyle;
    function GetColIndex: Integer;
    function GetColor: TColor;
    function GetComputedLocation: TPoint;
    function GetData: Pointer;
    function GetEmpty: Boolean;
    function GetEnabled: Boolean;
    function GetFont: TFont;
    function GetHint: WideString;
    function GetIsNull: Boolean;
    function GetList: TNxCellsList;
    function GetLocation: TPoint;
    function GetLocked: Boolean;
    function GetParentColor: Boolean;
    function GetRowIndex: Integer;
    function GetState: TNxCellStyle;
    function GetVisible: Boolean;
    procedure SetColor(const Value: TColor);
    procedure SetData(const Value: Pointer);
    procedure SetEmpty(const Value: Boolean);
    procedure SetEnabled(const Value: Boolean);
    procedure SetFont(const Value: TFont);
    procedure SetHint(const Value: WideString);
    procedure SetIsNull(const Value: Boolean);
    procedure SetList(const Value: TNxCellsList);
    procedure SetLocked(const Value: Boolean);
    procedure SetParentColor(const Value: Boolean);
    procedure SetState(const Value: TNxCellStyle);
    procedure SetVisible(const Value: Boolean);
  protected
    { INxObject }
    function ToObject: TObject;
    function GetOwner: INxCells; virtual;
    { Property Getters }
    function GetAsBoolean: Boolean; virtual;
    function GetAsDateTime: TDateTime; virtual;
    function GetAsFloat: Double; virtual;
    function GetAsInteger: Integer; virtual;
    function GetAsString: WideString; virtual;
    function GetAsUTF8String: UTF8String;
    function GetDisplayText: WideString; virtual;
    { Property Accessors }
    procedure SetAsBoolean(const Value: Boolean); virtual;
    procedure SetAsDateTime(const Value: TDateTime); virtual;
    procedure SetAsFloat(const Value: Double); virtual;
    procedure SetAsInteger(const Value: Integer); virtual;
    procedure SetAsString(const Value: WideString); virtual;
    procedure SetAsUTF8String(const Value: UTF8String);
  protected
    procedure DoFontChange(Sender: TObject);
    { Virtual Methods }
    procedure Changed(UpdateSpread: TNxCellUpdateSpread = cuCell); virtual;
    procedure ValueChanged; virtual;
    { Properties }
    property IsNull: Boolean read GetIsNull write SetIsNull;
    property Owner: INxCells read GetOwner;
  public
    constructor Create; virtual;
    destructor Destroy; override;
    { INxBase Methods }
    procedure AssignValue(Source: INxBase); virtual;
    { INxCell Methods }
    procedure Assign(Source: TObject); virtual;
    function HasLocation: Boolean;
    function HasOwner: Boolean; virtual;
    procedure Hide;
    procedure InvertValue; virtual;
    function IsEqual(Value: INxCell): Boolean; virtual;
    function IsFontSet: Boolean;
    function IsSet: Boolean; virtual;
    procedure Revert; virtual;
    procedure RevertStyle; virtual;
    procedure SetDummyValue; virtual; abstract;
    { INxBase Properties }
    property AsBoolean: Boolean read GetAsBoolean write SetAsBoolean;
    property AsDateTime: TDateTime read GetAsDateTime write SetAsDateTime;
    property AsFloat: Double read GetAsFloat write SetAsFloat;
    property AsInteger: Integer read GetAsInteger write SetAsInteger;
    property AsString: WideString read GetAsString write SetAsString;
    property AsUTF8String: UTF8String read GetAsUTF8String write SetAsUTF8String;
    { INxCell Properties }
    property ColIndex: Integer read GetColIndex;
    property ComputedLocation: TPoint read GetComputedLocation;
    property Data: Pointer read GetData write SetData;
    property DisplayText: WideString read GetDisplayText;
    property Font: TFont read GetFont write SetFont;
    property List: TNxCellsList read GetList write SetList;
    property Location: TPoint read GetLocation;
    property ParentColor: Boolean read GetParentColor write SetParentColor;
    property RowIndex: Integer read GetRowIndex;
    property State: TNxCellStyle read GetState write SetState;
    property Visible: Boolean read GetVisible write SetVisible;
  published
    { Serialized Properties }
    property Color: TColor read GetColor write SetColor;
    property Empty: Boolean read GetEmpty write SetEmpty;
    property Enabled: Boolean read GetEnabled write SetEnabled;
    property Hint: WideString read GetHint write SetHint;
    property Locked: Boolean read GetLocked write SetLocked;
    property Text: WideString read GetAsString write SetAsString;
  end;
  {$M-}

  { TNxBooleanCell }

  TNxBooleanCell = class(TNxCell6)
  private
    { Fields }
    FValue: Boolean;
  protected
    function GetAsBoolean: Boolean; override;
    function GetAsFloat: Double; override;
    function GetAsString: WideString; override;
    procedure SetAsBoolean(const Value: Boolean); override;
    procedure SetAsInteger(const Value: Integer); override;
    procedure SetAsString(const Value: WideString); override;
  public
    procedure AssignValue(Source: INxBase); override;
    procedure InvertValue; override;
    function IsEqual(Value: INxCell): Boolean; override;
    function IsSet: Boolean; override;
    procedure Revert; override;
    procedure SetDummyValue; override;
  end;

  { TNxBooleanCell }

  TNxDateTimeCell = class(TNxCell6)
  private
    { Fields }
    FValue: TDateTime;
  protected
    function GetAsBoolean: Boolean; override;
    function GetAsDateTime: TDateTime; override;
    function GetAsFloat: Double; override;
    function GetAsString: WideString; override;
    procedure SetAsBoolean(const Value: Boolean); override;
    procedure SetAsDateTime(const Value: TDateTime); override;
    procedure SetAsInteger(const Value: Integer); override;
    procedure SetAsString(const Value: WideString); override;
  public
    procedure AssignValue(Source: INxBase); override;
    constructor Create; override;
    function IsEqual(Value: INxCell): Boolean; override;
    function IsSet: Boolean; override;
    procedure Revert; override;
    procedure SetDummyValue; override;
  end;

  { TNxFloatCell }

  TNxFloatCell = class(TNxCell6)
  private
    { Fields }
    FValue: Double;
  protected
    function GetAsFloat: Double; override;
    function GetAsInteger: Integer; override;
    function GetAsString: WideString; override;
    procedure SetAsBoolean(const Value: Boolean); override;
    procedure SetAsFloat(const Value: Double); override;
    procedure SetAsInteger(const Value: Integer); override;
    procedure SetAsString(const Value: WideString); override;
  public
    procedure AssignValue(Source: INxBase); override;
    procedure InvertValue; override;
    function IsEqual(Value: INxCell): Boolean; override;
    function IsSet: Boolean; override;
    procedure Revert; override;
    procedure SetDummyValue; override;
  end;

  TNxIntegerCell = class(TNxCell6)
  private
    { Fields }
    FValue: Integer;
  protected
    function GetAsBoolean: Boolean; override;
    function GetAsFloat: Double; override;
    function GetAsInteger: Integer; override;
    function GetAsString: WideString; override;
    procedure SetAsBoolean(const Value: Boolean); override;
    procedure SetAsInteger(const Value: Integer); override;
    procedure SetAsString(const Value: WideString); override;
  public
    procedure AssignValue(Source: INxBase); override;
    procedure InvertValue; override;
    function IsEqual(Value: INxCell): Boolean; override;
    function IsSet: Boolean; override;
    procedure Revert; override;
    procedure SetDummyValue; override;
  end;

  TNxStringCell = class(TNxCell6)
  private
    { Fields }
    FValue: WideString;
  protected
    function GetAsBoolean: Boolean; override;
    function GetAsFloat: Double; override;
    function GetAsInteger: Integer; override;
    function GetAsString: WideString; override;
    procedure SetAsBoolean(const Value: Boolean); override;
    procedure SetAsInteger(const Value: Integer); override;
    procedure SetAsString(const Value: WideString); override;
  public
    procedure AssignValue(Source: INxBase); override;
    function IsEqual(Value: INxCell): Boolean; override;
    function IsSet: Boolean; override;
    procedure Revert; override;
    procedure SetDummyValue; override;
  end;

  TNxIncrementCell = class(TNxCell6)
  private
    { Fields }
    FValue: WideString;
  protected
    function GetAsInteger: Integer; override;
    function GetAsString: WideString; override;
  public
    function IsEqual(Value: INxCell): Boolean; override;
    procedure SetDummyValue; override;
  end;

  { TNxCellsList }

  TNxCellsList = class(TInterfaceList)
  private
    FRow: Pointer;
    function GetItem(Index: Integer): INxCell;
    function GetRow: INxCellsRow;
    procedure SetItem(Index: Integer; const Value: INxCell);
  public
    constructor Create(AOwner: INxCellsRow);
    property Item[Index: Integer]: INxCell read GetItem write SetItem; default;
    property Row: INxCellsRow read GetRow;
  end;

  { TNxRow6 }

  TNxRowState = set of (rsFiltered, rsVisible, rsDeleting, rsSelected);

  INxParentRow = interface
    ['{57FD87EB-8BF9-4FC2-B59A-A8E7F21AFF68}']
    { Property Accessors }
    function GetChildRow(const Index: Integer): INxRow;
    function GetChildRowCount: Integer;
    function GetChildShowingCount: Integer;
    function GetExpanded: Boolean;
    function GetFirstChild: INxRow;
    function GetLastChild: INxRow;
    function GetLastDescendant: INxRow;
    procedure SetExpanded(const Value: Boolean);
    { Methods }
    function ChildsShown: Boolean;
    function HasChildren: Boolean;
    function HasParent: Boolean;
    function IndexOf(AChild: INxRow): Integer;
    procedure ClearChildren;
    procedure Collapse(Recurse: Boolean);
    procedure Expand(Recurse: Boolean);
    { Properties }
    property ChildRow[const Index: Integer]: INxRow read GetChildRow; default;
    property ChildRowCount: Integer read GetChildRowCount;
    property ChildShowingCount: Integer read GetChildShowingCount;
    property Expanded: Boolean read GetExpanded write SetExpanded;
    property FirstChild: INxRow read GetFirstChild;
    property LastChild: INxRow read GetLastChild;
    property LastDescendant: INxRow read GetLastDescendant;
  end;

  INxRows = interface(INxParentRow)
    ['{822E3565-686A-4453-BD9B-781EF65CDD07}']
    function GetBranches: TInterfaceList;
    { Methods }
    procedure AddChild(AChild: INxRow);
    procedure DeleteChild(AChild: INxRow);
    procedure InsertChild(AChild: INxRow; Index: Integer);
    procedure UpdateChildShowingCount(AParent: INxRow; Delta: Integer);
    procedure UpdateShownCount(ARow: INxRow; Delta: Integer);
    function IndexOf(ARow: INxRow): Integer;
    procedure Move(CurIndex, NewIndex: Integer);
    { Properties }
    property Branches: TInterfaceList read GetBranches;
  end;

  INxBranches = interface
    ['{DEB8DB76-3FFB-460C-A2B0-360EC2E4C29E}']
    procedure AddBranch(Branch: INxRow);
    procedure DeleteBranch(Branch: INxRow);
    function GetBranch(const Index: Integer): INxRow;
    function GetBranchCount: Integer;
    function GetBranchRowCount: Integer;
    procedure IncBranchRowCount(Delta: Integer);
    property Branch[const Index: Integer]: INxRow read GetBranch; default;
    property BranchCount: Integer read GetBranchCount;
    property BranchRowCount: Integer read GetBranchRowCount;
  end;

  INxChildren = interface
    ['{3DE03DA0-766C-433A-BD2E-4FA74473D766}']
    procedure AddTo(Parent: INxRow);
    procedure InsertTo(Parent: INxRow; Index: Integer);
  end;

  INxRow = interface(INxParentRow)
    ['{835D69E6-B229-4EA8-8042-8E92593D5759}']
    { Property Accessors }
    function GetAbsoluteIndex: Integer;
    function GetCovered: Boolean;
    function GetData: Pointer;
    function GetFiltered: Boolean;
    function GetHeight: Integer;
    function GetImageIndex: TImageIndex;
    function GetIndex: Integer;
    function GetLevel: Integer;
    function GetParentRow: INxRow;
    function GetRefCount: Integer;
    function GetSelected: Boolean;
    function GetShowing: Boolean;
    function GetShowingCount: Integer;
    function GetTag: LongInt;
    function GetVisible: Boolean;
    procedure SetData(const Value: Pointer);
    procedure SetFiltered(const Value: Boolean);
    procedure SetHeight(const Value: Integer);
    procedure SetImageIndex(const Value: TImageIndex);
    procedure SetParentRow(const Value: INxRow);
    procedure SetSelected(const Value: Boolean);
    procedure SetTag(const Value: LongInt);
    procedure SetVisible(const Value: Boolean);
    { Methods }
    procedure AddChildRow(AChildRow: INxRow); overload;
    function AddChildRow(const Count: Integer = 1;
      IgnoreDefaults: Boolean = True): INxCellsRow; overload;
    function AddSiblingRow(const Count: Integer = 1;
      IgnoreDefaults: Boolean = True): INxCellsRow;
    procedure Cleanup;
    procedure Delete;
    procedure DeleteChildRow(const Index: Integer);
    procedure Hide;
    function InsertChildRow(const Position, Count: Integer;
      IgnoreDefaults: Boolean = True): INxCellsRow; overload;
    procedure InsertChildRow(Index: Integer; ARow: INxRow); overload;


    function IsTopLevel: Boolean;
    function IsParentOf(Row: INxRow): Boolean;
    procedure MoveTo(Parent: INxRow; Index: Integer = -1);
    function NextSibling: INxRow;
    function PrevSibling: INxRow;
    procedure Show;
    procedure ToggleExpanded;
    { Properties }
    property AbsoluteIndex: Integer read GetAbsoluteIndex;
    property Covered: Boolean read GetCovered;
    property Data: Pointer read GetData write SetData;
    property Filtered: Boolean read GetFiltered write SetFiltered;
    property Height: Integer read GetHeight write SetHeight;
    property ImageIndex: TImageIndex read GetImageIndex write SetImageIndex;
    property Index: Integer read GetIndex;
    property Level: Integer read GetLevel;
    property ParentRow: INxRow read GetParentRow write SetParentRow;
    property Selected: Boolean read GetSelected write SetSelected;
    property Showing: Boolean read GetShowing;
    property ShowingCount: Integer read GetShowingCount;
    property Tag: Longint read GetTag write SetTag;
    property Visible: Boolean read GetVisible write SetVisible;
  end;

  { TNxBaseRow6 }

  TNxBaseRow6 = class(TInterfacedObject, INxRows, INxBranches)
  private
    FBranches: TInterfaceList;
    FBranchRowCount: Integer;
    FChildRowsList: TInterfaceList;
    FChildShowingCount: Integer;
    FExpanded: Boolean;
    FShowingCount: Integer;
  private
    function GetShowingCount: Integer;
  protected
    procedure ExpandChanged(const Value: Boolean); virtual; abstract;
    procedure DoDelete(const Index: Integer; Count: Integer); virtual; abstract;
    procedure DoMove(const CurIndex, NewIndex: Integer); virtual; abstract;
    { INxBranches }
    procedure AddBranch(Branch: INxRow);
    procedure DeleteBranch(Branch: INxRow); virtual;
    function GetBranch(const Index: Integer): INxRow;
    function GetBranchCount: Integer;
    function GetBranchRowCount: Integer;
    procedure IncBranchRowCount(Delta: Integer); virtual;
  protected
    { INxRows }
    procedure AddChild(AChildRow: INxRow); virtual;
    procedure DeleteChild(AChildRow: INxRow); virtual;
    procedure InsertChild(Child: INxRow; Index: Integer); virtual;
    procedure UpdateChildShowingCount(AParent: INxRow; Delta: Integer); virtual;
    procedure UpdateShownCount(Row: INxRow; Delta: Integer); virtual;
  public
    { INxParentRow }
    function ChildsShown: Boolean; virtual;
    function GetBranches: TInterfaceList;
    function GetChildRow(const Index: Integer): INxRow;
    function GetChildRowCount: Integer;
    function GetChildShowingCount: Integer;
    function GetExpanded: Boolean;
    function GetFirstChild: INxRow;
    function GetLastChild: INxRow;
    function GetLastDescendant: INxRow;
    function HasChildren: Boolean;
    function HasParent: Boolean; virtual; abstract;
    function IndexOf(AChild: INxRow): Integer;
    procedure ClearChildren; virtual;
    procedure Collapse(Recurse: Boolean);
    procedure Expand(Recurse: Boolean);
    procedure SetExpanded(const Value: Boolean);
  public
    constructor Create(ACells: INxCells; AHeight: Integer); virtual;
    destructor Destroy; override;
    { Methods }
    function Exist(Index: Integer): Boolean;
    procedure Move(CurIndex, NewIndex: Integer);
    { Properties }
    property Branches: TInterfaceList read GetBranches;
    property ChildRow[const Index: Integer]: INxRow read GetChildRow;
    property ChildRowCount: Integer read GetChildRowCount;
    property ShowingCount: Integer read GetShowingCount;
  end;

  { TNxParentRow6 }

  TNxParentRow6 = class(TNxBaseRow6)

  end;

  { TNxRow6 }

  TNxRow6 = class(TNxParentRow6, INxRow, INxChildren, IWeakly)
  private
    FData: Pointer;
    FHeight: Integer;
    FImageIndex: TImageIndex;
    FOwner: INxCells;
    FParentRow: IWeakReference;
    FReference: IWeakReference;
    FState: TNxRowState;
    FTag: LongInt;
    FVisible: Boolean;
    function GetCovered: Boolean;
    function GetData: Pointer;
    function GetFiltered: Boolean;
    function GetGrid: INxCellsControl;
    function GetHeight: Integer;
    function GetImageIndex: TImageIndex;
    function GetIndex: Integer;
    function GetParentRow: INxRow;
    function GetParentRows: INxRows;
    function GetRefCount: Integer;
    function GetSelected: Boolean;
    function GetShowing: Boolean;
    function GetTag: Longint;
    function GetVisible: Boolean;
    procedure SetData(const Value: Pointer);
    procedure SetFiltered(const Value: Boolean);
    procedure SetHeight(const Value: Integer);
    procedure SetImageIndex(const Value: TImageIndex);
    procedure SetParentRow(const Value: INxRow);
    procedure SetSelected(const Value: Boolean);
    procedure SetTag(const Value: Longint);
    procedure SetVisible(const Value: Boolean);
  protected
    procedure ExpandChanged(const Value: Boolean); override;
    { INxChildren }
    procedure AddTo(Parent: INxRow);
    procedure InsertTo(Parent: INxRow; Index: Integer);

    { INxRows }
    procedure AddChild(Child: INxRow); override;
    procedure InsertChild(Child: INxRow; Index: Integer); override;

    { Property Accessors }
    function GetAbsoluteIndex: Integer; virtual;
    { Event Handlers }
    procedure DoDelete(const Index: Integer; Count: Integer); override;
    procedure DoMove(const CurIndex, NewIndex: Integer); override;
    function GetLevel: Integer; virtual;
    { IWeakly }
    function WeakReference: IWeakReference; stdcall;
    { Methods }
    function CanUpdate(Options: TNxUpdateOptions): Boolean;
    function GetOwner: INxCells; virtual;
    procedure IncBranchRowCount(Delta: Integer); override;
    procedure Invalidate;
    procedure Cleanup; virtual;
    procedure UpdateShownCount(Row: INxRow; Delta: Integer); override;
    { Properties }
    property Grid: INxCellsControl read GetGrid;
    property ParentRows: INxRows read GetParentRows;
  public
    constructor Create(ACells: INxCells; AHeight: Integer); override;
    destructor Destroy; override;
    { Methods }
    procedure AddChildRow(AChildRow: INxRow); overload;
    function AddChildRow(const Count: Integer;
      IgnoreDefaults: Boolean = True): INxCellsRow; overload;
    function AddSiblingRow(const Count: Integer = 1;
      IgnoreDefaults: Boolean = True): INxCellsRow;
    function ChildsShown: Boolean; override;
    procedure ClearChildren; override;
    procedure Delete;
    procedure DeleteChildRow(const Index: Integer);
    function HasParent: Boolean; override;
    procedure Hide;
    procedure InsertChildRow(Index: Integer; AChildRow: INxRow); overload;
    function InsertChildRow(const Index, Count: Integer;
      IgnoreDefaults: Boolean = True): INxCellsRow; overload;

    function IsParentOf(Row: INxRow): Boolean;
    function IsTopLevel: Boolean;
    procedure MoveTo(Parent: INxRow; Index: Integer = -1);
    function NextSibling: INxRow;
    function PrevSibling: INxRow;
    procedure Show;
    procedure ToggleExpanded;
    { Properties }
    property AbsoluteIndex: Integer read GetAbsoluteIndex;
    property Covered: Boolean read GetCovered;
    property Data: Pointer read GetData write SetData;
    property Expanded: Boolean read GetExpanded write SetExpanded;
    property Filtered: Boolean read GetFiltered write SetFiltered;
    property Height: Integer read GetHeight write SetHeight;
    property ImageIndex: TImageIndex read GetImageIndex write SetImageIndex;
    property Index: Integer read GetIndex;
    property Level: Integer read GetLevel;
    property Owner: INxCells read GetOwner;
    property ParentRow: INxRow read GetParentRow write SetParentRow;
    property Selected: Boolean read GetSelected write SetSelected;
    property Showing: Boolean read GetShowing;
    property Tag: Longint read GetTag write SetTag;
    property Visible: Boolean read GetVisible write SetVisible;
  end;

  { TNxCellsRow6 }

  INxCellsRow = interface(INxRow)
    ['{CAD1C12C-088E-4DBC-82C4-35D2C64E0310}']
    { Property Accessors }
    function GetAsString: WideString;
    function GetCells: TNxCellsList;
    function GetIndex: Integer;
    function GetOwner: INxCells;
    procedure SetCells(const Value: TNxCellsList);
    procedure AssignCells(Source: INxCellsRow);
    procedure CreateCells(IgnoreDefaults: Boolean);
    procedure DestroyCells;
    function IndexOfCell(Cell: INxCell): Integer;
    { Properties }
    property AsString: WideString read GetAsString;
    property Cells: TNxCellsList read GetCells write SetCells;
    property Owner: INxCells read GetOwner;
  end;

  TNxCellsRow6 = class(TNxRow6, INxRow, INxCellsRow)
  private
    { Property Fields }
    FCellsList: TNxCellsList;
    { Property Accessors }
    function GetAsString: WideString;
    function GetCells: TNxCellsList;
    function GetCount: Integer;
    procedure SetCells(const Value: TNxCellsList);
  public
    constructor Create(ACells: INxCells; AHeight: Integer); override;
    destructor Destroy; override;
    procedure AssignCells(Source: INxCellsRow);
    procedure CreateCells(IgnoreDefaults: Boolean);
    procedure DestroyCells;
    function IndexOfCell(Cell: INxCell): Integer;
    { Properties }
    property AsString: WideString read GetAsString;
    property Cells: TNxCellsList read GetCells write SetCells;
    property Count: Integer read GetCount;
  end;

  INxCells = interface
    ['{FD374807-6B63-46A7-B096-218A848EBC58}']
    { Property Accessors }
    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 GetShowing(const Index: Integer): Boolean;
    function GetShowingCount: Integer;
    procedure SetControl(const Value: INxCellsControl);
    procedure SetFirstVisible(const Value: Integer);
    procedure SetLocation(const Value: TPoint);
    { Properties }
    property Cell[ACol, ARow: Integer]: INxCell read GetCell; default;
    property ChildRow[const Index: Integer]: INxRow read GetChildRow;
    property ChildRowCount: Integer read GetChildRowCount;
    property Control: INxCellsControl read GetControl write SetControl;
    property FirstVisible: Integer read GetFirstVisible write SetFirstVisible;
    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 Showing[const Index: Integer]: Boolean read GetShowing;
    property ShowingCount: Integer read GetShowingCount;
    { Methods }
    function AbsoluteIndexOf(Row: INxRow): Integer;
    function AddRow(const Count: Integer; IgnoreDefaults: Boolean): INxCellsRow;
    procedure CellChanged(Cell: INxCell);
    function GetRowsBetween(FromPos, ToPos: Integer): Integer;
    procedure FindFirstVisible;
    procedure FindLastVisible;
    procedure IncRowCount(const Delta: Integer);
    function HasLocation: Boolean;
    function IndexOf(Row: INxRow): Integer;
    function InsertRow(const Index, Count: Integer; IgnoreDefaults: Boolean): INxCellsRow;
    function IsDestroying: Boolean;
    function IsUpdating: Boolean;
    procedure AddColumn;
    procedure Clear;
    procedure DeleteColumn(Column: TNxColumn6);
    procedure DeleteRow(const Index: Integer);
    procedure MoveRow(const FromPos, ToPos: Integer);
//    procedure ProcessFilter(const Index: Integer);
//    procedure Sort(const Index: Integer; SortKind: TNxSortKind; Resort: Boolean);
  end;

  INxSelectable = interface
    ['{4C5F0403-4944-45CC-B1AC-0EBA233E5BF2}']
    function GetSelected(const Index: Integer): Boolean;
    function GetSelectedCount: Integer;
    procedure SetSelected(const Index: Integer; const Value: Boolean);
    procedure SelectNone;
    property Selected[const Index: Integer]: Boolean read GetSelected write SetSelected;
    property SelectedCount: Integer read GetSelectedCount;
  end;

  { TNxSortableObject }

  TNxSortableObject = class(TNxRow6)
  protected
    { Methods }
    function AlphabeticLessCompare(Cell1, Cell2: INxBase): Boolean;
    function AlphabeticGreaterCompare(Cell1, Cell2: INxBase): Boolean;
    function AlphabeticUnicodeLessCompare(Cell1, Cell2: INxBase): Boolean;
    function AlphabeticUnicodeGreaterCompare(Cell1, Cell2: INxBase): Boolean;
    function BooleanLessCompare(Cell1, Cell2: INxBase): Boolean;
    function BooleanGreaterCompare(Cell1, Cell2: INxBase): Boolean;
    function CaseInsensitiveLessCompare(Cell1, Cell2: INxBase): Boolean;
    function CaseInsensitiveGreaterCompare(Cell1, Cell2: INxBase): Boolean;
    function CustomLessCompare(Cell1, Cell2: INxBase): Boolean;
    function CustomGreaterCompare(Cell1, Cell2: INxBase): Boolean;
    function NumericLessCompare(Cell1, Cell2: INxBase): Boolean;
    function NumericGreaterCompare(Cell1, Cell2: INxBase): Boolean;
    function DateLessCompare(Cell1, Cell2: INxBase): Boolean;
    function DateGreaterCompare(Cell1, Cell2: INxBase): Boolean;
    function IPLessCompare(Cell1, Cell2: INxBase): Boolean;
    function IPGreaterCompare(Cell1, Cell2: INxBase): Boolean;
    { Event Handlers }
    procedure DoCellCompare(Cell1, Cell2: INxBase; var Compare: Integer); dynamic; abstract;
  end;

  { TNxCells }

  TNxCells6 = class(TNxSortableObject, INxCells)
  private
    FAutoSort: Boolean;
    FColCount: Integer;
    FColIndex: Integer;
    FControl: INxCellsControl;
    FDestroying: Boolean;
    FFirstVisible: Integer;
    FLastVisible: Integer;
    FRowCount: Integer;
    FRowIndex: Integer;
    FSelCount: Integer;
    FSorted: Boolean;
    FSortedCol: Integer;
    FSortedColumn: TNxColumn6;
    FSortKind: TNxSortKind;
    FSortType: TNxSortType;
    FUpdateCount: Integer;
    { Property Accessors }
    function GetAutoSort: Boolean;
    function GetCell(ACol, ARow: Integer): INxCell;
    function GetControl: INxCellsControl;
    function GetFirstVisible: Integer;
    function GetLastVisible: Integer;
    function GetLocation: TPoint;
    function GetRow(const Index: Integer): INxRow;
    function GetRowCount: Integer;
    function GetShowing(const Index: Integer): Boolean;
    procedure SetAutoSort(const Value: Boolean);
    procedure SetControl(const Value: INxCellsControl);
    procedure SetFirstVisible(const Value: Integer);
    procedure SetLocation(const Value: TPoint);
  protected
    { Property Accessors }
    function GetAbsoluteIndex: Integer; override;
    function GetLevel: Integer; override;
    function GetOwner: INxCells; override;
    { Event Handlers }
    procedure DoChildRemoved(const Index: Integer);
    procedure DoDelete(const Index: Integer; Count: Integer); override;
    { INxCells }
    procedure CellChanged(Cell: INxCell);
    procedure IncRowCount(const Delta: Integer);
    { INxBranches }
    procedure AddBranch(Branch: INxRow);
    procedure IncBranchRowCount(Delta: Integer); override;
    procedure ResortBranches;
    { INxRows }
    procedure DoCellCompare(Cell1, Cell2: INxBase; var Compare: Integer); override;
    procedure FindLastVisible;
    procedure FindFirstVisible;
  public
    constructor Create(ACells: INxCells; AHeight: Integer); override;
    destructor Destroy; override;
    { Methods }
    function AbsoluteIndexOf(Row: INxRow): Integer;
    function AddCells(ARow: INxCellsRow; IgnoreDefaults: Boolean): TNxCellsList;
    procedure AddColumn;
    function AddRow(const Count: Integer; IgnoreDefaults: Boolean): INxCellsRow;
    procedure BeginUpdate;
    procedure Clear;
    procedure DeleteColumn(Column: TNxColumn6);
    procedure DeleteRow(const Index: Integer);
    procedure EndUpdate;
    function GetRowsBetween(FromPos, ToPos: Integer): Integer;
    procedure FilterAll;
    function HasLocation: Boolean;
    procedure HideAll(Func: TNxHideAllFunc);
    function IndexOf(Row: INxRow): Integer;
    function InsertRow(const Index, Count: Integer; IgnoreDefaults: Boolean): INxCellsRow;
    function IsDestroying: Boolean;
    function IsFiltered(const AIndex: Integer): Boolean;
    function IsUnique(Cell: INxCell): Boolean;
    function IsUpdating: Boolean;
    procedure MoveRow(const FromPos, ToPos: Integer);
    procedure ProcessFilter(const Index: Integer);
    procedure Recalculate;
    procedure RelocateRow(const Index: Integer);
    procedure Sort(const Index: Integer; SortKind: TNxSortKind; Resort: Boolean);
    procedure SwapRows(const FromPos, ToPos: Integer);
    procedure UnfilterAll;
    procedure UnhideAll;
    { Properties }
    property AutoSort: Boolean read GetAutoSort write SetAutoSort;
    property Cell[ACol, ARow: Integer]: INxCell read GetCell; default;
    property ColCount: Integer read FColCount write FColCount;
    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;
  end;

  function CreateCell(DataType: TNxDataType): INxCell;

implementation

uses
  { Standard }
  SysUtils, DateUtils, Math, Dialogs, Windows;

function StrToIP(IP: string): TNxIPNumber;
var
	i, j: Integer;
  buff: string;
begin
  j := 0;
  buff := '';
  Result[0] := 0;
  Result[1] := 0;
  Result[2] := 0;
  Result[3] := 0;
  for i := 1 to Length(IP) do
  begin
    if CharInSet(IP[i], ['0'..'9']) then buff := buff + IP[i];
    if (IP[i] = '.') or (i = Length(IP)) then
		begin
	    Result[j] := StrToInt(buff);
      Inc(j);
      buff := '';
    end;
  end;
end;

function IsIPSmaler(IP1, IP2: string): Boolean;
var
	i, a, b: Integer;
  num1, num2: TNxIPNumber;
begin
  Result := True;
  num1 := StrToIP(IP1);
  num2 := StrToIP(IP2);

  for i := 3 downto 0 do
  begin
  	a := num1[i];
  	b := num2[i];
    if a > b then
    begin
      Result := True;  // IP1 > IP2
      Exit;
    end else
    if a < b then
    begin
      Result := False; // IP1 < IP2
      Exit;
    end;
  end;
end;

function UnicodeCompare(S1, S2: WideString): Integer;
begin
  Result := WideCompareStr(S1, S2);
end;

function UnicodeCompareInsensitive(S1, S2: WideString): Integer;
begin
  Result := WideCompareText(S1, S2);
end;

function NumericCompare(N1, N2: Extended): Integer;
begin
  if N1 > N2 then Result := 1 else if N2 > N1 then Result := -1 else Result := 0;
end;

function CreateCell(DataType: TNxDataType): INxCell;
begin
  case DataType of
    dtAutoInc: Result := TNxIncrementCell.Create;
    dtBoolean: Result := TNxBooleanCell.Create;
    dtDateTime: Result := TNxDateTimeCell.Create;
    dtFloat: Result := TNxFloatCell.Create;
    dtInteger: Result := TNxIntegerCell.Create;
    dtString: Result := TNxStringCell.Create;
  end;
end;

function CalculateChildShowingCount(AParent: INxRow): Integer;
var
  i: Integer;
  AChild: INxRow;
begin
  Result := 0;
  for i := 0 to Pred(AParent.ChildRowCount) do
  begin
    AChild := AParent.ChildRow[i];
    if AChild.Visible and not AChild.Filtered then
    begin
      Inc(Result);
      if AChild.Expanded then Inc(Result, CalculateChildShowingCount(AChild));
    end;
  end;
end;

{ TNxCell }

procedure TNxCell6.Assign(Source: TObject);
begin
  if Source is TNxCell6 then
  begin
    Color := TNxCell6(Source).Color;
    Data := TNxCell6(Source).Data;
    Font := TNxCell6(Source).Font;
    State := TNxCell6(Source).State;
  end;
end;

procedure TNxCell6.AssignValue(Source: INxBase);
begin

end;

procedure TNxCell6.Changed(UpdateSpread: TNxCellUpdateSpread);
begin
  if HasLocation then
  begin
    List.Row.Owner.Control.CellChanged(Self, UpdateSpread);

    Owner.CellChanged(Self);
  end;
end;

constructor TNxCell6.Create;
begin
  FColor := clWindow;
  FFont := nil;
  FHint := '';
  FState := [ctParentColor];
end;

destructor TNxCell6.Destroy;
begin
  FreeAndNil(FFont);
  inherited;
end;

procedure TNxCell6.DoFontChange(Sender: TObject);
begin
  Changed;
end;

function TNxCell6.GetComputedLocation: TPoint;
begin
  Result := Point(List.Row.IndexOfCell(Self), List.Row.AbsoluteIndex);
end;

function TNxCell6.GetAsBoolean: Boolean;
begin
  Result := False;
end;

function TNxCell6.GetAsDateTime: TDateTime;
begin
  Result := 0;
end;

function TNxCell6.GetAsFloat: Double;
begin
  Result := 0;
end;

function TNxCell6.GetAsInteger: Integer;
begin
  Result := 0;
end;

function TNxCell6.GetAsString: WideString;
begin
  Result := EmptyStr;
end;

function TNxCell6.GetAsUTF8String: UTF8String;
begin
  Result := UTF8String(AsString);
end;

function TNxCell6.GetColIndex: Integer;
begin
  if Assigned(List) then
  begin
    Result := Owner.Location.X;
  end else Result := Undefined;
end;

function TNxCell6.GetList: TNxCellsList;
begin
  Result := FList;
end;

function TNxCell6.GetOwner: INxCells;
begin
  { Default }
  Result := nil;

  { Notice: Insert row cell doesn't
            have Owner set! }
  if Assigned(List) then Result := List.Row.Owner;
end;

function TNxCell6.GetParentColor: Boolean;
begin
  Result := ctParentColor in FState;
end;

function TNxCell6.GetColor: TColor;
begin
  Result := FColor;
end;

function TNxCell6.GetData: Pointer;
begin
  Result := FData;
end;

function TNxCell6.GetDisplayText: WideString;
begin
  if List <> nil then
  begin
    Result := Owner.Control.GetDisplayText(Self);
  end
  else Result := EmptyStr;
end;

function TNxCell6.GetEmpty: Boolean;
begin
  Result := ctEmpty in FState;
end;

function TNxCell6.GetEnabled: Boolean;
begin
  Result := not (ctDisabled in FState);
end;

function TNxCell6.GetFont: TFont;
begin
  if FFont = nil then
  begin
    FFont := TFont.Create;
    FFont.OnChange := DoFontChange;
  end;

  Result := FFont;
end;

function TNxCell6.GetVisible: Boolean;
begin
  Result := not (ctHidden in FState);
end;

function TNxCell6.GetHint: WideString;
begin
  Result := FHint;
end;

function TNxCell6.GetIsNull: Boolean;
begin
  Result := ctNull in FState;
end;

function TNxCell6.GetLocation: TPoint;
begin
  Result := Point(ColIndex, RowIndex);
end;

function TNxCell6.GetLocked: Boolean;
begin
  Result := ctLocked in FState;
end;

function TNxCell6.GetRowIndex: Integer;
begin
  if Assigned(List) then
  begin
    Result := Owner.Location.Y;
  end else Result := Undefined;
end;

function TNxCell6.GetState: TNxCellStyle;
begin
  Result := FState;
end;

function TNxCell6.HasLocation: Boolean;
begin
  { Default }
  Result := False;

  { Belong to List of cells? }
  if HasOwner then
  begin
    Result := Owner.HasLocation;

    { Rebuild }
    if not Result then
    begin
      Owner.Location := ComputedLocation;

      { Yes, HasLocation }
      Result := True;
    end;
  end;
end;

function TNxCell6.HasOwner: Boolean;
begin
  Result := Assigned(FList);
end;

procedure TNxCell6.Hide;
begin
  Visible := False;
end;

procedure TNxCell6.InvertValue;
begin
end;

function TNxCell6.IsEqual(Value: INxCell): Boolean;
begin
  Result := False;
end;

function TNxCell6.IsFontSet: Boolean;
begin
  Result := Assigned(FFont);
end;

function TNxCell6.IsSet: Boolean;
begin
  Result := False;
end;

procedure TNxCell6.Revert;
begin
  { Revert to Default Value }
end;

procedure TNxCell6.RevertStyle;
begin
  { ToDo: Set Color & similar Prop. back to Column style }

  FColor := clWindow;
  FHint := '';
  FState := [];
  FreeAndNil(FFont);

  Changed; { Notify }
end;

procedure TNxCell6.SetAsBoolean(const Value: Boolean);
begin
  Changed; { Notify }
end;

procedure TNxCell6.SetAsDateTime(const Value: TDateTime);
begin
  Changed; { Notify }
end;

procedure TNxCell6.SetAsFloat(const Value: Double);
begin
  Changed; { Notify }
end;

procedure TNxCell6.SetAsInteger(const Value: Integer);
begin
  Changed; { Notify }
end;

procedure TNxCell6.SetAsString(const Value: WideString);
begin
  Changed; { Notify }
end;

procedure TNxCell6.SetAsUTF8String(const Value: UTF8String);
begin
  AsString := WideString(value);
end;

procedure TNxCell6.SetColor(const Value: TColor);
begin
  if Value <> FColor then
  begin
    FColor := Value;
    ParentColor := False;

    Changed; { Notify }
  end;
end;

procedure TNxCell6.SetData(const Value: Pointer);
begin
  FData := Value;
end;

procedure TNxCell6.SetEmpty(const Value: Boolean);
begin
  if Value <> Empty then
  begin
    if Value
      then Include(FState, ctEmpty)
      else Exclude(FState, ctEmpty);

    Changed; { Notify }
  end;
end;

procedure TNxCell6.SetEnabled(const Value: Boolean);
begin
  if Value <> Enabled then
  begin
    if Value
      then Exclude(FState, ctDisabled)
      else Include(FState, ctDisabled);
  end;
end;

procedure TNxCell6.SetFont(const Value: TFont);
begin
  if Value = nil then FreeAndNil(FFont)
  else
  begin
    { Create first! }
    if not Assigned(FFont) then
    begin
      FFont := TFont.Create;
      FFont.OnChange := DoFontChange;
    end;

    FFont.Assign(Value);

    { BestFit & etc. }
    Changed;
  end;
end;

procedure TNxCell6.SetHint(const Value: WideString);
begin
  if Value <> FHint then
  begin
    FHint := Value;

    Changed; { Notify }
  end;
end;

procedure TNxCell6.SetIsNull(const Value: Boolean);
begin
  if Value then Include(FState, ctNull) else Exclude(FState, ctNull);
end;

procedure TNxCell6.SetLocked(const Value: Boolean);
begin
  if Value then Include(FState, ctLocked)
    else Exclude(FState, ctLocked);
end;

procedure TNxCell6.SetParentColor(const Value: Boolean);
begin
  if Value <> ParentColor then
  begin
    if Value then Include(FState, ctParentColor) else Exclude(FState, ctParentColor);
    Changed; { Notify }
  end;
end;

procedure TNxCell6.SetState(const Value: TNxCellStyle);
begin
  FState := Value;
end;

procedure TNxCell6.SetVisible(const Value: Boolean);
begin
  if Value <> Visible then
  begin
    if Value
      then Exclude(FState, ctHidden)
      else Include(FState, ctHidden);
    Changed(cuRow);
  end;
end;

function TNxCell6.ToObject: TObject;
begin
  Result := Self as TObject;
end;

procedure TNxCell6.ValueChanged;
begin
  if HasLocation then
  begin
//    Owner.Control.CellChanged(Self, 0);
    Owner.CellChanged(Self);
  end;
end;

{ TNxCellsList }

constructor TNxCellsList.Create(AOwner: INxCellsRow);
begin
  inherited Create;
  FRow := Pointer(AOwner);
end;

function TNxCellsList.GetItem(Index: Integer): INxCell;
begin
  Supports(Items[Index], INxCell, Result);
end;

function TNxCellsList.GetRow: INxCellsRow;
begin
  Result := INxCellsRow(FRow);
end;

procedure TNxCellsList.SetItem(Index: Integer; const Value: INxCell);
begin
  Items[Index] := Value;
end;

{ TNxRow6 }

procedure TNxBaseRow6.AddBranch(Branch: INxRow);
var
  RowIndex, i: Integer;
begin
  if not Assigned(FBranches)
    then FBranches := TInterfaceList.Create;

  RowIndex := Branch.Index;

  { Don't add duplicates }
  if FBranches.IndexOf(INxRow(Branch)) = -1 then
  begin
    for i := 0 to Pred(FBranches.Count) do
    begin
      if RowIndex < INxRow(FBranches[i]).Index then
      begin
        FBranches.Insert(i, INxRow(Branch));
        Exit;
      end;
    end;
    FBranches.Add(Branch as INxRow);
  end;
end;

procedure TNxBaseRow6.AddChild(AChildRow: INxRow);
begin
  { Add into TList }
  FChildRowsList.Add(AChildRow);

  IncBranchRowCount((AChildRow as INxBranches).BranchRowCount + 1);
end;

function TNxBaseRow6.ChildsShown: Boolean;
begin
  Result := True;
end;

procedure TNxBaseRow6.ClearChildren;

  procedure Cleanup;
  var
    i: Integer;
  begin
    { Detach parents of childs }
    for i := 0 to Pred(ChildRowCount) do
      ChildRow[i].Cleanup;
  end;

begin
  FreeAndNil(FBranches);

//  Cleanup;
  FChildRowsList.Clear;

  FBranchRowCount := 0;
  FShowingCount := 0;
end;

procedure TNxBaseRow6.Collapse(Recurse: Boolean);
var
  i: Integer;
begin
  SetExpanded(False);
  for i := 0 to Pred(ChildRowCount) do
  begin
    ChildRow[i].Expanded := False;
    if ChildRow[i].HasChildren and Recurse
      then ChildRow[i].Collapse(True);
  end;
end;

constructor TNxBaseRow6.Create;
begin
  inherited Create;
  FBranches := nil;
  FBranchRowCount := 0;
  FChildShowingCount := 0;
  FChildRowsList := TInterfaceList.Create;
end;

procedure TNxBaseRow6.DeleteBranch(Branch: INxRow);
begin
  if FBranches <> nil then
  begin
    FBranches.Remove(Branch);
    if FBranches.Count = 0 then FreeAndNil(FBranches);
  end;
end;

procedure TNxBaseRow6.DeleteChild(AChildRow: INxRow);
var
  Count: Integer;
begin
  Count := (AChildRow as INxBranches).BranchRowCount + 1;

  { Decr. counters & repaint }
  DoDelete(AChildRow.Index, Count);

  { Total rows in branch }
  IncBranchRowCount(-Count);

  { Was a Branch? }
  if AChildRow.HasChildren then
  begin
    DeleteBranch(AChildRow);
  end;

  FChildRowsList.Delete(AChildRow.Index);
end;

destructor TNxBaseRow6.Destroy;
begin
  if HasChildren then ClearChildren;
  FreeAndNil(FChildRowsList);
  inherited;
end;

function TNxBaseRow6.Exist(Index: Integer): Boolean;
begin
  Result := InRange(Index, 0, Pred(FChildRowsList.Count));
end;

procedure TNxBaseRow6.Expand(Recurse: Boolean);
var
  i: Integer;
begin
  for i := 0 to Pred(ChildRowCount) do
  begin
    ChildRow[i].Expanded := True;
    if ChildRow[i].HasChildren and Recurse
      then ChildRow[i].Expand(True);
  end;
  SetExpanded(True);
end;

function TNxBaseRow6.GetBranch(const Index: Integer): INxRow;
begin
  Result := INxRow(FBranches[Index]);
end;

function TNxBaseRow6.GetBranchCount: Integer;
begin
  if Assigned(FBranches) then Result := FBranches.Count
    else Result := 0;
end;

function TNxBaseRow6.GetBranches: TInterfaceList;
begin
  Result := FBranches;
end;

function TNxBaseRow6.GetBranchRowCount: Integer;
begin
  Result := FBranchRowCount;
end;

function TNxBaseRow6.GetChildRow(const Index: Integer): INxRow;
begin
  Result := INxRow(FChildRowsList[Index]);
end;

function TNxBaseRow6.GetChildRowCount: Integer;
begin
  Result := FChildRowsList.Count;
end;

function TNxBaseRow6.GetChildShowingCount: Integer;
begin
  Result := FChildShowingCount;
end;

function TNxBaseRow6.GetExpanded: Boolean;
begin
  Result := FExpanded;
end;

function TNxBaseRow6.GetFirstChild: INxRow;
begin
  if HasChildren then Result := ChildRow[0] else Result := nil;
end;

function TNxBaseRow6.GetLastChild: INxRow;
begin
  if HasChildren then Result := ChildRow[ChildRowCount - 1] else Result := nil;
end;

function TNxBaseRow6.GetLastDescendant: INxRow;
begin
  Result := GetLastChild;
  if Assigned(Result) and Result.HasChildren then Result := GetLastDescendant;
end;

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

function TNxBaseRow6.HasChildren: Boolean;
begin
  Result := FChildRowsList.Count > 0;
end;

procedure TNxBaseRow6.IncBranchRowCount(Delta: Integer);
begin
  Inc(FBranchRowCount, Delta);
end;

function TNxBaseRow6.IndexOf(AChild: INxRow): Integer;
begin
  Result := FChildRowsList.IndexOf(AChild);
end;

procedure TNxBaseRow6.InsertChild(Child: INxRow; Index: Integer);
begin
  { Add to TInterfaceList }
  FChildRowsList.Insert(Index, Child);

  IncBranchRowCount((Child as INxBranches).BranchRowCount + 1);
end;

procedure TNxBaseRow6.Move(CurIndex, NewIndex: Integer);
var
  T: INxRow;
begin
  if Exist(CurIndex) and Exist(NewIndex)
    and (CurIndex <> NewIndex) then
  begin
    T := INxRow(FChildRowsList[CurIndex]);
    DeleteChild(T);
    InsertChild(T, NewIndex);
  end;
end;

procedure TNxBaseRow6.SetExpanded(const Value: Boolean);
var
  DeltaCount: Integer;
begin
  if Value <> FExpanded then
  begin
    FExpanded := Value;
    if FExpanded then
    begin
      DeltaCount := CalculateChildShowingCount(Self as INxRow);
    end else
      DeltaCount := -CalculateChildShowingCount(Self as INxRow);

    UpdateShownCount(Self as INxRow, DeltaCount);

    ExpandChanged(FExpanded);
  end;
end;

procedure TNxBaseRow6.UpdateChildShowingCount(AParent: INxRow; Delta: Integer);
begin
  Inc(FChildShowingCount, Delta);
end;

procedure TNxBaseRow6.UpdateShownCount(Row: INxRow; Delta: Integer);
begin
  Inc(FShowingCount, Delta);
end;

{ TNxRow6 }

procedure TNxRow6.AddTo(Parent: INxRow);
begin
  if Assigned(Parent) then
  begin
    FParentRow := (Parent AS IWeakly).WeakReference;

    { Add into TList & integrate }
    ParentRows.AddChild(Self as INxRow);
  end;
end;

procedure TNxRow6.AddChild(Child: INxRow);
var
  BranchRowCount: Integer;
begin
  inherited;

  BranchRowCount := (Child as INxBranches).BranchRowCount + 1;

  Owner.IncRowCount(BranchRowCount);

  { Note: As soon row get childrens,
          new Branch need to be created! }

  if ParentRow <> nil then
  begin
    (ParentRow as INxBranches).AddBranch(Self as INxRow);
  end;

  if Child.HasChildren then
  begin
    (Self as INxBranches).AddBranch(Child as INxRow);
  end;

  { Visible & not Expanded }
  if Showing and Expanded then
  begin
    UpdateShownCount(Child, BranchRowCount);
  end;
end;

function TNxRow6.AddChildRow(const Count: Integer;
  IgnoreDefaults: Boolean): INxCellsRow;
var
  AbsoluteIndex, i, LastIndex: Integer;
begin
  FChildRowsList.Capacity := ChildRowCount + Count;

  { Old last }
  LastIndex := ChildRowCount;

  for i := 0 to Pred(Count) do
  begin

    { Create Row & Set Height }
    Result := TNxCellsRow6.Create(Owner, Grid.RowHeight);

    { Create Cell objects }
    (Result as INxCellsRow).CreateCells(IgnoreDefaults);

    { Add into list, create Branches, update Counts etc. }
    (Result as INxChildren).AddTo(Self);
  end;

  Owner.Location := EmptyPoint;

  if Owner.FirstVisible = -1 then
    Owner.FirstVisible := LastIndex;

  { Cache }
  AbsoluteIndex := Self.AbsoluteIndex;

  { Expose childrens }
  if Grid.CanUpdate([uoRepaint]) and Grid.CanUpdate([uoScrollBar])
    and Grid.CanUpdate([uoRecount]) then
      if Assigned(Owner) and not Owner.IsUpdating then
      begin
        Grid.ShowChildRows(ChildRow[LastIndex], Count, True);

        { Repaint toggle btn. }
        Grid.RefreshRow(AbsoluteIndex);

        { LastAddedRow & AutoSelect }
        Grid.SetLastAddedRow(AbsoluteIndex + ChildRowCount);
      end;
end;

function TNxRow6.AddSiblingRow(const Count: Integer; IgnoreDefaults: Boolean): INxCellsRow;
var
  AbsoluteIndex, i, LastIndex: Integer;
begin
  FChildRowsList.Capacity := ChildRowCount + Count;

  { Old last }
  LastIndex := ParentRow.ChildRowCount;

  for i := 0 to Pred(Count) do
  begin

    { Create Row & Set Height }
    Result := TNxCellsRow6.Create(Owner, Grid.RowHeight);

    { Create Cell objects }
    (Result as INxCellsRow).CreateCells(IgnoreDefaults);

    { Add into list, create Branches, update Counts etc. }
    (Result as INxChildren).AddTo(Self.ParentRow);
  end;

  Owner.Location := EmptyPoint;

  if Owner.FirstVisible = -1 then
    Owner.FirstVisible := LastIndex;

  { Cache }
  AbsoluteIndex := Self.AbsoluteIndex;

  { Expose childrens }
  if Grid.CanUpdate([uoRepaint]) and Grid.CanUpdate([uoScrollBar])
    and Grid.CanUpdate([uoRecount]) then
      if Assigned(Owner) and not Owner.IsUpdating then
      begin
        Grid.ShowChildRows(Self.ParentRow.ChildRow[LastIndex], Count, True);

        { Repaint toggle btn. }
        Grid.RefreshRow(AbsoluteIndex);

        { LastAddedRow & AutoSelect }
        Grid.SetLastAddedRow(AbsoluteIndex + ChildRowCount);
      end;
end;

procedure TNxRow6.AddChildRow(AChildRow: INxRow);
begin
  AChildRow.ParentRow := Self;
end;

function TNxRow6.CanUpdate(Options: TNxUpdateOptions): Boolean;
begin
  Result := Grid.CanUpdate(Options);
end;

function TNxRow6.ChildsShown: Boolean;
begin
  Result := Expanded and Showing;
end;

procedure TNxRow6.Cleanup;
begin
  FParentRow := nil;
end;

procedure TNxRow6.ClearChildren;
begin

  { Is this row is a Branch, does it
    have any childs? }

  if HasChildren then
  begin

    { Note: If FRefCount is 0, object is already destroying,
            any access will inc & dec FRefCount and call
            destructor again (!) }

    if FRefCount > 0 then

      if HasParent then
      begin

        if ChildsShown then
        begin
          Grid.ChildsCleared(Self as INxRow);
        end;

        UpdateShownCount(Self as INxRow, -FBranchRowCount);

        (ParentRow as INxBranches).DeleteBranch(Self as INxRow);

        { Total row count }
        Owner.IncRowCount(-FBranchRowCount);

        IncBranchRowCount(-FBranchRowCount);
      end;

      { Destroy childs & set count to 0 }
      inherited;
    end;

end;

constructor TNxRow6.Create(ACells: INxCells; AHeight: Integer);
begin
  inherited;
  FOwner := ACells;
  FExpanded := True;
  FHeight := AHeight;
  FImageIndex := -1;
  FParentRow := nil;
  FReference := TWeakReference.Create(Self);
  FState := [rsVisible];
  FTag := 0;
  FVisible := True;
end;

procedure TNxRow6.Delete;
begin
  ParentRow.DeleteChildRow(Index);
end;

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

destructor TNxRow6.Destroy;
begin
  FReference.Disconnect;
  FReference := nil;
  inherited;
  FOwner := nil;
end;

procedure TNxRow6.DoDelete(const Index: Integer; Count: Integer);
begin
  UpdateShownCount(Self, - Count);
  Owner.IncRowCount(-Count);

  { Repaint }
  Grid.ChildDeleted(ChildRow[Index], False);
end;

procedure TNxRow6.DoMove(const CurIndex, NewIndex: Integer);
begin
  Grid.RowMoved(CurIndex, NewIndex);
end;

procedure TNxRow6.ExpandChanged(const Value: Boolean);
begin
  Grid.ExpandedChanged(Self, Value);
end;

function TNxRow6.GetAbsoluteIndex: Integer;
begin
  Result := Owner.AbsoluteIndexOf(Self);
end;

function TNxRow6.GetCovered: Boolean;
begin
  Result := False;
  if ParentRow <> nil then
  begin
    if not IsTopLevel
      then Result := ParentRow.Covered or not ParentRow.Expanded or not ParentRow.Visible
      else Result := False;
  end;
end;

function TNxRow6.GetData: Pointer;
begin
  Result := FData;
end;

function TNxRow6.GetOwner: INxCells;
begin
  Result := FOwner;
end;

function TNxRow6.GetFiltered: Boolean;
begin
  Result := rsFiltered in FState;
end;

function TNxRow6.GetGrid: INxCellsControl;
begin
  Result := Owner.Control;
end;

function TNxRow6.GetHeight: Integer;
begin
  Result := FHeight;
end;

function TNxRow6.GetImageIndex: TImageIndex;
begin
  Result := FImageIndex;
end;

function TNxRow6.GetIndex: Integer;
begin
  Result := ParentRows.IndexOf(Self as INxRow);
end;

function TNxRow6.GetLevel: Integer;
begin
  if ParentRow = nil then Result := -1
    else Result := ParentRow.Level + 1;
end;

function TNxRow6.GetParentRow: INxRow;
begin
  if Assigned(FParentRow) then
    Result := FParentRow.GetIntf as INxRow
  else
    Result := nil;
end;

function TNxRow6.GetParentRows: INxRows;
begin
  Result := ParentRow as INxRows;
end;

function TNxRow6.GetRefCount: Integer;
begin
  Result := FRefCount;
end;

function TNxRow6.GetSelected: Boolean;
begin
  Result := rsSelected in FState;
end;

function TNxRow6.GetShowing: Boolean;
begin
  Result := Visible and not Filtered and not Covered;
end;

function TNxRow6.GetTag: Longint;
begin
  Result := FTag;
end;

function TNxRow6.GetVisible: Boolean;
begin
  Result := FVisible;
end;

function TNxRow6.HasParent: Boolean;
begin
  Result := Assigned(ParentRow);
end;

procedure TNxRow6.Hide;
begin
  Visible := False;
end;

procedure TNxRow6.IncBranchRowCount(Delta: Integer);
begin
  inherited IncBranchRowCount(Delta);
  if ParentRow <> nil then
  begin
    (ParentRow as INxBranches).IncBranchRowCount(Delta);
  end;
end;

procedure TNxRow6.InsertTo(Parent: INxRow; Index: Integer);
begin
  if Assigned(Parent) then
  begin
    FParentRow := (Parent AS IWeakly).WeakReference;

    { Add into TList & integrate }
    ParentRows.InsertChild(Self as INxRow, Index);
  end;
end;

procedure TNxRow6.InsertChild(Child: INxRow; Index: Integer);
var
  BranchRowCount: Integer;
begin
  inherited InsertChild(Child, Index);

  BranchRowCount := (Child as INxBranches).BranchRowCount + 1;

  Owner.IncRowCount(BranchRowCount);

  { Note: As soon row get childrens,
          new Branch need to be created! }

  if ParentRow <> nil then
  begin
    (ParentRow as INxBranches).AddBranch(Self as INxRow);
  end;

  if Child.HasChildren then
  begin
    (Self as INxBranches).AddBranch(Child as INxRow);
  end;

  { Visible & not Expanded }
  if Showing and Expanded then
  begin
    UpdateShownCount(Child, BranchRowCount);

    { 11/9/2017: MoveRow repaint bug. }
    Grid.ShowChildRows(Child, 1, False);
  end;
end;

procedure TNxRow6.InsertChildRow(Index: Integer; AChildRow: INxRow);
begin
  (AChildRow as INxChildren).InsertTo(Self as INxRow, Index);
end;

function TNxRow6.InsertChildRow(const Index, Count: Integer;
  IgnoreDefaults: Boolean): INxCellsRow;
var
  i, LastIndex: Integer;
begin
  FChildRowsList.Capacity := ChildRowCount + Count;

  LastIndex := Index;

  for i := 0 to Pred(Count) do
  begin
    { Create Row & Set Height }
    Result := TNxCellsRow6.Create(Owner, Grid.RowHeight);

    { Create Cell objects }
    (Result as INxCellsRow).CreateCells(IgnoreDefaults);

    { Add into list, create Branches, update Counts etc. }
    (Result as INxChildren).InsertTo(Self, Index);
  end;

  Owner.Location := EmptyPoint;

  Grid.ShowChildRows(ChildRow[LastIndex], Count, True);

  { Toggle button }
  Grid.RefreshRow((Self as INxRow).AbsoluteIndex);

  { LastAddedRow & AutoSelect }
  Grid.SetLastAddedRow(Result.AbsoluteIndex);
end;

procedure TNxRow6.Invalidate;
begin
  if CanUpdate([uoRepaint])
    then Owner.Control.RefreshRow(Index);
end;

function TNxRow6.IsParentOf(Row: INxRow): Boolean;
begin
  if Row.IsTopLevel or (Row = Self as INxRow) then Result := False else
  begin
    if INxRow(Self) = Row.ParentRow then Result := True
      else Result := IsParentOf(Row.ParentRow);
  end
end;

function TNxRow6.IsTopLevel: Boolean;
begin
  Result := Level = 0;
end;

procedure TNxRow6.MoveTo(Parent: INxRow; Index: Integer = -1);
var
  ARow: INxRow;
begin
  if Parent <> ParentRow then
  begin
    ARow := Self;

    ParentRow := Parent;

    Grid.RefreshRow(Parent.AbsoluteIndex);

    Owner.FindFirstVisible;
  end;

  if Index <> -1 then
  begin
    (ParentRow as INxRows).Move(Self.Index, Index);
  end;
end;

function TNxRow6.NextSibling: INxRow;
begin
  Result := nil;
  if Index < Pred(ParentRow.ChildRowCount) then Result := ParentRow.ChildRow[Index + 1];
end;

function TNxRow6.PrevSibling: INxRow;
begin
  Result := nil;
  if Index > 0 then Result := ParentRow.ChildRow[Index - 1];
end;

procedure TNxRow6.SetData(const Value: Pointer);
begin
  FData := Value;
end;

procedure TNxRow6.SetFiltered(const Value: Boolean);
var
  PrevShown: Boolean;
begin
  if Value <> Filtered then
  begin
    PrevShown := Showing;

    if Value
      then Include(FState, rsFiltered)
      else Exclude(FState, rsFiltered);

    if not Owner.IsUpdating
      and (Showing <> PrevShown) then
    begin
      { Row changed appearance }
      ParentRows.UpdateShownCount(Self, IfThen(PrevShown, -1, 1));

      Grid.VisibilityChanged(Index, Showing);
    end;
  end;
end;

procedure TNxRow6.SetHeight(const Value: Integer);
var
  Delta: Integer;
begin
  if Value <> FHeight then
  begin
    Delta := Value - FHeight;

    FHeight := Value;

    if (Owner <> nil) and (Grid <> nil)
      then Grid.HeightChanged(Index, Delta);
  end;
end;

procedure TNxRow6.SetImageIndex(const Value: TImageIndex);
begin
  if Value <> FImageIndex then
  begin
    FImageIndex := Value;
    Invalidate;
  end;
end;

procedure TNxRow6.SetParentRow(const Value: INxRow);
begin

  if Value <> ParentRow then
  begin
    { Delete in TList }
    if ParentRow <> nil then
    begin
      ParentRows.DeleteChild(Self as INxRow);
    end;

    { Add into TList & integrate }
    if Value <> nil then
    begin
      FParentRow := (Value AS IWeakly).WeakReference;

      ParentRows.AddChild(Self as INxRow);

      Grid.ShowChildRows(Self as INxRow, 1, True);
    end
    else FParentRow := nil;
  end;
end;

procedure TNxRow6.SetSelected(const Value: Boolean);
begin
  if Value <> Selected then
  begin
    if Value
      then Include(FState, rsSelected)
      else Exclude(FState, rsSelected);

    Invalidate;
  end;
end;

procedure TNxRow6.SetTag(const Value: Integer);
begin
  FTag := Value;
end;

procedure TNxRow6.SetVisible(const Value: Boolean);
var
  PrevShown: Boolean;
begin

  if (Value <> FVisible) then
  begin
    PrevShown := Showing;

    FVisible := Value;

    { Is still unchanged? }
    if not Owner.IsUpdating
      and (Showing <> PrevShown) then
    begin

      { Update VisibleCount (& ScrollBar) }
      if Showing then
      begin
        ParentRows.UpdateShownCount(Self as INxRow, 1);

        if Grid.CanUpdate([uoRecount]) then
        begin
          if (Index < Owner.FirstVisible)
            or (Owner.FirstVisible < 0)
            then Owner.FirstVisible := Index;
        end;

      end else
      begin
        ParentRows.UpdateShownCount(Self as INxRow, - (1 + FBranchRowCount));

        if Grid.CanUpdate([uoRecount]) then
        begin
          if Index = Owner.FirstVisible
            then Owner.FindFirstVisible;
        end;

      end;

      { Notice: Index calc. slow things a bit! }
      Grid.VisibilityChanged(Index, FVisible);

    end;

  end;

// todo : implement
//  if CellsOwner.ShownCount = 0 then CellsOwner.ShownCount := -1;

end;

procedure TNxRow6.Show;
begin
  Visible := True;
end;

procedure TNxRow6.ToggleExpanded;
begin
  Expanded := not Expanded;
end;

procedure TNxRow6.UpdateShownCount(Row: INxRow; Delta: Integer);
begin
  if Delta <> 0 then
  begin
    if (Self as INxRow) <> Row
      then inherited;

    if ParentRow <> nil
      then ParentRows.UpdateShownCount(Row, Delta);
  end;
end;

function TNxRow6.WeakReference: IWeakReference;
begin
  Result := FReference;
end;

{ TNxCellsRow6 }

procedure TNxCellsRow6.AssignCells(Source: INxCellsRow);
var
  i: Integer;
begin
  for i := 0 to Pred(Count) do
    Cells[i].AssignValue(Source.Cells[i]);
end;

constructor TNxCellsRow6.Create(ACells: INxCells; AHeight: Integer);
begin
  inherited;
  FCellsList := TNxCellsList.Create(Self);
end;

procedure TNxCellsRow6.CreateCells;
var
  i: Integer;
  Cell: INxCell;
begin
  FCellsList.Count := Grid.Columns.Count;

  for i := 0 to Pred(FCellsList.Count) do
  begin

    with Grid.Columns[i] do
    begin
      Cell := CreateCell(DataType);
      Cell.Color := Color;

      { Set default values }
      if not IgnoreDefaults
        then AssignDefaultValue(Cell);
    end;

    { Notify Control }
    Cell.List := FCellsList;

    FCellsList.Item[i] := Cell;
  end;
end;

destructor TNxCellsRow6.Destroy;
begin
//  WritelnDebug('[Destroying ' + IntToStr(Tag) + ']');
  { Note: TreeCol View may call this method
          when Row is set to nil }

  DestroyCells;
  inherited;
end;

procedure TNxCellsRow6.DestroyCells;
begin
  { Destroy cells }
  FreeAndNil(FCellsList);
end;

function TNxCellsRow6.GetAsString: WideString;
var
  i: Integer;
begin
  Result := EmptyStr;
  for i := 0 to Pred(Count) do
  begin
    Result := Result + Cells[i].AsString;
    if Succ(i) < Pred(Count)
      then Result := Result + ',';
  end;
end;

function TNxCellsRow6.GetCells: TNxCellsList;
begin
  Result := FCellsList;
end;

function TNxCellsRow6.GetCount: Integer;
begin
  Result := FCellsList.Count;
end;

function TNxCellsRow6.IndexOfCell(Cell: INxCell): Integer;
begin
  Result := FCellsList.IndexOf(Cell);
end;

procedure TNxCellsRow6.SetCells(const Value: TNxCellsList);
begin
  FCellsList := Value;
end;


{ TNxSortableObject }

function TNxSortableObject.AlphabeticGreaterCompare(Cell1,
  Cell2: INxBase): Boolean;
begin
  Result := Cell1.AsString >= Cell2.AsString;
end;

function TNxSortableObject.AlphabeticLessCompare(Cell1,
  Cell2: INxBase): Boolean;
begin
  Result := Cell1.AsString <= Cell2.AsString;
end;

function TNxSortableObject.AlphabeticUnicodeGreaterCompare(Cell1,
  Cell2: INxBase): Boolean;
begin
  Result := WideCompareStr(Cell1.AsString, Cell2.AsString) > 0;
end;

function TNxSortableObject.AlphabeticUnicodeLessCompare(Cell1,
  Cell2: INxBase): Boolean;
begin
  Result := WideCompareStr(Cell1.AsString, Cell2.AsString) < 0;
end;

function TNxSortableObject.BooleanGreaterCompare(Cell1,
  Cell2: INxBase): Boolean;
begin
  Result := Cell1.AsBoolean >= Cell2.AsBoolean;
end;

function TNxSortableObject.BooleanLessCompare(Cell1, Cell2: INxBase): Boolean;
begin
  Result := Cell1.AsBoolean <= Cell2.AsBoolean;
end;

function TNxSortableObject.CaseInsensitiveGreaterCompare(Cell1,
  Cell2: INxBase): Boolean;
begin
  Result := LowerCase(Cell1.AsString) >= LowerCase(Cell2.AsString);
end;

function TNxSortableObject.CaseInsensitiveLessCompare(Cell1,
  Cell2: INxBase): Boolean;
begin
  Result := LowerCase(Cell1.AsString) <= LowerCase(Cell2.AsString);
end;

function TNxSortableObject.CustomGreaterCompare(Cell1, Cell2: INxBase): Boolean;
var
  Compare: Integer;
begin
  Compare := 0;

  { Event }
  DoCellCompare(Cell1, Cell2, Compare);

  Result := Compare >= 0;
end;

function TNxSortableObject.CustomLessCompare(Cell1, Cell2: INxBase): Boolean;
var
  Compare: Integer;
begin
  Compare := 0;

  { Event }
  DoCellCompare(Cell1, Cell2, Compare);

  Result := Compare <= 0;
end;

function TNxSortableObject.DateGreaterCompare(Cell1, Cell2: INxBase): Boolean;
begin
  Result := Cell1.AsDateTime >= Cell2.AsDateTime;
end;

function TNxSortableObject.DateLessCompare(Cell1, Cell2: INxBase): Boolean;
begin
  Result := Cell1.AsDateTime <= Cell2.AsDateTime;
end;

function TNxSortableObject.IPGreaterCompare(Cell1, Cell2: INxBase): Boolean;
begin
  Result := IsIPSmaler(Cell2.AsString, Cell1.AsString);
end;

function TNxSortableObject.IPLessCompare(Cell1, Cell2: INxBase): Boolean;
begin
  Result := IsIPSmaler(Cell1.AsString, Cell2.AsString);
end;

function TNxSortableObject.NumericGreaterCompare(Cell1,
  Cell2: INxBase): Boolean;
begin
  Result := Cell1.AsFloat >= Cell2.AsFloat;
end;

function TNxSortableObject.NumericLessCompare(Cell1, Cell2: INxBase): Boolean;
begin
  Result := Cell1.AsFloat <= Cell2.AsFloat;
end;

{ TNxCells }

function TNxCells6.AbsoluteIndexOf(Row: INxRow): Integer;

  function Abc: Integer;
  var
    Index, i: Integer;
    Branches: INxBranches;
  begin
    i := 0;
    Index := Row.Index;
    Result := Index;

    if Row.ParentRow = nil
      then Branches := Self as INxBranches
      else Branches := Row.ParentRow as INxBranches;

    while (i < Branches.BranchCount) and
      (Branches[i].Index < Index) do
    begin
      Inc(Result, (Branches[i] as INxBranches).BranchRowCount);
      Inc(i);
    end;

  end;

begin
  if Row.IsTopLevel then Result := 0
  else Result := Row.ParentRow.AbsoluteIndex + 1;

  Inc(Result, Abc);
end;

procedure TNxCells6.AddBranch(Branch: INxRow);
var
  i, RowIndex: Integer;
begin
  if not Assigned(FBranches)
    then FBranches := TInterfaceList.Create;

  RowIndex := Branch.Index;

  if FBranches.IndexOf(INxRow(Branch)) = -1 then
  begin
    for i := 0 to Pred(FBranches.Count) do
    begin
      if RowIndex < INxRow(FBranches[i]).Index then
      begin
        FBranches.Insert(i, INxRow(Branch));
        Exit;
      end;
    end;
    FBranches.Add(INxRow(Branch));
  end;

end;

function TNxCells6.AddCells(ARow: INxCellsRow; IgnoreDefaults: Boolean): TNxCellsList;
var
  i: Integer;
  Cell: INxCell;
begin

  for i := 0 to Pred(FColCount) do
  begin

    with Control.Columns[i] do
    begin
      Cell := CreateCell(DataType);
      Cell.Color := Color;

      { Set default values }
      if not IgnoreDefaults
        then AssignDefaultValue(Cell);
    end;

    { Notify Control }
    Cell.List := ARow.Cells;

    ARow.Cells.Add(Cell);
  end;

  Result := ARow.Cells;
end;

procedure TNxCells6.AddColumn;
var
  i: Integer;
  Cell: INxCell;
begin
  for i := 0 to Pred(RowCount) do
  begin
    Cell := CreateCell(Control.Columns[FColCount].DataType);

    { Notify Control }
    Cell.List := (Row[i] as INxCellsRow).Cells;

    (Row[i] as INxCellsRow).Cells.Add(Cell);
  end;

  Inc(FColCount);
end;

function TNxCells6.AddRow(const Count: Integer;
  IgnoreDefaults: Boolean): INxCellsRow;
begin
  Result := AddChildRow(Count, IgnoreDefaults);
end;

procedure TNxCells6.BeginUpdate;
begin
  Inc(FUpdateCount);
end;

procedure TNxCells6.CellChanged(Cell: INxCell);
begin
  FSorted := False;
end;

procedure TNxCells6.Clear;
begin
  FreeAndNil(FBranches);

  ClearChildren;

  { Don't forget }
  FRowCount := 0;

  { Reset Fields }
  FColIndex := -1;
  FFirstVisible := -1;
  FLastVisible := -1;
  FRowIndex := -1;
  FSelCount := 0;
  FSorted := False;
  FSortedCol := -1;
end;

constructor TNxCells6.Create(ACells: INxCells; AHeight: Integer);
begin
  inherited;
  FAutoSort := False;
  FDestroying := False;
  FColIndex := -1;
  FFirstVisible := 0;
  FLastVisible := -1;
  FRowCount := 0;
  FRowIndex := -1;
  FSorted := False;
  FSortedCol := -1;
  FSortType := stNone;
  FSelCount := 0;
  FUpdateCount := 0;
end;

procedure TNxCells6.DeleteColumn(Column: TNxColumn6);
begin
  Dec(FColCount);
end;

procedure TNxCells6.DeleteRow(const Index: Integer);
var
  T: INxRow;
begin
  T := Row[Index];

  if Control.CanUpdate([uoRecount]) then
  begin

    Dec(FLastVisible);

    if Index = FFirstVisible
      then FindFirstVisible;

    if Index = FLastVisible
      then FindLastVisible;

  end;

  T.ClearChildren;

  { Delete from parent TList }
//  T.ParentRows.RemoveRow(T.Index);

  T.ParentRow := nil;
end;

destructor TNxCells6.Destroy;
begin
  try
    FDestroying := True;

    FSortedColumn := nil;

    { Warning: if grid is flat FRefCount
               for rows must be 1! }

    { Destroy Branches ! }
    inherited Destroy;
  finally
    FDestroying := False;
  end;
end;

procedure TNxCells6.DoCellCompare(Cell1, Cell2: INxBase; var Compare: Integer);
begin
  FControl.DoCellCompare(INxCell(Cell1), INxCell(Cell2), Compare);
end;

procedure TNxCells6.DoChildRemoved(const Index: Integer);
begin
  Control.VisibilityChanged(Index, False);
  Dec(FRowCount);
end;

procedure TNxCells6.DoDelete(const Index: Integer; Count: Integer);
begin
  IncRowCount(-Count);
  Dec(FShowingCount, Count);

  { Refresh grid }
  Grid.ChildDeleted(ChildRow[Index], False);
end;

procedure TNxCells6.EndUpdate;
begin
  Dec(FUpdateCount);
end;

procedure TNxCells6.FilterAll;
var
  i: Integer;
  CurrentRow: INxRow;
begin
  { Reset First }
  FFirstVisible := Undefined;

  FShowingCount := 0;

  try
    { Don't do calc, everything will
      be reset. }
    BeginUpdate;

    for i := 0 to Pred(RowCount) do
    begin
      CurrentRow := Row[i];

      { Set Filtered }
      CurrentRow.Filtered := IsFiltered(i);

      { Not Filtered }
      if CurrentRow.Showing then
      begin
        if FFirstVisible = Undefined
          then FFirstVisible := i;

        FLastVisible := i;

        { VertScrollBar.Max }
        Inc(FShowingCount);
      end;

    end; { for RowIndex }

  finally
    EndUpdate;
  end;
end;

procedure TNxCells6.FindFirstVisible;
var
  i: Integer;
begin
  if FShowingCount = 0 then FFirstVisible := -1 else
    for i := 0 to Pred(RowCount) do
      if Row[i].Showing then
      begin
        FFirstVisible := i;

        { Done }
        Exit;
      end;
end;

procedure TNxCells6.FindLastVisible;
var
  i: Integer;
begin
  if FShowingCount = 0 then FLastVisible := -1 else
    for i := Pred(RowCount) downto 0 do
      if Row[i].Showing then
      begin
        FLastVisible := i;

        { Done }
        Exit;
      end;
end;

function TNxCells6.GetAbsoluteIndex: Integer;
begin
  Result := 0;
end;

function TNxCells6.GetAutoSort: Boolean;
begin
  Result := FAutoSort;
end;

function TNxCells6.GetControl: INxCellsControl;
begin
  Result := FControl;
end;

function TNxCells6.GetFirstVisible: Integer;
begin
  Result := FFirstVisible;
end;

function TNxCells6.GetCell(ACol, ARow: Integer): INxCell;
var
  CellsRow: INxCellsRow;
  LocationRow: INxRow;
begin
  { Default }
  Result := nil;

  { Set Location Prop. }
  FColIndex := ACol;
  FRowIndex := ARow;

  LocationRow := Row[FRowIndex];

  { Get Item }
  if Supports(LocationRow, INxCellsRow, CellsRow) then
  begin
    Result := CellsRow.Cells.Item[FColIndex];
  end;
end;

function TNxCells6.GetLastVisible: Integer;
begin
  Result := FLastVisible;
end;

function TNxCells6.GetLevel: Integer;
begin
  { Always -1 }
  Result := -1;
end;

function TNxCells6.GetLocation: TPoint;
begin
  Result := Point(FColIndex, FRowIndex);
end;

function TNxCells6.GetOwner: INxCells;
begin
  Result := Self as INxCells;
end;

function TNxCells6.GetRow(const Index: Integer): INxRow;
var
  AbsIndex: Integer;


  procedure QuickFindRow(Branches: INxBranches; Childrens: INxRows);
  var
    i: Integer;
    LocalIndex: Integer;
    ABranch: INxBranches;
    ARow: INxRow;
  begin
    if Result <> nil then Exit;

//    g := True;

  //  AbsIndex := Min(Index, Pred(Childrens.ChildRowCount));

 //   Delta := Branches.BranchRowCount - Childrens.ChildRowCount;

   // if Delta > 0 then
    begin

      for i := 0 to Pred(Branches.BranchCount) do
      begin
        if Result <> nil then Exit;

        ABranch := (Branches.Branch[i] as INxBranches);
        ARow := Branches.Branch[i];

        LocalIndex := ARow.Index;

        if AbsIndex <= LocalIndex then
        begin
          Result := INxRow(Childrens[AbsIndex]);

          Exit;
        end;


        if InRange(AbsIndex, Succ(LocalIndex), LocalIndex + ABranch.BranchRowCount) then
        begin
          Dec(AbsIndex);

          Dec(AbsIndex, LocalIndex);

          QuickFindRow(ABranch, INxRows(ARow));

        end;

        Dec(AbsIndex, ABranch.BranchRowCount);

      end;

      if Result = nil then
      begin
//        if AbsIndex < Childrens.ChildRowCount then

        Result := Childrens[AbsIndex];
      end;

    end;
  end;

begin
  { Seeking for row at absolute index }

  { Default }
  AbsIndex := Index;

  Result := nil;

  QuickFindRow(Self as INxBranches, Self as INxRows);

  FRowIndex := Index;
end;

function TNxCells6.GetRowCount: Integer;
begin
  Result := FRowCount;
end;

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

  { Exchange }
  if ToPos < FromPos then
  begin
    t := FromPos;
    FromPos := ToPos;
    ToPos := t;
  end;

  i := FromPos;

  while i < ToPos do
  begin
    if Row[i].Showing then Inc(Result);
    Inc(i);
  end;
end;

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

function TNxCells6.HasLocation: Boolean;
begin
  Result := (FColIndex <> -1) and (FRowIndex <> -1);
end;

procedure TNxCells6.HideAll(Func: TNxHideAllFunc);
var
  i: Integer;
  CurrentRow: INxRow;
begin
  { Reset First }
  FFirstVisible := Undefined;

  FShowingCount := 0;

  try
    { Don't do calc, everything will
      be reset. }
    BeginUpdate;

    for i := 0 to Pred(RowCount) do
    begin
      CurrentRow := Row[i];

      { Call proc }
      CurrentRow.Visible := Assigned(Func) and not Func(i);

      { Not hidden }
      if CurrentRow.Showing then
      begin
        if FFirstVisible = Undefined
          then FFirstVisible := i;

        FLastVisible := i;

        { VertScrollBar.Max }
        Inc(FShowingCount);
      end;

    end; { for RowIndex }

  finally
    EndUpdate;
  end;
end;

procedure TNxCells6.IncBranchRowCount(Delta: Integer);
begin
  Inc(FBranchRowCount, Delta);
end;

procedure TNxCells6.IncRowCount(const Delta: Integer);
begin
  Inc(FRowCount, Delta);
  Inc(FLastVisible, Delta);
end;

function TNxCells6.IndexOf(Row: INxRow): Integer;
begin
  Result := FChildRowsList.IndexOf(Row);
end;

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

function TNxCells6.IsDestroying: Boolean;
begin
  Result := FDestroying;
end;

function TNxCells6.IsFiltered(const AIndex: Integer): Boolean;
var
  i: Integer;
begin
  { Default, not filtered }
  Result := False;

  for i := 0 to Pred(Control.Columns.Count) do

    with Control.Columns[i] do

      if FilterEnabled then

        { Should Filter? }
        if not IsMatchFilter(Cell[i, AIndex]) then
        begin
          { Set as filtered }
          Result := True;

          { Stop }
          Exit;
        end;

end;

function TNxCells6.IsUnique(Cell: INxCell): Boolean;
var
  i: Integer;
begin
  { Default }
  IsUnique := True;

  for i := 0 to Pred(RowCount) do
    if Self.Cell[Cell.ColIndex, i].IsEqual(Cell) then
    begin
      IsUnique := False; { Not Unique }

      Break; { Done. }
    end;
end;

function TNxCells6.IsUpdating: Boolean;
begin
  Result := FUpdateCount > 0;
end;

procedure TNxCells6.MoveRow(const FromPos, ToPos: Integer);
var
  T: INxRow;
begin
  if not AutoSort then
  begin
    T := Row[FromPos];
    FChildRowsList.Remove(FChildRowsList[FromPos]);
    FChildRowsList.Insert(ToPos, T);

    { Re-sort required }
    FSorted := False;
  end;
end;

procedure TNxCells6.ProcessFilter(const Index: Integer);
begin
  Row[Index].Filtered := IsFiltered(Index);
end;

procedure TNxCells6.Recalculate;
begin
  FindFirstVisible;
  FindLastVisible;
end;

{$HINTS OFF}
procedure TNxCells6.RelocateRow(const Index: Integer);
var
  Col: Integer;
  Cell1: INxCell;
  Kind: TNxSortKind;
  SortType: TNxSortType;

  procedure SearchBetween(FromPos, ToPos: Integer);
  var
    Middle, CompareResult: Integer;
    Cell2: INxCell;
  begin
    Middle := Pred((FromPos - ToPos) div 2);

    Cell2 := Cell[Col, Middle];

    case SortType of
      stAlphabetic: CompareResult := UnicodeCompare(Cell1.AsString, Cell2.AsString);
      stBoolean: ;
      stCaseInsensitive: ;
      stCustom: ;
      stNumeric: ;
      stDate: ;
      stIP: ;
      stUnicodeAlphabetic: ;
    end;

    case Kind of
      skAscending: ;

    end;

  end;

begin
  if Assigned(Control.Columns.SortedColumn) then
  begin
    Col := Control.Columns.SortedColumn.Index;
    Kind := Control.Columns.SortedColumn.SortKind;
    SortType := Control.Columns.SortedColumn.SortType;

    { Cell to Compare }
    Cell1 := Cell[Col, Index];

    SearchBetween(0, Pred(RowCount));
  end;
end;
{$HINTS ON}

procedure TNxCells6.ResortBranches;

  function GreaterCompare(Row1, Row2: INxRow): Boolean;
  begin
    Result := Row1.Index >= Row2.Index;
  end;

  function LessCompare(Row1, Row2: INxRow): Boolean;
  begin
    Result := Row1.Index <= Row2.Index;
  end;

  procedure Exchange(Pos1, Pos2: Integer);
  var
    P: IInterface;
  begin
    P := FBranches[Pos1];
    FBranches[Pos1] := FBranches[Pos2];
    FBranches[Pos2] := P;
  end;

  function DivideAsc(Lft, Rgt: Integer): Integer;
  var
    i, j: Integer;
    Pivot: INxRow;
  begin
  	Pivot := FBranches[Lft + 1 + Random(Rgt - Lft)] as INxRow;

    i := Lft - 1;
    j := Rgt + 1;

    repeat

      repeat Inc(i) until LessCompare(Pivot, FBranches[i] as INxRow);
	    repeat Dec(j) until GreaterCompare(Pivot, FBranches[j] as INxRow);

      if FBranches[j] <> FBranches[i]
        then Exchange(i, j);

		until j <= i;

    if FBranches[j] <> FBranches[i]
      then Exchange(i, j);

    Result := i;
  end;

  procedure QuickSort(Lft, Rgt: Integer);
  var
    Middle: Integer;
  begin
    if Lft < Rgt then
    begin
      Middle := DivideAsc(Lft, Rgt);
      QuickSort(Lft, Middle - 1);
      QuickSort(Middle, Rgt);
    end;
  end;

begin
  if Assigned(FBranches) then
  begin
    QuickSort(0, FBranches.Count - 1);
  end;
end;

procedure TNxCells6.SetAutoSort(const Value: Boolean);
begin
  FAutoSort := Value;
end;

procedure TNxCells6.SetControl(const Value: INxCellsControl);
begin
  FControl := Value;
  FColCount := FControl.Columns.Count;
end;

procedure TNxCells6.SetFirstVisible(const Value: Integer);
begin
  FFirstVisible := Value;
end;

procedure TNxCells6.SetLocation(const Value: TPoint);
begin
  FColIndex := Value.X;
  FRowIndex := Value.Y;
end;

procedure TNxCells6.Sort(const Index: Integer; SortKind: TNxSortKind; Resort: Boolean);
var
	ALessCompFunc: TNxCompareFunc;
	AGreaterCompFunc: TNxCompareFunc;

  function GetChildCell(ACol, ARow: Integer): INxBase;
  begin
    Result := (Self.ChildRow[ARow] as INxCellsRow).Cells[ACol];
  end;

  procedure Exchange(Pos1, Pos2: Integer);
  var
    P: IInterface;
  begin
    P := FChildRowsList[Pos1];
    FChildRowsList[Pos1] := FChildRowsList[Pos2];
    FChildRowsList[Pos2] := P;
  end;

  procedure InverseSort;
  var
    i, Middle, Count: Integer;
  begin
    if RowCount > 0 then
    begin
      Middle := Pred(ChildRowCount div 2);

      { Optimize }
      Count := ChildRowCount;

      for i := 0 to Middle do Exchange(i, Pred(Count) - i);
    end;
  end;

  function DivideAsc(l, r: Integer; LessCompFunc,
  	GreaterCompFunc: TNxCompareFunc): Integer;
  var
    i, j: Integer;
    pivot: INxBase;
  begin
  	pivot := GetChildCell(Index, l + 1 + Random(r - l));

    i := l - 1;
    j := r + 1;

    repeat

      repeat Inc(i) until LessCompFunc(pivot, GetChildCell(Index, i));
	    repeat Dec(j) until GreaterCompFunc(pivot, GetChildCell(Index, j));

      if GetChildCell(Index, j) <> GetChildCell(Index, i)
        then Exchange(i, j)

		until j <= i;

    if GetChildCell(Index, j) <> GetChildCell(Index, i)
      then Exchange(i, j);

    Result := i;
  end;

  function DivideDesc(l, r: Integer; LessCompFunc,
  	GreaterCompFunc: TNxCompareFunc): Integer;
  var
    i, j: Integer;
    pivot: INxBase;
  begin
  	pivot := GetChildCell(Index, l + 1 + Random(r - l));
    i := l - 1;
    j := r + 1;
    repeat
      repeat Inc(i) until GreaterCompFunc(pivot, GetChildCell(Index, i));
	    repeat Dec(j) until LessCompFunc(pivot, GetChildCell(Index, j));
      if (GetChildCell(Index, j) <> GetChildCell(Index, i)) then Exchange(i, j)
		until j <= i;
    if (GetChildCell(Index, j) <> GetChildCell(Index, i)) then Exchange(i, j);
    Result := i;
  end;

  procedure Quicksort(Lft, Rgt: Integer);
  var
    Middle: Integer;
  begin
    if Lft < Rgt then
    begin
	    if FSortedColumn.SortKind = skAscending
      	then Middle := DivideAsc(Lft, Rgt, ALessCompFunc, AGreaterCompFunc)
      	else Middle := DivideDesc(Lft, Rgt, ALessCompFunc, AGreaterCompFunc);

      Quicksort(Lft, Middle - 1);
      Quicksort(Middle, Rgt);
    end;
  end;

begin
  FSortedColumn := Control.Columns[Index];

  case FSortedColumn.SortType of
    stAlphabetic:
    begin
      ALessCompFunc := AlphabeticLessCompare;
      AGreaterCompFunc := AlphabeticGreaterCompare;
    end;
    stBoolean:
    begin
      ALessCompFunc := BooleanLessCompare;
      AGreaterCompFunc := BooleanGreaterCompare;
    end;
    stCaseInsensitive:
    begin
      ALessCompFunc := CaseInsensitiveLessCompare;
      AGreaterCompFunc := CaseInsensitiveGreaterCompare;
    end;
    stCustom:
    begin
      ALessCompFunc := CustomLessCompare;
      AGreaterCompFunc := CustomGreaterCompare;
    end;
    stNumeric:
    begin
      ALessCompFunc := NumericLessCompare;
      AGreaterCompFunc := NumericGreaterCompare;
    end;
    stDate:
    begin
      ALessCompFunc := DateLessCompare;
      AGreaterCompFunc := DateGreaterCompare;
    end;
    stIP:
    begin
      ALessCompFunc := IPLessCompare;
      AGreaterCompFunc := IPGreaterCompare;
    end;
    stUnicodeAlphabetic:
    begin
      ALessCompFunc := AlphabeticUnicodeLessCompare;
      AGreaterCompFunc := AlphabeticUnicodeGreaterCompare;
    end;
  end;

  if (Index = FSortedCol) and FSorted and (SortKind <> FSortKind) and not Resort then
  begin
    FSortKind := SortKind;
    InverseSort;
  end else
  begin
    FSortKind := SortKind;
    Quicksort(0, Pred(ChildRowCount));
  end;

  ResortBranches;

  FSortedCol := Index;
  FSortKind := SortKind;
  FSorted := True;
end;

procedure TNxCells6.SwapRows(const FromPos, ToPos: Integer);
var
  P: IInterface;
begin
  P := FChildRowsList[FromPos];
  FChildRowsList[FromPos] := FChildRowsList[ToPos];
  FChildRowsList[ToPos] := P;
end;

procedure TNxCells6.UnfilterAll;
var
  i: Integer;
  ARow: INxRow;
begin
  for i := 0 to Pred(RowCount) do
  begin
    ARow := Row[i];

    if ARow.Filtered and not ARow.Covered then Inc(FShowingCount);
    ARow.Filtered := False;
  end;
end;

procedure TNxCells6.UnhideAll;
var
  i: Integer;
begin
  for i := 0 to Pred(RowCount) do Row[i].Show;
end;

{ TNxIncrementCell }

function TNxIncrementCell.GetAsInteger: Integer;
begin
  Result := RowIndex;
end;

function TNxIncrementCell.GetAsString: WideString;
begin
  Result := IntToStr(AsInteger);
end;

function TNxIncrementCell.IsEqual(Value: INxCell): Boolean;
begin
  Result := RowIndex = Value.AsInteger;
end;

procedure TNxIncrementCell.SetDummyValue;
begin
  FValue := '1';
end;

{ TNxFloatCell }

procedure TNxFloatCell.AssignValue(Source: INxBase);
begin
  AsFloat := Source.AsFloat;
end;

function TNxFloatCell.GetAsFloat: Double;
begin
  Result := FValue;
end;

function TNxFloatCell.GetAsInteger: Integer;
begin
  Result := Round(FValue);
end;

function TNxFloatCell.GetAsString: WideString;
begin
  Result := FloatToStr(FValue);
end;

procedure TNxFloatCell.InvertValue;
begin
  AsFloat := 0 - FValue;
end;

function TNxFloatCell.IsEqual(Value: INxCell): Boolean;
begin
  Result := FValue = Value.AsFloat;
end;

function TNxFloatCell.IsSet: Boolean;
begin
  Result := FValue <> 0;
end;

procedure TNxFloatCell.Revert;
begin
  AsFloat := 0;
end;

procedure TNxFloatCell.SetAsBoolean(const Value: Boolean);
begin
  AsFloat := IfThen(Value, 1, 0);
end;

procedure TNxFloatCell.SetAsFloat(const Value: Double);
begin
  if not Locked
    and (Value <> FValue) then
  begin
    FValue := Value;

    { Notify }
    Changed;
  end;
end;

procedure TNxFloatCell.SetAsInteger(const Value: Integer);
begin
  AsFloat := Round(Value);
end;

procedure TNxFloatCell.SetAsString(const Value: WideString);
begin
  AsFloat := StrToFloatDef(Value, 0);
end;

procedure TNxFloatCell.SetDummyValue;
begin
  FValue := 4571.4
end;

{ TNxBooleanCell }

procedure TNxBooleanCell.AssignValue(Source: INxBase);
begin
  AsBoolean := Source.AsBoolean;
end;

function TNxBooleanCell.GetAsBoolean: Boolean;
begin
  Result := FValue;
end;

function TNxBooleanCell.GetAsFloat: Double;
begin
  Result := IfThen(FValue, 1, 0);
end;

function TNxBooleanCell.GetAsString: WideString;
begin
  Result := BoolToStr(FValue, True);
end;

procedure TNxBooleanCell.InvertValue;
begin
  AsBoolean := not AsBoolean;
end;

function TNxBooleanCell.IsEqual(Value: INxCell): Boolean;
begin
  Result := FValue = Value.AsBoolean;
end;

function TNxBooleanCell.IsSet: Boolean;
begin
  Result := FValue;
end;

procedure TNxBooleanCell.Revert;
begin
  AsBoolean := False;
end;

procedure TNxBooleanCell.SetAsBoolean(const Value: Boolean);
begin
  if not Locked
    and Value <> FValue then
  begin
    FValue := Value;

    { Notify }
    Changed;
  end;
end;

procedure TNxBooleanCell.SetAsInteger(const Value: Integer);
begin
  AsBoolean := Value = 1;
end;

procedure TNxBooleanCell.SetAsString(const Value: WideString);
begin
  AsBoolean := StrToBoolEx(Value);
end;

procedure TNxBooleanCell.SetDummyValue;
begin
  FValue := True;
end;

{ TNxIntegerCell }

procedure TNxIntegerCell.AssignValue(Source: INxBase);
begin
  AsInteger := Source.AsInteger;
end;

function TNxIntegerCell.GetAsBoolean: Boolean;
begin
  Result := FValue = 1;
end;

function TNxIntegerCell.GetAsFloat: Double;
begin
  Result := FValue;
end;

function TNxIntegerCell.GetAsInteger: Integer;
begin
  Result := FValue;
end;

function TNxIntegerCell.GetAsString: WideString;
begin
  Result := IntToStr(FValue);
end;

procedure TNxIntegerCell.InvertValue;
begin
  AsInteger := 0 - FValue;
end;

function TNxIntegerCell.IsEqual(Value: INxCell): Boolean;
begin
  Result := FValue = Value.AsInteger;
end;

function TNxIntegerCell.IsSet: Boolean;
begin
  Result := FValue <> 0;
end;

procedure TNxIntegerCell.Revert;
begin
  AsInteger := 0;
end;

procedure TNxIntegerCell.SetAsBoolean(const Value: Boolean);
begin
  AsInteger := IfThen(Value, 1, 0);
end;

procedure TNxIntegerCell.SetAsInteger(const Value: Integer);
begin
  if not Locked
    and (Value <> FValue) then
  begin
    FValue := Value;

    { Notify }
    Changed;
  end;
end;

procedure TNxIntegerCell.SetAsString(const Value: WideString);
begin
  AsInteger := StrToIntDef(Value, 0);
end;

procedure TNxIntegerCell.SetDummyValue;
begin
  FValue := 6;
end;

{ TNxDateTimeCell }

procedure TNxDateTimeCell.AssignValue(Source: INxBase);
begin
  AsDateTime := Source.AsDateTime;
end;

constructor TNxDateTimeCell.Create;
begin
  inherited;
  FValue := 0;
end;

function TNxDateTimeCell.GetAsBoolean: Boolean;
begin
  Result := FValue > 0;
end;

function TNxDateTimeCell.GetAsDateTime: TDateTime;
begin
  Result := FValue;
end;

function TNxDateTimeCell.GetAsFloat: Double;
begin
  Result := FValue;
end;

function TNxDateTimeCell.GetAsString: WideString;
begin
  Result := DateTimeToStr(FValue);
end;

function TNxDateTimeCell.IsEqual(Value: INxCell): Boolean;
begin
  Result := FValue = Value.AsDateTime;
end;

function TNxDateTimeCell.IsSet: Boolean;
begin
  Result := AsDateTime <> 0;
end;

procedure TNxDateTimeCell.Revert;
begin
  AsDateTime := 0;
end;

procedure TNxDateTimeCell.SetAsBoolean(const Value: Boolean);
begin
  AsFloat := IfThen(Value, 1, 0);
end;

procedure TNxDateTimeCell.SetAsDateTime(const Value: TDateTime);
begin
  if not Locked
    and (Value <> FValue) then
  begin
    FValue := Value;

    { Notify }
    Changed;
  end;
end;

procedure TNxDateTimeCell.SetAsInteger(const Value: Integer);
begin
  AsDateTime := Value;
end;

procedure TNxDateTimeCell.SetAsString(const Value: WideString);
begin
  AsDateTime := StrToDateTimeDef(Value, 0)
end;

procedure TNxDateTimeCell.SetDummyValue;
begin
  FValue := Today;
end;

{ TNxStringCell }

procedure TNxStringCell.AssignValue(Source: INxBase);
begin
  AsString := Source.AsString;
end;

function TNxStringCell.GetAsBoolean: Boolean;
begin
  Result := StrToBoolEx(FValue);
end;

function TNxStringCell.GetAsFloat: Double;
begin
  Result := StrToFloatDef(FValue, 0);
end;

function TNxStringCell.GetAsInteger: Integer;
begin
  Result := StrToIntDef(FValue, 0);
end;

function TNxStringCell.GetAsString: WideString;
begin
  Result := FValue;
end;

function TNxStringCell.IsEqual(Value: INxCell): Boolean;
begin
  Result := Value.AsString = Self.AsString;
end;

function TNxStringCell.IsSet: Boolean;
begin
  Result := FValue <> EmptyStr;
end;

procedure TNxStringCell.Revert;
begin
  AsString := '';
end;

procedure TNxStringCell.SetAsBoolean(const Value: Boolean);
begin
  AsString := BoolToStr(Value, True);
end;

procedure TNxStringCell.SetAsInteger(const Value: Integer);
begin
  AsString := IntToStr(Value);
end;

procedure TNxStringCell.SetAsString(const Value: WideString);
begin
  if not Locked
    and (Value <> FValue) then
  begin
    FValue := Value;

    { Change Notif. }
    inherited;
  end;
end;

procedure TNxStringCell.SetDummyValue;
begin
  FValue := 'Next';
end;

procedure TNxCell6.SetList(const Value: TNxCellsList);
begin
  FList := Value;
end;

end.
