{********************************************}
{ TeeChart Animations Editor                 }
{ Copyright (c) 2000-2020 by Steema Software }
{********************************************}
unit TeeAnimationEditors;
{$I TeeDefs.inc}

interface

uses
  {$IFNDEF LINUX}
  Windows, Messages,
  {$ENDIF}
  SysUtils, Classes,

  Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls, ExtCtrls,
  TeeAnimate, TeCanvas;

type
  TAnimationEditors = class(TForm)
    PageControl1: TPageControl;
    TabNumber: TTabSheet;
    TabProperty: TTabSheet;
    TabColor: TTabSheet;
    PageControl2: TPageControl;
    TabStart: TTabSheet;
    TabEnd: TTabSheet;
    Label3: TLabel;
    EStart: TEdit;
    Label4: TLabel;
    EEnd: TEdit;
    CBUseEnd: TCheckBox;
    CBKeepEnd: TCheckBox;
    CBUseStart: TCheckBox;
    Panel1: TPanel;
    Panel2: TPanel;
    TreeView1: TTreeView;
    Label1: TLabel;
    Splitter1: TSplitter;
    Panel3: TPanel;
    Panel4: TPanel;
    LProps: TListBox;
    Label2: TLabel;
    BStartColor: TButtonColor;
    BEndColor: TButtonColor;
    CBMethod: TComboFlat;
    Label5: TLabel;
    CBKeepEnd2: TCheckBox;
    TabToggle: TTabSheet;
    procedure FormShow(Sender: TObject);
    procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
    procedure LPropsClick(Sender: TObject);
    procedure EStartChange(Sender: TObject);
    procedure EEndChange(Sender: TObject);
    procedure CBUseEndClick(Sender: TObject);
    procedure CBKeepEndClick(Sender: TObject);
    procedure CBUseStartClick(Sender: TObject);
    procedure BStartColorClick(Sender: TObject);
    procedure BEndColorClick(Sender: TObject);
    procedure PageControl1GetImageIndex(Sender: TObject; TabIndex: Integer;
      var ImageIndex: Integer);
    procedure PageControl1Change(Sender: TObject);
  private
    { Private declarations }
    Animation : TTeeAnimation;

    IChanging : Boolean;

    procedure FillProperties;
  public
    { Public declarations }
  end;

implementation

uses
  {$IFNDEF D6}
  TeeProcs,  // <-- needs StrToFloatDef function
  {$ENDIF}
  TypInfo, TeeAnimations;

{$IFNDEF LCL}
{$R *.DFM}
{$ELSE}
{$R *.lfm}
{$ENDIF}

type
  TProperties=class
  public
    Count : Integer;
    Properties : PPropList;

    Constructor Create(const AInstance:TObject);
    Destructor Destroy; override;

    function IsKind(Index:Integer; Kind:TTypeKind):Boolean;
    function IsColorProperty(Index:Integer):Boolean;
    function IsNumberProperty(Index:Integer):Boolean;
  end;

Constructor TProperties.Create(const AInstance:TObject);
begin
  inherited Create;

  if AInstance.ClassInfo<>nil then
  begin
    {$IFNDEF D6}
    Properties:=nil;
    {$ENDIF}

    Count:=GetPropList(AInstance{$IFNDEF D6}.ClassInfo,tkProperties{$ENDIF},Properties);

    if Count>0 then
      {$IFDEF D6}
      {$IFNDEF LCL}
      SortPropList(Properties,Count);
      {$ENDIF}
      {$ENDIF}
  end;
end;

Destructor TProperties.Destroy;
begin
  FreeMemory(Properties);
  inherited;
end;

function TProperties.IsKind(Index: Integer; Kind:TTypeKind): Boolean;
begin
  result:=Properties[Index].PropType^.Kind=Kind;
end;

function TProperties.IsColorProperty(Index:Integer):Boolean;
begin
  result:=IsKind(Index,tkInteger) and
          (Properties[Index].PropType^.Name='TColor');
end;

function TProperties.IsNumberProperty(Index:Integer):Boolean;
begin
  result:=IsKind(Index,tkInteger) or
          IsKind(Index,tkInt64) or
          IsKind(Index,tkFloat);
end;

type
  TPropertyAnimationAccess=class(TPropertyAnimation);
  
