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

interface

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

     Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, Buttons, Menus, ComCtrls,

     TeEngine, Chart, TeeAnimations, TeeAnimate, TeeAnimationEditor,
     TeeAnimationEditors, TeCanvas;

type
  TFormTeeAnimations = class(TForm)
    Splitter1: TSplitter;
    PanelEditor: TPanel;
    Panel1: TPanel;
    Panel3: TPanel;
    LBAnims: TListBox;
    PanelItems: TPanel;
    Splitter2: TSplitter;
    PageControl2: TPageControl;
    TabOptions: TTabSheet;
    TabAnimation: TTabSheet;
    Panel2: TPanel;
    Panel5: TPanel;
    LBItems: TListBox;
    Panel4: TPanel;
    SBPlay: TSpeedButton;
    SBPause: TSpeedButton;
    SBStop: TSpeedButton;
    SBFrame: TSpeedButton;
    LFramesPerSecond: TLabel;
    TBFramesPerSecond: TTrackBar;
    CBLoop: TCheckBox;
    BAdd: TSpeedButton;
    BDelete: TSpeedButton;
    SBAddItem: TSpeedButton;
    BDeleteItem: TSpeedButton;
    BRename: TSpeedButton;
    PopupMenu1: TPopupMenu;
    Timer1: TTimer;
    CBSpeed: TComboFlat;
    PanelButtons: TPanel;
    Panel6: TPanel;
    BOK: TButton;
    BCancel: TButton;
    CBAutoPlay: TCheckBox;
    procedure FormShow(Sender: TObject);
    procedure BAddClick(Sender: TObject);
    procedure SBPlayClick(Sender: TObject);
    procedure SBPauseClick(Sender: TObject);
    procedure SBStopClick(Sender: TObject);
    procedure SBFrameClick(Sender: TObject);
    procedure TBFramesPerSecondChange(Sender: TObject);
    procedure CBLoopClick(Sender: TObject);
    procedure BDeleteClick(Sender: TObject);
    procedure LBAnimsClick(Sender: TObject);
    procedure LBItemsClick(Sender: TObject);
    procedure SBAddItemClick(Sender: TObject);
    procedure BDeleteItemClick(Sender: TObject);
    procedure BRenameClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Timer1Timer(Sender: TObject);
    procedure CBSpeedChange(Sender: TObject);
    procedure CBAutoPlayClick(Sender: TObject);
  private
    { Private declarations }

    IAnimate : TTeeAnimate;
    IOldAnimation : TTeeAnimationTool;
    IAnimEditor : TAnimationEditor;

    procedure AnimateStop(Sender: TObject);
    procedure ChangedAnimation(Sender: TObject);
    procedure CheckPlayButtons(Sender: TObject);
    procedure ClearOnChangeEvent;
    procedure ClearOnStopEvent;
    procedure FillAnimations;
    function NewAnimation:TTeeAnimationTool;
    procedure PopupSelected(Sender:TObject);
    procedure RefreshAnimate(const Value:TTeeAnimate);
    function SelectedAnimate:TTeeAnimate;
    function SelectedAnimation:TTeeAnimationTool;
    function SelectedItem:TTeeAnimation;
  protected
    OnAdded   : TNotifyEvent;
    OnDeleted : TNotifyEvent;
  public
    { Public declarations }
    Animations : TChartAnimations;

    class function Edit(const AOwner:TComponent; const Animate:TTeeAnimate):Boolean; overload;
    class function Edit(const AOwner:TComponent; const AChart:TChart):Boolean; overload;

    procedure RefreshAnimations(const Value:TChartAnimations);
    Procedure Reload;
    procedure SelectAnimation(const Value:TTeeCustomAnimation);
    procedure SelectIfAny;
  end;

implementation

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

uses
  TeeAnimationsGallery, TeeEditTools, TeePenDlg, TeeConst, TeeProcs;

function TFormTeeAnimations.NewAnimation:TTeeAnimationTool;
var tmpGally : TTeeAnimationsGallery;
begin
  result:=nil;

  tmpGally:=TTeeAnimationsGallery.Create(Owner);
  try
    PositionToCenter(tmpGally);

    if tmpGally.ShowModal=mrOk then
       result:=tmpGally.AddNewAnimation(Animations);
  finally
    tmpGally.Free;
  end;
