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

interface

{$I TMSDEFS.INC}

uses
  FMX.TMSGrid, FMX.TMSGridData, Classes, FMX.Bind.DBLinks, Math, UITypes, System.Bindings.EvalProtocol, Data.Bind.Components,
  Data.Bind.Editors, System.Bindings.ObjEval, FMX.Dialogs, SysUtils, FMX.Types, FMX.Edit, FMX.ExtCtrls, FMX.ListBox
  {$IFDEF DELPHIXE3_LVL}
  ,Data.Bind.Grid, Data.Db, Data.Bind.ObjectScope, Data.Bind.DBScope, FMX.Bind.Grid, Generics.Collections, FMX.Colors, UIConsts
  {$ENDIF}
  {$IFDEF DELPHIXE5_LVL}
  , FMX.Graphics, FMX.DateTimeCtrls
  {$ENDIF}
  ;

type

  TTMSFMXProtectedGrid = class(TTMSFMXGrid);

  TTMSFMXBindDBGridLink = class(TBindGridLink)
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TTMSFMXGridItemEditorObject = class
  private
    FGrid: TTMSFMXGrid;
    FIndex: Integer;
    function GetOwner: TTMSFMXGrid;
    function GetCells(ACol: Integer): string;
    procedure SetCells(ACol: Integer; const Value: string);
    function GetBitmaps(ACol: Integer): TBitmap;
    procedure SetBitmaps(ACol: Integer; const Value: TBitmap);
    function GetBooleans(ACol: Integer): Boolean;
    procedure SetBooleans(ACol: Integer; const Value: Boolean);
    function GetAlphaColors(ACol: Integer): TAlphaColor;
    procedure SetAlphaColors(ACol: Integer; const Value: TAlphaColor);
    function GetColors(ACol: Integer): TColor;
    procedure SetColors(ACol: Integer; const Value: TColor);
  public
    property Owner: TTMSFMXGrid read GetOwner;
    property AlphaColors[ACol: Integer]: TAlphaColor read GetAlphaColors write SetAlphaColors;
    property Colors[ACol: Integer]: TColor read GetColors write SetColors;
    property Cells[ACol: Integer]: string read GetCells write SetCells;
    property Bitmaps[ACol: Integer]: TBitmap read GetBitmaps write SetBitmaps;
    property Booleans[ACol: Integer]: Boolean read GetBooleans write SetBooleans;
  end;

  TTMSFMXBindGridEditor = class(TBindGridEditor)
  private
    FUpdateCounter: Integer;
    FEditorObject: TTMSFMXGridItemEditorObject;
    function IsEmpty: Boolean;
    function GetFixedRows: Integer;
    function GetFixedCols: Integer;
    function GetRowIndex: Integer;
    function GetColumnIndex: Integer;
  protected
    procedure BeginUpdate; override;
    procedure EndUpdate; override;
    function AddItem(Select: Boolean): IScope; override;
    function CanInsertItem: Boolean; override;
    function InsertItem(Select: Boolean): IScope; override;
    function CurrentItem: IScope; override;
    function GetRowCount: Integer; override;
    function MoveNext: Boolean; override;
    procedure DeleteToEnd; override;
    procedure ClearList; override;
    function GetSelectedText: string; override;
    procedure SetSelectedText(const AValue: string); override;
    procedure GetColumnIndices(ANames: TStrings); override;
    procedure GetColumnNames(ANames: TStrings); override;
    function GetPositionGetter(var AGetter: string;
      var ABase: Integer): Boolean; override;
    function GetPositionSetter(var ASetter: string;
      var ABase: Integer): Boolean; override;

    function CurrentObject: TObject; override;
    function CurrentIndex: Integer; override;

  public
    constructor Create(AGrid: TTMSFMXGrid);
    destructor Destroy; override;
  end;

  TTMSFMXBindGridEditorFactory = class(TBindEditorFactory)
  public
    constructor Create; override;
    function Supports(AIntf: TGuid; AObject: TObject): Boolean; override;
    function CreateEditor(AIntf: TGuid;
      AObject: TObject): IInterface; override;
  end;

  {$IFDEF DELPHIXE3_LVL}
  TTMSFMXBaseLinkGridToDataSourceControlManager = class(TInterfacedObject, ILinkGridToDataSourceControlManager)
  private
    FGrid: TTMSFMXGrid;
    FDefaultColumnWidth: Integer;
    function AddColumn(
      const ADescription: TCreateColumnDescription): Integer; overload;
    function DescribeColumn(
      AIndex: Integer; const ADescription: TCreateColumnDescription): TLinkGridColumnDescription; overload;
    procedure UpdateColumn(AIndex: Integer;
      const ACreateDescription: TCreateColumnDescription); overload;
  protected
    function FormatCellDataExpression(AIndex: Integer): string; virtual; abstract;
    function SupportsMemberType(AMemberType: TScopeMemberType; var ALiteralExpression: string): Boolean; virtual;
    function CreateColumnDescription(
      AIndex: Integer; ADescription: TCreateColumnDescription): TLinkGridColumnDescription; virtual;
    function GetColumnClass(
       ADescription: TCreateColumnDescription): TComponentClass; virtual;
  protected
    procedure BeginUpdate;
    procedure EndUpdate;
    function GetDefaultColumnWidth: Integer;
    procedure SetDefaultColumnWidth(AWidth: Integer);
    function CanAddColumn(AColumn: TBaseLinkGridToDataSourceColumn): Boolean; overload;
    function CanAddColumn(ADataSource: TBaseLinkingBindSource; const AMemberName: string): Boolean; overload;
    procedure ClearColumns;
    function DescribeColumn(AIndex: Integer; AColumn: TBaseLinkGridToDataSourceColumn): TLinkGridColumnDescription; overload;
    function DescribeColumn(AIndex: Integer; ADataSource: TBaseLinkingBindSource; const AMemberName: string): TLinkGridColumnDescription; overload;
    function AddColumn(AColumn: TBaseLinkGridToDataSourceColumn): Integer; overload;
    function AddColumn(ADataSource: TBaseLinkingBindSource; const AMemberName: string): Integer; overload;
    procedure UpdateColumn(AIndex: Integer; AColumn: TBaseLinkGridToDataSourceColumn); overload;
    procedure UpdateColumn(AIndex: Integer; ADataSource: TBaseLinkingBindSource; const AMemberName: string); overload;
    function GetPositionGetter(var AGetter: string; var ABase: Integer): Boolean;
    function GetPositionSetter(var ASetter: string; var ABase: Integer): Boolean;
  public
    constructor Create(AGrid: TTMSFMXGrid);
  end;

  TTMSFMXLinkGridToDataSourceColumnFactory = class(TLinkGridToDataSourceColumnFactory)
  public
    function FrameworkExt: string; override;
    function GridClasses: TArray<TComponentClass>; override;
    function UsesUnits: TArray<string>; override;
    function Supports(AIntf: TGuid; AGrid: TComponent): Boolean; override;
    function CreateFactory(AIntf: TGuid;
     AGrid: TComponent): IInterface; override;
  end;

  TTMSFMXLinkGridToDataSourceControlManager = class(TTMSFMXBaseLinkGridToDataSourceControlManager)
  private
    FGrid: TTMSFMXGrid;
  protected
    function FormatCellDataExpression(AIndex: Integer): string; override;
    function SupportsMemberType(AMemberType: TScopeMemberType; var ALiteralExpression: string): Boolean; override;
  public
    constructor Create(AGrid: TTMSFMXGrid);
  end;

  {$ENDIF}

