{********************************************}
{   TCustomChart & TChart Components         }
{ Copyright (c) 1995-2020 by Steema Software }
{    All Rights Reserved                     }
{********************************************}
unit Chart;
{.$DEFINE FMX}
{$I TeeDefs.inc}

interface

uses {$IFNDEF FMX}
     {$IFNDEF LINUX}
     Windows,
     {$ENDIF}
     {$ENDIF}

     Classes, SysUtils,

     {$IFDEF D18}
     System.Generics.Collections,
     {$ENDIF}
     
     {$IFDEF FMX}
     System.UIConsts, System.UITypes, FMX.Types, System.Types,
     FMX.Controls,

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

     {$IFDEF D16}
     System.UITypes, System.Types,
     {$ENDIF}

     Graphics,
     Controls,

     {$IFNDEF D16}
     {$IFDEF D6}
     Types,
     {$ENDIF}
     {$ENDIF}

     {$ENDIF}

     {$IFDEF FMX}
     FMXTee.Procs, FMXTee.Engine, FMXTee.Canvas
     {$ELSE}
     TeeProcs, TeEngine, TeCanvas
     {$ENDIF}
     ;

Const TeeMsg_DefaultFunctionName  = 'TeeFunction';   { <-- dont translate }
      TeeMsg_DefaultSeriesName    = 'Series';        { <-- dont translate }
      TeeMsg_DefaultToolName      = 'ChartTool';     { <-- dont translate }
      TeeMsg_DefaultAnimationName = 'ChartAnimation';{ <-- dont translate }
      ChartComponentPalette       = 'TeeChart';      { <-- dont translate }

      TeeMaxLegendColumns        =  3;

      TeeDefaultLegendSymbolHeight = 100; // %
      TeeDefaultLegendSymbolWidth  = 20; // %

      // Default pixels distance between titles chart.
      // Corresponds to Chart1.Title.VertMargin property.
      TeeTitleFootDistance       =  5;

      {$IFDEF D16}
      TeeAllComponentPlatformIDs=(pidWin32 or pidWin64 or pidOSX32
              {$IFDEF D18}
                 or {$IFDEF D26}pidiOSSimulator32{$ELSE}pidiOSSimulator{$ENDIF}
                 or {$IFDEF D26}pidiOSDevice32{$ELSE}pidiOSDevice{$ENDIF}
              {$ENDIF}
              {$IFDEF D19}
                 or {$IFDEF D26}pidAndroid32Arm{$ELSE}pidAndroid{$ENDIF}
              {$ENDIF}
              {$IFDEF D22}or pidiOSDevice64{$ENDIF}
              {$IFDEF D26}or pidOSX64{$ENDIF}
              {$IFDEF D27}or pidAndroid64Arm{$ENDIF}
              );
      {$ENDIF}

var   AnimatedZoomFactor   : Double=3.0;   { controls the animated zoom "speed" }
      TeeScrollKeyShift    : TShiftState=[];  { keys that should be pressed to start scroll }

//    Moved to: Chart1.Panning.MouseWheel (default changed to: False)
//    TeeUseMouseWheel     : Boolean=True;  { use the mouse wheel to scroll the axes }

type
  // These record definitions (TAxisSavedScaled and TLegendItemText) should be
  // here at top of this "type" declaration,
  // to skip a C++ bug fixed in XE2:
  // http://qc.embarcadero.com/wc/qcmain.aspx?d=88686
  // "The symbol ... has zero size, or excessive offset in module ..."
  TAxisSavedScales=Record
    Auto    : Boolean;
    AutoMin : Boolean;
    AutoMax : Boolean;
    Min     : TAxisValue;
    Max     : TAxisValue;
  end;

  TAllAxisSavedScales=Array of TAxisSavedScales;

  // Internal, see CalcRect and DrawLegend
  TLegendItemText=Array[0..TeeMaxLegendColumns-1] of String;
  TLegendItemWidth=Array[0..TeeMaxLegendColumns-1] of Integer;

  TCustomChartWall=class(TTeeCustomShape)
  private
    FAutoHide : Boolean;
    FDark3D   : Boolean;
    FEnd      : Integer;
    FSize     : Integer;
    FStart    : Integer;

    Function ApplyDark3D:Boolean;
    procedure CalcPositions(out ATop,ABottom:Integer);
    Function GetPen:TChartPen;
    Procedure InitColor(const AColor:TColor);
    Function IsColorStored:Boolean;
    procedure SetAutoHide(const Value: Boolean);
    Procedure SetDark3D(const Value:Boolean);
    Procedure SetEnd(const Value:Integer);
    Procedure SetPen(const Value:TChartPen);
    Procedure SetSize(const Value:Integer);
    Procedure SetStart(const Value:Integer);
    Function TryDrawWall(const APos1,APos2:Integer; out ABlend:TTeeBlend):Boolean;
  protected
    DefaultColor : TColor;

    procedure DoEndBlending(const Blend:TTeeBlend);
    Function ShouldDraw:Boolean;
  public
    Constructor Create(const AOwner:TCustomTeePanel); override;
    Procedure Assign(Source:TPersistent); override;

    property AutoHide:Boolean read FAutoHide write SetAutoHide default False;
    property Color stored IsColorStored nodefault;
    property Dark3D:Boolean read FDark3D write SetDark3D default True;
    property EndPosition:Integer read FEnd write SetEnd default 100;
    property Pen:TChartPen read GetPen write SetPen;
    property Size:Integer read FSize write SetSize default 0;
    property StartPosition:Integer read FStart write SetStart default 0;
    property Transparency;
  end;

  TChartWall=class(TCustomChartWall)
  published
    property AutoHide;
    property Brush;
    property Color;
    property Dark3D;
    property EndPosition;
    property Gradient;
    property Pen;
    property RoundSize;
    property Size;
    property StartPosition;
    property Transparency;
    property Transparent;
    property Visible default True;
  end;

  { TCustomChartLegend Component }

  TLegendStyle=(lsAuto,lsSeries,lsValues,lsLastValues,lsSeriesGroups);

  TLegendAlignment=(laLeft,laRight,laTop,laBottom);

  LegendException =class(Exception);

  TOnGetLegendText=Procedure( Sender:TCustomAxisPanel;
                              LegendStyle:TLegendStyle;
                              Index:Integer;
                              var LegendText:String) of Object;

  TCustomChartLegend=class;

  TLegendSymbolSize=(lcsPercent,lcsPixels);

  TLegendSymbolPosition=(spLeft,spRight);

  TSymbolDrawEvent=procedure(Sender:TObject; Series:TChartSeries;
                             ValueIndex:Integer; R:TRect) of object;

  TSymbolCalcHeight=function:Integer of object;

  TLegendSymbol=class(TPersistent)
  private
    FContinuous : Boolean;
    FDefaultPen : Boolean;
    FEmboss     : TTeeEmboss;
    FGradient   : TTeeGradient;
    FHeight     : Integer;
    FHeightUnits: TLegendSymbolSize;
    FOnDraw     : TSymbolDrawEvent;
    FPen        : TChartPen;
    FPosition   : TLegendSymbolPosition;
    FShadow     : TTeeShadow;
    FSquared    : Boolean;
    FUseImages  : Boolean;
    FVisible    : Boolean;
    FWidth      : Integer;
    FWidthUnits : TLegendSymbolSize;

    function CalcHeight:Integer;
    Procedure CanvasChanged(Sender:TObject);
    procedure Repaint;
    procedure SetBooleanProperty(var Variable:Boolean; const Value: Boolean);
    procedure SetContinuous(const Value: Boolean);
    procedure SetDefaultPen(const Value: Boolean);
    Procedure SetEmboss(const Value:TTeeEmboss);
    Procedure SetGradient(const Value:TTeeGradient);
    procedure SetHeight(Const Value: Integer);
    procedure SetHeightUnits(const Value: TLegendSymbolSize);
    procedure SetPen(const Value: TChartPen);
    Procedure SetPosition(Const Value:TLegendSymbolPosition);
    Procedure SetShadow(const Value:TTeeShadow);
    procedure SetSquared(const Value: Boolean);
    procedure SetUseImages(const Value: Boolean);
    procedure SetVisible(const Value: Boolean);
    procedure SetWidth(Const Value: Integer);
    procedure SetWidthUnits(const Value: TLegendSymbolSize);
  protected
    CalcItemHeight : TSymbolCalcHeight;

    Function CalcWidth(Value:Integer):Integer;
    procedure Draw(AColor:TColor; const R:TRect);
  public
    Parent : TCustomTeePanel;

    Constructor Create(const AParent:TCustomTeePanel);
    Destructor Destroy; override;

    Procedure Assign(Source:TPersistent); override;
  published
    property Continuous:Boolean read FContinuous write SetContinuous default False;
    property DefaultPen:Boolean read FDefaultPen write SetDefaultPen default True;
    property Emboss:TTeeEmboss read FEmboss write SetEmboss;
    property Gradient:TTeeGradient read FGradient write SetGradient;
    property Height:Integer read FHeight write SetHeight default TeeDefaultLegendSymbolHeight;
    property HeightUnits:TLegendSymbolSize read FHeightUnits
                                 write SetHeightUnits default lcsPercent;
    property Pen:TChartPen read FPen write SetPen;
    property Position:TLegendSymbolPosition read FPosition write SetPosition default spLeft;
    property Shadow:TTeeShadow read FShadow write SetShadow;
    property Squared:Boolean read FSquared write SetSquared default True; // Changed from False
    property UseImages:Boolean read FUseImages write SetUseImages default True;
    property Visible:Boolean read FVisible write SetVisible default True;
    property Width:Integer read FWidth write SetWidth default TeeDefaultLegendSymbolWidth;
    property WidthUnits:TLegendSymbolSize read FWidthUnits
                                 write SetWidthUnits default lcsPercent;

    property OnDraw:TSymbolDrawEvent read FOnDraw write FOnDraw;
  end;

  TTeeCustomShapePosition=class(TCustomTextShape)
  private
    Function GetLeft:Integer; {$IFDEF D9}inline;{$ENDIF}
    Function GetTop:Integer; {$IFDEF D9}inline;{$ENDIF}
    function IsCustomStored:Boolean;
    procedure SetCustomPosition(Const Value:Boolean);
    procedure SetLeft(Const Value: Integer);
    procedure SetTop(Const Value: Integer);
  protected
    FCustomPosition : Boolean;
    DefaultCustom   : Boolean;
  public
    Procedure Assign(Source:TPersistent); override;

    property CustomPosition:Boolean read FCustomPosition write SetCustomPosition stored IsCustomStored;
    property Left:Integer read GetLeft write SetLeft stored FCustomPosition;
    property Top:Integer read GetTop write SetTop stored FCustomPosition;
  end;

  // A Text Shape object (with Left and Top position), publishing all inherited properties.
  TTextShape=class(TTeeCustomShapePosition)
  published
    property Alignment;
    property Angle;
    property AutoSize; // <--- For TMagnifyTool.Shape compatibility.
    property Bevel;
    property BevelWidth;
    property Brush;
    property Children;
    property ChildLayout;
    property ClipText;
    property Color;
    property Cursor;
    property CustomPosition;
    property Emboss;
    property Font;
    property Frame;
    property Gradient;
    property Left;
    property Margins;
    property Picture;
    property Shadow;
    property ShapeCallout;
    property ShapeStyle;
    property Text;
    property TextAlignment;
    property TextFormat;
    property Top;
    property Transparency;
    property Transparent;
    property VertTextAlign;
    property Visible;
  end;

  TStringsShape=class(TTextShape)
  private
    FTitleText   : TStrings;
    IChangingText: Boolean;

    function GetCaption: String;
    procedure SetCaption(const Value: String);
    Procedure SetText(const Value:TStrings);
    procedure TextChanged(Sender:TObject);
  protected
    procedure ChangedText; override;
  public
    Constructor Create(const AOwner: TCustomTeePanel); override;
    Destructor Destroy; override;

    procedure Clear;
    property Caption:String read GetCaption write SetCaption;
  published
    property Text:TStrings read FTitleText write SetText;
  end;

  TCheckBoxesStyle=(cbsCheck, cbsRadio);

  TLegendTitle=class(TStringsShape)
  private
  protected
    procedure InitEmboss(const AEmboss:TTeeEmboss); override;
    procedure InitShadow(const AShadow:TTeeShadow); override;
  public
    Constructor Create(const AOwner: TCustomTeePanel); override;

    Procedure Assign(Source:TPersistent); override;
  published
    property TextFormat;
    property Transparency;
    property Visible default True;
  end;

  TLegendItem=class(TPersistent)
  private
    FAlign  : TCanvasTextAlign;
    FFont   : TTeeFont;
    FLeft   : Integer;
    FSymbol : TRect;
    FText   : String;
    FText2  : String;
    FText3  : String;
    FTop    : Integer;

    ILegend : TCustomChartLegend;

    function GetFont:TTeeFont;
    procedure SetAlign(const Value:TCanvasTextAlign);
    procedure SetCustom(SetPos:Boolean);
    procedure SetFont(const Value:TTeeFont);
    procedure SetLeft(const Value:Integer);
    procedure SetText(const Value:String);
    procedure SetText2(const Value:String);
    procedure SetText3(const Value:String);
    procedure SetTextVariable(var Variable:String; const Value:String);
    procedure SetTop(const Value:Integer);
  public
    Destructor Destroy; override;

    function HasFont:Boolean;
    property SymbolRect:TRect read FSymbol write FSymbol;
  published
    property Align:TCanvasTextAlign read FAlign write SetAlign;
    property Font:TTeeFont read GetFont write SetFont;
    property Left:Integer read FLeft write SetLeft;
    property Text:String read FText write SetText;
    property Text2:String read FText2 write SetText2;
    property Text3:String read FText3 write SetText3;
    property Top:Integer read FTop write SetTop;
  end;

  TLegendItems=class({$IFDEF D18}TList<TLegendItem>{$ELSE}TList{$ENDIF})
  private
    FCustom : Boolean;
    FCustomPosition : Boolean;

    ILegend : TCustomChartLegend;

    Procedure ClearItems;
    function GetItem(Index:Integer):TLegendItem;
    procedure SetCustom(const Value: Boolean);
    procedure SetCustomPosition(const Value: Boolean);
  public
    {$IFDEF D18}
    Destructor Destroy; override;
    {$ENDIF}

    {$IFNDEF AUTOREFCOUNT}
    Procedure Clear; {$IFNDEF D18}override;{$ENDIF}
    {$ENDIF}

    property Custom:Boolean read FCustom write SetCustom;
    property CustomPosition:Boolean read FCustomPosition write SetCustomPosition;
    property Items[Index:Integer]:TLegendItem read GetItem; default;
  end;

  TLegendJustify=(ljAutomatic, ljYes, ljNo);

  TLegendCalcSize=procedure(Sender:TCustomChartLegend; var ASize:Integer) of object;

  TTeeLegendEvent=class(TTeeEvent);

  TCustomChart=class;

  TCustomChartLegend=class(TTeeCustomShapePosition)
  private
    FAlignment       : TLegendAlignment;
    FCheckBoxes      : Boolean;
    FCheckBoxesStyle : TCheckBoxesStyle;
    FCurrentPage     : Boolean;
    // FCustomSize : Boolean;
    FDividingLines   : TChartHiddenPen;
    FDrawBehind      : Boolean;
    FFirstValue      : Integer;
    FFontSeriesColor : Boolean;
    FGlobalTransp    : TTeeTransparency;
    FHorizJustify    : TLegendJustify;
    FHorizMargin     : Integer;
    FInverted        : Boolean;
    FItems           : TLegendItems;
    FLeftPercent     : Integer;
    FLegendStyle     : TLegendStyle;
    FMaxNumRows      : Integer;
    FPosUnits        : TTeeUnits;
    FResizeChart     : Boolean;
    FSelected        : TTeeSelected;
    FSeries          : TChartSeries;
    FSymbol          : TLegendSymbol;
    FTextStyle       : TLegendTextStyle;
    FTextSymbolGap   : Integer;
    FTitle           : TLegendTitle;
    FTopLeftPos      : Integer;
    FTopPercent      : Integer;
    FVertMargin      : Integer;
    FVertSpacing     : Integer;

    { Internal }
    IColorWidth  : Integer;
    ICustomFont  : Boolean;
    ILastValue   : Integer;
    ITotalItems  : Integer;
    IDrawTitle   : Boolean;
    IItems       : Array of TLegendItemText;
    IItemsWidth  : Array of TLegendItemWidth;
    IColorWidths : Array of Integer;
    IJustify     : Boolean;
    IMaxWidth    : Integer;
    ISpaceWidth  : Integer;
    ItemsTopMargin : Integer;
    ItemHeight   : Integer;
    XLegendBox   : Integer;
    XLegendText  : Integer;
    XLegendColor : Integer;

    function CalcCursor:TCursor;
    function CalcInverted:Boolean;
    Function CalcItemHeight:Integer;
    function CalcItemWidthColor(const AWidth:Integer):Integer;
    Procedure CheckBoxChanged;
    function ColumnWidth(const AColumn:Integer):Integer;
    function DividingWidth:Integer;
    Function FirstItemTop:Integer;
    function GetItem(Index:Integer):TLegendItem;
    Function GetRectLegend:TRect;
    function GetSelected:TTeeSelected;
    Function GetSymbolWidth:Integer;
    function GetTitle:TLegendTitle;
    Function GetVertical:Boolean;
    Function HasCheckBoxes:Boolean;
    procedure OffsetParent(const Positive:Integer);
    Procedure PrepareSymbolPen;
    Procedure SetAlignment(Const Value:TLegendAlignment);
    Procedure SetCheckBoxes(Const Value:Boolean);
    Procedure SetCheckBoxesStyle(Const Value:TCheckBoxesStyle);
    procedure SetCurrentPage(Const Value: Boolean);
    Procedure SetDividingLines(Const Value:TChartHiddenPen);
    procedure SetDrawBehind(Const Value: Boolean);
    Procedure SetFirstValue(Const Value:Integer);
    procedure SetFontSeriesColor(const Value: Boolean);
    procedure SetGlobalTransp(const Value: TTeeTransparency);
    Procedure SetHorizJustify(Const Value:TLegendJustify);
    Procedure SetHorizMargin(Const Value:Integer);
    Procedure SetInverted(Const Value:Boolean);
    Procedure SetLeftPercent(Const Value:Integer);
    Procedure SetLegendStyle(Const Value:TLegendStyle);
    Procedure SetMaxNumRows(Const Value:Integer);
    Procedure SetPosUnits(Const Value:TTeeUnits);
    Procedure SetResizeChart(Const Value:Boolean);
    Procedure SetSelected(Const Value:TTeeSelected);
    Procedure SetSeries(Const Value:TChartSeries);
    Procedure SetSymbol(Const Value:TLegendSymbol);
    Procedure SetSymbolWidth(Const Value:Integer);
    Procedure SetTextSymbolGap(Const Value:Integer);
    Procedure SetTextStyle(Const Value:TLegendTextStyle);
    procedure SetTitle(const Value: TLegendTitle);
    Procedure SetTopLeftPos(Const Value:Integer);
    Procedure SetTopPercent(Const Value:Integer);
    Procedure SetVertMargin(Const Value:Integer);
    Procedure SetVertSpacing(Const Value:Integer);
    function TitleWidth:Integer;
  protected
    InternalLegendStyle : TLegendStyle;
    FOnCalcSize         : TLegendCalcSize;


    Procedure CalcLegendStyle;
    Function DoMouseDown(Const x,y:Integer; PointIndex:Integer):Boolean;
    Function GetLegendSeries:TChartSeries;
  public
    NumCols        : Integer;
    NumRows        : Integer;
    ColumnWidthAuto: Boolean;
    ColumnWidths   : Array of Integer;

    Constructor Create(const AOwner:TCustomTeePanel); override;
    Destructor Destroy; override;

    Procedure CalcRect;

    Procedure Assign(Source:TPersistent); override;

    Function Clicked(x,y:Integer):Integer; overload;
    Function Clicked(const P:TPoint):Integer; overload;

    Procedure DrawLegend;
    Function FormattedValue(const ASeries:TChartSeries; ValueIndex:Integer):String;
    Function FormattedLegend(AIndex:Integer):String;
    Function ShouldDraw:Boolean;
    property TotalLegendItems:Integer read ITotalItems;

    { public properties }
    property RectLegend:TRect read GetRectLegend;
    property Vertical:Boolean read GetVertical;

    { to be published }
    property Alignment:TLegendAlignment read FAlignment write SetAlignment
                                        default laRight;
    property CheckBoxes:Boolean read FCheckBoxes write SetCheckBoxes default False;
    property CheckBoxesStyle:TCheckBoxesStyle read FCheckBoxesStyle write SetCheckBoxesStyle default cbsCheck;
    property ColorWidth:Integer read GetSymbolWidth write SetSymbolWidth default TeeDefaultLegendSymbolWidth;
    property CurrentPage:Boolean read FCurrentPage write SetCurrentPage default True;

    // property CustomSize:Boolean; <-- TODO

    property DividingLines:TChartHiddenPen read FDividingLines write SetDividingLines;
    property DrawBehind:Boolean read FDrawBehind write SetDrawBehind default False;
    property FirstValue:Integer read FFirstValue write SetFirstValue default 0;
    property FontSeriesColor:Boolean read FFontSeriesColor write SetFontSeriesColor default False;
    property GlobalTransparency:TTeeTransparency read FGlobalTransp write SetGlobalTransp default 0;
    property HorizJustify:TLegendJustify read FHorizJustify write SetHorizJustify default ljAutomatic;
    property HorizMargin:Integer read FHorizMargin write SetHorizMargin default 0;
    property Inverted:Boolean read FInverted write SetInverted default False;
    property Item[Index:Integer]:TLegendItem read GetItem; default;
    property Items:TLegendItems read FItems;
    property LastValue:Integer read ILastValue;
    property LeftPercent:Integer read FLeftPercent write SetLeftPercent default 0;
    property LegendStyle:TLegendStyle read FLegendStyle
                                      write SetLegendStyle default lsAuto;
    property MaxNumRows:Integer read FMaxNumRows write SetMaxNumRows default 0;
    property PositionUnits:TTeeUnits read FPosUnits write SetPosUnits default muPixels;
    property ResizeChart:Boolean read FResizeChart write SetResizeChart default True;
    property Selected:TTeeSelected read GetSelected write SetSelected;
    property Series:TChartSeries read FSeries write SetSeries;
    property Symbol:TLegendSymbol read FSymbol write SetSymbol;
    property TextStyle:TLegendTextStyle read FTextStyle
                                        write SetTextStyle default ltsLeftValue;
    property TextSymbolGap:Integer read FTextSymbolGap write SetTextSymbolGap default 2;
    property Title:TLegendTitle read GetTitle write SetTitle;
    property TopPercent:Integer read FTopPercent write SetTopPercent default 0;
    property TopPos:Integer read FTopLeftPos write SetTopLeftPos default 10;
    property VertMargin:Integer read FVertMargin write SetVertMargin default 0;
    property VertSpacing:Integer read FVertSpacing write SetVertSpacing default 0;
    property Visible;
  end;

  TChartLegend=class(TCustomChartLegend)
  published
    property Alignment;
    property Bevel;
    property BevelWidth;
    property Brush;
    property CheckBoxes;
    property CheckBoxesStyle;
    property Children;
    property Color;
    property ColorWidth;
    property CurrentPage;
    property CustomPosition;
    property DividingLines;
    property DrawBehind;
    property Emboss;
    property FirstValue;
    property Font;
    property FontSeriesColor;
    property Frame;
    property GlobalTransparency;
    property Gradient;
    property HorizJustify;
    property HorizMargin;
    property Inverted;
    property Left;
    property LeftPercent;
    property LegendStyle;
    property MaxNumRows;
    property Picture;
    property PositionUnits;
    property ResizeChart;
    property RoundSize;
    property Selected;
    property Shadow;
    property ShapeCallout;
    property ShapeStyle;
    property Symbol;
    property TextStyle;
    property TextSymbolGap;
    property Title;
    property Top;
    property TopPercent;
    property TopPos;
    property Transparency;
    property Transparent;
    property VertMargin;
    property VertSpacing;
    property Visible;

    property ClipText;
    property Cursor;
    property Margins;

    // Note: The "Text", "TextAlignment" and "VertTextAlign" properties
    // arent published here, as they are calculated by the Chart Legend.

    property TextFormat;
  end;

  TChartTitle=class(TStringsShape)
  private
    FAdjustFrame : Boolean;
    FVertMargin  : Integer;

    IOnTop       : Boolean;

    Function GetShapeBounds:TRect;
    Procedure SetAdjustFrame(Value:Boolean);
    Procedure SetVertMargin(const Value:Integer);
    function ShouldDraw:Boolean;
  public
    Constructor Create(const AOwner: TCustomTeePanel); override;

    Procedure Assign(Source:TPersistent); override;

    Function Clicked(x,y:Integer):Boolean; overload;
    Function Clicked(const P:TPoint):Boolean; overload;

    procedure DrawTitle;

    property TitleRect:TRect read GetShapeBounds;
  published
    property AdjustFrame:Boolean read FAdjustFrame write SetAdjustFrame
                                 default True;

    property Alignment default taCenter;
    property Bevel;
    property BevelWidth;
    property Brush;
    property Children;
    property ChildLayout;
    property Color;
    property CustomPosition;
    property Emboss;
    property Font;
    property Frame;
    property Gradient;
    property Left;
    property Picture;
    property Shadow;
    property ShapeCallout;
    property ShapeStyle;
    property TextFormat;
    property Top;
    property Transparency;
    property Transparent default True;
    property VertMargin:Integer read FVertMargin write SetVertMargin
                                default TeeTitleFootDistance;
    property Visible;
  end;

  TChartFootTitle=class(TChartTitle)
  public
    Constructor Create(const AOwner: TCustomTeePanel); override;
  end;

  TChartClick=procedure( Sender:TCustomChart;
                         Button:TMouseButton;
                         Shift: TShiftState;
                         X, Y: Integer) of object;

  TChartClickAxis=procedure( Sender:TCustomChart;
                             Axis:TChartAxis;
                             Button:TMouseButton;
                             Shift: TShiftState;
                             X, Y: Integer) of object;

  TChartClickSeries=procedure( Sender:TCustomChart;
                               Series:TChartSeries;
                               ValueIndex:Integer;
                               Button:TMouseButton;
                               Shift: TShiftState;
                               X, Y: Integer) of object;

  TChartClickTitle=procedure( Sender:TCustomChart;
                              ATitle:TChartTitle;
                              Button:TMouseButton;
                              Shift: TShiftState;
                              X, Y: Integer) of object;

  TOnGetLegendPos=Procedure( Sender:TCustomChart; Index:Integer;
                             var X,Y,XColor:Integer) of object;

  TOnGetLegendRect=Procedure( Sender:TCustomChart; var Rect:TRect) of object;

  TChartBackWall=class(TChartWall)
  protected
    procedure InitEmboss(const AEmboss:TTeeEmboss); override;
    procedure InitShadow(const AShadow:TTeeShadow); override;
  public
    Constructor Create(const AOwner: TCustomTeePanel); override;
  published
    property Color default {$IFDEF FMX}TAlphaColors.Silver{$ELSE}clSilver{$ENDIF};
    property Emboss;
    property Picture;
    property Shadow;
    property Transparent default True;
  end;

  TChartRightWall=class(TChartWall)
  published
    property Color default {$IFDEF FMX}TAlphaColors.Silver{$ELSE}clSilver{$ENDIF};
    property Picture;
    property Visible default False;
  end;

  TChartBottomWall=class(TChartWall)
  published
    property Picture;
  end;

  TChartLeftWall=class(TChartWall)
  published
    property Picture;
  end;

  TChartWalls=class(TPersistent)
  private
    FBack   : TChartBackWall;
    FBottom : TChartBottomWall;
    FChart  : TCustomChart;
    FLeft   : TChartLeftWall;
    FRight  : TChartRightWall;

    function GetRight:TChartRightWall;
    function GetVisible: Boolean;
    procedure SetBack(const Value: TChartBackWall);
    procedure SetBottom(const Value: TChartBottomWall);
    procedure SetLeft(const Value: TChartLeftWall);
    procedure SetRight(const Value: TChartRightWall);
    procedure SetSize(const Value: Integer);
    procedure SetVisible(const Value: Boolean);
  public
    Constructor Create(const Chart:TCustomChart);
    Destructor Destroy; override;
    Procedure Assign(Source:TPersistent); override;
    procedure Hide;
    procedure Show;

    property Back:TChartBackWall read FBack write SetBack;
    property Bottom:TChartBottomWall read FBottom write SetBottom;
    property Left:TChartLeftWall read FLeft write SetLeft;
    property Right:TChartRightWall read GetRight write SetRight;
    property Size:Integer write SetSize;
    property Visible:Boolean read GetVisible write SetVisible;
  end;

  TChartAllowScrollEvent=Procedure( Sender:TChartAxis; var AMin,AMax:TAxisValue;
                                    var AllowScroll:Boolean ) of object;

  TCustomChart=class(TCustomAxisPanel)
  private
    FColorPaletteIndex : Integer;
    FFoot              : TChartTitle;
    FLegend            : TChartLegend;
    FSavedScales       : TAllAxisSavedScales;
    FSubFoot           : TChartTitle;
    FSubTitle          : TChartTitle;
    FTitle             : TChartTitle;
    FWalls             : TChartWalls;
    {$IFDEF TEEOCX}
    FFixedPrnFontPPI    : Integer; //PixelsPerInch
    FMouseWheelToParent : Boolean;
    {$ENDIF}

    { events }
    FOnAllowScroll     : TChartAllowScrollEvent;
    FOnClickAxis       : TChartClickAxis;
    FOnClickBackGround : TChartClick;
    FOnClickLegend     : TChartClick;
    FOnClickSeries     : TChartClickSeries;
    FOnClickTitle      : TChartClickTitle;
    FOnGetLegendPos    : TOnGetLegendPos;
    FOnGetLegendRect   : TOnGetLegendRect;
    FOnGetLegendText   : TOnGetLegendText;

    Function ActiveSeriesUseAxis:Boolean;
    Function ActiveSeriesCanZoomScroll:Boolean;
    procedure BroadcastMouseEvent(AEvent:TChartMouseEvent;
                         Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure BroadcastWheelEvent(AEvent:TWheelMouseEvent;
                         WheelDelta:Integer; X, Y: Integer);
    procedure BroadcastDragDropEvent(AEvent:TChartDragDropEvent; Source: TObject; X, Y: Integer; State: TDragState;
      var Accept: Boolean; var DragObject: TDragObject);
    procedure BroadcastKeyEvent(AEvent:TChartKeyEvent; var Key:Word; var Key1: Char; Shift: TShiftState);
    Function CalcNeedClickedPart(Pos:TPoint; Needed:Boolean):TChartClickedPart;
    function CanWheelScroll:Boolean;
    Procedure DrawBackWall;
    Procedure DrawLeftWall;
    Procedure DrawRightWall;
    Function DrawWallFirst(APos:Integer):Boolean;
    function GetBackWall: TChartBackWall;
    function GetBottomWall: TChartBottomWall;
    function GetFoot:TChartTitle;
    Function GetFrame:TChartPen;
    function GetLeftWall: TChartLeftWall;
    function GetRightWall: TChartRightWall;
    function GetSubFoot:TChartTitle;
    function GetSubTitle:TChartTitle;
    function GetTitle:TChartTitle;
    procedure InternalWheel(WheelDelta: Integer);
    Procedure PrepareWallCanvas(const AWall:TChartWall);
    procedure ReadBackColor(Reader: TReader);
    procedure ReadColorPalette(Reader: TReader);
    procedure ReadColorPaletteIndex(Reader: TReader);
    Procedure RestoreScales(var Saved:TAllAxisSavedScales);
    Function SaveScales:TAllAxisSavedScales;
    Procedure ScrollVerticalAxes(Up:Boolean);
    Procedure SetBackColor(Value:TColor);
    Procedure SetBackWall(const Value:TChartBackWall);
    Procedure SetBottomWall(const Value:TChartBottomWall);
    procedure SetColorPaletteIndex(const Value:Integer);
    Procedure SetFoot(const Value:TChartTitle);
    Procedure SetFrame(const Value:TChartPen);
    Procedure SetLeftWall(const Value:TChartLeftWall);
    Procedure SetLegend(const Value:TChartLegend);
    Procedure SetRightWall(const Value:TChartRightWall);
    procedure SetSubFoot(const Value: TChartTitle);
    procedure SetSubTitle(const Value: TChartTitle);
    procedure SetTitle(const Value:TChartTitle);
    procedure SetWalls(const Value:TChartWalls);

    procedure WriteColorPalette(Writer: TWriter);
    procedure WriteColorPaletteIndex(Writer: TWriter);

    procedure AddZoomSnapshot;
    procedure RemoveZoomSnapshot;
    procedure ZoomBackOne;
  protected
    RestoredAxisScales : Boolean;

    Procedure CalcWallsRect; override;
    Function CalcWallSize(Axis:TChartAxis):Integer; override;
    Procedure CalcZoomPoints; dynamic;
    Procedure DefineProperties(Filer:TFiler); override;

    {$IFDEF FMX}
    procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean); override;
    {$ELSE}
    function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
                          MousePos: TPoint): Boolean; override;
    function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    {$ENDIF}

    Procedure DoZoom(Const TopI,TopF,BotI,BotF,LefI,LefF,RigI,RigF:TAxisValue);
    Function DrawLeftWallFirst:Boolean;
    Function DrawRightWallAfter:Boolean;
    procedure DrawLegendShape(const Rect:TRect; const AGraphic:TGraphic=nil); override;
    procedure DrawTitlesAndLegend(BeforeSeries:Boolean); override;
    Procedure DrawWalls; override;

    function Form3DClass:TClass; dynamic;

    Function GetBackColor:TColor; override;

    {$IFNDEF D11}
    Procedure GetChildren(Proc:TGetChildProc; Root:TComponent); override;
    {$ENDIF}

    {$IFDEF FMX}
    Procedure InternalDraw(Const UserRectangle:TRect); override;
    {$ENDIF}

    Function InternalFormattedLegend( const ALegend:TCustomChartLegend;
                                      SeriesOrValueIndex:Integer):String;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
                        X, Y: TCoordinate); override;

    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
                      X, Y: TCoordinate); override;

    procedure MouseMove(Shift: TShiftState;
                        X, Y: TCoordinate); override;

    {$IFDEF FMX}
    procedure BeginAutoDrag; override;
    procedure DragDrop(const Data: TDragObject; const Point: TPoint); override;
    procedure DragEnd; override;
    procedure DragOver(const Data: TDragObject; const Point: TPoint;
                 var {$IFDEF D20}Operation: TDragOperation{$ELSE}Accept: Boolean{$ENDIF}); override;
    procedure KeyDown(var Key: Word; var KeyChar: System.WideChar; Shift: TShiftState); override;
    procedure KeyUp(var Key: Word; var KeyChar: System.WideChar;
      Shift: TShiftState); override;
    {$ELSE}
    procedure DoEndDrag(Target: TObject; X, Y: Integer); override;
    procedure DoStartDrag(var DragObject: TDragObject); override;
    procedure DragOver(Source: TObject; X, Y: Integer; State: TDragState;
      var Accept: Boolean); override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure KeyPress(var Key: Char); override;
    {$ENDIF}

    {$IFDEF FMX}
    {$IFDEF D17}
    property Action;
    property Anchors;
    {$ENDIF}
    {$ENDIF}
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    Procedure Assign(Source:TPersistent); override;
    Function AxisTitleOrName(const Axis:TChartAxis):String;
    Procedure CalcClickedPart(Pos:TPoint; out Part:TChartClickedPart);

    {$IFNDEF FMX}
    procedure DragDrop(Source: TObject; X, Y: Integer); override;
    {$ENDIF}

    Procedure FillSeriesSourceItems(ASeries:TChartSeries; Proc:TGetStrProc); dynamic;
    Procedure FillValueSourceItems(ValueList:TChartValueList; Proc:TGetStrProc); dynamic;
    Function GetASeries:TChartSeries; { return first active Series }

    {$IFDEF D11}
    Procedure GetChildren(Proc:TGetChildProc; Root:TComponent); override;
    {$ENDIF}

    Function IsFreeSeriesColor(AColor:TColor; CheckBackground:Boolean;
                               ASeries:TChartSeries=nil):Boolean; override;
    Procedure NextPage;
    Procedure PreviousPage;
    Procedure RemoveAllSeries;
    Procedure ClearChart;
    Procedure SeriesDown(const ASeries:TChartSeries);
    Procedure SeriesUp(const ASeries:TChartSeries);
    procedure UndoZoom; override;
    Procedure ZoomPercent(Const PercentZoom:Double);
    Procedure ZoomRect(Const Rect:TRect);
    Function FormattedValueLegend(ASeries:TChartSeries; ValueIndex:Integer):String; override;
    Function FormattedLegend(SeriesOrValueIndex:Integer):String;

    property BackColor:TColor read GetBackColor write SetBackColor; { compatibility }
    property ColorPaletteIndex:Integer read FColorPaletteIndex write SetColorPaletteIndex default -1; //TV52015978
    property Walls:TChartWalls read FWalls write SetWalls;

    {$IFDEF TEEOCX}
    property FixedPrnFontPPI:Integer read FFixedPrnFontPPI write FFixedPrnFontPPI default 0;
    property MouseWheelToParent:Boolean read FMouseWheelToParent write FMouseWheelToParent default true;
    {$ENDIF}

    { to be published }
    property BackWall:TChartBackWall read GetBackWall write SetBackWall;
    property Frame:TChartPen read GetFrame write SetFrame;
    property BottomWall:TChartBottomWall read GetBottomWall write SetBottomWall;
    property Foot:TChartTitle read GetFoot write SetFoot;
    property LeftWall:TChartLeftWall read GetLeftWall write SetLeftWall;
    property Legend:TChartLegend read FLegend write SetLegend;
    property RightWall:TChartRightWall read GetRightWall write SetRightWall;
    property SubFoot:TChartTitle read GetSubFoot write SetSubFoot;
    property SubTitle:TChartTitle read GetSubTitle write SetSubTitle;
    property Title:TChartTitle read GetTitle write SetTitle;

    { events }
    property OnAllowScroll:TChartAllowScrollEvent read FOnAllowScroll write FOnAllowScroll;
    property OnClickAxis:TChartClickAxis read FOnClickAxis write FOnClickAxis;
    property OnClickBackground:TChartClick read FOnClickbackground write FOnClickBackGround;
    property OnClickLegend:TChartClick read FOnClickLegend write FOnClickLegend;
    property OnClickSeries:TChartClickSeries read FOnClickSeries write FOnClickSeries;
    property OnClickTitle:TChartClickTitle read FOnClickTitle write FOnClickTitle;
    property OnGetLegendPos:TOnGetLegendPos read FOnGetLegendPos write FOnGetLegendPos;
    property OnGetLegendRect:TOnGetLegendRect read FOnGetLegendRect write FOnGetLegendRect;
    property OnGetLegendText:TOnGetLegendText read FOnGetLegendText write FOnGetLegendText;

    { TPanel properties }
    property Align;

    property BevelInner;
    property BevelOuter;
    property BevelWidth;
    property BorderWidth;
    property Color;

    {$IFNDEF FMX}
    property UseDockManager default True;
    property DockSite;
    property DragCursor;
    {$ENDIF}

    property DragMode;
    property Enabled;

    {$IFDEF FMX}
    {$IFDEF D23}
    property ParentShowHint;
    {$ENDIF}
    {$ELSE}
    property ParentColor;
    property ParentShowHint;
    property PopupMenu;
    property TabStop;
    {$ENDIF}

    property ShowHint;
    property TabOrder;

    {$IFNDEF FMX}
    {$IFDEF D14}
    property Touch;
    {$ENDIF}
    {$ENDIF}

    property Visible;

    {$IFDEF FMX}
    {$IFDEF D17}
    //property Action;
    //property Anchors;
    {$ENDIF}
    {$ELSE}
    property Anchors;
    {$ENDIF}

    {$IFDEF FMX}
    property ClipChildren default True;
    property Locked;
    {$ELSE}

    property AutoSize;
    property Constraints;
    property DragKind;

    {$IFNDEF LCL}
    property Locked;
    {$ENDIF}

    {$ENDIF}

    { TPanel events }
    property OnClick;

    {$IFDEF FMX}
    property OnDragEnter;
    property OnDragLeave;
    property OnDragOver;
    property OnDragDrop;
    property OnDragEnd;
    property OnEnter;
    property OnExit;
    property OnPainting;
    property OnPaint;
    property OnApplyStyleLookup;
    property OnCanFocus;

    {$IFDEF D25}
    property OnResized;
    {$ENDIF}

    {$ELSE}
    property OnContextPopup;
    property OnEndDrag;
    property OnStartDrag;
    property OnEnter;
    property OnExit;
    property OnKeyPress;
    property OnDragDrop;
    property OnDragOver;
    property OnMouseWheelDown;
    property OnMouseWheelUp;
    {$ENDIF}

    property OnDblClick;
    property OnKeyDown;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;
    property OnMouseWheel;

    {$IFNDEF FMX}
    {$IFNDEF LCL}
    property OnCanResize;
    {$ENDIF}
    property OnConstrainedResize;
    property OnDockDrop;
    property OnDockOver;
    property OnEndDock;
    property OnGetSiteInfo;
    property OnStartDock;
    property OnUnDock;
    {$ENDIF}

    {$IFDEF D10}
    property OnMouseEnter;
    property OnMouseLeave;
    {$ENDIF}

    {$IFDEF D14}
    {$IFNDEF FMX}
    property OnGesture;
    {$ENDIF}
    {$ENDIF}
  end;

  {$IFDEF D16}
  [ComponentPlatformsAttribute(TeeAllComponentPlatformIDs)]
  {$ENDIF}
  TChart=class(TCustomChart)
  {$IFNDEF FMX}
  public
    property DockManager;
  {$ENDIF}
  published
    { TCustomChart Properties }
    property AllowPanning;
    property BackImage;
    property BackImageInside;
    property BackImageMode;
    property BackImageTransp;
    property BackWall;
    property Border;
    property BorderRound;
    property BottomWall;
    property Foot;
    property Gradient;
    property LeftWall;
    property Legend;
    property MarginBottom;
    property MarginLeft;
    property MarginRight;
    property MarginTop;
    property MarginUnits;
    property PrintProportional;
    property RightWall;
    property ScrollMouseButton;
    property SubFoot;
    property SubTitle;
    property Title;

    { TCustomChart Events }
    property OnAllowScroll;
    property OnClickAxis;
    property OnClickBackground;
    property OnClickLegend;
    property OnClickSeries;
    property OnClickTitle;
    property OnGetLegendPos;
    property OnGetLegendRect;
    property OnScroll;
    property OnUndoZoom;
    property OnZoom;

    { TCustomAxisPanel properties }
    property Animations;
    property AxisBehind;
    property AxisVisible;
    property BottomAxis;
    property Chart3DPercent;
    property ClipPoints;
    property DepthAxis;
    property DepthTopAxis;
    property CustomAxes;
    property Emboss;
    property Frame;
    property Hover;
    property LeftAxis;
    property Monochrome;
    property Pages;
    property Panning;
    property RightAxis;
    property SeriesGroups;
    property SeriesList;
    property Shadow;
    property Tools;
    property TopAxis;
    property View3D;
    property View3DOptions;
    property View3DWalls;
    property Zoom;
    property ZoomWheel;

    { TCustomAxisPanel events }
    property OnAddSeries;
    property OnAfterDraw;
    property OnBeforeDrawAxes;
    property OnBeforeDrawChart;
    property OnBeforeDrawSeries;
    property OnBeforePrint;
    property OnGetAxisLabel;
    property OnGetLegendText;
    property OnGetNextAxisLabel;
    property OnPageChange;
    property OnRemoveSeries;
    property OnValidDataSource;

    { TPanel properties }
    property Align;
    property BevelInner;
    property BevelOuter;
    property BevelWidth;
    property BorderWidth;
    property Color;

    {$IFDEF FMX}

    {$IFDEF D23}
    property ParentShowHint;
    {$ENDIF}

    property OnDragEnter;
    property OnDragLeave;
    property OnDragOver;
    property OnDragDrop;
    property OnDragEnd;
    property OnEnter;
    property OnExit;
    property OnPainting;
    property OnPaint;
    property OnApplyStyleLookup;
    property OnCanFocus;

    property ClipChildren default True;
    property Locked;
    {$ELSE}

    property UseDockManager default True;
    property DockSite;
    property DragCursor;
    property DragMode;
    property ParentColor;
    property ParentShowHint;
    property PopupMenu;
    property TabStop;

    property AutoSize;
    property Constraints;

    property OnDragDrop;
    property OnDragOver;
    property OnEndDrag;
    property OnStartDrag;
    property OnEnter;
    property OnExit;
    property OnKeyPress;

    {$ENDIF}

    property Enabled;
    property ShowHint;
    property TabOrder;

    {$IFDEF D14}
    {$IFNDEF FMX}
    property Touch;
    {$ENDIF}
    {$ENDIF}

    property Visible;

    {$IFDEF FMX}

    {$IFDEF D17}
    property Action;
    property Anchors;
    //property HelpContext;
    //property HelpKeyword;
    //property HelpType;
    //property StyleLookup;
    {$ENDIF}

    property ClipParent default False;
    property Cursor default crDefault;

    {$IFNDEF D21} // Removed in XE7
    property DesignVisible default True;
    {$ENDIF}

    property DragMode default TDragMode.dmManual;
    property EnableDragHighlight default True;
    property Height;
    property HitTest default True;
    property Margins;
    property Opacity;
    property Padding;
    property PopupMenu;
    property Position;
    property RotationAngle;
    property RotationCenter;
    property Scale;

    {$IFDEF D21}
    property Size;
    {$ENDIF}

    property Width;

    {$ELSE}
    property Anchors;
    {$ENDIF}

    {$IFNDEF FMX}
    property DragKind;
    {$IFNDEF LCL}
    property Locked;
    {$ENDIF}
    {$ENDIF}

    { TPanel events }
    property OnClick;

    {$IFNDEF FMX}
    property OnContextPopup;
    {$ENDIF}

    property OnDblClick;

    property OnKeyDown;
    property OnKeyUp;
    property OnMouseDown;

    {$IFDEF D10}
    property OnMouseEnter;
    property OnMouseLeave;
    {$ENDIF}

    property OnMouseMove;
    property OnMouseUp;
    property OnResize;

    property OnMouseWheel;

    {$IFNDEF FMX}

    {$IFNDEF LCL}
    property OnCanResize;
    {$ENDIF}

    property OnMouseWheelDown;
    property OnMouseWheelUp;
    property OnConstrainedResize;
    {$ENDIF}

    {$IFNDEF FMX}
    property OnDockDrop;
    property OnDockOver;
    property OnEndDock;

    {$IFDEF D14}
    property OnGesture;
    {$ENDIF}

    property OnGetSiteInfo;
    property OnStartDock;
    property OnUnDock;
    {$ENDIF}
  end;

  TTeeSeriesTypes=class({$IFDEF D18}TList<TTeeSeriesType>{$ELSE}TList{$ENDIF})
  private
    {$IFNDEF D18}
    Function Get(Index:Integer):TTeeSeriesType;
    {$ENDIF}
  public
    {$IFNDEF D18}
    Procedure Clear; override;
    {$ENDIF}

    Function Find(const ASeriesClass:String):TChartSeriesClass; overload;
    Function Find(const ASeriesClass:TChartSeriesClass; SubIndex:Integer=0):TTeeSeriesType; overload;

    {$IFNDEF D18}
    property Items[Index:Integer]:TTeeSeriesType read Get; default;
    {$ENDIF}
  end;

  TTeeToolTypes=class(TList{$IFDEF TEELISTMETA}<TTeeCustomToolClass>{$ENDIF}) // QC115377
  {$IFNDEF TEELISTMETA}
  private
    Function Get(Index:Integer):TTeeCustomToolClass;
  public
    property Items[Index:Integer]:TTeeCustomToolClass read Get; default;
  end
  {$ENDIF};

  {$IFNDEF FMX}
  TTeeDragObject = class(TDragObject)
  private
    FPart: TChartClickedPart;
  protected
    function GetDragCursor(Accepted: Boolean; X, Y: Integer): TCursor; override;
    {$IFNDEF LCL}
    procedure Finished(Target: TObject; X, Y: Integer; Accepted: Boolean); override;
    {$ENDIF}
  public
    constructor Create(Const APart: TChartClickedPart); {$IFDEF LCL}reintroduce;{$ENDIF}
    property Part: TChartClickedPart read FPart;
  end;
  {$ENDIF}

  // Used by TDataImport and TeeURL TImportChart
  TCustomChartLink = class(TComponent)
  private
    procedure SetChart(const Value: TCustomChart);
  protected
    FChart : TCustomChart;

    procedure Notification( AComponent: TComponent;
                            Operation: TOperation); override;
  public
    property Chart:TCustomChart read FChart write SetChart;
  end;

  TColorPalette=record
    Name : String;
    Colors : Array of TColor;
  end;

  TColorPalettes=class
  private
    IPalettes : Array of TColorPalette;

    procedure Add(const AName:String; const APalette:Array of TColor);
    procedure CopyPalette(const Index:Integer; const APalette:Array of TColor);
    function GetName(const Index:Integer):String;
    procedure SetName(const Index:Integer; const AName:String);
  public
    class procedure AddTo(const Items:TStrings; AddDefault:Boolean=False);
    class procedure ApplyPalette(const Chart:TCustomAxisPanel; Index:Integer); overload;
    class procedure ApplyPalette(const Chart:TCustomAxisPanel; const Palette:Array of TColor); overload;

    function Count:Integer;
    function IndexOf(const AName:String):Integer;

    class procedure GetPalette(Index:Integer; out Palette:TColorArray);
    class procedure RegisterPalette(const AName:string; const APalette: Array of TColor);

    property Name[const Index:Integer]:String read GetName write SetName; default;
  end;

  function ColorPalettes : TColorPalettes;

