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

interface

uses
  System.SysUtils, System.UITypes, System.Types, System.Classes,
  {$IFDEF D18}
  System.Generics.Collections,
  {$ENDIF}
  System.Variants, FMX.Types, FMX.Controls, FMX.Forms,
  FMX.ListBox, FMX.Edit, FMX.Memo, FMX.Dialogs, FMX.Layouts, FMX.TabControl,
  FMXTee.Chart, FMXTee.Procs, FMXTee.RadioGroup;

type
  {$IFDEF FPC}
  TEncoding=class
  public
    class var
    Ascii,
    BigEndianUnicode,
    Unicode,
    UTF7,
    UTF8,
    Default : TEncoding;
  end;
  {$ENDIF}

  TTeeExportFormat=class
  protected
    Tag : Integer;
    FFilterIndex : Integer;

    {$IFNDEF TEELITEEMB}
    class function AddItem(AList:TListBox; const AText:String; AFormat:TTeeExportFormat):TListBoxItem;
    {$ENDIF}
    class procedure AddSubFormats(const APanel:TCustomTeePanel;
         const AList:TListBox; var FilterIndex:Integer); virtual; {$IFDEF TEELITEEMB}abstract;{$ENDIF}
    Procedure CheckSize; overload;
    Procedure CheckSize(Width, Height:Integer); overload;
    function CreateProperties(const AFormClass:String):TForm;
    Procedure DoCopyToClipboard; virtual; abstract;
  public
    Panel  : TCustomTeePanel;

    Height : Integer;
    Width  : Integer;

    Constructor Create; virtual;

    procedure CopyToClipboard;
    function Description:String; virtual; abstract;
    function Editor(AOwner:TComponent):TForm; virtual; {$IFDEF TEELITEEMB}abstract;{$ENDIF}
    function FileExtension:String; virtual;
    class function FileFilter:String; virtual; abstract;
    function FileFilterIndex:Integer; virtual;
    procedure IncFileFilterIndex(var FilterIndex: Integer); virtual;
    Procedure SaveToFile(Const FileName:String); overload;
    Procedure SaveToFile(Const FileName:String; const AOptions:TForm); overload;
    class procedure SaveToFile(APanel:TCustomTeePanel; const FileName:String); overload;
    class procedure SaveToFile(APanel:TCustomTeePanel; const FileName:String; const AOptions:TForm; AWidth, AHeight:Integer); overload; virtual;
    Procedure SaveToStream(Stream:TStream); overload; virtual; abstract;
    Procedure SaveToStream(Stream:TStream; const AOptions:TForm); overload; virtual;
  end;

  TExportEditor = class(TForm)
    PageControlType: TTabControl;
    TabPicture: TTabItem;
    SplitPic: TSplitter;
    RGFormat: TGroupBox;
    LBFormat: TListBox;
    PageOptions: TTabControl;
    TabOptions: TTabItem;
    TabSize: TTabItem;
    Label1: TLabel;
    Label2: TLabel;
    LabelPicSize: TLabel;
    UDWidth: TSpinBox;
    UDHeight: TSpinBox;
    CBAspect: TCheckBox;
    CBPicSize: TCheckBox;
    TabNative: TTabItem;
    LabelSize: TLabel;
    CBNativeData: TCheckBox;
    CBFileSize: TCheckBox;
    BViewNative: TButton;
    TabData: TTabItem;
    Panel4: TPanel;
    Splitter1: TSplitter;
    Panel5: TPanel;
    Label3: TLabel;
    CBSeries: TComboBox;
    CBDataPreview: TCheckBox;
    Panel3: TPanel;
    PageDataOptions: TTabControl;
    TabInclude: TTabItem;
    CBIndex: TCheckBox;
    CBLabels: TCheckBox;
    CBHeader: TCheckBox;
    CBColors: TCheckBox;
    TabDataOptions: TTabItem;
    Label4: TLabel;
    Label6: TLabel;
    LabelEncoding: TLabel;
    LabelXML: TLabel;
    CBDelim: TComboBox;
    ECustom: TEdit;
    EQuotes: TEdit;
    CBEncoding: TComboBox;
    CBXMLEncoding: TComboBox;
    PanelPreview: TPanel;
    MemoData: TMemo;
    Panel1: TPanel;
    BCopy: TButton;
    BSave: TButton;
    PanelClose: TPanel;
    BClose: TButton;
    BPreview: TButton;
    RGNativeFormat: TRadioGroup;
    RGDataFormat: TRadioGroup;
    SaveDialogPicture: TSaveDialog;
    SaveDialogNative: TSaveDialog;
    SaveDialogData: TSaveDialog;
    TabFormat: TTabItem;
    CBUseSeriesFormat: TCheckBox;
    Label5: TLabel;
    BFormat: TButton;
    SeriesValueFormat: TComboEdit;
    RBJson: TRadioButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure PageControlTypeChange(Sender: TObject);
    procedure PageOptionsChange(Sender: TObject);
    procedure BViewNativeClick(Sender: TObject);
    procedure CBSeriesChange(Sender: TObject);
    procedure CBDelimChange(Sender: TObject);
    procedure CBXMLEncodingChange(Sender: TObject);
    procedure BCopyClick(Sender: TObject);
    procedure BSaveClick(Sender: TObject);
    procedure BPreviewClick(Sender: TObject);
    procedure LBFormatChange(Sender: TObject);
    procedure RGTextFormatChange(Sender: TObject);
    procedure RGDataFormatChange(Sender: TObject);
    procedure UDHeightChangeTracking(Sender: TObject);
    procedure CBNativeDataChange(Sender: TObject);
    procedure CBFileSizeChange(Sender: TObject);
    procedure CBDataPreviewChange(Sender: TObject);
    procedure CBIndexChange(Sender: TObject);
    procedure CBLabelsChange(Sender: TObject);
    procedure CBHeaderChange(Sender: TObject);
    procedure CBColorsChange(Sender: TObject);
    procedure CBUseSeriesFormatChange(Sender: TObject);
    procedure BFormatClick(Sender: TObject);
    procedure SeriesValueFormatChangeTracking(Sender: TObject);
    procedure CBPicSizeChange(Sender: TObject);
    procedure ECustomTyping(Sender: TObject);
    procedure EQuotesTyping(Sender: TObject);
  private
   { Private declarations }

    Chart : TCustomChart;

    ChangingSize : Boolean;
    FLastForm    : TCommonCustomForm;
    IAspect      : Single;
    IFormats     : Array of TTeeExportFormat;

    Function CanChangeSize:Boolean;
    procedure CheckFileSize;
    function CreateTempFile:String;
    procedure EnableButtons;
    Function GetDataFilterIndex:Integer;
    function GetNativeExtension:String;
    function GetNativeFilter:String;
    Function GetSeparator:Char;
    Function GuessPictureFormat(const FileName:String):TTeeExportFormat;
    function NativeAsString:String;
    Function NativeAsText:Boolean;
    Function PictureFormat:TTeeExportFormat;
    procedure RefreshChart(AChart:TCustomChart);
    Procedure SaveDataToFile(Const FileName:String);
    Procedure SaveNativeToFile(Const FileName:String);
    Procedure SavePictureToFile(Const FileName:String);
    function SelectedEncoding:TEncoding;
    procedure TryPreviewData;
  protected
    Function CreateData:TTeeExportData;
    Function CreateNativeStream:TStream;
    Procedure DoSaveNativeToFile( Const FileName:String;
                                  IncludeData:Boolean);
    Function ExistData:Boolean;
  public
    DataFilter      : String;
    InitDir         : String;
    NativeExtension : String;
    NativeFilter    : String;

   { Public declarations }
    class function CreateEditor(AOwner:TComponent; AChart:TCustomChart):TExportEditor;
    class procedure Edit(AOwner:TComponent; AChart:TCustomChart);
  end;

  TTeeExportFormatClass=class of TTeeExportFormat;

  TTeeExportFormats=class(TList{$IFDEF D18}<TTeeExportFormatClass>{$ENDIF})
  private
    Function Get(Index:Integer):TTeeExportFormatClass;
  public
    class Procedure RegisterFormat(AFormat:TTeeExportFormatClass);
    class Procedure UnRegisterFormat(AFormat:TTeeExportFormatClass);

    property Format[Index:Integer]:TTeeExportFormatClass read Get; default;
  end;

