{*********************************************}
{  TeeChart and TeeTree for Adobe Flex/Flash  }
{                                             }
{  Copyright (c) 2007-2020 by Steema Software }
{  All Rights Reserved                        }
{*********************************************}
unit TeeFlexCanvas;
{$I TeeDefs.inc}

interface

uses {$IFNDEF LINUX}
     Windows,
     {$ENDIF}
     Classes, SysUtils, TypInfo,
     {$IFDEF D6}
     StrUtils,
     {$ENDIF}

     {$IFDEF D17}
     System.UITypes,
     {$ENDIF}

     Graphics, Forms, Buttons, Controls, StdCtrls, ExtCtrls, Clipbrd, ComCtrls,

     {$IFNDEF D6}
     FileCtrl,
     {$ENDIF}

     {$IFDEF D6}
     Types,
     {$ENDIF}
     TeeConst, TeCanvas, TeeProcs, TeEngine, TeeExport, TeeAnimations;

type
  TFlexCanvas=class(TTeeNeutralCanvas, ICanvasHyperlinks, ICanvasToolTips)
  private
    FX           : Integer;
    FY           : Integer;
    FStrings     : TStrings;

    IAddedInitApp : Boolean;
    IApplication : Integer;
    IIdent       : String;
    IItems       : TStrings;
    IImageID     : Integer;
    IPanel       : TCustomTeePanel;
    IPath        : TStrings;
    IScript      : Integer;
    IStyle       : Integer;
    ITreeMode    : Boolean;

    function AddAnimation(const ID:String):String;
    procedure AddAnimationTypes;
    procedure AddMarksTip;
    procedure AddItem(const S:String); {$IFDEF D10}inline;{$ENDIF}
    procedure AddTag(const ATag,AText:String; Finish:Boolean=True);

    function CalcID(const Prefix:String):String;
    function FlexAlpha:String; overload;
    function FlexAlpha(const Alpha:TTeeTransparency):String; overload;

    function FlexBrush:String;
    function FlexFloatToStr(const Value:Double):String;

    function FlexFont(AFont:TFont; AColor:TColor=clDefault):String;
    function FlexGradient(const Gradient:TCustomTeeGradient):String; overload;
    function FlexGradient(const StartColor,MidColor,EndColor:TColor;
                          Direction:TGradientDirection;
                          Balance: Integer):String; overload;
    function FlexPen:String;

    function FlexPoints(const Points: Array of TPoint):String;
    function FlexPosition(x,y:Integer):String;
    function FlexSize(w,h:Integer):String; overload;
    function FlexSize(const R:TRect):String; overload;
    function ImageFileName(const Graphic:TGraphic):String;
  protected
    { Protected declarations }

    // ICanvasHyperlinks
    procedure AddLink(x,y:Integer; const Text,URL,Hint:String);

    // ICanvasToolTips
    procedure AddToolTip(const Entity,ToolTip:String);

    function BeginEntity(const Entity:String; const Visual:TVisualBlock=nil; const Transform:TTeeTransform=nil):TVisualBlock; override;
    procedure EndEntity; override;

    { 2d }
    procedure SetPixel(X, Y: Integer; Value: TColor); override;

    { 3d }
    procedure SetPixel3D(X,Y,Z:Integer; Value: TColor); override;
  public
    { Public declarations }
    EmbeddImages : Boolean;
    ImagePath    : String;

    Constructor CreateText(const Panel:TCustomTeePanel; const AStrings:TStrings);
    Destructor Destroy; override;

    Function InitWindow( const DestCanvas:TCanvas;
                         const A3DOptions:TView3DOptions;
                         ABackColor:TColor;
                         Is3D:Boolean;
                         Const UserRect:TRect):TRect; override;

    procedure Arc(const Left, Top, Right, Bottom, StartX, StartY, EndX, EndY: Integer); override;
    procedure Draw(X, Y: Integer; const Graphic: TGraphic); override;
    procedure FillRect(const Rect: TRect); override;
    procedure Ellipse(const X1, Y1, X2, Y2: Integer); override;
    procedure LineTo(const X,Y:Integer); override;
    procedure MoveTo(const X,Y:Integer); override;
    procedure Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); override;
    procedure Rectangle(const X0,Y0,X1,Y1:Integer); reintroduce; override;
    procedure RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer); override;
    procedure StretchDraw(const Rect: TRect; const Graphic: TGraphic); override;
    Procedure TextOut(X,Y:Integer; const Text:String); override;

    Procedure ClipEllipse(Const Rect:TRect; Inverted:Boolean=False); override;
    procedure ClipRectangle(Const Rect:TRect); override;
    procedure UnClipRectangle; override;

    Procedure GradientFill( Const Rect:TRect;
                            StartColor,EndColor:TColor;
                            Direction:TGradientDirection;
                            Balance:Integer=50;
                            RadialX:Integer=0;
                            RadialY:Integer=0); override;

    procedure RotateLabel(x,y:Integer; Const St:String; RotDegree:Double;
                          AllowHTML:Boolean=False); override;
    Procedure Polygon({$IFDEF D6}const{$ENDIF} Points: array of TPoint); override;
    Procedure Polyline({$IFDEF D6}const{$ENDIF} Points:Array of TPoint); override;

    Procedure ShowImage(const DestCanvas,DefaultCanvas:TCanvas; Const UserRect:TRect); override;

    { 3d }
    Procedure TextOut3D(X,Y,Z:Integer; const Text:String; AllowHTML:Boolean=False); override;
  end;

  TFlexOptions = class(TForm)
    PageControl1: TPageControl;
    TabOptions: TTabSheet;
    TabSheet2: TTabSheet;
    CBDelete: TCheckBox;
    CBEmbedd: TCheckBox;
    CBDebugMode: TCheckBox;
    CBStatic: TCheckBox;
    Label1: TLabel;
    SpeedButton1: TSpeedButton;
    Label2: TLabel;
    SpeedButton2: TSpeedButton;
    BCompile: TButton;
    ETemp: TEdit;
    EFlex: TEdit;
    CBPreview: TCheckBox;
    CBTreeMode: TCheckBox;
    CBFontAntialias: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
    procedure ETempChange(Sender: TObject);
    procedure EFlexChange(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure BCompileClick(Sender: TObject);
  private
    { Private declarations }

    IReadingOptions: Boolean;

    procedure CheckCompile;
    function Compile(const TargetFile:String):Boolean; overload;
    procedure ReadOptions;
  public
    { Public declarations }
    Panel : TCustomTeePanel;
    SilentMode : Boolean;

    procedure Compile(AWidth, AHeight: Integer; const FileName: String); overload;

    function Compile(const Strings:TStrings; AWidth, AHeight: Integer;
      const FileName: String):Boolean; overload;

    class procedure Compile(const AChart:TCustomTeePanel;
                            AWidth, AHeight:Integer;
                            const FlexPath, TempPath, FileName:String;
                            Preview:Boolean=True); overload;
  end;

  TFlexExportFormat=class(TTeeExportFormat)
  private
    Procedure CheckProperties;
  protected
    FProperties : TFlexOptions;
    Procedure DoCopyToClipboard; override;
  public
    EmbeddImages : Boolean;
    ImagePath    : String;

    function Compile(const TargetFile:String):Boolean;
    function Description:String; override;
    function FileExtension:String; override;
    class function FileFilter:String; override;
    Function FlexMXML:TStringList;
    Function Options(Check:Boolean=True):TForm; override;
    Procedure SaveToStream(Stream:TStream); override;
  end;

procedure TeeSaveToFlexFile( const APanel:TCustomTeePanel; const FileName: WideString;
                             AWidth:Integer=0; AHeight:Integer=0;
                             EmbededImages:Boolean=True);

implementation

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

{$R TeeFlexCanvas.res}

uses
  {$IFDEF LCL}
  jpeglib,
  {$ELSE} 
  jpeg,
  {$ENDIF}

  Math,

  TeeDosCommand, TeeStringsEditor, TeePenDlg;

Function ColorInternal(AColor:TColor):String;
begin
  //AColor:=ColorToRGB(AColor);

  case AColor of
    clBlack: result:='black';
    clWhite: result:='white';
    clRed:   result:='red';
    clGreen: result:='green';
    clBlue:  result:='blue';
   clYellow: result:='yellow';
    clGray:  result:='gray';
    clNavy:  result:='navy';
    clOlive: result:='olive';
    clLime:  result:='lime';
    clTeal:  result:='teal';
  clSilver:  result:='silver';
  clPurple:  result:='purple';
 clFuchsia:  result:='fuchsia';
  clMaroon:  result:='maroon';
  else
  begin
    result:='0x'+IntToHex(GetRValue(AColor),2)+
                IntToHex(GetGValue(AColor),2)+
                IntToHex(GetBValue(AColor),2);

//    if result='#000000' then result:='#0';
  end;
  end;
end;

Function FlexColor(const AColor:TColor):String;
begin
  result:='"'+ColorInternal(AColor)+'"';
end;

{ TFlexCanvas }
Constructor TFlexCanvas.CreateText(const Panel:TCustomTeePanel; const AStrings:TStrings);
begin
  Create;

  IPanel:=Panel;
  EmbeddImages:=True;
  FStrings:=AStrings;
  UseBuffer:=False;

  SupportsID:=True;
  FSupportsAlpha:=True;

  IPath:=TStringList.Create;

  IItems:=TStringList.Create;
  TStringList(IItems).Sorted:=True;

  IImageID:=0;
end;

Destructor TFlexCanvas.Destroy;
begin
  IItems.Free;
  IPath.Free;
  inherited;
end;

procedure TFlexCanvas.Arc(const Left, Top, Right, Bottom, StartX, StartY,
  EndX, EndY: Integer);
var StartAngle,
    EndAngle   : Single;
begin
  if Pen.Style<>psClear then
  begin
    CalcArcAngles(Left,Top,Right,Bottom,
                  StartX,StartY,EndX,EndY,StartAngle,EndAngle);

    AddTag('tee:Arc',
      'x0="'+IntToStr(Left)+'" y0="'+IntToStr(Top)+'" '+
      'x1="'+IntToStr(Right)+'" y1="'+IntToStr(Bottom)+'" '+
      'startAngle="'+FlexFloatToStr(StartAngle)+
      '" endAngle="'+FlexFloatToStr(EndAngle)+'" '+
      FlexAlpha+FlexPen);
  end;
end;

procedure TFlexCanvas.ClipEllipse(const Rect: TRect; Inverted:Boolean=False);
begin
// TODO
end;

procedure TFlexCanvas.ClipRectangle(const Rect: TRect);
begin
// TODO
end;

function TFlexCanvas.ImageFileName(const Graphic:TGraphic):String;

  procedure CalcResult(const tmp:TGraphic);
  var tmpExt : String;
  begin
    tmpExt:=GraphicExtension(TGraphicClass(tmp.ClassType));

    result:='TeeChart_Flex_Temp_'+IntToStr(IImageID)+'.'+tmpExt;
    Inc(IImageID);

    tmp.SaveToFile(ImagePath+'\'+result);
  end;

var tmp : TGraphic;
begin
  if TGraphicClass(Graphic.ClassType)=TBitmap then
  begin
    // Bitmap (*.bmp) not supported by Flash.
    // Save it to jpeg / png...

    tmp:=TJPEGImage.Create;
    try
      tmp.Assign(Graphic);

      CalcResult(tmp);
    finally
      tmp.Free;
    end;
  end
  else
    CalcResult(Graphic);
end;

function TFlexCanvas.FlexPosition(x,y:Integer):String;
begin
//  result:='x="'+FloatToStr(x*100/IPanel.Width)+'%" y="'+FloatToStr(y*100/IPanel.Height)+'%"';

  result:='x="'+IntToStr(x)+'" y="'+IntToStr(y)+'"';
end;

procedure TFlexCanvas.Draw(X, Y: Integer; const Graphic: TGraphic);

  function ImageSource:String;
  begin
    if EmbeddImages then
       result:='@Embed('''+ImageFileName(Graphic)+''')'
    else
       result:=ImageFileName(Graphic);
  end;

begin
  if Assigned(Graphic) then
     AddTag('mx:Image',FlexPosition(x,y)+' source="'+ImageSource+'"');
end;

function TFlexCanvas.FlexSize(w,h:Integer):String;
begin
  result:='width="'+IntToStr(w)+'" height="'+IntToStr(h)+'"';
end;

function TFlexCanvas.FlexSize(const R:TRect):String;
begin
  result:=FlexSize(R.Right-R.Left, R.Bottom-R.Top);
end;

procedure TFlexCanvas.Ellipse(const X1, Y1, X2, Y2: Integer);
begin
  AddTag('tee:Ellipse',FlexPosition(X1,Y1)+' '+FlexSize(X2-X1,Y2-Y1)+' '+
         FlexAlpha+FlexBrush+FlexPen);
end;

procedure TFlexCanvas.FillRect(const Rect: TRect);
begin
  AddTag('tee:Rectangle',FlexPosition(Rect.Left,Rect.Top)+' '+
         FlexSize(Rect)+' '+
         FlexAlpha+'strokeColor="" '+FlexBrush);
end;

function TFlexCanvas.FlexGradient(const Gradient:TCustomTeeGradient):String;
begin
  with Gradient do
  if Visible then
     result:=FlexGradient(StartColor,MidColor,EndColor,Direction,Balance)
  else
     result:='';
end;

function TFlexCanvas.FlexGradient(const StartColor,MidColor,EndColor: TColor;
                      Direction: TGradientDirection; Balance: Integer):String;

  function GradientDirection:String;
  begin
    case Direction of
      gdLeftRight: result:='LeftRight';
      gdTopBottom: result:='TopBottom';
      gdBottomTop: result:='BottomTop';
      gdRightLeft: result:='RightLeft';
    end;
  end;

begin
  if Direction=gdRadial then
     result:='gradientType="Radial" '
  else
     result:='gradientType="Linear" gradientDir="'+GradientDirection+'" ';


  result:=result+'startColor='+FlexColor(StartColor)+' '+
                 'endColor='+FlexColor(EndColor)+' ';

  if MidColor<>clNone then
     result:=result+'midColor='+FlexColor(MidColor)+' ';
end;

procedure TFlexCanvas.GradientFill(Const Rect: TRect; StartColor,
                                   EndColor: TColor;
                                   Direction: TGradientDirection;
                                   Balance: Integer=50;
                                   RadialX:Integer=0;
                                   RadialY:Integer=0);
begin
  AddTag('tee:Rectangle',FlexPosition(Rect.Left,Rect.Top)+' '+
      FlexSize(Rect)+' '+
      FlexGradient(StartColor,clNone,EndColor,Direction,Balance)+FlexAlpha);
end;

function TFlexCanvas.InitWindow(const DestCanvas: TCanvas;
  const A3DOptions: TView3DOptions; ABackColor: TColor; Is3D: Boolean;
  const UserRect: TRect): TRect;
begin
  result:=inherited InitWindow(DestCanvas,A3DOptions,ABackColor,Is3D,UserRect);

  IAddedInitApp:=False;

  AddItem('<?xml version="1.0"?>');
  AddItem('<!-- Generated by TeeChart Pro Version '+TeeChartVersion+' -->');
  AddItem('<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"');
  AddItem('                xmlns:tee="com.steema.graphics.*"');

  if ITreeMode then
     AddItem('                xmlns:s="library://ns.adobe.com/flex/spark"');

  AddItem('                '+FlexSize(UserRect));
  AddItem('                paddingTop="0"');
  AddItem('                paddingBottom="0"');
  AddItem('                paddingRight="0"');
  AddItem('                paddingLeft="0"');

  AddItem('>');

  IApplication:=FStrings.Count;

  AddItem('');
  AddItem('    <mx:Script>');
  AddItem('        <![CDATA[');
  AddItem('       ]]>');
  IScript:=FStrings.Count;

  AddItem('    </mx:Script>');
  AddItem('');

  IStyle:=FStrings.Count;
  AddItem('');

  AddAnimationTypes;

  // AddItem('    <s:Group '+FlexSize(UserRect)+'>');
  AddItem('    <mx:Canvas '+FlexSize(UserRect)+ ' horizontalScrollPolicy = "off" verticalScrollPolicy = "off">');
end;

function TFlexCanvas.FlexPen:String;
begin
  if Pen.Style<>psClear then
  begin
     result:=' strokeColor='+FlexColor(Pen.Color);

    if Pen.Width<>1 then
       result:=result+' strokeWidth="'+IntToStr(Pen.Width)+'"';

    case Pen.EndStyle of
      esRound: ;  // default: strokeCaps="ROUND"
      esSquare: result:=result+' strokeCaps="SQUARE"';
    else
      result:=result+' strokeCaps="NONE"';
    end;

    result:=result+' ';
  end
  else
    result:=' ';
end;

procedure TFlexCanvas.LineTo(const X, Y: Integer);

  function Pos0:String;
  begin
    result:='x0="'+IntToStr(FX)+'" y0="'+IntToStr(FY)+'"';
  end;

  function Pos1:String;
  begin
    result:='x1="'+IntToStr(x)+'" y1="'+IntToStr(y)+'"';
  end;

  function DashLenGap:String;
  var len, gap : Integer;
  begin
    len:=4;
    gap:=4;

    if Pen.SmallDots then
    begin
      len:=1;
      gap:=1;
    end
    else
    case Pen.Style of
        psDash: begin end;
         psDot: begin len:=2; gap:=2; end;
     psDashDot: begin len:=4; gap:=2; end;
  psDashDotDot: begin len:=4; gap:=3; end;
    end;

    result:=' len="'+IntToStr(len)+'" gap="'+IntToStr(gap)+'"';
  end;

begin
  if (not Pen.SmallDots) and (Pen.Style=psSolid) then
     AddTag('tee:Line',Pos0+' '+Pos1+FlexAlpha+FlexPen)
  else
     AddTag('tee:DashLine',Pos0+' '+Pos1+FlexAlpha+FlexPen+DashLenGap);
end;

procedure TFlexCanvas.MoveTo(const X, Y: Integer);
begin
  FX:=X;
  FY:=Y;
end;

procedure TFlexCanvas.Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
var StartAngle : Single;
    EndAngle   : Single;
    XC         : Integer;
    YC         : Integer;
    tmpS       : Extended;
    tmpC       : Extended;
begin
  XC:=(X1+X2) div 2;
  YC:=(Y1+Y2) div 2;

  CalcArcAngles(X1,Y1,X2,Y2,X3,Y3,X4,Y4,StartAngle,EndAngle);

  SinCos(TeePiStep*(StartAngle+EndAngle)*0.5,tmpS,tmpC);

  AddTag('tee:Pie',
     FlexPosition(X1,Y1)+' '+FlexSize(X2-X1,Y2-Y1)+
     ' x3="'+IntToStr(x3)+'" y3="'+IntToStr(y3)+'"'+
     ' x4="'+IntToStr(x4)+'" y4="'+IntToStr(y4)+'" '+
     ' cx="'+FlexFloatToStr(xc+(x2-x1)*0.5*tmpC)+
     '" cy="'+FlexFloatToStr(yc-(y2-y1)*0.5*tmpS)+'" '+
     'startAngle="'+FlexFloatToStr(StartAngle)+'" endAngle="'+
     FlexFloatToStr(EndAngle)+'" '+
     FlexAlpha+FlexBrush+FlexPen);
end;

function TFlexCanvas.FlexPoints(const Points: Array of TPoint):String;

  function PointToString(const P:TPoint):String;
  begin
    result:=IntToStr(P.X)+','+IntToStr(P.Y);
  end;

var t : Integer;
begin
  result:='points="[';

  if Length(Points)>0 then
  begin
    result:=result+PointToString(Points[0]);

    for t:=1 to Length(Points)-1 do
        result:=result+','+PointToString(Points[t]);
  end;

  result:=result+']"';
end;

function TFlexCanvas.CalcID(const Prefix:String):String;
var tmp : Integer;
begin
  tmp:=0;
  result:=Prefix;

  while IItems.IndexOf(result)<>-1 do
  begin
    Inc(tmp);
    result:=Prefix+'_'+IntToStr(tmp);
  end;

  IItems.Add(result);
end;

function TFlexCanvas.BeginEntity(const Entity:String; const Visual:TVisualBlock=nil; const Transform:TTeeTransform=nil):TVisualBlock;

  function FixID:String;
  const
    Alpha = ['A'..'Z', 'a'..'z', '_'];
    AlphaNumeric = Alpha + ['0'..'9'];

  var t : Integer;
  begin
    result:=Entity;

    for t:=1 to Length(result) do
        if not {$IFDEF D12}
               CharInSet(result[t],AlphaNumeric)
               {$ELSE}
               (result[t] in AlphaNumeric)
               {$ENDIF} then
           result[t]:='_';
  end;

var tmpID : String;
begin
  result:=Visual;

  tmpID:=FixID;

  if tmpID='' then
     tmpID:='Entity_'+IntToStr(IPath.Count);

  IPath.Add(tmpID);
  IIdent:=IIdent+' ';

  // Spark Groups:
  if ITreeMode then
  begin
    AddTag('s:Group',AddAnimation(tmpID),False);
    IIdent:=IIdent+' ';
  end;
end;

procedure TFlexCanvas.EndEntity;
begin
  IPath.Delete(IPath.Count-1);
  Delete(IIdent,1,1);

  // Spark Groups:
  if ITreeMode then
  begin
    AddItem(IIdent+'</s:Group>');
    Delete(IIdent,1,1);
  end;
end;

procedure TFlexCanvas.AddTag(const ATag,AText:String; Finish:Boolean=True);

  function CurrentID:String;
  var t : Integer;
  begin
    if IPath.Count=0 then
       result:=View3DOptions.Parent.Name+'_'
    else
       result:=IPath[0];

    for t:=1 to IPath.Count-1 do
        result:=result+'_'+IPath[t];
  end;

var tmp : String;
    tmpID : String;
begin
  tmpID:=CalcID(CurrentID);
  tmp:=IIdent+'<'+ATag+' id="'+tmpID+'" '+AText;

  if not ITreeMode then
     tmp:=tmp+AddAnimation(tmpID);

  if Finish then
     tmp:=tmp+'/>'
  else
     tmp:=tmp+'>';

  AddItem(tmp);
end;

function TFlexCanvas.FlexBrush:String;

  function BrushColor:String;
  begin
    if Brush.Style=bsClear then
       result:=' brushColor=""'
    else
       result:=' brushColor='+FlexColor(Brush.Color);
  end;

begin
  if Gradient.Visible then
     result:=FlexGradient(Gradient)
  else
     result:=BrushColor+' ';
end;

procedure TFlexCanvas.Polygon({$IFDEF D6}const{$ENDIF} Points: array of TPoint);
begin
  AddTag('tee:Polygon',FlexPoints(Points)+' '+FlexAlpha+FlexBrush+FlexPen);
end;

procedure TFlexCanvas.Polyline({$IFDEF D6}const{$ENDIF} Points:Array of TPoint);
begin
  AddTag('tee:Polyline',FlexPoints(Points)+' '+FlexAlpha+FlexPen);
end;

function TFlexCanvas.FlexFloatToStr(const Value:Double):String;
var Old : Char;
begin
  Old:={$IFDEF D15}FormatSettings.{$ENDIF}DecimalSeparator;
  {$IFDEF D15}FormatSettings.{$ENDIF}DecimalSeparator:='.';
  try
    result:=FormatFloat('0.####',Value);
  finally
    {$IFDEF D15}FormatSettings.{$ENDIF}DecimalSeparator:=Old;
  end;
end;

function TFlexCanvas.FlexAlpha:String;
begin
  result:=FlexAlpha(100-Round(100*ITransp));
end;

function TFlexCanvas.FlexAlpha(const Alpha:TTeeTransparency):String;
begin
  if Alpha=0 then
     result:=''
  else
     result:=' alpha="'+FlexFloatToStr(1-(Alpha/100))+'" ';
end;

procedure TFlexCanvas.Rectangle(const X0, Y0, X1, Y1: Integer);
begin
  AddTag('tee:Rectangle',FlexPosition(X0,Y0)+' '+
         FlexSize(X1-X0,Y1-Y0)+' '+FlexAlpha+FlexPen+FlexBrush);
end;

function TFlexCanvas.FlexFont(AFont:TFont; AColor:TColor=clDefault):String;

  function FontStyle:String;
  begin
    if fsItalic in Font.Style then
       result:=' fontStyle="italic" '
    else
       result:=''; // normal
  end;

  function FontWeight:String;
  begin
    if fsBold in Font.Style then
       result:=' fontWeight="bold" '
    else
       result:=''; // normal
  end;

  function TextDecoration:String;
  begin
    if fsUnderline in Font.Style then
       result:=' textDecoration="underline" '
    else
       result:=''; // none
  end;

begin
  if AColor=clDefault then
     AColor:=Font.Color;

  result:=FontStyle+
          FontWeight+
          ' fontSize="'+FlexFloatToStr(Font.Size*1.2)+'" '+
          TextDecoration+
          ' fontFamily="'+Font.Name+'" '+
          ' color='+FlexColor(AColor)+' ';
end;

procedure TFlexCanvas.RotateLabel(x, y: Integer; const St: String;
                                  RotDegree: Double; AllowHTML:Boolean=False);

  function TextRotation:String;
  begin
    if RotDegree=0 then
       result:=''
    else
       result:=FlexFloatToStr(RotDegree);  // embedded fonts only?
  end;

  procedure DoDrawText(AX,AY:Integer; AColor:TColor; ATransp:TTeeTransparency);
  begin
    AddTag('mx:Label','text="'+St+'" '+FlexPosition(AX-1,AY-1)+
           FlexFont(Font,AColor)+' textAlign="left" '+FlexAlpha(ATransp));
  end;

begin
  if TextAlign=TA_RIGHT then
     Dec(x,TextWidth(St))
  else
  if TextAlign=TA_CENTER then
     Dec(x,TextWidth(St) div 2);

  if Font.Shadow.Visible then
     DoDrawText(x+Font.Shadow.HorizSize,
                y+Font.Shadow.VertSize,
                Font.Shadow.Color,
                Font.Shadow.Transparency);

  if Font.Emboss.Visible then
     DoDrawText(x-Font.Emboss.HorizSize,
                y-Font.Emboss.VertSize,
                Font.Emboss.Color,
                Font.Emboss.Transparency);

  DoDrawText(x,y,Font.Color,100-Round(100*ITransp));
end;

procedure TFlexCanvas.RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer);
begin
  AddTag('tee:RoundRect',FlexPosition(X1,Y1)+' '+FlexSize(X2-X1,Y2-Y1)+' '+
         'rx="'+IntToStr(x3)+'" ry="'+IntToStr(y3)+'"'+
         FlexAlpha+FlexPen+FlexBrush);
end;

procedure TFlexCanvas.SetPixel(X, Y: Integer; Value: TColor);
begin
//TODO
end;

procedure TFlexCanvas.SetPixel3D(X, Y, Z: Integer; Value: TColor);
begin
  Calc3DPos(x,y,z);
  SetPixel(x,y,Value);
end;

procedure TFlexCanvas.ShowImage(const DestCanvas, DefaultCanvas: TCanvas;
  const UserRect: TRect);
begin
  AddItem('    </mx:Canvas>');   // </s:Group>
  AddItem('</mx:Application>');

  Pen.OnChange:=nil;
end;

procedure TFlexCanvas.StretchDraw(const Rect: TRect; const Graphic: TGraphic);

  function ImageSource:String;
  begin
    if EmbeddImages then
       result:='@Embed('''+ImageFileName(Graphic)+''')'
    else
       result:=ImageFileName(Graphic);
  end;

begin
  if Assigned(Graphic) then
  begin
    AddTag('mx:Image',FlexPosition(Rect.Left,Rect.Top)+' '+
      ' scaleX="'+FlexFloatToStr((Rect.Right-Rect.Left)/Graphic.Width)+'" '+
      ' scaleY="'+FlexFloatToStr((Rect.Bottom-Rect.Top)/Graphic.Height)+'" '+
      ' source="'+ImageSource+'"');
  end;
end;

procedure TFlexCanvas.TextOut(X, Y: Integer; const Text: String);
begin
  RotateLabel(x,y,Text,0);
end;

procedure TFlexCanvas.TextOut3D(X, Y, Z: Integer; const Text: String; AllowHTML:Boolean=False);
begin
  Calc3DPos(X,Y,Z);
  TextOut(X,Y,Text);
end;

procedure TFlexCanvas.UnClipRectangle;
begin
  // TODO
end;

function TFlexCanvas.AddAnimation(const ID: String): String;
var ineffect, outeffect, tmp: String;
    t: Integer;
begin
  result:='';

  for t:=0 to TCustomAxisPanel(IPanel).Animations.Count - 1 do
    if TCustomAxisPanel(IPanel).Animations[t] is TExpandAnimation then
    with TExpandAnimation(TCustomAxisPanel(IPanel).Animations[t]) do
     begin
       tmp:=GetEnumName(TypeInfo(TChartClickedPartStyle), Integer(Target));

       if Copy(tmp,1,2)='cp' then
          tmp:=Copy(tmp,3,Length(tmp));

       if StringContains(UpperCase(tmp), UpperCase(ID))<>0 then
       begin
         case Trigger of
            atMouseClick : begin
                             ineffect:='mouseDownEffect';
                             outeffect:='mouseUpEffect';
                           end;
            atMouseOver  : begin
                             ineffect:='rollOverEffect';
                             outeffect:='rollOutEffect';
                           end;
         end;

         result:=ineffect + '="{teeexpand}" ' + outeffect + '="{teeunexpand}"';
       end;
    end;
end;

procedure TFlexCanvas.AddAnimationTypes;
const
  mxMove  ='            <mx:Move duration="%d" suspendBackgroundProcessing="true" xBy="%d" yBy="%d"/>';
  mxResize='            <mx:Resize duration="%d" suspendBackgroundProcessing="true" widthBy="%d" heightBy="%d"/>';

var t: Integer;
begin
  if TCustomAxisPanel(IPanel).Animations.Count > 0 then
  begin
    for t := 0 to TCustomAxisPanel(IPanel).Animations.Count - 1 do
    begin
      if TCustomAxisPanel(IPanel).Animations[t] is TExpandAnimation then
      begin
        with TExpandAnimation(TCustomAxisPanel(IPanel).Animations[t]) do
        begin
          AddItem('    <mx:Parallel id="teeexpand">');
          AddItem('        <mx:children>');
          AddItem(Format(mxMove,[Speed*SizeBy,-SizeBy,-SizeBy]));
          AddItem(Format(mxResize,[Speed*SizeBy,2*SizeBy,2*SizeBy]));
          AddItem('        </mx:children>');
          AddItem('    </mx:Parallel>');
          AddItem('    <mx:Parallel id="teeunexpand">');
          AddItem('        <mx:children>');
          AddItem(Format(mxMove,[1,SizeBy,SizeBy]));
          AddItem(Format(mxResize,[1,-2*SizeBy,-2*SizeBy]));
          AddItem('        </mx:children>');
          AddItem('    </mx:Parallel>');
          AddItem('');
        end;
      end;

      // Glow
      (*
        <mx:Glow id="teeglow" duration="100"
            alphaFrom="0" alphaTo="1"
            blurXFrom="0.0" blurXTo="30.0"
            blurYFrom="0.0" blurYTo="30.0"  strength="2"
            color='+FlexColor(clRed)+' />

        <mx:Glow id="teeunglow" duration="800"
            alphaFrom="1" alphaTo="0"
            blurXFrom="30.0" blurXTo="0.0"
            blurYFrom="30.0" blurYTo="0.0"  strength="2"
            color='+FlexColor(clBlue)+' />

      *)
    end;
  end;
end;

procedure TFlexCanvas.AddItem(const S: String);
var tmp : String;
    i   : Integer;
begin
  if Length(S)<255 then
     FStrings.Add(S)
  else
  begin
    tmp:=S;

    while Length(tmp)>255 do
    begin
      i:=LastDelimiter(' ,',Copy(tmp,1,255));
      if i=0 then i:=255;

      FStrings.Add(Copy(tmp,1,i));
      Delete(tmp,1,i);
    end;

    if Length(tmp)>0 then
       FStrings.Add(tmp);
  end;
end;

procedure TFlexCanvas.AddLink(x,y:Integer; const Text, URL, Hint: String);
begin
  AddTag('mx:LinkButton','label="'+Text+'" '+FlexPosition(x,y)+
      ' toolTip="'+Hint+'" '+
      FlexFont(Font)+
      'click="navigateToURL(new URLRequest('''+URL+'''), '''+Hint+''')"');
end;

procedure TFlexCanvas.AddMarksTip;
begin
  FStrings.Insert(IStyle, '    <mx:Style>');
  Inc(IStyle);
  FStrings.Insert(IStyle, '      HtmlToolTip {');   // Namespace "." ?
  Inc(IStyle);
  FStrings.Insert(IStyle, '        fontFamily: "Verdana";');
  Inc(IStyle);
  FStrings.Insert(IStyle, '        fontSize: 12;');
  Inc(IStyle);
  FStrings.Insert(IStyle, '        fontStyle: "normal";');
  Inc(IStyle);
  FStrings.Insert(IStyle, '        color: #000000;');
  Inc(IStyle);
  FStrings.Insert(IStyle, '        backgroundColor: #FFFFFF;');
  Inc(IStyle);
  FStrings.Insert(IStyle, '      }');
  Inc(IStyle);
  FStrings.Insert(IStyle, '    </mx:Style>');
end;

procedure TFlexCanvas.AddToolTip(const Entity,ToolTip:String);
begin
  if not IAddedInitApp then
  begin
    AddMarksTip;

    FStrings.Insert(IApplication-1,' creationComplete = "initApp()"');
    FStrings.Insert(IScript,' }');
    FStrings.Insert(IScript,' import mx.managers.ToolTipManager;');
    FStrings.Insert(IScript,' import com.steema.graphics.HtmlToolTip;');
    FStrings.Insert(IScript,' ToolTipManager.toolTipClass = HtmlToolTip;');
    FStrings.Insert(IScript,' public function initApp():void {');

    Inc(IScript,4);

    IAddedInitApp:=True;
  end;

  FStrings.Insert(IScript,' '+Entity+'.toolTip = "'+ToolTip+'";');
  Inc(IScript);
end;

{ TFlexExportFormat }

function TFlexExportFormat.Compile(const TargetFile:String):Boolean;
begin
  CheckProperties;
  result:=FProperties.Compile(TargetFile);
end;

function TFlexExportFormat.Description: String;
begin
  result:=TeeMsg_AsFlex;
end;

procedure TFlexExportFormat.DoCopyToClipboard;
begin
  with FlexMXML do
  try
    TeeCopyClipboard(Text);
  finally
    Free;
  end;
end;

function TFlexExportFormat.FileExtension: String;
begin
  result:='mxml'; // Do not localize
end;

class function TFlexExportFormat.FileFilter: String;
begin
  result:=TeeMsg_FlexFilter;
end;

type
  TTeePanelAccess=class(TCustomTeePanel);

function TFlexExportFormat.FlexMXML: TStringList;
var tmp : TCanvas3D;
begin
  CheckSize;
  result:=TStringList.Create;
  Panel.AutoRepaint:=False;

  try
    tmp:=TTeePanelAccess(Panel).InternalCanvas;
    TTeePanelAccess(Panel).InternalCanvas:=nil;

    Panel.Canvas:=TFlexCanvas.CreateText(Panel, result);

    TFlexCanvas(Panel.Canvas).ImagePath:=ImagePath;
    TFlexCanvas(Panel.Canvas).EmbeddImages:=EmbeddImages;

    if not Assigned(Panel.Parent) then
      Panel.BufferedDisplay:=True;

    try
      Panel.Draw(Panel.Canvas.ReferenceCanvas,TeeRect(Width,Height));
    finally
      Panel.Canvas:=tmp;
    end;
  finally
    Panel.AutoRepaint:=True;
  end;
end;

Procedure TFlexExportFormat.CheckProperties;
begin
  if not Assigned(FProperties) then
  begin
    FProperties:=TFlexOptions.Create(nil);
    FProperties.Panel:=Panel;
  end;
end;

function TFlexExportFormat.Options(Check: Boolean): TForm;
begin
  if Check then
     CheckProperties;

  result:=FProperties;
end;

procedure TFlexExportFormat.SaveToStream(Stream: TStream);
begin
  with FlexMXML do
  try
    SaveToStream(Stream);
  finally
    Free;
  end;
end;

procedure TFlexOptions.FormCreate(Sender: TObject);
begin
  Align:=alClient;
  ReadOptions;
end;

procedure TFlexOptions.ReadOptions;
begin
  IReadingOptions:=True;
  try
    ETemp.Text:=TeeReadStringOption('FlexCanvas_Temp','');

    if ETemp.Text='' then
       ETemp.Text:=TTeeExportFormBase.GetTempFolder;

    EFlex.Text:=TeeReadStringOption('FlexCanvas_Compiler','');
  finally
    IReadingOptions:=False;
  end;
end;

procedure TeeSaveToFlexFile( const APanel:TCustomTeePanel; const FileName: WideString;
                             AWidth:Integer=0; AHeight:Integer=0;
                             EmbededImages:Boolean=True);
begin
  with TFlexExportFormat.Create do
  try
    Panel:=APanel;
    Height:=AHeight;
    Width:=AWidth;

    ImagePath:=ExtractFilePath(FileName);
    EmbeddImages:=EmbededImages;

    SaveToFile(FileName);
  finally
    Free;
  end;
end;

const
  TeeSWCLibrary='\tee.swc';

function TFlexOptions.Compile(const TargetFile:String): Boolean;
var d : TDosCommand;
    tmpPath   : String;
    tmpDeb    : String;
    tmpStatic : String;
    tmpFontAlias : String;
begin
  d:=TDosCommand.Create(Owner);
  try
    tmpPath:=ExtractFilePath(TargetFile);

    if Copy(tmpPath,Length(tmpPath),1)='\' then
       Delete(tmpPath,Length(tmpPath),1);

    if CBDebugMode.Checked then
       tmpDeb:='-debug=true '
    else
       tmpDeb:='';

    if CBStatic.Checked then
       tmpStatic:='-static-link-runtime-shared-libraries=true '
    else
       tmpStatic:='';

    tmpFontAlias:='-advanced-anti-aliasing=';

    if CBFontAntialias.Checked then
       tmpFontAlias:=tmpFontAlias+'true '
    else
       tmpFontAlias:=tmpFontAlias+'false ';

    d.CommandLine:='"'+EFlex.Text+'\mxmlc.exe" -use-network=true '+
                   tmpDeb+

                   '-library-path+="'+tmpPath+'" '+

                   tmpStatic+
                   tmpFontAlias+

                   //'-include-libraries="'+tmpPath+TeeSWCLibrary+'" '+

                   '"'+TargetFile+'"';

    Screen.Cursor:=crHourGlass;
    try
      d.Execute;

      while not d.Terminated do
            Sleep(100);

    finally
      Screen.Cursor:=crDefault;
    end;

    result:=d.DosExitCode=0;

    if not result then
    begin
      d.Lines.Insert(0,'');
      d.Lines.Insert(0,d.CommandLine);
      d.Lines.Insert(0,'Compiling:');

      TeeEditStrings(Owner, d.Lines);
    end;
  finally
    d.Free;
  end;
end;

procedure TFlexOptions.Compile(AWidth,AHeight: Integer; const FileName:String);
var tmp : TStringList;
    tmpName : String;
    tmpDest : String;
begin
  tmpDest:=ETemp.Text;

  if not {$IFDEF D15}SysUtils.{$ENDIF}DirectoryExists(tmpDest) then
     if TeeYesNo('Folder "'+tmpDest+'" does not exist.'#13+
                 'Do you want to create it?') then
        {$IFDEF D15}SysUtils.{$ENDIF}ForceDirectories(tmpDest);

  tmpName:=FileName;

  if tmpName='' then tmpName:=Panel.Name;
  if tmpName='' then tmpName:=Panel.ClassName+'1';

  if Copy(tmpDest,Length(tmpDest),1)='\' then
     tmpDest:=tmpDest+tmpName+'.mxml'
  else
     tmpDest:=tmpDest+'\'+tmpName+'.mxml';

  if AWidth=0 then AWidth:=Panel.Width;
  if AHeight=0 then AHeight:=Panel.Height;

  with TFlexExportFormat.Create do
  try
    Panel:=Self.Panel;
    Height:=AHeight;
    Width:=AWidth;

    ImagePath:=ExtractFilePath(tmpDest);
    EmbeddImages:=CBEmbedd.Checked;

    tmp:=FlexMXML;
  finally
    Free;
  end;

  try
    Compile(tmp,AWidth,AHeight,tmpDest);
  finally
    tmp.Free;
  end;
end;

function TFlexOptions.Compile(const Strings:TStrings; AWidth,
  AHeight: Integer; const FileName: String):Boolean;

  procedure DeleteFiles(const Path,Wildcards:String);
  var tmp : TStrings;
      t   : Integer;
  begin
    tmp:=TeeGetFilesFromDir(Path+'\'+Wildcards);
    try
      for t:=0 to tmp.Count-1 do
          DeleteFile(Path+'\'+tmp[t]);
      finally
        tmp.Free;
      end;
  end;

  function Chart1_html:TStringList;
  begin
    result:=TStringList.Create;
    result.Add('<html>');
    result.Add('  <head>');
    result.Add('  <style type=\"text/css\">');
    result.Add('    html, body');
    result.Add('    {');
    result.Add('      height: 100%;');
    result.Add('      margin: 0;');
    result.Add('      padding: 0;');
    result.Add('    }');
    result.Add('  </style>');
    result.Add('  </head>');
    result.Add('  <body>');
    result.Add('    <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"');
    result.Add('      id="Chart1" width="%WIDTH%" height="%HEIGHT%" menu="true"');
    result.Add('      codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">');
    result.Add('      <param name="movie" value="%MOVIE%" />');
    result.Add('      <param name="quality" value="high" />');
    result.Add('      <param name="menu" value="true" />');
    result.Add('      <param name="bgcolor" value="FFFFFF">');
    result.Add('      <param name="allowScriptAccess" value="sameDomain" />');
    result.Add('      <embed src="%MOVIE%" width="%WIDTH%" height="%HEIGHT% ">');
    result.Add('    </object>');
    result.Add('  </body>');
    result.Add('</html>');
  end;

  function GenerateHTML(const AMovie:String; AWidth, AHeight:Integer):String;
  var tmp : TStrings;
      t   : Integer;
  begin
    tmp:=Chart1_html;

    try
      for t:=0 to tmp.Count-1 do
      begin
        if Pos('%MOVIE%',tmp[t])>0 then
          tmp[t]:=StringReplace(tmp[t],'%MOVIE%',ExtractFileName(AMovie),[rfReplaceAll,rfIgnoreCase]);
        if Pos('%WIDTH%',tmp[t])>0 then
          tmp[t]:=StringReplace(tmp[t],'%WIDTH%',IntToStr(AWidth),[rfReplaceAll,rfIgnoreCase]);
        if Pos('%HEIGHT%',tmp[t])>0 then
          tmp[t]:=StringReplace(tmp[t],'%HEIGHT%',IntToStr(AHeight),[rfReplaceAll,rfIgnoreCase]);
      end;

      result:=ChangeFileExt(AMovie,'.html');

      tmp.SaveToFile(result);
    finally
      tmp.Free;
    end;
  end;

  procedure Preview(const Target:String);
  begin
    TeeGotoURL(Panel.Handle, Target);
  end;

  procedure Check_TeeSWC_Library(const TeeSWC:String);
  var swc : TResourceStream;
  begin
    if not FileExists(TeeSWC) then
    begin
      swc:=TeeResourceStream('TEEFLEXSWC');
      try
        swc.SaveToFile(TeeSWC);
      finally
        swc.Free;
      end;
    end;
  end;

begin
  Strings.SaveToFile(FileName);
  Check_TeeSWC_Library(ExtractFilePath(FileName)+TeeSWCLibrary);

  result:=Compile(FileName);

  if CBDelete.Checked then
  begin
    if FileExists(ETemp.Text+TeeSWCLibrary) then
       DeleteFile(ETemp.Text+TeeSWCLibrary);

    if FileExists(FileName) then
       DeleteFile(FileName);

    // Delete images only if they have been embedd inside the *.swf movie:
    if CBEmbedd.Checked then
       DeleteFiles(ETemp.Text,'TeeChart_Flex_Temp_*');
  end;

  if result and CBPreview.Checked then
     Preview(GenerateHTML(ChangeFileExt(FileName,'.swf'),AWidth,AHeight));
end;

procedure TFlexOptions.SpeedButton1Click(Sender: TObject);
var tmp : String;
begin
  tmp:=ETemp.Text;

  if TeeSelectFolder(TeeMsg_SelectTmpFolder,'',tmp) then
     ETemp.Text:=tmp;
end;

procedure TFlexOptions.SpeedButton2Click(Sender: TObject);
var tmp : String;
begin
  tmp:=EFlex.Text;

  if TeeSelectFolder(TeeMsg_PathAdobeFlexCompiler,'',tmp) then
     EFlex.Text:=tmp;
end;

procedure TFlexOptions.ETempChange(Sender: TObject);
begin
  CheckCompile;
  TeeSaveStringOption('FlexCanvas_Temp',ETemp.Text);
end;

procedure TFlexOptions.BCompileClick(Sender: TObject);
var tmpWidth, tmpHeight: Integer;
begin
  if Assigned(Parent) and (Parent.Owner is TTeeExportFormBase) then
  begin
    tmpWidth:=TTeeExportFormBase(Parent.Owner).UDWidth.Position;
    tmpHeight:=TTeeExportFormBase(Parent.Owner).UDHeight.Position;
  end
  else
  begin
    tmpWidth:=Panel.Width;
    tmpHeight:=Panel.Height;
  end;

  Compile(tmpWidth, tmpHeight, Panel.Name);
end;

procedure TFlexOptions.CheckCompile;
begin
  BCompile.Enabled:=Assigned(Panel) and (ETemp.Text<>'') and (EFlex.Text<>'');
end;

procedure TFlexOptions.EFlexChange(Sender: TObject);
begin
  if not IReadingOptions then
  begin
    if FileExists(EFlex.Text+'\mxmlc.exe') then
    begin
      CheckCompile;
      TeeSaveStringOption('FlexCanvas_Compiler',EFlex.Text);
    end
    else
    if not SilentMode then
      ShowMessageUser('Cannot find Flex compiler mxmlc.exe in folder: '#13+EFlex.Text);
  end;
end;

procedure TFlexOptions.FormShow(Sender: TObject);
begin
  CheckCompile;
end;

class procedure TFlexOptions.Compile(const AChart:TCustomTeePanel;
                           AWidth, AHeight:Integer;
                           const FlexPath, TempPath, FileName:String;
                           Preview:Boolean=True);
begin
  with TFlexOptions.Create(AChart) do
  try
    Panel:=AChart;
    ETemp.Text:=TempPath;
    EFlex.Text:=FlexPath;
    CBPreview.Checked:=Preview;

    Compile(AWidth,AHeight,FileName);
  finally
    Free;
  end;
end;

initialization
  RegisterTeeExportFormat(TFlexExportFormat);
  RegisterClass(TFlexOptions);
finalization
  UnRegisterTeeExportFormat(TFlexExportFormat);
end.