implementation

{$IFDEF DELPHIXE3_LVL}

{ TTMSFMXBaseLinkGridToDataSourceControlManager }

function TTMSFMXBaseLinkGridToDataSourceControlManager.AddColumn(
  ADataSource: TBaseLinkingBindSource; const AMemberName: string): Integer;
var
  LDescription: TCreateColumnDescription;
begin
  LDescription := TCreateColumnDescription.Create(ADataSource, AMemberName);
  Result := AddColumn(LDescription);
end;

procedure TTMSFMXBaseLinkGridToDataSourceControlManager.BeginUpdate;
begin
  if not Self.FGrid.SaveDataSetData then
    Self.FGrid.BeginUpdate;
end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.DescribeColumn(
  AIndex: Integer; AColumn: TBaseLinkGridToDataSourceColumn): TLinkGridColumnDescription;
begin
  Result := DescribeColumn(AIndex, TCreateColumnDescription.Create(AColumn as TLinkGridToDataSourceColumn));
end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.DescribeColumn(
  AIndex: Integer; ADataSource: TBaseLinkingBindSource; const AMemberName: string): TLinkGridColumnDescription;
begin
  Result := DescribeColumn(AIndex, TCreateColumnDescription.Create(ADataSource, AMemberName));
end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.AddColumn(
  AColumn: TBaseLinkGridToDataSourceColumn): Integer;
