{********************************************************************}
{                                                                    }
{ written by TMS Software                                            }
{            copyright © 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.TMSTableView;

interface

{$I TMSDEFS.INC}

uses
  Classes, SysUtils, UITypes, Types, StrUtils, Math, FMX.Types,
  FMX.Objects, FMX.TMSBaseControl, FMX.Ani, FMX.Controls,
  FMX.TMSHTMLText, FMX.TMSBitmapContainer, FMX.TMSBitmap,
  FMX.TMSSearchEdit, FMX.TMSBarButton, FMX.TMSHTMLEngine,
  FMX.StdCtrls, UIConsts, FMX.Styles, Generics.Collections
  {$IFDEF DELPHIXE5_LVL}
  , FMX.Graphics
  {$ENDIF}
  ;


type
  TTMSFMXTableViewItemOption = (ioEditRectangle, ioLeftRectangle, ioCenterRectangle, ioRightRectangle,
    ioBulbRectangle, ioDetailRectangle, ioDeleteRectangle, ioCaptionElement, ioDescriptionElement);

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

  // version history
  // v1.0.0.0 : first release
  // v1.1.0.3 : New : Mousewheel scrolling Support
  //          : Fixed : Issue with detail events
  //          : Reorganized packages
  // v1.1.1.0 : Fixed: Issues in LiveBindings with large datasets and navigation
  //          : New : Livebindings support for additional elements with an TTMSFMXTableViewEx component.
  //          : New : support for update 4
  // v1.1.1.1 : Fixed : Issue with setting the focus on the form (Can be activated / deactivated with ShowActivated property) in TTMSFMXPopup
  // v1.1.2.0 : New : HTML support in TTMSFMXBarButton
  //          : Improved : TMSIOS define
  // v1.1.2.1 : Fixed : Issue with parent in TTMSFMXPopup
  // v1.1.2.2 : Fixed : Issue with order of freeing objects
  //          : New : support for update 4 hotfix 1
  // v1.1.2.3 : Fixed : Issue with state changing with mouse out of bounds in TTMSFMXSlider
  //          : Fixed : Issue with resizing detail view
  // v1.1.2.4 : Fixed : Issue with alignment and resizing of detail view container when item has no detail view assigned
  // v1.1.2.5 : Fixed : Issue with scrolling when moving mouse outside view
  // v1.1.3.0 : New : OnSearchOpen / OnSearchClose called when swiping done or showing the filter with the ShowFilter property.
  //          : Fixed : Issue with scrolling after showing detail view
  // v1.1.3.1 : Fixed : Access violation when clicking edit button in TTMSFMXTableView
  //          : Fixed : Issue with OnDblClick not triggered
  //          : Fixed : Issue with popupmenu on background interfering with scrolling interaction
  //          : Fixed : Issue with selection persistence of items when scrolling
  // v1.1.3.2 : Fixed : Issue with memoryleak when reparenting items
  // v1.1.3.3 : Fixed : Issue with repainting header, footer, and edit buttons text
  //          : Fixed : Selection changed notification to observer in LiveBindings
  // v1.1.3.4 : Fixed : Issue with bitmap assignment
  // v1.5.0.0 : New : support for XE4
  // v1.5.0.1 : Fixed : Issue with displaying DetailView
  // v1.5.0.2 : Fixed : Issue on iOS with categories
  // v1.5.0.3 : Fixed : Issue with assign procedure collection item
  // v1.5.0.4 : Fixed : Issue with TableView resizing
  // v1.5.0.5 : Improved : Assign handling
  // v1.5.0.6 : Fixed : Issue with items return with incorrect CategoryID
  // v1.5.0.7 : Fixed : Issue with resizing
  // v1.5.0.8 : Fixed : Issue with assignment of item an OnItemClick

  CLICKMARGIN = 4;
  SWIPEMARGIN = 7;
  AllItemOptions = [ioEditRectangle, ioCenterRectangle, ioDetailRectangle, ioDeleteRectangle, ioCaptionElement, ioDescriptionElement];

type
  TTMSFMXTableView = class;

  TTMSFMXTableViewItem = class;

  TTMSFMXTableViewCategoryShape = class(TRoundRect)
  private
    OriginalPos: TPointF;
    FCategoryID: Integer;
    FLookupItemIndex: Integer;
    FCharacter: String;
  public
    constructor Create(AOwner: TComponent); override;
    function ShapeCaption: TText;
    property CategoryID: Integer read FCategoryID write FCategoryID;
    property Character: String read FCharacter write FCharacter;
    property LookupItemIndex: Integer read FLookupItemIndex write FLookupItemIndex;
  published
    property Fill;
    property Stroke;
    property StrokeThickness;
    property StrokeCap;
    property StrokeDash;
    property StrokeJoin;
  end;

  TProtectedControl = class(TControl);

  TTMSFMXTableViewLookupBarShape = class(TRectangle)
  private
    FMouseDown: Boolean;
    FTableView: TTMSFMXTableView;
    FFont: TFont;
    FFontFill: TBrush;
    procedure SetFont(const Value: TFont);
    procedure SetFontFill(const Value: TBrush);
  protected
    function GetCharacter(Idx: Integer): String;
    function GetCharacterCount: Integer;
  public
    procedure ProcessLookUp(X, Y: Single; DoLookup: Boolean = True);
    function GetCategoryAtXY(X, Y: Single): Integer;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Paint; override;
    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 DoMouseLeave; override;
  published
    property Font: TFont read FFont write SetFont;
    property FontFill: TBrush read FFontFill write SetFontFill;
    property Fill;
    property Stroke;
    property StrokeThickness;
    property StrokeCap;
    property StrokeDash;
    property StrokeJoin;
  end;

  TTMSFMXTableViewScrollBarShape = class(TRectangle)
  private
    FPageSize: Single;
    FPosition: Single;
    FElementOpacity: Single;
    procedure SetPageSize(const Value: Single);
    procedure SetPosition(const Value: Single);
    procedure SetElementOpacity(const Value: Single);
  public
    constructor Create(AOwner: TComponent); override;
    procedure Paint; override;
    property PageSize: Single read FPageSize write SetPageSize;
    property Position: Single read FPosition write SetPosition;
  published
    property ElementOpacity: Single read FElementOpacity write SetElementOpacity;
    property Fill;
    property Stroke;
    property StrokeThickness;
    property StrokeCap;
    property StrokeDash;
    property StrokeJoin;
  end;

  TTMSFMXTableViewBulbShape = class(TRoundRect)
  private
    FFillSelected: TBrush;
    FStrokeSelected: TBrush;
    FCaptionColorSelected: TAlphaColor;
    FCaptionColor: TAlphaColor;
    FIsSelected: Boolean;
    FItem: TTMSFMXTableViewItem;
    procedure SetFillSelected(const Value: TBrush);
    procedure SetStrokeSelected(const Value: TBrush);
    procedure SetCaptionColor(const Value: TAlphaColor);
    procedure SetCaptionColorSelected(const Value: TAlphaColor);
    procedure SetIsSelected(const Value: Boolean);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Paint; override;
    procedure UpdateControls;
    function ShapeCaption: TText;
  published
    property StrokeSelected: TBrush read FStrokeSelected write SetStrokeSelected;
    property FillSelected: TBrush read FFillSelected write SetFillSelected;
    property CaptionColor: TAlphaColor read FCaptionColor write SetCaptionColor default claBlack;
    property CaptionColorSelected: TAlphaColor read FCaptionColorSelected write SetCaptionColorSelected default claWhite;
    property Fill;
    property Stroke;
    property StrokeThickness;
    property StrokeCap;
    property StrokeDash;
    property StrokeJoin;
    property Item: TTMSFMXTableViewItem read FItem write FItem;
    property IsSelected: Boolean read FIsSelected write SetIsSelected default False;
  end;

  TTMSFMXTableViewItemShape = class(TRoundRect)
  private
    FItem: TTMSFMXTableViewItem;
    FFillSelected: TBrush;
    FStrokeSelected: TBrush;
    FCaptionColorSelected: TAlphaColor;
    FDescriptionColorSelected: TAlphaColor;
    FCaptionColor: TAlphaColor;
    FDescriptionColor: TAlphaColor;
    FIsSelected: Boolean;
    FFillEdit: TBrush;
    FStrokeEdit: TBrush;
    FSides: TSides;
    procedure SetFillSelected(const Value: TBrush);
    procedure SetStrokeSelected(const Value: TBrush);
    procedure SetCaptionColor(const Value: TAlphaColor);
    procedure SetCaptionColorSelected(const Value: TAlphaColor);
    procedure SetDescriptionColor(const Value: TAlphaColor);
    procedure SetDescriptionColorSelected(const Value: TAlphaColor);
    procedure SetIsSelected(const Value: Boolean);
    procedure SetFillEdit(const Value: TBrush);
    procedure SetStrokeEdit(const Value: TBrush);
    procedure SetSides(const Value: TSides);
  protected
    procedure UpdateControls;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Paint; override;
    function ShapeCaption: TText;
    function ShapePlaceHolder: TRectangle;
    function ShapeDescription: TText;
    function ShapeLeftRectangle: TRectangle;
    function ShapeRightRectangle: TRectangle;
    function ShapeCenterRectangle: TRectangle;
    function ShapeLeftRectangleImage: TTMSFMXBitmap;
    function ShapeBulbRectangle: TTMSFMXTableViewBulbShape;
    function ShapeBulbText: TTMSFMXHTMLText;
    function ShapeEditRectangle: TRectangle;
    function ShapeDetailRectangle: TRectangle;
    function ShapeDeleteRectangle: TRectangle;
    function ShapeDeleteButton: TTMSFMXBarButton;
  published
    property StrokeSelected: TBrush read FStrokeSelected write SetStrokeSelected;
    property FillSelected: TBrush read FFillSelected write SetFillSelected;
    property StrokeEdit: TBrush read FStrokeEdit write SetStrokeEdit;
    property FillEdit: TBrush read FFillEdit write SetFillEdit;
    property CaptionColor: TAlphaColor read FCaptionColor write SetCaptionColor default claBlack;
    property CaptionColorSelected: TAlphaColor read FCaptionColorSelected write SetCaptionColorSelected default claWhite;
    property DescriptionColor: TAlphaColor read FDescriptionColor write SetDescriptionColor default $FFB7B7B7;
    property DescriptionColorSelected: TAlphaColor read FDescriptionColorSelected write SetDescriptionColorSelected default claSilver;
    property Fill;
    property Stroke;
    property StrokeThickness;
    property StrokeCap;
    property StrokeDash;
    property StrokeJoin;
    property Item: TTMSFMXTableViewItem read FItem write FItem;
    property IsSelected: Boolean read FIsSelected write SetIsSelected default False;
    {$IFDEF DELPHIXE6_LVL}
    property Sides: TSides read FSides write SetSides default [TSide.Bottom, TSide.Left, TSide.Right];
    {$ELSE}
    property Sides: TSides read FSides write SetSides default [TSide.sdBottom, TSide.sdLeft, TSide.sdRight];
    {$ENDIF}
  end;

  TTMSFMXTableViewCategoryType = (ctNone, ctAlphaBetic, ctAlphaNumericFirst, ctAlphaNumericLast, ctCustom);

  TTMSFMXTableViewScrollIndicator = (siNone, siFade, siPermanent);

  TTMSFMXTableViewSelectionMode = (smSingleSelect, smMultiCtrlSelect, smMultiShiftSelect);

  TTMSFMXTableViewCategory = class(TCollectionItem)
  private
    FOwner: TTMSFMXTableView;
    FCaption: String;
    FID: integer;
    FLookupText: String;
    procedure SetCaption(const Value: String);
    procedure SetId(const Value: integer);
    procedure SetLookupText(const Value: String);
  public
    constructor Create(Collection: TCollection); override;
    function GetStrippedCaption: String;
  published
    property Caption: String read FCaption write SetCaption;
    property LookupText: String read FLookupText write SetLookupText;
    property Id: integer read FID write SetId;
  end;

  TTMSFMXTableViewSortingMode = (mAscending, mDescending);

  TTMSFMXTableViewCategories = class(TTMSFMXBaseCollection)
  private
    FOwner: TTMSFMXTableView;
    FOnChange: TNotifyEvent;
    function GetItem(Index: Integer): TTMSFMXTableViewCategory;
    procedure SetItem(Index: Integer; const Value: TTMSFMXTableViewCategory);
  protected
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    function GetOwner: TPersistent; override;
    function Compare(Item1, Item2 : TTMSFMXTableViewCategory; ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending) : integer; virtual;
    procedure QuickSort(L, R: Integer; ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending);
  public
    constructor Create(AOwner: TTMSFMXTableView);
    property Items[Index: Integer]: TTMSFMXTableViewCategory read GetItem write SetItem; default;
    function ItemById(id: integer): TTMSFMXTableViewCategory;
    function ItemIndexById(id: integer): integer;
    function Add: TTMSFMXTableViewCategory;
    function Insert(Index: Integer): TTMSFMXTableViewCategory;
    procedure Sort(ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending);
    procedure Clear;
  end;

  TTMSFMXTableViewItem = class(TCollectionItem)
  private
    FOwner: TTMSFMXTableView;
    FCaption: string;
    FCategoryID: integer;
    FDescription: String;
    FCategoryShape: TTMSFMXTableViewCategoryShape;
    FTag: Integer;
    FDataObject: TObject;
    FDataBoolean: Boolean;
    FDataString: String;
    FSelected: Boolean;
    FDeleteButton: Boolean;
    FGroupIndex: Integer;
    FDetailView: TControl;
    FBulbText: String;
    FLeftMargin: Integer;
    FRightMargin: Integer;
    FCanSelect: Boolean;
    FCanDelete: Boolean;
    FDataValue: Single;
    FCanEditCaption: Boolean;
    FCanEditDescription: Boolean;
    FBitmapName: String;
    FBitmap: TBitmap;
    FVisible: Boolean;
    FDataGroupIndex: Integer;
    procedure SetCaption(const Value: string);
    procedure SetCategoryID(const Value: integer);
    procedure SetDescription(const Value: String);
    procedure SetDataBoolean(const Value: Boolean);
    procedure SetDataObject(const Value: TObject);
    procedure SetDataString(const Value: String);
    procedure SetSelected(const Value: Boolean);
    procedure SetDeleteButton(const Value: Boolean);
    procedure SetGroupIndex(const Value: Integer);
    procedure SetDetailView(const Value: TControl);
    procedure SetBulbText(const Value: String);
    procedure SetLeftMargin(const Value: Integer);
    procedure SetRightMargin(const Value: Integer);
    procedure SetCanSelect(const Value: Boolean);
    procedure SetCanDelete(const Value: Boolean);
    procedure SetDataValue(const Value: Single);
    procedure SetCanEditCaption(const Value: Boolean);
    procedure SetCanEditDescription(const Value: Boolean);
    procedure SetBitmapName(const Value: String);
    procedure SetBitmap(const Value: TBitmap);
    procedure SetVisible(const Value: Boolean);
    function GetDetailView: TControl;
    procedure SetDataGroupIndex(const Value: Integer);
    function GetBitmapContainer: TTMSFMXBitmapContainer;
  protected
    procedure CheckAnchor(X, Y: Single);
    procedure DoAnchorClickItemCaption(Sender: TObject; AAnchor: String);
    procedure DoAnchorClickItemDescription(Sender: TObject; AAnchor: String);
    procedure DetailAnimationFinished(Sender: TObject);
    procedure BitmapChanged(Sender: TObject);
    procedure DoCustomize;
    procedure ApplyEx; virtual;
    procedure ItemCheckChanged(Sender: TObject);
    procedure ItemRadioChanged(Sender: TObject);
    procedure ItemTrackBarChanged(Sender: TObject);
    procedure ItemArcDialChanged(Sender: TObject);
    procedure ItemTextChanged(Sender: TObject);
    procedure UpdateTableViewItem;
    procedure UpdateBulb;
    procedure UpdateBitmap;
    procedure AssignBitmapContainer;
    procedure UpdateDetailDisclosure;
    procedure UpdateItemIndent;
    procedure UpdateCaption;
    procedure UpdateDescription;
    procedure UpdateAlignments;
    procedure ShowEditButton;
    procedure HideEditButton;
    function GetCaptionRect: TRectF;
    function GetDescriptionRect: TRectF;
    property TableView: TTMSFMXTableView read FOwner;
  public
    property BitmapContainer: TTMSFMXBitmapContainer read GetBitmapContainer;
    function GetStrippedCaption: String;
    constructor Create(Collection: TCollection); override;
    procedure Assign(Source: TPersistent); override;
    destructor Destroy; override;
    procedure ToggleDetailView(ADetailViewVisible: Boolean);
    procedure ShowDetailView;
    procedure HideDetailView;
    procedure UpdateItem;
    procedure ToggleDeleteButton;
    procedure ToggleEditButton;
    procedure UpdateDetailContainer;
    procedure EditButtonAnimFinished(Sender: TObject);
    procedure ShapeDeleteButtonClick(Sender: TObject);
    procedure ShowEditForCaption;
    procedure ShowEditForDescription;
    function Shape: TTMSFMXTableViewItemShape;
    function ShapeCaption: TText;
    function ShapeDescription: TText;
    function ShapeLeftRectangle: TRectangle;
    function ShapePlaceHolder: TRectangle;
    function ShapeRightRectangle: TRectangle;
    function ShapeCenterRectangle: TRectangle;
    function ShapeBulbText: TTMSFMXHTMLText;
    function ShapeBulbRectangle: TTMSFMXTableViewBulbShape;
    function ShapeEditChecked: TControl;
    function ShapeEditUnChecked: TControl;
    function ShapeEditRectangle: TRectangle;
    function ShapeDetailRectangle: TRectangle;
    function ShapeDeleteButton: TTMSFMXBarButton;
    function ShapeLeftRectangleImage: TTMSFMXBitmap;
    function DeleteAnimation: TAnimation;
    function EditAnimation: TAnimation;
    function DeleteOpacityAnimation: TAnimation;
    function EditOpacityAnimation: TAnimation;
    function ShapeDeleteRectangle: TRectangle;
    property CategoryShape: TTMSFMXTableViewCategoryShape read FCategoryShape write FCategoryShape;
    property ShapeLink: TTMSFMXTableViewItemShape read Shape;
  published
    property CanEditCaption: Boolean read FCanEditCaption write SetCanEditCaption default False;
    property CanEditDescription: Boolean read FCanEditDescription write SetCanEditDescription default False;
    property CanSelect: Boolean read FCanSelect write SetCanSelect default True;
    property CanDelete: Boolean read FCanDelete write SetCanDelete default True;
    property LeftMargin: Integer read FLeftMargin write SetLeftMargin default 0;
    property RightMargin: Integer read FRightMargin write SetRightMargin default 0;
    property DetailView: TControl read GetDetailView write SetDetailView;
    property Caption: string read FCaption write SetCaption;
    property Description: String read FDescription write SetDescription;
    property CategoryID: integer read FCategoryID write SetCategoryID default -1;
    property Tag: Integer read FTag write FTag default 0;
    property DataGroupIndex: Integer read FDataGroupIndex write SetDataGroupIndex default 0;
    property DataBoolean: Boolean read FDataBoolean write SetDataBoolean;
    property DataValue: Single read FDataValue write SetDataValue;
    property DataString: String read FDataString write SetDataString;
    property DataObject: TObject read FDataObject write SetDataObject;
    property Selected: Boolean read FSelected write SetSelected;
    property DeleteButton: Boolean read FDeleteButton write SetDeleteButton default False;
    property GroupIndex: Integer read FGroupIndex write SetGroupIndex default 0;
    property BulbText: String read FBulbText write SetBulbText;
    property BitmapName: String read FBitmapName write SetBitmapName;
    property Bitmap: TBitmap read FBitmap write SetBitmap;
    property Visible: Boolean read FVisible write SetVisible default True;
  end;

  TTMSFMXTableViewItems = class(TTMSFMXBaseCollection)
  private
    FOwner: TTMSFMXTableView;
    function GetItem(Index: Integer): TTMSFMXTableViewItem;
    procedure SetItem(Index: Integer; const Value: TTMSFMXTableViewItem);
  protected
    function CreateItemClass: TCollectionItemClass; virtual;
    function Compare(Item1, Item2: TTMSFMXTableViewItem; ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending): integer;
    procedure QuickSort(L, R: Integer; ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending);
    function GetOwner: TPersistent; override;
  public
    constructor Create(AOwner: TTMSFMXTableView);
    property Items[Index: Integer]: TTMSFMXTableViewItem read GetItem write SetItem; default;
    function Add: TTMSFMXTableViewItem;
    function Insert(Index: Integer): TTMSFMXTableViewItem;
    procedure Sort(ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending);
  end;

  TTMSFMXTableViewItemCompareEvent = procedure(Sender: TObject; Item1, Item2: TTMSFMXTableViewItem; var CompareResult: Integer) of object;

  TTMSFMXTableViewLookUpEvent = procedure(Sender: TObject; ACharacter: String; ACharacterID: Integer; BufferStart, BufferStop: Integer) of object;

  TTMSFMXTableViewItemSelectEvent = procedure(Sender: TObject; AItem: TTMSFMXTableViewItem) of object;

  TTMSFMXTableViewItemClickEvent = procedure(Sender: TObject; AItem: TTMSFMXTableViewItem) of object;

  TTMSFMXTableViewCustomizeCategoryEvent = procedure(Sender: TObject; ACategoryShape: TTMSFMXTableViewCategoryShape) of object;

  TTMSFMXTableViewCategoryClickEvent = procedure(Sender: TObject; ACharacter: String; ACharacterID: Integer) of object;

  TTMSFMXTableViewCustomizeItemEvent = procedure(Sender: TObject; AItem: TTMSFMXTableViewItem; AItemShape: TTMSFMXTableViewItemShape; AItemControlShape: TControl) of object;

  TTMSFMXTableViewCustomizeDetailAnimations = procedure(Sender: TObject; ListAnimation, DetailAnimation: TAnimation) of object;

  TTMSFMXTableViewItemDeleteButton = procedure(Sender: TObject; AItem: TTMSFMXTableViewItem; var AllowDelete: Boolean) of object;

  TTMSFMXTableViewItemBeforeDrawEvent = procedure(Sender: TObject; var AllowDraw: Boolean; ACanvas: TCanvas; AItem: TTMSFMXTableViewItem; ARectangle: TRectF) of object;

  TTMSFMXTableViewItemAfterDrawEvent = procedure(Sender: TObject; ACanvas: TCanvas; AItem: TTMSFMXTableViewItem; ARectangle: TRectF) of object;

  TTMSFMXTableViewScrollingEvent = procedure(Sender: TObject; APosition: Single) of object;

  TTMSFMXTableViewArchiveEvent = procedure(Sender: TObject; var AllowDelete: Boolean) of object;

  TTMSFMXTableViewItemDetailEvent = procedure(Sender: TObject; AItem: TTMSFMXTableViewItem) of object;

  TTMSFMXTableViewBeforeSearchEvent = procedure(Sender: TObject; var ASearchString: String; var AllowSearch: Boolean) of object;

  TTMSFMXTableViewBeforeFilterEvent = procedure(Sender: TObject; var AFilterString: String; var AllowFilter: Boolean) of object;

  TTMSFMXTableViewAfterSearchEvent = procedure(Sender: TObject; ASearchString: String) of object;

  TTMSFMXTableViewAfterFilterEvent = procedure(Sender: TObject; AFilterString: String) of object;

  TTMSFMXTableViewFiltering = (vfNone, vfFilterStart, vfFilterRandom, vfSearch);

  TTMSFMXTableViewLayoutMode = (lmNormal, lmGroup);

  TTMSFMXTableViewItemOptions = set of TTMSFMXTableViewItemOption;

  TTMSFMXTableViewCategoryAnchorClickEvent = procedure(Sender: TObject; ACharacter: String; ACharacterID: Integer; AAnchor: String) of object;

  TTMSFMXTableViewItemAnchorClickEvent = procedure(Sender: TObject; AItem: TTMSFMXTableViewItem; AAnchor: String) of object;

  TObjList = class(TList<TFmxObject>);
  TItemList = class(TList<TCollectionItem>);

  [ComponentPlatformsAttribute(TMSPlatforms)]
  TTMSFMXTableView = class(TTMSFMXBaseControl)
  private
    FClearDeleteButtons: Boolean;
    FCheckInplaceEdit: Boolean;
    FEditMode: Boolean;
    FBlockDeleteButton: Boolean;
    FFilterObjectList: TItemList;
    FFilter: String;
    FScrollAnimationStarted: Boolean;
    FSelectedItems: TItemList;
    FCheckDelete: Boolean;
    FShowDeleteButton: Boolean;
    FDetailViewVisible: Boolean;
    FBufferStart, FBufferStop: Integer;
    FDisplayList, FItemList: TObjList;
    FCategoryList: TObjList;
    FChar: array[1..36] of Boolean;
    FCustomChar: array of Boolean;
    FBlockUpdate: Boolean;
    FUpdateCount: Integer;
    FMouseUp, FMouseDown, FCheckItem: Boolean;
    FSp: Single;
    FDragX, FDragY, FScrollY: Single;
    FTimeStart, FTimeStop: Integer;
    FCurrentPos, FPosTo: Single;
    FItems: TTMSFMXTableViewItems;
    FCategoryType: TTMSFMXTableViewCategoryType;
    FCategories: TTMSFMXTableViewCategories;
    FOnItemCompare: TTMSFMXTableViewItemCompareEvent;
    FBufferSize: Integer;
    FAutoLoadBuffer: Boolean;
    FOnManualLoadPreviousBuffer: TNotifyEvent;
    FOnManualLoadNextBuffer: TNotifyEvent;
    FScrollIndicator: TTMSFMXTableViewScrollIndicator;
    FLookupBar: Boolean;
    FAutoLookup: Boolean;
    FOnManualLookUp: TTMSFMXTableViewLookUpEvent;
    FAutoToggleDetail: Boolean;
    FOnItemSelected: TTMSFMXTableViewItemSelectEvent;
    FOnCategoryCustomize: TTMSFMXTableViewCustomizeCategoryEvent;
    FOnItemCustomize: TTMSFMXTableViewCustomizeItemEvent;
    FOnAnimationCustomize: TTMSFMXTableViewCustomizeDetailAnimations;
    FOnItemData: TTMSFMXTableViewCustomizeItemEvent;
    FOnItemDelete: TTMSFMXTableViewItemDeleteButton;
    FAutoDeleteItem: Boolean;
    FSelectedItemIndex, FFocusedItemIndex, FInternalSelItemIndex: Integer;
    FMultiSelect: Boolean;
    FFiltering: TTMSFMXTableViewFiltering;
    FLayoutMode: TTMSFMXTableViewLayoutMode;
    FOnItemBeforeDraw: TTMSFMXTableViewItemBeforeDrawEvent;
    FOnItemAfterDraw: TTMSFMXTableViewItemAfterDrawEvent;
    FOnScroll: TTMSFMXTableViewScrollingEvent;
    FOnScrollFinished: TTMSFMXTableViewScrollingEvent;
    FTopItem: Integer;
    FOnMarkClick: TNotifyEvent;
    FOnMoveClick: TNotifyEvent;
    FOnArchiveClick: TTMSFMXTableViewArchiveEvent;
    FEditButton: Boolean;
    FBackButton: Boolean;
    FAutoFilter: Boolean;
    FShowFilter: Boolean;
    FMarkText: String;
    FMoveText: String;
    FArchiveText: String;
    FBitmapContainer: TTMSFMXBitmapContainer;
    FHeaderText: String;
    FFooterText: String;
    FItemOptions: TTMSFMXTableViewItemOptions;
    FOnItemBeforeDetail: TTMSFMXTableViewItemDetailEvent;
    FOnItemAfterDetail: TTMSFMXTableViewItemDetailEvent;
    FOnItemBeforeReturnDetail: TTMSFMXTableViewItemDetailEvent;
    FOnItemAfterReturnDetail: TTMSFMXTableViewItemDetailEvent;
    FOnBeforeSearch: TTMSFMXTableViewBeforeSearchEvent;
    FOnBeforeFilter: TTMSFMXTableViewBeforeFilterEvent;
    FOnAfterSearch: TTMSFMXTableViewAfterSearchEvent;
    FOnAfterFilter: TTMSFMXTableViewAfterFilterEvent;
    FOnItemClick: TTMSFMXTableViewItemClickEvent;
    FDefaultDetailView: TControl;
    FOnCategoryClick: TTMSFMXTableViewCategoryClickEvent;
    FShowFilterOnSwipe: Boolean;
    FOnBackButtonClick: TTMSFMXTableViewItemDetailEvent;
    FOnFooterAnchorClick: TTMSFMXHTMLTextAnchorClickEvent;
    FOnCategoryAnchorClick: TTMSFMXTableViewCategoryAnchorClickEvent;
    FOnItemCaptionAnchorClick: TTMSFMXTableViewItemAnchorClickEvent;
    FOnItemDescriptionAnchorClick: TTMSFMXTableViewItemAnchorClickEvent;
    FOnHeaderAnchorClick: TTMSFMXHTMLTextAnchorClickEvent;
    FBindingsCount: Integer;
    FOnSearchOpen: TNotifyEvent;
    FOnSearchClose: TNotifyEvent;
    procedure SetItems(const Value: TTMSFMXTableViewItems);
    procedure SetCategoryType(const Value: TTMSFMXTableViewCategoryType);
    procedure SetCategories(const Value: TTMSFMXTableViewCategories);
    procedure SetAutoLoadBuffer(const Value: Boolean);
    procedure SetScrollIndicator(const Value: TTMSFMXTableViewScrollIndicator);
    procedure SetBufferSize(const Value: Integer);
    procedure SetLookupBar(const Value: Boolean);
    procedure SetAutoLookup(const Value: Boolean);
    procedure SetAutoToggleDetail(const Value: Boolean);
    procedure SetAutoDeleteItem(const Value: Boolean);
    procedure SetSelectedItemIndex(const Value: Integer);
    procedure SetMultiSelect(const Value: Boolean);
    procedure SetFiltering(const Value: TTMSFMXTableViewFiltering);
    procedure SetLayoutMode(const Value: TTMSFMXTableViewLayoutMode);
    procedure SetTopItem(const Value: Integer);
    procedure SetEditButton(const Value: Boolean);
    procedure SetBackButton(const Value: Boolean);
    procedure SetAutoFilter(const Value: Boolean);
    procedure SetShowFilter(const Value: Boolean);
    procedure SetArchiveText(const Value: String);
    procedure SetMarkText(const Value: String);
    procedure SetMoveText(const Value: String);
    function GetVersion: String;
    function GetVersionNr: Integer;
    procedure SetVersion(const Value: String);
    procedure SetFooterText(const Value: String);
    procedure SetHeaderText(const Value: String);
    procedure SetItemOptions(const Value: TTMSFMXTableViewItemOptions);
    procedure SetDefaultDetailView(const Value: TControl);
    procedure SetShowFilterOnSwipe(const Value: Boolean);
  protected
//    procedure RefreshTableViewItems;
    function CreateItems: TTMSFMXTableViewItems; virtual;
    property BindingsCount: Integer read FBindingsCount write FBindingsCount;
    procedure ListMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean);
    procedure DoEnter; override;
    function CanObserve(const ID: Integer): Boolean; override;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    function GetInstance: NativeUInt; override;
    procedure UpdateEditButton;
    procedure UpdateFilter;
    procedure ShowBackButton(Show: Boolean);
    procedure HideBackButton;
    procedure HideEditButton;
    procedure ShowEditButton;
    procedure UpdateEditButtons;
    procedure EditButtonClick(Sender: TObject);
    function GetLookUpBarWidth: Single;
    function ClearDeleteButtons(AItem: TTMSFMXTableViewItem = nil): Boolean;
    procedure UpdateDefaultItemStyle;
    procedure UpdateDefaultCategory;
    procedure UpdateDefaultItem;
    function GetItemIndexForIndex(AIndex: Integer): Integer;
    function GetCountItems: Integer;
    function GetItem(I: integer): TTMSFMXTableViewItem;
    function isFiltered: Boolean;
    procedure DoLoadPreviousBuffer;
    procedure DoLoadNextBuffer;
    procedure SetFocusOnBackGround;
    procedure ListKeyDown(Sender: TObject; var Key: Word; var KeyChar: WideChar; Shift: TShiftState);
    procedure CloneChildren(Source, Destination: TFMXObject);
    function CanClone(Source: TFMXObject): Boolean;
    procedure ScrollAnimationProcess(Sender: TObject);
    procedure ScrollAnimationFinished(Sender: TObject);
    procedure BackButtonAnimFinished(Sender: TObject);
    procedure ApplyStyle; override;
    procedure AlignButtons;
    procedure AssignEvents;
    procedure AssignAnimations;
    procedure StopScrollAnimation;
    procedure StartScrollAnimation;
    procedure ListMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    procedure ListMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
    procedure ListMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    procedure ListDblClick(Sender: TObject);
    procedure ListClick(Sender: TObject);
    procedure ListMouseLeave(Sender: TObject);
    procedure CleanUpItems(innerelement: TControl);
    procedure CleanUpCategories(innerelement: TControl);
    procedure InitCategories;
    procedure InternalScrollItem(AItemIndex: Integer; SelectItem: Boolean = False; ASelectionMode: TTMSFMXTableViewSelectionMode = smSingleSelect);
    procedure ArchiveClick(Sender: TObject);
    procedure MoveClick(Sender: TObject);
    procedure MarkClick(Sender: TObject);
    procedure BackButtonClick(Sender: TObject);
    procedure InPlaceEditExit(Sender: TObject);
    procedure SearchEditChanging(Sender: TObject);
    procedure SearchEditChanged(Sender: TObject);
    procedure UpdateFooterText;
    procedure UpdateHeaderText;
    procedure DoFilter(AFilter: String);
    function DoesItemExist(innerelement: TControl; ItemName: String): TTMSFMXTableViewItemShape;
    function DoesCategoryExist(innerelement: TControl; CategoryName: String): TTMSFMXTableViewCategoryShape;
    function AsItem(AObject: TFmxObject): TTMSFMXTableViewItemShape;
    function AsCategory(AObject: TFmxObject): TTMSFMXTableViewCategoryShape;
    function IsItem(AObject: TFmxObject): Boolean;
    function IsCategory(AObject: TFmxObject): Boolean;
    function GetFullHeight: Single;
    function GetFullScrollBarHeight: Single;
    function GetCurrentPos: Single;
    function GetPositionTo: Single;
    function FindCategoryWithCharacter(ACategory: String): TTMSFMXTableViewCategoryShape;
    function FindCategoryWithCharacterID(ACategoryID: Integer): TTMSFMXTableViewCategoryShape;
    procedure UpdateItemInplaceEdit;
    procedure ShowInplaceEdit;
    procedure HideInplaceEdit;
    procedure UpdateCategoryPosition;
    procedure UpdateListPosition;
    procedure DeleteListItem(itemElement: TControl);
    procedure UpdateListItem(AItem: TTMSFMXTableViewItem; itemElement, defaultitemElement: TControl);
    procedure AddListItem(AItem: TTMSFMXTableViewItem; itemElement, innerelement: TControl; var Y: Single);
    procedure AddListCategory(AItem: TTMSFMXTableViewItem; categoryElement, innerelement: TControl; var Y: Single);
    procedure ProcessItem(AItem: TTMSFMXTableViewItem; ASelectionMode: TTMSFMXTableViewSelectionMode = smSingleSelect; ShowDetail: Boolean = True);
    procedure UpdateScrollBar;
    procedure UpdateScrollBarVisuals;
    procedure UpdateLookupBar;
    procedure ShowScrollBar;
    procedure HideScrollBar;
    procedure NotifyObserver;
    procedure UpdateTableViewItems;
    procedure ResetBufferSize;
    procedure ScrollToPosition(APosition: Single);
    function GetCategoryIDForItem(AItem: TTMSFMXTableViewItem): Integer;
    procedure PositionItem(AItem: TTMSFMXTableViewItem; OnTop: Boolean = False);
    procedure ScrollToCategoryId(ACategoryID: Integer);
    procedure ScrollToCategoryCharacter(ACharacter: String);
    procedure ScrollToItem(AItem: TTMSFMXTableViewItem);
    procedure DoAnchorClickHeader(Sender: TOBject; AAnchor: String);
    procedure DoAnchorClickFooter(Sender: TOBject; AAnchor: String);
    procedure DoAnchorClickCategory(Sender: TObject; AAnchor: String);
  public
    function GetUpdateCount: Integer;
    function SelectedItem: TTMSFMXTableViewItem;
    function CountSelected: Integer;
    property TopItem: Integer read FTopItem write SetTopItem;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    function ItemFromDifferentCategory(item1, item2: TTMSFMXTableViewItem): Boolean; virtual;
    function GetFooterRectangle: TRectangle;
    function GetFooterText: TText;
    function GetEditUncheckedDefaultImage: TImage;
    function GetEditCheckedDefaultImage: TImage;
    function GetItemDefaultDetailImage: TImage;
    function GetHeaderText: TText;
    function GetHeaderRectangle: TRectangle;
    function GetFooterEditRectangle: TRectangle;
    function GetItemDeleteRectangle: TRectangle;
    function GetListDetailContainer: TControl;
    function GetListContainer: TControl;
    function GetCurrentListView: TControl;
    function GetListBackGround: TControl;
    function GetEditButton: TControl;
    function GetBackButton: TControl;
    function GetListPlaceHolder: TControl;
    function GetDefaultItem: TControl;
    function GetSearchEdit: TTMSFMXSearchEdit;
    function GetBackButtonAnimation: TAnimation;
    function GetBackButtonOpacityAnimation: TAnimation;
    function GetDefaultCategory: TControl;
    function GetItemBulbRectangle: TControl;
    function GetItemEditRectangle: TControl;
    function GetItemLeftRectangle: TControl;
    function GetItemDetailRectangle: TControl;
    function GetItemCaption: TText;
    function GetItemDescription: TText;
    function GetItemRightRectangle: TControl;
    function GetItemCenterRectangle: TControl;
    function GetArchiveButton: TControl;
    function GetMarkButton: TControl;
    function GetMoveButton: TControl;
    function GetHeaderSearch: TControl;
    function GetInPlaceEdit: TTMSFMXSearchEdit;
    function GetLookupBar: TControl;
    function GetBufferStop: Integer;
    function GetBufferStart: Integer;
    function GetScrollBar: TControl;
    function GetScrollBarAnimation: TAnimation;
    function GetDetailAnimation: TAnimation;
    function GetListDetailAnimation: TAnimation;
    function GetListScrollAnimation: TAnimation;
    function ItemAtXY(X, Y: Single): TTMSFMXTableViewItem;
    function GetCharacterForItem(AItem: TTMSFMXTableViewItem): String;
    function FindItemWithFilter(AFilter: String): TTMSFMXTableViewItem;
    function IsItemInItemList(AItem: TTMSFMXTableViewItem): Boolean;
    function IsItemVisibleInList(AItem: TTMSFMXTableViewItem): Boolean;
    function FindFirstItemWithCategory(ACategory: String): TTMSFMXTableViewItem;
    function FindFirstItemWithCategoryID(ACategoryID: Integer): TTMSFMXTableViewItem;
    function isEditMode: Boolean;
    function isDetailMode: Boolean;
    procedure ToggleEditMode(AEditMode: Boolean);
    procedure EditMode;
    procedure CancelEditMode;
    procedure ApplyFilter(AFilter: String);
    procedure UpdateTableView;
    procedure BeginUpdate; override;
    procedure EndUpdate; override;
    procedure LoadNextBuffer(ABufferSize: Integer = -1);
    procedure LoadPreviousBuffer(ABufferSize: Integer = -1);
    procedure LoadBufferBetween(BufferStart, BufferStop: Integer);
    procedure Resize; override;
    procedure UpdateDetailContainerGrid;
    procedure LookupCustomCategory(ACategoryID: Integer);
    procedure LookupCategory(ACategory: String);
    procedure LookupItem(AItemIndex: Integer; SelectItem: Boolean = False);
    procedure DeSelectAllItems;
    procedure SelectAllItems;
    procedure SelectItemsBetween(AStartIndex, AStopIndex: Integer; AddToSelection: Boolean = False);
    procedure SelectItems(Selection: Array of Integer; AddToSelection: Boolean = False);
    property DetailContainer: TControl read GetListDetailContainer;
  published
    property Fill;
    property Stroke;
    property StrokeThickness;
    property StrokeCap;
    property StrokeDash;
    property StrokeJoin;
    property CanFocus;
    property OnBackButtonClick: TTMSFMXTableViewItemDetailEvent read FOnBackButtonClick write FOnBackButtonClick;
    property AutoFilter: Boolean read FAutoFilter write SetAutoFilter default True;
    property SelectedItemIndex: Integer read FSelectedItemIndex write SetSelectedItemIndex default -1;
    property Items: TTMSFMXTableViewItems read FItems write SetItems;
    property CategoryType: TTMSFMXTableViewCategoryType read FCategoryType write SetCategoryType default ctNone;
    property Categories: TTMSFMXTableViewCategories read FCategories write SetCategories;
    property OnItemCompare: TTMSFMXTableViewItemCompareEvent read FOnItemCompare write FOnItemCompare;
    property BufferSize: Integer read FBufferSize write SetBufferSize default 50;
    property AutoDeleteItem: Boolean read FAutoDeleteItem write SetAutoDeleteItem default True;
    property AutoLoadBuffer: Boolean read FAutoLoadBuffer write SetAutoLoadBuffer default True;
    property AutoLookup: Boolean read FAutoLookup write SetAutoLookup default True;
    property AutoToggleDetail: Boolean read FAutoToggleDetail write SetAutoToggleDetail default True;
    property ScrollIndicator: TTMSFMXTableViewScrollIndicator read FScrollIndicator write SetScrollIndicator default siFade;
    property LookupBar: Boolean read FLookupBar write SetLookupBar default True;
    property MultiSelect: Boolean read FMultiSelect write SetMultiSelect default False;
    property Filtering: TTMSFMXTableViewFiltering read FFiltering write SetFiltering default vfFilterRandom;
    property ShowFilter: Boolean read FShowFilter write SetShowFilter default False;
    property ShowFilterOnSwipe: Boolean read FShowFilterOnSwipe write SetShowFilterOnSwipe default True;
    property LayoutMode: TTMSFMXTableViewLayoutMode read FLayoutMode write SetLayoutMode default lmNormal;
    property EditButton: Boolean read FEditButton write SetEditButton default False;
    property BackButton: Boolean read FBackButton write SetBackButton default True;
    property MarkText: String read FMarkText write SetMarkText;
    property MoveText: String read FMoveText write SetMoveText;
    property ArchiveText: String read FArchiveText write SetArchiveText;
    property BitmapContainer: TTMSFMXBitmapContainer read FBitmapContainer write FBitmapContainer;
    property HeaderText: String read FHeaderText write SetHeaderText;
    property FooterText: String read FFooterText write SetFooterText;
    property ItemOptions: TTMSFMXTableViewItemOptions read FItemOptions write SetItemOptions default AllItemOptions;
    property DefaultDetailView: TControl read FDefaultDetailView write SetDefaultDetailView;

    property Version: String read GetVersion write SetVersion;

    property OnAfterFilter: TTMSFMXTableViewAfterFilterEvent read FOnAfterFilter write FOnAfterFilter;
    property OnBeforeFilter: TTMSFMXTableViewBeforeFilterEvent read FOnBeforeFilter write FOnBeforeFilter;
    property OnAfterSearch: TTMSFMXTableViewAfterSearchEvent read FOnAfterSearch write FOnAfterSearch;
    property OnBeforeSearch: TTMSFMXTableViewBeforeSearchEvent read FOnBeforeSearch write FOnBeforeSearch;
    property OnItemBeforeDetail: TTMSFMXTableViewItemDetailEvent read FOnItemBeforeDetail write FOnItemBeforeDetail;
    property OnItemAfterDetail: TTMSFMXTableViewItemDetailEvent read FOnItemAfterDetail write FOnItemAfterDetail;
    property OnItemBeforeReturnDetail: TTMSFMXTableViewItemDetailEvent read FOnItemBeforeReturnDetail write FOnItemBeforeReturnDetail;
    property OnItemAfterReturnDetail: TTMSFMXTableViewItemDetailEvent read FOnItemAfterReturnDetail write FOnItemAfterReturnDetail;
    property OnManualLoadNextBuffer: TNotifyEvent read FOnManualLoadNextBuffer write FOnManualLoadNextBuffer;
    property OnManualLoadPreviousBuffer: TNotifyEvent read FOnManualLoadPreviousBuffer write FOnManualLoadPreviousBuffer;
    property OnManualLookup: TTMSFMXTableViewLookUpEvent read FOnManualLookUp write FOnManualLookUp;
    property OnItemDelete: TTMSFMXTableViewItemDeleteButton read FOnItemDelete write FOnItemDelete;
    property OnItemSelected: TTMSFMXTableViewItemSelectEvent read FOnItemSelected write FOnItemSelected;
    property OnItemClick: TTMSFMXTableViewItemClickEvent read FOnItemClick write FOnItemClick;
    property OnItemData: TTMSFMXTableViewCustomizeItemEvent read FOnItemData write FOnItemData;
    property OnAnimationCustomize: TTMSFMXTableViewCustomizeDetailAnimations read FOnAnimationCustomize write FOnAnimationCustomize;
    property OnItemCustomize: TTMSFMXTableViewCustomizeItemEvent read FOnItemCustomize write FOnItemCustomize;
    property OnCategoryCustomize: TTMSFMXTableViewCustomizeCategoryEvent read FOnCategoryCustomize write FOnCategoryCustomize;
    property OnCategoryClick: TTMSFMXTableViewCategoryClickEvent read FOnCategoryClick write FOnCategoryClick;
    property OnItemBeforeDraw: TTMSFMXTableViewItemBeforeDrawEvent read FOnItemBeforeDraw write FOnItemBeforeDraw;
    property OnItemAfterDraw: TTMSFMXTableViewItemAfterDrawEvent read FOnItemAfterDraw write FOnItemAfterDraw;
    property OnScroll: TTMSFMXTableViewScrollingEvent read FOnScroll write FOnScroll;
    property OnScrollFinished: TTMSFMXTableViewScrollingEvent read FOnScrollFinished write FOnScrollFinished;
    property OnMoveClick: TNotifyEvent read FOnMoveClick write FOnMoveClick;
    property OnMarkClick: TNotifyEvent read FOnMarkClick write FOnMarkClick;
    property OnArchiveClick: TTMSFMXTableViewArchiveEvent read FOnArchiveClick write FOnArchiveClick;
    property OnHeaderAnchorClick: TTMSFMXHTMLTextAnchorClickEvent read FOnHeaderAnchorClick write FOnHeaderAnchorClick;
    property OnFooterAnchorClick: TTMSFMXHTMLTextAnchorClickEvent read FOnFooterAnchorClick write FOnFooterAnchorClick;
    property OnItemCaptionAnchorClick: TTMSFMXTableViewItemAnchorClickEvent read FOnItemCaptionAnchorClick write FOnItemCaptionAnchorClick;
    property OnItemDescriptionAnchorClick: TTMSFMXTableViewItemAnchorClickEvent read FOnItemDescriptionAnchorClick write FOnItemDescriptionAnchorClick;
    property OnCategoryAnchorClick: TTMSFMXTableViewCategoryAnchorClickEvent read FOnCategoryAnchorClick write FOnCategoryAnchorClick;
    property OnSearchOpen: TNotifyEvent read FOnSearchOpen write FOnSearchOpen;
    property OnSearchClose: TNotifyEvent read FOnSearchClose write FOnSearchClose;
  end;

