{**********************************************}
{   TeeChart and TeeTree Common Export Dialog  }
{   Copyright (c) 1996-2020 by Steema Software }
{**********************************************}
unit TeeExport;
{$I TeeDefs.inc}

interface

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

  {$IFDEF D6}
  Types,
  {$ENDIF}

  {$IFDEF D18}
  System.Generics.Collections,
  {$ENDIF}
  
  Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls, Buttons,

  TeCanvas, TeeProcs, TeeConst;

type
  TeeSeparatorChar=Char;

  TTeeExportFormat=class {$IFDEF D11}abstract{$ENDIF}
  private
    FPanel : TCustomTeePanel;
    FScale : Single;  // Default = 1
  protected
    FFilterIndex : Integer;

    Procedure CheckSize; overload;
    Procedure CheckSize(Width, Height:Integer); overload;
    function CreateProperties(const AFormClass:String):TForm;
    Procedure DoCopyToClipboard; virtual; abstract;
    function FileFilterIndex:Integer; virtual;

    function GetBitmap({$IFNDEF FMX}
                       APixelFormat:Graphics.TPixelFormat=pfDevice;
                       {$ENDIF}
                       ADpi:Integer=0):TBitmap;

    Procedure IncFileFilterIndex(Var FilterIndex:Integer); virtual;
    function ScaledRect:TRect;
    function WantsFilterIndex(Index:Integer):Boolean; virtual;
  public
    Height : Integer;
    Width  : Integer;

    Constructor Create; virtual;
    Destructor Destroy; override;

    Procedure CopyToClipboard;
    function Description:String; virtual; abstract;
    function FileExtension:String; virtual; abstract;
    class function FileFilter:String; virtual;
    function GraphicClass:TGraphicClass; virtual;
    function Options(Check:Boolean=True):TForm; 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;
    Procedure SaveToStream(Stream:TStream); overload; virtual; abstract;
    Procedure SaveToStream(Stream:TStream; const AOptions:TForm); overload; virtual;

    property Panel:TCustomTeePanel read FPanel write FPanel;
    property Scale:Single read FScale write FScale;
  end;

  TTeeExportFormBase = class(TForm)
    SaveDialogPicture: TSaveDialog;
    PageControl1: TPageControl;
    TabPicture: TTabSheet;
    TabData: TTabSheet;
    RGFormat: TGroupBox;
    SaveDialogData: TSaveDialog;
    TabNative: TTabSheet;
    CBNativeData: TCheckBox;
    SaveDialogNative: TSaveDialog;
    LabelSize: TLabel;
    CBFileSize: TCheckBox;
    PageOptions: TPageControl;
    TabOptions: TTabSheet;
    TabSize: TTabSheet;
    Label1: TLabel;
    EWidth: TEdit;
    UDWidth: TUpDown;
    Label2: TLabel;
    EHeight: TEdit;
    UDHeight: TUpDown;
    CBAspect: TCheckBox;
    Panel1: TPanel;
    BCopy: TButton;
    BSave: TButton;
    BSend: TButton;
    LBFormat: TListBox;
    Panel2: TPanel;
    SplitPic: TSplitter;
    CBNativeFormat: TRadioGroup;
    BViewNative: TButton;
    CBPicSize: TCheckBox;
    LabelPicSize: TLabel;
    Panel4: TPanel;
    Panel5: TPanel;
    Label3: TLabel;
    CBSeries: TComboFlat;
    RGText: TRadioGroup;
    Panel3: TPanel;
    PageDataOptions: TPageControl;
    TabInclude: TTabSheet;
    CBIndex: TCheckBox;
    CBLabels: TCheckBox;
    CBHeader: TCheckBox;
    CBColors: TCheckBox;
    TabDataOptions: TTabSheet;
    Label4: TLabel;
    Label6: TLabel;
    LabelEncoding: TLabel;
    LabelXML: TLabel;
    CBDelim: TComboFlat;
    ECustom: TEdit;
    EQuotes: TEdit;
    CBEncoding: TComboFlat;
    CBXMLEncoding: TComboFlat;
    Splitter1: TSplitter;
    PanelPreview: TPanel;
    CBDataPreview: TCheckBox;
    MemoData: TMemo;
    BPreview: TButton;
    BClose: TButton;
    LScale: TLabel;
    CBScale: TComboFlat;
    TabJSON: TTabSheet;
    RGJsonDate: TRadioGroup;
    Edit1: TEdit;
    TabExcel: TTabSheet;
    RGExcelVers: TRadioGroup;
    lblexcel: TLabel;
    procedure BCopyClick(Sender: TObject);
    procedure BSaveClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure RGFormatClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure EWidthChange(Sender: TObject);
    procedure PageControl1Change(Sender: TObject);
    procedure RGTextClick(Sender: TObject);
    procedure CBFileSizeClick(Sender: TObject);
    procedure CBNativeDataClick(Sender: TObject);
    procedure CBDelimChange(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure BSendClick(Sender: TObject);
    procedure Panel5Resize(Sender: TObject);
    procedure CBNativeFormatClick(Sender: TObject);
    procedure BViewNativeClick(Sender: TObject);
    procedure CBPicSizeClick(Sender: TObject);
    procedure PageOptionsChange(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure CBDataPreviewClick(Sender: TObject);
    procedure CBSeriesChange(Sender: TObject);
    procedure CBIndexClick(Sender: TObject);
    procedure ECustomChange(Sender: TObject);
    procedure EQuotesChange(Sender: TObject);
    procedure CBXMLEncodingChange(Sender: TObject);
    procedure SeriesValueFormatChange(Sender: TObject);
    procedure BPreviewClick(Sender: TObject);
    procedure CBScaleChange(Sender: TObject);
    procedure RGJsonDateClick(Sender: TObject);
  private
    { Private declarations }
    ChangingSize : Boolean;
    Formats      : TStrings;
    IAspect      : Double;

    Function CanChangeSize:Boolean;

    procedure CheckFileSize;
    Function GetDataFilterIndex:Integer;
    Function GuessPictureFormat(const FileName:String):TTeeExportFormat;
    Procedure FreeExportFormats;
    function NativeAsString:String;
    Function PictureFormat:TTeeExportFormat;
    Procedure SaveNativeToFile(Const FileName:String);
    Procedure SavePictureToFile(Const FileName:String);
  protected
    Function CreateData:TTeeExportData; virtual;
    Function CreateNativeStream:TStream; virtual;
    function CreateTempFile:String;
    Procedure DoSaveNativeToFile( Const FileName:String;
                                  IncludeData:Boolean); virtual;
    Function ExistData:Boolean; virtual;
    function GetNativeExtension:String;
    function GetNativeFilter:String;
    Function GetSeparator:TeeSeparatorChar;
    Function NativeAsText:Boolean;
    Procedure PrepareOnShow; virtual;
    Procedure SaveDataToFile(Const FileName:String);

    {$IFDEF D12}
    function SelectedEncoding:TEncoding;
    {$ENDIF}

    procedure TryPreviewData;
  public
    { Public declarations }
    ExportPanel     : TCustomTeePanel;
    EmailName       : String;
    NativeFilter    : String;
    NativeExtension : String;
    DataFilter      : String;
    InitDir         : String;

    procedure EnableButtons;
    class function GetTempFolder:String;
  end;

{ Retrieves a native "*.tee" file }
Procedure LoadTeeFromFile(Var APanel:TCustomTeePanel; Const AName:String);
Procedure LoadTeeFromStream(Var APanel:TCustomTeePanel; AStream:TStream);

{ Saves a Chart or TeePanel in native format to a "*.tee" file, stream or string }
Procedure SaveTeeToFile(const APanel:TCustomTeePanel; Const AName:String);
Procedure SaveTeeToStream(const APanel:TCustomTeePanel; const AStream:TStream);
function TeeToString(const APanel:TCustomTeePanel):String;

{ "Tee export formats" }
type
  TTeeExportFormatClass=class of TTeeExportFormat;

Procedure RegisterTeeExportFormat(AFormat:TTeeExportFormatClass);
Procedure UnRegisterTeeExportFormat(AFormat:TTeeExportFormatClass);

{ Show the Save dialog and save to AFormat export format }
{ example: TeeExportSavePanel(TGIFExportFormat,Chart1); }
Procedure TeeExportSavePanel(const AFormat:TTeeExportFormatClass; const APanel:TCustomTeePanel);

procedure TeeFillPictureDialog(const ADialog:TSaveDialog; const APanel:TCustomTeePanel; const AItems:TStrings);

type
  TTeeExportFormats=class(TList) // QC115377 {$IFDEF D18}<TTeeExportFormatClass>{$ENDIF})
  private
    Function Get(Index:Integer):TTeeExportFormatClass;
  public
    property Format[Index:Integer]:TTeeExportFormatClass read Get; default;
  end;

var
  TeeExportFormats : TTeeExportFormats=nil;

// Returns instance of TGraphic class that corresponds to FileExtension (ie: TJPEGImage for 'jpg')
function WithFileExtension(const FileExtension:String):TGraphic;

{ starts the MAPI (eg: Outlook) application with an empty new email
  message with the attachment file "FileName" }
Procedure InternalTeeEmailFile(Const FileName:String; Const Subject:String='TeeChart');

Function GetRegistryHelpPath(Const HelpFile:String):String;

implementation

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

Uses Clipbrd, ExtDlgs, TeeEmfOptions,

     {$IFNDEF LINUX}
     {$IFNDEF LCL}
     MAPI,
     {$ENDIF}
     Registry,
     {$ENDIF}

     {$IFDEF D15}
     IOUtils,
     {$ENDIF}

     TeeHtml, TeeBmpOptions, TeeStringsEditor,

     {$IFDEF MSWINDOWS}
     TeeGDIPlus,
     {$ENDIF}

     TeePenDlg;

Const
  LastTextFilter=5;

  DataXML_Index=1;
  DataExcel_Index=3;
  DataJSON_Index=4;
  DataOpenDoc_Index=5;

function WithFileExtension(const FileExtension:String):TGraphic;
var t : Integer;
    tmp : TTeeExportFormat;
    tmpExt : String;
begin
  result:=nil;

  if not Assigned(TeeExportFormats) then Exit;

  tmpExt:=UpperCase(FileExtension);

  for t:=0 to TeeExportFormats.Count-1 do
  begin
    tmp:=TeeExportFormats[t].Create;
    try
      if '.'+UpperCase(tmp.FileExtension)=tmpExt then
      begin
        if tmp.GraphicClass<>nil then
           result:=tmp.GraphicClass.Create;

        exit;
      end;
    finally
      tmp.Free;
    end;
  end;
end;

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

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

function TeeToString(const APanel:TCustomTeePanel):String;
var m : TStream;
    s : TStringStream;
begin
  m:=TMemoryStream.Create;
  try
    SaveTeeToStream(APanel,m);
    m.Position:=0;

    s:=TStringStream.Create('');
    try
      ObjectBinaryToText(m,s);
      result:=s.DataString;
    finally
      s.Free;
    end;
  finally
    m.Free;
  end;
end;

Procedure LoadTeeFromStream(Var APanel:TCustomTeePanel; AStream:TStream);
begin
  AStream.ReadComponent(APanel);
end;

Procedure LoadTeeFromFile(Var APanel:TCustomTeePanel; Const AName:String);
Var tmp : TFileStream;
begin
  tmp:=TFileStream.Create(AName,fmOpenRead);
  try
    LoadTeeFromStream(APanel,tmp);
  finally
    tmp.Free;
  end;
end;

{ Export Dialog }
procedure TTeeExportFormBase.BCopyClick(Sender: TObject);
begin
  if PageControl1.ActivePage=TabPicture then
  with PictureFormat do
  begin
    Width:=UDWidth.Position;
    Height:=UDHeight.Position;
    CopyToClipboard;
  end
  else
  if PageControl1.ActivePage=TabData then
  begin
    Screen.Cursor:=crHourGlass;
    try
      With CreateData do
      try
        CopyToClipboard;
      finally
        Free;
      end;
    finally
      Screen.Cursor:=crDefault;
    end;
  end
  else
  if (PageControl1.ActivePage=TabNative) and NativeAsText then
     Clipboard.AsText:=NativeAsString;
end;

function TTeeExportFormBase.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;

Procedure TTeeExportFormBase.SaveDataToFile(Const FileName:String);
begin
  Screen.Cursor:=crHourGlass;
  try
    With CreateData do
    try
      SaveToFile(FileName);
    finally
      Free;
    end;
  finally
    Screen.Cursor:=crDefault;
  end;
end;

Function TTeeExportFormBase.CreateData:TTeeExportData;
begin
  result:=nil;
end;

Function TTeeExportFormBase.GetSeparator:TeeSeparatorChar;
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 TTeeExportFormBase.GetDataFilterIndex:Integer;
begin
  if RGText.ItemIndex=0 then result:=CBDelim.ItemIndex+1
                        else result:=LastTextFilter+RGText.ItemIndex;
end;

Procedure TTeeExportFormBase.DoSaveNativeToFile( Const FileName:String;
                                                 IncludeData:Boolean);
begin
  SaveTeeToFile(ExportPanel,FileName);
end;

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

Procedure TTeeExportFormBase.SaveNativeToFile(Const FileName:String);
var tmp : String;
begin
  Screen.Cursor:=crHourGlass;
  try
    tmp:=ChangeFileExt(FileName,'.'+GetNativeExtension);
    DoSaveNativeToFile(tmp,CBNativeData.Checked);
  finally
    Screen.Cursor:=crDefault;
  end;
end;

Procedure TTeeExportFormBase.SavePictureToFile(Const FileName:String);
begin
  Screen.Cursor:=crHourGlass;
  try
    With GuessPictureFormat(FileName) do
    begin
      Width:=UDWidth.Position;
      Height:=UDHeight.Position;
      SaveToFile(FileName);
    end;
  finally
    Screen.Cursor:=crDefault;
  end;
end;

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

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

procedure TTeeExportFormBase.BSaveClick(Sender: TObject);
begin
  if PageControl1.ActivePage=TabPicture then  { as picture... }
  With SaveDialogPicture do
  begin
    With PictureFormat do
    begin
      DefaultExt:=FileExtension;
      FilterIndex:=FileFilterIndex;
    end;

    InitialDir:=InitDir;
    FileName:='';
    if Execute then
       SavePictureToFile(FileName);
  end
  else
  if PageControl1.ActivePage=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 PageControl1.ActivePage=TabData then    { series data... }
  With SaveDialogData do
  begin
    InitialDir:=InitDir;
    FileName:='';
    Filter:=DataFilter;
    FilterIndex:=GetDataFilterIndex;

    if (RGText.ItemIndex=3) and (RGExcelVers.ItemIndex=1) or (RGText.ItemIndex>3) then
      FilterIndex := FilterIndex + 1;      //Excel [>v2010] has two indexed filters

    if Execute then
    begin
      if FilterIndex<=LastTextFilter then
         RGText.ItemIndex:=0;

      SaveDataToFile(FileName);
    end;
  end;
end;

procedure TTeeExportFormBase.FormCreate(Sender: TObject);
Const
  DialogOptions : TOpenOptions = [ ofOverwritePrompt, ofHideReadOnly ];
begin
  Height:=TeeReadIntegerOption('ExportHeight',Height);
  Width:=TeeReadIntegerOption('ExportWidth',Width);

  Formats:=TStringList.Create;

  {$IFDEF D12}
  LabelEncoding.Visible:=True;
  CBEncoding.Visible:=True;
  {$ENDIF}

//  BorderStyle:=TeeBorderStyle;

  ChangingSize:=False;

  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+'|'+TeeMsg_JSONFilter;

  EmailName:=TeeMsg_TeeChartPalette;

  PageControl1.ActivePage:=TabPicture;
  PageDataOptions.ActivePage:=TabInclude;

  CBNativeFormat.ItemIndex:=0;

  SaveDialogPicture.Options:=DialogOptions;
  SaveDialogData.Options:=DialogOptions;
  SaveDialogNative.Options:=DialogOptions;
end;

function IsViewerRegistered(const FileExtension:String):Boolean;
{$IFDEF MSWINDOWS}
var tmp : TRegistry;
begin
  tmp:=TRegistry.Create;

  with tmp do
  try
    RootKey:=HKEY_CLASSES_ROOT;
    result:=KeyExists('.'+FileExtension);

//    if result then
//       if tmp.OpenKey('TeeChart\DefaultIcon',True) then
//          result:=tmp.ReadString('')=ParamStr(0)+',0';
  finally
    Free;
  end;
{$ELSE}
begin
  result:=False;
{$ENDIF}
end;

procedure TTeeExportFormBase.RGFormatClick(Sender: TObject);
var tmp : TForm;
    t   : Integer;
begin
  With TabOptions do
  for t:=0 to ControlCount-1 do
      {$IFDEF LCL}
      Controls[t].Visible:=False;
      {$ELSE}
      Controls[t].Hide;
      {$ENDIF}

  With PictureFormat do
  begin
    tmp:=Options;
    TabOptions.TabVisible:=Assigned(tmp) and (tmp.ControlCount>0);

    if TabOptions.TabVisible then
    begin
      PageOptions.ActivePage:=TabOptions;

      AddFormTo(tmp,TabOptions);
      TeeTranslateControl(tmp);
    end
    else
      PageOptions.ActivePage:=TabSize;

    CheckFileSize;

    BPreview.Enabled:=IsViewerRegistered(FileExtension);
  end;
end;

procedure TeeFillPictureDialog(const ADialog:TSaveDialog; const APanel:TCustomTeePanel; const AItems:TStrings);
var t   : Integer;
    tmp : TTeeExportFormat;
    tmpFilter : Integer;
begin
  if TeeExportFormats<>nil then
  begin
    if ADialog.Filter='' then tmpFilter:=0
                         else tmpFilter:=1;

    for t:=0 to TeeExportFormats.Count-1 do
    begin
      tmp:=TeeExportFormats[t].Create;
      tmp.Panel:=APanel;
      tmp.IncFileFilterIndex(tmpFilter);

      With ADialog do
      begin
        if Filter<>'' then
           Filter:=Filter+'|';
           
        Filter:=Filter+tmp.FileFilter;
      end;

      if Assigned(AItems) then
         AItems.AddObject(ReplaceChar(tmp.Description,'&'),tmp)
      else
         tmp.Free;
    end;
  end;
end;

procedure TTeeExportFormBase.PrepareOnShow;
begin
  TabData.TabVisible:=False;
  CBNativeData.Visible:=False;
end;

procedure TTeeExportFormBase.FormShow(Sender: TObject);

  procedure PrepareXMLEncoding;
  var tmp : String;
  begin
    tmp:=TeeDefaultXMLEncoding;

    if CBXMLEncoding.Items.IndexOf(tmp)=-1 then
       CBXMLEncoding.Items.Add(tmp);

    CBXMLEncoding.ItemIndex:=CBXMLEncoding.Items.IndexOf(tmp);
  end;

  procedure DoInternalPrepare;
  begin
    RGText.ItemIndex:=0;

    with SaveDialogData do Options:=Options+[ofEnableSizing];
    with SaveDialogPicture do Options:=Options+[ofEnableSizing];
    with SaveDialogNative do Options:=Options+[ofEnableSizing];

    if Assigned(ExportPanel) then
    begin
      ChangingSize:=True;
      With ExportPanel do
      begin
        UDWidth.Position :=Width;
        UDHeight.Position:=Height;
        IAspect:=Width/Height;
      end;

      FreeExportFormats;

      SaveDialogPicture.Filter:='';

      TeeFillPictureDialog(SaveDialogPicture,ExportPanel,Formats);

      LBFormat.Items:=Formats;

      CBDelim.ItemIndex:=1;
      LBFormat.ItemIndex:=0;

      RGFormatClick(Self);
      EnableButtons;
      ChangingSize:=False;
    end;

    TeeTranslateControl(Self);

    PrepareXMLEncoding;
  end;

begin
  if not Assigned(ExportPanel) then
     if Tag<>0 then
        ExportPanel:=TCustomTeePanel(Tag);

  DoInternalPrepare;

  // This should be always done, just in case a Series could have been destroyed
  // in between showing this editor twice:
  PrepareOnShow;
end;

Function TTeeExportFormBase.CanChangeSize:Boolean;
begin
  result:=Showing and (not ChangingSize) and
          CBAspect.Checked and (IAspect<>0);
end;

procedure TTeeExportFormBase.EWidthChange(Sender: TObject);
begin
  if CanChangeSize then
  begin
    ChangingSize:=True;

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

    with PictureFormat do
    begin
      Width:=UDWidth.Position;
      Height:=UDHeight.Position;
    end;

    CheckFileSize;

    ChangingSize:=False;
  end;
end;

procedure TTeeExportFormBase.PageControl1Change(Sender: TObject);
begin
  EnableButtons;

  if PageControl1.ActivePage=TabData then
     TryPreviewData;
end;

{$IFNDEF LINUX}
var IsMapiInstalled : Integer=-1;
{$ENDIF}

Function MapiInstalled:Boolean;
{$IFNDEF LINUX}
var tmp : Integer;
{$ENDIF}
begin
  {$IFNDEF LINUX}
  if IsMapiInstalled=-1 then
  begin
    tmp:=TeeLoadLibrary('Mapi32.dll');

    if tmp>0 then
    begin
      IsMapiInstalled:=1;
      TeeFreeLibrary(tmp);
    end
    else
      IsMapiInstalled:=0;
  end;

  result:=IsMapiInstalled=1;

  {$ELSE}

  result:=False;

  {$ENDIF}
end;

Function TTeeExportFormBase.ExistData:Boolean;
begin
  result:=False;
end;

Function TTeeExportFormBase.NativeAsText:Boolean;
begin
  result:=(CBNativeFormat.ItemIndex>0);
end;

procedure TTeeExportFormBase.EnableButtons;
begin
  BCopy.Enabled:=(PageControl1.ActivePage=TabPicture)
                 or
                 ((PageControl1.ActivePage=TabData) and ExistData)
                 or
                 ((PageControl1.ActivePage=TabNative) and NativeAsText);

  BSave.Enabled:=BCopy.Enabled or (PageControl1.ActivePage=TabNative);

  if (PageControl1.ActivePage=TabData) and
     ((RGText.ItemIndex=3) and (RGText.ItemIndex=5)) then
     BCopy.Enabled:=False;

  BSend.Enabled:=BSave.Enabled;

  BViewNative.Enabled:=((PageControl1.ActivePage=TabNative) and NativeAsText);

  BPreview.Enabled:=BCopy.Enabled;
end;

procedure TTeeExportFormBase.RGTextClick(Sender: TObject);
begin
  CBDelim.Enabled:=RGText.ItemIndex=0;
  ECustom.Enabled:=CBDelim.Enabled;

  {$IFDEF D12}
  CBEncoding.Enabled:=(RGText.ItemIndex<>DataExcel_Index); // Not encoding for Excel export
  {$ENDIF}

  CBXMLEncoding.Enabled:=RGText.ItemIndex=DataXML_Index;

  TabJSON.TabVisible:=RGText.ItemIndex=DataJSON_Index;
  TabExcel.TabVisible:=RGText.ItemIndex=DataExcel_Index;

  if (TabExcel.TabVisible) then
     PageDataOptions.TabIndex:=3
  else
     PageDataOptions.TabIndex:=0;

  EnableButtons;

  TryPreviewData;
end;

Function TTeeExportFormBase.CreateNativeStream:TStream;

  procedure ConvertToText;
  var tmp : TMemoryStream;
  begin
    result.Position:=0;
    tmp:=TMemoryStream.Create;
    ObjectBinaryToText(result,tmp);

    result.Free;
    result:=tmp;
  end;

begin
  result:=TMemoryStream.Create;
  SaveTeeToStream(ExportPanel,result);

  case CBNativeFormat.ItemIndex of
    1: ConvertToText;
    2: begin
         ConvertToText;
         result.Position:=0;
         ConvertTextToXML(result);
       end;
  end;
end;

procedure TTeeExportFormBase.CBFileSizeClick(Sender: TObject);
begin
  if CBFileSize.Checked then
  with CreateNativeStream do
  try
    LabelSize.Caption:=Format(TeeMsg_FileSize,[Size]);
  finally
    Free;
  end
  else LabelSize.Caption:='?';
end;

procedure TTeeExportFormBase.CBNativeDataClick(Sender: TObject);
begin
  CBFileSizeClick(Self);
end;

procedure TTeeExportFormBase.CBDelimChange(Sender: TObject);
begin
  TryPreviewData;
  
  ECustom.Enabled:=CBDelim.ItemIndex=LastTextFilter-1;

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

Procedure TTeeExportFormBase.FreeExportFormats;
var t : Integer;
begin
  if Assigned(Formats) then
  begin
    // Workaround.  We can't use LBFormat here, it comes empty already !
    for t:=0 to Formats.Count-1 do
        TTeeExportFormat(Formats.Objects[t]).Free;

    Formats.Clear;
  end;

  if not (csDestroying in LBFormat.ComponentState) then
     LBFormat.Items.Clear;
end;

procedure TTeeExportFormBase.FormDestroy(Sender: TObject);
begin
  FreeExportFormats;
  Formats.Free;
end;

function TTeeExportFormBase.PictureFormat: TTeeExportFormat;
begin
  result:=TTeeExportFormat(LBFormat.Items.Objects[LBFormat.ItemIndex]);
end;

Function TTeeExportFormBase.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.Items.Objects[t]);
    if Extension=UpperCase(result.FileExtension) then
       Exit;
  end;

  result:=PictureFormat;
end;

{$IFDEF MSWINDOWS}
function WindowsMajorVersion:Integer;
var
  OSVI: TOSVersionInfo;
begin
  FillChar(OSVI, SizeOf(OSVI), 0);
  OSVI.dwOSVersionInfoSize := SizeOf(OSVI);
  if not GetVersionEx(OSVI) then
     raise Exception.Create('Error calling Windows GetVersionEx to obtain version number.');

  result:=OSVI.dwMajorVersion;
end;

// Fixed.
// See http://bugs.teechart.net/show_bug.cgi?id=114

var
  GetLongPathName:function(lpszShortPath: PChar; lpszLongPath: PChar;
                           cchBuffer: DWORD): DWORD; stdcall;

  TeeKernel32:THandle=0;

function LongPathName(const FileName:String):String;
var
  BufferSize: DWord;
begin
  // "GetLongPathNameA/W" available only for Win2000, WinXP or Win2003, or newer !
  if (Win32Platform=VER_PLATFORM_WIN32_NT) and (WindowsMajorVersion>=5) then
  begin

    if not Assigned(GetLongPathName) then
      if TeeKernel32=0 then
      begin
        TeeKernel32:=TeeLoadLibrary(kernel32);

        if TeeKernel32<>0 then
           @GetLongPathName:=GetProcAddress(TeeKernel32,{$IFDEF UNICODE}'GetLongPathNameW'{$ELSE}'GetLongPathNameA'{$ENDIF});
      end;

    BufferSize := GetLongPathName(PChar(FileName), PChar(result), 0);
    SetLength(Result, BufferSize);
    GetLongPathName(PChar(FileName), PChar(Result), BufferSize);

    if BufferSize>0 then
       SetLength(result,BufferSize-1);
  end
  else
    result:=FileName;
end;
{$ENDIF}

type
  TPathName={$IFDEF D12}
            String
            {$ELSE}
            Array[0..MAX_PATH] of Char
            {$ENDIF};

class function TTeeExportFormBase.GetTempFolder:String;
{$IFNDEF D15}
{$IFNDEF LINUX}
var tmpPath : TPathName;
    {$IFDEF D12}
    tmpLen  : Integer;
    {$ENDIF}
{$ENDIF}
{$ENDIF}
begin
  {$IFDEF D15}
  result:=TPath.GetTempPath;
  {$ELSE}

  {$IFDEF LINUX}
  result:='';
  {$ELSE}

  {$IFDEF D12}
  tmpLen:=GetTempPath(0,nil);
  SetLength(tmpPath,tmpLen-1);
  {$ENDIF}

  if GetTempPath(MAX_PATH,{$IFDEF D12}PWideChar{$ENDIF}(tmpPath))=0 then
     Raise Exception.Create(TeeMsg_CanNotFindTempPath);

  {$IFDEF D12}
  result:=tmpPath;
  {$ELSE}
  result:=StrPas(tmpPath);
  {$ENDIF}
  {$ENDIF}

  {$IFNDEF D12}
  {$IFDEF MSWINDOWS}
  result:=LongPathName(result);
  {$ENDIF}
  {$ENDIF}
  {$ENDIF}
end;

function TTeeExportFormBase.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;

begin
  result:=GetTempFolder;

  if Copy(result,Length(result),1)<>'\' then
     result:=result+'\';

  result:=result+EmailName;

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

procedure TTeeExportFormBase.BSendClick(Sender: TObject);
{$IFNDEF LINUX}
var tmpName : String;
{$ENDIF}
begin
  {$IFNDEF LINUX}
  if IsMapiInstalled=-1 then
     MapiInstalled;

  if IsMapiInstalled=1 then
  begin
    tmpName:=CreateTempFile;
    InternalTeeEmailFile(tmpName,EmailName);
    DeleteFile(tmpName);
  end
  else
  {$ENDIF}
    BSend.Enabled:=False;
end;

{ TTeeExportFormat }

Constructor TTeeExportFormat.Create;
begin
  inherited;
  FScale:=1;
end;

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

{$IFDEF FMX}
type
  TFormClass=class of TForm;
{$ENDIF}

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

  if Assigned(f) then
  begin
    result:=f.Create(nil);

    {$IFDEF FMX}
    result.TagObject:=Self;
    {$ELSE}
    result.Tag:=ObjectToTag(Self);
    {$ENDIF}
  end
  else
    result:=nil;
end;

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

Destructor TTeeExportFormat.Destroy;
begin
  if Assigned(Options(False)) then Options.Free;
  inherited;
end;

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

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

function TTeeExportFormat.Options(Check:Boolean=True):TForm;
begin
  result:=nil;
end;

procedure TTeeExportFormat.SaveToFile(const FileName: String;
  const AOptions: TForm);
var tmpStream : TStream;
begin
  if Assigned(Panel) then
  begin
    {$IFNDEF TEEOCX} // Revise for 5.04
    if not Assigned(AOptions) then
       Options;
    {$ENDIF}

    CheckSize;
    tmpStream:=TFileStream.Create(Filename, Classes.fmCreate);
    try
      if Assigned(AOptions) then
        SaveToStream(tmpStream, AOptions)
      else
        SaveToStream(tmpStream);
    finally
      tmpStream.Free;
    end;
  end
  else raise Exception.Create(TeeMsg_ExportPanelNotSet);
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
//empty for vcl.net compatibility
end;

function TTeeExportFormat.GraphicClass:TGraphicClass; 
begin
  result:=nil;
end;

class function TTeeExportFormat.FileFilter:String;
begin
  result:='';
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, APanel.Width, APanel.Height);
end;