begin
  Result := AddColumn(TCreateColumnDescription.Create(AColumn as TLinkGridToDataSourceColumn));
end;

procedure TTMSFMXBaseLinkGridToDataSourceControlManager.UpdateColumn(
  AIndex: Integer; const ACreateDescription: TCreateColumnDescription);
var
  obj: TColumnDescObject;
begin
  FGrid.Cells[AIndex, 0] := ACreateDescription.Header;
  if ACreateDescription.Width <> -1 then
    FGrid.ColumnWidths[AIndex] := ACreateDescription.Width;

  if (AIndex >= 0) and (AIndex <= FGrid.ColumnDescList.Count - 1) then
  begin
    if FGrid.ColumnDescList.Items[AIndex] is TColumnDescObject then
    begin
      obj := FGrid.ColumnDescList.Items[AIndex] as TColumnDescObject;
      obj.ColumnDesc := ACreateDescription;
    end;
  end;
end;

procedure TTMSFMXBaseLinkGridToDataSourceControlManager.UpdateColumn(
  AIndex: Integer; AColumn: TBaseLinkGridToDataSourceColumn);
var
  LCreateDescription: TCreateColumnDescription;
begin
  LCreateDescription := TCreateColumnDescription.Create(AColumn as TLinkGridToDataSourceColumn);
  UpdateColumn(AIndex, LCreateDescription);
end;

procedure TTMSFMXBaseLinkGridToDataSourceControlManager.UpdateColumn(AIndex: Integer; ADataSource: TBaseLinkingBindSource; const AMemberName: string);
var
  LCreateDescription: TCreateColumnDescription;
begin
  LCreateDescription := TCreateColumnDescription.Create(ADataSource, AMemberName);
  UpdateColumn(AIndex, LCreateDescription);
end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.AddColumn(
  const ADescription: TCreateColumnDescription): Integer;
var
  obj: TColumnDescObject;
begin
  FGrid.ColumnCount := FGrid.ColumnCount + 1;
  if ADescription.Width <> -1 then
    FGrid.ColumnWidths[FGrid.ColumnCount - 1] := ADescription.Width;

  FGrid.Cells[FGrid.ColumnCount - 1, 0] := ADescription.Header;
  obj := TColumnDescObject.Create;
  obj.ColumnDesc := ADescription;
  if FGrid.ColumnDescList.IndexOf(obj) = -1 then
    FGrid.ColumnDescList.Add(obj)
  else
    obj.Free;

  Result := FGrid.ColumnCount -1;
end;

const
  sUnknown = '''(unknown)''';
  sSelectedText = 'SelectedText(Self)';

function TTMSFMXBaseLinkGridToDataSourceControlManager.CreateColumnDescription(
  AIndex: Integer;
  ADescription: TCreateColumnDescription): TLinkGridColumnDescription;
var
  LColumn: TLinkGridColumnDescription;
  FPairsList: TList<TLinkGridColumnExpressionPair>;
  LPair: TLinkGridColumnExpressionPair;
  LFormatColumnExpressions: TArray<TLinkGridColumnExpressionPair>;
  LFormatCellExpressions: TArray<TLinkGridColumnExpressionPair>;
  LParseCellExpressions: TArray<TLinkGridColumnExpressionPair>;
  LCellExpression: String;
  LMemberName: string;
  LMemberType: TScopeMemberType;
  LMemberGetter: string;
  LMemberSetter: string;
  LSelectedText: string;
  LLiteralExpression: string;