function GetStrippedHTMLText(ARect: TRectF; ACanvas: TCanvas; AString: String): String;
function GetTickCountX: DWORD;

implementation

{$R FMX.TMSTableView.res}

type
  {$HINTS OFF}
  TShadowedCollection = class(TPersistent)
  private
    FItemClass: TCollectionItemClass;
    FItems: TList<TCollectionItem>;
  end;
  {$HINTS ON}

function GetTickCountX: DWORD;
var
  h, m, s, ms: Word;
begin
  DecodeTime(Now, h, m, s, ms);
  Result := ms + s * 1000 + m * 60 * 1000 + h * 60 * 60 * 1000;
end;

function GetStrippedHTMLText(ARect: TRectF; ACanvas: TCanvas; AString: String): String;
var
  a, s, fa: String;
  XSize, YSize: Single;
  hl, ml: Integer;
  hr: TRectF;
  str: String;
begin
  str := AString;
  if (AnsiPos('</', str) > 0) or (AnsiPos('/>', str)  > 0) or (AnsiPos('<BR>', UpperCase(str)) > 0) then
  begin
    HTMLDrawEx(ACanvas,str, ARect,0,0,-1,-1,0,False,True,False,False,False,False,false,1.0,claBlue,
      claNull,claNull,claNull,a,s,fa,XSize,YSize,hl,ml,hr,0, nil, 1);
    Result := s;
  end
  else
    Result := AString;
end;

{ TTMSFMXTableView }

constructor TTMSFMXTableView.Create(AOwner: TComponent);
var
  i: integer;
begin
  inherited;
  CanFocus := True;
  {$IFDEF DELPHIXE6_LVL}
  Fill.Kind := TBrushKind.Solid;
  Stroke.Kind := TBrushKind.Solid;
  {$ELSE}
  Fill.Kind := TBrushKind.bkSolid;
  Stroke.Kind := TBrushKind.bkSolid;
  {$ENDIF}
  Width := 320;
  Height := 480;
  FItems := CreateItems;

  FLookupBar := True;
  FAutoLookup := True;
  FLayoutMode := lmNormal;
  FAutoToggleDetail := True;
  FAutoDeleteItem := True;
  FMultiSelect := False;
  FFiltering := vfFilterRandom;
  FBackButton := True;
  FEditButton := False;
  FShowFilterOnSwipe := True;

  FCategoryType := ctNone;
  FItemOptions := AllItemOptions;

  FSelectedItemIndex := -1;

  FFilterObjectList := TItemList.Create;

  FDisplayList := TObjList.Create;
  FSelectedItems := TItemList.Create;
  FCategoryList := TObjList.Create;
  FItemList := TObjList.Create;

  FCategories := TTMSFMXTableViewCategories.Create(Self);
  FAutoLoadBuffer := True;
  FAutoFilter := True;

  FBufferSize := 50;
  FBufferStart := -1;
  FBufferStop := -1;

  FScrollIndicator := siFade;

  if (csDesigning in ComponentState) and not
    ((csReading in Owner.ComponentState) or (csLoading in Owner.ComponentState)) then
  begin
    FArchiveText := 'Archive';
    FMarkText := 'Mark';
    FMoveText := 'Move';
    FFooterText := 'Footer';
    FHeaderText := 'Header';

    BeginUpdate;
    for I := 0 to 20 do
    begin
      with Items.Add do
      begin
        Caption := 'Item ' + inttostr(I);
        Description := 'Description of ' + Caption;
      end;
    end;
    EndUpdate;
  end;
end;

function TTMSFMXTableView.CreateItems: TTMSFMXTableViewItems;
begin
  Result := TTMSFMXTableViewItems.Create(Self);
end;

function TTMSFMXTableView.GetBufferStop: Integer;
begin
  Result := Min(FBufferStop, GetCountItems);
end;

function TTMSFMXTableView.GetCategoryIDForItem(
  AItem: TTMSFMXTableViewItem): Integer;
begin
  Result := -1;
  case CategoryType of
    ctCustom:
    begin
      if (AItem.CategoryID <> -1) and (AItem.CategoryID < Categories.Count) then
        Result := AItem.CategoryID
      else
        Result := -1;
    end;
  end;
end;

function TTMSFMXTableView.GetCharacterForItem(
  AItem: TTMSFMXTableViewItem): String;
var
  cat: TTMSFMXTableViewCategory;
begin
  Result := '';
  case CategoryType of
    ctAlphaNumericFirst, ctAlphaNumericLast, ctAlphaBetic:
    begin
      if Length(AItem.GetStrippedCaption) > 0 then
      begin
        {$IFDEF DELPHI_LLVM}
        Result := AItem.GetStrippedCaption[0]
        {$ELSE}
        Result := AItem.GetStrippedCaption[1]
        {$ENDIF}
      end
      else
        Result := '';
    end;
    ctCustom:
    begin
      if (AItem.CategoryID <> -1) and (AItem.CategoryID < Categories.Count) then
      begin
        cat := Categories[Categories.ItemIndexByID(AItem.CategoryID)];
        Result := cat.Caption;
      end
      else
      begin
        Result := '';
      end;
    end;
  end;
end;

function TTMSFMXTableView.GetCountItems: Integer;
begin
  if isFiltered then
    Result := FFilterObjectList.Count - 1
  else
    Result := Items.Count - 1;
end;

function TTMSFMXTableView.GetCurrentListView: TControl;
begin
  if FDetailViewVisible then
    Result := GetListDetailContainer
  else
    Result := GetListContainer;
end;

function TTMSFMXTableView.GetCurrentPos: Single;
var
  backgroundelement: TControl;
begin
  Result := 0;
  backgroundelement := GetListBackGround;
  if Assigned(backgroundelement) then
  begin
    if not (csDesigning in ComponentState) then
    begin
      if FCurrentPos <> FPosTo then
      begin
        if FCurrentPos < -50 then
          FCurrentPos := -50

        else if FCurrentPos > GetFullHeight + 50 then
          FCurrentPos := GetFullHeight + 50;
      end;

      Result := FCurrentPos;
    end
    else
      Result := 0;

    if GetFullHeight + backgroundelement.Height < backgroundelement.Height then
      Result := 0;
  end;
end;

function TTMSFMXTableView.GetFooterEditRectangle: TRectangle;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('footeredit');
  if fmxobj is TRectangle then
    Result := fmxobj as TRectangle;
end;

function TTMSFMXTableView.GetFooterRectangle: TRectangle;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('footer');
  if fmxobj is TRectangle then
    Result := fmxobj as TRectangle;
end;

function TTMSFMXTableView.GetFooterText: TText;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('footertext');
  if fmxobj is TText then
    Result := fmxobj as TText;
end;

function TTMSFMXTableView.GetFullHeight: Single;
var
  innerelement: TControl;
  backgroundelement: TControl;
begin
  Result := 0;
  innerelement := GetListContainer;
  backgroundelement := GetListBackGround;
  if Assigned(innerelement) and Assigned(backgroundelement) then
    Result := innerelement.Height - backgroundelement.height;
