unit uDataGridRoutines;

interface

uses
  System.SysUtils, System.Classes, System.UITypes,
  FMX.StdCtrls, FMX.TMSFNCDataGrid, FMX.TMSFNCDataGridData, FMX.TMSFNCGraphicsTypes,
  FMX.TMSFNCDataGridCell, FMX.TMSFNCDataGridRenderer, FMX.TMSFNCDataGridCore,
  FMX.TMSFNCDataGridBase;



type
  TDataGridFormattingSettings = record
    SortableGrid: Boolean;
    FixedColumns, FixedRows, FreezeRows : integer;
    TextColWidth, NumColWidth, DateColWidth, CBColWidth: Integer;
    TextColAlign, NumColAlign, DateColAlign, CheckColAlign: TTMSFNCGraphicsTextAlign;
    AdaptToStyle: Boolean;
    GlobalFontSize, HeaderFontSize: Integer;
    StrokeColor: TAlphaColor;
  end;

  TDataGridFilterParams = record
    Grid: TTMSFNCDataGrid;
    Column: Integer;
    Value: String;
    AddWildCard: Boolean;
    ApplyFilter: Boolean;
    FocusGrid: Boolean;
    RemoveFilters: Boolean;
    constructor Create(AGrid: TTMSFNCDataGrid);
  end;

  TDataGridFormatHelper = class
  private
    class var gfs: TDataGridFormattingSettings;

    class procedure OnGetCellLayoutHandler(Sender: TObject; ACell: TTMSFNCDataGridCell);
  public
    class var GridFormattingSettings: TDataGridFormattingSettings;

    class procedure FormatGrid(AGrid: TTMSFNCDataGrid; aGridFormattingSettings: TDataGridFormattingSettings; aTagString: string = ''; aTag: Integer = 0); overload;
    class procedure FormatGrid(AGrid: TTMSFNCDataGrid; SortableGrid: Boolean; TextColWidth: Integer = 200; NumColWidth: Integer = 75; DateColWidth: Integer = 120; CBColWidth: Integer = 80; TextColAlign: TTMSFNCGraphicsTextAlign = gtaLeading; NumColAlign: TTMSFNCGraphicsTextAlign = gtaTrailing; DateColAlign: TTMSFNCGraphicsTextAlign = gtaCenter; aTagString: string = ''; aTag: Integer = 0); overload;
  end;

  TDataGridRoutines = class
  public
    class function SelectedRowCount(AGrid: TTMSFNCDataGrid): Integer;
    class function GetSelectedRowIndex(var AGrid: TTMSFNCDataGrid; const sl: TStringList): Integer; overload;
    class function GetSelectedRowIndex(var AGrid: TTMSFNCDataGrid): Integer; overload;
    class function GetColIndex(var AGrid: TTMSFNCDataGrid; AColName: string): Integer;
    class function LookupInColumn(AGrid: TTMSFNCDataGrid; AColumn: Integer; AValue: string): Integer;
    class procedure LookupAndSelect(AGrid: TTMSFNCDataGrid; AColumn: Integer; AValue: string; FocusGrid: Boolean = false);
    class procedure FilterGrid(GridFilterParams: TDataGridFilterParams);
    class procedure DeleteSelectedRows(var AGrid: TTMSFNCDataGrid);
    class procedure LoadGridFromCSVStream(var AGrid: TTMSFNCDataGrid; out ams: TMemoryStream; SortGrid: Boolean = False; ClearStream: Boolean = False);
  end;

implementation

{ TDataGridFilterParams }

constructor TDataGridFilterParams.Create(AGrid: TTMSFNCDataGrid);
begin
  Self.Grid := AGrid;
  Self.Column := 0;
  Self.Value := '';
  Self.AddWildCard := True;
  Self.ApplyFilter := True;
  Self.FocusGrid := False;
  Self.RemoveFilters := True;
end;

{ TDataGridFormatHelper }

class procedure TDataGridFormatHelper.OnGetCellLayoutHandler(Sender: TObject; ACell: TTMSFNCDataGridCell);
var
  Grid: TTMSFNCDataGrid;
  chk : TCheckBox;
  colcount : integer;
