{*********************************************}
{  TeeChart Charting Library for FireMonkey   }
{  Copyright (c) 2012-2020 by Steema Software }
{  All Rights Reserved                        }
{*********************************************}
unit FMXTee.Editor.Series;
{$I TeeDefs.inc}

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.UIConsts, System.Classes,
  System.Generics.Collections,
  System.Variants, FMX.Types, FMX.Controls, FMX.Forms,
  {$IFNDEF UPTOXE3}
  FMX.StdCtrls,
  {$ENDIF}

  {$IFNDEF UPTOXE7}
  FMX.SpinBox, FMX.ComboEdit, FMX.EditBox, FMX.Controls.Presentation,
  {$ENDIF}

  FMX.ListBox, FMX.Edit, FMX.Dialogs, FMX.Objects, FMX.TabControl,
  FMXTee.Engine, FMXTee.Editor.CustomShape, FMXTee.Editor.Source,
  FMXTee.Editor.Series.Pointer, FMXTee.Editor.Stroke, FMXTee.Editor.Picture,

  {$IFDEF D19}
  FMX.Graphics,
  {$ENDIF}

  {$IFNDEF TEELITEEMB}
  FMXTee.Chart.Grid,
  {$ENDIF}

  FMX.Layouts, FMX.Effects;

type
  TFormClass=class of TForm;