begin
  AIndex := AIndex + FGrid.FixedColumns;
  LMemberName := ADescription.MemberName;
  LMemberType := ADescription.MemberType;
  LMemberGetter := ADescription.MemberGetter;
  LMemberSetter := ADescription.MemberSetter;

  FPairsList := TList<TLinkGridColumnExpressionPair>.Create;
  try
    FPairsList.Clear;
    LFormatColumnExpressions := FPairsList.ToArray;

    if not SupportsMemberType(LMemberType, LLiteralExpression) then
    begin
      Assert(LLiteralExpression <> '');
      LCellExpression := LLiteralExpression;
    end
    else
    begin
      LCellExpression := LMemberGetter;
      Assert(LMemberGetter <> '');
    end;

    if ADescription.CustomFormat <> '' then
      LCellExpression := Format(ADescription.CustomFormat, [LCellExpression]);
    FPairsList.Clear;
    if LCellExpression <> '' then
    begin
      LPair := TLinkGridColumnExpressionPair.Create(
        FormatCellDataExpression(AIndex), LCellExpression);
      FPairsList.Add(LPair);
      LFormatCellExpressions := FPairsList.ToArray;
    end;

    if (LMemberSetter = '')   then
      LCellExpression := ''
    else
    begin
      case LMemberType of
        mtUnknown:
          LCellExpression := '';
        mtBitmap:
          LCellExpression := '';
      else
        LCellExpression := LMemberSetter;
        Assert(LCellExpression <> '');
      end;
    end;
    FPairsList.Clear;
    if LCellExpression <> '' then
    begin
      if ADescription.CustomParse <> '' then
        LSelectedText := Format(ADescription.CustomParse, [sSelectedText])
      else
        LSelectedText := sSelectedText;

      LPair := TLinkGridColumnExpressionPair.Create(
      LSelectedText, LCellExpression);   // do not localize
      FPairsList.Add(LPair);
    end;
    LParseCellExpressions := FPairsList.ToArray;

    LColumn := TLinkGridColumnDescription.Create(nil, '',
     AIndex, '', ADescription.MemberName, ADescription.ColumnStyle,
     LFormatColumnExpressions, LFormatCellExpressions, LParseCellExpressions);

    Result := LColumn;
  finally
    FPairsList.Free;
  end;

end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.DescribeColumn(
  AIndex: Integer; const ADescription: TCreateColumnDescription): TLinkGridColumnDescription;
begin
  Result := CreateColumnDescription(AIndex, ADescription);
end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.GetColumnClass(ADescription: TCreateColumnDescription): TComponentClass;
begin
  Result := nil;
end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.GetDefaultColumnWidth: Integer;
begin
  Result := FDefaultColumnWidth;
end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.GetPositionGetter(
  var AGetter: string; var ABase: Integer): Boolean;
begin
  AGetter := 'Selected'; // Do not localize
  ABase := 1;
  Exit(True);
end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.GetPositionSetter(
  var ASetter: string; var ABase: Integer): Boolean;
begin
  ASetter := 'Selected'; // Do not localize
  ABase := 1;
  Exit(True);
end;


procedure TTMSFMXBaseLinkGridToDataSourceControlManager.SetDefaultColumnWidth(
  AWidth: Integer);
begin
  FDefaultColumnWidth := AWidth;
end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.SupportsMemberType(
  AMemberType: TScopeMemberType; var ALiteralExpression: string): Boolean;
begin
  Result := True;
end;

procedure TTMSFMXBaseLinkGridToDataSourceControlManager.EndUpdate;
begin
  if not Self.FGrid.SaveDataSetData then
    FGrid.EndUpdate;
end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.CanAddColumn(
  AColumn: TBaseLinkGridToDataSourceColumn): Boolean;
begin
  Result := True;
end;

function TTMSFMXBaseLinkGridToDataSourceControlManager.CanAddColumn(
  ADataSource: TBaseLinkingBindSource; const AMemberName: string): Boolean;
begin
  Result := True;
end;

procedure TTMSFMXBaseLinkGridToDataSourceControlManager.ClearColumns;
begin
  if Self.FGrid.SaveDataSetData then
    Exit;


  FGrid.BeginUpdate;
  try
    FGrid.ColumnCount := FGrid.FixedColumns;
    FGrid.ColumnDescList.Clear;
  finally
    FGrid.EndUpdate;
  end;
end;

constructor TTMSFMXBaseLinkGridToDataSourceControlManager.Create(AGrid: TTMSFMXGrid);
begin
  FGrid := AGrid;
  FDefaultColumnWidth := 64;
end;


{ TTMSFMXLinkGridToDataSourceControlManager }

constructor TTMSFMXLinkGridToDataSourceControlManager.Create(
  AGrid: TTMSFMXGrid);