function TTeeExportFormat.ScaledRect:TRect;
var tmp : Single;
begin
  tmp:=FScale;
  if tmp<=0 then tmp:=1;
  
  result:=TeeRect(Round(tmp*Width),Round(tmp*Height));
end;

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

function TTeeExportFormat.WantsFilterIndex(Index: Integer): Boolean;
begin
  result:=FileFilterIndex=Index;
end;

{ TTeeExportFormats }

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

{ Tools }
Procedure RegisterTeeExportFormat(AFormat:TTeeExportFormatClass);
begin
  if TeeExportFormats=nil then
     TeeExportFormats:=TTeeExportFormats.Create;

  if TeeExportFormats.IndexOf(AFormat)=-1 then
     TeeExportFormats.Add(AFormat);
end;

Procedure UnRegisterTeeExportFormat(AFormat:TTeeExportFormatClass);
begin
  if Assigned(TeeExportFormats) then
     TeeExportFormats.Remove(AFormat);
end;

Procedure TeeExportSavePanel(const AFormat:TTeeExportFormatClass; const APanel:TCustomTeePanel);
begin
  with AFormat.Create do
  try
    Panel:=APanel;

    With TSaveDialog.Create(nil) do
    try
      Filter:=FileFilter;
      DefaultExt:=FileExtension;
      Options:=Options+[ofOverwritePrompt];

      if Execute then
         SaveToFile(FileName);
    finally
      Free;
    end;
  finally
    Free;
  end;