end;


function TTMSFMXTableView.GetFullScrollBarHeight: Single;
var
  defitem, defcat, backgroundelement: TControl;
begin
  Result := 0;
  backgroundElement := GetListBackGround;
  defitem := GetDefaultItem;
  defcat := GetDefaultCategory;
  if Assigned(defcat) and Assigned(defitem) and Assigned(backgroundelement) then
    Result := ((GetCountItems + 1) * defitem.Height) + (FCategoryList.Count * defcat.Height);
end;

function TTMSFMXTableView.GetHeaderRectangle: TRectangle;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('header');
  if fmxobj is TRectangle then
    Result := fmxobj as TRectangle;
end;

function TTMSFMXTableView.GetInPlaceEdit: TTMSFMXSearchEdit;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('inplaceEdit');
  if fmxobj is TTMSFMXSearchEdit then
    Result := fmxobj as TTMSFMXSearchEdit;
end;

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

function TTMSFMXTableView.GetItem(I: integer): TTMSFMXTableViewItem;
begin
  Result := nil;
  if (i >= 0) and (i <= GetCountItems) then
  begin
    if isFiltered then
      Result := (TObject(FFilterObjectList.Items[I]) as TTMSFMXTableViewItem)
    else
      Result := Items[I];
  end;
end;

function TTMSFMXTableView.GetItemBulbRectangle: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itembulbrectangle');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetItemCaption: TText;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemcaption');
  if fmxobj is TText then
    Result := fmxobj as TText;
end;

function TTMSFMXTableView.GetItemCenterRectangle: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemcenterrectangle');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetItemDeleteRectangle: TRectangle;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemdeleterectangle');
  if fmxobj is TRectangle then
    Result := fmxobj as TRectangle;
end;

function TTMSFMXTableView.GetItemDescription: TText;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemdescription');
  if fmxobj is TText then
    Result := fmxobj as TText;
end;

function TTMSFMXTableView.GetItemEditRectangle: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemeditrectangle');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetItemIndexForIndex(AIndex: Integer): Integer;
var
  I: Integer;
begin
  Result := -1;
  if isFiltered then
  begin
    for I := 0 to GetCountItems do
    begin
      if GetItem(I).Index = AIndex then
        Result := I;
    end;
  end
  else
    Result := AIndex;
end;

function TTMSFMXTableView.GetItemDetailRectangle: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemdetailrectangle');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetItemLeftRectangle: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemleftrectangle');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetItemRightRectangle: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemrightrectangle');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetDefaultCategory: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('defaultcategory');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetDefaultItem: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('defaultitem');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetBackButtonOpacityAnimation: TAnimation;
var
  btnBack: TControl;
  fmxobj: TFMXObject;
  I: Integer;
begin
  Result := nil;
  btnBack := GetBackButton;
  if Assigned(btnBack) then
  begin
    for I := 0 to btnBack.ChildrenCount - 1 do
    begin
      fmxobj := btnBack.Children[I];
      if (fmxobj is TAnimation) and (fmxobj.StyleName = 'headerbackbuttonopacityanimation') then
      begin
        Result := fmxobj as TAnimation;
        Break;
      end;
    end;
  end;
end;

function TTMSFMXTableView.GetBackButtonAnimation: TAnimation;
var
  btnBack: TControl;
  fmxobj: TFMXObject;
  I: Integer;
begin
  Result := nil;
  btnBack := GetBackButton;
  if Assigned(btnBack) then
  begin
    for I := 0 to btnBack.ChildrenCount - 1 do
    begin
      fmxobj := btnBack.Children[I];
      if (fmxobj is TAnimation) and (fmxobj.StyleName = 'headerbackbuttonanimation') then
      begin
        Result := fmxobj as TAnimation;
        Break;
      end;
    end;
  end;
end;

function TTMSFMXTableView.GetDetailAnimation: TAnimation;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemdetailanimation');
  if fmxobj is TAnimation then
    Result := fmxobj as TAnimation;
end;

function TTMSFMXTableView.GetEditButton: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('headereditbutton');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetItemDefaultDetailImage: TImage;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('detailimageshape');
  if fmxobj is TImage then
    Result := fmxobj as TImage;
end;

function TTMSFMXTableView.GetEditCheckedDefaultImage: TImage;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('checkedimageshape');
  if fmxobj is TImage then
    Result := fmxobj as TImage;
end;

function TTMSFMXTableView.GetEditUncheckedDefaultImage: TImage;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('uncheckedimageshape');
  if fmxobj is TImage then
    Result := fmxobj as TImage;
end;

function TTMSFMXTableView.GetHeaderSearch: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('headersearch');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetHeaderText: TText;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('headertext');
  if fmxobj is TText then
    Result := fmxobj as TText;
end;

function TTMSFMXTableView.GetScrollBar: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemscrollbar');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetScrollBarAnimation: TAnimation;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemscrollbaranimation');
  if fmxobj is TAnimation then
    Result := fmxobj as TAnimation;
end;

function TTMSFMXTableView.GetSearchEdit: TTMSFMXSearchEdit;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('headersearchedit');
  if fmxobj is TTMSFMXSearchEdit then
    Result := fmxobj as TTMSFMXSearchEdit;
end;

function TTMSFMXTableView.GetUpdateCount: Integer;
begin
  Result := FUpdateCount;
end;

function TTMSFMXTableView.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 TTMSFMXTableView.GetVersionNr: integer;
begin
  Result := MakeLong(MakeWord(BLD_VER,REL_VER),MakeWord(MIN_VER,MAJ_VER));
end;

procedure TTMSFMXTableView.HideBackButton;
var
  btn: TControl;
begin
  btn := GetBackButton;
  if Assigned(btn) then
    btn.Visible := False;
end;

procedure TTMSFMXTableView.HideEditButton;
var
  editbutton: TControl;
begin
  editbutton := GetEditButton;
  if Assigned(editbutton) then
    editbutton.Visible := False;
end;

procedure TTMSFMXTableView.HideInplaceEdit;
var
  ipl: TTMSFMXSearchEdit;
begin
  ipl := GetInPlaceEdit;
  if Assigned(ipl) then
  begin
    if ipl.Visible then
    begin
      ipl.Parent := Self;
      ipl.Visible := False;
    end;
  end;
end;

procedure TTMSFMXTableView.HideScrollBar;
var
  anim: TAnimation;
begin
  if ScrollIndicator <> siFade then
    Exit;

  anim := GetScrollBarAnimation;
  if Assigned(anim) and (anim is TFloatAnimation) then
  begin
    (anim as TFloatAnimation).StartFromCurrent := True;
    (anim as TFloatAnimation).StopValue := 0;
    anim.Start;
  end;
end;

function TTMSFMXTableView.GetListBackGround: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itembackground');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetListContainer: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('items');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetListDetailAnimation: TAnimation;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemlistdetailanimation');
  if fmxobj is TAnimation then
    Result := fmxobj as TAnimation;
end;

function TTMSFMXTableView.GetListDetailContainer: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemdetailcontainer');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetListPlaceHolder: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemplaceholder');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetListScrollAnimation: TAnimation;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemscrollanimation');
  if fmxobj is TAnimation then
    Result := fmxobj as TAnimation;
end;

function TTMSFMXTableView.GetLookupBar: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemlookupbar');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetLookUpBarWidth: Single;
var
  lookup: TControl;
begin
  Result := 25;
  lookup := GetLookupBar;
  if Assigned(lookup) then
  begin
    if lookup.Visible then
      Result := lookup.Width + 9;
  end;
end;

function TTMSFMXTableView.GetMarkButton: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemmarkbutton');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetMoveButton: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemmovebutton');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetPositionTo: Single;
begin
  if not (csDesigning in ComponentState) then
  begin
    if FPosTo < 0 then
      FPosTo := 0
    else if FPosTo > GetFullHeight then
      FPosTo := GetFullHeight;

    Result := FPosTo;
  end
  else
    Result := 0;
end;

procedure TTMSFMXTableView.InitCategories;
var
  I, j: Integer;
  s: String;
  c: Integer;
  l: Integer;
begin
  if (csDestroying in ComponentState) then
    Exit;

  {$IFDEF DELPHI_LLVM}
  l := 0;
  {$ELSE}
  l := 1;
  {$ENDIF}

  case CategoryType of
    ctAlphaBetic, ctAlphaNumericFirst, ctAlphaNumericLast:
    begin
      for I := 1 to 36 do
        FChar[I] := false;

      for I := 0 to Items.Count - 1 do
      begin
          S := AnsiUpperCase(Items[I].GetStrippedCaption);
          if (S <> '') then
          begin
            if (Ord(s[l]) <= 90) then
            begin
              j := Ord(s[l]) - 64;
              if (j < 27) and (J > 0) then
              begin
                if (CategoryType = ctAlphaNumericLast) or (CategoryType = ctAlphaBetic) then
                  FChar[j] := true
                else
                  FChar[10 + j] := true;
              end
              else
              begin
                j := Ord(s[l]) - 48;
                if (j < 11) and (j >= 0) then
                begin
                if (CategoryType = ctAlphaNumericLast) or (CategoryType = ctAlphaBetic) then
                    FChar[27 + j] := true
                  else
                    FChar[j + 1] := true;
                end;
              end;
            end
            else
            begin
              c := 0;
              if CategoryType = ctAlphaNumericFirst then
                c := 10;

              if (AnsiCompareStr('A',S) < 0) and (AnsiCompareStr('B',S) > 0) then
                FChar[1+c] := true
              else
              if (AnsiCompareStr('C',S) < 0) and (AnsiCompareStr('D',S) > 0) then
                FChar[3+c] := true
              else
              if (AnsiCompareStr('E',S) < 0) and (AnsiCompareStr('F',S) > 0) then
                FChar[5+c] := true
              else
              if (AnsiCompareStr('G',S) < 0) and (AnsiCompareStr('H',S) > 0) then
                FChar[7+c] := true
              else
              if (AnsiCompareStr('H',S) < 0) and (AnsiCompareStr('I',S) > 0) then
                FChar[8+c] := true
              else
              if (AnsiCompareStr('I',S) < 0) and (AnsiCompareStr('J',S) > 0) then
                FChar[9+c] := true
              else
              if (AnsiCompareStr('J',S) < 0) and (AnsiCompareStr('K',S) > 0) then
                FChar[10+c] := true
              else
              if (AnsiCompareStr('K',S) < 0) and (AnsiCompareStr('L',S) > 0) then
                FChar[11+c] := true
              else
              if (AnsiCompareStr('L',S) < 0) and (AnsiCompareStr('M',S) > 0) then
                FChar[12+c] := true
              else
              if (AnsiCompareStr('N',S) < 0) and (AnsiCompareStr('N',S) > 0) then
                FChar[14+c] := true
              else
              if (AnsiCompareStr('O',S) < 0) and (AnsiCompareStr('P',S) > 0) then
                FChar[15+c] := true
              else
              if (AnsiCompareStr('P',S) < 0) and (AnsiCompareStr('Q',S) > 0) then
                FChar[16+c] := true
              else
              if (AnsiCompareStr('R',S) < 0) and (AnsiCompareStr('S',S) > 0) then
                FChar[18+c] := true
              else
              if (AnsiCompareStr('S',S) < 0) and (AnsiCompareStr('T',S) > 0) then
                FChar[19+c] := true
              else
              if (AnsiCompareStr('T',S) < 0) and (AnsiCompareStr('U',S) > 0) then
                FChar[20+c] := true
              else
              if (AnsiCompareStr('U',S) < 0) and (AnsiCompareStr('V',S) > 0) then
                FChar[21+c] := true
              else
              if (AnsiCompareStr('W',S) < 0) and (AnsiCompareStr('X',S) > 0) then
                FChar[22+c] := true
              else
              if (AnsiCompareStr('Z',S) < 0) then
                FChar[26+c] := true
            end;
          end;
        end;
      end;
    ctCustom:
    begin
      for I := 0 to Categories.Count - 1 do
      begin
        SetLength(FCustomChar, I + 1);
        FCustomChar[I] := false;
      end;

      for I := 0 to Items.Count - 1 do
      begin
        S := Items[I].GetStrippedCaption;
        if (S <> '') then
        begin
          j := Items[I].CategoryID;
          if (j < Categories.Count) and (J > -1) then
            FCustomChar[j] := true
        end;
      end;
    end;
  end;
end;

procedure TTMSFMXTableView.InPlaceEditExit(Sender: TObject);
begin
  UpdateItemInplaceEdit;
  HideInplaceEdit;
end;

procedure TTMSFMXTableView.InternalScrollItem(AItemIndex: Integer;
  SelectItem: Boolean; ASelectionMode: TTMSFMXTableViewSelectionMode);
var
  i: integer;
  bfStart, bfStop, bfStopReal: Integer;
  it: TTMSFMXTableViewItem;
  isloaded: Boolean;
begin
  i := AItemIndex;

  if (i >= 0) and (i <= GetCountItems) then
  begin
    it := GetItem(I);

    isloaded := IsItemInItemList(it);
    if not isloaded then
    begin
      if (i < GetBufferStart) and (i >= BufferSize - 1) then
        i := i - BufferSize + 1;

      bfStart := i;
      bfStop := i + BufferSize - 1;

      bfStopReal := Min(bfStop, GetCountItems);
      bfStart := bfStart - (bfStop - bfStopReal);

      if BufferSize > 0 then
      begin
        LoadBufferBetween(bfStart, bfStopReal);
        UpdateTableViewItems;
      end;
    end;

    if not IsItemVisibleInList(it) then
      PositionItem(it);

    if SelectItem then
      ProcessItem(it, ASelectionMode, False);
  end;
end;

function TTMSFMXTableView.IsCategory(AObject: TFmxObject): Boolean;
begin
  Result := Assigned(AObject) and (AObject.ClassType = TTMSFMXTableViewCategoryShape);
end;

function TTMSFMXTableView.isDetailMode: Boolean;
begin
  Result := FDetailViewVisible;
end;

function TTMSFMXTableView.isEditMode: Boolean;
begin
  Result := FEditMode;
end;

function TTMSFMXTableView.isFiltered: Boolean;
begin
  Result := ((Filtering = vfFilterStart) or (Filtering = vfFilterRandom)) and (FFilter <> '');
end;

function TTMSFMXTableView.IsItem(AObject: TFmxObject): Boolean;
begin
  Result := Assigned(AObject) and (AObject.ClassType = TTMSFMXTableViewItemShape);
end;

function TTMSFMXTableView.IsItemInItemList(
  AItem: TTMSFMXTableViewItem): Boolean;
var
  i: integer;
begin
  Result := False;
  if Assigned(FItemList) then
  begin
    for I := 0 to FItemList.Count - 1 do
    begin
      if AsItem(FItemList[I]).Item = AItem then
      begin
        Result := True;
        Break;
      end;
    end;
  end;
end;

function TTMSFMXTableView.IsItemVisibleInList(
  AItem: TTMSFMXTableViewItem): Boolean;
var
  i: integer;
  ypos: Single;
  backgroundelement: TControl;
  shp: TTMSFMXTableViewItemShape;
begin
  backgroundelement := GetListBackGround;
  Result := False;
  if Assigned(backgroundelement) then
  begin
    if Assigned(FItemList) then
    begin
      for I := 0 to FItemList.Count - 1 do
      begin
        if AsItem(FItemList[I]).Item = AItem then
        begin
          shp := AItem.Shape;
          if Assigned(shp) then
          begin
            ypos := shp.Position.Y - GetCurrentPos;
            Result := (ypos > 0) and (ypos + shp.Height < backgroundElement.height);
            Break;
          end;
        end;
      end;
    end;
  end;
end;

function TTMSFMXTableView.ItemAtXY(X, Y: Single): TTMSFMXTableViewItem;
var
  innerelement: TControl;
  i: integer;
  ctrl: TControl;
begin
  Result := nil;
  innerelement := GetListContainer;
  if Assigned(innerelement) then
  begin
    if Assigned(FItemList) then
    begin
      for I := 0 to FItemList.Count - 1 do
      begin
        ctrl := TControl(FItemList.Items[I]);
        if Assigned(ctrl) then
        begin
         if PointInRect(PointF(X, Y + GetCurrentPos), ctrl.ParentedRect) and IsItem(ctrl) then
          begin
           Result := AsItem(ctrl).Item;
            Break;
          end;
        end;
      end;
    end;
  end;
end;

function TTMSFMXTableView.ItemFromDifferentCategory(item1,
  item2: TTMSFMXTableViewItem): Boolean;
begin
  Result := false;
  if (item1 = nil) or (item2 = nil) then
  begin
    Result := true
  end
  else
  begin
    case CategoryType of
      ctAlphaBetic, ctAlphaNumericFirst, ctAlphaNumericLast:
      begin
        if (Length(item1.GetStrippedCaption) > 0) and (Length(item2.GetStrippedCaption) > 0) then
        begin
          {$IFDEF DELPHI_LLVM}
          Result := (Item1.GetStrippedCaption[0] <> item2.GetStrippedCaption[0]);
          {$ELSE}
          Result := (Item1.GetStrippedCaption[1] <> item2.GetStrippedCaption[1]);
          {$ENDIF}
        end
        else
        begin
          if (Length(item1.GetStrippedCaption) = 0) and (Length(item2.GetStrippedCaption) = 0) then
            Result := false
          else
            Result := true;
        end;
      end;
      ctCustom:
      begin
        if (item1.CategoryID > -1) and (item2.CategoryID > -1) then
        begin
          Result := (Item1.CategoryID <> item2.CategoryID);
        end
        else
        begin
          if (item1.CategoryID = -1) and (item2.CategoryID = -1) then
            Result := false
          else
            Result := true;
        end;
      end;
    end;
  end;
end;

procedure TTMSFMXTableView.ListClick(Sender: TObject);
begin
  if Assigned(OnClick) then
    OnClick(Self);
end;

procedure TTMSFMXTableView.ListDblClick(Sender: TObject);
begin
  if Assigned(OnDblClick) then
    OnDblClick(Self);
end;

procedure TTMSFMXTableView.ListKeyDown(Sender: TObject; var Key: Word; var KeyChar: WideChar;
  Shift: TShiftState);
var
  selmode: TTMSFMXTableViewSelectionMode;
  bfsize: Integer;
begin
  if FEditMode then
    Exit;

  bfsize := BufferSize;
  if bfsize = 0 then
    bfsize := 10;

  selmode := smSingleSelect;
  if ssCtrl in Shift then
    selmode := smMultiCtrlSelect
  else if ssShift in Shift then
    selmode := smMultiShiftSelect;

  case Key of
    vkSpace:
    begin
      //show detail
      if (SelectedItemIndex >= 0) and (SelectedItemIndex <= GetCountItems) then
        ProcessItem(GetItem(FFocusedItemIndex));
    end;
    vkUp, vkLeft: InternalScrollItem(FFocusedItemIndex - 1, True, selmode);
    vkDown, vkRight: InternalScrollItem(FFocusedItemIndex + 1, True, selmode);
    vkPrior: InternalScrollItem(Max(0, FFocusedItemIndex - bfsize), True, selmode);
    vkNext: InternalScrollItem(Min(GetCountItems, FFocusedItemIndex + bfsize), True, selmode);
    vkHome: InternalScrollItem(0, True, selmode);
    vkEnd: InternalScrollItem(GetCountItems, True, selmode);
  end;
end;

procedure TTMSFMXTableView.UpdateDefaultCategory;
var
  cat: TTMSFMXTableViewCategoryShape;
begin
  cat := GetDefaultCategory as TTMSFMXTableViewCategoryShape;
  if Assigned(cat) then
  begin
    case LayoutMode of
      lmNormal:
      begin
        {$IFDEF DELPHIXE6_LVL}
        cat.Fill.Kind := TBrushKind.Gradient;
        cat.Stroke.Kind := TBrushKind.Solid;
        {$ELSE}
        cat.Fill.Kind := TBrushKind.bkGradient;
        cat.Stroke.Kind := TBrushKind.bkSolid;
        {$ENDIF}
        cat.Margins.Left := 0;
        cat.Margins.Right := 0;
        cat.Margins.Top := 0;
        cat.Margins.Bottom := 0;
      end;
      lmGroup:
      begin
        {$IFDEF DELPHIXE6_LVL}
        cat.Fill.Kind := TBrushKind.None;
        cat.Stroke.Kind := TBrushKind.None;
        {$ELSE}
        cat.Fill.Kind := TBrushKind.bkNone;
        cat.Stroke.Kind := TBrushKind.bkNone;
        {$ENDIF}
        cat.Margins.Left := 25;
        cat.Margins.Right := GetLookUpBarWidth;
        cat.Margins.Top := 5;
        cat.Margins.Bottom := 5;
      end;
    end;
  end;
end;

procedure TTMSFMXTableView.UpdateDefaultItem;
var
  it: TControl;
begin
  it := GetDefaultItem;
  if Assigned(it) then
  begin
    case LayoutMode of
      lmNormal:
      begin
        it.Margins.Left := 0;
        it.Margins.Right := 0;
      end;
      lmGroup:
      begin
        it.Margins.Left := 25;
        it.Margins.Right := GetLookUpBarWidth;
      end;
    end;
  end;
end;

procedure TTMSFMXTableView.UpdateDefaultItemStyle;
var
  detail, blb, edit: TControl;
begin
  blb := GetItemBulbRectangle;
  edit := GetItemEditRectangle;
  detail := GetItemDetailRectangle;
  if Assigned(blb) and Assigned(edit) and Assigned(detail) then
  begin
    detail.Visible := False;
    blb.Visible := False;
    edit.Visible := False;
  end;
end;

procedure TTMSFMXTableView.UpdateDetailContainerGrid;
var
  detailContainer: TControl;
  container: TControl;
  background: TControl;
begin
  if (csDesigning in ComponentState) or (csLoading in ComponentState) then
    Exit;

  detailContainer := GetListDetailContainer;
  container := GetListContainer;
  background := GetListBackGround;

  if Assigned(detailContainer) and Assigned(container) and Assigned(background) then
  begin  
    if FDetailViewVisible then
    begin
      detailcontainer.Position.X := 0;
      detailcontainer.Position.Y := 0; 
      detailcontainer.Width  := Width;
//      container.Align := TAlignLayout.alNone;
      container.Width := Width;     
      container.Position.X := -container.Width;
//      container.Position.Y := 0;
      detailcontainer.BringToFront;             
    end    
    else
    begin
      detailcontainer.SendToBack;
//      container.Align := TAlignLayout.alClient;
      container.Position.X := 0;
      container.Position.Y := 0;          
      detailcontainer.Position.X := Width;
      detailcontainer.Position.Y := 0;
      UpdateTableView;
    end;
  end;
end;

procedure TTMSFMXTableView.UpdateEditButton;
begin
  if FEditButton then
    ShowEditButton
  else
    HideEditButton;

  UpdateFilter;
end;

procedure TTMSFMXTableView.UpdateEditButtons;
var
  btnMark, btnMove, btnArchive: TTMSFMXBarButton;
  selcount: integer;
begin
  btnMark := GetMarkButton as TTMSFMXBarButton;
  btnMove := GetMoveButton as TTMSFMXBarButton;
  btnArchive := GetArchiveButton as TTMSFMXBarButton;
  if Assigned(btnMark) and Assigned(btnMove) and Assigned(btnArchive) then
  begin
    selcount := CountSelected;
    btnMove.Enabled := selcount > 0;
    btnArchive.Enabled := selcount > 0;
    btnMark.Enabled := selcount > 0;
    if selcount > 0 then
    begin
      btnMark.Text := MarkText + ' ('+inttostr(selcount) + ')';
      btnMove.Text := MoveText +' ('+inttostr(selcount) + ')';
      btnArchive.Text := ArchiveText + ' ('+inttostr(selcount) + ')';
    end
    else
    begin
      btnMark.Text := MarkText;
      btnMove.Text := MoveText;
      btnArchive.Text := ArchiveText;
    end;

    btnMark.Repaint;
    btnMove.Repaint;
    btnArchive.Repaint;
  end;
end;

procedure TTMSFMXTableView.UpdateFilter;
var
  headersearch: TControl;
  edb: TTMSFMXBarButton;
begin
  headersearch := GetHeaderSearch;
  edb := GetEditButton as TTMSFMXBarButton;
  if Assigned(headersearch) then
  begin
    headersearch.Visible := ShowFilter;
    if headersearch.Visible then
    begin
      if Assigned(OnSearchOpen) then
        OnSearchOpen(Self);
    end
    else
    begin
      if Assigned(OnSearchClose) then
        OnSearchClose(Self);
    end;
  end;

  if FEditMode then
  begin
    FEditMode := False;
    ToggleEditMode(False);
  end;

  if Assigned(edb) then
  begin
    edb.Visible := ShowFilter or EditButton;
    if ShowFilter then
      edb.Kind := bkCancel
    else
      edb.Kind := bkEdit;
  end;
end;

procedure TTMSFMXTableView.UpdateFooterText;
var
  shptxt: TText;
begin
  shptxt := GetFooterText;
  if Assigned(shptxt) then
  begin
    shptxt.Text := FooterText;
    shptxt.Repaint;
  end;
end;

procedure TTMSFMXTableView.UpdateHeaderText;
var
  shptxt: TText;
begin
  shptxt := GetHeaderText;
  if Assigned(shptxt) then
  begin
    shptxt.Text := HeaderText;
    shptxt.Repaint;
  end;
end;

procedure TTMSFMXTableView.UpdateItemInplaceEdit;
var
  ipl: TTMSFMXSearchEdit;
begin
  ipl := GetInplaceEdit;
  if Assigned(ipl) then
  begin
    if ipl.Visible and (ipl.Tag >= 0) and (ipl.Tag <= Items.Count - 1) then
    begin
      case ipl.Mode of
        emCaption: Items[ipl.Tag].Caption := ipl.Text;
        emDescription: Items[ipl.Tag].Description := ipl.Text;
      end;
    end;
  end;
end;

procedure TTMSFMXTableViewItem.UpdateAlignments;
var
  right, del, bulb, detail, place: TControl;
  rv, dv, bv, dtv, plv: Boolean;
begin
  right := ShapeRightRectangle;
  del := ShapeDeleteRectangle;
  bulb := ShapeBulbRectangle;
  detail := ShapeDetailRectangle;
  place := ShapePlaceHolder;
  if Assigned(right) and Assigned(del) and Assigned(bulb) and Assigned(detail) and Assigned(place) then
  begin
    {$IFDEF DELPHIXE6_LVL}
    right.Align := TAlignLayout.None;
    del.Align := TAlignLayout.None;
    bulb.Align := TAlignLayout.None;
    detail.Align := TAlignLayout.None;
    place.Align := TAlignLayout.None;
    {$ELSE}
    right.Align := TAlignLayout.alNone;
    del.Align := TAlignLayout.alNone;
    bulb.Align := TAlignLayout.alNone;
    detail.Align := TAlignLayout.alNone;
    place.Align := TAlignLayout.alNone;
    {$ENDIF}

    rv := right.Visible;
    dv := del.Visible;
    bv := bulb.Visible;
    dtv := detail.Visible;
    plv := place.Visible;

    right.Visible := False;
    del.Visible := False;
    bulb.Visible := False;
    detail.Visible := False;
    place.Visible := False;

    right.Position.X := 0;
    del.Position.X := 0;
    bulb.Position.X := 0;
    detail.Position.X := 0;
    place.Position.X := 0;

    {$IFDEF DELPHIXE6_LVL}
    place.Align := TAlignLayout.Right;
    del.Align := TAlignLayout.Right;
    detail.Align := TAlignLayout.Right;
    bulb.Align := TAlignLayout.Right;
    right.Align := TAlignLayout.Right;
    {$ELSE}
    place.Align := TAlignLayout.alRight;
    del.Align := TAlignLayout.alRight;
    detail.Align := TAlignLayout.alRight;
    bulb.Align := TAlignLayout.alRight;
    right.Align := TAlignLayout.alRight;
    {$ENDIF}

    bulb.Visible := bv;
    detail.Visible := dtv;
    right.Visible := rv;
    place.Visible := plv;
    del.Visible := dv;
  end;
