{

TCustomGrid, TDrawGrid and TStringGrid for Lazarus
Copyright (C) 2002  Jesus Reyes Aguilar.
email: jesusrmx@yahoo.com.mx

THIS CONTROL IS FREEWARE - USE AS YOU WILL

If you release sourcecode that uses this control, please credit me
or leave this header intact.  If you release a compiled application
that uses this code, please credit me somewhere in a little bitty
location so I can at least get bragging rights!
(Extract: from Tony's checkbook tracker, http://tony.maro.net)

This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

VERSION: 0.8.2
---------------
CHANGES		Demo Program
			
			Too many internal changes to be listed, scrollbars are now
			proportional to client/grid sizes (with goSmoothScroll option
			and almost proptional without it), removed OnEditor, etc.
			
ADDED		goSmoothScroll. (default) allows scroll the grid by pixel basis
            goThumbTracking. The grid acts always as if this is set, the 
			  value is ignored due to current implementation, however if
			  the user set it explicitly then, when the user is scrolling,
			  the focused cell will be following the scroll position.
			goTabs.
			goAlwaysShowEditor. Still need some working
			
NEW			AutoAdvance. Choose where the next cell position should go 
              if a RETURN or TABS(if enabled) is pressed
			
			  aaRight. Selected cell will go to the right
			  aaDown.  Selected cell will go to down
		  
BUGS		goEditing: 
			  - pressing RETURN doesn't edit the current cell
			  - pressing other keys doesn't start editing (need F2)
			goTabs:
			  - Shift-TAB doesn't work
			goAlwaysShowEditor: 
			  - Still working :)
			...
						

VERSION: 0.8.1
---------------
DATE: 28-DEC-2002

CHANGES -- Continued migrating properties from TCustomGrid to TDrawGrid
	   (onCellAttr, DefaultCellAttr, FixedColor, etc.)

FIXES   -- FGrid in TDrawGrid was not destroyed
        -- goEditing now works. I mean, you can now stop showing the 
	   editor at F2 (although editor needs more work)
           Default cell editor
	-- DefaultEditor parent is now TStringGrid
	-- Some fpc 1.1 issues (Mattias)
	

VERSION: 0.8.0
---------------
DATE: 20-DEC-2002

CHANGES Many internal changes (width,height removed from pcellsprop,
        fgrid removed from tcustomgrid, colRowToClientCellRect now
        uses col,row instead of point(col,row), cleaned DynamicArray,
        drawcells splitted in DrawFixedCells, DrawInteriorCells, DrawFocused
        so TStringGrid can implement ordered cell drawin and TCustomGrid
        draw cells is simpler, etc).

ADDED   ExchangeColRow(IsColumn: Boolean; Index, WithIndex: Integer);
        DeleteColRow(IsColumn:Boolea; Index:Integer);
        MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer);
        SortColRow(IsColumn: Boolean; Index: Integer);
        SortColRow(IsColumn: Boolean; Index,FromIndex,ToIndex: Integer);
        Property OnColRowMoved: TgridOperationEvent
        Property OnColRowDeleted: TgridOperationEvents
        Property OnColRowExchanged: TgridOperationEvents

ADDED   TcustomGrid derivatives can now replace sort algorithm overriding
        Sort method and using exchangeColRow as needed.
        

VERSION:  0.7.3
-----------------
DATE: 10-DIC-2002

ADDED goDblClickAutoSize to grid Options, Doubleclicking col's right edge
      automatically adjust column width (in TStringGrid).
      Implemented AutoAdjustColumn() and AutoAdjustColumns.

FIXED col, row increment after grid.clear don't show the grid ( if
      fixed rows-cols = 0 )
      
ADDED version info to saved grid files.

ADDED NEW DEMO: mysql_query. A program that connects to MySQL and shows query
      results in a grid which you can save and load.
      

VERSION:  0.7.2
-----------------
DATE: 5-DIC-2002
FIXED a bug that prevents col, and row sizing. MouseDown uses only Left clicks

VERSION:  0.7.1
-----------------
DATE: 3-DIC-2002
ADDED LoadFromFile and SaveToFile to XML file.
  SaveOptions   (soDesign,soPosition,soAttributes,soContent);
  soDesign:     Save & Load ColCount,RowCount,FixedCols,FixedRows,
                ColWidths, RowHeights and Options (TCustomGrid)
  soPosition:   Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)
  soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TDrawGrid)
  soContent:    Save & Load Text (TStringGrid)
  
ADDED TCustomgrid.Clear.
                Wipe completly the grid.
ADDED goRelaxedRowSelect option
                You can see focused cell and navigate freely if goRowSelect is
                set.
FIXED Crash on reducing Rowcount


VERSION:  0.7.0
-----------------
RELEASE DATE: 30-NOV-2002

This unit version provides TCustomGrid, TDrawGrid and TStringGrid for lazarus
from the component user perpective there should be to much differences.
This release has only basic editing support.

Old Features:
  Almost all that T*Grid can do.

New Features :

  OnHeaderClick:
              Detect clicks on Row(Column) Headers, it uses a property: DragDx
              as a threshold in order to detect Col(Row) moving or clicking.
              
  OnCellAttr: In this Event You can easily customize the grid.
  OnDrawCell: Draw your specific cells here and then call .DefaultDrawCell
              to let the grid draw other cells.
  SortColumn,
  SortRow:    Sorting capabilities are built! you need only write one
              OnCompareCells handler to do your custom sorting needs.
              
  Exposed: DeleteColumn, DeleteRow, MoveColumn, MoveRow.
  
  RowAttr[],RowColor[],RowFontColor[],RowAlign[]
  ColAttr[],ColColor[],ColFontColor[],ColAlign[]
  CellAttr[],CellColor[],CellFontColor[],CellAlign[]

  GridLineStyle, FocusColor, etc.
  
Bugs:

  + Editor: it has a unneeded feature "auto cell filling" :)
    
  others.
}

{.$Define dbgScroll}

unit Grids;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LCLProc, LCLType, LCLLinux, Controls, GraphType, Graphics,
  Forms, VCLGlobals, DynamicArray, LMessages, Messages, XMLCfg, StdCtrls,
  LResources;

Const
  //GRIDFILEVERSION = 1; // Original
  GRIDFILEVERSION = 2; // Introduced goSmoothScroll

Const
  GM_SETVALUE = LM_USER + 100;
  GM_GETVALUE = LM_USER + 101;
  GM_SETGRID  = LM_USER + 102;

Const
  CA_LEFT     =   $1;
  CA_CENTER   =   $2;
  CA_RIGHT    =   $4;
  CL_TOP      =   $8;
  CL_CENTER   =   $10;
  CL_BOTTOM   =   $20;


Type
  TGridOption = (
    goFixedVertLine,      // Ya
    goFixedHorzLine,      // Ya
    goVertLine,           // Ya
    goHorzLine,           // Ya
    goRangeSelect,        // Ya
    goDrawFocusSelected,  // Ya
    goRowSizing,          // Ya
    goColSizing,          // Ya
    goRowMoving,          // Ya
    goColMoving,          // Ya
    goEditing,            // Ya
    goTabs,               // Ya
    goRowSelect,          // Ya
    goAlwaysShowEditor,   // Ya
    goThumbTracking,      // ya
    // Additional Options
    goColSpanning,        // Enable cellextent calcs
    goRelaxedRowSelect,   // User can see focused cell on goRowSelect
    goDblClickAutoSize,   // dblclicking columns borders (on hdrs) resize col.
    goSmoothScroll        // Switch scrolling mode (pixel scroll is by default)
  );
  TGridSaveOptions = (
    soDesign,
    soAttributes,
    soContent,
    soPosition
  );

  TGridOptions = set of TGridOption;
  TGridDrawState = set of (gdSelected, gdFocused, gdFixed);
  TGridState =
    (gsNormal, gsSelecting, gsRowSizing, gsColSizing,gsRowMoving,gsColMoving);
  TGridZone = (gzNormal, gzFixedCols, gzFixedRows, gzFixedCells);
  TSaveOptions = Set of TGridSaveOptions;
  
  TUpdateOption = (uoNone, uoQuick, uoFull);
  TAutoAdvance = (aaDown,aaRight);
  

Type
  PCellAttr=^TCellAttr;
  TCellAttr=Record
    Color: TColor;
    FontColor: TColor;
    FontFace: Pchar;
    TextStyle: TTextStyle;
  End;
      
  PCellProps= ^TCellProps;
  TCellProps=Record
    Attr: PCellAttr;
    Data: TObject;
    Text: pchar;
  End;
  
Type
  TCustomGrid = Class;
      
  PGridMessage=^TGridMessage;
  TGridMessage=Record
    Grid: TCustomGrid;
    Col,Row: Integer;
    Value: String;
  End;

  { Default cell editor for TStringGrid }
  TStringCellEditor=Class(TCustomEdit)
  Private
    FGrid: TCustomGrid;
    FLeaving: Boolean;
  Protected
    Procedure doExit; Override;
    procedure KeyDown(var Key : Word; Shift : TShiftState); Override;
    Procedure msg_SetValue(Var Msg: TLMessage); Message GM_SETVALUE;
    Procedure msg_GetValue(Var Msg: TLMessage); Message GM_GETVALUE;
    Procedure msg_SetGrid(Var Msg: TLMessage); Message GM_SETGRID;
  End;
  

  TOnDrawCell =
    Procedure(Sender: TObject; Col, Row: Integer; Rect: TRect;
              aState:TGridDrawState) of Object;
  TOnBeforeSelectionEvent =
    Procedure(Sender: TObject; Col, Row: Integer;
              Var CanChange: Boolean) of Object;
  TOnSelectEvent =
    Procedure(Sender: TObject; Col,Row: Integer) of Object;
  TOnCellAttrEvent =
    Procedure(Sender:TObject; const Col, Row:Integer; aState: TGridDrawState;
              Var CellProps: TCellAttr) of Object;
  TGridOperationEvent =
    Procedure (Sender: TObject; IsColumn:Boolean;
               sIndex,tIndex: Integer) of object;
  THdrEvent =
    Procedure(Sender: TObject; IsColumn: Boolean; Index: Integer) of Object;
  TOnCompareCells =
    Function (Sender: TObject; Acol,ARow,Bcol,BRow: Integer): Integer of Object;

  TVirtualGrid=Class
    Private
      FColCount: Integer;
      FRowCount: Integer;
      FCells, FCols, FRows: TArray;
      function GetCells(Col, Row: Integer): PCellProps;
      Function Getrows(Row: Integer): Pcellprops;
      Function Getcols(Col: Integer): Pcellprops;
      procedure SetCells(Col, Row: Integer; const AValue: PCellProps);
      Procedure Setrows(Row: Integer; Const Avalue: Pcellprops);
      Procedure Setcolcount(Const Avalue: Integer);
      Procedure Setrowcount(Const Avalue: Integer);
      Procedure Setcols(Col: Integer; Const Avalue: Pcellprops);
      Procedure DisposeCell(Var P: PcellProps);
    Protected
      Function GetDefaultCell: PcellProps;
      Procedure doDestroyItem(Sender: TObject; Col,Row:Integer; Var Item: Pointer);
      Procedure doNewItem(Sender: TObject; Col,Row:Integer; Var Item: Pointer);
      Procedure DeleteColRow(IsColumn: Boolean; Index: Integer);
      Procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer);
      Procedure ExchangeColRow(IsColumn:Boolean; Index,WithIndex: Integer);
    Public
      Constructor Create;
      Destructor Destroy; Override;
      Procedure Clear;
      Property ColCount: Integer Read FColCount Write SetColCount;
      Property RowCount: Integer Read FRowCount Write SetRowCount;
      
      Property Celda[Col,Row: Integer]: PCellProps Read GetCells Write SetCells;
      Property Cols[Col: Integer]: PCellProps Read GetCols Write SetCols;
      property Rows[Row: Integer]: PCellProps Read GetRows Write SetRows;
  End;

      
  Type
    TGridCoord = Type TPoint;
    TGridRect  = Type TRect;

    TGridDataCache=Record
      FixedWidth: Integer;    // Sum( Fixed ColsWidths[i] )
      FixedHeight: Integer;   // Sum( Fixed RowsHeights[i] )
      GridWidth: Integer;     // Sum( ColWidths[i] )
      GridHeight: Integer;    // Sum( RowHeights[i] )
      ClientWidth: Integer;   // Width-VertScrollbar.Size
      ClientHeight: Integer;  // Height-HorzScrollbar.Size
      ScrollWidth: Integer;   // ClientWidth-FixedWidth
      ScrollHeight: Integer;  // ClientHeight-FixedHeight
      VisibleGrid: TRect;     // Visible non fixed rectagle of cells
      MaxClientXY: Tpoint;    // VisibleGrid.BottomRight coordinates
      ValidGrid: Boolean;     // true if there is something to show
      AccumWidth: TList;       // Accumulated width per column
      AccumHeight: TList;     // Accumulated Height per column
      HScrDiv,VScrDiv: Double;      // Tx const for ThumbTracking
      TLColOff,TLRowOff: Integer;   // TopLeft Offset in pixels
    End;