function TeeExportFormats : TTeeExportFormats;

implementation

{$R *.fmx}

uses
  {$IFNDEF FPC}
  System.IOUtils,
  {$ENDIF}
  FMX.Platform, FMXTee.Canvas, FMXTee.Engine, FMXTee.Constants,
  FMXTee.Editor.Export.Bitmap, FMXTee.Editor.Strings, FMXTee.Store,
  FMXTee.Editor.Formatting, FMXTee.Editor.Stroke;

Const
  LastTextFilter=5;

var
  ExportFormats : TTeeExportFormats=nil;

function TeeExportFormats:TTeeExportFormats;
begin
  if not Assigned(ExportFormats) then
     ExportFormats:=TTeeExportFormats.Create;

  result:=ExportFormats;
end;

procedure TExportEditor.FormCreate(Sender: TObject);
begin
  NativeExtension:=TeeMsg_TeeExtension; { default extension is *.tee }

  NativeFilter:=TeeMsg_NativeFilter;  { default "TeeChart files (*.tee)|*.tee" }

  DataFilter:=TeeMsg_TextFilter1+'|'+TeeMsg_TextFilter2+'|'+TeeMsg_TextFilter3+
              '|'+TeeMsg_TextFilter4+'|'+TeeMsg_TextFilter5+'|'+TeeMsg_XMLFilter+
              '|'+TeeMsg_HTMLFilter+'|'+TeeMsg_ExcelFilter;

  PageControlType.ActiveTab:=TabPicture;
  PageOptions.ActiveTab:=TabOptions;
  PageDataOptions.ActiveTab:=TabInclude;