end;

procedure TTMSFMXTableViewItem.UpdateBitmap;
var
  shpimg: TTMSFMXBitmap;
begin
  shpimg := ShapeLeftRectangleImage;
  if Assigned(shpimg) then
  begin
    shpimg.BitmapContainer := FOwner.BitmapContainer;
    shpimg.BitmapName := BitmapName;
    shpimg.Bitmap.Assign(Bitmap);
  end;
end;

procedure TTMSFMXTableViewItem.UpdateBulb;
var
  shptxt: TTMSFMXHTMLText;
  shp: TTMSFMXTableViewBulbShape;
begin
  shptxt := ShapeBulbText;
  shp := ShapeBulbRectangle;
  if Assigned(shp) and Assigned(shptxt) then
  begin
    shp.Item := Self;
    shp.Visible := BulbText <> '';
    if shp.Visible then
    begin
      shptxt.Text := BulbText;
      shp.Width := Max(30, shptxt.GetTextWidth + 6);
    end;
    shp.UpdateControls;
  end;

  UpdateAlignments;
end;

procedure TTMSFMXTableViewItem.UpdateCaption;
var
  shp: TText;
begin
  shp := ShapeCaption;
  if Assigned(shp) then
    shp.Text := FCaption;
end;

procedure TTMSFMXTableViewItem.UpdateItemIndent;
var
  shp: TTMSFMXTableViewItemShape;
begin
  shp := Shape;
  if Assigned(shp) then
  begin
    if FOwner.LayoutMode = lmGroup then
    begin
      shp.Margins.Left := 25 + LeftMargin;
      shp.Margins.Right := FOwner.GetLookUpBarWidth + RightMargin;
    end
    else
    begin
      shp.Margins.Left := LeftMargin;
      shp.Margins.Right := RightMargin
    end;
  end;
end;

procedure TTMSFMXTableViewItem.UpdateDescription;
var
  shp: TText;
begin
  shp := ShapeDescription;
  if Assigned(shp) then
    shp.Text := FDescription;
end;

procedure TTMSFMXTableViewItem.UpdateDetailContainer;
var
  detailContainer: TControl;

  background: TControl;
  dtv: TControl;
begin
  if (csDesigning in FOwner.ComponentState) or (csLoading in FOwner.ComponentState) then
    Exit;

  detailContainer := FOwner.GetListDetailContainer;
  background := FOwner.GetListBackGround;

  if Assigned(DetailView) and Assigned(detailContainer) and Assigned(background) then
  begin
    detailcontainer.Width := background.Width;
    detailcontainer.Height := background.Height;
    detailcontainer.Position.Y := 0;//background.Position.Y;
    detailcontainer.Position.X := background.Position.X + background.Width;

    detailcontainer.BringToFront;
    
    dtv := detailView;
    dtv.Visible := True;
    {$IFDEF DELPHIXE6_LVL}
    dtv.Align := TAlignLayout.Client;
    {$ELSE}
    dtv.Align := TAlignLayout.alClient;
    {$ENDIF}
    detailcontainer.InsertObject(detailcontainer.ChildrenCount, dtv);
    dtv.BringToFront;
  end;
end;

procedure TTMSFMXTableViewItem.UpdateDetailDisclosure;
var
  detaildisclosure: TRectangle;
begin
  detaildisclosure := ShapeDetailRectangle;
  if Assigned(detaildisclosure) then
    detaildisclosure.Visible := Assigned(DetailView);

  UpdateAlignments;
end;

procedure TTMSFMXTableView.UpdateLookupBar;
var
  lookup, background: TControl;
  placeholder, place: TControl;
  scroll: TControl;
  i: integer;
begin
  lookup := GetLookupBar;
  background := GetListBackGround;
  placeholder := GetListPlaceHolder;
  scroll := GetScrollBar;
  if Assigned(placeholder) and Assigned(scroll) and Assigned(lookup) and (lookup is TTMSFMXTableViewLookupBarShape) then
  begin
    (lookup as TTMSFMXTableViewLookupBarShape).FTableView := Self;
    lookup.Position.X := background.Width - lookup.Width - 4;
    lookup.Position.Y := 8;
    lookup.Height := background.Height - 16;
    lookup.Visible := LookupBar and (CategoryType <> ctNone) and not isFiltered and not FDetailViewVisible;
    placeholder.Width := lookup.Width - 8;
    if ScrollIndicator <> siNone then
      placeholder.Width := placeholder.Width + scroll.Width + 1;

    placeholder.Visible := lookup.Visible and (LayoutMode = lmNormal);
    if Assigned(FItemList) then
    begin
      for I := 0 to FItemList.Count - 1 do
      begin
        if IsItem(FItemList.Items[I]) then
        begin
          place := AsItem(FItemList.Items[I]).Item.ShapePlaceHolder;
          if Assigned(place) then
          begin
            place.Width := placeholder.Width;
            place.Visible := placeholder.Visible;
          end;
        end;
      end;
    end;
  end;
end;

procedure TTMSFMXTableView.UpdateListItem(AItem: TTMSFMXTableViewItem;
  itemElement, defaultitemElement: TControl);
var
  shpr: TRectangle;
  shpb: TButton;
  shptxt: TText;
begin
  if itemElement.ChildrenCount > 0 then
    Exit;

  CloneChildren(defaultitemElement, itemElement);

  shpr := AItem.ShapeDeleteRectangle;
  if Assigned(shpr) then
  begin
    if not AItem.DeleteButton then
      shpr.Width := 0
    else
      shpr.Width := 60;
  end;

  shpb := AItem.ShapeDeleteButton;
  if Assigned(shpb) then
    shpb.OnClick := AItem.ShapeDeleteButtonClick;

  shptxt := AItem.ShapeCaption;
  if Assigned(shptxt) and (shptxt is TTMSFMXHTMLText) then
  begin
    shptxt.Text := AItem.Caption;
    (shptxt as TTMSFMXHTMLText).OnAnchorClick := AItem.DoAnchorClickItemCaption;
  end;

  shptxt := AItem.ShapeDescription;
  if Assigned(shptxt) and (shptxt is TTMSFMXHTMLText) then
  begin
    shptxt.Text := AItem.Description;
    (shptxt as TTMSFMXHTMLText).OnAnchorClick := AItem.DoAnchorClickItemDescription;
  end;

  AItem.UpdateBulb;
  AItem.UpdateItemIndent;
  AItem.UpdateDetailDisclosure;
  AItem.UpdateCaption;
  AItem.UpdateDescription;

  AItem.AssignBitmapContainer;
  AItem.UpdateBitmap;

  if FEditMode and AItem.CanDelete then
    AItem.ShowEditButton
  else
    AItem.HideEditButton;

  AItem.ApplyEx;

  AItem.DoCustomize;
end;

procedure TTMSFMXTableView.UpdateListPosition;
var
  innerelement: TControl;
  y: Single;
begin
  innerElement := GetListContainer;
  if Assigned(innerElement) then
  begin
    y := -GetCurrentPos;
    innerelement.Position.Y := Y;
  end;

  UpdateCategoryPosition;

//  RefreshTableViewItems;

  if Assigned(OnScroll) then
    OnScroll(Self, GetCurrentPos);
end;

procedure TTMSFMXTableView.UpdateCategoryPosition;
var
  innerElement, backgroundElement: TControl;
  y, ycat: Single;
  I: Integer;
  cat, catfloat, nextcatfloat: TTMSFMXTableViewCategoryShape;
begin
  if LayoutMode = lmNormal then
  begin
    innerElement := GetListContainer;
    backgroundElement := GetListBackGround;
    if Assigned(innerElement) and Assigned(backgroundElement) then
    begin
      y := -GetCurrentPos;

      catfloat := nil;
      for I := 0 to FCategoryList.Count - 1 do
      begin
        cat := TTMSFMXTableViewCategoryShape(FCategoryList.Items[I]);
        ycat := cat.OriginalPos.Y;
        if ycat + y < 0 then
          catfloat := cat;
      end;

      for I := 0 to FCategoryList.Count - 1 do
      begin
        cat := TTMSFMXTableViewCategoryShape(FCategoryList.Items[I]);
        if cat <> catfloat then
          cat.Position.Y := cat.OriginalPos.Y;
      end;

      if Assigned(catfloat) then
      begin
        ycat := catfloat.OriginalPos.Y;
        if ycat + y < 0 then
          catfloat.Position.Y := -y
        else
          catfloat.Position.Y := catfloat.OriginalPos.Y
      end;

      if Assigned(catfloat) and (FCategoryList.IndexOf(catfloat) < FCategoryList.Count - 1) then
      begin
        nextcatfloat := TTMSFMXTableViewCategoryShape(FCategoryList.Items[FCategoryList.IndexOf(catfloat) + 1]);
        ycat := nextcatfloat.OriginalPos.Y;
        if ycat + y < catfloat.Height then
        begin
          catfloat.Position.Y := nextcatfloat.Position.Y - catfloat.Height;
        end;
      end;
    end;
  end;
  UpdateScrollBar;  
end;

procedure TTMSFMXTableView.UpdateScrollBar;
var
  scrollbar, background: TControl;
  h, pos, scpos, dfh: Single;
  defitem, defcat: TControl;
begin
  scrollbar := GetScrollBar;
  background := GetListBackGround;
  defitem := GetDefaultItem;
  defcat := GetDefaultCategory;
  if Assigned(scrollbar) and (scrollbar is TTMSFMXTableViewScrollBarShape)
    and Assigned(defitem) and assigned(defcat) then
  begin
    if scrollbar.Visible and (ScrollIndicator <> siNone) then
    begin
      h := 0;
      dfh := GetFullScrollBarHeight;
      if (dfh > 0) and (background.Height > 0) then
        h := (background.Height / dfh) * background.Height;

      h := Max(40, h);

      pos := 0;
      if (GetCountItems > 0) then
        scpos := GetCurrentPos + (dfh * GetBufferStart / (GetCountItems + 1))
      else
        scpos := 0;

      if (scpos > 0) and (dfh > 0) then
        pos := (background.Height - h) / (dfh - background.Height) * scpos;

      (scrollbar as TTMSFMXTableViewScrollBarShape).PageSize := h;
      (scrollbar as TTMSFMXTableViewScrollBarShape).Position := pos;
    end;
  end;
end;

procedure TTMSFMXTableView.UpdateScrollBarVisuals;
var
  scrollbar, background, lookup: TControl;
begin
  scrollbar := GetScrollBar;
  background := GetListBackGround;
  lookup := GetLookupBar;
  if Assigned(lookup) and Assigned(scrollbar) and (scrollbar is TTMSFMXTableViewScrollBarShape) then
  begin
    scrollbar.Width := 5;
    scrollbar.Position.X := background.Width - scrollbar.Width - 5;
    if lookup.Visible then
      scrollbar.Position.X := scrollbar.Position.X - lookup.Width;

    scrollbar.Position.Y := 0;
    scrollbar.Height := background.Height;
    scrollbar.Visible := (ScrollIndicator <> siNone) and ((GetCountItems + 1) > 0);
    if ScrollIndicator = siFade then
      (scrollbar as TTMSFMXTableViewScrollBarShape).ElementOpacity := 0
    else
      (scrollbar as TTMSFMXTableViewScrollBarShape).ElementOpacity := 1;
  end;
end;

procedure TTMSFMXTableView.UpdateTableView;
begin
  UpdateTableViewItems;
end;

procedure TTMSFMXTableView.UpdateTableViewItems;
var
  I, J: Integer;
  categoryElement, itemElement, innerElement: TControl;
  y: Single;
  chk: Boolean;
  bufstart, bufstop: Integer;
  str: String;
begin
  if (csDestroying in ComponentState) or (csLoading in ComponentState) or (FUpdateCount > 0) or not Assigned(FItemList) then
    Exit;

  BeginUpdate;

  HideInplaceEdit;

  FFilterObjectList.Clear;
  if isFiltered then
  begin
    for I := 0 to Items.Count - 1 do
    begin
      str := Items[i].GetStrippedCaption;
      chk := AnsiContainsText(str, FFilter);
      if Filtering = vfFilterStart then
        chk := chk and AnsiStartsText(FFilter, str);

      if chk then
        FFilterObjectList.Add(Items[i]);
    end;
  end;

  if BufferSize > 0 then
  begin
    if (FBufferStart = -1) and (FBufferStop = -1) then
    begin
      FBufferStart := 0;
      FBufferStop := BufferSize - 1;
    end;
    bufstart := GetBufferStart;
    bufstop := GetBufferStop;
  end
  else
  begin
    bufstart := 0;
    bufstop := GetCountItems;
  end;

  FDisplayList.Clear;
  FItemList.Clear;
  FCategoryList.Clear;
  InitCategories;

  innerElement := GetListContainer;
  itemElement := GetDefaultItem;
  categoryElement := GetDefaultCategory;

  CleanUpItems(innerelement);
  CleanUpCategories(innerelement);

  if Assigned(innerElement) and Assigned(itemElement) and Assigned(categoryElement) then
  begin
    y := 0;
    J := bufstart;
    for I := bufstart to bufstop do
    begin
      if (CategoryType <> ctNone) and not isFiltered then
      begin
        if FDisplayList.Count = 0 then
        begin
          AddListCategory(GetItem(I), categoryElement, innerElement, y);
          AddListItem(GetItem(I), itemElement, innerElement, y);
          Inc(J);
        end
        else
        begin
          if ItemFromDifferentCategory(GetItem(I), GetItem(J - 1)) then
          begin
            AddListCategory(GetItem(I), categoryElement, innerElement, y);
            AddListItem(GetItem(I), itemElement, innerElement, y);
            Inc(J);
          end
          else
          begin
            AddListItem(GetItem(I), itemElement, innerElement, y);
            Inc(J);
          end;
        end;
      end
      else
        AddListItem(GetItem(I), itemElement, innerElement, y);
    end;
  end;

//  RefreshTableViewItems;

  for I := 0 to FCategoryList.Count - 1 do
    TTMSFMXTableViewCategoryShape(FCategoryList[I]).BringToFront;

  if Assigned(innerelement) and (FDisplayList.Count > 0) then
    innerelement.Height := y;

  UpdateScrollBar;
  UpdateEditButtons;

  FBlockUpdate := True;
  EndUpdate;
  FBlockUpdate := False;

  UpdateListPosition;
end;

procedure TTMSFMXTableView.DeleteListItem(
  itemElement: TControl);
begin
  if Assigned(itemElement) then
    itemElement.DeleteChildren;
end;

procedure TTMSFMXTableView.DeSelectAllItems;
var
  I: Integer;
  it: TTMSFMXTableViewItem;
begin
  for I := FSelectedItems.Count - 1 downto 0 do
  begin
    it := (TTMSFMXTableViewItem(FSelectedItems[I]));
    it.Selected := False;
    FSelectedItems.Remove(it);
  end;
end;

destructor TTMSFMXTableView.Destroy;
begin
  FFilterObjectList.Free;
  FCategoryList.Free;
  FDisplayList.Free;
  FItemList.Free;
  FItems.Free;
  FSelectedItems.Free;
  FCategories.Free;
  inherited;
end;

procedure TTMSFMXTableView.DoAnchorClickHeader(Sender: TOBject;
  AAnchor: String);
begin
  if Assigned(OnHeaderAnchorClick) then
    OnHeaderAnchorClick(Sender, AAnchor);
end;

procedure TTMSFMXTableView.DoEnter;
begin
  inherited;
  SetFocusOnBackGround;
end;

procedure TTMSFMXTableView.DoAnchorClickCategory(Sender: TObject;
  AAnchor: String);
var
  achar: String;
  aId: Integer;
  s: TTMSFMXHTMLText;
  t: TTMSFMXTableViewCategoryShape;
begin
  achar := '';
  aid := -1;
  if Sender is TTMSFMXHTMLText then
  begin
    t := nil;
    s := Sender as TTMSFMXHTMLText;
    if Assigned(s) and (s.Parent is TTMSFMXTableViewCategoryShape) then
      t := s.Parent as TTMSFMXTableViewCategoryShape;

    if Assigned(t) then
    begin
      achar := t.Character;
      aid := t.CategoryID;
    end;
  end;

  if Assigned(OnCategoryAnchorClick) then
    OnCategoryAnchorClick(Sender, achar, aid, AAnchor);
end;

procedure TTMSFMXTableView.DoAnchorClickFooter(Sender: TOBject;
  AAnchor: String);
begin
  if Assigned(OnFooterAnchorClick) then
    OnFooterAnchorClick(Sender, AAnchor);
end;

function TTMSFMXTableView.DoesCategoryExist(innerelement: TControl;
  CategoryName: String): TTMSFMXTableViewCategoryShape;
var
  i: integer;
  ctrl: TFMXObject;
  l: TTMSFMXTableViewCategoryShape;
begin
  Result := nil;
  if Assigned(innerElement) then
  begin
    for I := 0 to innerElement.ChildrenCount - 1 do
    begin
      ctrl := innerElement.Children[i];
      if IsCategory(ctrl) then
      begin
        l := AsCategory(ctrl);
        if UpperCase(l.StyleName) = UpperCase(CategoryName) then
        begin
          Result := l;
          Break;
        end;
      end;
    end;
  end;
end;

function TTMSFMXTableView.DoesItemExist(innerelement: TControl; ItemName: String): TTMSFMXTableViewItemShape;
var
  i: integer;
  ctrl: TFMXObject;
  l: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  if Assigned(innerElement) then
  begin
    for I := 0 to innerElement.ChildrenCount - 1 do
    begin
      ctrl := innerElement.Children[i];
      if IsItem(ctrl) then
      begin
        l := AsItem(ctrl);
        if UpperCase(l.StyleName) = UpperCase(ItemName) then
        begin
          Result := l;
          Break;
        end;
      end;
    end;
  end;
end;

procedure TTMSFMXTableView.ApplyFilter(AFilter: String);
var
  str: String;
  allow: Boolean;
begin
  str := AFilter;
  allow := true;
  case Filtering of
    vfFilterStart, vfFilterRandom:
    begin
      if Assigned(OnBeforeFilter) then
        OnBeforeFilter(Self, str, allow);
    end;
    vfSearch:
    begin
      if Assigned(OnBeforeSearch) then
        OnBeforeSearch(Self, str, allow);
    end;
  end;

  if allow then
    DoFilter(str);
end;

procedure TTMSFMXTableView.DoLoadNextBuffer;
begin
  LoadNextBuffer;
  UpdateTableViewItems;
  StopScrollAnimation;
  FCurrentPos := 0;
  FPosTo := FCurrentPos;
  Fsp := 0;
  UpdateListPosition;
end;

procedure TTMSFMXTableView.DoLoadPreviousBuffer;
begin
  LoadPreviousBuffer;
  UpdateTableViewItems;
  StopScrollAnimation;
  FCurrentPos := GetFullHeight;
  FPosTo := FCurrentPos;
  Fsp := 0;
  UpdateListPosition;
end;

procedure TTMSFMXTableView.EditButtonClick(Sender: TObject);
var
  edit: TTMSFMXSearchEdit;
begin
  if not FEditMode and ((Sender as TTMSFMXBarButton).Kind = bkCancel) then
  begin
    ShowFilter := False;
    UpdateLookupBar;
    edit := GetSearchEdit;
    if Assigned(edit) then
    begin
      if edit.Text <> '' then
      begin
        edit.Text := '';
        ApplyFilter('');
      end;
    end;
  end
  else
  begin
    FEditMode := not FEditMode;
    ToggleEditMode(FEditMode);
  end;
end;

procedure TTMSFMXTableView.EditMode;
begin
  FEditMode := True;
  ToggleEditMode(True);
end;

procedure TTMSFMXTableView.EndUpdate;
begin
  inherited;
  Dec(FUpdateCount);
  if not FBlockUpdate then
    UpdateTableViewItems;
end;

function TTMSFMXTableView.FindCategoryWithCharacter(
  ACategory: String): TTMSFMXTableViewCategoryShape;
var
  I: Integer;
  cat: TTMSFMXTableViewCategoryShape;
begin
  Result := nil;
  for I := 0 to FCategoryList.Count - 1 do
  begin
    if IsCategory(FCategoryList[I]) then
    begin
      cat := AsCategory(FCategoryList[I]);
      if cat.Character = ACategory then
      begin
        Result := cat;
        Break;
      end;
    end;
  end;
end;

function TTMSFMXTableView.FindCategoryWithCharacterID(
  ACategoryID: Integer): TTMSFMXTableViewCategoryShape;
var
  I: Integer;
  cat: TTMSFMXTableViewCategoryShape;
begin
  Result := nil;
  for I := 0 to FCategoryList.Count - 1 do
  begin
    if IsCategory(FCategoryList[I]) then
    begin
      cat := AsCategory(FCategoryList[I]);
      if cat.CategoryID = ACategoryID then
      begin
        Result := cat;
        Break;
      end;
    end;
  end;
end;

function TTMSFMXTableView.FindFirstItemWithCategory(ACategory: String): TTMSFMXTableViewItem;
var
  I: Integer;
begin
  Result := nil;
  for I := 0 to Items.Count - 1 do
  begin
    if GetCharacterForItem(Items[I]) = ACategory then
    begin
      Result := Items[I];
      Break;
    end;
  end;
end;

function TTMSFMXTableView.FindFirstItemWithCategoryID(
  ACategoryID: Integer): TTMSFMXTableViewItem;
var
  I: Integer;
begin
  Result := nil;
  for I := 0 to Items.Count - 1 do
  begin
    if Items[I].CategoryID = ACategoryID then
    begin
      Result := Items[I];
      Break;
    end;
  end;
end;

function TTMSFMXTableView.FindItemWithFilter(
  AFilter: String): TTMSFMXTableViewItem;
var
  i: integer;
begin
  Result := nil;
  for I := 0 to Items.Count - 1 do
  begin
    if AnsiContainsText(Items[I].GetStrippedCaption, AFilter) then
    begin
      Result := Items[I];
      Break;
    end;
  end;
end;

function TTMSFMXTableView.GetArchiveButton: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('itemarchivebutton');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetBackButton: TControl;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('headerbackbutton');
  if fmxobj is TControl then
    Result := fmxobj as TControl;
end;

function TTMSFMXTableView.GetBufferStart: Integer;
begin
  Result := Max(0, FBufferStart);
end;

procedure TTMSFMXTableView.AddListCategory(AItem: TTMSFMXTableViewItem; categoryElement, innerelement: TControl; var Y: Single);
var
  sn: String;
  newsec, sec: TTMSFMXTableViewCategoryShape;
  shptxt: TText;
begin
  sn := 'FMXTableViewCategory'+inttostr(FCategoryList.Count);
  sec := DoesCategoryExist(innerelement, sn);
  if Assigned(sec) then
  begin
    newsec := sec;
//    newsec.BeginUpdate;
  end
  else
  begin
    newsec := AsCategory(categoryElement.Clone(innerElement));
//    newsec.BeginUpdate;
    CloneChildren(categoryElement, newsec);
    if LayoutMode = lmGroup then
      {$IFDEF DELPHIXE6_LVL}
      newsec.Align := TAlignLayout.Top;
      {$ELSE}
      newsec.Align := TAlignLayout.alTop;
      {$ENDIF}

    newsec.HitTest := False;
    newsec.StyleName := sn;
//    newsec.Name := sn + 'Name';
    newsec.Visible := True;
  end;

  newsec.LookupItemIndex := AItem.Index;
  newsec.Position.Y := y;
  if LayoutMode = lmGroup then
    y := y + newsec.Margins.Top + newsec.Margins.Bottom;

  newsec.Position.X := 0;
  newsec.OriginalPos.X := newsec.Position.X;
  newsec.OriginalPos.Y := newsec.Position.Y;
  newsec.Width := innerelement.Width;
  innerElement.InsertObject(innerElement.ChildrenCount, newsec);
  FDisplayList.Add(newsec);
  FCategoryList.Add(newsec);

  newsec.CategoryID := GetCategoryIDForItem(AItem);
  newsec.Character := GetCharacterForItem(AItem);


  shptxt := newsec.ShapeCaption;
  if Assigned(shptxt) and (shptxt is TTMSFMXHTMLText) then
  begin
    shptxt.Text := newsec.Character;
    (shptxt as TTMSFMXHTMLText).OnAnchorClick := DoAnchorClickCategory;
  end;

  newsec.EndUpdate;

  if Assigned(OnCategoryCustomize) then
    OnCategoryCustomize(Self, newsec);

  y := y + newsec.Height;
end;

procedure TTMSFMXTableView.AddListItem(AItem: TTMSFMXTableViewItem; itemElement, innerelement: TControl; var Y: Single);
var
  sn: String;
  newsec, sec: TTMSFMXTableViewItemShape;
  shpr: TRectangle;
  shptxt: TText;
  shpb: TTMSFMXBarButton;
  i: integer;
begin
  if not AItem.Visible then
    Exit;

  sn := 'FMXTableViewItem'+inttostr(AItem.Index);
  sec := DoesItemExist(innerelement, sn);
  if Assigned(sec) then
  begin
    newsec := sec;
//    newsec.BeginUpdate;
  end
  else
  begin
    newsec := AsItem(itemElement.Clone(innerElement));
//    newsec.BeginUpdate;
    CloneChildren(itemElement, newsec);
    if LayoutMode = lmGroup then
      {$IFDEF DELPHIXE6_LVL}
      newsec.Align := TAlignLayout.Top;
      {$ELSE}
      newsec.Align := TAlignLayout.alTop;
      {$ENDIF}
    newsec.HitTest := False;
    newsec.StyleName := sn;