Type
  //TCustomGrid=Class(TScrollBox)
  //TCustomGrid=Class(TCustomControl)
  TCustomGrid=Class(TScrollingWinControl)
  Private
    FAutoAdvance: TAutoAdvance;
    FDefaultDrawing: Boolean;
    FEditor: TWinControl;
    FOnCompareCells: TOnCompareCells;
    FGridLineStyle: TPenStyle;
    FGridLineWidth: Integer;
    FDefColWidth, FDefRowHeight: Integer;
    FCol,FRow, FFixedCols, FFixedRows: Integer;
    FGridLineColor: TColor;
    
    FFocusColor: TColor;
    FCols,FRows: TList;
    
    FsaveOptions: TSaveOptions;
    FScrollBars: TScrollStyle;
    FSelectActive: Boolean;
    FTopLeft: TPoint;
    FSplitter, FPivot: TPoint;
    FRange: TRect;
    FDragDx: Integer;
    FMoveLast: TPoint;
    
    FUpdateCount: Integer;
    FUpdateScrollBarsCount: Integer;

    
    // Cached Values
    FGCache: TGridDataCache;
    // Options
    FOptions: TGridOptions;

    FOnDrawCell: TOnDrawcell;
    FOnBeforeSelection: TOnBeforeSelectionEvent;
    FOnSelection: TOnSelectEvent;
    FOnTopLeftChange: TNotifyEvent;
    
    Procedure AdjustCount(IsColumn:Boolean; OldValue, NewValue:Integer);
    Procedure CheckFixedCount(aCol,aRow,aFCol,aFRow: Integer);
    Procedure CacheVisibleGrid;
    function GetSelection: TGridRect;
    Function doColSizing(X,Y: Integer): Boolean;
    Function doRowSizing(X,Y: Integer): Boolean;
    Procedure doColMoving(X,Y: Integer);
    Procedure doRowMoving(X,Y: Integer);
    
    Function OffsetToColRow(IsCol,Fisical:Boolean; Offset:Integer; Var Rest:Integer): Integer;
    Function ColRowToOffset(IsCol,Fisical:Boolean; Index: Integer; Var Ini,Fin:Integer): Boolean;

    function GetLeftCol: Integer;
    function GetTopRow: Longint;
    function GetVisibleColCount: Integer;
    function GetVisibleRowCount: Integer;
    Function Getrowheights(Arow: Integer): Integer;
    Function Getcolcount: Integer;
    Function Getrowcount: Integer;
    Function Getcolwidths(Acol: Integer): Integer;
    Function GetVisibleGrid: TRect;
    Procedure MyTextRect(R: TRect; Offx,Offy:Integer; S:String; Ts: TTextStyle);
    Function ScrollToCell(Const aCol,aRow: Integer): Boolean;
    Function ScrollGrid(Relative:Boolean; DCol,DRow: Integer): TPoint;
    procedure SetDefaultDrawing(const AValue: Boolean);
    procedure SetEditor(const AValue: TWinControl);
    procedure SetFocusColor(const AValue: TColor);
    procedure SetGridLineStyle(const AValue: TPenStyle);
    procedure SetSelectActive(const AValue: Boolean);
    procedure SetSelection(const AValue: TGridRect);
    procedure SetFixedCols(const AValue: Integer);
    procedure SetFixedRows(const AValue: Integer);
    procedure SetGridLineColor(const AValue: TColor);
    procedure SetGridLineWidth(const AValue: Integer);
    procedure SetLeftCol(const AValue: Integer);
    procedure SetOptions(const AValue: TGridOptions);
    procedure SetScrollBars(const AValue: TScrollStyle);
    procedure SetTopRow(const AValue: Integer);
    Procedure Setrowheights(Arow: Integer; Avalue: Integer);
    Procedure Setcolwidths(Acol: Integer; Avalue: Integer);
    Procedure SetColCount(Valor: Integer);
    Procedure SetRowCount(Valor: Integer);
    Procedure SetDefColWidth(Valor: Integer);
    Procedure SetDefRowHeight(Valor: Integer);
    Procedure SetCol(Valor: Integer);
    Procedure SetRow(Valor: Integer);
    Procedure doTopleftChange(DimChg: Boolean);
    Procedure TryScrollTo(aCol,aRow: integer);
    Procedure UpdateScrollBarPos(Which: TControlScrollbar);
    procedure VisualChange;
    Procedure WMEraseBkgnd(var Message: TLMEraseBkgnd); message LM_ERASEBKGND;
    Procedure WMHScroll(var Message : TLMHScroll); message LM_HScroll;
    Procedure WMVScroll(var Message : TLMVScroll); message LM_VScroll;
    procedure WMSize(var Msg: TWMSize); message WM_SIZE;

  protected
    fGridState: TGridState;
    Procedure AutoAdjustColumn(aCol: Integer); Virtual;
    function  CellRect(ACol, ARow: Integer): TRect;
    
    Procedure ColRowDeleted(IsColumn: Boolean; Index: Integer); Dynamic;
    Procedure ColRowExchanged(IsColumn: Boolean; Index,WithIndex: Integer); Dynamic;
    Procedure ColRowMoved(IsColumn: Boolean; FromIndex,ToIndex: Integer); Dynamic;

    procedure ColWidthsChanged; dynamic;
    procedure DblClick; Override;
    Procedure doExit; Override;
    Procedure doEnter; Override;

    Procedure DrawEdges;
    Procedure DrawBackGround; Virtual;
    Procedure DrawFixedCells; Virtual;
    Procedure DrawInteriorCells; Virtual;
    Procedure DrawFocused; Virtual;
    Procedure DrawFocusRect(aCol,aRow:Integer; ARect:TRect; aState:TGridDrawstate); Virtual;
    Procedure DrawColRowMoving;
    procedure DrawCell(aCol,aRow:Integer; aRect:TRect; aState:TGridDrawState); Virtual;
    
    Procedure DrawByRows; Virtual;
    Procedure DrawRow(aRow: Integer); Virtual;

    Procedure HeaderClick(IsColumn: Boolean; Index: Integer); Dynamic;
    procedure InvalidateCol(ACol: Integer);
    procedure InvalidateRow(ARow: Integer);
    Procedure InvalidateCell(aCol, aRow: Integer);
    Procedure InvalidateGrid;
    procedure KeyDown(var Key : Word; Shift : TShiftState); Override;
    procedure KeyUp(var Key : Word; Shift : TShiftState); Override;
    Procedure LoadContent(cfg: TXMLConfig); Virtual;
    Procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
    Procedure MouseMove(Shift: TShiftState; X,Y: Integer);Override;
    Procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
    Function  MoveExtend(Relative: Boolean; DCol, DRow: Integer): Boolean;
    Procedure MoveSelection; Virtual;
    Procedure DrawCellGrid(Rect: TRect; aCol,aRow: Integer; astate: TGridDrawState);
    Procedure Paint; override;
    Procedure ResetOffset(chkCol, ChkRow: Boolean);
    procedure RowHeightsChanged; dynamic;
    function  SelectCell(ACol, ARow: Integer): Boolean; virtual;
    procedure SizeChanged(OldColCount, OldRowCount: Integer); dynamic;
    Procedure Sort(ColSorting: Boolean; Index,IndxFrom,IndxTo:Integer); virtual;
    Procedure TopLeftChanged; dynamic;
    Procedure SaveContent(cfg: TXMLConfig); Virtual;
    
    // Editor support
  Protected
    FEditorHiding: Boolean;
    FEditorKey: Boolean;
    Procedure CancelEditor; Virtual;
    Procedure GetEditorValue; Virtual;
    Procedure HideEditor;
    Procedure SetEditorValue; Virtual;
    Procedure ShowEditor;
    Procedure PosEditor;
    Procedure ExitEditor(Sender: TWinControl);
    procedure EditorKeyDown(var Key : Word; Shift : TShiftState);

    
    Property AutoAdvance: TAutoAdvance Read FAutoAdvance Write FAutoAdvance default aaRight;
    Property ColWidths[aCol: Integer]: Integer Read GetColWidths Write SetColWidths;
    Property ColCount: Integer Read GetColCount Write SetColCount;
    Property Col: Integer Read FCol Write SetCol;
    Property DefaultColWidth: Integer Read FDefColWidth Write SetDefColWidth;
    Property DefaultRowHeight: Integer Read FDefRowHeight write SetDefRowHeight;
    property DefaultDrawing: Boolean read FDefaultDrawing write SetDefaultDrawing default True;
    Property DragDx: Integer read FDragDx Write FDragDx;
    Property Editor: TWinControl Read FEditor Write SetEditor;
    Property FixedCols: Integer read FFixedCols write SetFixedCols;
    Property FixedRows: Integer read FFixedRows write SetFixedRows;
    Property FocusColor: TColor read FFocusColor write SetFocusColor;
    Property GridLineColor: TColor read FGridLineColor write SetGridLineColor;
    Property GridLineStyle: TPenStyle read FGridLineStyle write SetGridLineStyle;
    property GridWidth: Integer read FGCache.GridWidth;
    property GridHeight: Integer read FGCache.GridHeight;
    property GridLineWidth: Integer read FGridLineWidth write SetGridLineWidth default 1;
    property LeftCol:Integer read GetLeftCol write SetLeftCol;
    property Options: TGridOptions read FOptions write SetOptions;
    property ScrollBars: TScrollStyle read FScrollBars write SetScrollBars;
    Property RowCount: Integer Read GetRowCount Write SetRowCount;
    Property Row: Integer Read FRow Write SetRow;
    Property SaveOptions: TSaveOptions Read FsaveOptions Write FSaveOptions;
    Property SelectActive: Boolean read FSelectActive write SetSelectActive;
    property Selection: TGridRect read GetSelection write SetSelection;
    property TopRow: Integer read GetTopRow write SetTopRow;
    Property RowHeights[aRow: Integer]: Integer Read GetRowHeights Write SetRowHeights;
    property VisibleColCount: Integer read GetVisibleColCount;
    property VisibleRowCount: Integer read GetVisibleRowCount;

    Property OnDrawCell: TOnDrawCell Read FOnDrawCell Write FOnDrawCell;
    Property OnBeforeSelection: TOnBeforeSelectionEvent Read fOnBeforeSelection Write fOnBeforeSelection;
    Property OnSelection: TOnSelectEvent Read fOnSelection Write fOnSelection;
    Property OnTopLeftChange: TNotifyEvent Read FOnTopLeftChange Write FOnTopLeftChange;
    Property OnCompareCells: TOnCompareItem Read FOnCompareCells Write FOnCompareCells;

  Public
    Constructor Create(AOwner: TComponent); Override;
    Destructor Destroy; override;
    Procedure Invalidate; Override;
    
    { Exposed procs }
    Procedure DeleteColRow(IsColumn: Boolean; Index: Integer);
    Procedure ExchangeColRow(IsColumn: Boolean; Index, WithIndex: Integer);
    Procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer);
    Procedure SortColRow(IsColumn: Boolean; Index:Integer); Overload;
    Procedure SortColRow(IsColumn: Boolean; Index,FromIndex,ToIndex: Integer); Overload;
    
    Procedure BeginUpdate;
    Procedure AutoAdjustColumns;
    Procedure Clear;
    Procedure EndUpdate(UO: TUpdateOption); Overload;
    Procedure EndUpdate(FullUpdate: Boolean); Overload;
    Procedure LoadFromFile(FileName: String);
    Procedure SaveToFile(FileName: String);

    Function ColRowToClientCellRect(aCol, aRow: Integer): TRect;
    Function MouseToCell(Mouse: TPoint): TPoint;
    Function MouseToLogcell(Mouse: TPoint): TPoint;
    Function MouseToGridZone(X,Y: Integer; CellCoords: Boolean): TGridZone;
    Function IsCellVisible(aCol, aRow: Integer): Boolean;
    Function IscellSelected(aCol,aRow: Integer): Boolean;
  End;
      

  TDrawGrid=Class(TCustomGrid)
  Private
    FCellAttr: TCellAttr; // Attibute used to render Cell
    FOnColRowDeleted: TgridOperationEvent;
    FOnColRowExchanged: TgridOperationEvent;
    FOnColRowMoved: TgridOperationEvent;
    FOnHeaderClick: THdrEvent;
    FGrid: TVirtualGrid;
    FDefCellAttr, FdefSelCellAttr, FdefFixedCellAttr: TCellAttr;
    FOnCellAttr: TOnCellAttrEvent;


    function GetCellAlign(ACol, ARow: Integer): Integer;
    function GetCellAttr(ACol, ARow: Integer): TCellAttr;
    function GetCellColor(ACol, ARow: Integer): TColor;
    function GetCellFontCOlor(ACol, ARow: Integer): TColor;
    function GetColAlign(aCol: Integer): Integer;
    function GetColAttr(aCol: Integer): TCellAttr;
    function GetColColor(aCol: Integer): TColor;
    function GetColFontColor(aCol: Integer): TColor;
    function GetFixedColor: TColor;
    function GetRowAlign(aRow: Integer): Integer;
    function GetRowAttr(aRow: Integer): TCellAttr;
    function GetRowColor(aRow: Integer): TColor;
    function GetRowFontColor(aRow: Integer): TColor;
    procedure SetCellAlign(ACol, ARow: Integer; const AValue: Integer);
    procedure SetCellAttr(ACol, ARow: Integer; const AValue: TCellAttr);
    procedure SetCellColor(ACol, ARow: Integer; const AValue: TColor);
    procedure SetCellFontCOlor(ACol, ARow: Integer; const AValue: TColor);
    procedure SetColAlign(aCol: Integer; const AValue: Integer);
    procedure SetColAttr(aCol: Integer; const AValue: TCellAttr);
    procedure SetColColor(aCol: Integer; const AValue: TColor);
    procedure SetColFontColor(aCol: Integer; const AValue: TColor);
    procedure SetDefaultCellAttr(const AValue: TCellAttr);
    procedure SetFixedColor(const AValue: TColor);
    procedure SetRowAlign(aRow: Integer; const AValue: Integer);
    procedure SetRowAttr(aRow: Integer; const AValue: TCellAttr);
    procedure SetRowColor(aRow: Integer; const AValue: TColor);
    procedure SetRowFontColor(aRow: Integer; const AValue: TColor);

  Protected
    Procedure CalcCellExtent(acol, aRow: Integer; Var aRect: TRect); Virtual;
    Procedure DrawCell(aCol,aRow: Integer; aRect: TRect; aState:TGridDrawState); Override;
    Procedure DrawFocusRect(aCol,aRow: Integer; ARect: TRect; aState: TGridDrawstate); Override;
    Procedure ColRowExchanged(IsColumn: Boolean; Index,WithIndex: Integer); Override;
    Procedure ColRowDeleted(IsColumn: Boolean; Index: Integer); Override;
    Procedure ColRowMoved(IsColumn: Boolean; FromIndex,ToIndex: Integer); Override;
    Procedure HeaderClick(IsColumn: Boolean; Index: Integer); Override;
    procedure SizeChanged(OldColCount, OldRowCount: Integer); Override;
    Procedure SaveContent(cfg: TXMLConfig); Override;
    Procedure LoadContent(Cfg: TXMLConfig); Override;
  Public
    // to easy user call
    Constructor Create(AOwner: TComponent); Override;
    Destructor Destroy; override;

    Procedure DefaultDrawCell(aCol,aRow: Integer; Var aRect: TRect; aState:TGridDrawState);
    property Canvas;

    Property ColAttr[aCol: Integer]: TCellAttr read GetColAttr write SetColAttr;
    Property ColColor[aCol: Integer]: TColor read GetColColor write SetColColor;
    Property ColFontColor[aCol: Integer]: TColor read GetColFontColor write SetColFontColor;
    Property ColAlign[aCol: Integer]: Integer read GetColAlign write SetColAlign;

    property FixedColor: TColor read GetFixedColor write SetFixedColor default clBtnFace;

    Property RowAttr[aRow: Integer]: TCellAttr read GetRowAttr write SetRowAttr;
    Property RowColor[aRow: Integer]: TColor read GetRowColor write SetRowColor;
    Property RowFontColor[aRow: Integer]: TColor read GetRowFontColor write SetRowFontColor;
    Property RowAlign[aRow: Integer]: Integer read GetRowAlign write SetRowAlign;

    Property CellAttr[ACol,ARow:Integer]: TCellAttr read GetCellAttr write SetCellAttr;
    property CellColor[ACol, ARow:Integer]: TColor read GetCellColor write SetCellColor;
    Property CellFontCOlor[ACol,ARow:Integer]: TColor read GetCellFontCOlor write SetCellFontCOlor;
    Property CellAlign[ACol,ARow: Integer]: Integer read GetCellAlign write SetCellAlign;
    Property DefaultCellAttr: TCellAttr read fDefCellAttr write SetDefaultCellAttr;

    property Col;
    property ColWidths;
    //property EditorMode;
    property GridHeight;
    property GridWidth;
    property LeftCol;
    property Selection;
    property Row;
    property RowHeights;
    Property GridLineColor;
    Property GridLineStyle;
    Property FocusColor;
    Property SaveOptions;
    //property TabStops;
    property TopRow;
  Published
    property Align;
    property Anchors;
    Property AutoAdvance;
    //property BiDiMode;
    //property BorderStyle;
    property Color default clWindow;
    property ColCount;
    //property Constraints;
    property Ctl3D;
    property DefaultColWidth;
    property DefaultRowHeight;
    property DefaultDrawing;
    //property DragCursor;
    //property DragKind;
    //property DragMode;
    property Enabled;
    property FixedCols;
    property RowCount;
    property FixedRows;
    property Font;
    property GridLineWidth;
    property Options;
    //property ParentBiDiMode;
    //property ParentColor;
    //property ParentCtl3D;
    //property ParentFont;
    //property ParentShowHint;
    //property PopupMenu;
    //property ScrollBars;
    //property ShowHint;
    //property TabOrder;
    //property TabStop;
    property Visible;
    property VisibleColCount;
    property VisibleRowCount;

    Property OnDrawCell;
    Property OnBeforeSelection;
    Property OnSelection;
    Property OnCellAttr: TOnCellAttrEvent read fonCellAttr Write fOnCellAttr;
    Property OnTopleftChange;
    Property OnCompareCells;
    Property OnColRowMoved: TgridOperationEvent Read FOnColRowMoved Write FOnColRowMoved;
    Property OnColRowDeleted: TgridOperationEvent Read FOnColRowDeleted Write FOnColRowDeleted;
    Property OnColRowExchanged: TgridOperationEvent Read FOnColRowExchanged Write FOnColRowExchanged;
    Property OnHeaderClick: THdrEvent Read FOnHeaderClick Write FOnHeaderClick;
  End;
  
  TStringGrid = class(TDrawGrid)
    private
      FDefEditor: TStringCellEditor;
      function GetCells(ACol, ARow: Integer): string;
      function GetCols(Index: Integer): TStrings;
      function GetObjects(ACol, ARow: Integer): TObject;
      function GetRows(Index: Integer): TStrings;
      procedure SetCells(ACol, ARow: Integer; const AValue: string);
      procedure SetCols(Index: Integer; const AValue: TStrings);
      procedure SetObjects(ACol, ARow: Integer; AValue: TObject);
      procedure SetRows(Index: Integer; const AValue: TStrings);
    protected
      Procedure AutoAdjustColumn(aCol: Integer); Override;
      Procedure CalcCellExtent(acol, aRow: Integer; Var aRect: TRect); Override;
      Procedure DrawCell(aCol,aRow: Integer; aRect: TRect; aState:TGridDrawState); Override;
      Procedure GetEditorValue; Override;
      Procedure SetEditorValue; Override;
      Procedure SaveContent(cfg: TXMLConfig); Override;
      Procedure LoadContent(cfg: TXMLConfig); Override;
      Procedure DrawInteriorCells; Override;
      Procedure MoveSelection; Override;
    public
      Constructor Create(AOWner: TComponent); Override;
      Destructor Destroy; Override;
      property Cells[ACol, ARow: Integer]: string read GetCells write SetCells;
      property Cols[Index: Integer]: TStrings read GetCols write SetCols;
      property Objects[ACol, ARow: Integer]: TObject read GetObjects write SetObjects;
      property Rows[Index: Integer]: TStrings read GetRows write SetRows;
  End;
  
  Procedure DebugRect(S:String; R:TRect);
  Procedure DebugPoint(S:String; P:TPoint);
      

implementation

{
// Dibujar una linea en el borde izquierdo de esta celda
Dc:=GetDC(handle);
Pen:=CreatePen(PS_SOLID, 3, clRed);
OldPen:=SelectObject(Dc, Pen);
MoveToEx(Dc, R.left, 0, nil);
LineTo(Dc, R.Left, FGCache.MaxClientXY.Y);
SelectObject(Dc, OldPen);
DeleteObject(Pen);
ReleaseDC(Handle, Dc);
FMoveLast:=P;
}

Function PointIgual(Const P1,P2: TPoint): Boolean;
begin
  result:=(P1.X=P2.X)And(P1.Y=P2.Y);
End;
Function RectIgual(Const R1,R2: TRect): Boolean;
begin
  Result:=CompareMem(@R1,@R2, SizeOf(R1));

End;
Function Min(Const I,J: Integer): Integer;
begin
  If I<J then Result:=I

  Else        Result:=J;
End;
Function Max(Const I,J: Integer): Integer;
begin
  If I>J then Result:=I

  Else        Result:=J;
End;
Function NormalizarRect(Const R:TRect): TRect;
Begin
  Result.Left:=Min(R.Left, R.Right);

  Result.Top:=Min(R.Top, R.Bottom);
  Result.Right:=Max(R.Left, R.Right);
  Result.Bottom:=Max(R.Top, R.Bottom);
End;

function GetDefaultCellAttr: TCellAttr;
begin
  With Result do begin
    FontColor:=clBlack;
    Color:=clWindow;
    FontFace:=nil;
    With TextStyle do Begin
      Alignment:=taLeftJustify;
      Layout:=tlCenter;
      SingleLine:=False;
      WordBreak:=False;
      Opaque:=False;
      Clipping:=False;
    End;
  End;
end;

procedure DebugAttr(Msg: String; Attr: TCellAttr);
Begin
  With Attr do begin
    WriteLn(Msg);
    WriteLn('Color=',ColorToString(Attr.Color));
    WriteLn('FontColor=',ColorToString(Attr.FontColor));
    With TextStyle do begin
      WriteLn('Textstyle.Alignment=', Ord(Alignment));
      WriteLn('TextStyle.Layout=',Ord(Layout));
      WriteLn('TextStyle.SingleLine=',Singleline);
      WriteLn('TextStyle.Clipping=',Clipping);
      WriteLn('TextStyle.Wordbreak=',WordBreak);
      WriteLn('TextStyle.Opaque=',Opaque);
      WriteLn('TextStyle.SystemFont',systemFont);
    End;
  End;
End;
Function LoadCellAttrFromXMLPath(Cfg: TXMLConfig; Path: String): TCellAttr;
begin
  Result:=GetDefaultCellAttr;

  With Result do begin
    Color:=StringToColor(Cfg.GetValue(Path+'/color', ColorToString(Color)));
    FontColor:=
      StringToColor(Cfg.GetValue(Path+'/fontcolor',ColorToString(FontColor)));
    with TextStyle do begin
      Alignment:=
        TAlignment(cfg.GetValue(Path+'/textstyle/alignment/value',
          Integer(Alignment)));
      Layout:=
        TTextLayout(    cfg.GetValue(Path+'/textstyle/layout/value',
          Integer(layout)));
      SingleLine:= cfg.GetValue(Path+'/textstyle/SingleLine/value',SingleLine);
      Clipping:= cfg.GetValue(Path+'/textstyle/clipping/value',Clipping);
      WordBreak:= cfg.GetValue(Path+'/textStyle/wordbreak/value',WordBreak);
      Opaque:= cfg.GetValue(Path+'/textstyle/opaque/value',Opaque);
      SystemFont:= cfg.GetValue(Path+'/textstyle/systemfont/value',SystemFont);
    End;
  End;
End;

Function CellAttrIgual(Const Ca1,Ca2: TCellAttr): Boolean;
begin
  Result:=CompareMem(@Ca1,@Ca2,SizeOf(Ca1));
End;

Procedure CellAlignToAttr(Align: Integer; Var Attr: TCellAttr);
begin
  With Attr.TextStyle do Begin
    If Align And CA_LEFT    = CA_LEFT     Then Alignment:=taLeftJustify Else
    If Align And CA_CENTER  = CA_CENTER   Then Alignment:=taCenter
    Else                                       Alignment:=taRightJustify;
    If Align And CL_TOP     = CL_TOP      Then Layout:=tlTop Else
    If Align AND CL_CENTER  = CL_CENTER   Then Layout:=tlCenter
    Else                                       Layout:=tlBottom;
  End;
End;

Procedure AttrToCellAlign(Attr: TCellAttr; Var Align: Integer);
begin
  With Attr.TextStyle do Begin
    Align:=0;
    Case Alignment of
      taCenter: Align:=CA_CENTER;
      taRightJustify: Align:=CA_RIGHT;
      Else Align:=CA_LEFT;
    End;
    Case Layout of
      tlTop: Align:=Align or CL_TOP;
      tlBottom: Align:=Align or CL_BOTTOM;
      Else Align:=ALign or CL_CENTER;
    end;
  End;
End;


Function TCustomGrid.Getrowheights(Arow: Integer): Integer;
Begin
  Result:=Integer(FRows[aRow]);
  if Result<0 Then Result:=fDefRowHeight;
End;

function TCustomGrid.GetTopRow: Longint;
begin
  Result:=fTopLeft.y;
end;

function TCustomGrid.GetVisibleColCount: Integer;
Var
  R: TRect;
begin
  R:=FGCache.VisibleGrid;
  Result:=r.Right-r.left+1+FFixedCols;
end;

function TCustomGrid.GetVisibleRowCount: Integer;
Var
  R: TRect;
begin
  R:=FGCache.VisibleGrid;
  Result:=r.bottom-r.top+1+FFixedRows;
end;

function TCustomGrid.GetLeftCol: Integer;
begin
  result:=fTopLeft.x;
end;

