{********************************************************************}
{                                                                    }
{ written by TMS Software                                            }
{            copyright © 2013 - 2014                                 }
{            Email : info@tmssoftware.com                            }
{            Web : http://www.tmssoftware.com                        }
{                                                                    }
{ The source code is given as is. The author is not responsible      }
{ for any possible damage done due to the use of this code.          }
{ The complete source code remains property of the author and may    }
{ not be distributed, published, given or sold in any form as such.  }
{ No parts of the source code can be included in any other component }
{ or application without written authorization of the author.        }
{********************************************************************}

unit FMX.TMSCustomGrid;

interface

{$I TMSDEFS.INC}

uses
  {$IFNDEF IOS}
  {$IFNDEF MACOS}
  {$IFNDEF ANDROID}
  Windows,
  {$ENDIF}
  {$ENDIF}
  {$ENDIF}
  {$IFNDEF ANDROID}
  FMX.Printer,
  {$ENDIF}
  Classes, SysUtils, UITypes, FMX.ListBox, FMX.Types, Types, FMX.TMSGridData, FMX.TMSGridDataUtil, FMX.TMSXUtil,
  FMX.TMSBaseControl, FMX.TMSGridOptions, FMX.Layouts, FMX.Edit, FMX.Objects, FMX.Controls, Math,
  FMX.TMSGridCell, FMX.TMSBitmapContainer, FMX.TMSEdit, FMX.ExtCtrls, FMX.Colors, FMX.Forms,
  FMX.TMSBitmap, FMX.Dialogs , FMX.StdCtrls, UIConsts, FMX.Styles
  {$IFDEF DELPHIXE5_LVL}
  , FMX.Graphics, FMX.DateTimeCtrls
  {$ENDIF}
  {$IFDEF DELPHIXE6_LVL}
  , System.Math.Vectors, System.Character
  {$ENDIF}
  ;

const
  MAJ_VER = 2; // Major version nr.
  MIN_VER = 0; // Minor version nr.
  REL_VER = 3; // Release nr.
  BLD_VER = 8; // Build nr.

  // version history
  // v1.0.0.0 : First Release
  // v1.5.0.0 : New : OnCanColumnDrag, OnCanRowDrag events added
  //          : New : OnColumnDragged, OnRowDragged events added
  //          : New : Livebindings support
  //          : Fixed : Issue with Options.Mouse.ColumnDragging = false / Options.Mouse.RowDragging = false
  //          : Fixed : Issue with OnCanInsertRow/OnCanDeleteRow triggering
  //          : Fixed : Issue with selection on top when dragging in touch mode
  //          : Fixed : Access violation when touching outside the grid in touch mode
  //          : Fixed : Enabling touch mode in none selection mode.
  //          : Fixed : Issue with Escape key not cancelling edit
  // v1.5.1.0 : New : Fixed cell single and range selection
  //          : New : Autosizing columns / rows on double-click
  //          : New : Column persistence
  //          : Fixed : Issue with accessing and adding progressbar cells
  //          : Fixed : Issue with checkbox and radiobutton not rendering correctly on iOS project
  // v1.5.1.1 : Fixed : Issue with sorting & filtering
  // v1.5.1.2 : Fixed : Issue with InsertRow on non-initialized grid
  // v1.5.2.0 : New : Options.Filtering.MultiColumn added to perform automatic multicolumn filtering
  // v1.5.2.1 : Fixed : Issue with XE3 Mac filtering
  // v1.5.2.2 : Fixed : Issue with anchor detection when scrolling
  // v1.5.2.3 : Fixed : Issue with ccCount column calc
  // v1.5.3.0 : New : Exposed functions CancelEdit / StopEdit
  // v1.5.3.1 : Fixed : Issue with editing cell functionality while selecting all cells
  //          : Fixed : Improvement in printing on different DPI values
  // v1.5.3.2 : Fixed : Issue with peristing column state
  //          : Fixed : Issue with printing DPI on different real and virtual printers
  // v1.5.3.3 : Fixed : Issue with dblclick
  //          : Fixed : Issue with dropdown window visible after editing
  // v1.5.3.4 : Fixed : Issue with dblclick focusing issue
  // v1.5.3.5 : Fixed : Issue with hidden radio button checked property
  // v1.5.4.0 : Fixed : Issue with clipboard on empty grid
  //          : New : Event OnTopLeftChanged
  // v1.5.4.1 : Fixed : Issue with hide column
  // v1.5.4.2 : Improved : Added Event OnCellEditCancel
  // v1.5.5.0 : New : fnSelectedCells, fnFindInCurrentRow, fnFindInCurrentCol options enabled in Find function
  // v1.5.5.1 : Fixed : Issue with fixed cell property persistence
  //          : Fixed : Issue with border width in new columns persistence collection
  // v1.5.5.2 : Fixed : Issue with anchors & readonly cells
  // v1.5.5.3 : Fixed : Issue with handling Columns[x].ReadOnly
  // v2.0.0.0 : New : support for XE4
  //          : Fixed : Issue with SortIndexes memory leak when cleared
  // v2.0.0.1 : Improved : NextPage and PreviousPage methods
  // v2.0.0.2 : Fixed : Issue with LoadFromFile column widths
  // v2.0.0.3 : Fixed : Issue with iOS cell objects not being freed
  //          : Fixed : Issue with Scrolling when scrollbars are not visible
  //          : Fixed : Issue with readonly cells when using columns
  //          : Fixed : Issue accessing correct cell colors
  //          : Fixed : Issue with LiveBindings editing and row selection
  //          : Improved : Added Fixed Disjunct Row and Column Selection
  // v2.0.0.4 : Fixed : Issue with grid editing / inserting in LiveBindings
  // v2.0.1.0 : New : VisibleRowCount and VisibleColumnCount functionality
  //          : Fixed : Issue with disjunct selection and sorting
  // v2.0.1.1 : Fixed : Issue with autosizing and nodes
  // v2.0.1.2 : Fixed : Issue inserting and deleting records in LiveBindings
  // v2.0.1.3 : Fixed : Issue with column swapping
  // v2.0.1.4 : Fixed : Issue with OnColumnSorted event not triggered
  // v2.0.1.5 : Fixed : Issue with deleting rows
  // v2.0.1.6 : Fixed : Issue with CSV parsing
  //          : Fixed : Issue with money editing
  // v2.0.1.8 : Fixed : Issue with disposing objects on iOS
  // v2.0.1.9 : Fixed : Issue with column moving & column sizes
  //          : Fixed : Issue with save to file & linebreaks on iOS / Android
  //          : Fixed : Issue with column, row dragging out of the grid boundaries
  // v2.0.2.0 : Fixed : Issue with string conversion on iOS / Android
  //          : Fixed : Issue with column, row dragging out of the grid boundaries
  // v2.0.2.1 : Fixed : Issue with row sorting and objects
  // v2.0.2.2 : Fixed : Issue with OnCellEditDone event and focus changes
  // v2.0.3.0 : Improved : Disjunct deselection of column, rows and cells in TTMSFMXGrid
  // v2.0.3.1 : Fixed : Issue with Keyboard handling for ComboBox editor type
  // v2.0.3.2 : Fixed : Issue with column hiding and memory leak in sorting data
  // v2.0.3.3 : Fixed : Issue with column/row hiding combinations
  //          : Fixed : Issue with Escape key
  // v2.0.3.4 : Fixed : Issues with column/row handling
  // v2.0.3.5 : Fixed : Issue with unhiding row combinations
  // v2.0.3.6 : Improved : OnFormatCellDataExpression
  //          : Fixed : Issue with selection initialization
  //          : Fixed : Issues with column/row memory handling
  //          : Fixed : Issue with columnwidth / rowheight calculation
  //          : Fixed : Issue with columnwidth / rowheight order when insert new column/row
  // v2.0.3.7 : Fixed : Issue with initializing grid with default nr. of columns
  // v2.0.3.8 : Fixed : Issue with wordwrap & cell comments
  //          : Fixed : Issue with cell controls & scrolling


  CLICKMARGIN = 2;

type
  TTMSFMXCustomGrid = class;

  TTMSFMXGridScrollMode = (smPixelScrolling, smCellScrolling);

  TTMSFMXGridOnGetCellClass = procedure(Sender: TObject; ACol, ARow: Integer; var CellClassType: TFMXObjectClass) of object;
  TTMSFMXGridTopLeftChanged = procedure(Sender: TObject; ATopRow, ALeftCol: Integer) of object;
  TTMSFMXGridOnGetCellControl = procedure(Sender: TObject; ACol, ARow: Integer; var AControl: TFMXObject) of object;
  TTMSFMXGridOnGetCellData = procedure(Sender: TObject; ACol, ARow: Integer; var CellString: String) of object;
  TTMSFMXGridOnGetCellProperties =  procedure(Sender: TObject; ACol, ARow: Integer; Cell: TFMXObject) of object;
  TTMSFMXGridOnGetCellAppearance =  procedure(Sender: TObject; ACol, ARow: Integer; Cell: TFMXObject; ACellState: TCellState) of object;
  TTMSFMXGridOnGetCellMergeInfo = procedure(Sender: TObject; ACol, ARow: Integer; var ABaseCol: Integer; var ABaseRow: Integer; var AColSpan: Integer; var ARowSpan: Integer) of object;
  TTMSFMXGridOnGetCellEditorCustomClassType = procedure(Sender: TObject; ACol, ARow: Integer; var CellEditorCustomClassType: TFMXObjectClass) of object;
  TTMSFMXGridOnGetCellEditorType = procedure(Sender: TObject; ACol, ARow: Integer; var CellEditorType: TTMSFMXGridEditorType) of object;
  TTMSFMXGridOnGetCellEditorProperties = procedure(Sender: TObject; ACol, ARow: Integer; CellEditor: TFMXObject) of object;
  TTMSFMXGridOnCellEditGetData = procedure(Sender: TObject; ACol, ARow: Integer; CellEditor: TFMXObject; var CellString: String) of object;
  TTMSFMXGridOnCellEditValidateData = procedure(Sender: TObject; ACol, ARow: Integer; CellEditor: TFMXObject; var CellString: String; var Allow: Boolean) of object;
  TTMSFMXGridOnCellEditSetData = procedure(Sender: TObject; ACol, ARow: Integer; CellEditor: TFMXObject; var CellString: String) of object;

  TTMSFMXGridOnCellEditGetColor = procedure(Sender: TObject; ACol, ARow: Integer; CellEditor: TFMXObject; var CellColor: TAlphaColor) of object;
  TTMSFMXGridOnCellEditValidateColor = procedure(Sender: TObject; ACol, ARow: Integer; CellEditor: TFMXObject; var CellColor: TAlphaColor; var Allow: Boolean) of object;
  TTMSFMXGridOnCellEditSetColor = procedure(Sender: TObject; ACol, ARow: Integer; CellEditor: TFMXObject; var CellColor: TAlphaColor) of object;

  TTMSFMXGridOnCellEditDone = procedure(Sender: TObject; ACol, ARow: Integer; CellEditor: TFMXObject) of object;
  TTMSFMXGridOnCellEditCancel = procedure(Sender: TObject; ACol, ARow: Integer) of object;
  TTMSFMXGridOnCanSizeColumn = procedure(Sender: TObject; ACol: Integer; var Allow: Boolean) of object;
  TTMSFMXGridOnCanSizeRow = procedure(Sender: TObject; ARow: Integer; var Allow: Boolean) of object;
  TTMSFMXGridOnColumnSize = procedure(Sender: TObject; ACol: Integer; var NewWidth: Single) of object;
  TTMSFMXGridOnRowSize = procedure(Sender: TObject; ARow: Integer; var NewHeight: Single) of object;
  TTMSFMXGridOnColumnSized = procedure(Sender: TObject; ACol: Integer; NewWidth: Single) of object;
  TTMSFMXGridOnRowSized = procedure(Sender: TObject; ARow: Integer; NewHeight: Single) of object;
  TTMSFMXGridOnColumnDragged = procedure(Sender: TObject; FromCol, ToCol: integer) of object;
  TTMSFMXGridOnRowDragged = procedure(Sender: TObject; FromRow, ToRow: integer) of object;
  TTMSFMXGridOnCanSortColumn = procedure(Sender: TObject; ACol: Integer; var Allow: Boolean) of object;
  TTMSFMXGridOnColumnSorted = procedure(Sender: TObject; ACol: Integer; Direction: TSortDirection) of object;
  TTMSFMXGridOnCellAnchorClick = procedure(Sender: TObject; ACol, ARow: Integer; AAnchor: String) of object;
  TTMSFMXGridOnGetCellReadOnly = procedure(Sender: TObject; ACol, ARow: Integer; var AReadOnly: Boolean) of object;
  TTMSFMXGridOnGetCellLayout = procedure(Sender: TObject; ACol, ARow: Integer; ALayout: TTMSFMXGridCellLayout; ACellState: TCellState) of object;
  TTMSFMXGridOnCellClick = procedure(Sender: TObject; ACol, ARow: Integer) of object;
  TTMSFMXGridOnGetCellIsFixed = procedure(Sender: TObject; ACol, ARow: Integer; var ACellFixed: Boolean) of object;
  {$IFNDEF ANDROID}
  TTMSFMXGridOnPrintProgress = procedure(Sender: TObject; APageIndex: Integer; APrinter: TPrinter; APrintProgress: Single) of object;
  TTMSFMXGridOnPrintNewPage = procedure(Sender: TObject; APageIndex: Integer; APrinter: TPrinter) of object;
  TTMSFMXGridOnPrintEndPage = procedure(Sender: TObject; APageIndex: Integer; APrinter: TPrinter) of object;
  TTMSFMXGridOnPrintEvent = procedure(Sender: TObject; APrinter: TPrinter) of object;
  {$ENDIF}
  TTMSFMXGridOnPrintBeforeDrawCell = procedure(Sender: TObject; APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; ACol, ARow: Integer; ACell: TFmxObject) of object;
  TTMSFMXGridOnPrintDrawCell = procedure(Sender: TObject; APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; ACol, ARow: Integer; ACell: TFmxObject; var Allow: Boolean) of object;
  TTMSFMXGridOnPrintAfterDrawCell = procedure(Sender: TObject; APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; ACol, ARow: Integer; ACell: TFmxObject) of object;

  TTMSFMXGridOnPrintBeforeDraw = procedure(Sender: TObject; APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; var Allow: Boolean) of object;
  TTMSFMXGridOnPrintAfterDraw = procedure(Sender: TObject; APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF) of object;
  TTMSFMXGridOnGetRowIsBand = procedure(Sender: TObject; ARow: Integer; var ARowBand: Boolean) of object;
  TTMSFMXGridOnCanInsertRow = procedure(Sender: TObject; ARow: Integer; var Allow: Boolean) of object;
  TTMSFMXGridOnCanDeleteRow = procedure(Sender: TObject; ARow: Integer; var Allow: Boolean) of object;
  TTMSFMXGridOnCanAppendColumn = procedure(Sender: TObject; ACol: Integer; var Allow: Boolean) of object;
  TTMSFMXGridOnCanAppendRow = procedure(Sender: TObject; ARow: Integer; var Allow: Boolean) of object;

  TTMSFMXGridOnInsertRow = procedure(Sender: TObject; ARow: Integer) of object;
  TTMSFMXGridOnDeleteRow = procedure(Sender: TObject; ARow: Integer) of object;
  TTMSFMXGridOnAppendColumn = procedure(Sender: TObject; ACol: Integer) of object;
  TTMSFMXGridOnAppendRow = procedure(Sender: TObject; ARow: Integer) of object;

  TTMSFMXGridOnSelectCell = procedure(Sender: TObject; ACol, ARow: Integer; var Allow: Boolean) of object;

  TTMSFMXGridOnPrintDrawText = procedure (Sender: TObject; APageIndex: Integer; ACanvas: TCanvas; var ARect: TRectF; var AText: String; var Allow: Boolean) of object;

  TTMSFMXGridCellControlEvent = procedure(Sender: TObject; ACol, ARow: Integer; Cell: TFMXObject) of object;

  TGridCellArray = array of array of TFMXObject;

  TTMSFMXGridSizeMode = (smColumnSizing, smRowSizing, smPreviousColumnSizing, smPreviousRowSizing);

  TTMSFMXGridDraggingMode = (dmNone, dmRowDragging, dmColumnDragging);

  TProtectedButton = class(TButton);

  TProtectedControl = class(TControl);

  TTMSFMXGridCellBeforeDraw = procedure(Sender: TObject; ACol, ARow: Integer; ACanvas: TCanvas; var ARect: TRectF;
    var ATextRect: TRectF; var ADrawText: Boolean; var ADrawBackGround: Boolean; var AllowDraw: Boolean) of object;

  TTMSFMXGridCellAfterDraw = procedure(Sender: TObject;  ACol, ARow: Integer;ACanvas: TCanvas; ARect: TRectF) of object;

  TEditCellStart = (csNone, csStart, csEnd);

  TCellCalendarBox = class(TCalendarEdit)
    constructor Create(AOwner: TComponent); override;
  end;

  TControlProtected = class(TControl);

  TTMSFMXCustomGrid = class(TTMSFMXGridData)
  private
    FTopr, FLeftC: Integer;
    FDblClick: Boolean;
    FCellKeyDown: Boolean;
    FSaveTopCell: TFmxObject;
    FPrevSelection: TCellRange;
    FCellAnchor: TCell;
    FCellAnchorString: string;
    FLookupString: string;
    FDraggingMode: TTMSFMXGridDraggingMode;
    FDragBitmap: TImage;
    FDragFixed, FTouchScrolling: Boolean;
    FDragCell, FDragScrollCell: TCell;
    FEditCell: TCell;
    FEditControl: TFMXObject;
    FCellEdit: TTMSFMXEdit;
    FCellComboBox: TComboBox;
    FRealCell: TCell;
    FX, FY, FPrevX, FPrevY, FSizeX, FSaveDragX, FSaveDragY,
    FSizeY, FDragX, FDragY, FDownX, FDownY, FTouchX, FTouchY: Single;
    FScrollTimer, FDragTimer: TTimer;
    FSaveC: TFMXObject;
    FPrevSelectedCell: TCell;
    FFirstCell, FSaveCell: TCell;
    FMouseDown,FMouseDblClick, FMouseUp, FMouseSizing, FSizing: Boolean;
    FSizeMode: TTMSFMXGridSizeMode;
    FMouseDragging: Boolean;
    FUpdatingGrid: Boolean;
    FCStart, FRStart, FCStop, FRStop: Integer;
    FReset: Boolean;
    FCellArray: TGridCellArray;
    FScrollMode: TTMSFMXGridScrollMode;
    FOnGetCellClass: TTMSFMXGridOnGetCellClass;
    FOnGetCellProperties: TTMSFMXGridOnGetCellProperties;
    FOnGetCellData: TTMSFMXGridOnGetCellData;
    FFreezeRows: Integer;
    FFreezeColumns: Integer;
    FOnGetCellAppearance: TTMSFMXGridOnGetCellAppearance;
    FFixedShowSelection: Boolean;
    FOnGetCellMergeInfo: TTMSFMXGridOnGetCellMergeInfo;
    FOnCellEditSetData: TTMSFMXGridOnCellEditSetData;
    FOnCellEditDone: TTMSFMXGridOnCellEditDone;
    FOnGetCellEditorCustomClassType: TTMSFMXGridOnGetCellEditorCustomClassType;
    FOnCellEditValidateData: TTMSFMXGridOnCellEditValidateData;
    FOnCellEditGetData: TTMSFMXGridOnCellEditGetData;
    FOnGetCellEditorProperties: TTMSFMXGridOnGetCellEditorProperties;
    FOnCellButtonClick: TTMSFMXGridCellControlEvent;
    FOnCellRadioButtonClick: TTMSFMXGridCellControlEvent;
    FOnCellCheckBoxClick: TTMSFMXGridCellControlEvent;
    FOnCellBitmapClick: TTMSFMXGridCellControlEvent;
    FOnCellCommentClick: TTMSFMXGridCellControlEvent;
    FOnCellSortClick: TTMSFMXGridCellControlEvent;
    FOnRowSized: TTMSFMXGridOnRowSized;
    FOnColumnSized: TTMSFMXGridOnColumnSized;
    FOnCanSizeRow: TTMSFMXGridOnCanSizeRow;
    FOnCanSizeColumn: TTMSFMXGridOnCanSizeColumn;
    FOnCanDragRow: TTMSFMXGridOnCanSizeRow;
    FOnCanDragColumn: TTMSFMXGridOnCanSizeColumn;
    FOnColumnDragged: TTMSFMXGridOnColumnDragged;
    FOnRowDragged: TTMSFMXGridOnRowDragged;
    FOnRowSize: TTMSFMXGridOnRowSize;
    FOnColumnSize: TTMSFMXGridOnColumnSize;
    FOnCellNodeClick: TTMSFMXGridCellControlEvent;
    FOnCanSortColumn: TTMSFMXGridOnCanSortColumn;
    FOnColumnSorted: TTMSFMXGridOnColumnSorted;
    FOnCellAnchorClick: TTMSFMXGridOnCellAnchorClick;
    FOnCellClick: TTMSFMXGridOnCellClick;
    FOnFixedCellClick: TTMSFMXGridOnCellClick;
    FOnFixedCellSpinBoxChange: TTMSFMXGridCellControlEvent;
    FOnFixedCellBitmapClick: TTMSFMXGridCellControlEvent;
    FOnFixedCellCheckBoxClick: TTMSFMXGridCellControlEvent;
    FOnFixedCellButtonClick: TTMSFMXGridCellControlEvent;
    FOnGetCellReadOnly: TTMSFMXGridOnGetCellReadOnly;
    FOnGetCellLayout: TTMSFMXGridOnGetCellLayout;
    FOnGetCellIsFixed: TTMSFMXGridOnGetCellIsFixed;
    {$IFNDEF ANDROID}
    FOnPrintDrawCell: TTMSFMXGridOnPrintDrawCell;
    FOnPrintBeforeDrawCell: TTMSFMXGridOnPrintBeforeDrawCell;
    FOnPrintProgress: TTMSFMXGridOnPrintProgress;
    FOnPrintEnd: TTMSFMXGridOnPrintEvent;
    FOnPrintBegin: TTMSFMXGridOnPrintEvent;
    FOnPrintNewPage: TTMSFMXGridOnPrintNewPage;
    FOnPrintEndPage: TTMSFMXGridOnPrintEndPage;
    FOnPrintAfterDrawCell: TTMSFMXGridOnPrintAfterDrawCell;
    FOnPrintDrawTitle: TTMSFMXGridOnPrintDrawText;
    FOnPrintDrawPageNumber: TTMSFMXGridOnPrintDrawText;
    FOnPrintDrawDescription: TTMSFMXGridOnPrintDrawText;
    {$ENDIF}
    FOnPrintBeforeDraw: TTMSFMXGridOnPrintBeforeDraw;
    FOnPrintAfterDraw: TTMSFMXGridOnPrintAfterDraw;
    {$IFNDEF ANDROID}
    FOnPrintPreviewProgress: TTMSFMXGridOnPrintProgress;
    {$ENDIF}
    FOnGetCellControl: TTMSFMXGridOnGetCellControl;
    FOnGetCellEditorType: TTMSFMXGridOnGetCellEditorType;
    FCellSpinBox: TSpinBox;
    FCellComboEdit: TComboEdit;
    FCellEditBtn: TTMSFMXEditBtn;
    FCellDatePicker: TCellCalendarBox;
    FCellDateEdit: TCalendarEdit;
    FCellColorComboBox: TColorComboBox;
    FCellArcDial: TArcDial;
    FCellTrackBar: TTrackBar;
    FCellColorPicker: TComboColorBox;
    FOnCellEditGetColor: TTMSFMXGridOnCellEditGetColor;
    FOnCellEditSetColor: TTMSFMXGridOnCellEditSetColor;
    FOnCellEditValidateColor: TTMSFMXGridOnCellEditValidateColor;
    FOnGetRowIsBand: TTMSFMXGridOnGetRowIsBand;
    FOnCanDeleteRow: TTMSFMXGridOnCanDeleteRow;
    FOnCanAppendRow: TTMSFMXGridOnCanAppendRow;
    FOnCanAppendColumn: TTMSFMXGridOnCanAppendColumn;
    FOnCanInsertRow: TTMSFMXGridOnCanInsertRow;
    FOnDeleteRow: TTMSFMXGridOnDeleteRow;
    FOnAppendRow: TTMSFMXGridOnAppendRow;
    FOnAppendColumn: TTMSFMXGridOnAppendColumn;
    FOnInsertRow: TTMSFMXGridOnInsertRow;
    FOnCellAfterDraw: TTMSFMXGridCellAfterDraw;
    FOnCellBeforeDraw: TTMSFMXGridCellBeforeDraw;
    FOnCellShowPopup: TTMSFMXGridCellControlEvent;
    FOnCellHidePopup: TTMSFMXGridCellControlEvent;
    FOnFixedCellDropDownButtonClick: TTMSFMXGridCellControlEvent;
    FEditing: boolean;
    FOnSelectCell: TTMSFMXGridOnSelectCell;
    FOnTopLeftChanged: TTMSFMXGridTopLeftChanged;
    FOnCellEditCancel: TTMSFMXGridOnCellEditCancel;
    function GetVersion: String;
    function GetVersionNr: Integer;
    procedure SetVersion(const Value: String);
    procedure SetScrollMode(const Value: TTMSFMXGridScrollMode);
    procedure SetFreezeColumns(const Value: Integer);
    procedure SetFreezeRows(const Value: Integer);
    procedure SetFixedShowSelection(const Value: Boolean);
    function GetLeftCol: Integer;
    function GetTopRow: Integer;
    procedure SetLeftCol(const Value: Integer);
    procedure SetTopRow(const Value: Integer);
  protected
    procedure DoExit; override;
    procedure ResetMouseInteraction;
    function FindPopup(AControl: TComponent): TPopup;

    procedure DrawTitle(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF);
    procedure DrawDescription(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF);
    procedure DrawPageNumber(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF);

    function GetRealTopRow: Integer;
    function GetRealLeftCol: Integer;

    function GetCellAppearance(AState: TCellState; ACol, ARow: Integer): TTMSFMXGridCellLayout;
    procedure ApplyAdditionalStyle(AControl: TFMXObject); override;
    function GetCellByObject(Cell: TFMXObject): TCell;
    function GetDisplayCellByObject(Cell: TFMXObject): TCell;
    procedure UpdateGrid(Update: Boolean = False); override;
    procedure UpdateHeader;
    procedure UpdateFooter;
    procedure HideEdit(AEnterKey: Boolean = False);
    procedure CellControlKeyDown(Sender: TObject; var Key: Word; var KeyChar: WideChar; Shift: TShiftState); virtual;
    procedure CellEditKeyDown(Sender: TObject; var Key: Word; var KeyChar: WideChar; Shift: TShiftState); virtual;
    procedure CellEditBtnKeyDown(Sender: TObject; var Key: Word; var KeyChar: WideChar; Shift: TShiftState); virtual;
    procedure CellComboBoxKeyDown(Sender: TObject; var Key: Word; var KeyChar: WideChar; Shift: TShiftState); virtual;
    procedure CellComboBoxChange(Sender: TObject); virtual;
    procedure ScrollTimer(Sender: TObject);
    procedure DragTimer(Sender: TObject);
    function GetHeaderHeight: Single;
    function GetInstance: NativeUInt; override;
    procedure ScrollBarVChanged(Sender: TObject);
    procedure ScrollBarHChanged(Sender: TObject);
    procedure ApplyCellState(ACol, ARow: Integer; Cell: TFMXObject);
    function GetCellState(ACol, ARow: Integer): TCellState; override;

    procedure DblClick; override;
    procedure ClearFixedSorting(ACell: TFmxObject);
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Single); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
    procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean); override;
    procedure KeyDown(var Key: Word; var KeyChar: WideChar; Shift: TShiftState); override;
    procedure DialogKey(var Key: Word; Shift: TShiftState); override;
    procedure ProcessTab(var Key: Word; Shift: TShiftState; Mode: TTMSFMXGridTabKeyDirection; Handling: TTMSFMXGridTabKeyHandling; DirectEdit: Boolean; CellStart: TEditCellStart = csNone);
    procedure DoCellKeyDown(ACol, ARow: Integer; var Key: Word; var KeyChar: WideChar; Shift: TShiftState);
    procedure DoCellKeyUp(ACol, ARow: Integer; var Key: Word; var KeyChar: WideChar; Shift: TShiftState);
    function GetPageScrollSize: Integer;
    procedure KeyUp(var Key: Word; var KeyChar: WideChar; Shift: TShiftState); override;

    procedure LayoutChanged(Sender: TObject);

    function GetHorizontalPos(AStartCol, AStopCol: Integer): Single;
    function GetVerticalPos(AStartRow, AStopRow: Integer): Single;
    procedure AutoSizeColumnInt(ACol: Integer; Accurate: Boolean = True);
    procedure AutoSizeRowInt(ARow: integer);

    function GetColumnViewPortSize: Single;
    function GetRowViewPortSize: Single;

    procedure DoCheckBoxClick(Sender: TObject);
    procedure DoRadioButtonClick(Sender: TObject);
    procedure DoButtonClick(Sender: TObject);
    procedure DoComboBoxChange(Sender: TObject);
    procedure DoBitmapClick(Sender: TObject);
    procedure DoCommentClick(Sender: TObject);
    procedure DoShowPopup(Sender: TObject);
    procedure DoHidePopup(Sender: TObject);
    procedure DoNodeClick(Sender: TObject);

    procedure DoFixedCheckBoxClick(Sender: TObject);
    procedure DoFixedButtonClick(Sender: TObject);
    procedure DoFixedDropDownButtonClick(Sender: TObject);
    procedure DoFixedBitmapClick(Sender: TObject);
    procedure DoFixedSpinBoxChange(Sender: TObject);

    procedure DoCellAnchorClick(ACol, ARow: Integer; AAnchor: String); override;
    procedure DoCanSizeColumn(ACol: Integer; var Allow: Boolean); override;
    procedure DoCanSizeRow(ARow: Integer; var Allow: Boolean); override;
    procedure DoColumnSize(ACol: Integer; var NewWidth: Single); override;
    procedure DoRowSize(ARow: Integer; var NewHeight: Single); override;
    procedure DoColumnSized(ACol: Integer; NewWidth: Single); override;
    procedure DoRowSized(ARow: Integer; NewHeight: Single); override;
    procedure DoCanSortColumn(ACol: Integer; var Allow: Boolean); override;
    procedure DoColumnSorted(ACol: Integer; Direction: TSortDirection); override;
    procedure DoCanDragColumn(ACol: integer; var Allow: Boolean); virtual;
    procedure DoCanDragRow(ARow: integer; var Allow: Boolean); virtual;
    procedure DoColumnDragged(FromColumn, ToColumn: integer); virtual;
    procedure DoRowDragged(FromRow, ToRow: integer); virtual;

    function DoIsCellSelected(ACol, ARow: Integer): Boolean; override;
    function DoIsFixedCellSelected(ACol, ARow: Integer): Boolean;

    procedure DoFixedCellDropDownButtonClick(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoFixedCellCheckBoxClick(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoFixedCellSpinBoxChange(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoFixedCellButtonClick(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoFixedCellBitmapClick(ACol, ARow: Integer; Cell: TFMXObject); override;

    procedure DoCellShowPopup(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoCellHidePopup(ACol, ARow: Integer; Cell: TFMXObject); override;

    procedure DoCellCheckBoxClick(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoCellRadioButtonClick(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoGetCellIsFixed(ACol, ARow: Integer; var ACellFixed: Boolean); override;
    procedure DoGetRowIsBand(ARow: Integer; var ARowBand: Boolean); override;
    procedure DoCellButtonClick(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoCellBitmapClick(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoCellCommentClick(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoCellSortClick(Shift: TShiftState; ACol, ARow: Integer; Cell: TFMXObject; Direction: TSortDirection); override;
    procedure DoCellNodeClick(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoInsertRow(ARow: Integer); override;
    procedure DoDeleteRow(ARow: Integer); override;
    procedure DoAppendRow(ARow: Integer); override;
    procedure DoAppendColumn(ACol: Integer); override;

    procedure DoCanInsertRow(ARow: Integer; var Allow: Boolean); override;
    procedure DoCanDeleteRow(ARow: Integer; var Allow: Boolean); override;
    procedure DoCanAppendRow(ARow: Integer; var Allow: Boolean); override;
    procedure DoCanAppendColumn(ACol: Integer; var Allow: Boolean); override;

    procedure DoCellClick(ACol, ARow: Integer); override;
    procedure DoFixedCellClick(Shift: TShiftState; ACol, ARow: Integer); override;

    procedure DoGetCellControl(ACol, ARow: Integer; var AControl: TFMXObject); override;
    procedure DoGetCellClass(ACol, ARow: Integer; var CellClassType: TFMXObjectClass); override;
    procedure DoGetCellData(ACol, ARow: Integer; var CellString: String); override;
    procedure DoGetCellProperties(ACol, ARow: Integer; Cell: TFMXObject); override;
    procedure DoGetCellAppearance(ACol, ARow: Integer; Cell: TFMXObject; ACellState: TCellState); override;
    procedure DoGetCellLayout(ACol, ARow: Integer; ALayout: TTMSFMXGridCellLayout; ACellState: TCellState); override;
    procedure DoGetCellMergeInfo(ACol, ARow: Integer; var ABaseCol: Integer; var ABaseRow: Integer; var AColSpan: Integer; var ARowSpan: Integer); override;
    procedure DoGetCellEditorCustomClassType(ACol, ARow: Integer; var CellEditorCustomClassType: TFMXObjectClass); override;
    procedure DoGetCellEditorType(ACol, ARow: Integer; var CellEditorType: TTMSFMXGridEditorType); override;
    procedure DoCellEditGetData(ACol, ARow: Integer; CellEditor: TFMXObject; var CellString: String); override;
    procedure DoCellEditValidateData(ACol, ARow: Integer; CellEditor: TFMXObject; var CellString: String; var Allow: Boolean); override;
    procedure DoCellEditSetData(ACol, ARow: Integer; CellEditor: TFMXObject; var CellString: String); override;

    procedure DoCellEditGetColor(ACol, ARow: Integer; CellEditor: TFMXObject; var CellColor: TAlphaColor); override;
    procedure DoCellEditValidateColor(ACol, ARow: Integer; CellEditor: TFMXObject; var CellColor: TAlphaColor; var Allow: Boolean); override;
    procedure DoCellEditSetColor(ACol, ARow: Integer; CellEditor: TFMXObject; var CellColor: TAlphaColor); override;

    procedure DoCellEditDone(ACol, ARow: Integer; CellEditor: TFMXObject); override;
    procedure DoCellEditCancel(ACol, ARow: Integer);
    procedure DoGetCellEditorProperties(ACol, ARow: Integer; CellEditor: TFMXObject); override;


    {$IFNDEF ANDROID}
    procedure DoPrintDrawCell(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; ACol, ARow: Integer; Cell: TFMXObject; var Allow: Boolean);
    procedure DoPrintBeforeDrawCell(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; ACol, ARow: Integer; Cell: TFMXObject);
    procedure DoPrintAfterDrawCell(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; ACol, ARow: Integer; Cell: TFMXObject);
    procedure DoPrintDrawTitle(APageIndex: Integer; ACanvas: TCanvas; var ARect: TRectF; var ATitle: String; var Allow: Boolean);
    procedure DoPrintDrawDescription(APageIndex: Integer; ACanvas: TCanvas; var ARect: TRectF; var ADescription: String; var Allow: Boolean);
    procedure DoPrintDrawPageNumber(APageIndex: Integer; ACanvas: TCanvas; var ARect: TRectF; var APageNumberText: String; var Allow: Boolean);
    procedure DoPrintBeforeDraw(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; var Allow: Boolean);
    procedure DoPrintAfterDraw(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF);

    procedure DoPrintBegin(APrinter: TPrinter);
    procedure DoPrintProgress(APageIndex: Integer; APrinter: TPrinter; APrintProgress: Single);
    procedure DoPrintNewPage(APageIndex: Integer; APrinter: TPrinter);
    procedure DoPrintEndPage(APageIndex: Integer; APrinter: TPrinter);
    procedure DoPrintEnd(APrinter: TPrinter);
    {$ENDIF}

    procedure DoGetCellReadOnly(ACol, ARow: Integer; var AReadOnly: Boolean); override;

    procedure DoBeforeDrawCell(Sender: TObject; ACanvas: TCanvas; var ARect: TRectF;
    var ATextRect: TRectF; var ADrawText: Boolean; var ADrawBackGround: Boolean; var AllowDraw: Boolean); virtual;
    procedure DoAfterDrawCell(Sender: TObject; ACanvas: TCanvas; ARect: TRectF); virtual;

    procedure DoBeforeDrawGridCell(Sender: TObject; ACol, ARow: Integer; ACanvas: TCanvas; var ARect: TRectF;
      var ATextRect: TRectF; var ADrawText: Boolean; var ADrawBackGround: Boolean; var AllowDraw: Boolean); virtual;
    procedure DoAfterDrawGridCell(Sender: TObject; ACol, ARow: Integer; ACanvas: TCanvas; ARect: TRectF); virtual;
  public
    {$IFNDEF ANDROID}
    property OnPrintPreviewProgress: TTMSFMXGridOnPrintProgress read FOnPrintPreviewProgress write FOnPrintPreviewProgress;
    {$ENDIF}

    procedure Assign(Source: TPersistent); override;
    procedure EditCell(ACell: TCell; CellStart: TEditCellStart = csNone; InsertKey: Boolean = False; KeyChar: WideChar = #0; Key: Word = 0); virtual;
    procedure CancelEdit;
    procedure StopEdit;

    procedure NextPage;
    function VisibleRowCount: Integer;
    function VisibleColumnCount: Integer;
    procedure PreviousPage;

    function GetDisplayCell(ACell: TCell): TCell;
    function GetRealCell(ACell: TCell): TCell;
    function GetCellObject(ACell: TCell): TControl;
    function GetCell(ACell: TCell): TControl;

    {$IFNDEF ANDROID}
    procedure Print(ACanvas: TCanvas = nil);
    function GetPrintPageCount: Integer;
    function GetPrintDPI: TPoint;
    procedure DoPrint(ASelection: TCellRange; APages: array of Integer; AFileName: String = ''; ACanvas: TCanvas = nil; AResetPosition: Boolean = False; AWidth: Integer = -1; AHeight: Integer = -1);
    procedure PrintPageSelection(ASelection: TCellRange; APageIndex: Integer = 1; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
    procedure PrintPageSelectionToImage(ASelection: TCellRange; AFileName: String; APageIndex: Integer = 1; AWidth: Integer = -1; AHeight: Integer = -1);
    procedure PrintPagesSelection(ASelection: TCellRange; APageIndexes: array of Integer; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
    procedure PrintPageFromToSelection(ASelection: TCellRange; APageFrom, APageTo: Integer; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
    procedure PrintPage(APageIndex: Integer = 1; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
    procedure PrintPages(APageIndexes: array of Integer; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
    procedure PrintPageFromTo(APageFrom, APageTo: Integer; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
    procedure PrintPageToImage(AFileName: String; APageIndex: Integer = 1; AWidth: Integer = -1; AHeight: Integer = -1);
    procedure PrintSelection(ASelection: TCellRange; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
    {$ENDIF}

    procedure Scroll(VerticalValue, HorizontalValue: Single);
    procedure Navigate(ACell: TCell; ForceScroll: Boolean = False);
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure ApplyStyle; override;
    procedure BeginUpdate; override;
    procedure EndUpdate; override;
    procedure ResetUpdate; override;
    procedure Resize; override;

    procedure AutoSizeColumn(ACol: Integer; Accurate: Boolean = True);
    procedure AutoSizeColumns(Accurate: Boolean = True);
    procedure AutoSizeRow(ARow: integer);
    procedure AutoSizeRows;

    procedure SelectCell(Cell: TCell; Shift: TShiftState = []; MouseDragging: Boolean = False); override;
    procedure UpdateGridCells; override;
    procedure CalculatePosition(var cStart: Integer; var cStop: Integer; var rStart: Integer; var rStop: Integer);
    procedure UpdateGridCellAppearance;
    procedure DoUpdateGridCellAppearance(ASelection: TCellRange);
    procedure DisplayCells(cellContainer: TControl; x, y, xOrig, yOrig: Single; CStart, CStop, RStart, RStop, Ci, Ri: Integer; ResetCi: Integer; Fixed: Boolean = False);
    procedure RemoveAllCells;
    procedure AssignEvents;
    function GetRealRect(AContainer: TControl; ARect: TRectF): TRectF;
    function GetCellContainer: TControl;
    function GetCellWrapper: TControl;
    function GetTotalColumnWidth: Single;
    function GetTotalRowHeight: Single;
    function GetVerticalScrollBar: TScrollBar;
    function GetHorizontalScrollBar: TScrollBar;
    function GetHeader: TControl;
    function GetFooter: TControl;

    function XYToCell(X, Y: Single): TCell;

    function GetFixedCols: Integer;
    function GetFixedRows: Integer;
    function GetFixedWidth: Single;
    function GetFixedPrintWidth: Single;
    function GetFixedPrintHeight: Single;
    function GetFixedOnlyHeight: Single;
    function GetFixedOnlyWidth: Single;
    function GetFixedHeight: Single;
    function GetFixedRightWidth: Single;
    function GetFixedFooterHeight: Single;

    property Editing: boolean read FEditing;
    property CellEdit: TTMSFMXEdit read FCellEdit write FCellEdit;
    property CellEditBtn: TTMSFMXEditBtn read FCellEditBtn write FCellEditBtn;
    property CellComboBox: TComboBox read FCellComboBox write FCellComboBox;
    property CellComboEdit: TComboEdit read FCellComboEdit write FCellComboEdit;
    property CellSpinBox: TSpinBox read FCellSpinBox write FCellSpinBox;
    property CellDatePicker: TCellCalendarBox read FCellDatePicker write FCellDatePicker;
    property CellDateEdit: TCalendarEdit read FCellDateEdit write FCellDateEdit;
    property CellColorPicker: TComboColorBox read FCellColorPicker write FCellColorPicker;
    property CellColorComboBox: TColorComboBox read FCellColorComboBox write FCellColorComboBox;
    property CellTrackBar: TTrackBar read FCellTrackBar write FCellTrackBar;
    property CellArcDial: TArcDial read FCellArcDial write FCellArcDial;
    property ActiveEditControl: TFmxObject read FEditControl write FEditControl;
  published
    property Anchors;
    property CanFocus default true;
    property Version: String read GetVersion write SetVersion;

    property Fill;
    property Stroke;
    property StrokeThickness;
    property StrokeCap;
    property StrokeDash;
    property StrokeJoin;
    property TopRow: Integer read GetTopRow write SetTopRow;
    property LeftCol: Integer read GetLeftCol write SetLeftCol;
    property ScrollMode: TTMSFMXGridScrollMode read FScrollMode write SetScrollMode default smCellScrolling;
    property FixedShowSelection: Boolean read FFixedShowSelection write SetFixedShowSelection default True;
    property FreezeColumns: Integer read FFreezeColumns write SetFreezeColumns default 0;
    property FreezeRows: Integer read FFreezeRows write SetFreezeRows default 0;
    property OnTopLeftChanged: TTMSFMXGridTopLeftChanged read FOnTopLeftChanged write FOnTopLeftChanged;
    property OnGetCellClass: TTMSFMXGridOnGetCellClass read FOnGetCellClass write FOnGetCellClass;
    property OnGetCellControl: TTMSFMXGridOnGetCellControl read FOnGetCellControl write FOnGetCellControl;
    property OnGetCellData: TTMSFMXGridOnGetCellData read FOnGetCellData write FOnGetCellData;
    property OnGetCellProperties: TTMSFMXGridOnGetCellProperties read FOnGetCellProperties write FOnGetCellProperties;
    property OnGetCellAppearance: TTMSFMXGridOnGetCellAppearance read FOnGetCellAppearance write FOnGetCellAppearance;
    property OnGetCellLayout: TTMSFMXGridOnGetCellLayout read FOnGetCellLayout write FOnGetCellLayout;
    property OnGetCellMergeInfo: TTMSFMXGridOnGetCellMergeInfo read FOnGetCellMergeInfo write FOnGetCellMergeInfo;
    property OnGetCellReadOnly: TTMSFMXGridOnGetCellReadOnly read FOnGetCellReadOnly write FOnGetCellReadOnly;
    property OnGetRowIsBand: TTMSFMXGridOnGetRowIsBand read FOnGetRowIsBand write FOnGetRowIsBand;

    property OnCanInsertRow: TTMSFMXGridOnCanInsertRow read FOnCanInsertRow write FOnCanInsertRow;
    property OnCanAppendRow: TTMSFMXGridOnCanAppendRow read FOnCanAppendRow write FOnCanAppendRow;
    property OnCanAppendColumn: TTMSFMXGridOnCanAppendColumn read FOnCanAppendColumn write FOnCanAppendColumn;
    property OnCanDeleteRow: TTMSFMXGridOnCanDeleteRow read FOnCanDeleteRow write FOnCanDeleteRow;

    property OnInsertRow: TTMSFMXGridOnInsertRow read FOnInsertRow write FOnInsertRow;
    property OnAppendRow: TTMSFMXGridOnAppendRow read FOnAppendRow write FOnAppendRow;
    property OnAppendColumn: TTMSFMXGridOnAppendColumn read FOnAppendColumn write FOnAppendColumn;
    property OnDeleteRow: TTMSFMXGridOnDeleteRow read FOnDeleteRow write FOnDeleteRow;

    property OnCellAnchorClick: TTMSFMXGridOnCellAnchorClick read FOnCellAnchorClick write FOnCellAnchorClick;

    property OnGetCellEditorCustomClassType: TTMSFMXGridOnGetCellEditorCustomClassType read FOnGetCellEditorCustomClassType write FOnGetCellEditorCustomClassType;
    property OnGetCellEditorType: TTMSFMXGridOnGetCellEditorType read FOnGetCellEditorType write FOnGetCellEditorType;
    property OnCellEditGetData: TTMSFMXGridOnCellEditGetData read FOnCellEditGetData write FOnCellEditGetData;
    property OnCellEditValidateData: TTMSFMXGridOnCellEditValidateData read FOnCellEditValidateData write FOnCellEditValidateData;
    property OnCellEditSetData: TTMSFMXGridOnCellEditSetData read FOnCellEditSetData write FOnCellEditSetData;

    property OnCellEditGetColor: TTMSFMXGridOnCellEditGetColor read FOnCellEditGetColor write FOnCellEditGetColor;
    property OnCellEditValidateColor: TTMSFMXGridOnCellEditValidateColor read FOnCellEditValidateColor write FOnCellEditValidateColor;
    property OnCellEditSetColor: TTMSFMXGridOnCellEditSetColor read FOnCellEditSetColor write FOnCellEditSetColor;

    property OnCellEditDone: TTMSFMXGridOnCellEditDone read FOnCellEditDone write FOnCellEditDone;
    property OnCellEditCancel: TTMSFMXGridOnCellEditCancel read FOnCellEditCancel write FOnCellEditCancel;
    property OnGetCellEditorProperties: TTMSFMXGridOnGetCellEditorProperties read FOnGetCellEditorProperties write FOnGetCellEditorProperties;
    property OnGetCellIsFixed: TTMSFMXGridOnGetCellIsFixed read FOnGetCellIsFixed write FOnGetCellIsFixed;


    property OnFixedCellBitmapClick: TTMSFMXGridCellControlEvent read FOnFixedCellBitmapClick write FOnFixedCellBitmapClick;
    property OnFixedCellButtonClick: TTMSFMXGridCellControlEvent read FOnFixedCellButtonClick write FOnFixedCellButtonClick;
    property OnFixedCellDropDownButtonClick: TTMSFMXGridCellControlEvent read FOnFixedCellDropDownButtonClick write FOnFixedCellDropDownButtonClick;
    property OnFixedCellCheckBoxClick: TTMSFMXGridCellControlEvent read FOnFixedCellCheckBoxClick write FOnFixedCellCheckBoxClick;
    property OnFixedCellSpinBoxChange: TTMSFMXGridCellControlEvent read FOnFixedCellSpinBoxChange write FOnFixedCellSpinBoxChange;


    property OnCellBeforeDraw: TTMSFMXGridCellBeforeDraw read FOnCellBeforeDraw write FOnCellBeforeDraw;
    property OnCellAfterDraw: TTMSFMXGridCellAfterDraw read FOnCellAfterDraw write FOnCellAfterDraw;
    property OnCellBitmapClick: TTMSFMXGridCellControlEvent read FOnCellBitmapClick write FOnCellBitmapClick;
    property OnCellButtonClick: TTMSFMXGridCellControlEvent read FOnCellButtonClick write FOnCellButtonClick;
    property OnCellRadioButtonClick: TTMSFMXGridCellControlEvent read FOnCellRadioButtonClick write FOnCellRadioButtonClick;
    property OnCellShowPopup: TTMSFMXGridCellControlEvent read FOnCellShowPopup write FOnCellShowPopup;
    property OnCellHidePopup: TTMSFMXGridCellControlEvent read FOnCellHidePopup write FOnCellHidePopup;
    property OnCellCheckBoxClick: TTMSFMXGridCellControlEvent read FOnCellCheckBoxClick write FOnCellCheckBoxClick;
    property OnCellCommentClick: TTMSFMXGridCellControlEvent read FOnCellCommentClick write FOnCellCommentClick;
    property OnCellSortClick: TTMSFMXGridCellControlEvent read FOnCellSortClick write FOnCellSortClick;
    property OnCellNodeClick: TTMSFMXGridCellControlEvent read FOnCellNodeClick write FOnCellNodeClick;

    property OnCanSizeColumn: TTMSFMXGridOnCanSizeColumn read FOnCanSizeColumn write FOnCanSizeColumn;
    property OnCanSizeRow: TTMSFMXGridOnCanSizeRow read FOnCanSizeRow write FOnCanSizeRow;
    property OnColumnSize: TTMSFMXGridOnColumnSize read FOnColumnSize write FOnColumnSize;
    property OnRowSize: TTMSFMXGridOnRowSize read FOnRowSize write FOnRowSize;
    property OnColumnSized: TTMSFMXGridOnColumnSized read FOnColumnSized write FOnColumnSized;
    property OnRowSized: TTMSFMXGridOnRowSized read FOnRowSized write FOnRowSized;

    property OnCanDragColumn: TTMSFMXGridOnCanSizeColumn read FOnCanDragColumn write FOnCanDragColumn;
    property OnCanDragRow: TTMSFMXGridOnCanSizeRow read FOnCanDragRow write FOnCanDragRow;
    property OnColumnDragged: TTMSFMXGridOnColumnDragged read FOnColumnDragged write FOnColumnDragged;
    property OnRowDragged: TTMSFMXGridOnRowDragged read FOnRowDragged write FOnRowDragged;

    property OnColumnSorted: TTMSFMXGridOnColumnSorted read FOnColumnSorted write FOnColumnSorted;
    property OnCanSortColumn: TTMSFMXGridOnCanSortColumn read FOnCanSortColumn write FOnCanSortColumn;

    property OnCellClick: TTMSFMXGridOnCellClick read FOnCellClick write FOnCellClick;
    property OnFixedCellClick: TTMSFMXGridOnCellClick read FOnFixedCellClick write FOnFixedCellClick;

    {$IFNDEF ANDROID}
    property OnPrintBegin: TTMSFMXGridOnPrintEvent read FOnPrintBegin write FOnPrintBegin;
    property OnPrintNewPage: TTMSFMXGridOnPrintNewPage read FOnPrintNewPage write FOnPrintNewPage;
    property OnPrintDrawCell: TTMSFMXGridOnPrintDrawCell read FOnPrintDrawCell write FOnPrintDrawCell;
    property OnPrintBeforeDrawCell: TTMSFMXGridOnPrintBeforeDrawCell read FOnPrintBeforeDrawCell write FOnPrintBeforeDrawCell;
    property OnPrintAfterDrawCell: TTMSFMXGridOnPrintAfterDrawCell read FOnPrintAfterDrawCell write FOnPrintAfterDrawCell;
    property OnPrintProgress: TTMSFMXGridOnPrintProgress read FOnPrintProgress write FOnPrintProgress;
    property OnPrintEnd: TTMSFMXGridOnPrintEvent read FOnPrintEnd write FOnPrintEnd;
    property OnPrintEndPage: TTMSFMXGridOnPrintEndPage read FOnPrintEndPage write FOnPrintEndPage;
    property OnPrintDrawTitle: TTMSFMXGridOnPrintDrawText read FOnPrintDrawTitle write FOnPrintDrawTitle;
    property OnPrintDrawDescription: TTMSFMXGridOnPrintDrawText read FOnPrintDrawDescription write FOnPrintDrawDescription;
    property OnPrintDrawPageNumber: TTMSFMXGridOnPrintDrawText read FOnPrintDrawPageNumber write FOnPrintDrawPageNumber;
    {$ENDIF}

    property OnPrintBeforeDraw: TTMSFMXGridOnPrintBeforeDraw read FOnPrintBeforeDraw write FOnPrintBeforeDraw;
    property OnPrintAfterDraw: TTMSFMXGridOnPrintAfterDraw read FOnPrintAfterDraw write FOnPrintAfterDraw;

    property OnSelectCell: TTMSFMXGridOnSelectCell read FOnSelectCell write FOnSelectCell;

  end;

procedure GetAspectSize(var x, y, w, h: Single; ow, oh, nw, nh: Single);

implementation

constructor TCellCalendarBox.Create(AOwner: TComponent);
begin
  inherited;
  InputSupport := False;
end;

procedure GetAspectSize(var x, y, w, h: Single; ow, oh, nw, nh: Single);
begin
  if (ow > 0) and (oh > 0) and (nw > 0) and (nh > 0) then
  begin
    if ow / oh < nw / nh then
    begin
      h := nh;
      w := nh * ow / oh;
    end
    else
    begin
      w := nw;
      h := nw * oh / ow;
    end;
  end
  else
  begin
    w := 0;
    h := 0;
  end;
end;

{$R FMX.TMSGrid.res}

function PtInR(const Rect: TRectF; const P: TPointF): Boolean;
begin
  Result := (P.X >= Rect.Left) and (P.X < Rect.Right) and (P.Y >= Rect.Top)
    and (P.Y < Rect.Bottom);
end;

{ TTMSFMXGrid }

procedure TTMSFMXCustomGrid.ApplyAdditionalStyle(AControl: TFMXObject);
var
  bl, nl, sl, fl, fsell, focl, gl, sml: TTMSFMXGridCell;
begin
  if Assigned(AControl) then
  begin
    gl := GetDefaultGroupLayout(AControl);
    sml := GetDefaultSummaryLayout(AControl);
    nl := GetDefaultNormalLayout(AControl);
    sl := GetDefaultSelectedLayout(AControl);
    fl := GetDefaultFixedLayout(AControl);
    fsell := GetDefaultFixedSelectedLayout(AControl);
    focl := GetDefaultFocusedLayout(AControl);
    bl := GetDefaultBandLayout(AControl);

    if Assigned(bl) and Assigned(gl) and Assigned(sml) and Assigned(nl) and Assigned(sl) and Assigned(fl) and Assigned(fsell) and Assigned(focl) then
    begin
      fl.Layout.Fill.Gradient.Color := claWhite;
      fl.Layout.Fill.Gradient.Color1 := MakeColor(230, 230, 230);
      {$IFDEF DELPHIXE6_LVL}
      fl.Layout.Fill.Kind := TBrushKind.Gradient;
      fl.Layout.Sides := [TSide.Right, TSide.Bottom];
      {$ELSE}
      fl.Layout.Fill.Kind := TBrushKind.bkGradient;
      fl.Layout.Sides := [TSide.sdRight, TSide.sdBottom];
      {$ENDIF}
      fl.Layout.Font.Style := [TFontStyle.fsBold];

      sl.Layout.Fill.Color := claLightsteelblue;
      sl.Layout.FontFill.Color := claWhite;
      sl.Layout.Stroke.Color := claWhite;
      {$IFDEF DELPHIXE6_LVL}
      sl.Layout.Sides := [TSide.Right, TSide.Bottom];
      {$ELSE}
      sl.Layout.Sides := [TSide.sdRight, TSide.sdBottom];
      {$ENDIF}

      focl.Layout.Fill.Color := claLightsteelblue;
      focl.Layout.FontFill.Color := claWhite;
      focl.Layout.Stroke.Color := claBlack;
      {$IFDEF DELPHIXE6_LVL}
      focl.Layout.StrokeDash := TStrokeDash.Dash;
      {$ELSE}
      focl.Layout.StrokeDash := TStrokeDash.sdDash;
      {$ENDIF}

      fsell.Layout.Fill.Gradient.Color := claWhite;
      fsell.Layout.Fill.Gradient.Color1 := claLightsteelblue;
      {$IFDEF DELPHIXE6_LVL}
      fsell.Layout.Fill.Kind := TBrushKind.Gradient;
      {$ELSE}
      fsell.Layout.Fill.Kind := TBrushKind.bkGradient;
      {$ENDIF}
      fsell.Layout.Sides := fl.Layout.Sides;
      fsell.Layout.Font.Style := [TFontStyle.fsBold];

      {$IFDEF DELPHIXE6_LVL}
      nl.Layout.Sides := [TSide.Right, TSide.Bottom];
      {$ELSE}
      nl.Layout.Sides := [TSide.sdRight, TSide.sdBottom];
      {$ENDIF}

      gl.Layout.Assign(nl.Layout);
      sml.Layout.Assign(nl.Layout);

      bl.Layout.Assign(nl.Layout);
      bl.Layout.Fill.Color := claLightyellow;

      gl.Layout.Fill.Color := MakeColor(110,147,195);
      gl.Layout.FontFill.Color := claWhite;
      sml.Layout.Fill.Color := MakeColor(185,197,213);
    end;
  end;
end;

procedure TTMSFMXCustomGrid.ApplyCellState(ACol, ARow: Integer; Cell: TFMXObject);
var
  cl: TTMSFMXGridCell;
  ctl: TTMSFMXFixedGridCell;
  idx: Integer;
begin
  if (Cell is TTMSFMXGridCell) then
  begin
    cl := cell as TTMSFMXGridCell;

    if Assigned(cl) then
    begin
      if (SelectionMode <> smNone) and (FocusedCell.Row = ARow) and (FocusedCell.Col = ACol) then
        cl.AssignLayout(GetCellAppearance(csFocused, ACol, ARow))
      else
      begin
        if IsFixed(ACol, ARow) then
        begin
          if (SelectionMode <> smNone) and FixedShowSelection and DoIsFixedCellSelected(ACol, ARow) and not IsNormalFixed(ACol, ARow) then
            cl.AssignLayout(GetCellAppearance(csFixedSelected, ACol, ARow))
          else
            cl.AssignLayout(GetCellAppearance(csFixed, ACol, ARow));

          if ((FixedRightColumns > 0) and (ACol >= ColumnCount - 1 - FixedRightColumns)) and ((FixedFooterRows > 0) and (ARow >= RowCount - 1 - FixedFooterRows)) then
            {$IFDEF DELPHIXE6_LVL}
            (cell as TTMSFMXGridCell).Layout.Sides := [TSide.Top, TSide.Left];
            {$ELSE}
            (cell as TTMSFMXGridCell).Layout.Sides := [TSide.sdTop, TSide.sdLeft];
            {$ENDIF}

          if ((FixedRightColumns > 0) and (ACol >= ColumnCount - 1 - FixedRightColumns)) then
          begin
            {$IFDEF DELPHIXE6_LVL}
            cl.Layout.Sides := [TSide.Left, TSide.Bottom];
            if ACol = ColumnCount - 1 then
              cl.Layout.Sides := cl.Layout.Sides + [TSide.Right];
            {$ELSE}
            cl.Layout.Sides := [TSide.sdLeft, TSide.sdBottom];
            if ACol = ColumnCount - 1 then
              cl.Layout.Sides := cl.Layout.Sides + [TSide.sdRight];
            {$ENDIF}
          end;
          if ((FixedFooterRows > 0) and (ARow >= RowCount - 1 - FixedFooterRows)) then
          begin
            {$IFDEF DELPHIXE6_LVL}
            cl.Layout.Sides := [TSide.Top, TSide.Right];
            if ARow = RowCount - 1 then
              cl.Layout.Sides := cl.Layout.Sides + [TSide.Bottom];
            {$ELSE}
            cl.Layout.Sides := [TSide.sdTop, TSide.sdRight];
            if ARow = RowCount - 1 then
              cl.Layout.Sides := cl.Layout.Sides + [TSide.sdBottom];
            {$ENDIF}
          end;

          if (FixedFooterRows > 0) and (FixedRightColumns > 0) and (ACol >= ColumnCount - 1 - FixedRightColumns)
            and (ARow >= RowCount - 1 - FixedFooterRows) then
          begin
            {$IFDEF DELPHIXE6_LVL}
            cl.Layout.Sides := [TSide.Left, TSide.Top];
            if ARow = RowCount - 1 then
              cl.Layout.Sides := cl.Layout.Sides + [TSide.Bottom];
            if ACol = ColumnCount - 1 then
              cl.Layout.Sides := cl.Layout.Sides + [TSide.Right];
            {$ELSE}
            cl.Layout.Sides := [TSide.sdLeft, TSide.sdTop];
            if ARow = RowCount - 1 then
              cl.Layout.Sides := cl.Layout.Sides + [TSide.sdBottom];
            if ACol = ColumnCount - 1 then
              cl.Layout.Sides := cl.Layout.Sides + [TSide.sdRight];
            {$ENDIF}
          end;
        end
        else if (SelectionMode <> smNone) and DoIsCellSelected(ACol,ARow) then
          cl.AssignLayout(GetCellAppearance(csSelected, ACol, ARow))
        else
          cl.AssignLayout(GetCellAppearance(csNormal, ACol, ARow));
      end;

      {$IFDEF DELPHIXE6_LVL}
      if IsFixed(ACol, ARow) then
      begin
        case Options.Borders.FixedCellBorders of
          bNone: cl.Layout.Sides := [];
          bVertical: cl.Layout.Sides := cl.Layout.Sides - [TSide.Top, TSide.Bottom];
          bHorizontal: cl.Layout.Sides := cl.Layout.Sides - [TSide.Left, TSide.Right];
        end;
      end
      else
      begin
        case Options.Borders.CellBorders of
          bNone: cl.Layout.Sides := [];
          bVertical: cl.Layout.Sides := cl.Layout.Sides - [TSide.Top, TSide.Bottom];
          bHorizontal: cl.Layout.Sides := cl.Layout.Sides - [TSide.Left, TSide.Right];
        end;
      end;
      {$ELSE}
      if IsFixed(ACol, ARow) then
      begin
        case Options.Borders.FixedCellBorders of
          bNone: cl.Layout.Sides := [];
          bVertical: cl.Layout.Sides := cl.Layout.Sides - [TSide.sdTop, TSide.sdBottom];
          bHorizontal: cl.Layout.Sides := cl.Layout.Sides - [TSide.sdLeft, TSide.sdRight];
        end;
      end
      else
      begin
        case Options.Borders.CellBorders of
          bNone: cl.Layout.Sides := [];
          bVertical: cl.Layout.Sides := cl.Layout.Sides - [TSide.sdTop, TSide.sdBottom];
          bHorizontal: cl.Layout.Sides := cl.Layout.Sides - [TSide.sdLeft, TSide.sdRight];
        end;
      end;
      {$ENDIF}

      if (cl is TTMSFMXFixedGridCell) then
      begin
        ctl := (cl as TTMSFMXFixedGridCell);
        ctl.SortFill.Assign(GetDefaultSortFill);
        ctl.SortFont.Assign(GetDefaultSortFont);
        ctl.SortFontFill.Color := GetDefaultSortFontFill.Color;

        ctl.SortStroke.Assign(GetDefaultSortStroke);
        ctl.SortIndex := -1;
        ctl.SortKind := skNone;

        case Options.Sorting.Mode of
          gsmNone: ctl.SortKind := skNone;
          gsmNormal:
          begin
            if ACol = SortColumn then
            begin
              case SortDirection of
                sdAscending: ctl.SortKind := skAscending;
                sdDescending: ctl.SortKind := skDescending;
              end;
            end;
          end;
          gsmIndexed:
          begin
            idx := SortIndexes.FindIndex(ACol);
            if idx <> -1 then
            begin
              ctl.SortFill.Assign(GetDefaultIndexedSortFill);
              ctl.SortFont.Assign(GetDefaultIndexedSortFont);
              ctl.SortFontFill.Color := GetDefaultIndexedSortFontFill.Color;
              ctl.SortStroke.Assign(GetDefaultIndexedSortStroke);
              ctl.SortIndex := idx + 1;
              if SortIndexes.SortDirections[idx] then
                ctl.SortKind := skAscending
              else
                ctl.SortKind := skDescending;
            end;
          end;
        end;
      end;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.ApplyStyle;
begin
  inherited;
  AssignEvents;
  UpdateHeader;
  UpdateFooter;
  UpdateGrid;
end;

procedure TTMSFMXCustomGrid.Assign(Source: TPersistent);
begin
  if (Source is TTMSFMXCustomGrid) then
  begin
    Options.Assign((Source as TTMSFMXCustomGrid).Options);
    ColumnCount := (Source as TTMSFMXCustomGrid).ColumnCount;
    RowCount := (Source as TTMSFMXCustomGrid).RowCount;
    FixedColumns := (Source as TTMSFMXCustomGrid).FixedColumns;
    FixedRows := (Source as TTMSFMXCustomGrid).FixedRows;
    FixedRightColumns := (Source as TTMSFMXCustomGrid).FixedRightColumns;
    FixedFooterRows := (Source as TTMSFMXCustomGrid).FixedFooterRows;
    FreezeColumns := (Source as TTMSFMXCustomGrid).FreezeColumns;
    FreezeRows := (Source as TTMSFMXCustomGrid).FreezeRows;
    BitmapContainer := (Source as TTMSFMXCustomGrid).BitmapContainer;
    Enabled := (Source as TTMSFMXCustomGrid).Enabled;
    FixedShowSelection := (Source as TTMSFMXCustomGrid).FixedShowSelection;
    EnableDragHighlight := (Source as TTMSFMXCustomGrid).EnableDragHighlight;
    DefaultColumnWidth := (Source as TTMSFMXCustomGrid).DefaultColumnWidth;
    DefaultRowHeight := (Source as TTMSFMXCustomGrid).DefaultRowHeight;
    LeftCol := (Source as TTMSFMXCustomGrid).LeftCol;
    TopRow := (Source as TTMSFMXCustomGrid).TopRow;
    SelectionMode := (Source as TTMSFMXCustomGrid).SelectionMode;
    ScrollMode := (Source as TTMSFMXCustomGrid).ScrollMode;
  end;
end;

procedure TTMSFMXCustomGrid.AssignEvents;
var
  scrollbarv, scrollbarh: TScrollBar;
  nl, gl, sml, sl, fl, fsell, focl: TTMSFMXGridCell;
begin
  scrollbarv := GetVerticalScrollBar;
  scrollbarh := GetHorizontalScrollBar;
  if Assigned(scrollbarv) then
    scrollbarv.OnChange := ScrollBarVChanged;
  if Assigned(scrollbarh) then
    scrollbarh.OnChange := ScrollBarHChanged;

  gl := GetDefaultGroupLayout;
  sml := GetDefaultSummaryLayout;
  nl := GetDefaultNormalLayout;
  sl := GetDefaultSelectedLayout;
  fl := GetDefaultFixedLayout;
  fsell := GetDefaultFixedSelectedLayout;
  focl := GetDefaultFocusedLayout;

  if Assigned(gl) then
    gl.Layout.OnChange := LayoutChanged;

  if Assigned(sml) then
    sml.Layout.OnChange := LayoutChanged;

  if Assigned(nl) then
    nl.Layout.OnChange := LayoutChanged;

  if Assigned(fl) then
    fl.Layout.OnChange := LayoutChanged;

  if Assigned(sl) then
    sl.Layout.OnChange := LayoutChanged;

  if Assigned(focl) then
    focl.Layout.OnChange := LayoutChanged;

  if Assigned(fsell) then
    fsell.Layout.OnChange := LayoutChanged;
end;

procedure TTMSFMXCustomGrid.AutoSizeColumn(ACol: Integer; Accurate: Boolean = True);
begin
  AutoSizeColumnInt(ACol, Accurate);
  UpdateGridCells;
end;

procedure TTMSFMXCustomGrid.AutoSizeColumnInt(ACol: Integer; Accurate: Boolean = True);
var
  i: integer;
  cell: TFMXObject;
  cellclasstype,prevclasstype: TFmxObjectClass;
  cellstr: string;
  m,nw: single;
  tw: Single;
  ctl: TTMSFMXGridCell;
  ft: TFont;
  l: TTMSFMXGridCellLayout;
  cellContainer: TControl;
begin
  cellContainer := GetCellContainer;
  if Assigned(cellContainer) then
  begin
    l := TTMSFMXGridCellLayout.Create(nil);
    m := 0;
    nw := 0;

    Cell := nil;
    ft := nil;
    prevclasstype := nil;

    if not Accurate then
    begin
      prevclasstype := TTMSFMXGridCell;
      cell := TTMSFMXGridCell.Create(Self);
      GetCellContainer.AddObject(cell);
      (cell as TTMSFMXGridCell).Layout.Font.OnChanged := nil;
      (Cell as TTMSFMXGridCell).Text := 'W';
      nw := (Cell as TTMSFMXGridCell).GetTextWidth;
      ft := TFont.Create;
      ft.Assign((cell as TTMSFMXGridCell).Layout.Font);
    end;

    for i := 0 to RowCount - 1 do
    begin
      DoGetCellClass(ACol,  i, cellclasstype);
      DoGetCellData(ACol, i, cellstr);

      if (prevclasstype <> cellclasstype) and (prevclasstype <> nil) and Assigned(cell) then
        cell.Free;

      if (prevclasstype <> cellclasstype) then
      begin
        cell := cellclasstype.Create(Self);
        GetCellContainer.AddObject(cell);
        if (cell is TTMSFMXGridCell) then
        begin
          (cell as TTMSFMXGridCell).Layout.Font.OnChanged := nil;
          (cell as TTMSFMXGridCell).HitTest := False;
          (cell as TTMSFMXGridCell).BitmapContainer := BitmapContainer;
        end;
      end;

      prevclasstype := cellclasstype;

      if (cell is TTMSFMXGridCell) then
      begin
        (cell as TTMSFMXGridCell).Text := cellstr;
        (cell as TTMSFMXGridCell).Width := ColumnWidths[ACol];
        (cell as TTMSFMXGridCell).Height := RowHeights[i];
      end;

      ApplyCellState(ACol, i, cell);

      if (Cell is TTMSFMXGridCell) then
      begin
        l.Assign((Cell as TTMSFMXGridCell).Layout);
        DoGetCellLayout(ACol, i, l, GetCellState(ACol, i));
        (Cell as TTMSFMXGridCell).AssignLayout(l);
      end;

      DoGetCellAppearance(ACol, i, cell, GetCellState(ACol, i));
      DoGetCellProperties(ACol, i, cell);

      if (cell is TTMSFMXGridCell) then
      begin
        ctl := (cell as TTMSFMXGridCell);
        if not Accurate then
        begin
          if (ctl.Layout.Font.Size = ft.Size) and (ctl.Layout.Font.Style = ft.Style) and
            (ctl.Layout.Font.Family = ft.Family) then
          begin
            tw := Length(ctl.GetStrippedHTMLText);
            if tw > m then
              m := tw;
          end;
        end
        else
        begin
          nw := ctl.GetTextWidth;
          if nw > m then
            m := nw;
        end;
      end;
    end;

    if Assigned(cell) then
      cell.Free;

    if Assigned(ft) then
      ft.Free;


    if Accurate then
      ColumnWidths[ACol] := Round(m + 5)
    else
      ColumnWidths[ACol] := Round((m * nw) + 5);

    l.Free;
  end;
end;

procedure TTMSFMXCustomGrid.AutoSizeColumns(Accurate: Boolean = True);
var
  i: integer;
begin
  for i := 0 to ColumnCount - 1 do
    AutoSizeColumn(i, Accurate);

  UpdateGridCells;
end;

procedure TTMSFMXCustomGrid.AutoSizeRow(ARow: integer);
begin
  AutoSizeRowInt(ARow);
  UpdateGridCells;
end;

procedure TTMSFMXCustomGrid.AutoSizeRowInt(ARow: integer);
var
  i: integer;
  cell: TFMXObject;
  cellclasstype,prevclasstype: TFmxObjectClass;
  cellstr: string;
  m,nw: single;
  ctl: TTMSFMXGridCell;
  l: TTMSFMXGridCellLayout;
  cellContainer: TControl;
begin

  cellContainer := GetCellContainer;
  if Assigned(cellContainer) then
  begin
    l := TTMSFMXGridCellLayout.Create(nil);
    m := 0;
    prevclasstype := nil;
    cell := nil;

    for i := 0 to ColumnCount - 1 do
    begin
      DoGetCellClass(i, ARow, cellclasstype);
      DoGetCellData(i, ARow, cellstr);

      if (prevclasstype <> cellclasstype) and (prevclasstype <> nil) and Assigned(cell) then
        cell.Free;

      if (prevclasstype <> cellclasstype) then
      begin
        cell := cellclasstype.Create(self);
        cellContainer.AddObject(cell);
        if (cell is TTMSFMXGridCell) then
        begin
          (cell as TTMSFMXGridCell).Layout := nil;
          (cell as TTMSFMXGridCell).HitTest := False;
          (cell as TTMSFMXGridCell).BitmapContainer := BitmapContainer;
        end;
      end;

      prevclasstype := cellclasstype;

      if (cell is TTMSFMXGridCell) then
      begin
        (cell as TTMSFMXGridCell).Text := cellstr;
        (cell as TTMSFMXGridCell).Width := ColumnWidths[i];
        (cell as TTMSFMXGridCell).Height := RowHeights[ARow];
      end;

      ApplyCellState(i, ARow, cell);
      if (Cell is TTMSFMXGridCell) then
      begin
        l.Assign((Cell as TTMSFMXGridCell).Layout);
        DoGetCellLayout(i, ARow, l, GetCellState(i, ARow));
        (Cell as TTMSFMXGridCell).AssignLayout(l);
      end;
      DoGetCellAppearance(i, ARow, cell, GetCellState(i, ARow));
      DoGetCellProperties(i, ARow, cell);

      if (cell is TTMSFMXGridCell) then
      begin
        ctl := (cell as TTMSFMXGridCell);
        nw := ctl.GetTextHeight;
        if nw > m then
          m := nw;
      end;
    end;

    if Assigned(cell) then
      cell.Free;

    RowHeights[ARow] := m;

    l.Free;
  end;
end;

procedure TTMSFMXCustomGrid.AutoSizeRows;
var
  i: integer;
begin
  for i := 0 to RowCount - 1 do
    AutoSizeRowInt(i);
  UpdateGridCells;

end;

procedure TTMSFMXCustomGrid.BeginUpdate;
begin
  inherited;
  UpdateCount := UpdateCount + 1;
end;

procedure TTMSFMXCustomGrid.CalculatePosition(var cStart: Integer; var cStop: Integer; var rStart: Integer; var rStop: Integer);
var
  vVal, hVal, vMax, hMax: Single;
  scrollv, scrollh: Single;
  scrollbarv, scrollbarh: TScrollbar;
  I: Integer;
  rBounds: TRectF;
  cellbackground: TControl;
  cfs, rfs, cfst, rfst: Integer;
  fw, fh, fwr, fhr: Single;
  cellContainer: TControl;
  cw, rh: Single;
  x, y: Single;
begin
  scrollbarv := GetVerticalScrollBar;
  scrollbarh := GetHorizontalScrollBar;
  cellbackground := GetCellWrapper;
  cellContainer := GetCellContainer;
  if Assigned(scrollbarv) and Assigned(scrollbarh) and Assigned(cellbackground) and Assigned(cellContainer) then
  begin
    vVal := scrollbarv.Value;
    hVal := scrollbarh.Value;
    hMax := scrollbarh.Max;
    vMax := scrollbarv.Max;
    scrollv := 0;
    scrollh := 0;

    if ScrollMode = smCellScrolling then
    begin
      hMax := GetTotalColumnWidth;
      vMax := GetTotalRowHeight;

      for I := FixedRows to Round(vVal) - 1 + FixedRows do scrollv := scrollv + RowHeights[I];
      for I := FixedColumns to Round(hVal) - 1 + FixedColumns do scrollh := scrollh + ColumnWidths[I];

      vVal := scrollv;
      hval := scrollh;
    end;

    cfs := GetFixedCols;
    rfs := GetFixedRows;
    cfst := FixedRightColumns;
    rfst := FixedFooterRows;

    CStart := cfs;
    RStart := rfs;

    rBounds := cellbackground.BoundsRect;
    fw := GetFixedWidth;
    fh := GetFixedHeight;
    fwr := GetFixedRightWidth;
    fhr := GetFixedFooterHeight;

    if hVal > hMax / 2 then
    begin
      CStop := ColumnCount - 1 - cfst;
      x := cellContainer.Width - fwr;
      repeat
        cw := ColumnWidths[CStop];
        if (x - cw > hVal + rBounds.Width - fwr) then
        begin
          x := x - cw;
          Dec(CStop);
        end;
      until x - cw <= hVal + rBounds.Width - fwr;
    end
    else
    begin
      x := fw;
      repeat
        cw := ColumnWidths[CStart];
        if (x + cw <= hVal + fw) then
        begin
          x := x + cw;
          inc(CStart);
        end;
      until x + cw > hVal + fw;
    end;

    if vval > vMax / 2 then
    begin
      RStop := RowCount - 1 - rfst;
      y := CellContainer.Height - fhr;
      repeat
        rh := RowHeights[RStop];
        if (y - rh > vVal + rBounds.Height - fhr) then
        begin
          y := y - rh;
          Dec(RStop);
        end;
      until y - rh <= vVal + rBounds.Height - fhr;
    end
    else
    begin
      y := fh;
      repeat
        rh := RowHeights[RStart];
        if (y + rh <= vVal + fh) then
        begin
          y := y + rh;
          inc(RStart);
        end;
      until y + rh > vVal + fh;
    end;

    if hval > hMax / 2 then
    begin
      CStart := CStop;
      while (CStart > 0) and (x > hVal + fw) do
      begin
        x := x - ColumnWidths[CStart];
        if not ((CStart > 0) and (x > hVal + fw)) then
          Break;
        Dec(CStart);
      end;
    end
    else
    begin
      CStop := CStart;
      while (CStop < ColumnCount - 1) and (x < hVal + rBounds.Width - fwr) do
      begin
        x := x + ColumnWidths[CStop];
        if not ((CStop < ColumnCount - 1) and (x < hVal + rBounds.Width - fwr)) then
          Break;
        Inc(CStop);
      end;
    end;

    if vval > vMax / 2 then
    begin
      RStart := RStop;
      while (RStart > 0) and (y > vVal + fh) do
      begin
        y := y - RowHeights[RStart];
        if not ((RStart > 0) and (y > vVal + fh)) then
          Break;
        Dec(RStart);
      end;
    end
    else
    begin
      RStop := RStart;
      while (RStop < RowCount - 1) and (y < vVal + rBounds.Height - fhr) do
      begin
        y := y + RowHeights[RStop];
        if not ((RStop < RowCount - 1) and (y < vVal + rBounds.Height - fhr)) then
          Break;
        Inc(RStop);
      end;
    end;

    RStart := Max(FixedRows, Min(RowCount - 1 - FixedFooterRows, RStart));
    CStart := Max(FixedColumns, Min(ColumnCount - 1 - FixedFooterRows, CStart));
    RStop := Max(FixedRows, Min(RowCount - 1 - FixedFooterRows, RStop));
    CStop := Max(FixedColumns, Min(ColumnCount - 1 - FixedRightColumns, CStop));
  end;
end;

procedure TTMSFMXCustomGrid.StopEdit;
begin
  HideEdit;
end;

procedure TTMSFMXCustomGrid.CancelEdit;
var
  celleditor: TTMSFMXGridEditorType;
  p: TPopup;
begin
  DoGetCellEditorType(FEditCell.Col, FEditCell.Row, cellEditor);
  if cellEditor <> etCustom then
  begin
    p := FindPopup(FEditControl);
    if Assigned(p) then
    begin
      if p.IsOpen then
        p.IsOpen := False;
    end;

    FEditControl.Parent := nil;
    FEditControl := nil;
  end
  else
  begin
    FEditControl.Free;
    FEditControl := nil;
  end;

  FEditing := False;

  DoCellEditCancel(FEditCell.Col, FEditCell.Row);

  SetFocus;
end;

procedure TTMSFMXCustomGrid.CellComboBoxChange(Sender: TObject);
begin

end;

procedure TTMSFMXCustomGrid.CellComboBoxKeyDown(Sender: TObject; var Key: Word;
  var KeyChar: WideChar; Shift: TShiftState);
begin
  if Shift <> [] then
    Exit;
  case Key of
    vkF4:
    begin
      if FEditControl = CellComboEdit then
        CellComboEdit.DropDown
      else if FEditControl = CellDatePicker then
        CellDatePicker.DropDown
      else if FEditControl = CellDateEdit then
        CellDateEdit.DropDown
      else if FEditControl = CellColorPicker then
        CellColorPicker.DropDown
      else if FEditControl = CellColorComboBox then
        CellColorComboBox.DropDown;
    end;
    vkReturn, vkF2:
    begin
      HideEdit(Key = vkReturn);
      if Key = vkReturn then
        Key := 0;
    end;
    vkEscape: CancelEdit;
  end;
end;

procedure TTMSFMXCustomGrid.CellControlKeyDown(Sender: TObject; var Key: Word;
  var KeyChar: WideChar; Shift: TShiftState);
begin
  if Shift <> [] then
    Exit;
  case Key of
    vkEscape: CancelEdit;
    vkReturn, vkF2:
    begin
      HideEdit(Key = vkReturn);
      if Key = vkReturn then
        Key := 0;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.CellEditBtnKeyDown(Sender: TObject; var Key: Word;
  var KeyChar: WideChar; Shift: TShiftState);
var
  k: Word;
  Allow: Boolean;
begin
  if Shift <> [] then
    Exit;

  case Key of
    vkF4: CellEditBtn.DropDown;
    vkEscape: CancelEdit;
    vkReturn, vkF2: HideEdit(Key = vkReturn);
    vkLeft:
    begin
      if Options.Keyboard.ArrowKeyDirectEdit then
      begin
        if FEditControl = FCellEdit then
        begin
          if CellEdit.SelStart = 0 then
          begin
            k := vkTab;
            Key := 0;
            ProcessTab(k, [ssShift], tkdNextColumnCell, tkhNextCell, True, csEnd);
          end;
        end;
      end;
    end;
    vkRight:
    begin
      if Options.Keyboard.ArrowKeyDirectEdit then
      begin
        if FEditControl = FCellEdit then
        begin
          if CellEdit.SelStart = Length(CellEdit.Text) then
          begin
            k := vkTab;
            Key := 0;
            ProcessTab(k, [], tkdNextColumnCell, tkhNextCell, True, csStart);
          end;
        end;
      end;
    end;
    vkUp, vkDown:
    begin
      if not CellEditBtn.Popup.IsOpen then
      begin
        HideEdit;
        KeyDown(Key, KeyChar, Shift);

        if Options.Keyboard.ArrowKeyDirectEdit then
        begin
          Allow := True;
          DoCanEditCell(FocusedCell.Col, FocusedCell.Row, Allow);
          if Allow then
            EditCell(FocusedCell);
        end;
      end;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.CellEditKeyDown(Sender: TObject; var Key: Word;
  var KeyChar: WideChar; Shift: TShiftState);
var
  k: Word;
  Allow: Boolean;
begin
  if Shift <> [] then
    Exit;
  case Key of
    vkEscape: CancelEdit;
    vkReturn, vkF2: HideEdit(Key = vkReturn);
    vkLeft:
    begin
      if Options.Keyboard.ArrowKeyDirectEdit then
      begin
        if FEditControl = FCellEdit then
        begin
          if CellEdit.SelStart = 0 then
          begin
            k := vkTab;
            Key := 0;
            ProcessTab(k, [ssShift], tkdNextColumnCell, tkhNextCell, True, csEnd);
          end;
        end;
      end;
    end;
    vkRight:
    begin
      if Options.Keyboard.ArrowKeyDirectEdit then
      begin
        if FEditControl = FCellEdit then
        begin
          if CellEdit.SelStart = Length(CellEdit.Text) then
          begin
            k := vkTab;
            Key := 0;
            ProcessTab(k, [], tkdNextColumnCell, tkhNextCell, True, csStart);
          end;
        end;
      end;
    end;
    vkUp, vkDown:
    begin
      HideEdit;
      KeyDown(Key, KeyChar, Shift);
      if Options.Keyboard.ArrowKeyDirectEdit then
      begin
        Allow := True;
        DoCanEditCell(FocusedCell.Col, FocusedCell.Row, Allow);
        if Allow then
          EditCell(FocusedCell);
      end;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.ClearFixedSorting(ACell: TFMXObject);
var
  c, r: Integer;
  ctl: TTMSFMXFixedGridCell;
  cell: TFMXObject;
begin
  for c := 0 to Length(FCellArray) - 1 do
  begin
    for r := 0 to FixedRows - 1 do
    begin
      cell := FCellArray[c, r];
      if Assigned(cell) and (cell is TTMSFMXFixedGridCell) and (cell <> ACell) then
      begin
        ctl := cell as TTMSFMXFixedGridCell;
        ctl.SortIndex := - 1;
        ctl.SortKind := skNone;
      end;
    end;
  end;
end;

constructor TTMSFMXCustomGrid.Create(AOwner: TComponent);
begin
  inherited;
  FLeftC := 0;
  FTopr := 0;
  CanFocus := True;
  {$IFDEF DELPHIXE6_LVL}
  Fill.Kind := TBrushKind.Solid;
  Stroke.Kind := TBrushKind.Solid;
  {$ELSE}
  Fill.Kind := TBrushKind.bkSolid;
  Stroke.Kind := TBrushKind.bkSolid;
  {$ENDIF}
  FFixedShowSelection := True;
  FFreezeRows := 0;
  FFreezeColumns := 0;
  FScrollMode := smCellScrolling;
  FScrollTimer := TTimer.Create(Self);
  FScrollTimer.OnTimer := ScrollTimer;
  FScrollTimer.Interval := Options.Mouse.AutoScrollingInterval;
  FScrollTimer.Enabled := False;

  FDragTimer := TTimer.Create(Self);
  FDragTimer.OnTimer := DragTimer;
  FDragTimer.Interval := Options.Mouse.AutoScrollingInterval;
  FDragTimer.Enabled := False;

  Width := 370;
  Height := 270;

  FCellEdit := TTMSFMXEdit.Create(Self);
  FCellEdit.DisableFocusEffect := True;
  {$IFDEF DELPHIXE6_LVL}
  FCellEdit.TextAlign := TTextAlign.Leading;
  {$ELSE}
  FCellEdit.TextAlign := TTextAlign.taLeading;
  {$ENDIF}
  FCellEdit.OnKeyDown := CellEditKeyDown;

  FCellEditBtn := TTMSFMXEditBtn.Create(Self);
  FCellEditBtn.DisableFocusEffect := True;
  {$IFDEF DELPHIXE6_LVL}
  FCellEditBtn.TextAlign := TTextAlign.Leading;
  {$ELSE}
  FCellEditBtn.TextAlign := TTextAlign.taLeading;
  {$ENDIF}
  FCellEditBtn.OnKeyDown := CellEditBtnKeyDown;

  FCellSpinBox := TSpinBox.Create(Self);
  FCellSpinBox.DisableFocusEffect := True;
  {$IFDEF DELPHIXE6_LVL}
  FCellSpinBox.TextAlign := TTextAlign.Leading;
  {$ELSE}
  FCellSpinBox.TextAlign := TTextAlign.taLeading;
  {$ENDIF}
  FCellSpinBox.OnKeyDown := CellControlKeyDown;

  FCellComboEdit := TComboEdit.Create(Self);
  FCellComboEdit.DisableFocusEffect := True;
  {$IFDEF DELPHIXE6_LVL}
  FCellComboEdit.TextAlign := TTextAlign.Leading;
  {$ELSE}
  FCellComboEdit.TextAlign := TTextAlign.taLeading;
  {$ENDIF}
  FCellComboEdit.OnKeyDown := CellComboBoxKeyDown;
  FCellComboEdit.OnChange := CellComboBoxChange;

  FCellDatePicker := TCellCalendarBox.Create(Self);
  FCellDatePicker.DisableFocusEffect := True;
  FCellDatePicker.OnKeyDown := CellComboBoxKeyDown;
  FCellDatePicker.OnChange := CellComboBoxChange;

  FCellDateEdit := TCalendarEdit.Create(Self);
  FCellDateEdit.DisableFocusEffect := True;
  {$IFDEF DELPHIXE6_LVL}
  FCellDateEdit.TextAlign := TTextAlign.Leading;
  {$ELSE}
  FCellDateEdit.TextAlign := TTextAlign.taLeading;
  {$ENDIF}
  FCellDateEdit.OnKeyDown := CellComboBoxKeyDown;
  FCellDateEdit.OnChange := CellComboBoxChange;

  FCellColorComboBox := TColorComboBox.Create(Self);
  FCellColorComboBox.DisableFocusEffect := True;
  FCellColorComboBox.OnKeyDown := CellComboBoxKeyDown;
  FCellColorComboBox.OnChange := CellComboBoxChange;

  FCellArcDial := TArcDial.Create(Self);
  FCellArcDial.DisableFocusEffect := True;
  FCellArcDial.OnKeyDown := CellControlKeyDown;

  FCellTrackBar := TTrackBar.Create(Self);
  FCellTrackBar.DisableFocusEffect := True;
  FCellTrackBar.OnKeyDown := CellControlKeyDown;

  FCellColorPicker := TComboColorBox.Create(Self);
  FCellColorPicker.DisableFocusEffect := True;
  FCellColorPicker.OnKeyDown := CellComboBoxKeyDown;
  FCellColorPicker.OnChange := CellComboBoxChange;

  FCellComboBox := TComboBox.Create(Self);
  FcellComboBox.DisableFocusEffect := True;
  FCellComboBox.OnKeyDown := CellComboBoxKeyDown;
  FCellComboBox.OnChange := CellComboBoxChange;
end;

procedure TTMSFMXCustomGrid.DblClick;
begin
  inherited;
  FDblClick := True;
  if FMouseDblClick then
  begin
    FMouseDblClick := False;
    case FSizeMode of
      smPreviousColumnSizing, smColumnSizing: if Options.Mouse.ColumnAutoSizeOnDblClick then AutoSizeColumn(FDragCell.Col);
      smPreviousRowSizing, smRowSizing: if Options.Mouse.RowAutoSizeOnDblClick then AutoSizeRow(FDragCell.Row);
    end;
  end;
end;

destructor TTMSFMXCustomGrid.Destroy;
begin
  FCellEdit.Free;
  FCellEditBtn.Free;
  FCellComboBox.Free;
  FCellSpinBox.Free;
  FCellComboEdit.Free;
  FCellDatePicker.Free;
  FCellDateEdit.Free;
  FCellColorComboBox.Free;
  FCellArcDial.Free;
  FCellTrackBar.Free;
  FCellColorPicker.Free;
  inherited;
end;

procedure TTMSFMXCustomGrid.DialogKey(var Key: Word; Shift: TShiftState);
begin
  ProcessTab(Key, Shift, Options.Keyboard.TabKeyDirection, Options.Keyboard.TabKeyHandling, Options.Keyboard.TabKeyDirectEdit);
  inherited;
end;

procedure TTMSFMXCustomGrid.DisplayCells(cellContainer: TControl; x, y, xOrig, yOrig: Single; CStart, CStop, RStart, RStop, Ci, Ri: Integer; ResetCi: Integer; Fixed: Boolean = False);
var
  cw, rh, cwr, rhr, cwrt, rhrt, xr, yr: Single;
  cell: TFMXObject;
  c, r, bc, br, cs, rs: Integer;
  cellclasstype: TFmxObjectClass;
  cellr: TRectF;
  cellstr: String;
  I: Integer;
  l: TTMSFMXGridCellLayout;
  AControl: TFMXObject;
  bmp: TTMSFMXBitmap;
begin
  l := TTMSFMXGridCellLayout.Create(nil);
  for c := CStart to CStop do
  begin
    cw := ColumnWidths[c];
    for r := RStart to RStop do
    begin
      bc := c;
      br := r;
      cs := 0;
      rs := 0;

      rh := RowHeights[r];

      DoGetCellMergeInfo(c, r, bc, br, cs, rs);

      AControl := nil;

      DoGetCellControl(bc, br, AControl);

      DoGetCellClass(bc, br, cellclasstype);

      cell := nil;
      if (Length(FCellArray) > ci) then
        if (Length(FCellArray[ci]) > ri) then
        begin
          cell := FCellArray[ci, ri];
        end;

      if Assigned(cell) then
      begin
        if (cell.ClassType <> cellclasstype) or (cell.ChildrenCount > 0) then
        begin
          if (Cell is TTMSFMXContainerGridCell) then
          begin
            for I := 0 to cell.ChildrenCount - 1 do
              cell.RemoveObject(cell.Children[I]);
          end;

          cell.Free;
          cell := nil;
          FCellArray[ci, ri] := nil;
        end;
      end;

      cwr := cw;
      rhr := rh;

      cwrt := 0;
      for I := bc to bc + cs - 1 do
        cwrt := cwrt + ColumnWidths[I];

      if cwrt > 0 then
        cwr := cwrt;

      rhrt := 0;
      for I := br to br + rs - 1 do
        rhrt := rhrt + RowHeights[I];

      if rhrt > 0 then
        rhr := rhrt;

      xr := 0;
      for I := bc to c - 1 do
        xr := xr + ColumnWidths[I];

      yr := 0;
      for I := br to r - 1 do
        yr := yr + RowHeights[I];


      cellr := RectF(x - xr, y - yr, x - xr + cwr, y - yr + rhr);
      if not Assigned(cell) then
      begin
        cell := cellclasstype.Create(cellContainer);
        cellContainer.AddObject(cell);

        if (Length(FCellArray) <= ci) then
          SetLength(FCellArray, ci + 1);

        if (Length(FCellArray[ci]) <= ri) then
          SetLength(FCellArray[ci], ri + 1);
      end;

      if Assigned(cell) then
      begin
        if (bc = FocusedCell.Col) and (br = FocusedCell.Row) then
          FSaveC := Cell;

        FCellArray[ci, ri] := cell;

        if cell is TControl then
          (cell as TControl).BeginUpdate;

        if Assigned(AControl) and (bc = c) and (br = r) then
        begin
          cell.AddObject(AControl);
          {$IFDEF DELPHIXE6_LVL}
          (AControl as TControl).Align := TAlignLayout.Client;
          {$ELSE}
          (AControl as TControl).Align := TAlignLayout.alClient;
          {$ENDIF}
        end;

        DoGetCellData(bc, br, cellstr);
        if cell is TTMSFMXGridCell then
        begin
          (cell as TTMSFMXGridCell).OnBeforeDraw := DoBeforeDrawCell;
          (cell as TTMSFMXGridCell).OnAfterDraw := DoAfterDrawCell;
          (cell as TTMSFMXGridCell).Locked := True;
          (cell as TTMSFMXGridCell).Layout := nil;
          (cell as TTMSFMXGridCell).HitTest := False;
          (cell as TTMSFMXGridCell).Text := cellstr;
          (cell as TTMSFMXGridCell).BitmapContainer := BitmapContainer;
        end;

        if (cell is TTMSFMXCheckGridCell) then
          (cell as TTMSFMXCheckGridCell).CheckBox.OnChange := DoCheckBoxClick;

        if (cell is TTMSFMXNodeGridCell) then
        begin
          (cell as TTMSFMXNodeGridCell).NodeShapeOpen.OnClick := DoNodeClick;
          (cell as TTMSFMXNodeGridCell).NodeShapeClosed.OnClick := DoNodeClick;
        end;

        if (cell is TTMSFMXCommentGridCell) then
        begin
          (cell as TTMSFMXCommentGridCell).Comment.OnClick := DoCommentClick;
          (cell as TTMSFMXCommentGridCell).OnShowPopup := DoShowPopup;
          (cell as TTMSFMXCommentGridCell).OnHidePopup := DoHidePopup;
        end;

        if (cell is TTMSFMXRadioGridCell) then
          (cell as TTMSFMXRadioGridCell).RadioButton.OnChange := DoRadioButtonClick;

        if (cell is TTMSFMXButtonGridCell) then
          (cell as TTMSFMXButtonGridCell).Button.OnClick := DoButtonClick;

        if (cell is TTMSFMXBitmapGridCell) then
          (cell as TTMSFMXBitmapGridCell).Bitmap.OnClick := DoBitmapClick;

        if (cell is TTMSFMXFixedGridCell) then
        begin
          (Cell as TTMSFMXFixedGridCell).Bitmap.OnClick := DoFixedBitmapClick;
          (Cell as TTMSFMXFixedGridCell).Button.OnClick := DoFixedButtonClick;
          (Cell as TTMSFMXFixedGridCell).DropDownButton.OnClick := DoFixedDropDownButtonClick;
          if not (Cell as TTMSFMXFixedGridCell).DropDownButtonHasBitmap then
          begin
            bmp := GetDefaultDropDownBitmap;
            if Assigned(bmp) then
            begin
              bmp := (bmp.Clone((cell as TTMSFMXFixedGridCell).DropDownButton) as TTMSFMXBitmap);
              {$IFDEF DELPHIXE6_LVL}
              bmp.Align := TAlignLayout.Center;
              {$ELSE}
              bmp.Align := TAlignLayout.alCenter;
              {$ENDIF}
              bmp.Visible := True;
              bmp.HitTest := False;
              (cell as TTMSFMXFixedGridCell).DropDownButton.AddObject(bmp);
            end;
          end;
          (Cell as TTMSFMXFixedGridCell).CheckBox.OnChange := DoFixedCheckBoxClick;
          (Cell as TTMSFMXFixedGridCell).SpinBox.OnChange := DoFixedSpinBoxChange;
          (cell as TTMSFMXFixedGridCell).OnShowPopup := DoShowPopup;
          (cell as TTMSFMXFixedGridCell).OnHidePopup := DoHidePopup;
        end;


        if cell is TControl then
        begin
          (cell as TControl).SetBounds(cellr.Left, cellr.Top, cellr.Width, cellr.Height);
          (cell as TControl).Visible := True;
          if Fixed then
            (cell as TControl).BringToFront
          else
            (cell as TControl).SendToBack;
        end;

        ApplyCellState(bc, br, cell);

        if (Cell is TTMSFMXGridCell) then
        begin
          l.Assign((Cell as TTMSFMXGridCell).Layout);
          DoGetCellLayout(bc, br, l, GetCellState(bc, br));
          (Cell as TTMSFMXGridCell).AssignLayout(l);
        end;

        DoGetCellAppearance(bc, br, cell, GetCellState(bc, br));
        DoGetCellProperties(bc, br, cell);

        if cell is TControl then
          (cell as TControl).EndUpdate;
      end;


      y := y + rh;
      Inc(ri);
    end;

    y := yOrig;
    x := x + cw;
    Inc(ci);
    ri := ResetCi;
  end;

  l.Free;
end;

procedure TTMSFMXCustomGrid.DoGetCellClass(ACol, ARow: Integer;
  var CellClassType: TFmxObjectClass);
begin
  inherited;
  if Assigned(OnGetCellClass) then
    OnGetCellClass(Self, ACol, ARow, CellClassType);
end;

procedure TTMSFMXCustomGrid.DoGetCellControl(ACol, ARow: Integer;
  var AControl: TFMXObject);
begin
  inherited;
  if Assigned(OnGetCellControl) then
    OnGetCellControl(Self, ACol, ARow, AControl);
end;

procedure TTMSFMXCustomGrid.DoGetCellData(ACol, ARow: Integer;
  var CellString: String);
begin
  inherited;
  if Assigned(OnGetCellData) then
    OnGetCellData(Self, ACol, ARow, CellString);
end;

procedure TTMSFMXCustomGrid.DoCellEditGetColor(ACol, ARow: Integer;
  CellEditor: TFMXObject; var CellColor: TAlphaColor);
begin
  inherited;
end;

procedure TTMSFMXCustomGrid.DoCellEditGetData(ACol, ARow: Integer; CellEditor: TFMXObject; var CellString: String);
begin
  inherited;
  if Assigned(OnCellEditGetData) then
    OnCellEditGetData(Self, ACol, ARow, CellEditor, CellString);
end;

procedure TTMSFMXCustomGrid.DoGetCellEditorProperties(ACol,
  ARow: Integer; CellEditor: TFMXObject);
begin
  inherited;
  if Assigned(OnGetCellEditorProperties) then
    OnGetCellEditorProperties(Self, ACol, ARow, CellEditor);
end;

procedure TTMSFMXCustomGrid.DoGetCellEditorType(ACol, ARow: Integer;
  var CellEditorType: TTMSFMXGridEditorType);
begin
  inherited;
  if Assigned(OnGetCellEditorType) then
    OnGetCellEditorType(Self, ACol, ARow, CellEditorType);
end;

procedure TTMSFMXCustomGrid.DoGetCellLayout(ACol, ARow: Integer;
  ALayout: TTMSFMXGridCellLayout; ACellState: TCellState);
begin
  inherited;
  if Assigned(OnGetCellLayout) then
    OnGetCellLayout(Self, ACol, ARow, ALayout, ACellState);
end;

procedure TTMSFMXCustomGrid.DoGetCellEditorCustomClassType(ACol, ARow: Integer;
  var CellEditorCustomClassType: TFMXObjectClass);
begin
  inherited;
  if Assigned(OnGetCellEditorCustomClassType) then
    OnGetCellEditorCustomClassType(Self, ACol, ARow, CellEditorCustomClassType);
end;

procedure TTMSFMXCustomGrid.DoGetCellMergeInfo(ACol, ARow: Integer; var ABaseCol: Integer; var ABaseRow: Integer; var AColSpan: Integer; var ARowSpan: Integer);
begin
  inherited;
  if Assigned(OnGetCellMergeInfo) then
    OnGetCellMergeInfo(Self, ACol, ARow, ABaseCol, ABaseRow, AColSpan, ARowSpan);
end;

procedure TTMSFMXCustomGrid.DoCanAppendColumn(ACol: Integer; var Allow: Boolean);
begin
  inherited;
  if Assigned(OnCanAppendColumn) then
    OnCanAppendColumn(Self, ACol, Allow);
end;

procedure TTMSFMXCustomGrid.DoCanAppendRow(ARow: Integer; var Allow: Boolean);
begin
  inherited;
  if Assigned(OnCanAppendRow) then
    OnCanAppendRow(Self, ARow, Allow);
end;

procedure TTMSFMXCustomGrid.DoBeforeDrawCell(Sender: TObject; ACanvas: TCanvas;
  var ARect, ATextRect: TRectF; var ADrawText, ADrawBackGround,
  AllowDraw: Boolean);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) then
  begin
    if (ctl is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoBeforeDrawGridCell(Sender, Cell.Col, Cell.Row, ACanvas, ARect, ATextRect, ADrawText, ADrawBackGround, AllowDraw);
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoBeforeDrawGridCell(Sender: TObject; ACol, ARow: Integer;
  ACanvas: TCanvas; var ARect, ATextRect: TRectF; var ADrawText,
  ADrawBackGround, AllowDraw: Boolean);
begin
  if Assigned(OnCellBeforeDraw) then
    OnCellBeforeDraw(Sender, ACol, ARow, ACanvas, Arect, ATextRect, ADrawText, ADrawBackGround, AllowDraw);
end;

procedure TTMSFMXCustomGrid.DoBitmapClick(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) and Assigned(ctl.Parent) then
  begin
    if (ctl.Parent is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl.Parent as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoCellBitmapClick(Cell.Col, Cell.Row, (ctl.Parent as TTMSFMXGridCell));
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoButtonClick(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) and Assigned(ctl.Parent) then
  begin
    if (ctl.Parent is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl.Parent as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoCellButtonClick(Cell.Col, Cell.Row, (ctl.Parent as TTMSFMXGridCell));
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoCheckBoxClick(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) and Assigned(ctl.Parent) then
  begin
    if (ctl.Parent is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl.Parent as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoCellCheckBoxClick(Cell.Col, Cell.Row, (ctl.Parent as TTMSFMXGridCell));
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoCellCommentClick(ACol, ARow: Integer;
  Cell: TFMXObject);
begin
  inherited;

  if (Cell is TTMSFMXCommentGridCell) then
  begin
    if (Cell as TTMSFMXCommentGridCell).IsPopupOpen then
      (Cell as TTMSFMXCommentGridCell).HidePopup
    else
      (Cell as TTMSFMXCommentGridCell).ShowPopup;
  end;

  if Assigned(OnCellCommentClick) then
    OnCellCommentClick(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoColumnDragged(FromColumn, ToColumn: integer);
begin
  if Assigned(OnColumnDragged) then
    OnColumnDragged(Self,FromColumn,ToColumn);
end;

procedure TTMSFMXCustomGrid.DoColumnSize(ACol: Integer;
  var NewWidth: Single);
begin
  inherited;
  if Assigned(OnColumnSize) then
    OnColumnSize(Self, ACol, NewWidth);
end;

procedure TTMSFMXCustomGrid.DoColumnSized(ACol: Integer;
  NewWidth: Single);
begin
  inherited;
  if Assigned(OnColumnSized) then
    OnColumnSized(Self, ACol, NewWidth);
end;

procedure TTMSFMXCustomGrid.DoColumnSorted(ACol: Integer;
  Direction: TSortDirection);
begin
  inherited;
  if Assigned(OnColumnSorted) then
    OnColumnSorted(Self, ACol, Direction);
end;

procedure TTMSFMXCustomGrid.DoComboBoxChange(Sender: TObject);
begin

end;

procedure TTMSFMXCustomGrid.DoCommentClick(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) and Assigned(ctl.Parent) then
  begin
    if (ctl.Parent is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl.Parent as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoCellCommentClick(Cell.Col, Cell.Row, (ctl.Parent as TTMSFMXGridCell));
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoCanDeleteRow(ARow: Integer; var Allow: Boolean);
begin
  inherited;
  if Assigned(OnCanDeleteRow) then
    OnCanDeleteRow(Self, Arow, Allow);
end;

procedure TTMSFMXCustomGrid.DoCanDragColumn(ACol: integer; var Allow: Boolean);
begin
  if Assigned(OnCanDragColumn) then
    OnCanDragColumn(Self, ACol, Allow);
end;

procedure TTMSFMXCustomGrid.DoCanDragRow(ARow: integer; var Allow: Boolean);
begin
  if Assigned(OnCanDragRow) then
    OnCanDragRow(Self, ARow, Allow);
end;

procedure TTMSFMXCustomGrid.DoFixedBitmapClick(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) and Assigned(ctl.Parent) then
  begin
    if (ctl.Parent is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl.Parent as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoFixedCellBitmapClick(Cell.Col, Cell.Row, (ctl.Parent as TTMSFMXGridCell));
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoFixedButtonClick(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) and Assigned(ctl.Parent) then
  begin
    if (ctl.Parent is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl.Parent as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoFixedCellButtonClick(Cell.Col, Cell.Row, (ctl.Parent as TTMSFMXGridCell));
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoFixedCellBitmapClick(ACol, ARow: Integer;
  Cell: TFMXObject);
begin
  inherited;
  if Assigned(OnFixedCellBitmapClick) then
    OnFixedCellBitmapClick(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoFixedCellButtonClick(ACol, ARow: Integer;
  Cell: TFMXObject);
begin
  inherited;
  if Assigned(OnFixedCellButtonClick) then
    OnFixedCellButtonClick(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoFixedCellCheckBoxClick(ACol, ARow: Integer;
  Cell: TFMXObject);
begin
  inherited;
  if Assigned(OnFixedCellCheckBoxClick) then
    OnFixedCellCheckBoxClick(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoFixedCellClick(Shift: TShiftState; ACol, ARow: Integer);
var
  cell: TFmxObject;
  cl: TCell;
begin
  inherited;
  cl.Col := ACol;
  cl.Row := ARow;
  cell := GetCellObject(GetDisplayCell(cl));
  if Assigned(cell) then
  begin
    if cell is TTMSFMXFixedGridCell then
      DoCellSortClick(Shift, ACol, ARow, cell, sdAscending);
  end;

  if Assigned(OnFixedCellClick) then
    OnFixedCellClick(Self, ACol, ARow);
end;

procedure TTMSFMXCustomGrid.DoFixedCellDropDownButtonClick(ACol, ARow: Integer;
  Cell: TFMXObject);
begin
  inherited;

  if (Cell is TTMSFMXFixedGridCell) then
  begin
    if (Cell as TTMSFMXFixedGridCell).IsPopupOpen then
      (Cell as TTMSFMXFixedGridCell).HidePopup
    else
      (Cell as TTMSFMXFixedGridCell).ShowPopup;
  end;

  if Assigned(OnFixedCellDropDownButtonClick) then
    OnFixedCellDropDownButtonClick(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoFixedCellSpinBoxChange(ACol, ARow: Integer;
  Cell: TFMXObject);
begin
  inherited;
  if Assigned(OnFixedCellSpinBoxChange) then
    OnFixedCellSpinBoxChange(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoFixedCheckBoxClick(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) and Assigned(ctl.Parent) then
  begin
    if (ctl.Parent is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl.Parent as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoFixedCellCheckBoxClick(Cell.Col, Cell.Row, (ctl.Parent as TTMSFMXGridCell));
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoFixedDropDownButtonClick(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) and Assigned(ctl.Parent) then
  begin
    if (ctl.Parent is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl.Parent as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoFixedCellDropDownButtonClick(Cell.Col, Cell.Row, (ctl.Parent as TTMSFMXGridCell));
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoFixedSpinBoxChange(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) and Assigned(ctl.Parent) then
  begin
    if (ctl.Parent is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl.Parent as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoFixedCellSpinBoxChange(Cell.Col, Cell.Row, (ctl.Parent as TTMSFMXGridCell));
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoAfterDrawCell(Sender: TObject; ACanvas: TCanvas;
  ARect: TRectF);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) then
  begin
    if (ctl is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoAfterDrawGridCell(Sender, Cell.Col, Cell.Row, ACanvas, ARect);
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoAfterDrawGridCell(Sender: TObject; ACol, ARow: Integer;
  ACanvas: TCanvas; ARect: TRectF);
begin
  if Assigned(OnCellAfterDraw) then
    OnCellAfterDraw(Sender, ACol, ARow, ACanvas, ARect);
end;

procedure TTMSFMXCustomGrid.DoAppendColumn(ACol: Integer);
begin
  inherited;
  if Assigned(OnAppendColumn) then
    OnAppendColumn(Self, ACol);
end;

procedure TTMSFMXCustomGrid.DoAppendRow(ARow: Integer);
begin
  inherited;
  if Assigned(OnAppendRow) then
    OnAppendRow(Self, ARow);
end;

procedure TTMSFMXCustomGrid.DoDeleteRow(ARow: Integer);
begin
  inherited;
  if Assigned(OnDeleteRow) then
    OnDeleteRow(Self, ARow);
end;

procedure TTMSFMXCustomGrid.DoExit;
begin
  inherited;
  FMouseDown := False;
end;

procedure TTMSFMXCustomGrid.DoInsertRow(ARow: Integer);
begin
  inherited;
  if Assigned(OnInsertRow) then
    OnInsertRow(Self, ARow);
end;

procedure TTMSFMXCustomGrid.DoCanSizeColumn(ACol: Integer;
  var Allow: Boolean);
begin
  inherited;
  if Assigned(OnCanSizeColumn) then
    OnCanSizeColumn(Self, ACol, Allow);
end;

procedure TTMSFMXCustomGrid.DoCanSizeRow(ARow: Integer;
  var Allow: Boolean);
begin
  inherited;
  if Assigned(OnCanSizeRow) then
    OnCanSizeRow(Self, ARow, Allow);
end;

procedure TTMSFMXCustomGrid.DoCanSortColumn(ACol: Integer;
  var Allow: Boolean);
begin
  inherited;
  if Assigned(OnCanSortColumn) then
    OnCanSortColumn(Self, ACol, Allow);
end;

procedure TTMSFMXCustomGrid.DoCellAnchorClick(ACol, ARow: Integer;
  AAnchor: String);
begin
  inherited;
  if Options.URL.Open then
  begin
    if pos('://', AAnchor) > 0 then
      XOpenURL(AAnchor)
    else
      {$IFDEF DELPHI_LLVM}
      XOpenFile(AAnchor);
      {$ELSE}
      XOpenFile('open', AAnchor, '', '');
      {$ENDIF}
  end;

  if Assigned(OnCellAnchorClick) then
    OnCellAnchorClick(Self, ACol, ARow, AAnchor);
end;

procedure TTMSFMXCustomGrid.DoCellBitmapClick(ACol, ARow: Integer;
  Cell: TFMXObject);
begin
  inherited;
  if Assigned(OnCellBitmapClick) then
    OnCellBitmapClick(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoCellButtonClick(ACol, ARow: Integer;
  Cell: TFMXObject);
begin
  inherited;
  if Assigned(OnCellButtonClick) then
    OnCellButtonClick(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoCellCheckBoxClick(ACol, ARow: Integer;
  Cell: TFMXObject);
begin
  inherited;
  if Assigned(OnCellCheckBoxClick) then
    OnCellCheckBoxClick(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoCellClick(ACol, ARow: Integer);
begin
  inherited;
  if Assigned(OnCellClick) then
    OnCellClick(Self, ACol, ARow);
end;

procedure TTMSFMXCustomGrid.DoCellEditDone(ACol, ARow: Integer; CellEditor: TFMXObject);
begin
  inherited;
  if Assigned(OnCellEditDone) then
    OnCellEditDone(Self, ACol, ARow, CellEditor);
end;

procedure TTMSFMXCustomGrid.DoCellEditCancel(ACol, ARow: Integer);
begin
  inherited;
  if Assigned(OnCellEditCancel) then
    OnCellEditCancel(Self, ACol, ARow);
end;

procedure TTMSFMXCustomGrid.DoCellEditSetColor(ACol, ARow: Integer;
  CellEditor: TFMXObject; var CellColor: TAlphaColor);
begin
  inherited;

end;

procedure TTMSFMXCustomGrid.DoCellEditSetData(ACol, ARow: Integer; CellEditor: TFMXObject; var CellString: String);
begin
  if Assigned(OnCellEditSetData) then
    OnCellEditSetData(Self, ACol, ARow, CellEditor, CellString);

  inherited;
end;

procedure TTMSFMXCustomGrid.DoCellEditValidateColor(ACol, ARow: Integer;
  CellEditor: TFMXObject; var CellColor: TAlphaColor; var Allow: Boolean);
begin
  inherited;
  if Assigned(OnCellEditValidateColor) then
    OnCellEditValidateColor(Self, ACol, ARow, CellEditor, CellColor, Allow);
end;

procedure TTMSFMXCustomGrid.DoCellEditValidateData(ACol, ARow: Integer;
  CellEditor: TFMXObject; var CellString: String; var Allow: Boolean);
begin
  inherited;
  if Assigned(OnCellEditValidateData) then
    OnCellEditValidateData(Self, ACol, ARow, CellEditor, CellString, Allow);
end;

procedure TTMSFMXCustomGrid.DoCellHidePopup(ACol, ARow: Integer; Cell: TFMXObject);
begin
  inherited;
  if Assigned(OnCellHidePopup) then
    OnCellHidePopup(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoGetCellIsFixed(ACol, ARow: Integer; var ACellFixed: Boolean);
begin
  inherited;
  if Assigned(OnGetCellIsFixed) then
    OnGetCellIsFixed(Self, ACol, ARow, ACellFixed);
end;

procedure TTMSFMXCustomGrid.DoCellKeyDown(ACol, ARow: Integer; var Key: Word;
  var KeyChar: WideChar; Shift: TShiftState);
var
  cell: TFMXObject;
  cl: TCell;
begin
  cl.Col := ACol;
  cl.Row := ARow;
  cell := GetCellObject(GetDisplayCell(cl));
  if Assigned(cell) then
  begin
    case Key of
      vkSpace: FCellKeyDown := True;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoCellKeyUp(ACol, ARow: Integer; var Key: Word;
  var KeyChar: WideChar; Shift: TShiftState);
var
  cell: TFMXObject;
  cl: TCell;
  btn: TProtectedButton;
begin
  cl.Col := ACol;
  cl.Row := ARow;
  cell := GetCellObject(GetDisplayCell(cl));
  if Assigned(cell) then
  begin
    case Key of
      vkSpace:
      begin
        if FCellKeyDown then
        begin
          if (Cell is TTMSFMXCheckGridCell) then
            (cell as TTMSFMXCheckGridCell).CheckBox.IsCheckedProg := not (cell as TTMSFMXCheckGridCell).CheckBox.IsCheckedProg
          else if (Cell is TTMSFMXRadioGridCell) then
            (cell as TTMSFMXRadioGridCell).RadioButton.IsCheckedProg := not (cell as TTMSFMXRadioGridCell).RadioButton.IsCheckedProg
          else if (Cell is TTMSFMXButtonGridCell) then
          begin
            btn := TProtectedButton((cell as TTMSFMXButtonGridCell).Button);
            btn.Click;
          end
          else if (Cell is TTMSFMXFixedGridCell) then
          begin
            (cell as TTMSFMXFixedGridCell).CheckBox.IsCheckedProg  := not (cell as TTMSFMXFixedGridCell).CheckBox.IsCheckedProg;
            (cell as TTMSFMXFixedGridcell).Button.IsPressed := not (cell as TTMSFMXFixedGridcell).Button.IsPressed;
          end;
        end;
        FCellKeyDown := False;
      end;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoCellNodeClick(ACol, ARow: Integer;
  Cell: TFMXObject);
begin
  inherited;

  if not (Cell is TTMSFMXNodeGridCell) then
  begin
    {$IFDEF WIN32}
    outputdebugstring(pchar('CLASS IS NOT A NODE!!!!! ' + cell.ClassName));
    {$ENDIF}
    Exit;
  end;

  case (Cell as TTMSFMXNodeGridCell).State of
    nsClosed: (Cell as TTMSFMXNodeGridCell).State := nsOpen;
    nsOpen: (Cell as TTMSFMXNodeGridCell).State := nsClosed;
  end;
  if Assigned(OnCellNodeClick) then
    OnCellNodeClick(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoCellRadioButtonClick(ACol,
  ARow: Integer; Cell: TFMXObject);
begin
  inherited;
  if Assigned(OnCellRadioButtonClick) then
    OnCellRadioButtonClick(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoRadioButtonClick(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) and Assigned(ctl.Parent) then
  begin
    if (ctl.Parent is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl.Parent as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoCellRadioButtonClick(Cell.Col, Cell.Row, (ctl.Parent as TTMSFMXGridCell));
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoRowDragged(FromRow, ToRow: integer);
begin
  if Assigned(OnRowDragged) then
    OnRowDragged(Self, FromRow, ToRow);
end;

procedure TTMSFMXCustomGrid.DoRowSize(ARow: Integer;
  var NewHeight: Single);
begin
  inherited;
  if Assigned(OnRowSize) then
    OnRowSize(Self, ARow, NewHeight);
end;

procedure TTMSFMXCustomGrid.DoRowSized(ARow: Integer;
  NewHeight: Single);
begin
  inherited;
  if Assigned(OnRowSized) then
    OnRowSized(Self, ARow, NewHeight);
end;

procedure TTMSFMXCustomGrid.DoShowPopup(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) then
  begin
    if (ctl is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoCellShowPopup(Cell.Col, Cell.Row, (ctl as TTMSFMXGridCell));
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DoUpdateGridCellAppearance(ASelection: TCellRange);
var
  c, r, ci, ri: Integer;
  cell: TFmxObject;
  bc, br, cs, rs: Integer;
  fc, fr: Integer;
  incfr, incfc: Boolean;
  cStart, cStop, rStart, rStop: Integer;
  clStart, clStop: TCell;
  l: TTMSFMXGridCellLayout;
begin
  l := TTMSFMXGridCellLayout.Create(nil);
  fc := 0;
  FSaveTopCell := nil;
  incfc := False;

  cStart := ASelection.StartCol;
  cStop := ASelection.EndCol;
  rStart := ASelection.StartRow;
  rStop := ASelection.EndRow;

  clStart.Col := cStart;
  clStop.Col := cStop;
  clStart.Row := rStart;
  clStop.Row := rStop;

  clStart := GetDisplayCell(clStart);
  clStop := GetDisplayCell(clStop);

  for c := clStart.Col to clStop.Col do
  begin
    if (c >= 0) and (c <= Length(FCellArray) - 1) then
    begin
      if incfc then
        Inc(fc);

      fr := 0;
      incfr := false;
      for r := clStart.Row to clStop.Row do
      begin
        if (r >= 0) and (r <= Length(FCellArray[c]) - 1) then
        begin
          if incfr then
            inc(fr);

          cell := FCellArray[c, r];
          if Assigned(cell) then
          begin
            if cell is TControl then
              (cell as TControl).BeginUpdate;

            if cell is TTMSFMXGridCell then
            begin
              bc := c + FCStart - GetFixedCols;
              if bc > FCStop then
              begin
                bc := ColumnCount - FixedRightColumns + fc;
                incfc := True;
              end
              else if bc < FCStart then
                bc := c;

              br := r + FRStart - GetFixedRows;
              if br > FRStop then
              begin
                br := RowCount - FixedFooterRows + fr;
                incfr := True;
              end
              else if br < FRStart then
                br := r;

              cs := 0;
              rs := 0;
              DoGetCellMergeInfo(bc, br, bc, br, cs, rs);

              ci := bc;
              ri := br;

              if (ci = FocusedCell.Col) and (ri = FocusedCell.Row) then
                FSaveTopCell := cell;

              ApplyCellState(ci, ri, cell);

              if (Cell is TTMSFMXGridCell) then
              begin
                l.Assign((Cell as TTMSFMXGridCell).Layout);
                DoGetCellLayout(ci, ri, l, GetCellState(ci, ri));
                (Cell as TTMSFMXGridCell).AssignLayout(l);
              end;

              DoGetCellAppearance(ci, ri, cell, GetCellState(ci, ri));
              DoGetCellProperties(ci, ri, cell);
            end;

            if cell is TControl then
              (cell as TControl).EndUpdate;
          end;
        end;
      end;
    end;
  end;
  l.Free;
end;

procedure TTMSFMXCustomGrid.DoGetCellAppearance(ACol, ARow: Integer;
  Cell: TFMXObject; ACellState: TCellState);
begin
  inherited;
  if Assigned(OnGetCellAppearance) then
    OnGetCellAppearance(Self, ACol, ARow, Cell, ACellState);
end;

procedure TTMSFMXCustomGrid.DoGetCellProperties(ACol, ARow: Integer;
  Cell: TFMXObject);
begin
  inherited;
  if Assigned(OnGetCellProperties) then
    OnGetCellProperties(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoGetCellReadOnly(ACol, ARow: Integer;
  var AReadOnly: Boolean);
begin
  inherited;
  if Assigned(OnGetCellReadOnly) then
    OnGetCellReadOnly(Self, ACol, ARow, AReadOnly);
end;

procedure TTMSFMXCustomGrid.DoGetRowIsBand(ARow: Integer; var ARowBand: Boolean);
begin
  inherited;
  if Assigned(OnGetRowIsBand) then
    OnGetRowIsBand(Self, ARow, ARowBand);
end;

procedure TTMSFMXCustomGrid.DoHidePopup(Sender: TObject);
begin

end;

function TTMSFMXCustomGrid.DoIsFixedCellSelected(ACol, ARow: Integer): Boolean;
begin
  Result := ((ACol >= StartCell.Col) and (ACol <= StopCell.Col) and (SelectionMode <> smSingleRow) and (SelectionMode <> smDisjunctRow) and (SelectionMode <> smDisjunctCell)
    and (SelectionMode <> smRowRange)) or ((ARow >= StartCell.Row) and (ARow <= StopCell.Row)
      and (SelectionMode <> smSingleColumn) and (SelectionMode <> smDisjunctColumn) and (SelectionMode <> smDisjunctCell) and (SelectionMode <> smColumnRange));

  if SelectionMode = smDisjunctRow then
    Result := RowSelect[ARow]
  else if SelectionMode = smDisjunctColumn then
    Result := ColumnSelect[ACol]
  else if SelectionMode = smDisjunctCell then
    Result := FixedCellSelect[ACol, ARow];
end;

procedure TTMSFMXCustomGrid.DoCanInsertRow(ARow: Integer; var Allow: Boolean);
begin
  inherited;
  if Assigned(OnCanInsertRow) then
    OnCanInsertRow(Self, ARow, Allow);
end;

function TTMSFMXCustomGrid.DoIsCellSelected(ACol, ARow: Integer): boolean;
begin
  if (SelectionMode in [smDisjunctRow, smDisjunctColumn, smDisjunctCell]) then
    Result := inherited DoIsCellSelected(ACol,ARow)
  else
    Result := ((ACol >= StartCell.Col) and (ARow >= StartCell.Row) and
      (ACol <= StopCell.Col) and (ARow <= StopCell.Row));
end;

procedure TTMSFMXCustomGrid.DoNodeClick(Sender: TObject);
var
  ctl: TControl;
  cell: TCell;
begin
  ctl := Sender as TControl;
  if Assigned(ctl) and Assigned(ctl.Parent) then
  begin
    if (ctl.Parent is TTMSFMXGridCell) then
    begin
      cell := GetCellByObject((ctl.Parent as TTMSFMXGridCell));
      if (Cell.Row <> -1) and (Cell.Col <> -1) then
        DoCellNodeClick(Cell.Col, Cell.Row, (ctl.Parent as TTMSFMXGridCell));
    end;
  end;
end;

{$IFNDEF ANDROID}
procedure TTMSFMXCustomGrid.DoPrintAfterDraw(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF);
begin
  if Assigned(OnPrintAfterDraw) then
    OnPrintAfterDraw(Self, APageIndex, ACanvas, ARect);
end;

procedure TTMSFMXCustomGrid.DoPrintAfterDrawCell(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; ACol, ARow: Integer; Cell: TFMXObject);
begin
  if Assigned(OnPrintAfterDrawCell) then
    OnPrintAfterDrawCell(Self, APageIndex, ACanvas, ARect, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoPrintBeforeDraw(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; var Allow: Boolean);
begin
  if Assigned(OnPrintBeforeDraw) then
    OnPrintBeforeDraw(Self, APageIndex, ACanvas, ARect,Allow);
end;

procedure TTMSFMXCustomGrid.DoPrintBeforeDrawCell(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; ACol, ARow: Integer; Cell: TFMXObject);
begin
  if Assigned(OnPrintBeforeDrawCell) then
    OnPrintBeforeDrawCell(Self, APageIndex, ACanvas, ARect, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoPrintBegin(APrinter: TPrinter);
begin
  if Assigned(OnPrintBegin) then
    OnPrintBegin(Self, APrinter);
end;

procedure TTMSFMXCustomGrid.DoPrintDrawCell(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF; ACol, ARow: Integer; Cell: TFMXObject;
  var Allow: Boolean);
begin
  if Assigned(OnPrintDrawCell) then
    OnPrintDrawCell(Self, APageIndex, ACanvas, ARect, ACol, ARow, Cell, Allow);
end;

procedure TTMSFMXCustomGrid.DoPrintDrawDescription(APageIndex: Integer;
  ACanvas: TCanvas; var ARect: TRectF; var ADescription: String;
  var Allow: Boolean);
begin
  if Assigned(OnPrintDrawDescription) then
    OnPrintDrawDescription(Self, APageIndex, ACanvas, Arect, ADescription, Allow);
end;

procedure TTMSFMXCustomGrid.DoPrintDrawPageNumber(APageIndex: Integer;
  ACanvas: TCanvas; var ARect: TRectF; var APageNumberText: String;
  var Allow: Boolean);
begin
  if Assigned(OnPrintDrawPageNumber) then
    OnPrintDrawPageNumber(Self, APageIndex, ACanvas, Arect, APageNumberText, Allow);
end;

procedure TTMSFMXCustomGrid.DoPrintDrawTitle(APageIndex: Integer; ACanvas: TCanvas;
  var ARect: TRectF; var ATitle: String; var Allow: Boolean);
begin
  if Assigned(OnPrintDrawTitle) then
    OnPrintDrawTitle(Self, APageIndex, ACanvas, Arect, ATitle, Allow);
end;

procedure TTMSFMXCustomGrid.DoPrintEnd(APrinter: TPrinter);
begin
  if Assigned(OnPrintEnd) then
    OnPrintEnd(Self, APrinter);
end;

procedure TTMSFMXCustomGrid.DoPrintEndPage(APageIndex: Integer; APrinter: TPrinter);
begin
  if Assigned(OnPrintEndPage) then
    OnPrintEndPage(Self, APageIndex, APrinter);
end;

procedure TTMSFMXCustomGrid.DoPrintNewPage(APageIndex: Integer; APrinter: TPrinter);
begin
  if Assigned(OnPrintNewPage) then
    OnPrintNewPage(Self, APageIndex, APrinter);
end;

procedure TTMSFMXCustomGrid.DoPrintProgress(APageIndex: Integer; APrinter: TPrinter; APrintProgress: Single);
begin
  if Assigned(OnPrintProgress) then
    OnPrintProgress(Self, APageIndex, APrinter, APrintProgress);

  if Assigned(OnPrintPreviewProgress) then
    OnPrintPreviewProgress(Self, APageIndex, APrinter, APrintProgress);
end;
{$ENDIF}

procedure TTMSFMXCustomGrid.DoCellShowPopup(ACol, ARow: Integer; Cell: TFMXObject);
begin
  inherited;
  if Assigned(OnCellShowPopup) then
    OnCellShowPopup(Self, ACol, ARow, Cell);
end;

procedure TTMSFMXCustomGrid.DoCellSortClick(Shift: TShiftState; ACol, ARow: Integer;
  Cell: TFMXObject; Direction: TSortDirection);
var
  AllowSort: Boolean;
  sortc: TTMSFMXFixedGridCell;
begin
  if Options.Sorting.Mode <> gsmNone then
  begin
    AllowSort := true;
    DoCanSortColumn(ACol, AllowSort);
    if AllowSort then
    begin
      sortc := (Cell as TTMSFMXFixedGridCell);
      if Assigned(sortc) then
      begin
        if (Options.Sorting.Mode = gsmIndexed) then
        begin
          if not (ssShift in Shift) then
          begin
            SortIndexes.Clear;
            ClearFixedSorting(sortc);
          end;
        end
        else
          ClearFixedSorting(sortc);

        case sortc.SortKind of
          skAscending:
          begin
            Direction := sdDescending;

            if Options.Sorting.Mode = gsmIndexed then
            begin
              if SortIndexes.FindIndex(ACol) = -1 then
                SortIndexes.AddIndex(ACol, sdDescending)
              else
                SortIndexes.ToggleIndex(ACol);

              sortc.SortIndex := SortIndexes.FindIndex(ACol) + 1;
              sortc.SortFill.Assign(GetDefaultIndexedSortFill);
              sortc.SortFont.Assign(GetDefaultIndexedSortFont);
              sortc.SortFontFill.Color := GetDefaultIndexedSortFontFill.Color;
              sortc.SortStroke.Assign(GetDefaultIndexedSortStroke);
            end
            else
            begin
              sortc.SortIndex := -1;
              sortc.SortFill.Assign(GetDefaultSortFill);
              sortc.SortFont.Assign(GetDefaultSortFont);
              sortc.SortFontFill.Color := GetDefaultSortFontFill.Color;
              sortc.SortStroke.Assign(GetDefaultSortStroke);
            end;

            sortc.SortKind := skDescending;

          end;
          skNone, skDescending:
          begin
            Direction := sdAscending;
            if Options.Sorting.Mode = gsmIndexed then
            begin
              if SortIndexes.FindIndex(ACol) = -1 then
                SortIndexes.AddIndex(ACol, sdAscending)
              else
                SortIndexes.ToggleIndex(ACol);

              sortc.SortIndex := SortIndexes.FindIndex(ACol) + 1;
              sortc.SortFill.Assign(GetDefaultIndexedSortFill);
              sortc.SortFont.Assign(GetDefaultIndexedSortFont);
              sortc.SortFontFill.Color := GetDefaultIndexedSortFontFill.Color;
              sortc.SortStroke.Assign(GetDefaultIndexedSortStroke);
            end
            else
            begin
              sortc.SortIndex := -1;
              sortc.SortFill.Assign(GetDefaultSortFill);
              sortc.SortFont.Assign(GetDefaultSortFont);
              sortc.SortFontFill.Color := GetDefaultSortFontFill.Color;
              sortc.SortStroke.Assign(GetDefaultSortStroke);
            end;

            sortc.SortKind := skAscending;
          end;
        end;

        inherited;

        if Assigned(OnCellSortClick) then
          OnCellSortClick(Self, ACol, ARow, Cell);

        DoColumnSorted(ACol, Direction);
      end;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.DragTimer(Sender: TObject);
var
  cellWrapper: TControl;
  prev: TCell;
begin
  if not Options.Mouse.AutoDragging then
  begin
    FDragTimer.Enabled := False;
    Exit;
  end;

  cellWrapper := GetCellWrapper;
  if (FX >= cellWrapper.Width) then
  begin
    prev := FDragScrollCell;
    FDragScrollCell.Col := FDragScrollCell.Col + Round(FX - cellwrapper.Width) * Options.Mouse.AutoScrollingSpeed;
    Navigate(FDragScrollCell);
  end
  else if FX <= 0 then
  begin
    prev := FDragScrollCell;
    FDragScrollCell.Col := FDragScrollCell.Col + Round(FX) * Options.Mouse.AutoScrollingSpeed;
    Navigate(FDragScrollCell);
  end;

  if (FY >= cellWrapper.Height) then
  begin
    prev := FDragScrollCell;
    FDragScrollCell.Row := FDragScrollCell.Row + Round(FY - cellwrapper.Height) * Options.Mouse.AutoScrollingSpeed;
    Navigate(FDragScrollCell);
  end
  else if FY <= 0 then
  begin
    prev := FDragScrollCell;
    FDragScrollCell.Row := FDragScrollCell.Row + Round(FY) * Options.Mouse.AutoScrollingSpeed;
    Navigate(FDragScrollCell);
  end;
end;


procedure TTMSFMXCustomGrid.DrawDescription(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF);
var
  r: TRectF;
  vAlign, tAlign: TTextAlign;
  allowtxt: Boolean;
  str: string;
begin
  if Options.Printing.Description <> '' then
  begin
    ACanvas.Fill.Color := claBlack;
    r := ARect;

    {$IFDEF DELPHIXE6_LVL}
    valign := TTextAlign.Leading;
    tAlign := TTextAlign.Leading;
    case Options.Printing.DescriptionPosition of
      ppTopLeft: tAlign := TTextAlign.Leading;
      ppTopCenter: tAlign := TTextAlign.Center;
      ppTopRight: tAlign := TTextAlign.Trailing;
      ppBottomLeft: vAlign := TTextAlign.Trailing;
      ppBottomCenter:
      begin
        vAlign := TTextAlign.Trailing;
        tAlign := TTextAlign.Center;
      end;
      ppBottomRight:
      begin
        vAlign := TTextAlign.Trailing;
        tAlign := TTextAlign.Trailing;
      end;
    end;
    {$ELSE}
    valign := TTextAlign.taLeading;
    tAlign := TTextAlign.taLeading;
    case Options.Printing.DescriptionPosition of
      ppTopLeft: tAlign := TTextAlign.taLeading;
      ppTopCenter: tAlign := TTextAlign.taCenter;
      ppTopRight: tAlign := TTextAlign.taTrailing;
      ppBottomLeft: vAlign := TTextAlign.taTrailing;
      ppBottomCenter:
      begin
        vAlign := TTextAlign.taTrailing;
        tAlign := TTextAlign.taCenter;
      end;
      ppBottomRight:
      begin
        vAlign := TTextAlign.taTrailing;
        tAlign := TTextAlign.taTrailing;
      end;
    end;
    {$ENDIF}

    allowtxt := True;
    str := Options.Printing.Description;
    {$IFNDEF ANDROID}
    DoPrintDrawDescription(APageIndex, ACanvas, r, str, allowtxt);
    {$ENDIF}
    if Allowtxt then
      ACanvas.FillText(r, str, False, 1, [], tAlign, vAlign);
  end;
end;

procedure TTMSFMXCustomGrid.DrawPageNumber(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF);
var
  r: TRectF;
  vAlign, tAlign: TTextAlign;
  allowtxt: Boolean;
  str: string;
  d: Double;
begin
  ACanvas.Fill.Color := claBlack;
  r := ARect;

  {$IFDEF DELPHIXE6_LVL}
  valign := TTextAlign.Leading;
  tAlign := TTextAlign.Leading;
  case Options.Printing.PageNumberPosition of
    ppTopLeft: tAlign := TTextAlign.Leading;
    ppTopCenter: tAlign := TTextAlign.Center;
    ppTopRight: tAlign := TTextAlign.Trailing;
    ppBottomLeft: vAlign := TTextAlign.Trailing;
    ppBottomCenter:
    begin
      vAlign := TTextAlign.Trailing;
      tAlign := TTextAlign.Center;
    end;
    ppBottomRight:
    begin
      vAlign := TTextAlign.Trailing;
      tAlign := TTextAlign.Trailing;
    end;
  end;
  {$ELSE}
  valign := TTextAlign.taLeading;
  tAlign := TTextAlign.taLeading;
  case Options.Printing.PageNumberPosition of
    ppTopLeft: tAlign := TTextAlign.taLeading;
    ppTopCenter: tAlign := TTextAlign.taCenter;
    ppTopRight: tAlign := TTextAlign.taTrailing;
    ppBottomLeft: vAlign := TTextAlign.taTrailing;
    ppBottomCenter:
    begin
      vAlign := TTextAlign.taTrailing;
      tAlign := TTextAlign.taCenter;
    end;
    ppBottomRight:
    begin
      vAlign := TTextAlign.taTrailing;
      tAlign := TTextAlign.taTrailing;
    end;
  end;
  {$ENDIF}

  allowtxt := True;
  d := APageIndex;
  str := Format(Options.Printing.PageNumberFormat, [d]);
  {$IFNDEF ANDROID}
  DoPrintDrawPageNumber(APageIndex, ACanvas, r, str, allowtxt);
  {$ENDIF}
  if Allowtxt then
    ACanvas.FillText(r, str, False, 1, [], tAlign, vAlign);
end;

procedure TTMSFMXCustomGrid.DrawTitle(APageIndex: Integer; ACanvas: TCanvas; ARect: TRectF);
var
  r: TRectF;
  vAlign, tAlign: TTextAlign;
  allowtxt: Boolean;
  str: string;
begin
  if Options.Printing.Title <> '' then
  begin
    ACanvas.Fill.Color := claBlack;
    r := ARect;

    {$IFDEF DELPHIXE6_LVL}
    valign := TTextAlign.Leading;
    tAlign := TTextAlign.Leading;
    case Options.Printing.TitlePosition of
      ppTopLeft: tAlign := TTextAlign.Leading;
      ppTopCenter: tAlign := TTextAlign.Center;
      ppTopRight: tAlign := TTextAlign.Trailing;
      ppBottomLeft: vAlign := TTextAlign.Trailing;
      ppBottomCenter:
      begin
        vAlign := TTextAlign.Trailing;
        tAlign := TTextAlign.Center;
      end;
      ppBottomRight:
      begin
        vAlign := TTextAlign.Trailing;
        tAlign := TTextAlign.Trailing;
      end;
    end;
    {$ELSE}
    valign := TTextAlign.taLeading;
    tAlign := TTextAlign.taLeading;
    case Options.Printing.TitlePosition of
      ppTopLeft: tAlign := TTextAlign.taLeading;
      ppTopCenter: tAlign := TTextAlign.taCenter;
      ppTopRight: tAlign := TTextAlign.taTrailing;
      ppBottomLeft: vAlign := TTextAlign.taTrailing;
      ppBottomCenter:
      begin
        vAlign := TTextAlign.taTrailing;
        tAlign := TTextAlign.taCenter;
      end;
      ppBottomRight:
      begin
        vAlign := TTextAlign.taTrailing;
        tAlign := TTextAlign.taTrailing;
      end;
    end;
    {$ENDIF}

    allowtxt := True;
    str := Options.Printing.Title;
    {$IFNDEF ANDROID}
    DoPrintDrawTitle(APageIndex, ACanvas, r, str, allowtxt);
    {$ENDIF}
    if Allowtxt then
      ACanvas.FillText(r, str, False, 1, [], tAlign, vAlign);
  end;
end;

procedure TTMSFMXCustomGrid.EditCell(ACell: TCell; CellStart: TEditCellStart = csNone; InsertKey: Boolean = False; KeyChar: WideChar = #0; Key: Word = 0);
var
  CellEditorClassType: TFmxObjectClass;
  cellstring: String;
  ctl: TFMXObject;
  realc: TCell;
  cellContainer: TControl;
  r: TRectF;
  AReadOnly: Boolean;
  EditorType: TTMSFMXGridEditorType;
  c: TAlphaColor;
  dtc: Boolean;
begin
  if not Options.Editing.Enabled then
    Exit;

  cellContainer := GetCellContainer;
  if Assigned(cellContainer) then
  begin
    AReadOnly := False;
    DoGetCellReadOnly(ACell.Col, ACell.Row, AReadOnly);

    //check for Data checkbox and exit
    dtc := DoGetCellIsDataCheckBox(ACell.Col, ACell.Row);

    if AReadOnly or dtc then
      Exit;

    realc := GetDisplayCell(ACell);

    ctl := nil;
    if (realc.Col >= 0) and (realc.Col <= Length(FCellArray) - 1) then
    begin
      if (realc.Row >= 0) and (realc.Row <= Length(FCellArray[realc.Col]) - 1) then
        ctl := FCellArray[realc.Col, realc.Row];
    end;

    if Assigned(ctl) and (ctl is TTMSFMXGridCell) then
    begin
      EditorType := etEdit;
      FEditControl := CellEdit;
      CellEditorClassType := TTMSFMXEdit;
      DoGetCellEditorType(ACell.Col, ACell.Row, EditorType);

      if EditorType = etCustom then
      begin
        DoGetCellEditorCustomClassType(ACell.Col, ACell.Row, CellEditorClassType);
        if CellEditorClassType <> nil then
        begin
          FEditControl := CellEditorClassType.Create(Self);
          if FEditControl is TControl then
          begin
            (FEditControl as TControl).DisableFocusEffect := True;
            (FEditControl as TControl).OnKeyDown := CellControlKeyDown;
          end;
        end;
      end
      else
      begin
        case EditorType of
          etEditBtn, etNumericEditBtn, etSignedNumericEditBtn, etFloatEditBtn, etSignedFloatEditBtn, etUppercaseEditBtn, etMixedCaseEditBtn,
          etLowerCaseEditBtn, etMoneyEditBtn, etHexEditBtn, etAlphaNumericEditBtn, etValidCharsEditBtn: FEditControl := CellEditBtn;
          etComboBox: FEditControl := CellComboBox;
          etComboEdit: FEditControl := CellComboEdit;
          etSpinBox: FEditControl := CellSpinBox;
          etDatePicker: FEditControl := CellDatePicker;
          etDateEdit: FEditControl := CellDateEdit;
          etColorPicker: FEditControl := CellColorPicker;
          etColorComboBox: FEditControl := CellColorComboBox;
          etTrackBar: FEditControl := CellTrackBar;
          etArcDial: FEditControl := CellArcDial;
        end;

        case EditorType of
          etEdit: CellEdit.EditType := etString;
          etNumericEdit: CellEdit.EditType := etNumeric;
          etSignedNumericEdit: CellEdit.EditType := etSignedNumeric;
          etFloatEdit: CellEdit.EditType := etFloat;
          etSignedFloatEdit: CellEdit.EditType := etSignedFloat;
          etUppercaseEdit: CellEdit.EditType := etUppercase;
          etMixedCaseEdit: CellEdit.EditType := etMixedCase;
          etLowerCaseEdit: CellEdit.EditType := etLowerCase;
          etMoneyEdit: CellEdit.EditType := etMoney;
          etHexEdit: CellEdit.EditType := etHex;
          etAlphaNumericEdit: CellEdit.EditType := etAlphaNumeric;
          etValidCharsEdit: CellEdit.EditType := etValidChars;
          etEditBtn: CellEditBtn.EditType := etString;
          etNumericEditBtn: CellEditBtn.EditType := etNumeric;
          etSignedNumericEditBtn: CellEditBtn.EditType := etSignedNumeric;
          etFloatEditBtn: CellEditBtn.EditType := etFloat;
          etSignedFloatEditBtn: CellEditBtn.EditType := etSignedFloat;
          etUppercaseEditBtn: CellEditBtn.EditType := etUppercase;
          etMixedCaseEditBtn: CellEditBtn.EditType := etMixedCase;
          etLowerCaseEditBtn: CellEditBtn.EditType := etLowerCase;
          etMoneyEditBtn: CellEditBtn.EditType := etMoney;
          etHexEditBtn: CellEditBtn.EditType := etHex;
          etAlphaNumericEditBtn: CellEditBtn.EditType := etAlphaNumeric;
          etValidCharsEditBtn: CellEditBtn.EditType := etValidChars;
        end;

        if (EditorType in [etEdit, etEditBtn]) and (Options.Editing.AutoComplete <> acDisabled) then
        begin
          CellEdit.Lookup.DisplayList.Assign(Options.Editing.AutoCompleteItems);
          CellEdit.AutoComplete := Options.Editing.AutoComplete = acNormal;
          CellEdit.Lookup.Enabled := Options.Editing.AutoComplete <> acNormal;
        end;
      end;

      if Assigned(FEditControl) and (FEditControl is TControl) then
      begin
        if FEditControl is TStyledControl then
        begin
          (FEditControl as TStyledControl).NeedStyleLookUp;
          (FEditControl as TStyledControl).Repaint;
        end;
        DoGetCellEditorProperties(ACell.Col, ACell.Row, FEditControl);
        cellstring := Cells[ACell.Col, ACell.Row];
        if FEditControl is TComboColorBox then
          DoCellEditGetColor(ACell.Col, ACell.Row, FEditControl, c)
        else
          DoCellEditGetData(ACell.Col, ACell.Row, FEditControl, cellstring);

        if (FEditControl is TTMSFMXEdit) or (FEditControl is TComboEdit) then
        begin
          if InsertKey then
          begin
            if (FEditControl is TTMSFMXEdit) then
              (FEditControl as TTMSFMXEdit).Text := ''
            else if (FEditControl is TComboEdit) then
              (FEditControl as TComboEdit).Text := '';

            if (FEditControl is TTMSFMXEdit) and (EditorType in [etMoneyEdit, etMoneyEditBtn]) then
              TTMSFMXEdit(FEditControl).SelectAll;

            TProtectedControl(FEditControl).KeyDown(Key, KeyChar, [])
          end
          else
          begin
            if (FEditControl is TTMSFMXEdit) then
              (FEditControl as TTMSFMXEdit).Text := cellString
            else if (FEditControl is TComboEdit) then
              (FEditControl as TComboEdit).Text := cellstring;
          end;
        end
        else if (FEditControl is TTrackBar) then
        begin
          if (CellString <> '') then
            (FEditControl as TTrackBar).Value := StrToFloat(cellString)
          else
            (FEditControl as TTrackBar).Value := 0;
        end
        else if (FEditControl is TArcDial) then
        begin
          if (CellString <> '') then
            (FEditControl as TArcDial).Value := StrToFloat(cellString)
          else
            (FEditControl as TArcDial).Value := 0;
        end
        else if (FEditControl is TCalendarEdit) then
        begin
          (FEditControl as TCalendarEdit).Text := cellString;
          if cellString = '' then
            (FEditControl as TCalendarEdit).Date := Now;
        end
        else if (FEditControl is TComboColorBox) then
          (FEditControl as TComboColorBox).Color := c
        else if (FEditControl is TCellCalendarBox) then
        begin
          (FEditControl as TCellCalendarBox).Text := cellString;
          if cellString = '' then
            (FEditControl as TCellCalendarBox).Date := Now;
        end
        else if FEditControl is TSpinBox then
          (FEditControl as TSpinBox).Text := cellString
        else if FEditControl is TComboBox then
          (FEditControl as TComboBox).ItemIndex := (FEditControl as TComboBox).Items.IndexOf(cellstring);

        FEditControl.StyleName := 'grideditcontrol';

        r := (ctl as TTMSFMXGridCell).GetTextRect;
        (FEditControl as TControl).SetBounds((ctl as TControl).Position.X + r.Left,
          (ctl as TControl).Position.Y + r.Top, r.Width, r.Height);

        FEditControl.Parent := cellContainer;
        (FEditControl as TControl).Visible := True;
        FEditControl.BringToFront;
        (FEditControl as TControl).SetFocus;

        if FEditControl is TTMSFMXEdit then
        begin
          if InsertKey then
          begin
            (FEditControl as TTMSFMXEdit).SelLength := 0;
            (FEditControl as TTMSFMXEdit).CaretPosition := Length((FEditControl as TTMSFMXEdit).Text);
          end
          else
          begin
            case CellStart of
              csStart:
              begin
                (FEditControl as TTMSFMXEdit).SelLength := 0;
                (FEditControl as TTMSFMXEdit).CaretPosition := 0;
              end;
              csEnd:
              begin
                (FEditControl as TTMSFMXEdit).SelLength := 0;
                (FEditControl as TTMSFMXEdit).CaretPosition := Length((FEditControl as TTMSFMXEdit).Text);
              end;
            end;
          end;
        end;

        if (Options.Mouse.DirectComboDrop) then
        begin
          if (FEditControl is TComboBox)  then
            (FEditControl as TComboBox).DropDown;

          if (FEditControl is TCellCalendarBox) then
            (FEditControl as TCellCalendarBox).DropDown;

          if (FEditControl is TCalendarEdit) then
            (FEditcontrol as TCalendarEdit).DropDown;

          if (FEditControl is TColorComboBox) then
            (FEditControl as TColorComboBox).DropDown;

          if (FEditControl is TComboColorBox) then
            (FEditControl as TComboColorBox).DropDown;
        end;

        FEditCell := ACell;
        FEditing := true;
      end;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.EndUpdate;
begin
  inherited;
  UpdateCount := UpdateCount - 1;
  if not BlockUpdate and (UpdateCount = 0) then
    UpdateGrid;
  if (UpdateCount = 0) then
    Repaint;
end;

function TTMSFMXCustomGrid.FindPopup(AControl: TComponent): TPopup;
var
  i: integer;
begin
  Result := nil;
  for I := 0 to AControl.ComponentCount - 1 do
  begin
    if AControl.Components[I] is TPopup then
    begin
      Result := AControl.Components[I] as TPopup;
      Break;
    end
    else
      Result := FindPopup(AControl.Components[i]);
  end;
end;

procedure TTMSFMXCustomGrid.ResetMouseInteraction;
begin
  FTouchScrolling := False;
  FDraggingMode := dmNone;
  FDragFixed := False;
  FFirstCell.Row := -1;
  FFirstCell.Col := -1;
  FMouseDown := False;
  FMouseUp := False;
  FMouseDragging := False;
  FMouseDblClick := FMouseSizing;
  FMouseSizing := False;
  FSizing := False;
end;

procedure TTMSFMXCustomGrid.ResetUpdate;
begin
  inherited EndUpdate;
  UpdateCount := 0;
end;


function TTMSFMXCustomGrid.GetCell(ACell: TCell): TControl;
begin
  ACell := GetDisplayCell(ACell);
  Result := nil;
  if (ACell.Col >= 0) and (ACell.Col <= Length(FCellArray) - 1) then
  begin
    if (ACell.Row >= 0) and (ACell.Row <= Length(FcellArray[ACell.Col]) - 1) then
    begin
      Result := FCellArray[ACell.Col, ACell.Row] as TControl;
    end;
  end;
end;

function TTMSFMXCustomGrid.GetCellAppearance(
  AState: TCellState; ACol, ARow: Integer): TTMSFMXGridCellLayout;
var
  c: TTMSFMXGridCell;
begin
  c := nil;
  Result := nil;
  case AState of
    csNormal:
    begin
      if GroupColumn <> -1 then
      begin
        if IsNode(DisplToRealRow(ARow)) then
          c := GetDefaultGroupLayout
        else if IsSummary(DisplToRealRow(ARow)) then
          c := GetDefaultSummaryLayout
        else
        begin
          if IsBand(DisplToRealRow(ARow)) and Options.Bands.Enabled then
            c := GetDefaultBandLayout
          else
            c := GetDefaultNormalLayout;
        end;
      end
      else
      begin
        if IsBand(DisplToRealRow(ARow)) and Options.Bands.Enabled then
          c := GetDefaultBandLayout
        else
          c := GetDefaultNormalLayout;
      end;
    end;
    csFocused: c := GetDefaultFocusedLayout;
    csFixed: c := GetDefaultFixedLayout;
    csFixedSelected: c := GetDefaultFixedSelectedLayout;
    csSelected: c := GetDefaultSelectedLayout;
  end;

  if Assigned(c) then
    Result := c.Layout;
end;

function TTMSFMXCustomGrid.GetCellByObject(Cell: TFMXObject): TCell;
var
  c: Integer;
  r: Integer;
begin
  Result.Col := -1;
  Result.Row := -1;
  for c := 0 to Length(FCellArray) - 1 do
  begin
    for r := 0 to Length(FCellArray[c]) - 1 do
    begin
      if FCellArray[c, r] = Cell then
      begin
        Result.Col := c;
        Result.Row := r;
        Result := GetRealCell(Result);
        Exit;
      end;
    end;
  end;
end;

function TTMSFMXCustomGrid.GetCellContainer: TControl;
var
  obj: TFmxObject;
begin
  Result := nil;
  obj := FindStyleResource('gridcells');
  if Assigned(obj) and (obj is TControl) then
    Result := obj as TControl;
end;

function TTMSFMXCustomGrid.GetCellObject(ACell: TCell): TControl;
begin
  Result := nil;
  if (ACell.Col >= 0) and (ACell.Col <= Length(FCellArray) - 1) then
  begin
    if (ACell.Row >= 0) and (ACell.Row <= Length(FcellArray[ACell.Col]) - 1) then
    begin
      Result := FCellArray[ACell.Col, ACell.Row] as TControl;
    end;
  end;
end;

function TTMSFMXCustomGrid.GetCellState(ACol, ARow: Integer): TCellState;
begin
  if (SelectionMode <> smNone) and (FocusedCell.Row = ARow) and (FocusedCell.Col = ACol) then
    Result := csFocused
  else
  begin
    if IsFixed(ACol, ARow) then
    begin
      if (SelectionMode <> smNone) and FixedShowSelection and DoIsFixedCellSelected(ACol,ARow) then
        Result := csFixedSelected
      else
        Result := csFixed
    end
    else if (SelectionMode <> smNone) and DoIsCellSelected(ACol,ARow) then
      Result := csSelected
    else
      Result := csNormal;
  end;
end;

function TTMSFMXCustomGrid.GetCellWrapper: TControl;
var
  obj: TFmxObject;
begin
  Result := nil;
  obj := FindStyleResource('gridwrapper');
  if Assigned(obj) and (obj is TControl) then
    Result := obj as TControl;
end;

function TTMSFMXCustomGrid.GetColumnViewPortSize: Single;
var
  I,cnt: Integer;
  w: Single;
  cellwrapper: TControl;
begin
  Result := 0;
  w := GetFixedWidth + GetFixedRightWidth;
  cnt := 0;
  cellwrapper := GetCellWrapper;
  if Assigned(cellwrapper) then
  begin
    for I := ColumnCount - 1 downto 0 do
    begin
      w := w + ColumnWidths[I];
      if w > cellwrapper.Width then
      begin
        Result := Max(1, cnt);
        Break;
      end;
      Inc(cnt);
    end;
  end;
end;

function TTMSFMXCustomGrid.GetDisplayCell(ACell: TCell): TCell;
var
  maxc, maxr: Integer;
begin
  maxc := Length(FCellArray) - 1;
  
  if ACell.Col > ColumnCount - 1 - FixedRightColumns then
    Result.Col := FCStop - FCStart + FixedRightColumns + FixedColumns - (ColumnCount - ACell.Col - 1)
  else if ACell.Col >= GetFixedCols then
    Result.Col := ACell.Col - FCStart + GetFixedCols
  else
    Result.Col := ACell.Col;

  Result.Col := Max(0, Min(Result.Col, maxc));

  maxr := 0;
  if (Result.Col >= 0) and (Result.Col <= maxc) then
  begin
    maxr := Length(FCellArray[Result.Col]) - 1;    
    if ACell.Row > RowCount - 1 - FixedRightColumns then
      Result.Row := FRStop - FRStart + FixedFooterRows + FixedRows - (RowCount - ACell.Row - 1)
    else if ACell.Row >= GetFixedRows then
      Result.Row := ACell.Row - FRStart + GetFixedRows
    else
      Result.Row := ACell.Row;
  end;

  Result.Row := Max(0, Min(Result.Row, maxr));
end;

function TTMSFMXCustomGrid.GetDisplayCellByObject(Cell: TFMXObject): TCell;
var
  c: Integer;
  r: Integer;
begin
  Result.Col := -1;
  Result.Row := -1;
  for c := 0 to Length(FCellArray) - 1 do
  begin
    for r := 0 to Length(FCellArray[c]) - 1 do
    begin
      if FCellArray[c, r] = Cell then
      begin
        Result.Col := c;
        Result.Row := r;
        Exit;
      end;
    end;
  end;
end;

function TTMSFMXCustomGrid.GetFixedCols: Integer;
begin
  Result := FixedColumns + FreezeColumns;
end;

function TTMSFMXCustomGrid.GetFixedFooterHeight: Single;
var
  I: Integer;
begin
  Result := 0;
  for I := RowCount - 1 downto RowCount - FixedFooterRows do
    Result := Result + RowHeights[I];
end;

function TTMSFMXCustomGrid.GetFixedHeight: Single;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to FixedRows + FreezeRows - 1 do
    Result := Result + RowHeights[I];
end;

function TTMSFMXCustomGrid.GetFixedOnlyHeight: Single;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to FixedRows - 1 do
    Result := Result + RowHeights[I];
end;

function TTMSFMXCustomGrid.GetFixedOnlyWidth: Single;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to FixedColumns - 1 do
    Result := Result + ColumnWidths[I];
end;

function TTMSFMXCustomGrid.GetFixedPrintHeight: Single;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to FixedRows - 1 do
    Result := Result + RowHeights[I];
end;

function TTMSFMXCustomGrid.GetFixedPrintWidth: Single;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to FixedColumns - 1 do
    Result := Result + ColumnWidths[I];
end;

function TTMSFMXCustomGrid.GetFixedRightWidth: Single;
var
  I: Integer;
begin
  Result := 0;
  for I := ColumnCount - 1 downto ColumnCount - FixedRightColumns do
    Result := Result + ColumnWidths[I];
end;

function TTMSFMXCustomGrid.GetFixedRows: Integer;
begin
  Result := FixedRows + FreezeRows;
end;

function TTMSFMXCustomGrid.GetFixedWidth: Single;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to FixedColumns + FreezeColumns - 1 do
    Result := Result + ColumnWidths[I];
end;

function TTMSFMXCustomGrid.GetFooter: TControl;
var
  fmx: TFMXObject;
begin
  Result := nil;
  fmx := FindStyleResource('gridfooter');
  if Assigned(fmx) and (fmx is TControl) then
    Result := fmx as TControl;
end;

function TTMSFMXCustomGrid.GetHeader: TControl;
var
  fmx: TFMXObject;
begin
  Result := nil;
  fmx := FindStyleResource('gridheader');
  if Assigned(fmx) and (fmx is TControl) then
    Result := fmx as TControl;
end;

function TTMSFMXCustomGrid.GetHeaderHeight: Single;
var
  h: TControl;
begin
  Result := 0;
  h := GetHeader;
  if Assigned(h) then
  begin
    if h.Visible then
      Result := h.Height;
  end;
end;

function TTMSFMXCustomGrid.GetHorizontalPos(AStartCol, AStopCol: Integer): Single;
var
  I: Integer;
begin
  Result := 0;
  for I := AStartCol to AStopCol - 1 do
    Result := Result + ColumnWidths[I];
end;

function TTMSFMXCustomGrid.GetHorizontalScrollBar: TScrollBar;
var
  obj: TFMXObject;
begin
  Result := nil;
  obj := FindStyleResource('gridhorizontalscrollbar');
  if Assigned(obj) and (obj is TScrollBar) then
    Result := obj as TScrollBar;
end;

function TTMSFMXCustomGrid.GetInstance: NativeUInt;
begin
  Result := HInstance;
end;

function TTMSFMXCustomGrid.GetLeftCol: Integer;
begin
  Result := FCStart;
end;

function TTMSFMXCustomGrid.GetPageScrollSize: Integer;
var
  vscrollbar: TScrollBar;
begin
  Result := 0;
  vscrollbar := GetVerticalScrollBar;
  if Assigned(vscrollbar) then
  begin
    if Options.Keyboard.PageScrollSize = 0 then
      Result := Round(vscrollbar.ViewportSize / vscrollbar.Max * RowCount)
    else
      Result := Options.Keyboard.PageScrollSize;
  end;
end;

{$IFNDEF ANDROID}
function TTMSFMXCustomGrid.GetPrintDPI: TPoint;
begin
  if (Options.Printing.DPI.X > -1) and (Options.Printing.DPI.Y > -1) then
  begin
    Result := Options.Printing.DPI;
    Printer.ActivePrinter.SelectDPI(Result);
  end
  else
  begin
    Result := Printer.ActivePrinter.ActiveDPI;
    if (Result.X = 0) or (Result.Y = 0) then
    begin
      Result := Point(72,72);
      if Printer.PageWidth > 1000 then
        Result := Point(300,300);
      if Printer.PageWidth > 4000 then
        Result := Point(600,600);
    end;
  end;
end;


function TTMSFMXCustomGrid.GetPrintPageCount: Integer;
var
  c, r: Integer;
  cw, rh: Single;
  x, y: Single;
  APageIndex: Integer;
  rSt, csave, cSt: Integer;
  FFixedRowsSave, FFixedColumnsSave: Integer;
  FCSave, FRSave: Integer;
  FFixedRowGet, FFixedColGet: Boolean;
  FSetRSave, FSetCSave: Boolean;
  dpi: TPoint;
  sx, sy: Single;
  bmpx, bmpy: Single;
begin
  dpi := GetPrintDPI;

  sx := dpi.X / 72;
  sy := dpi.Y / 72;

  sx := Max(1, sx);
  sy := Max(1, sy);

  APageIndex := 1;
  Printer.Orientation := Options.Printing.PrinterOrientation;
  bmpx := Printer.PageWidth / sx;
  bmpy := Printer.PageHeight / sy;

  x := Options.Printing.PrintMargins.Left;
  y := Options.Printing.PrintMargins.Top;

  rSt := 0;
  FFixedRowsSave := 0;
  FFixedColumnsSave := 0;
  cSave := -1;
  FCSave := 0;
  cSt := -1;
  FFixedRowGet := False;
  FFixedColGet := False;
  FSetRSave := True;
  FSetCSave := True;

  c := 0;

  while c <= ColumnCount - 1 do
  begin
    if cSt = -1 then
      cSt := c;

    if Options.Printing.RepeatFixedColumns and (FixedColumns > 0) then
    begin
      if (FFixedColumnsSave < FixedColumns) then
      begin
        FFixedColGet := True;
        if FSetCSave then
        begin
          FSetCSave := False;
          FCSave := cSt;
        end;

        cst := FFixedColumnsSave;
        c := cst;
        Inc(FFixedColumnsSave);
      end
      else if FFixedColGet then
      begin
        FFixedColGet := False;
        cSt := FCSave;
        c := FCSave;

        if cst < FixedColumns then
        begin
          cst := FixedColumns;
          c := cst;
        end;
      end;
    end;

    cw := ColumnWidths[c];

    r := rst;
    FRSave := rst;
    while r <= RowCount - 1 do
    begin
      if Options.Printing.RepeatFixedRows and (FixedRows > 0) then
      begin
        if (FFixedRowsSave < FixedRows) then
        begin
          FFixedRowGet := True;
          if FSetRSave then
          begin
            FSetRSave := False;
            FRSave := rSt;
          end;

          rSt := FFixedRowsSave;
          r := rst;
          Inc(FFixedRowsSave);
        end
        else if FFixedRowGet then
        begin
          FFixedRowGet := False;
          rSt := FRSave;
          r := FRSave;

          if rst < FixedRows then
          begin
            rst := FixedRows;
            r := rst;
          end;
        end;
      end;

      rh := RowHeights[r];

      y := y + rh;
      if (y + RowHeights[r + 1] > bmpy - Options.Printing.PrintMargins.Bottom) then
        Break;

      Inc(r);
    end;

    y := Options.Printing.PrintMargins.Top;
    x := x + cw;
    Inc(c);

    FFixedRowsSave := 0;

    if (x + ColumnWidths[c] > bmpx - Options.Printing.PrintMargins.Right) or (c = ColumnCount) then
    begin
      rst := r + 1;

      if cSave = -1 then
        cSave := c;

      if r = RowCount then
      begin
        c := cSave;
        cst := -1;
        rst := 0;
        cSave := -1;
      end
      else
        c := cst;

      FFixedRowsSave := 0;
      FFixedColumnsSave := 0;
      FSetRSave := True;
      FSetCSave := True;

      if (c < ColumnCount) then
      begin
        Inc(APageIndex);
        x := Options.Printing.PrintMargins.Left;
        y := Options.Printing.PrintMargins.Top;
      end;
    end;
  end;

  Result := APageIndex;
end;

{$ENDIF}

function TTMSFMXCustomGrid.GetRealCell(ACell: TCell): TCell;
begin
  if ACell.Col > Length(FCellArray) - 1 - FixedRightColumns then
    Result.Col := (ColumnCount - 1) - (Length(FCellArray) - 1 - ACell.Col)
  else if ACell.Col >= GetFixedCols then
    Result.Col := ACell.Col + FCStart - GetFixedCols
  else
    Result.Col := ACell.Col;

  if (ACell.Col >= 0) and (ACell.Col <= Length(FCellArray) - 1) then
  begin
    if ACell.Row > Length(FCellArray[ACell.Col]) - 1 - FixedRightColumns then
      Result.Row := (RowCount - 1) - (Length(FCellArray[ACell.Col]) - 1 - ACell.Row)
    else if ACell.Row >= GetFixedRows then
      Result.Row := ACell.Row + FRStart - GetFixedRows
    else
      Result.Row := ACell.Row;
  end;
end;

function TTMSFMXCustomGrid.GetRealLeftCol: Integer;
var
  I: Integer;
  ctl: TControl;
  cellcontainer: TControl;
begin
  cellContainer := GetCellContainer;
  Result := 0;
  if Length(FCellArray) > 0 then
  begin
    for I := 0 to Length(FCellArray) - 1 do
    begin
      if Assigned(FCellarray[I, 0]) and (FCellArray[I, 0] is TControl) then
      begin
        ctl := (FCellArray[I, 0] as TControl);
        if (ctl.Position.X + ctl.Width + cellcontainer.Position.X > GetFixedWidth) then
          Break
        else
          Inc(Result);
      end;
    end;
  end;

  Result := Result + FCStart - FixedColumns;
end;

function TTMSFMXCustomGrid.GetRealRect(AContainer: TControl; ARect: TRectF): TRectF;
begin
  Result := RectF(AContainer.Position.X + ARect.Left, AContainer.Position.Y + ARect.Top,
    AContainer.Position.X + ARect.Left + ARect.Width, AContainer.Position.Y + ARect.Top + ARect.Height);
end;

function TTMSFMXCustomGrid.GetRealTopRow: Integer;
var
  I: Integer;
  ctl: TControl;
  cellcontainer: TControl;
begin
  cellContainer := GetCellContainer;
  Result := 0;
  if Length(FCellArray) > 0 then
  begin
    for I := 0 to Length(FCellArray[0]) - 1 do
    begin
      if Assigned(FCellarray[0, I]) and (FCellArray[0, I] is TControl) then
      begin
        ctl := (FCellArray[0, I] as TControl);
        if (ctl.Position.Y + ctl.Height + cellcontainer.Position.Y > GetFixedHeight) then
          Break
        else
          Inc(Result);
      end;
    end;
  end;

  Result := Result + FRStart - FixedRows;
end;

function TTMSFMXCustomGrid.GetRowViewPortSize: Single;
var
  I,cnt: Integer;
  h: Single;
  cellwrapper: TControl;
begin
  Result := 0;
  h := GetFixedHeight + GetFixedFooterHeight;
  cnt := 0;
  cellwrapper := GetCellWrapper;
  if Assigned(cellwrapper) then
  begin
    for I := RowCount - 1 downto 0 do
    begin
      h := h + RowHeights[I];
      if h > cellwrapper.Height then
      begin
        Result := Max(1, cnt);
        Break;
      end;
      Inc(cnt);
    end;
  end;
end;

function TTMSFMXCustomGrid.GetTopRow: Integer;
begin
  Result := FRStart;
end;

function TTMSFMXCustomGrid.GetTotalColumnWidth: Single;
var
  I: Integer;
begin
  Result := ((ColumnCount - Min(ColumnCount, ColumnW.Count)) * DefaultColumnWidth);
  for I := 0 to Min(ColumnCount - 1, ColumnW.Count - 1) do
    Result := Result + ColumnW[I].Value;
end;

function TTMSFMXCustomGrid.GetTotalRowHeight: Single;
var
  I: Integer;
begin
  Result := ((RowCount - Min(RowCount, RowH.Count)) * DefaultRowHeight);
  for I := 0 to Min(RowCount - 1, RowH.Count - 1) do
    Result := Result + RowH[I].Value;
end;

function TTMSFMXCustomGrid.GetVersion: String;
var
  vn: Integer;
begin
  vn := GetVersionNr;
  Result := IntToStr(Hi(Hiword(vn)))+'.'+IntToStr(Lo(Hiword(vn)))+'.'+IntToStr(Hi(Loword(vn)))+'.'+IntToStr(Lo(Loword(vn)));
end;

function TTMSFMXCustomGrid.GetVersionNr: integer;
begin
  Result := MakeLong(MakeWord(BLD_VER,REL_VER),MakeWord(MIN_VER,MAJ_VER));
end;

function TTMSFMXCustomGrid.GetVerticalPos(AStartRow, AStopRow: Integer): Single;
var
  I: Integer;
begin
  Result := 0;
  for I := AStartRow to AStopRow - 1 do
    Result := Result + RowHeights[I];
end;

function TTMSFMXCustomGrid.GetVerticalScrollBar: TScrollBar;
var
  obj: TFMXObject;
begin
  Result := nil;
  obj := FindStyleResource('gridverticalscrollbar');
  if Assigned(obj) and (obj is TScrollBar) then
    Result := obj as TScrollBar;
end;

procedure TTMSFMXCustomGrid.HideEdit(AEnterKey: Boolean = False);
var
  str: String;
  allow: Boolean;
  cellcontainer: TControl;
  cl, rl: TCell;
  navigate: Boolean;
  cellEditor: TTMSFMXGridEditorType;
  col: TAlphaColor;
  p: TPopup;
  r, c: Integer;
begin
  cellContainer := GetCellContainer;
  FEditing := false;

  if Assigned(cellcontainer) and Assigned(FEditControl) then
  begin
    str := '';
    if FEditControl is TTMSFMXEdit then
      str := (FEditControl as TEdit).Text
    else if FEditControl is TSpinBox then
      str := (FEditControl as TSpinBox).Text
    else if FEditControl is TCellCalendarBox then
      str := (FEditControl as TCellCalendarBox).Text
    else if FEditControl is TCalendarEdit then
      str := (FEditControl as TCalendarEdit).Text
    else if FEditControl is TTrackBar then
      str := FloatToStr((FEditControl as TTrackBar).Value)
    else if FEditControl is TArcDial then
      str := FloatToStr((FEditControl as TArcDial).Value)
    else if FEditControl is TComboEdit then
      str := (FEditControl as TComboEdit).Text
    else if FEditControl is TComboColorBox then
      col := (FEditControl as TComboColorBox).Color
    else if (FEditControl is TComboBox) and Assigned((FEditControl as TComboBox).Selected) then
      str := (FEditControl as TComboBox).Selected.Text;

    Allow := True;
    if (FEditControl is TComboColorBox) then
      DoCellEditValidateColor(FEditCell.Col, FEditCell.Row, FEditControl, col, Allow)
    else
      DoCellEditValidateData(FEditCell.Col, FEditCell.Row, FEditControl, str, allow);

    if Allow then
    begin
      if (FEditControl is TComboColorBox) then
        DoCellEditSetColor(FEditCell.Col, FEditCell.Row, FEditControl, col)
      else
        DoCellEditSetData(FEditCell.Col, FEditCell.Row, FEditControl, str);

      if Options.Editing.AutoHistory and (FEditControl is TTMSFMXEdit) then
      begin
        Options.Editing.AutoCompleteItems.Add(str);
      end;
    end;

    DoCellEditDone(FEditCell.Col, FEditCell.Row, FEditControl);

    DoGetCellEditorType(FEditCell.Col, FEditCell.Row, cellEditor);

    if cellEditor <> etCustom then
    begin
      p := FindPopup(FEditControl);
      if Assigned(p) then
      begin
        if p.IsOpen then
          p.IsOpen := False;
      end;

      FEditControl.Parent := nil;
      FEditControl := nil;
    end
    else
    begin
      FEditControl.Free;
      FEditControl := nil;
    end;

    navigate := false;
    cl := FocusedCell;
    rl := FRealCell;

    if AEnterKey then
    begin
      case Options.Keyboard.EnterKeyHandling of
        ekhNextColumn, ekhNextColumnInRow, ekhNextColumnAndAppend, ekhNextColumnAndRowAppend:
        begin
          navigate := True;
          c := cl.Col;
          cl := NextSelectableColumn(cl.Col, cl.Row);
          cl.Row := rl.Row;

          Allow := True;
          DoCanAppendColumn(cl.Col + 1, Allow);
          if Allow and (Options.Keyboard.EnterKeyHandling = ekhNextColumnAndAppend) then
          begin
            while (c = cl.Col) do
            begin
              ColumnCount := ColumnCount + 1;
              cl := NextSelectableColumn(cl.Col, cl.Row);
              DoAppendColumn(ColumnCount - 1);
            end;
          end;

          if (c = cl.Col) then
          begin
            r := cl.row;
            cl.Col := FixedColumns;
            cl := NextSelectableColumn(cl.Col, cl.Row);

            if r = cl.Row then
            begin
              if Options.Keyboard.EnterKeyHandling = ekhNextColumnInRow then
                cl := NextSelectableColumn(FixedColumns - 1, cl.Row - 1)
              else
              begin
                Allow := True;
                DoCanAppendRow(cl.Row + 1, Allow);
                if Allow and (cl.Row = RowCount - 1 - FixedFooterRows) then
                begin
                  if (Options.KeyBoard.EnterKeyHandling = ekhNextColumnAndRowAppend) then
                  begin
                    while (r = cl.Row) do
                    begin
                      RowCount := RowCount + 1;
                      cl := NextSelectableColumn(FixedColumns - 1, cl.Row + 1);
                      DoAppendRow(RowCount - 1);
                    end;
                  end;
                end
                else
                  cl := NextSelectableColumn(FixedColumns - 1, cl.Row + 1);
              end;

              while (cl.Col = FixedColumns - 1) and (cl.Row = r + 1) do
              begin
                r := cl.Row;
                cl := NextSelectableColumn(FixedColumns - 1, cl.row + 1);
              end;
            end;
          end;
        end;
        ekhNextRow, ekhNextRowInColumn, ekhNextRowAndAppend, ekhNextRowAndColumnAppend:
        begin
          navigate := True;
          r := cl.Row;
          cl := NextSelectableRow(cl.Col, cl.Row);
          cl.Col := rl.Col;

          Allow := True;
          DoCanAppendRow(cl.Row + 1, Allow);
          if Allow and (Options.Keyboard.EnterKeyHandling = ekhNextRowAndAppend) then
          begin
            while (r = cl.Row) do
            begin
              RowCount := RowCount + 1;
              cl := NextSelectableRow(cl.Col, cl.Row);
              DoAppendRow(RowCount - 1);
            end;
          end;

          if (r = cl.Row) then
          begin
            c := cl.Col;
            cl.Row := FixedRows;
            cl := NextSelectableRow(cl.Col, cl.Row);

            if c = cl.Col then
            begin
              if Options.Keyboard.EnterKeyHandling = ekhNextColumnInRow then
                cl := NextSelectableRow(cl.Col - 1, FixedRows - 1)
              else
              begin
                Allow := True;
                DoCanAppendColumn(cl.Col + 1, Allow);
                if Allow and (cl.Col = ColumnCount - 1 - FixedRightColumns) then
                begin
                  if (Options.KeyBoard.EnterKeyHandling = ekhNextRowAndColumnAppend) then
                  begin
                    while (c = cl.Col) do
                    begin
                      ColumnCount := ColumnCount + 1;
                      cl := NextSelectableRow(cl.Col + 1, FixedRows - 1);
                      DoAppendColumn(ColumnCount - 1);
                    end;
                  end;
                end
                else
                  cl := NextSelectableRow(cl.Col + 1, FixedRows - 1);
              end;

              while (cl.Row = FixedRows - 1) and (cl.Col = c + 1) do
              begin
                c := cl.Col;
                cl := NextSelectableRow(cl.Col + 1, FixedRows - 1);
              end;
            end;
          end;
        end;
      end;
    end;

    SetFocus;
    if navigate then
    begin
      SelectCell(cl);
      if AEnterKey and Options.Keyboard.EnterKeyDirectEdit then
      begin
        Allow := True;
        DoCanEditCell(FocusedCell.Col, FocusedCell.Row, Allow);
        if Allow then
          EditCell(FocusedCell);
      end;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.KeyDown(var Key: Word; var KeyChar: WideChar;
  Shift: TShiftState);
var
  vscroll, hscroll: TScrollBar;
  sel, selprev, real: TCell;
  cs, rs, i, j: Integer;
  Allow: boolean;
begin
  if Key = vkTab then
    Exit;

  inherited;

  if (Key = vkSpace) or ((Key = 0) and (KeyChar = ' ')) then
  begin
    Key := vkSpace;
    if (FocusedCell.Row <> -1) and (FocusedCell.Col <> -1) then
      DoCellKeyDown(FocusedCell.Col, FocusedCell.Row, Key, KeyChar, Shift);

    Exit;
  end;

  if not Options.Editing.Enabled and Options.Lookup.Enabled then
  begin
    if Key in [vkescape,vkinsert,vkdelete,vkup,vkdown,vkleft,vkright,vkhome,vkend,vkprior,vknext,vktab] then
      FLookupString := ''
    else
    begin
      if Options.Lookup.Incremental then
        FLookupString := FLookupString + KeyChar
      else
        FLookupString := KeyChar;

      LookupInColumn(Focusedcell.Col,FLookupString,true,true);
    end;
  end;

  if Options.Editing.Enabled then
  begin
    DoCanEditCell(FocusedCell.Col, FocusedCell.Row,Allow);

    if Allow then
    begin
      {$IFDEF DELPHIXE6_LVL}
      if KeyChar.IsInArray([^H]) or (Char(KeyChar) >= #32) then
      {$ELSE}
      if (CharInSet(Char(KeyChar), [^H]) or (Char(KeyChar) >= #32)) then
      {$ENDIF}
      begin
        EditCell(FocusedCell, csStart, True, KeyChar, Key);
        Exit;
      end
      else if (Key = vkF2) or (Key = vkReturn) then
      begin
        EditCell(FocusedCell);
        Exit;
      end;
    end;
  end;

  FPrevSelectedCell := FocusedCell;
  vscroll := GetVerticalScrollBar;
  hscroll := GetHorizontalScrollBar;
  if Assigned(vscroll) and Assigned(hscroll) then
  begin
    if (ssShift in Shift) and (not (ssCtrl in Shift)) and (not (ssAlt in Shift)) and
        (Key = vkInsert) and not FEditing then
          PasteFromClipboard;

    if (ssCtrl in Shift) and not (ssShift in Shift) and not (ssAlt in Shift) then
    begin
      case Key of
//        vkSpace : RowSelect[FocusedCell.Row] := not RowSelect[FocusedCell.Row];
        Ord('M'): if Options.Keyboard.AllowCellMergeShortCut then MergeSelection(Selection);
        Ord('S'): if Options.Keyboard.AllowCellMergeShortCut then SplitCell(Selection.StartCol, Selection.StartRow);
        Ord('C'):
        begin
          if Options.Clipboard.Enabled and not FEditing then
            CopyToClipboard(True);
        end;
        Ord('V'):
        begin
          if Options.Clipboard.Enabled and not FEditing then
            PasteFromClipboard;
        end;
        Ord('A'):
        begin
          if (SelectionMode in [smCellRange, smRowRange, smColumnRange, smDisjunctCell, smDisjunctColumn, smDisjunctRow]) then
          begin
            SetStartCell(FixedColumns, FixedRows);
            SetStopCell(ColumnCount - 1 - FixedRightColumns, RowCount - 1 - FixedFooterRows);
            SetFocusCell(FixedRows, FixedColumns);
          end;
          if (SelectionMode in [smDisjunctRow]) then
          begin
            BlockUpdate := True;
            for i := FixedRows to RowCount - FixedFooterRows - 1 do
              RowSelect[i] := true;
            BlockUpdate := False;
          end;

          if (SelectionMode in [smDisjunctColumn]) then
          begin
            BlockUpdate := True;
            for i := FixedColumns to ColumnCount - 1 - FixedRightColumns do
              ColumnSelect[i] := true;
            BlockUpdate := False;
          end;

          if (SelectionMode in [smDisjunctCell]) then
          begin
            BlockUpdate := True;
            for i := FixedColumns to ColumnCount - 1 - FixedRightColumns do
              for j := FixedRows to RowCount - FixedFooterRows - 1 do
                CellSelect[i, j] := True;
            BlockUpdate := False;
          end;

          Navigate(FocusedCell);
        end;
        vkInsert: CopyToClipboard;
        vkNext: TopRow := TopRow + GetPageScrollSize;
        vkPrior: TopRow := TopRow - GetPageScrollSize;
        vkUp:
        begin
          TopRow := TopRow - 1;
        end;
        vkDown:
        begin
          rs := RowSpan(LeftCol, TopRow);
          if rs > 0 then
            TopRow := TopRow + rs
          else
            TopRow := TopRow + 1;
        end;
        vkLeft: LeftCol := LeftCol - 1;
        vkRight:
        begin
          cs := ColSpan(LeftCol, TopRow);
          if cs > 0 then
            LeftCol := LeftCol + cs
          else
            LeftCol := LeftCol + 1;
        end;
        vkHome: TopRow := FixedRows;
        vkEnd: TopRow := RowCount - 1 - FixedFooterRows;
      end;
    end
    else
    begin
      sel := FocusedCell;
      real := FRealCell;
      selPrev := FPrevSelectedCell;

      case Key of
        vkDelete:
        begin
          if Options.Keyboard.DeleteKeyHandling = dkhDeleteRow then
          begin
            if RowCount > FixedRows + FixedFooterRows then
            begin
              Allow := true;
              DoCanDeleteRow(sel.Row,Allow);
              if Allow then
              begin
                DeleteRow(sel.Row);
                sel.Row := Max(FixedRows, Min(sel.Row, RowCount - 1 - FixedFooterRows));
                DoDeleteRow(sel.Row);
              end;
            end;
          end;
        end;
        vkInsert:
        begin
          case Options.Keyboard.InsertKeyHandling of
            ikhInsertRowBefore:
            begin
              Allow := true;
              DoCanInsertRow(sel.Row,Allow);
              if Allow then
              begin
                InsertRow(sel.Row);
                sel.Row := Max(FixedRows, Min(sel.Row, RowCount - 1 - FixedFooterRows));
                DoInsertRow(sel.Row);
              end;
            end;
            ikhInsertRowAfter:
            begin
              Allow := true;
              DoCanInsertRow(sel.Row,Allow);
              if Allow then
              begin
                InsertRow(sel.Row + 1);
                sel.Row := Max(FixedRows, Min(sel.Row + 1, RowCount - 1 - FixedFooterRows));
                DoInsertRow(sel.Row);
              end;
            end;
          end;
        end;
        vkNext:
        begin
          sel := NextSelectableRow(sel.Col, sel.Row + GetPageScrollSize - 1);
          sel.Col := real.Col;
        end;
        vkPrior:
        begin
          sel := PreviousSelectableRow(sel.Col, sel.Row - GetPageScrollSize + 1);
          sel.Col := real.Col;
        end;
        vkUp:
        begin
          sel := PreviousSelectableRow(sel.Col, sel.Row);
          sel.Col := real.Col;
        end;
        vkDown:
        begin
          sel := NextSelectableRow(sel.Col, sel.Row);
          sel.Col := real.Col;
        end;
        vkLeft:
        begin
          sel := PreviousSelectableColumn(sel.Col, sel.Row);
          sel.Row := real.Row;
        end;
        vkRight:
        begin
          sel := NextSelectableColumn(sel.Col, sel.Row);
          sel.Row := real.Row;
        end;
        vkHome:
        begin
          if (ssAlt in Shift) then
          begin
            sel.Row := FixedRows;
            sel.Col := real.Col;
            sel := NextSelectableRow(sel.Col, sel.Row - 1);
          end
          else
          begin
            sel.Col := FixedColumns;
            sel.Row := real.Row;
            sel := NextSelectableColumn(sel.Col - 1, sel.Row);
          end;
        end;
        vkEnd:
        begin
          if (ssAlt in Shift) then
          begin
            sel.Row := RowCount - 1 - FixedFooterRows;
            sel.Col := real.Col;
            sel := PreviousSelectableRow(sel.Col, sel.Row + 1);
          end
          else
          begin
            sel.Col := ColumnCount - 1 - FixedRightColumns;
            sel.Row := real.Row;
            sel := PreviousSelectableColumn(sel.Col + 1, sel.Row);
          end;
        end;
      end;

      case Key of
        vkUp, vkLeft, vkDown, vkRight, vkHome, vkEnd, vkNext, vkPrior, vkDelete, vkInsert:
        begin
          if (ssCtrl in Shift) then
            Navigate(sel)
          else
            SelectCell(sel, Shift)
        end;
      end;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.KeyUp(var Key: Word; var KeyChar: WideChar;
  Shift: TShiftState);
begin
  inherited;
  if (Key = vkSpace) or ((Key = 0) and (KeyChar = ' ')) then
  begin
    Key := vkSpace;
    if (FocusedCell.Row <> -1) and (FocusedCell.Col <> -1) then
      DoCellKeyUp(FocusedCell.Col, FocusedCell.Row, Key, KeyChar, Shift);
  end;
end;

procedure TTMSFMXCustomGrid.LayoutChanged(Sender: TObject);
begin
  if ((Sender as TTMSFMXGridCellLayout).Owner is TTMSFMXGridCell) then
    ((Sender as TTMSFMXGridCellLayout).Owner as TTMSFMXGridCell).Repaint;
  UpdateGridCellAppearance;
end;

procedure TTMSFMXCustomGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
var
  cell: TCell;
  obj: TControl;
  sx, sy: Single;
  cellContainer: TControl;
  fixed, rowsz, colsz: Boolean;
  allow: Boolean;
begin
  inherited;

  Capture;
  if not (Button = TMouseButton.mbLeft) then
    Exit;

  cellContainer := GetCellContainer;

  sx := X;
  sy := Y;
  if Assigned(cellContainer) then
  begin
    sx := X - cellContainer.Position.X;
    sy := Y - GetHeaderHeight - cellContainer.position.Y;
  end;

  cell := XYToCell(X, Y - GetHeaderHeight);

  obj := GetCellObject(GetDisplayCell(cell));
  if Assigned(obj) then
  begin
    FDragCell := cell;
    fixed := IsFixed(cell.Col, cell.Row);

    if fixed then
    begin
      rowsz := Options.Mouse.FixedRowSizing;
      colsz := Options.Mouse.FixedColumnSizing;
    end
    else
    begin
      rowsz := Options.Mouse.RowSizing;
      colsz := Options.Mouse.ColumnSizing;
    end;
    if rowsz and (sy >= obj.Position.Y + obj.Height - 1.5) and (sx > obj.Position.X + 1.5) and (sx < obj.Position.X + obj.Width - 1.5) then
    begin
      allow := True;
      DoCanSizeRow(cell.Row, allow);
      if allow then
      begin
        FMouseSizing := True;
        FSizeMode := smRowSizing;
      end;
    end
    else if rowsz and (sy <= obj.Position.Y + 1.5) and (sx > obj.Position.X + 1.5) and (sx < obj.Position.X + obj.Width - 1.5) then
    begin
      Allow := True;
      DoCanSizeRow(cell.Row - 1, allow);
      if allow then
      begin
        FMouseSizing := True;
        FSizeMode := smPreviousRowSizing;
      end;
    end
    else if colsz and (sx >= obj.Position.X + obj.Width - 1.5) and (sy > obj.Position.Y + 1.5) and (sy < obj.Position.Y + obj.Height - 1.5) then
    begin
      Allow := True;
      DoCanSizeColumn(cell.Col, allow);
      if allow then
      begin
        FMouseSizing := True;
        FSizeMode := smColumnSizing;
      end;
    end
    else if colsz and (sx <= obj.Position.X - 1.5) and (sy > obj.Position.Y + 1.5) and (sy < obj.Position.Y + obj.Height - 1.5) then
    begin
      Allow := True;
      DoCanSizeColumn(cell.Col - 1, allow);
      if Allow then
      begin
        FMouseSizing := True;
        FSizeMode := smPreviousColumnSizing;
      end;
    end
    else
    begin
      FMouseDown := True;
      FMouseUp := True;
      if not IsFixed(Cell.Col, Cell.Row) then
      begin
        Cell := BaseCell(Cell.Col, Cell.Row);
        if ((Cell.Col = FocusedCell.Col) and (Cell.Row = FocusedCell.Row)) and not Options.Mouse.DirectEdit then
        begin
          FMouseUp := False;
          DoCanEditCell(FocusedCell.Col, FocusedCell.Row, Allow);
          FMouseUp := not Allow;

          if Allow then
            EditCell(FocusedCell);
        end
        else
          HideEdit;
      end
      else
      begin
        FDragFixed := (Options.Mouse.RowDragging and ((Cell.Col < FixedColumns) or (Cell.Col > ColumnCount - 1 - FixedRightColumns)));
        FDragFixed := FDragFixed or ((Options.Sorting.Mode <> gsmNone) or Options.Mouse.ColumnDragging) and ((Cell.Row < FixedRows) or (Cell.Row > RowCount - 1 - FixedFooterRows));
      end;
    end;
  end;

  FTouchScrolling := False;

  FPrevX := X;
  FPrevY := Y - GetHeaderHeight;

  FSizeX := FPrevX;
  FSizeY := FPrevY;

  FDragX := FPrevX;
  FDragY := FPrevY;

  FDownX := FPrevX;
  FDownY := FPrevY;

  FTouchX := FPrevX;
  FTouchY := FPrevY;

  FCellAnchor.Col := -1;
  FCellAnchor.Row := -1;
  FCellAnchorString := '';
end;

procedure TTMSFMXCustomGrid.MouseMove(Shift: TShiftState; X, Y: Single);
var
  res: TCell;
  cellWrapper, cellcontainer: TControl;
  enablescroll: Boolean;
  obj: TControl;
  sx, sy: Single;
  fixed: Boolean;
  rowsz, colsz: Boolean;
  cell: TFmxObject;
  bmpc, bmp: TBitmap;
  dx, dy: Single;
  Allow: Boolean;
  newsz: Single;
  newposx, newposy: Single;
  mx, my: Single;
  touchscroll: Boolean;
  hscroll, vscroll: TScrollBar;
  vs, hs: Single;
  fxd: Boolean;
begin
  inherited;

  if FDblClick then
  begin
    FDblClick := False;
    FMouseDown := False;
    ReleaseCapture;
  end;

  FCellAnchor.Col := -1;
  FCellAnchor.Row := -1;
  FCellAnchorString := '';

  FX := X;
  FY := Y - GetHeaderHeight;

  if not FUpdatingGrid then
  begin
    if FMouseDown and (((FX < FDownX - CLICKMARGIN) or (FX > FDownX + CLICKMARGIN)) or ((FY < FDownY - CLICKMARGIN) or (FY > FDownY + CLICKMARGIN))) then
    begin
      if not FDragFixed then
      begin
        HideEdit;
        cellcontainer := GetCellContainer;
        cellWrapper := GetCellWrapper;

        touchscroll := Options.Mouse.TouchScrolling and ((SelectionMode = smSingleCell) or (SelectionMode = smSingleRow) or (SelectionMode = smSingleColumn) or (SelectionMode = smNone));

        enablescroll := False;
        if (((-cellcontainer.Position.X > 0) and (FX < GetFixedWidth))
         or ((-cellContainer.Position.X < GetTotalColumnWidth - GetFixedRightWidth) and (FX > cellWrapper.Width - GetFixedRightWidth)))
         or (((-cellcontainer.Position.Y > 0) and (FY < GetFixedHeight))
          or ((-cellContainer.Position.Y < GetTotalRowHeight - GetFixedFooterHeight) and (FY > cellWrapper.Height - GetFixedFooterHeight))) then
        enablescroll := Options.Mouse.AutoScrolling and not touchscroll;

        if (FX > cellWrapper.Width - GetFixedRightWidth) then
          enablescroll := not (fcsRow in Options.Mouse.FixedCellSelection) and not (fcsRowRange in Options.Mouse.FixedCellSelection);

        if (FY > cellWrapper.Height - GetFixedFooterHeight) then
          enablescroll := not (fcsColumn in Options.Mouse.FixedCellSelection) and not (fcsColumnRange in Options.Mouse.FixedCellSelection);


        FScrollTimer.Enabled := enablescroll;

        if not FScrollTimer.Enabled then
        begin
          if Options.Mouse.TouchScrolling and touchscroll then
          begin
            vscroll := GetVerticalScrollBar;
            hscroll := GetHorizontalScrollBar;
            if assigned(vscroll) and Assigned(hscroll) then
            begin
              FTouchScrolling := True;
              vs := vscroll.Value;
              hs := hscroll.Value;
              vs := vs + ((FTouchY - Y) * Options.Mouse.TouchScrollingSensitivity);
              hs := hs + ((FTouchX - X) * Options.Mouse.TouchScrollingSensitivity);
              Scroll(vs, hs);
              FTouchX := X;
              FTouchY := Y;
            end;
          end
          else
          begin
            FMouseDragging := True;
            res := XYToCell(X, Y - GetHeaderHeight);
            if (res.Col <> -1) and (res.Row <> -1) then
            begin
              fxd := IsFixed(res.Col, res.Row);
              case SelectionMode of
                smSingleRow: if (fcsRow in Options.Mouse.FixedCellSelection) then fxd := False;
                smSingleColumn: if (fcsColumn in Options.Mouse.FixedCellSelection) then fxd := False;
                smRowRange: if (fcsRowRange in Options.Mouse.FixedCellSelection) then fxd := False;
                smColumnRange: if (fcsColumnRange in Options.Mouse.FixedCellSelection) then fxd := False;
              end;

              if not fxd then
                SelectCell(res, Shift, FMouseDragging);
            end;
          end;
        end;
      end
      else
      begin
        if (FDragCell.Col <> -1) and (FDragCell.Row <> -1) then
        begin
          cellWrapper := GetCellWrapper;
          if not Assigned(FDragBitmap) then
          begin
            cellContainer := GetCellContainer;
            cell := GetCellObject(GetDisplayCell(FDragCell));
            if Assigned(cell) and (cell is TControl) then
            begin
              Allow := Options.Mouse.ColumnDragging;
              if Allow then
                DoCanDragColumn(FDragCell.Col, Allow);

              if ((FDragCell.Row < FixedRows) or (FDragCell.Row > RowCount - 1 - FixedFooterRows)) and (Allow) then
              begin
                Cursor := crUpArrow;
                FDragBitmap := TImage.Create(Self);
                FDragBitmap.HitTest := False;
                FDragBitmap.Width := (cell as TControl).Width + 1;
                FDragBitmap.Height := Min(cellcontainer.Height, cellwrapper.Height);
                dx := -(cell as TControl).Position.X - cellcontainer.Position.X + 1;
                FDragBitmap.Position.X := -dx;
                FDragBitmap.Position.Y := GetHeaderHeight - 1;
                bmp := TBitmap.Create(Round(FDragBitmap.Width), Round(FDragBitmap.Height));
                bmp.Canvas.BeginScene;
                bmp.Canvas.Fill.Assign(GetColumnDragLayoutFill);
                bmp.Canvas.FillRect(RectF(0, 0, bmp.Width, bmp.Height), 0, 0, AllCorners, 0.5);
                bmpc := TBitmap.Create(bmp.Width, bmp.Height);
                bmpc.Canvas.BeginScene;
                PaintTo(bmpc.Canvas, RectF(dx, -GetHeaderHeight + 1, Width + dx, Height - GetHeaderHeight));
                bmpc.Canvas.EndScene;

                bmp.Canvas.DrawBitmap(bmpc, RectF(0, 0, bmp.Width, bmp.Height), RectF(0, 0, bmp.Width, bmp.Height), 0.5);

                bmpc.Free;

                bmp.Canvas.Stroke.Assign(GetColumnDragLayoutStroke);
                bmp.Canvas.StrokeThickness := GetColumnDragLayoutStrokeThickNess;
                bmp.Canvas.StrokeCap := GetColumnDragLayoutStrokeCap;
                bmp.Canvas.StrokeDash := GetColumnDragLayoutStrokeDash;
                bmp.Canvas.StrokeJoin := GetColumnDragLayoutStrokeJoin;
                bmp.Canvas.DrawRect(RectF(0, 0, bmp.Width, bmp.Height), 0, 0, AllCorners, 0.5);
                bmp.Canvas.EndScene;
                FDragBitmap.Bitmap.Assign(bmp);
                bmp.Free;
                FDraggingMode := dmColumnDragging;
                FSaveDragX := FDragBitmap.Position.X;
                AddObject(FDragBitmap);
                FDragBitmap.BringToFront;
                FDragX := FX;
                FDragY := FY;
              end
              else
              begin
                Allow := Options.Mouse.RowDragging;
                if Allow then
                  DoCanDragColumn(FDragCell.Row, Allow);

                if ((FDragCell.Col < FixedColumns) or (FDragCell.Col > ColumnCount - 1 - FixedRightColumns)) and (Allow) then
                begin
                  Cursor := crUpArrow;
                  FDragBitmap := TImage.Create(Self);
                  FDragBitmap.HitTest := False;
                  FDragBitmap.Width := Min(cellcontainer.Width, cellwrapper.Width);
                  FDragBitmap.Height := (cell as TControl).Height + 1;
                  dy := -(cell as TControl).Position.Y - cellcontainer.Position.Y + 1 - GetHeaderHeight;
                  FDragBitmap.Position.Y := -dy;
                  FDragBitmap.Position.X := 0;
                  bmp := TBitmap.Create(Round(FDragBitmap.Width), Round(FDragBitmap.Height));
                  bmp.Canvas.BeginScene;
                  bmp.Canvas.Fill.Assign(GetRowDragLayoutFill);
                  bmp.Canvas.FillRect(RectF(0, 0, bmp.Width, bmp.Height), 0, 0, AllCorners, 0.5);
                  bmpc := TBitmap.Create(bmp.Width, bmp.Height);
                  bmpc.Canvas.BeginScene;

                  PaintTo(bmpc.Canvas, RectF(0, dy, Width, Height + dy));

                  bmp.Canvas.DrawBitmap(bmpc, RectF(0, 0, bmp.Width, bmp.Height), RectF(0, 0, bmp.Width, bmp.Height), 0.5);

                  bmpc.Free;

                  bmp.Canvas.Stroke.Assign(GetRowDragLayoutStroke);
                  bmp.Canvas.StrokeThickness := GetRowDragLayoutStrokeThickNess;
                  bmp.Canvas.StrokeCap := GetRowDragLayoutStrokeCap;
                  bmp.Canvas.StrokeDash := GetRowDragLayoutStrokeDash;
                  bmp.Canvas.StrokeJoin := GetRowDragLayoutStrokeJoin;
                  bmp.Canvas.DrawRect(RectF(0, 0, bmp.Width, bmp.Height), 0, 0, AllCorners, 0.5);
                  bmp.Canvas.EndScene;
                  FDragBitmap.Bitmap.Assign(bmp);
                  bmp.Free;
                  FDraggingMode := dmRowDragging;
                  FSaveDragY := FDragBitmap.Position.Y;
                  AddObject(FDragBitmap);
                  FDragBitmap.BringToFront;
                  FDragX := FX;
                  FDragY := FY;
                end;
              end;
            end
          end
          else
          begin
            if (((FDragCell.Row < FixedRows) or (FDragCell.Row > RowCount - 1 - FixedFooterRows))) and (Options.Mouse.ColumnDragging) then
            begin
              newposx := FSaveDragX + (FX - FDragX);
              if (newposx <= cellWrapper.Width - FDragBitmap.Width) and (newposx >= 0) then
              begin
                FDragBitmap.Position.X := Max(0, Min(cellWrapper.Width - FDragBitmap.Width,  newposx));
                FSaveDragX := FDragBitmap.Position.X;
                FDragTimer.Enabled := False;
                if (newposx > GetFixedWidth) and (newposx < cellwrapper.Width - GetFixedRightWidth) then
                  FDragScrollCell := XYToCell(newposx, cellWrapper.Height / 2);
              end
              else if (newposx > cellWrapper.Width - FDragBitmap.Width) then
              begin
                FDragBitmap.Position.X := cellWrapper.Width - FDragBitmap.Width;
                FSaveDragX := newposx;
                FDragTimer.Enabled := True;
              end
              else if (newposx < 0) then
              begin
                FDragBitmap.Position.X := 0;
                FSaveDragX := newposx;
                FDragTimer.Enabled := True;
              end;

              FDragX := FX;
              FDragY := FY;
            end
            else if (((FDragCell.Col < FixedColumns) or (FDragCell.Col > ColumnCount - 1 - FixedRightColumns))) and (Options.Mouse.RowDragging) then
            begin
              newposy := FSaveDragY + (FY - FDragY);
              if (newposy <= cellWrapper.Height - FDragBitmap.Height + GetHeaderHeight) and (newposy >= GetHeaderHeight) then
              begin
                FDragBitmap.Position.Y := Max(GetHeaderHeight, Min(cellWrapper.Height - FDragBitmap.Height + GetHeaderHeight,  newposy));
                FSaveDragY := FDragBitmap.Position.Y;
                FDragTimer.Enabled := False;
                if (newposy > GetFixedHeight) and (newposy < cellwrapper.Height - GetFixedFooterHeight) then
                  FDragScrollCell := XYToCell(cellWrapper.Width / 2, newposy);
              end
              else if (newposy > cellWrapper.Height - FDragBitmap.Height + GetHeaderHeight) then
              begin
                FDragBitmap.Position.Y := cellWrapper.Height - FDragBitmap.Height + GetHeaderHeight;
                FSaveDragY := newposy;
                FDragTimer.Enabled := True;
              end
              else if (newposy < GetHeaderHeight) then
              begin
                FDragBitmap.Position.Y := GetHeaderHeight;
                FSaveDragY := newposy;
                FDragTimer.Enabled := True;
              end;

              FDragX := FX;
              FDragY := FY;
            end;

          end;
        end;
      end;
    end
    else if not FMouseSizing then
    begin
      cellcontainer := GetCellContainer;
      sx := X;
      sy := Y;
      if Assigned(cellContainer) then
      begin
        sx := X - cellContainer.Position.X;
        sy := Y - GetHeaderHeight - cellContainer.position.Y;
      end;

      res := XYToCell(X, Y - GetHeaderHeight);
      if (res.col <> -1) and (res.row <> -1) then
      begin
        obj := GetCellObject(GetDisplayCell(res));
        if Assigned(obj) then
        begin
          fixed := IsFixed(res.Col, res.Row);

          if fixed then
          begin
            rowsz := Options.Mouse.FixedRowSizing;
            colsz := Options.Mouse.FixedColumnSizing;
          end
          else
          begin
            rowsz := Options.Mouse.RowSizing;
            colsz := Options.Mouse.ColumnSizing;
          end;

          if rowsz and (sy <= obj.Position.Y + 1.5) and (sx > obj.Position.X + 1.5) and (sx < obj.Position.X + obj.Width - 1.5) then
          begin
            allow := True;
            DoCanSizeRow(res.Row - 1, allow);
            if allow then
              Cursor := crSizeNS
          end
          else if (rowsz and (sy >= obj.Position.Y + obj.Height - 1.5) and (sx > obj.Position.X + 1.5) and (sx < obj.Position.X + obj.Width - 1.5)) then
          begin
            allow := True;
            DoCanSizeRow(res.Row, allow);
            if allow then
              Cursor := crSizeNS
          end
          else if (colsz and (sx <= obj.Position.X - 1.5) and (sy > obj.Position.Y + 1.5) and (sy < obj.Position.Y + obj.Height - 1.5)) then
          begin
            allow := True;
            DoCanSizeColumn(res.Col - 1, allow);
            if allow then
              Cursor := crSizeWE
          end
          else if (colsz and (sx >= obj.Position.X + obj.Width - 1.5) and (sy > obj.Position.Y + 1.5) and (sy < obj.Position.Y + obj.Height - 1.5)) then
          begin
            allow := True;
            DoCanSizeColumn(res.Col, allow);
            if allow then
              Cursor := crSizeWE
          end
          else
          begin
            mx := X - cellContainer.Position.X - obj.Position.X;
            my := Y - GetHeaderHeight - cellContainer.position.Y - obj.Position.Y;
            if (obj is TTMSFMXGridCell) then
            begin
              if (obj as TTMSFMXGridCell).IsHTML then
                FCellAnchorString := (obj as TTMSFMXGridCell).XYToAnchor(mx, my)
              else
              begin
                FCellAnchorString := (obj as TTMSFMXGridCell).Text;
                if not IsUrl(FCellAnchorString) then
                  FCellAnchorString := '';
              end;

              if FCellAnchorString <> '' then
                Cursor := crHandPoint
              else
                Cursor := crDefault;
            end;
          end;
        end;
      end;
    end
    else
    begin
      if (FDragCell.Col <> -1) and (FDragCell.Col <> -1) then
      begin
        HideEdit;
        case FSizeMode of
          smColumnSizing:
          begin
            newsz := ColumnWidths[FDragCell.Col] + (FX - FSizeX);
            DoColumnSize(FDragCell.Col, newsz);
            ColumnWidths[FDragCell.Col] := newsz;
          end;
          smRowSizing:
          begin
            newsz := RowHeights[FDragCell.Row] + (FY - FSizeY);
            DoRowSize(FDragCell.Row, newsz);
            RowHeights[FDragCell.Row] := newsz;
          end;
          smPreviousColumnSizing:
          begin
            newsz := ColumnWidths[FDragCell.Col - 1] + (FX - FSizeX);
            DoColumnSize(FDragCell.Col - 1, newsz);
            ColumnWidths[FDragCell.Col - 1] := newsz;
          end;
          smPreviousRowSizing:
          begin
            newsz := RowHeights[FDragCell.Row - 1] + (FY - FSizeY);
            DoRowSize(FDragCell.Row - 1, newsz);
            RowHeights[FDragCell.Row - 1] := newsz;
          end;
        end;


        FSizeX := FX;
        FSizeY := FY;
        UpdateGrid(True);
        FSizing := True;
      end;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
var
  res: TCell;
  Allow: Boolean;
  obj: TControl;
  mx: Single;
  my: Single;
  fxd: Boolean;
  cellcontainer: TControl;
begin
  inherited;

  if FSizing then
  begin
    case FSizeMode of
      smColumnSizing: DoColumnSized(FDragCell.Col, ColumnWidths[FDragCell.Col]);
      smRowSizing: DoRowSized(FDragCell.Row, RowHeights[FDragCell.Row]);
      smPreviousColumnSizing: DoColumnSized(FDragCell.Col - 1, ColumnWidths[FDragCell.Col - 1]);
      smPreviousRowSizing: DoRowSized(FDragCell.Row - 1, RowHeights[FDragCell.Row - 1]);
    end;
    UpdateGrid;
  end;

  ReleaseCapture;
  FScrollTimer.Enabled := False;
  FDragTimer.Enabled := False;

  if Assigned(FDragBitmap) then
  begin
    RemoveObject(FDragBitmap);
    FDragBitmap := nil;
  end;

  if not (Button = TMouseButton.mbLeft) then
    Exit;

  if not FUpdatingGrid and FMouseUp then
  begin
    res := XYToCell(X, Y - GetHeaderHeight);
    if (res.Col <> -1) and (res.Row <> -1) then
    begin
      obj := GetCellObject(GetDisplayCell(res));
      cellcontainer := GetCellContainer;
      if Assigned(obj) and Assigned(cellcontainer) then
      begin
        mx := X - cellContainer.Position.X - obj.Position.X;
        my := Y - GetHeaderHeight - cellContainer.position.Y - obj.Position.Y;
        FCellAnchor.Col := -1;
        FCellAnchor.Row := -1;
        if (obj is TTMSFMXGridCell) and not FMouseDragging then
        begin
          if (obj as TTMSFMXGridCell).IsHTML then
            FCellAnchorString := (obj as TTMSFMXGridCell).XYToAnchor(mx, my)
          else
          begin
            FCellAnchorString := (obj as TTMSFMXGridCell).Text;
            if not IsUrl(FCellAnchorString) then
              FCellAnchorString := '';
          end;

          if FCellAnchorString <> '' then
            FCellAnchor := res;
        end;
      end;

      if (FCellAnchor.Col = -1) and (FCellAnchor.Row = -1) and (FCellAnchorString = '') then
      begin
        fxd := IsFixed(res.Col, res.Row);
        case SelectionMode of
          smCellRange:
          begin
            if (fcsAll in Options.Mouse.FixedCellSelection) and (res.Col < FixedColumns) and (res.Row < FixedRows) then
              fxd := False;
          end;
          smSingleRow: if (fcsRow in Options.Mouse.FixedCellSelection) then fxd := False;
          smSingleColumn: if (fcsColumn in Options.Mouse.FixedCellSelection) then fxd := False;
          smRowRange: if (fcsRowRange in Options.Mouse.FixedCellSelection) then fxd := False;
          smColumnRange: if (fcsColumnRange in Options.Mouse.FixedCellSelection) then fxd := False;
          smDisjunctRow: if (fcsDisjunctRow in Options.Mouse.FixedCellSelection) then fxd := False;
          smDisjunctColumn: if (fcsDisjunctColumn in Options.Mouse.FixedCellSelection) then fxd := False;
        end;

        if not fxd and not FDragFixed and not FTouchScrolling then
        begin
          SelectCell(res, Shift, FMouseDragging);
          DoCellClick(res.Col, res.Row);

          if not FMouseDragging and Options.Mouse.DirectEdit then
          begin
            DoCanEditCell(FocusedCell.Col, FocusedCell.Row, Allow);
            if Allow then
              EditCell(FocusedCell);
          end;
        end
        else
        begin
          if FDraggingMode = dmColumnDragging then
          begin
            BeginUpdate;
            MoveColumn(FDragCell.Col, res.Col);
            EndUpdate;
            DoColumnDragged(FDragCell.Col, res.Col);
          end
          else
          if FDraggingMode = dmRowDragging then
          begin
            BeginUpdate;
            MoveRow(FDragCell.Row, res.Row);
            EndUpdate;
            DoRowDragged(FDragCell.Row, res.Row);
          end
          else
            DoFixedCellClick(Shift, res.Col, res.Row);
        end;
      end
      else
        DoCellAnchorClick(FCellAnchor.Col, FCellAnchor.Row, FCellAnchorString);
    end;
  end;

  FTouchScrolling := False;
  FDraggingMode := dmNone;
  FDragFixed := False;
  FFirstCell.Row := -1;
  FFirstCell.Col := -1;
  FMouseDown := False;
  FMouseUp := False;
  FMouseDragging := False;
  FMouseDblClick := FMouseSizing;
  FMouseSizing := False;
  FSizing := False;
end;

procedure TTMSFMXCustomGrid.MouseWheel(Shift: TShiftState; WheelDelta: Integer;
  var Handled: Boolean);
var
  sel: TCell;
  vscroll, hscroll: TScrollBar;
begin
  inherited;
  if (ssCtrl in Shift) or (Options.Mouse.WheelScrollKeepSelection) then
  begin
    vscroll := GetVerticalScrollBar;
    hscroll := GetHorizontalScrollBar;
    if Assigned(vscroll) and Assigned(hscroll) then
    begin
      if WheelDelta > 0 then
        TopRow := TopRow - Options.Mouse.WheelScrollSize
      else
        TopRow := TopRow + Options.Mouse.WheelScrollSize;
    end;
  end
  else
  begin
    sel := FocusedCell;
    if WheelDelta > 0 then
      sel.Row := sel.Row - Options.Mouse.WheelScrollSize
    else
      sel.Row := sel.Row + Options.Mouse.WheelScrollSize;

    SelectCell(sel, Shift);
  end;

  Handled := True;
end;

procedure TTMSFMXCustomGrid.Navigate(ACell: TCell; ForceScroll: Boolean = False);
var
  hs, vs, rhs, rvs, vss, hss: Single;
  cellwrapper: TControl;
  hscroll, vscroll: TScrollbar;
  cellContainer: TControl;
  totalh, totalw: Single;
  cs, rs: Integer;
  c, r: Integer;
  toth, totw: Single;
  cl: TCell;
begin
  if BlockUpdate then
    Exit;


  cellwrapper := GetCellWrapper;
  hscroll := GetHorizontalScrollBar;
  vscroll := GetVerticalScrollBar;
  cellContainer := GetCellContainer;

  if Assigned(cellwrapper) and assigned(hscroll) and assigned(vscroll) and Assigned(cellContainer) then
  begin
    vs := vscroll.Value;
    hs := hscroll.Value;

    vss := vs;
    hss := hs;

    if ScrollMode = smCellScrolling then
    begin
      vss := -cellContainer.Position.Y;
      hss := -cellContainer.Position.X;
    end;

    totalw := 0;
    totalh := 0;
    ACell := BaseCell(ACell.Col, ACell.Row);
    cs := ColSpan(ACell.Col, ACell.Row);
    rs := RowSpan(ACell.Col, ACell.Row);
    if cs > 0 then
    begin
      for c := ACell.Col to ACell.Col + cs - 1 do
        totalw := totalw + ColumnWidths[c];
    end
    else
      totalw := ColumnWidths[ACell.Col];

    if rs > 0 then
    begin
      for r := ACell.Row to ACell.Row + rs - 1 do
        totalh := totalh + RowHeights[r];
    end
    else
      totalh := RowHeights[ACell.Row];

    rvs := GetVerticalPos(0, ACell.Row);
    if ForceScroll then
    begin
      case ScrollMode of
        smPixelScrolling: vs := rvs - GetFixedHeight;
        smCellScrolling: vs := ACell.Row - FixedRows - FreezeRows;
      end;
    end
    else
    begin
      if (rvs < vss + GetFixedHeight) then
      begin
        case ScrollMode of
          smPixelScrolling: vs := rvs - GetFixedHeight;
          smCellScrolling: vs := (ACell.Row - FixedRows - FreezeRows);
        end;
      end
      else
      begin
        case ScrollMode of
          smPixelScrolling:
          begin
            if (rvs + totalh > vss + cellWrapper.Height - GetFixedFooterHeight) then
              vs := rvs + totalh - cellWrapper.Height + GetFixedFooterHeight;
          end;
          smCellScrolling:
          begin
            while (rvs + totalh > vss + cellWrapper.Height - GetFixedFooterHeight) do
            begin
              toth := 0;
              vs := vs + 1;
              cl := BaseCell(ACell.Col, Round(vs));
              rs := RowSpan(cl.Col, cl.Row);
              if rs > 0 then
              begin
                for r := cl.Row to cl.Row + rs - 1 do
                  toth := toth + RowHeights[r];
              end
              else
              begin
                rs := 1;
                toth := RowHeights[cl.Row];
              end;

              vs := vs + rs - 1;
              vss := vss + toth;
            end;
          end;
        end;
      end;
    end;

    rhs := GetHorizontalPos(0, ACell.Col);
    if ForceScroll then
    begin
      case ScrollMode of
        smPixelScrolling: hs := rhs - GetFixedWidth;
        smCellScrolling: hs := ACell.Col - FixedColumns - FreezeColumns;
      end;
    end
    else
    begin
      if (rhs < hss + GetFixedWidth) then
      begin
        case ScrollMode of
          smPixelScrolling: hs := rhs - GetFixedWidth;
          smCellScrolling: hs := ACell.Col - FixedColumns - FreezeColumns;
        end;
      end
      else
      begin
        case ScrollMode of
          smPixelScrolling:
          begin
            if (rhs + totalw > hss + cellWrapper.Width - GetFixedRightWidth) then
              hs := rhs + totalw - cellWrapper.Width + GetFixedRightWidth;
          end;
          smCellScrolling:
          begin
            while (rhs + totalw > hss + cellWrapper.Width - GetFixedRightWidth) do
            begin
              totw := 0;
              hs := hs + 1;
              cl := BaseCell(Round(hs), ACell.Row);
              cs := ColSpan(cl.Col, cl.Row);
              if cs > 0 then
              begin
                for c := cl.Col to cl.Col + cs - 1 do
                  totw := totw + ColumnWidths[c];
              end
              else
              begin
                cs := 1;
                totw := ColumnWidths[cl.Col];
              end;

              hs := hs + cs - 1;
              hss := hss + totw;
            end;
          end;
        end;
      end;
    end;


    Scroll(vs, hs);
  end;
end;


procedure TTMSFMXCustomGrid.NextPage;
var
  c: TCell;
begin
  c := FocusedCell;
  FocusedCell := NextSelectableRow(c.Col, c.Row + GetPageScrollSize - 1);
end;

procedure TTMSFMXCustomGrid.PreviousPage;
var
  c: TCell;
begin
  c := FocusedCell;
  FocusedCell := NextSelectableRow(c.Col, c.Row - GetPageScrollSize + 1);
end;

{$IFNDEF ANDROID}
procedure TTMSFMXCustomGrid.Print(ACanvas: TCanvas = nil);
begin
  DoPrint(CellRange(0, 0, ColumnCount - 1, RowCount - 1), [], '', ACanvas);
end;

procedure TTMSFMXCustomGrid.PrintPage(APageIndex: Integer = 1; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
begin
  DoPrint(CellRange(0, 0, ColumnCount - 1, RowCount - 1), [APageIndex], '', ACanvas, AResetPosition, AWidth, AHeight);
end;

procedure TTMSFMXCustomGrid.PrintPageFromTo(APageFrom, APageTo: Integer; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
var
  I: Integer;
  pages: array of Integer;
begin
  for I := 0 to APageTo - APageFrom  do
  begin
    SetLength(pages, Length(Pages) + 1);
    pages[Length(pages) - 1] := I + APageFrom;
  end;

  DoPrint(CellRange(0, 0, columnCount - 1, Rowcount - 1), pages, '', ACanvas, AResetPosition, AWidth, AHeight);
end;

procedure TTMSFMXCustomGrid.PrintPageFromToSelection(ASelection: TCellRange;
  APageFrom, APageTo: Integer; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
var
  I: Integer;
  pages: array of Integer;
begin
  for I := 0 to APageTo - APageFrom do
  begin
    SetLength(pages, Length(Pages) + 1);
    pages[Length(pages) - 1] := I + APageFrom;
  end;

  DoPrint(ASelection, pages, '', ACanvas, AResetPosition, AWidth, AHeight);
end;

procedure TTMSFMXCustomGrid.PrintPages(APageIndexes: array of Integer; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
begin
  DoPrint(CellRange(0, 0, ColumnCount - 1, RowCount - 1), APageIndexes, '', ACanvas, AResetPosition, AWidth, AHeight);
end;

procedure TTMSFMXCustomGrid.PrintPageSelection(ASelection: TCellRange;
  APageIndex: Integer = 1; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
begin
  DoPrint(ASelection, [APageIndex], '', ACanvas, AResetPosition, AWidth, AHeight);
end;

procedure TTMSFMXCustomGrid.PrintPageSelectionToImage(ASelection: TCellRange; AFileName: String; APageIndex: Integer = 1; AWidth: Integer = -1; AHeight: Integer = -1);
begin
  DoPrint(ASelection, [APageIndex], AFileName, nil, False, AWidth, AHeight);
end;

procedure TTMSFMXCustomGrid.PrintPagesSelection(ASelection: TCellRange;
  APageIndexes: array of Integer; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False;AWidth: Integer = -1; AHeight: Integer = -1);
begin
  DoPrint(ASelection, APageIndexes, '', ACanvas, AResetPosition, AWidth, AHeight);
end;

procedure TTMSFMXCustomGrid.PrintPageToImage(AFileName: String; APageIndex: Integer = 1; AWidth: Integer = -1; AHeight: Integer = -1);
begin
  DoPrint(CellRange(0, 0, ColumnCount - 1, RowCount - 1), [APageIndex], AFileName, nil, False, AWidth, AHeight);
end;

procedure TTMSFMXCustomGrid.PrintSelection(ASelection: TCellRange; ACanvas: TCanvas = nil; AResetPosition: Boolean = False; AWidth: Integer = -1; AHeight: Integer = -1);
begin
  DoPrint(ASelection, [], '', ACanvas, AResetPosition, AWidth, AHeight);
end;

{$ENDIF}

procedure TTMSFMXCustomGrid.ProcessTab(var Key: Word; Shift: TShiftState; Mode: TTMSFMXGridTabKeyDirection; Handling: TTMSFMXGridTabKeyHandling; DirectEdit: Boolean; CellStart: TEditCellStart = csNone);
var
  cl, rl: TCell;
  c, r: integer;
  Allow: Boolean;
begin
  if (Key = vkTab) then
  begin
    HideEdit;
    if IsFocused then
    begin
      case Handling of
        tkhNextCell: Key := 0;
        tkhMixed:
        begin
          if ssShift in Shift then
          begin
            cl.Col := FixedColumns;
            cl.Row := FixedRows;

            if IsNormalFixed(cl.Col, cl.Row) then
            begin
              case Mode of
                tkdNextColumnCell: cl := NextSelectableColumn(cl.Col, cl.Row);
                tkdNextRowCell: cl := NextSelectableRow(cl.Col, cl.Row);
              end;
            end;

            if not ((FocusedCell.Col = cl.Col) and (FocusedCell.Row = cl.Row)) then
              Key := 0
          end
          else
          begin
            cl.Col := ColumnCount - 1 - FixedRightColumns;
            cl.Row := RowCount - 1 - FixedFooterRows;

            if IsNormalFixed(cl.Col, cl.Row) then
            begin
              case Mode of
                tkdNextColumnCell: cl := PreviousSelectableColumn(cl.Col, cl.Row);
                tkdNextRowCell: cl := PreviousSelectableRow(cl.Col, cl.Row);
              end;
            end;

            if not ((FocusedCell.Col = cl.Col) and (FocusedCell.Row = cl.Row)) then
              Key := 0
          end;
        end
      end;

      if Key = 0 then
      begin
        case Handling of
          tkhNextCell, tkhMixed:
          begin
            cl := FocusedCell;
            rl := FRealCell;
            cl := BaseCell(cl.Col, cl.Row);

            if ssShift in Shift then
            begin
              case Mode of
                tkdNextColumnCell:
                begin
                  c := cl.Col;
                  cl := PreviousSelectableColumn(cl.Col, cl.Row);
                  cl.Row := rl.Row;
                  if (c = cl.Col) then
                  begin
                    r := cl.row;
                    cl.Col := ColumnCount - 1 - FixedRightColumns;
                    cl := PreviousSelectableColumn(cl.Col, cl.Row);
                    if r = cl.Row then
                    begin
                      cl := PreviousSelectableColumn(ColumnCount - FixedRightColumns, cl.Row - 1);
                      while (cl.Col = ColumnCount - FixedRightColumns) and (cl.Row = r - 1) do
                      begin
                        r  := cl.Row;
                        cl := PreviousSelectableColumn(ColumnCount - FixedRightColumns, cl.Row - 1);
                      end;
                    end;
                    if (Handling = tkhNextCell) and ((cl.Row < FixedRows) or (r = cl.row)) then
                    begin
                      cl.Col := ColumnCount - 1 - FixedRightColumns;
                      cl.Row := RowCount - 1 - FixedFooterRows;
                      cl := PreviousSelectableColumn(cl.Col, cl.Row);
                    end;
                  end;
                end;
                tkdNextRowCell:
                begin
                  r := cl.Row;
                  cl := PreviousSelectableRow(cl.Col, cl.Row);
                  cl.Col := rl.Col;
                  if (r = cl.Row) then
                  begin
                    c := cl.Col;
                    cl.Row := RowCount - 1 - FixedFooterRows;
                    cl := PreviousSelectableRow(cl.Col, cl.Row);
                    if c = cl.Col then
                    begin
                      cl := PreviousSelectableRow(cl.Col - 1, RowCount - FixedFooterRows);
                      while (cl.Row = RowCount - FixedFooterRows) and (cl.Col = c - 1) do
                      begin
                        c  := cl.Col;
                        cl := PreviousSelectableRow(cl.Col - 1, RowCount - FixedFooterRows);
                      end;
                    end;
                    if (Handling = tkhNextCell) and ((cl.Col < FixedColumns) or (c = cl.Col)) then
                    begin
                      cl.Col := ColumnCount - 1 - FixedRightColumns;
                      cl.Row := RowCount - 1 - FixedFooterRows;
                      cl := PreviousSelectableRow(cl.Col, cl.Row);
                    end;
                  end;
                end;
              end;
              SelectCell(cl);
              if DirectEdit then
              begin
                Allow := True;
                DoCanEditCell(FocusedCell.Col, FocusedCell.Row, Allow);
                if Allow then
                  EditCell(FocusedCell, CellStart);
              end;
            end
            else
            begin
              case Mode of
                tkdNextColumnCell:
                begin
                  c := cl.Col;
                  cl := NextSelectableColumn(cl.Col, cl.Row);
                  cl.Row := rl.Row;
                  if (c = cl.Col) then
                  begin
                    r := cl.row;
                    cl.Col := FixedColumns;
                    cl := NextSelectableColumn(cl.Col, cl.Row);
                    if r = cl.Row then
                    begin
                      cl := NextSelectableColumn(FixedColumns - 1, cl.Row + 1);
                      while (cl.Col = FixedColumns - 1) and (cl.Row = r + 1) do
                      begin
                        r := cl.Row;
                        cl := NextSelectableColumn(FixedColumns - 1, cl.row + 1);
                      end;
                    end;

                    if (Handling = tkhNextCell) and ((cl.Row > RowCount - 1 - FixedFooterRows) or (r = cl.row)) then
                    begin
                      cl.Col := FixedColumns;
                      cl.Row := FixedRows;
                      cl := NextSelectableColumn(cl.Col, cl.Row);
                    end;
                  end;
                end;
                tkdNextRowCell:
                begin
                  r := cl.Row;
                  cl := NextSelectableRow(cl.Col, cl.Row);
                  cl.Col := rl.Col;
                  if (r = cl.Row) then
                  begin
                    c := cl.Col;
                    cl.Row := FixedRows;
                    cl := NextSelectableRow(cl.Col, cl.Row);
                    if c = cl.Col then
                    begin
                      cl := NextSelectableRow(cl.Col + 1, FixedRows - 1);
                      while (cl.Row = FixedRows - 1) and (cl.Col = c + 1) do
                      begin
                        c := cl.Col;
                        cl := NextSelectableRow(cl.Col + 1, FixedRows - 1);
                      end;
                    end;
                    if (Handling = tkhNextCell) and ((cl.Col > ColumnCount - 1 - FixedRightColumns) or (c = cl.Col)) then
                    begin
                      cl.Col := FixedColumns;
                      cl.Row := FixedRows;
                      cl := NextSelectableRow(cl.Col, cl.Row);
                    end;
                  end;
                end;
              end;
              SelectCell(cl);
              if DirectEdit then
              begin
                Allow := True;
                DoCanEditCell(FocusedCell.Col, FocusedCell.Row, Allow);
                if Allow then
                  EditCell(FocusedCell, CellStart);
              end;
            end;
          end;
        end;
      end;
    end
    else
    begin
      if Handling <> tkhNextControl then
      begin
        if ssShift in Shift then
        begin
          cl.Col := ColumnCount - 1 - FixedRightColumns;
          cl.Row := RowCount - 1 - FixedFooterRows;
          if IsNormalFixed(cl.Col, cl.Row) then
          begin
           case Mode of
               tkdNextColumnCell: cl := PreviousSelectableColumn(cl.Col, cl.Row);
              tkdNextRowCell: cl := PreviousSelectableRow(cl.Col, cl.Row);
            end;
          end;
          SelectCell(cl);
        end
        else
        begin
          cl.Col := FixedColumns;
          cl.Row := FixedRows;
          if IsNormalFixed(cl.Col, cl.Row) then
          begin
            case Mode of
             tkdNextColumnCell: cl := NextSelectableColumn(cl.Col, cl.Row);
               tkdNextRowCell: cl := NextSelectableRow(cl.Col, cl.Row);
            end;
          end;
          SelectCell(cl)
        end;
      end;
    end;
  end;
end;

{$IFNDEF ANDROID}
procedure TTMSFMXCustomGrid.DoPrint(ASelection: TCellRange; APages: array of Integer; AFileName: String = ''; ACanvas: TCanvas = nil;  AResetPosition: Boolean = False; AWidth: Integer = -1; AHeight: Integer = -1);

function CanPrintPage(APageIndex: Integer): Boolean;
  var
    I: Integer;
begin
  Result := False;
  if Length(APages) = 0 then
    Result := True
  else
  begin
    for I := 0 to Length(APages) - 1 do
    begin
      if APages[I] = APageIndex then
      begin
        Result := True;
        Break;
      end;
    end;
  end;
end;

var
  cell: TFMXObject;
  cellclasstype,prevclasstype: TFmxObjectClass;
  l: TTMSFMXGridCellLayout;
  c, r: Integer;
  cellstr: String;
  cwr, rhr, cw, rh, cwrt, rhrt: Single;
  I: Integer;
  xr, yr: Single;
  bc, br: Integer;
  cs, rs: Integer;
  cellr, cellrtxt: TRectF;
  x, y: Single;
  ctl: TTMSFMXGridCell;
  evnt: TNotifyEvent;
  bmp: TBitmap;
  AllowDraw, AllowDrawbmp: Boolean;
  APageIndex: Integer;
  AProgress: Single;
  rSt, csave, cSt: Integer;
  chk: Boolean;
  FFirstPage: Boolean;
  rDrawText: TRectF;
  rText: TRectF;
  FFixedRowsSave, FFixedColumnsSave: Integer;
  FCSave, FRSave: Integer;
  st: TCellState;
  nml, fix: TTMSFMXGridCell;
  FFixedRowGet, FFixedColGet: Boolean;
  FSetRSave, FSetCSave: Boolean;
  j: Integer;
  xs, ys: Single;
  ph, pw: Single;
  xts, yts: Single;
  FSaveXBounds: Single;
  FSaveYBounds: Single;
  w, h: Integer;
  AControl: TFMXObject;
  dpi: TPoint;
  sx, sy: Single;
  bmpx, bmpy: Single;
  sm: TMatrix;
begin
  dpi := GetPrintDPI;

  sx := dpi.X / 72;
  sy := dpi.Y / 72;

  sx := Max(1, sx);
  sy := Max(1, sy);

  nml := GetDefaultNormalLayout;
  fix := GetDefaultFixedLayout;
  
  APageIndex := 1;
  AProgress := 0;

  if (AFileName = '') and (ACanvas = nil) then
    Printer.Orientation := Options.Printing.PrinterOrientation;

  if (AFileName = '') and (ACanvas = nil) then
    DoPrintBegin(Printer)
  else
    DoPrintBegin(nil);

  if (AFileName = '') and (ACanvas = nil) then
  begin
    Printer.Orientation := Options.Printing.PrinterOrientation;
    DoPrintBegin(Printer);
    Printer.BeginDoc;
    if CanPrintPage(APageIndex) then
      DoPrintNewPage(APageIndex, Printer);
  end;

  w := Printer.PageWidth;
  h := Printer.PageHeight;

  if (AWidth > -1) then
    w := AWidth;

  if (AHeight > -1) then
    h := AHeight;

  bmp := TBitmap.Create(w, h);
  bmp.Canvas.BeginScene;
  bmp.Canvas.Fill.Color := claWhite;
  {$IFNDEF DELPHIXE5_LVL}
  sm := CreateScaleMatrix(sx, sy);
  {$ENDIF}
  {$IFDEF DELPHIXE5_LVL}
  sm := TMatrix.Identity;
  sm.m11 := sx;
  sm.m22 := sy;
  {$ENDIF}
  bmp.Canvas.SetMatrix(sm);
  bmpx := bmp.Width / sx;
  bmpy := bmp.Height / sy;

  AllowDrawbmp := True;
  if CanPrintPage(APageIndex) then
    DoPrintBeforeDraw(APageIndex, bmp.Canvas, RectF(0, 0, bmp.Width, bmp.Height), AllowDrawbmp);

  evnt := bmp.Canvas.Font.OnChanged;
  bmp.Canvas.Font.OnChanged := nil;

  x := Options.Printing.PrintMargins.Left;
  y := Options.Printing.PrintMargins.Top;

  rDrawText := RectF(Options.Printing.PrintMargins.Left + 2, 2, bmp.Width - Options.Printing.PrintMargins.Right - 2, bmp.Height - 2);

  if AllowDrawbmp then
    bmp.Canvas.FillRect(RectF(0, 0, bmp.Width, bmp.Height), 0, 0, AllCorners, 1);

  rText := rDrawText;
  DrawTitle(APageIndex, bmp.Canvas, rText);

  rText := rDrawText;
  DrawDescription(APageIndex, bmp.Canvas, rText);

  rText := rDrawText;
  DrawPageNumber(APageIndex, bmp.Canvas, rText);

  l := TTMSFMXGridCellLayout.Create(nil);
  prevclasstype := TTMSFMXGridCell;
  cell := TTMSFMXGridCell.Create(Self);
  rSt := ASelection.StartRow;
  FFixedRowsSave := 0;
  FFixedColumnsSave := 0;
  cSave := -1;
  cSt := -1;
  FFirstPage := True;
  FFixedRowGet := False;
  FFixedColGet := False;
  FSetRSave := True;
  FSetCSave := True;


  FSaveXBounds := Options.Printing.PrintMargins.Left + GetFixedPrintWidth;

  for I := ASelection.StartCol to ASelection.EndCol do
  begin
    FSaveXBounds := FSaveXBounds + ColumnWidths[I];
    if (FSaveXBounds + ColumnWidths[I] > bmpx - Options.Printing.PrintMargins.Right) then
      Break;
  end;

  FSaveYBounds := Options.Printing.PrintMargins.Top + GetFixedPrintHeight;

  for I := ASelection.StartRow to ASelection.EndRow do
  begin
    if (FSaveYBounds + RowHeights[I] > bmpy - Options.Printing.PrintMargins.Bottom) then
      Break;

    FSaveYBounds := FSaveYBounds + RowHeights[I];
  end;
    
  c := ASelection.StartCol;


  for i := ASelection.StartCol to ASelection.EndCol do
    for j := ASelection.StartRow to ASelection.EndRow do 
      MergeCellPrintPageNr[i,j] := -1;
  

  pw := GetFixedPrintWidth;  
  ph := GetFixedPrintHeight;

  FCSave := -1;
  while c <= ASelection.EndCol do
  begin
    if cSt = -1 then
      cSt := c;

    if Options.Printing.RepeatFixedColumns and (FixedColumns > 0) then
    begin      
      if (FFixedColumnsSave < FixedColumns) then
      begin
        FFixedColGet := True;
        if FSetCSave then          
        begin
          FSetCSave := False;
          FCSave := cSt;
        end;
            
        cst := FFixedColumnsSave;
        c := cst;
        Inc(FFixedColumnsSave);
      end
      else if FFixedColGet then             
      begin
        FFixedColGet := False;          
        cSt := FCSave;
        c := FCSave;

        if cst < FixedColumns then
        begin
          cst := FixedColumns;
          c := cst;
        end;
      end;
    end;      

    cw := ColumnWidths[c];

    r := rst;
    FRSave := rst;
    while r <= ASelection.EndRow do
    begin
      if Options.Printing.RepeatFixedRows and (FixedRows > 0) then
      begin      
        if (FFixedRowsSave < FixedRows) then
        begin
          FFixedRowGet := True;
          if FSetRSave then
          begin
            FSetRSave := False;
            FRSave := rSt;
          end;
            
          rSt := FFixedRowsSave;
          r := rst;
          Inc(FFixedRowsSave);
        end
        else if FFixedRowGet then             
        begin
          FFixedRowGet := False;          
          rSt := FRSave;
          r := FRSave;

          if rst < FixedRows then
          begin
            rst := FixedRows;
            r := rst;
          end;
        end;
      end;

      bc := c;
      br := r;
      cs := 0;
      rs := 0;

      rh := RowHeights[r];

      DoGetCellMergeInfo(c, r, bc, br, cs, rs);

      chk := (c = bc) and (r = br);
      if chk then
        MergeCellPrintPageNr[bc, br] := APageIndex
      else if (bc <> c) and (br <> r) then           
        chk := MergeCellPrintPageNr[bc, br] <> APageIndex;
                                                               
      if chk then
      begin
        AControl := nil;
        DoGetCellControl(bc, br, AControl);
        DoGetCellClass(bc,  br, cellclasstype);
        DoGetCellData(bc, br, cellstr);

        if (prevclasstype <> cellclasstype) and (prevclasstype <> nil) and Assigned(cell) then
          cell.Free;

        if (prevclasstype <> cellclasstype) then
          cell := cellclasstype.Create(Self);

        prevclasstype := cellclasstype;

        ApplyCellState(bc, br, cell);

        if (Cell is TTMSFMXGridCell) then
        begin
          if Assigned(nml) and Assigned(fix) then          
          begin
            if Options.Printing.PrintCellBackGround <> cbFull then
            begin
              st := GetCellState(bc, br);
              case st of
                csFocused: (cell as TTMSFMXGridCell).Layout.Assign(nml.Layout);
                csFixedSelected: (cell as TTMSFMXGridCell).Layout.Assign(fix.Layout);
                csSelected: (cell as TTMSFMXGridCell).Layout.Assign(nml.Layout);
              end;
            end;
          end;
                                                                                       
          l.Assign((Cell as TTMSFMXGridCell).Layout);
          DoGetCellLayout(bc, br, l, GetCellState(bc, br));
          (Cell as TTMSFMXGridCell).AssignLayout(l);
        end;

        DoGetCellAppearance(bc, br, cell, GetCellState(c, r));
        DoGetCellProperties(bc, br, cell);

        cwr := cw;
        rhr := rh;

        cwrt := 0;
        for I := bc to bc + cs - 1 do
          cwrt := cwrt + ColumnWidths[I];

        if cwrt > 0 then
          cwr := cwrt;

        rhrt := 0;
        for I := br to br + rs - 1 do
          rhrt := rhrt + RowHeights[I];

        if rhrt > 0 then
          rhr := rhrt;

        xr := 0;
        for I := bc to c - 1 do
          xr := xr + ColumnWidths[I];

        yr := 0;
        for I := br to r - 1 do
          yr := yr + RowHeights[I];


        xts := x - xr;
        yts := y - yr;
        xs := xts + cwr;
        ys := yts + rhr;

        if FSaveXBounds > -1 then
          xs := Min(FSaveXBounds, xs);

        if ((APageIndex = 0) or (Options.Printing.RepeatFixedColumns)) and (c > FixedColumns) then
          xts := Max(Options.Printing.PrintMargins.Left + pw, xts)
        else
          xts := Max(Options.Printing.PrintMargins.Left, xts);


        if FSaveYBounds > -1 then
          ys := Min(FSaveYBounds, ys);


        if ((APageIndex = 0) or (Options.Printing.RepeatFixedRows)) and (r > FixedRows) then
          yts := Max(Options.Printing.PrintMargins.Top + ph, yts)
        else
          yts := Max(Options.Printing.PrintMargins.Top, yts);

        cellr := RectF(xts, yts, xs, ys);

        if cell is TTMSFMXGridCell then
        begin
          ctl := (Cell as TTMSFMXGridCell);
          ctl.Width := cellr.Width;
          ctl.Height := cellr.Height;
          ctl.Text := cellstr;
          if Options.Printing.PrintCellBackGround <> cbFull then
          begin
            ctl.Layout.Stroke.Color := claBlack;
            {$IFDEF DELPHIXE6_LVL}
            ctl.Layout.StrokeDash := TStrokeDash.Solid;
            ctl.Layout.Sides := [TSide.Right, tSide.Bottom];
            {$ELSE}
            ctl.Layout.StrokeDash := TStrokeDash.sdSolid;
            ctl.Layout.Sides := [TSide.sdRight, tSide.sdBottom];
            {$ENDIF}
            if Options.Printing.PrintCellBackGround = cbNone then
              ctl.Layout.FontFill.Color := claBlack;
          end;

          {$IFDEF DELPHIXE6_LVL}
          if Options.Printing.PrintCellBackGround <> cbFull then
          begin
            if Options.Printing.RepeatFixedColumns and (FixedColumns > 0) then
            begin
              if (c = 0) then
                ctl.Layout.Sides := ctl.Layout.Sides + [tSide.Left];
            end
            else
            begin
              if (c = cst) then
                ctl.Layout.Sides := ctl.Layout.Sides + [tSide.Left];
            end;

            if Options.Printing.RepeatFixedRows and (FixedRows > 0) then
            begin
              if (r = 0) then
                ctl.Layout.Sides := ctl.Layout.Sides + [tSide.Top];
            end
            else
            begin
              if (r = rst) then
                ctl.Layout.Sides := ctl.Layout.Sides + [tSide.Top];
            end;
          end;
          {$ELSE}
          if Options.Printing.PrintCellBackGround <> cbFull then
          begin
            if Options.Printing.RepeatFixedColumns and (FixedColumns > 0) then
            begin
              if (c = 0) then
                ctl.Layout.Sides := ctl.Layout.Sides + [tSide.sdLeft];
            end
            else
            begin
              if (c = cst) then
                ctl.Layout.Sides := ctl.Layout.Sides + [tSide.sdLeft];
            end;

            if Options.Printing.RepeatFixedRows and (FixedRows > 0) then
            begin
              if (r = 0) then
                ctl.Layout.Sides := ctl.Layout.Sides + [tSide.sdTop];
            end
            else
            begin
              if (r = rst) then
                ctl.Layout.Sides := ctl.Layout.Sides + [tSide.sdTop];
            end;
          end;
          {$ENDIF}
        end;

        if Assigned(bmp) then
          if CanPrintPage(APageIndex) then
            DoPrintBeforeDrawCell(APageIndex, bmp.Canvas, cellr, bc, br, cell);

        AllowDraw := True;
        if Assigned(bmp) then
          if CanPrintPage(APageIndex) then
            DoPrintDrawCell(APageIndex, bmp.Canvas,cellr, bc, br, cell, AllowDraw);

        if AllowDraw then
        begin
          if (cell is TTMSFMXGridCell) then
          begin
            ctl := (Cell as TTMSFMXGridCell);
            TControlProtected(ctl).Scale.X := sx;
            TControlProtected(ctl).Scale.Y := sy;
            cellrtxt := ctl.GetTextRectBase(cellr);
            AddObject(ctl);
            if Assigned(bmp) then
              ctl.Print(bmp.Canvas, cellr, cellrtxt, True, Options.Printing.PrintCellBackGround <> cbNone, sx, sy);

            if Assigned(AControl) and (AControl is TControl) then
              (AControl as TControl).PaintTo(bmp.Canvas, cellr);

            RemoveObject(ctl);
          end
          else if cell is TControl then
          begin
            if Assigned(bmp) then
              (cell as TControl).PaintTo(bmp.Canvas, cellr);
          end;
        end;

        if Assigned(bmp) then
          if CanPrintPage(APageIndex) then
            DoPrintAfterDrawCell(APageIndex, bmp.Canvas,cellr, bc, br, cell);
      end;

      AProgress := AProgress + 1;
      DoPrintProgress(APageIndex, Printer, (AProgress / ((ASelection.EndCol - ASelection.StartCol + 1) * (ASelection.EndRow - ASelection.StartRow + 1))) * 100);

      y := y + rh;
      if Assigned(bmp) then
      begin
        if (y + RowHeights[r + 1] > bmpy - Options.Printing.PrintMargins.Bottom) then
        begin
          FSaveYBounds := Y + RowHeights[r + 1];
          Break;
        end;
      end;

      Inc(r);        
    end;

    y := Options.Printing.PrintMargins.Top;
    x := x + cw;
    Inc(c);

    FFixedRowsSave := 0;

    if not Assigned(bmp) or (Assigned(bmp) and (x + ColumnWidths[c] > bmpx - Options.Printing.PrintMargins.Right) or (c = ASelection.EndCol + 1)) then
    begin
      FSaveXBounds := x;

      if Assigned(bmp) then
      begin
        if CanPrintPage(APageIndex) then
          DoPrintAfterDraw(APageIndex, bmp.Canvas, RectF(0, 0, bmp.Width, bmp.Height));

        bmp.Canvas.EndScene;

        if CanPrintPage(APageIndex) then
        begin
          if AFileName = '' then
          begin
            if ACanvas <> nil then
              ACanvas.DrawBitmap(bmp, RectF(0, 0, bmp.Width, bmp.Height), RectF(0, 0, bmp.Width, bmp.Height), 1);
            begin
              Printer.Canvas.DrawBitmap(bmp, RectF(0, 0, bmp.Width, bmp.Height), RectF(0, 0, bmp.Width, bmp.Height), 1);
              DoPrintEndPage(APageIndex, Printer);
            end;
          end
          else
            bmp.SaveToFile(AFileName);
        end;
      end;

      if Assigned(bmp) then
      begin
        bmp.Free;
        bmp := nil;
      end;

      rst := r + 1;

      if cSave = -1 then
        cSave := c;

      if r = ASelection.EndRow + 1 then
      begin
        c := cSave;
        cst := -1;
        rst := ASelection.StartRow;
        cSave := -1;
      end
      else
        c := cst;       

      FFixedRowsSave := 0;
      FFixedColumnsSave := 0;
      FSetRSave := True;
      FSetCSave := True;

      if (c < ASelection.EndCol + 1) then
      begin
        Inc(APageIndex);
        if CanPrintPage(APageIndex) and (AFileName = '') and (ACanvas = nil) then
        begin
          if FFirstPage then
          begin
            FFirstPage := False;
            if CanPrintPage(1) then
              Printer.NewPage
          end
          else
            Printer.NewPage;

          DoPrintNewPage(APageIndex, Printer);
        end;

        bmp := TBitmap.Create(w, h);
        bmp.Canvas.BeginScene;
        bmp.Canvas.Fill.Color := claWhite;
        {$IFNDEF DELPHIXE5_LVL}
        sm := CreateScaleMatrix(sx, sy);
        {$ENDIF}
        {$IFDEF DELPHIXE5_LVL}
        sm := TMatrix.Identity;
        sm.m11 := sx;
        sm.m22 := sy;
        {$ENDIF}

        bmp.Canvas.SetMatrix(sm);
        bmpx := bmp.Width / sx;
        bmpy := bmp.Height / sy;

        AllowDrawbmp := True;
        if CanPrintPage(APageIndex) then
          DoPrintBeforeDraw(APageIndex, bmp.Canvas, RectF(0, 0, bmp.Width, bmp.Height), AllowDrawbmp);

        evnt := bmp.Canvas.Font.OnChanged;
        bmp.Canvas.Font.OnChanged := nil;

        if (ACanvas = nil) or AResetPosition then
        begin
          x := Options.Printing.PrintMargins.Left;
          y := Options.Printing.PrintMargins.Top;
        end;

        if AllowDrawbmp then
          bmp.Canvas.FillRect(RectF(0, 0, bmp.Width, bmp.Height), 0, 0, AllCorners, 1);

        rText := rDrawText;
        DrawTitle(APageIndex, bmp.Canvas, rText);

        rText := rDrawText;
        DrawDescription(APageIndex, bmp.Canvas, rText);

        rText := rDrawText;
        DrawPageNumber(APageIndex, bmp.Canvas, rText);
      end;
    end;
  end;


  if Assigned(cell) then
    cell.Free;

  l.Free;

  if (AFileName = '') and (ACanvas = nil) then
    Printer.EndDoc;                               

  if (AFileName = '') and (ACanvas = nil) then
    DoPrintEnd(Printer)
  else
    DoPrintEnd(nil);

  if Assigned(bmp) then
  begin
    bmp.Canvas.EndScene;
    bmp.Free;
  end;
end;
{$ENDIF}

procedure TTMSFMXCustomGrid.RemoveAllCells;
var
  cellcontainer: TControl;
  I,J: Integer;
  ctl: TFMXObject;
begin
  cellcontainer := GetCellContainer;

  if Assigned(cellcontainer) then
  begin
    for I := cellcontainer.ChildrenCount - 1 downto 0 do
    begin
      ctl := cellContainer.Children[I];

      if Assigned(ctl) and (ctl.StyleName <> 'grideditcontrol') then
      begin
        if ctl is TTMSFMXContainerGridCell then
        begin
          for J := 0 to ctl.ChildrenCount - 1 do
          begin
            ctl.RemoveObject(ctl.Children[J]);
          end;
        end;
        ctl.DisposeOf;
      end;
    end;
  end;
end;

procedure TTMSFMXCustomGrid.Resize;
begin
  BlockUpdate := True;
  BeginUpdate;
  inherited;
  EndUpdate;
  BlockUpdate := False;
  UpdateGrid(True);
end;

procedure TTMSFMXCustomGrid.Scroll(VerticalValue, HorizontalValue: Single);
var
  vscrollbar, hscrollbar: TScrollBar;
  valx, valy: Single;
  cellContainer, cellwrapper: TControl;
begin
  vscrollbar := GetVerticalScrollBar;
  hscrollbar := GetHorizontalScrollBar;
  cellContainer := GetCellContainer;
  cellWrapper := GetCellWrapper;

  if Assigned(cellcontainer) and assigned(cellwrapper) and Assigned(vscrollbar) and Assigned(hscrollbar) then
  begin
    valx := vscrollbar.Value;
    valy := hscrollbar.Value;
    BlockUpdate := True;
    if cellcontainer.Height > cellwrapper.Height then
      vscrollbar.Value := VerticalValue
    else
      vscrollbar.Value := 0;

    if cellcontainer.Width > cellwrapper.Width then
      hscrollbar.Value := HorizontalValue
    else
      hscrollbar.Value := 0;

    BlockUpdate := False;
    if (valx <> vscrollbar.Value) or (valy <> hscrollbar.Value) then
      UpdateGridCells
    else
      UpdateGridCellAppearance;
  end;
end;

procedure TTMSFMXCustomGrid.ScrollBarVChanged(Sender: TObject);
begin
  if FReset then
    Exit;

  UpdateGridCells;
end;

procedure TTMSFMXCustomGrid.ScrollBarHChanged(Sender: TObject);
begin
  if FReset then
    Exit;
  UpdateGridCells;
end;

procedure TTMSFMXCustomGrid.ScrollTimer(Sender: TObject);
var
  c: TCell;
  cellwrapper: TControl;
begin
  if not Options.Mouse.AutoScrolling then
  begin
    FScrollTimer.Enabled := False;
    Exit;
  end;

  cellWrapper := GetCellWrapper;
  if (FX >= cellWrapper.Width - GetFixedRightWidth) then
  begin
    c := FocusedCell;
    c.Col := c.Col + Round(FX - (cellwrapper.Width - GetFixedRightWidth)) * Options.Mouse.AutoScrollingSpeed;
    SetFocusCell(c.Col, c.Row);
    SelectCell(FocusedCell, [], FMouseDragging);
  end
  else if FX <= GetFixedWidth then
  begin
    c := FocusedCell;
    c.Col := c.Col + Round(FX - GetFixedWidth) * Options.Mouse.AutoScrollingSpeed;
    SetFocusCell(c.Col, c.Row);
    SelectCell(FocusedCell, [], FMouseDragging);
  end;

  if (FY >= cellWrapper.Height - GetFixedFooterHeight) then
  begin
    c := FocusedCell;
    c.Row := c.Row + Round(FY - (cellwrapper.Height - GetFixedFooterHeight)) * Options.Mouse.AutoScrollingSpeed;
    SetFocusCell(c.Col, c.Row);
    SelectCell(FocusedCell, [], FMouseDragging);
  end
  else if FY <= GetFixedHeight then
  begin
    c := FocusedCell;
    c.Row := c.Row + Round(FY - GetFixedHeight) * Options.Mouse.AutoScrollingSpeed;
    SetFocusCell(c.Col, c.Row);
    SelectCell(FocusedCell, [], FMouseDragging);
  end;
end;

procedure TTMSFMXCustomGrid.SelectCell(Cell: TCell; Shift: TShiftState = []; MouseDragging: Boolean = False);
var
  sta, stc: TCell;
  rs, cs, rsstart, csstart: Integer;
  bs: TCell;
  cst, rst: Integer;
  ic, ir: Integer;
  I, J: Integer;
  doUpdate: Boolean;
  Allow: Boolean;
  key: Word;
  k: Char;
  cntr, cntc, cntcl: Integer;
begin
  inherited;
  FPrevSelection := Selection;

  if (Cell.Row < FixedRows) and (Cell.Col < FixedColumns) and (fcsAll in Options.Mouse.FixedCellSelection) then
  begin
    //select all
    k := #31;
    key := Ord('A');
    KeyDown(key,k, [ssCtrl]);
    Exit;
  end;

  Cell.Row := Max(FixedRows, Min(Cell.Row, RowCount - 1 - FixedFooterRows));
  Cell.Col := Max(FixedColumns, Min(Cell.Col, ColumnCount - 1 - FixedRightColumns));

  Allow := True;
  if Assigned(OnSelectCell) then
    OnSelectCell(Self, Cell.Col, Cell.Row, Allow);

  if not Allow then
    Exit;

  FPrevSelectedCell := FocusedCell;
  FRealCell := Cell;
  Cell := BaseCell(Cell.Col, Cell.Row);
  SetFocusCell(Cell.Col, Cell.Row);
  case SelectionMode of
    smSingleCell:
    begin
      SetStartCell(FocusedCell.Col, FocusedCell.Row);
      SetStopCell(FocusedCell.Col, FocusedCell.Row);
    end;
    smSingleRow:
    begin
      SetStartCell(FixedColumns, FocusedCell.Row);
      SetStopCell(ColumnCount - 1 - FixedRightColumns, FocusedCell.Row);
    end;
    smSingleColumn:
    begin
      SetStartCell(FocusedCell.Col, FixedRows);
      SetStopCell(FocusedCell.Col, RowCount - 1 - FixedFooterRows);
    end;
    smCellRange, smRowRange, smColumnRange, smDisjunctRow, smDisjunctColumn, smDisjunctCell:
    begin
      doUpdate := True;
      if (not MouseDragging or (FFirstCell.Row = -1) or (FFirstCell.Col = -1)) and not (ssShift in Shift) and not (ssCtrl in shift) then
      begin
        SetStartCell(FRealCell.Col, FocusedCell.Row);
        SetStopCell(FocusedCell.Col, FocusedCell.Row);
        FFirstCell := StartCell;
        FSaveCell := StartCell;
      end
      else if (ssShift in Shift) and ((FSaveCell.Row = -1) or (FSaveCell.Col = -1)) then
      begin
        SetStartCell(FocusedCell.Col, FocusedCell.Row);
        SetStopCell(FocusedCell.Col, FocusedCell.Row);
        FSaveCell := StartCell;
      end
      else if (ssCtrl in Shift) and ((SelectionMode = smDisjunctRow) or (SelectionMode = smDisjunctColumn) or (SelectionMode = smDisjunctCell)) then
      begin
        case SelectionMode of
          smDisjunctRow:
          begin
            if FocusedCell.Row < StartCell.Row then
            begin
              SetStartCell(FRealCell.Col, FocusedCell.Row);
              FFirstCell := StopCell;
              FSaveCell := StopCell;
            end
            else if FocusedCell.Row > StopCell.Row then
            begin
              SetStopCell(FocusedCell.Col, FocusedCell.Row);
              FFirstCell := StartCell;
              FSaveCell := StartCell;
            end
            else
              doUpdate := False;
          end;
          smDisjunctColumn:
          begin
            if FocusedCell.Col < StartCell.Col then
            begin
              SetStartCell(FocusedCell.Col, FRealCell.Row);
              FFirstCell := StopCell;
              FSaveCell := StopCell;
            end
            else if FocusedCell.Col > StopCell.Col then
            begin
              SetStopCell(FocusedCell.Col, FocusedCell.Row);
              FFirstCell := StartCell;
              FSaveCell := StartCell;
            end
            else
              doUpdate := False;
          end;
          smDisjunctCell:
          begin

            doUpdate := False;
            if FocusedCell.Col < StartCell.Col then
            begin
              if FocusedCell.Row < StartCell.Row then
                SetStartCell(FocusedCell.Col, FocusedCell.Row)
              else if FocusedCell.Row > StopCell.Row then
                SetStopCell(FocusedCell.Col, FocusedCell.Row)
              else if (FocusedCell.Row >= StartCell.Row) or (FocusedCell.Row <= StopCell.Row) then
                SetStartCell(FocusedCell.Col, StartCell.Row);
            end
            else if FocusedCell.Col > StopCell.Col then
            begin
              if FocusedCell.Row < StartCell.Row then
                SetStartCell(FocusedCell.Col, FocusedCell.Row)
              else if FocusedCell.Row > StopCell.Row then
                SetStopCell(FocusedCell.Col, FocusedCell.Row)
              else if (FocusedCell.Row >= StartCell.Row) or (FocusedCell.Row >= StopCell.Row) then
                SetStopCell(FocusedCell.Col, StopCell.Row);
            end
            else if (FocusedCell.Col >= StartCell.Col) and (FocusedCell.Col <= StopCell.Col) then
            begin
              if FocusedCell.Row < StartCell.Row then
                SetStartCell(StartCell.Col, FocusedCell.Row)
              else if FocusedCell.Row > StopCell.Row then
                SetStopCell(StopCell.Col, FocusedCell.Row);
            end
          end;
        end;
      end;

      if (ssShift in Shift) then
        FFirstCell := FSaveCell;

      if doUpdate then
      begin
        if (FocusedCell.Col < FFirstCell.Col) then
        begin
          SetStartCell(FocusedCell.Col, StartCell.Row);
          SetStopCell(FFirstCell.Col, StopCell.Row);
        end
        else
        begin
          SetStopCell(FocusedCell.Col, StopCell.Row);
          SetStartCell(FFirstCell.Col, StartCell.Row);
        end;

        if (FocusedCell.Row < FFirstCell.Row) then
        begin
          SetStartCell(StartCell.Col, FocusedCell.Row);
          SetStopCell(StopCell.Col, FFirstCell.Row);
        end
        else
        begin
          SetStopCell(StopCell.Col, FocusedCell.Row);
          SetStartCell(StartCell.Col, FFirstCell.Row);
        end;
      end;

      case SelectionMode of
        smDisjunctRow, smDisjunctColumn, smDisjunctCell:
        begin
          if not (ssCtrl in Shift) and not (ssShift in Shift) then
          begin
            SetStartCell(FocusedCell.Col, FocusedCell.Row);
            SetStopCell(FocusedCell.Col, FocusedCell.Row);
            FSaveCell := FocusedCell;
          end;
        end;
      end;

      case SelectionMode of
        smRowRange, smDisjunctRow:
        begin
          SetStartCell(FixedColumns, StartCell.Row);
          SetStopCell(ColumnCount - 1 - FixedRightColumns, StopCell.Row);
        end;
        smColumnRange, smDisjunctColumn:
        begin
          SetStartCell(StartCell.Col, FixedRows);
          SetStopCell(StopCell.Col, RowCount - 1 - FixedFooterRows);
        end;
      end;
    end;
  end;

  stc := StopCell;
  sta := StartCell;

  if StopCell.Col < StartCell.Col then
  begin
    SetStopCell(sta.Col, StopCell.Row);
    SetStartCell(stc.Col, StartCell.Row);
  end;

  if StopCell.Row < StartCell.Row then
  begin
    SetStopCell(StopCell.Col, sta.Row);
    SetStartCell(StartCell.Col, stc.Row);
  end;

  Navigate(FocusedCell);

  cs := StopCell.Col;
  csstart := StartCell.Col;
  rs := StopCell.Row;
  rsstart := StartCell.Row;
  for ic := Max(FCStart, csstart) to Min(FCStop, cs) do
  begin
    for ir := Max(FRStart, rsstart) to Min(FRStop, rs) do
    begin
      bs := BaseCell(ic, ir);
      cst := ColSpan(bs.Col, bs.Row);
      if bs.Col + cst - 1 > cs then
        cs := bs.Col + cst - 1;

      if bs.Col < csstart then
        csstart := bs.Col;

      rst := RowSpan(bs.Col, bs.Row);
      if bs.Row + rst - 1 > rs then
        rs := bs.Row + rst - 1;

      if bs.Row < rsstart then
        rsstart := bs.Row;
    end;
  end;

  SetStopCell(cs,rs);
  SetStartCell(csstart, rsstart);

  case SelectionMode of
    smDisjunctRow:
    begin
      BlockUpdate := True;

      if not (ssCtrl in Shift) then
        ClearRowSelect;

      if ssShift in Shift then
      begin
        for I := StartCell.Row to StopCell.Row do
          RowSelect[I] := not RowSelect[I];
      end
      else
      begin
        RowSelect[FocusedCell.Row] := not RowSelect[FocusedCell.Row];
        cntr := GetSelectedRowCount;
        if not RowSelect[FocusedCell.Row] and (cntr = 0) then
          RowSelect[FocusedCell.Row] := True;
      end;

      if not RowSelect[FocusedCell.Row] then
        for I := StartCell.Row to StopCell.Row do
          if (I <> FocusedCell.Row) and (RowSelect[I]) then
            SetFocusCell(FocusedCell.Col, I);

      BlockUpdate := False;
    end;
    smDisjunctColumn:
    begin
      BlockUpdate := True;

      if not (ssCtrl in Shift) then
        ClearColumnSelect;

      if ssShift in Shift then
      begin
        for I := StartCell.Col to StopCell.Col do
          ColumnSelect[I] := not ColumnSelect[I];
      end
      else
      begin
        ColumnSelect[FocusedCell.Col] := not ColumnSelect[FocusedCell.Col];
        cntc := GetSelectedColumnCount;
        if not ColumnSelect[FocusedCell.Col] and (cntc = 0) then
          ColumnSelect[FocusedCell.Col] := True;
      end;

      if not ColumnSelect[FocusedCell.Col] then
        for I := StartCell.Col to StopCell.Col do
          if (I <> FocusedCell.Col) and (ColumnSelect[I]) then
            SetFocusCell(I, FocusedCell.Row);

      BlockUpdate := False;
    end;
    smDisjunctCell:
    begin
      BlockUpdate := True;

      if not (ssCtrl in Shift) then
        ClearCellSelect;

      if ssShift in Shift then
      begin
        for I := StartCell.Col to StartCell.Row do
          for J := StartCell.Row to StopCell.Row do
            CellSelect[I, J] := not CellSelect[I, J];
      end
      else
      begin
        CellSelect[FocusedCell.Col, FocusedCell.Row] := not CellSelect[FocusedCell.Col, FocusedCell.Row];
        cntcl := GetSelectedCellCount;
        if not CellSelect[FocusedCell.Col, FocusedCell.Row] and (cntcl = 0) then
          CellSelect[FocusedCell.Col, FocusedCell.Row] := True;
      end;

      if not CellSelect[FocusedCell.Col, FocusedCell.Row] then
        for I := StartCell.Col to StartCell.Row do
          for J := StartCell.Row to StopCell.Row do
            if (I <> FocusedCell.Col) and (J <> FocusedCell.Row) and CellSelect[I, J] then
              SetFocusCell(I, J);

      BlockUpdate := False;
    end;
  end;

  Navigate(FocusedCell);
end;

procedure TTMSFMXCustomGrid.SetFixedShowSelection(const Value: Boolean);
begin
  if FFixedShowSelection <> Value then
  begin
    FFixedShowSelection := Value;
    UpdateGrid;
  end;
end;

procedure TTMSFMXCustomGrid.SetFreezeColumns(const Value: Integer);
begin
  if FFreezeColumns <> Value then
  begin
    FFreezeColumns := Value;
    UpdateGrid;
  end;
end;

procedure TTMSFMXCustomGrid.SetFreezeRows(const Value: Integer);
begin
  if FFreezeRows <> Value then
  begin
    FFreezeRows := Value;
    UpdateGrid;
  end;
end;

procedure TTMSFMXCustomGrid.SetLeftCol(const Value: Integer);
var
  cl: TCell;
begin
  cl.Row := Max(FixedRows, Min(FRStart, RowCount - 1 - FixedFooterRows));
  cl.Col := Max(FixedColumns, Min(Value, ColumnCount - 1 - FixedRightColumns));

  Navigate(cl,  True);
end;

procedure TTMSFMXCustomGrid.SetScrollMode(const Value: TTMSFMXGridScrollMode);
begin
  if FScrollMode <> Value then
  begin
    FScrollMode := Value;
    FReset := True;
    UpdateGrid;
    FReset := False;
  end;
end;

procedure TTMSFMXCustomGrid.SetTopRow(const Value: Integer);
var
  cl: TCell;
begin
  cl.Row := Max(FixedRows, Min(Value, RowCount - 1 - FixedFooterRows));
  cl.Col := Max(FixedColumns, Min(FCStart, ColumnCount - 1 - FixedRightColumns));
  Navigate(cl, True);
end;

procedure TTMSFMXCustomGrid.SetVersion(const Value: String);
begin

end;

procedure TTMSFMXCustomGrid.UpdateFooter;
var
  f: TControl;
begin
  f := GetFooter;
  if Assigned(f) then
    f.Visible := Options.Footer.Visible;
end;

procedure TTMSFMXCustomGrid.UpdateGrid(Update: Boolean = False);
var
  cellContainer, cellWrapper: TControl;
  scrollbarv, scrollbarh: TScrollBar;
  cl: TCell;
begin
  if (UpdateCount > 0) or BlockUpdate or (csDestroying in ComponentState) then
    Exit;

  FUpdatingGrid := True;

  cellContainer := GetCellContainer;
  cellWrapper := GetCellWrapper;
  scrollbarv := GetVerticalScrollBar;
  scrollbarh := GetHorizontalScrollBar;
  if Assigned(cellContainer) and Assigned(cellWrapper) and Assigned(scrollbarv) and Assigned(scrollbarh) then
  begin
    if not Update then
    begin
      if (cellContainer is TStyledControl) then
      begin
        (cellContainer as TStyledControl).NeedStyleLookUp;
        (cellContainer as TStyledControl).Repaint;
      end;
    end;

    cellContainer.Width := GetTotalColumnWidth;
    cellContainer.Height := GetTotalRowHeight;

    scrollbarh.Visible := (cellcontainer.Width > cellWrapper.Width) and Options.ScrollBar.HorizontalScrollBarVisible;

    scrollbarv.Visible  := (cellcontainer.Height > cellWrapper.Height) and Options.ScrollBar.VerticalScrollBarVisible;
    if scrollbarv.Visible then
      scrollbarh.Margins.Right := scrollbarv.Width
    else
      scrollbarh.Margins.Right := 0;

    scrollbarh.Visible := (cellcontainer.Width > cellWrapper.Width) and Options.ScrollBar.HorizontalScrollBarVisible;

//    if scrollbarv.Visible then
    begin
      if ScrollMode = smCellScrolling then
      begin
        scrollbarv.Max := RowCount - FixedRows - FreezeRows - FixedFooterRows;
        scrollbarv.ViewportSize := GetRowViewPortSize;
      end
      else
      begin
        scrollbarv.Max := cellcontainer.Height;
        scrollbarv.ViewPortSize := cellWrapper.Height;
      end;
    end;

//    if scrollbarh.Visible then
    begin
      if ScrollMode = smCellScrolling then
      begin
        scrollbarh.Max := ColumnCount - FixedColumns - FreezeColumns - FixedRightColumns;
        scrollbarh.ViewportSize := GetColumnViewPortSize;
      end
      else
      begin
        scrollbarh.Max := cellcontainer.Width;
        scrollbarh.ViewPortSize := cellWrapper.Width;
      end;
    end;

    if not Update then
      RemoveAllCells;

    if FReset then
    begin
      scrollbarv.Value := 0;
      scrollbarh.Value := 0;
    end;

    if (ColumnCount > FixedColumns) and (RowCount > FixedRows) then
    begin
      if not FirstCellApply then
      begin
        cl.Col := FixedColumns;
        cl.Row := FixedRows;
        if IsNormalFixed(cl.Col, cl.Row) then
          cl := NextSelectableColumn(cl.Col, cl.Row);

        SetStartCell(cl.Col, cl.Row);
        SetStopCell(cl.Col, cl.Row);
        SetFocusCell(cl.Col, cl.Row);
        FPrevSelectedCell := FocusedCell;
        FSaveCell := FocusedCell;
        FFirstCell.Col := -1;
        FFirstCell.Row := -1;
        FRealCell := FocusedCell;
        FPrevSelection := Selection;
        case SelectionMode of
          smRowRange, smSingleRow, smDisjunctRow: SetStopCell(ColumnCount - 1 - FixedRightColumns, StopCell.Row);
          smColumnRange, smSingleColumn, smDisjunctColumn: SetStopCell(StopCell.Col, RowCount - 1 - FixedFooterRows);
        end;

        BlockUpdate := True;
        case SelectionMode of
          smDisjunctRow: RowSelect[FocusedCell.Row] := True;
          smDisjunctColumn: ColumnSelect[FocusedCell.Col] := True;
          smDisjunctCell: CellSelect[FocusedCell.Col, FocusedCell.Row] := True;
         end;
        BlockUpdate := False;

        FirstCellApply := True;
      end;
    end
    else
    begin
      cl.Col := -1;
      cl.Row := -1;
      StartCell := cl;
      StopCell := cl;
      FocCell := cl;

      FPrevSelectedCell := FocusedCell;
      FSaveCell := FocusedCell;
      FFirstCell.Col := -1;
      FFirstCell.Row := -1;
      FRealCell := FocusedCell;
      FPrevSelection := Selection;
    end;

    if not Update then
      SetLength(FCellArray, 0, 0);
    UpdateGridCells;
  end;

  FUpdatingGrid := False;
end;

procedure TTMSFMXCustomGrid.UpdateGridCellAppearance;
var
  cl: TCellRange;
begin
  if (UpdateCount > 0) or BlockUpdate or (csDestroying in ComponentState) then
    Exit;

  BlockUpdate := True;
  BeginUpdate;

  cl.StartCol := FPrevSelection.StartCol;
  cl.EndCol := FPrevSelection.EndCol;
  cl.StartRow := 0;
  cl.EndRow := FixedRows - 1;
  DoUpdateGridCellAppearance(cl);

  cl.StartCol := 0;
  cl.EndCol := FixedColumns - 1;
  cl.StartRow := FPrevSelection.StartRow;
  cl.EndRow := FPrevSelection.EndRow;
  DoUpdateGridCellAppearance(cl);

  cl.StartCol := FPrevSelection.StartCol;
  cl.EndCol := FPrevSelection.EndCol;
  cl.StartRow := RowCount - FixedFooterRows;
  cl.EndRow := RowCount - 1;
  DoUpdateGridCellAppearance(cl);

  cl.StartCol := ColumnCount - FixedRightColumns;
  cl.EndCol := ColumnCount - 1;
  cl.StartRow := FPrevSelection.StartRow;
  cl.EndRow := FPrevSelection.EndRow;
  DoUpdateGridCellAppearance(cl);

  DoUpdateGridCellAppearance(FPrevSelection);

  cl.StartCol := Selection.StartCol;
  cl.EndCol := Selection.EndCol;
  cl.StartRow := 0;
  cl.EndRow := FixedRows - 1;
  DoUpdateGridCellAppearance(cl);

  cl.StartCol := 0;
  cl.EndCol := FixedColumns - 1;
  cl.StartRow := Selection.StartRow;
  cl.EndRow := Selection.EndRow;
  DoUpdateGridCellAppearance(cl);

  cl.StartCol := Selection.StartCol;
  cl.EndCol := Selection.EndCol;
  cl.StartRow := RowCount - FixedFooterRows;
  cl.EndRow := RowCount - 1;
  DoUpdateGridCellAppearance(cl);

  cl.StartCol := ColumnCount - FixedRightColumns;
  cl.EndCol := ColumnCount - 1;
  cl.StartRow := Selection.StartRow;
  cl.EndRow := Selection.EndRow;
  DoUpdateGridCellAppearance(cl);

  DoUpdateGridCellAppearance(Selection);

//  if Assigned(FSaveTopCell) then
//    FSaveTopCell.BringToFront;

  EndUpdate;
  BlockUpdate := False;
end;

procedure TTMSFMXCustomGrid.UpdateGridCells;
var
  scrollbarv, scrollbarh: TScrollBar;
  cellContainer: TControl;
  cellbackground: TControl;
  vVal, hVal: Single;
  cw, rh: Single;
  x, y, xs, ys: Single;
  rBounds: TRectF;
  xOrig, yOrig: Single;
  scrollv, scrollh: Single;
  i,j: Integer;
  fw, fh, fwr, fhr: Single;
  cfs, rfs, cfst, rfst: Integer;
  hMax, vMax: Single;
  cl: TCell;
begin
  if (UpdateCount > 0) or BlockUpdate or (csDestroying in ComponentState) then
    Exit;

  FSaveC := nil;

  BlockUpdate := true;
  scrollbarv := GetVerticalScrollBar;
  scrollbarh := GetHorizontalScrollBar;
  cellContainer := GetCellContainer;
  cellbackground := GetCellWrapper;

  if Assigned(cellContainer) and Assigned(cellbackground) and Assigned(scrollbarv) and Assigned(scrollbarh) then
  begin
    BeginUpdate;

    //hide children
    for I := 0 to cellContainer.ChildrenCount - 1 do
      (cellContainer.Children[I] as TControl).Visible := False;

    vVal := scrollbarv.Value;
    hVal := scrollbarh.Value;
    hMax := scrollbarh.Max;
    vMax := scrollbarv.Max;
    scrollv := 0;
    scrollh := 0;

    if ScrollMode = smCellScrolling then
    begin
      hMax := GetTotalColumnWidth;
      vMax := GetTotalRowHeight;

      for I := FixedRows + FreezeRows to Round(vVal) - 1 + FixedRows + FreezeRows do scrollv := scrollv + RowHeights[I];
      for I := FixedColumns + FreezeColumns to Round(hVal) - 1 + FixedColumns + FreezeColumns do scrollh := scrollh + ColumnWidths[I];

      vVal := scrollv;
      hval := scrollh;
    end;

    cellContainer.Position.X := Round(-hVal);
    cellContainer.Position.Y := Round(-vVal);

    if (cellContainer.Width > 0) and (cellContainer.Height > 0) then
    begin
      cfs := GetFixedCols;
      rfs := GetFixedRows;
      cfst := FixedRightColumns;
      rfst := FixedFooterRows;

      FCStart := cfs;
      FRStart := rfs;

      rBounds := cellbackground.BoundsRect;
      fw := GetFixedWidth;
      fh := GetFixedHeight;
      fwr := GetFixedRightWidth;
      fhr := GetFixedFooterHeight;
      xOrig := 0;
      yOrig := 0;

      if hVal > hMax / 2 then
      begin
        FCStop := ColumnCount - 1 - cfst;
        x := cellContainer.Width - fwr;
        repeat
          cw := ColumnWidths[FCStop];
          if (x - cw > hVal + rBounds.Width - fwr) then
          begin
            x := x - cw;
            Dec(FCStop);
          end;
        until x - cw <= hVal + rBounds.Width - fwr;
      end
      else
      begin
        x := fw;
        repeat
          cw := ColumnWidths[FCStart];
          if (x + cw <= hVal + fw) then
          begin
            x := x + cw;
            inc(FCStart);
          end;
        until x + cw > hVal + fw;

        xOrig := x;
      end;

      if vval > vMax / 2 then
      begin
        FRStop := RowCount - 1 - rfst;
        y := CellContainer.Height - fhr;
        repeat
          rh := RowHeights[FRStop];
          if (y - rh > vVal + rBounds.Height - fhr) then
          begin
            y := y - rh;
            Dec(FRStop);
          end;
        until y - rh <= vVal + rBounds.Height - fhr;
      end
      else
      begin
        y := fh;
        repeat
          rh := RowHeights[FRStart];
          if (y + rh <= vVal + fh) then
          begin
            y := y + rh;
            inc(FRStart);
          end;
        until y + rh > vVal + fh;

        yOrig := y;
      end;

      if hval > hMax / 2 then
      begin
        FCStart := FCStop;
        while (FCStart > 0) and (x > hVal + fw) do
        begin
          x := x - ColumnWidths[FCStart];
          if not ((FCStart > 0) and (x > hVal + fw)) then
            Break;
          Dec(FCStart);
        end;

        xOrig := x;
      end
      else
      begin
        FCStop := FCStart;
        while (FCStop < ColumnCount - 1) and (x < hVal + rBounds.Width - fwr) do
        begin
          x := x + ColumnWidths[FCStop];
          if not ((FCStop < ColumnCount - 1) and (x < hVal + rBounds.Width - fwr)) then
            Break;
          Inc(FCStop);
        end;
      end;

      if vval > vMax / 2 then
      begin
        FRStart := FRStop;
        while (FRStart > 0) and (y > vVal + fh) do
        begin
          y := y - RowHeights[FRStart];
          if not ((FRStart > 0) and (y > vVal + fh)) then
            Break;
          Dec(FRStart);
        end;

        yOrig := y;
      end
      else
      begin
        FRStop := FRStart;
        while (FRStop < RowCount - 1) and (y < vVal + rBounds.Height - fhr) do
        begin
          y := y + RowHeights[FRStop];
          if not ((FRStop < RowCount - 1) and (y < vVal + rBounds.Height - fhr)) then
            Break;
          Inc(FRStop);
        end;
      end;

      FRStart := Max(FixedRows, Min(RowCount - 1 - FixedFooterRows, FRStart));
      FCStart := Max(FixedColumns, Min(ColumnCount - 1 - FixedFooterRows, FCStart));
      FRStop := Max(FixedRows, Min(RowCount - 1 - FixedFooterRows, FRStop));
      FCStop := Max(FixedColumns, Min(ColumnCount - 1 - FixedRightColumns, FCStop));

      x := xOrig;
      y := yOrig;

      xs := -cellContainer.Position.X;
      ys := -cellContainer.Position.Y;

      if (ColumnCount > FixedColumns) and (RowCount > FixedRows) then
        DisplayCells(cellContainer, x, y, x, y, FCStart, FCStop, FRStart, FRStop, cfs, rfs, rfs);

      if Assigned(FSaveC) then
        FSaveC.BringToFront;

      if RowCount > FixedRows then
        DisplayCells(cellContainer, xs, y, xs, y, 0, cfs - 1, FRStart, FRStop, 0, rfs, rfs, True);

      if ColumnCount > FixedColumns then
        DisplayCells(cellContainer, x, ys, x, ys, FCStart, FCStop, 0, rfs - 1, cfs, 0, 0, True);

      DisplayCells(cellContainer, xs, ys, xs, ys, 0, cfs - 1, 0, rfs - 1, 0, 0, 0, True);

      if (ColumnCount > FixedColumns + FixedRightColumns) and (RowCount > FixedRows) then
      begin
        DisplayCells(cellContainer, xs + cellbackground.Width - fwr, y, xs + cellbackground.width - fwr, y,
          ColumnCount - cfst, ColumnCount - 1, FRStart , FRStop, FCStop - FCStart + cfs + 1, rfs, rfs, True);

        DisplayCells(cellContainer, xs + cellBackground.Width - fwr, ys, xs + cellBackground.Width - fwr, ys,
          ColumnCount - cfst, ColumnCount - 1, 0, rfs - 1, FCStop - FCStart + cfs + 1, 0, 0, True);
      end;

      if (ColumnCount > FixedColumns) and (RowCount > FixedRows + FixedFooterRows) then
      begin
        DisplayCells(cellContainer, x, ys + cellbackground.Height - fhr, xs, ys + cellbackground.Height - fhr,
          FCStart, FCStop, RowCount - rfst, RowCount - 1, cfs, FRStop - FRStart + rfs + 1, FRStop - FRStart + rfs + 1, True);
      end;

      if (ColumnCount > FixedColumns + FixedRightColumns) and (RowCount > FixedRows + FixedFooterRows) then
      begin
        DisplayCells(cellContainer, xs + cellBackground.Width - fwr, ys + cellBackground.Height - fhr, xs + cellBackground.Width - fwr,
          ys + cellbackground.Height - fhr, ColumnCount - cfst, ColumnCount - 1, RowCount - rfst, RowCount - 1, FCStop - FCStart + cfs + 1 ,
         FRStop - FRStart + rfs + 1, FRStop - FRStart + rfs + 1, True);
      end;

      if RowCount > FixedRows then
      begin
        DisplayCells(cellContainer, xs, ys + cellBackground.Height - fhr, xs,  ys + cellbackground.Height - fhr,
          0, cfs - 1, RowCount - rfst, RowCount - 1, 0, FRStop - FRStart + rfs + 1, FRStop - FRStart + rfs + 1, True);
      end;

      for I := cellContainer.ChildrenCount - 1 downto 0 do
      begin
        if not (cellContainer.Children[I] as TControl).Visible and (cellContainer.Children[I] <> FEditControl) then
        begin
          cl := GetDisplayCellByObject(cellContainer.Children[I]);
          if (cl.Col <> -1) and (cl.Row <> -1) then
          begin
            if Assigned(FCellArray[cl.Col, cl.Row]) then
            begin
              if cellcontainer.Children[I] is TTMSFMXContainerGridCell then
              begin
                for J := 0 to cellcontainer.Children[I].ChildrenCount - 1 do
                  cellcontainer.Children[I].RemoveObject(cellcontainer.Children[I].Children[J]);
              end;

              FCellArray[cl.Col, cl.Row].DisposeOf;
              FCellArray[cl.Col, cl.Row] := nil;
            end;
          end;
        end;
      end;
    end;

    EndUpdate;
  end;

  if Assigned(FEditControl) and (FEditControl is TControl) then
  begin
    FEditControl.BringToFront;
    (FEditControl as TControl).Visible := True;
    (FEditControl as TControl).SetFocus;
  end;

  if (FTopr <> TopRow) or (FLeftC <> LeftCol) then
  begin
    if Assigned(OnTopLeftChanged) then
      OnTopLeftChanged(Self, TopRow, LeftCol);
  end;

  FTopr := TopRow;
  FLeftC := LeftCol;

  BlockUpdate := False;
end;

procedure TTMSFMXCustomGrid.UpdateHeader;
var
  h: TControl;
begin
  h := GetHeader;
  if Assigned(h) then
    h.Visible := Options.Header.Visible;
end;

function TTMSFMXCustomGrid.VisibleColumnCount: Integer;
begin
  Result := 0;
  if not Assigned(FCellArray) then
    Exit;

  Result := Length(FCellArray);
end;

function TTMSFMXCustomGrid.VisibleRowCount: Integer;
begin
  Result := 0;
  if not Assigned(FCellArray) then
    Exit;

  if Length(FCellArray) > 0 then
    Result := Length(FCellArray[0]);
end;

function TTMSFMXCustomGrid.XYToCell(X, Y: Single): TCell;
var
  cellContainer, cellWrapper: TControl;
  I: Integer;
  val: Single;
  cell: TFmxObject;
begin
  Result.Row := -1;
  Result.Col := -1;

  if (RowCount = 0) or (ColumnCount = 0) then
    Exit;

  cellContainer := GetCellContainer;
  cellWrapper := GetCellWrapper;
  if Assigned(cellContainer) and Assigned(cellWrapper) then
  begin
    if (Y < GetFixedHeight) then
    begin
      val := 0;
      for I := 0 to GetFixedRows do
      begin
        val := val + RowHeights[I];
        if val >= Y then
        begin
          Result.Row := I;
          Break;
        end;
      end;
    end
    else if (Y > cellwrapper.Height - GetFixedFooterHeight) then
    begin
      val := cellwrapper.Height;
      for I := RowCount - 1 downto RowCount - 1 - FixedFooterRows do
      begin
        val := val - RowHeights[I];
        if val <= Y then
        begin
          Result.Row := I;
          Break;
        end;
      end;
    end
    else
    begin
      if (GetFixedCols >= 0) and (GetFixedCols <= Length(FCellArray) - 1) then
      begin
        if (GetFixedRows >= 0) and (GetFixedRows <= Length(FCellArray[GetFixedCols]) - 1) then
        begin
          cell := FCellArray[GetFixedCols, GetFixedRows];
          if Assigned(cell) and (cell is TControl) then
          begin
            val := (cell as TControl).Position.Y + cellContainer.Position.Y;
            for I := FRStart to FRStop do
            begin
              val := val + RowHeights[I];
              if val >= Y then
              begin
                Result.Row := I;
                Break;
              end;
            end;
          end;
        end;
      end;
    end;

    if (X < GetFixedWidth) then
    begin
      val := 0;
      for I := 0 to GetFixedCols do
      begin
        val := val + ColumnWidths[I];
        if val >= X then
        begin
          Result.Col := I;
          Break;
        end;
      end;
    end
    else if (X > cellwrapper.Width - GetFixedRightWidth) then
    begin
      val := cellwrapper.Width;
      for I := ColumnCount - 1 downto ColumnCount - 1 - FixedFooterRows do
      begin
        val := val - ColumnWidths[I];
        if val <= X then
        begin
          Result.Col := I;
          Break;
        end;
      end;
    end
    else
    begin
      if (GetFixedCols >= 0) and (GetFixedCols <= Length(FCellArray) - 1) then
      begin
        if (GetFixedRows >= 0) and (GetFixedRows <= Length(FCellArray[GetFixedCols]) - 1) then
        begin
          cell := FCellArray[GetFixedCols, 0];
          if Assigned(cell) and (cell is TControl) then
          begin
            val := (cell as TControl).Position.X + cellContainer.Position.X;
            for I := FCStart to FCStop do
            begin
              val := val + ColumnWidths[I];
              if val >= X then
              begin
                Result.Col := I;
                Break;
              end;
            end;
          end;
        end;
      end;
    end;
  end;
end;

end.