//    newsec.Name := sn + 'Name';
    newsec.Visible := True;
  end;

  newsec.Item := AItem;
  newsec.Position.Y := y;
  newsec.Position.X := 0;
  newsec.Width := innerelement.Width;

  innerElement.InsertObject(innerElement.ChildrenCount, newsec);
  newsec.EndUpdate;

  FDisplayList.Add(newsec);
  FItemList.Add(newsec);

  shpr := AItem.ShapeDeleteRectangle;
  if Assigned(shpr) then
  begin
    if not AItem.DeleteButton then
      shpr.Width := 0
    else
      shpr.Width := 60;
  end;

  shpb := AItem.ShapeDeleteButton;
  if Assigned(shpb) then
    shpb.OnClick := AItem.ShapeDeleteButtonClick;

  shptxt := AItem.ShapeCaption;
  if Assigned(shptxt) and (shptxt is TTMSFMXHTMLText) then
  begin
    shptxt.Text := AItem.Caption;
    (shptxt as TTMSFMXHTMLText).OnAnchorClick := AItem.DoAnchorClickItemCaption;
  end;

  shptxt := AItem.ShapeDescription;
  if Assigned(shptxt) and (shptxt is TTMSFMXHTMLText) then
  begin
    shptxt.Text := AItem.Description;
    (shptxt as TTMSFMXHTMLText).OnAnchorClick := AItem.DoAnchorClickItemDescription;
  end;

  if FSelectedItems.IndexOf(AItem) <> -1 then
    AItem.Selected := True;

  if FCategoryList.Count > 0 then
    AItem.CategoryShape := AsCategory(FcategoryList[FCategoryList.Count - 1]);

  if LayoutMode = lmGroup then
  begin
    AItem.Shape.Corners := AllCorners;
    AItem.Shape.Sides := AllSides;
    AItem.Shape.Margins.Bottom := 10;
    AItem.Shape.Margins.Top := 10;
    y := y + 20;
    i := GetItemIndexForIndex(AItem.Index);
    if (i - 1 >= 0) and (i - 1 <= GetCountItems) then
    begin
      //top
      if (GetItem(i - 1).GroupIndex = AItem.GroupIndex) and not ItemFromDifferentCategory(GetItem(i - 1), AItem) then
      begin
        {$IFDEF DELPHIXE6_LVL}
        AItem.Shape.Corners := AItem.Shape.Corners - [TCorner.TopLeft, TCorner.TopRight];
        AItem.Shape.Sides := AItem.Shape.Sides - [TSide.Top];
        {$ELSE}
        AItem.Shape.Corners := AItem.Shape.Corners - [TCorner.crTopLeft, TCorner.crTopRight];
        AItem.Shape.Sides := AItem.Shape.Sides - [TSide.sdTop];
        {$ENDIF}
        AItem.Shape.Margins.Top := 0;
        y := y - 10;
      end;
    end;

    if (i + 1 >= 0) and (i + 1 <= GetCountItems) then
    begin
     //bottom
      if (GetItem(i + 1).GroupIndex = AItem.GroupIndex) and not ItemFromDifferentCategory(GetItem(i + 1), AItem) then
      begin
        {$IFDEF DELPHIXE6_LVL}
        AItem.Shape.Corners := AItem.Shape.Corners - [TCorner.BottomLeft, TCorner.BottomRight];
        {$ELSE}
        AItem.Shape.Corners := AItem.Shape.Corners - [TCorner.crBottomLeft, TCorner.crBottomRight];
        {$ENDIF}
        AItem.Shape.Margins.Bottom := 0;
        y := y - 10;        
      end;
    end;
  end;

  AItem.UpdateBulb;
  AItem.UpdateItemIndent;
  AItem.UpdateDetailDisclosure;
  AItem.UpdateCaption;
  AItem.UpdateDescription;

  AItem.AssignBitmapContainer;
  AItem.UpdateBitmap;

  if FEditMode and AItem.CanDelete then
    AItem.ShowEditButton
  else
    AItem.HideEditButton;

  AItem.ApplyEx;

  AItem.DoCustomize;

  y := y + newsec.Height;
end;

procedure TTMSFMXTableView.ScrollAnimationFinished(Sender: TObject);
var
  innerelement: TControl;
begin
  FScrollAnimationStarted := False;
  innerelement := GetListContainer;

  if Assigned(innerelement) then
  begin
    FCurrentPos := -innerelement.Position.Y;
    FPosTo := FCurrentPos;
  end;
  HideScrollBar;

  if Assigned(OnScrollFinished) then
    OnScrollFinished(Self, GetCurrentPos);
end;

procedure TTMSFMXTableView.ScrollAnimationProcess(Sender: TObject);
var
  innerelement: TControl;
begin
  if (csDesigning in ComponentState) or (csDestroying in ComponentState) then
    Exit;

  innerelement := GetListContainer;

  if Assigned(innerelement) then
    FCurrentPos := -innerelement.Position.Y;

  UpdateCategoryPosition;

//  RefreshTableViewItems;

  if Assigned(OnScroll) then
    OnScroll(Self, GetCurrentPos);
end;

procedure TTMSFMXTableView.ShowBackButton(Show: Boolean);
var
  btn: TControl;
begin
  btn := GetBackButton;
  if Assigned(btn) then
    btn.Visible := Show and BackButton and not FEditMode;
end;

procedure TTMSFMXTableView.ShowEditButton;
var
  editbutton: TControl;
begin
  editbutton := GetEditButton;
  if Assigned(editbutton) then
    editbutton.Visible := True;
end;

procedure TTMSFMXTableView.ShowInplaceEdit;
var
  ipl: TTMSFMXSearchEdit;
begin
  ipl := GetInPlaceEdit;
  if Assigned(ipl) then
  begin
    if not ipl.Visible then
    begin
      ipl.Visible := True;
      ipl.SetFocus;
    end;
  end;
end;

procedure TTMSFMXTableView.ShowScrollBar;
var
  anim: TAnimation;
begin
  if (ScrollIndicator <> siFade) or (GetFullHeight <= 0) then
    Exit;

  anim := GetScrollBarAnimation;
  if Assigned(anim) and (anim is TFloatAnimation) then
  begin
    (anim as TFloatAnimation).StartFromCurrent := True;
    (anim as TFloatAnimation).StopValue := 1;
    anim.Start;
  end;
end;

procedure TTMSFMXTableView.StopScrollAnimation;
var
  scrollanim: TAnimation;
begin
  scrollanim := GetListScrollAnimation;
  if Assigned(scrollanim) then
    scrollanim.StopAtCurrent;
end;

procedure TTMSFMXTableView.ToggleEditMode(AEditMode: Boolean);
var
  i: integer;
  it: TTMSFMXTableViewItem;
  edb: TTMSFMXBarButton;
  footeredit: TRectangle;
  footer: TText;
  shpblb: TTMSFMXTableViewBulbShape;
  shp: TTMSFMXTableViewItemShape;
begin
  edb := GetEditButton as TTMSFMXBarButton;
  footer := GetFooterText;
  footeredit := GetFooterEditRectangle;

  if Assigned(edb) and Assigned(footer) and Assigned(footeredit) then
  begin
    if AEditMode then
    begin
      edb.Kind := bkCancel;
      footeredit.Visible := True;
      footer.Visible := False;
    end
    else
    begin
      ShowBackButton(FDetailViewVisible);
      DeSelectAllItems;
      edb.Kind := bkEdit;
      footeredit.Visible := False;
      footer.Visible := True;
    end;

    for I := 0 to FItemList.Count - 1 do
    begin
      it := AsItem(FItemList[I]).Item;
      if Assigned(it) then
      begin
        if it.CanDelete then
          it.ToggleEditButton
        else
          it.Selected := False;

        shp := it.Shape;
        if Assigned(shp)then
        begin
          shp.UpdateControls;
          shpblb := it.ShapeBulbRectangle;
          if Assigned(shpblb) then
          begin
            shpblb.UpdateControls;
            shpblb.Repaint;
          end;
          shp.Repaint;
          it.DeleteButton := False;
        end;
      end;
    end;
  end;
end;

procedure TTMSFMXTableView.DoFilter(AFilter: String);
var
  it: TTMSFMXTableViewItem;
begin
  case Filtering of
    vfFilterStart, vfFilterRandom:
    begin
      BeginUpdate;
      FFilter := AFilter;
      FBufferStart := -1;
      FBufferStop := -1;
      FCurrentPos := 0;
      FPosTo := 0;
      EndUpdate;
      UpdateListPosition;
      UpdateLookupBar;
      UpdateScrollBarVisuals;
    end;
    vfSearch:
    begin
      it := FindItemWithFilter(AFilter);
      if Assigned(it) then
        LookupItem(it.Index, True);
    end;
  end;
end;

procedure TTMSFMXTableView.ApplyStyle;
begin
  inherited;
  AlignButtons;
  AssignEvents;
  AssignAnimations;
  UpdateFooterText;
  UpdateHeaderText;
  UpdateFilter;
  UpdateEditButton;
  UpdateDefaultItemStyle;
  UpdateDefaultItem;
  UpdateDefaultCategory;
  UpdateLookupBar;
  UpdateScrollBarVisuals;
  UpdateTableViewItems;
end;

procedure TTMSFMXTableView.ArchiveClick(Sender: TObject);
var
  I: Integer;
  AllowDelete: Boolean;
begin
  AllowDelete := True;
  if Assigned(OnArchiveClick) then
    OnArchiveClick(Self, AllowDelete);

  if AllowDelete then
  begin
    BeginUpdate;
    for I := Items.Count - 1 downto 0 do
    begin
      if Items[I].Selected then
        Items[I].ShapeDeleteButtonClick(Self);
    end;
    EndUpdate;
  end;
end;

function TTMSFMXTableView.AsCategory(AObject: TFmxObject): TTMSFMXTableViewCategoryShape;
begin
  Result := AObject as TTMSFMXTableViewCategoryShape;
end;

function TTMSFMXTableView.AsItem(AObject: TFmxObject): TTMSFMXTableViewItemShape;
begin
  Result := AObject as TTMSFMXTableViewItemShape;
end;


procedure TTMSFMXTableView.Assign(Source: TPersistent);
begin
  if (Source is TTMSFMXTableView) then
  begin
    inherited Assign(Source);
    FItems.Assign((Source as TTMSFMXTableView).Items);
    FDefaultDetailView := (Source as TTMSFMXTableView).DefaultDetailView;
    FHeaderText := (Source as TTMSFMXTableView).HeaderText;
    FFooterText := (Source as TTMSFMXTableView).FooterText;
    FAutoDeleteItem := (Source as TTMSFMXTableView).AutoDeleteItem;
    FAutoLookup := (Source as TTMSFMXTableView).AutoLookup;
    FAutoToggleDetail := (Source as TTMSFMXTableView).AutoToggleDetail;
    FAutoFilter := (Source as TTMSFMXTableView).AutoFilter;
    FAutoLoadBuffer := (Source as TTMSFMXTableView).AutoLoadBuffer;
    FBitmapContainer := (Source as TTMSFMXTableView).BitmapContainer;
    FCategoryType := (Source as TTMSFMXTableView).CategoryType;
    FCategories.Assign((Source as TTMSFMXTableView).Categories);
    FEditButton := (Source as TTMSFMXTableView).EditButton;
    FFiltering := (Source as TTMSFMXTableView).Filtering;
    FItemOptions := (Source as TTMSFMXTableView).ItemOptions;
    FLayoutMode := (Source as TTMSFMXTableView).LayoutMode;
    FLookupBar := (Source as TTMSFMXTableView).LookupBar;
    FMarkText := (Source as TTMSFMXTableView).MarkText;
    FMoveText := (Source as TTMSFMXTableView).MoveText;
    FMultiSelect := (Source as TTMSFMXTableView).MultiSelect;
    FSelectedItemIndex := (Source as TTMSFMXTableView).SelectedItemIndex;
    FShowFilter := (Source as TTMSFMXTableView).ShowFilter;
    FShowFilterOnSwipe := (Source as TTMSFMXTableView).ShowFilterOnSwipe;
    FScrollIndicator := (Source as TTMSFMXTableView).ScrollIndicator;
    FStyleName := (Source as TTMSFMXTableView).StyleName;
  end;
end;


procedure TTMSFMXTableView.AssignAnimations;
var
  innerelement: TControl;
  animlist, anim, animscroll,backbtnanimopc, backbtnanim: TAnimation;
begin
  innerelement := GetListContainer;
  animlist := GetListDetailAnimation;
  anim := GetDetailAnimation;
  animscroll := GetListScrollAnimation;
  backbtnanim := GetBackButtonAnimation;
  backbtnanimopc := GetBackButtonOpacityAnimation;
  if Assigned(backbtnanim) and Assigned(backbtnanimopc) and Assigned(anim) and Assigned(animlist) and Assigned(innerelement) and Assigned(animscroll) then
  begin
    animlist.Duration := 0.6;
    anim.Duration := 0.6;
    backbtnanim.Duration := 0.6;
    backbtnanimopc.Duration := 0.6;

    animscroll.OnProcess := ScrollAnimationProcess;
    animscroll.OnFinish := ScrollAnimationFinished;
    backbtnanim.OnFinish := BackButtonAnimFinished;

    if Assigned(OnAnimationCustomize) then
      OnAnimationCustomize(Self, animlist, anim);
  end;
end;

procedure TTMSFMXTableView.AlignButtons;
var
  btnMark, btnArchive, btnMove: TControl;
  footeredit: TRectangle;
begin
  btnMark := GetMarkButton;
  btnArchive := GetArchiveButton;
  btnMove := GetMoveButton;
  footeredit := GetFooterEditRectangle;
  if Assigned(btnMark) and Assigned(btnArchive) and Assigned(btnMove) then
  begin
    btnArchive.Width := (footeredit.Width - (36 / 3)) / 3 - 36;
    btnMark.Width := btnArchive.Width;
  end;
end;

procedure TTMSFMXTableView.AssignEvents;
var
  background: TControl;
  editbutton: TControl;
  btnMark, btnMove, btnArchive: TControl;
  btnback: TControl;
  editsearch: TTMSFMXSearchEdit;
  ipl: TTMSFMXSearchEdit;
  hdtxt, fttxt: TText;
begin
  background := GetListBackGround;
  if Assigned(background) then
  begin
    background.CanFocus := True;
    background.OnKeyDown := ListKeyDown;
    background.OnMouseDown := ListMouseDown;
    background.OnMouseMove := ListMouseMove;
    background.OnMouseUp := ListMouseUp;
    background.OnDblClick := ListDblClick ;
    background.OnClick := ListClick;
    background.OnMouseLeave := ListMouseLeave;
    background.OnMouseWheel := ListMouseWheel;
  end;
  editbutton := GetEditButton;
  if Assigned(editbutton) then
    editbutton.OnClick := EditButtonClick;

  btnMark := GetMarkButton;
  btnMove := GetMoveButton;
  btnArchive := GetArchiveButton;
  if Assigned(btnMark) then
    btnMark.OnClick := MarkClick;

  if Assigned(btnArchive) then
    btnArchive.OnClick := ArchiveClick;

  if Assigned(btnMove) then
    btnMove.OnClick := MoveClick;

  btnback := GetBackButton;
  if Assigned(btnBack) then
    btnBack.OnClick := BackButtonClick;

  editsearch := GetSearchEdit;
  if Assigned(editsearch) then
  begin
    editsearch.OnChange := SearchEditChanged;
    editsearch.OnChangeTracking := SearchEditChanging;
  end;

  ipl := GetInPlaceEdit;
  if Assigned(ipl) then
    ipl.OnExit := InPlaceEditExit;

  hdtxt := GetHeaderText;
  if Assigned(hdtxt) and (hdtxt is TTMSFMXHTMLText) then
    (hdtxt as TTMSFMXHTMLText).OnAnchorClick := DoAnchorClickHeader;

  fttxt := GetFooterText;
  if Assigned(fttxt) and (fttxt is TTMSFMXHTMLText) then
    (fttxt as TTMSFMXHTMLText).OnAnchorClick := DoAnchorClickFooter;
end;

procedure TTMSFMXTableView.BackButtonAnimFinished(Sender: TObject);
begin
  if not FDetailViewVisible then
    HideBackButton;
end;

procedure TTMSFMXTableView.BackButtonClick(Sender: TObject);
var
  sel: TTMSFMXTableViewItem;
begin
  sel := nil;
  if (FFocusedItemIndex >= 0) and (FFocusedItemIndex <= Items.Count - 1) then
    sel := Items[FFocusedItemIndex];

  if AutoToggleDetail and not FEditMode then
  begin
    if Assigned(sel) then
    begin
      sel.ToggleDetailView(FDetailViewVisible);
      FDetailViewVisible := not FDetailViewVisible;
    end;
  end;

  if Assigned(OnBackButtonClick) then
    OnBackButtonClick(Self, sel);
end;

procedure TTMSFMXTableView.BeginUpdate;
begin
  inherited;
  Inc(FUpdateCount);
end;

procedure TTMSFMXTableView.CancelEditMode;
begin
  FEditMode := False;
  ToggleEditMode(False);
end;

function TTMSFMXTableView.CanClone(Source: TFMXObject): Boolean;
begin
  Result := True;
  if Source = GetItemEditRectangle then
    Result := ioEditRectangle in ItemOptions
  else if Source = GetItemDeleteRectangle then
    Result := ioDeleteRectangle in ItemOptions
  else if Source = GetItemBulbRectangle then
    Result := ioBulbRectangle in ItemOptions
  else if Source = GetItemLeftRectangle then
    Result := ioLeftRectangle in ItemOptions
  else if Source = GetItemCenterRectangle then
    Result := ioCenterRectangle in ItemOptions
  else if Source = GetItemRightRectangle then
    Result := ioRightRectangle in ItemOptions
  else if Source = GetItemDetailRectangle then
    Result := ioDetailRectangle in ItemOptions
  else if Source = GetItemCaption then
    Result := ioCaptionElement in ItemOptions
  else if Source = GetItemDescription then
    Result := ioDescriptionElement in ItemOptions;
end;

function TTMSFMXTableView.CanObserve(const ID: Integer): Boolean;
begin
  Result := False;
  if ID = TObserverMapping.EditGridLinkID then
    Result := True
  else if ID = TObserverMapping.PositionLinkID then
    Result := True;
end;

procedure TTMSFMXTableView.CleanUpCategories(innerelement: TControl);
var
  str: TStringList;
  lName: String;
  i: integer;
  ctrl: TFMXObject;
  l: TTMSFMXTableViewCategoryShape;
begin
  str := TStringList.Create;
  for i := 0 to FCategoryList.Count - 1 do
  begin
    lName := 'FMXTableViewCategory'+inttostr(i);
    str.Add(UpperCase(lName+'Name'));
  end;

  if Assigned(innerElement) then
  begin
    for I := innerElement.ChildrenCount - 1 downto 0 do
    begin
      ctrl := innerElement.Children[i];
      if IsCategory(ctrl) then
      begin
        l := AsCategory(ctrl);
        if str.IndexOf(UpperCase(l.Name)) = -1 then
        begin
          {$IFDEF DELPHI_LLVM}
          l.Release;
          {$ELSE}
          l.Free;
          {$ENDIF}
        end;
      end;
    end;
  end;

  str.Free;
end;

procedure TTMSFMXTableView.CleanUpItems(innerelement: TControl);
var
  str: TStringList;
  lName: String;
  i: integer;
  ctrl: TFMXObject;
  l: TTMSFMXTableViewItemShape;
begin
  str := TStringList.Create;
  for i := 0 to FItemList.Count - 1 do
  begin
    lName := 'FMXTableViewItem'+inttostr(i);
    str.Add(UpperCase(lName+'Name'));
  end;

  if Assigned(innerElement) then
  begin
    for I := innerElement.ChildrenCount - 1 downto 0 do
    begin
      ctrl := innerElement.Children[i];
      if IsItem(ctrl) then
      begin
        l := AsItem(ctrl);
        if str.IndexOf(UpperCase(l.Name)) = -1 then
        begin
          {$IFDEF DELPHI_LLVM}
          l.Release;
          {$ELSE}
          l.Free;
          {$ENDIF}
        end;
      end;
    end;
  end;

  str.Free;
end;

function TTMSFMXTableView.ClearDeleteButtons(AItem: TTMSFMXTableViewItem = nil): Boolean;
var
  I: Integer;
  it: TTMSFMXTableViewItem;
begin
  Result := False;
  for I := 0 to FItemList.Count - 1 do
  begin
    it := AsItem(FItemList[I]).Item;
    if it <> AItem then
    begin
      if it.DeleteButton then
        Result := True;
      it.FDeleteButton := False;
      it.ToggleDeleteButton;
    end;
  end;
end;

procedure TTMSFMXTableView.CloneChildren(Source, Destination: TFMXObject);
var
  i: integer;
  fmxclone: TFmxObject;
begin
  for I := 0 to Source.ChildrenCount - 1 do
  begin
    if CanClone(Source.Children[I]) then
    begin
      fmxclone := Source.Children[I].Clone(Destination);
      if Assigned(fmxclone) and (fmxclone is TTMSFMXBitmap) then
      begin
        if (fmxclone as TTMSFMXBitmap).StyleName = 'itemeditunchecked' then
          (fmxclone as TTMSFMXBitmap).LinkedImage := GetEditUncheckedDefaultImage
        else if (fmxclone as TTMSFMXBitmap).StyleName = 'itemeditchecked' then
          (fmxclone as TTMSFMXBitmap).LinkedImage := GetEditcheckedDefaultImage
        else if (fmxclone as TTMSFMXBitmap).StyleName = 'itemdetailimage' then
          (fmxclone as TTMSFMXBitmap).LinkedImage := GetItemDefaultDetailImage;
      end;

      Destination.InsertObject(Destination.ChildrenCount, fmxclone);
      CloneChildren(Source.Children[I], fmxclone);
    end;
  end;
end;

function TTMSFMXTableView.CountSelected: Integer;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to Items.Count - 1 do
  begin
    if Items[I].Selected then
      Inc(Result);
  end;
end;

procedure TTMSFMXTableView.ListMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
begin
  if Button = TMouseButton.mbRight then
    Exit;


  if Assigned(Sender) and (Sender is TControl) then
    TProtectedControl(Sender as TControl).Capture;

  if ClearDeleteButtons then
    FClearDeleteButtons := True;

  SetFocusOnBackground;
  StopScrollAnimation;
  FDragX := X;
  FDragY := Y;
  FScrollY := Y;
  FTimeStart := GetTickCountX;
  FMouseDown := True;
  FCheckItem := True;
  FCheckDelete := True;
  FCheckInplaceEdit := True;
end;

procedure TTMSFMXTableView.ListMouseLeave(Sender: TObject);
begin
  FMouseDown := False;
  FMouseUp := False;
  FShowDeleteButton := False;
  FCheckDelete := False;
  FCheckInplaceEdit := False;
end;

procedure TTMSFMXTableView.ListMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Single);
var
  it: TTMSFMXTableViewItem;
begin
  if FMouseDown then
  begin
    if ((FDragX < X - SWIPEMARGIN) or (FDragX > X + SWIPEMARGIN)) then
    begin
      FCheckItem := False;
      FCheckInplaceEdit := False;
      FShowDeleteButton := not FEditMode;
    end;

    if ((FDragY < Y - CLICKMARGIN) or (FDragY > Y + CLICKMARGIN)) then
    begin
      FCheckInplaceEdit := False;
      FCheckDelete := False;
      FShowDeleteButton := False;
      FCheckItem := False;
      FSp := 0;

      if (Y - FDragY) > 0 then
        FCurrentPos := GetCurrentPos - Abs(Y - FDragY)
      else
        FCurrentPos := GetCurrentPos + Abs(Y - FDragY);

      FDragY := Y;
      FPosTo := GetCurrentPos;
      UpdateListPosition;
      ShowScrollBar;
    end;
    if FShowDeleteButton and FCheckDelete then
    begin
      FCheckDelete := False;
      it := ItemAtXY(X, Y);
      if Assigned(it) then
      begin
        it.DeleteButton := not it.DeleteButton;
        FShowDeleteButton := False;
      end;
    end;
  end
  else
  begin
    it := ItemAtXY(X, Y);
    if Assigned(it) then
    begin
      it.CheckAnchor(X, Y);
    end;
  end;
end;

procedure TTMSFMXTableView.ListMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
var
  it: TTMSFMXTableViewItem;
  ipl: TTMSFMXSearchEdit;
  checknext, checkprevious: Boolean;
begin
  if Assigned(Sender) and (Sender is TControl) then
    TProtectedControl(Sender as TControl).ReleaseCapture;

  FMouseDown := False;
  FCheckDelete := False;
  FShowDeleteButton := False;
  FTimeStop := GetTickCountX;
  FMouseUp := True;

  if FClearDeleteButtons then
  begin
    FClearDeleteButtons := False;
    Exit;
  end;

  if ((FScrollY < Y - CLICKMARGIN) or (FScrollY > Y + CLICKMARGIN)) then
  begin
    FMouseUp := false;
    if ((FTimeStop - FTimeStart) < 500) and ((FTimeStop - FTimeStart) > 0) then
    begin
      FCheckItem := False;

      FSp := Abs(Y - FScrollY) / (FTimeStop - FTimeStart);

      if FSp > 0 then
      begin
        if (Y - FScrollY) > 0 then
          FPosTo := FPosTo - (Abs(Y - FScrollY) * FSp)
        else
          FPosTo := FPosTo + (Abs(Y - FScrollY) * FSp);
      end;
    end;

    if ShowFilterOnSwipe and (FCurrentPos < -25) and (FBufferStart = 0) then
      ShowFilter := True;

    FSp := 1;
    StartScrollAnimation;
  end;

  if FCheckInplaceEdit and not FEditMode then
  begin
    UpdateItemInplaceEdit;
    HideInplaceEdit;

    it := ItemAtXY(X, Y);
    if Assigned(it) then
    begin
      ipl := GetInPlaceEdit;
      if Assigned(ipl) then
      begin
        ipl.Tag := it.Index;

        if PointInRect(PointF(X, Y), it.GetCaptionRect) and it.CanEditCaption then
        begin
          FCheckItem := False;
          it.ShowEditForCaption;
        end
        else if PointInRect(PointF(X, Y), it.GetDescriptionRect) and it.CanEditDescription then
        begin
          FCheckItem := False;
          it.ShowEditForDescription;
        end;
      end;
    end;
  end;

  if FCheckItem then
  begin
    it := ItemAtXY(X, Y);
    if ssCtrl in Shift then
      ProcessItem(it, smMultiCtrlSelect, False)
    else if ssShift in Shift then
      ProcessItem(it, smMultiShiftSelect, False)
    else
      ProcessItem(it);

    if Assigned(OnItemClick) and Assigned(it) then
      OnItemClick(Self, it);
  end;

  if GetFullHeight < 0 then
    checknext := ((Y - FScrollY) < 0)
  else
    checknext := (GetCurrentPos > GetFullHeight + 25);

  checknext := checknext and (BufferSize > 0) and (FBufferStop < GetCountItems);

  if GetFullHeight < 0 then
    checkprevious := ((Y - FScrollY) > 0)
  else
    checkprevious := (GetCurrentPos < - 25);

  checkprevious := checkprevious and (BufferSize > 0) and (FBufferStart > 0);

  if checkNext then
  begin
    if AutoLoadBuffer then
    begin
      DoLoadNextBuffer;
    end
    else
    begin
      if Assigned(OnManualLoadNextBuffer) then
        OnManualLoadNextBuffer(Self);
    end;
  end
  else if checkprevious then
  begin
    if AutoLoadBuffer then
    begin
      DoLoadPreviousBuffer;
    end
    else
    begin
      if Assigned(OnManualLoadPreviousBuffer) then
        OnManualLoadPreviousBuffer(Self);
    end;
  end;
end;

procedure TTMSFMXTableView.ListMouseWheel(Sender: TObject; Shift: TShiftState;
  WheelDelta: Integer; var Handled: Boolean);
begin
  if WheelDelta > 0 then
    InternalScrollItem(FFocusedItemIndex - 1, True, smSingleSelect)
  else if WheelDelta < 0 then
    InternalScrollItem(FFocusedItemIndex + 1, True, smSingleSelect);

  Handled := True;
  inherited;
end;

procedure TTMSFMXTableView.LoadBufferBetween(BufferStart, BufferStop: Integer);
begin
  FBufferStart := BufferStart;
  FBufferStop := BufferStop;