Function TCustomGrid.Getcolcount: Integer;
Begin
  Result:=FCols.Count;
End;

Function TCustomGrid.Getrowcount: Integer;
Begin
  Result:=FRows.Count;
End;

Function TCustomGrid.Getcolwidths(Acol: Integer): Integer;
Begin
  Result:=Integer(FCols[aCol]);
  if result<0 then Result:=fDefColWidth;
End;

procedure TCustomGrid.SetEditor(const AValue: TWinControl);
begin
  if FEditor=AValue then exit;
  FEditor:=AValue;
  if FEditor<>nil Then Begin
    FEditor.Perform(GM_SETGRID, LongInt(Self), 0);
  End;
end;

procedure TCustomGrid.SetFixedCols(const AValue: Integer);
begin
  if FFixedCols=AValue then exit;
  CheckFixedCount(ColCount, RowCount, AValue, FFixedRows);
  FFixedCols:=AValue;
  fTopLeft.x:=AValue;
  fCol:=Avalue;
  doTopleftChange(true);
end;

procedure TCustomGrid.SetFixedRows(const AValue: Integer);
begin
  if FFixedRows=AValue then exit;
  CheckFixedCount(ColCount, RowCount, FFixedCols, AValue);
  FFixedRows:=AValue;
  fTopLeft.y:=AValue;
  FRow:=AValue;
  doTopleftChange(true);
end;

procedure TCustomGrid.SetGridLineColor(const AValue: TColor);
begin
  if FGridLineColor=AValue then exit;
  FGridLineColor:=AValue;
  Invalidate;
end;

procedure TCustomGrid.SetLeftCol(const AValue: Integer);
begin
  TryScrollTo(AValue, FTopLeft.Y);
end;

procedure TCustomGrid.SetOptions(const AValue: TGridOptions);
begin
  if FOptions=AValue then exit;
  FOptions:=AValue;
  If goRowSelect in Options Then Begin
    FRange:=Rect(FFixedCols, FRow, ColCount-1, FRow);
    FOptions:=FOptions - [goAlwaysShowEditor];
  End
  Else                           FRange:=Rect(FCol,FRow,FCol,FRow);
  VisualChange;
end;

procedure TCustomGrid.SetScrollBars(const AValue: TScrollStyle);
begin
  if FScrollBars=AValue then exit;
  FScrollBars:=AValue;
  VisualChange;
end;

procedure TCustomGrid.SetTopRow(const AValue: Integer);
begin
  TryScrollTo(FTopLeft.X, Avalue);
end;

Procedure TCustomGrid.Setrowheights(Arow: Integer; Avalue: Integer);
Begin
  If AValue<0 Then AValue:=-1;
  if AValue<>Integer(FRows[ARow]) Then begin
    FRows[ARow]:=Pointer(AValue);
    VisualChange;
    RowHeightsChanged;
  End;
End;

Procedure TCustomGrid.Setcolwidths(Acol: Integer; Avalue: Integer);
Begin
  If AValue<0 Then Avalue:=-1;
  If Avalue<>Integer(FCols[ACol]) Then begin
    FCols[ACol]:=Pointer(AValue);
    VisualChange;
    ColWidthsChanged;
  end;
End;

Procedure TCustomGrid.AdjustCount(IsColumn: Boolean; OldValue, newValue: Integer);
  Procedure AddDel(Lst: TList; aCount: Integer);
  Begin
    While lst.Count<aCount do Lst.Add(Pointer(-1)); // default width/height
    Lst.Count:=aCount;
  End;
Begin
  If IsColumn Then begin
    AddDel(FCols, NewValue);
    FGCache.AccumWidth.Count:=NewValue;
    If (OldValue=0)And(NewValue>=0) Then Begin
      FTopLeft.X:=FFixedCols;
      If RowCount=0 then begin
        FFixedRows:=0;
        FTopLeft.Y:=0;
        AddDel(FRows, 1); FGCache.AccumHeight.Count:=1;
      End;
    End;
    SizeChanged(OldValue, FRows.Count);
  End else begin
    AddDel(fRows, NewValue);
    FGCache.AccumHeight.Count:=NewValue;
    If (OldValue=0)And(NewValue>=0) then Begin
      FTopleft.Y:=FFixedRows;
      If FCols.Count=0 then begin
        FFixedCols:=0;
        FTopLeft.X:=0;
        AddDel(FCols, 1); FGCache.AccumWidth.Count:=1;
      End;
    End;
    SizeChanged(FCols.Count, OldValue);
  End;
  VisualChange;
End;

Procedure TCustomGrid.SetColCount(Valor: Integer);
Var
  OldC: Integer;
begin
  If Valor=FCols.Count Then Exit;
  OldC:=FCols.Count;
  CheckFixedCount(Valor, RowCount, FFixedCols, FFixedRows);
  AdjustCount(True, OldC, Valor);
End;

Procedure TCustomGrid.SetRowCount(Valor: Integer);
Var
  OldR: Integer;
begin
  If Valor=FRows.Count Then Exit;
  OldR:=FRows.Count;
  CheckFixedCount(ColCount, Valor, FFixedCols, FFixedRows);
  AdjustCount(False, OldR, Valor);
End;

Procedure TCustomGrid.SetDefColWidth(Valor: Integer);
begin
  If Valor=fDefColwidth Then Exit;
  FDefColWidth:=Valor;
  VisualChange;
End;

Procedure TCustomGrid.SetDefRowHeight(Valor: Integer);
begin
  If Valor=fDefRowHeight Then Exit;
  FDefRowheight:=Valor;
  VisualChange;
End;

procedure TCustomGrid.SetCol(Valor: Integer);
begin
  If Valor=FCol Then Exit;
  MoveExtend(False, Valor, FRow);
end;

procedure TCustomGrid.SetRow(Valor: Integer);
begin
  If Valor=FRow Then Exit;
  MoveExtend(False, FCol, Valor);
end;

procedure TCustomGrid.Sort(ColSorting: Boolean; Index, IndxFrom, IndxTo: Integer);
  Procedure QuickSort(L,R: Integer);
  Var
    i,j: Integer;
    P{,Q}: Integer;
  begin
    Repeat
      I:=L;
      J:=R;
      P:=(L+R)Div 2;
      Repeat
        If ColSorting Then begin
          While OnCompareCells(Self, Index, P, Index, i)>0 do I:=I+1;
          While OnCompareCells(Self, Index, P, Index, j)<0 do J:=J-1;
        end Else begin
          While OnCompareCells(Self, P, Index, i, Index)>0 do I:=I+1;
          While OnCompareCells(Self, P, Index, j, Index)<0 do J:=J-1;
        End;
        If I<=J Then Begin
          ExchangeColRow(Not ColSorting, i,j);
          I:=I+1;
          J:=j-1;
        End;
      Until I>J;
      If L<J Then QuickSort(L,J);
      L:=I;
    Until I>=R;
  End;
begin
  BeginUpdate;
  QuickSort(IndxFrom, IndxTo);
  EndUpdate(True);
end;

procedure TCustomGrid.doTopleftChange(dimChg: Boolean);
begin
  TopLeftChanged;
  If dimchg then Begin
    VisualChange;
  End Else begin
    CacheVisibleGrid;
    Invalidate;
  End;
  UpdateScrollBarPos(nil);
end;

procedure TCustomGrid.VisualChange;
Var
  Tw,Th: Integer;
  Dh,DV: Integer;
  
  Function MaxTopLeft: TPoint;
  Var
    i: Integer;
    W,H: Integer;
  begin
    Result:=Point(ColCount-1, RowCount-1);
    W:=0;
    For i:=ColCount-1 downTo FFixedCols Do begin
      W:=W+GetColWidths(i);
      If W<FGCache.ScrollWidth Then Result.x:=i
      Else         Break;
    End;
    H:=0;
    For i:=RowCount-1 downto FFixedRows do begin
      H:=H+GetRowHeights(i);
      If H<FGCache.ScrollHeight Then Result.y:=i
      Else         Break;
    End;
  End;
Var
  Mtl: TPoint;
  {$Ifdef TestSbars} vs,hs: Boolean; {$Endif}
begin
  // Calculate New Cached Values
  FGCache.GridWidth:=0;
  For Tw:=0 To ColCount-1 do begin
    FGCache.AccumWidth[Tw]:=Pointer(FGCache.GridWidth);
    FGCache.GridWidth:=FGCache.GridWidth + GetColWidths(Tw);
    If Tw<FixedCols Then FGCache.FixedWidth:=FGCache.GridWidth;
    {$IfDef dbgScroll}
    WriteLn('FGCache.AccumWidth[',Tw,']=',Integer(FGCache.AccumWidth[Tw]));
    {$Endif}
  End;
  FGCache.Gridheight:=0;
  For Tw:=0 To RowCount-1 do begin
    FGCache.AccumHeight[Tw]:=Pointer(FGCache.Gridheight);
    FGCache.Gridheight:=FGCache.Gridheight+GetRowHeights(Tw);
    If Tw<FixedRows Then FGCache.FixedHeight:=FGCache.GridHeight;
    {$IfDef dbgScroll}
    WriteLn('FGCache.AccumHeight[',Tw,']=',Integer(FGCache.AccumHeight[Tw]));
    {$Endif}
  End;
  
  Dh:=18{GetSystemMetrics(SM_CYHSCROLL)};
  DV:=18{GetSystemMetrics(SM_CXVSCROLL)};
  TW:=FGCache.GridWidth;
  TH:=FGCache.GridHeight;

  If Not(goSmoothScroll in Options) Then begin
    FGCache.TLColOff:=0;
    FGCache.TLRowOff:=0;
  End;

  {$Ifdef TestSBars}
  vs:=VertScrollBar.Visible;
  hs:=HorzScrollBar.Visible;
  {$Endif}

  HorzScrollBar.Visible:=
     (FScrollbars in [ssHorizontal, ssBoth]) or
    ((FScrollbars in [ssAutoHorizontal,ssAutoBoth]) and (TW>Width-Dv));
    
  VertScrollBar.Visible:=
     (FScrollbars in [ssVertical, ssBoth]) or
    ((FScrollbars in [ssAutoVertical, ssAutoBoth]) and (TH>height-Dh));
  
  If Not HorzScrollBar.Visible Then DH:=0;
  If Not VertScrollBar.Visible Then DV:=0;
  
  {$IfDef TestSBars}
  If (vs<VertScrollBar.Visible) Then
    WriteLn('Vertical Scrollbar Visible=', VertScrollBar.visible);
  if (hs<>HorzScrollBar.Visible) Then
    WriteLn('Horizontal Scrollbar Visible=', HorzScrollBar.Visible );
  {$Endif}

  FGCache.ClientWidth:=Width - DV;
  FGCache.ClientHeight:=Height - DH;
  FGCache.ScrollWidth:=FGCache.ClientWidth-FGCache.FixedWidth;
  FGCache.ScrollHeight:=FGCache.ClientHeight-FGCache.FixedHeight;
  
  Mtl:=MaxTopLeft;
  {$Ifdef DbgScroll}
  DebugPoint('MaxTopLeft',MaxTopLeft);
  {$Endif}
  FGCache.HScrDiv:=0;
  FGCache.VScrDiv:=0;

  With FGCache do
  If FScrollBars in [ssAutoHorizontal, ssAutoBoth] then begin
    If HorzScrollBar.Visible Then begin
      HorzScrollBar.Range:= GridWidth+2;
      
      If NOt (goSmoothScroll in Options) Then Begin
        TW:= Integer(AccumWidth[Mtl.X])-(HorzScrollBar.Range-ClientWidth);
        HorzScrollBar.Range:= HorzScrollBar.Range + TW - FixedWidth + 1;
      End;
      
      If HorzScrollBar.Range>ClientWidth Then
        HScrDiv:= (ColCount-FixedRows-1)/(HorzScrollBar.range-ClientWidth);

      {$Ifdef dbgScroll}
      Writeln('TotWidth=',GridWidth,'ClientWidth=',ClientWidth,' Horz Range=',HorzScrolLBar.Range);
      {$Endif}
    End
  End Else
  If FScrollBars in [ssHorizontal, ssBoth] Then HorzScrolLBar.Range:=0;
  
  With FGCache do
  If FScrollBars in [ssAutoVertical, ssAutoBoth] Then Begin
    If VertScrollBar.Visible Then begin
      VertScrollBar.Range:=GridHeight + 2;

      If Not (goSmoothScroll in Options) Then begin
        TH:= Integer(accumHeight[Mtl.Y])-(VertScrollBar.Range-ClientHeight);
        VertScrollBar.Range:= VertScrollBar.Range + TH - FixedHeight + 1;
      End;

      If VertScrolLBar.Range>ClientHeight Then
        VScrDiv:= (RowCount-FixedRows-1)/(VertScrollBar.Range-ClientHeight);

      {$Ifdef dbgScroll}
      Writeln('TotHeight=',GridHeight,'ClientHeight=',ClientHeight,' Vert Range=',VertScrolLBar.Range);
      {$Endif}
    End
  End Else
  If FScrollBars in [ssVertical, ssBoth] then VertScrollbar.Range:=0;

  CacheVisibleGrid;
  Invalidate;
end;

function TCustomGrid.CellRect(ACol, ARow: Integer): TRect;
begin
  Result:=ColRowToClientCellRect(aCol,aRow);
end;

// The visible grid Depends on  TopLeft and ClientWidht,ClientHeight,
// Col/Row Count, So it Should be called inmediately after any change
// like that
function TCustomGrid.GetVisibleGrid: TRect;
Var
  w: Integer;
  MaxRight: Integer;
  MaxBottom: Integer;
begin
  If (FTopLeft.X<0)or(FTopLeft.y<0) then begin
    Result:=Rect(-1,-1,-1,-1);
    Exit;
  End;
  // visible TopLeft Cell
  Result.TopLeft:=fTopLeft;
  Result.BottomRight:=Result.TopLeft;
  
  // Max visible coordinates
  MaxRight:= FGCache.ClientWidth;
  MaxBottom:=FGCache.ClientHeight;
    
  // Left Margin of next visible Column and Rightmost visible cell
  w:=GetColWidths(Result.Left) + FGCache.FixedWidth- FGCache.TLColOff;
  While (Result.Right<ColCount-1)And(W<MaxRight) do begin
    Inc(Result.Right);
    W:=W+GetColWidths(Result.Right);
  End;

  // Top Margin of next visible Row and Bottom most visible cell
  w:=GetRowheights(Result.Top) + FGCache.FixedHeight - FGCache.TLRowOff;
  While (Result.Bottom<RowCount-1)And(W<MaxBottom) do begin
    Inc(Result.Bottom);
    W:=W+GetRowHeights(Result.Bottom);
  End;
end;

{Calculate the TopLeft needed to show cell[aCol,aRow]}
Function TCustomGrid.ScrollToCell(Const aCol,aRow: Integer): Boolean;
Var
  RNew: TRect;
  Fw,Fh,Cw,Ch: Integer;
  OldTopLeft:TPoint;
  Xinc,YInc: Integer;
begin
  Cw:=FGCache.ClientWidth; //ClientWidth;
  Ch:=FGCache.ClientHeight; //ClientHeight;

  Fw:=FGCache.FixedWidth; //GetFixedWidth;
  Fh:=FGcache.FixedWidth; //GetFixedHeight;
  
  OldTopLeft:=fTopLeft;
  While (fTopLeft.x>=0) And
        (fTopLeft.x<ColCount)And
        (fTopLeft.y>=0) And
        (fTopLeft.y<RowCount) do begin

    RNew:=ColRowToClientCellRect(aCol,aRow);
    
    Xinc:=0;
    if Rnew.Left<fw then Xinc:=-1
    Else If RNew.Right>Cw Then XInc:=1;
    Yinc:=0;
    if RNew.Top<fh Then Yinc:=-1
    Else If RNew.Bottom>Ch Then YInc:=1;
    
    With FTopLeft do
    If ((XInc=0)And(Yinc=0)) or
       ((X=aCol)and(y=aRow)) Or // Only Perfect fit !
       ((X+XInc>=ColCount)or(Y+Yinc>=RowCount)) Or // Last Posible
       ((X+XInc<0)Or(Y+Yinc<0)) // Least Posible
    Then Break;

    Inc(FTopLeft.x, XInc);
    Inc(FTopLeft.y, Yinc);
  end;

  Result:=Not PointIgual(OldTopleft,FTopLeft);
  If result Then doTopleftChange(False);
End;

{Returns a valid TopLeft from a proposed TopLeft[DCol,DRow] which are
 relative or absolute coordinates }
function TCustomGrid.ScrollGrid(Relative: Boolean; DCol, DRow: Integer): TPoint;
begin
  Result:=FTopLeft;
  If Not Relative Then begin
    DCol:=DCol-Result.x;
    DRow:=DRow-Result.y;
  End;
  
  If DCol+Result.x<FFixedCols Then DCol:=Result.x-FFixedCols Else
  If DCol+Result.x>ColCount-1 Then DCol:=ColCount-1-Result.x;
  If DRow+Result.y<FFixedRows Then DRow:=Result.y-FFixedRows Else
  If DRow+Result.y>RowCount-1 Then DRow:=RowCount-1-Result.y;

  Inc(Result.x, DCol);
  Inc(Result.y, DRow);
end;

procedure TCustomGrid.TopLeftChanged;
begin
  If Assigned(OnTopLeftChange) Then OnTopLeftChange(Self);
end;

{$hints off}
procedure TCustomGrid.HeaderClick(IsColumn: Boolean; Index: Integer);
begin
end;
procedure TCustomGrid.ColRowMoved(IsColumn: Boolean; FromIndex,ToIndex: Integer);
begin
end;
procedure TCustomGrid.ColRowExchanged(isColumn: Boolean; Index, WithIndex: Integer);
begin
end;
procedure TCustomGrid.DrawFocusRect(aCol, aRow: Integer; ARect: TRect;
  aState: TGridDrawstate);
begin
end;
procedure TCustomGrid.AutoAdjustColumn(aCol: Integer);
begin
end;
procedure TCustomGrid.SizeChanged(OldColCount, OldRowCount: Integer);
begin
end;
procedure TCustomGrid.ColRowDeleted(IsColumn: Boolean; Index: Integer);
begin
end;
{$hints on}


procedure TCustomGrid.Paint;
begin
  Inherited Paint;
  If Not (csDesigning in ComponentState) Then begin
    If FUpdateCount=0 Then begin
      //WriteLn('Paint: FGCache.ValidGrid=',FGCache.ValidGrid );
      DrawEdges;
      DrawBackGround;
      If FGCache.ValidGrid Then begin
        {
        DrawFixedCells;
        DrawInteriorCells;
        DrawFocused;
        }
        DrawByRows;
        DrawColRowMoving;
      End;
    End;
  End;
End;

procedure TCustomGrid.ResetOffset(chkCol, ChkRow: Boolean);
begin
  With FGCache do begin
    If ChkCol Then ChkCol:=TLColOff<>0;
    If ChkCol Then TlColOff:=0;
    If ChkRow Then ChkRow:=TLRowOff<>0;
    If ChkRow Then TlRowOff:=0;
    If ChkRow or ChkCol Then begin
      CacheVisibleGrid;
      Invalidate;
      If ChkCol Then UpdateScrollBarPos(HorzScrollBar);
      If ChkRow Then UpdateScrolLBarPos(VertScrollBar);
    End;
  End;
end;


function TCustomGrid.SelectCell(ACol, ARow: Integer): Boolean;
begin
  Result:=MoveExtend(False, aCol, aRow);
end;

procedure TCustomGrid.DrawBackGround;
begin
  {
    The user can draw a something here :)
    
  Canvas.Brush.Color:=Color;
  Canvas.FillRect(Parent.ClientRect);
  }
