{*********************************************}
{  TeeChart LiveBindings Registration         }
{  Copyright (c) 2012 by Steema Software      }
{  All Rights Reserved                        }
{*********************************************}
unit VCLTee.Bind.Editors;
{$I TeeDefs.inc}

interface

uses
  Data.DB,
  {$IFDEF FMX}
  FMXTee.Engine
  {$ELSE}
  VCLTee.TeEngine
  {$ENDIF}
  ;

function GuessBestSeries(AField:TField):TChartSeriesClass;

implementation

uses
  System.Classes, System.SysUtils,
  Data.Bind.Components, Data.Bind.Editors, System.Bindings.EvalProtocol,
  System.Bindings.ObjEval,
  {$IFDEF FMX}
  FMXTee.Procs, FMXTee.Chart, FMXTee.Series
  {$ELSE}
  VCLTee.TeeProcs, VCLTee.Chart, VCLTee.Series
  {$ENDIF}
  ;

type
  TSeriesEditorObject=class
  private
    FIndex: Integer;
    FItemIndex: Integer;
    FSeries: TChartSeries;

    function GetValue: String;
    procedure SetValue(const Value: String);
    function GetText: String;
    procedure SetText(const Value: String);
    function GetX: String;
    procedure SetX(const Value: String);
  public
    property Text: String read GetText write SetText;
    property Owner: TChartSeries read FSeries;
    property Value: String read GetValue write SetValue;
    property X: String read GetX write SetX;
  end;

  TBindListSeriesEditor = class(TBindListEditor)
  private
    FEditorObject: TSeriesEditorObject;
  public
    constructor Create(ASeries: TChartSeries);
    destructor Destroy; override;
    procedure BeginUpdate; override;
    procedure EndUpdate; override;
    procedure DeleteToEnd; override;
    function AddItem(Select: Boolean): IScope; override;
    function InsertItem(Select: Boolean): IScope; override;
    function CanInsertItem: Boolean; override;
    function MoveNext: Boolean; override;
    function GetRowCount: Integer; override;
    function CurrentItem: IScope; override;
    procedure ClearList; override;
    function GetSelectedText: string; override;
    procedure SetSelectedText(const AValue: string); override;
  end;

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

  TChartItemEditorObject = class
  private
    FChart: TCustomChart;
    FIndex: Integer;
    FItemIndex: Integer;
    function GetSeries(ACol: Integer): string;
    procedure SetSeries(ACol: Integer; const Value: string);
  public
    property Owner: TCustomChart read FChart;
    property Series[ACol: Integer]: string read GetSeries write SetSeries;
  end;

  TBindListChartEditor = class(TBindGridEditor)
  private
    FEditorObject: TChartItemEditorObject;
    function IsEmpty: Boolean;
    function GetRowIndex: Integer;
    function GetColumnIndex: Integer;
    function Series:TChartSeries;
  protected
    procedure BeginUpdate; override;
    procedure EndUpdate; override;
    function AddItem(Select: Boolean): IScope; override;
    function InsertItem(Select: Boolean): IScope; override;
    function CanInsertItem: Boolean; 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 GetColumnNames(ANames: TStrings); override;
    procedure GetColumnIndices(ANames: TStrings); override;
  public
    constructor Create(AChart: TCustomChart);
    destructor Destroy; override;
  end;

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

function GuessBestSeries(AField:TField):TChartSeriesClass;
var tmp : Integer;
begin
  result:=TBarSeries;

  if Assigned(AField) and Assigned(AField.DataSet) then
  begin
    tmp:=AField.DataSet.RecordCount;

    if tmp>100 then
       result:=TFastLineSeries
    else
    if tmp>20 then
       result:=TLineSeries;
  end;
end;

function SeriesValue(ASeries:TChartSeries; AList:TChartValueList; AIndex:Integer):String;
var tmpValue : TChartValue;
begin
  tmpValue:=AList.Value[AIndex];

  if AList.DateTime then
     DateTimeToString(result,DateTimeDefaultFormat(tmpValue),tmpValue)
  else
     result:=FormatFloat(ASeries.ValueFormat,tmpValue);
end;

{ TBindListSeriesEditor }

function TBindListSeriesEditor.AddItem(Select: Boolean): IScope;
begin
  FEditorObject.FIndex := FEditorObject.FSeries.Add(0);
  if Select then
    FEditorObject.FItemIndex := FEditorObject.FIndex;
  Result := WrapObject(FEditorObject);
end;

function TBindListSeriesEditor.MoveNext: Boolean;
begin
  if FEditorObject.FSeries.Count = 0 then
    Exit(False);

  if FEditorObject.FIndex = -1 then
  begin
    FEditorObject.FIndex := FEditorObject.FItemIndex;
    if FEditorObject.FIndex < 0 then
      FEditorObject.FIndex := 0;
  end
  else
    Inc(FEditorObject.FIndex);

  Result := (FEditorObject.FIndex >= 0) and (FEditorObject.FIndex < FEditorObject.FSeries.Count);
end;

function TBindListSeriesEditor.GetRowCount: Integer;
begin
  Result := FEditorObject.FSeries.Count;