end;

procedure TTMSFMXTableView.LoadNextBuffer(ABufferSize: Integer = -1);
var
  bfstart, bfstop, bfStopReal: Integer;
begin
  bfStop := FBufferStop;

  bfStart := bfStop;
  if ABufferSize = -1 then
    bfStop := bfStop + BufferSize - 1
  else
    bfStop := bfStop + ABufferSize;

  bfStopReal := Min(bfStop, GetCountItems);
  bfStart := bfStart - (bfStop - bfStopReal);

  FBufferStart := bfStart + 1;
  FBufferStop := bfStopReal + 1;
end;

procedure TTMSFMXTableView.LoadPreviousBuffer(ABufferSize: Integer);
var
  bfstart, bfstop, bfStartReal: Integer;
begin
  bfStart := FBufferStart;

  bfStop := bfStart;
  if ABufferSize = -1 then
    bfStart := bfStart - BufferSize + 1
  else
    bfStart := bfStart - ABufferSize;

  bfStartReal := Max(bfStart, 0);
  bfStop := bfStop + (bfStartReal - bfStart);

  FBufferStart := bfStartReal - 1;
  FBufferStop := bfStop - 1;
end;

procedure TTMSFMXTableView.LookupCategory(ACategory: String);
var
  it: TTMSFMXTableViewItem;
  i: integer;
  bfStart, bfStop, bfStopReal: Integer;
  isloaded: Boolean;
begin
  it := FindFirstItemWithCategory(ACategory);
  if Assigned(it) then
  begin
    isloaded := IsItemInItemList(it);
    bfStart := GetBufferStart;
    bfStopReal := GetBufferStop;
    if not isloaded then
    begin
      i := it.Index;

      bfStart := i;
      bfStop := i + BufferSize - 1;

      bfStopReal := Min(bfStop, Items.Count - 1);
      bfStart := bfStart - (bfStop - bfStopReal);

      if BufferSize > 0 then
      begin
        LoadBufferBetween(bfStart, bfStopReal);
        UpdateTableViewItems;
      end;
    end;

    if AutoLookUp then
    begin
      ScrollToCategoryCharacter(ACategory)
    end
    else
    begin
      if Assigned(OnManualLookup) then
        OnManualLookup(Self, ACategory, -1, bfStart, bfStopReal);
    end;
  end;
end;

procedure TTMSFMXTableView.LookupCustomCategory(ACategoryID: Integer);
var
  it: TTMSFMXTableViewItem;
  i: integer;
  bfStart, bfStop, bfStopReal: Integer;
  isloaded: Boolean;
begin
  it := FindFirstItemWithCategoryID(ACategoryID);
  if Assigned(it) then
  begin
    isloaded := IsItemInItemList(it);
    bfStart := GetBufferStart;
    bfStopReal := GetBufferStop;
    if not isloaded then
    begin
      i := it.Index;

      bfStart := i;
      bfStop := i + BufferSize - 1;

      bfStopReal := Min(bfStop, Items.Count - 1);
      bfStart := bfStart - (bfStop - bfStopReal);

      if BufferSize > 0 then
      begin
        LoadBufferBetween(bfStart, bfStopReal);
        UpdateTableViewItems;
      end;
    end;

    if AutoLookUp then
      ScrollToCategoryID(ACategoryID)
    else
    begin
      if Assigned(OnManualLookup) then
        OnManualLookup(Self, '', ACategoryID, bfStart, bfStopReal);
    end;
  end;
end;

procedure TTMSFMXTableView.LookupItem(AItemIndex: Integer; SelectItem: Boolean = False);
var
  i: integer;
  bfStart, bfStop, bfStopReal: Integer;
  it: TTMSFMXTableViewItem;
  isloaded: Boolean;
begin
  i := AItemIndex;

  if (i >= 0) and (i <= GetCountItems) then
  begin
    it := GetItem(i);
    isloaded := IsItemInItemList(it);
    if not isloaded then
    begin
      bfStart := i;
      bfStop := i + BufferSize - 1;

      bfStopReal := Min(bfStop, GetCountItems);
      bfStart := bfStart - (bfStop - bfStopReal);

      if BufferSize > 0 then
      begin
        LoadBufferBetween(bfStart, bfStopReal);
        UpdateTableViewItems;
      end;
    end;

    ScrollToItem(it);

    if SelectItem then
      ProcessItem(it, smSingleSelect, False);
  end;
end;

procedure TTMSFMXTableView.MarkClick(Sender: TObject);
begin
  if Assigned(OnMarkClick) then
    OnMarkClick(Self);
end;

procedure TTMSFMXTableView.MoveClick(Sender: TObject);
begin
  if Assigned(OnMoveClick) then
    OnMoveClick(Self);
end;

procedure TTMSFMXTableView.Notification(AComponent: TComponent;
  Operation: TOperation);
var
  i: integer;
begin
  if (Operation = opRemove) and (AComponent = FBitmapContainer) then
    FBitmapContainer := nil;

  if (Operation = opRemove) and (AComponent = FDefaultDetailView) then
    FDefaultDetailView := nil;

  inherited;

  if (csDestroying in ComponentState) then
    Exit;

  if (Operation = opRemove) then
  begin
    for I := 0 to Items.Count - 1 do
    begin
      if (AComponent = Items[I].FDetailView) then
      begin
        Items[I].FDetailView := nil;
      end;
    end;
  end;
end;

procedure TTMSFMXTableView.NotifyObserver;
begin
  if Observers.IsObserving(TObserverMapping.PositionLinkID) then
  begin
    try
      TLinkObservers.PositionLinkPosChanged(Observers)
    except
      TLinkObservers.EditGridLinkReset(Observers);
    end;
  end;
end;

procedure TTMSFMXTableView.StartScrollAnimation;
var
  scrollanim: TAnimation;
begin
  scrollanim := GetListScrollAnimation;
  if Assigned(scrollanim) and (GetFullHeight > 0) then
  begin
    FScrollAnimationStarted := True;
    (scrollanim as TFloatAnimation).StartFromCurrent := True;
    (scrollanim as TFloatAnimation).StopValue := -GetPositionTo;
    (scrollanim as TFloatAnimation).Duration := FSp * 1.5;
    scrollanim.Start;
  end;
end;

procedure TTMSFMXTableView.PositionItem(AItem: TTMSFMXTableViewItem;
  OnTop: Boolean);
var
  shp: TTMSFMXTableViewItemShape;
  backgroundelement: TControl;
  pos: Single;
  cat: TTMSFMXTableViewCategoryShape;
begin
  shp := AItem.Shape;

  pos := 0;
  if Assigned(shp) then
    pos := shp.Position.Y;

  backgroundelement := GetListBackGround;
  if Assigned(backgroundelement) and Assigned(shp) then
  begin
    if shp.Position.Y - GetCurrentPos + shp.Height >= backgroundelement.Height then
      FCurrentPos := (pos - backgroundelement.Height + shp.Height)
    else
    begin
      FCurrentPos := pos;

      cat := AItem.CategoryShape;
      if Assigned(cat) then
      begin
        FCurrentPos := FCurrentPos - cat.Height;
        if AItem.Index = 0 then
          FCurrentPos := 0;
      end;
    end;
  end;

  FCurrentPos := Max(0, Min(GetCurrentPos, GetFullHeight));
  FPosTo := FCurrentPos;
  UpdateListPosition;
end;

procedure TTMSFMXTableView.ProcessItem(AItem: TTMSFMXTableViewItem; ASelectionMode: TTMSFMXTableViewSelectionMode = smSingleSelect; ShowDetail: Boolean = True);
var
  it: Integer;
  m: TTMSFMXTableViewSelectionMode;
begin
  if (FEditMode and not AItem.CanDelete) then
    Exit;

  if MultiSelect and not FEditMode then
    m := ASelectionMode
  else
    m := smSingleSelect;

  if Assigned(AItem) then
  begin
    case m of
      smSingleSelect:
      begin
        if not FEditMode then
          DeSelectAllItems;
      end;
      smMultiShiftSelect:
      begin
        it := FInternalSelItemIndex;
        DeSelectAllItems;
        if (it >= 0) and (it <= GetCountItems) then
          SelectItemsBetween(GetItemIndexForIndex(AItem.Index), it);
      end;
    end;
    FFocusedItemIndex := GetItemIndexForIndex(AItem.Index);
    if m <> smMultiShiftSelect then
    begin
      FSelectedItemIndex := AItem.Index;
      AItem.Selected := not AItem.Selected;
      FInternalSelItemIndex := GetItemIndexForIndex(AItem.Index);
      if Assigned(OnItemSelected) then
        OnItemSelected(Self, AItem);
    end;
    if AutoToggleDetail and ShowDetail and not FEditMode and Assigned(AItem.DetailView) then
    begin
      AItem.ToggleDetailView(FDetailViewVisible);
      FDetailViewVisible := not FDetailViewVisible;
    end;
  end;
end;

{procedure TTMSFMXTableView.RefreshTableViewItems;
var
  I: Integer;
  c: TControl;
  innerelement, itemElement, backgroundElement: TControl;
begin
  if LayoutMode = lmGroup then
    Exit;

  backgroundElement := GetListBackGround;
  innerelement := GetListContainer;
  itemElement := GetDefaultItem;
  if Assigned(backgroundElement) and Assigned(itemElement) then
  begin
    for I := 0 to FDisplayList.Count - 1 do
    begin
      c := TControl(FDisplayList[I]);
      if Assigned(c) and (c is TTMSFMXTableViewItemShape) then
      begin
        if ((c.position.Y + innerelement.Position.Y + c.Height >= 0) and (c.Position.Y + innerelement.Position.Y <= backgroundElement.Height)) then
        begin
          UpdateListItem((c as TTMSFMXTableViewItemShape).Item, c, itemElement);
        end
        else if ((c.position.Y + innerelement.Position.Y + c.Height < 0) or (c.Position.Y + innerelement.Position.Y > backgroundElement.Height)) then
        begin
          DeleteListItem(c);
        end;
      end;
    end;


//    for I := 0 to FCategoryList.Count - 1 do
//    begin
//      c := TControl(FCategoryList[I]);
//      if Assigned(c) then
//      begin
//        if (c.Parent = nil) and ((c.position.Y + innerelement.Position.Y + c.Height >= 0) and (c.Position.Y + innerelement.Position.Y <= backgroundElement.Height)) then
//          c.Parent := innerelement
//        else if (c.Parent <> nil) and ((c.position.Y + innerelement.Position.Y + c.Height < 0) or (c.Position.Y + innerelement.Position.Y > backgroundElement.Height)) then
//          c.Parent := nil;
//
//        c.BringToFront;
//      end
//    end;
  end;
end;
}

procedure TTMSFMXTableView.ResetBufferSize;
begin
  FBufferStart := -1;
  FBufferStop := -1;
end;

procedure TTMSFMXTableView.Resize;
begin
  inherited;
  NeedStyleLookup;
  UpdateLookupBar;
  UpdateScrollBarVisuals;
  UpdateScrollBar;
  UpdateDetailContainerGrid;
end;

procedure TTMSFMXTableView.ScrollToCategoryCharacter(ACharacter: String);
var
  cat: TTMSFMXTableViewCategoryShape;
begin
  cat := FindCategoryWithCharacter(ACharacter);
  if Assigned(cat) then
  begin
    FCurrentPos := cat.Position.Y;
    FCurrentPos := Max(0, Min(FCurrentPos, GetFullHeight));
    FPosTo := FCurrentPos;
    UpdateListPosition;
  end;
end;

procedure TTMSFMXTableView.ScrollToCategoryId(ACategoryID: integer);
var
  cat: TTMSFMXTableViewCategoryShape;
begin
  cat := FindCategoryWithCharacterID(ACategoryID);
  if Assigned(cat) then
  begin
    FCurrentPos := cat.Position.Y;
    FCurrentPos := Max(0, Min(FCurrentPos, GetFullHeight));
    FPosTo := FCurrentPos;
    UpdateListPosition;
  end;
end;

procedure TTMSFMXTableView.ScrollToItem(AItem: TTMSFMXTableViewItem);
var
  cat: TTMSFMXTableViewCategoryShape;
  shp: TTMSFMXTableViewItemShape;
begin
  shp := AItem.Shape;
  if Assigned(shp) then
    FCurrentPos := shp.Position.Y;

  cat := nil;
  if (CategoryType <> ctNone) and not isFiltered then
    cat := AItem.CategoryShape;

  if Assigned(cat) then
    FcurrentPos := FCurrentPos - cat.Height;

  FCurrentPos := Max(0, Min(GetCurrentPos, GetFullHeight));
  FPosTo := FCurrentPos;
  UpdateListPosition;
end;

procedure TTMSFMXTableView.ScrollToPosition(APosition: Single);
begin
  FCurrentPos := APosition;
  FPosTo := APosition;
  UpdateListPosition;
end;

procedure TTMSFMXTableView.SearchEditChanged(Sender: TObject);
begin
  if not AutoFilter then
    ApplyFilter((Sender as TTMSFMXSearchEdit).Text);
end;

procedure TTMSFMXTableView.SearchEditChanging(Sender: TObject);
begin
  if AutoFilter then
    ApplyFilter((Sender as TTMSFMXSearchEdit).Text);
end;

procedure TTMSFMXTableView.SelectAllItems;
var
  I: Integer;
begin
  for I := 0 to GetCountItems do
    GetItem(i).Selected := True;
end;

function TTMSFMXTableView.SelectedItem: TTMSFMXTableViewItem;
begin
  Result := nil;
  if (SelectedItemIndex >= 0) and (SelectedItemIndex <= GetCountItems) then
    Result := GetItem(SelectedItemIndex);
end;

procedure TTMSFMXTableView.SelectItems(Selection: Array of Integer;
  AddToSelection: Boolean = False);
var
  I: Integer;
begin
  if not AddToSelection then
    DeSelectAllItems;

  for I := 0 to Length(Selection) - 1 do
    GetItem(Selection[I]).Selected := True;
end;

procedure TTMSFMXTableView.SelectItemsBetween(AStartIndex, AStopIndex: Integer;
  AddToSelection: Boolean = False);
var
  I: Integer;
  start, stop: integer;
begin
  if not AddToSelection then
    DeSelectAllItems;

  start := AStartIndex;
  stop := AStopIndex;

  if start > stop then
  begin
    start := AStopIndex;
    stop := AStartIndex;
  end;

  if (stop >= start) and (start >= 0) and (start <= GetCountItems) and (stop >= 0) and (stop <= GetCountItems) then
  begin
    for I := start to stop do
      GetItem(I).Selected := True;
  end;
end;

procedure TTMSFMXTableView.SetArchiveText(const Value: String);
begin
  if FArchiveText <> Value then
  begin
    FArchiveText := Value;
    UpdateEditButtons;
  end;
end;

procedure TTMSFMXTableView.SetAutoDeleteItem(const Value: Boolean);
begin
  if FAutoDeleteItem <> Value then
    FAutoDeleteItem := Value;
end;

procedure TTMSFMXTableView.SetAutoFilter(const Value: Boolean);
begin
  if FAutoFilter <> Value then
    FAutoFilter := Value;
end;

procedure TTMSFMXTableView.SetAutoLoadBuffer(const Value: Boolean);
begin
  if FAutoLoadBuffer <> Value then
    FAutoLoadBuffer := Value;
end;

procedure TTMSFMXTableView.SetAutoLookup(const Value: Boolean);
begin
  if FAutoLookup <> Value then
    FAutoLookup := Value;
end;

procedure TTMSFMXTableView.SetAutoToggleDetail(const Value: Boolean);
begin
  if FAutoToggleDetail <> Value then
    FAutoToggleDetail := Value;
end;

procedure TTMSFMXTableView.SetBackButton(const Value: Boolean);
begin
  if FBackButton <> Value then
  begin
    FBackButton := Value;
    if FBackButton then
      ShowBackButton(FDetailViewVisible)
    else
      HideBackButton;
  end;
end;

procedure TTMSFMXTableView.SetBufferSize(const Value: Integer);
begin
  if FBufferSize <> Value then
  begin
    FBufferSize := Value;
    FBufferStart := -1;
    FBufferStop := -1;
  end;
end;

procedure TTMSFMXTableView.SetCategories(
  const Value: TTMSFMXTableViewCategories);
begin
  if FCategories <> Value then
    FCategories.Assign(Value);
end;

procedure TTMSFMXTableView.SetMarkText(const Value: String);
begin
  if FMarkText <> Value then
  begin
    FMarkText := Value;
    UpdateEditButtons;
  end;
end;

procedure TTMSFMXTableView.SetMoveText(const Value: String);
begin
  if FMoveText <> Value then
  begin
    FMoveText := Value;
    UpdateEditButtons;
  end;
end;

procedure TTMSFMXTableView.SetMultiSelect(const Value: Boolean);
begin
  if FMultiSelect <> Value then
    FMultiSelect := Value;
end;

procedure TTMSFMXTableView.SetCategoryType(
  const Value: TTMSFMXTableViewCategoryType);
begin
  if FCategoryType <> Value then
  begin
    BeginUpdate;
    FCategoryType := Value;
    UpdateLookupBar;
    UpdateScrollBarVisuals;
    EndUpdate;
  end;
end;

procedure TTMSFMXTableView.SetDefaultDetailView(const Value: TControl);
begin
  FDefaultDetailView := Value;
end;

procedure TTMSFMXTableView.SetEditButton(const Value: Boolean);
begin
  if FEditButton <> Value then
  begin
    FEditButton := Value;
    UpdateEditButton;
  end;
end;

procedure TTMSFMXTableView.SetFiltering(const Value: TTMSFMXTableViewFiltering);
begin
  if FFiltering <> Value then
  begin
    FFiltering := Value;
    UpdateLookupBar;
    UpdateScrollBarVisuals;
  end;
end;

procedure TTMSFMXTableView.SetFocusOnBackGround;
var
  background: TControl;
begin
  background := GetListBackGround;
  if Assigned(background) then
    background.SetFocus;
end;

procedure TTMSFMXTableView.SetFooterText(const Value: String);
begin
  if FFooterText <> Value then
  begin
    FFooterText := Value;
    UpdateFooterText;
  end;
end;

procedure TTMSFMXTableView.SetHeaderText(const Value: String);
begin
  if FHeaderText <> Value then
  begin
    FHeaderText := Value;
    UpdateHeaderText;
  end;
end;

procedure TTMSFMXTableView.SetItemOptions(
  const Value: TTMSFMXTableViewItemOptions);
begin
  if FItemOptions <> Value then
  begin
    FItemOptions := Value;
    ApplyStyle;
  end;
end;

procedure TTMSFMXTableView.SetItems(const Value: TTMSFMXTableViewItems);
begin
  if FItems <> Value then
    FItems.Assign(Value);
end;

procedure TTMSFMXTableView.SetLayoutMode(
  const Value: TTMSFMXTableViewLayoutMode);
begin
  if FLayoutMode <> Value then
  begin
    FLayoutMode := Value;
    BeginUpdate;
    UpdateDefaultCategory;
    UpdateDefaultItem;
    EndUpdate;
  end;
end;

procedure TTMSFMXTableView.SetLookupBar(const Value: Boolean);
begin
  if FLookupBar <> Value then
  begin
    FLookupBar := Value;
    UpdateLookupBar;
    UpdateScrollBarVisuals;
  end;
end;

procedure TTMSFMXTableView.SetScrollIndicator(
  const Value: TTMSFMXTableViewScrollIndicator);
begin
  if FScrollIndicator <> Value then
  begin
    FScrollIndicator := Value;
    UpdateScrollBar;
    UpdateScrollBarVisuals;
  end;
end;

procedure TTMSFMXTableView.SetSelectedItemIndex(const Value: Integer);
begin
  FSelectedItemIndex := Value;
  FFocusedItemIndex := GetItemIndexForIndex(Value);
  FInternalSelItemIndex := FFocusedItemIndex;
  if (Value >= 0) and (Value <= Items.Count - 1) then
  begin
    ProcessItem(Items[Value], smSingleSelect, False);
    InternalScrollItem(FSelectedItemIndex, True);
  end;
end;

procedure TTMSFMXTableView.SetShowFilter(const Value: Boolean);
begin
  if FShowFilter <> Value then
  begin
    FShowFilter := Value;
    UpdateFilter;
  end;
end;

procedure TTMSFMXTableView.SetShowFilterOnSwipe(const Value: Boolean);
begin
  if FShowFilterOnSwipe <> Value then
    FShowFilterOnSwipe := Value;
end;

procedure TTMSFMXTableView.SetTopItem(const Value: Integer);
begin
  FTopItem := Value;
  LookupItem(FTopItem, False);
end;

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

end;

{ TTMSFMXTableViewItemShape }

constructor TTMSFMXTableViewItemShape.Create(AOwner: TComponent);
begin
  inherited;
  Height := 35;
  if (csDesigning in ComponentState) and not
    ((csReading in Owner.ComponentState) or (csLoading in Owner.ComponentState)) then
  begin
    Fill.Color := MakeColor(247, 247, 247);
    Stroke.Color := MakeColor(224, 224, 224);
    StrokeThickness := 2;
    Corners := [];
  end;
  FIsSelected := False;
  {$IFDEF DELPHIXE6_LVL}
  FSides := [TSide.Left, TSide.Right, TSide.Bottom];
  FFillSelected := TBrush.Create(TBrushKind.Solid, claWhite);
  FStrokeSelected := TBrush.Create(TBrushKind.Solid, claBlack);
  FFillEdit := TBrush.Create(TBrushKind.Solid, MakeColor(230, 230, 230));
  FStrokeEdit := TBrush.Create(TBrushKind.Solid, MakeColor(230, 230, 230));
  {$ELSE}
  FSides := [TSide.sdLeft, TSide.sdRight, TSide.sdBottom];
  FFillSelected := TBrush.Create(TBrushKind.bkSolid, claWhite);
  FStrokeSelected := TBrush.Create(TBrushKind.bkSolid, claBlack);
  FFillEdit := TBrush.Create(TBrushKind.bkSolid, MakeColor(230, 230, 230));
  FStrokeEdit := TBrush.Create(TBrushKind.bkSolid, MakeColor(230, 230, 230));
  {$ENDIF}
  FCaptionColor := claBlack;
  FCaptionColorSelected := claWhite;
  FDescriptionColor := $FFB7B7B7;
  FDescriptionColorSelected := claSilver;
end;

destructor TTMSFMXTableViewItemShape.Destroy;
begin
  FFillEdit.Free;
  FStrokeEdit.Free;
  FStrokeSelected.Free;
  FFillSelected.Free;
  inherited;
end;

procedure TTMSFMXTableViewItemShape.Paint;
var
  r: TRectF;
  allowpaint: Boolean;
  chk: Boolean;
begin
  allowpaint := True;
  if Assigned(Item) then
  begin
    if Assigned(Item.FOwner) then
    begin
      if Assigned(Item.FOwner.OnItemBeforeDraw) then
        Item.FOwner.OnItemBeforeDraw(Self, allowpaint,Canvas, Item, BoundsRect);
    end;
  end;

  if allowpaint then
  begin
    chk := False;
    if Assigned(Item) then
    begin
      if Item.FOwner.FEditMode then
        chk := IsSelected and Item.CanDelete;
    end;

    if chk then
    begin
      Canvas.Fill.Assign(FillEdit);
      Canvas.Stroke.Assign(StrokeEdit);
    end
    else
    begin
      if IsSelected then
      begin
        Canvas.Fill.Assign(FillSelected);
        Canvas.Stroke.Assign(StrokeSelected);
      end
      else
      begin
        Canvas.Stroke.Assign(Stroke);
        Canvas.Fill.Assign(Fill);
      end;
    end;

    r := BoundsRect;
    Canvas.FillRect(r, 10, 10, Corners, Opacity);
    Canvas.DrawRectSides(r, 10, 10, Corners, Opacity, Sides);
  end;

  if Assigned(Item) then
  begin
    if Assigned(Item.FOwner) then
    begin
      if Assigned(Item.FOwner.OnItemAfterDraw) then
        Item.FOwner.OnItemAfterDraw(Self, Canvas, Item, BoundsRect);
    end;
  end;
end;

procedure TTMSFMXTableViewItemShape.SetCaptionColor(const Value: TAlphaColor);
begin
  if FCaptionColor <> Value then
  begin
    FCaptionColor := Value;
    UpdateControls;
  end;
end;

procedure TTMSFMXTableViewItemShape.SetCaptionColorSelected(
  const Value: TAlphaColor);
begin
  if FCaptionColorSelected <> Value then
  begin
    FCaptionColorSelected := Value;
    UpdateControls;
  end;
end;

procedure TTMSFMXTableViewItemShape.SetDescriptionColor(
  const Value: TAlphaColor);
begin
  if FDescriptionColor <> Value then
  begin
    FDescriptionColor := Value;
    UpdateControls;
  end;
end;

procedure TTMSFMXTableViewItemShape.SetDescriptionColorSelected(
  const Value: TAlphaColor);
begin
  if FDescriptionColorSelected <> Value then
  begin
    FDescriptionColorSelected := Value;
    UpdateControls;
  end;
end;

procedure TTMSFMXTableViewItemShape.SetFillEdit(const Value: TBrush);
begin
  if FFillEdit <> Value then
  begin
    FFillEdit.Assign(Value);
    Repaint;
  end;
end;

procedure TTMSFMXTableViewItemShape.SetFillSelected(const Value: TBrush);
begin
  if FFillSelected <> Value then
  begin
    FFillSelected.Assign(Value);
    Repaint;
  end;
end;

procedure TTMSFMXTableViewItemShape.SetIsSelected(const Value: Boolean);
begin
  if FIsSelected <> Value then
  begin
    FIsSelected := Value;
    UpdateControls;
    Repaint;
  end;
end;

procedure TTMSFMXTableViewItemShape.SetSides(const Value: TSides);
begin
  if FSides <> Value  then
  begin
    FSides := Value;
    Repaint;
  end;
end;

procedure TTMSFMXTableViewItemShape.SetStrokeEdit(const Value: TBrush);
begin
  if FStrokeEdit <> Value then
  begin
    FStrokeEdit.Assign(Value);
    Repaint;
  end;
end;

procedure TTMSFMXTableViewItemShape.SetStrokeSelected(const Value: TBrush);
begin
  if FStrokeSelected <> Value then
  begin
    FStrokeSelected.Assign(Value);
    Repaint;
  end;
end;

function TTMSFMXTableViewItemShape.ShapeBulbRectangle: TTMSFMXTableViewBulbShape;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itembulbrectangle');
    if fmxobj is TTMSFMXTableViewBulbShape then
      Result := fmxobj as TTMSFMXTableViewBulbShape;
  end;
end;

function TTMSFMXTableViewItemShape.ShapeBulbText: TTMSFMXHTMLText;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itembulbcaption');
    if fmxobj is TTMSFMXHTMLText then
      Result := fmxobj as TTMSFMXHTMLText;
  end;
end;

function TTMSFMXTableViewItemShape.ShapeCaption: TText;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemcaption');
    if fmxobj is TText then
      Result := fmxobj as TText;
  end;
end;

function TTMSFMXTableViewItemShape.ShapeCenterRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemcenterrectangle');
    if fmxobj is TRectangle then
      Result := fmxobj as TRectangle;
  end;
end;