end;

procedure TCustomGrid.DrawFixedCells;
Var
  Gds: TGridDrawState;
  i,j: Integer;
begin
  Gds:=[gdFixed];
  // Draw fixed fixed Cells
  For i:=0 to FFixedCols-1 do
    For j:=0 to fFixedRows-1 do
      DrawCell(i,j, ColRowToClientCellRect(i,j), gds);
      
  With FGCache.VisibleGrid do begin
    // Draw fixed column headers
    For i:=left to Right do
      For j:=0 to fFixedRows-1 do
        DrawCell(i,j, ColRowToClientCellRect(i,j), gds);
    // Draw fixed row headers
    For i:=0 to FFixedCols-1 do
      For j:=Top to Bottom do
        DrawCell(i,j, ColRowToClientCellRect(i,j), gds);
  End;
end;

procedure TCustomGrid.DrawInteriorCells;
Var
  Gds: TGridDrawState;
  i,j: Integer;
begin
  With FGCache.VisibleGrid do Begin
    For i:=Left to Right do
      For j:=Top to Bottom do begin
        Gds:=[];
        if (i=FCol)And(J=FRow) Then Continue;
        If IsCellSelected(i,j) Then Include(gds, gdSelected);
        DrawCell(i,j, ColRowToClientCellRect(i,j), gds);
      End;
  End;
end;

procedure TCustomGrid.DrawColRowMoving;
begin
  If (FGridState=gsColMoving)And(fMoveLast.x>=0) Then begin
    Canvas.Pen.Width:=3;
    Canvas.Pen.Color:=clRed;
    Canvas.MoveTo(fMoveLast.y, 0);
    Canvas.Lineto(fMovelast.y, FGCache.MaxClientXY.Y);
    Canvas.Pen.Width:=1;
  end Else
  If (FGridState=gsRowMoving)And(FMoveLast.y>=0) then begin
    Canvas.Pen.Width:=3;
    Canvas.Pen.Color:=clRed;
    Canvas.MoveTo(0, FMoveLast.X);
    Canvas.LineTo(FGCache.MaxClientXY.X, FMoveLast.X);
    Canvas.Pen.Width:=1;
  End;
end;

procedure TCustomGrid.DrawCell(aCol, aRow: Integer; aRect: TRect;
  aState: TGridDrawState);
begin
  DrawCellGrid(aRect,aCol,aRow,aState);
end;

procedure TCustomGrid.DrawByRows;
Var
  i: Integer;
begin
  // Draw Rows
  With FGCache.VisibleGrid do
    For i:=Top To Bottom do DrawRow(i);
  // Draw Fixed Rows
  For i:=0 to FFixedRows-1 Do DrawRow(i);
end;

procedure TCustomGrid.DrawRow(aRow: Integer);
Var
  Gds: TGridDrawState;
  i: Integer;
  Rs: Boolean;
  R: TRect;
begin

  ColRowToOffSet(False, True, aRow, R.Top, R.Bottom);
  
  // Draw columns in this row
  With FGCache.VisibleGrid do
    If ARow<FFixedRows Then Begin
      gds:=[gdFixed];
      For i:=Left to Right do Begin
        ColRowToOffset(true, True, i, R.Left, R.Right);
        DrawCell(i,aRow, R{ColRowToClientCellRect(i,aRow)},gds)
      End;
    End Else begin
      Rs:=(goRowSelect in Options);
      For i:=Left To Right do begin
        Gds:=[];
        If (i=Fcol)And(FRow=ARow) Then begin
          // Focused Cell
          Include(gds, gdFocused);
          // Check if need to be selected
          If (goDrawFocusSelected in Options) or
             (Rs And Not(goRelaxedRowSelect in Options)) Then Include(gds, gdSelected);
        End Else
        If IsCellSelected(i, ARow) Then Include(gds, gdSelected);
        ColRowToOffset(True, True, i, R.Left, R.Right);
        DrawCell(i,aRow, R{ColRowToClientCellRect(i,aRow)}, gds);
      End;
      // Draw the focus Rect
      If (ARow=FRow) And
         (IsCellVisible(FCol,ARow) Or (Rs And (ARow>=Top) And (ARow<=Bottom)))
      Then Begin
        ColRowToOffset(True, True, FCol, R.Left, R.Right);
        DrawFocusRect(FCol,FRow, R{ColRowToClienTCellRect(FCol,FRow)}, [gdFocused]);
      End;
    End; // Else Begin

  // Draw Fixed Columns
  gds:=[gdFixed];
  For i:=0 to FFixedCols-1 do begin
    ColRowToOffset(True, True, i, R.Left, R.Right);
    DrawCell(i,aRow, R{ColRowToClientCellRect(i,aRow)},gds);
  End;
end;

procedure TCustomGrid.DrawEdges;
Var
  P:  TPoint;
  Cr: TRect;
begin
  P:=FGCache.MaxClientXY;
  Cr:=Bounds(0,0, FGCache.ClientWidth, FGCache.ClientHeight);
  If P.x<Cr.Right Then begin
    Cr.Left:=P.x;
    Canvas.Brush.Color:=Color;
    Canvas.FillRect(cr);
    Cr.Left:=0;
    Cr.Right:=p.x;
  End;
  If P.y<Cr.Bottom Then begin
    Cr.Top:=p.y;
    Canvas.Brush.Color:=Color;
    Canvas.FillRect(cr);
  End;
end;

procedure TCustomGrid.DrawFocused;
Var
  R: TRect;
  gds: TGridDrawState;
begin
  gds:=[gdFocused];
  If IsCellVisible(FCol,FRow) Then begin
    If goDrawFocusSelected in Options Then Include(gds,gdSelected);
    If (goRowSelect in Options) And Not (goRelaxedRowSelect in Options) Then
      Include(gds, gdSelected);
    R:=colrowToClientCellRect(fCol,fRow);
    DrawCell(fCol,fRow,R, gds);
    DrawFocusRect(fCol,fRow, R, gds);
  End Else
    If  ((goRowSelect in Options) And
        (Frow>=FGCache.VisibleGrid.Top) And
        (Frow<=FGCache.VisibleGrid.Bottom))
    Then begin
      R:=colrowToClientCellRect(fCol,fRow);
      DrawFocusRect(fcol,fRow, R, gds);
    End;
end;

Procedure DebugRect(S:String; R:TRect);
begin
  WriteLn(S, 'L=',R.Left, ' T=',R.Top, ' R=',R.Right,' B=',R.Bottom);
End;
Procedure DebugPoint(S:String; P:TPoint);
begin
  WriteLn(S, 'X=',P.X,' Y=',P.Y);
End;

procedure TCustomGrid.DrawCellGrid(Rect: TRect; aCol,aRow: Integer; aState: TGridDrawState);
Var
  dv,dh: Boolean;
begin
  // Draw Cell Grid or Maybe in the future Borders..
  Dv:= goVertLine in Options;
  Dh:= goHorzLine In Options;
  If (gdFixed in aState) Then begin
    With Canvas, Rect do Begin
      Pen.Style:=psSolid;
      Pen.Color:=cl3DDkShadow;
        MoveTo(Left,Bottom-1);
        LineTo(Right-1,Bottom-1);
        LineTo(Right-1,Top-1);
      //if ARow=FItemIndex then begin
          Pen.Color:=cl3DDkShadow;
          MoveTo(Left,Bottom-1);
          LineTo(Left,Top);
          LineTo(Right-1,Top);
          Pen.Color:=cl3DLight;
          MoveTo(Left+1,Bottom-2);
          LineTo(Right-1,Bottom-2);
      //End;
    End;
    //Canvas.Frame3d(Rect, 1, bvLowered{bvNone}{bvRaised});
    Dh:=Dh and (goFixedHorzLine in Options);
    Dv:=Dv and (goFixedVertLine in Options);
  End;
  
  Canvas.Pen.Style:=fGridLineStyle;
  Canvas.Pen.Color:=fGridLineColor;
  If Dh Then begin
  //If fDrawHorzGrid then begin
    Canvas.MoveTo(Rect.Left,Rect.Top);
    Canvas.LineTo(Rect.Right,Rect.Top);
    If aRow=RowCount-1 Then begin
      Canvas.MoveTo(Rect.Left,Rect.Bottom);
      Canvas.LineTo(Rect.Right,Rect.Bottom);
    End;
  End;
  
  If Dv Then begin
  //If FDrawVertGrid Then begin
    Canvas.MoveTo(Rect.Left,Rect.Top);
    Canvas.LineTo(Rect.Left,Rect.Bottom);
    If aCol=ColCount-1 Then begin
      Canvas.Moveto(Rect.Right, Rect.Top);
      Canvas.LineTo(Rect.Right, Rect.bottom);
    End;
  End;
end;


procedure TCustomGrid.MyTextRect(R: TRect; Offx, Offy: Integer; S: String;
  Ts: TTextStyle);
Var
  Rorg: TRect;
  tmpRgn: HRGN;
begin
  If Ts.Clipping Then begin
    //IntersectClipRect(Canvas.handle, R.Left,R.Top,R.Right,R.Bottom);
  
    GetClipBox(Canvas.Handle, @ROrg);
    //DebugRect('Ini Rect = ', ROrg);
    tmpRGN:=CreateRectRgn(R.Left, R.Top, R.Right, R.Bottom);
    SelectClipRgn(Canvas.Handle, tmpRGN);
    //GetClipBox(Canvas.Handle, @Rtmp);
    //DebugRect('Set Rect = ', Rtmp);
    DeleteObject(tmpRGN);
  End;

  //If Ts.Opaque Then Canvas.FillRect(R);
  Canvas.TextOut(R.Left+Offx, R.Top+Offy,  S);

  If Ts.Clipping Then begin
    tmpRGN:=CreateRectRgn(Rorg.Left, Rorg.Top, Rorg.Right, Rorg.Bottom);
    SelectClipRgn(Canvas.Handle, tmpRGN);
    //GetClipBox(Canvas.Handle, @Rtmp);
    //DebugRect('End Rect = ', Rtmp);
    DeleteObject(tmpRGN);
  End;
end;

procedure TCustomGrid.WMEraseBkgnd(var Message: TLMEraseBkgnd);
begin
  Message.Result:=1;
  WriteLn('TCustomGrid.WMEraseBkgnd');
end;