procedure TAnimationEditors.FillProperties;

  function IsValidObject(const AInstance:TObject):Boolean;
  var t : Integer;
  begin
    if AInstance=TreeView1.Items[0].Data then
       result:=False
    else
    if AInstance is TList then
       result:=True
    else
    if Animation is TColorsAnimation then
    begin
      result:=False;

      with TProperties.Create(AInstance) do
      try
        for t:=0 to Count-1 do
            if IsKind(t,tkClass) or IsColorProperty(t) then
            begin
              result:=True;
              break;
            end;
      finally
        Free;
      end;
    end
    else
    if Animation is TNumberAnimation then
    begin
      result:=False;

      with TProperties.Create(AInstance) do
      try
        for t:=0 to Count-1 do
            if IsKind(t,tkClass) or IsNumberProperty(t) then
            begin
              result:=True;
              break;
            end;
      finally
        Free;
      end;
    end
    else
       result:=True;
  end;

  procedure FillNode(const AParent:TTreeNode; const AInstance:TObject);

    procedure CheckSelectedNode(const ANode:TTreeNode; const AInst:TObject);
    begin
      if Animation is TPropertyAnimation then
         if TPropertyAnimationAccess(Animation).IRealInstance=AInst then
         begin
           ANode.Selected:=True;
           ANode.MakeVisible;
         end;
    end;

  var Node,Node2 : TTreeNode;
      t,tt  : Integer;
      o,o2 : TObject;
      tmpS : String;
  begin
    CheckSelectedNode(AParent,AInstance);

    with TProperties.Create(AInstance) do
    try
      for t:=0 to Count-1 do
        if IsKind(t,tkClass) then
        begin
          o:=GetObjectProp(AInstance,Properties[t]);

          if Assigned(o) and IsValidObject(o) then
          begin
            Node:=TreeView1.Items.AddChildObject(AParent,String(Properties[t].Name),o);
            FillNode(Node,o);

            if o is TList then
               for tt:=0 to TList(o).Count-1 do
               begin
                 o2:=TList(o).Items[tt];

                 if o2 is TComponent then
                    tmpS:=TComponent(o2).Name
                 else
                    tmpS:=IntToStr(tt);

                 Node2:=TreeView1.Items.AddChildObject(Node,tmpS,o2);

                 FillNode(Node2,o2);
               end;
          end;
        end;
    finally
      Free;
    end;
  end;

var Node : TTreeNode;
    tmp : TObject;
    tmpS : String;
begin
  IChanging:=True;

  TreeView1.Items.BeginUpdate;
  TreeView1.Items.Clear;

  if Animation is TCustomAnimationChart then
     tmp:=TCustomAnimationChart(Animation).Chart
  else
     tmp:=Animation.TagObject;

  if Assigned(tmp) then
  begin
    if tmp is TComponent then
       tmpS:=TComponent(tmp).Name
    else
       tmpS:=tmp.ClassName;

    Node:=TreeView1.Items.AddChildObject(nil,tmpS,tmp);
    FillNode(Node,tmp);
  end;

  TreeView1.Items.EndUpdate;

  IChanging:=False;
end;

procedure TAnimationEditors.FormShow(Sender: TObject);
begin
  Animation:=TTeeAnimation(Tag);

  IChanging:=True;

  if Assigned(Animation) then
  begin
    if Animation is TColorsAnimation then
    begin
      TabNumber.TabVisible:=False;
      TabToggle.TabVisible:=False;

      TabColor.TabVisible:=True;
      PageControl1.ActivePage:=TabColor;
    end
    else
    if Animation is TNumberAnimation then
    begin
      TabNumber.TabVisible:=True;
      TabColor.TabVisible:=False;
      TabToggle.TabVisible:=False;

      if Animation is TNumberAnimation then
         PageControl1.ActivePage:=TabNumber
      else
         PageControl1.ActivePage:=TabProperty;
    end
    else
    if Animation is TToggleAnimation then
    begin
      TabToggle.TabVisible:=True;
      TabNumber.TabVisible:=False;
      TabColor.TabVisible:=False;

      PageControl1.ActivePage:=TabProperty;
    end;

    if TabNumber.TabVisible then
    begin
      EStart.Text:=FloatToStr(TNumberAnimation(Animation).StartValue);
      EEnd.Text:=FloatToStr(TNumberAnimation(Animation).EndValue);

      CBUseStart.Checked:=TRangePropertyAnimation(Animation).UseStartValue;
      CBKeepEnd.Checked:=TRangePropertyAnimation(Animation).KeepEndValue;
      CBUseEnd.Checked:=TRangePropertyAnimation(Animation).UseEndValue;
    end;

    if TabColor.TabVisible then
    begin
      BStartColor.SymbolColor:=TColorsAnimation(Animation).StartColor;
      BEndColor.SymbolColor:=TColorsAnimation(Animation).EndColor;
      CBKeepEnd2.Checked:=TColorsAnimation(Animation).KeepEndValue;
    end;
  end;

  IChanging:=False;
end;