end;

procedure TFormTeeAnimations.FillAnimations;
var t : Integer;
    tmp : TTeeCustomAnimation;
begin
  LBAnims.Clear;

  for t:=0 to Animations.Count-1 do
  begin
    tmp:=Animations[t];
    LBAnims.Items.AddObject(tmp.Title,tmp);
  end;
end;

procedure TFormTeeAnimations.RefreshAnimations(const Value:TChartAnimations);
begin
  if Assigned(Value) then
  begin
    Animations:=Value;
    FillAnimations;
    SelectIfAny;
  end;
end;

class function TFormTeeAnimations.Edit(const AOwner:TComponent; const Animate:TTeeAnimate):Boolean;
begin
  with TFormTeeAnimations.Create(AOwner) do
  try
    Splitter1.Hide;
    Panel1.Hide;
    PanelButtons.Show;

    RefreshAnimate(Animate);
    result:=ShowModal=mrOk;
  finally
    Free;
  end;
end;

procedure TFormTeeAnimations.FormShow(Sender: TObject);
begin
  if not Assigned(Animations) then
     Animations:=TChartAnimations(Tag);

  RefreshAnimations(Animations);
  TeeTranslateControl(Self);
end;

procedure TFormTeeAnimations.Reload;
begin
  if Assigned(Animations) and Assigned(Animations.Owner) then
     if Animations.Count<>LBAnims.Items.Count then
     begin
       FillAnimations;
       SelectIfAny;
     end;

  if LBAnims.ItemIndex<>-1 then
     LBAnimsClick(Self);
end;

procedure TFormTeeAnimations.SelectAnimation(const Value: TTeeCustomAnimation);
begin
  LBAnims.ItemIndex:=LBAnims.Items.IndexOfObject(Value);
  LBAnimsClick(Self);
end;

procedure TFormTeeAnimations.BAddClick(Sender: TObject);
var tmpAnim : TTeeAnimationTool;
    tmpS : String;
begin
  tmpAnim:=NewAnimation;

  if Assigned(tmpAnim) then
  begin
    tmpS:=tmpAnim.Title;
    LBAnims.Items.AddObject(tmpS,tmpAnim);
    LBAnims.ItemIndex:=LBAnims.Items.Count-1;
    LBAnimsClick(Self);
  end;
end;

procedure TFormTeeAnimations.SBPlayClick(Sender: TObject);
begin
  if SelectedAnimation=nil then
  begin
    if Assigned(IAnimate) then
       IAnimate.Play;
  end
  else
     SelectedAnimation.Play;

  CheckPlayButtons(nil);
end;

procedure TFormTeeAnimations.SBPauseClick(Sender: TObject);
begin
  if SelectedAnimation=nil then
  begin
    if Assigned(IAnimate) then
       if IAnimate.Paused then
          IAnimate.Continue
       else
          IAnimate.Pause;
  end
  else
  begin
    if SelectedAnimation.Paused then
       SelectedAnimation.Continue
    else
       SelectedAnimation.Pause;
  end;

  CheckPlayButtons(nil);
end;

procedure TFormTeeAnimations.SBStopClick(Sender: TObject);
begin
  if SelectedAnimation=nil then
  begin
    if Assigned(IAnimate) then
       IAnimate.Stop;
  end
  else
    SelectedAnimation.Stop;

  CheckPlayButtons(nil);
end;

procedure TFormTeeAnimations.SBFrameClick(Sender: TObject);
begin
  SelectedAnimate.NextFrame;
  CheckPlayButtons(nil);
end;

procedure TFormTeeAnimations.TBFramesPerSecondChange(Sender: TObject);
begin
  SelectedAnimate.Speed:=TBFramesPerSecond.Position;
  LFramesPerSecond.Caption:=IntToStr(SelectedAnimate.Speed);
end;

procedure TFormTeeAnimations.CBLoopClick(Sender: TObject);
begin
  SelectedAnimate.Loop:=CBLoop.Checked;
end;