begin
  FGrid := AGrid;
  inherited Create(AGrid);
end;

function TTMSFMXLinkGridToDataSourceControlManager.FormatCellDataExpression(
  AIndex: Integer): string;
var
  obj: TColumnDescObject;
  idx: Integer;
  dts: TCustomPrototypeBindSource;
begin
  Result := Format('Cells[%d]', [AIndex]);
  if (AIndex - FGrid.FixedColumns >= 0) and (AIndex - FGrid.FixedColumns <= FGrid.ColumnDescList.Count - 1) then
  begin
    if FGrid.ColumnDescList.Items[AIndex - FGrid.FixedColumns] is TColumnDescObject then
    begin
      obj := (FGrid.ColumnDescList.Items[AIndex - FGrid.FixedColumns] as TColumnDescObject);
      if Assigned(obj) then
      begin
        case obj.ColumnDesc.MemberType of
          mtUnknown: Result := Format('Cells[%d]', [AIndex]);
          mtText:   Result := Format('Cells[%d]', [AIndex]);
          mtInteger: Result := Format('Cells[%d]', [AIndex]);
          mtFloat: Result := Format('Cells[%d]', [AIndex]);
          mtBitmap: Result := Format('Bitmaps[%d]', [AIndex]);
          mtMemo: Result := Format('Cells[%d]', [AIndex]);
          mtBoolean: Result := Format('Booleans[%d]', [AIndex]);
          mtBCD: Result := Format('Cells[%d]', [AIndex]);
          mtUInteger:
          begin
            Result := Format('Cells[%d]', [AIndex]);
            if Assigned(obj.ColumnDesc.DataSource) then
            begin
              if obj.ColumnDesc.DataSource is TCustomPrototypeBindSource then
              begin
                idx := (AIndex - FGrid.FixedColumns);
                dts := (obj.ColumnDesc.DataSource as TCustomPrototypeBindSource);
                if (idx >= 0) and (idx <= dts.FieldDefs.Count - 1) then
                begin
                  if obj.ColumnDesc.MemberName = dts.FieldDefs[idx].Name then
                  begin
                    if dts.FieldDefs[idx].Generator = 'AlphaColors' then
                      Result := Format('AlphaColors[%d]', [AIndex])
                    else if dts.FieldDefs[idx].Generator = 'Colors' then
                      Result := Format('Colors[%d]', [AIndex])
                  end;
                end;
              end;
            end;
          end;
          mtDateTime: Result := Format('Cells[%d]', [AIndex]);
          mtCurrency: Result := Format('Cells[%d]', [AIndex]);
          mtObject: Result := Format('Bitmaps[%d]', [AIndex]);
          mtVariant: Result := Format('Cells[%d]', [AIndex]);
        end;

        if Assigned(FGrid.OnFormatCellDataExpression) then
          FGrid.OnFormatCellDataExpression(Self, obj.ColumnDesc, AIndex, Result);
      end;
    end;
  end;
end;

function TTMSFMXLinkGridToDataSourceControlManager.SupportsMemberType(
  AMemberType: TScopeMemberType; var ALiteralExpression: string): Boolean;
begin
  Result := True;
  case AMemberType of
    mtUnknown:
    begin
      Result := False;
      ALiteralExpression := sUnknown
    end;
  end;
end;

{ TTMSFMXLinkGridToDataSourceColumnFactory }

function TTMSFMXLinkGridToDataSourceColumnFactory.FrameworkExt: string;
const
  sFmx = 'FMX';
begin
  Result := sFmx;
end;

function TTMSFMXLinkGridToDataSourceColumnFactory.GridClasses: TArray<TComponentClass>;
begin
  Result := TArray<TComponentClass>.Create(TTMSFMXGrid);
end;

function TTMSFMXLinkGridToDataSourceColumnFactory.UsesUnits: TArray<string>;
begin
  Result := TArray<string>.Create('FMX.TMSGridDataBinding'); // Do not localize
end;

function TTMSFMXLinkGridToDataSourceColumnFactory.CreateFactory(AIntf: TGuid;
  AGrid: TComponent): IInterface;
begin
  Result := TTMSFMXLinkGridToDataSourceControlManager.Create(TTMSFMXGrid(AGrid));
end;