begin
  Grid := TTMSFNCDataGrid(Sender);

  if ACell.Row = 0 then
    Exit; // Skip header row

  if Grid.Columns[ACell.Column].formatting.&Type = gdftBoolean then
  begin
    // Verwijder bestaande controle als die bestaat
    if Assigned(ACell.Control) then
      FreeAndNil(ACell.Control);

    chk := TCheckBox.Create(Grid);
    chk.Text := '';
    chk.Width := 20;
    chk.Height := 20;
    chk.Enabled := Grid.Columns[ACell.Column].ReadOnly;
    ACell.Control := chk;
    ACell.ControlPosition := gcpCenterCenter;
  end;
end;

class procedure TDataGridFormatHelper.FormatGrid(AGrid: TTMSFNCDataGrid; aGridFormattingSettings: TDataGridFormattingSettings; aTagString: string = ''; aTag: Integer = 0);
var
  i: Integer;
  AlreadyUpdating : boolean;
begin
  gfs := aGridFormattingSettings;

  AlreadyUpdating := aGrid.IsUpdating;

  if not AlreadyUpdating then
    AGrid.BeginUpdate;

  try
    AGrid.RowCount := 1;
    AGrid.TagString := aTagString;
    AGrid.Tag := aTag;
    AGrid.AdaptToStyle := gfs.AdaptToStyle;
    AGrid.Options.Sorting.Enabled := gfs.SortableGrid;

    for i := 0 to AGrid.ColumnCount - 1 do
    begin
      AGrid.Columns[i].Appearance.NormalLayout.Font.Size := gfs.GlobalFontSize;
      AGrid.Columns[i].Appearance.NormalLayout.Stroke.Color := gfs.StrokeColor;
      AGrid.Columns[i].Appearance.FixedLayout.Font.Size := gfs.HeaderFontSize;
      AGrid.Columns[i].Appearance.FixedLayout.Font.Style := [TFontStyle.fsBold];

      case AGrid.Columns[i].Editor of
        getEdit:
          begin
            AGrid.Columns[i].Width := gfs.TextColWidth;
            AGrid.Columns[i].Appearance.NormalLayout.TextAlign := gfs.TextColAlign;
          end;
        getNumericEdit, getFloatEdit:
          begin
            AGrid.Columns[i].Width := gfs.NumColWidth;
            AGrid.Columns[i].Appearance.NormalLayout.TextAlign := gfs.NumColAlign;
            AGrid.Columns[i].Formatting.&Type := gdftFloat;
            AGrid.Columns[i].Formatting.Format := '#,##0.00';
          end;
        getDatePicker:
          begin
            AGrid.Columns[i].Width := gfs.DateColWidth;
            AGrid.Columns[i].Appearance.NormalLayout.TextAlign := gfs.DateColAlign;
            AGrid.Columns[i].Formatting.&Type := gdftDate;
            AGrid.Columns[i].Formatting.Format := 'dd/MM/yyyy';
          end;
      end;
    end;

    if AGrid.ColumnCount > 0 then
    begin
      AGrid.FixedRowCount := gfs.FixedRows;
      AGrid.FreezeRowCount := gfs.FreezeRows;
      AGrid.FixedColumnCount := gfs.FixedColumns;
    end;

    AGrid.OnGetCellLayout := TDataGridFormatHelper.OnGetCellLayoutHandler;
  finally
    if not AlreadyUpdating then
      AGrid.EndUpdate;
  end;
end;

class procedure TDataGridFormatHelper.FormatGrid(AGrid: TTMSFNCDataGrid; SortableGrid: Boolean; TextColWidth, NumColWidth, DateColWidth, CBColWidth: Integer; TextColAlign, NumColAlign, DateColAlign: TTMSFNCGraphicsTextAlign; aTagString: string; aTag: Integer);
var
  fs: TDataGridFormattingSettings;
begin
  fs.SortableGrid := SortableGrid;
  fs.TextColWidth := TextColWidth;
  fs.NumColWidth := NumColWidth;
  fs.DateColWidth := DateColWidth;
  fs.CBColWidth := CBColWidth;
  fs.TextColAlign := TextColAlign;
  fs.NumColAlign := NumColAlign;
  fs.DateColAlign := DateColAlign;
  fs.CheckColAlign := gtaCenter;
  fs.AdaptToStyle := True;
  fs.GlobalFontSize := 13;
  fs.HeaderFontSize := 30;
  fs.StrokeColor := TAlphaColors.Gray;

  FormatGrid(AGrid, fs, aTagString, aTag);