//
// NOTE: WMHScroll and VMHScroll
// This methods are used to pre-calculate the scroll position
//
{$IFDEF Scr1}
  {
    -- This is the original 'row' scroll method
    -- in WMHScroll.Pos arrives a value from 0 .. [Max TopLeft Column]-FixedCols
    -- in WMVScroll.Pos arrives a value from 0 .. [Max TopLeft Row   ]-FixedRows
    -- Scrollbar sizes are not proportional :(
  }
{$ELSE}
  {
    -- This is the new 'not so pixel' scroll method,
   }
{$Endif}

procedure TCustomGrid.WMHScroll(var Message: TLMHScroll);
Var
  C,Tl: Integer;
begin
  // Avoid invalidating right know, just let the scrollbar
  // calculate its position
  BeginUpdate;
  Inherited;
  Message.Result:=1;
  EndUpdate(uoNone);

  {$IfDef dbgScroll}
  WriteLn('HSCROLL: Code=',Message.ScrollCode,' Position=', Message.Pos);
  {$Endif}
  

  {$IfDef Scr1}
    TL:=FFixedCols+ Message.Pos;
    If FEditor<>nil then getEditorValue;
    Inc(FUpdateScrollBarsCount);
    TryScrollTo(Tl, FTopLeft.Y);
    Dec(FUpdateScrollBarsCount);
    Message.Result:=1;
  {$Else}
  
    If FGCache.HScrDiv<=0 Then Exit;
    If FEditor<>nil then getEditorValue;
    
    If goThumbTracking in Options Then Begin
      C:=FFixedCols + Round( Message.Pos * FGCache.HScrDiv );
      If (FCol<>C) Then begin
        Inc(FUpdateScrollBarsCount);
        MoveExtend(False, C, FRow);
        Dec(FUpdateScrollBarsCount);
      End;
    End Else begin
    
      C:=Message.Pos+FGCache.FixedWidth;
      TL:=OffsetToColRow(True, False, C, FGCache.TLColOff);
      {$Ifdef dbgScroll}
      WriteLn('---- Offset=',C, ' TL=',TL, ' TLColOFf=', FGCache.TLColOff);
      {$Endif}
      If Not (goSmoothScroll in Options) then FGCache.TLColOff:=0;
      
      If TL<>FTopLeft.X Then begin
        Inc(FUpdateScrollBarsCount);
        TryScrollTo(Tl, FTopLeft.Y);
        Dec(FUpdateScrollBarsCount);
      End Else
      If goSmoothScroll in Options Then begin
        CacheVisibleGrid;
        Invalidate;
      End;
      
    End;
  {$Endif}
end;

procedure TCustomGrid.WMVScroll(var Message: TLMVScroll);
Var
  C: Integer;
  TL: Integer;
begin
  // Avoid invalidating right know, just let the scrollbar
  // calculate its position
  BeginUpdate;
  Inherited;
  Message.Result:=1;
  EndUpdate(uoNone);

  {$IfDef dbgScroll}
  WriteLn('VSCROLL: Code=',Message.ScrollCode,' Position=', Message.Pos);
  {$Endif}


  {$IfDef Scr1}
    TL:= Message.pos+FFixedRows;
    If FEditor<>nil then getEditorValue;
    Inc(FUpdateScrollBarsCount);
    TryScrollTo(FtopLeft.x, TL);
    Dec(FUpdateScrollBarsCount);
  {$Else}
    If FGCache.VScrDiv<=0 Then Exit;
    If FEditor<>nil then getEditorValue;
    If goThumbTracking in Options Then begin
      C:=FFixedRows + Round( Message.Pos * FGCache.VScrDiv );
      If (C<>FRow) Then begin
        Inc(FUpdateScrollBarsCount);
        MoveExtend(False, FCol, C);
        Dec(FUpdateScrollBarsCount);
      End;
    End Else begin
      C:=Message.Pos+FGCache.Fixedheight;
      TL:=OffsetToColRow(False, False, C, FGCache.TLRowOff);

      {$Ifdef dbgScroll}
      WriteLn('---- Offset=',C, ' TL=',TL, ' TLRowOFf=', FGCache.TLRowOff);
      {$Endif}
      If Not (goSmoothScroll in Options) Then FGCache.TLRowOff:=0;

      If TL<>FTopLeft.Y Then begin
        Inc(FUpdateScrollBarsCount);
        TryScrollTo(FTopLeft.X, Tl);
        Dec(FUpdateScrollBarsCount);
      End Else
      If goSmoothScroll in Options Then begin
        CacheVisibleGrid;
        Invalidate;
      End;
    End;
  {$Endif}
end;

procedure TCustomGrid.WMSize(var Msg: TWMSize);
begin
  Inherited;
  visualChange;
end;

{ Scroll grid to the given Topleft[aCol,aRow] as needed }
procedure TCustomGrid.TryScrollTo(aCol, aRow: Integer);
Var
  TryTL: TPoint;
begin
  TryTL:=ScrollGrid(False,aCol, aRow);
  if Not PointIgual(TryTL, FTopLeft) Then begin
    FTopLeft:=TryTL;
    doTopleftChange(False);
  End;
end;

procedure TCustomGrid.SetGridLineWidth(const AValue: Integer);
begin
  // Todo
  if FGridLineWidth=AValue then exit;
  FGridLineWidth:=AValue;
  Invalidate;
end;

{ Reposition the scrollbars according to the current TopLeft }
procedure TCustomGrid.UpdateScrollbarPos(Which: TControlScrollbar);
Var
  I: Integer;
begin
  // Adjust ScrollBar Positions
  {$IfDef Scr1}
    Write('UptateScrollbarPos: Scr1 - ');
    If (FScrollBars in [ssAutoHorizontal, ssAutoBoth]) And
        HorzScrolLBar.Visible Then begin
          I:=fTopLeft.X-FFixedCols;
          {$IfDef dbgScroll}
          Write('HorzScrollBar Pos=', I);
          {$Endif}
          HorzScrolLBar.Position:=I;
        End;
    If (FScrolLBars in [ssAutoVertical, ssAutoBoth]) And
        VertScrolLBar.Visible Then Begin
          I:=fTopLeft.Y-FFixedRows;
          {$IfDef dbgScroll}
          Write('  VertScrollBar Pos=', I);
          {$Endif}
          VertScrolLBar.Position:=I;
        End;
    WriteLn(' Done');
  {$Else Scr1}
    // Special condition only When scrolling by draging
    // the scrollbars see: WMHScroll and WVHScroll
    If FUpdateScrollBarsCount=0 Then begin
    
      if (Which=HorzScrollBar)or(Which=nil) Then
        If (FScrollBars in [ssAutoHorizontal, ssAutoBoth]) And
            HorzScrolLBar.Visible Then begin
            With FGCache do
              HorzScrollBar.Position:=
                Integer(AccumWidth[FTopLeft.x])-TLColOff-FixedWidth;
        End;

      If (Which=VertScrollBar)Or(Which=nil) Then
        If (FScrolLBars in [ssAutoVertical, ssAutoBoth]) And
            VertScrolLBar.Visible Then begin
            With FGCache do
              VertScrollBar.Position:=
                Integer(AccumHeight[FTopLeft.y])-TLRowOff-FixedHeight;
        End;
    End; {If FUpd...}
  {$Endif}
end;

procedure TCustomGrid.CheckFixedCount(aCol,aRow,aFCol,aFRow: Integer);
begin
  If (aFCol<>0)And (aFCol>=aCol) Then
    Raise Exception.Create('FixedCols can''t be <= than ColCount');
  If (aFRow<>0)And (aFRow>=aRow) Then
    Raise Exception.Create('FixedRows can''t be <= than RowCount');
end;

{ Save to the cache the current visible grid (excluding fixed cells) }
procedure TCustomGrid.CacheVisibleGrid;
Var
  R: TRect;
begin
  With FGCache do begin
    VisibleGrid:=GetVisibleGrid;
    With VisibleGrid do
      ValidGrid:=(Left>=0)And(Top>=0)And(Right>=Left)And(Bottom>=Top);
    If Not ValidGrid Then MaxClientXY:=Point(0,0)
    Else begin
      R:=ColRowToClientCellrect(VisibleGrid.Right, VisibleGrid.Bottom);
      MaxClientXY:=R.BottomRight;
    End;
  End;
end;

function TCustomGrid.GetSelection: TGridRect;
begin
  Result:=FRange;
end;

procedure TCustomGrid.SetDefaultDrawing(const AValue: Boolean);
begin
  if FDefaultDrawing=AValue then exit;
  FDefaultDrawing:=AValue;
  Invalidate;
end;

procedure TCustomGrid.SetFocusColor(const AValue: TColor);
begin
  if FFocusColor=AValue then exit;
  FFocusColor:=AValue;
  InvalidateCell(FCol,FRow);
end;

procedure TCustomGrid.SetGridLineStyle(const AValue: TPenStyle);
begin
  if FGridLineStyle=AValue then exit;
  FGridLineStyle:=AValue;
  Invalidate;
end;

procedure TCustomGrid.SetSelectActive(const AValue: Boolean);
begin
  if FSelectActive=AValue then exit;
  FSelectActive:=AValue;
  If FSelectActive Then FPivot:=Point(FCol,FRow);
end;

procedure TCustomGrid.SetSelection(const AValue: TGridRect);
begin
  If goRangeSelect in Options Then begin
    fRange:=NormalizarRect(aValue);
    Invalidate;
  End;
end;

function TCustomGrid.doColSizing(X, Y: Integer): Boolean;
Var
  R: TRect;
  Loc: Integer;
begin
  Result:=False;
  If gsColSizing = fGridState Then begin
    If x>FSplitter.y Then
      ColWidths[FSplitter.x]:=x-FSplitter.y
    Else
      if ColWidths[FSplitter.x]>0 Then ColWidths[FSplitter.X]:=0;
    Result:=True;
  End Else
  If (fGridState=gsNormal)And(Y<FGCache.FixedHeight)And(X>FGCache.FixedWidth) Then
  begin
    FSplitter.X:= OffsetToColRow(True, True, X, Loc);
    FSplitter.Y:=0;
    If FSplitter.X>=0 Then begin
      R:=ColRowToClientCellRect(FSplitter.x, FSplitter.y);
      FSplitter.y:=X;                       // Resizing X reference
      If (R.Right-X)<(X-R.Left) then Loc:=R.Right
      Else begin
        Loc:=R.Left;
        Dec(FSplitter.x);                   // Resizing col is the previous
      End;
      IF (Abs(Loc-x)<=2)And(FSplitter.X>=FFixedCols) then Cursor:=crHSplit
      Else                                                Cursor:=crDefault;
      Result:=True;
    End;
  End
    Else
      If (cursor=crHSplit) Then Cursor:=crDefault;
end;

function TCustomGrid.doRowSizing(X, Y: Integer): Boolean;
Var
  R: TRect;
  Loc: Integer;
begin
  Result:=False;
  If gsRowSizing = fGridState Then begin
    If y>FSplitter.x Then
      RowHeights[FSplitter.y]:=y-FSplitter.x
    Else
      if RowHeights[FSplitter.y]>0 Then RowHeights[FSplitter.Y]:=0;
    Result:=True;
  End Else
  If (fGridState=gsNormal)And(X<FGCache.FixedWidth)And(Y>FGCache.FixedHeight) Then
  begin
    fSplitter.Y:=OffsetToColRow(False, True, Y, Loc);
    If Fsplitter.Y>=0 Then begin
      ColRowToOffset(False, True, FSplitter.Y, R.Top, R.Bottom);
      FSplitter.X:=Y;
      If (R.Bottom-Y)<(Y-R.Top) Then Loc:=R.Bottom
      Else begin
        Loc:=R.Top;
        Dec(FSplitter.y);
      End;
      IF (Abs(Loc-y)<=2)And(FSplitter.Y>=FFixedRows) then Cursor:=crVSplit
      Else                                                Cursor:=crDefault;
      Result:=True;
    End;
    {
    FSplitter:=MouseToCell(Point(X,Y)); // Resizing Row
    R:=ColRowToClientCellRect(FSplitter.x, FSplitter.y);
    Fsplitter.x:=y;                       // Resizing y reference
    If (R.Bottom-Y)<(Y-R.Top) Then Loc:=R.Bottom
    Else Begin
      Loc:=R.Top;
      Dec(FSplitter.y);                   // Resizing row is the previous
    End;
    IF (Abs(Loc-y)<=2)And(FSplitter.Y>=FFixedRows) then Cursor:=crVSplit
    Else                                                Cursor:=crDefault;
    Result:=True;
    }
  End
    Else
      If Cursor=crVSplit Then Cursor:=crDefault;
end;

procedure TCustomGrid.doColMoving(X, Y: Integer);
Var
  P: TPoint;
  R: TRect;
begin
  P:=MouseToCell(Point(X,Y));
  If (Abs(FSplitter.Y-X)>fDragDx)And(Cursor<>crMultiDrag) Then begin
    Cursor:=crMultiDrag;
    FMoveLast:=Point(-1,-1);
    ResetOffset(True, False);
  End;
  if (Cursor=crMultiDrag)And
     (P.x>=FFixedCols) And
     ((P.X<=FSplitter.X)or(P.X>FSplitter.X))And
     (P.X<>FMoveLast.X) Then begin
      R:=ColRowToClientCellRect(P.x, P.y);
      If P.x<=FSplitter.X Then fMoveLast.Y:=R.left
      Else                     FMoveLast.Y:=R.Right;
      fMoveLast.X:=P.X;
      Invalidate;
  End;
end;

procedure TCustomGrid.doRowMoving(X, Y: Integer);
Var
  P: TPoint;
  R: TRect;
begin
  P:=MouseToCell(Point(X,Y));
  If (Cursor<>crMultiDrag)And(Abs(FSplitter.X-Y)>fDragDx) Then begin
    Cursor:=crMultiDrag;
    FMoveLast:=Point(-1,-1);
    ResetOffset(False, True);
  End;
  if (Cursor=crMultiDrag)And
     (P.y>=FFixedRows) And
     ((P.y<=FSplitter.Y)or(P.Y>FSplitter.Y))And
     (P.y<>FMoveLast.Y) Then begin
      R:=ColRowToClientCellRect(P.x, P.y);
      If P.y<=FSplitter.y Then fMoveLast.X:=R.Top
      Else                     FMoveLast.X:=R.Bottom;
      fMoveLast.Y:=P.Y;
      Invalidate;
  End;
end;


Function TCustomGrid.OffsetToColRow(IsCol, Fisical: Boolean; Offset: Integer;
  var Rest: Integer): Integer;
begin
  Result:=0; //Result:=-1;
  Rest:=0;
  If Offset<0 Then Exit; // Out of Range;
  With FGCache do
  If IsCol Then begin

    // Begin to count Cols from 0 but ...
    If Fisical And (Offset>FixedWidth-1) Then begin
      Result:=FTopLeft.X;  // In scrolled view, then Begin from FtopLeft col
      Offset:=Offset-FixedWidth+Integer(AccumWidth[Result])+TLColOff;
      If Offset>GridWidth-1 Then Begin
        Result:=ColCount-1;
        Exit;
      End;
    End;
    While Offset>(Integer(AccumWidth[Result])+GetColWidths(Result)-1) do Inc(Result);
    Rest:=Offset;
    If Result<>0 Then Rest:=Offset-Integer(AccumWidth[Result]);

  End Else Begin
  
    If Fisical And (Offset>FixedHeight-1) Then begin
      Result:=FTopLeft.Y;
      Offset:=Offset-FixedHeight+Integer(AccumHeight[Result])+TLRowOff;
      If Offset>GridHeight-1 Then Begin
        Result:=RowCount-1;
        Exit; // Out of Range
      End;
    End;
    While Offset>(Integer(AccumHeight[Result])+GetRowHeights(Result)-1) do Inc(Result);
    Rest:=Offset;
    If Result<>0 Then Rest:=Offset-Integer(AccumHeight[Result]);
    
  End;
end;

function TCustomGrid.ColRowToOffset(IsCol,Fisical:Boolean; Index:Integer; Var Ini,Fin:Integer): Boolean;
Var
  Dim: Integer;
begin
  With FGCache do begin
    If IsCol Then begin
      Ini:=Integer(AccumWidth[Index]);
      Dim:=GetColWidths(Index);
    End Else begin
      Ini:=Integer(AccumHeight[Index]);
      Dim:= GetRowheights(Index);
    End;
    if Not Fisical Then begin
      Fin:=Ini + Dim;
      Exit;
    End;
    If IsCol Then Begin
      If index>=FFixedCols Then
        Ini:=Ini-Integer(AccumWidth[FTopLeft.X]) + FixedWidth -  TLColOff;
    End Else begin
      if Index>=FFixedRows then
        Ini:=Ini-Integer(AccumHeight[FTopLeft.Y]) + FixedHeight - TLRowOff;
    End;
    Fin:=Ini + Dim;
  End;
end;

{
Function TCustomGrid.GetColRowOffset(IsCol, Fisical: Boolean; Offset: Integer;
  var Rest: Integer): Integer;
Var
  i,j,k: Integer;
  L: TList;
begin
  Result:=-1;
  If Offset<0 Then Exit; // Out of Range;
  j:=0; k:=0;
  If IsCol Then begin
    If Offset>FGCache.GridWidth Then Exit; // Out of Range;
    If Fisical Then
      If Offset>FGCache.FixedWidth Then begin
        k:=FTopLeft.X;
        Offset:=Offset - FGCache.FixedWidth;
      End;
    for i:=k to ColCount-1 do begin
      if Offset<j Then Break;
      j:=j+getColWidths(i);
      Result:=i;
    End;
  End  Else Begin
    If Offset>FGCache.GridHeight Then Exit; // Out of Range
    If Fisical Then
      If Offset>FGCache.FixedHeight Then begin
        k:=FTopLeft.Y;
        Offset:=Offset - FGCache.FixedHeight;
      End;
    For i:=k to RowCount-1 do begin
      If Offset<j Then Break;
      j:=j+GetRowheights(i);
      Result:=i;
    End;

  End;
  Rest:=Offset;
end;
}
function TCustomGrid.MouseToGridZone(X, Y: Integer; CellCoords: Boolean): TGridZone;
begin
  Result:=gzNormal;
  If CellCoords Then Begin
    If (X<fFixedCols) then
      If Y<FFixedRows Then  Result:= gzFixedCells
      Else                  Result:= gzFixedRows
    Else
    If (Y<fFixedRows) Then
      If X<FFixedCols Then  Result:= gzFixedCells
      Else                  Result:= gzFixedCols;
  End Else begin
    If X<=FGCache.FixedWidth Then
      If Y<=FGcache.FixedHeight Then  Result:=gzFixedCells
      Else                            Result:=gzFixedRows
    Else
    If Y<=FGCache.FixedHeight Then
      if X<=FGCache.FixedWidth Then   Result:=gzFixedCells
      Else                            Result:=gzFixedCols;
  End;
end;

procedure TCustomGrid.ExchangeColRow(IsColumn: Boolean; Index, WithIndex: Integer
  );
begin
  If IsColumn Then FCols.Exchange(Index, WithIndex)
  Else             FRows.Exchange(Index, WithIndex);
  ColRowExchanged(IsColumn, Index, WithIndex);
  VisualChange;
end;

procedure TCustomGrid.MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer);
begin
  If IsColumn then FCols.Move(FromIndex, ToIndex)
  Else             FRows.Move(FromIndex, ToIndex);
  ColRowMoved(IsColumn, FromIndex, ToIndex);
  VisualChange;
End;

procedure TCustomGrid.SortColRow(IsColumn: Boolean; Index: Integer);
begin
  If IsColumn Then SortColRow(IsColumn, Index, FFixedRows, RowCount-1)
  Else             SortColRow(IsColumn, Index, FFixedCols, ColCount-1);
end;

procedure TCustomGrid.SortColRow(IsColumn: Boolean; Index, FromIndex,
  ToIndex: Integer);
begin
  If Assigned(OnCompareCells) Then begin
    BeginUpdate;
    Sort(IsColumn, Index, FromIndex, ToIndex);
    EndUpdate(true);
  End;
end;

procedure TCustomGrid.DeleteColRow(IsColumn: Boolean; Index: Integer);
begin
  If IsColumn Then FCols.Delete(Index)
  Else             FRows.Delete(Index);
  ColRowDeleted(IsColumn, Index);
  VisualChange;
end;

procedure TCustomGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
Var
  Gz: TGridZone;
  R: TRect;
begin
  inherited MouseDown(Button, Shift, X, Y);
  If Not FGCache.ValidGrid Then Exit;
  If Not (ssLeft in Shift) Then Exit;

  Gz:=MouseToGridZone(X,Y, False);
  Case Gz of
    gzFixedCols:
      begin
        if (goColSizing in Options)And(Cursor=crHSplit) then Begin
          R:=ColRowToClientCellRect(FSplitter.x, FTopLeft.y);
          FSplitter.y:=R.Left;
          fGridState:= gsColSizing;
        End Else begin
          // ColMoving or Clicking
          fGridState:=gsColMoving;
          FSplitter:=MouseToCell(Point(X,Y));
          FMoveLast:=Point(-1,-1);
          FSplitter.Y:=X;
        End;
      End;
    gzFixedRows:
      If (goRowSizing in Options)And(Cursor=crVSplit) Then begin
        R:=ColRowToClientcellRect(FTopLeft.X, FSplitter.y);
        FSplitter.x:=R.top;
        fGridState:= gsRowSizing;
      End Else begin
        // RowMoving or Clicking
        fGridState:=gsRowMoving;
        fSplitter:=MouseToCell(Point(X,Y));
        FMoveLast:=Point(-1,-1);
        FSplitter.X:=Y;
      End;
    gzNormal:
      Begin
        if csDesigning in ComponentState Then Exit;

        // is user clicking on a selection?
        // is user dragging the selection?
        // is user only selecting a new cell range?
        fGridState:=gsSelecting;
        FSplitter:=MouseToCell(Point(X,Y));

        If Not (goEditing in Options) Then begin
          If ssShift in Shift Then begin
            SelectActive:=(goRangeSelect in Options);
          End Else begin
            If Not SelectACtive Then Begin
              FPivot:=FSplitter;
              FSelectActive:=true;
            End;
          End;
          MoveExtend(False, fsplitter.X, fSplitter.y);
        End;
      End;
  End;
end;

procedure TCustomGrid.MouseMove(Shift: TShiftState; X, Y: Integer);
Var
  p: TPoint;
begin
  inherited MouseMove(Shift, X, Y);
  If Not FGCache.ValidGrid Then Exit;

  Case fGridState of
    gsSelecting:
      begin
        P:=MouseToLogcell(Point(X,Y));
        MoveExtend(False, P.x, P.y);
      End;
    gsColMoving: If goColMoving in Options Then doColMoving(X,Y);
    gsRowMoving: If goRowMoving in Options Then doRowMoving(X,Y);
    Else
      begin
        If goColSizing in Options Then doColSizing(X,Y);
        If goRowSizing in Options Then doRowSizing(X,Y);
      End;
  End;
end;

procedure TCustomGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
var
   Cur: TPoint;
begin
  inherited MouseUp(Button, Shift, X, Y);
  If Not FGCache.ValidGrid Then Exit;
  Cur:=MouseToCell(Point(x,y));

  Case fGridState of
    gsSelecting:
      Begin
        MoveExtend(False, Cur.x, Cur.y);
        SelectActive:=False;
      End;
    gsColMoving:
      begin
        //WriteLn('Move Col From ',Fsplitter.x,' to ', FMoveLast.x);
        If FMoveLast.X>=0 Then Begin
          MoveColRow(True, Fsplitter.X, FMoveLast.X);
          Cursor:=crDefault;
        End Else
          If Cur.X=FSplitter.X Then HeaderClick(True, FSplitter.X);
      End;
    gsRowMoving:
      Begin
        //WriteLn('Move Row From ',Fsplitter.Y,' to ', FMoveLast.Y);
        If FMoveLast.Y>=0 Then begin
          MoveColRow(False, Fsplitter.Y, FMoveLast.Y);
          Cursor:=crDefault;
        End Else
          If Cur.Y=FSplitter.Y Then HeaderClick(False, FSplitter.Y);
      End;
  End;
  fGridState:=gsNormal;
end;

procedure TCustomGrid.DblClick;
begin
  If (goColSizing in Options) And (Cursor=crHSplit) Then begin
    If (goDblClickAutoSize in Options) Then begin
      AutoAdjustColumn( FSplitter.X );
    End Else
      WriteLn('Got Doubleclick on Col Resizing: AutoAdjust?');
  End Else
  If  (goDblClickAutoSize in Options) And
      (goRowSizing in Options) And
      (Cursor=crVSplit) Then begin
        WriteLn('Got DoubleClick on Row Resizing: AutoAdjust?');
  End
  Else
    Inherited DblClick;
end;

procedure TCustomGrid.doExit;
begin
  inherited doExit;
  Invalidate;
end;

procedure TCustomGrid.doEnter;
begin
  inherited doEnter;
  //If (goEditing in Options)And(goAlwaysShowEditor in Options) Then ShowEditor;
end;

procedure TCustomGrid.KeyDown(var Key: Word; Shift: TShiftState);
  Procedure MoveSel(Rel: Boolean; aCol,aRow: Integer);
  begin
    // Always reset Offset in kerboard Events
    FGCache.TLColOff:=0;
    FGCache.TLRowOff:=0;
    SelectActive:=(ssShift in Shift);
    If MoveExtend(Rel, aCol, aRow) Then Key:=0;
  End;
Var
  R: TRect;
  Relaxed: Boolean;
begin
  inherited KeyDown(Key, Shift);
  If Not FGCache.ValidGrid Then Exit;
  
  Relaxed:=Not (goRowSelect in Options) or (goRelaxedRowSelect in Options);
  
  Case Key of
    VK_LEFT:
      Begin
        If Relaxed Then MoveSel(True,-1, 0)
        Else            MoveSel(true, 0,-1);
      End;
    VK_RIGHT:
      begin
        If Relaxed Then MoveSel(True, 1, 0)
        Else            MoveSel(True, 0, 1);
      End;
    VK_UP:
      Begin
        MoveSel(True, 0, -1);
      End;
    VK_DOWN:
      begin
        MoveSel(True, 0, 1);
      End;
    VK_PRIOR:
      begin
        R:=FGCache.Visiblegrid;
        MoveSel(True, 0, R.Top-R.Bottom);
      End;
    VK_NEXT:
      Begin
        R:=FGCache.VisibleGrid;
        MoveSel(True, 0, R.Bottom-R.Top);
      End;
    VK_HOME:
      begin
        If ssCtrl in Shift Then MoveSel(False, FCol, FFixedRows)
        Else
          if Relaxed Then MoveSel(False, FFixedCols, FRow)
          Else            MoveSel(False, FCol, FFixedRows);
      End;
    VK_END:
      Begin
        If ssCtrl in Shift Then MoveSel(False, FCol, RowCount-1)
        Else
          if Relaxed Then MoveSel(False, ColCount-1, FRow)
          Else            MoveSel(False, FCol, RowCount-1);
      End;
    VK_F2:
      begin
        ShowEditor;
        Key:=0;
      End;
    {$IfDef Dbg}
    Else WriteLn('KeyDown: ', Key);
    {$Endif}
  End;
end;

procedure TCustomGrid.KeyUp(var Key: Word; Shift: TShiftState);
begin
  inherited KeyUp(Key, Shift);
end;


{ Returns a reactagle corresponding to a fisical cell[aCol,aRow] }
function TCustomGrid.ColRowToClientCellRect(aCol, aRow: Integer): TRect;
begin
  ColRowToOffset(True, True, ACol, Result.Left, Result.Right);
  ColRowToOffSet(False,True, ARow, Result.Top, Result.Bottom);
end;

{ Convert a fisical Mouse coordinate into fisical a cell coordinate }
function TCustomGrid.MouseToCell(Mouse: TPoint): TPoint;

Var
   //i,j: Integer;
   //P: TPoint;
   d: Integer;
begin
  Result.X:= OffsetToColRow(True, True, Mouse.x, d);
  Result.Y:= OffsetToColRow(False,True, Mouse.y, d);
{  Exit;
  P:=Point(0,0);
  If Mouse.x>FGCache.FixedWidth Then begin
    P.x:=fTopLeft.x;
    Mouse.X:=Mouse.X-FGCache.FixedWidth;
  End;
  If Mouse.y>FGCache.FixedHeight Then begin
    p.y:=fTopLeft.y;
    Mouse.Y:=Mouse.Y-FGCache.Fixedheight;
  End;

  Result.x:=-1; Result.y:=-1;
  j:=0;
  for i:=P.x to ColCount-1 do begin
    if Mouse.x<j Then Break;
    j:=j+getColWidths(i);
    Result.x:=i;
  End;
  j:=0;
  For i:=P.y to RowCount-1 do begin
    if Mouse.y<j then break;
    j:=j+getRowheights(i);
    Result.y:=i;
  End;
}
end;