var
  TeeSeriesTypes: TTeeSeriesTypes=nil; { List of Series types }
  TeeToolTypes: TTeeToolTypes=nil; { List of Tool types }
  TeeAnimationTypes: TTeeToolTypes=nil; { List of Animation types }

{ Adds a new Series component definition for the Gallery }
{ Setting ANumGallerySeries to zero makes the Series to not appear on the
  Gallery. }
Procedure RegisterTeeSeries( ASeriesClass:TChartSeriesClass;
                             ADescription,AGalleryPage:PString;
                             ANumGallerySeries : Integer ); overload;

// Same as above, at default "Standard" tab with NumSeries = 2
Procedure RegisterTeeSeries( ASeriesClass:TChartSeriesClass;
                             ADescription:PString); overload;

{ Adds a new Function component definition for the Gallery }
Procedure RegisterTeeFunction( AFunctionClass:TTeeFunctionClass;
                               ADescription,AGalleryPage:PString;
                               ANumGallerySeries : Integer=1 );

{ Adds a new Function component to the basic functions gallery }
Procedure RegisterTeeBasicFunction( AFunctionClass:TTeeFunctionClass;
                                    ADescription:PString);

{ Adds a new Series & Function components definition for the Gallery }
Procedure RegisterTeeSeriesFunction( ASeriesClass:TChartSeriesClass;
                                     AFunctionClass:TTeeFunctionClass;
                                     ADescription,AGalleryPage:PString;
                                     ANumGallerySeries:Integer=1;
                                     ASubIndex:Integer=0);

{ Removes Series from Gallery }
Procedure UnRegisterTeeSeries(Const ASeriesList:Array of TChartSeriesClass);

{ Removes Functions from Gallery }
Procedure UnRegisterTeeFunctions(Const AFunctionList:Array of TTeeFunctionClass);

{ Assigns all Series properties from Old to New. (Data values ARE assigned). }
Procedure AssignSeries(var OldSeries,NewSeries:TChartSeries);

{ Creates a new "class" TeeFunction and sets its ParentSeries to ASeries }
Function CreateNewTeeFunction( const ASeries:TChartSeries;
                               AClass:TTeeFunctionClass):TTeeFunction;

{ Creates a new "class" TChartSeries and sets its ParentChart to AChart }
Function CreateNewSeries( AOwner:TComponent;
                          const AChart:TCustomAxisPanel;
                          AClass:TChartSeriesClass;
                          AFunctionClass:TTeeFunctionClass=nil):TChartSeries;

// Creates a new Series that is a copy of ASeries.
Function CloneChartSeries(const ASeries:TChartSeries):TChartSeries; overload;
Function CloneChartSeries(const ASeries:TChartSeries; const AChart:TCustomAxisPanel):TChartSeries; overload;
Function CloneChartSeries(const ASeries:TChartSeries; const AOwner:TComponent; const AChart:TCustomAxisPanel):TChartSeries; overload;

// Duplicates a Chart Tool. Adds the new Tool to ATool.ParentChart
function CloneChartTool(const ATool:TTeeCustomTool; const AOwner:TComponent):TTeeCustomTool;

// Duplicates a Chart Animation. Adds the new Animation to AAnimation.ParentChart
function CloneChartAnimation(const AAnimation:TTeeCustomAnimation; const AOwner:TComponent):TTeeCustomAnimation;

// Copies all properties, Series and Tools from Origin to Dest
procedure CloneChart(const Dest,Origin:TCustomChart; const AOwner:TComponent; ForceManualData:Boolean);

Function ChangeSeriesType(var ASeries:TChartSeries; NewType:TChartSeriesClass ):TChartSeries;

procedure ChangeAllSeriesType( const AChart:TCustomChart; AClass:TChartSeriesClass );

Function GetNewSeriesName(const AOwner:TComponent):TComponentName;

Procedure RegisterTeeTools({$IFNDEF BCB}const{$ENDIF} ATools:Array of TTeeCustomToolClass);
Procedure UnRegisterTeeTools({$IFNDEF BCB}const{$ENDIF} ATools:Array of TTeeCustomToolClass);

Procedure RegisterTeeAnimations({$IFNDEF BCB}const{$ENDIF} AAnimations:Array of TTeeCustomAnimationClass);
Procedure UnRegisterTeeAnimations({$IFNDEF BCB}const{$ENDIF} AAnimations:Array of TTeeCustomAnimationClass);

{ Returns the name of a Series in itīs "gallery" style:
   TLineSeries returns "Line"
   TPieSeries returns "Pie"
   etc.
}
Function GetGallerySeriesName(const ASeries:TChartSeries):String;

{ Draws the Series "Legend" on the specified rectangle and Canvas }
Procedure PaintSeriesLegend(const ASeries:TChartSeries; const ACanvas:TCanvas; Const R:TRect);

var
  TeePageNumToolClass : TTeeCustomToolClass = nil;

type
  // Note: Order of type declarations here is important to avoid BCB bug:
  TTeeCanvasClass=class of TCanvas3D;

  TChartThemeClass=class of TChartTheme;

  TTeeThemeSelectorProc=Function(const Chart:TCustomChart):TChartThemeClass;

  TChartTheme=class(TPersistent)
  public
    Chart : TCustomChart;

    constructor Create(AChart: TCustomChart); virtual;

    procedure Apply; virtual;
    function Description:string; virtual;
    procedure SetDefaultValues; virtual;
  end;

var
  TeeThemeSelectorHook : TTeeThemeSelectorProc=nil;
  TeeNewChartHook      : TTeeThemeSelectorProc=nil;
  TeeAntiAliasCanvas   : TTeeCanvasClass=nil;

const
  ExcelPalette:Array[0..21] of TColor=
  (
    $FF9999,
    $663399,
    $CCFFFF,
    $FFFFCC,
    $660066,
    $8080FF,
    $CC6600,
    $FFCCCC,
    $800000,
    $FF00FF,
    $00FFFF,
    $FFFF00,
    $800080,
    $000080,
    $808000,
    $FF0000,
    $FFCC00,
    $FFFFCC,
    $CCFFCC,
    $00FFFF,
    $FFCC99,
    $CC99FF
  );

  VictorianPalette:Array[0..10] of TColor=
  (
    $A1A55D,
    $3153C4,
    $0996E7,
    $4AE8F6,
    $A7A2B1,
    $84A7C9,
    $51798C,
    $B7CDD8,
    $536508,
    $7BD8F7,
    $846401
  );

  PastelsPalette:Array[0..13] of TColor=
  (
    $FFFFCC,
    $CCFFFF,
    $FFCCCC,
    $CCCC00,
    $CCCCCC,
    $999900,
    $999999,
    $CCCCFF,
    $66CCFF,
    $FFCCCC,
    $9999FF,
    $99FFFF,
    $FFCC99,
    $CCFFCC
  );

  SolidPalette:Array[0..7] of TColor=
  (
    $FF0000,
    $0000FF,
    $00FF00,
    $00CCFF,
    $404040,
    $00FFFF,
    $C000FF,
    $FFFFFF
  );

  ClassicPalette:Array[0..11] of TColor=
  (
    $FF0000,
    $00FF00,
    $FFFF00,
    $0000FF,
    $FF00FF,
    $00FFFF,
    $800000,
    $008000,
    $808000,
    $000080,
    $800080,
    $008080
  );

  WebPalette:Array[0..8] of TColor=
  (
    $00A5FF,
    $CE0000,
    $00CE00,
    $40FFFF,
    $FFFF40,
    $FF40FF,
    $0040FF,
    $A58080,
    $408080
  );

  ModernPalette:Array[0..13] of TColor=
  (
    $6699FF,
    $6666FF,
    $FFCC99,
    $669966,
    $99CCCC,
    $CC6699,
    $6666CC,
    $99CCFF,
    $FF6699,
    $CCCCCC,
    $CCFF66,
    $FF9966,
    $996699,
    $FFCCCC 
  );

  RainbowPalette:Array[0..24] of TColor=
  (
    $000099,
    $0000C3,
    $0000EE,
    $001AFF,
    $0046FF,
    $0073FF,
    $009FFF,
    $00CBFF,
    $00F7FF,
    $08F4E3,
    $11E7C3,
    $1BDAA3,
    $25CD83,
    $2EC063,
    $38B342,
    $42A622,
    $4B9A02,
    $6A870C,
    $8A751A,
    $AA6328,
    $CB5036,
    $EB3E44,
    $FF2A61,
    $FF1596,
    $FF00CC
  );

  WindowsXPPalette:Array[0..12] of TColor=
  (
    $FE9B82,
    $24D1FC,
    $0DBC7C,
    $2F85FD,
    $FCFEFD,
    $214EE2,
    $D63829,
    $0094B7,
    $00865A,
    $0046D2,
    $FAE5D3,
    $D8D8D8,
    $7B715F
  );

  MacOSPalette:Array[0..15] of TColor=
  (
    $FFFFFF,
    $05F3FC,
    $0264FF,
    $0608DD,
    $8408F2,
    $A50046,
    $D40000,
    $EAAB02,
    $14B71F,
    $116400,
    $052C56,
    $3A7190,
    $C0C0C0,
    $808080,
    $404040,
    $000000
  );

  WindowsVistaPalette:Array[0..14] of TColor=
  (
    $D21F00,
    $0102E0,
    $02661E,
    $7ECDE8,
    $ACABAF,
    $D9D0A4,
    $3C3B3D,
    $31DD95,
    $01009E,
    $74F7DC,
    $FDFD45,
    $748ED1,
    $91D8A0,
    $657AD5,
    $D99596
  );

  GrayScalePalette:Array[0..14] of TColor=
  (
    $f0f0f0,
    $e0e0e0,
    $d0d0d0,
    $c0c0c0,
    $b0b0b0,
    $a0a0a0,
    $909090,
    $808080,
    $707070,
    $606060,
    $505050,
    $404040,
    $303030,
    $202020,
    $101010
  );

  OperaPalette:Array[0..11] of TColor=
  (
    $a36644,
    $359cf3,
    $144cf1,
    $a8974e,
    $6b402b,
    $637b1d,
    $0e08b3,
    $5dc0f2,
    $9eb75d,
    $707070,
    $8deaf3,
    $b4b4b4
  );

  WarmPalette:Array[0..7] of TColor=
  (
    $8deaf3,
    $5dc0f2,
    $359cf3,
    $1c81f5,
    $156bf3,
    $144cf1,
    $0a18e6,
    $0e08b3
  );

  CoolPalette:Array[0..7] of TColor=
  (
    $6b402b,
    $8c543b,
    $a36644,
    $a8974e,
    $9eb75d,
    $8aa041,
    $7d922b,
    $637b1d
  );

  OnBlackPalette:Array[0..3] of TColor=
  (
    $5ae6c8,
    $dc965a,
    $285ae6,
    $0fa0e6
  );

  MetroPalette:Array[0..9] of TColor=
  (
    $ff00a2, // Purple
    $9700ff, // Magenta
    $a9ab00, // Teal
    $26bf8c, // Lime
    $0050a0, // Brown
    $b871e6, // Pink
    $0996f0, // Orange
    $e2a11b, // Blue
    $0014e5, // Red
    $339933  // Green
  );

  iOSPalette:Array[0..11] of TColor=
  (
    $FDCC48,  // Pale blue
    $4DCD86,  // Pale green
    $9A9A9A,  // Gray
    $84D2F7,  // Sand
    $985F46,  // Blue
    $415FA6,  // Maroon
    $487B9C,
    $D2618C,  // Purple
    $AC8E75,
    $0489FF,
    $3E39E2,  // Red
    $97D1B5
  );

  AndroidPalette:Array[0..14] of TColor=
  (
    $E5B533,
    $CC66AA,
    $00CC99,
    $33BBFF,
    $4444FF,
    $CC9900,
    $CC3399,
    $009966,
    $0088FF,
    $0000CC,
    $E5C488,
    $DEB1CF,
    $00FFCC,
    $91ECFF,
    $BBBBFF
  );

  LookoutPalette:Array[0..11] of TColor=
  (
    $D69977,
    $68CFFF,
    $477FFF,
    $DBCA81,
    $9E735E,
    $96AE50,
    $413BE6,
    $90F3FF,
    $D1EA90,
    $A3A3A3,
    $C0FFFF,
    $E7E7E7
  );   

  SeawashPalette:Array[0..11] of TColor=
  (
    $055CDC,
    $00ACFF,
    $B8C56E,
    $A9D0E8,
    $7D8B97,
    $A7BAC7,
    $D6DAC1,
    $9FC9FF,
    $E9D1AC,
    $9B926D,
    $97E3D3,
    $C3F5FF
  );

  ActivityPalette:Array[0..9] of TColor=
  (
    $07D9F8,
    $070DF5,
    $8F03F7,
    $4010D2,
    $078DF6,
    $FF00FB,
    $08FFF9,
    $0749F5,
    $8303C0,
    $19E4D6
  );


implementation

Uses {$IFDEF LCL}
     GraphType, LCLType,
     {$ENDIF}

     {$IFDEF FMX}
     FMX.Platform,
     {$ENDIF}

     Math,

     {$IFDEF FMX}
     FMXTee.Constants
     {$ELSE}
     TeeConst
     {$ENDIF};


{ TCustomChartWall }
Constructor TCustomChartWall.Create(const AOwner:TCustomTeePanel);
Begin
  inherited;

  FDark3D:=True;
  FEnd:=100;
  InitColor(clTeeColor);
End;

Function TCustomChartWall.IsColorStored:Boolean;
begin
  result:=Color<>DefaultColor;
end;

Procedure TCustomChartWall.Assign(Source:TPersistent);
Begin
  if Source is TCustomChartWall then
  With TCustomChartWall(Source) do
  Begin
    Self.FAutoHide:=AutoHide;
    Self.FDark3D:=Dark3D;
    Self.FEnd:=EndPosition;
    Self.FSize:=Size;
    Self.FStart:=StartPosition;
  end;

  inherited;
End;

Function TCustomChartWall.ApplyDark3D:Boolean;
begin
  result:=(Brush.Style<>bsClear) and (Brush.Bitmap=nil) and Dark3D;