end;

{$IFNDEF LINUX}
{$IFNDEF LCL}
{$DEFINE USE_MAPI}
{$ENDIF}
{$ENDIF}

Procedure InternalTeeEmailFile(Const FileName:String; Const Subject:String='TeeChart');
{$IFDEF USE_MAPI}

  function MAPIErrorText(Code:Cardinal):String;
  begin
    case Code of
      3: result:=' (Cannot Login)';
    else
      result:='';
    end;
  end;

var tmpName2     : String;
    MapiMessage  : TMapiMessage;
    MapiFileDesc : TMapiFileDesc;
    MError       : Cardinal;

    tmpPath      : String;

    {$IFDEF D12}
    tmpAnsiPath    : AnsiString;
    tmpAnsiName    : AnsiString;
    tmpAnsiSubject : AnsiString;
    {$ENDIF}

{$ENDIF}
begin
  {$IFDEF USE_MAPI}
  With MapiFileDesc do
  begin
    ulReserved:=0;
    flFlags:=0;
    nPosition:=Cardinal(-1);

    tmpPath:=FileName;

    {$IFDEF D12}
    tmpAnsiPath:=AnsiString(tmpPath);
    lpszPathName:=PAnsiChar(tmpAnsiPath);
    {$ELSE}
    lpszPathName:=PChar(tmpPath);
    {$ENDIF}

    tmpName2:=ExtractFileName(FileName);

    {$IFDEF D12}
    tmpAnsiName:=AnsiString(tmpName2);
    lpszFileName:=PAnsiChar(tmpAnsiName);
    {$ELSE}
    lpszFileName:=PChar(tmpName2);
    {$ENDIF}

    lpFileType:=nil;
  end;

  with MapiMessage do
  begin
    ulReserved := 0;

    {$IFDEF D12}
    tmpAnsiSubject:=AnsiString(Subject);
    lpszSubject := PAnsiChar(tmpAnsiSubject);
    {$ELSE}
    lpszSubject := PChar(Subject);
    {$ENDIF}

    lpszNoteText := nil;
    lpszMessageType := nil;
    lpszDateReceived := nil;
    lpszConversationID := nil;

    flFlags := 0;
    lpOriginator := nil;
    nRecipCount := 0;
    lpRecips := nil;
    nFileCount := 1;

    //TODO: IntPtr?
    lpFiles := @MapiFileDesc;
  end;

  MError:=MapiSendMail(0, Application.Handle, MapiMessage,
                       MAPI_DIALOG or MAPI_LOGON_UI or MAPI_NEW_SESSION, 0);

  if MError<>0 then
     if MError<>MAPI_E_USER_ABORT then
        raise Exception.CreateFmt(TeeMsg_CanNotEmailChart+MAPIErrorText(MError),[MError]);
  {$ENDIF}