{ Convert a fisical Mouse coordinate into logical a cell coordinate }
function TCustomGrid.MouseToLogcell(Mouse: TPoint): TPoint;
Var
  gz: TGridZone;
begin
  Gz:=MouseToGridZone(Mouse.x, Mouse.y, False);
  If gz=gzNormal Then Result:=MouseToCell(Mouse)
  Else begin
    Result:=MouseToCell(Mouse);
    If (gz=gzFixedRows)or(gz=gzFixedCells) then begin
      Result.x:= fTopLeft.x-1;
      If Result.x<FFixedCols Then Result.x:=FFixedCols;
    End;
    If (gz=gzFixedCols)or(gz=gzFixedCells) Then begin
      Result.y:=fTopleft.y-1;
      If Result.y<fFixedRows Then Result.y:=FFixedRows;
    End;
  End;
end;

function TCustomGrid.ISCellVisible(aCol, aRow: Integer): Boolean;
begin
  With FGCache.VisibleGrid do
    Result:= (Left<=ACol)And(aCol<=Right)And(Top<=aRow)And(aRow<=Bottom);
end;

procedure TCustomGrid.InvalidateCol(ACol: Integer);
Var
  R: TRect;
begin
  {$ifdef dbg} WriteLn('InvalidateCol  Col=',aCol); {$Endif}
  R:=ColRowToClientCellRect(aCol, FTopLeft.y);
  R.Bottom:=FGCache.MaxClientXY.Y;
  InvalidateRect(Handle, @R, True);
End;

procedure TCustomGrid.InvalidateRow(ARow: Integer);
Var
  R: TRect;
begin
  {$ifdef dbg} WriteLn('InvalidateRow  Row=',aRow); {$Endif}
  R:=ColRowToClientCellRect(fTopLeft.x, aRow);
  R.Right:=FGCache.MaxClientXY.X;
  InvalidateRect(Handle, @R, True);
End;


Function TCustomGrid.MoveExtend(Relative: Boolean; DCol, DRow: Integer): Boolean;
Var
  InvalidateAll: Boolean;
begin
  Result:=False;
  
  dCol:=FCol*(1-Byte(Not Relative))+DCol;
  dRow:=FRow*(1-Byte(Not Relative))+DRow;
  If dCol<FFixedCols Then dCol:=FFixedCols Else
  If dCol>ColCount-1 Then dcol:=ColCount-1;
  If dRow<FFixedRows Then dRow:=FFixedRows Else
  If dRow>RowCount-1 Then dRow:=RowCount-1;

  // Change on Focused cell?
  If (Dcol=FCol)And(DRow=FRow) Then Exit;

  Result:=True;
  if Assigned(OnBeforeSelection) Then OnBeforeSelection(Self, DCol, DRow, Result);
  If Not Result Then Exit;
  
  // Going to change selection, get editor value before that
  GetEditorValue;
  
  // default range
  If goRowSelect in Options Then FRange:=Rect(FFixedCols, DRow, Colcount-1, DRow)
  Else                           FRange:=Rect(DCol,DRow,DCol,DRow);
  
  InvalidateAll:=False;
  If SelectActive Then
    if goRangeSelect in Options Then begin
      If goRowSelect in Options Then Begin
        FRange.Top:=Min(fPivot.y, DRow);
        FRange.Bottom:=Max(fPivot.y, DRow);
      End Else begin
        FRange:=NormalizarRect(Rect(Fpivot.x,FPivot.y, DCol, DRow));
      End;
      InvalidateAll:=True;
    End;

  If Not ScrollToCell(DCol, DRow) Then
    If InvalidateAll Then Begin
      //InvalidateSelection;
      Invalidate
    End Else begin
      //InvalidateCell(FCol, FRow);
      InvalidateCell(DCol, DRow);
    End;
    
  fCol:=DCol;
  fRow:=DRow;
  Editor:=nil;
  
  MoveSelection;
  
  if //Not SelectActive And
    (goEditing in Options) And
    (goAlwaysShowEditor in Options) And
    Not(goRowSelect in Options) Then ShowEditor;
end;

procedure TCustomGrid.MoveSelection;
begin
  if Assigned(onSelection) Then OnSelection(Self, FCol, FRow);
end;

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

procedure TCustomGrid.EndUpdate(UO: TUpdateOption);
begin
  Dec(FUpdateCount);
  If FUpdateCount=0 Then
    Case UO of
      uoQuick: Invalidate;
      uoFull: VisualChange;
    End;
end;

procedure TCustomGrid.EndUpdate(FullUpdate: Boolean);
begin
  EndUpdate(uoFull);
end;

function TCustomGrid.IsCellSelected(aCol, aRow: Integer): Boolean;
begin
  Result:=  (FRange.Left<=aCol)   And
            (aCol<=FRange.Right)  And
            (FRange.Top<=aRow)    And
            (aRow<=FRange.Bottom);
end;

procedure TCustomGrid.InvalidateCell(aCol, aRow: Integer);
Var
  R: TRect;
begin
  {$IfDef dbgPaint}
    WriteLn('InvalidateCell  Col=',aCol, ' Row=',aRow);
  {$Endif}
  R:=ColRowToClientCellRect(aCol, aRow);
  InvalidateRect(Handle, @R, False);
  //InvalidateRect(Handle, @R, True);
End;

procedure TCustomGrid.InvalidateGrid;
begin
  If FUpdateCount=0 Then Invalidate;
end;

procedure TCustomGrid.Invalidate;
begin
  If FUpdateCount=0 Then
    inherited Invalidate;
end;

procedure TCustomGrid.GetEditorValue;
begin
  If Not (csDesigning in ComponentState) Then begin
    HideEditor;
  End;
end;

procedure TCustomGrid.SetEditorValue;
begin
  If Not (csDesigning in ComponentState) Then
    PosEditor;
end;

procedure TCustomGrid.HideEditor;
begin
  if (Editor<>nil) And Editor.HandleAllocated And Editor.Visible Then begin
    If Not FEditorHiding Then begin
      FEditorHiding:=True;
      Editor.Visible:=False;
      Editor.Parent:=nil;
      LCLLinux.SetFocus(Self.Handle);
      FEDitorHiding:=False;
    End;
  End;
end;

procedure TCustomGrid.ShowEditor;
begin
  If Not (csDesigning in ComponentState)And(goEditing in Options) Then
    If (Editor<>nil) And Not Editor.Visible Then Begin
      ResetOffset(True, True);
      Editor.Parent:=Self;
      SetEditorValue;
      Editor.Visible:=True;
      LCLLinux.SetFocus(Editor.Handle);
    End;
end;

procedure TCustomGrid.PosEditor;
Var
   R: TRect;
   CurVisible: Boolean;
begin
  If fEditor<>nil Then begin
    R:=ColRowToClientCellRect(FCol,FRow);
    FEditor.SetBounds(R.Left, R.Top, R.Right-R.Left, R.Bottom-R.Top);
  End;
end;

procedure TCustomGrid.ExitEditor(Sender: TWinControl);
begin
  //WriteLn('Editor is losing the focus..');
  If Not FEditorHiding Then begin
    // Editor losing focus for any reason
    //WriteLn('Hey, What is happening here?');
    FEditorHiding:=True;
    GetEditorValue;
    If Editor<>nil Then Begin
      Editor.Visible:=False;
      Editor.Parent:=nil;
    End;
    FEditorHiding:=False;
  End;
end;

procedure TCustomGrid.EditorKeyDown(var Key: Word; Shift: TShiftState);
begin
  FEditorKey:=True; // Just a flag to see from where the event comes
  Case Key of
    VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN,
    VK_PRIOR, VK_NEXT:
    Begin
      If Not(ssShift in Shift) Then KeyDown(Key, Shift);
    End;
    VK_RETURN, VK_TAB:
    Begin
      If (Key=VK_TAB) And Not (goTabs in Options) Then begin
          // let the focus go
          Exit;
      End;
      Key:=0;
      Case FAutoAdvance of
        aaRight: Key:=VK_RIGHT * Integer( FCol<ColCount-1 );
        aaDown : Key:=VK_DOWN * Integer( FRow<RowCount-1 );
      End;
      If Key=0 Then begin
        GetEditorValue;
        ShowEditor;
        // Select All !
      End Else KeyDown(Key, Shift);
    End;
  End;
  FEditorKey:=False;
end;

procedure TCustomGrid.CancelEditor;
begin
  HideEditor;
  SetFocus;
end;

procedure TCustomGrid.ColWidthsChanged;
begin
  //
end;
procedure TCustomGrid.RowHeightsChanged;
begin
  //
end;

procedure TCustomGrid.SaveContent(cfg: TXMLConfig);
Var
  i,j,k: Integer;
  Path: String;
begin
  cfg.SetValue('grid/version', GRIDFILEVERSION);
  
  Cfg.SetValue('grid/saveoptions/create', soDesign in SaveOptions);
  If soDesign in SaveOptions then begin
    Cfg.SetValue('grid/design/columncount',  ColCount);
    Cfg.SetValue('grid/design/rowcount',  RowCount);
    Cfg.SetValue('grid/design/fixedcols', FixedCols);
    Cfg.SetValue('grid/design/fixedrows', Fixedrows);
    Cfg.SetValue('grid/design/defaultcolwidth', DefaultColWidth);
    Cfg.SetValue('grid/design/defaultRowHeight',DefaultRowHeight);

    j:=0;
    For i:=0 to ColCount-1 do begin
      k:=Integer(FCols[i]);
      If (k>=0)And(k<>DefaultColWidth) Then Begin
        inc(j);
        cfg.SetValue('grid/design/columns/columncount',j);
        cfg.SetValue('grid/design/columns/column'+IntToStr(j)+'/index', i);
        cfg.SetValue('grid/design/columns/column'+IntToStr(j)+'/width', k);
      End;
    End;
    j:=0;
    For i:=0 to RowCount-1 do begin
      k:=Integer(FRows[i]);
      If (k>=0)And(k<>DefaultRowHeight) Then begin
        inc(j);
        cfg.SetValue('grid/design/rows/rowcount',j);
        cfg.SetValue('grid/design/rows/row'+IntToStr(j)+'/index', i);
        cfg.SetValue('grid/design/rows/row'+IntToStr(j)+'/height',k);
      End;
    End;


    Path:='grid/design/options/';
    Cfg.SetValue(Path+'goFixedVertLine/value', goFixedVertLine in options);
    Cfg.SetValue(Path+'goFixedHorzLine/value', goFixedHorzLine in options);
    Cfg.SetValue(Path+'goVertLine/value',  goVertLine in options);
    Cfg.SetValue(Path+'goHorzLine/value',  goHorzLine in options);
    Cfg.SetValue(Path+'goRangeSelect/value', goRangeSelect in options);
    Cfg.SetValue(Path+'goDrawFocusSelected/value', goDrawFocusSelected in options);
    Cfg.SetValue(Path+'goRowSizing/value', goRowSizing in options);
    Cfg.SetValue(Path+'goColSizing/value', goColSizing in options);
    Cfg.SetValue(Path+'goRowMoving/value', goRowMoving in options);
    Cfg.SetValue(Path+'goColMoving/value', goColMoving in options);
    Cfg.SetValue(Path+'goEditing/value', goEditing in options);
    Cfg.SetValue(Path+'goTabs/value', goTabs in options);
    Cfg.SetValue(Path+'goRowSelect/value', goRowSelect in options);
    Cfg.SetValue(Path+'goAlwaysShowEditor/value', goAlwaysShowEditor in options);
    Cfg.SetValue(Path+'goThumbTracking/value', goThumbTracking in options);
    Cfg.SetValue(Path+'goColSpanning/value', goColSpanning in options);
    cfg.SetValue(Path+'goRelaxedRowSelect/value', goRelaxedRowSelect in options);
    cfg.SetValue(Path+'goDblClickAutoSize/value', goDblClickAutoSize in options);
    Cfg.SetValue(Path+'goSmoothScroll/value', goSmoothScroll in Options);
  End;
  
  Cfg.SetValue('grid/saveoptions/position', soPosition in SaveOptions);
  If soPosition in SaveOptions then begin
    Cfg.SetValue('grid/position/topleftcol',ftopleft.x);
    Cfg.SetValue('grid/position/topleftrow',ftopleft.y);
    Cfg.SetValue('grid/position/col',fCol);
    Cfg.SetValue('grid/position/row',fRow);
    if goRangeSelect in Options Then begin
      Cfg.SetValue('grid/position/selection/left',Selection.left);
      Cfg.SetValue('grid/position/selection/top',Selection.top);
      Cfg.SetValue('grid/position/selection/right',Selection.right);
      Cfg.SetValue('grid/position/selection/bottom',Selection.bottom);
    End;
  End;
end;

procedure TCustomGrid.LoadContent(cfg: TXMLConfig);
Var
  CreateSaved: Boolean;
  Opt: TGridOptions;
  i,j,k: Integer;
  path: String;
  
    Procedure GetValue(optStr:String; aOpt:TGridOption);
    begin
      If Cfg.GetValue(Path+OptStr+'/value', False) Then Opt:=Opt+[aOpt];
    End;
    
begin
  If soDesign in FSaveOptions Then begin
    CreateSaved:=Cfg.GetValue('grid/saveoptions/create', false);
    If CreateSaved Then begin
      Clear;
      FixedCols:=0;
      FixedRows:=0;
      ColCount:=Cfg.GetValue('grid/design/columncount', 5);
      RowCount:=Cfg.GetValue('grid/design/rowcount', 5);
      FixedCols:=Cfg.GetValue('grid/design/fixedcols', 1);
      FixedRows:=Cfg.GetValue('grid/design/fixedrows', 1);
      DefaultRowheight:=Cfg.GetValue('grid/design/defaultrowheight', 24);
      DefaultColWidth:=Cfg.getValue('grid/design/defaultcolwidth', 64);

      Path:='grid/design/columns/';
      k:=cfg.getValue(Path+'columncount',0);
      For i:=1 to k do begin
        j:=cfg.getValue(Path+'column'+IntToStr(i)+'/index',-1);
        If (j>=0)And(j<=ColCount-1) Then begin
          ColWidths[j]:=cfg.getValue(Path+'column'+IntToStr(i)+'/width',-1);
        End;
      End;
      Path:='grid/design/rows/';
      k:=cfg.getValue(Path+'rowcount',0);
      For i:=1 to k do begin
        j:=cfg.getValue(Path+'row'+IntToStr(i)+'/index',-1);
        If (j>=0)And(j<=ColCount-1) Then begin
          RowHeights[j]:=cfg.getValue(Path+'row'+IntToStr(i)+'/height',-1);
        End;
      End;

      Opt:=[];
      Path:='grid/design/options/';
      GetValue('goFixedVertLine', goFixedVertLine);
      GetValue('goFixedHorzLine', goFixedHorzLine);
      GetValue('goVertLine',goVertLine);
      GetValue('goHorzLine',goHorzLine);
      GetValue('goRangeSelect',goRangeSelect);
      GetValue('goDrawFocusSelected',goDrawFocusSelected);
      GetValue('goRowSizing',goRowSizing);
      GetValue('goColSizing',goColSizing);
      GetValue('goRowMoving',goRowMoving);
      GetValue('goColMoving',goColMoving);
      GetValue('goEditing',goEditing);
      GetValue('goRowSelect',goRowSelect);
      GetValue('goTabs',goTabs);
      GetValue('goAlwaysShowEditor',goAlwaysShowEditor);
      GetValue('goThumbTracking',goThumbTracking);
      GetValue('goColSpanning', goColSpanning);
      GetValue('goRelaxedRowSelect',goRelaxedRowSelect);
      GetValue('goDblClickAutoSize',goDblClickAutoSize);
      If GRIDFILEVERSION>=2 Then begin
        GetValue('goSmoothScroll',goSmoothScroll);
      End;
      
      Options:=Opt;
    End;

    CreateSaved:=Cfg.GetValue('grid/saveoptions/position', false);
    If CreateSaved Then begin
      i:=Cfg.GetValue('grid/position/topleftcol',-1);
      j:=Cfg.GetValue('grid/position/topleftrow',-1);
      if MouseToGridZone(i,j,true)=gzNormal Then Begin
        tryScrollto(i,j);
      End;
      i:=Cfg.GetValue('grid/position/col',-1);
      j:=Cfg.GetValue('grid/position/row',-1);
      If (i>=FFixedCols)And(i<=ColCount-1) And
         (j>=FFixedRows)And(j<=RowCount-1) Then Begin
        MoveExtend(false, i,j);
      End;
      if goRangeSelect in Options Then begin
        FRange.left:=Cfg.getValue('grid/position/selection/left',FCol);
        FRange.Top:=Cfg.getValue('grid/position/selection/top',FRow);
        FRange.Right:=Cfg.getValue('grid/position/selection/right',FCol);
        FRange.Bottom:=Cfg.getValue('grid/position/selection/bottom',FRow);
      End;
    End;
  end;
end;

constructor TCustomGrid.Create(AOwner: TComponent);
begin
  // Inherited create Calls SetBounds->WM_SIZE->VisualChange so
  // fGrid needs to be created before that
  FCols:=TList.Create;
  FRows:=TList.Create;
  FGCache.AccumWidth:=TList.Create;
  FGCache.AccumHeight:=TList.Create;
  inherited Create(AOwner);
  AutoScroll:=False;
  FOptions:=
    [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect,
     goSmoothScroll ];
  FScrollbars:=ssAutoBoth;
  fGridState:=gsNormal;
  fDefColWidth:=64;//40;
  fDefRowHeight:=24;//18;
  fGridLineColor:=clGray;
  FGridLineStyle:=psSolid;
  fFocusColor:=clRed;

  FRange:=Rect(-1,-1,-1,-1);
  FDragDx:=3;

  SetBounds(0,0,200,100);
  ColCount:=5;
  RowCount:=5;
  FixedCols:=1;
  FixedRows:=1;
  Editor:=nil;
end;

destructor TCustomGrid.Destroy;
begin
  {$Ifdef dbg}WriteLn('TCustomGrid.Destroy');{$Endif}
  FreeThenNil(FGCache.AccumWidth);
  FreeThenNil(FGCache.AccumHeight);
  FreeThenNil(FCols);
  FreeThenNil(FRows);
  inherited Destroy;
End;

procedure TCustomGrid.SaveToFile(FileName: String);
Var
  Cfg: TXMLConfig;
begin
  If FileExists(FileName) then DeleteFile(FileName);

  Cfg:=TXMLConfig.Create(FileName);
  Try
    SaveContent(Cfg);
  Finally
    Cfg.Flush;
    FreeThenNil(Cfg);
  End;
end;

procedure TCustomGrid.LoadFromFile(FileName: String);
Var
  Cfg: TXMLConfig;
  Version: Integer;
begin
  If Not FileExists(FileName) Then
    Raise Exception.Create('Grid file doesn''t exists');
    
  Cfg:=TXMLConfig.Create(FileName);
  Try
    Version:=cfg.GetValue('grid/version',-1);
    if Version=-1 Then Raise Exception.Create('Not a valid grid file');
    BeginUpdate;
    LoadContent(Cfg);
    EndUpdate(True);
  Finally
    FreeThenNil(Cfg);
  End;
end;

procedure TCustomGrid.Clear;
Var
  OldR,OldC: Integer;
begin
  OldR:=RowCount;
  OldC:=ColCount;
  FFixedCols:=0;
  FFixedRows:=0;
  FRows.Count:=0;
  FCols.Count:=0;
  FTopLeft:=Point(-1,-1);
  VisualChange;
  SizeChanged(OldR,OldC);
end;

procedure TCustomGrid.AutoAdjustColumns;
Var
  i: Integer;