end;

procedure TExportEditor.FormDestroy(Sender: TObject);
var t: Integer;
begin
  FreeAndNil(FLastForm);

  for t := 0 to Length(IFormats)-1 do
      IFormats[t].Free;

  IFormats:=nil;
end;

function TExportEditor.GetDataFilterIndex: Integer;
begin
  if RGDataFormat.ItemIndex=0 then
     result:=CBDelim.ItemIndex+1
  else
     result:=LastTextFilter+RGDataFormat.ItemIndex;
end;

function TExportEditor.GetNativeExtension: String;
begin
  case RGNativeFormat.ItemIndex of
    0: result:=NativeExtension;
    1: result:='txt'; // Do not localize
    2: result:='xml'; // Do not localize
  end;
end;

Function TeeDefaultFilterExtension(const Extension:String):String;
begin
  result:='(*.'+Extension+')|*.'+Extension;
end;

function TExportEditor.GetNativeFilter: String;
begin
  result:=NativeFilter+' '+TeeDefaultFilterExtension(GetNativeExtension);
end;

function TExportEditor.GetSeparator: Char;
begin
  Case CBDelim.ItemIndex of
    0: result:=' ';
    1: result:=TeeTabDelimiter;
    2: result:=',';
    3: result:=';';
  else
    if ECustom.Text='' then
       result:=' '
    else
       result:=ECustom.Text[1];
  end;
end;

function TExportEditor.GuessPictureFormat(
  const FileName: String): TTeeExportFormat;
var Extension : String;
    t         : Integer;
begin
  Extension:=UpperCase(ExtractFileExt(FileName));
  if Copy(Extension,1,1)='.' then
     Delete(Extension,1,1);

  for t:=0 to LBFormat.Items.Count-1 do
  begin
    result:=TTeeExportFormat(LBFormat.ListItems[t].Data);
    if Extension=UpperCase(result.FileExtension) then
       Exit;
  end;

  result:=PictureFormat;
end;

procedure TExportEditor.LBFormatChange(Sender: TObject);
var tmp : TForm;
begin
  FreeAndNil(FLastForm);

  if LBFormat.ItemIndex<>-1 then
  begin
    tmp:=PictureFormat.Editor(Self);

    if Assigned(tmp) then
    begin
      FLastForm:=EmbeddForm(tmp,TabOptions);
      FLastForm.Activate;

      PageOptions.ActiveTab:=TabOptions;
    end
    else
      PageOptions.ActiveTab:=TabSize;

    CheckFileSize;
  end;
end;

procedure TExportEditor.PageControlTypeChange(Sender: TObject);
begin
  EnableButtons;

  if PageControlType.ActiveTab=TabData then
     TryPreviewData;
end;

function NewItem(AOwner:TComponent; const AText:String; AObject:TObject):TListBoxItem;
begin
  result:=TListBoxItem.Create(AOwner);
  result.Data:=AObject;
  result.Text:=AText;
end;

type
  TSeriesAccess=class(TChartSeries);

procedure FillSeriesCombo(AOwner:TComponent; ACombo:TComboBox; AList:TCustomSeriesList);
var t : Integer;
    tmp : TChartSeries;
begin
  with AList do
  for t:=0 to Count-1 do
  begin
    tmp:=Items[t];

    if (((csDesigning in tmp.ComponentState) and
       (not TSeriesAccess(tmp).DontSerialize)) or tmp.ShowInEditor) and
       not TSeriesAccess(tmp).IInternalUse then
           ACombo.AddObject(NewItem(AOwner, SeriesTitleOrName(tmp),tmp));
  end;
end;