end;

function TBindListSeriesEditor.CurrentItem: IScope;
begin
  if FEditorObject.FIndex = -1 then
    FEditorObject.FIndex := FEditorObject.FItemIndex;

  if FEditorObject.FIndex <> -1 then
    Result := WrapObject(FEditorObject)
  else
    Result := nil;
end;

procedure TBindListSeriesEditor.BeginUpdate;
begin
  FEditorObject.FSeries.BeginUpdate;
end;

function TBindListSeriesEditor.CanInsertItem: Boolean;
begin
  Result := FEditorObject.FItemIndex <> -1;
end;

procedure TBindListSeriesEditor.ClearList;
begin
  FEditorObject.FSeries.Clear;
  FEditorObject.FIndex:=-1;
  FEditorObject.FItemIndex:=-1;
end;

constructor TBindListSeriesEditor.Create(ASeries: TChartSeries);
begin
  FEditorObject := TSeriesEditorObject.Create;
  FEditorObject.FIndex := -1;
  FEditorObject.FSeries := ASeries;
  FEditorObject.FItemIndex:= -1;
end;

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

procedure TBindListSeriesEditor.EndUpdate;
begin
  FEditorObject.FSeries.EndUpdate;
end;

function TBindListSeriesEditor.GetSelectedText: string;
begin
  Result := '';

  with FEditorObject do
  if FItemIndex <> -1 then
     if FItemIndex < FSeries.Count then
        result:=SeriesValue(FSeries,FSeries.MandatoryValueList,FItemIndex);
end;

function TBindListSeriesEditor.InsertItem(Select: Boolean): IScope;
begin
  if not CanInsertItem then
    Result := nil
  else
  begin
    FEditorObject.FIndex := FEditorObject.FItemIndex;
    FEditorObject.FSeries.AddXY(FEditorObject.FIndex, 0);

    if Select then
      FEditorObject.FItemIndex := FEditorObject.FIndex;

    Result := WrapObject(FEditorObject);
  end;
end;

procedure TBindListSeriesEditor.SetSelectedText(const AValue: string);
var
  t: Integer;
begin
  with FEditorObject,FSeries do
  for t := 0 to Count - 1 do
      if SameText(SeriesValue(FSeries,FSeries.MandatoryValueList,t), AValue) then
      begin
        FEditorObject.FItemIndex := t;
        Exit;
      end;

  FEditorObject.FItemIndex := -1;
end;

procedure TBindListSeriesEditor.DeleteToEnd;
begin
  if FEditorObject.FIndex = -1 then
    FEditorObject.FIndex := FEditorObject.FItemIndex;

  FEditorObject.FSeries.Delete(FEditorObject.FIndex, FEditorObject.FSeries.Count-FEditorObject.FIndex);
  FEditorObject.FIndex := FEditorObject.FSeries.Count - 1;
end;

{ TSeriesEditorObject }

function TSeriesEditorObject.GetText: String;
begin
  result:=FSeries.XLabel[FIndex];
end;

function TSeriesEditorObject.GetValue: String;
begin
  result:=SeriesValue(FSeries,FSeries.MandatoryValueList,FIndex);
end;

function TSeriesEditorObject.GetX: String;
begin
  result:=SeriesValue(FSeries,FSeries.NotMandatoryValueList,FIndex);
end;

procedure TSeriesEditorObject.SetText(const Value: String);
begin
  FSeries.XLabel[FIndex]:=Value;
end;

procedure TSeriesEditorObject.SetValue(const Value: String);
begin
  FSeries.MandatoryValueList.Value[FIndex]:=StrToFloat(Value);
end;

procedure TSeriesEditorObject.SetX(const Value: String);
begin
  FSeries.NotMandatoryValueList.Value[FIndex]:=StrToFloat(Value);
end;

{ TBindSeriesEditorFactory }

constructor TBindSeriesEditorFactory.Create;
begin
  inherited;
end;

function TBindSeriesEditorFactory.CreateEditor(AIntf: TGuid;
  AObject: TObject): IInterface;
begin
  Result := TBindListSeriesEditor.Create(TChartSeries(AObject));
end;

function TBindSeriesEditorFactory.Supports(AIntf: TGuid; AObject:
  TObject): Boolean;
begin
  Result := (AIntf = {$IFDEF D17}IBindListEditorCommon{$ELSE}IBindListEditor{$ENDIF})
            and Assigned(AObject)
            and AObject.InheritsFrom(TChartSeries);
end;

{ TChartItemEditorObject }

function TChartItemEditorObject.GetSeries(ACol: Integer): string;
begin
  Result := SeriesValue(FChart[ACol], FChart[ACol].MandatoryValueList, FIndex);
end;

procedure TChartItemEditorObject.SetSeries(ACol: Integer;
  const Value: string);
var tmp : Double;
begin
  Assert(ACol<FChart.SeriesCount,'ACol >= SeriesCount');

  if FIndex > -1 then
  begin
    if TryStrToFloat(Value,tmp) then
      if FIndex < FChart[ACol].Count then
         FChart[ACol].MandatoryValueList[FIndex] := tmp
      else
         FChart[ACol].Add(tmp)
    else
      if FIndex < FChart[ACol].Count then
         FChart[ACol].SetNull(FIndex,True)
      else
         FChart[ACol].AddNull;
  end;