end;

Function TCustomChartWall.GetPen:TChartPen;
begin
  result:=Frame;
end;

type
  TTeePanelAccess=class(TCustomTeePanel);

Procedure TCustomChartWall.SetDark3D(const Value:Boolean);
begin
  TTeePanelAccess(ParentChart).SetBooleanProperty(FDark3D,Value);
end;

Procedure TCustomChartWall.SetEnd(const Value:Integer);
begin
  TTeePanelAccess(ParentChart).SetIntegerProperty(FEnd,Value);
end;

Procedure TCustomChartWall.SetPen(const Value:TChartPen);
Begin
  Frame:=Value;
end;

Procedure TCustomChartWall.SetSize(const Value:Integer);
Begin
  TTeePanelAccess(ParentChart).SetIntegerProperty(FSize,Value);
End;

Procedure TCustomChartWall.SetStart(const Value:Integer);
begin
  TTeePanelAccess(ParentChart).SetIntegerProperty(FStart,Value);
end;

procedure TCustomChartWall.InitColor(const AColor: TColor);
var Old : Boolean;
begin
  Old:=ParentChart.AutoRepaint;
  ParentChart.AutoRepaint:=False;
  Color:=AColor;
  DefaultColor:=AColor;
  ParentChart.AutoRepaint:=Old;
end;

procedure TCustomChartWall.CalcPositions(out ATop,ABottom:Integer);
begin
  with TCustomChart(ParentChart) do
       ABottom:={$IFDEF FMX}Round{$ENDIF}(ChartRect.Bottom) + CalcWallSize(BottomAxis);

  if EndPosition<>100 then
     Dec(ABottom,Round((ABottom-ParentChart.ChartRect.Top)*(100-EndPosition)*0.01));

  ATop:={$IFDEF FMX}Round{$ENDIF}(ParentChart.ChartRect.Top);

  if StartPosition<>0 then
     Inc(ATop,Round((ParentChart.ChartRect.Bottom-ATop)*StartPosition*0.01));
end;

Function TCustomChartWall.TryDrawWall(const APos1,APos2:Integer; out ABlend:TTeeBlend):Boolean;
var P         : TFourPoints;
    tmpTop    : Integer;
    tmpBottom : Integer;
begin
  result:=(not Transparent) and (Transparency>0);

  if result then
  begin
    if ParentChart.Canvas.SupportsAlpha then
       ABlend:=ParentChart.Canvas.BeginBlending(TeeZeroRect,Transparency)
    else
    begin
      CalcPositions(tmpTop,tmpBottom);

      with ParentChart.Canvas do
      begin
        P[0]:=Calculate3DPosition(APos1, tmpTop, 0);
        P[1]:=Calculate3DPosition(APos1, tmpTop, ParentChart.Width3D);
        P[2]:=Calculate3DPosition(APos1, APos2, ParentChart.Width3D);
        P[3]:=Calculate3DPosition(APos1, APos2, 0);
      end;

      ABlend:=ParentChart.Canvas.BeginBlending(RectFromPolygon(P,4),Transparency);
    end;
  end;
end;

procedure TCustomChartWall.SetAutoHide(const Value: Boolean);
begin
  TTeePanelAccess(ParentChart).SetBooleanProperty(FAutoHide,Value);
end;

procedure TCustomChartWall.DoEndBlending(const Blend:TTeeBlend);
begin
  if Transparency>0 then
  begin
    //if Assigned(Blend) then //TV52014479
    begin
      if Assigned(FPicture) and (Picture.Filters.Count>0) and (not Assigned(Picture.Graphic)) then
         Picture.Filters.ApplyTo(Blend.Bitmap);

      ParentChart.Canvas.EndBlending(Blend);
    end;
  end;
end;

function TCustomChartWall.ShouldDraw: Boolean;
begin
  result:=TCustomChart(ParentChart).View3DWalls and Visible and (not AutoHide);
end;

type
  TShadowAccess=class(TTeeShadow);
  TEmbossAccess=class(TTeeEmboss);

{ TChartBackWall }
Constructor TChartBackWall.Create(const AOwner: TCustomTeePanel);
begin
  inherited;
  InitColor({$IFDEF FMX}TAlphaColors.Silver{$ELSE}clSilver{$ENDIF});
  InitTransparent(True);
end;

procedure TChartBackWall.InitEmboss(const AEmboss:TTeeEmboss);
begin
  InitShadow(AEmboss);
end;

procedure TChartBackWall.InitShadow(const AShadow:TTeeShadow);
begin
  TShadowAccess(AShadow).InitValues(False,{$IFDEF FMX}claDarkGray{$ELSE}clDkGray{$ENDIF},AShadow.Size);
end;

function RemoveLastLineBreak(const S:String):String;
const
  Offset=1;
var tmp : Integer;
begin
  result:=S;
  tmp:=Length(result);

  if tmp>2 then
     if Copy(result,tmp-Offset,2)=#13#10 then
        Delete(result,tmp-Offset,2);
end;

{ TStringsShape }

Constructor TStringsShape.Create(const AOwner: TCustomTeePanel);
begin
  inherited;

  FTitleText:=TStringList.Create;
  TStringList(FTitleText).OnChange:=TextChanged;
end;

Destructor TStringsShape.Destroy;
begin
  FTitleText.Free;
  inherited;
end;

procedure TStringsShape.ChangedText;
begin
  IChangingText:=True;
  FTitleText.Text:=FText;
  IChangingText:=False;

  inherited;
end;

procedure TStringsShape.TextChanged;
begin
  if not IChangingText then
  begin
    FText:=RemoveLastLineBreak(FTitleText.Text);
    ParentChart.Invalidate;
  end;
end;

Procedure TStringsShape.SetText(const Value:TStrings);
begin
  FTitleText.Assign(Value);
  TextChanged(Self);
end;

function TStringsShape.GetCaption: String;
begin
  if Text.Count>0 then result:=Text[0]
                  else result:='';
end;

procedure TStringsShape.SetCaption(const Value: String);
begin
  if Value='' then
     Text.Clear
  else
     Text.Text:={$IFDEF LCL}UTF8Decode{$ENDIF}(Value);
end;

procedure TStringsShape.Clear;
begin
  Caption:='';
end;

{ TChartTitle }
type
  TTeeFontAccess=class(TTeeFont);

Constructor TChartTitle.Create(const AOwner: TCustomTeePanel);
Begin
  inherited;
  IOnTop:=True;

  FAlignment:=taCenter; // use inherited protected FAlignment to avoid panel Invalidate
  
  FAdjustFrame:=True;  // AutoSize

  IDraw2D:=True;

  InitTextAlignment(taCenter);

  FVertMargin:=TeeTitleFootDistance;

  { inherited }
  InitTransparent(True);

  TTeeFontAccess(Font).InitColor({$IFDEF FMX}TAlphaColors.Blue{$ELSE}clBlue{$ENDIF});
end;

Procedure TChartTitle.Assign(Source:TPersistent);
Begin
  if Source is TChartTitle then
  With TChartTitle(Source) do
  Begin
    Self.FAdjustFrame:=FAdjustFrame;
    Self.Text:=FTitleText;
    Self.FVertMargin:=FVertMargin;
  end;

  inherited;
end;

Function TChartTitle.Clicked(const P:TPoint):Boolean;
begin
  result:=Visible and PointInRect(ShapeBounds,P);
end;

Function TChartTitle.Clicked(x,y:Integer):Boolean;
begin
  result:=Visible and Clicked(TeePoint(x,y));
end;

function TChartTitle.ShouldDraw:Boolean;
begin
  result:=Visible and (Text.Count>0);
end;

procedure TChartTitle.DrawTitle;
var tmpMargin : Integer;
    tmpR      : TRect;
    tmp       : Integer;
    tmpVPos   : Integer;
    tmpX      : Integer;
    tmpHalfBorder : Integer;
Begin
  if ShouldDraw then
  begin
    AutoSize:=True;
    CalcBounds(ParentChart);

    if Frame.Visible and (Frame.Width>1) then
       tmpHalfBorder:={$IFDEF FMX}Round{$ENDIF}(Frame.Width) div 2
    else
       tmpHalfBorder:=0;

    { resize Title to maximum Chart width }
    if not FCustomPosition then
    begin
      tmpMargin:=Round(ParentChart.Width*0.02)+TTeePanelAccess(ParentChart).BorderSize;

      if IOnTop then
         tmpVPos:={$IFDEF FMX}Round{$ENDIF}(ParentChart.ChartRect.Top)+tmpHalfBorder
      else
         tmpVPos:={$IFDEF FMX}Round{$ENDIF}(ParentChart.ChartRect.Bottom-Height)-tmpHalfBorder;

      if AdjustFrame then
      begin
        AutoSize:=True;

        case Alignment of
            taLeftJustify  : MoveTo(tmpMargin,tmpVPos);
            taRightJustify : MoveTo({$IFDEF FMX}Round{$ENDIF}(ParentChart.Width-Width)-tmpMargin,tmpVPos);
            taCenter       : begin
                               tmp:=ParentChart.ChartXCenter;
                               tmpX:=tmp-({$IFDEF FMX}Round{$ENDIF}(Width) div 2);

                               if tmpX<tmpMargin then
                                  tmpX:=tmpMargin;

                               MoveTo(tmpX,tmpVPos);
                             end;
        end;
      end
      else
      begin
        AutoSize:=False;

        MoveTo(tmpMargin,tmpVPos);
        Width:=ParentChart.Width-2*tmpMargin;
      end;
    end;

    Draw;

    { calculate Chart positions after drawing the titles / footers }
    if not FCustomPosition then
    begin
      tmpR:=ParentChart.ChartRect;

      tmpMargin:=FVertMargin+tmpHalfBorder;

      if IOnTop then
         tmpR.Top:=ShapeBounds.Bottom+tmpMargin
      else
         tmpR.Bottom:=ShapeBounds.Top-tmpMargin;

      ParentChart.ChartRect:=tmpR;
      ParentChart.RecalcWidthHeight;
    end;
  end;
end;

Function TChartTitle.GetShapeBounds:TRect;
begin
  result:=ShapeBounds;
end;

Procedure TChartTitle.SetAdjustFrame(Value:Boolean);
begin
  TTeePanelAccess(ParentChart).SetBooleanProperty(FAdjustFrame,Value);
end;

Procedure TChartTitle.SetVertMargin(const Value:Integer);
begin
  if FVertMargin<>Value then
  begin
    FVertMargin:=Value;
    Repaint;
  end;
end;

{ TChartFootTitle }

Constructor TChartFootTitle.Create(const AOwner: TCustomTeePanel);
Begin
  inherited;

  IOnTop:=False;

  //TODO: DCCIL problem prevents using "with" here...
  TTeeFontAccess(Font).InitColor({$IFDEF FMX}TAlphaColors.Red{$ELSE}clRed{$ENDIF});
  TTeeFontAccess(Font).InitStyle([{$IFDEF FMX}TFontStyle.{$ENDIF}fsItalic]);
end;

{ TChartWalls }

Constructor TChartWalls.Create(const Chart: TCustomChart);
begin
  inherited Create;

  FChart:=Chart;
  FBack:=TChartBackWall.Create(FChart);
  FLeft:=TChartLeftWall.Create(FChart);
  FLeft.InitColor({$IFDEF FMX}$FFFFFF80{$ELSE}$80FFFF{$ENDIF});

  FBottom:=TChartBottomWall.Create(FChart);
  FBottom.InitColor({$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF});
end;

Destructor TChartWalls.Destroy;
begin
  FBack.Free;
  FBottom.Free;
  FLeft.Free;
  FRight.Free;
  inherited;
end;

type
  TChartRightWallAccess=class(TChartRightWall);

function TChartWalls.GetRight:TChartRightWall;
begin
  if not Assigned(FRight) then
  begin
    FRight:=TChartRightWall.Create(FChart);

    FChart.AutoRepaint:=False;

    FRight.Visible:=False;
    TChartRightWallAccess(FRight).DefaultVisible:=False;

    FRight.InitColor({$IFDEF FMX}TAlphaColors.Silver{$ELSE}clSilver{$ENDIF});

    FChart.AutoRepaint:=True;
  end;

  result:=FRight;
end;

procedure TChartWalls.Assign(Source: TPersistent);
begin
  if Source is TChartWalls then
  with TChartWalls(Source) do
  begin
    Self.Back   :=Back;
    Self.Bottom :=Bottom;
    Self.Left   :=Left;
    Self.Right  :=Right;
  end
  else inherited;
end;

procedure TChartWalls.Hide;
begin
  Visible:=False;
end;

procedure TChartWalls.Show;
begin
  Visible:=True;
end;

procedure TChartWalls.SetBack(const Value: TChartBackWall);
begin
  Back.Assign(Value);
end;

procedure TChartWalls.SetBottom(const Value: TChartBottomWall);
begin
  Bottom.Assign(Value);
end;

procedure TChartWalls.SetLeft(const Value: TChartLeftWall);
begin
  Left.Assign(Value);
end;

procedure TChartWalls.SetRight(const Value: TChartRightWall);
begin
  Right.Assign(Value);
end;

function TChartWalls.GetVisible: Boolean;
begin
  result:=FChart.View3DWalls;
end;

procedure TChartWalls.SetVisible(const Value: Boolean);
begin
  FChart.View3DWalls:=Value;
end;

procedure TChartWalls.SetSize(const Value: Integer);
begin
  Left.Size:=Value;
  Right.Size:=Value;
  Back.Size:=Value;
  Bottom.Size:=Value;
end;

{ TCustomChart }
Constructor TCustomChart.Create(AOwner: TComponent);
Begin
  inherited;

  AutoRepaint:=False;

  if csDesigning in ComponentState then
     Title.FTitleText.Add(ClassName);

  FWalls:=TChartWalls.Create(Self);
  FLegend:=TChartLegend.Create(Self);

  RestoredAxisScales:=True;

  if Assigned(TeeNewChartHook) then
  begin
     if (csDesigning in ComponentState) and
        (
          (not Assigned(Owner)) or
          (not (csLoading in Owner.ComponentState))
        ) then
           TeeNewChartHook(Self);
  end
  else
    SetColorPaletteIndex(TeeReadIntegerOption('DefaultPalette',13));

  AutoRepaint:=True;
end;

Destructor TCustomChart.Destroy;
Begin
  FSavedScales:=nil;
  AutoRepaint:=False;
  FSubTitle.Free;
  FTitle.Free;
  FSubFoot.Free;
  FFoot.Free;
  FWalls.Free;
  FreeAndNil(FLegend);
  
  inherited;
end;

type
  TSeriesAccess=class(TChartSeries);

Function TCustomChart.FormattedValueLegend(ASeries:TChartSeries; ValueIndex:Integer):String;
var tmp : TCustomChartLegend;
Begin
  if Assigned(ASeries) then
  begin
    tmp:=TCustomChartLegend(TSeriesAccess(ASeries).ILegend);
    if not Assigned(tmp) then tmp:=Legend;
    result:=tmp.FormattedValue(ASeries,ValueIndex);
  end
  else
     result:='';
end;

{$IFDEF FMX}
Procedure TCustomChart.InternalDraw(Const UserRectangle:TRect);
begin
  inherited;

  if Zoom.Active then
     DrawZoomRectangle;
end;
{$ENDIF}

Function TCustomChart.InternalFormattedLegend( const ALegend:TCustomChartLegend;
                                               SeriesOrValueIndex:Integer):String;
begin
  result:=ALegend.FormattedLegend(SeriesOrValueIndex);

  if Assigned(FOnGetLegendText) then
     FOnGetLegendText(Self,ALegend.InternalLegendStyle,SeriesOrValueIndex,result);
end;

Function TCustomChart.FormattedLegend(SeriesOrValueIndex:Integer):String;
Begin
  result:=InternalFormattedLegend(Legend,SeriesOrValueIndex);
end;

procedure TCustomChart.SetLegend(const Value:TChartLegend);
begin
  FLegend.Assign(Value);
end;

// "remember" the axis scales when zooming, to restore when unzooming
Function TCustomChart.SaveScales:TAllAxisSavedScales;
var t : Integer;
begin
  SetLength(result,Axes.Count);

  for t:=0 to Axes.Count-1 do
  with Axes[t] do
    if not IsDepthAxis then
    begin
      result[t].Auto:=Automatic;
      result[t].AutoMin:=AutomaticMinimum;
      result[t].AutoMax:=AutomaticMaximum;
      result[t].Min:=Minimum;
      result[t].Max:=Maximum;
    end;
end;

type
  TAxisAccess=class(TChartAxis);

// restore the "remembered" axis scales when unzooming
Procedure TCustomChart.RestoreScales(var Saved:TAllAxisSavedScales);
var t : Integer;
begin
  for t:=0 to Axes.Count-1 do
  with TAxisAccess(Axes[t]) do
    if not IsDepthAxis then
    begin
      Automatic:=Saved[t].Auto;
      AutomaticMinimum:=Saved[t].AutoMin;
      AutomaticMaximum:=Saved[t].AutoMax;

      {$IFDEF LCL} // Lazarus FPC bug?
      SetMinMax(Saved[t].Min,Saved[t].Max);
      {$ELSE}

      InternalSetMinimum(Saved[t].Min);
      InternalSetMaximum(Saved[t].Max);

      {$ENDIF}
    end;

  Saved:=nil;
end;

procedure TCustomChart.SetBackWall(const Value:TChartBackWall);
begin
  Walls.Back:=Value;
end;

function TCustomChart.GetBackWall: TChartBackWall;
begin
  result:=Walls.Back;
end;

function TCustomChart.GetBottomWall: TChartBottomWall;
begin
  result:=Walls.Bottom;
end;

function TCustomChart.GetLeftWall: TChartLeftWall;
begin
  result:=Walls.Left;
end;

function TCustomChart.GetRightWall: TChartRightWall;
begin
  result:=Walls.Right;
end;

Function TCustomChart.GetFrame:TChartPen;
begin
  if Assigned(Walls.Back) then result:=Walls.Back.Pen
                          else result:=nil;
end;

Procedure TCustomChart.SetFrame(const Value:TChartPen);
begin
  BackWall.Pen.Assign(Value);
end;

Function TCustomChart.GetBackColor:TColor;
begin
  if (not Walls.Visible) or (not BackWall.Visible) or BackWall.Transparent then
     result:=inherited GetBackColor
  else
  if BackWall.Gradient.Visible then
     result:=BackWall.Gradient.StartColor
  else
     result:=BackWall.Color;
end;

Procedure TCustomChart.SetBackColor(Value:TColor);
begin
  BackWall.Color:=Value;
  { fix 4.01: do not set backwall solid when loading dfms... }
  if Assigned(Parent) and (not (csLoading in ComponentState)) then
     BackWall.Brush.Style:=bsSolid;
end;

Function TCustomChart.IsFreeSeriesColor(AColor:TColor; CheckBackground:Boolean;
                                        ASeries:TChartSeries=nil):Boolean;
var t : Integer;
Begin
  for t:=0 to SeriesCount-1 do
  if ((Series[t]<>ASeries) and (Series[t].SeriesColor=AColor)) or 
     (CheckBackground and
     ( (AColor=Color) or (AColor=BackWall.Color) )) then
  begin
    result:=False;
    exit;
  end;
  result:=(not CheckBackground) or ( (AColor<>Color) and (AColor<>BackWall.Color) );
end;

{$IFDEF FMX}
procedure TCustomChart.KeyDown(var Key: Word; var KeyChar: System.WideChar; Shift: TShiftState);
{$ELSE}
procedure TCustomChart.KeyDown(var Key: Word; Shift: TShiftState);
{$ENDIF}
var tmp : Char;
begin
  inherited;

  tmp:=#0;
  BroadcastKeyEvent(ckeDown, Key, tmp, Shift);
end;

{$IFNDEF FMX}
procedure TCustomChart.KeyPress(var Key: Char);
var tmpW : Word;
    tmpS : TShiftState;
begin
  inherited;

  tmpW:=0;
  tmpS:=[];

  BroadcastKeyEvent(ckePress, tmpW, Key, tmpS);
end;
{$ENDIF}

{$IFDEF FMX}
procedure TCustomChart.KeyUp(var Key: Word; var KeyChar: System.WideChar;
      Shift: TShiftState);
{$ELSE}
procedure TCustomChart.KeyUp(var Key: Word; Shift: TShiftState);
{$ENDIF}
var tmp : Char;
begin
  inherited;

  tmp:=#0;
  BroadcastKeyEvent(ckeUp, Key, tmp, Shift);
end;

procedure TCustomChart.SetLeftWall(const Value:TChartLeftWall);
begin
  Walls.Left:=Value;
end;

procedure TCustomChart.SetBottomWall(const Value:TChartBottomWall);
begin
  Walls.Bottom:=Value;
end;

procedure TCustomChart.SetRightWall(const Value:TChartRightWall);
begin
  Walls.Right:=Value;
end;

type
  TCanvas3DAccess=class(TCanvas3D);

Procedure TCustomChart.DrawRightWall;
var tmpB : Integer;
    tmp  : Integer;
    tmpTop   : Integer;
    tmpBlend : TTeeBlend;
    tmpTransp : Boolean;
begin
  if Assigned(FWalls.FRight) then // optimization

  if RightWall.Visible and ActiveSeriesUseAxis and View3D and View3DWalls then
  begin
    TCanvas3DAccess(Canvas).BeginEntity('Right');

    PrepareWallCanvas(RightWall);

    RightWall.CalcPositions(tmpTop,tmpB);

    tmpTransp:=RightWall.TryDrawWall({$IFDEF FMX}Round{$ENDIF}(ChartRect.Right),tmpB,tmpBlend);

    with RightWall do
    begin
      if Size>0 then
      begin
        if BackWall.Visible then tmp:=BackWall.Size
                            else tmp:=0;

        Canvas.Cube({$IFDEF FMX}Round{$ENDIF}(ChartRect.Right),
                    {$IFDEF FMX}Round{$ENDIF}(ChartRect.Right+Size),
                    tmpTop,tmpB,0,Width3D+tmp,ApplyDark3D)
      end
      else
      if Canvas.SupportsFullRotation then
         Canvas.RectangleZ({$IFDEF FMX}Round{$ENDIF}(ChartRect.Right+1),
                      tmpTop,tmpB,0,Succ(Width3D)) // v9
      else
         Canvas.RectangleZ({$IFDEF FMX}Round{$ENDIF}(ChartRect.Right),
                      tmpTop,tmpB,0,Succ(Width3D));

      if Assigned(FPicture) and FPicture.Valid then
         Canvas.StretchDraw(TeeRect(0,tmpTop,Width3D,tmpB),
                            Picture.Filtered,
                            ChartRect.Right,cpX);

      if tmpTransp then
         DoEndBlending(tmpBlend);
    end;

    TCanvas3DAccess(Canvas).EndEntity;
  end;
end;

Function TCustomChart.DrawWallFirst(APos:Integer):Boolean;
var P : TFourPoints;
    tmpBottom : TCoordinate;
begin
  if Canvas.SupportsFullRotation then
     result:=True
  else
  begin
    P[0]:=Canvas.Calculate3DPosition(APos,ChartRect.Top,0);

    tmpBottom:=ChartRect.Bottom+CalcWallSize(BottomAxis);

    P[1]:=Canvas.Calculate3DPosition(APos,tmpBottom,0);
    P[2]:=Canvas.Calculate3DPosition(APos,tmpBottom,Width3D+BackWall.Size);

    result:=TeeCull(P);
  end;
end;

Function TCustomChart.DrawRightWallAfter:Boolean;
begin
  result:=not DrawWallFirst({$IFDEF FMX}Round{$ENDIF}(ChartRect.Right));
end;

Function TCustomChart.DrawLeftWallFirst:Boolean;
begin
  result:=DrawWallFirst({$IFDEF FMX}Round{$ENDIF}(ChartRect.Left));
end;

type
  TTeeShapeAccess=class(TTeeCustomShapeBrushPen);

procedure TCustomChart.DrawTitlesAndLegend(BeforeSeries:Boolean);

  Procedure DrawAxisAfter(const Axis:TChartAxis);
  begin
    if IsAxisVisible(Axis) then
    begin
      TAxisAccess(Axis).IHideBackGrid:=True;
      Axis.Draw(False);
      TAxisAccess(Axis).IHideBackGrid:=False;
    end;
  end;

  Procedure DrawAxisGridAfter(const Axis:TChartAxis);
  begin
    if IsAxisVisible(Axis) then
    begin
      TAxisAccess(Axis).IHideSideGrid:=True;
      TAxisAccess(Axis).DrawGrids(Length(Axis.Tick));
      TAxisAccess(Axis).IHideSideGrid:=False;
    end;
  end;

  Procedure DrawTitleFoot(DrawAll,CustomOnly:Boolean);
  var
    tmpBeginDone : Boolean;

    Procedure DoDrawTitle(const ATitle:TChartTitle; const TitleID:String);
    begin
      if Assigned(ATitle) and (DrawAll or (ATitle.CustomPosition=CustomOnly)) then
      begin
        if ATitle.ShouldDraw then
        begin
          if not tmpBeginDone then
          begin
            TCanvas3DAccess(Canvas).BeginEntity('Titles');
            tmpBeginDone:=True;
          end;

          TCanvas3DAccess(Canvas).BeginEntity(TitleID, nil, TTeeShapeAccess(ATitle).Transform);

          ATitle.DrawTitle;

          TCanvas3DAccess(Canvas).EndEntity;
        end;
      end;
    end;

  begin
    tmpBeginDone:=False;

    DoDrawTitle(FTitle,'Title');
    DoDrawTitle(FSubTitle,'Subtitle');
    DoDrawTitle(FFoot,'Foot');
    DoDrawTitle(FSubFoot,'Subfoot');

    if tmpBeginDone then
       TCanvas3DAccess(Canvas).EndEntity;
  end;

// Draw title and foot, or draw foot and title, depending
// if legend is at left/right or at top/bottom.
// Top/bottom legends need to leave space for the title and foot
// before they get displayed.
begin
  Canvas.FrontPlaneBegin;

  // Draw titles and legend before series
  if BeforeSeries then
  begin
    if Legend.ShouldDraw then
    begin
      if Legend.Vertical then
      begin
        Legend.CalcRect;

        if Legend.DrawBehind then
           Legend.DrawLegend;

        DrawTitleFoot(False,False);
      end
      else
      begin
        DrawTitleFoot(False,False);

        Legend.CalcRect;

        if Legend.DrawBehind then
           Legend.DrawLegend;
      end;
    end
    else DrawTitleFoot(False,False);
  end
  else
  begin
    // Draw legend and titles after chart and series
    if not Legend.DrawBehind then
       if Legend.ShouldDraw then
          Legend.DrawLegend;

    DrawTitleFoot(False,True);
  end;

  Canvas.FrontPlaneEnd;

  if not Canvas.SupportsFullRotation then 
  if not BeforeSeries then
  if ActiveSeriesUseAxis then
  begin
    if View3D then
    begin
      if DrawRightWallAfter then
      begin
        if Assigned(Walls.FRight) and Walls.Right.ShouldDraw then
           DrawRightWall;

        DrawAxisAfter(RightAxis);
      end;

      if DrawLeftWallFirst then
      begin
        if Walls.Left.ShouldDraw then
           DrawLeftWall;
        DrawAxisAfter(LeftAxis);
      end;

      if DrawBackWallAfter(Width3D) then
      begin
        DrawAxisGridAfter(TopAxis);
        DrawAxisGridAfter(BottomAxis);

        if Walls.Back.ShouldDraw then
           DrawBackWall;
      end;
    end;
  end;
end;

// Returns wall size in pixels corresponding to Axis parameter.
// For example: Left axis corresponds to Left wall.
Function TCustomChart.CalcWallSize(Axis:TChartAxis):Integer;
var tmpW : TCustomChartWall;
begin
  result:=0;

  if View3D and View3DWalls then
  begin
    if Axis=LeftAxis then tmpW:=Walls.Left else
    if Axis=BottomAxis then tmpW:=Walls.Bottom else
    if Axis=RightAxis then
    begin
      if Assigned(Walls.FRight) then
         tmpW:=Walls.Right
      else
         Exit;
    end
    else
       tmpW:=Walls.Back;

    if tmpW.Visible then result:=tmpW.Size;
  end;
end;

Function TCustomChart.ActiveSeriesUseAxis:Boolean;
var t : Integer;
begin
  result:=True;

  for t:=0 to SeriesCount-1 do
  With Series[t] do
  if Active then
     if UseAxis then
     begin
       result:=True;
       Exit;
     end
     else result:=False;
end;

Function TCustomChart.ActiveSeriesCanZoomScroll:Boolean;
var t : Integer;
begin
  result:=True;

  for t:=0 to SeriesCount-1 do
  With Series[t] do
  if Active then
     if CanZoomScroll then
     begin
       result:=True;
       Exit;
     end
     else result:=False;
end;

Procedure TCustomChart.PrepareWallCanvas(const AWall:TChartWall);
begin
  With AWall do
  begin
    Canvas.AssignVisiblePen(Frame);

    if Transparent then Canvas.Brush.Style:=bsClear
                   else SetBrushCanvas(Color,Brush,Brush.Color)
  end;
end;

type
  TVisualAccess=class(TVisualBlock);

Procedure TCustomChart.DrawLeftWall;  { Left wall only }
var tmpB     : Integer;
    tmpBlend : TTeeBlend;
    tmpTop   : Integer;
    tmpR     : TRect;
    tmpTransp : Boolean;
begin
  if View3D and View3DWalls then
  begin
    TCanvas3DAccess(Canvas).BeginEntity('Left',LeftWall.Visual);

    PrepareWallCanvas(LeftWall);

    LeftWall.CalcPositions(tmpTop,tmpB);

    if Assigned(LeftWall.Visual) then
    with TVisualAccess(LeftWall.Visual) do // Experimental
    begin
      tmpR.Left:=ChartRect.Left-LeftWall.Size;
      tmpR.Right:=ChartRect.Left;
      tmpR.Top:=tmpTop;
      tmpR.Bottom:=tmpB;

      SetBounds(tmpR,0,Width3D,Self.Height);

      with LeftWall do
           SetFormatting(Pen,Brush,False,Color,Transparency);
    end
    else
    begin
      tmpTransp:=LeftWall.TryDrawWall({$IFDEF FMX}Round{$ENDIF}(ChartRect.Left),tmpB,tmpBlend);

      with LeftWall do
      begin
        if Size>0 then
           Canvas.Cube({$IFDEF FMX}Round{$ENDIF}(ChartRect.Left-Size),
                       {$IFDEF FMX}Round{$ENDIF}(ChartRect.Left),
                       tmpTop,
                       tmpB,
                       0,
                       Width3D,
                       ApplyDark3D)
        else
        if Canvas.SupportsFullRotation then
           Canvas.RectangleZ({$IFDEF FMX}Round{$ENDIF}(ChartRect.Left-1),
                             tmpTop,tmpB,0,Width3D)
        else
           Canvas.RectangleZ({$IFDEF FMX}Round{$ENDIF}(ChartRect.Left),
                             tmpTop,tmpB,0,Width3D);

        if Assigned(FPicture) and FPicture.Valid then
           Canvas.StretchDraw(TeeRect(0,tmpTop,Width3D,tmpB),
                                Picture.Filtered,
                                ChartRect.Left,cpX);

        if tmpTransp then
           DoEndBlending(tmpBlend);
      end;
    end;

    TCanvas3DAccess(Canvas).EndEntity;
  end;
end;

{$IFDEF FMX}
procedure TCustomChart.DragDrop(const Data: TDragObject; const Point: TPoint);
var tmpB : Boolean;
    tmpO : TDragObject;
begin
  inherited;

  tmpO:=Data;
  BroadcastDragDropEvent(cdeDrop, Data.Source,
             {$IFDEF FMX}Round{$ENDIF}(Point.X),
             {$IFDEF FMX}Round{$ENDIF}(Point.Y),
             TDragState(nil), tmpB, tmpO);