procedure TExportEditor.RefreshChart(AChart: TCustomChart);

  procedure FillPictureFormats;
  var t : Integer;
      tmp : TTeeExportFormat;
      tmpIndex : Integer;
  begin
    LBFormat.BeginUpdate;
    try
      LBFormat.Clear;

      tmpIndex:=1;

      for t:=0 to TeeExportFormats.Count-1 do
      begin
        TeeExportFormats[t].AddSubFormats(Chart,LBFormat,tmpIndex);

        with SaveDialogPicture do
        begin
          if Filter<>'' then
             Filter:=Filter+'|';

          Filter:=Filter+TeeExportFormats[t].FileFilter;
        end;
      end;

      // XE2 Upd1 bug: (item0 is always multi-selected)
      //LBFormat.Sorted:=True;
    finally
      LBFormat.EndUpdate;

      SetLength(IFormats, LBFormat.Count);
      for t := 0 to LBFormat.Count-1 do
          IFormats[t]:=LBFormat.Items.Objects[t] as TTeeExportFormat;

      tmp:=GuessPictureFormat('*.PNG'); // Default selected format

      if Assigned(tmp) then
         LBFormat.ItemIndex:=tmp.FFilterIndex-2
      else
         LBFormat.ItemIndex:=0;

      LBFormatChange(Self);
    end;
  end;

begin
  Chart:=AChart;

  CBSeries.Clear;
  CBSeries.AddObject(NewItem(Self, TeeMsg_All,nil));

  FillSeriesCombo(Self, CBSeries, Chart.SeriesList);

  CBSeries.ItemIndex:=0;

  FillPictureFormats;

  UDWidth.Value:=Chart.Width;
  UDHeight.Value:=Chart.Height;

  CBDelim.ItemIndex:=1;
  RGNativeFormat.ItemIndex:=0;
  RGDataFormat.ItemIndex:=0;

  TFormatEditor.AddDefaultFormats(SeriesValueFormat);

  SeriesValueFormat.Enabled:=not CBUseSeriesFormat.IsChecked;
  BFormat.Enabled:=SeriesValueFormat.Enabled;
end;

procedure TExportEditor.PageOptionsChange(Sender: TObject);
begin
  if PageOptions.ActiveTab=TabSize then
     CheckFileSize;
end;

function TExportEditor.ExistData: Boolean;
begin
  result:=(Chart.SeriesCount>0);
end;

procedure TExportEditor.CBPicSizeChange(Sender: TObject);
begin
  CheckFileSize;
end;

procedure TExportEditor.CBNativeDataChange(Sender: TObject);
begin
  CBFileSizeChange(Self);
end;

procedure TExportEditor.CBFileSizeChange(Sender: TObject);
begin
  if CBFileSize.IsChecked then
  with CreateNativeStream do
  try
    LabelSize.Text:=Format(TeeMsg_FileSize,[Size]);
  finally
    Free;
  end
  else
    LabelSize.Text:='?';
end;

procedure TExportEditor.CBHeaderChange(Sender: TObject);
begin
  TryPreviewData;
end;

procedure TExportEditor.BViewNativeClick(Sender: TObject);
var tmp : String;
    tmpSt : TStrings;
begin
  tmp:=NativeAsString;

  tmpSt:=TStringList.Create;
  try
    tmpSt.Text:=tmp;
    TStringsEditor.Edit(Self,tmpSt);
  finally
    tmpSt.Free;
  end;
end;

function TExportEditor.PictureFormat: TTeeExportFormat;
begin
  result:=TTeeExportFormat(LBFormat.Selected.Data);
end;

procedure TExportEditor.CBSeriesChange(Sender: TObject);
begin
  TryPreviewData;
end;

procedure TExportEditor.CBUseSeriesFormatChange(Sender: TObject);
begin
  SeriesValueFormat.Enabled:=not CBUseSeriesFormat.IsChecked;
  BFormat.Enabled:=SeriesValueFormat.Enabled;

  TryPreviewData;
end;

procedure TExportEditor.RGDataFormatChange(Sender: TObject);
begin
  CBDelim.Enabled:=RGDataFormat.ItemIndex=0;
  ECustom.Enabled:=CBDelim.Enabled;

  CBEncoding.Enabled:=(RGDataFormat.ItemIndex=0) or (RGDataFormat.ItemIndex=1)
                       or (RGDataFormat.ItemIndex=2);

  CBXMLEncoding.Enabled:=RGDataFormat.ItemIndex=1;

  EnableButtons;

  TryPreviewData;
end;

function TExportEditor.NativeAsString: String;
var s   : TStringStream;
    tmp : TStream;