end;

Function GetRegistryHelpPath(Const HelpFile:String):String;
Const WindowsHelp='SOFTWARE\Microsoft\Windows\Help';
begin
  result:='';
  {$IFNDEF LINUX}
  With TRegistry.Create do
  try
    RootKey:=HKEY_LOCAL_MACHINE;
    if OpenKeyReadOnly(WindowsHelp) then
       result:=ReadString(HelpFile)+'\'+HelpFile;
  finally
    Free;
  end;
  {$ENDIF}
end;

procedure TTeeExportFormBase.Panel5Resize(Sender: TObject);
begin
  CBSeries.Width:=Panel5.Width-14;
end;

procedure TTeeExportFormBase.CBNativeFormatClick(Sender: TObject);
begin
  EnableButtons;
  CBFileSizeClick(Self);
end;

procedure TTeeExportFormBase.BViewNativeClick(Sender: TObject);
var tmp : String;
begin
  tmp:=NativeAsString;
  TeeEditStrings(Self,tmp,True);
end;

procedure TTeeExportFormBase.CBPicSizeClick(Sender: TObject);
begin
  CheckFileSize;
end;

procedure TTeeExportFormBase.CheckFileSize;
var tmp : TStream;
begin
  if CBPicSize.Checked then
  with PictureFormat do
  begin
    tmp:=TMemoryStream.Create;
    try
      SaveToStream(tmp);
      LabelPicSize.Caption:=FileSizeToStr(tmp.Size);
    finally
      tmp.Free;
    end;
  end
  else
    LabelPicSize.Caption:='?';