function TFormTeeAnimations.SelectedAnimation: TTeeAnimationTool;
begin
  if LBAnims.ItemIndex=-1 then
     result:=nil
  else
     result:=TTeeAnimationTool(LBAnims.Items.Objects[LBAnims.ItemIndex]);
end;

function TFormTeeAnimations.SelectedAnimate:TTeeAnimate;
begin
  if SelectedAnimation<>nil then
     result:=SelectedAnimation.Animate
  else
     result:=IAnimate;
end;

function TFormTeeAnimations.SelectedItem:TTeeAnimation;
begin
  if LBItems.ItemIndex=-1 then
     result:=nil
  else
     result:=TTeeAnimation(LBItems.Items.Objects[LBItems.ItemIndex]);
end;

procedure TFormTeeAnimations.BDeleteClick(Sender: TObject);
var tmp : Integer;
begin
  tmp:=LBAnims.ItemIndex;

  if TeeYesNo(Format(TeeMsg_SureToDelete,[LBAnims.Items[tmp]])) then
  begin
    SelectedAnimation.Free;

    LBAnims.Items.Delete(tmp);

    if tmp>LBAnims.Items.Count-1 then
       LBAnims.ItemIndex:=LBAnims.Items.Count-1
    else
       LBAnims.ItemIndex:=tmp;

    LBAnimsClick(Self);
  end;

  BDelete.Enabled:=LBAnims.ItemIndex<>-1;
  BRename.Enabled:=BDelete.Enabled;
end;

type
  TAnimateAccess=class(TTeeAnimate);
  
procedure TFormTeeAnimations.RefreshAnimate(const Value:TTeeAnimate);
var t : Integer;
    tmpFPS : Integer;
begin
  ClearOnStopEvent;

  IAnimate:=Value;

  TAnimateAccess(IAnimate).IOnStop:=AnimateStop;

  LBItems.Clear;

  for t:=0 to Value.Animations.Count-1 do
      LBItems.Items.AddObject(Value.Animations[t].EditorName,Value.Animations[t]);

  if LBItems.Items.Count>0 then
     LBItems.ItemIndex:=0;

  LBItemsClick(Self);

  Timer1.Enabled:=True;

  CheckPlayButtons(nil);

  tmpFPS:=Value.Speed;

  while tmpFPS>TBFramesPerSecond.Max do
     TBFramesPerSecond.Max:=2*TBFramesPerSecond.Max;

  if TBFramesPerSecond.Max>120 then
     TBFramesPerSecond.Frequency:=TBFramesPerSecond.Max div 12;

  TBFramesPerSecond.Position:=tmpFPS;
  LFramesPerSecond.Caption:=IntToStr(tmpFPS);

  CBLoop.Checked:=Value.Loop;

  CBSpeed.ItemIndex:=CBSpeed.Items.IndexOf(FloatToStr(Value.SpeedFactor)+'x');
end;

procedure TFormTeeAnimations.LBAnimsClick(Sender: TObject);
begin
  ClearOnChangeEvent;

  PanelEditor.Visible:=False;
  PanelEditor.Visible:=LBAnims.ItemIndex<>-1;
  PanelEditor.Enabled:=PanelEditor.Visible;

  if IOldAnimation<>SelectedAnimation then
  begin
    if SelectedAnimation<>nil then
    begin
      RefreshAnimate(SelectedAnimation.Animate);

      CBAutoPlay.Enabled:=True;
      CBAutoPlay.Checked:=SelectedAnimation.AutoPlay;
    end
    else
      CBAutoPlay.Enabled:=False;

    BDelete.Enabled:=PanelEditor.Enabled;
    BRename.Enabled:=BDelete.Enabled;

    IOldAnimation:=SelectedAnimation;
  end;
end;

procedure TFormTeeAnimations.LBItemsClick(Sender: TObject);

  function AddEditor(const Animation:TTeeAnimation):TForm;
  var tmpFormClass : TFormClass;
  begin
    tmpFormClass:=TFormClass(TeeGetClass(Animation.EditorClass));

    if Assigned(tmpFormClass) and (tmpFormClass<>TAnimationEditor) then
    begin
      result:=tmpFormClass.Create(Self);
      result.Align:=alClient;

      TeeTranslateControl(result);

      AddFormTo(result,TabOptions,Animation);
    end
    else
      result:=nil;
  end;