begin
  s:=TStringStream.Create('');
  try
    tmp:=CreateNativeStream;
    try
      tmp.Position:=0;
      s.CopyFrom(tmp,tmp.Size);
      result:=s.DataString;
    finally
      tmp.Free;
    end;
  finally
    s.Free;
  end;
end;

Function TExportEditor.NativeAsText:Boolean;
begin
  result:=(RGNativeFormat.ItemIndex>0);
end;

procedure TExportEditor.CheckFileSize;
var tmp : TStream;
begin
  if CBPicSize.IsChecked then
  with PictureFormat do
  begin
    tmp:=TMemoryStream.Create;
    try
      SaveToStream(tmp);
      LabelPicSize.Text:=Format(TeeMsg_FileSize,[tmp.Size]);
    finally
      tmp.Free;
    end;
  end
  else
    LabelPicSize.Text:='?';
end;

procedure TExportEditor.EnableButtons;
begin
  BCopy.Enabled:=(PageControlType.ActiveTab=TabPicture)
                 or
                 ((PageControlType.ActiveTab=TabData) and ExistData)
                 or
                 ((PageControlType.ActiveTab=TabNative) and NativeAsText);

  BSave.Enabled:=BCopy.Enabled or (PageControlType.ActiveTab=TabNative);

  if (PageControlType.ActiveTab=TabData) and (RGDataFormat.ItemIndex=3) then
     BCopy.Enabled:=False;

  //BSend.Enabled:=BSave.Enabled;

  BViewNative.Enabled:=((PageControlType.ActiveTab=TabNative) and NativeAsText);

  BPreview.Enabled:=BCopy.Enabled;
end;

procedure TExportEditor.EQuotesTyping(Sender: TObject);
begin
  TryPreviewData;
end;

procedure TExportEditor.RGTextFormatChange(Sender: TObject);
begin
  EnableButtons;
  CBFileSizeChange(Self);
end;

procedure TExportEditor.SaveDataToFile(const FileName: String);
begin
  TeeChangeCursor(Self,crHourGlass);
  try
    With CreateData do
    try
      SaveToFile(FileName);
    finally
      Free;
    end;
  finally
    TeeChangeCursor(Self,crDefault);
  end;
end;

procedure TExportEditor.SaveNativeToFile(const FileName: String);
var tmp : String;
begin
  TeeChangeCursor(Self,crHourGlass);
  try
    tmp:=ChangeFileExt(FileName,'.'+GetNativeExtension);
    DoSaveNativeToFile(tmp,CBNativeData.IsChecked);
  finally
    TeeChangeCursor(Self,crDefault);
  end;
end;

procedure TExportEditor.SavePictureToFile(const FileName: String);
begin
  TeeChangeCursor(Self,crHourGlass);
  try
    With GuessPictureFormat(FileName) do
    begin
      Width:=Round(UDWidth.Value);
      Height:=Round(UDHeight.Value);
      SaveToFile(FileName);
    end;
  finally
    TeeChangeCursor(Self,crDefault);
  end;
end;

function TExportEditor.SelectedEncoding: TEncoding;
begin
  case CBEncoding.ItemIndex of
    0: result:=TEncoding.Ascii;
    1: result:=TEncoding.BigEndianUnicode;
    3: result:=TEncoding.Unicode;
    4: result:=TEncoding.UTF7;
    5: result:=TEncoding.UTF8;
  else
    result:=TEncoding.Default;
  end;
end;

procedure TExportEditor.SeriesValueFormatChangeTracking(Sender: TObject);
begin
  if not (csLoading in ComponentState) then
     TryPreviewData;
end;

procedure TExportEditor.TryPreviewData;
begin
  MemoData.Visible:=CBDataPreview.IsChecked and (RGDataFormat.ItemIndex<>3);

  if MemoData.Visible then
  begin
    MemoData.Text:='';

    with CreateData do
    try
      MemoData.BeginUpdate;
      try
        MemoData.Lines.Text:=AsString;
      finally
        MemoData.EndUpdate;
      end;
    finally
      Free;
    end;
  end;
end;

procedure TExportEditor.UDHeightChangeTracking(Sender: TObject);
begin
  if CanChangeSize and (not (csLoading in ComponentState)) then
  begin
    ChangingSize:=True;

    if Sender=UDWidth then
       UDHeight.Value:=Round(UDWidth.Value/IAspect)
    else
       UDWidth.Value:=Round(UDHeight.Value*IAspect);

    with PictureFormat do
    begin
      Width:=Round(UDWidth.Value);
      Height:=Round(UDHeight.Value);
    end;

    CheckFileSize;

    ChangingSize:=False;
  end;
end;