end;

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

procedure TTeeExportFormBase.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  TeeSaveIntegerOption('ExportHeight',Height);
  TeeSaveIntegerOption('ExportWidth',Width);
end;

procedure TTeeExportFormBase.CBDataPreviewClick(Sender: TObject);
begin
  TryPreviewData;
end;

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

{$IFDEF D12}
function TTeeExportFormBase.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;
{$ENDIF}

procedure TTeeExportFormBase.TryPreviewData;
begin
  MemoData.Visible:=CBDataPreview.Checked and
                    (RGText.ItemIndex<>DataExcel_Index) and
                    (RGText.ItemIndex<>DataOpenDoc_Index);

  if MemoData.Visible then
  begin
    MemoData.Clear;

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

procedure TTeeExportFormBase.CBIndexClick(Sender: TObject);
begin
  TryPreviewData;
end;

procedure TTeeExportFormBase.ECustomChange(Sender: TObject);
begin
  TryPreviewData;
end;

procedure TTeeExportFormBase.EQuotesChange(Sender: TObject);
begin
  TryPreviewData;
end;

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

procedure TTeeExportFormBase.SeriesValueFormatChange(Sender: TObject);
begin
  TryPreviewData;
end;

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

  Screen.Cursor:=crHourGlass;
  try
    TeeGotoURL(Handle,tmpName);
  finally
    Screen.Cursor:=crDefault;
  end;