function TTMSFMXLinkGridToDataSourceColumnFactory.Supports(AIntf: TGuid;
  AGrid: TComponent): Boolean;
begin
  Result := False;
  if AIntf = ILinkGridToDataSourceControlManager then
    if AGrid.InheritsFrom(TTMSFMXGrid) then
      Result := True;
end;
{$ENDIF}

{ TTMSFMXBindGridEditorFactory }

constructor TTMSFMXBindGridEditorFactory.Create;
begin
  inherited;
end;

function TTMSFMXBindGridEditorFactory.CreateEditor(AIntf: TGuid;
  AObject: TObject): IInterface;
begin
  Result := TTMSFMXBindGridEditor.Create(TTMSFMXGrid(AObject));
end;

function TTMSFMXBindGridEditorFactory.Supports(AIntf: TGuid;
  AObject: TObject): Boolean;
begin
  Result := False;
  if {$IFDEF DELPHIXE3_LVL}(AIntf = IBindListEditorCommon) or {$ENDIF} (AIntf = IBindListEditor) then
    if AObject.InheritsFrom(TTMSFMXGrid) then
      Result := True;
end;

{ TTMSFMXBindGridEditor }

function TTMSFMXBindGridEditor.AddItem(Select: Boolean): IScope;
begin
  FEditorObject.FGrid.RowCount := FEditorObject.FGrid.RowCount + 1;
  FEditorObject.FIndex := FEditorObject.FGrid.RowCount - 1;
  if Select then
    FEditorObject.FGrid.Selected := FEditorObject.FIndex;
  Result := WrapObject(FEditorObject);
end;

function TTMSFMXBindGridEditor.GetFixedRows: Integer;
begin
  Result := FEditorObject.FGrid.FixedRows;
end;

const
  sSelected = 'Selected';


function TTMSFMXBindGridEditor.GetPositionGetter(var AGetter: string;
  var ABase: Integer): Boolean;
begin
  ABase := FEditorObject.FGrid.FixedRows;
  AGetter := sSelected;
  Result := True;
end;

function TTMSFMXBindGridEditor.GetPositionSetter(var ASetter: string;
  var ABase: Integer): Boolean;
begin
  ABase := FEditorObject.FGrid.FixedRows;
  ASetter := sSelected;
  Result := True;
end;

function TTMSFMXBindGridEditor.GetFixedCols: Integer;
begin
  Result := FEditorObject.FGrid.FixedColumns;
end;

function TTMSFMXBindGridEditor.GetRowIndex: Integer;
begin
  if FEditorObject.FGrid.Selected >= FEditorObject.FGrid.RowCount then
    Result := FEditorObject.FGrid.RowCount
  else
    Result := FEditorObject.FGrid.Selected;
end;

function TTMSFMXBindGridEditor.GetColumnIndex: Integer;
begin
  Result := FEditorObject.FGrid.ColumnIndex;
end;

procedure TTMSFMXBindGridEditor.GetColumnIndices(ANames: TStrings);
var
  I: Integer;
begin
  for I := 0 to FEditorObject.FGrid.ColumnCount - 1 do
    ANames.Add(IntToStr(I));
end;

procedure TTMSFMXBindGridEditor.GetColumnNames(ANames: TStrings);
var
  I: Integer;
begin
  for I := 0 to FEditorObject.FGrid.ColumnCount - 1 do
    ANames.Add(FEditorObject.FGrid.Columns[I].Name);
end;

function TTMSFMXBindGridEditor.CurrentItem: IScope;
begin
  //FEditorObject.FIndex := FEditorObject.FStringGrid.Row;
  if FEditorObject.FIndex = -1 then
    FEditorObject.FIndex := GetRowIndex;
  Result := WrapObject(FEditorObject);
end;

function TTMSFMXBindGridEditor.CurrentObject: TObject;
begin
  Result := nil
end;

function TTMSFMXBindGridEditor.CurrentIndex: Integer;
begin
  if FEditorObject.FIndex = -1 then
    FEditorObject.FIndex := GetRowIndex;
  Result := FEditorObject.FIndex;
end;

procedure TTMSFMXBindGridEditor.BeginUpdate;
begin
  if not FEditorObject.FGrid.SaveDataSetData then
  begin
    FEditorObject.FGrid.BeginUpdate;
    Inc(FUpdateCounter);
    if FUpdateCounter = 1 then
      FEditorObject.FIndex := -1
  end;