function TExportEditor.CanChangeSize: Boolean;
begin
  result:=(not ChangingSize) and
          CBAspect.IsChecked and (IAspect<>0);
end;

procedure TExportEditor.CBColorsChange(Sender: TObject);
begin
  TryPreviewData;
end;

procedure TExportEditor.CBDataPreviewChange(Sender: TObject);
begin
  TryPreviewData;
end;

procedure TExportEditor.CBIndexChange(Sender: TObject);
begin
  TryPreviewData;
end;

procedure TExportEditor.CBLabelsChange(Sender: TObject);
begin
  TryPreviewData;
end;

procedure TExportEditor.CBDelimChange(Sender: TObject);
begin
  TryPreviewData;

  ECustom.Enabled:=CBDelim.ItemIndex=LastTextFilter-1;

  if ECustom.Enabled and ECustom.CanFocus then
     ECustom.SetFocus;
end;

procedure TExportEditor.ECustomTyping(Sender: TObject);
begin
  TryPreviewData;
end;

class procedure TExportEditor.Edit(AOwner: TComponent; AChart: TCustomChart);
begin
  with CreateEditor(AOwner, AChart) do
  try
    PanelClose.Visible:=True;
    ShowModal;
  finally
    Free;
  end;
end;

procedure TExportEditor.CBXMLEncodingChange(Sender: TObject);
begin
  TryPreviewData;
end;

function TExportEditor.CreateData: TTeeExportData;

  Function SelectedSeries:TChartSeries;
  begin
    result:=TChartSeries(CBSeries.Selected.Data);
  end;

begin
  Case RGDataFormat.ItemIndex of
    0: begin
         result:=TSeriesDataText.Create(Chart,SelectedSeries);

         TSeriesDataText(result).TextDelimiter:=GetSeparator;
         TSeriesDataText(result).TextQuotes:=EQuotes.Text; // 7.0

         {$IFDEF D12}
         TSeriesDataText(result).FileEncoding:=SelectedEncoding; // 9.0
         {$ENDIF}
       end;

    1: begin
         result:=TSeriesDataXML.Create(Chart,SelectedSeries);

         TSeriesDataXML(result).Encoding:=CBXMLEncoding.Selected.Text;

         {$IFDEF D12}
         TSeriesDataXML(result).FileEncoding:=SelectedEncoding; // 9.0
         {$ENDIF}
       end;

    2: begin
         result:=TSeriesDataHTML.Create(Chart,SelectedSeries);
         {$IFDEF D12}
         TSeriesDataText(result).FileEncoding:=SelectedEncoding; // 9.0
         {$ENDIF}
       end;

    3: result:=TSeriesDataXLS.Create(Chart,SelectedSeries);

  else
    result:=TSeriesDataJSON.Create(Chart,SelectedSeries);
  end;

  // Configure options
  with TSeriesData(result) do
  begin
    IncludeLabels:=CBLabels.IsChecked;
    IncludeIndex :=CBIndex.IsChecked;
    IncludeHeader:=CBHeader.IsChecked;
    IncludeColors:=CBColors.IsChecked;

    UseSeriesFormat:=CBUseSeriesFormat.IsChecked;

    if not UseSeriesFormat then
       ValueFormat:=LocalToDelphiFormat(SeriesValueFormat.Text);
  end;
end;

class function TExportEditor.CreateEditor(AOwner: TComponent;
  AChart: TCustomChart): TExportEditor;
begin
  result:=TExportEditor.Create(AOwner);
  result.PanelClose.Visible:=False;
  result.RefreshChart(AChart);
end;

Procedure SaveTeeToStream(APanel:TCustomTeePanel; AStream:TStream);
begin
  AStream.WriteComponent(APanel);
end;

function TExportEditor.CreateNativeStream: TStream;
begin
  result:=TMemoryStream.Create;

  case RGNativeFormat.ItemIndex of
    0: SaveChartToStream(Chart,result,CBNativeData.IsChecked,False);
    1: SaveChartToStream(Chart,result,CBNativeData.IsChecked,True);
    2: SaveChartToXMLStream(Chart,result,CBNativeData.IsChecked);
  end;
end;

function TExportEditor.CreateTempFile: String;

  Procedure AdjustExtension(Const Extension:String);
  begin
    result:=ChangeFileExt(result,'.'+Extension);
  end;

  Function GetDataExtension(Index:Integer):String;
  var t  : Integer;
      i  : Integer;
      St : String;
  begin
    St:=SaveDialogData.Filter;

    for t:=1 to (2*Index)-1 do
    begin
      i:=Pos('|',St);
      if i>0 then Delete(St,1,i);
    end;

    i:=Pos('|',St);
    if i>0 then Delete(St,i,Length(St));

    i:=Pos('.',St);
    if i>0 then Delete(St,1,i);

    result:=St;
  end;

  {$IFDEF FPC}
  function GetTempPath:String;
  begin
    result:=GetTempDir;
  end;
  {$ENDIF}