end;

{ TBindChartEditorFactory }

constructor TBindChartEditorFactory.Create;
begin
  inherited;
end;

function TBindChartEditorFactory.CreateEditor(AIntf: TGuid;
  AObject: TObject): IInterface;
begin
  Result := TBindListChartEditor.Create(TCustomChart(AObject));
end;

function TBindChartEditorFactory.Supports(AIntf: TGuid; AObject:
  TObject): Boolean;
begin
  Result := (AIntf = IBindListEditor)
            and Assigned(AObject)
            and AObject.InheritsFrom(TCustomChart);
end;

type
  TSeriesAccess=class(TChartSeries);

{ TBindListChartEditor }

function TBindListChartEditor.Series:TChartSeries;
begin
  if (GetColumnIndex=-1) or (FEditorObject.FChart.SeriesCount<=GetColumnIndex) then
  begin
    result:=TBarSeries.Create(FEditorObject.FChart);
    TSeriesAccess(result).DontSerialize:=True;
  end
  else
    result:=FEditorObject.FChart[GetColumnIndex];
end;

function TBindListChartEditor.AddItem(Select: Boolean): IScope;
begin
  Inc(FEditorObject.FIndex);

  while (FEditorObject.FChart.SeriesCount>FEditorObject.FIndex) and
        (not TSeriesAccess(FEditorObject.FChart[FEditorObject.FIndex]).DontSerialize) do
    Inc(FEditorObject.FIndex);

  if Select then
     FEditorObject.FItemIndex := FEditorObject.FIndex;

  Result := WrapObject(FEditorObject);
end;

function TBindListChartEditor.GetRowIndex: Integer;
begin
  if FEditorObject.FItemIndex >= Series.Count then
     Result := Series.Count - 1
  else
     Result := FEditorObject.FItemIndex;
end;

function TBindListChartEditor.GetColumnIndex: Integer;
begin
  Result := FEditorObject.FIndex;
end;

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

procedure TBindListChartEditor.GetColumnNames(ANames: TStrings);
var
  I: Integer;
begin
  for I := 0 to FEditorObject.FChart.SeriesCount - 1 do
      ANames.Add(FEditorObject.FChart[I].Name);
end;

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

procedure TBindListChartEditor.BeginUpdate;
begin
  {$IFDEF FMX}
  FEditorObject.FChart.BeginUpdate;
  {$ENDIF}
end;

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

procedure TBindListChartEditor.ClearList;
var t : Integer;
begin
  FEditorObject.FIndex := -1;
  FEditorObject.FItemIndex := -1;

  for t := 0 to FEditorObject.FChart.SeriesCount-1 do
      if TSeriesAccess(FEditorObject.FChart[t]).DontSerialize then
         FEditorObject.FChart[t].Clear;
end;

constructor TBindListChartEditor.Create(AChart: TCustomChart);
begin
  FEditorObject := TChartItemEditorObject.Create;
  FEditorObject.FChart := AChart;
  FEditorObject.FIndex := -1;
  FEditorObject.FItemIndex:= -1;
end;

procedure TBindListChartEditor.DeleteToEnd;
begin
  if FEditorObject.FIndex = -1 then
     FEditorObject.FIndex := GetRowIndex;

  if FEditorObject.FIndex = -1 then
     ClearList
  else
     Series.Delete(FEditorObject.FIndex, Series.Count-FEditorObject.FIndex);
end;

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

procedure TBindListChartEditor.EndUpdate;
begin
  {$IFDEF FMX}
  FEditorObject.FChart.EndUpdate;
  {$ENDIF}
end;

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

function TBindListChartEditor.IsEmpty: Boolean;
begin
  Result := (FEditorObject.FChart.SeriesCount=0)
end;

function TBindListChartEditor.GetRowCount: Integer;
begin
  if IsEmpty then
     Result := 0
  else
     Result := Series.Count;
end;

function TBindListChartEditor.GetSelectedText: string;
begin
  Result := SeriesValue(Series, Series.MandatoryValueList, GetRowIndex);
end;

function TBindListChartEditor.MoveNext: Boolean;
begin
  if Series.Count = 0 then
     Exit(False);

  if FEditorObject.FIndex = -1 then
     FEditorObject.FIndex :=  GetRowIndex
  else
     FEditorObject.FIndex :=  FEditorObject.FIndex + 1;

  Result := FEditorObject.FIndex < Series.Count;
end;

procedure TBindListChartEditor.SetSelectedText(const AValue: string);
begin
  Series.MandatoryValueList[GetRowIndex]:=StrToFloat(AValue);
end;

initialization
  RegisterBindEditorFactory([TBindSeriesEditorFactory,
    TBindChartEditorFactory]);
finalization
  UnregisterBindEditorFactory([TBindSeriesEditorFactory,
    TBindChartEditorFactory]);
end.