begin
  For i:=0 to ColCount do
    AutoAdjustColumn(i);
end;

{ TVirtualGrid }

function TVirtualGrid.GetCells(Col, Row: Integer): PCellProps;
begin
  // todo: Check range
  Result:=nil;
  try
    Result:=FCells[Col,Row];
  Except
    WriteLn('GetCell: Index Out of range Cell[Col=',Col,' Row=',Row,']');
  End;
end;

Function Tvirtualgrid.Getrows(Row: Integer): Pcellprops;
Begin
  Result:= FRows[Row, 0];
End;

Function Tvirtualgrid.Getcols(Col: Integer): Pcellprops;
Begin
  result:=FCols[Col, 0];
End;

procedure TVirtualGrid.SetCells(Col, Row: Integer; const AValue: PCellProps);
Var
   Cell: PCellProps;
begin
  // todo: Check range
  Cell:=FCells[Col,Row];
  If Cell<>nil Then DisposeCell(Cell);
  Cell:=AValue;
  FCells[Col,Row]:=Cell;
end;

Procedure Tvirtualgrid.Setrows(Row: Integer; Const Avalue: Pcellprops);
Var
   Cell: PCellProps;
begin
  // todo: Check range
  Cell:=FRows[Row,0];
  If Cell<>nil Then DisposeCell(Cell);
  FCells[Row,0]:=AValue;
end;

Procedure Tvirtualgrid.Setcolcount(Const Avalue: Integer);
Begin
  If FColCount=Avalue then Exit;
  {$Ifdef dbgMem}
    WriteLn('TVirtualGrid.SetColCount Value=',AValue);
  {$Endif}
  FColCount:=AValue;
  {$Ifdef dbgMem}
    Write('TVirtualGrid.SetColCount->FCOLS: ');
  {$Endif}
  FCols.SetLength(FColCount, 1);
  {$Ifdef dbgMem}
    Write('TVirtualGrid.SetColCount->FCELLS(',FColCount,',',FRowCount,'): ');
  {$Endif}
  FCells.SetLength(FColCount, FRowCount);
End;


Procedure Tvirtualgrid.Setrowcount(Const Avalue: Integer);
Begin
  If FRowCount=AValue Then Exit;
  {$Ifdef dbgMem}
    WriteLn('TVirtualGrid.SetRowCount Value=',AValue);
  {$Endif}
  FRowCount:=AValue;
  {$Ifdef dbgMem}
    Write('TVirtualGrid.SetRowCount->FROWS: ');
  {$Endif}
  FRows.SetLength(FRowCount,1);
  {$Ifdef dbgMem}
    Write('TVirtualGrid.SetRowCount->FCELLS(',FColCount,',',FRowCount,'): ');
  {$Endif}
  FCells.SetLength(FColCount, FRowCount);
End;

Procedure Tvirtualgrid.Setcols(Col: Integer; Const Avalue: Pcellprops);
Var
   Cell: PCellProps;
begin
  // todo: Check range
  Cell:=FCols[Col,0];
  If Cell<>nil Then DisposeCell(Cell);
  FCols[Col,0]:=AValue;
End;

Procedure Tvirtualgrid.Clear;
Begin
  {$Ifdef dbgMem}Write('FROWS: ');{$Endif}FRows.Clear;
  {$Ifdef dbgMem}Write('FCOLS: ');{$Endif}FCols.Clear;
  {$Ifdef dbgMem}Write('FCELLS: ');{$Endif}FCells.Clear;
  FColCount:=0;
  FRowCount:=0;
End;

Procedure Tvirtualgrid.Disposecell(Var P: Pcellprops);
Begin
  If P<>nil then begin
    If P^.Text<>nil Then begin
      StrDispose(P^.Text);
    End;
    If P^.Attr<>nil Then Dispose(P^.Attr);
    Dispose(P);
    P:=nil;
  End;
End;

function TVirtualGrid.GetDefaultCell: PcellProps;
begin
  New(Result);
  Result^.Text:=nil;
  Result^.Attr:=nil;
end;

Procedure Tvirtualgrid.Dodestroyitem (Sender: Tobject; Col,Row: Integer;
  Var Item: Pointer);
Begin
  {$IFNDef LogNil} If Item<>nil then {$Endif}
  {$Ifdef dbgMem}
    WriteLn('TVirtualGrid.doDestroyItem Col=',Col,' Row= ',
            Row,' Item=',Integer(Item));
  {$endif}
  If Item<>nil Then begin
    DisposeCell(pCellProps(Item));
    Item:=nil;
  End;
End;

Procedure Tvirtualgrid.Donewitem(Sender: Tobject; Col,Row:Integer;
  Var Item: Pointer);
Begin
  {$Ifdef dbgMem}
    WriteLn('TVirtualGrid.doNewItem Col=',Col,' Row= ',
            Row,' Item=',Integer(Item));
  {$endif}
  If Sender=FCols Then begin
    // Procesar Nueva Columna
    Item:=GetDefaultCell;
  End Else
  if Sender=FRows Then begin
    // Procesar Nuevo Renglon
    Item:=GetDefaultCell;
  End Else begin
    // Procesar Nueva Celda
    Item:=nil;
  End;
End;

constructor TVirtualGrid.Create;
begin
  Inherited Create;
  {$Ifdef dbg}WriteLn('TVirtualGrid.Create');{$Endif}
  FCells:=TArray.Create;
  FCells.OnDestroyItem:=@doDestroyItem;
  FCells.OnNewItem:=@doNewItem;
  FCols:= TArray.Create;
  FCols.OnDestroyItem:=@doDestroyItem;
  FCols.OnNewItem:=@doNewItem;
  FRows:=TArray.Create;
  FRows.OnDestroyItem:=@doDestroyItem;
  FRows.OnNewItem:=@doNewItem;
  RowCount:=4;
  ColCount:=4;
end;

destructor TVirtualGrid.Destroy;
begin
  {$Ifdef dbg}WriteLn('TVirtualGrid.Destroy');{$Endif}
  Clear;
  FreeThenNil(FRows);
  FreeThenNil(FCols);
  FreeThenNil(FCells);
  inherited Destroy;
end;
procedure TVirtualGrid.DeleteColRow(IsColumn: Boolean; Index: Integer);
begin
  FCells.DeleteColRow(IsColumn, Index);
  If IsColumn Then begin
    FCols.DeleteColRow(True, Index);
    Dec(FColCount);
  End Else begin
    FRows.DeleteColRow(True, Index);
    Dec(fRowCount);
  End;
end;

procedure TVirtualGrid.MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer
  );
begin
  FCells.MoveColRow(IsColumn, FromIndex, ToIndex);
  If IsColumn then FCols.MoveColRow(True, FromIndex, ToIndex)
  Else             FRows.MoveColRow(True, FromIndex, ToIndex);
end;

procedure TVirtualGrid.ExchangeColRow(IsColumn: Boolean; Index,
  WithIndex: Integer);
begin
  FCells.ExchangeColRow(IsColumn, Index, WithIndex);
  If IsColumn Then FCols.ExchangeColRow(true, Index, WithIndex)
  Else             FRows.ExchangeColRow(True, Index, WithIndex);
end;

{ TStringCellEditor }

procedure TStringCellEditor.doExit;
begin
  inherited doExit;
  FGrid.ExitEditor(Self);
end;

procedure TStringCellEditor.KeyDown(var Key: Word; Shift: TShiftState);
begin
  {$IfDef dbg}
  WriteLn('INI: Key=',Key,' SelStart=',SelStart,' SelLenght=',SelLength);
  {$Endif}
  If FGrid<>nil then Fgrid.EditorKeyDown(Key, Shift);
  {$IfDef dbg}
  WriteLn('FIN: Key=',Key,' SelStart=',SelStart,' SelLenght=',SelLength);
  {$Endif}
  inherited KeyDown(Key, Shift);
end;

procedure TStringCellEditor.msg_SetValue(var Msg: TLMessage);
begin
  With PGridMessage(Msg.LParam)^  do begin
    Text:= Value;
  End;
end;

procedure TStringCellEditor.msg_GetValue(var Msg: TLMessage);
begin
  With PGridMessage(Msg.LParam)^  do begin
    Value:=Text;
  End;
end;

procedure TStringCellEditor.msg_SetGrid(var Msg: TLMessage);
begin
  FGrid:=TCustomGrid(Msg.WParam);
end;

{ TDrawGrid }

function TDrawGrid.GetCellAttr(ACol, ARow: Integer): TCellAttr;
Var
  c: PCellProps;
begin
  C:=FGrid.Celda[ACol,ARow];
  If (C<>nil)And(C^.Attr<>nil) then Result:=C^.Attr^
  Else                              Result:=FDefCellAttr;
end;

function TDrawGrid.GetCellAlign(ACol, ARow: Integer): Integer;
Var
  Attr: TCellAttr;
begin
  Attr:=GetCellAttr(Acol,ARow);
  AttrToCellAlign(Attr, Result);
end;

function TDrawGrid.GetCellColor(ACol, ARow: Integer): TColor;
Var
  Attr: TCellAttr;
begin
  Attr:=GetCellAttr(ACol,ARow);
  Result:=Attr.Color;
end;

function TDrawGrid.GetCellFontCOlor(ACol, ARow: Integer): TColor;
Var
  Attr: TCellAttr;
begin
  Attr:=GetCellAttr(ACol,ARow);
  Result:=Attr.FontColor;
end;

function TDrawGrid.GetColAlign(aCol: Integer): Integer;
Var
  Attr: TCellAttr;
begin
  Attr:=GetColAttr(aCol);
  AttrToCellAlign(Attr, Result);
end;

function TDrawGrid.GetColAttr(aCol: Integer): TCellAttr;
Var
  c: PCellProps;
begin
  C:=FGrid.Cols[ACol];
  If (C<>nil)And(C^.Attr<>nil) then Result:=C^.Attr^
  Else                              Result:=FDefCellAttr;
end;

function TDrawGrid.GetColColor(aCol: Integer): TColor;
Var
  Attr: TCellAttr;
begin
  Attr:=GetColAttr(ACol);
  Result:=Attr.Color;
end;

function TDrawGrid.GetColFontColor(aCol: Integer): TColor;
Var
  Attr: TCellAttr;
begin
  Attr:=GetColAttr(ACol);
  Result:=Attr.FontColor;
end;

function TDrawGrid.GetFixedColor: TColor;
begin
  Result:=fDefFixedCellAttr.Color;
end;

function TDrawGrid.GetRowAlign(aRow: Integer): Integer;
Var
  Attr: TCellAttr;
begin
  Attr:=GetRowAttr(aRow);
  AttrToCellAlign(Attr, Result);
end;

function TDrawGrid.GetRowAttr(aRow: Integer): TCellAttr;
Var
  c: PCellProps;
begin
  C:=FGrid.Rows[ARow];
  If (C<>nil)And(C^.Attr<>nil) then Result:=C^.Attr^
  Else                              Result:=FDefCellAttr;
end;

function TDrawGrid.GetRowColor(aRow: Integer): TColor;
Var
  Attr: TCellAttr;
begin
  Attr:=GetColAttr(ARow);
  Result:=Attr.Color;
end;

function TDrawGrid.GetRowFontColor(aRow: Integer): TColor;
Var
  Attr: TCellAttr;
begin
  Attr:=GetColAttr(ARow);
  Result:=Attr.FontColor;
end;

procedure TDrawGrid.SetCellAlign(ACol, ARow: Integer; const AValue: Integer);
Var
  Attr: TCellAttr;
begin
  Attr:=GetCellAttr(ACol, Arow);
  CellAlignToAttr(aValue, Attr);
  SetCellAttr(ACol,ARow,Attr);
end;

procedure TDrawGrid.SetCellAttr(ACol, ARow: Integer; const AValue: TCellAttr);
Var
  c: PCellProps;
  IsNew: Boolean;
begin
  C:=FGrid.Celda[ACol,ARow];
  IsNew:=C=nil;
  If IsNew Then C:=FGrid.GetDefaultCell;
  if C^.Attr=nil Then New(C^.Attr);
  C^.Attr^:=Avalue;
  If IsNew then FGrid.Celda[aCol,ARow]:=C; // Celda takes care
end;

procedure TDrawGrid.SetCellColor(ACol, ARow: Integer; const AValue: TColor);
Var
  Attr: TCellAttr;
begin
  Attr:=GetCellAttr(ACol, Arow);
  attr.Color:=AValue;
  SetCellAttr(ACol,ARow,Attr);
end;

procedure TDrawGrid.SetCellFontCOlor(ACol, ARow: Integer; const AValue: TColor);
Var
  Attr: TCellAttr;
begin
  Attr:=GetCellAttr(ACol, Arow);
  Attr.FontColor:=Avalue;
  SetCellAttr(ACol,ARow,Attr);
end;

procedure TDrawGrid.SetColAlign(aCol: Integer; const AValue: Integer);
Var
  Attr: TCellAttr;
begin
  Attr:=GetColAttr(ACol);
  CellAlignToAttr(aValue, Attr);
  SetColAttr(aCol, Attr);
end;

procedure TDrawGrid.SetColAttr(aCol: Integer; const AValue: TCellAttr);
Var
  c: PCellProps;
  IsNew: Boolean;
begin
  C:=FGrid.Cols[ACol];
  IsNew:=C=nil;
  If IsNew Then C:=FGrid.GetDefaultCell;
  if C^.Attr=nil Then New(C^.Attr);
  C^.Attr^:=Avalue;
  If IsNew then FGrid.Cols[aCol]:=C; // Celda takes care
end;

procedure TDrawGrid.SetColColor(aCol: Integer; const AValue: TColor);
Var
  Attr: TCellAttr;
begin
  Attr:=GetColAttr(ACol);
  Attr.Color:=AValue;
  SetColAttr(aCol, Attr);
end;

procedure TDrawGrid.SetColFontColor(aCol: Integer; const AValue: TColor);
Var
  Attr: TCellAttr;
begin
  Attr:=GetColAttr(ACol);
  Attr.FontColor:=AValue;
  SetColAttr(aCol, Attr);
end;

procedure TDrawGrid.SetDefaultCellAttr(const AValue: TCellAttr);
begin
  If CellAttrIgual(FDefCellAttr, AValue) Then Exit;
  FDefCellAttr:=AValue;
  Invalidate;
end;

procedure TDrawGrid.SetFixedColor(const AValue: TColor);
begin
  if fDefFixedCellAttr.Color=AValue then exit;
  fDefFixedCellAttr.Color:=aValue;
  Invalidate;
end;

procedure TDrawGrid.SetRowAlign(aRow: Integer; const AValue: Integer);
Var
  Attr: TCellAttr;
begin
  Attr:=GetRowAttr(ARow);
  CellAlignToAttr(Avalue, Attr);
  SetRowAttr(aRow, Attr);
end;

procedure TDrawGrid.SetRowAttr(aRow: Integer; const AValue: TCellAttr);
Var
  c: PCellProps;
  IsNew: Boolean;
begin
  C:=FGrid.Rows[aRow];
  IsNew:=C=nil;
  If IsNew Then C:=FGrid.GetDefaultCell;
  if C^.Attr=nil Then New(C^.Attr);
  C^.Attr^:=Avalue;
  If IsNew then FGrid.Rows[aRow]:=C; // Celda takes care
end;

procedure TDrawGrid.SetRowColor(aRow: Integer; const AValue: TColor);
Var
  Attr: TCellAttr;
begin
  Attr:=GetRowAttr(ARow);
  Attr.Color:=AValue;
  SetRowAttr(aRow, Attr);
end;

procedure TDrawGrid.SetRowFontColor(aRow: Integer; const AValue: TColor);
Var
  Attr: TCellAttr;
begin
  Attr:=GetRowAttr(ARow);
  Attr.FontColor:=AValue;
  SetRowAttr(aRow, Attr);
end;


procedure TDrawGrid.CalcCellExtent(acol, aRow: Integer; var aRect: TRect);
begin
  //
end;

procedure TDrawGrid.DrawCell(aCol,aRow: Integer; aRect: TRect;
  aState:TGridDrawState);
Begin
  If Assigned(OnDrawCell) Then OnDrawCell(Self,aCol,aRow,aRect,aState)
  Else                          DefaultDrawCell(aCol,aRow,aRect,aState);
  Inherited DrawCellGrid(aRect,aCol,aRow,aState); // Draw the grid
End;

procedure TDrawGrid.DrawFocusRect(aCol, aRow: Integer; ARect: TRect;
  aState: TGridDrawstate);
begin
  // Draw focused cell if we have the focus
  If Self.Focused Then begin
    If (gdFocused in aState)Then begin
      Canvas.Pen.Color:=FFocusColor;
      Canvas.Pen.Style:=psDot;
      If goRowSelect in Options Then begin
        Canvas.MoveTo(FGCache.FixedWidth+1, aRect.Top+1);
        Canvas.LineTo(FGCache.MaxClientXY.x-1, aRect.Top+1);
        Canvas.LineTo(FGCache.MaxClientXY.x-1, aRect.Bottom-1);
        Canvas.LineTo(FGCache.FixedWidth+1, aRect.Bottom-1);
        Canvas.LineTo(FGCache.FixedWidth+1, aRect.Top+1);
      End Else begin
        Canvas.MoveTo(aRect.Left+1, aRect.Top+1);
        Canvas.LineTo(ARect.Right-1, ARect.Top+1);
        Canvas.LineTo(aRect.Right-1, aRect.bottom-1);
        Canvas.LineTo(aRect.Left+1, aRect.Bottom-1);
        Canvas.Lineto(aRect.left+1, aRect.top+1);
      End;
      Canvas.Pen.Style:=psSolid;
    End;
  End;
end;

procedure TDrawGrid.ColRowExchanged(IsColumn:Boolean; Index, WithIndex: Integer);
begin
  Fgrid.ExchangeColRow(IsColumn, Index, WithIndex);
  if Assigned(OnColRowExchanged) Then
    OnColRowExchanged(Self, IsColumn, Index, WithIndex);
end;

procedure TDrawGrid.ColRowDeleted(IsColumn: Boolean; Index: Integer);
begin
  FGrid.DeleteColRow(IsColumn, Index);
  If Assigned(OnColRowDeleted) Then
    OnColRowDeleted(Self, IsColumn, Index, Index);
end;

procedure TDrawGrid.ColRowMoved(IsColumn: Boolean; FromIndex, ToIndex: Integer);
begin
  FGrid.MoveColRow(IsColumn, FromIndex, ToIndex);
  If Assigned(OnColRowMoved) Then
    OnColRowMoved(Self, IsColumn, FromIndex, toIndex);
end;

procedure TDrawGrid.HeaderClick(IsColumn: Boolean; Index: Integer);
begin
  inherited HeaderClick(IsColumn, Index);
  If Assigned(OnHeaderClick) Then OnHeaderClick(Self, IsColumn, Index);
end;

procedure TDrawGrid.SizeChanged(OldColCount, OldRowCount: Integer);
begin
  If OldColCount<>ColCount Then fGrid.ColCount:=ColCOunt;
  If OldRowCount<>RowCount Then fGrid.RowCount:=RowCount;
end;