type
  TSeriesEditor = class(TForm)
    PageSeries: TTabControl;
    TheTabSheet: TTabItem;
    TabGeneral: TTabItem;
    GB5: TGroupBox;
    L27: TLabel;
    LDepth: TLabel;
    CBCursor: TComboBox;
    UDDepth: TSpinBox;
    CBDepth: TCheckBox;
    GB2: TGroupBox;
    L15: TLabel;
    L21: TLabel;
    Button1: TButton;
    Button2: TButton;
    EPercentFormat: TEdit;
    GBHorizAxis: TGroupBox;
    CBHorizAxis: TComboBox;
    CBXDateTime: TCheckBox;
    GBVertAxis: TGroupBox;
    CBVertAxis: TComboBox;
    CBYDateTime: TCheckBox;
    CBZDateTime: TCheckBox;
    CBShowInEditor: TCheckBox;
    GroupBox1: TGroupBox;
    CBSortBy: TComboBox;
    CBSortOrder: TComboBox;
    BVisual: TButton;
    TabMarks: TTabItem;
    PageControlMarks: TTabControl;
    TabMarksStyle: TTabItem;
    L23: TLabel;
    Label3: TLabel;
    Label1: TLabel;
    Label6: TLabel;
    CBMarksVisible: TCheckBox;
    CBMarkClip: TCheckBox;
    UDMarksAngle: TSpinBox;
    UDEvery: TSpinBox;
    CBMultiLine: TCheckBox;
    CBAllVisible: TCheckBox;
    RGMarkStyle: TComboBox;
    CBTextAlign: TComboBox;
    CBFontSeriesColor: TCheckBox;
    TabSheet2: TTabItem;
    TabSymbol: TTabItem;
    TabMargins: TTabItem;
    TabDataSource: TTabItem;
    PanelRandomSource: TPanel;
    Label2: TLabel;
    ESamples: TEdit;
    CBSamplesDefault: TCheckBox;
    BApplyRandom: TButton;
    Button3: TButton;
    PanelFunctions: TPanel;
    PanTop: TPanel;
    CBDataSourcestyle: TComboBox;
    NewDataButton: TButton;
    EditDataButton: TButton;
    DeleteDataButton: TButton;
    PanelSeriesSource: TPanel;
    Label5: TLabel;
    CBSeriesSource: TComboBox;
    BApplySource: TButton;
    BClearSource: TButton;
    CBPersistent: TCheckBox;
    Panel1: TPanel;
    ImageSeries: TImage;
    LabelSeriesClass: TLabel;
    PaintBox1: TPaintBox;
    CBSeries: TComboBox;
    CBFormat: TComboEdit;
    GlowEffect1: TGlowEffect;
    LBFunctions: TListBoxItem;
    PanelManual: TPanel;
    TabMarksFormat: TTabItem;
    LBSeriesSource: TListBoxItem;
    PageArrows: TTabControl;
    TabArrowOptions: TTabItem;
    TabArrowBorder: TTabItem;
    TabArrowPointer: TTabItem;
    Label11: TLabel;
    CBHead: TComboBox;
    ListBoxItem27: TListBoxItem;
    ListBoxItem28: TListBoxItem;
    ListBoxItem29: TListBoxItem;
    Label12: TLabel;
    UDHeadSize: TSpinBox;
    L32: TLabel;
    UDArrowsLength: TSpinBox;
    Label4: TLabel;
    UDArrowDist: TSpinBox;
    CBOnTop: TCheckBox;
    ListBoxItem32: TListBoxItem;
    ListBoxItem33: TListBoxItem;
    TabControl1: TTabControl;
    TabGenOptions: TTabItem;
    TabGenLegend: TTabItem;
    Layout1: TLayout;
    CBShowInLegend: TCheckBox;
    Label7: TLabel;
    ELegendTitle: TEdit;
    TabHover: TTabItem;
    TabTail: TTabItem;
    CBTailVisible: TCheckBox;
    Label8: TLabel;
    SBTailHeight: TSpinBox;
    Label9: TLabel;
    SBTailWidth: TSpinBox;
    Label10: TLabel;
    SBTailMargin: TSpinBox;
    GroupBox2: TGroupBox;
    CBTailAuto: TCheckBox;
    Label13: TLabel;
    CBTailEdge: TComboBox;
    Label14: TLabel;
    CBTailAlign: TComboBox;
    procedure FormCreate(Sender: TObject);
    procedure CBCursorChange(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure CBHorizAxisChange(Sender: TObject);
    procedure CBVertAxisChange(Sender: TObject);
    procedure CBSortByChange(Sender: TObject);
    procedure CBSortOrderChange(Sender: TObject);
    procedure BVisualClick(Sender: TObject);
    procedure PageControlMarksChange(Sender: TObject);
    procedure RGMarkStyleClick(Sender: TObject);
    procedure CBTextAlignChange(Sender: TObject);
    procedure CBHeadChange(Sender: TObject);
    procedure BApplyRandomClick(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure ChartGrid1SetEditText;
    procedure CBDataSourcestyleChange(Sender: TObject);
    procedure CBSeriesSourceChange(Sender: TObject);
    procedure BApplySourceClick(Sender: TObject);
    procedure BClearSourceClick(Sender: TObject);
    procedure PaintBox1Click(Sender: TObject);
    procedure CBSeriesChange(Sender: TObject);
    procedure CBPersistentChange(Sender: TObject);
    procedure CBMarksVisibleChange(Sender: TObject);
    procedure UDEveryChangeTracking(Sender: TObject);
    procedure CBMarkClipChange(Sender: TObject);
    procedure CBMultiLineChange(Sender: TObject);
    procedure CBFontSeriesColorChange(Sender: TObject);
    procedure UDMarksAngleChangeTracking(Sender: TObject);
    procedure UDDepthChangeTracking(Sender: TObject);
    procedure CBXDateTimeChange(Sender: TObject);
    procedure CBYDateTimeChange(Sender: TObject);
    procedure CBShowInLegendChange(Sender: TObject);
    procedure CBShowInEditorChange(Sender: TObject);
    procedure CBSamplesDefaultChange(Sender: TObject);
    procedure CBDepthChange(Sender: TObject);
    procedure PaintBox1Paint(Sender: TObject; const Canvas: TCanvas);
    procedure UDArrowsLengthChangeTracking(Sender: TObject);
    procedure UDArrowDistChangeTracking(Sender: TObject);
    procedure UDHeadSizeChangeTracking(Sender: TObject);
    procedure CBZDateTimeChange(Sender: TObject);
    procedure CBAllVisibleChange(Sender: TObject);
    procedure CBFormatChangeTracking(Sender: TObject);
    procedure ELegendTitleTyping(Sender: TObject);
    procedure EPercentFormatTyping(Sender: TObject);
    procedure ESamplesTyping(Sender: TObject);
    procedure PageArrowsChange(Sender: TObject);
    procedure CBOnTopChange(Sender: TObject);
    procedure TabControl1Change(Sender: TObject);
    procedure SBTailHeightChangeTracking(Sender: TObject);
    procedure CBTailVisibleChange(Sender: TObject);
    procedure SBTailWidthChangeTracking(Sender: TObject);
    procedure SBTailMarginChangeTracking(Sender: TObject);
    procedure CBTailEdgeChange(Sender: TObject);
    procedure CBTailAutoChange(Sender: TObject);
    procedure CBTailAlignChange(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
   { Private declarations }
    Series : TChartSeries;

    ChangingSeries  : Boolean;

    {$IFNDEF TEELITEEMB}
    FunctionEditorClass : TFormClass;
    FChartGrid      : TChartGrid;
    {$ENDIF}

    DataSourceStyle : TBaseSourceEditor;
    IMarks          : TTeeShapeEditor;
    IMarksSymbol    : TTeeShapeEditor;
    FMarginsEditor  : TForm;
    LastSeries      : TChartSeries;
    LastSeriesForm  : TCustomForm;
    IArrowBorder    : TStrokeEditor;
    IArrowPointer   : TSeriesPointerEditor;
    ILegend         : TBackImageEditor;
    IHover          : TTeeShapeEditor;

    procedure CheckCBSamples;
    procedure CreateDataSourceForm;
    procedure DestroyDataSourceForm;
    Function FindZList:TChartValueList;
{$IFNDEF TEELITEEMB}	
    function IndexOfSource(const ASource:TTeeSeriesSourceClass):Integer;
{$ENDIF}	
    class function MarkSymbolsForm(const AOwner: TComponent;
                   const Symbol:TSeriesMarksSymbol):TTeeShapeEditor;
    procedure RefreshSeries(const ASeries:TChartSeries);
    procedure RefreshTailAlign(const ATail:TMarksTail);
    procedure RemoveAllSources;
  public
   { Public declarations }
    class function CreateEditor(const AOwner:TComponent; const ASeries:TChartSeries):TSeriesEditor;
    class procedure Edit(const AOwner: TComponent; const ASeries: TChartSeries);
    class procedure EditMarks(const AOwner: TComponent; const AMarks: TSeriesMarks);
    function Embedd(const AForm:TForm; const ATitle:String=''):TCommonCustomForm;
  end;

implementation

{$R *.fmx}

uses
  FMXTee.Canvas, FMXTee.Editor.Formatting, FMXTee.Procs, FMXTee.Editor.Margins,
  {$IFNDEF TEELITEEMB}
  FMXTee.Editor.Functions, FMXTee.Editor.Functions.Average,
  {$ENDIF}
  FMXTee.Chart;

type
   TChartSeriesAccess=class(TChartSeries);

function TSeriesEditor.FindZList: TChartValueList;
begin
  result:=TChartSeriesAccess(Series).ValueListOfAxis(Series.ParentChart.DepthAxis);

  if not Assigned(result) then
     result:=TChartSeriesAccess(Series).ValueListOfAxis(Series.ParentChart.DepthTopAxis);
end;

procedure TSeriesEditor.FormCreate(Sender: TObject);
begin
  PageSeries.ActiveTab:=TheTabSheet;
  PageControlMarks.ActiveTab:=TabMarksStyle;

  {$IFDEF TEELITEEMB}
  LBSeriesSource.Free;
  LBFunctions.Free;
  {$ENDIF}
end;

type
  // Temporary class, used to assign to Source combobox items "Data" property
  // as an object.
  // (Because Linux etc do not support a "class of" to be a TObject)
  TSourceClassObject=class
  public
    Source : TTeeSeriesSourceClass;

    Constructor Create(const ASource:TTeeSeriesSourceClass);
  end;

Constructor TSourceClassObject.Create(const ASource:TTeeSeriesSourceClass);
begin
  inherited Create;
  Source:=ASource;
end;

procedure TSeriesEditor.RemoveAllSources;
var tmpItem : TListBoxItem;
begin
  { remove source styles from combobox }
  if CBDataSourcestyle<>nil then
  with CBDataSourceStyle do
       while ChildrenCount>4 do
       begin
         tmpItem:=Children[ChildrenCount-1] as TListBoxItem;
         (tmpItem.Data as TSourceClassObject).DisposeOf;

         Children[ChildrenCount-1].DisposeOf;
       end;
end;

procedure TSeriesEditor.FormDestroy(Sender: TObject);
begin
  RemoveAllSources;
end;

procedure TSeriesEditor.CBCursorChange(Sender: TObject);
var tmp : Integer;
begin
  if IdentToCursor(TeeCursorPrefix+CBCursor.Selected.Text,tmp) then
     Series.Cursor:=tmp;
end;

procedure TSeriesEditor.CBDepthChange(Sender: TObject);
begin
  if CBDepth.IsChecked then
     Series.Depth:=TeeAutoDepth;

  UDDepth.Enabled:=not CBDepth.IsChecked;
end;

procedure TSeriesEditor.Button1Click(Sender: TObject);
begin
  CBFormat.Text:=TFormatEditor.Change(Self,CBFormat.Text);
  CBFormatChangeTracking(Self);
end;

procedure TSeriesEditor.Button2Click(Sender: TObject);
begin
  EPercentFormat.Text:=TFormatEditor.Change(Self,EPercentFormat.Text);
  EPercentFormatTyping(Self);
end;

procedure TSeriesEditor.CBFormatChangeTracking(Sender: TObject);
begin
  if Assigned(Series) then
     Series.ValueFormat:=LocalToDelphiFormat(CBFormat.Text);
end;

procedure TSeriesEditor.CBHorizAxisChange(Sender: TObject);
begin
  Series.HorizAxis:=THorizAxis(CBHorizAxis.ItemIndex);
end;

procedure TSeriesEditor.CBXDateTimeChange(Sender: TObject);
begin
  Series.XValues.DateTime:=CBXDateTime.IsChecked;
end;

procedure TSeriesEditor.CBVertAxisChange(Sender: TObject);
begin
  Series.VertAxis:=TVertAxis(CBVertAxis.ItemIndex);
end;

procedure TSeriesEditor.CBYDateTimeChange(Sender: TObject);
begin
  Series.YValues.DateTime:=CBYDateTime.IsChecked;
end;

procedure TSeriesEditor.CBZDateTimeChange(Sender: TObject);
var tmpList : TChartValueList;
begin
  tmpList:=FindZList;

  if Assigned(tmpList) then
     tmpList.DateTime:=CBZDateTime.IsChecked
  else
     CBZDateTime.Visible:=False;
end;

procedure TSeriesEditor.CBShowInEditorChange(Sender: TObject);
begin
  Series.ShowInEditor:=CBShowInEditor.IsChecked;
end;

procedure TSeriesEditor.CBSortByChange(Sender: TObject);
var t : Integer;
    tmpSortedList : TChartValueList;
begin // Change sorting order...

  tmpSortedList:=TChartValueList(CBSortBy.Selected.Data);

  for t:=0 to Series.ValuesList.Count-1 do
    if (CBSortBy.ItemIndex<2) or (Series.ValuesList[t]<>tmpSortedList) then
       Series.ValuesList[t].Order:=loNone
    else
    if CBSortOrder.ItemIndex=0 then
       Series.ValuesList[t].Order:=loAscending
    else
       Series.ValuesList[t].Order:=loDescending;

  CBSortOrder.Enabled:=CBSortBy.ItemIndex<>0;

  if CBSortBy.ItemIndex<>0 then
     if CBSortBy.ItemIndex=1 then
     begin
       // Sort points by Series Text
       if CBSortOrder.ItemIndex=0 then
          Series.SortByLabels(loAscending)
       else
          Series.SortByLabels(loDescending)
     end
     else
       Series.CheckOrder;
end;

procedure TSeriesEditor.CBSortOrderChange(Sender: TObject);
begin
  CBSortByChange(Self);
end;

procedure TSeriesEditor.CBShowInLegendChange(Sender: TObject);
begin
  Series.ShowInLegend:=CBShowInLegend.IsChecked;
end;

procedure TSeriesEditor.BVisualClick(Sender: TObject);
begin
// TODO
end;

class Function TSeriesEditor.MarkSymbolsForm(const AOwner: TComponent;
              const Symbol:TSeriesMarksSymbol):TTeeShapeEditor;
begin
  result:=TTeeShapeEditor.CreateEditor(AOwner,Symbol);

  with result do
  begin
    Name:=TeeGetUniqueName(AOwner,'MarksSymbolEditor'); // Do not localize

    BBackColor.Visible:=False;
    TabFont.Visible:=False;
  end;
end;

procedure TSeriesEditor.PageControlMarksChange(Sender: TObject);
begin
  if (PageControlMarks.ActiveTab=TabSymbol) and (not Assigned(IMarksSymbol)) then
     IMarksSymbol:=TTeeShapeEditor(EmbeddForm(MarkSymbolsForm(Self,Series.Marks.Symbol), TabSymbol))
  else
  if (PageControlMarks.ActiveTab=TabMargins) and (not Assigned(FMarginsEditor)) then
     FMarginsEditor:=TMarginsEditor(EmbeddForm(TMarginsEditor.CreateEditor(Self,Series.Marks.Margins),TabMargins))
  else
  if (PageControlMarks.ActiveTab=TabMarksFormat) and (not Assigned(IMarks)) then
  begin
    IMarks:=TTeeShapeEditor.CreateEditor(Self,Series.Marks);
    IMarks.Name:=TeeGetUniqueName(Self,'MarksEditor'); // Do not localize
    EmbeddForm(IMarks,TabMarksFormat);
  end;
end;

procedure TSeriesEditor.CBMarksVisibleChange(Sender: TObject);
begin
  Series.Marks.Visible:=CBMarksVisible.IsChecked;
end;

procedure TSeriesEditor.CBMarkClipChange(Sender: TObject);
begin
  Series.Marks.Clip:=CBMarkClip.IsChecked;
end;

procedure TSeriesEditor.ELegendTitleTyping(Sender: TObject);
begin
  Series.LegendTitle:=ELegendTitle.Text;
end;

function TSeriesEditor.Embedd(const AForm: TForm; const ATitle: String): TCommonCustomForm;
var tmp : TTabItem;
begin
  tmp:=TTabItem.Create(Self);
  tmp.Text:=ATitle;

  result:=EmbeddForm(AForm,tmp);

  PageSeries.AddObject(tmp);
end;

procedure TSeriesEditor.EPercentFormatTyping(Sender: TObject);
begin
  Series.PercentFormat:=LocalToDelphiFormat(EPercentFormat.Text);
end;

procedure TSeriesEditor.ESamplesTyping(Sender: TObject);
var tmpNum : Integer;
begin
  tmpNum:=StrToIntDef(ESamples.Text,0);
  TChartSeriesAccess(Series).INumSampleValues:=tmpNum;
  CheckCBSamples;
  CBSamplesDefault.IsChecked:=False;
  BApplyRandom.Enabled:=True;
end;

procedure TSeriesEditor.CBMultiLineChange(Sender: TObject);
begin
  Series.Marks.MultiLine:=CBMultiLine.IsChecked;
end;

procedure TSeriesEditor.CBOnTopChange(Sender: TObject);
begin
  Series.Marks.OnTop:=CBOnTop.IsChecked;
end;

procedure TSeriesEditor.CBAllVisibleChange(Sender: TObject);
var t : Integer;
begin
  with Series.ParentChart do
  for t:=0 to SeriesCount-1 do
      Series[t].Marks.Visible:=CBAllVisible.IsChecked;

  CBMarksVisible.IsChecked:=CBAllVisible.IsChecked;
end;

procedure TSeriesEditor.RefreshTailAlign(const ATail:TMarksTail);
begin
  CBTailEdge.Enabled:=not ATail.AutoAlign;
  CBTailAlign.Enabled:=not ATail.AutoAlign;
end;

type
  TCustomFormClass=class of TCustomForm;
  TCustomChartAccess=class(TCustomChart);

{$IFNDEF TEELITEEMB}
function TSeriesEditor.IndexOfSource(const ASource:TTeeSeriesSourceClass):Integer;
var t : Integer;
begin
  for t:=4 to CBDataSourceStyle.Count-1 do
      if TSourceClassObject(CBDataSourcestyle.ListBox.Items.Objects[t]).Source=ASource then
      begin
        result:=t;
        Exit;
      end;

  result:=-1;
end;
{$ENDIF}

procedure TSeriesEditor.RefreshSeries(const ASeries: TChartSeries);

  procedure SetLabelSeriesClass;
  var tmpString : String;
  begin
    tmpString := GetGallerySeriesName(Series);

    // Example: Replace "Point & Figure" with --> "Point && Figure"

    Index := Pos('&', tmpString);

    if Index > 0 then
       Insert('&', tmpString, Index + 1);

    LabelSeriesClass.Text:=ReplaceChar(tmpString+': '+SeriesTitleOrName(Series),#13); // Do not localize
  end;

  procedure AddSortByItems;
  var t : Integer;
      tmpItem : TListBoxItem;
  begin
    while CBSortBy.Count>2 do
          CBSortBy.ListBox.ItemByIndex(2).DisposeOf;

    CBSortBy.ItemIndex:=0;

    with Series.ValuesList do
    for t:=0 to Count-1 do
    if ValueList[t].Name<>'' then
    begin
      tmpItem:=TListBoxItem.Create(Self);
      tmpItem.Text:=ValueList[t].Name;
      tmpItem.Data:=ValueList[t];

      CBSortBy.AddObject(tmpItem);

      if ValueList[t].Order<>loNone then
      begin
        CBSortBy.ItemIndex:=CBSortBy.Count-1;

        if ValueList[t].Order=loAscending then
           CBSortOrder.ItemIndex:=0
        else
           CBSortOrder.ItemIndex:=1;
      end;
    end;

    CBSortOrder.Enabled:=CBSortBy.ItemIndex<>0;
  end;

  procedure SetSeriesIcon;
  var tmpBitmap : TBitmap;
  begin
    tmpBitmap:=TBitmap.Create{$IFNDEF D19}(0,0){$ENDIF};
    try
      TeeGetBitmapEditor(Series,tmpBitmap);
      ImageSeries.Bitmap.Assign(tmpBitmap);
    finally
      tmpBitmap.Free;
    end;
  end;

  {$IFNDEF TEELITEEMB}
  Procedure AddTeeSources;
  var t: Integer;
      tmpSource : TTeeSeriesSourceClass;
      tmpItem : TListBoxItem;
  begin
    RemoveAllSources;

    { add sources that are available for this chart type }
    for t:=0 to TeeSources.Count-1 do { 5.02 }
    begin
      tmpSource:={$IFNDEF TEELISTMETA}TTeeSeriesSourceClass{$ENDIF}(TeeSources[t]);

      if IndexOfSource(tmpSource)=-1 then
         if tmpSource.Available(Series.ParentChart) then
         begin
           tmpItem:=TListBoxItem.Create(Self);

           tmpItem.Text:=tmpSource.Description;
           tmpItem.Data:=TSourceClassObject.Create(tmpSource);

           CBDataSourceStyle.AddObject(tmpItem);
         end;
    end;
  end;
  {$ENDIF}
  
  procedure SetTabGeneral;
  var tmpList : TChartValueList;
      tmpSt   : String;
  begin
    // General

    CBPersistent.IsChecked:=tssIsPersistent in Series.Style;

    CBShowInLegend.IsChecked:=Series.ShowInLegend;

    CBShowInEditor.Visible:=csDesigning in Series.ComponentState;
    CBShowInEditor.IsChecked:=Series.ShowInEditor;

    TFormatEditor.AddDefaultFormats(CBFormat);

    tmpSt:=DelphiToLocalFormat(Series.ValueFormat);
    With CBFormat.Items do if IndexOf(tmpSt)=-1 then Add(tmpSt);
    CBFormat.Text:=tmpSt;

    EPercentFormat.Text:=DelphiToLocalFormat(Series.PercentFormat);

    CBDepth.IsChecked:=Series.Depth=TeeAutoDepth;
    UDDepth.Enabled:=not CBDepth.IsChecked;

    if UDDepth.Enabled then
       UDDepth.Value:=Series.Depth;

    ELegendTitle.Text:=Series.LegendTitle;

    CBXDateTime.IsChecked:=Series.XValues.DateTime;
    CBYDateTime.IsChecked:=Series.YValues.DateTime;

    tmpList:=FindZList;
    CBZDateTime.Visible:=Assigned(tmpList);
    if CBZDateTime.Visible then
       CBZDateTime.IsChecked:=tmpList.DateTime;

    CBHorizAxis.ItemIndex:=Ord(Series.HorizAxis);
    CBVertAxis.ItemIndex:=Ord(Series.VertAxis);

    BVisual.Visible:=Assigned(Series) and Assigned(TeeOnChangeVisual) and
                     (TCustomChartAccess(Series.ParentChart).Form3DClass<>nil);

    AddSortByItems;
  end;

  procedure SetTailProperties(const ATail:TMarksTail);
  begin
    CBTailVisible.IsChecked:=ATail.Visible;
    SBTailHeight.Value:=ATail.Height;
    SBTailWidth.Value:=ATail.Width;
    SBTailMargin.Value:=ATail.Margin;

    CBTailAuto.IsChecked:=ATail.AutoAlign;

    CBTailEdge.ItemIndex:=Ord(ATail.Edge);
    CBTailAlign.ItemIndex:=Ord(ATail.Align);

    RefreshTailAlign(ATail);
  end;

  procedure SetTabMarks(const AMarks:TSeriesMarks);
  begin
    // Marks
    CBHead.ItemIndex:=Ord(AMarks.Callout.ArrowHead);

    CBMarksVisible.IsChecked:=AMarks.Visible;
    CBMarkClip.IsChecked:=AMarks.Clip;
    CBMultiLine.IsChecked:=AMarks.MultiLine;
    CBFontSeriesColor.IsChecked:=AMarks.FontSeriesColor;

    CBOnTop.IsChecked:=AMarks.OnTop;

    RGMarkStyle.ItemIndex:=Ord(AMarks.Style);

    UDEvery.Value:=AMarks.DrawEvery;
    UDMarksAngle.Value:=AMarks.Angle;
    UDArrowsLength.Value:=AMarks.ArrowLength;
    UDArrowDist.Value:=AMarks.Callout.Distance;
    UDHeadSize.Value:=AMarks.Callout.ArrowHeadSize;

    SetTailProperties(AMarks.Tail);
  end;

  procedure SetTabDataSource;

    procedure SetOtherSource;
    {$IFNDEF TEELITEEMB}
    var tmpSource : TTeeSeriesSourceClass;
        t         : Integer;
    {$ENDIF}
    begin
      {$IFNDEF TEELITEEMB}
      tmpSource:=nil;

      for t:=0 to TeeSources.Count-1 do
      if {$IFNDEF TEELISTMETA}TTeeSeriesSourceClass{$ENDIF}(TeeSources[t]).HasSeries(Series) then
      begin
        tmpSource:={$IFNDEF TEELISTMETA}TTeeSeriesSourceClass{$ENDIF}(TeeSources[t]);
        break;
      end;

      if Assigned(tmpSource) then
         CBDataSourceStyle.ItemIndex:=IndexOfSource(tmpSource)
      else
      {$ENDIF}
         CBDataSourceStyle.ItemIndex:=0;
    end;

  begin
    // DataSource

    {$IFNDEF TEELITEEMB}
    FunctionEditorClass:={TObject}(TFunctionEditor);  // iOS SIGSEV

    if Assigned(Series.FunctionType) then
       CBDataSourceStyle.ItemIndex:=3
    else
    {$ENDIF}
    if Series.DataSource is TChartSeries then
       CBDataSourcestyle.ItemIndex:=2
    else
    if Series.DataSource=nil then
       if Series.ManualData then
          CBDataSourcestyle.ItemIndex:=0
       else
          CBDataSourcestyle.ItemIndex:=1
    else
      SetOtherSource;

    //CBDataSourcestyleChange(Self);
  end;

var t : Integer;
    tmp : TListBoxItem;
    tmpClass : TPersistentClass;
begin
  Series:=ASeries;

  if CBSeries.Count=0 then
  begin
    if Assigned(Series.ParentChart) then
    for t := 0 to Series.ParentChart.SeriesCount-1 do
    begin
      tmp:=TListBoxItem.Create(Self);
      tmp.Text:=SeriesTitleOrName(Series.ParentChart[t]);
      tmp.Data:=Series.ParentChart[t];
      CBSeries.AddObject(tmp);
    end;

    ChangingSeries:=True;
    CBSeries.ItemIndex:=Series.ParentChart.SeriesList.IndexOf(Series);
    ChangingSeries:=False;
  end;

  {$IFNDEF TEELITEEMB}
  AddTeeSources;
  {$ENDIF}

  SetLabelSeriesClass;

  SetSeriesIcon;

  TeeFillCursors(CBCursor,Series.Cursor);

  SetTabGeneral;
  SetTabMarks(Series.Marks);

  // Series Editor dialog

  StartClassGroup(TFmxObject);
  ActivateClassGroup(TFmxObject);

  tmpClass:=GetClass(Series.EditorClass);

  if Assigned(tmpClass) then
  begin
    TheTabSheet.Visible:=True;

    if (not Assigned(LastSeriesForm)) or (LastSeriesForm.ClassType<>tmpClass) then
    begin
      LastSeriesForm.Free;
      LastSeriesForm:=TCustomFormClass(tmpClass).Create(Self);
      EmbeddForm(LastSeriesForm,TheTabSheet);
    end;

    LastSeriesForm.TagObject:=Series;

    LastSeriesForm.OnActivate(Self);
    //LastSeriesForm.Activate;  <-- XE2
  end
  else
  begin
    FreeAndNil(LastSeriesForm);
    TheTabSheet.Visible:=False;
    PageSeries.ActiveTab:=TabGeneral;
  end;

  SetTabDataSource;
end;

procedure TSeriesEditor.RGMarkStyleClick(Sender: TObject);
begin
  Series.Marks.Style:=TSeriesMarksStyle(RGMarkStyle.ItemIndex);
end;

procedure TSeriesEditor.SBTailHeightChangeTracking(Sender: TObject);
begin
  Series.Marks.Tail.Height:=Round(SBTailHeight.Value);
end;

procedure TSeriesEditor.SBTailMarginChangeTracking(Sender: TObject);
begin
  Series.Marks.Tail.Margin:=Round(SBTailMargin.Value);
end;

procedure TSeriesEditor.SBTailWidthChangeTracking(Sender: TObject);
begin
  Series.Marks.Tail.Width:=Round(SBTailWidth.Value);
end;

procedure TSeriesEditor.TabControl1Change(Sender: TObject);
begin
  if (TabControl1.ActiveTab=TabGenLegend) and (not Assigned(ILegend)) then
  begin
    ILegend:=TBackImageEditor.InsertForm(Series.Legend.Picture,TabGenLegend);
    ILegend.TabBounds.Visible:=False;
  end
  else
  if (TabControl1.ActiveTab=TabHover) and (not Assigned(IHover)) then
  begin
    IHover:=TTeeShapeEditor.InsertForm(Series.Selected.Hover,TabHover);
  end;
end;

procedure TSeriesEditor.PageArrowsChange(Sender: TObject);
begin
  if (PageArrows.ActiveTab=TabArrowBorder) and (not Assigned(IArrowBorder)) then
     IArrowBorder:=TStrokeEditor.InsertForm(Series.Marks.Callout.Arrow,TabArrowBorder)
  else
  if (PageArrows.ActiveTab=TabArrowPointer) and (not Assigned(IArrowPointer)) then
     IArrowPointer:=TSeriesPointerEditor.InsertForm(Series.Marks.Callout,TabArrowPointer);
end;

procedure TSeriesEditor.CBTailEdgeChange(Sender: TObject);
begin
  Series.Marks.Tail.Edge:=TTailEdge(CBTailEdge.ItemIndex);
end;

procedure TSeriesEditor.CBTailAutoChange(Sender: TObject);
begin
  Series.Marks.Tail.AutoAlign:=CBTailAuto.IsChecked;
  RefreshTailAlign(Series.Marks.Tail);
end;

procedure TSeriesEditor.CBTailAlignChange(Sender: TObject);
begin
  Series.Marks.Tail.Align:=TTailAlign(CBTailAlign.ItemIndex);
end;

procedure TSeriesEditor.CBTailVisibleChange(Sender: TObject);
begin
  Series.Marks.Tail.Visible:=CBTailVisible.IsChecked;
end;

procedure TSeriesEditor.CBTextAlignChange(Sender: TObject);
begin
  Series.Marks.TextAlign:=TAlignment(CBTextAlign.ItemIndex);
end;

procedure TSeriesEditor.CBFontSeriesColorChange(Sender: TObject);
begin
  Series.Marks.FontSeriesColor:=CBFontSeriesColor.IsChecked;
end;

procedure TSeriesEditor.CBHeadChange(Sender: TObject);
begin
  Series.Marks.Callout.ArrowHead:=TArrowHeadStyle(CBHead.ItemIndex);
end;

class function TSeriesEditor.CreateEditor(const AOwner: TComponent; const ASeries: TChartSeries):TSeriesEditor;
begin
  result:=TSeriesEditor.Create(AOwner);
  result.RefreshSeries(ASeries);
end;

procedure TSeriesEditor.CBSamplesDefaultChange(Sender: TObject);
begin
  if CBSamplesDefault.IsChecked then
  begin
    ESamples.Text:=IntToStr(Series.NumSampleValues);
    CBSamplesDefault.Enabled:=False;
    BApplyRandom.Enabled:=True;
  end;
end;

procedure TSeriesEditor.BApplyRandomClick(Sender: TObject);
var tmpNum : Integer;
begin
  Series.DataSource:=nil;

  tmpNum:=TChartSeriesAccess(Series).INumSampleValues;
  if tmpNum=0 then Series.FillSampleValues
              else Series.FillSampleValues(tmpNum);
end;

procedure TSeriesEditor.Button3Click(Sender: TObject);
begin
  Series.Clear;
end;

procedure TSeriesEditor.ChartGrid1SetEditText;
begin
// TODO
end;

procedure TSeriesEditor.CBDataSourcestyleChange(Sender: TObject);

  Procedure CheckOldSource;
  var tmpSource : TTeeSeriesSource;
  begin
    { if the datasource is an internal "Series Source",
      remove and free it ! }
    if Assigned(Series.DataSource) and
       (Series.DataSource is TTeeSeriesSource) and
       (Series.DataSource.Owner=Series) then
    begin
      tmpSource:=TTeeSeriesSource(Series.DataSource);
      tmpSource.Series:=nil;
      tmpSource.Free;
      Series.DataSource:=nil;
      Series.Clear;
    end;

    Series.SetFunction(nil);
  end;

begin
  if Assigned(DataSourceStyle) then
     DataSourceStyle.BApply.Enabled:=False;

  { change datasource form }
  Case CBDataSourceStyle.ItemIndex of
     0: begin  // Manual data. No source.
          CheckOldSource;
          TChartSeriesAccess(Series).ManualData:=True;
        end;
     1: CheckOldSource; // Random values.
  end;

  {$IFNDEF TEELITEEMB}
  PanelManual.Visible:=CBDataSourcestyle.ItemIndex=0;

  if PanelManual.Visible then
  begin
    if not Assigned(FChartGrid) then
    begin
      FChartGrid:=TChartGrid.Create(Self);
      FChartGrid.Align:=TAlignLayout.{$IFDEF D20}Client{$ELSE}alClient{$ENDIF};
      PanelManual.AddObject(FChartGrid);
    end;

    FChartGrid.Series:=Series;
  end;

  PanelFunctions.Visible:=CBDataSourcestyle.ItemIndex=3;
  {$ENDIF}

  PanelRandomSource.Visible:=CBDataSourcestyle.ItemIndex=1;
  PanelSeriesSource.Visible:=CBDataSourcestyle.ItemIndex=2;

  CreateDataSourceForm;
end;

procedure TSeriesEditor.DestroyDataSourceForm;
begin
  if Assigned(DataSourceStyle) and DataSourceStyle.CloseQuery then
     FreeAndNil(DataSourceStyle);
end;

procedure TSeriesEditor.CheckCBSamples;
var tmpNum : Integer;
begin
  tmpNum:=TChartSeriesAccess(Series).INumSampleValues;
  CBSamplesDefault.IsChecked:=(tmpNum=0) or (tmpNum=Series.NumSampleValues);
  CBSamplesDefault.Enabled:=not CBSamplesDefault.IsChecked;
end;

procedure TSeriesEditor.CreateDataSourceForm;

  Procedure EnableButton(AButton:TButton);
  begin
    AButton.Visible:=Assigned(AButton.OnClick);
  end;

  procedure ShowFunctionsPanel;
  {$IFNDEF TEELITEEMB}
  var tmpForm   : TCustomForm;
      tmpSource : TTeeSeriesSourceClass;
      tmpClass  : TComponentClass;
  {$ENDIF}
  begin
    {$IFNDEF TEELITEEMB}
    PanelFunctions.Visible:=True;

    With CBDataSourceStyle do
    if ItemIndex=3 then
       tmpForm:=FunctionEditorClass.Create(Self)
    else
    begin
      tmpSource:=TSourceClassObject(Selected.Data).Source;

      if tmpSource.HasNew then
      begin
        EnableButton(NewDataButton);
        EnableButton(EditDataButton);
        EnableButton(DeleteDataButton);
      end;

      tmpClass:=tmpSource.Editor;

      if Assigned(tmpClass) then
         tmpForm:=TFormClass(tmpClass).Create(Self)
      else
         tmpForm:=nil;
    end;

    if Assigned(tmpForm) then
    begin
      DataSourceStyle:=TBaseSourceEditor(tmpForm);

      DataSourceStyle.Translate;

      EmbeddForm(DataSourceStyle,PanelFunctions);

      DataSourceStyle.TagObject:=Series;

      DataSourceStyle.OnActivate(Self);
      //DataSourceStyle.Activate; <-- XE2

      PanelFunctions.BringToFront;
    end;
    {$ENDIF}
  end;

  procedure ShowSeriesSource;
  begin
    {$IFNDEF TEELITEEMB}
    PanelSeriesSource.Visible:=True;
    CBSeriesSource.Clear;
    TFunctionEditor.FillSeriesSources(nil,CBSeriesSource,Series,False);

    CBSeriesSource.ItemIndex:=CBSeriesSource.ListBox.Items.IndexOfObject(Series.DataSource);

    PanelSeriesSource.BringToFront;

    BClearSource.Enabled:=CBSeriesSource.ItemIndex<>-1;

    BApplySource.Enabled:=True;
    {$ENDIF}
  end;

var tmpNum    : Integer;
begin
  DestroyDataSourceForm;

  if not Assigned(DataSourceStyle) then
  begin
    NewDataButton.Visible:=False;
    EditDataButton.Visible:=False;
    DeleteDataButton.Visible:=False;

    {$IFNDEF TEELITEEMB}
    if Assigned(FChartGrid) then
    begin
      FChartGrid.Visible:=False;
      //ChartGridNavigator1.Visible:=False;
    end;
    {$ENDIF}

    PanelFunctions.Visible:=False;
    PanelRandomSource.Visible:=False;
    PanelSeriesSource.Visible:=False;

    case CBDataSourceStyle.ItemIndex of
      0: begin
           {$IFNDEF TEELITEEMB}
           FChartGrid.Chart:=TCustomChart(Series.ParentChart);
           FChartGrid.Series:=Series;
           FChartGrid.Visible:=True;

           //  ChartGridNavigator1.Visible:=True;
           {$ENDIF}
         end;

      1: begin
           PanelRandomSource.Visible:=True;
           tmpNum:=TChartSeriesAccess(Series).INumSampleValues;

           CheckCBSamples;

           if tmpNum=0 then
              tmpNum:=Series.NumSampleValues;

           ESamples.Text:=IntToStr(tmpNum);

           PanelRandomSource.BringToFront;
         end;

      2: ShowSeriesSource;
    else
       ShowFunctionsPanel;
    end;
  end;
end;

procedure TSeriesEditor.CBSeriesSourceChange(Sender: TObject);
begin
  BApplySource.Enabled:=True;
end;

procedure TSeriesEditor.BApplySourceClick(Sender: TObject);
begin
  Series.DataSource:=CBSeriesSource.Selected.Data as TComponent;
end;

procedure TSeriesEditor.BClearSourceClick(Sender: TObject);
begin
  Series.DataSource:=nil;
  CBSeriesSource.ItemIndex:=-1;
end;

procedure TSeriesEditor.CBPersistentChange(Sender: TObject);
begin
  With Series do
  if CBPersistent.IsChecked then Style:=Style+[tssIsPersistent]
                            else Style:=Style-[tssIsPersistent];
end;

procedure TSeriesEditor.PaintBox1Click(Sender: TObject);
begin
  Series.Color:=EditColor(Self,Series.Color);
  PaintBox1.Repaint;
end;

procedure TSeriesEditor.PaintBox1Paint(Sender: TObject; const Canvas: TCanvas);
begin
  with PaintBox1.Canvas do
  begin
    Fill.Kind:=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};

    Fill.Color:=Self.Fill.Color;
    FillRect(PaintBox1.BoundsRect,0,0,AllCorners,1);

    if Assigned(Series) then
       Fill.Color:=Series.Color;

    FillRect(PaintBox1.BoundsRect,0,0,AllCorners,1);
  end;
end;

procedure TSeriesEditor.CBSeriesChange(Sender: TObject);
var tmpSeries : TChartSeries;
begin
  if not ChangingSeries then
  begin
    tmpSeries:=TChartSeries(CBSeries.Selected.Data);

    if tmpSeries<>LastSeries then
    begin
      RefreshSeries(tmpSeries);
      LastSeries:=tmpSeries;
    end;
  end;
end;

procedure TSeriesEditor.UDArrowDistChangeTracking(Sender: TObject);
begin
  if Assigned(Series) then
     Series.Marks.Callout.Distance:=Round(UDArrowDist.Value);
end;

procedure TSeriesEditor.UDArrowsLengthChangeTracking(Sender: TObject);
begin
  if Assigned(Series) then
     Series.Marks.ArrowLength:=Round(UDArrowsLength.Value);
end;

procedure TSeriesEditor.UDDepthChangeTracking(Sender: TObject);
begin
  if Assigned(Series) then
     Series.Depth:=Round(UDDepth.Value);
end;

procedure TSeriesEditor.UDEveryChangeTracking(Sender: TObject);
begin
  if Assigned(Series) then
     Series.Marks.DrawEvery:=Round(UDEvery.Value);
end;

procedure TSeriesEditor.UDHeadSizeChangeTracking(Sender: TObject);
begin
  if Assigned(Series) then
     Series.Marks.Callout.ArrowHeadSize:=Round(UDHeadSize.Value);
end;

procedure TSeriesEditor.UDMarksAngleChangeTracking(Sender: TObject);
begin
  if Assigned(Series) then
     Series.Marks.Angle:=Round(UDMarksAngle.Value);
end;

class procedure TSeriesEditor.EditMarks(const AOwner: TComponent;
  const AMarks: TSeriesMarks);
begin
  with TSeriesEditor.CreateEditor(AOwner,AMarks.ParentSeries) do
  try
    Panel1.Visible:=False;
    TheTabSheet.Visible:=False;
    TabGeneral.Visible:=False;
    TabDataSource.Visible:=False;

    PageSeries.ActiveTab:=TabMarks;

    ShowModal;
  finally
    Free;
  end;
end;

class procedure TSeriesEditor.Edit(const AOwner: TComponent;
  const ASeries: TChartSeries);
begin
  with TSeriesEditor.CreateEditor(AOwner,ASeries) do
  try
    ShowModal;
  finally
    Free;
  end;
end;

end.