end;

function TTMSFMXBindGridEditor.CanInsertItem: Boolean;
begin
  Result := False; // Not supported
end;

procedure TTMSFMXBindGridEditor.ClearList;
begin
  if FEditorObject.FGrid.SaveDataSetData then
    Exit;

  FEditorObject.FIndex := -1;
  if GetFixedRows > 0 then
  begin
    FEditorObject.FGrid.RowCount := GetFixedRows;
  end
  else
    FEditorObject.FGrid.RowCount := 0;
end;

constructor TTMSFMXBindGridEditor.Create(AGrid: TTMSFMXGrid);
begin
  FEditorObject := TTMSFMXGridItemEditorObject.Create;
  FEditorObject.FGrid := AGrid;
  FEditorObject.FIndex := -1;
end;

procedure TTMSFMXBindGridEditor.DeleteToEnd;
begin
  if FEditorObject.FIndex = -1 then
    FEditorObject.FIndex := GetRowIndex;
  if (FEditorObject.FIndex = -1) or (FEditorObject.FIndex = GetFixedRows) then
    ClearList
  else
    FEditorObject.FGrid.RowCount := FEditorObject.FIndex;
end;

destructor TTMSFMXBindGridEditor.Destroy;
begin
  FEditorObject.Free;
  inherited;
end;

procedure TTMSFMXBindGridEditor.EndUpdate;
begin
  if not FEditorObject.FGrid.SaveDataSetData then
  begin
    FEditorObject.FGrid.EndUpdate;
    Dec(FUpdateCounter);
    Assert(FUpdateCounter >= 0);
    if FUpdateCounter = 0 then
      FEditorObject.FIndex := -1;
  end;
end;

function TTMSFMXBindGridEditor.InsertItem(Select: Boolean): IScope;
begin
  Result := nil;
end;

function TTMSFMXBindGridEditor.IsEmpty: Boolean;
begin
  Result := (FEditorObject.FGrid.RowCount = GetFixedRows)
end;

function TTMSFMXBindGridEditor.GetRowCount: Integer;
begin
  if IsEmpty then
    Result := 0
  else
    Result := FEditorObject.FGrid.RowCount - GetFixedRows;
end;

function TTMSFMXBindGridEditor.MoveNext: Boolean;
begin
  Assert(FUpdateCounter > 0);
  if FEditorObject.FGrid.RowCount = 0 then
    Exit(False);
  if FEditorObject.FIndex = -1 then
    FEditorObject.FIndex := GetRowIndex
  else
    FEditorObject.FIndex :=  FEditorObject.FIndex + 1;
  Result := FEditorObject.FIndex < FEditorObject.FGrid.RowCount;
end;

procedure TTMSFMXBindGridEditor.SetSelectedText(const AValue: string);
begin
  if GetRowIndex >= GetFixedRows then
    if GetColumnIndex >= GetFixedCols then
      FEditorObject.FGrid.Cells[GetColumnIndex, GetRowIndex] := AValue;
end;

function TTMSFMXBindGridEditor.GetSelectedText: string;
var
  edt: TFmxObject;
begin
  Result := '';
  with FEditorObject do
  begin
    if GetRowIndex >= GetFixedRows then
    begin
      if GetColumnIndex >= GetFixedCols then
      begin
        if FGrid.Editing then
        begin
          edt := FGrid.ActiveEditControl;
          if Assigned(edt) then
          begin
            if edt is TEdit then
              Result := (edt as TEdit).Text
            else if edt is TComboBox then
            begin
              if Assigned((edt as TComboBox).Selected) then
                Result := (edt as TComboBox).Selected.Text
            end
            else if edt is TCalendarEdit then
              Result := (edt as TCalendarEdit).Text;
          end;
        end
        else
          Result := FGrid.Cells[GetColumnIndex + FGrid.FixedColumns, GetRowIndex];
      end;
    end;
  end;
end;

{ TTMSFMXGridItemEditorObject }

function TTMSFMXGridItemEditorObject.GetAlphaColors(ACol: Integer): TAlphaColor;
begin
  Result := claNull;
  if FIndex <> -1 then
    Result := FGrid.Colors[ACol, FIndex];
end;