end;

procedure TTeeExportFormBase.CBScaleChange(Sender: TObject);
begin
  PictureFormat.Scale:=StrToFloat(CBScale.Text);
  CheckFileSize;
end;

function TTeeExportFormat.GetBitmap({$IFNDEF FMX}
                       APixelFormat:Graphics.TPixelFormat=pfDevice;
                       {$ENDIF}
                       ADpi:Integer=0):TBitmap;
var tmpBitmap : TBitmap;
begin
  tmpBitmap:=Panel.TeeCreateBitmap(Panel.Color,ScaledRect,APixelFormat,ADPI);

  if Scale<>1 then
  begin
    result:=TBitmap.Create;
    result.PixelFormat:=APixelFormat;

    TeeSetBitmapSize(result,Width,Height);

    {$IFDEF MSWINDOWS}
    TGDIPlusCanvas.ResizeBitmap(tmpBitmap,result);
    {$ELSE}
    result.Canvas.StretchDraw(TeeRect(0,0,Width,Height),tmpBitmap);
    {$ENDIF}

    if ADpi>0 then
       TeeSetBitmapDPI(result, ADpi);

    tmpBitmap.Free;
  end
  else
    result:=tmpBitmap;
end;

procedure TTeeExportFormBase.RGJsonDateClick(Sender: TObject);
begin
  TryPreviewData;
end;

initialization
  TeeHtml.GraphicFileExtension:=WithFileExtension;
finalization
  FreeAndNil(TeeExportFormats);
end.