end;
{$ELSE}
procedure TCustomChart.DragDrop(Source: TObject; X, Y: Integer);
var tmpO : TDragObject;
    tmpB : Boolean;
begin
  inherited;

  tmpO:=nil;
  tmpB:=True;

  BroadcastDragDropEvent(cdeDrop, Source, X, Y, TDragState(nil), tmpB, tmpO);
end;
{$ENDIF}

{$IFDEF FMX}
procedure TCustomChart.DragOver(const Data: TDragObject; const Point: TPoint;
         var {$IFDEF D20}Operation: TDragOperation{$ELSE}Accept: Boolean{$ENDIF});
var tmp : TDragObject;
    {$IFDEF D20}
    tmpB : Boolean;
    {$ENDIF}
begin
  inherited;

  tmp:=Data;

  {$IFDEF D20}
  tmpB:=True;
  {$ENDIF}

  BroadcastDragDropEvent(cdeOver, Data.Source, {$IFDEF FMX}Round{$ENDIF}(Point.X),
              {$IFDEF FMX}Round{$ENDIF}(Point.Y), TDragState.dsDragMove,
                  {$IFDEF D20}tmpB{$ELSE}Accept{$ENDIF}, tmp);

  {$IFDEF D20}
  if tmpB then
     Operation:=TDragOperation.Move;
  {$ENDIF}
end;