function TTMSFMXTableViewItemShape.ShapeDeleteButton: TTMSFMXBarButton;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemdeletebutton');
    if fmxobj is TTMSFMXBarButton then
      Result := fmxobj as TTMSFMXBarButton;
  end;
end;

function TTMSFMXTableViewItemShape.ShapeDeleteRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemdeleterectangle');
    if fmxobj is TRectangle then
      Result := fmxobj as TRectangle;
  end;
end;

function TTMSFMXTableViewItemShape.ShapeDescription: TText;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemdescription');
    if fmxobj is TText then
      Result := fmxobj as TText;
  end;
end;


function TTMSFMXTableViewItemShape.ShapeDetailRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemdetailrectangle');
    if fmxobj is TRectangle then
      Result := fmxobj as TRectangle;
  end;
end;

function TTMSFMXTableViewItemShape.ShapeEditRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemeditrectangle');
    if fmxobj is TRectangle then
      Result := fmxobj as TRectangle;
  end;
end;

function TTMSFMXTableViewItemShape.ShapeLeftRectangleImage: TTMSFMXBitmap;
var
  shp: TRectangle;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := ShapeLeftRectangle;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemleftrectangleimage');
    if fmxobj is TTMSFMXBitmap then
      Result := fmxobj as TTMSFMXBitmap;
  end;
end;

function TTMSFMXTableViewItemShape.ShapePlaceHolder: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemplaceholder');
    if fmxobj is TRectangle then
      Result := fmxobj as TRectangle;
  end;
end;

function TTMSFMXTableViewItemShape.ShapeLeftRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemleftrectangle');
    if fmxobj is TRectangle then
      Result := fmxobj as TRectangle;
  end;
end;

function TTMSFMXTableViewItemShape.ShapeRightRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemrightrectangle');
    if fmxobj is TRectangle then
      Result := fmxobj as TRectangle;
  end;
end;

procedure TTMSFMXTableViewItemShape.UpdateControls;
var
  shpcap, shpdesc: TText;
  chk: Boolean;
begin
  shpcap := ShapeCaption;
  shpdesc := ShapeDescription;

  chk := IsSelected;
  if Assigned(Item) then
  begin
    if Item.FOwner.FEditMode then
      chk := False;
  end;

  if Assigned(shpcap) then
  begin
    if chk then
      shpcap.Color := CaptionColorSelected
    else
      shpcap.Color := CaptionColor;
  end;

  if Assigned(shpdesc) then
  begin
    if chk then
      shpdesc.Color := DescriptionColorSelected
    else
      shpdesc.Color := DescriptionColor;
  end;

end;

{ TTMSFMXTableViewBulbShape }

constructor TTMSFMXTableViewBulbShape.Create(AOwner: TComponent);
begin
  inherited;
  Height := 35;
  Fill.Color := MakeColor(136,149,176);
  Stroke.Color := MakeColor(136,149,176);
  FIsSelected := False;
  {$IFDEF DELPHIXE6_LVL}
  FFillSelected := TBrush.Create(TBrushKind.Solid, claWhite);
  FStrokeSelected := TBrush.Create(TBrushKind.Solid, claWhite);
  {$ELSE}
  FFillSelected := TBrush.Create(TBrushKind.bkSolid, claWhite);
  FStrokeSelected := TBrush.Create(TBrushKind.bkSolid, claWhite);
  {$ENDIF}
  FCaptionColor := claWhite;
  FCaptionColorSelected := $FF0260E0;
end;

destructor TTMSFMXTableViewBulbShape.Destroy;
begin
  FStrokeSelected.Free;
  FFillSelected.Free;
  inherited;
end;

procedure TTMSFMXTableViewBulbShape.Paint;
var
  r: TRectF;
  chk: Boolean;
begin
  chk := IsSelected;
  if Assigned(Item) then
  begin
    if Item.FOwner.FEditMode then
      chk := False;
  end;

  if chk then
  begin
    Canvas.Fill.Assign(FillSelected);
    Canvas.Stroke.Assign(FStrokeSelected);
  end
  else
  begin
    Canvas.Stroke.Assign(Stroke);
    Canvas.Fill.Assign(Fill);
  end;

  r := BoundsRect;
  InflateRect(r, -0.5, -0.5);
  Canvas.FillRect(r, 10, 10, Corners, AbsoluteOpacity);
  Canvas.DrawRect(r, 10, 10, Corners, AbsoluteOpacity);
end;

procedure TTMSFMXTableViewBulbShape.SetCaptionColor(const Value: TAlphaColor);
begin
  if FCaptionColor <> Value then
  begin
    FCaptionColor := Value;
    UpdateControls;
  end;
end;

procedure TTMSFMXTableViewBulbShape.SetCaptionColorSelected(
  const Value: TAlphaColor);
begin
  if FCaptionColorSelected <> Value then
  begin
    FCaptionColorSelected := Value;
    UpdateControls;
  end;
end;

procedure TTMSFMXTableViewBulbShape.SetFillSelected(const Value: TBrush);
begin
  if FFillSelected <> Value then
  begin
    FFillSelected.Assign(Value);
    Repaint;
  end;
end;

procedure TTMSFMXTableViewBulbShape.SetIsSelected(const Value: Boolean);
begin
  if FIsSelected <> Value then
  begin
    FIsSelected := Value;
    UpdateControls;
    Repaint;
  end;
end;

procedure TTMSFMXTableViewBulbShape.SetStrokeSelected(const Value: TBrush);
begin
  if FStrokeSelected <> Value then
  begin
    FStrokeSelected.Assign(Value);
    Repaint;
  end;
end;

function TTMSFMXTableViewBulbShape.ShapeCaption: TText;
var
  shp: TTMSFMXTableViewBulbShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Self;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itembulbcaption');
    if fmxobj is TText then
      Result := fmxobj as TText;
  end;
end;

procedure TTMSFMXTableViewBulbShape.UpdateControls;
var
  shpcap: TText;
  chk: Boolean;
begin
  shpcap := ShapeCaption;

  chk := False;
  if Assigned(Item) then
  begin
    if not Item.FOwner.FEditMode then
      chk := IsSelected
  end;

  if chk then
    shpcap.Color := CaptionColorSelected
  else
    shpcap.Color := CaptionColor;
end;

{ TTMSFMXTableViewItem }

procedure TTMSFMXTableViewItem.ToggleDetailView(ADetailViewVisible: Boolean);
var
  anim, animlist,backbtnanimopc, backbtnanim: TAnimation;
  innerelement: TControl;
  detailcontainer: TControl;
begin
  if Assigned(DetailView) then
  begin
    innerelement := FOwner.GetListContainer;
    detailcontainer := FOwner.GetListDetailContainer;
    if Assigned(innerelement) and Assigned(detailContainer) then
    begin
      animlist := FOwner.GetListDetailAnimation;
      anim := FOwner.GetDetailAnimation;
      backbtnanim := FOwner.GetBackButtonAnimation;
      backbtnanimopc := FOwner.GetBackButtonOpacityAnimation;
      if Assigned(backbtnanim) and Assigned(backbtnanimopc) and Assigned(anim) and Assigned(animlist) and (anim is TFloatAnimation) and (animlist is TFloatAnimation) then
      begin
        anim.OnFinish := DetailAnimationFinished;
        (anim as TFloatAnimation).StartFromCurrent := True;
        (animlist as TFloatAnimation).StartFromCurrent := True;
        (backbtnanim as TFloatAnimation).StartFromCurrent := True;
        (backbtnanimopc as TFloatAnimation).StartFromCurrent := True;
        if not ADetailViewVisible then
        begin
          UpdateDetailContainer;
          detailcontainer.Visible := True;
          FOwner.ShowBackButton(True);
          (anim as TFloatAnimation).StopValue := 0;
          (animlist as TFloatAnimation).StopValue := -innerelement.Width;
          (backbtnanim as TFloatAnimation).StopValue := 58;
          (backbtnanimopc as TFloatAnimation).StopValue := 1;
          backbtnanimopc.Duration := 0.8;
        end
        else
        begin
          (anim as TFloatAnimation).StopValue := innerelement.Width;
          (animlist as TFloatAnimation).StopValue := 0;
          (backbtnanim as TFloatAnimation).StopValue := 0;
          (backbtnanimopc as TFloatAnimation).StopValue := 0;
          backbtnanimopc.Duration := 0.2;
        end;

        FOwner.UpdateLookupBar;
        FOwner.UpdateScrollBarVisuals;


        if not FOwner.FDetailViewVisible then
        begin
          if Assigned(FOwner.OnItemBeforeDetail) then
            FOwner.OnItemBeforeDetail(Self, Self);
        end
        else
        begin
          if Assigned(FOwner.OnItemBeforeReturnDetail) then
            FOwner.OnItemBeforeReturnDetail(Self, Self);
        end;

        backbtnanim.Start;
        backbtnanimopc.Start;
        animlist.Start;
        anim.Start;
      end;
    end;
  end;
end;

procedure TTMSFMXTableViewItem.ToggleEditButton;
var
  anim, animopc: TAnimation;
  shpedit: TRectangle;
begin
  anim := EditAnimation;
  animopc := EditOpacityAnimation;
  shpedit := ShapeEditRectangle;
  if Assigned(shpedit) and Assigned(anim) and Assigned(animopc) then
  begin
    anim.Duration := 0.2;
    animopc.Duration := 0.2;
    anim.OnFinish := EditButtonAnimFinished;
    if FOwner.FEditMode then
    begin
      shpedit.Visible := True;
      (anim as TFloatAnimation).StopValue := 30;
      (animopc as TFloatAnimation).StopValue := 1;
    end
    else
    begin
      (anim as TFloatAnimation).StopValue := 0;
      (animopc as TFloatAnimation).stopValue := 0;
    end;
    anim.Start;
    animopc.Start;
  end;
end;

procedure TTMSFMXTableViewItem.SetDetailView(const Value: TControl);
begin
  FDetailView := Value;
  UpdateDetailContainer;
  UpdateDetailDisclosure;
end;

procedure TTMSFMXTableViewItem.ItemArcDialChanged(Sender: TObject);
begin
  if Sender is TArcDial then
    DataValue := (Sender as TArcDial).Value;
end;

procedure TTMSFMXTableViewItem.ApplyEx;
begin
//
end;

procedure TTMSFMXTableViewItem.Assign(Source: TPersistent);
begin
  if (Source is TTMSFMXTableViewItem) then
  begin
    FCanEditCaption := (Source as TTMSFMXTableViewItem).CanEditCaption;
    FCanEditDescription := (Source as TTMSFMXTableViewItem).CanEditDescription;
    FCanSelect := (Source as TTMSFMXTableViewItem).CanSelect;
    FCanDelete := (Source as TTMSFMXTableViewItem).CanDelete;
    FLeftMargin := (Source as TTMSFMXTableViewItem).LeftMargin;
    FRightMargin := (Source as TTMSFMXTableViewItem).RightMargin;
    FDetailView := (Source as TTMSFMXTableViewItem).DetailView;
    FCaption := (Source as TTMSFMXTableViewItem).Caption;
    FDescription := (Source as TTMSFMXTableViewItem).Description;
    FCategoryID := (Source as TTMSFMXTableViewItem).CategoryID;
    FTag := (Source as TTMSFMXTableViewItem).Tag;
    FDataGroupIndex := (Source as TTMSFMXTableViewItem).DataGroupIndex;
    FDataValue := (Source as TTMSFMXTableViewItem).DataValue;
    FDataString := (Source as TTMSFMXTableViewItem).DataString;
    FDataObject := (Source as TTMSFMXTableViewItem).DataObject;
    FSelected := (Source as TTMSFMXTableViewItem).Selected;
    FDeleteButton := (Source as TTMSFMXTableViewItem).DeleteButton;
    FGroupIndex := (Source as TTMSFMXTableViewItem).GroupIndex;
    FBulbText := (Source as TTMSFMXTableViewItem).BulbText;
    FBitmapName := (Source as TTMSFMXTableViewItem).BitmapName;
    FBitmap.Assign((Source as TTMSFMXTableViewItem).Bitmap);
    FVisible := (Source as TTMSFMXTableViewItem).Visible;
  end;
end;

procedure TTMSFMXTableViewItem.AssignBitmapContainer;
var
  shpcap, shpdesc: TTMSFMXHTMLText;
begin
  shpcap := ShapeCaption as TTMSFMXHTMLText;
  shpdesc := ShapeDescription as TTMSFMXHTMLText;
  if Assigned(shpcap) then
    shpcap.BitmapContainer := FOwner.BitmapContainer;

  if Assigned(shpdesc) then
    shpdesc.BitmapContainer := FOwner.BitmapContainer;
end;

procedure TTMSFMXTableViewItem.BitmapChanged(Sender: TObject);
begin
  UpdateBitmap;
end;

procedure TTMSFMXTableViewItem.HideDetailView;
begin
  if not FOwner.FDetailViewVisible then
    Exit;

  ToggleDetailView(True);
  FOwner.FDetailViewVisible := False;
end;

procedure TTMSFMXTableViewItem.CheckAnchor(X, Y: Single);
var
  shpcap: TText;
  shpdesc: TText;
  r: TRectF;
  a: String;
begin
  shpcap := ShapeCaption;
  shpdesc := ShapeDescription;
  if Assigned(shpcap) and (shpcap is TTMSFMXHTMLText) then
  begin
    r := GetCaptionRect;
    if (shpcap as TTMSFMXHTMLText).IsHTML and PointInRect(PointF(X, Y), r) then
    begin
      shpdesc.HitTest := False;
      (shpcap as TTMSFMXHTMLText).HTMLDraw(True, X - r.Left, Y - r.Top, a);
      if a <> '' then
        shpcap.HitTest := True;
    end;
  end;

  if Assigned(shpdesc) and (shpdesc is TTMSFMXHTMLText) then
  begin
    r := GetDescriptionRect;
    if (shpdesc as TTMSFMXHTMLText).IsHTML and PointInRect(PointF(X, Y), r) then
    begin
      shpdesc.HitTest := False;
      (shpdesc as TTMSFMXHTMLText).HTMLDraw(True, X - r.Left, Y - r.Top, a);
      if a <> '' then
        shpdesc.HitTest := True;
    end;
  end;
end;

constructor TTMSFMXTableViewItem.Create(Collection: TCollection);
begin
  inherited;
  FOwner := (Collection as TTMSFMXTableViewItems).FOwner;
  FBitmap := TBitmap.Create(0, 0);
  FBitmap.OnChange := BitmapChanged;
  FCanEditCaption := False;
  FCanEditDescription := False;
  FDataGroupIndex := 0;
  FCanSelect := True;
  FCategoryID := -1;
  FDeleteButton := False;
  FRightMargin := 0;
  FLeftMargin := 0;
  FSelected := False;
  FVisible := True;
  FGroupIndex := 0;
  FDeleteButton := False;
  FCanDelete := True;
  UpdateTableViewItem;
end;

function TTMSFMXTableViewItem.DeleteAnimation: TAnimation;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemdeleteanimation');
    if fmxobj is TAnimation then
      Result := fmxobj as TAnimation;
  end;
end;

function TTMSFMXTableViewItem.DeleteOpacityAnimation: TAnimation;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemdeleteopacityanimation');
    if fmxobj is TAnimation then
      Result := fmxobj as TAnimation;
  end;
end;

destructor TTMSFMXTableViewItem.Destroy;
begin
  FBitmap.Free;
  FOwner.FSelectedItems.Remove(Self);
  inherited;
  UpdateTableViewItem;
end;

procedure TTMSFMXTableViewItem.DetailAnimationFinished(Sender: TObject);
begin
  FOWner.UpdateDetailContainerGrid;
  if FOwner.FDetailViewVisible then
  begin
    if Assigned(FOwner.OnItemAfterDetail) then
      FOwner.OnItemAfterDetail(Self, Self);
  end
  else
  begin
    if Assigned(FOwner.OnItemAfterReturnDetail) then
      FOwner.OnItemAfterReturnDetail(Self, Self);
  end;
end;

procedure TTMSFMXTableViewItem.DoAnchorClickItemCaption(Sender: TObject;
  AAnchor: String);
begin
  if Assigned(FOwner.OnItemCaptionAnchorClick) then
    FOwner.OnItemCaptionAnchorClick(Sender, Self, AAnchor);
end;

procedure TTMSFMXTableViewItem.DoAnchorClickItemDescription(Sender: TObject;
  AAnchor: String);
begin
  if Assigned(FOwner.OnItemDescriptionAnchorClick) then
    FOwner.OnItemDescriptionAnchorClick(Sender, Self, AAnchor);
end;

procedure TTMSFMXTableViewItem.DoCustomize;
var
  shpr: TRectangle;
  c: TControl;
begin
  shpr := ShapeRightRectangle;
  if Assigned(shpr) then
  begin
    c := shpr.FindStyleResource('item_checkbox') as TControl;
    if Assigned(c) and (c is TCheckBox) then
    begin
      (c as TCheckBox).OnChange := ItemCheckChanged;
      (c as TCheckBox).IsChecked := DataBoolean;
    end;

    c := shpr.FindStyleResource('item_radiobutton') as TControl;
    if Assigned(c) and (c is TRadioButton) then
    begin
      (c as TRadioButton).OnChange := ItemRadioChanged;
      (c as TRadioButton).IsChecked := DataBoolean;
      (c as TRadioButton).GroupName := 'group_'+inttostr(DataGroupIndex);
    end;

    c := shpr.FindStyleResource('item_trackbar') as TControl;
    if Assigned(c) and (c is TTrackBar) then
    begin
      (c as TTrackBar).Value := DataValue;
      (c as TTrackBar).OnChange := ItemTrackBarChanged;
    end;

    c := shpr.FindStyleResource('item_arcdial') as TControl;
    if Assigned(c) and (c is TArcDial) then
    begin
      (c as TArcDial).Value := DataValue;
      (c as TArcDial).OnChange := ItemArcDialChanged;
    end;
  end;

  if Assigned(FOwner.OnItemCustomize) then
    FOwner.OnItemCustomize(Self, Self, shape, ShapeRightRectangle);
end;

function TTMSFMXTableViewItem.EditAnimation: TAnimation;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemeditanimation');
    if fmxobj is TAnimation then
      Result := fmxobj as TAnimation;
  end;
end;

procedure TTMSFMXTableViewItem.EditButtonAnimFinished(Sender: TObject);
begin
  if not FOwner.FEditMode then
    HideEditButton;
end;

function TTMSFMXTableViewItem.EditOpacityAnimation: TAnimation;
var
  shp: TTMSFMXTableViewItemShape;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemeditopacityanimation');
    if fmxobj is TAnimation then
      Result := fmxobj as TAnimation;
  end;
end;

function TTMSFMXTableViewItem.GetBitmapContainer: TTMSFMXBitmapContainer;
begin
  Result := FOwner.BitmapContainer;
end;

function TTMSFMXTableViewItem.GetCaptionRect: TRectF;
var
  cap, el, shpc, shp: TControl;
  ptc: TPointF;
begin
  Result := RectF(0, 0, 0, 0);
  cap := ShapeCaption;
  el := FOwner.GetListContainer;
  shpc := ShapeCenterRectangle;
  shp := Shape;
  if assigned(shp) and Assigned(shpc) and Assigned(el) and Assigned(cap) then
  begin
    ptc := PointF(cap.Position.X, cap.Position.Y);
    ptc.X := ptc.X + shpc.Position.Y;
    ptc.Y := ptc.Y + shpc.Position.Y;

    ptc.X := ptc.X + shp.Position.X;
    ptc.Y := ptc.Y + shp.Position.Y;

    ptc.X := ptc.X + el.Position.X;
    ptc.Y := ptc.Y + el.Position.Y;

    Result := RectF(ptc.X, ptc.Y, ptc.X + cap.Width, ptc.Y + cap.Height);
  end;
end;

function TTMSFMXTableViewItem.GetDescriptionRect: TRectF;
var
  desc, el, shpc, shp: TControl;
  ptc: TPointF;
begin
  Result := RectF(0, 0, 0, 0);
  desc := ShapeDescription;
  el := FOwner.GetListContainer;
  shpc := ShapeCenterRectangle;
  shp := Shape;
  if assigned(shp) and Assigned(shpc) and Assigned(el) and Assigned(desc) then
  begin
    ptc := PointF(desc.Position.X, desc.Position.Y);
    ptc.X := ptc.X + shpc.Position.Y;
    ptc.Y := ptc.Y + shpc.Position.Y;

    ptc.X := ptc.X + shp.Position.X;
    ptc.Y := ptc.Y + shp.Position.Y;

    ptc.X := ptc.X + el.Position.X;
    ptc.Y := ptc.Y + el.Position.Y;

    Result := RectF(ptc.X, ptc.Y, ptc.X + desc.Width, ptc.Y + desc.Height);
  end;
end;

function TTMSFMXTableViewItem.GetDetailView: TControl;
begin
  Result := nil;
  if Assigned(FDetailView) then
    Result := FDetailView
  else if Assigned(FOwner.DefaultDetailView) then
    Result := FOwner.DefaultDetailView;
end;

function TTMSFMXTableViewItem.GetStrippedCaption: String;
begin
  Result := GetStrippedHTMLText(FOwner.BoundsRect, FOwner.Canvas, Caption);
end;

procedure TTMSFMXTableViewItem.HideEditButton;
var
  shpEdit: TRectangle;
begin
  shpEdit := ShapeEditRectangle;
  if Assigned(shpEdit) then
  begin
    shpEdit.Visible := False;
    shpEdit.Width := 0;
  end;
end;

procedure TTMSFMXTableViewItem.ItemCheckChanged(Sender: TObject);
begin
  if Sender is TCheckBox then
    DataBoolean := (Sender as TCheckBox).IsChecked;
end;

procedure TTMSFMXTableViewItem.ItemRadioChanged(Sender: TObject);
begin
  if Sender is TRadioButton then
    DataBoolean := (Sender as TRadioButton).IsChecked;
end;

procedure TTMSFMXTableViewItem.ItemTrackBarChanged(Sender: TObject);
begin
  if Sender is TTrackBar then
    DataValue := (Sender as TTrackBar).Value;
end;

procedure TTMSFMXTableViewItem.ItemTextChanged(Sender: TObject);
begin
  if Sender is TText then
    DataString := (Sender as TText).text;
end;

procedure TTMSFMXTableViewItem.SetCategoryID(const Value: integer);
begin
  if FCategoryID <> Value then
    FCategoryID := Value;
end;

procedure TTMSFMXTableViewItem.SetDataBoolean(const Value: Boolean);
begin
  FDataBoolean := Value;
  UpdateItem;
end;

procedure TTMSFMXTableViewItem.SetDataGroupIndex(const Value: Integer);
begin
  FDataGroupIndex := Value;
  UpdateItem;
end;

procedure TTMSFMXTableViewItem.SetDataObject(const Value: TObject);
begin
  FDataObject := Value;
  UpdateItem;
end;

procedure TTMSFMXTableViewItem.SetDataString(const Value: String);
begin
  FDataString := Value;
  UpdateItem;
end;

procedure TTMSFMXTableViewItem.SetDataValue(const Value: Single);
begin
  FDataValue := Value;
  UpdateItem;
end;

procedure TTMSFMXTableViewItem.SetDeleteButton(const Value: Boolean);
begin
  if FOwner.FBlockDeleteButton then
    Exit;

  if FDeleteButton <> Value then
  begin
    if CanDelete then
    begin
      FDeleteButton := Value;
      ToggleDeleteButton;
      FOwner.FBlockDeleteButton := True;
      FOwner.ClearDeleteButtons(Self);
      FOwner.FBlockDeleteButton := False;
      UpdateAlignments;
    end;
  end;
end;

procedure TTMSFMXTableViewItem.SetDescription(const Value: String);
begin
  if FDescription <> Value then
  begin
    FDescription := Value;
    if FOwner.FUpdateCount = 0 then
      UpdateDescription;
  end;
end;

procedure TTMSFMXTableViewItem.SetGroupIndex(const Value: Integer);
begin
  if FGroupIndex <> Value then
    FGroupIndex := Value;
end;

procedure TTMSFMXTableViewItem.SetLeftMargin(const Value: Integer);
begin
  if FLeftMargin <> Value then
  begin
    FLeftMargin := Value;
    UpdateItemIndent;
  end;
end;

procedure TTMSFMXTableViewItem.SetRightMargin(const Value: Integer);
begin
  if FRightMargin <> Value then
  begin
    FRightMargin := Value;
    UpdateItemIndent;
  end;
end;

procedure TTMSFMXTableViewItem.SetSelected(const Value: Boolean);
var
  shp: TTMSFMXTableViewItemShape;
  shpbulb: TTMSFMXTableViewBulbShape;
  shpchecked, shpunchecked: TControl;
begin
  if CanSelect then
  begin
    FSelected := Value;
    if FSelected then
    begin
      if FOwner.FSelectedItems.IndexOf(Self) = -1 then
        FOwner.FSelectedItems.Add(Self);
    end
    else
      FOwner.FSelectedItems.Remove(Self);

    shp := Shape;
    if Assigned(shp) then
      shp.IsSelected := FSelected;

    shpbulb := ShapeBulbRectangle;
    if Assigned(shpbulb) then
      shpbulb.IsSelected := FSelected;

    shpchecked := ShapeEditChecked;
    shpunchecked := ShapeEditUnChecked;
    if Assigned(shpchecked) and Assigned(shpunchecked) then
    begin
      if FSelected then
      begin
        shpunchecked.Visible := False;
        shpchecked.Visible := True;
      end
      else
      begin
        shpchecked.Visible := False;
        shpunchecked.Visible := True;
      end;
    end;

    FOwner.UpdateEditButtons;
    FOwner.NotifyObserver;
  end;
end;

procedure TTMSFMXTableViewItem.SetVisible(const Value: Boolean);
begin
  if FVisible <> Value then
  begin
    FVisible := Value;
    if FOwner.FUpdateCount = 0 then
      UpdateTableViewItem;
  end;
end;

procedure TTMSFMXTableViewItem.SetBitmap(const Value: TBitmap);
begin
  if FBitmap <> Value then
  begin
    FBitmap.Assign(Value);
    if FOwner.FUpdateCount = 0 then
      UpdateBitmap;
  end;
end;

procedure TTMSFMXTableViewItem.SetBitmapName(const Value: String);
begin
  if FBitmapName <> Value then
  begin
    FBitmapName := Value;
    if FOwner.FUpdateCount = 0 then
      UpdateBitmap;
  end;
end;

procedure TTMSFMXTableViewItem.SetBulbText(const Value: String);
begin
  if FBulbText <> Value then
  begin
    FBulbText := Value;
    if FOwner.FUpdateCount = 0 then
      UpdateBulb;
  end;
end;

procedure TTMSFMXTableViewItem.SetCanDelete(const Value: Boolean);
begin
  if FCanDelete <> Value then
  begin
    FCanDelete := Value;
    if not FCanDelete then
    begin
      FDeleteButton := False;
      ToggleDeleteButton;
    end;
  end;
end;

procedure TTMSFMXTableViewItem.SetCanEditCaption(const Value: Boolean);
begin
  if FCanEditCaption <> Value then
    FCanEditCaption := Value;
end;

procedure TTMSFMXTableViewItem.SetCanEditDescription(const Value: Boolean);
begin
  if FCanEditDescription <> Value then
    FCanEditDescription := Value;
end;