begin
  result:={$IFNDEF FPC}TPath.{$ENDIF}GetTempPath+'\'+TeeMsg_TeeChartPalette;

  if PageControlType.ActiveTab=TabPicture then
  begin
    AdjustExtension(PictureFormat.FileExtension);
    SavePictureToFile(result);
  end
  else
  if PageControlType.ActiveTab=TabNative then
  begin
    AdjustExtension(GetNativeExtension);
    SaveNativeToFile(result);
  end
  else
  if PageControlType.ActiveTab=TabData then
  begin
    AdjustExtension(GetDataExtension(GetDataFilterIndex));
    SaveDataToFile(result);
  end;
end;

Procedure SaveTeeToFile(APanel:TCustomTeePanel; Const AName:String);
Var tmp        : TFileStream;
    OldVisible : Boolean;
begin
  tmp:=TFileStream.Create(AName,fmCreate);
  try
    OldVisible:=APanel.Visible;
    APanel.Visible:=True;
    try
      SaveTeeToStream(APanel,tmp);
    finally
      APanel.Visible:=OldVisible;
    end;
  finally
    tmp.Free;
  end;
end;

procedure TExportEditor.DoSaveNativeToFile(const FileName: String;
  IncludeData: Boolean);
begin
  case RGNativeFormat.ItemIndex of
    0: SaveChartToFile(Chart,FileName,IncludeData,False);
    1: SaveChartToFile(Chart,FileName,IncludeData,True);
    2: SaveChartToXMLFile(Chart,FileName,IncludeData);
  end;
end;

procedure TExportEditor.BCopyClick(Sender: TObject);
begin
  if PageControlType.ActiveTab=TabPicture then
  with PictureFormat do
  begin
    Width:=Round(UDWidth.Value);
    Height:=Round(UDHeight.Value);
    CopyToClipboard;
  end
  else
  if PageControlType.ActiveTab=TabData then
  begin
    TeeChangeCursor(Self,crHourGlass);
    try
      With CreateData do
      try
        CopyToClipboard;
      finally
        Free;
      end;
    finally
      TeeChangeCursor(Self,crDefault);
    end;
  end
  else
  if (PageControlType.ActiveTab=TabNative) and NativeAsText then
     TeeCopyClipboard(NativeAsString);
end;

procedure TExportEditor.BSaveClick(Sender: TObject);
begin
  if PageControlType.ActiveTab=TabPicture then  { as picture... }
  With SaveDialogPicture do
  begin
    With PictureFormat do
    begin
      DefaultExt:=FileExtension;

//      FilterIndex:=FileFilterIndex;

//      if FilterIndex=-1 then
         FilterIndex:=Tag+1;
    end;

    InitialDir:=InitDir;
    FileName:='';
    if Execute then
       SavePictureToFile(FileName);
  end
  else
  if PageControlType.ActiveTab=TabNative then   { as native *.tee file... }
  With SaveDialogNative do
  begin
    InitialDir:=InitDir;
    FileName:='';
    DefaultExt:=GetNativeExtension;
    Filter:=GetNativeFilter;

    if Execute then
       SaveNativeToFile(FileName);
  end
  else
  if PageControlType.ActiveTab=TabData then    { series data... }
  With SaveDialogData do
  begin
    InitialDir:=InitDir;
    FileName:='';
    Filter:=DataFilter;
    FilterIndex:=GetDataFilterIndex;

    if Execute then
    begin
      if FilterIndex<=LastTextFilter then
         RGDataFormat.ItemIndex:=0
      else
         RGDataFormat.ItemIndex:=FilterIndex-LastTextFilter;

      SaveDataToFile(FileName);
    end;
  end;
end;

procedure TExportEditor.BFormatClick(Sender: TObject);
begin
  SeriesValueFormat.Text:=TFormatEditor.Change(Self,SeriesValueFormat.Text);
  TryPreviewData;
end;

procedure TExportEditor.BPreviewClick(Sender: TObject);
var tmpName : String;
begin
  tmpName:=CreateTempFile;

  TeeChangeCursor(Self,crHourGlass);
  try
    TeeGotoURL(Handle,tmpName);
  finally
    TeeChangeCursor(Self,crDefault);
  end;
end;