var tmpItem : TTeeAnimation;
    tmpEditor : TForm;
begin
  ClearOnChangeEvent;

  PageControl2.Enabled:=LBItems.ItemIndex<>-1;

  RemoveControls(TabOptions);

  if LBItems.ItemIndex<>-1 then
  begin
    tmpItem:=SelectedItem;

    if tmpItem is TCustomAnimationChart then
    begin
      if TCustomAnimationChart(tmpItem).Chart=nil then
         if SelectedAnimation<>nil then
            TCustomAnimationChart(tmpItem).Chart:=SelectedAnimation.ParentChart
    end
    else
    if (not Assigned(tmpItem.TagObject)) and (SelectedAnimation<>nil) then
       tmpItem.TagObject:=SelectedAnimation.ParentChart;

    if not Assigned(IAnimEditor) then
    begin
      IAnimEditor:=TAnimationEditor.Create(Self);
      IAnimEditor.Align:=alClient;
      TeeTranslateControl(IAnimEditor, [IAnimEditor.Gallery]);
      AddFormTo(IAnimEditor,TabAnimation,tmpItem);
    end
    else
      IAnimEditor.RefreshAnimation(tmpItem);

    tmpEditor:=AddEditor(tmpItem);

    if tmpItem is TPropertyAnimation then
       TPropertyAnimation(tmpItem).OnChange:=ChangedAnimation;

    TabOptions.Visible:=Assigned(tmpEditor);
    TabOptions.TabVisible:=TabOptions.Visible;

    if TabOptions.Visible then
       PageControl2.ActivePage:=TabOptions
    else
    begin
      PageControl2.ActivePage:=TabAnimation;
    end;
  end;

  BDeleteItem.Enabled:=LBItems.ItemIndex<>-1;
end;

procedure TFormTeeAnimations.ChangedAnimation(Sender: TObject);
begin
  LBItems.Items[LBItems.ItemIndex]:=SelectedItem.EditorName;
end;

procedure TFormTeeAnimations.AnimateStop(Sender: TObject);
begin
  CheckPlayButtons(Self);
end;

procedure TFormTeeAnimations.CheckPlayButtons(Sender: TObject);
begin
  with SelectedAnimate do
  begin
    SBPlay.Enabled:=not Playing;
    SBPause.Enabled:=Playing and (not Paused);
    SBStop.Enabled:=Playing or Paused;
    SBFrame.Enabled:=Paused;
  end;
end;

procedure TFormTeeAnimations.SBAddItemClick(Sender: TObject);
var tmp : TMenuItem;
    t   : Integer;
    P   : TPoint;
    tmpClass : TTeeAnimationClass;
begin
  if PopupMenu1.Items.Count=0 then
  begin
    for t:=0 to TeeAnimationClasses.Count-1 do
    begin
      tmpClass:=TTeeAnimationClass(GetClass(TeeAnimationClasses[t]));

      if (not Assigned(Animations)) or tmpClass.IsValidOwner(Animations.Owner) then
      begin
        tmp:=TMenuItem.Create(Self);
        tmp.Caption:=TTeeAnimationClass(GetClass(TeeAnimationClasses[t])).Description;
        tmp.Tag:=t;
        tmp.OnClick:=PopupSelected;
        PopupMenu1.Items.Add(tmp);
      end;
    end;
  end;

  P.X:=SBAddItem.Left+SBAddItem.Width;
  P.Y:=SBAddItem.Top+SBAddItem.Height;

  P:=SBAddItem.ClientToScreen(P);
  PopupMenu1.Popup(P.X,P.Y);
end;