end;

{ TDataGridRoutines }

class function TDataGridRoutines.SelectedRowCount(AGrid: TTMSFNCDataGrid): Integer;
var
  sl: TStringList;
begin
  sl := TStringList.Create;
  try
    GetSelectedRowIndex(AGrid, sl);
    Result := sl.Count;
  finally
    sl.Free;
  end;
end;

class function TDataGridRoutines.GetSelectedRowIndex(var AGrid: TTMSFNCDataGrid; const sl: TStringList): Integer;
var
  i: Integer;
begin
  sl.Clear;
  for i := 1 to AGrid.RowCount - 1 do
    if AGrid.SelectedRows[i] then
      sl.Add(i.ToString);
  Result := sl.Count;
end;

class function TDataGridRoutines.GetSelectedRowIndex(var AGrid: TTMSFNCDataGrid): Integer;
begin
  Result := AGrid.FocusedCell.Row;
end;

class function TDataGridRoutines.GetColIndex(var AGrid: TTMSFNCDataGrid; AColName: string): Integer;
begin
  Result := AGrid.ColumnByHeader(AColName).Index;
end;

class function TDataGridRoutines.LookupInColumn(AGrid: TTMSFNCDataGrid; AColumn: Integer; AValue: string): Integer;
var
  Row: Integer;
begin
  Result := -1;
  for Row := 1 to AGrid.RowCount - 1 do
    if SameText(AGrid.Cells[AColumn, Row].ToString, AValue) then
    begin
      Result := Row;
      Exit;
    end;
end;

class procedure TDataGridRoutines.LookupAndSelect(AGrid: TTMSFNCDataGrid; AColumn: Integer; AValue: string; FocusGrid: Boolean = false);
var
  r: Integer;
begin
  AGrid.BeginUpdate;
  try
    r := LookupInColumn(AGrid, AColumn, AValue);
    if r > 0 then
      AGrid.SelectedRows[r] := true;
  finally
    AGrid.EndUpdate;
  end;

  if FocusGrid then
    AGrid.SetFocus;
end;

class procedure TDataGridRoutines.FilterGrid(GridFilterParams: TDataGridFilterParams);
begin
  if GridFilterParams.RemoveFilters then
    GridFilterParams.Grid.Filter.Clear;

  if Trim(GridFilterParams.Value) <> '' then
  begin
    with GridFilterParams.Grid.Filter.Add do
    begin
      Column := GridFilterParams.Column;
      if GridFilterParams.AddWildCard then
        Condition := GridFilterParams.Value + '*'
      else
        Condition := GridFilterParams.Value;
    end;

    if GridFilterParams.ApplyFilter then
      GridFilterParams.Grid.ApplyFilter;
  end;

  if GridFilterParams.FocusGrid then
    GridFilterParams.Grid.SetFocus;
end;

class procedure TDataGridRoutines.DeleteSelectedRows(var AGrid: TTMSFNCDataGrid);
var
  sl: TStringList;
  i: Integer;
begin
  sl := TStringList.Create;
  try
    GetSelectedRowIndex(AGrid, sl);
    for i := sl.Count - 1 downto 0 do
      AGrid.DeleteRow(StrToInt(sl[i]));
  finally
    sl.Free;
  end;
end;

class procedure TDataGridRoutines.LoadGridFromCSVStream(var AGrid: TTMSFNCDataGrid; out ams: TMemoryStream; SortGrid: Boolean = False; ClearStream: Boolean = False);
begin
  AGrid.BeginUpdate;
  try
    AGrid.ClearData;

    if ams.Size > 0 then
      AGrid.LoadFromCSVStreamData(ams);

    if SortGrid then
      AGrid.Sort(0,  gsdAscending);
  finally
    AGrid.EndUpdate;
  end;

  if AGrid.RowCount > 1 then
    AGrid.SelectedRows[1];

  if ClearStream then
    ams.Clear;
end;

end.