function TTMSFMXGridItemEditorObject.GetBitmaps(ACol: Integer): TBitmap;
begin
  Result := nil;
  if FIndex <> -1 then
    Result := FGrid.Bitmaps[ACol, FIndex];
end;

function TTMSFMXGridItemEditorObject.GetBooleans(ACol: Integer): Boolean;
begin
  Result := False;
  if FIndex <> -1 then
    Result := FGrid.Booleans[ACol, FIndex];
end;

function TTMSFMXGridItemEditorObject.GetCells(ACol: Integer): string;
begin
  Result := '';
  if FIndex <> -1 then
    Result := FGrid.Cells[ACol, FIndex];
end;

function TTMSFMXGridItemEditorObject.GetColors(ACol: Integer): TColor;
begin
  Result := claNull;
  if FIndex <> -1 then
    Result := FGrid.Colors[ACol, FIndex];
end;

function TTMSFMXGridItemEditorObject.GetOwner: TTMSFMXGrid;
begin
  Result := FGrid;
end;

procedure TTMSFMXGridItemEditorObject.SetAlphaColors(ACol: Integer;
  const Value: TAlphaColor);
begin
  if FIndex > -1 then
    if FIndex <= Self.FGrid.RowCount then
      FGrid.Colors[ACol, FIndex] := Value;
end;

procedure TTMSFMXGridItemEditorObject.SetBitmaps(ACol: Integer;
  const Value: TBitmap);
begin
  if FIndex > -1 then
    if FIndex <= Self.FGrid.RowCount then
      FGrid.Bitmaps[ACol, FIndex] := Value;
end;

procedure TTMSFMXGridItemEditorObject.SetBooleans(ACol: Integer;
  const Value: Boolean);
begin
  if FIndex > -1 then
    if FIndex <= Self.FGrid.RowCount then
      FGrid.Booleans[ACol, FIndex] := Value;
end;

procedure TTMSFMXGridItemEditorObject.SetCells(ACol: Integer;
  const Value: string);
begin
  if FIndex > -1 then
  begin
    if FIndex <= Self.FGrid.RowCount then
    begin
//      if FGrid.Editing then
//        TTMSFMXProtectedGrid(FGrid).CancelEdit;

      FGrid.Cells[ACol, FIndex] := Value;
    end;
  end;
end;

procedure TTMSFMXGridItemEditorObject.SetColors(ACol: Integer;
  const Value: TColor);
var
  alc: TAlphaColor;
  R, G, B: Byte;
begin
  if FIndex > -1 then
  begin
    if FIndex <= Self.FGrid.RowCount then
    begin
      R := Byte(Value);
      G := Byte(Value shr 8);
      B := Byte(Value shr 16);
      alc := MakeColor(R, G, B);
      FGrid.Colors[ACol, FIndex] := alc;
    end;
  end;
end;

{ TTMSFMXBindGridLink }

constructor TTMSFMXBindDBGridLink.Create(AOwner: TComponent);
var
  LItem: TExpressionItem;
begin
  inherited;
  Assert(ColumnExpressions.Count = 0);
  Assert(FormatControlExpressions.Count = 0);
  Assert(ClearControlExpressions.Count = 0);
  Assert(PosSourceExpressions.Count = 0);
  Assert(PosControlExpressions.Count = 0);

  LItem := PosControlExpressions.AddExpression;
  LItem.ControlExpression := 'Selected';
  LItem.SourceExpression := 'Math_Max(1, DBUtils_ActiveRecord(Self) + (1))';

  LItem := PosSourceExpressions.AddExpression;
  LItem.ControlExpression := 'Math_Max(1, Selected)';
  LItem.SourceExpression := 'DBUtils_ValidRecNo(Self)';
end;

initialization
begin
  {$IFDEF DELPHIXE3_LVL}
  RegisterLinkGridToDataSourceColumnFactory([TTMSFMXLinkGridToDataSourceColumnFactory]);
  {$ENDIF}
  RegisterBindEditorFactory([TTMSFMXBindGridEditorFactory]);
end;

finalization
begin
  {$IFDEF DELPHIXE3_LVL}
  UnRegisterLinkGridToDataSourceColumnFactory([TTMSFMXLinkGridToDataSourceColumnFactory]);
  {$ENDIF}
  UnRegisterBindEditorFactory([TTMSFMXBindGridEditorFactory]);
end;

end.