{ TTeeExportFormats }

function TTeeExportFormats.Get(Index: Integer): TTeeExportFormatClass;
begin
  result:={$IFNDEF D18}TTeeExportFormatClass{$ENDIF}(Items[Index]);
end;

class procedure TTeeExportFormats.RegisterFormat(AFormat: TTeeExportFormatClass);
begin
  TeeExportFormats.Add(AFormat);
end;

class procedure TTeeExportFormats.UnRegisterFormat(AFormat: TTeeExportFormatClass);
begin
  if Assigned(ExportFormats) then
     ExportFormats.Remove(AFormat);
end;

{ TTeeExportFormat }

{$IFNDEF TEELITEEMB}
class procedure TTeeExportFormat.AddSubFormats(const APanel:TCustomTeePanel; const AList:TListBox; var FilterIndex:Integer);
var tmp : TTeeExportFormat;
begin
  tmp:=TTeeExportFormat(NewInstance);
  tmp.Create;
  tmp.Panel:=APanel;
  tmp.IncFileFilterIndex(FilterIndex);
  AddItem(AList,tmp.Description,tmp);
end;
{$ENDIF}

type
  TFormClass=class of TForm;

function TTeeExportFormat.CreateProperties(const AFormClass:String):TForm;
var f : TFormClass;
begin
  f:=TFormClass(TeeGetClass(AFormClass));

  if Assigned(f) then
  begin
    result:=f.Create(nil);
    result.TagObject:=Self;
  end
  else
    result:=nil;
end;

procedure TTeeExportFormat.CheckSize;
begin
  if Width=0 then Width:=Round(Panel.Width);
  if Height=0 then Height:=Round(Panel.Height);
end;

procedure TTeeExportFormat.CheckSize(Width, Height: Integer);
begin
  Self.Width:=Width;
  Self.Height:=Height;
end;

Constructor TTeeExportFormat.Create;
begin
  inherited;
end;

procedure TTeeExportFormat.CopyToClipboard;
begin
  if Assigned(Panel) then
  begin
    //Options;
    CheckSize;
    DoCopyToClipboard;
  end
  else raise Exception.Create(TeeMsg_ExportPanelNotSet);
end;

{$IFNDEF TEELITEEMB}
class function TTeeExportFormat.AddItem(AList:TListBox; const AText:String; AFormat:TTeeExportFormat):TListBoxItem;
begin
  AFormat.Tag:=AList.Count;

  result:=TListBoxItem.Create(AList.Owner);
  result.Text:=StringReplace(AText,'&','',[]);
  result.Data:=AFormat;
  AList.AddObject(result);
end;

function TTeeExportFormat.Editor(AOwner:TComponent):TForm;
begin
  result:=nil;
end;
{$ENDIF}

function TTeeExportFormat.FileExtension: String;
begin
  result:='';
end;

function TTeeExportFormat.FileFilterIndex: Integer;
begin
  result:=FFilterIndex;
end;

procedure TTeeExportFormat.IncFileFilterIndex(var FilterIndex: Integer);
begin
  Inc(FilterIndex);
  FFilterIndex:=FilterIndex;
end;

procedure TTeeExportFormat.SaveToFile(const FileName: String;
  const AOptions: TForm);
var tmpStream : TStream;
begin
  if Assigned(Panel) then
  begin
    //Options;
    CheckSize;

    tmpStream:=TFileStream.Create(Filename, fmCreate);
    try
      if Assigned(AOptions) then
        SaveToStream(tmpStream, AOptions)
      else
        SaveToStream(tmpStream);
    finally
      tmpStream.Free;
    end;
  end
  else raise Exception.Create(TeeMsg_ExportPanelNotSet);
end;

procedure TTeeExportFormat.SaveToFile(const FileName: String);
begin
  SaveToFile(FileName,nil);
end;

class procedure TTeeExportFormat.SaveToFile(APanel: TCustomTeePanel;
  const FileName: String);
begin
  SaveToFile(APanel, FileName, nil, Round(APanel.Width), Round(APanel.Height));
end;

class procedure TTeeExportFormat.SaveToFile(APanel: TCustomTeePanel;
  const FileName: String; const AOptions: TForm; AWidth, AHeight: Integer);
begin
  with Self.Create do
  try
    Panel:=APanel;
    CheckSize(AWidth, AHeight);
    SaveToFile(FileName, AOptions);
  finally
    Free;
  end;
end;

procedure TTeeExportFormat.SaveToStream(Stream: TStream; const AOptions: TForm);
begin
end;

initialization
finalization
  FreeAndNil(ExportFormats);
end.