procedure TFormTeeAnimations.BDeleteItemClick(Sender: TObject);
var tmp : Integer;
begin
  tmp:=LBItems.ItemIndex;

  if TeeYesNo(Format(TeeMsg_SureToDelete,[LBItems.Items[tmp]])) then
  begin
    if SelectedAnimation=nil then
    begin
      if Assigned(IAnimate) then
         IAnimate.Animation[tmp].Free;
    end
    else
      SelectedAnimation.Animations[tmp].Free;

    LBItems.Items.Delete(tmp);

    if tmp>LBItems.Items.Count-1 then
       LBItems.ItemIndex:=LBItems.Items.Count-1
    else
       LBItems.ItemIndex:=tmp;

    LBItemsClick(Self);
  end;
end;

procedure TFormTeeAnimations.BRenameClick(Sender: TObject);
var tmp : String;
begin
  tmp:=SelectedAnimation.Name;

  if InputQuery(TeeMsg_ChangeAnimationName,TeeMsg_NewAnimationName,tmp) then
  if tmp<>'' then
  begin
    SelectedAnimation.Name:=tmp;
    LBAnims.Items[LBAnims.ItemIndex]:=tmp;
  end;
end;

procedure TFormTeeAnimations.PopupSelected(Sender:TObject);
var tmp : TTeeAnimation;
    tmpClass : TTeeAnimationClass;
    tmpOwner : TComponent;
begin
  tmpClass:=TTeeAnimationClass(GetClass(TeeAnimationClasses[TMenuItem(Sender).Tag]));

  if SelectedAnimation=nil then
     if Assigned(IAnimate) then
        tmpOwner:=IAnimate.Owner
     else
        tmpOwner:=Self
  else
     tmpOwner:=SelectedAnimation.ParentChart;

  tmp:=tmpClass.Create(tmpOwner);
  tmp.Animate:=SelectedAnimate;

  if (tmp is TCustomAnimationChart) and (SelectedAnimation<>nil) then
     TCustomAnimationChart(tmp).Chart:=SelectedAnimation.ParentChart
  else
  if SelectedAnimation<>nil then
     tmp.TagObject:=SelectedAnimation.ParentChart;

  LBItems.Items.AddObject(tmp.EditorName,tmp);
  LBItems.ItemIndex:=LBItems.Items.Count-1;
  LBItemsClick(Self);
end;

procedure TFormTeeAnimations.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  ClearOnStopEvent;
  ClearOnChangeEvent;
end;

procedure TFormTeeAnimations.ClearOnStopEvent;
begin
  if Assigned(IAnimate) then
     TAnimateAccess(IAnimate).IOnStop:=nil;
end;

procedure TFormTeeAnimations.ClearOnChangeEvent;
var t : Integer;
    tmp : TTeeAnimation;
begin
  for t:=0 to LBItems.Items.Count-1 do
  begin
    tmp:=TTeeAnimation(LBItems.Items.Objects[t]);

    if tmp is TPropertyAnimation then
       TPropertyAnimation(tmp).OnChange:=nil;
  end;
end;

procedure TFormTeeAnimations.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled:=False;

  if TabOptions.Visible then
  begin
    PageControl2.ActivePage:=TabAnimation;
    PageControl2.ActivePage:=TabOptions;
  end;
end;

procedure TFormTeeAnimations.SelectIfAny;
begin
  if Animations.Count>0 then
  begin
    LBAnims.ItemIndex:=0;
    LBAnimsClick(Self);
  end
  else
  begin
    PanelEditor.Visible:=True;
    PanelEditor.Visible:=False;
  end;
end;

procedure TFormTeeAnimations.CBSpeedChange(Sender: TObject);
var s : String;
begin
  s:=CBSpeed.Items[CBSpeed.ItemIndex];
  Delete(s,Length(s),1);
  SelectedAnimate.SpeedFactor:=StrToFloat(s);
end;

procedure TFormTeeAnimations.CBAutoPlayClick(Sender: TObject);
begin
  SelectedAnimation.AutoPlay:=CBAutoPlay.Checked;
end;

class function TFormTeeAnimations.Edit(const AOwner: TComponent;
  const AChart: TChart): Boolean;
begin
  with TFormTeeAnimations.Create(AOwner) do
  try
    Splitter1.Hide;
    Panel1.Hide;
    PanelButtons.Show;

    Animations:=AChart.Animations;
    result:=ShowModal=mrOk;
  finally
    Free;
  end;
end;

end.