procedure TAnimationEditors.TreeView1Change(Sender: TObject;
  Node: TTreeNode);
var t : Integer;
    tmp : Boolean;
    tmpIndex : Integer;
begin
  LProps.Items.BeginUpdate;
  LProps.Items.Clear;

  with TProperties.Create(TreeView1.Selected.Data) do
  try
    for t:=0 to Count-1 do
      if (not IsKind(t,tkClass)) and (not IsKind(t,tkMethod)) then
      begin
        if Animation is TColorsAnimation then
           tmp:=IsColorProperty(t)
        else
        if Animation is TNumberAnimation then
           tmp:=IsNumberProperty(t)
        else
           tmp:=True;

        if tmp then
           LProps.Items.Add(String(Properties[t].Name));
      end;
  finally
    Free;
  end;

  if LProps.Items.Count>0 then
  begin
    tmpIndex:=LProps.Items.IndexOf(TPropertyAnimation(Animation).PropertyName);

    if tmpIndex=-1 then
       LProps.ItemIndex:=0
    else
       LProps.ItemIndex:=tmpIndex;
  end;

  LProps.Items.EndUpdate;

  LPropsClick(Self);
end;

procedure TAnimationEditors.LPropsClick(Sender: TObject);

  function CalcPropertyName:String;
  begin
    result:=LProps.Items[LProps.ItemIndex];
  end;

  function CalcPropertyInstance:String;
  var tmpInst : TObject;
      tmpParent : TTreeNode;
      i,
      tmpIndex : Integer;
  begin
    tmpParent:=TreeView1.Selected;
    tmpInst:=tmpParent.Data;

    if Assigned(tmpParent.Parent) then
       result:=tmpParent.Text+'.'
    else
       result:='';

    while Assigned(tmpParent.Parent) do
    begin
      tmpIndex:=tmpParent.Index;
      tmpParent:=tmpParent.Parent;

      tmpInst:=tmpParent.Data;

      if (tmpInst is TList) or (tmpInst is TCollection) then
      begin
        i:=Pos('.',result);
        if i>0 then
           Delete(result,1,i);

        result:=tmpParent.Text+'.Item'+IntToStr(tmpIndex)+'.'+result;
      end
      else
      if Assigned(tmpParent.Parent) then
         result:=tmpParent.Text+'.'+result;
    end;

    TPropertyAnimation(Animation).Instance:=tmpInst;
  end;

var Prefix : String;
    tmp : String;
begin
  if not IChanging then
  begin
    Prefix:=CalcPropertyInstance;

    if LProps.ItemIndex=-1 then
       tmp:=''
    else
       tmp:=Prefix+CalcPropertyName;

    TPropertyAnimation(Animation).PropertyName:=tmp;

    //DEBUG: Label2.Caption:=tmp;
  end;
end;

procedure TAnimationEditors.EStartChange(Sender: TObject);
begin
  if not IChanging then
  with TNumberAnimation(Animation) do
       StartValue:=StrToFloatDef(EStart.Text,StartValue);
end;

procedure TAnimationEditors.EEndChange(Sender: TObject);
begin
  if not IChanging then
  with TNumberAnimation(Animation) do
       EndValue:=StrToFloatDef(EEnd.Text,EndValue);
end;

procedure TAnimationEditors.CBUseEndClick(Sender: TObject);
begin
  if not IChanging then
     TRangePropertyAnimation(Animation).UseEndValue:=CBUseEnd.Checked;
end;

procedure TAnimationEditors.CBKeepEndClick(Sender: TObject);
begin
  if not IChanging then
     TRangePropertyAnimation(Animation).KeepEndValue:=(Sender as TCheckBox).Checked;
end;

procedure TAnimationEditors.CBUseStartClick(Sender: TObject);
begin
  if not IChanging then
     TRangePropertyAnimation(Animation).UseStartValue:=CBUseStart.Checked;
end;

procedure TAnimationEditors.BStartColorClick(Sender: TObject);
begin
  TColorsAnimation(Animation).StartColor:=BStartColor.SymbolColor;
end;

procedure TAnimationEditors.BEndColorClick(Sender: TObject);
begin
  TColorsAnimation(Animation).EndColor:=BEndColor.SymbolColor;
end;

procedure TAnimationEditors.PageControl1GetImageIndex(Sender: TObject;
  TabIndex: Integer; var ImageIndex: Integer);
begin
  // Dummy, to avoid List index out of bounds in D7
end;

procedure TAnimationEditors.PageControl1Change(Sender: TObject);
begin
  if PageControl1.ActivePage=TabProperty then
     if TreeView1.Items.Count=0 then
        FillProperties;
end;

initialization
  RegisterClass(TAnimationEditors);
end.