procedure TTMSFMXTableViewItem.SetCanSelect(const Value: Boolean);
var
  shp: TTMSFMXTableViewItemShape;
begin
  if FCanSelect <> Value then
  begin
    FCanSelect := Value;
    if not FCanSelect then
    begin
      FSelected := False;
      FOwner.FSelectedItems.Remove(Self);

      shp := Shape;
      if Assigned(shp) then
        shp.IsSelected := FSelected;
    end;
  end;
end;

procedure TTMSFMXTableViewItem.SetCaption(const Value: string);
begin
  if FCaption <> Value then
  begin
    FCaption := Value;
    if FOwner.FUpdateCount = 0 then
      UpdateCaption;
  end;
end;

function TTMSFMXTableViewItem.Shape: TTMSFMXTableViewItemShape;
var
  innerelement: TControl;
  fmxobj: TFMXObject;
begin
  Result := nil;
  innerelement := FOwner.GetListContainer;
  if Assigned(innerelement) then
  begin
    fmxobj := innerelement.FindStyleResource('FMXTableViewItem'+inttostr(Index));
    if fmxobj is TTMSFMXTableViewItemShape then
      Result := FOwner.AsItem(fmxobj);
  end;
end;

function TTMSFMXTableViewItem.ShapeBulbRectangle: TTMSFMXTableViewBulbShape;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeBulbRectangle;
end;

function TTMSFMXTableViewItem.ShapeBulbText: TTMSFMXHTMLText;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeBulbText;
end;

function TTMSFMXTableViewItem.ShapeCaption: TText;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeCaption;
end;

function TTMSFMXTableViewItem.ShapeCenterRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeCenterRectangle;
end;

function TTMSFMXTableViewItem.ShapeDeleteButton: TTMSFMXBarButton;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeDeleteButton;
end;

procedure TTMSFMXTableViewItem.ShapeDeleteButtonClick(Sender: TObject);
var
  allow: Boolean;
begin
  allow := True;
  if Assigned(FOwner.OnItemDelete) then
      FOwner.OnItemDelete(Self, Self, allow);

  if FOwner.AutoDeleteItem and allow then
  begin
    {$IFDEF DELPHI_LLVM}
    Self.Release;
    {$ELSE}
    Self.Free;
    {$ENDIF}
  end;
end;

function TTMSFMXTableViewItem.ShapeDeleteRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeDeleteRectangle;
end;

function TTMSFMXTableViewItem.ShapeDescription: TText;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeDescription;
end;

function TTMSFMXTableViewItem.ShapeDetailRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeDetailRectangle;
end;

function TTMSFMXTableViewItem.ShapeEditChecked: TControl;
var
  shp: TRectangle;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := ShapeEditRectangle;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemeditchecked');
    if fmxobj is TControl then
      Result := fmxobj as TControl;
  end;
end;

function TTMSFMXTableViewItem.ShapeEditRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeEditRectangle;
end;

function TTMSFMXTableViewItem.ShapeEditUnChecked: TControl;
var
  shp: TRectangle;
  fmxobj: TFMXObject;
begin
  Result := nil;
  shp := ShapeEditRectangle;
  if Assigned(shp) then
  begin
    fmxobj := shp.FindStyleResource('itemeditunchecked');
    if fmxobj is TControl then
      Result := fmxobj as TControl;
  end;
end;

function TTMSFMXTableViewItem.ShapeLeftRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeLeftRectangle;
end;

function TTMSFMXTableViewItem.ShapeLeftRectangleImage: TTMSFMXBitmap;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeLeftRectangleImage;
end;

function TTMSFMXTableViewItem.ShapePlaceHolder: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapePlaceHolder;
end;

function TTMSFMXTableViewItem.ShapeRightRectangle: TRectangle;
var
  shp: TTMSFMXTableViewItemShape;
begin
  Result := nil;
  shp := Shape;
  if Assigned(shp) then
    Result := shp.ShapeRightRectangle;
end;

procedure TTMSFMXTableViewItem.ShowDetailView;
begin
  if FOwner.FDetailViewVisible then
    Exit;

  ToggleDetailView(False);
  FOwner.FDetailViewVisible := True;
end;

procedure TTMSFMXTableViewItem.ShowEditButton;
var
  shpEdit: TRectangle;
begin
  shpEdit := ShapeEditRectangle;
  if Assigned(shpEdit) then
  begin
    shpEdit.Visible := True;
    shpEdit.Width := 30;
  end;
end;

procedure TTMSFMXTableViewItem.ShowEditForCaption;
var
  ipl: TTMSFMXSearchEdit;
  cap: TText;
  shpc: TControl;
begin
  if CanEditCaption then
  begin
    ipl := FOwner.GetInPlaceEdit;
    cap := ShapeCaption;
    shpc := ShapeCenterRectangle;
    if Assigned(ipl) and Assigned(cap) then
    begin
      Selected := False;
      ipl.Text := Caption;
      Caption := '';
      ipl.Font.Assign(cap.Font);
      ipl.Mode := emCaption;
      ipl.Position.X := cap.Position.X;
      ipl.Position.Y := cap.Position.Y;
      ipl.Width := cap.Width;
      ipl.Height := cap.Height;
      FOwner.ShowInplaceEdit;
      shpc.InsertObject(shpc.ChildrenCount, ipl);
    end;
  end;
end;

procedure TTMSFMXTableViewItem.ShowEditForDescription;
var
  ipl: TTMSFMXSearchEdit;
  desc: TText;
  shpc: TControl;
begin
  if CanEditDescription then
  begin
    ipl := FOwner.GetInPlaceEdit;
    desc := ShapeDescription;
    shpc := ShapeCenterRectangle;
    if Assigned(ipl) and Assigned(desc) then
    begin
      Selected := False;
      ipl.Text := Description;
      Description := '';
      ipl.Font.Assign(desc.Font);
      ipl.Mode := emDescription;
      ipl.Position.X := desc.Position.X;
      ipl.Position.Y := desc.Position.Y;
      ipl.Width := desc.Width;
      ipl.Height := desc.Height;
      FOwner.ShowInplaceEdit;
      shpc.InsertObject(shpc.ChildrenCount, ipl);
    end;
  end;
end;

procedure TTMSFMXTableViewItem.ToggleDeleteButton;
var
  anim, animopc: TAnimation;
begin
  anim := DeleteAnimation;
  animopc := DeleteOpacityAnimation;
  if Assigned(anim) and Assigned(animopc) then
  begin
    anim.Duration := 0.2;
    animopc.Duration := 0.2;
    if FDeleteButton then
    begin
      (anim as TFloatAnimation).StopValue := 60;
      (animopc as TFloatAnimation).stopValue := 1;
    end
    else
    begin
      (anim as TFloatAnimation).StopValue := 0;
      (animopc as TFloatAnimation).stopValue := 0;
    end;
    anim.Start;
    animopc.Start;
  end;
end;

procedure TTMSFMXTableViewItem.UpdateItem;
var
  shpr: TRectangle;
  ct: TControl;
begin
  shpr := ShapeRightRectangle;
  if Assigned(shpr) then
  begin
    ct := (shpr.FindStyleResource('item_checkbox') as TControl);
    if Assigned(ct) and (ct is TCheckBox) then
      (ShapeRightRectangle.FindStyleResource('item_checkbox') as TCheckBox).IsChecked := DataBoolean;

    ct := (shpr.FindStyleResource('item_text') as TControl);
    if Assigned(ct) and (ct is TText) then
      (ShapeRightRectangle.FindStyleResource('item_text') as TText).Text := DataString;

    ct := (shpr.FindStyleResource('item_radiobutton') as TControl);
    if Assigned(ct) and (ct is TRadioButton) then
      (ShapeRightRectangle.FindStyleResource('item_radiobutton') as TRadioButton).IsChecked := DataBoolean;

    ct := (shpr.FindStyleResource('item_trackbar') as TControl);
    if Assigned(ct) and (ct is TTrackBar) then
      (ShapeRightRectangle.FindStyleResource('item_trackbar') as TTrackBar).Value := DataValue;

    ct := (shpr.FindStyleResource('item_arcdial') as TControl);
    if Assigned(ct) and (ct is TArcDial) then
      (ShapeRightRectangle.FindStyleResource('item_arcdial') as TArcDial).Value := DataValue;
  end;

  if Assigned(FOwner.OnItemData) then
    FOwner.OnItemData(Self, Self, Shape, ShapeRightRectangle);
end;

procedure TTMSFMXTableViewItem.UpdateTableViewItem;
begin
  FOwner.UpdateTableViewItems;
end;

{ TTMSFMXTableViewItems }

function TTMSFMXTableViewItems.Add: TTMSFMXTableViewItem;
begin
  result := TTMSFMXTableViewItem(inherited Add);
end;

constructor TTMSFMXTableViewItems.Create(AOwner: TTMSFMXTableView);
begin
  inherited Create(CreateItemClass);
  FOwner := AOwner;
end;

function TTMSFMXTableViewItems.CreateItemClass: TCollectionItemClass;
begin
  Result := TTMSFMXTableViewItem;
end;

function TTMSFMXTableViewItems.GetItem(
  Index: Integer): TTMSFMXTableViewItem;
begin
  Result := TTMSFMXTableViewItem(inherited Items[Index]);
end;

function TTMSFMXTableViewItems.GetOwner: TPersistent;
begin
  Result := FOwner;
end;

function TTMSFMXTableViewItems.Insert(Index: Integer): TTMSFMXTableViewItem;
begin
  Result := TTMSFMXTableViewItem(inherited Insert(Index));
end;

procedure TTMSFMXTableViewItems.SetItem(Index: Integer;
  const Value: TTMSFMXTableViewItem);
begin
  inherited Items[Index] := Value;
end;

function TTMSFMXTableViewItems.Compare(Item1, Item2: TTMSFMXTableViewItem;  ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending): integer;
var
  cIdx1, cIdx2: integer;
  itemcap1, itemcap2: String;
begin
  Result := 0;

  if Assigned(FOwner.OnItemCompare) then
    FOwner.OnItemCompare(FOwner, Item1, Item2, Result)
  else
  begin
    case FOwner.CategoryType of
      ctNone, ctAlphaBetic, ctAlphaNumericFirst, ctAlphaNumericLast:
      begin
        if not ACaseSensitive then
          Result := AnsiCompareStr(UpperCase(item1.GetStrippedCaption), UpperCase(item2.GetStrippedCaption))
        else
          Result := AnsiCompareStr(item1.GetStrippedCaption, item2.GetStrippedCaption)
      end;
      ctCustom:
      begin
        cIdx1 := FOwner.Categories.ItemIndexById(item1.CategoryID);
        cIdx2 := FOwner.Categories.ItemIndexById(item2.CategoryID);

        if cIdx1 < cIdx2 then
          result :=  -1
        else if cIdx1 > cIdx2 then
          result := 1
        else
        begin
          itemcap1 := item1.GetStrippedCaption;
          itemcap2 := item2.GetStrippedCaption;
          if not ACaseSensitive then
          begin
            itemcap1 := UpperCase(itemcap1);
            itemcap2 := UpperCase(itemcap2);
          end;

          if itemcap1 < itemcap2 then
            result :=  -1
          else if itemcap1 > itemcap2 then
            result := 1
          else result := 0
        end;
      end;
    end;

    if FOwner.LayoutMode = lmGroup then
    begin
      cIdx1 := item1.GroupIndex;
      cIdx2 := item2.GroupIndex;

      if cIdx1 < cIdx2 then
        result :=  -1
      else if cIdx1 > cIdx2 then
        result := 1;
    end;

    case ASortingMode of
      mDescending: Result := Result * -1;
    end;
  end;
end;

procedure TTMSFMXTableViewItems.QuickSort(L, R: Integer; ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending);
var
  I, J, p: Integer;
  Save: TCollectionItem;
  SortList: TList<TCollectionItem>;
begin

  //This cast allows us to get at the private elements in the base class
  SortList := TShadowedCollection(Self).FItems;

  repeat
    I := L;
    J := R;
    P := (L + R) shr 1;
    repeat
      while Compare(Items[I], Items[P], ACaseSensitive, ASortingMode) < 0 do
        Inc(I);
      while Compare(Items[J], Items[P], ACaseSensitive, ASortingMode) > 0 do
        Dec(J);
      if I <= J then begin
        Save              := SortList.Items[I];
        SortList.Items[I] := SortList.Items[J];
        SortList.Items[J] := Save;
        if P = I then
          P := J
        else if P = J then
          P := I;
        Inc(I);
        Dec(J);
      end;
    until I > J;
    if L < J then
      QuickSort(L, J, ACaseSensitive, ASortingMode);
    L := I;
  until I >= R;
end;

procedure TTMSFMXTableViewItems.Sort(ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending);
begin
  BeginUpdate;
  if Count > 1 then
    QuickSort(0, pred(Count), ACaseSensitive, ASortingMode);
  EndUpdate;
end;

{ TTMSFMXTableViewCategory }

constructor TTMSFMXTableViewCategory.Create(Collection: TCollection);
begin
  inherited;
  Fowner := (Collection as TTMSFMXTableViewCategories).FOwner;
  FID := (Collection as TTMSFMXTableViewCategories).Count - 1;
end;

procedure TTMSFMXTableViewCategory.SetId(const Value: integer);
begin
  if FId <> value then
    FId := Value;
end;

procedure TTMSFMXTableViewCategory.SetLookupText(const Value: String);
begin
  if FLookupText <> Value then
    FLookupText := Value;
end;

function TTMSFMXTableViewCategory.GetStrippedCaption: String;
begin
  Result := GetStrippedHTMLText(FOwner.BoundsRect, FOwner.Canvas, Caption);
end;

procedure TTMSFMXTableViewCategory.SetCaption(const Value: String);
begin
  if FCaption <> value then
    FCaption := Value;
end;

{ TTMSFMXTableViewCategories }

function TTMSFMXTableViewCategories.Add: TTMSFMXTableViewCategory;
begin
  Result := TTMSFMXTableViewCategory(inherited Add);
end;

procedure TTMSFMXTableViewCategories.Clear;
begin
  if Count > 0 then
  begin
    while Count > 0 do
    begin
      {$IFDEF DELPHI_LLVM}
      TCollectionItem(Items[Count - 1]).Release;
      {$ELSE}
      TCollectionItem(Items[Count - 1]).Free;
      {$ENDIF}
    end;
  end;
end;

function TTMSFMXTableViewCategories.Compare(Item1, Item2: TTMSFMXTableViewCategory; ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending): integer;
var
  item1cap, item2cap: String;
begin
  item1cap := item1.GetStrippedCaption;
  item2cap := item2.GetStrippedCaption;

  if not ACaseSensitive then
  begin
    item1cap := UpperCase(item1cap);
    item2cap := UpperCase(item2cap);
  end;

  if item1cap < item2cap then
    Result :=  -1
  else if item1cap > item2cap then
    Result := 1
  else
    Result := 0;

  case ASortingMode of
    mDescending: Result := Result * -1;
  end;
end;

constructor TTMSFMXTableViewCategories.Create(AOwner: TTMSFMXTableView);
begin
  inherited Create(TTMSFMXTableViewCategory);
  FOwner := AOwner;
end;

function TTMSFMXTableViewCategories.GetItem(
  Index: Integer): TTMSFMXTableViewCategory;
begin
  Result := TTMSFMXTableViewCategory(inherited Items[Index]);
end;

function TTMSFMXTableViewCategories.GetOwner: TPersistent;
begin
  Result := FOwner;
end;

function TTMSFMXTableViewCategories.Insert(
  Index: Integer): TTMSFMXTableViewCategory;
begin
  Result := TTMSFMXTableViewCategory(inherited Insert(Index));
end;

function TTMSFMXTableViewCategories.ItemById(
  id: integer): TTMSFMXTableViewCategory;
var
  i: integer;
  it: TTMSFMXTableViewCategory;
begin
  Result := nil;
  for I := 0 to Count - 1 do
  begin
    it := Items[I];
    if it.ID = id then
    begin
      Result := it;
      break;
    end;
  end;
end;

function TTMSFMXTableViewCategories.ItemIndexByID(id: integer): integer;
var
  ci: TTMSFMXTableViewCategory;
begin
  ci := ItemByID(id);
  if Assigned(ci) then
    result := ci.Index
  else
    result := -1;
end;

procedure TTMSFMXTableViewCategories.QuickSort(L, R: Integer; ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending);
var
  I, J, p: Integer;
  Save: TCollectionItem;
  SortList: TList<TCollectionItem>;
begin
  //This cast allows us to get at the private elements in the base class
  SortList := TShadowedCollection(Self).FItems;

  repeat
    I := L;
    J := R;
    P := (L + R) shr 1;
    repeat
      while Compare(Items[I], Items[P], ACaseSensitive, ASortingMode) < 0 do
        Inc(I);
      while Compare(Items[J], Items[P], ACaseSensitive, ASortingMode) > 0 do
        Dec(J);
      if I <= J then begin
        Save              := SortList.Items[I];
        SortList.Items[I] := SortList.Items[J];
        SortList.Items[J] := Save;
        if P = I then
          P := J
        else if P = J then
          P := I;
        Inc(I);
        Dec(J);
      end;
    until I > J;
    if L < J then
      QuickSort(L, J, ACaseSensitive, ASortingMode);
    L := I;
  until I >= R;
end;

procedure TTMSFMXTableViewCategories.SetItem(Index: Integer;
  const Value: TTMSFMXTableViewCategory);
begin
  inherited Items[Index] := Value;
end;

procedure TTMSFMXTableViewCategories.Sort(ACaseSensitive: Boolean = True; ASortingMode: TTMSFMXTableViewSortingMode = mAscending);
begin
  FOwner.BeginUpdate;
  if Count > 1 then
    QuickSort(0, pred(Count), ACaseSensitive, ASortingMode);
  FOwner.EndUpdate;
end;

{ TTMSFMXTableViewScrollBarShape }

constructor TTMSFMXTableViewScrollBarShape.Create(AOwner: TComponent);
begin
  inherited;
  FPosition := 0;
  FPageSize := 40;
  FElementOpacity := 1;
end;

procedure TTMSFMXTableViewScrollBarShape.Paint;
var
  r: TRectF;
begin
  r.Left := 0;
  r.Top := Position;
  r.Right := Width;
  r.Bottom := Position + PageSize;
  Canvas.FillRect(r, r.Width, r.Width, AllCorners, ElementOpacity, Fill);
end;

procedure TTMSFMXTableViewScrollBarShape.SetElementOpacity(const Value: Single);
begin
  if FElementOpacity <> Value then
  begin
    FElementOpacity := Value;
    Repaint;
  end;
end;

procedure TTMSFMXTableViewScrollBarShape.SetPageSize(const Value: Single);
begin
  if FPageSize <> Value then
  begin
    FPageSize := Value;
    Repaint;
  end;
end;

procedure TTMSFMXTableViewScrollBarShape.SetPosition(const Value: Single);
begin
  if FPosition <> Value then
  begin
    FPosition := Value;
    Repaint;  
  end;
end;

{ TTMSFMXTableViewLookupBarShape }

constructor TTMSFMXTableViewLookupBarShape.Create(AOwner: TComponent);
begin
  inherited;
  FFont :=  TFont.Create;
  FFont.Size := 11;
  FFont.Style := [TFontStyle.fsBold];
  {$IFDEF DELPHIXE6_LVL}
  FFontFill := TBrush.Create(TBrushKind.Solid, MakeColor(106,115,125));
  {$ELSE}
  FFontFill := TBrush.Create(TBrushKind.bkSolid, MakeColor(106,115,125));
  {$ENDIF}
  Fill.Color := MakeColor(157,165,169);
  Stroke.Color := Fill.Color;
end;

destructor TTMSFMXTableViewLookupBarShape.Destroy;
begin
  FFont.Free;
  FFontFill.Free;
  inherited;
end;

procedure TTMSFMXTableViewLookupBarShape.DoMouseLeave;
begin
  inherited;
  FMouseDown := False;
  Repaint;
end;

function TTMSFMXTableViewLookupBarShape.GetCategoryAtXY(X, Y: Single): Integer;
var
  i, s: Integer;
  r: TRectF;
  str: String;
  ypos: Single;
begin
  Canvas.Font.Assign(Font);
  Result := -1;
  s := FTableView.Categories.Count - 1;
  ypos := 3;
  for I := 0 to s do
  begin
    str := FTableView.Categories[I].LookupText;
    r.Left := 0;
    r.Top := ypos;
    r.Height := Canvas.TextHeight(str);
    ypos := ypos + r.Height + 3;
    r.Width := Width;
    if PointInRect(PointF(X, Y), r) then
    begin
      Result := I;
      Break;
    end;
  end;
end;

function TTMSFMXTableViewLookupBarShape.GetCharacter(Idx: Integer): String;
begin
  Result := '';
  if Assigned(FTableView) then
  begin
    if (FTableView.CategoryType = ctAlphaNumericLast) or (FTableView.CategoryType = ctAlphaBetic) then
    begin
      if Idx < 27 then
        Result := chr(ord('A') + (Idx - 1))
      else
        Result := chr(ord('0') + (Idx - 27));
    end
    else
    begin
      if Idx < 11 then
        Result := chr(ord('0') + (Idx - 1))
      else
        Result := chr(ord('A') + (Idx - 11));
    end;
  end;
end;

function TTMSFMXTableViewLookupBarShape.GetCharacterCount: Integer;
begin
  Result := 26;
  if Assigned(FTableView) and ((FTableView.CategoryType = ctAlphaNumericFirst) or (FTableView.CategoryType = ctAlphaNumericLast)) then
    Result := 36;
end;

procedure TTMSFMXTableViewLookupBarShape.MouseDown(Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
begin
  inherited;
  FMouseDown := True;
  ProcessLookup(X, Y);
  Repaint;
end;

procedure TTMSFMXTableViewLookupBarShape.MouseMove(Shift: TShiftState; X,
  Y: Single);
begin
  inherited;
  if FMouseDown then
    ProcessLookup(X, Y);
end;

procedure TTMSFMXTableViewLookupBarShape.MouseUp(Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
begin
  inherited;
  FMouseDown := False;
  ProcessLookup(X, Y, False);
  Repaint;
end;

procedure TTMSFMXTableViewLookupBarShape.Paint;
var
  str: String;
  I, s: Integer;
  r: TRectF;
  f: TBrush;
  ypos: Single;
begin
  {$IFDEF DELPHIXE6_LVL}
  f := TBrush.Create(TBrushKind.Solid, claNull);
  {$ELSE}
  f := TBrush.Create(TBrushKind.bkSolid, claNull);
  {$ENDIF}
  f.Assign(Canvas.Fill);
  if Assigned(FTableView) then
  begin
    {$IFDEF DELPHIXE6_LVL}
    Canvas.Stroke.Kind := TBrushKind.None;
    {$ELSE}
    Canvas.Stroke.Kind := TBrushKind.bkNone;
    {$ENDIF}
    Canvas.Fill.Assign(FontFill);
    Canvas.Font.Assign(Font);
    case FTableView.CategoryType of
      ctAlphaBetic, ctAlphaNumericFirst, ctAlphaNumericLast:
      begin
        s := GetCharacterCount;
        for I := 1 to s do
        begin
          str := GetCharacter(I);
          r.Left := 0;
          r.Top := (I - 1) * (Height / s);
          r.Height := (Height / s);
          r.Width := Width;
          {$IFDEF DELPHIXE6_LVL}
          Canvas.FillText(r, str, False, 1, [], TTextAlign.Center);
          {$ELSE}
          Canvas.FillText(r, str, False, 1, [], TTextAlign.taCenter);
          {$ENDIF}
        end;
      end;
      ctCustom:
      begin
        s := FTableView.Categories.Count - 1;
        ypos := 3;
        for I := 0 to s do
        begin
          str := FTableView.Categories[I].LookupText;
          r.Left := 0;
          r.Top := ypos;
          r.Height := Canvas.TextHeight(str);
          ypos := ypos + r.Height + 3;
          r.Width := Width;

          {$IFDEF DELPHIXE6_LVL}
          Canvas.FillText(r, str, False, 1, [], TTextAlign.Center);
          {$ELSE}
          Canvas.FillText(r, str, False, 1, [], TTextAlign.taCenter);
          {$ENDIF}
        end;
      end;
    end;
  end;

  Canvas.Fill.Assign(f);
  if FMouseDown then
  begin
    case FTableView.CategoryType of
      ctAlphaBetic, ctAlphaNumericFirst, ctAlphaNumericLast: Canvas.FillRect(BoundsRect, Width / 2, Width / 2, AllCorners, 0.65);
      ctCustom: Canvas.FillRect(BoundsRect, 0, 0,AllCorners, 0.65);
    end;
  end;

  f.Free;
end;

procedure TTMSFMXTableViewLookupBarShape.ProcessLookUp(X, Y: Single; DoLookup: Boolean = True);
var
  l: Integer;
  ch: String;
  catid: Integer;
begin
  if Assigned(FTableView) then
  begin
    case FTableView.CategoryType of
      ctCustom:
      begin
        l := GetCategoryAtXY(X, Y);
        if (l > -1) and (l < FTableView.Categories.Count) then
        begin
          catid := FTableView.Categories[l].Id;
          if (catid >= 0) and (catid <= Length(FTableView.FCustomChar) -1) then
          begin
            if FTableView.FCustomChar[catid] then
            begin
              if DoLookup then
                FTableView.LookupCustomCategory(catid)
              else if Assigned(FTableView.OnCategoryClick) then
                FTableView.OnCategoryClick(Self, '', catid);
            end;
          end;
        end;
      end;
      ctAlphaBetic, ctAlphaNumericFirst, ctAlphaNumericLast:
      begin
        l := 1 + Round(GetCharacterCount / Height * Y);
        if (l >= 1) and (l <= GetCharacterCount) then
        begin
          if Assigned(FTableView) then
          begin
            ch := GetCharacter(l);
            if (ch <> '') then
            begin
              if DoLookup then
                FTableView.LookupCategory(ch)
              else if Assigned(FTableView.OnCategoryClick) then
                FTableView.OnCategoryClick(Self, ch, -1);
            end;
          end;
        end;
      end;
    end;
  end;
end;

procedure TTMSFMXTableViewLookupBarShape.SetFont(const Value: TFont);
begin
  if FFont <> Value then
  begin
    FFont.Assign(Value);
    Repaint;
  end;
end;

procedure TTMSFMXTableViewLookupBarShape.SetFontFill(const Value: TBrush);
begin
  if FFontFill <> Value then
  begin
    FFontFill.Assign(Value);
    Repaint;
  end;
end;

{ TTMSFMXTableViewCategoryShape }

constructor TTMSFMXTableViewCategoryShape.Create(AOwner: TComponent);
begin
  inherited;
  Corners := [];
end;

function TTMSFMXTableViewCategoryShape.ShapeCaption: TText;
var
  fmxobj: TFMXObject;
begin
  Result := nil;
  fmxobj := FindStyleResource('categorytext');
  if fmxobj is TText then
    Result := fmxobj as TText;
end;


initialization
begin
  RegisterFmxClasses([TTMSFMXTableView, TTMSFMXTableViewItemShape,
                      TTMSFMXTableViewCategoryShape,
                      TTMSFMXTableViewScrollBarShape,
                      TTMSFMXTableViewLookupBarShape,
                      TTMSFMXTableViewBulbShape]);
end;

end.