{$ELSE}
procedure TCustomChart.DragOver(Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
var tmp : TDragObject;
begin
  inherited;

  tmp:=nil;
  BroadcastDragDropEvent(cdeOver, Source, X, Y, State, Accept, tmp);
end;
{$ENDIF}

Procedure TCustomChart.DrawBackWall;
var tmpRect : TRect;

  procedure TryDrawShadow(const AShadow:TTeeShadow);
  var tmpP : TFourPoints;
  begin
    if Assigned(AShadow) and AShadow.ShouldDraw then
    if View3D then
    begin
      if View3DOptions.Orthogonal then
         AShadow.Draw(Canvas,Canvas.CalcRect3D(tmpRect,Width3D))
      else
      begin
        Canvas.FourPointsFromRect(tmpRect,Width3D,tmpP);
        AShadow.Draw(Canvas,tmpP);
      end;
    end
    else
      AShadow.Draw(Canvas,tmpRect);
  end;

var
  tmpBlend : TTeeBlend;

  Procedure CreateBlender;
  begin
    with BackWall do
    if Transparency>0 then
       if Canvas.SupportsAlpha then
          tmpBlend:=Canvas.BeginBlending(TeeZeroRect,Transparency)
       else
          tmpBlend:=Canvas.BeginBlending(Canvas.RectFromRectZ(tmpRect,Width3D),Transparency);
  end;

var
  tmpTop    : Integer;
  tmpBottom : Integer;

  procedure SetChartRect;
  begin
    tmpRect.Left:=ChartRect.Left;
    tmpRect.Top:=tmpTop;
    tmpRect.Right:=ChartRect.Right;
    tmpRect.Bottom:=tmpBottom;
  end;

var tmp : Integer;
begin
  TCanvas3DAccess(Canvas).BeginEntity('Back',BackWall.Visual);

  PrepareWallCanvas(BackWall);

  with BackWall do
  begin
    CalcPositions(tmpTop,tmpBottom);

    if Assigned(Visual) then
    with TVisualAccess(BackWall.Visual) do
    begin
      SetChartRect;

      if BackWall.Size>0 then
         tmpRect.Left:=tmpRect.Left-CalcWallSize(LeftAxis);

      SetBounds(tmpRect,Width3D,Width3D+BackWall.Size,Self.Height);
      SetFormatting(Pen,Brush,False,Color,Transparency);
    end
    else
    begin
      tmpBlend:=nil;

      if View3D then
      begin
        if View3DWalls then
        begin
          SetChartRect;

          if Size>0 then
             tmpRect.Left:=tmpRect.Left-CalcWallSize(LeftAxis);

          CreateBlender;

          TryDrawShadow(FShadow);
          TryDrawShadow(FEmboss);

          Canvas.AssignVisiblePen(Frame);

          if Size>0 then
          begin
             if Width3D<0 then tmp:=-Size else tmp:=Size;

             if ShapeStyle=fosRoundRectangle then
                Canvas.Cube(tmpRect,Width3D,Width3D+tmp,ApplyDark3D,RoundSize)
             else
                Canvas.Cube(tmpRect,Width3D,Width3D+tmp,ApplyDark3D)
          end
          else
          if Canvas.SupportsFullRotation then
             Canvas.RectangleWithZ(tmpRect,Width3D+1) // add more space
          else
          if ShapeStyle=fosRoundRectangle then
             Canvas.RoundRectWithZ(tmpRect,RoundSize,RoundSize,Width3D)
          else
             Canvas.RectangleWithZ(tmpRect,Width3D);
        end;
      end
      else
      begin
        SetChartRect;

        tmpRect.Right:=tmpRect.Right+1;

        if EndPosition=100 then
           tmpRect.Bottom:=tmpRect.Bottom+1;

        CreateBlender;

        TryDrawShadow(FShadow);
        TryDrawShadow(FEmboss);

        if Canvas.SupportsFullRotation then
           Canvas.RectangleWithZ(tmpRect,1)
        else
        if ShapeStyle=fosRoundRectangle then
           Canvas.RoundRect(tmpRect,RoundSize,RoundSize)
        else
           Canvas.Rectangle(tmpRect);
      end;

      if Assigned(FPicture) and Picture.Valid then
      begin
        SetChartRect;
        Picture.Draw(Self,tmpRect,Width3D);
      end
      else
      if HasBackImage and BackImage.Inside then
      begin
        SetChartRect;

        if Pen.Visible then
        begin
          tmpRect.Top:=tmpRect.Top+1;
          tmpRect.Left:=tmpRect.Left+1;
        end;

        DrawBitmap(tmpRect,Width3D);
      end;

      DoEndBlending(tmpBlend);
    end;
  end;

  TCanvas3DAccess(Canvas).EndEntity;
end;

function TCustomChart.Form3DClass:TClass;
begin
  result:=nil;
end;

Procedure TCustomChart.DrawWalls;  { Left and Bottom wall only }

  Procedure DrawBottomWall;
  var P    : TFourPoints;
      tmpB : Integer;
      tmpLeft : Integer;
      tmpZ0 : Integer;
      tmpZ1 : Integer;

    Procedure CalcBottomWallPoints;
    begin
      tmpB:={$IFDEF FMX}Round{$ENDIF}(ChartRect.Bottom);
      P[0]:=Canvas.Calculate3DPosition(tmpLeft,  tmpB,tmpZ0);
      P[1]:=Canvas.Calculate3DPosition(tmpLeft,  tmpB,tmpZ1);
      P[2]:=Canvas.Calculate3DPosition(ChartRect.Right, tmpB,tmpZ1);
      P[3]:=Canvas.Calculate3DPosition(ChartRect.Right, tmpB,tmpZ0);
    end;

  var tmpBlend : TTeeBlend;
      tmpRight : Integer;
  begin
    TCanvas3DAccess(Canvas).BeginEntity('Bottom');

    PrepareWallCanvas(BottomWall);

    tmpB:={$IFDEF FMX}Round{$ENDIF}(ChartRect.Bottom);
    if Canvas.SupportsFullRotation then Inc(tmpB);

    tmpZ0:=0;
    if BottomWall.StartPosition<>0 then
       Inc(tmpZ0,Round(Width3D*BottomWall.StartPosition*0.01));

    tmpZ1:=Width3D;
    if BottomWall.EndPosition<>100 then
       Dec(tmpZ1,Round(Width3D*(100-BottomWall.EndPosition)*0.01));

    tmpLeft:={$IFDEF FMX}Round{$ENDIF}(ChartRect.Left);

    //MM TV52010497
    with Walls.Left do
    if Visible and AutoHide and DrawLeftWallFirst then
       Dec(tmpLeft,CalcWallSize(Axes.Left));

    tmpBlend:=nil;

    if not BottomWall.Transparent then
    begin
      CalcBottomWallPoints;

      if BottomWall.Transparency>0 then
         if Canvas.SupportsAlpha then
            tmpBlend:=Canvas.BeginBlending(TeeZeroRect,BottomWall.Transparency)
         else
            tmpBlend:=Canvas.BeginBlending(RectFromPolygon(P,4),BottomWall.Transparency);
    end;

    tmpRight:={$IFDEF FMX}Round{$ENDIF}(ChartRect.Right);

    with BottomWall do
    begin
      if Size>0 then
         Canvas.Cube(tmpLeft,tmpRight,tmpB,tmpB+Size,tmpZ0,tmpZ1,ApplyDark3D)
      else
      if Canvas.SupportsFullRotation then
         Canvas.RectangleY(tmpLeft,tmpB+1,tmpRight,tmpZ0,tmpZ1) // v9
      else
         Canvas.RectangleY(tmpLeft,tmpB,tmpRight,tmpZ0,tmpZ1);

      if Assigned(FPicture) and FPicture.Valid then
         Canvas.StretchDraw(TeeRect(tmpLeft,tmpZ1,tmpRight,tmpZ0),
                            Picture.Filtered,
                            tmpB,cpY);

      if not Transparent then
         if Transparency>0 then
            DoEndBlending(tmpBlend);
    end;

    TCanvas3DAccess(Canvas).EndEntity;
  end;

var tmpOk : Boolean;
begin
  if ActiveSeriesUseAxis then
  begin
    tmpOk:=Canvas.SupportsFullRotation;

    TCanvas3DAccess(Canvas).BeginEntity('Walls');

    if View3D and View3DWalls then
    begin
      if Assigned(FWalls.FRight) and RightWall.Visible and (tmpOk or (not DrawRightWallAfter)) then
         DrawRightWall;
    end;

    if View3DWalls then
      if BackWall.Visible and (tmpOk or (not DrawBackWallAfter(Width3D))) then
         DrawBackWall;

    if View3D and View3DWalls then
    begin
      if LeftWall.Visible and (tmpOk or (not DrawLeftWallFirst)) then
         DrawLeftWall;

      if BottomWall.Visible then
         DrawBottomWall;

      if (not tmpOk) and DrawBackWallAfter(0) then
      begin
        if IsAxisVisible(Axes.Top) then
           Axes.Top.Draw(True);

        if IsAxisVisible(Axes.Bottom) then
           Axes.Bottom.Draw(True);
      end;
    end;

    TCanvas3DAccess(Canvas).EndEntity;
  end;
end;

Procedure TCustomChart.SeriesUp(const ASeries:TChartSeries);
var t : Integer;
Begin  { scroll up ASeries in SeriesList. This changes the Z order }
  t:=ASeries.SeriesIndex;
  if t>0 then ExchangeSeries(t,t-1);
end;

Procedure TCustomChart.SeriesDown(const ASeries:TChartSeries);
var t : Integer;
Begin  { scroll down ASeries in SeriesList. This changes the Z order }
  t:=ASeries.SeriesIndex;
  if t<SeriesCount-1 then ExchangeSeries(t,t+1);
end;

Procedure TCustomChart.NextPage;
Begin
  Pages.NextPage;
End;

Procedure TCustomChart.PreviousPage;
Begin
  Pages.PreviousPage;
End;

Procedure TCustomChart.RemoveAllSeries;
Begin
  While SeriesCount>0 do RemoveSeries(0);
End;

Procedure TCustomChart.ClearChart;

  procedure InitChart;
  type
    TCustomChartClass=class of TCustomChart;

  var tmpChart : TCustomChart;
      tmpClass : TCustomChartClass;
  begin
    // Use ClassType to call derived classes Create constructor
    tmpClass:=TCustomChartClass(ClassType);

    tmpChart:=tmpClass.Create(nil);
    try
      tmpChart.Parent:=Parent;
      Assign(tmpChart);
    finally
      tmpChart.Free;
    end;
  end;

var t : Integer;
begin
  for t:=0 to SeriesCount-1 do
    if Assigned(Series[t].Datasource) then
      {$IFDEF TEEOCX}
      {$IFDEF DATABASE}
      if (Series[t].DataSource is TTeeADOQuery) then
         Series[t].Datasource.Free
      else
      {$ENDIF}
      {$ENDIF}
        Series[t].Datasource:=nil;

  FreeAllSeries;
  SeriesList.Clear; //TV52015747
  Tools.Clear;
  Animations.Clear;

  for t:=0 to Axes.Count-1 do
      Axes[t].Items.Clear;

  Axes.Reset;

  InitChart;
end;

Procedure TCustomChart.DoZoom(Const topi,topf,boti,botf,lefi,leff,rigi,rigf:TAxisValue);

  Procedure DoZoomAnimated;

    Procedure ZoomAxis(const AAxis:TChartAxis; Const tmpA,tmpB:TAxisValue);
    Begin { apply a portion of the desired zoom to achieve animation }
      with AAxis do
        SetMinMax(Minimum+((tmpA-Minimum)/AnimatedZoomFactor),
                  Maximum-((Maximum-tmpB)/AnimatedZoomFactor));
    end;

  var t : Integer;
  begin
    for t:=1 to Zoom.AnimatedSteps-1 do
    begin
      if Zoom.Direction<>tzdHorizontal then // Bug found and fixed by customer
      begin
        ZoomAxis(LeftAxis,Lefi,Leff);
        ZoomAxis(RightAxis,Rigi,Rigf);
      end;

      if Zoom.Direction<>tzdVertical then
      begin
        ZoomAxis(TopAxis,Topi,Topf);
        ZoomAxis(BottomAxis,Boti,Botf);
      end;

      {$IFDEF FMX}
      Repaint;
      {$ELSE}
      Refresh;  // force repaint
      {$ENDIF}

      // OnAnimatedZoom event ?
    end;
  end;

Begin
  if RestoredAxisScales then
  begin
    FSavedScales:=SaveScales;
    RestoredAxisScales:=False;
  end;

  if Zoom.Animated then
     DoZoomAnimated; { animation... }

  // Final zoom
  if Zoom.Direction<>tzdHorizontal then // Bug found and fixed by customer
  begin
    LeftAxis.SetMinMax(Lefi,Leff);
    RightAxis.SetMinMax(Rigi,Rigf);
  end;

  if Zoom.Direction<>tzdVertical then
  begin
    TopAxis.SetMinMax(Topi,Topf);
    BottomAxis.SetMinMax(Boti,Botf);
  end;

  Zoomed:=True;

  if Assigned(FOnZoom) then
     FOnZoom(Self);
end;

Procedure TCustomChart.ZoomRect(Const Rect:TRect);
Begin
  With Zoom do
  Begin
    X0:={$IFDEF FMX}Round{$ENDIF}(Rect.Left);
    Y0:={$IFDEF FMX}Round{$ENDIF}(Rect.Top);
    X1:={$IFDEF FMX}Round{$ENDIF}(Rect.Right);
    Y1:={$IFDEF FMX}Round{$ENDIF}(Rect.Bottom);
    CalcZoomPoints;
  end;
End;

Procedure TCustomChart.ZoomPercent(Const PercentZoom:Double);

  Procedure CalcAxisScale(const Axis:TChartAxis; out tmpA,tmpB:TAxisValue);
  var tmpDelta  : TAxisValue;
      AMin      : TAxisValue;
      AMax      : TAxisValue;
      a,b       : Integer;
      ApplyZoom : Boolean;
  Begin
    if not Axis.Horizontal then
    begin
      ApplyZoom:=Zoom.Direction<>tzdHorizontal;
      a:=Axis.IEndPos;   // ChartRect.Bottom
      b:=Axis.IStartPos; // ChartRect.Top
    end
    else
    begin
      ApplyZoom:=Zoom.Direction<>tzdVertical;
      a:=Axis.IStartPos; // ChartRect.Left
      b:=Axis.IEndPos;   // ChartRect.Right;
    end;

    AMin:=Axis.CalcPosPoint(a);
    AMax:=Axis.CalcPosPoint(b);

    if ApplyZoom then
    begin
      tmpDelta:=(AMax-AMin)*(PercentZoom-100.0)*0.01;
      tmpA:=AMin+tmpDelta;
      tmpB:=AMax-tmpDelta;
    end
    else
    begin
      tmpA:=AMin;
      tmpB:=AMax;
    end;
  end;

var Lefi : TAxisValue;
    Leff : TAxisValue;
    Rigi : TAxisValue;
    Rigf : TAxisValue;
    Topi : TAxisValue;
    Topf : TAxisValue;
    Boti : TAxisValue;
    Botf : TAxisValue;
Begin
  { zoom a given "percent" }

  CalcAxisScale(LeftAxis,Lefi,Leff);
  CalcAxisScale(RightAxis,Rigi,Rigf);
  CalcAxisScale(TopAxis,Topi,Topf);
  CalcAxisScale(BottomAxis,Boti,Botf);

  DoZoom(Topi,Topf,Boti,Botf,Lefi,Leff,Rigi,Rigf);
  Repaint;
end;

Procedure TCustomChart.CalcClickedPart(Pos:TPoint; out Part:TChartClickedPart);
begin
  Part:=CalcNeedClickedPart(Pos,False);
end;

Function TCustomChart.CalcNeedClickedPart(Pos:TPoint; Needed:Boolean):TChartClickedPart;

  procedure ClickedAxis(const Axis:TChartAxis);
  begin
    if Axis.Clicked(Pos) then
    begin
      result.Part:=cpAxis;
      result.AAxis:=Axis;
    end
    else
    if Assigned(TAxisAccess(Axis).FAxisTitle) then
    with Axis.Title do
    if Visible and (Caption<>'') then
       if Clicked(Pos) then
       begin
         result.Part:=cpAxisTitle;
         result.AAxis:=Axis;
       end;
  end;

  Function ClickedLegend(var Part:TChartClickedPart):Boolean;  
  begin
    Part.PointIndex:=Legend.Clicked(Pos);

    if Part.PointIndex<>-1 then
    begin
      Part.Part:=cpLegend;
      result:=True;
    end
    else
      result:=False;
  end;

  Function LegendOnTop:Boolean;
  begin
    result:=Legend.CustomPosition;
  end;

  function DoClickedPointer(const Series:TChartSeries):Integer;
  var i: Integer;
      tmpX,
      tmpY: Integer;
  begin
    result:=TeeNoPointClicked;

    for i := 0 to Series.Count - 1 do
    begin
      tmpX:=Series.CalcXPos(i);
      tmpY:=Series.CalcYPos(i);

      if TSeriesAccess(Series).ClickedPointer(i,tmpX,tmpY,Pos) then
      begin
        result:=i;
        exit;
      end;
    end;
  end;

var t        : Integer;
    tmpIndex : Integer;
begin
  With result do
  begin
    Part:=cpNone;
    PointIndex:=-1;
    ASeries:=nil;
    AAxis:=nil;

    if Legend.Visible and LegendOnTop then
       if ClickedLegend(result) then
          Exit;

    { IMPORTANT: Z order inverted }

    for t:=SeriesCount-1 downto 0 do
    With TSeriesAccess(Self.Series[t]) do
    if Active and
       ((not Needed) or RaiseClicked or Assigned(FOnClickSeries)) then
    Begin
      if Marks.Visible then
      begin
        PointIndex:=Marks.Clicked(Pos);

        if PointIndex<>-1 then
        begin
          ASeries:=Series[t];
          Part:=cpSeriesMarks;
          Exit;
        end;
      end;

      PointIndex:=Clicked(Pos);
      
      if PointIndex<>-1 then
      begin
        ASeries:=Series[t];

        tmpIndex:=DoClickedPointer(ASeries);
        if tmpIndex<>-1 then
        begin
          PointIndex:=tmpIndex;
          Part:=cpSeriesPointer;
        end
        else
          Part:=cpSeries;

        Exit;
      end;
    end;

    for t:=0 to Axes.Count-1 do
    begin
      ClickedAxis(Axes[t]);

      if Assigned(result.AAxis) then
         Exit;
    end;

    if Legend.Visible and (not LegendOnTop) then  
       if ClickedLegend(result) then
          Exit;

    if Title.Clicked(Pos) then Part:=cpTitle
    else
    if SubTitle.Clicked(Pos) then Part:=cpSubTitle
    else
    if Foot.Clicked(Pos) then Part:=cpFoot
    else
    if SubFoot.Clicked(Pos) then Part:=cpSubFoot
    else
    if PointInRect(ChartRect,Pos) and  { <-- should be PtInCube(ChartRect,0,MaxZ) }
       (CountActiveSeries>0) then
             Part:=cpChartRect;
  end;
end;

Function TCustomChart.AxisTitleOrName(const Axis:TChartAxis):String;
Begin
  result:=Axis.Title.Caption;

  if result='' then
  begin
    if Axis=DepthAxis then result:=TeeMsg_DepthAxis
    else
    if Axis=Axes.DepthTop then result:=TeeMsg_DepthTopAxis
    else
    With Axis do
    if Horizontal then
       if OtherSide then result:=TeeMsg_TopAxis
                    else result:=TeeMsg_BottomAxis
    else
       if OtherSide then result:=TeeMsg_RightAxis
                    else result:=TeeMsg_LeftAxis;
  end;
end;

type
  TToolAccess=class(TTeeCustomTool);

procedure TCustomChart.BroadcastDragDropEvent(AEvent:TChartDragDropEvent; Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean; var DragObject: TDragObject);
var t : Integer;
    tmp : TTeeCustomTool;
begin
  for t:=0 to Tools.Count-1 do
  begin
    tmp:=Tools[t];

    if tmp.Active then
    begin
      TToolAccess(tmp).ChartDragDropEvent(AEvent,Source,X,Y,State,Accept,DragObject);
    end;
  end;
end;

procedure TCustomChart.BroadcastKeyEvent(AEvent: TChartKeyEvent; var Key: Word;
  var Key1: Char; Shift: TShiftState);
var t : Integer;
    tmp : TTeeCustomTool;
begin
  for t:=0 to Tools.Count-1 do
  begin
    tmp:=Tools[t];

    if tmp.Active then
    begin
      TToolAccess(tmp).ChartKeyEvent(AEvent,Key,Key1,Shift);
    end;
  end;
end;

procedure TCustomChart.BroadcastMouseEvent(AEvent:TChartMouseEvent;
                    Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

  procedure CheckList(const AList:TChartTools);
  var t : Integer;
      tmp : TTeeCustomTool;
  begin
    for t:=0 to AList.Count-1 do
    begin
      tmp:=AList[t];

      if tmp.Active then
      begin
        TToolAccess(tmp).ChartMouseEvent(AEvent,Button,Shift,X,Y);
        if CancelMouse then
           Break;
      end;
    end;
  end;

begin
  CheckList(Tools);
  CheckList(Animations);
end;

procedure TCustomChart.BroadcastWheelEvent(AEvent:TWheelMouseEvent;
                         WheelDelta:Integer; X, Y: Integer);
var t : Integer;
    tmp : TTeeCustomTool;
begin
  for t:=0 to Tools.Count-1 do
  begin
    tmp:=Tools[t];

    if tmp.Active then
    begin
      TToolAccess(tmp).WheelMouseEvent(AEvent,WheelDelta,X,Y);

      if CancelMouse then
         Break;
    end;
  end;
end;

procedure TCustomChart.MouseDown(Button: TMouseButton; Shift: TShiftState;
             X, Y: TCoordinate);
var IClicked : Boolean;

    Procedure CheckZoomPanning;
    Begin
      if ActiveSeriesCanZoomScroll then
      begin
        if Zoom.Allow and (Button=Zoom.MouseButton) and
           (Zoom.KeyShift<=Shift) then
        Begin
          if Zoom.Direction=tzdVertical then x:=ChartRect.Left;
          if Zoom.Direction=tzdHorizontal then y:=ChartRect.Top;

          Zoom.Activate({$IFDEF FMX}Round{$ENDIF}(x),{$IFDEF FMX}Round{$ENDIF}(y));

          if Zoom.Direction=tzdVertical then
             Zoom.X1:={$IFDEF FMX}Round{$ENDIF}(ChartRect.Right);

          if Zoom.Direction=tzdHorizontal then
             Zoom.Y1:={$IFDEF FMX}Round{$ENDIF}(ChartRect.Bottom);

          // Micro-optimization, do not repaint chart for 0 size zoom:
          if (Zoom.X0<>Zoom.X1) or (Zoom.Y0<>Zoom.Y1) then
             TryDrawZoom;

          IClicked:=True;
        end;

        if (AllowPanning<>pmNone) and (Button=ScrollMouseButton) and
           (TeeScrollKeyShift<=Shift ) then
        Begin
          Panning.Activate({$IFDEF FMX}Round{$ENDIF}(x),
                           {$IFDEF FMX}Round{$ENDIF}(y));

          if (not Panning.InsideBounds) and Assigned(Parent) then
             {$IFDEF FMX}Capture{$ELSE}SetCaptureControl(Self){$ENDIF};

          IClicked:=True;
        end;

        // was: crDefault; { <-- todo: ScrollCursor:=crHandPoint }
        if IClicked then
           Cursor:=OriginalCursor;
      end;
    end;

var
  xInt, yInt : Integer;

    Procedure CheckBackground;
    begin
      if Assigned(FOnClickBackground) then
      begin
        CancelMouse:=True;
        FOnClickBackGround(Self, Button, Shift, xInt, yInt);
        IClicked:=CancelMouse;
      end;
    end;

    Procedure CheckTitle(const ATitle:TChartTitle);
    begin
      if Assigned(FOnClickTitle) then
      begin
        CancelMouse:=True;
        FOnClickTitle(Self, ATitle, Button, Shift, xInt, yInt);
        IClicked:=CancelMouse;
      end;

      if not IClicked then CheckZoomPanning;
    end;

var tmpPart : TChartClickedPart;
Begin
  inherited;

  xInt:={$IFDEF FMX}Round{$ENDIF}(x);
  yInt:={$IFDEF FMX}Round{$ENDIF}(y);

  if CancelMouse or Zoom.Active or Panning.Active then
     Exit;

  CancelMouse:=False;
  BroadcastMouseEvent(cmeDown, Button, Shift, xInt, yInt);

  if not CancelMouse then
  begin
    tmpPart:=CalcNeedClickedPart(TeePoint(x,y),True);
    IClicked:=False;

    Case tmpPart.Part of
      cpLegend: begin
                  IClicked:=FLegend.DoMouseDown(xInt,yInt,tmpPart.PointIndex);

                  if Assigned(FOnClickLegend) then
                  begin
                    CancelMouse:=True;
                    FOnClickLegend(Self,Button,Shift,xInt,yInt);
                    IClicked:=CancelMouse;
                  end;
                end;

      cpSeries,
cpSeriesPointer : Begin
                  CancelMouse:=False;

                  // Fixes TV52014945 and TV52015075
                  with tmpPart do
                    TSeriesAccess(ASeries).MouseDown(PointIndex,Part=cpSeriesPointer,Button,Shift,xInt,yInt);

                  IClicked:=CancelMouse;

                  if Assigned(FOnClickSeries) then
                  begin
                    CancelMouse:=True;
                    FOnClickSeries(Self,tmpPart.ASeries,tmpPart.PointIndex,Button,Shift,xInt,yInt);
                    IClicked:=CancelMouse;
                  end;

                  if not IClicked then
                     CheckZoomPanning;
                 end;

        cpAxis: begin
                  if Assigned(FOnClickAxis) then
                  begin
                    CancelMouse:=True;
                    FOnClickAxis(Self,TChartAxis(tmpPart.AAxis),Button,Shift,xInt,yInt);
                    IClicked:=CancelMouse;
                  end;

                  if not IClicked then
                     CheckZoomPanning;
                 end;

     cpTitle   : CheckTitle(Title);
     cpFoot    : CheckTitle(Foot);
     cpSubTitle: CheckTitle(SubTitle);
     cpSubFoot : CheckTitle(SubFoot);

   cpChartRect : begin
                   CheckBackground;

                   if not IClicked then
                      CheckZoomPanning;
                 end;
    end;

    if not IClicked then
       CheckBackground;
  end;

  CancelMouse:=False;
End;

procedure CheckDoubleOverflow(const AValue:Extended; var AResult:TAxisValue);
begin
  {$IFNDEF TEEVALUEEXTENDED}
  if Abs(AValue)<=MaxDouble then
  {$ENDIF}
     AResult:=AValue;
end;

procedure TCustomChart.MouseMove(Shift: TShiftState; X, Y: TCoordinate);

  Procedure ProcessPanning(const Axis:TChartAxis; IniPos,EndPos:Integer);
  var Delta          : TAxisValue;
      tmpMin         : TAxisValue;
      tmpMax         : TAxisValue;
      tmpAllowScroll : Boolean;
      pointDiff      : Integer;
      AxisPosMin     : Integer;
      AxisPosMax     : Integer;
      changeAtMin    : TAxisValue;
      changeAtMax    : TAxisValue;
      activeMin,
      activeMax      : TAxisValueExtended;

      {$IFDEF FMX}
      {$IFDEF D18}
      {$IFNDEF D20}
      tmpScale       : Single;
      {$ENDIF}
      {$ENDIF}
      {$ENDIF}
  begin
    with TAxisAccess(Axis) do
    begin
      if MinimumOffset=0 then
         activeMin:=InternalMinimum
      else
         activeMin:=Minimum;

      if MaximumOffset=0 then
         activeMax:=InternalMaximum
      else
         activeMax:=Maximum;

      if Logarithmic then
      begin
        pointDiff := EndPos - IniPos;

        AxisPosMin := CalcPosValue(activeMin);
        AxisPosMax := CalcPosValue(activeMax);

        changeAtMin:=activeMin-CalcPosPoint(AxisPosMin+pointDiff);
        changeAtMax:=activeMax-CalcPosPoint(AxisPosMax+pointDiff);

        CheckDoubleOverflow(activeMin+changeAtMin,tmpMin);
        CheckDoubleOverflow(activeMax+changeAtMax,tmpMax);

        // avoid brusk drags breaking the axis
        if tmpMin > tmpMax then
        Begin
          tmpMin := activeMin;
          tmpMax := activeMax;
        end;
      end
      else
      begin
        Delta:=CalcPosPoint(IniPos)-CalcPosPoint(EndPos);

        {$IFDEF FMX}
        {$IFDEF D18}
        {$IFNDEF D20}
        tmpScale:=Canvas.ReferenceCanvas.Scale;

        // iOS Simulator, Scale might be zero
        if tmpScale<>0 then
           Delta:=Delta*tmpScale;
        {$ENDIF}
        {$ENDIF}
        {$ENDIF}

        CheckDoubleOverflow(activeMin+Delta,tmpMin);
        CheckDoubleOverflow(activeMax+Delta,tmpMax);
      end;
    end;

    tmpAllowScroll:=True;

    if Assigned(FOnAllowScroll) then
       FOnAllowScroll(TChartAxis(Axis),tmpMin,tmpMax,tmpAllowScroll);

    if tmpAllowScroll then
       Axis.SetMinMax(tmpMin,tmpMax);
  end;

var
  Panned : Boolean;

   Procedure PanAxis(AxisHorizontal:Boolean; Const Pos1,Pos2:Integer);
   var tmpAxis : TChartAxis;
       t       : Integer;
       tmpMode : TPanningMode;
   begin
     if AxisHorizontal then tmpMode:=pmHorizontal
                       else tmpMode:=pmVertical;

     if (Pos1<>Pos2) and
        ( (AllowPanning=tmpMode) or (AllowPanning=pmBoth) ) then
     begin
       for t:=0 to Axes.Count-1 do
       begin
         tmpAxis:=Axes[t];

         if not tmpAxis.IsDepthAxis then
            if (AxisHorizontal and tmpAxis.Horizontal) or
               ((not AxisHorizontal) and (not tmpAxis.Horizontal)) then
               ProcessPanning(tmpAxis,Pos2,Pos1);
       end;

       Panned:=True;
     end;
   end;

   function NotLegendVisible(UpTo:Integer):Integer;
   var t : Integer;
   begin
     result:=0;

     for t:=0 to UpTo do
         if not Series[t].Legend.Visible then
            Inc(result);
   end;
   
   function FirstSelectedSeries:Integer;
   var t : Integer;
   begin
     result:=-1;

     for t:=SeriesCount-1 downto 0 do
         with Series[t] do
         if Active and (Selected.HoverIndex<>-1) then
         begin
           if Legend.Visible then
              result:=t-NotLegendVisible(t);

           Exit;
         end;
   end;

var tmpHover,
    xInt,
    yInt : Integer;

    tmpCursor : TCursor;

    tmpSeries : TChartSeries;
begin
  inherited;

  {$IFDEF FMX}
  // Protection: It might happen MouseMove event is called before the Chart
  // has been painted (ie: ReferenceCanvas is nil)
  if not Assigned(Canvas.ReferenceCanvas) then
     Exit;
  {$ENDIF}

  xInt:={$IFDEF FMX}Round{$ENDIF}(x);
  yInt:={$IFDEF FMX}Round{$ENDIF}(y);

  if not (csDesigning in ComponentState) then
     BroadcastMouseEvent(cmeMove,{$IFDEF FMX}TMouseButton.{$ENDIF}mbLeft,Shift,xInt,yInt);

  if not CancelMouse then
  with Zoom do
  if Active then
  Begin
    if Direction=tzdVertical then
    begin
      x:=ChartRect.Right;
      xInt:={$IFDEF FMX}Round{$ENDIF}(x);
    end;

    if Direction=tzdHorizontal then
    begin
      y:=ChartRect.Bottom;
      yInt:={$IFDEF FMX}Round{$ENDIF}(y);
    end;

    if (x<>X1) or (y<>Y1) then
    begin
      TryDrawZoom;

      if KeepAspectRatio then
         CorrectAspectRatio(ChartRect,X,Y)
      else
      begin
        X1:=xInt;
        Y1:=yInt;
      end;

      if not FullRepaint then
         DrawZoomRectangle;
    end;

  end
  else
  With Panning do { scroll }
  if Active then
  Begin

    if (not InsideBounds) or PointInRect(ChartRect,x,y) then
    Begin

      if (x<>X1) or (y<>Y1) then
      Begin
        Panned:=False;
        //Check; //TF02012624

        if RestoredAxisScales then
        begin
          FSavedScales:=SaveScales;
          RestoredAxisScales:=False;
        end;

        PanAxis(True,XInt,X1);
        PanAxis(False,YInt,Y1);
        X1:=xInt;
        Y1:=yInt;

        if Panned then
        Begin
          if Assigned(FOnScroll) then
             FOnScroll(Self);

          Invalidate;
        end;
      End;
    end
    else
      Panning.Active:=False;

  end
  else
  begin
    tmpCursor:=Legend.CalcCursor;

    tmpHover:=Legend.Clicked(xInt,yInt);

    if (tmpCursor<>crDefault) and (tmpHover<>-1) then
       Cursor:=tmpCursor
    else
    if tmpHover=-1 then
       if not CheckMouseSeries(xInt,yInt) then { is mouse not over Series ? }
          if not CheckMouseAxes(xInt,yInt) then
             Cursor:=OriginalCursor; { change back original cursor }

    if Hover.Visible then
    begin
      if Legend.InternalLegendStyle=lsValues then
      begin
        tmpSeries:=Legend.GetLegendSeries;

        if Assigned(tmpSeries) then
           if tmpHover<>-1 then
              tmpSeries.Selected.HoverIndex:=tmpSeries.LegendToValueIndex(tmpHover)
           else
              tmpHover:=TSeriesAccess(tmpSeries).ValueIndexToLegend(tmpSeries.Selected.HoverIndex)
        else
           tmpHover:=-1;

      end
      else
      if tmpHover=-1 then
         tmpHover:=FirstSelectedSeries; // <-- Legend ItemIndex of Series

      Legend.Selected.HoverIndex:=tmpHover;
    end
  end;
end;

Procedure TCustomChart.ScrollVerticalAxes(Up:Boolean);
var t        : Integer;
    tmp      : TAxisValue;
    tmpMin   : TAxisValue;
    tmpMax   : TAxisValue;
    tmpAllow : Boolean;
begin
  if RestoredAxisScales then //TV52016525
  begin
    FSavedScales:=SaveScales;
    RestoredAxisScales:=False;
  end;

  for t:=0 to Axes.Count-1 do
  with Axes[t] do
  if not Horizontal then
  begin
    tmp:=(Maximum-Minimum)*3.0*0.01;

    if not Up then
       tmp:=-tmp;

    tmpAllow:=True;

    if Assigned(FOnAllowScroll) then
    begin
      tmpMin:=Axes[t].Minimum+tmp;
      tmpMax:=Axes[t].Maximum+tmp;

      FOnAllowScroll(Axes[t],tmpMin,tmpMax,tmpAllow);
    end;

    if tmpAllow then
       Scroll(tmp,False);
  end;
end;

{$IFDEF FMX}
procedure TCustomChart.DragEnd;
var tmpB : Boolean;
    tmpO : TDragObject;
begin
  inherited;

  with GetCursorPos{$IFDEF FMX}.Round{$ENDIF} do
       BroadcastDragDropEvent(cdeEnd, Self, X, Y, TDragState(nil), tmpB, tmpO);
end;
{$ELSE}
procedure TCustomChart.DoEndDrag(Target: TObject; X, Y: Integer);
var tmpB : Boolean;
    tmpO : TDragObject;
begin
  inherited;
  BroadcastDragDropEvent(cdeEnd, Target, X, Y, TDragState(nil), tmpB, tmpO);
end;
{$ENDIF}

procedure TCustomChart.InternalWheel(WheelDelta: Integer);
var tmpUp : Boolean;
    tmpP  : TPoint;
    tmpEvent : TWheelMouseEvent;
begin
  tmpUp:=WheelDelta>0;

  if CanWheelScroll then
  begin
    if Panning.MouseWheel=pmwInverted then
       tmpUp:=not tmpUp;

    ScrollVerticalAxes(tmpUp);
  end
  else
  if Zoom.Allow and (Zoom.MouseWheel<>pmwNone) then
  begin
    if Zoom.MouseWheel=pmwInverted then
       tmpUp:=not tmpUp;

    if tmpUp then
       ZoomPercent(110)
    else
       ZoomPercent(90);
  end
  else
  if (ZoomWheel<>pmwNone) and (View3D or Canvas.SupportsFullRotation) then
  begin
    if ZoomWheel=pmwInverted then
       tmpUp:=not tmpUp;

    if tmpUp then
       View3DOptions.ZoomFloat:=View3DOptions.ZoomFloat*1.1
    else
       View3DOptions.ZoomFloat:=View3DOptions.ZoomFloat*0.9;
  end;

  tmpP:=GetCursorPos;

  if tmpUp then
     tmpEvent:=wmeUp
  else
     tmpEvent:=wmeDown;

  BroadcastWheelEvent(tmpEvent, WheelDelta,{$IFDEF FMX}Round{$ENDIF}(tmpP.X),{$IFDEF FMX}Round{$ENDIF}(tmpP.Y));
end;

{$IFDEF FMX}
procedure TCustomChart.MouseWheel(Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean);
begin
  inherited;
  InternalWheel(WheelDelta);
end;
{$ELSE}
function TCustomChart.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean;
begin
  result:=inherited DoMouseWheel(Shift,WheelDelta,MousePos);

  with GetCursorPos do
       BroadcastWheelEvent(wmeMove,WheelDelta,X,Y);
end;
{$ENDIF}

function TCustomChart.CanWheelScroll:Boolean;
begin
  result:=(Panning.MouseWheel<>pmwNone) and
         ((AllowPanning=pmBoth) or (AllowPanning=pmVertical)) and
         (not (csDesigning in ComponentState));
end;

{$IFNDEF FMX}
function TCustomChart.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean;
begin
  {$IFDEF TEEOCX}
  if (not FMouseWheelToParent) then
    result:=true   // TA05012954
  else
  {$ENDIF}
  result:=inherited DoMouseWheelDown(Shift,MousePos);

  InternalWheel(-1);
end;

function TCustomChart.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean;
begin
  {$IFDEF TEEOCX}
  if (not FMouseWheelToParent) then
    result:=true   // TA05012954
  else
  {$ENDIF}
  result:=inherited DoMouseWheelUp(Shift,MousePos);

  InternalWheel(1);
end;
{$ENDIF}

{$IFDEF FMX}
procedure TCustomChart.BeginAutoDrag;
var tmpB : Boolean;
    tmpO : TDragObject;
begin
  inherited;
  BroadcastDragDropEvent(cdeStart, TObject(nil), 0, 0, TDragState(nil), tmpB, tmpO);
end;
{$ELSE}
procedure TCustomChart.DoStartDrag(var DragObject: TDragObject);
var tmpB : Boolean;
    tmpO : TDragObject;
begin
  inherited;
  BroadcastDragDropEvent(cdeStart, TObject(nil), 0, 0, TDragState(nil), tmpB, tmpO);
end;
{$ENDIF}

procedure TCustomChart.UndoZoom;
begin
  With Zoom do
  begin
    if History and (AxesHistory.Count>1) then
    begin
      ZoomBackOne;
      RemoveZoomSnapshot;
    end
    else
    begin
      if History and (AxesHistory.Count=1) then
         RemoveZoomSnapshot;

      if not RestoredAxisScales then
      begin
        RestoreScales(FSavedScales);
        RestoredAxisScales:=True;
      end;
    end;
  end;

  Zoomed:=False;

  inherited;
end;

procedure TCustomChart.AddZoomSnapshot;
var snapshot: TZoomSnapShot;
    i       : Integer;
begin
  snapshot:=TZoomSnapShot.Create;

  SetLength(snapshot.AxesMinMax,8);

  snapshot.AxesMinMax[0]:=Axes.Left.Minimum;
  snapshot.AxesMinMax[1]:=Axes.Left.Maximum;
  snapshot.AxesMinMax[2]:=Axes.Top.Minimum;
  snapshot.AxesMinMax[3]:=Axes.Top.Maximum;
  snapshot.AxesMinMax[4]:=Axes.Right.Minimum;
  snapshot.AxesMinMax[5]:=Axes.Right.Maximum;
  snapshot.AxesMinMax[6]:=Axes.Bottom.Minimum;
  snapshot.AxesMinMax[7]:=Axes.Bottom.Maximum;

  if CustomAxes.Count>0 then
  begin
    snapshot.CustomCount:=CustomAxes.Count;
    SetLength(snapshot.CustomMinMax,snapshot.CustomCount*2);

    for i:=0 to snapshot.CustomCount-1 do
    begin
      SetLength(snapshot.CustomMinMax[i],2);
      snapshot.CustomMinMax[i][0]:=CustomAxes.Items[i].Minimum;
      snapshot.CustomMinMax[i][1]:=CustomAxes.Items[i].Maximum;
    end;
  end;

  Zoom.AxesHistory.Add(snapshot);
end;

procedure TCustomChart.RemoveZoomSnapshot;
begin
  with Zoom.AxesHistory do
    if Count>0 then
    {$IFDEF D18}
       Delete(Count-1);
    {$ELSE}
    begin
      Items[Count-1].Free;
      Delete(Count-1);
    end;
    {$ENDIF}
end;

procedure TCustomChart.ZoomBackOne;
var snapshot: TZoomSnapShot;
begin
  snapshot:=Zoom.AxesHistory.Items[Zoom.AxesHistory.Count - 2];

  Axes.Left.SetMinMax(snapshot.AxesMinMax[0], snapshot.AxesMinMax[1]);
  Axes.Top.SetMinMax(snapshot.AxesMinMax[2], snapshot.AxesMinMax[3]);
  Axes.Right.SetMinMax(snapshot.AxesMinMax[4], snapshot.AxesMinMax[5]);
  Axes.Bottom.SetMinMax(snapshot.AxesMinMax[6], snapshot.AxesMinMax[7]);
end;

type
  TTChartSeriesAccess=class(TChartSeries);

Procedure TCustomChart.CalcZoomPoints;
var Series : TChartSeries;
    t : Integer;
Begin
  Series:=nil;

  With Zoom, Axes do
  Begin
    Check;

    for t:=0 to SeriesCount-1 do
    begin
    if SeriesList[t].Active and SeriesList[t].CanZoomScroll then
       Series:=SeriesList[t];
    end;

    if Assigned(Series) and TTChartSeriesAccess(Series).FZoomSeries then
    begin
      DoZoom(Series.XScreenToValue(X0),Series.XScreenToValue(X1),
             Series.XScreenToValue(X0),Series.XScreenToValue(X1),
             Series.YScreenToValue(Y1),Series.YScreenToValue(Y0),
             Series.YScreenToValue(Y1),Series.YScreenToValue(Y0));
    end
    else
    begin
      DoZoom( Top.CalcPosPoint(X0),Top.CalcPosPoint(X1),
              Bottom.CalcPosPoint(X0),Bottom.CalcPosPoint(X1),
              Left.CalcPosPoint(Y1),Left.CalcPosPoint(Y0),
              Right.CalcPosPoint(Y1),Right.CalcPosPoint(Y0));
    end;

    if History then
       AddZoomSnapshot;
  end;
End;

procedure TCustomChart.MouseUp(Button: TMouseButton; Shift: TShiftState;
                         X, Y: TCoordinate);
var xInt, yInt : Integer;
Begin
  CancelMouse:=False;

  inherited;

  xInt:={$IFDEF FMX}Round{$ENDIF}(x);
  yInt:={$IFDEF FMX}Round{$ENDIF}(y);

  if Zoom.Active and (Button=Zoom.MouseButton) then
  With Zoom do
  Begin
    Active:=False;

    TryDrawZoom;

    Canvas.Pen.Mode:=pmCopy;

    if not KeepAspectRatio then
    begin
      if Direction<>tzdVertical then X1:=xInt;
      if Direction<>tzdHorizontal then Y1:=yInt;
    end;

    if ((Direction=tzdVertical) or ( Abs(X1-X0)>MinimumPixels )) and
       ((Direction=tzdHorizontal) or ( Abs(Y1-Y0)>MinimumPixels )) then
    begin
      if UpLeftZooms or
         ((Direction=tzdVertical) and (Y1>Y0)) or
         ((Direction<>tzdVertical) and (X1>X0)) then
           CalcZoomPoints // <-- do zoom in !
      else
         UndoZoom; // <-- do zoom out !

      Invalidate;
    end;
  end;

  Panning.Active:=False;

  if (not Panning.InsideBounds) and Assigned(Parent) then
     {$IFDEF FMX}ReleaseCapture{$ELSE}SetCaptureControl(nil){$ENDIF};

  BroadcastMouseEvent(cmeUp,Button,Shift,XInt,YInt);
End;

Procedure TCustomChart.CalcWallsRect;
var tmp : Integer;
begin
  CalcSize3DWalls;

  if not CustomChartRect then
  begin
    { for 3D orthogonal mode only }
    if View3D and View3DOptions.Orthogonal then
    begin
      if ActiveSeriesUseAxis then tmp:=BackWall.Size
                             else tmp:=0;

      With ChartRect do
      begin
        if View3DOptions.OrthoAngle>90 then
        begin
          Left:=Left+Abs(Width3D)+tmp;

          if LeftWall.Visible then
             Left:=Left+LeftWall.Size+1;
        end
        else
        begin
          Right:=Right-(Abs(Width3D)+tmp);

          if Assigned(FWalls.FRight) and RightWall.Visible then
             Right:=Right-(RightWall.Size+1);
        end;

        Top:=Top+Abs(Height3D)+tmp;
      end;
    end;
  end;

  RecalcWidthHeight;
end;

Procedure TCustomChart.Assign(Source:TPersistent);
begin
  if Source is TCustomChart then
  With TCustomChart(Source) do
  begin
    Self.ColorPaletteIndex:=ColorPaletteIndex;
    Self.Foot       :=FFoot;
    Self.Legend     :=FLegend;
    Self.SubFoot    :=FSubFoot;
    Self.SubTitle   :=FSubTitle;
    Self.Title      :=FTitle;
    Self.Walls      :=Walls;
  end;

  inherited;
end;

Procedure TCustomChart.GetChildren(Proc:TGetChildProc; Root:TComponent);
var t : Integer;
begin
  inherited;
  for t:=0 to Tools.Count-1 do Proc(Tools[t]);
  for t:=0 to Animations.Count-1 do Proc(Animations[t]);
end;

Procedure TCustomChart.FillSeriesSourceItems( ASeries:TChartSeries;
                                              Proc:TGetStrProc);
begin { abstract }
end;

Procedure TCustomChart.FillValueSourceItems( ValueList:TChartValueList;
                                             Proc:TGetStrProc);
var t:Integer;
Begin
  With ValueList.Owner do
  if Assigned(DataSource) and (DataSource is TChartSeries) then
     With TChartSeries(DataSource) do
     for t:=0 to ValuesList.Count-1 do Proc(ValuesList[t].Name);
end;

Function TCustomChart.GetASeries:TChartSeries;
var t : Integer;
Begin
  for t:=0 to SeriesCount-1 do
  if Series[t].Active then
  begin
    result:=SeriesList[t];
    exit;
  end;

  result:=nil;
End;

procedure TCustomChart.SetWalls(const Value:TChartWalls);
begin
  Walls.Assign(Value);
end;

procedure TCustomChart.SetColorPaletteIndex(const Value:Integer);
begin
  if FColorPaletteIndex<>Value then
  begin
    // if we want custom colors, first reset to default:
    if Value=-2 then
       if FColorPaletteIndex<=0 then
          ColorPalettes.ApplyPalette(Self,TeeColorPalette)  // Default palette
       else
          ColorPalettes.ApplyPalette(Self,FColorPaletteIndex);

    FColorPaletteIndex:=Value;

    // if not custom, apply:
    if FColorPaletteIndex<>-2 then
       ColorPalettes.ApplyPalette(Self,FColorPaletteIndex);
  end;
end;

procedure TCustomChart.SetTitle(const Value:TChartTitle);
begin
  if Assigned(Value) then
     Title.Assign(Value)
  else
     FreeAndNil(FTitle);
end;

procedure TCustomChart.SetFoot(const Value:TChartTitle);
begin
  if Assigned(Value) then
     Foot.Assign(Value)
  else
     FreeAndNil(FFoot);
end;

function TCustomChart.GetSubFoot:TChartTitle;
begin
  if not Assigned(FSubFoot) then
     FSubFoot:=TChartFootTitle.Create(Self);

  result:=FSubFoot;
end;

function TCustomChart.GetFoot:TChartTitle;
begin
  if not Assigned(FFoot) then
     FFoot:=TChartFootTitle.Create(Self);

  result:=FFoot;
end;

function TCustomChart.GetSubTitle:TChartTitle;
begin
  if not Assigned(FSubTitle) then
    FSubTitle:=TChartTitle.Create(Self);

  result:=FSubTitle;
end;

function TCustomChart.GetTitle:TChartTitle;
begin
  if not Assigned(FTitle) then
     FTitle:=TChartTitle.Create(Self);

  result:=FTitle;
end;

procedure TCustomChart.SetSubTitle(const Value: TChartTitle);
begin
  if Assigned(Value) then
     SubTitle.Assign(Value)
  else
     FreeAndNil(FSubTitle);
end;

procedure TCustomChart.SetSubFoot(const Value: TChartTitle);
begin
  if Assigned(Value) then
     SubFoot.Assign(Value)
  else
     FreeAndNil(FSubFoot);
end;

procedure TCustomChart.ReadBackColor(Reader: TReader);
begin
  if Reader.NextValue=vaIdent then
     BackColor:=StringToColor(Reader.ReadIdent)
  else
     BackColor:=Reader.ReadInt64;
end;

procedure TCustomChart.WriteColorPaletteIndex(Writer: TWriter);
begin
  Writer.WriteInteger(ColorPaletteIndex);
end;

procedure TCustomChart.WriteColorPalette(Writer: TWriter);
var t : Integer;
begin
  Writer.WriteListBegin;

  for t:=0 to Length(ColorPalette)-1 do
      Writer.WriteInteger(ColorPalette[t]);

  Writer.WriteListEnd;
end;

procedure TCustomChart.DefineProperties(Filer: TFiler);

  function DoColorPaletteIndex:Boolean;
  begin
    if Filer.Ancestor <> nil then
       result:=ColorPaletteIndex<>TCustomChart(Filer.Ancestor).ColorPaletteIndex
    else
       result:=(ColorPaletteIndex>-1) or (ColorPaletteIndex=-2);
  end;

begin
  inherited;
  Filer.DefineProperty('BackColor',ReadBackColor,nil,False); // Do not localize

  Filer.DefineProperty('ColorPaletteIndex',ReadColorPaletteIndex,  // Do not localize
                        WriteColorPaletteIndex,DoColorPaletteIndex);

  Filer.DefineProperty('ColorPalette',ReadColorPalette,  // Do not localize
                        WriteColorPalette,ColorPaletteIndex=-2);
end;

procedure TCustomChart.ReadColorPaletteIndex(Reader: TReader);
begin
  ColorPaletteIndex:=Reader.ReadInt64;
end;

procedure TCustomChart.ReadColorPalette(Reader: TReader);
var L : Integer;
begin
  ColorPalette:=nil;

  L:=0;

  Reader.ReadListBegin;

  while not Reader.EndOfList do
  begin
    SetLength(ColorPalette,L+1);
    ColorPalette[L]:=Reader.ReadInt64;
    Inc(L);
  end;

  Reader.ReadListEnd;

  if Length(ColorPalette)=0 then
     ColorPaletteIndex:=-1;
end;

procedure TCustomChart.DrawLegendShape(const Rect: TRect; const AGraphic:TGraphic);
begin
  if (AGraphic<>nil) and Legend.Symbol.UseImages then
     Canvas.StretchDraw(Rect,AGraphic)
  else
     Legend.Symbol.Draw(Canvas.Brush.Color,Rect);
end;

{ TTeeCustomShapePosition }
Procedure TTeeCustomShapePosition.SetCustomPosition(Const Value:Boolean);
Begin
  if Assigned(ParentChart) then
     TTeePanelAccess(ParentChart).SetBooleanProperty(FCustomPosition,Value)
  else
     FCustomPosition:=Value;
end;

Procedure TTeeCustomShapePosition.SetLeft(Const Value:Integer);
Begin
  FCustomPosition:=True;

  if Assigned(ParentChart) then
     TTeePanelAccess(ParentChart).
       {$IFDEF FMX}
       SetSingleProperty
       {$ELSE}
       SetIntegerProperty
       {$ENDIF}(ShapeBounds.Left,Value)
  else
     ShapeBounds.Left:=Value;
end;

Procedure TTeeCustomShapePosition.SetTop(Const Value:Integer);
Begin
  FCustomPosition:=True;

  if Assigned(ParentChart) then
     TTeePanelAccess(ParentChart).
        {$IFDEF FMX}
        SetSingleProperty
        {$ELSE}
        SetIntegerProperty
        {$ENDIF}(ShapeBounds.Top,Value)
  else
     ShapeBounds.Top:=Value;
end;

Procedure TTeeCustomShapePosition.Assign(Source:TPersistent);
Begin
  if Source is TTeeCustomShapePosition then
  With TTeeCustomShapePosition(Source) do
  Begin
    Self.FCustomPosition:=FCustomPosition;

    if Self.FCustomPosition then
    begin
      Self.Left :=Left;
      Self.Top  :=Top;
    end;
  end;

  inherited;
end;

function TTeeCustomShapePosition.GetLeft: Integer;
begin
  result:={$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Left);
end;

function TTeeCustomShapePosition.GetTop: Integer;
begin
  result:={$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Top);
end;

function TTeeCustomShapePosition.IsCustomStored: Boolean;
begin
  result:=FCustomPosition<>DefaultCustom;
end;

{ TLegendSymbol }
Constructor TLegendSymbol.Create(const AParent:TCustomTeePanel);
begin
  inherited Create;

  Parent:=AParent;
  FVisible:=True;
  FDefaultPen:=True;

  FUseImages:=True;

  FPen:=TChartPen.Create(CanvasChanged);

  FWidth:=TeeDefaultLegendSymbolWidth;
  FHeight:=TeeDefaultLegendSymbolHeight;

  FSquared:=True;

  FGradient:=TTeeGradient.Create(CanvasChanged);

  FShadow:=TTeeShadow.Create(CanvasChanged);
  TShadowAccess(FShadow).InitValues(FShadow.Visible,FShadow.Color,1);

  FEmboss:=TTeeEmboss.Create(CanvasChanged);
  TEmbossAccess(FEmboss).InitValues(FEmboss.Visible,FEmboss.Color,1);
end;

Destructor TLegendSymbol.Destroy;
begin
  FEmboss.Free;
  FShadow.Free;

  FGradient.Free;
  
  FPen.Free;
  inherited;
end;

Procedure TLegendSymbol.CanvasChanged(Sender:TObject);
begin
  Repaint;
end;

Procedure TLegendSymbol.Assign(Source:TPersistent);
Begin
  if Source is TLegendSymbol then
  With TLegendSymbol(Source) do
  Begin
    Self.FContinuous :=Continuous;
    Self.Gradient    :=Gradient;
    Self.FHeight     :=Height;
    Self.FHeightUnits:=HeightUnits;
    Self.Pen         :=Pen;
    Self.FPosition   :=Position;
    Self.Emboss      :=Emboss;
    Self.Shadow      :=Shadow;
    Self.FSquared    :=Squared;
    Self.FUseImages  :=UseImages;
    Self.FWidth      :=Width;
    Self.FWidthUnits :=WidthUnits;
    Self.FVisible    :=Visible;
  end;
end;

function TLegendSymbol.CalcHeight:Integer;
begin
  if FHeightUnits=lcsPercent then
     if FHeight>100 then
        result:=Parent.Canvas.FontHeight
     else
        result:=Round(FHeight*(CalcItemHeight+1-5)*0.01)
  else
     result:=FHeight;
end;

Function TLegendSymbol.CalcWidth(Value:Integer):Integer;
begin
  if Visible then
     if Squared then
     begin
        result:=CalcHeight;

        if Assigned(Parent) and (Parent is TCustomChart) then
           result:=result - TCustomChart(Parent).Legend.VertSpacing;
     end
     else
     if FWidthUnits=lcsPercent then result:=Round(Width*Value*0.01)
                               else result:=Width
  else
     result:=0;
end;

procedure TLegendSymbol.Repaint;
begin
  if Assigned(Parent) then
     Parent.Invalidate;
end;

type
  TPanelAccess=class(TCustomTeePanel);

procedure TLegendSymbol.SetWidth(Const Value: Integer);
begin
  if Assigned(Parent) then
     TPanelAccess(Parent).SetIntegerProperty(FWidth,Value)
  else
     FWidth:=Value;
end;

procedure TLegendSymbol.SetWidthUnits(const Value: TLegendSymbolSize);
begin
  if FWidthUnits<>Value then
  begin
    FWidthUnits:=Value;
    Repaint;
  end;
end;

procedure TLegendSymbol.SetHeight(Const Value: Integer);
begin
  if Assigned(Parent) then
     TPanelAccess(Parent).SetIntegerProperty(FHeight,Value)
  else
     FHeight:=Value;
end;

procedure TLegendSymbol.SetHeightUnits(const Value: TLegendSymbolSize);
begin
  if FHeightUnits<>Value then
  begin
    FHeightUnits:=Value;
    Repaint;
  end;
end;

procedure TLegendSymbol.SetPosition(Const Value: TLegendSymbolPosition);
begin
  if FPosition<>Value then
  begin
    FPosition:=Value;
    Repaint;
  end;
end;

Procedure TLegendSymbol.SetShadow(const Value:TTeeShadow);
begin
  FShadow.Assign(Value);
end;

Procedure TLegendSymbol.SetEmboss(const Value:TTeeEmboss);
begin
  FEmboss.Assign(Value);
end;

procedure TLegendSymbol.SetBooleanProperty(var Variable:Boolean; const Value: Boolean);
begin
  if Variable<>Value then
  begin
    Variable:=Value;
    Repaint;
  end;
end;

procedure TLegendSymbol.SetContinuous(const Value: Boolean);
begin
  SetBooleanProperty(FContinuous,Value);
end;

procedure TLegendSymbol.SetVisible(const Value: Boolean);
begin
  SetBooleanProperty(FVisible,Value);
end;

procedure TLegendSymbol.SetDefaultPen(const Value: Boolean);
begin
  SetBooleanProperty(FDefaultPen,Value);
end;

procedure TLegendSymbol.SetPen(const Value: TChartPen);
begin
  FPen.Assign(Value);
end;

procedure TLegendSymbol.SetSquared(const Value: Boolean);
begin
  SetBooleanProperty(FSquared,Value);
end;

procedure TLegendSymbol.SetUseImages(const Value: Boolean);
begin
  SetBooleanProperty(FUseImages,Value);
end;

procedure TLegendSymbol.SetGradient(const Value: TTeeGradient);
begin
  Gradient.Assign(Value);
end;

type
  TChartAccess=class(TCustomAxisPanel);

procedure TLegendSymbol.Draw(AColor: TColor; const R:TRect);
var
  tmp : TCanvas3D;

  procedure TryDrawShadow(const AShadow:TTeeShadow);
  begin
    if AShadow.ShouldDraw then
    begin
      if tmp.SupportsFullRotation then
         AShadow.ZPosition:=0.5;

      AShadow.Draw(tmp,R);
    end;
  end;

var OldColor : TColor;
    OldStyle : {$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF};
    tmpR     : TRect;
    tmpSize  : TCoordinate;
    OldBitmap : {$IFDEF FMX}TBrushBitmap{$ELSE}{$IFDEF LCL}TCustomBitmap{$ELSE}TBitmap{$ENDIF}{$ENDIF};
begin
  tmp:=Parent.Canvas;

  if FShadow.ShouldDraw or FEmboss.ShouldDraw then
  begin
    OldColor:=tmp.Brush.Color;
    OldStyle:=tmp.Brush.Style;
    OldBitmap:=tmp.Brush.Bitmap;

    TryDrawShadow(FShadow);
    TryDrawShadow(FEmboss);

    tmp.Brush.Color:=OldColor;
    tmp.Brush.Style:=OldStyle;

    // Setting OldBitmap wrongly resets Brush.Style to bsSolid, so do it only
    // when OldBitmap is different
    if OldBitmap<>tmp.Brush.Bitmap then
       tmp.Brush.Bitmap:=OldBitmap;
  end;

  if not DefaultPen then
     tmp.AssignVisiblePen(Pen);

  if tmp.Brush.Gradient.Visible then
  begin
    Gradient.EndColor:=AColor;
    tmp.Gradient.Assign(tmp.Brush.Gradient);
  end
  else
  begin
    tmp.Gradient.Visible:=False;
    tmp.Brush.Color:=AColor;
  end;

  if tmp.Pen.Style<>psClear then
  begin
     if DefaultPen then
     begin
       if tmp.Brush.Color=TChartAccess(Parent).LegendColor then  { <-- color conflict ! }
       begin
         if tmp.Brush.Color={$IFDEF FMX}TAlphaColors.Black{$ELSE}clBlack{$ENDIF} then
            tmp.Pen.Color:={$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF};
       end;
     end;

     tmpSize:=tmp.Pen.Width;

     tmpR.Left:=R.Left+tmpSize;
     tmpR.Top:=R.Top+tmpSize;
     tmpR.Right:=R.Right-tmpSize;
     tmpR.Bottom:=R.Bottom-tmpSize;

     tmp.Rectangle(tmpR);
  end
  else
  if tmp.Brush.Style<>bsClear then
  begin
    tmpR:=R;

    tmpR.Right:=tmpR.Right+1;
    tmpR.Bottom:=tmpR.Bottom+1;
    tmp.Rectangle(tmpR)
  end;
end;

{ TLegendItem }

Destructor TLegendItem.Destroy;
begin
  FFont.Free;
  inherited;
end;

function TLegendItem.HasFont:Boolean;
begin
  result:=Assigned(FFont);
end;

function TLegendItem.GetFont:TTeeFont;
var Old : Boolean;
begin
  if not HasFont then
     if Assigned(ILegend) then
     begin
       Old:=ILegend.ParentChart.AutoRepaint;
       ILegend.ParentChart.AutoRepaint:=False;

       FFont:=TTeeFont.Create(ILegend.CanvasChanged);
       FFont.Assign(ILegend.Font);

       ILegend.ParentChart.AutoRepaint:=Old;
     end
     else
        FFont:=TTeeFont.Create(nil);

  result:=FFont;
end;

procedure TLegendItem.SetFont(const Value:TTeeFont);
begin
  if Assigned(Value) then
     Font.Assign(Value)
  else
     FreeAndNil(FFont);

  if Assigned(ILegend) then
     ILegend.Repaint;
end;

procedure TLegendItem.SetAlign(const Value:TCanvasTextAlign);
begin
  if FAlign<>Value then
  begin
    FAlign:=Value;
    SetCustom(False);
  end;
end;

procedure TLegendItem.SetLeft(const Value:Integer);
begin
  if FLeft<>Value then
  begin
    FLeft:=Value;
    SetCustom(True);
  end;
end;

procedure TLegendItem.SetText(const Value:String);
begin
  SetTextVariable(FText,Value);
end;

procedure TLegendItem.SetText2(const Value:String);
begin
  SetTextVariable(FText2,Value);
end;

procedure TLegendItem.SetText3(const Value:String);
begin
  SetTextVariable(FText3,Value);
end;

procedure TLegendItem.SetTextVariable(var Variable:String; const Value:String);
begin
  if Variable<>Value then
  begin
    Variable:=Value;
    SetCustom(False);
  end;
end;

procedure TLegendItem.SetTop(const Value:Integer);
begin
  if FTop<>Value then
  begin
    FTop:=Value;
    SetCustom(True);
  end;
end;

procedure TLegendItem.SetCustom(SetPos:Boolean);
begin
  if Assigned(ILegend) then
  begin
    ILegend.Items.FCustom:=True;

    if SetPos then
       ILegend.Items.FCustomPosition:=True;

    ILegend.Repaint;
  end;
end;

{ TLegendItems }

{$IFDEF D18}
Destructor TLegendItems.Destroy;
begin
  ClearItems;
  inherited;
end;
{$ENDIF}

function TLegendItems.GetItem(Index:Integer):TLegendItem;
begin
  while Index>(Count-1) do Add(nil);

  result:=TLegendItem(inherited Items[Index]);

  if not Assigned(result) then
  begin
    result:=TLegendItem.Create;
    result.ILegend:=ILegend;
    inherited Items[Index]:=result;
  end;
end;

procedure TLegendItems.SetCustom(const Value: Boolean);
begin
  FCustom:=Value;
  ILegend.Repaint;
end;

procedure TLegendItems.SetCustomPosition(const Value: Boolean);
begin
  FCustomPosition:=Value;
  ILegend.Repaint;
end;

Procedure TLegendItems.ClearItems;
{$IFNDEF AUTOREFCOUNT}
var t : Integer;
{$ENDIF}
begin
  {$IFNDEF AUTOREFCOUNT}
  for t:=0 to Count-1 do
      TLegendItem(Items[t]).Free;
  {$ENDIF}

  inherited Clear;
end;

{$IFNDEF AUTOREFCOUNT}
Procedure TLegendItems.Clear;
begin
  ClearItems;
  ILegend.Repaint;
end;
{$ENDIF}

{ TCustomChartLegend }

type
  TTeePenAccess=class(TTeePen);

Const TeeLegendOff2  = 2;
      TeeLegendOff4  = 4;

Constructor TCustomChartLegend.Create(const AOwner: TCustomTeePanel);
begin
  inherited;

  InternalLegendStyle:=lsAuto;
  ILastValue:=TeeAllValues;

  IDraw2D:=True;

  FAlignment:=laRight;
  FCurrentPage:=True;

  FSymbol:=TLegendSymbol.Create(AOwner);
  FSymbol.CalcItemHeight:=CalcItemHeight;

  if Assigned(ParentChart) then
     FDividingLines:=TChartHiddenPen.Create(ParentChart.CanvasChanged)
  else
     FDividingLines:=TChartHiddenPen.Create(nil);

  FDividingLines.EndStyle:=esFlat; // <-- not supported in FMX
  TTeePenAccess(FDividingLines).DefaultEnd:=esFlat;

  FLegendStyle:=lsAuto;
  FTextStyle:=ltsLeftValue;
  FTextSymbolGap:=2;
  FTopLeftPos:=10; { % }
  FResizeChart:=True;
  ColumnWidthAuto:=True;
  SetLength(ColumnWidths,TeeMaxLegendColumns);

  FItems:=TLegendItems.Create;
  FItems.ILegend:=Self;

  FPosUnits:=muPixels;

  TSeriesMarks.InitGradient(Gradient);
end;

Destructor TCustomChartLegend.Destroy;
begin
  FSelected.Free;
  IItems:=nil;  // Release memory
  FItems.Free;
  FTitle.Free;
  FSymbol.Free;
  FDividingLines.Free;

  inherited;
end;

function TCustomChartLegend.GetSelected:TTeeSelected;
begin
  if not Assigned(FSelected) then
     FSelected:=TTeeSelected.CreateChart(Self);

  result:=FSelected;
end;

function TCustomChartLegend.GetTitle:TLegendTitle;
begin
  if not Assigned(FTitle) then
     FTitle:=TLegendTitle.Create(ParentChart);

  result:=FTitle;
end;

Procedure TCustomChartLegend.Assign(Source:TPersistent);
Begin
  if Source is TCustomChartLegend then
  With TCustomChartLegend(Source) do
  Begin
    Self.FAlignment   :=FAlignment;
    Self.FCheckBoxes  :=FCheckBoxes;
    Self.FCheckBoxesStyle  :=FCheckBoxesStyle;
    Self.FCurrentPage :=FCurrentPage;
    Self.DividingLines:=DividingLines;
    Self.FDrawBehind  :=FDrawBehind;
    Self.FFirstValue  :=FFirstValue;
    Self.FFontSeriesColor:=FFontSeriesColor;
    Self.FGlobalTransp:=FGlobalTransp;
    Self.FHorizJustify:=FHorizJustify;
    Self.FHorizMargin :=FHorizMargin;
    Self.FInverted    :=FInverted;
    Self.FLeftPercent :=FLeftPercent;
    Self.FLegendStyle :=FLegendStyle;
    Self.FMaxNumRows  :=FMaxNumRows;
    Self.FPosUnits    :=FPosUnits;
    Self.FResizeChart :=FResizeChart;
    Self.Selected     :=FSelected;
    Self.Symbol       :=Symbol;
    Self.FTextStyle   :=FTextStyle;
    Self.Title        :=FTitle;
    Self.FTopLeftPos  :=FTopLeftPos;
    Self.FTopPercent  :=FTopPercent;
    Self.FVertMargin  :=FVertMargin;
    Self.FVertSpacing :=FVertSpacing;
  end;
  inherited;
end;

Procedure TCustomChartLegend.SetAlignment(Const Value:TLegendAlignment);
Begin
  if FAlignment<>Value then
  begin
    FAlignment:=Value;
    Repaint;
  end;
end;

Procedure TCustomChartLegend.SetFirstValue(Const Value:Integer);
Begin
  if Value<0 then
     Raise LegendException.Create(TeeMsg_LegendFirstValue)
  else
     TTeePanelAccess(ParentChart).SetIntegerProperty(FFirstValue,Value);
End;

Procedure TCustomChartLegend.SetHorizJustify(Const Value:TLegendJustify);
Begin
  if FHorizJustify<>Value then
  begin
    FHorizJustify:=Value;
    Repaint;
  end;
end;

Procedure TCustomChartLegend.SetHorizMargin(Const Value:Integer);
Begin
  TTeePanelAccess(ParentChart).SetIntegerProperty(FHorizMargin,Value);
end;

Procedure TCustomChartLegend.SetInverted(Const Value:Boolean);
Begin
  TTeePanelAccess(ParentChart).SetBooleanProperty(FInverted,Value);
end;

Procedure TCustomChartLegend.SetCurrentPage(Const Value:Boolean);
Begin
  TTeePanelAccess(ParentChart).SetBooleanProperty(FCurrentPage,Value);
end;

procedure TCustomChartLegend.SetFontSeriesColor(const Value: Boolean);
begin
  TTeePanelAccess(ParentChart).SetBooleanProperty(FFontSeriesColor,Value);
end;

procedure TCustomChartLegend.SetGlobalTransp(const Value: TTeeTransparency);
begin
  if FGlobalTransp<>Value then
  begin
    FGlobalTransp:=Value;
    Repaint;
  end;
end;

Procedure TCustomChartLegend.SetLeftPercent(Const Value:Integer);
begin
  TTeePanelAccess(ParentChart).SetIntegerProperty(FLeftPercent,Value);
end;

Procedure TCustomChartLegend.SetLegendStyle(Const Value:TLegendStyle);
Begin
  if FLegendStyle<>Value then
  begin
    FLegendStyle:=Value;
    CalcLegendStyle; { <-- force recalc of InternalLegendStyle }
    Repaint;
  end;
end;

Procedure TCustomChartLegend.SetMaxNumRows(Const Value:Integer);
Begin
  TTeePanelAccess(ParentChart).SetIntegerProperty(FMaxNumRows,Value);
end;

Procedure TCustomChartLegend.SetPosUnits(Const Value:TTeeUnits);
begin
  if FPosUnits<>Value then
  begin
    FPosUnits:=Value;
    Repaint;
  end;
end;

Procedure TCustomChartLegend.SetResizeChart(Const Value:Boolean);
Begin
  if ParentChart<>nil then
     TTeePanelAccess(ParentChart).SetBooleanProperty(FResizeChart,Value);
end;

Procedure TCustomChartLegend.SetSelected(Const Value:TTeeSelected);
begin
  if Assigned(Value) then
     Selected.Assign(Value)
  else
     FreeAndNil(FSelected);
end;

Procedure TCustomChartLegend.CheckBoxChanged;
Begin
  Repaint;

  if Assigned(ParentChart) then
     TTeePanelAccess(ParentChart).BroadcastTeeEventClass(TTeeLegendEvent);
end;

Procedure TCustomChartLegend.SetCheckBoxes(Const Value:Boolean);
Begin
  if FCheckBoxes<>Value then
  begin
    FCheckBoxes:=Value;
    CheckBoxChanged;
  end;
end;

Procedure TCustomChartLegend.SetCheckBoxesStyle(Const Value:TCheckBoxesStyle);
var t : Integer;
    tmpCount : Integer;
Begin
  if FCheckBoxesStyle<>Value then
  begin
    FCheckBoxesStyle:=Value;

    if FCheckBoxesStyle=cbsRadio then
    begin
      tmpCount:=0;

      // Ensure only one series or series group is active (visible)
      if InternalLegendStyle=lsAuto then
         CalcLegendStyle;

      if InternalLegendStyle=lsSeriesGroups then
      begin
        with TCustomAxisPanel(ParentChart) do
        for t:=0 to SeriesGroups.Count-1 do
            if SeriesGroups[t].Active<>gaNo then
            begin
              Inc(tmpCount);

              if tmpCount>1 then
                 SeriesGroups[t].Hide;
            end;
      end
      else
      begin
        with TCustomAxisPanel(ParentChart) do
        for t:=0 to SeriesCount-1 do
        if Series[t].Active and Series[t].ShowInLegend then
        begin
          Inc(tmpCount);

          if tmpCount>1 then
             Series[t].Active:=False;
        end;
      end;
    end;

    CheckBoxChanged;
  end;
end;

Procedure TCustomChartLegend.SetTextStyle(Const Value: TLegendTextStyle);
Begin
  if FTextStyle<>Value then
  begin
    FTextStyle:=Value;
    Repaint;
  end;
end;

Procedure TCustomChartLegend.SetTopLeftPos(Const Value:Integer);
Begin
  TTeePanelAccess(ParentChart).SetIntegerProperty(FTopLeftPos,Value);
end;

Procedure TCustomChartLegend.SetTopPercent(Const Value:Integer);
begin
  TTeePanelAccess(ParentChart).SetIntegerProperty(FTopPercent,Value);
end;

Procedure TCustomChartLegend.SetVertMargin(Const Value:Integer);
Begin
  TTeePanelAccess(ParentChart).SetIntegerProperty(FVertMargin,Value);
end;

Procedure TCustomChartLegend.SetVertSpacing(Const Value:Integer);
Begin
  TTeePanelAccess(ParentChart).SetIntegerProperty(FVertSpacing,Value);
end;

Procedure TCustomChartLegend.SetSymbolWidth(Const Value:Integer);
Begin
  FSymbol.Width:=Value;
end;

Procedure TCustomChartLegend.SetTextSymbolGap(Const Value:Integer);
begin
  TTeePanelAccess(ParentChart).SetIntegerProperty(FTextSymbolGap,Value);
end;

Procedure TCustomChartLegend.CalcLegendStyle;

  function ActiveSeriesInLegend:Integer;
  var t : Integer;
      tmp : TChartSeries;
  begin
    result:=0;

    for t:=0 to TCustomChart(ParentChart).SeriesCount-1 do
    begin
      tmp:=TCustomChart(ParentChart).SeriesList[t];

      if tmp.Active and tmp.Legend.Visible then
         Inc(result);
    end;
  end;

begin
  if FLegendStyle=lsAuto then
  begin
    if CheckBoxes or (ActiveSeriesInLegend>1) then
       InternalLegendStyle:=lsSeries
    else
       InternalLegendStyle:=lsValues;
  end
  else
    InternalLegendStyle:=FLegendStyle;
end;

Function TCustomChartLegend.FormattedLegend(AIndex:Integer):String;
var tmpSeries : TChartSeries;
Begin
  With TCustomChart(ParentChart) do
  Case InternalLegendStyle of
    lsSeries: result:=SeriesTitleLegend(AIndex, not CheckBoxes);
    lsValues: result:=FormattedValueLegend(GetLegendSeries, AIndex);
lsLastValues: begin
                tmpSeries:=SeriesLegend(AIndex, not CheckBoxes);
                result:=FormattedValueLegend(tmpSeries, tmpSeries.CountLegendItems-1);
              end;
lsSeriesGroups: result:=SeriesList.Groups[AIndex].Name;  // Active groups only?
  end;
end;

Procedure TCustomChartLegend.SetDividingLines(Const Value:TChartHiddenPen);
begin
  FDividingLines.Assign(Value);
end;

procedure TCustomChartLegend.SetDrawBehind(Const Value: Boolean);
begin
  TTeePanelAccess(ParentChart).SetBooleanProperty(FDrawBehind,Value);
end;

Procedure TCustomChartLegend.SetSymbol(Const Value:TLegendSymbol);
begin
  FSymbol.Assign(Value);
end;

Function TCustomChartLegend.GetVertical:Boolean;
begin
  result:=(Alignment=laLeft) or (Alignment=laRight)
end;

function TCustomChartLegend.GetItem(Index:Integer):TLegendItem;
begin
  result:=Items[Index];
end;

Function TCustomChartLegend.GetRectLegend:TRect;
begin
  result:=ShapeBounds;
end;

Function TCustomChartLegend.GetSymbolWidth:Integer;
begin
  result:=FSymbol.Width
end;

Function TCustomChartLegend.GetLegendSeries:TChartSeries;
var t   : Integer;
    tmp : TChartSeries;
begin
  result:=FSeries;

  if not Assigned(result) then
  begin
    result:=TCustomChart(ParentChart).GetASeries;

    // If first series has ShowInLegend=False, try to find another one
    if (not Assigned(result)) or (not result.ShowInLegend) then
    for t:=0 to TCustomChart(ParentChart).SeriesCount-1 do
    begin
      tmp:=TCustomChart(ParentChart)[t];

      if tmp.Active and tmp.ShowInLegend then
      begin
        result:=tmp;
        break;
      end;
    end;
  end;
end;

Procedure TCustomChartLegend.PrepareSymbolPen;
begin
  if Symbol.DefaultPen then TCustomChart(ParentChart).LegendPen:=nil
                       else TCustomChart(ParentChart).LegendPen:=Symbol.Pen;
end;

Function TCustomChartLegend.CalcItemHeight:Integer;
begin
  result:=ParentChart.Canvas.FontHeight;

  if Symbol.Visible then
     if Symbol.HeightUnits=lcsPercent then
     begin
       if Symbol.Height>100 then
          result:=Round(result*Symbol.Height*0.01);
     end
     else
       result:=Max(result,Symbol.Height);

  if HasCheckBoxes then
     result:=Math.Max(6+TeeCheckBoxSize,result);

  Inc(result,FVertSpacing);

  if Vertical and DividingLines.Visible then
     result:=result+{$IFDEF FMX}Round{$ENDIF}(DividingLines.Width);
end;

Function TCustomChartLegend.FirstItemTop:Integer;
begin
  result:={$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Top);

  if Frame.Visible then
     Inc(result,({$IFDEF FMX}Round{$ENDIF}(Frame.Width) div 2));

  if IDrawTitle then
     Inc(result,{$IFDEF FMX}Round{$ENDIF}(Title.Height)+2);
end;

// Move Legend bounds using Parent Left/Top offset
procedure TCustomChartLegend.OffsetParent(const Positive:Integer);
begin
  {$IFDEF FMX}
  ShapeBounds.Offset(Positive*ParentChart.ChartBounds.Left,Positive*ParentChart.ChartBounds.Top);
  {$ELSE}
  {$IFDEF D16}System.Types.{$ENDIF}OffsetRect(ShapeBounds,Positive*ParentChart.ChartBounds.Left,
                        Positive*ParentChart.ChartBounds.Top);
  {$ENDIF}
end;

// Returns the Width of AColumn, as the maximum width of all Rows
function TCustomChartLegend.ColumnWidth(const AColumn:Integer):Integer;
var Col,
    tmpIndex,
    t : Integer;
    tmp : Integer;
begin
  result:=0;

  if ColumnWidthAuto then
    for Col:=0 to TeeMaxLegendColumns-1 do
    begin
      tmp:=0;

      // tmp:=ColumnWidths[Col]; <-- Justify ??

      for t:=0 to NumRows-1 do
      begin
        tmpIndex:=AColumn+t*NumCols;

        if ITotalItems>tmpIndex then
           tmp:=Max(tmp,IItemsWidth[tmpIndex,Col])
        else
           break;
      end;

      Inc(result,tmp);
    end
  else
    result:=ColumnWidths[AColumn*TeeMaxLegendColumns]+ColumnWidths[AColumn*TeeMaxLegendColumns+1]+ColumnWidths[AColumn*TeeMaxLegendColumns+2];

  result:=CalcItemWidthColor(result);

  if AColumn<NumCols-1 then
     Inc(result,DividingWidth+TeeLegendOff2);
end;

// Prepare and calculate Legend shape dimensions
Procedure TCustomChartLegend.CalcRect;

  procedure TryResizeChartRect;

    Function CalcMargin(AMargin,ADefault,ASize:Integer):Integer;
    begin
      if AMargin=0 then result:=ADefault*ASize div 100
                   else result:=AMargin;
    end;

  var tmp : Integer;
  begin
    if ResizeChart and (not CustomPosition) then
    with ParentChart do
    begin
      Case {$IFDEF LCL}Self.{$ENDIF}Alignment of
        laLeft   : ChartRect.Left  :=ShapeBounds.Right;
        laRight  : ChartRect.Right :=ShapeBounds.Left;

        laTop    : begin
                     ChartRect.Top:=ShapeBounds.Bottom;
                     if Shadow.Visible then
                        ChartRect.Top:=ChartRect.Top+Shadow.VertSize;

                     if Emboss.Visible then
                        ChartRect.Top:=ChartRect.Top+Emboss.VertSize;
                   end;

        laBottom : ChartRect.Bottom:=ShapeBounds.Top;
      end;

      if Vertical then
      begin
        tmp:=CalcMargin(HorizMargin,TeeDefHorizMargin,ChartWidth);

        if {$IFDEF LCL}Self.{$ENDIF}Alignment=laLeft then
           ChartRect.Left:=ChartRect.Left+tmp
        else
           ChartRect.Right:=ChartRect.Right-tmp;
      end
      else
      begin
        tmp:=CalcMargin(VertMargin,TeeDefVerticalMargin,ChartHeight);

        if {$IFDEF LCL}Self.{$ENDIF}Alignment=laTop then
           ChartRect.Top:=ChartRect.Top+tmp
        else
           ChartRect.Bottom:=ChartRect.Bottom-tmp;
      end;

      ReCalcWidthHeight;
    end;
  end;

  var tmpInverted : Boolean;

  // Returns the maximum number of Legend items available to display
  Function CalcTotalItems:Integer;
  var tmpSeries : TChartSeries;
      t         : Integer;
  begin
    if FItems.FCustom then
       result:=FItems.Count
    else
    begin
      result:=0;

      if InternalLegendStyle=lsSeriesGroups then
         result:=TCustomChart(ParentChart).SeriesList.Groups.Count
      else
      if (InternalLegendStyle=lsSeries) or (InternalLegendStyle=lsLastValues) then
      begin
        for t:=0 to TCustomChart(ParentChart).SeriesCount-1 do
        begin
          With TCustomChart(ParentChart).Series[t] do
               if (CheckBoxes or Active) and ShowInLegend then
                  Inc(result);
        end;

        Dec(result,FirstValue);
      end
      else
      begin
        tmpSeries:=GetLegendSeries;

        if Assigned(tmpSeries) and tmpSeries.ShowInLegend then
        begin
          if TSeriesAccess(tmpSeries).InvertedLegend then
             tmpInverted:=not tmpInverted;

          result:=tmpSeries.CountLegendItems-FirstValue;
        end;
      end;
    end;

    result:=Math.Max(0,result);
  end;

  var FrameWidth : Integer;
      tmp      : TCoordinate;

  Function MaxLegendRows(const YLegend:Integer):Integer;
  begin
    if (YLegend<ParentChart.ChartRect.Bottom) and (ItemHeight>0) then
    Begin
      result:={$IFDEF FMX}Round{$ENDIF}(ParentChart.ChartHeight-FrameWidth-YLegend+ParentChart.ChartRect.Top) div ItemHeight;
      result:=Math.Min(result,TotalLegendItems);
    end
    else
      result:=0;
  end;

  // Prepares and stores all legend items to display
  Procedure GetItems;

    Procedure SetItem(AIndex,APos:Integer);
    var tmpSt    : String;
        i        : Integer;
        tmpLines : Integer;
        tmpC     : TCustomChart;
    begin
      tmpC:=ParentChart as TCustomChart;

      if FItems.Custom and (FItems.Count>AIndex-FirstValue) then
      begin
        IItems[AIndex,0]:=FItems[AIndex-FirstValue].FText;
        IItems[AIndex,1]:=FItems[AIndex-FirstValue].FText2;
        IItems[AIndex,2]:=FItems[AIndex-FirstValue].FText3;
      end
      else
      begin
        tmpSt:=tmpC.InternalFormattedLegend(Self,APos);

        if tmpSt='' then
        begin
          IItems[AIndex,0]:='';
          IItems[AIndex,1]:='';
          IItems[AIndex,2]:='';
        end
        else
        begin
          i:=StringContains(TeeColumnSeparator,tmpSt);

          if i=0 then
          begin
            IItems[AIndex,0]:=tmpSt;
            IItems[AIndex,1]:='';
            IItems[AIndex,2]:='';
          end
          else
          begin
            IItems[AIndex,0]:=Copy(tmpSt,1,i-1);

            tmpSt:=Copy(tmpSt,i+1,Length(tmpSt)-i);
            i:=StringContains(TeeColumnSeparator,tmpSt);

            if i=0 then
            begin
              IItems[AIndex,1]:=tmpSt;
              IItems[AIndex,2]:='';
            end
            else
            begin
              IItems[AIndex,1]:=Copy(tmpSt,1,i-1);
              IItems[AIndex,2]:=Copy(tmpSt,i+1,Length(tmpSt)-i);
            end;
          end;
        end;
      end;

      IColorWidths[AIndex]:=0;

      for i:=0 to TeeMaxLegendColumns-1 do
        if IItems[AIndex,i] = '' then
           IItemsWidth[AIndex,i]:=0
        else
        begin
           IItemsWidth[AIndex,i]:=ParentChart.MultiLineTextWidth(IItems[AIndex,i],tmpLines,TextFormat)+FTextSymbolGap;
           Inc(IColorWidths[AIndex],ISpaceWidth+IItemsWidth[AIndex, i]-1);
        end;

      IColorWidths[AIndex]:=Symbol.CalcWidth(IColorWidths[AIndex]);
    end;

  var t : Integer;
  begin
    SetLength(IItems,ILastValue-FirstValue+1);
    SetLength(IItemsWidth,Length(IItems));
    SetLength(IColorWidths,Length(IItems));

    if tmpInverted then
       for t:=ILastValue downto FirstValue do
           SetItem(ILastValue-t,t)
    else
       for t:=FirstValue to ILastValue do
           SetItem(t-FirstValue,t);

    IColorWidth:=0;
    for t:=FirstValue to ILastValue do
      IColorWidth:=Max(IColorWidth, IColorWidths[t-FirstValue]);
  end;

  procedure CalcColumnsWidth;
  var t  : Integer;
      tt : Integer;
      i  : Integer;
  Begin
    TTeePanelAccess(ParentChart).TryAssignFont(Font);

    if ColumnWidthAuto then
    begin
      SetLength(ColumnWidths,NumCols*TeeMaxLegendColumns);
      for i:=0 to NumCols-1 do
        for t:=0 to TeeMaxLegendColumns-1 do
        begin
          ColumnWidths[i*TeeMaxLegendColumns+t]:=0;

          tt:=FirstValue+i;
          while tt<=ILastValue do
          begin
            ColumnWidths[i*TeeMaxLegendColumns+t]:=Math.Max(ColumnWidths[i*TeeMaxLegendColumns+t],IItemsWidth[tt-FirstValue,t]);
            Inc(tt, Max(1, NumCols));
          end;
        end;
    end;
  end;

  // Calculates maximum legend item width
  Function MaxColumnsWidth:Integer;
  var i      : Integer;
      t      : Integer;
      tmpMax : Integer;
  begin
    result:=0;

    for i:=0 to NumCols-1 do
    begin
      tmpMax:=0;
      for t:=0 to TeeMaxLegendColumns-1 do
        if ColumnWidths[i*TeeMaxLegendColumns+t]>0 then
           Inc(tmpMax,ISpaceWidth+ColumnWidths[i*TeeMaxLegendColumns+t]-1);

      result:=Max(result,tmpMax);
    end;
  end;

  function HorizTopLeftPos:TCoordinate;
  begin
    result:={$IFNDEF FMX}Round{$ENDIF}(1.0*FTopLeftPos*
            (ParentChart.ChartBounds.Right-ParentChart.ChartBounds.Left)*0.01);
  end;

var
  IHorizTotalWidth : Integer;

  // Calculates the total necessary legend width to fit all columns
  procedure RecalcTotalWidth;
  var t : Integer;
  begin
    IHorizTotalWidth:=TeeLegendOff2;

    for t:=0 to NumCols-1 do
        Inc(IHorizTotalWidth, ColumnWidth(t));
  end;

  // Returns the max number of legend items that can be displayed in the
  // horizontal legend, using AWidth as max pixel space.
  function CalcFittingItems(AWidth:Integer; AllRows:Boolean=False):Integer;
  var tmp,
      tmpFirst,
      tmpWidth : Integer;
  begin
    result:=0;
    IHorizTotalWidth:=0;
    tmpWidth:=AWidth;

    tmpFirst:=FirstValue;
    NumCols:=ITotalItems;
    NumRows:=1;

    repeat
      CalcColumnsWidth;
      tmp:=ColumnWidth(tmpFirst);

      if tmp<=tmpWidth then
      begin
        Inc(result);
        Inc(tmpFirst);

        Dec(tmpWidth,tmp);

        Inc(IHorizTotalWidth,tmp);
      end
      else
        break;

    until (tmpWidth<=0) or (tmpFirst>ILastValue);

    if AllRows then
    begin
      repeat
        NumCols:=Max(result,1);

        NumRows:=ITotalItems div NumCols;
        if (ITotalItems mod NumCols)>0 then Inc(NumRows);

        CalcColumnsWidth;
        RecalcTotalWidth;

        if (IHorizTotalWidth>AWidth) and (result>0) then
          Dec(result);

      until (IHorizTotalWidth<=AWidth) or (result<1);
    end;
  end;

  Function MaxLegendColumns(const AJustify:TLegendJustify):Integer;
  var tmp : TCoordinate;
      tmpWidth : TCoordinate;
      Old : Integer;
  begin
    if Length(IItemsWidth)>0 then
    with TTeePanelAccess(ParentChart) do
    Begin
      tmpWidth:=ChartBounds.Right-ChartBounds.Left;

      tmp:=BorderSize+(HorizMargin*tmpWidth {$IFDEF FMX}*0.01{$ELSE}div 100{$ENDIF});

      if not CustomPosition then
         tmp:=tmp+HorizTopLeftPos;

      result:={$IFDEF FMX}Round{$ENDIF}(tmpWidth-2*FrameWidth-tmp);

      if AJustify=ljYes then
         IJustify:=True
      else
      begin
        Old:=result;

        result:=CalcFittingItems(result);

        IJustify:=False;

        if (result=0) and (FHorizJustify=ljAutomatic) then
        begin
          IJustify:=True;
          result:=Old;
        end;
      end;

      if IJustify then
        result:=CalcFittingItems(result, true)
      else
        result:=Math.Min(result,TotalLegendItems);

    end
    else result:=0;
  end;

  Procedure ResizeVertical;
  var tmp  : TCoordinate;
      tmpH : Integer;
  begin
    ItemsTopMargin:=Round(ItemHeight*0.1);
    tmp:=TeeLegendOff2+ItemHeight*NumRows+2*ItemsTopMargin+FrameWidth;

    if IDrawTitle then
       tmp:=tmp+Title.Height+2+2;

    tmpH:=ItemHeight div 6;

    if Symbol.Visible and Symbol.Shadow.Visible then
        if Symbol.Shadow.VertSize>tmpH then
           tmp:=tmp+Symbol.Shadow.VertSize-tmpH;

    if Symbol.Visible and Symbol.Emboss.Visible then
        if Symbol.Emboss.VertSize>tmpH then
           tmp:=tmp+Symbol.Emboss.VertSize-tmpH;

    With ShapeBounds do
    if (Alignment=laBottom) and (not CustomPosition) then
       Top:=Bottom-tmp
    else
       Bottom:=Top+tmp;
  end;

  function TotalColumnWidths(ColRow: Integer):Integer;
  var t : Integer;
  begin
    result:=0;

    for t:=0 to TeeMaxLegendColumns-1 do
        Inc(result,ColumnWidths[ColRow*TeeMaxLegendColumns+t]);
  end;

  // Calculates the horizontal position of legend symbols
  Procedure CalcSymbolTextPos(ALeftPos:Integer);
  var tmp : Integer;
      tmpShadow : Integer;
  begin
    tmp:=ALeftPos+TeeLegendOff2+TeeLegendOff4+(FrameWidth div 2);

    if Symbol.Visible and (Symbol.Position=spLeft) and (InternalLegendStyle<>lsSeriesGroups) then
    begin
      XLegendColor:=tmp;
      XLegendText:=XLegendColor+IColorWidth+TeeLegendOff4+FTextSymbolGap;

      tmpShadow:=Abs(Symbol.Shadow.HorizSize);

      if Symbol.Shadow.Visible and (tmpShadow<>0) then
      begin
        Inc(XLegendText,tmpShadow);

        if Symbol.Shadow.HorizSize<0 then
           if Symbol.Position=spLeft then
              Inc(XLegendColor,tmpShadow);
      end;
    end
    else
    begin
      if not Symbol.Visible then
         Inc(tmp,TeeLegendOff2);  // Margin

      XLegendText:=tmp;

      if Symbol.Visible then
         if Symbol.Position=spRight then
            XLegendColor:=XLegendText+TotalColumnWidths(0)+FTextSymbolGap
         else
            XLegendColor:=XLegendText-FTextSymbolGap;
    end;
  end;

  // Prepares coordinates for vertical legend alignment
  Procedure CalcVerticalPositions;
  var tmpTotalWidth : Integer;

    Procedure CalcWidths;
    begin
      CalcColumnsWidth;
      IMaxWidth:=MaxColumnsWidth;

      tmpTotalWidth:=TeeLegendOff2+IMaxWidth+FTextSymbolGap{$IFDEF FMX}+TeeLegendOff4{$ENDIF};

      if InternalLegendStyle=lsSeriesGroups then
         IColorWidth:=0  // Series groups have no symbol (yet!)
      else
      begin
        IColorWidth:=Symbol.CalcWidth(tmpTotalWidth);

        if Symbol.Visible then
        begin
          Inc(tmpTotalWidth,IColorWidth);

          Inc(tmpTotalWidth,TeeLegendOff4);

          if Symbol.Shadow.Visible then
             Inc(tmpTotalWidth,Abs(Symbol.Shadow.HorizSize));

          if Symbol.Emboss.Visible then
             Inc(tmpTotalWidth,Abs(Symbol.Emboss.HorizSize));
        end;
      end;

      Inc(tmpTotalWidth,FrameWidth);

      if Assigned(FOnCalcSize) then
         FOnCalcSize(Self,tmpTotalWidth);

      if IDrawTitle then
         tmpTotalWidth:=Max(tmpTotalWidth,TitleWidth);
    end;

  var tmpW : TCoordinate;
  begin
    With ShapeBounds do
    begin
      if CustomPosition or (Alignment=laLeft) then
      begin
        if not CustomPosition then
           Left:=ParentChart.ChartRect.Left+FrameWidth+1;

        CalcWidths;
      end
      else
      begin
        Right:=ParentChart.ChartRect.Right;

        if Shadow.Visible then
           Right:=Right-Max(0,Shadow.HorizSize);

        if Assigned(FEmboss) and FEmboss.Visible then
           Left:=Left-Emboss.HorizSize;

        Right:=Right-((FrameWidth div 2)+1);

        CalcWidths;

        Left:=Right-tmpTotalWidth;

        if HasCheckBoxes then
           Left:=Left-(TeeCheckBoxSize+TeeLegendOff4);
      end;

      tmpW:=Left;

      if HasCheckBoxes then
      begin
        XLegendBox:={$IFDEF FMX}System.Round{$ENDIF}(tmpW)+TeeLegendOff4;
        tmpW:=XLegendBox+TeeCheckBoxSize;
      end;

      Right:=tmpW+tmpTotalWidth;
      CalcSymbolTextPos({$IFDEF FMX}System.Round{$ENDIF}(tmpW));
    end;
  end;

  // Prepares coordinates for horizontal legend alignment
  Procedure CalcHorizontalPositions;
  var HalfMaxWidth : Integer;
      tmpW         : TCoordinate;
  begin
    HalfMaxWidth:=IHorizTotalWidth;

    if not Vertical then
       if IJustify then
          Inc(HalfMaxWidth,NumCols*TeeLegendOff2)
       else
          Inc(HalfMaxWidth,TeeLegendOff4);

    if IDrawTitle then // TV52010115
       HalfMaxWidth:=Max(HalfMaxWidth,TitleWidth);

    if Vertical then
       tmpW:=ParentChart.ChartWidth
    else
       tmpW:=ParentChart.ChartBounds.Right-ParentChart.ChartBounds.Left;

    HalfMaxWidth:=Math.Min({$IFDEF FMX}Round{$ENDIF}(tmpW-HorizTopLeftPos),HalfMaxWidth);

    HalfMaxWidth:=HalfMaxWidth div 2;

    With ShapeBounds do
    begin
      if not CustomPosition then
         Left:=ParentChart.ChartXCenter-HalfMaxWidth;

      Right:=Left+(2*HalfMaxWidth);
      tmpW:=Left;
    end;

    if HasCheckBoxes then
    begin
      XLegendBox:={$IFDEF FMX}Round{$ENDIF}(tmpW)+TeeLegendOff4;
      tmpW:=tmpW+TeeCheckBoxSize+TeeLegendOff4;
    end;

    CalcSymbolTextPos({$IFDEF FMX}Round{$ENDIF}(tmpW));
  end;

  // Calls OnGetLegendRect event
  Procedure GetLegendRectangle;
  var tmpRect : TRect;
  begin
    tmpRect:=ShapeBounds;
    TCustomChart(ParentChart).FOnGetLegendRect(TCustomChart(ParentChart),tmpRect);
    ShapeBounds:=tmpRect;
  end;

  procedure CalcHorizNumRows;
  begin
    if NumCols>0 then
    begin
      NumRows:=ITotalItems div NumCols;
      if (ITotalItems mod NumCols)>0 then Inc(NumRows);

      if MaxNumRows <> 0 then
         NumRows:=Math.Min(NumRows,MaxNumRows)
      else
         NumRows:=Math.Min(NumRows,10);
    end
    else
      NumRows:=0;
  end;

  procedure CalculatePercentSize;
  begin
    tmp:=ShapeBounds.Right-ShapeBounds.Left;
    ShapeBounds.Left:={$IFNDEF FMX}Round{$ENDIF}(LeftPercent*ParentChart.Width*0.01);
    ShapeBounds.Right:=ShapeBounds.Left+tmp;

    tmp:=ShapeBounds.Bottom-ShapeBounds.Top;
    ShapeBounds.Top:={$IFNDEF FMX}Round{$ENDIF}(TopPercent*ParentChart.Height*0.01);
    ShapeBounds.Bottom:=ShapeBounds.Top+tmp;
  end;

var tmpChart : TCustomChart;
    tmpPage  : TChartPage;
    IsPage   : Boolean;
    tmpRect  : TRect;
    tmpSize  : Integer;
begin
  tmpInverted:=Inverted;

  CalcLegendStyle;

  { calculate Frame Width offset }
  if Frame.Visible then FrameWidth:={$IFDEF FMX}Round{$ENDIF}(Frame.Width)
                   else FrameWidth:=0;

  if Bevel<>bvNone then
     Inc(FrameWidth,BevelWidth);

  if CustomPosition then
  begin
    if PositionUnits=muPercent then
       CalculatePercentSize;

    OffsetParent(1);
  end;

  tmpChart:=TCustomChart(ParentChart);
  tmpPage:=tmpChart.Pages;

  IsPage:=(InternalLegendStyle=lsValues) and FCurrentPage and (tmpPage.MaxPointsPerPage>0);

  if IsPage then
     FirstValue:=tmpPage.FirstValueIndex;

  if (tmpPage.MaxPointsPerPage>0) and (InternalLegendStyle<>lsValues) then
     FirstValue:=0;

  ITotalItems:=CalcTotalItems;

  if IsPage then
     ITotalItems:=Math.Min(ITotalItems,tmpPage.MaxPointsPerPage);

  tmpChart.LegendColor:=Color;

  TTeePanelAccess(tmpChart).TryAssignFont(Font);
  ISpaceWidth:=tmpChart.Canvas.TextWidth(' '); // Do not localize

  // Calculate default Height for each Legend Item
  ItemHeight:=CalcItemHeight;

  IDrawTitle:=Assigned(FTitle) and FTitle.Visible and (FTitle.Text.Text<>'');

  if IDrawTitle then
     FTitle.CalcBounds(tmpChart);

  if Vertical then
  begin
    if not FCustomPosition then
    begin
      tmpRect:=tmpChart.ChartRect;

      with tmpRect do
        ShapeBounds.Top:=Top+{$IFNDEF FMX}Round{$ENDIF}(1.0*FTopLeftPos*(Bottom-Top)*0.01)+(FrameWidth div 2);
    end;

    NumCols:=1;

    NumRows:=MaxLegendRows(FirstItemTop);

    if MaxNumRows <> 0 then
       NumRows:=Math.Min(NumRows,MaxNumRows);

    ILastValue:=FirstValue+Math.Min(ITotalItems,NumRows)-1;
    GetItems;  // Only Visible !!
  end
  else
  begin
    ILastValue:=FirstValue+ITotalItems-1;
    GetItems;  // All !!

    if not FCustomPosition then
       if Alignment<>laBottom then
          ShapeBounds.Top:=tmpChart.ChartRect.Top+(FrameWidth div 2)+1;

    NumCols:=MaxLegendColumns(FHorizJustify);
    CalcHorizNumRows;

    if NumRows>1 then
    begin
      // Force justify, because one single row cannot fit all items
      IJustify:=True;

      NumCols:=MaxLegendColumns(ljYes);
      CalcHorizNumRows;

      RecalcTotalWidth;
    end;

    { adjust the last index, now we know the max number of rows... }
    if IJustify then
       ILastValue:=FirstValue+Math.Min(ITotalItems,NumCols*NumRows)-1
    else
       ILastValue:=FirstValue+ITotalItems-1;

    if (not FCustomPosition) and (Alignment=laBottom) then
    begin
      tmpSize:=(FrameWidth div 2)+1;

      if Shadow.Visible then
         Inc(tmpSize,Shadow.VertSize);

      if Emboss.Visible then
         Inc(tmpSize,Emboss.VertSize);

      if Assigned(FOnCalcSize) then
         FOnCalcSize(Self,tmpSize);

      ShapeBounds.Bottom:=tmpChart.ChartRect.Bottom-tmpSize;
    end;
  end;

  if ILastValue>=FirstValue then  // Should draw legend ?
  begin
    ResizeVertical;

    if Vertical then CalcVerticalPositions
                else CalcHorizontalPositions;

    if Assigned(TCustomChart(ParentChart).FOnGetLegendRect) then
       GetLegendRectangle;

    TryResizeChartRect;
  end;
end;

// Calculates one item width (using text AWidth param and optional item color, etc)
function TCustomChartLegend.CalcItemWidthColor(const AWidth:Integer):Integer;
begin
  result:=AWidth+IColorWidth+FTextSymbolGap+TeeLegendOff4+2*TeeLegendOff2;

  if HasCheckBoxes then
     Inc(result,TeeCheckBoxSize+2*TeeLegendOff2);
end;

function TCustomChartLegend.TitleWidth:Integer;
begin
  result:={$IFDEF FMX}Round{$ENDIF}(Title.Width){$IFDEF FMX}+2*TeeLegendOff4{$ENDIF};

  TTeePanelAccess(ParentChart).TryAssignFont(Title.Font);
  Inc(result,ParentChart.Canvas.TextWidth(' '));
end;

type
  TCustomChartAccess=class(TCustomChart);

Procedure TCustomChartLegend.DrawLegend;

  Procedure DrawSymbol(const ASeries:TChartSeries; AIndex,AItemIndex:Integer; Const R:TRect);
  begin
    {$IFNDEF TEEOCX} //AX event always assigned
    if not Assigned(Symbol.FOnDraw) then
    {$ENDIF}
    begin
      if IColorWidth>0 then
      begin
        if Assigned(ASeries) then
           ASeries.DrawLegend(AIndex,R)
        else
           FSymbol.Draw({$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF},R);

        if Assigned(FSelected) and (FSelected.ItemIndex=AItemIndex) and FSelected.Visible then
        begin
          ParentChart.Canvas.AssignVisiblePen(FSelected.Pen);
          ParentChart.Canvas.Brush.Style:=bsClear;
          ParentChart.Canvas.Rectangle(R);
        end;
      end;
    end
    {$IFNDEF TEEOCX}
    else
    {$ELSE}
    ;
    if Assigned(Symbol.FOnDraw) Then
    {$ENDIF}
      Symbol.FOnDraw(Symbol,ASeries,AItemIndex,R)
  end;

var tmpSeries   : TChartSeries;
    tmpInverted : Boolean;

  Procedure DrawLegendItem(ItemIndex,ItemOrder:Integer);
  type
    TSetLegendTextStyle=Set of TLegendTextStyle;

  var
    PosXLegend : Integer;
    IncPos     : Boolean;

    procedure DrawBox(Checked:Boolean; OffsetX,PosYLegend:Integer);
    var tmpY,
        tmpBox : Integer;
    begin
      tmpBox:=XLegendBox;

      if not Vertical then
         Inc(tmpBox,OffsetX);

      tmpY:=PosYLegend+((ItemHeight-FVertSpacing-TeeCheckBoxSize) div 2)-1;

      ParentChart.Canvas.DrawCheckBox(tmpBox,tmpY,Checked,Color,CheckBoxesStyle=cbsCheck);
    end;

    function GetItemText(ItemOrder,Column:Integer):String;
    begin
      if (not Vertical) and tmpInverted then
         result:=IItems[ItemOrder+(ITotalItems-ILastValue-1),Column]
      else
         result:=IItems[ItemOrder,Column];
    end;

    procedure PrepareFontSeriesColor;
    var tmpColor : TColor;
    begin
      if (InternalLegendStyle=lsSeries) or
         (InternalLegendStyle=lsLastValues) then
          tmpColor:=TCustomChart(ParentChart).SeriesLegend(ItemIndex,not CheckBoxes).SeriesColor
      else
          tmpColor:=tmpSeries.LegendItemColor(ItemIndex);

      ParentChart.Canvas.Font.Color:=tmpColor;
    end;

    // Returns the X (left) position of AColumn
    function ColumnLeft(const AColumn:Integer):Integer;
    var t : Integer;
    begin
      result:=0;

      // Loop all previous columns
      for t:=0 to AColumn-1 do
          Inc(result,ColumnWidth(t));
    end;

    procedure PrepareTextAlign(const AText:String; const ACol:Integer);

      Procedure SetRightAlign(Const ASet:TSetLegendTextStyle);
      begin
        if FTextStyle in ASet then
        begin
          ParentChart.Canvas.TextAlign:=TA_RIGHT;

          if Vertical or IJustify then
             Inc(posXLegend,ColumnWidths[(ItemOrder mod NumCols)*TeeMaxLegendColumns+ACol] {$IFNDEF FMX}-TeeLegendOff4{$ENDIF})
          else
             Inc(posXLegend,ParentChart.Canvas.TextWidth(AText));

          IncPos:=False;
        end
        else
          ParentChart.Canvas.TextAlign:=TA_LEFT;
      end;

    begin
      if InternalLegendStyle=lsSeries then
         ParentChart.Canvas.TextAlign:=ta_Left
      else
         if ACol=0 then
            SetRightAlign([ ltsXValue,
                            ltsValue,
                            ltsPercent,
                            ltsXandValue,
                            ltsXandPercent,
                            ltsLeftPercent,
                            ltsLeftValue ])
         else
         if ACol=1 then
            SetRightAlign([ ltsRightValue,
                            ltsXandValue,
                            ltsXandPercent,
                            ltsRightPercent ]);
    end;

    function CalculateItemBounds(const PosXColor,PosYLegend:Integer):TRect;
    begin
      result.Left:=PosXColor;
      result.Right:=PosXColor+IColorWidths[ItemIndex-FirstValue];

      result.Top:=PosYLegend;

      if FSymbol.HeightUnits=lcsPercent then
      begin
        result.Bottom:=PosYLegend+ItemHeight+1;

        if (not FSymbol.Continuous) or (ItemOrder=0) then
           result.Top:=result.Top+2;

        if (not FSymbol.Continuous) or (ItemOrder=(ILastValue-FirstValue)) then
           result.Bottom:=result.Bottom-(1+2+FVertSpacing);

        if FSymbol.Height<=100 then
           result.Bottom:=result.Top+Round(FSymbol.Height*(result.Bottom-result.Top)*0.01);
      end
      else
        result.Bottom:=result.Top+FSymbol.Height;
    end;

    procedure DrawItemSymbol(const R:TRect; const AX,AY:Integer);
    var tmpIndex : Integer;
    begin
      if InternalLegendStyle=lsSeriesGroups then
      begin
        if CheckBoxes then
           DrawBox(TCustomChart(ParentChart).SeriesList.Groups[ItemIndex].Active<>gaNo,AX,AY);

         // DrawSymbol(nil,-1,R)  // Groups have no symbol ! (yet)
      end
      else
      if (InternalLegendStyle=lsSeries) or (InternalLegendStyle=lsLastValues) then
      begin
        if CheckBoxes then
        begin
          tmpSeries:=TCustomChart(ParentChart).SeriesLegend(ItemIndex,False);
          DrawBox(tmpSeries.Active,AX,AY);
          DrawSymbol(tmpSeries, TeeAllValues, ItemIndex, R);
        end
        else
          DrawSymbol(TCustomChart(ParentChart).ActiveSeriesLegend(ItemIndex), TeeAllValues, ItemIndex, R);
      end
      else
      begin
        if Assigned(tmpSeries) then
        begin
          tmpIndex:=TSeriesAccess(tmpSeries).CalcLegendIndex(ItemIndex);
          DrawSymbol(tmpSeries,tmpIndex,tmpIndex,R);
        end
        else
          DrawSymbol(nil,-1,ItemIndex,R);
      end;
    end;

    procedure DrawDividingLine(const ARow,AX,AY:Integer);
    var tmpBorder : Integer;
    begin
      // For horizontal legend position, draw lines only when drawing row zero:
      if Vertical or (ARow=0) then
      begin
        ParentChart.Canvas.AssignVisiblePen(FDividingLines);
        ParentChart.Canvas.BackMode:=cbmTransparent;

        if Pen.Visible then
           tmpBorder:={$IFDEF FMX}Round{$ENDIF}(1+Pen.Width) div 2
        else
           tmpBorder:=0;

        if Vertical then
           ParentChart.Canvas.DoHorizLine({$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Left+tmpBorder),
                       {$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Right-tmpBorder),
                       AY-(FVertSpacing div 2) )
        else
           ParentChart.Canvas.DoVertLine({$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Left+AX+1),
                      {$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Top+tmpBorder),
                      {$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Bottom-tmpBorder));
      end;
    end;

    procedure TrySetHoverFont;
    begin
      if TCustomAxisPanel(ParentChart).Hover.Visible and
         Assigned(FSelected) and
         FSelected.Hover.Visible then
           if ItemIndex=FSelected.HoverIndex then
              ParentChart.Canvas.Font.Color:=FSelected.Hover.Font.Color; // <-- Font Style, Size, etc ??
    end;

    function GetCustomText(const AIndex:Integer; const AItem:TLegendItem):String;
    begin
      case AIndex of
        0 : result:=AItem.FText;
        1 : result:=AItem.FText2;
      else
        result:=AItem.FText3;
      end;
    end;

    { calculate Frame Width offset }
    function FrameWidth:Integer;
    begin
      if Frame.Visible then result:={$IFDEF FMX}Round{$ENDIF}(Frame.Width)
                       else result:=0;

      if Bevel<>bvNone then
         Inc(result,BevelWidth);
    end;

    function TotalColumnsWidth:Integer;
    var t : Integer;
    begin
      result:=0;

      for t:=0 to TeeMaxLegendColumns-1 do
          Inc(result,ColumnWidths[(ItemOrder mod NumCols)*TeeMaxLegendColumns+t]);
    end;

    procedure MoveToNextColumnX;
    var tmpSize : Integer;
    begin
      tmpSize:=ColumnWidth(ItemOrder);

      Inc(XLegendText,tmpSize);
      Inc(XLegendColor,tmpSize);
      Inc(XLegendBox,tmpSize);
    end;

    function PrepareCanvasID:Boolean;
    begin
      result:=TCanvas3DAccess(ParentChart.Canvas).SupportsID;

      if result then
         TCanvas3DAccess(ParentChart.Canvas).BeginEntity(IntToStr(ItemOrder));
    end;

    procedure PrepareCustomItem(const AItem:TLegendItem; var posXLegend,tmpTop:Integer);
    begin
      if FItems.FCustomPosition then
      begin
        posXLegend:=AItem.Left;
        tmpTop:=AItem.Top;
        ParentChart.Canvas.TextAlign:=AItem.Align;
      end
      else
      begin
        AItem.FLeft:=posXLegend;
        AItem.FTop:=tmpTop;
        AItem.FAlign:=ParentChart.Canvas.TextAlign;
      end;

      if AItem.HasFont then
      begin
        ICustomFont:=True;
        TTeePanelAccess(ParentChart).TryAssignFont(AItem.Font);
      end
      else
      if ICustomFont then
      begin
        TTeePanelAccess(ParentChart).TryAssignFont(AItem.Font);

        if FontSeriesColor then
           PrepareFontSeriesColor;

        ICustomFont:=False;
      end;
    end;

  var PosYLegend : Integer;
      tmp        : Integer;
      R          : TRect;
      tmpSt      : String;
      tmpOrder   : Integer;
      PosXColor  : Integer;
      tmpX       : Integer;
      tmpTop     : Integer;
      tmpCanID   : Boolean;
      tmpBox     : Integer;
      t          : Integer;
      tmpXText   : Integer;
  begin
    if ItemOrder>=ITotalItems then exit;

    // Prepare canvas
    tmpCanID:=PrepareCanvasID;

    ParentChart.Canvas.Brush.Color:=Color;
    ParentChart.Canvas.Brush.Style:=bsClear;
    ParentChart.Canvas.BackMode:=cbmTransparent;

    PrepareSymbolPen;

    tmp:=XLegendText;

    posYLegend:=FirstItemTop+1+ItemsTopMargin;

    PosXColor:=Left+TeeLegendOff2+TeeLegendOff4+(FrameWidth div 2);

    if CheckBoxes then
       Inc(PosXColor,TeeCheckBoxSize+FTextSymbolGap);

    tmpBox:=0;

    if Vertical then
       tmpOrder:=ItemOrder
    else
    begin
      tmpOrder:=ItemOrder div NumCols;

      tmpX:=ColumnLeft(ItemOrder mod NumCols);

      if IJustify then
      begin
        Inc(tmp,tmpX);
        tmpBox:=tmpX;
      end;

      Inc(PosXColor,tmpX);
    end;

    // Move symbol to right
    if Symbol.Position=spRight then
       Inc(PosXColor,TotalColumnsWidth+FTextSymbolGap+TeeLegendOff2);

    // Calculate item Y
    Inc(posYLegend,tmpOrder*ItemHeight+(FVertSpacing div 2));

    // Try to call OnGetLegendPos
    if Assigned(TCustomChart(ParentChart).FOnGetLegendPos) then
       TCustomChart(ParentChart).FOnGetLegendPos(TCustomChart(ParentChart),ItemIndex,tmp,posYLegend,PosXColor);

    PosXLegend:=tmp-TeeLegendOff2;

    if FontSeriesColor then
       PrepareFontSeriesColor
    {$IFDEF FMX}
    else
       ParentChart.Canvas.Brush.Color:=Font.Color
    {$ENDIF}
    ;

    for t:=0 to TeeMaxLegendColumns-1 do
    begin
      if FItems.FCustom and (FItems.Count>ItemIndex) then
         tmpSt:=GetCustomText(t,Item[ItemIndex])
      else
         tmpSt:=GetItemText(ItemOrder,t);

      IncPos:=True;

      PrepareTextAlign(tmpSt,t);

      if tmpSt<>'' then
      begin
        if HasCheckBoxes then tmpTop:=PosYLegend+1
                         else tmpTop:=PosYLegend;

        if t=0 then
           if FItems.FCustom and (FItems.Count>ItemIndex) then
              PrepareCustomItem(Item[ItemIndex],posXLegend,tmpTop)
           else
           with Item[ItemIndex] do // Alias effect. Do not streamline.
           begin
             FLeft:=posXLegend;
             FTop:=tmpTop;
             FAlign:=ParentChart.Canvas.TextAlign;
             FText:=tmpSt;
             FText2:=GetItemText(ItemOrder,1);
             FText3:=GetItemText(ItemOrder,2);
           end;

        // Hover
        if not FontSeriesColor then
           TTeePanelAccess(ParentChart).TryAssignFont(Font);

        TrySetHoverFont;

        tmpXText:=PosXLegend;
        {$IFDEF FPCLINUX}
        case ParentChart.Canvas.TextAlign of
          TA_CENTER: tmpXText:=PosXLegend-ParentChart.Canvas.TextWidth(tmpSt) div 2;
          TA_RIGHT: tmpXText:=PosXLegend-ParentChart.Canvas.TextWidth(tmpSt);
        end;
        {$ENDIF}

        ParentChart.Canvas.TextOut(tmpXText,tmpTop,tmpSt,TextFormat=ttfHtml);  { <-- draw text }
      end;

      if IncPos then
         if Vertical then
            Inc(posXLegend,ColumnWidths[t])
         else
            Inc(posXLegend,ColumnWidths[(ItemOrder mod NumCols)*TeeMaxLegendColumns+t]);

      Inc(posXLegend,ISpaceWidth);
    end;

    if FItems.FCustom and FItems.FCustomPosition then
       R:=Item[ItemIndex].FSymbol
    else
    begin
      R:=CalculateItemBounds(PosXColor,PosYLegend);
      Item[ItemIndex].FSymbol:=R;
    end;

    DrawItemSymbol(R,tmpBox,PosYLegend);

    if (ItemOrder>0) and FDividingLines.Visible then
       DrawDividingLine(tmpOrder,ColumnLeft(ItemOrder),PosYLegend);

    if (not Vertical) and (not IJustify) then
       MoveToNextColumnX;

    if tmpCanID then
       TCanvas3DAccess(ParentChart.Canvas).EndEntity;
  end;

  // Draws all legend items
  Procedure DrawItems;
  var t : Integer;
  begin
    tmpSeries:=GetLegendSeries;

    ICustomFont:=False;

    if not FItems.Custom then
       FItems.ClearItems;

    if tmpInverted then
       for t:=ILastValue downto FirstValue do
           DrawLegendItem(t,(ILastValue-t))
    else
       for t:=FirstValue to ILastValue do
           DrawLegendItem(t,(t-FirstValue));
  end;

  procedure DrawLegendTitle;
  var tmpX : TCoordinate;
  begin
    case Title.Alignment of
      taLeftJustify: tmpX:=ShapeBounds.Left+TeeLegendOff2;
      taCenter: tmpX:=(ShapeBounds.Left+ShapeBounds.Right-TitleWidth) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};
    else
      tmpX:=ShapeBounds.Right-Title.Width-TeeLegendOff2;
    end;

    Title.MoveTo(tmpX,ShapeBounds.Top+TeeLegendOff2);
    Title.Draw;

    // Reset back Legend font
    TTeePanelAccess(ParentChart).TryAssignFont(Font);
  end;

var tmpBlend : TTeeBlend;
begin
  TCanvas3DAccess(ParentChart.Canvas).BeginEntity('Legend',nil,Transform);

  if ILastValue>=FirstValue then  // Should draw legend ?
  begin
    tmpInverted:=CalcInverted;

    if FGlobalTransp<>0 then
       tmpBlend:=ParentChart.Canvas.BeginBlending(ShapeBounds,FGlobalTransp)
    else
       tmpBlend:=nil;

    if ParentChart.Canvas.SupportsFullRotation then
       Draw(ShapeBounds,0,1)
    else
       Draw;

    if IDrawTitle then
       DrawLegendTitle;

    TCanvas3DAccess(ParentChart.Canvas).BeginEntity('Items');
    DrawItems;
    TCanvas3DAccess(ParentChart.Canvas).EndEntity;

    if FGlobalTransp<>0 then
       ParentChart.Canvas.EndBlending(tmpBlend);

    //ResizeChartRect;
  end;

  if CustomPosition then
     OffsetParent(-1);

  TCanvas3DAccess(ParentChart.Canvas).EndEntity;
end;

Function TCustomChartLegend.FormattedValue(const ASeries:TChartSeries; ValueIndex:Integer):String;
begin
  if ValueIndex<>TeeAllValues then
     // eliminate breaks in string...
     result:=ReplaceChar(ASeries.LegendString(ValueIndex,TextStyle),
                         TeeLineSeparator,' ')  // Do not localize
  else
     result:='';
end;

procedure TCustomChartLegend.SetSeries(const Value: TChartSeries);
begin
  FSeries:=Value;
  Repaint;
end;

Function TCustomChartLegend.Clicked(x,y:Integer):Integer;
begin
  result:=Clicked(TeePoint(x,y));
end;

function TCustomChartLegend.CalcCursor:TCursor;
begin
  if Assigned(FSelected) and FSelected.Visible then
     result:=crHandPoint
  else
     result:=Cursor;
end;

function TCustomChartLegend.CalcInverted:Boolean;
var tmpSeries : TChartSeries;
begin
  result:=Inverted;

  if InternalLegendStyle=lsValues then
  begin
    tmpSeries:=GetLegendSeries;

    if Assigned(tmpSeries) and TSeriesAccess(tmpSeries).InvertedLegend then
       result:=not result;
  end;
end;

function TCustomChartLegend.DividingWidth:Integer;
begin
  if DividingLines.Visible then
     result:={$IFDEF FMX}Round{$ENDIF}(DividingLines.Width)+TeeLegendOff2
  else
     result:=0;
end;

Function TCustomChartLegend.Clicked(const P:TPoint):Integer;
var
  tmpH : Integer;
  tmpInverted : Boolean;
  cRow : Integer;

  Function ClickedRow:Integer;
  var t   : Integer;
      tmp : Integer;
  begin
    result:=-1;

    tmp:=FirstItemTop+1;

    for t:=0 to NumRows-1 do
    begin
      if (P.y>=tmp) and (P.y<=(tmp+tmpH)) then
      begin
        result:=t;

        if tmpInverted then
           result:=NumRows-t-1;

        break;
      end;

      Inc(tmp,tmpH);
    end;
  end;

  Function GetLegendBounds:TRect;
  var tmp : Integer;
  begin
    result:=ShapeBounds;

    if Assigned(FOnCalcSize) and Vertical then // Has scrollbar ?
    begin
      tmp:=0;
      FOnCalcSize(Self,tmp);
      result.Right:=result.Right-tmp;
    end;
  end;

  function CalcJustified:Integer;
  var tmpW   : TCoordinate;
      tmp2   : TCoordinate;
      t  : Integer;
      tmpMod : Integer;
  begin
    result:=-1;

    tmp2:=ShapeBounds.Left;

    for t:=0 to NumCols-1 do
    begin
      tmpW:=ColumnWidth(t mod ITotalItems);

      if (P.x>=tmp2) and (P.x<=(tmp2+tmpW)) then
      begin
        result:=cRow;

        if result<>-1 then
        begin
          result:=NumCols*result;

          if tmpInverted then
          begin
            Inc(result,NumCols-t-1);

            // Decrement empty items at last row, if any.
            tmpMod:=((LastValue-FirstValue)+1) mod NumCols;

            if tmpMod>0 then
               Dec(result,NumCols-tmpMod);
          end
          else
             Inc(result,t);

          if result>ITotalItems-1 then result:=-1
                                  else Inc(result,FirstValue); // TV52010293

        end;
      end;

      tmp2:=tmp2+tmpW;
    end;
  end;

  function CalcNotJustified:Integer;
  var tmpW   : TCoordinate;
      tmp2   : TCoordinate;
      t      : Integer;
  begin
    result:=-1;

    tmp2:=ShapeBounds.Left+1;

    for t:=0 to ITotalItems-1 do
    begin
      tmpW:=ColumnWidth(t mod ITotalItems);

      if (P.x>=tmp2) and (P.x<=(tmp2+tmpW)) then
      begin
        if tmpInverted then
           result:=(NumCols-t-1)+FirstValue
        else
           result:=t+FirstValue;

        break;
      end
      else
        tmp2:=tmp2+tmpW;
    end;
  end;

begin
  result:=-1;

  if PointInRect(GetLegendBounds,P) then { inside legend }
  begin
    TTeePanelAccess(ParentChart).TryAssignFont(Font);
    tmpH:=CalcItemHeight;

    tmpInverted:=CalcInverted;
    cRow:=ClickedRow;

    if Vertical then
       result:=cRow+FirstValue
    else
    if NumCols>0 then
    begin
      if IJustify then
         result:=CalcJustified
      else
         result:=CalcNotJustified;
    end;
  end;
end;

Function TCustomChartLegend.HasCheckBoxes:Boolean;
begin
  result:=CheckBoxes and (InternalLegendStyle<>lsValues);
end;

Function TCustomChartLegend.DoMouseDown(Const x,y:Integer; PointIndex:Integer):Boolean;

  procedure CheckSeriesGroups;
  var t : Integer;
  begin
    if CheckBoxesStyle=cbsCheck then
    begin
      with TCustomChart(ParentChart).SeriesList.Groups[PointIndex] do
           if Active=gaNo then Active:=gaYes
                          else Active:=gaNo
    end
    else
    with TCustomChart(ParentChart).SeriesList.Groups do
    for t:=0 to Count-1 do
        if t=PointIndex then
           Items[t].Active:=gaYes
        else
           Items[t].Active:=gaNo;
  end;

  procedure ToogleSeries;
  var tmpSeries : TChartSeries;
  begin
    tmpSeries:=TCustomChart(ParentChart).SeriesLegend(PointIndex,False);

    if Assigned(tmpSeries) then
       tmpSeries.Active:=not tmpSeries.Active;
  end;

  procedure SelectOneSeries;
  var t   : Integer;
      tmpSeries : TChartSeries;
  begin
    with TCustomChart(ParentChart) do
        for t:=0 to SeriesCount-1 do
        begin
          tmpSeries:=SeriesLegend(t,False);

          if Assigned(tmpSeries) then
             tmpSeries.Active:=t=PointIndex;
        end;
  end;

begin
  result:=False;

  Selected.ItemIndex:=PointIndex;

  if (PointIndex<>-1) and HasCheckBoxes then
  begin
    if InternalLegendStyle=lsSeriesGroups then
       CheckSeriesGroups
    else
    if CheckBoxesStyle=cbsCheck then
       ToogleSeries
    else
       SelectOneSeries;

    result:=True;
  end;
end;

{ Utility routines }

{ Draws the Series "Legend" on the specified rectangle and Canvas }
Procedure PaintSeriesLegend(const ASeries:TChartSeries; const ACanvas:TCanvas; Const R:TRect);
var OldCanvas : TCanvas;
    OldCanvas3D : TCanvas3D;
    tmpChart  : TCustomChart;
    OldBitmap : TBitmap;
    IsTempChart : Boolean;
begin
  tmpChart:=TCustomChart(ASeries.ParentChart);

  IsTempChart:=not Assigned(tmpChart);

  if IsTempChart then
  begin
    tmpChart:=TCustomChart.Create(nil);
    ASeries.ParentChart:=tmpChart;
  end;

  ACanvas.{$IFDEF FMX}Fill{$ELSE}Brush{$ENDIF}.Style:=bsSolid;
  ACanvas.{$IFDEF FMX}Fill{$ELSE}Brush{$ENDIF}.Color:=tmpChart.Legend.Color;
  tmpChart.Legend.PrepareSymbolPen;

  with tmpChart do
  begin
    if Canvas.SupportsFullRotation or
      (Canvas.ClassType<>TTeeCanvas3D) then
    begin
      OldCanvas3D:=Canvas;
      InternalCanvas:=TTeeCanvas3D.Create;
      InternalCanvas.ReferenceCanvas:=ACanvas;
    end
    else
      OldCanvas3D:=nil;

    if Canvas is TTeeCanvas3D then
    begin
      OldBitmap:=TTeeCanvas3D(Canvas).Bitmap;
      TTeeCanvas3D(Canvas).Bitmap:=nil;
    end
    else
      OldBitmap:=nil;

    OldCanvas:=Canvas.ReferenceCanvas;
    Canvas.ReferenceCanvas:=ACanvas;
    try
      Canvas.FrontPlaneBegin;
      ASeries.DrawLegend(-1,R);
      Canvas.FrontPlaneEnd;
    finally
      Canvas.ReferenceCanvas:=OldCanvas;
    end;

    if Canvas is TTeeCanvas3D then
       TTeeCanvas3D(Canvas).Bitmap:=OldBitmap;
  end;

  if IsTempChart then
  begin
    OldCanvas3D.Free;
    ASeries.ParentChart:=nil;
    tmpChart.Free;
  end
  else
  if Assigned(OldCanvas3D) and Assigned(ASeries.ParentChart) then
  begin
    {$IFNDEF AUTOREFCOUNT}
    ASeries.ParentChart.Canvas.Free;
    {$ENDIF}
    
    TCustomChart(ASeries.ParentChart).InternalCanvas:=OldCanvas3D;
  end;
end;

{ Returns a valid Name for a new Series (eg: Series1, Series2, etc) }
Function GetNewSeriesName(const AOwner:TComponent):TComponentName;
begin
  result:=TeeGetUniqueName(AOwner,TeeMsg_DefaultSeriesName);
end;

{ Copies all properties from OldSeries to NewSeries }
Procedure AssignSeries(var OldSeries,NewSeries:TChartSeries);
var OldName   : TComponentName;
    tmpSeries : TChartSeries;
    t         : Integer;
begin
  if NewSeries<>OldSeries then
  begin
    NewSeries.Assign(OldSeries);

    With NewSeries do
    begin    { events }
      AfterDrawValues  :=OldSeries.AfterDrawValues;
      BeforeDrawValues :=OldSeries.BeforeDrawValues;
      OnAfterAdd       :=OldSeries.OnAfterAdd;
      OnBeforeAdd      :=OldSeries.OnBeforeAdd;
      OnClearValues    :=OldSeries.OnClearValues;
      OnClick          :=OldSeries.OnClick;
      OnDblClick       :=OldSeries.OnDblClick;
      OnGetMarkText    :=OldSeries.OnGetMarkText;
    end;

    OldName:=OldSeries.Name;

    While OldSeries.LinkedSeries.Count>0 do
    begin
      tmpSeries:=OldSeries.LinkedSeries[0];

      { after removing... }
      With tmpSeries.DataSources do
      begin
        if IndexOf(NewSeries)=-1 then Add(NewSeries);
        if IndexOf(OldSeries)<>-1 then Remove(OldSeries);
      end;

      TSeriesAccess(NewSeries).AddLinkedSeries(tmpSeries);
      TSeriesAccess(OldSeries).RemoveLinkedSeries(tmpSeries);
    end;

    // Replace old series with new series in tools
    with OldSeries.ParentChart do
    for t:=0 to Tools.Count-1 do
    if Tools[t] is TTeeCustomToolSeries then
       with TTeeCustomToolSeries(Tools[t]) do
            if Series=OldSeries then Series:=NewSeries;

    // Swap Series on Chart list
    OldSeries.ParentChart.ExchangeSeries(OldSeries,NewSeries);

    {$IFDEF AUTOREFCOUNT}
    if Assigned(OldSeries.Owner) then
       OldSeries.Owner.RemoveComponent(OldSeries);

    OldSeries.ParentChart:=nil;
    {$ENDIF}

    FreeAndNil(OldSeries);

    NewSeries.Name:=OldName;
    TChartAccess(NewSeries.ParentChart).BroadcastSeriesEvent(NewSeries,seChangeTitle);
    NewSeries.CheckDataSource;

    if TSeriesAccess(NewSeries).ManualData then
       NewSeries.RefreshSeries;
  end;
end;

{ Creates a new TeeFunction object, associates it with a Series, and returns it }
Function CreateNewTeeFunction(const ASeries:TChartSeries; AClass:TTeeFunctionClass):TTeeFunction;
begin
  result:=AClass.Create(ASeries.Owner);
  result.ParentSeries:=ASeries;
  result.Name:=TeeGetUniqueName(ASeries.Owner,TeeMsg_DefaultFunctionName);
end;

{ Creates a new Series object and sets the Name, ParentChart and
  FunctionType properties }
Function CreateNewSeries( AOwner:TComponent;
                          const AChart:TCustomAxisPanel;
                          AClass:TChartSeriesClass;
                          AFunctionClass:TTeeFunctionClass=nil):TChartSeries;
var t : Integer;
begin
  if not Assigned(AOwner) then
     AOwner:=AChart;

  {
    If the Chart already contains Series as Owner, make new Series
    have the Chart as owner as well.  This helps when saving charts
    to *.tee files, that have series created at run-time
  }
  if Assigned(AChart) then
    for t:=0 to AChart.SeriesCount-1 do
    if AChart[t].Owner=AChart then
    begin
      AOwner:=AChart;
      break;
    end;

  result:=AClass.Create(AOwner);
  result.Name:=GetNewSeriesName(AOwner);
  result.ParentChart:=AChart;

  if Assigned(AFunctionClass) then
     CreateNewTeeFunction(result,AFunctionClass);
end;

// Duplicates a Series. Adds the new Series to ASeries.ParentChart
Function CloneChartSeries(const ASeries:TChartSeries):TChartSeries;
begin
  result:=CloneChartSeries(ASeries,ASeries.ParentChart);
end;

// Duplicates ASeries, adds the new Series to AChart
Function CloneChartSeries(const ASeries:TChartSeries; const AChart:TCustomAxisPanel):TChartSeries;
begin
  result:=CloneChartSeries(ASeries,ASeries.Owner,AChart);
end;

// Duplicates ASeries with AOwner as new series owner, adds the new Series to AChart
Function CloneChartSeries(const ASeries:TChartSeries; const AOwner:TComponent; const AChart:TCustomAxisPanel):TChartSeries;
var tmp : TTeeFunctionClass;
begin
  with ASeries do
  begin
    if FunctionType=nil then
       tmp:=nil
    else
       tmp:=TTeeFunctionClass(FunctionType.ClassType);

    result:=CreateNewSeries(AOwner,AChart,TChartSeriesClass(ClassType),tmp);
  end;

  result.Assign(ASeries);
end;

// Duplicates a Chart Tool. Adds the new Tool to ATool.ParentChart
function CloneChartTool(const ATool:TTeeCustomTool; const AOwner:TComponent):TTeeCustomTool;
begin
  result:=TTeeCustomToolClass(ATool.ClassType).Create(AOwner);

  {$IFNDEF TEEOCX}
  result.Name:=TeeGetUniqueName(AOwner,TeeMsg_DefaultToolName);
  {$ELSE}
  //due to Owner=nil in ocx version
  //result.Name:=TeeAXGetUniqueName(TeeMsg_DefaultToolName);
  {$ENDIF}

  result.Assign(ATool);
end;

// Duplicates a Chart Animation. Adds the new Animation to AAnimation.ParentChart
function CloneChartAnimation(const AAnimation:TTeeCustomAnimation; const AOwner:TComponent):TTeeCustomAnimation;
begin
  result:=TTeeCustomAnimationClass(AAnimation.ClassType).Create(AOwner);

  {$IFNDEF TEEOCX}
  result.Name:=TeeGetUniqueName(AOwner,TeeMsg_DefaultAnimationName);
  {$ELSE}
  //due to Owner=nil in ocx version
  //result.Name:=TeeAXGetUniqueName(TeeMsg_DefaultToolName);
  {$ENDIF}

  result.Assign(AAnimation);
end;

// Copies all properties, Series and Tools from Origin to Dest
procedure CloneChart(const Dest,Origin:TCustomChart; const AOwner:TComponent; ForceManualData:Boolean);
var t,tt,
    tmpIndex : Integer;
    Old : Boolean;
begin
  Old:=False;

  Dest.FreeAllSeries;
  Dest.Tools.Clear;

  Dest.Assign(Origin);

  for t:=0 to Origin.SeriesCount-1 do
  begin
    if ForceManualData then
    begin
      Old:=TSeriesAccess(Origin[t]).ManualData;
      TSeriesAccess(Origin[t]).ManualData:=True;
    end;

    try
      CloneChartSeries(Origin[t],AOwner,Dest);
    finally
      if ForceManualData then
         TSeriesAccess(Origin[t]).ManualData:=Old;
    end;
  end;

  // Relink Custom Axes within Destination Series
  for t:=0 to Origin.SeriesCount-1 do
  begin
    if Origin[t].CustomVertAxis <> nil then
       Dest[t].CustomVertAxis:=Dest.CustomAxes[Origin[t].CustomVertAxis.Index];

    if Origin[t].CustomHorizAxis <> nil then
       Dest[t].CustomHorizAxis:=Dest.CustomAxes[Origin[t].CustomHorizAxis.Index];
  end;

  // Relink DataSources within Destination Series
  for t:=0 to Origin.SeriesCount-1 do
  if Origin[t].DataSources.Count>0 then
  begin
    Dest[t].DataSources.Clear;

    for tt:=0 to Origin[t].DataSources.Count-1 do
    begin
      if {$IFNDEF D18}TObject{$ENDIF}(Origin[t].DataSources[tt]) is TChartSeries then
      begin
        tmpIndex:=Origin.SeriesList.IndexOf(TChartSeries(Origin[t].DataSources[tt]));

        if tmpIndex>-1 then
           Dest[t].DataSources.Add(Dest[tmpIndex]);
      end;
    end;
  end;

  // Clone Tools:
  for t:=0 to Origin.Tools.Count-1 do
        CloneChartTool(Origin.Tools[t],AOwner).ParentChart:=Dest;
end;

{ Replaces ASeries object with a new Series object of another class }
Function ChangeSeriesType(var ASeries:TChartSeries; NewType:TChartSeriesClass):TChartSeries;
var NewSeries : TChartSeries;
begin
  if ASeries.ClassType<>NewType then { only if different classes }
  begin
    NewSeries:=CreateNewSeries(ASeries.Owner,ASeries.ParentChart,NewType);

    if Assigned(NewSeries) then
    begin
      AssignSeries(ASeries,NewSeries);
      ASeries.Free;
      ASeries:=NewSeries;  { <-- change parameter }
    end;
  end;

  result:=ASeries;
end;

{ Replaces all Series objects in a Chart with a new Series class type }
procedure ChangeAllSeriesType( const AChart:TCustomChart; AClass:TChartSeriesClass );
var t         : Integer;
    tmpSeries : TChartSeries;
begin
  for t:=0 to AChart.SeriesCount-1 do
  begin
    tmpSeries:=AChart.Series[t];
    ChangeSeriesType(tmpSeries,AClass);
  end;
end;

function TCustomChartLegend.ShouldDraw: Boolean;
begin
  result:=Visible and
      (HasCheckBoxes or (TCustomChart(ParentChart).CountActiveSeries>0))
end;

procedure TCustomChartLegend.SetTitle(const Value: TLegendTitle);
begin
  if Assigned(Value) then Title.Assign(Value)
                     else FreeAndNil(FTitle);
end;

{ TTeeSeriesTypes }

{$IFNDEF D18}
Procedure TTeeSeriesTypes.Clear;
var t : Integer;
begin
  for t:=0 to Count-1 do Items[t].Free;
  inherited;
end;

Function TTeeSeriesTypes.Get(Index:Integer):TTeeSeriesType;
begin
  result:=TTeeSeriesType({$IFOPT R-}List{$ELSE}inherited Items{$ENDIF}[Index]);
end;
{$ENDIF}

Function TTeeSeriesTypes.Find(const ASeriesClass:String):TChartSeriesClass;
var t : Integer;
    tmp : TTeeSeriesType;
begin
  for t:=0 to Count-1 do
  begin
    tmp:=Items[t];

    if (tmp.FunctionClass=nil) and (tmp.SeriesClass.ClassName=ASeriesClass) then
    begin
      result:=tmp.SeriesClass;
      Exit;
    end;
  end;

  result:=nil;
end;

Function TTeeSeriesTypes.Find(const ASeriesClass:TChartSeriesClass; SubIndex:Integer=0):TTeeSeriesType;
var t : Integer;
begin
  for t:=0 to Count-1 do
  begin
    result:=Items[t];

    if (result.SeriesClass=ASeriesClass) and (result.FunctionClass=nil) then
       if (SubIndex=0) or (result.SubIndex=SubIndex) then
          Exit;
  end;

  result:=nil;
end;

// Workaround a possible C++ compiler bug (initialization order of units)
function CheckTeeSeriesTypes:TTeeSeriesTypes;
begin
  if not Assigned(TeeSeriesTypes) then
     TeeSeriesTypes:=TTeeSeriesTypes.Create;

  result:=TeeSeriesTypes;
end;

// D18: Manually free list items (cannot override Clear for generic collection)
procedure FreeSeriesTypes;
{$IFDEF D18}
{$IFNDEF AUTOREFCOUNT}
var t : Integer;
{$ENDIF}
{$ENDIF}
begin
  {$IFDEF D18}
  {$IFNDEF AUTOREFCOUNT}
  for t:=0 to TeeSeriesTypes.Count-1 do TeeSeriesTypes[t].Free;
  {$ENDIF}
  {$ENDIF}
  
  FreeAndNil(TeeSeriesTypes);
end;

Procedure RegisterTeeSeriesFunction( ASeriesClass:TChartSeriesClass;
                                     AFunctionClass:TTeeFunctionClass;
                                     ADescription,AGalleryPage:PString;
                                     ANumGallerySeries:Integer=1;
             ASubIndex:Integer=0);
var t             : Integer;
    NewSeriesType : TTeeSeriesType;
    tmpTypes      : TTeeSeriesTypes;
begin
  TeeActivateGroup;

  if Assigned(ASeriesClass) then Classes.RegisterClass(ASeriesClass);
  if Assigned(AFunctionClass) then Classes.RegisterClass(AFunctionClass);

  tmpTypes:=CheckTeeSeriesTypes;

  if ASubIndex=0 then
  begin
    for t:=0 to tmpTypes.Count-1 do
    With tmpTypes[t] do
      if (SeriesClass=ASeriesClass) and
         (FunctionClass=AFunctionClass) then
            Exit;
  end;

  NewSeriesType:=TTeeSeriesType.Create;
  with NewSeriesType do
  begin
    SeriesClass   := ASeriesClass;
    FunctionClass := AFunctionClass;
    Description   := ADescription;
    GalleryPage   := AGalleryPage;
    NumGallerySeries := ANumGallerySeries;
    SubIndex         := ASubIndex;
  end;

  tmpTypes.Add(NewSeriesType);
end;

{ Adds a new Series component definition for the Gallery }
Procedure RegisterTeeSeries( ASeriesClass:TChartSeriesClass;
                             ADescription,AGalleryPage:PString;
                             ANumGallerySeries : Integer );
begin
  RegisterTeeSeriesFunction( ASeriesClass,
                             nil,
                             ADescription,
                             AGalleryPage,
                             ANumGallerySeries );
end;

Procedure RegisterTeeSeries( ASeriesClass:TChartSeriesClass;
                             ADescription:PString); overload;
begin
  RegisterTeeSeries(ASeriesClass,ADescription,@TeeMsg_GalleryStandard,2);
end;

Procedure RegisterTeeFunction( AFunctionClass:TTeeFunctionClass;
                               ADescription,AGalleryPage:PString;
                               ANumGallerySeries : Integer=1 );
begin
  RegisterTeeSeriesFunction( nil,
                             AFunctionClass,
                             ADescription,
                             AGalleryPage,
                             ANumGallerySeries);
end;

Procedure RegisterTeeBasicFunction( AFunctionClass:TTeeFunctionClass;
                                    ADescription:PString);
begin
  RegisterTeeFunction(AFunctionClass,ADescription,@TeeMsg_GalleryStandard,2);
end;

Procedure InternalUnRegister(IsSeries:Boolean; AClass:TComponentClass);
var tmp      : TTeeSeriesType;
    t        : Integer;
    tmpTypes : TTeeSeriesTypes;
begin
  t:=0;
  tmpTypes:=CheckTeeSeriesTypes;

  While t<tmpTypes.Count do
  begin
    tmp:=tmpTypes[t];

    if (IsSeries and (tmp.SeriesClass=AClass)) or
     ((not IsSeries) and (tmp.FunctionClass=AClass)) then
    begin
      tmp.Free;
      tmpTypes.Delete(t);
    end
    else Inc(t);
  end;
end;

Procedure UnRegisterTeeSeries(Const ASeriesList:Array of TChartSeriesClass);
var t : Integer;
begin
  for t:=Low(ASeriesList) to High(ASeriesList) do
      InternalUnRegister(True,ASeriesList[t]);
end;

Procedure UnRegisterTeeFunctions(Const AFunctionList:Array of TTeeFunctionClass);
var t : Integer;
begin
  for t:=Low(AFunctionList) to High(AFunctionList) do
      InternalUnRegister(False,AFunctionList[t]);
end;

{$IFNDEF FMX}

{ TTeeDragObject }

Constructor TTeeDragObject.Create(Const APart: TChartClickedPart);
begin
  inherited Create {$IFDEF LCL}(nil){$ENDIF};
  FPart:=APart;
end;

function TTeeDragObject.GetDragCursor(Accepted: Boolean; X, Y: Integer): TCursor;
begin
  if Accepted then Result := crTeeHand
              else Result := crNoDrop;
end;

{$IFNDEF LCL}
procedure TTeeDragObject.Finished(Target: TObject; X, Y: Integer; Accepted: Boolean);
begin
end;
{$ENDIF}

{$ENDIF}

{ TTeeToolTypes }

{$IFNDEF TEELISTMETA}
function TTeeToolTypes.Get(Index: Integer): TTeeCustomToolClass;
begin
  result:=TTeeCustomToolClass({$IFOPT R-}List{$ELSE}inherited Items{$ENDIF}[Index]);
end;
{$ENDIF}

// Workaround a possible C++ bug (initialization order of TeeTools vs. Chart units)
function CheckTeeToolTypes:TTeeToolTypes;
begin
  if not Assigned(TeeToolTypes) then
     TeeToolTypes:=TTeeToolTypes.Create;

  result:=TeeToolTypes;
end;

Procedure RegisterTeeTools({$IFNDEF BCB}const{$ENDIF} ATools:Array of TTeeCustomToolClass);
var t : Integer;
begin
  TeeActivateGroup;

  with CheckTeeToolTypes do
  for t:=Low(ATools) to High(ATools) do
      if IndexOf(ATools[t])=-1 then
      begin
        Classes.RegisterClass(ATools[t]);
        Add(ATools[t]);
      end;
end;

Procedure UnRegisterTeeTools({$IFNDEF BCB}const{$ENDIF} ATools:Array of TTeeCustomToolClass);
var t   : Integer;
    tmp : Integer;
begin
  TeeActivateGroup;

  with CheckTeeToolTypes do
  for t:=Low(ATools) to High(ATools) do
  begin
    tmp:=IndexOf(ATools[t]);
    if tmp<>-1 then Delete(tmp);

    UnRegisterClass(ATools[t]);
  end;
end;

function CheckTeeAnimationTypes:TTeeToolTypes;
begin
  if not Assigned(TeeAnimationTypes) then
     TeeAnimationTypes:=TTeeToolTypes.Create;

  result:=TeeAnimationTypes;
end;

Procedure RegisterTeeAnimations({$IFNDEF BCB}const{$ENDIF} AAnimations:Array of TTeeCustomAnimationClass);
var t : Integer;
begin
  TeeActivateGroup;

  with CheckTeeAnimationTypes do
  for t:=Low(AAnimations) to High(AAnimations) do
  if IndexOf(AAnimations[t])=-1 then
  begin
    Classes.RegisterClass(AAnimations[t]);
    Add(AAnimations[t]);
  end;
end;

Procedure UnRegisterTeeAnimations({$IFNDEF BCB}const{$ENDIF} AAnimations:Array of TTeeCustomAnimationClass);
var t   : Integer;
    tmp : Integer;
begin
  TeeActivateGroup;

  with CheckTeeAnimationTypes do
  for t:=Low(AAnimations) to High(AAnimations) do
  begin
    tmp:=IndexOf(AAnimations[t]);
    if tmp<>-1 then Delete(tmp);

    UnRegisterClass(AAnimations[t]);
  end;
end;

Function GetGallerySeriesName(const ASeries:TChartSeries):String;
var AType : TTeeSeriesType;
begin
  AType:=CheckTeeSeriesTypes.Find(TChartSeriesClass(ASeries.ClassType));

  if Assigned(AType) then result:=AType.Description^
                     else result:=ASeries.ClassName;
end;

{ TLegendTitle }

Constructor TLegendTitle.Create(const AOwner: TCustomTeePanel);
begin
  inherited;

  IDraw2D:=True;

  InitTransparent(True);

  TTeeFontAccess(Font).InitStyle([{$IFDEF FMX}TFontStyle.{$ENDIF}fsBold]);
end;

procedure TLegendTitle.Assign(Source: TPersistent);
begin
  if Source is TLegendTitle then
  with TLegendTitle(Source) do
  begin
    Self.Text:=Text;
  end;

  inherited;
end;

procedure TLegendTitle.InitEmboss(const AEmboss:TTeeEmboss);
begin
  InitShadow(AEmboss);
end;

procedure TLegendTitle.InitShadow(const AShadow:TTeeShadow);
begin
  TShadowAccess(AShadow).InitValues(AShadow.Visible,
        {$IFDEF FMX}TAlphaColors.Silver{$ELSE}clSilver{$ENDIF},2);
end;

{ TCustomChartLink }

procedure TCustomChartLink.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;

  if (Operation=opRemove) and Assigned(FChart) and (AComponent=FChart) then
     Chart:=nil;
end;

procedure TCustomChartLink.SetChart(const Value: TCustomChart);
begin
  if FChart<>Value then
  begin
    if Assigned(FChart) then
       FChart.RemoveFreeNotification(Self);

    FChart:=Value;

    if Assigned(FChart) then
       FChart.FreeNotification(Self);
  end;
end;

{ TColorPalettes }

class procedure TColorPalettes.RegisterPalette(const AName:String;
                                           const APalette: Array of TColor);
var tmp : Integer;
begin
  tmp:=ColorPalettes.IndexOf(AName);

  if tmp=-1 then
     ColorPalettes.Add(AName,APalette)
  else
     ColorPalettes.CopyPalette(tmp,APalette); // replace existing named palette
end;

var
  IColorPalettes : TColorPalettes=nil;

function ColorPalettes : TColorPalettes;
begin
  if not Assigned(IColorPalettes) then
  begin
    IColorPalettes:=TColorPalettes.Create;

    with IColorPalettes do
    begin
      RegisterPalette(TeeMsg_ThemeTeeChart,[]);                        //    0
      RegisterPalette(TeeMsg_ThemeExcel,ExcelPalette);                 //    1
      RegisterPalette(TeeMsg_ThemeVictorian,VictorianPalette);         //    2
      RegisterPalette(TeeMsg_ThemePastels,PastelsPalette);             //    3
      RegisterPalette(TeeMsg_ThemeSolid,SolidPalette);                 //    4
      RegisterPalette(TeeMsg_ThemeClassic,ClassicPalette);             //    5
      RegisterPalette(TeeMsg_ThemeWeb,WebPalette);                     //    6
      RegisterPalette(TeeMsg_ThemeModern,ModernPalette);               //    7
      RegisterPalette(TeeMsg_ThemeRainbow,RainbowPalette);             //    8
      RegisterPalette(TeeMsg_ThemeWinXP,WindowsXPPalette);             //    9
      RegisterPalette(TeeMsg_ThemeMacOS,MacOSPalette);                 //   10
      RegisterPalette(TeeMsg_ThemeWinVista,WindowsVistaPalette);       //   11
      RegisterPalette(TeeMsg_ThemeGrayScale,GrayScalePalette);         //   12
      RegisterPalette(TeeMsg_ThemeOpera,OperaPalette);                 //   13
      RegisterPalette(TeeMsg_ThemeWarm,WarmPalette);                   //   14
      RegisterPalette(TeeMsg_ThemeCool,CoolPalette);                   //   15
      RegisterPalette(TeeMsg_ThemeBlack,OnBlackPalette);               //   16
      RegisterPalette(TeeMsg_Metro,MetroPalette);                      //   17
      RegisterPalette(TeeMsg_iOSPalette,iOSPalette);                   //   18
      RegisterPalette(TeeMsg_AndroidPalette,AndroidPalette);           //   19
      RegisterPalette(TeeMsg_LookoutPalette,LookoutPalette);           //   20
      RegisterPalette(TeeMsg_SeawashPalette,SeawashPalette);           //   21
      RegisterPalette(TeeMsg_ActivityPalette,ActivityPalette);         //   22
    end;
  end;

  result:=IColorPalettes;
end;

procedure TColorPalettes.Add(const AName:String; const APalette:Array of TColor);
var L : Integer;
begin
  L:=Count;
  SetLength(IPalettes,L+1);

  IPalettes[L].Name:=AName;
  CopyPalette(L,APalette);
end;

function TColorPalettes.Count:Integer;
begin
  result:=Length(IPalettes);
end;

function TColorPalettes.IndexOf(const AName:String):Integer;
var t : Integer;
begin
  for t:=0 to Count-1 do
      if SameText(IPalettes[t].Name,AName) then
      begin
        result:=t;
        Exit;
      end;

  result:=-1;
end;

procedure TColorPalettes.CopyPalette(const Index:Integer; const APalette:Array of TColor);
var t,
    L : Integer;
begin
  L:=Length(APalette);

  SetLength(IPalettes[Index].Colors,L);

  for t:=0 to L-1 do
      IPalettes[Index].Colors[t]:=APalette[t+Low(APalette)];
end;

function TColorPalettes.GetName(const Index:Integer):String;
begin
  result:=IPalettes[Index].Name;
end;

procedure TColorPalettes.SetName(const Index:Integer; const AName:String);
begin
  IPalettes[Index].Name:=AName;
end;

class procedure TColorPalettes.AddTo(const Items:TStrings; AddDefault:Boolean=False);
var t : Integer;
begin
  if AddDefault then
     Items.Add(TeeMsg_Default);

  for t:=0 to ColorPalettes.Count-1 do
      Items.Add(ColorPalettes.IPalettes[t].Name);
end;

class procedure TColorPalettes.GetPalette(Index:Integer; out Palette:TColorArray);

  procedure CopyPalette(const Source:Array of TColor);
  var t : Integer;
      {$IFDEF FMX}
      tmp : Byte;
      {$ENDIF}
  begin
    SetLength(Palette,Length(Source));

    for t:=0 to High(Source) do
    {$IFDEF FMX}
    begin
      Palette[t]:=Source[t];

      with TAlphaColorRec(Palette[t]) do
      begin
        tmp:=R;
        R:=B;
        B:=tmp;
        A:=$FF;
      end;

    end;
    {$ELSE}
        Palette[t]:={$IFDEF FMX}TAlphaColors.Alpha or {$ENDIF}Source[t];
    {$ENDIF}
  end;

begin
  if (Index>=0) and (Index<ColorPalettes.Count) then
     CopyPalette(ColorPalettes.IPalettes[Index].Colors)
  else
     CopyPalette([]);
end;

class procedure TColorPalettes.ApplyPalette(const Chart: TCustomAxisPanel;
  Index: Integer);
var tmp : TColorArray;
begin
  {$IFNDEF D7}
  tmp:=nil;
  {$ENDIF}

  GetPalette(Index,tmp);
  ApplyPalette(Chart,tmp);
  tmp:=nil;

  if Chart is TCustomChart then
     TCustomChartAccess(Chart).FColorPaletteIndex:=Index;

  Chart.Invalidate;
end;

type
  TCustomAxisPanelAccess=class(TCustomAxisPanel);

class procedure TColorPalettes.ApplyPalette(const Chart: TCustomAxisPanel;
  const Palette: array of TColor);
var t, L : Integer;
begin
  L:=Length(Palette);

  if L=0 then
     Chart.ColorPalette:=nil
  else
  begin
    SetLength(Chart.ColorPalette,L);

    for t:=0 to L-1 do
        Chart.ColorPalette[t]:=Palette[t];
  end;

  TCustomAxisPanelAccess(Chart).ColorPaletteChanged;
end;

{ TChartTheme }
Constructor TChartTheme.Create(AChart: TCustomChart);
begin
  inherited Create;
  Chart:=AChart;
end;

procedure TChartTheme.Apply; // BCB does not accept "abstract;"
begin
end;

// Not "abstract", due to BCB compatibility
function TChartTheme.Description: String;
begin
  result:=TeeMsg_ThemeDefault;
end;

procedure TChartTheme.SetDefaultValues;
begin
end;

initialization
  Randomize;
  CheckTeeSeriesTypes;
  CheckTeeToolTypes;
  CheckTeeAnimationTypes;

finalization
  FreeAndNil(IColorPalettes);
  FreeSeriesTypes;
  FreeAndNil(TeeToolTypes);
  FreeAndNil(TeeAnimationTypes);
end.