procedure TDrawGrid.SaveContent(cfg: TXMLConfig);
Var
  i,j,k: Integer;
  c: PCellProps;
  path: String;

  procedure SaveAttr;
  begin
    With c^.Attr^ do begin
      Cfg.SetValue(Path+'/color', ColorToString(Color));
      Cfg.SetValue(Path+'/fontcolor',ColorToString(FontColor));
      Cfg.SetValue(Path+'/textstyle/alignment/value', Ord(TextStyle.Alignment));
      cfg.SetValue(Path+'/textstyle/layout/value',Ord(TextStyle.Layout));
      cfg.SetValue(Path+'/textstyle/singleLine/value',TextStyle.SingleLine);
      cfg.SetValue(Path+'/textstyle/clipping/value',TextStyle.Clipping);
      cfg.SetValue(Path+'/textstyle/wordbreak/value',TextStyle.WordBreak);
      cfg.SetValue(Path+'/textstyle/opaque/value',TextStyle.Opaque);
      cfg.SetValue(Path+'/textstyle/systemfont/value',TextStyle.SystemFont);
    End;
  End;
begin
  Inherited SaveContent(cfg);
  Cfg.SetValue('grid/saveoptions/attributes', soAttributes in SaveOptions);
  if Not (soAttributes in SaveOptions) then Exit;
  
  // Save Columns
  j:=0;
  For i:=0 to ColCount-1 do begin
    c:=fGrid.Cols[i];
    If (c<>nil)And(c^.Attr<>nil) Then begin
      Inc(j);
      Cfg.SetValue('grid/attributes/columns/columncount', j);
      path:='grid/attributes/columns/column'+IntToStr(j);
      Cfg.SetValue(Path+'/index', i);
      SaveAttr;
    End;
  End;
  // Save Rows
  j:=0;
  For i:=0 to RowCount-1 do begin
    c:=fGrid.Rows[i];
    If (c<>nil)And(c^.Attr<>nil) Then begin
      Inc(j);
      Path:='grid/attributes/rows/row'+IntToStr(j);
      Cfg.SetValue(Path+'/index', i);
      SaveAttr;
    End;
  End;
  // Save attributtes of Cells
  k:=0;
  For i:=0 to ColCount-1 do
    For j:=0 to RowCount-1 do begin
      C:=fGrid.Celda[i,j];
      If (c<>nil)And(c^.Attr<>nil) Then begin
        Inc(k);
        Cfg.SetValue('grid/attributes/cells/cellcount',k);
        Path:='grid/attributes/cells/cell'+IntToStr(k);
        cfg.SetValue(Path+'/column',i);
        cfg.SetValue(Path+'/row',j);
        SaveAttr;
      End;
    End;
end;

procedure TDrawGrid.LoadContent(Cfg: TXMLConfig);
Var
  i,j,k: Integer;
  B: Boolean;
  path: String;
begin

  Inherited LoadContent(Cfg);

  If Not (soAttributes in SaveOptions) Then Exit;
  B:=Cfg.GetValue('grid/saveoptions/attributes',false);
  If Not B Then Exit;

  // Load Columns
  Path:='grid/attributes/columns/';
  k:=cfg.getValue(Path+'columncount',0);
  For i:=1 to k do begin
    j:=cfg.getValue(Path+'column'+IntToStr(i)+'/index', -1);
    if (j>=0)And(j<=Colcount-1) Then begin
      ColAttr[j]:=LoadCellAttrFromXMLPath(cfg, Path+'column'+IntToStr(i));
    End;
  End;
  // Load Rows
  Path:='grid/attributes/rows/';
  k:=cfg.getValue(Path+'rowcount',0);
  For i:=1 to k do begin
    j:=cfg.getValue(Path+'row'+IntToStr(i)+'/index', -1);
    if (j>=0)and(j<=RowCount-1) Then begin
      RowAttr[j]:=LoadCellAttrFromXMLPath(cfg, Path+'row'+IntToStr(i));
    End;
  End;
  // Load Cells
  Path:='grid/attributes/cells/';
  k:=cfg.getValue(Path+'cellcount',0);
  While k>0 do begin
    i:=cfg.getValue(Path+'cell'+inttoStr(k)+'/column', -1);
    j:=cfg.getValue(Path+'cell'+inttostr(k)+'/row', -1);
    if (j>=0)And(j<=rowcount-1)and(i>=0)and(i<=Colcount-1) Then begin
      CellAttr[i,j]:=LoadCellAttrFromXMLPath(cfg, Path+'cell'+IntToStr(k));
    End;
    dec(k);
  End;
end;

constructor TDrawGrid.Create(AOwner: TComponent);
begin
  fGrid:=TVirtualGrid.Create;
  FDefCellAttr:=GetDefaultCellAttr;
  FDefSelCellAttr:=FDefCellAttr;
  With FDefSelCellAttr do begin
    Color:=clBlack;
    FontColor:=clWhite;
  End;
  fdefFixedCellAttr:=FDefCelLAttr;
  fdefFixedCellAttr.Color:=clBtnFace;
  inherited Create(AOwner);
end;

destructor TDrawGrid.Destroy;
begin
  {$Ifdef dbg}WriteLn('TDrawGrid.Destroy');{$Endif}
  FreeThenNil(FGrid);
  inherited Destroy;
end;

procedure TDrawGrid.DefaultDrawCell(aCol, aRow: Integer; Var aRect: TRect;
  aState: TGridDrawState);
Var
   c: PcellProps;
begin
  // Set draw Cell Attributes
  if DefaultDrawing then Begin
    fCellAttr.Color:=Self.Color;
    FCellAttr.FontColor:=Self.Font.Color;
    FCellAttr.TextStyle.Clipping:=False;
  End Else begin
    // Column -> Row -> Cell Specific Attributtes
    fCellAttr:=fDefCellAttr;
    If gdSelected in aState then FCellAttr:=FDefSelCellAttr
    Else begin
      If gdFixed in aState Then begin
        FCellAttr:=FDefFixedCellAttr;
        Case MouseToGridZone(aCol, aRow, true) of
          gzFixedRows: C:=FGrid.Cols[aCol];
          gzFixedCols: C:=FGrid.Rows[aRow];
          Else c:=nil;
        End;
        If (c<>nil)and(C^.Attr<>nil) Then FCellAttr:=C^.Attr^;
      End
      Else begin
        C:=FGrid.Cols[aCol]; If (c<>nil)and(C^.Attr<>nil) Then FCellAttr:=C^.Attr^;
        C:=FGrid.Rows[aRow]; If (c<>nil)And(C^.Attr<>nil) Then FCellAttr:=C^.Attr^;
      End;
      C:= FGrid.Celda[aCol,aRow];If (C<>nil)And(C^.Attr<>nil) Then FCellAttr:=C^.Attr^;
    End;
    if Assigned(fonCellAttr) Then fonCellAttr(Self, aCol,aRow, aState, FCellAttr);
    If goColSpanning in Options Then CalcCellExtent(acol, arow, aRect);
  End;
  Canvas.Brush.Color:=fCellAttr.Color;
  Canvas.Font.Color:=fCellAttr.FontColor;
  Canvas.FillRect(aRect);
end;

{ TStringGrid }

Function TStringGrid.Getcells(aCol, aRow: Integer): String;
Var
   C: PCellProps;
Begin
  Result:='';
  C:=FGrid.Celda[aCol,aRow];
  If C<>nil Then Result:=C^ .Text;
End;

function TStringGrid.GetCols(Index: Integer): TStrings;
Var
  i,j: Integer;
begin
  Result:=nil;
  If (ColCount>0)And(index>=0)and(Index<ColCount) Then begin
    Result:=TStringList.Create;
    For i:=0 to RowCount-1 do Begin
      j:=Result.Add( Cells[Index, i] );
      Result.Objects[j]:=Objects[Index, i];
    End;
  End;
end;

function TStringGrid.GetObjects(ACol, ARow: Integer): TObject;
Var
  C: PCellProps;
begin
  Result:=nil;
  C:=Fgrid.Celda[aCol,aRow];
  If C<>nil Then Result:=C^.Data;
end;

function TStringGrid.GetRows(Index: Integer): TStrings;
Var
  i,j: Integer;
begin
  Result:=nil;
  If (RowCount>0)And(index>=0)and(Index<RowCount) Then begin
    Result:=TStringList.Create;
    For i:=0 to ColCount-1 do Begin
      j:=Result.Add( Cells[i, Index] );
      Result.Objects[j]:=Objects[i, Index];
    End;
  End;
end;

Procedure TStringGrid.Setcells(aCol, aRow: Integer; Const Avalue: String);
Var
  C: PCellProps;
Begin
  C:= FGrid.Celda[aCol,aRow];
  If C<>nil Then begin
    If C^.Text<>nil Then StrDispose(C^.Text);
    C^.Text:=StrNew(pchar(aValue));
    InvalidateCell(aCol, aRow);
  End Else Begin
    If AValue<>'' Then Begin
      New(C);
      C^.Text:=StrNew(pchar(Avalue));
      C^.Attr:=nil;
      FGrid.Celda[aCol,aRow]:=C;
      InvalidateCell(aCol, aRow);
    End;
  End;
End;

procedure TStringGrid.SetCols(Index: Integer; const AValue: TStrings);
begin

end;

procedure TStringGrid.SetObjects(ACol, ARow: Integer; AValue: TObject);
Var
  c: PCellProps;
begin
  C:=FGrid.Celda[aCol,aRow];
  If c<>nil Then C^.Data:=AValue
  Else begin
    c:=fGrid.GetDefaultCell;
    c^.Data:=Avalue;
    FGrid.Celda[aCol,aRow]:=c;
  End;
end;

procedure TStringGrid.SetRows(Index: Integer; const AValue: TStrings);
begin

end;

procedure TStringGrid.AutoAdjustColumn(aCol: Integer);
Var
  i,W: Integer;
  Ts: TSize;
begin
  if (aCol<0)or(aCol>ColCount-1) Then Exit;
  W:=0;
  For i:=0 to RowCount-1 do begin
    Ts:=Canvas.TextExtent(Cells[aCol, i]);
    If Ts.Cx>W Then W:=Ts.Cx;
  End;
  If W=0 Then W:=DefaultColWidth
  Else        W:=W + 8;
  ColWidths[aCol]:=W;
end;

procedure TStringGrid.CalcCellExtent(acol, aRow: Integer; var aRect: TRect);
Var
  S: String;
  Ts: Tsize;
  nc: PcellProps;
  i: integer;
begin
  inherited CalcCellExtent(acol,arow, aRect);
  S:=Cells[aCol,aRow];
  If Not FCellAttr.TextStyle.Clipping Then begin
    // Calcular el numero de celdas necesarias para contener todo
    // El Texto
    Ts:=Canvas.TextExtent(S);
    i:=aCol;
    While (Ts.Cx>(aRect.Right-aRect.Left))and(i<ColCount) do begin
      inc(i);
      Nc:=FGrid.Celda[i, aRow];
      if (nc<>nil)And(Nc^.Text<>'')Then Break;
      aRect.Right:=aRect.Right + getColWidths(i);
    End;
    fcellAttr.TextStyle.Clipping:=i<>aCol;
  End;
end;

procedure TStringGrid.DrawCell(aCol, aRow: Integer; aRect: TRect;
  aState: TGridDrawState);
Var
  S: String;
begin
  inherited DrawCell(aCol, aRow, aRect, aState);
  S:=Cells[aCol,aRow];
  If S<>'' Then begin
    Canvas.TextRect(aRect, 3, 0, S, FCellAttr.TextStyle);
  End;
end;

procedure TStringGrid.GetEditorValue;
Var
  msg: TGridMessage;
begin
  If (FEditor<>nil) And FEditor.Visible Then Begin
    Msg.grid:=Self;
    Msg.Col:=FCol;
    msg.Row:=FRow;
    msg.Value:='';
    FEditor.Perform(GM_GETVALUE, Integer(Self), Integer(@Msg));
    Cells[FCol,FRow]:=msg.Value;
  End;
  inherited GetEditorValue;
end;

procedure TStringGrid.SetEditorValue;
Var
  msg: TGridMessage;
begin
  if FEditor<>nil Then begin
    Msg.Grid:=Self;
    Msg.Col:=FCol;
    Msg.Row:=FRow;
    Msg.Value:=Cells[FCol,FRow];
    FEditor.Perform(GM_SETVALUE, Integer(Self), Integer(@msg));
  End;
  inherited SetEditorValue;
end;

procedure TStringGrid.SaveContent(cfg: TXMLConfig);
Var
  i,j,k: Integer;
  c: PCellProps;
begin
  inherited SaveContent(cfg);
  cfg.SetValue('grid/saveoptions/content', soContent in SaveOptions);
  If soContent in SaveOptions Then begin
    // Save Cell Contents
    k:=0;
    For i:=0 to ColCount-1 do
      For j:=0 to RowCount-1 do begin
        C:=fGrid.Celda[i,j];
        If (c<>nil) And (C^.Text<>'') Then begin
          Inc(k);
          Cfg.SetValue('grid/content/cells/cellcount',k);
          cfg.SetValue('grid/content/cells/cell'+IntToStr(k)+'/column',i);
          cfg.SetValue('grid/content/cells/cell'+IntToStr(k)+'/row',j);
          cfg.SetValue('grid/content/cells/cell'+IntToStr(k)+'/text', c^.Text);
        End;
      End;
   End;
end;

procedure TStringGrid.LoadContent(Cfg: TXMLConfig);
Var
  ContentSaved: Boolean;
  i,j,k: Integer;
begin
  inherited LoadContent(Cfg);
  If soContent in FSaveOptions Then begin
    ContentSaved:=Cfg.GetValue('grid/saveoptions/content', false);
    If ContentSaved Then begin
      k:=cfg.getValue('grid/content/cells/cellcount', 0);
      While k>0 do begin
        i:=cfg.GetValue('grid/content/cells/cell'+IntToStr(k)+'/column', -1);
        j:=cfg.GetValue('grid/content/cells/cell'+IntTostr(k)+'/row',-1);
        if (j>=0)And(j<=rowcount-1)and(i>=0)and(i<=Colcount-1) Then
          Cells[i,j]:=cfg.GetValue('grid/content/cells/cell'+IntToStr(k)+'/text','');
        Dec(k);
      End;
    End;
  End;
end;

procedure TStringGrid.DrawInteriorCells;
Var
  i,j: Integer;
  gds: TGridDrawState;
  c: PCellProps;
begin
  With FGCache.VisibleGrid do
  if goColSpanning in Options Then begin
    //
    // Ordered draw should be done in order to this work
    //
    Gds:=[];
    // Draw Empty (nil) cells First
    For i:=Left to Right do
      For j:=Top to Bottom do begin
        if IsCellSelected(i,j) Then Continue;
        C:=Fgrid.Celda[i,j];
        If (c=nil) Then DrawCell(i,j, ColRowToClientCellRect(i,j), gds);
      End;
     // Draw Cells Empty Cells (Text='') With Attribute
    For i:=Left to Right do
      For j:=Top to Bottom do begin
        if IsCellSelected(i,j) Then Continue;
        If (i=FCol)or(j=FRow) Then Continue;
        C:=Fgrid.Celda[i,j];
        If (c<>nil)And(C^.Text='') Then
          DrawCell(i,j, ColRowToClientCellRect(i,j), gds);
      End;
    // Draw Cells Not Empty (Text<>'')
    For i:=Left to Right do
      For j:=Top to Bottom do begin
        if IsCellSelected(i,j) Then Continue;
        C:=Fgrid.Celda[i,j];
        If (C<>nil)And(C^.Text<>'') Then
          DrawCell(i,j, ColRowToClientCellRect(i,j), gds);
      End;

    gds:=[gdSelected];
    For i:=Left To Right do
      For j:=Top to Bottom do
        If IsCellSelected(i,j) Then begin
          DrawCell(i,j, colRowToClientCellRect(i,j), gds);
        End;

  End else inherited DrawInteriorCells;
end;

procedure TStringGrid.MoveSelection;
begin
  If goEditing in Options Then Editor:=fDefEditor;
  inherited MoveSelection;
end;

constructor TStringGrid.Create(AOWner: TComponent);
begin
  inherited Create(AOWner);
  if Not (csDesigning in componentState) Then begin
    FDefEditor:=TStringCellEditor.Create(nil);
    FDefEditor.Name:='Default_StringCellEditor';
    FDefEditor.Visible:=False;
    FDefEditor.Align:=alNone;
  End Else Begin
    FDefEditor:=nil;
  End;
end;

destructor TStringGrid.Destroy;
begin
  {$Ifdef dbg}WriteLn('TStringGrid.Destroy');{$Endif}
  if FdefEditor<>nil Then begin
    FDefEDitor.Parent:=nil;
    FreeThenNil(FDefEditor);
  End;
  inherited Destroy;
end;



initialization

LazarusResources.Add('tdrawgrid','XPM',[
  '/* XPM */'#10'static char *tdrawgrid[]={'#10'"23 23 8 1",'#10'"# c #000000",'
  +#10'"f c #0000c0",'#10'"b c #808080",'#10'". c #a0a0a0",'#10'"c c #ff0000",'
  +#10'"d c #ff00ff",'#10'"a c #ffffc0",'#10'"e c #ffffff",'#10'"....#.....#...'
  +'..#.....#",'#10'"....#.....#.....#.....#",'#10'"....#.....#.....#.....#",'
  +#10'"....#.....#.....#.....#",'#10'"#######################",'#10'"....#aaaa'
  +'abaaaaabaaaaaa",'#10'"....#aaaaabaaaaabaaaaaa",'#10'"....#aaacccccccabaaaaa'
  +'a",'#10'"....#aaacccccccabaadaaa",'#10'"#####bbbcccccccbbbdddbb",'#10'"....'
  +'#eeecccccccebedddee",'#10'"....#eeecccccccebddddde",'#10'"....#eeecccccffff'
  +'ddddde",'#10'"....#eeeccccffffffddddd",'#10'"#####bbbbbbbffffffddddd",'#10
  +'"....#aaaaabaffffffaaaaa",'#10'"....#aaaaabaffffffaaaaa",'#10'"....#aaaaaba'
  +'affffaaaaaa",'#10'"....#aaaaabaaaaabaaaaaa",'#10'"#####bbbbbbbbbbbbbbbbbb",'
  +#10'"....#eeeeebeeeeebeeeeee",'#10'"....#eeeeebeeeeebeeeeee",'#10'"....#eeee'
  +'ebeeeeebeeeeee"};'#10
]);
LazarusResources.Add('tstringgrid','XPM',[
  '/* XPM */'#10'static char *tstringgrid[]={'#10'"23 23 5 1",'#10'"# c #000000'
  +'",'#10'"b c #808080",'#10'". c #a0a0a0",'#10'"a c #ffffc0",'#10'"c c #fffff'
  +'f",'#10'"....#.....#.....#.....#",'#10'"....#.....#.....#.....#",'#10'"....'
  +'#.....#.....#.....#",'#10'"....#.....#.....#.....#",'#10'"#################'
  +'######",'#10'"....#aaaaabaaaaabaaaaaa",'#10'"....#aaaaabaaaaabaaaaaa",'#10
  +'"....#aaaaabaaaaabaaaaaa",'#10'"....#aaaaabaaaaabaaaaaa",'#10'"#####bbbbbbb'
  +'bbbbbbbbbbb",'#10'"....#cccccbcccccbcccccc",'#10'"....#cccccbcccccbcccccc",'
  +#10'"....#cccccbcccccbcccccc",'#10'"....#cccccbcccccbcccccc",'#10'"#####bbbb'
  +'bbbbbbbbbbbbbb",'#10'"....#aaaaabaaaaabaaaaaa",'#10'"....#aaaaabaaaaabaaaaa'
  +'a",'#10'"....#aaaaabaaaaabaaaaaa",'#10'"....#aaaaabaaaaabaaaaaa",'#10'"####'
  +'#bbbbbbbbbbbbbbbbbb",'#10'"....#cccccbcccccbcccccc",'#10'"....#cccccbcccccb'
  +'cccccc",'#10'"....#cccccbcccccbcccccc"};'#10
]);

end.
