{**********************************************}
{   TeeChart Tools                             }
{   Copyright (c) 1999-2020 by Steema Software }
{**********************************************}
unit TeeTools;
{$I TeeDefs.inc}

interface

// This unit defines and implements several "Chart Tools".
// Tools appear at the Chart editor "Tools" tab and can be added
// both at design-time and run-time.

// The purpose of a "tool" is to perform some generic functionality
// outside the Chart or Series code. Chart Tools are notified of
// chart drawing (repaints) and chart mouse events (move, up, down).

Uses {$IFNDEF LINUX}
     {$IFNDEF FMX}
     Windows,
     {$ENDIF}
     {$ENDIF}
     SysUtils, Classes,

     {$IFDEF D18}
     System.Generics.Collections,
     {$ENDIF}

     {$IFDEF FMX}
     System.UITypes, FMX.Types,

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

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

     {$ELSE}

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

     Graphics, Controls, ExtCtrls, Forms,
     {$ENDIF}

     {$IFDEF D6}
     Types,
     {$ENDIF}

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

// Default number of pixels to consider a line is "clicked"
// (ie: when the mouse is more or less 3 pixels from a line)
Const
  // Used at TColorLineTool, TAxisArrowTool and TCursorTool
  TeeClickTolerance = 3;

  clTeeScrollBar = {$IFDEF FMX}TAlphaColor{$ENDIF}($F0F0F0);

type
  TAnnotationTool=class;

  TAnnotationPosition=(ppLeftTop,ppLeftBottom,ppRightTop,ppRightBottom,ppCenter);

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

  TAnnotationCallout=class(TCallout)
  private
    FX : Integer;
    FY : Integer;
    FZ : Integer;

    Function CloserPoint(const R:TRect; const P:TPoint):TPoint;
    procedure SetX(const Value:Integer);
    procedure SetY(const Value:Integer);
    procedure SetZ(const Value:Integer);
  public
    Constructor Create(const AOwner: TChartSeries);

    Procedure Assign(Source:TPersistent); override;
  published
    property Visible default False;
    property XPosition:Integer read FX write SetX default 0;
    property YPosition:Integer read FY write SetY default 0;
    property ZPosition:Integer read FZ write SetZ default 0;
  end;

  // Annotation tool, use it to display text anywhere on the chart.
  TAnnotationTool=class(TTeeCustomTool)
  private
    FCallout      : TAnnotationCallout;
    FDraw3D       : Boolean;
    FOnAfterDraw  : TNotifyEvent;
    FOnBeforeDraw : TNotifyEvent;
    FOnClick      : TAnnotationClick;
    FOnDblClick   : TAnnotationClick;
    FPosition     : TAnnotationPosition;
    FPositionUnits: TTeeUnits;
    FSelected     : TTeeSelected;
    FShape        : TTextShape;
    FZ            : Integer;

    function GetAutoSize:Boolean;
    function GetBounds:TRect;
    function GetBoundsParent(const AParent:TCustomAxisPanel):TRect;
    function GetClipText:Boolean;
    function GetCursor:TCursor;
    function GetHeight: Integer;
    function GetLeft: Integer;
    function GetTextAlign: TAlignment;
    function GetTop: Integer;
    function GetWidth: Integer;
    procedure InternalSetParent(const Value: TCustomAxisPanel);
    function IsNotAutoSize: Boolean;
    procedure Repaint;
    procedure SetAutoSize(const Value: Boolean);
    procedure SetBounds(const Value: TRect);
    procedure SetCallout(const Value: TAnnotationCallout);
    procedure SetClipText(const Value: Boolean);
    procedure SetCursor(const Value:TCursor);
    procedure SetDraw3D(const Value:Boolean);
    procedure SetHeight(const Value: Integer);
    procedure SetLeft(const Value: Integer);
    procedure SetPosition(const Value: TAnnotationPosition);
    procedure SetPositionUnits(const Value: TTeeUnits);
    procedure SetSelected(const Value: TTeeSelected);
    procedure SetShape(const Value: TTextShape);
    procedure SetTextAlign(const Value: TAlignment);
    procedure SetTop(const Value: Integer);
    procedure SetWidth(const Value: Integer);
    procedure SetZ(const Value: Integer);
  protected
    IKeepCursor : Boolean; // Special case for TRectangleTool Edge hits

    Procedure ChartEvent(AEvent:TChartToolEvent); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
    function CreateShape:TTextShape; virtual;

    // XE2 and up problem: "DrawText" conflicts with Windows api "DrawTextW",
    // so the best solution is to rename our method to "DoDrawText":
    Procedure DoDrawText(const AParent:TCustomAxisPanel); overload; virtual;
    Procedure DoDrawText; overload;

    class Function GetEditorClass:String; override;
    function GetXOffset:Integer; virtual;
    Function GetText:String; virtual;
    Function GetTextBounds(out x,y:Integer):TRect; overload;
    Function GetTextBounds(Panel:TCustomAxisPanel; out x,y:Integer):TRect; overload;
    Function GetTextBounds(const Panel:TCustomAxisPanel; const ABounds:TRect;
                           out x,y:Integer):TRect; overload; virtual;
    procedure SetName(const Value: TComponentName); override;
    Procedure SetActive(Value:Boolean); override;
    procedure SetParentChart(const Value: TCustomAxisPanel); override;
    Procedure SetText(const Value:String);
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;

    Function Clicked(x,y:Integer):Boolean;
    class Function Description:String; override;
    class Function LongDescription:String; override;

    property Bounds:TRect read GetBounds write SetBounds;
  published
    property Active;
    property AutoSize:Boolean read GetAutoSize write SetAutoSize default True;
    property Callout:TAnnotationCallout read FCallout write SetCallout;
    property ClipText:Boolean read GetClipText write SetClipText default True;
    property Cursor:TCursor read GetCursor write SetCursor default crDefault;
    property Draw3D:Boolean read FDraw3D write SetDraw3D default False;
    property Position:TAnnotationPosition read FPosition write SetPosition
              default ppLeftTop;
    property PositionUnits:TTeeUnits read FPositionUnits write SetPositionUnits
                                                         default muPixels;
    property Selected:TTeeSelected read FSelected write SetSelected;
    property Shape:TTextShape read FShape write SetShape;
    property Text:String read GetText write SetText stored False;
    property TextAlignment:TAlignment read GetTextAlign
                                      write SetTextAlign default taLeftJustify;

    property Height:Integer read GetHeight write SetHeight stored IsNotAutoSize;
    property Left:Integer read GetLeft write SetLeft stored False;
    property Top:Integer read GetTop write SetTop stored False;
    property Width:Integer read GetWidth write SetWidth stored IsNotAutoSize;
    property Z:Integer read FZ write SetZ default 0;

    property OnAfterDraw:TNotifyEvent read FOnAfterDraw write FOnAfterDraw;
    property OnBeforeDraw:TNotifyEvent read FOnBeforeDraw write FOnBeforeDraw;
    property OnClick:TAnnotationClick read FOnClick write FOnClick;
    property OnDblClick:TAnnotationClick read FOnDblClick write FOnDblClick;
  end;

  // Style of cursor tool
  TCursorToolStyle=( cssHorizontal,
                     cssVertical,
                     cssBoth,
                     cssScope,
                     cssScopeOnly
                     );

  TCursorTool=class;

  TCursorClicked=(ccNone,ccHorizontal,ccVertical,ccBoth);

  TCursorToolChangeEvent=procedure( Sender:TCursorTool;
                                    x,y:Integer;
                                    Const XValue,YValue:Double;
                                    Series:TChartSeries;
                                    ValueIndex:Integer) of object;

  TCursorToolGetAxisRect=procedure(Sender:TCursorTool; var Rect:TRect) of object;

  TSnapStyle=(ssDefault, ssHorizontal, ssVertical);

  TScopeCursorStyle=(scsRectangle, scsCircle, scsDiamond, scsEmpty, scsCamera);

  // Cursor tool, use it to draw a draggable line (cursor)
  TCursorTool=class(TTeeCustomToolSeries)
  private
    FAxisAnnotation: TAnnotationTool;
    FClick         : Integer;
    FFollowMouse   : Boolean;
    FFullRepaint   : Boolean;
    FHorizSize     : Integer;

    FOnChange      : TCursorToolChangeEvent;
    FOnGetAxisRect : TCursorToolGetAxisRect;
    FOnSnapChange  : TCursorToolChangeEvent;

    FScopeSize     : Integer;
    FScopeStyle    : TScopeCursorStyle;
    FSnap          : Boolean;
    FSnapStyle     : TSnapStyle;
    FStyle         : TCursorToolStyle;
    FUseChartRect  : Boolean;
    FUseSeriesZ    : Boolean;
    FVertSize      : Integer;

    IValidXPoint   : Boolean;
    IValidYPoint   : Boolean;
    IAxisValid     : Boolean;

    IDragging      : TCursorClicked;
    IOldSnap       : Integer;

    Procedure CalcValuePositions(X,Y:Integer);
    procedure DoChange;
    Procedure DrawCursorLines(Draw3D:Boolean; X,Y:Integer);
    procedure DrawScope(Draw3D:Boolean; X,Y:Integer);
    function GetAxisAnnotation:TAnnotationTool;
    Function GetAxisRect:TRect;
    Function InMouseRectangle(x,y:Integer; UseSize:Boolean=False):Boolean;
    function MouseRectangle(const R:TRect; const X,Y:Integer; UseSize:Boolean):TRect;
    procedure SetAxisAnnotation(const Value:TAnnotationTool);
    Procedure SetFullRepaint(const Value:Boolean);
    Procedure SetHorizSize(const Value:Integer);
    Procedure SetScopeSize(const Value:Integer);
    Procedure SetScopeStyle(const Value:TScopeCursorStyle);
    Procedure SetStyle(Value:TCursorToolStyle);
    procedure SetUseChartRect(const Value: Boolean);
    procedure SetUseSeriesZ(const Value: Boolean);
    Procedure SetVertSize(const Value:Integer);
    procedure SetXValue(const Value: Double);
    procedure SetYValue(const Value: Double);
    function ShouldForceRepaint:Boolean;
    Function Z:Integer;
  protected
    IXValue : Double;
    IYValue : Double;
    IPoint  : TPoint;

    Procedure CalcScreenPositions;
    Procedure Changed(SnapPoint:Integer); virtual;
    Procedure ChartEvent(AEvent:TChartToolEvent); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
    procedure GetChildren(Proc: TGetChildProc; Root:TComponent); override;
    function GetChildOwner: TComponent; override;
    class Function GetEditorClass:String; override;
    procedure ReadState(Reader: TReader); override;
    procedure SetSeries(const Value: TChartSeries); override;
    procedure SetParentChart(const Value: TCustomAxisPanel); override;
  public
    CustomZ : Double;

    Constructor Create(AOwner:TComponent); override;

    procedure Assign(Source:TPersistent); override;

    Function Clicked(x,y:Integer):TCursorClicked;
    class Function Description:String; override;
    class Function LongDescription:String; override;
    Function NearestPoint(AStyle:TCursorToolStyle; out Difference:Double):Integer;
    Function SnapToPoint:Integer;
    procedure RedrawCursor;

    property UseChartRect:Boolean read FUseChartRect write SetUseChartRect default False;
    property OnGetAxisRect:TCursorToolGetAxisRect read FOnGetAxisRect write FOnGetAxisRect;
  published
    property Active;
    property AxisAnnotation:TAnnotationTool read GetAxisAnnotation write SetAxisAnnotation;
    property ClickTolerance:Integer read FClick write FClick default TeeClickTolerance;
    property FollowMouse:Boolean read FFollowMouse write FFollowMouse default False;
    property FullRepaint:Boolean read FFullRepaint write SetFullRepaint default False;
    property HorizSize:Integer read FHorizSize write SetHorizSize default 0;
    property Pen;
    property Series;
    property ScopeSize:Integer read FScopeSize write SetScopeSize default 4;
    property ScopeStyle:TScopeCursorStyle read FScopeStyle write SetScopeStyle default scsRectangle;
    property Snap:Boolean read FSnap write FSnap default False;
    property SnapStyle:TSnapStyle read FSnapStyle write FSnapStyle default ssDefault;
    property Style:TCursorToolStyle read FStyle write SetStyle default cssBoth;
    property UseSeriesZ:Boolean read FUseSeriesZ write SetUseSeriesZ default False;
    property VertSize:Integer read FVertSize write SetVertSize default 0;
    property XValue:Double read IXValue write SetXValue;
    property YValue:Double read IYValue write SetYValue;

    { events }
    property OnChange:TCursorToolChangeEvent read FOnChange write FOnChange;
    property OnSnapChange:TCursorToolChangeEvent read FOnSnapChange write FOnSnapChange;
  end;

  TDragMarksTool=class;

  TDragMarkEvent=procedure(Sender:TDragMarksTool; Index:Integer;
                           Button:TMouseButton; Shift: TShiftState;
                           X,Y: Integer) of object;

  // Drag Marks tool, use it to allow the end-user to drag series Marks
  TDragMarksTool=class(TTeeCustomToolSeries)
  private
    FOnDragged  : TDragMarkEvent;
    FOnDragging : TDragMarkEvent;

    IOldX   : Integer;
    IOldY   : Integer;
    Index   : Integer;
    ISeries : TChartSeries;
  protected
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
    class Function GetEditorClass:String; override;
  public
    Constructor Create(AOwner:TComponent); override;
    class Function Description:String; override;
    class Function LongDescription:String; override;
  published
    property Active;
    property Series;

    property OnDraggedMark:TDragMarkEvent read FOnDragged write FOnDragged;
    property OnDraggingMark:TDragMarkEvent read FOnDragging write FOnDragging;
  end;

  TAxisArrowTool=class;

  TAxisArrowClickEvent=procedure(Sender:TAxisArrowTool; AtStart:Boolean) of object;

  TAxisArrowToolPosition=(aaStart,aaEnd,aaBoth);

  // Axis Arrow tool, use it to display small "arrows" at start and end
  // positions of an axis. These arrows can be clicked to scroll the axis.
  TAxisArrowTool=class(TTeeCustomToolAxis)
  private
    FHeadWidth     : Integer;
    FLength        : Integer;
    FPosition      : TAxisArrowToolPosition;
    FScrollPercent : Integer;
    FScrollInverted: Boolean;
    FSizePercent   : Integer;

    FOnClick       : TAxisArrowClickEvent;

    Function ClickedArrow(x,y:Integer):Integer;
    procedure SetHeadWidth(const Value: Integer);
    procedure SetLength(const Value: Integer);
    procedure SetPosition(const Value: TAxisArrowToolPosition);
    procedure SetSizePercent(const Value: Integer);
  protected
    Procedure ChartEvent(AEvent:TChartToolEvent); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
    class function GetEditorClass: String; override;
  public
    Constructor Create(AOwner:TComponent); override;

    class Function Description:String; override;
    class Function LongDescription:String; override;

    property Axis;
  published
    property Active;
    property Position:TAxisArrowToolPosition read FPosition write SetPosition default aaBoth;
    property Brush;
    property HeadWidth:Integer read FHeadWidth write SetHeadWidth default 8;
    property Length:Integer read FLength write SetLength default 16;
    property Pen;
    property ScrollInverted:Boolean read FScrollInverted write FScrollInverted default False;
    property ScrollPercent:Integer read FScrollPercent write FScrollPercent default 10;
    property SizePercent:Integer read FSizePercent write SetSizePercent default 50;

    { events }
    property OnClick:TAxisArrowClickEvent read FOnClick write FOnClick;
  end;

const
  DefaultScrollBarSize=18;

type
  TScrollThumbStyle=(stsCustom, stsThumb1, stsThumb2, stsThumb3, stsThumb4);

  TScrollBarThumb=class(TTeeCustomShapeBrushPen)
  private
    FSize    : Integer;
    FStyle   : TScrollThumbStyle;

    IScroll  : TTeeCustomTool;

    function GetGradient:TTeeGradient;
    procedure SetGradient(const Value: TTeeGradient);
    function GetImage:TTeePicture;
    function HasImage:Boolean;
    procedure SetImage(const Value: TTeePicture);
    procedure SetSize(const Value: Integer);
    procedure SetStyle(const Value: TScrollThumbStyle);
  public
    Constructor Create(const AOwner:TTeeCustomTool);

    procedure Assign(Source:TPersistent); override;
  published
    property Brush;
    property Pen;
    property Visible;

    // Gradient and Image published for back compatibility
    property Gradient:TTeeGradient read GetGradient write SetGradient;
    property Image:TTeePicture read GetImage write SetImage;

    property Size:Integer read FSize write SetSize default 0;
    property Style:TScrollThumbStyle read FStyle write SetStyle default stsCustom;
  end;

  TTeeScrollBarDrawStyle=(dsAlways,dsWhenNeeded);

  TTeeScrollBar=class;

  TTeeScrollBarPart=(spNone,spThumb,spArrowInc,spArrowDec,spResizeInc,spResizeDec);

  TSetPositionEvent=procedure(Sender:TTeeScrollBar; Value:Integer) of object;

  TTeeScrollBar=class(TTeeCustomTool)
  private
    FDrawStyle    : TTeeScrollBarDrawStyle;
    FInitial      : Integer;

    ThumbBegin,
    ThumbEnd      : TCoordinate;

    FThumb        : TScrollBarThumb;
    FTimer        : TTimer;
    FOnScrolled   : TNotifyEvent;

    FIn : TTeeScrollBarPart;

    FirstTime     : Boolean;
    OldPoint      : TPoint;

    FAllowResize  : Boolean;
    FAutoRepeat   : Boolean;
    FBevel        : TPanelBevel;
    FArrowBrush   : TTeeBrush;
    FArrows       : Boolean;
    FDisabledArrow: TTeeBrush;
    FMinSize      : Integer;
    FPosition     : Integer;
    FOnSetPosition: TSetPositionEvent;
    FOnChangeSize : TNotifyEvent;
    FRoundSize    : Integer;
    FSize         : Integer;

    FDecOutline   : Boolean;
    FIncOutline   : Boolean;

    Procedure ApplyScroll(Delta:Double; ActivateTimer:Boolean);
    Function ClickedDec(const P:TPoint):Boolean;
    Function ClickedInc(const P:TPoint):Boolean;
    function ClickedResize(IsInc:Boolean; const X,Y:TCoordinate):Boolean;
    function ClickedThumb(const P:TPoint): Boolean;
    Function DecRectangle:TRect;
    Procedure DoScroll;
    Procedure DoTimer(Sender:TObject);
    procedure DecArrow(out P:TTrianglePoints); // Not a function due to *.hpp generation bug
    procedure DrawArrow(IsDisabled:Boolean; const P:TTrianglePoints);
    function GetBackColor:TColor;
    function GetGradient:TTeeGradient;
    function GetThumbBrush:TTeeBrush;
    function GetThumbSize:Integer;
    procedure IncArrow(out P:TTrianglePoints); // Not a function due to *.hpp generation bug
    Function IncRectangle:TRect;
    Procedure MouseMove(X,Y:Integer);
    Procedure MouseUp;
    Procedure ProcessClick(const P:TPoint);
    procedure SetArrowBrush(const Value: TTeeBrush);
    procedure SetArrows(const Value: Boolean);
    procedure SetBackColor(const Value: TColor);
    procedure SetBevel(const Value: TPanelBevel);
    procedure SetDisabledArrow(const Value: TTeeBrush);
    procedure SetGradient(const Value: TTeeGradient);
    procedure SetHorizontal(const Value: Boolean);
    procedure SetMinSize(const Value: Integer);
    procedure SetRoundSize(const Value: Integer);
    procedure SetThumb(const Value: TScrollBarThumb);
    procedure SetThumbBrush(const Value: TTeeBrush);
    procedure SetThumbSize(const Value: Integer);
    Function ThumbRectangle:TRect;
  protected
    FHorizontal : Boolean;
    R           : TRect;

    Max         : Integer;
    ThumbLength : Integer;

    Function CalcDelta(const A,B:TCoordinate):Double; virtual;
    procedure ChartEvent(AEvent:TChartToolEvent); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
    function CurrentCount:Integer; virtual;
    function DeltaMain:Integer; virtual;
    procedure Draw;
    class Function GetEditorClass:String; override;
    function GetPosition:Integer; virtual;
    procedure InflateThumbLength(IsInc:Boolean; const ADelta:Double); virtual;
    procedure SetParentChart(const Value: TCustomAxisPanel); override;
    procedure SetPosition(Value:Integer); virtual;
    procedure SetSize(const Value: Integer); virtual;
    function ShouldDraw(var R:TRect):Boolean; virtual;
    function TotalCount:Integer; virtual;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;
    class Function Description:String; override;

    procedure EnsureVisible(AIndex:Integer);

    Function MainRectangle:TRect;
    Function ScrollRectangle:TRect;

    property Horizontal:Boolean read FHorizontal write SetHorizontal default False;
    property Position:Integer read GetPosition write SetPosition;
  published
    property Active;
    property AllowResize:Boolean read FAllowResize write FAllowResize default False;
    property ArrowBrush:TTeeBrush read FArrowBrush write SetArrowBrush;
    property Arrows:Boolean read FArrows write SetArrows default True;
    property AutoRepeat:Boolean read FAutoRepeat write FAutoRepeat default True;
    property BackColor:TColor read GetBackColor write SetBackColor default clTeeScrollBar;
    property Bevel:TPanelBevel read FBevel write SetBevel default bvRaised;
    property Brush;
    property DisabledArrow:TTeeBrush read FDisabledArrow write SetDisabledArrow;
    property DrawStyle:TTeeScrollBarDrawStyle read FDrawStyle
                                              write FDrawStyle;
    property Gradient:TTeeGradient read GetGradient write SetGradient;
    property InitialDelay:Integer read FInitial write FInitial default 250;
    property MinThumbSize:Integer read FMinSize write SetMinSize default 4;
    property Pen;
    property RoundSize:Integer read FRoundSize write SetRoundSize default 0;
    property Size:Integer read FSize write SetSize default DefaultScrollbarSize;
    property Thumb:TScrollBarThumb read FThumb write SetThumb;
    property ThumbBrush:TTeeBrush read GetThumbBrush write SetThumbBrush;
    property ThumbSize:Integer read GetThumbSize write SetThumbSize default 0;

    property OnChangeSize:TNotifyEvent read FOnChangeSize write FOnChangeSize;
    property OnScrolled:TNotifyEvent read FOnScrolled write FOnScrolled;
    property OnSetPosition:TSetPositionEvent read FOnSetPosition write FOnSetPosition;
  end;

  // Note: Its not possible to derive from TTeeCustomToolAxis and have a ScrollBar
  // encapsulated property. Instead, it should be derived directly from TTeeScrollBar
  // and duplicate the Read/Write etc of the Axis property.
  TAxisScrollBarTool=class(TTeeScrollBar)
  private
    FAxis : TChartAxis;
    FMargin : Integer;

    procedure ChangeAxisMinMax(const AMin,AMax:Double);
    procedure PrepareMargin;
    procedure PrepareScrollBar;
    procedure ReadAxis(Reader: TReader);
    procedure SetMargin(const Value:Integer);
    procedure WriteAxis(Writer: TWriter);
  protected
    Procedure ChartEvent(AEvent:TChartToolEvent); override;
    function CurrentCount:Integer; override;
    procedure DefineProperties(Filer: TFiler); override;
    class Function GetEditorClass:String; override;
    procedure InflateThumbLength(IsInc:Boolean; const ADelta:Double); override;
    procedure InternalSetAxis(const Value: TChartAxis);
    procedure Reposition;
    Procedure SetActive(Value:Boolean); override;
    procedure SetAxis(const Value: TChartAxis);
    procedure SetParentChart(const Value: TCustomAxisPanel); override;
    procedure SetPosition(Value:Integer); override;
    procedure SetSize(const Value: Integer); override;
    function ShouldDraw(var R:TRect):Boolean; override;
  public
    Constructor Create(AOwner:TComponent); override;

    procedure Assign(Source:TPersistent); override;

    class Function Description:String; override;
    class procedure GetAxisLimits(const Axis:TChartAxis; out ARange,AMin,AMax:Double);
    class Function LongDescription:String; override;

    property Axis:TChartAxis read FAxis write SetAxis stored False;
  published
    property AllowResize default True;
    property Margin:Integer read FMargin write SetMargin default 5;
  end;

  TAxisScrollLimit=(slNone, slMinimum, slMaximum, slBoth);

  TAxisScrollTool=class(TTeeCustomToolAxis)
  private
    FLimit : TAxisScrollLimit;
    FScrollInverted : Boolean;

    OldX,
    OldY   : Integer;
    InAxis : TChartAxis;
  protected
    procedure SetAxis(const Value: TChartAxis); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
    class Function GetEditorClass:String; override;
  public
    procedure Assign(Source:TPersistent); override;

    class Function Description:String; override;
    class Function LongDescription:String; override;

    property Axis;	
  published
    property Active;
    property Limit:TAxisScrollLimit read FLimit write FLimit default slNone;
    property ScrollInverted:Boolean read FScrollInverted write FScrollInverted default False;
  end;

  TDrawLineHandle=(chNone,chStart,chEnd,chSeries);

  TDrawLineStyle=(dlLine,
                  dlHorizParallel,
                  dlVertParallel,
                  dlRectangle,
                  dlEllipse);

  TDrawLineTool=class;

  // Element of Lines collection in TDrawLineTool class
  TDrawLine=class(TCollectionItem)
  private
    FPen   : TTeePen;
    FStyle : TDrawLineStyle;

    IParent : TDrawLineTool; // [weak] necessary?

    function GetPen:TTeePen;
    function GetX0: Single;
    function GetX1: Single;
    function GetY0: Single;
    function GetY1: Single;
    function IsPenStored: Boolean;
    procedure SetPen(const Value: TTeePen);
    Procedure SetStyle(Value:TDrawLineStyle);
    procedure SetX0(const Value: Single);
    procedure SetX1(const Value: Single);
    procedure SetY0(const Value: Single);
    procedure SetY1(const Value: Single);
  public
    EndPos   : TFloatPoint;
    StartPos : TFloatPoint;

    // Necessary to overcome an Android AV (maybe related to [weak] IParent or codegen bug) 
    Constructor Create(Collection: TCollection); override;

    Constructor CreateXY(Collection:TCollection; Const X0,Y0,X1,Y1:Double);

    Destructor Destroy; override;

    Procedure Assign(Source:TPersistent); override;
    function Clicked(X,Y:Integer; AHandle:TDrawLineHandle; PixelsTolerance:Integer=0):Boolean;
    Procedure DrawHandles;
    Function EndHandle:TRect;
    Function StartHandle:TRect;

    property Parent:TDrawLineTool read IParent;
  published
    property Pen:TTeePen read GetPen write SetPen stored IsPenStored;
    property Style:TDrawLineStyle read FStyle write SetStyle default dlLine;
    property X0:Single read GetX0 write SetX0;
    property Y0:Single read GetY0 write SetY0;
    property X1:Single read GetX1 write SetX1;
    property Y1:Single read GetY1 write SetY1;
  end;

  // Collection of lines (TDrawLine) used in TDrawLineTool
  TDrawLines=class(TOwnedCollection)
  private
    Function Get(Index:Integer):TDrawLine;
    Procedure Put(Index:Integer; Const Value:TDrawLine);
  public
    function AddLine(const X0,Y0,X1,Y1:Double):TDrawLine;
    Function Last:TDrawLine;
    property Line[Index:Integer]:TDrawLine read Get write Put; default;
  end;

  TDrawLineSelecting=procedure( Sender:TDrawLineTool; Line:TDrawLine;
                                var AllowSelect:Boolean) of object;

  // Draw Line tool, use it to allow the end-user to draw, drag, select
  // and move lines.
  TDrawLineTool=class(TTeeCustomToolSeries)
  private
    FButton       : TMouseButton;
    FClick        : Integer;
    FEnableDraw   : Boolean;
    FEnableSelect : Boolean;
    FFullRepaint  : Boolean;
    FLines        : TDrawLines;
    FSelectNew    : Boolean;
    FStyle        : TDrawLineStyle;

    FOnDraggedLine: TNotifyEvent;
    FOnDragLine   : TNotifyEvent;
    FOnNewLine    : TNotifyEvent;
    FOnSelect     : TNotifyEvent;
    FOnSelecting  : TDrawLineSelecting;

    IDrawing  : Boolean;
    IMoved    : Boolean;
    IPoint    : TPoint;
    ISelected : TDrawLine;
    Procedure DrawLine(Const StartPos,EndPos:TPoint; AStyle:TDrawLineStyle);
    Function InternalClicked(X,Y:Integer; AHandle:TDrawLineHandle; ClickTolerance:Integer):TDrawLine; overload;
    Function InternalClicked(X,Y:Integer; AHandle:TDrawLineHandle):TDrawLine; overload;
    Procedure RedrawLine(const ALine:TDrawLine);
    Procedure SetEnableSelect(Value:Boolean);
    Procedure SetFullRepaint(Value:Boolean);
    Procedure SetLines(Const Value:TDrawLines);
    Procedure SetSelected(const Value:TDrawLine);
    function ShouldForceRepaint:Boolean;
  protected
    IHandle   : TDrawLineHandle;

    Procedure ChartEvent(AEvent:TChartToolEvent); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               AButton:TMouseButton;
                               AShift: TShiftState; X, Y: Integer); override;
    Procedure ClipDrawingRegion; virtual;
    class function GetEditorClass: String; override;
  public
    FromPoint : TPoint;
    ToPoint   : TPoint;

    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    Procedure Assign(Source:TPersistent); override;

    Function AxisPoint(Const P:TFloatPoint):TPoint;
    Function Clicked(X,Y:Integer):TDrawLine; overload;
    Function Clicked(X,Y:Integer; ClickTolerance:Integer):TDrawLine; overload; //
    Procedure DeleteSelected;
    Function ScreenPoint(const P:TPoint):TFloatPoint;

    class Function Description:String; override;
    class Function LongDescription:String; override;

    property Selected:TDrawLine read ISelected write SetSelected;
  published
    property Active;
    property Button:TMouseButton read FButton write FButton default {$IFDEF FMX}TMouseButton.{$ENDIF}mbLeft;
    property ClickTolerance:Integer read FClick write FClick default 0;
    property EnableDraw:Boolean read FEnableDraw write FEnableDraw default True;
    property EnableSelect:Boolean read FEnableSelect write SetEnableSelect default True;
    property FullRepaint:Boolean read FFullRepaint write SetFullRepaint default False;
    property Lines:TDrawLines read FLines write SetLines;
    property SelectNewLines:Boolean read FSelectNew write FSelectNew default True;
    property Pen;
    property Series;
    property Style:TDrawLineStyle read FStyle write FStyle default dlLine;

    { events }
    property OnDraggedLine:TNotifyEvent read FOnDraggedLine write FOnDraggedLine;
    property OnDragLine:TNotifyEvent read FOnDragLine write FOnDragLine;
    property OnNewLine:TNotifyEvent read FOnNewLine write FOnNewLine;
    property OnSelect:TNotifyEvent read FOnSelect write FOnSelect;
    property OnSelecting:TDrawLineSelecting read FOnSelecting write FOnSelecting;
  end;

{ This variable can be used to set the default "DrawLine" class used by
  TDrawLineTool when creating new lines }
  TDrawLineClass=class of TDrawLine;

var
  TeeDrawLineClass:TDrawLineClass=TDrawLine;
  {$IFDEF TEEOCX}
  tmpChart : TCustomChart;
  tmpForm : TForm;
  isFullScreenRunning : Boolean;
  {$ENDIF}

type
  TMarkToolMouseAction = (mtmMove, mtmClick);

  TMarksTipTool=class;

  TMarksTipGetText=procedure(Sender:TMarksTipTool; var Text:String) of object;

  // Marks tip tool, use it to display "tips" or "hints" when the end-user
  // moves or clicks the mouse over a series point.
  TMarksTipTool=class(TTeeCustomToolSeries)
  private
    FFormat      : TTextShape;
    FMouseAction : TMarkToolMouseAction;
    FOnCancel    : TNotifyEvent;
    FOnGetText   : TMarksTipGetText;
    FStyle       : TSeriesMarksStyle;
    FSystemHints : Boolean;

    FHidePause,
    FMouseDelay : Integer;

    ITimer : TTimer;

    function GetFont:TTeeFont;
    function GetHideDelay: Integer;
    function GetMouseDelay: Integer;
    procedure SetFont(const Value:TTeeFont);
    procedure SetFormat(const Value:TTextShape);
    procedure SetHideDelay(const Value: Integer);
    Procedure SetMouseAction(Value:TMarkToolMouseAction);
    procedure SetMouseDelay(const Value: Integer);
    procedure TimerCancelHint(Sender:TObject);
  protected
    Function Chart:TCustomChart;
    Procedure ChartEvent(AEvent:TChartToolEvent); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
    class function GetEditorClass: String; override;
    Procedure SetActive(Value:Boolean); override;
    procedure Loaded; override;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;

    procedure HideHint;

    class Function Description:String; override;
    class Function LongDescription:String; override;
  published
    property Active;

    property Font:TTeeFont read GetFont write SetFont stored False;
    property Format:TTextShape read FFormat write SetFormat;
    property HideDelay:Integer read GetHideDelay
                               write SetHideDelay default 2500;

    property MouseAction:TMarkToolMouseAction read FMouseAction
                                               write SetMouseAction default mtmMove;
    property MouseDelay:Integer read GetMouseDelay
                                write SetMouseDelay default 500;

    property Series;
    property Style:TSeriesMarksStyle read FStyle write FStyle default smsLabelOrValue;
    property SystemHints:Boolean read FSystemHints write FSystemHints default {$IFDEF FMX}False{$ELSE}True{$ENDIF};

    property OnCancelHint:TNotifyEvent read FOnCancel write FOnCancel;
    property OnGetText:TMarksTipGetText read FOnGetText write FOnGetText;
  end;

  TNearestToolStyle=(hsNone,hsCircle,hsRectangle,hsDiamond,hsSeries);

  // Nearest tool, use it to display a graphical signal when the mouse is
  // moving near a series point.
  TNearestTool=class(TTeeCustomToolSeries)
  private
    FDistance   : Integer;
    FFullRepaint: Boolean;
    FHint       : Boolean;
    FHintStyle  : TSeriesMarksStyle;
    FLinePen    : TTeePen;
    FSize       : Integer;
    FStyle      : TNearestToolStyle;

    FOnChange   : TNotifyEvent;
    FOnGetHint  : TMarksTipGetText;

    IMouse      : TPoint;

    Function GetDrawLine:Boolean;
    procedure PaintHint;
    procedure SetDrawLine(const Value: Boolean);
    procedure SetLinePen(const Value: TTeePen);
    procedure SetSize(const Value: Integer);
    procedure SetStyle(const Value: TNearestToolStyle);
    function ShouldForceRepaint:Boolean;
    function ZAxisCalc(const Value:Double):Integer;
  protected
    procedure ChartEvent(AEvent: TChartToolEvent); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
    class function GetEditorClass: String; override;
  public
    Point : Integer;

    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;

    class Function GetNearestPoint(Series:TChartSeries; X,Y:Integer; MaxDistance:Integer=0):Integer; overload;
    class Function GetNearestPoint(Series:TChartSeries; X,Y:Integer; IncludeNulls:Boolean; MaxDistance:Integer=0):Integer; overload;

    class Function Description:String; override;
    class Function LongDescription:String; override;
  published
    property Active;
    property Brush;
    property Distance:Integer read FDistance write FDistance default 0;
    property DrawLine:Boolean read GetDrawLine write SetDrawLine default True;
    property FullRepaint:Boolean read FFullRepaint write FFullRepaint default True;

    property Hint:Boolean read FHint write FHint default False;
    property HintStyle:TSeriesMarksStyle read FHintStyle write FHintStyle default smsLabelValue;

    property LinePen:TTeePen read FLinePen write SetLinePen;
    property Pen;
    property Series;
    property Size:Integer read FSize write SetSize default 20;
    property Style:TNearestToolStyle read FStyle write SetStyle default hsCircle;

    property OnChange:TNotifyEvent read FOnChange write FOnChange;
    property OnGetHint:TMarksTipGetText read FOnGetHint write FOnGetHint;
  end;

  TColorLineTool=class;

  // Color band tool, use it to display a coloured rectangle (band)
  // at the specified axis and position.
  TColorBandTool=class(TTeeCustomToolAxis)
  private
    FAllowDrag    : Boolean;
    FNoLimitDrag  : Boolean;
    FColor        : TColor;
    FCursor       : TCursor;
    FDrawBehind   : Boolean;
    FDrawBehindAxes: Boolean;
    FEnd          : Double;
    FOnChanged    : TNotifyEvent;
    FOnClick      : TMouseEvent;
    FOnDragging   : TNotifyEvent;
    FOnResized    : TNotifyEvent;
    FOnResizing   : TNotifyEvent;
    FPicture      : TTeePicture;
    FStart        : Double;
    FTransparency : TTeeTransparency;

    FLineEnd      : TColorLineTool;
    FLineStart    : TColorLineTool;

    IDragged  : Boolean;
    IDragging : Boolean;
    IOld      : Double;

    Function Chart:TCustomAxisPanel;
    procedure DoChanged;
    procedure DragLine(Sender:TColorLineTool);
    procedure EndDragLine(Sender:TColorLineTool);
    function GetEndLinePen: TTeePen;
    function GetGradient: TChartGradient;
    function GetPicture: TTeePicture;
    function GetResizeEnd: Boolean;
    function GetResizeStart: Boolean;
    function GetStartLinePen: TTeePen;
    procedure InitColorLine(const Value:TColorLineTool);
    Function NewColorLine:TColorLineTool;
    procedure PaintBand;
    procedure PictureChanged(Sender:TObject);
    Procedure SetColor(Value:TColor);
    procedure SetDrawBehind(const Value: Boolean);
    procedure SetDrawBehindAxes(const Value: Boolean);
    procedure SetEnd(const Value: Double);
    procedure SetEndLinePen(const Value: TTeePen);
    procedure SetGradient(const Value: TChartGradient);
    procedure SetLines;
    procedure SetPicture(const Value: TTeePicture);
    procedure SetResizeEnd(const Value: Boolean);
    procedure SetResizeStart(const Value: Boolean);
    procedure SetStart(const Value: Double);
    procedure SetStartLinePen(const Value: TTeePen);
    procedure SetTransparency(const Value: TTeeTransparency);
  protected
    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}
    IParent        : TCustomAxisPanel;

    procedure ChartEvent(AEvent: TChartToolEvent); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;

    procedure GetChildren(Proc: TGetChildProc; Root:TComponent); override;
    function GetChildOwner: TComponent; override;
    class function GetEditorClass: String; override;
    procedure Loaded; override;
    procedure ReadState(Reader: TReader); override;
    procedure SetAxis(const Value: TChartAxis); override;
    procedure SetParent(const Value:TCustomAxisPanel);
    procedure SetParentChart(const Value: TCustomAxisPanel); override;
    Function ZPosition:Integer;
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;

    Function BoundsRect:TRect;
    Function Clicked(X,Y:Integer):Boolean;
    class Function Description:String; override;
    class Function LongDescription:String; override;

    property StartLine:TColorLineTool read FLineStart;
    property EndLine:TColorLineTool read FLineEnd;

    property OnChanged:TNotifyEvent read FOnChanged write FOnChanged;

    property Axis;
  published
    property Active;
    property AllowDrag:Boolean read FAllowDrag write FAllowDrag default True;
    property Brush;
    property Color:TColor read FColor write SetColor default {$IFDEF FMX}claWhite{$ELSE}clWhite{$ENDIF};
    property Cursor:TCursor read FCursor write FCursor default crDefault;
    property DrawBehind:Boolean read FDrawBehind write SetDrawBehind default True;
    property DrawBehindAxes:Boolean read FDrawBehindAxes write SetDrawBehindAxes default False;
    property EndValue:Double read FEnd write SetEnd;
    property Gradient:TChartGradient read GetGradient write SetGradient;
    property NoLimitDrag:Boolean read FNoLimitDrag write FNoLimitDrag default True;
    property Pen;
    property Picture:TTeePicture read GetPicture write SetPicture;
    property ResizeEnd:Boolean read GetResizeEnd write SetResizeEnd default False;
    property ResizeStart:Boolean read GetResizeStart write SetResizeStart default False;
    property StartValue:Double read FStart write SetStart;
    property Transparency:TTeeTransparency read FTransparency
                                           write SetTransparency default 0;

    // Deprecated, already persisted at StartLine.Pen and EndLine.Pen
    property EndLinePen:TTeePen read GetEndLinePen write SetEndLinePen;
    property StartLinePen:TTeePen read GetStartLinePen write SetStartLinePen;

    // Events
    property OnClick:TMouseEvent read FOnClick write FOnClick;
    property OnDragging:TNotifyEvent read FOnDragging write FOnDragging;
    property OnResized:TNotifyEvent read FOnResized write FOnResized;
    property OnResizing:TNotifyEvent read FOnResizing write FOnResizing;
  end;

  TGridBandBrush=class(TTeeBrush)
  private
    FTransp    : TTeeTransparency;
    procedure SetTransp(const Value: TTeeTransparency);
  public
    Constructor Create(const OnChangeEvent:TNotifyEvent); override;

    Procedure Assign(Source:TPersistent); override;
  published
    property BackColor default clNone;
    property Transparency:TTeeTransparency read FTransp write SetTransp default 0;
  end;

  // TGridBandTool
  // Draws alternate color bands on axis grid space.
  TGridBandTool=class(TTeeCustomToolAxis)
  private
    FBand1 : TGridBandBrush;
    FBand2 : TGridBandBrush;

    procedure CleanAxis;
    Procedure DrawGrids(Sender:TChartAxis);
    Procedure SetBand1(const Value:TGridBandBrush);
    Procedure SetBand2(const Value:TGridBandBrush);
  protected
    StartPos : Integer;
    EndPos   : Integer;

    class function GetEditorClass: String; override;
    procedure SetAxis(const Value: TChartAxis); override;
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;

    Function BandBackColor(const ABand:TGridBandBrush):TColor;
    class Function Description:String; override;
    class Function LongDescription:String; override;

    property Axis;
  published
    property Active;
    property Band1:TGridBandBrush read FBand1 write SetBand1;
    property Band2:TGridBandBrush read FBand2 write SetBand2;
  end;

  TColorLineStyle=(clCustom,clMaximum,clCenter,clMinimum);

  TColorLineToolOnDrag=procedure(Sender:TColorLineTool) of object;

  // Color line tool, use it to display a vertical or horizontal line
  // at the specified axis and position.
  // The line can be optionally dragged at runtime.
  TColorLineTool=class(TTeeCustomToolAxis)
  private
    FAllowDrag     : Boolean;
    FAnnotation    : TAnnotationTool;
    FAnnValue      : Boolean;
    FClick         : Integer;
    FDragRepaint   : Boolean;
    FDraw3D        : Boolean;
    FDrawBehind    : Boolean;
    FNoLimitDrag   : Boolean;
    FOnBeginDragLine: TColorLineToolOnDrag;
    FOnDragLine    : TColorLineToolOnDrag;
    FOnEndDragLine : TColorLineToolOnDrag;
    FStyle         : TColorLineStyle;
    FValue         : Double;

    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}
    IParent        : TCustomAxisPanel;

    InternalOnEndDragLine : TColorLineToolOnDrag;

    Function Chart:TCustomAxisPanel;
    Procedure DrawColorLine(Back:Boolean);
    function GetAnnotation:TAnnotationTool;
    procedure GetAxisStartEnd(out AStart,AEnd:Integer);
    procedure SetAnnotation(const Value: TAnnotationTool);
    procedure SetAnnValue(const Value: Boolean);
    procedure SetDragRepaint(const Value: Boolean);
    procedure SetDraw3D(const Value: Boolean);
    procedure SetDrawBehind(const Value: Boolean);
    procedure SetStyle(const Value: TColorLineStyle);
    procedure SetValue(const AValue: Double);
    function ShouldForceRepaint:Boolean;
  protected
    IDragging      : Boolean;

    ShowOutsideAxis : Boolean; // (default False). Paint if outside Axis min/max

    Function CalcValue:Double;
    procedure ChartEvent(AEvent: TChartToolEvent); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
    procedure DoEndDragLine; virtual;
    procedure GetChildren(Proc: TGetChildProc; Root:TComponent); override;
    function GetChildOwner: TComponent; override;
    class function GetEditorClass: String; override;
    procedure InternalDrawLine(Back:Boolean);
    procedure ReadState(Reader: TReader); override;
    procedure SetParent(Value:TCustomAxisPanel); virtual;
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
  public
    Constructor Create(AOwner:TComponent); override;

    Procedure Assign(Source:TPersistent); override;

    Function Clicked(x,y:Integer):Boolean;
    class Function Description:String; override;
    class Function LongDescription:String; override;

    Function LimitValue(const AValue:Double):Double; // moved from private

    property Axis;
  published
    property Active;
    property Annotation:TAnnotationTool read GetAnnotation write SetAnnotation;
    property AnnotationValue:Boolean read FAnnValue write SetAnnValue default False;
    property AllowDrag:Boolean read FAllowDrag write FAllowDrag default True;
    property ClickTolerance:Integer read FClick write FClick default TeeClickTolerance;
    property DragRepaint:Boolean read FDragRepaint write SetDragRepaint default False;
    property Draw3D:Boolean read FDraw3D write SetDraw3D default True;
    property DrawBehind:Boolean read FDrawBehind write SetDrawBehind default False;
    property NoLimitDrag:Boolean read FNoLimitDrag write FNoLimitDrag default False;
    property Pen;
    property Style:TColorLineStyle read FStyle write SetStyle default clCustom;
    property Value:Double read FValue write SetValue;

    property OnBeginDragLine:TColorLineToolOnDrag read FOnBeginDragLine write FOnBeginDragLine;
    property OnDragLine:TColorLineToolOnDrag read FOnDragLine write FOnDragLine;
    property OnEndDragLine:TColorLineToolOnDrag read FOnEndDragLine write FOnEndDragLine;
  end;

  // Rotate tool, use it to allow the end-user to do 3D rotation of the chart
  // at runtime by dragging the chart using the specified mouse button.

  TRotateToolStyles = ( rsAll, rsRotation, rsElevation );

  TRotateTool=class(TTeeCustomTool)
  private
    FButton    : TMouseButton;
    FCursor    : TCursor;
    FInertia   : Integer;
    FInverted  : Boolean;
    FOnRotate  : TNotifyEvent;
    FSpeed     : Integer;
    FStyle     : TRotateToolStyles;

    IDragging  : Boolean;
    IFirstTime : Boolean;
    IOldRepaint: Boolean;
    IOldX      : Integer;
    IOldY      : Integer;
    ITimer     : TTimer;
    IDifX      : Double;
    IDifY      : Double;
    IOldCursor : TCursor;

    procedure DoTimer(Sender: TObject);
    procedure DoRotation(const IDifX,IDifY:Double);
  protected
    procedure ChartEvent(AEvent: TChartToolEvent); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               AButton:TMouseButton;
                               AShift: TShiftState; X, Y: Integer); override;
    class function GetEditorClass: String; override;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;

    class Function Description:String; override;
    class Function LongDescription:String; override;

    class function ElevationChange(FullRotation:Boolean; const AAngle,AChange:Double):Double;
    procedure Rotate(const RotationDegrees,ElevationDegrees:Double);
    class function RotationChange(FullRotation:Boolean; const AAngle,AChange:Double):Double;
  published
    property Active;
    property Button:TMouseButton read FButton write FButton default {$IFDEF FMX}TMouseButton.{$ENDIF}mbLeft;
    property Cursor:TCursor read FCursor write FCursor default crHandPoint;
    property Inertia:Integer read FInertia write FInertia default 0;
    property Inverted:Boolean read FInverted write FInverted default False;
    property Pen;
    property Speed:Integer read FSpeed write FSpeed default 50; { % }
    property Style:TRotateToolStyles read FStyle write FStyle default rsAll;

    property OnRotate:TNotifyEvent read FOnRotate write FOnRotate;
  end;

  // Chart Image tool, use it to display a picture using the specified
  // Series axes as boundaries. When the axis are zoomed or scrolled,
  // the picture will follow the new boundaries.
  TChartImageTool=class(TTeeCustomToolSeries)
  private
    FPicture : TTeePicture;
    procedure SetPicture(const Value: TTeePicture);
  protected
    procedure ChartEvent(AEvent: TChartToolEvent); override;
    class function GetEditorClass: String; override;
    procedure SetSeries(const Value: TChartSeries); override;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;

    class Function Description:String; override;
    class Function LongDescription:String; override;
  published
    property Active;
    property Pen;
    property Picture:TTeePicture read FPicture write SetPicture;
    property Series;
  end;

  TRectangleTool=class(TAnnotationTool)
  private
    FAllowDrag   : Boolean;
    FAllowResize : Boolean;
    FOnDragged   : TNotifyEvent;
    FOnDragging  : TNotifyEvent;
    FOnResized   : TNotifyEvent;
    FOnResizing  : TNotifyEvent;

    P            : TPoint;
    IDrag        : Boolean;
    IEdge        : Integer;

    function GetResizing:Boolean;
  protected
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
  public
    Constructor Create(AOwner:TComponent); override;

    procedure Assign(Source:TPersistent); override;

    Function Bitmap(ChartOnly:Boolean=False):TBitmap;
    Function ClickedEdge(x,y:Integer):Integer;
    class Function Description:String; override;
    class Function LongDescription:String; override;

    property Dragging:Boolean read IDrag;
    property Resizing:Boolean read GetResizing;
  published
    property AllowDrag:Boolean read FAllowDrag write FAllowDrag default True;
    property AllowResize:Boolean read FAllowResize write FAllowResize default True;
    property AutoSize default False;
    property Cursor default crHandPoint;
    property PositionUnits default muPixels;

    property OnDragged:TNotifyEvent read FOnDragged write FOnDragged;
    property OnDragging:TNotifyEvent read FOnDragging write FOnDragging;
    property OnResized:TNotifyEvent read FOnResized write FOnResized;
    property OnResizing:TNotifyEvent read FOnResizing write FOnResizing;
  end;

  TClipSeriesTool=class(TTeeCustomToolSeries)
  protected
    Procedure SeriesEvent(AEvent:TChartToolEvent; ASeries:TChartSeries); override;
  public
    class Function Description:String; override;
    class Function LongDescription:String; override;
  published
    property Active;
    property Series;
  end;

  TFullScreenTool=class(TTeeCustomTool)
  private
    FKey      : Word;
    FOnReset  : TNotifyEvent;

    {$IFNDEF TEEOCX}
    OldAlign  : {$IFDEF FMX}TAlignLayout{$ELSE}TAlign{$ENDIF};
    {$ENDIF}
    OldParent : {$IFDEF FMX}TFmxObject{$ELSE}TWinControl{$ENDIF};
    {$IFNDEF TEEOCX}
    OldRChart : TRect;
    {$ENDIF}
    IFreeForm : Boolean;

    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    {$IFNDEF FMX}
    procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    {$ENDIF}
    procedure RemoveFullScreen;
  protected
    Procedure SetActive(Value:Boolean); override;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    class Function Description:String; override;
    class Function LongDescription:String; override;
    class function ScreenDesktopRect:TRect;
  published
    property Active default False;
    property ReleaseKey:Word read FKey write FKey default TeeKey_ESCAPE;

    // Events
    property OnReset:TNotifyEvent read FOnReset write FOnReset;
  end;

  TRepaintMonitorMode=(rmFrames, rmMsec, rmMemory, rmTotalMemory);

  TRepaintMonitor=class(TRectangleTool)
  private
    FChart       : TChart;
    FInterval    : Integer;
    FMode        : TRepaintMonitorMode;

    FTotalFrames,
    FMonitor     : Integer;
    FMonitorTime : Single;
    FMonitorText : String;

    IBitmap : TBitmap;
    ILastMsec,
    ILastSecond : Single;
    IMsec,
    ILastMonitor : Integer;
    IMemory : Cardinal;

    function CalcText:Cardinal;
    function GetChart:TChart;
    function GetShowChart:Boolean;
    procedure SetMode(const Value:TRepaintMonitorMode);
    procedure SetShowChart(const Value:Boolean);
  protected
    Procedure ChartEvent(AEvent:TChartToolEvent); override;
    class Function GetEditorClass:String; override;
    function GetText:String; override;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    class Function Description:String; override;
    class Function LongDescription:String; override;

    property Chart:TChart read GetChart;
  published
    property Interval:Integer read FInterval write FInterval default 1000;
    property Mode:TRepaintMonitorMode read FMode write SetMode default rmFrames;
    property ShowChart:Boolean read GetShowChart write SetShowChart default True;
  end;

  TTeeCombo=class(TAnnotationTool)
  private
    FAutoHide : Boolean;
    FExpanded : Boolean;

    FItemIndex : Integer;
    FItems : TStringList;
    FOnChange: TNotifyEvent;

    IWidth,
    IHeight,
    ILastSelected,
    IUnderMouse : Integer;

    procedure CheckItems(Sender:TObject);
    procedure RecalcHeight;
    procedure SetAutoHide(const Value: Boolean);
    procedure SetItems(const Value: TStringList);
    procedure SetItemIndex(const Value: Integer);
  protected
    TopIndex : Integer;

    procedure ChartKeyEvent(AEvent: TChartKeyEvent;
                     var Key: Word; var Key1: Char; Shift: TShiftState); override;
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
    function ClickedItem(X,Y:Integer):Integer;
    Procedure DoDrawText(const AParent:TCustomAxisPanel); override;
    class Function GetEditorClass:String; override;
    function ItemHeight:Integer;
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;

    procedure Collapse;
    function Count:Integer;
    procedure Expand;

    class Function Description:String; override;
    class Function LongDescription:String; override;

    property Expanded:Boolean read FExpanded;
  published
    property AutoHide:Boolean read FAutoHide write SetAutoHide default False;
    property ItemIndex:Integer read FItemIndex write SetItemIndex default -1;
    property Items:TStringList read FItems write SetItems;

    property OnChange:TNotifyEvent read FOnChange write FOnChange;
  end;

implementation

Uses
  Math,
  {$IFDEF FMX}
  FMXTee.Constants, FMXTee.ProConstants, FMX.Forms, FMXTee.Html, FMX.Platform
  {$ELSE}
  TeeConst, TeeProCo,
  {$IFDEF TEEOCX}TeeStore,{$ENDIF}
  TeeHtml
  {$ENDIF}
  ;

{$IFDEF FMX}
const
  clWhite=claWhite;
  clBlack=claBlack;
  clSilver=claSilver;
  mbLeft=TMouseButton.mbLeft;
{$ENDIF}

// For old FMX versions:
// FMX: Changing Cursor delays invalidating a lot:
// FMX, bad Cursor handling forces to invalidate here:
// See: QC119248

{$IFDEF FMX}
{$IFDEF D21}
{$DEFINE TEESETCURSOR}
{$ENDIF}
{$ELSE}
{$DEFINE TEESETCURSOR}
{$ENDIF}

{ TCursorTool }

Constructor TCursorTool.Create(AOwner:TComponent);
begin
  inherited;
  FClick:=TeeClickTolerance;
  FStyle:=cssBoth;
  IOldSnap:=-1;
  FScopeSize:=4;
end;

procedure TCursorTool.Assign(Source:TPersistent);
begin
  if Source is TCursorTool then
  with TCursorTool(Source) do
  begin
    Self.AxisAnnotation:=FAxisAnnotation;
    Self.FUseChartRect:=FUseChartRect;
    Self.IXValue:=IXValue;
    Self.IYValue:=IYValue;
    Self.FClick:=FClick;
    Self.FFollowMouse:=FFollowMouse;
    Self.FFullRepaint:=FFullRepaint;
    Self.FHorizSize:=FHorizSize;
    Self.FScopeSize:=FScopeSize;
    Self.FScopeStyle:=FScopeStyle;
    Self.FSnap:=FSnap;
    Self.FSnapStyle:=FSnapStyle;
    Self.FStyle:=FStyle;
    Self.FUseSeriesZ:=FUseSeriesZ;
    Self.FVertSize:=FVertSize;
  end;

  inherited;
end;

Procedure TCursorTool.CalcValuePositions(X,Y:Integer);
begin
  if UseSeriesZ then
     ParentChart.Canvas.Calculate2DPosition(x,y,Z);

  Case IDragging of
    ccVertical  : IXValue:=GetHorizAxis.CalcPosPoint(X);
    ccHorizontal: IYValue:=GetVertAxis.CalcPosPoint(Y);
  else
  begin
    IXValue:=GetHorizAxis.CalcPosPoint(X);
    IYValue:=GetVertAxis.CalcPosPoint(Y);
  end;
  end;
end;

Procedure TCursorTool.CalcScreenPositions;
var tmpSnap : Integer;
begin
  if not IAxisValid then
     Exit;
     
  if IValidXPoint then
     IPoint.X:=GetHorizAxis.CalcPosValue(IXValue);

  if IValidYPoint then
     IPoint.Y:=GetVertAxis.CalcPosValue(IYValue);

  if (not IValidXPoint) or (not IValidYPoint) then
  begin
    if not IValidXPoint then
    begin
      if FollowMouse then
         IPoint.X:=0
      else
         IPoint.X:=GetHorizAxis.CenterPosition;

      // Set flag at first recalc:
      IValidXPoint:=True;
    end;

    if not IValidYPoint then
    begin
      if FollowMouse then
         IPoint.Y:=0
      else
         IPoint.Y:=GetVertAxis.CenterPosition;

      // Set flag at first recalc:
      IValidYPoint:=True;
    end;

    {$IFDEF FMX}
    CalcValuePositions(Round(IPoint.X),Round(IPoint.Y));
    {$ELSE}
    CalcValuePositions(IPoint.X,IPoint.Y);
    {$ENDIF}

    tmpSnap:=SnapToPoint;

    // Recalc IPoint again, as it could be SnapToPoint has changed
    // IXValue or IYValue values:
    CalcScreenPositions;

    Changed(tmpSnap);
  end;
end;

Procedure TCursorTool.Changed(SnapPoint:Integer);
begin
  if Assigned(FOnChange) then
     FOnChange(Self,{$IFDEF FMX}Round{$ENDIF}(IPoint.X),
                    {$IFDEF FMX}Round{$ENDIF}(IPoint.Y),
                    IXValue,IYValue,Series,SnapPoint);
end;

class Function TCursorTool.GetEditorClass:String;
begin
  result:='TCursorToolEditor';
end;

class Function TCursorTool.Description:String;
begin
  result:=TeeMsg_CursorTool;
end;

class Function TCursorTool.LongDescription:String;
begin
  result:=TeeMsg_CursorToolDesc;
end;

Function TeeGetFirstLastSeries(Series:TChartSeries;
                               out AMin,AMax:Integer):Boolean;
begin
  AMin:=Series.FirstValueIndex;
  if AMin<0 then AMin:=0;

  AMax:=Series.LastValueIndex;
  if AMax<0 then AMax:=Series.Count-1
  else
  if AMax>=Series.Count then AMax:=Series.Count-1;

  result:=(Series.Count>0) and (AMin<=Series.Count) and (AMax<=Series.Count);
end;

Function TCursorTool.NearestPoint(AStyle:TCursorToolStyle;
                                  out Difference:Double):Integer;
var t      : Integer;
    tmpDif : Double;
    tmpMin : Integer;
    tmpMax : Integer;
    tmp    : Double;
begin
  result:=-1;
  Difference:=-1;

  if TeeGetFirstLastSeries(Series,tmpMin,tmpMax) then
  for t:=tmpMin to tmpMax do
  begin

    if FSnapStyle=ssDefault then
    begin
      case AStyle of
        cssHorizontal: tmpDif:=Abs(IYValue-Series.YValues.Value[t]);
        cssVertical  : tmpDif:=Abs(IXValue-Series.XValues.Value[t]);
      else
        if Series.HasZValues then
           tmp:=Series.GetYValueList('Z').Value[t] // ie: ColorGrid
        else
           tmp:=Series.YValues.Value[t];

        tmpDif:=Sqrt(Sqr(IXValue-Series.XValues.Value[t])+Sqr(IYValue-tmp));
      end;
    end
    else
    if FSnapStyle=ssHorizontal then
       tmpDif:=Abs(IYValue-Series.YValues.Value[t])
    else
       tmpDif:=Abs(IXValue-Series.XValues.Value[t]);

    if (Difference=-1) or (tmpDif<Difference) then
    begin
      Difference:=tmpDif;
      result:=t;
    end;
  end;
end;

Function TCursorTool.SnapToPoint:Integer;
var Difference : Double;
begin
  if Assigned(Series) and FSnap then result:=NearestPoint(FStyle,Difference)
                                else result:=-1;

  if result<>-1 then
  Case FStyle of
    cssHorizontal: IYValue:=Series.YValues.Value[result];
    cssVertical  : IXValue:=Series.XValues.Value[result];
  else
    begin
      IXValue:=Series.XValues.Value[result];
      IYValue:=Series.YValues.Value[result]
    end;
  end;
end;

function CreateAnnotation(const AOwner:TComponent; const AParent:TCustomAxisPanel):TAnnotationTool;
begin
  result:=TAnnotationTool.Create(AOwner);

  result.Shape.Visible:=False; // <-- hidden by default
  result.Shape.Shadow.Visible:=False;
  result.Shape.Alignment:=taCenter;
  result.TextAlignment:=taCenter;

  if Assigned(AParent) then
  begin
    AParent.AutoRepaint:=False;
    result.InternalSetParent(AParent);
    AParent.AutoRepaint:=True;
  end;
end;

function TCursorTool.GetAxisAnnotation:TAnnotationTool;
begin
  if not Assigned(FAxisAnnotation) then
     FAxisAnnotation:=CreateAnnotation(Self,ParentChart);

  result:=FAxisAnnotation;
end;

Function TCursorTool.GetAxisRect:TRect;
{$IFNDEF NEXTGEN}
var tmp : TChartAxis;
{$ENDIF}
begin
  if (UseChartRect) or (Series=nil) then
     result:=ParentChart.ChartRect
  else
  begin
    {$IFDEF NEXTGEN}
    result:=ParentChart.ChartRect;
    {$ELSE}
    tmp:=GetHorizAxis;
    result.Left:=tmp.IStartPos;  // <-- Obscure bug SIGFAULT 10 here Android and iOS
    result.Right:=tmp.IEndPos;

    tmp:=GetVertAxis;
    result.Top:=tmp.IStartPos;
    result.Bottom:=tmp.IEndPos;
    {$ENDIF}
  end;

  if Assigned(FOnGetAxisRect) then
     FOnGetAxisRect(Self,result);
end;

Function TCursorTool.Z:Integer;
var tmpIndex : Integer;
    tmpZList : TChartValueList;
begin
  if UseSeriesZ and Assigned(Series) then
  begin
     // 3D Series ? Snap -> CalcZPos
    if Snap then
    begin
      result:=Series.MiddleZ;

      tmpZList:=Series.GetYValueList('Z');

      if Assigned(tmpZList) then
      begin
        tmpIndex:=SnapToPoint;

        if tmpIndex<>-1 then
           result:=Series.ParentChart.Axes.Depth.CalcYPosValue(tmpZList.Value[tmpIndex]);
      end
    end
    else
      result:=Series.StartZ
  end
  else
     result:=Round(CustomZ);
end;

function IsVisible(const Annotation:TAnnotationTool):Boolean;
begin
  result:=Assigned(Annotation) and Annotation.Visible and Annotation.Shape.Visible;
end;

procedure TCursorTool.DrawScope(Draw3D:Boolean; X,Y:Integer);
var tmp : TRect;
    tmpCanvas : TCanvas3D;
begin
  tmp:=RectFromCenter(X,Y,ScopeSize);

  tmpCanvas:=ParentChart.Canvas;

  if Draw3D then
  begin
    if ScopeStyle=scsCamera then
    begin
      tmpCanvas.Rectangle(tmp.Left-HorizSize,tmp.Top-VertSize,
                          tmp.Right+HorizSize,tmp.Bottom+VertSize,Z);

      tmpCanvas.HorizLine3D(tmp.Left,tmp.Right,Y,Z);
      tmpCanvas.VertLine3D(X,tmp.Top,tmp.Bottom,Z);
    end
    else
    begin
      tmpCanvas.HorizLine3D(tmp.Left-HorizSize,tmp.Left,Y,Z);
      tmpCanvas.HorizLine3D(tmp.Right,tmp.Right+HorizSize,Y,Z);

      tmpCanvas.VertLine3D(X,tmp.Top-VertSize,tmp.Top,Z);
      tmpCanvas.VertLine3D(X,tmp.Bottom,tmp.Bottom+VertSize,Z);

      case ScopeStyle of
        scsRectangle: tmpCanvas.Rectangle(tmp,Z);
        scsCircle   : tmpCanvas.EllipseWithZ(tmp.Left,tmp.Top,tmp.Right,tmp.Bottom,Z);
        scsDiamond  : tmpCanvas.Diamond(tmp,Z,Z);
      end;
    end;
  end
  else
  begin
    if ScopeStyle=scsCamera then
    begin
      tmpCanvas.Rectangle(tmp.Left-HorizSize,tmp.Top-VertSize,
                          tmp.Right+HorizSize,tmp.Bottom+VertSize);

      tmpCanvas.DoHorizLine(tmp.Left,tmp.Right,Y);
      tmpCanvas.DoVertLine(X,tmp.Top,tmp.Bottom);
    end
    else
    begin
      tmpCanvas.DoHorizLine(tmp.Left-HorizSize,tmp.Left,Y);
      tmpCanvas.DoHorizLine(tmp.Right,tmp.Right+HorizSize,Y);

      tmpCanvas.DoVertLine(X,tmp.Top-VertSize,tmp.Top);
      tmpCanvas.DoVertLine(X,tmp.Bottom,tmp.Bottom+VertSize);

      case ScopeStyle of
        scsRectangle: tmpCanvas.Rectangle(tmp);
        scsCircle   : tmpCanvas.Ellipse(tmp);
        scsDiamond  : tmpCanvas.Diamond(tmp,Z,Z);
      end;
    end;
  end;
end;

Procedure TCursorTool.DrawCursorLines(Draw3D:Boolean; X,Y:Integer);

  Procedure DrawHorizontal(const ABounds,ARect:TRect);
  var tmpCanvas: TCanvas3D;
  begin
    // Avoid painting cursor line outside rectangle (ie: when scrolling a chart)
    if (Y>=ABounds.Top) and (Y<=ABounds.Bottom) then
    begin
      tmpCanvas:=ParentChart.Canvas;

      if Draw3D then
         tmpCanvas.HorizLine3D(ARect.Left,ARect.Right,Y,Z)
      else
         tmpCanvas.DoHorizLine(ARect.Left,ARect.Right,Y);
    end;
  end;

  Procedure DrawVertical(const ABounds,ARect:TRect);
  var tmpCanvas: TCanvas3D;
  begin
    // Avoid painting cursor line outside rectangle (ie: when scrolling a chart)
    if (X>=ABounds.Left) and (X<=ABounds.Right) then
    begin
      tmpCanvas:=ParentChart.Canvas;

      if Draw3D then
         tmpCanvas.VertLine3D(X,ARect.Top,ARect.Bottom,Z)
      else
         tmpCanvas.DoVertLine(X,ARect.Top,ARect.Bottom);
    end;
  end;

  procedure DrawBoth(const ABounds,ARect:TRect);
     Begin
    DrawHorizontal(ABounds,ARect);
    DrawVertical(ABounds,ARect);
  end;

// Note: These two rect variables are here instead of declared before the above
// procedures that use them, because a strange Android codegen (SIGFAULT 10)
var tmpChartRect,
    tmpR : TRect;
begin
  if FStyle=cssScopeOnly then
  begin
    if ScopeStyle<>scsEmpty then
       DrawScope(Draw3D,X,Y);
  end
  else
  begin
    tmpChartRect:=GetAxisRect;
    tmpR:=MouseRectangle(tmpChartRect,X,Y,True);

    case FStyle of
      cssHorizontal: DrawHorizontal(tmpChartRect,tmpR);
      cssVertical  : DrawVertical(tmpChartRect,tmpR);
      cssScope     : begin
                       if ScopeStyle<>scsEmpty then
                          DrawScope(Draw3D,X,Y);

                       DrawBoth(tmpChartRect,tmpR);
                     end;
    else
      DrawBoth(tmpChartRect,tmpR);
    end;
  end;
end;

procedure TCursorTool.RedrawCursor;

  procedure DrawAnnotation;
  var tmpR : TRect;
      tmp: Integer;
  begin
    if Style=cssHorizontal  then
       FAxisAnnotation.Text:=GetVertAxis.LabelValue(IYValue)
    else
       FAxisAnnotation.Text:=GetHorizAxis.LabelValue(IXValue);

    tmpR:=FAxisAnnotation.GetBoundsParent(ParentChart);

    if Style=cssHorizontal then
    begin
      FAxisAnnotation.Top:={$IFDEF FMX}Round{$ENDIF}(IPoint.Y-((tmpR.Bottom-tmpR.Top) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF}));

      tmp:=GetVertAxis.PosAxis;
      if GetVertAxis.Axis.Visible then
         Inc(tmp,{$IFDEF FMX}Round{$ENDIF}(GetVertAxis.Axis.Width));

      FAxisAnnotation.Left:={$IFDEF FMX}Round{$ENDIF}(tmp-(tmpR.Right-tmpR.Left));
    end
    else
    begin
      FAxisAnnotation.Left:={$IFDEF FMX}Round{$ENDIF}(IPoint.X-((tmpR.Right-tmpR.Left) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF}));

      tmp:=GetHorizAxis.PosAxis;
      if GetHorizAxis.Axis.Visible then
         Inc(tmp,{$IFDEF FMX}Round{$ENDIF}(GetHorizAxis.Axis.Width));

      FAxisAnnotation.Top:={$IFDEF FMX}Round{$ENDIF}(tmp);
    end;

    FAxisAnnotation.DoDrawText(ParentChart);
  end;

var tmpAnnotation : Boolean;
    tmpCanvas     : TCanvas3D;
begin
  if Assigned(ParentChart) then
  begin
    tmpAnnotation:=IsVisible(FAxisAnnotation);

    tmpCanvas:=ParentChart.Canvas;

    tmpCanvas.Brush.Style:=bsClear;

    if ParentChart.Monochrome then
       tmpCanvas.Pen.Color:={$IFDEF FMX}TAlphaColors.LtGray{$ELSE}clLtGray{$ENDIF}  //TV52012044
    else
    begin
      if ShouldForceRepaint then
         tmpCanvas.AssignVisiblePenColor(Pen,Pen.Color)
      else
         TeePrepareXorPen(ParentChart,Pen);
    end;

    DrawCursorLines(ParentChart.View3D,{$IFDEF FMX}Round{$ENDIF}(IPoint.X),{$IFDEF FMX}Round{$ENDIF}(IPoint.Y));

    if tmpAnnotation then
       DrawAnnotation;

    tmpCanvas.Pen.Mode:=pmCopy;
  end;
end;

function TCursorTool.MouseRectangle(const R:TRect; const X,Y:Integer; UseSize:Boolean):TRect;
begin
  result:=R;

  if UseSize then
  begin
    if HorizSize<>0 then
    begin
      result.Left:=X-HorizSize;
      result.Right:=X+HorizSize;
    end;

    if VertSize<>0 then
    begin
      result.Top:=Y-VertSize;
      result.Bottom:=Y+VertSize;
    end;
  end;
end;

Function TCursorTool.InMouseRectangle(x,y:Integer; UseSize:Boolean=False):Boolean;
begin
  if UseSeriesZ then
     ParentChart.Canvas.Calculate2DPosition(x,y,Z);

  result:=PointInRect(MouseRectangle(GetAxisRect,x,y,UseSize),x,y);
end;

// Use "xor" only when the canvas supports it, we're not in "fullrepaint" mode,
// the canvas supports XOR mode, and there is no annotation visible:
function TCursorTool.ShouldForceRepaint:Boolean;
begin
  result:=FullRepaint or (not ParentChart.Canvas.SupportsXORMode) or
          IsVisible(FAxisAnnotation);
end;

Procedure TCursorTool.ChartMouseEvent( AEvent: TChartMouseEvent;
                                       Button:TMouseButton;
                                       Shift: TShiftState; X, Y: Integer);

  Procedure MouseMove;
  var tmpSnap : Integer;
      tmp     : TCursorClicked;
      tmpXor  : Boolean;
  begin
    { if mouse button is pressed and dragging then... }
    if ((IDragging<>ccNone) or FollowMouse) and InMouseRectangle(X,Y) then
    begin
      tmpXor:=not ShouldForceRepaint;

      if tmpXor then
      begin
        {$IFDEF FMX}
        ParentChart.Canvas.ReferenceCanvas.BeginScene;
        ParentChart.Canvas.ReferenceCanvas.SetMatrix(ParentChart.AbsoluteMatrix);
        {$ENDIF}
        RedrawCursor;
      end;

      CalcValuePositions(X,Y);

      tmpSnap:=SnapToPoint;  { snap to the nearest point of SnapSeries }

      { draw again the cursor at the new position }
      CalcScreenPositions;

      if tmpXor then
      begin
        RedrawCursor;
        {$IFDEF FMX}
        ParentChart.Canvas.ReferenceCanvas.EndScene;
        {$ENDIF}
      end
      else
        Repaint;

      Changed(tmpSnap);

      if Assigned(FOnSnapChange) and (IOldSnap<>tmpSnap) then
         FOnSnapChange(Self,IPoint.{$IFDEF FMX}Round.{$ENDIF}X,
                            IPoint.{$IFDEF FMX}Round.{$ENDIF}Y,
                            IXValue,IYValue,Series,tmpSnap);

      IOldSnap:=tmpSnap;
    end
    else
    begin  { mouse button is not pressed, user is not dragging the cursor }
           { change the mouse cursor when passing over the Cursor }
      tmp:=Clicked(x,y);

      case tmp of
         ccHorizontal : ParentChart.Cursor:=crVSplit;
         ccVertical   : ParentChart.Cursor:=crHSplit;
         ccBoth       : ParentChart.Cursor:=crSizeAll;
      end;

      ParentChart.CancelMouse:=tmp<>ccNone;
    end;
  end;

begin
  Case AEvent of
      cmeUp: IDragging:=ccNone;

    cmeMove: MouseMove;

    cmeDown: if not FFollowMouse then
             begin
               IDragging:=Clicked(x,y);

               if IDragging<>ccNone then
                  ParentChart.CancelMouse:=True;
             end;
  end;
end;

Function TCursorTool.Clicked(x,y:Integer):TCursorClicked;
var tmp : TPoint;
begin
  result:=ccNone;

  if InMouseRectangle(x,y,True) then
  begin
    tmp:=IPoint;

    if UseSeriesZ then
       ParentChart.Canvas.Calculate2DPosition(x,y,Z);

    if ((FStyle=cssBoth) or (FStyle=cssScope) or (FStyle=cssScopeOnly)) and
        (Abs(Y-tmp.Y)<=ClickTolerance) and
        (Abs(X-tmp.X)<=ClickTolerance) then
       result:=ccBoth
    else
    if (FStyle<>cssVertical) and (FStyle<>cssScopeOnly) and
       (Abs(Y-tmp.Y)<=ClickTolerance) then
         result:=ccHorizontal
    else
    if (FStyle<>cssHorizontal) and (FStyle<>cssScopeOnly) and
       (Abs(X-tmp.X)<=ClickTolerance) then
         result:=ccVertical
  end;
end;

// Necessary to persist AxisAnnotation
procedure TCursorTool.GetChildren(Proc: TGetChildProc; Root:TComponent);
begin
  inherited;

  if Assigned(FAxisAnnotation) then
     Proc(FAxisAnnotation);
end;

// Necessary to persist AxisAnnotation
function TCursorTool.GetChildOwner: TComponent;
begin
  result:=Self;
end;

function ShouldFreeChild(const Value:TComponent):Boolean;
begin
  result:=Assigned(Value) and
         (not (csLoading in Value.ComponentState))
         and (not (csAncestor in Value.ComponentState));
end;

// Necessary to persist Annotation
procedure TCursorTool.ReadState(Reader: TReader);
begin
  inherited;

  if ShouldFreeChild(FAxisAnnotation) then
     FreeAndNil(FAxisAnnotation);

  if (ComponentCount>0) and (Components[0] is TAnnotationTool) then
  begin
    FAxisAnnotation:=TAnnotationTool(Components[0]);
    FAxisAnnotation.InternalSetParent(ParentChart);
  end;
end;

procedure TCursorTool.SetAxisAnnotation(const Value:TAnnotationTool);
begin
  if Assigned(Value) then
     AxisAnnotation.Assign(Value)
  else
     FreeAndNil(FAxisAnnotation);
end;

Procedure TCursorTool.SetFullRepaint(const Value:Boolean);
begin
  SetBooleanProperty(FFullRepaint,Value);
end;

Procedure TCursorTool.SetHorizSize(const Value:Integer);
begin
  SetIntegerProperty(FHorizSize,Value);
end;

Procedure TCursorTool.SetScopeSize(const Value:Integer);
begin
  SetIntegerProperty(FScopeSize,Value);
end;

Procedure TCursorTool.SetScopeStyle(const Value:TScopeCursorStyle);
begin
  if FScopeStyle<>Value then
  begin
    FScopeStyle:=Value;
    Repaint;
  end;
end;

Procedure TCursorTool.SetVertSize(const Value:Integer);
begin
  SetIntegerProperty(FVertSize,Value);
end;

Procedure TCursorTool.SetStyle(Value:TCursorToolStyle);
begin
  if FStyle<>Value then
  begin
    FStyle:=Value;
    SnapToPoint;
    Repaint;
  end;
end;

procedure TCursorTool.SetSeries(const Value: TChartSeries);
begin
  inherited;

  IOldSnap:=-1;

  if Assigned(Series) and (not (csLoading in ComponentState)) then
  begin
    SnapToPoint;
    Repaint;
  end;
end;

procedure TCursorTool.SetParentChart(const Value: TCustomAxisPanel);
begin
  inherited;

  if Assigned(FAxisAnnotation) then
     FAxisAnnotation.InternalSetParent(ParentChart);

  Repaint;
end;

procedure TCursorTool.ChartEvent(AEvent: TChartToolEvent);
begin
  inherited;

  if AEvent=cteAfterDraw then
  begin
    IAxisValid:=True;
    CalcScreenPositions;
    RedrawCursor;
  end;
end;

procedure TCursorTool.SetXValue(const Value: Double);
begin
  IValidXPoint:=True;

  if IXValue<>Value then
  begin
    IXValue:=Value;
    IOldSnap:=-1;
    DoChange;
  end;
end;

procedure TCursorTool.DoChange;
begin
  CalcScreenPositions;
  Changed(-1);
  Repaint;
end;

procedure TCursorTool.SetYValue(const Value: Double);
begin
  IValidYPoint:=True;

  if IYValue<>Value then
  begin
    IYValue:=Value;
    IOldSnap:=-1;
    DoChange;
  end;
end;

procedure TCursorTool.SetUseChartRect(const Value: Boolean);
begin
  SetBooleanProperty(FUseChartRect,Value);
end;

procedure TCursorTool.SetUseSeriesZ(const Value: Boolean);
begin
  SetBooleanProperty(FUseSeriesZ,Value);
end;

{ TDragMarksTool }
Constructor TDragMarksTool.Create(AOwner:TComponent);
begin
  inherited;
  Index:=TeeNoPointClicked;
end;

class function TDragMarksTool.Description: String;
begin
  result:=TeeMsg_DragMarksTool;
end;

class function TDragMarksTool.LongDescription: String;
begin
  result:=TeeMsg_DragMarksDesc;
end;

Procedure TDragMarksTool.ChartMouseEvent( AEvent: TChartMouseEvent;
                                       Button:TMouseButton;
                                       Shift: TShiftState; X, Y: Integer);

  Procedure MouseMove;

    Procedure CheckCursor;

      Function CheckCursorSeries(const ASeries:TChartSeries):Boolean;
      begin
        result:=ASeries.Active and ASeries.Marks.Visible and
                (ASeries.Marks.Clicked(x,y)<>-1);
      end;

    var tmp : Boolean;
        t   : Integer;
    begin
      tmp:=False;

      if Assigned(Series) then
         tmp:=CheckCursorSeries(Series)
      else
      With ParentChart do
      for t:=SeriesCount-1 downto 0 do
      begin
        tmp:=CheckCursorSeries(Series[t]);
        if tmp then
           Break;
      end;

      if tmp then
      begin
        ParentChart.Cursor:=crHandPoint;
        ParentChart.CancelMouse:=True;
      end;
    end;

  var DifX : Integer;
      DifY : Integer;
      tmp  : TSeriesMarkPosition;
  begin
    if Index=TeeNoPointClicked then
       CheckCursor
    else
    begin
      tmp:=ISeries.Marks.Positions.Position[Index];

      if Assigned(tmp) then
      begin
        DifX:=X-IOldX;
        DifY:=Y-IOldY;

        tmp.Custom:=True;
        tmp.ArrowFix:=True; // <-- Force arrow "From" point to be automatic

        tmp.Offset(TeePoint(DifX,DifY));

        IOldX:=X;
        IOldY:=Y;
        ParentChart.CancelMouse:=True;
        Repaint;
      end;
    end;
  end;

  Procedure MouseDown;

    Function CheckSeries(const ASeries:TChartSeries):Integer;
    begin
      result:=-1;

      if ASeries.Active then
      begin
        result:=ASeries.Marks.Clicked(x,y);

        if result<>-1 then
        begin
          ISeries:=ASeries;
          Index:=result;
          Exit;
        end;
      end;
    end;

  var t : Integer;
  begin
    if Assigned(Series) then
       CheckSeries(Series)
    else
    With ParentChart do
    for t:=SeriesCount-1 downto 0 do
        if CheckSeries(Series[t])<>-1 then
           Break;

    if Index<>TeeNoPointClicked then
    begin
      IOldX:=X;
      IOldY:=Y;
    end;
  end;

begin
  Case AEvent of
    cmeUp  : begin
               if Assigned(FOnDragged) and (Index<>TeeNoPointClicked) then
                  FOnDragged(Self, Index, Button, Shift, X, Y);

               Index:=TeeNoPointClicked;
             end;

    cmeDown: begin
               MouseDown;

               if Index<>TeeNoPointClicked then
                  ParentChart.CancelMouse:=True;
             end;

    cmeMove: begin
               MouseMove;

               if Assigned(FOnDragging) and (Index<>TeeNoPointClicked) then
                  FOnDragging(Self, Index, Button, Shift, X, Y);
             end;
  end;
end;

class function TDragMarksTool.GetEditorClass: String;
begin
  result:='TDragMarksToolEditor';
end;

{ TAxisArrowTool }

Constructor TAxisArrowTool.Create(AOwner: TComponent);
begin
  inherited;
  FLength:=16;
  FHeadWidth:=8;
  FPosition:=aaBoth;
  FScrollPercent:=10;
  FSizePercent:=50;
end;

procedure TAxisArrowTool.ChartEvent(AEvent: TChartToolEvent);
var tmpZ : Integer;

  Procedure DrawArrow(APos,ALength:Integer);
  var P0 : TPoint;
      P1 : TPoint;
      tmpPos : Integer;
  begin
    tmpPos:=Axis.PosAxis;

    if Axis.Horizontal then
    begin
      P0:=TeePoint(APos+ALength,tmpPos);
      P1:=TeePoint(APos,tmpPos)
    end
    else
    begin
      P0:=TeePoint(tmpPos,APos+ALength);
      P1:=TeePoint(tmpPos,APos);
    end;

    ParentChart.Canvas.Arrow(True,P0,P1,HeadWidth,HeadWidth,tmpZ-5,tmpZ+5,SizePercent);
  end;

begin
  inherited;

  if (AEvent=cteAfterDraw) and Assigned(Axis) then
  begin
    ParentChart.Canvas.AssignBrush(Brush);
    ParentChart.Canvas.AssignVisiblePen(Pen);

    if ParentChart.View3D and Axis.OtherSide then
       tmpZ:=ParentChart.Width3D
    else
       tmpZ:=0;

    if (FPosition=aaStart) or (FPosition=aaBoth) then
       DrawArrow(Axis.IStartPos,Length);

    if (FPosition=aaEnd) or (FPosition=aaBoth) then
       DrawArrow(Axis.IEndPos,-Length);
  end;
end;

class function TAxisArrowTool.Description: String;
begin
  result:=TeeMsg_AxisArrowTool;
end;

class function TAxisArrowTool.LongDescription: String;
begin
  result:=TeeMsg_AxisArrowDesc;
end;

procedure TAxisArrowTool.SetHeadWidth(const Value: Integer);
begin
  SetIntegerProperty(FHeadWidth,Value);
end;

procedure TAxisArrowTool.SetLength(const Value: Integer);
begin
  SetIntegerProperty(FLength,Value);
end;

Function TAxisArrowTool.ClickedArrow(x,y:Integer):Integer;

  Procedure Check(Pos1,Pos2:Integer);
  begin
    { to-do: right/top axis Z ! }
    With Axis do
    if (Abs(Pos1-PosAxis)<Max(TeeClickTolerance,HeadWidth div 2)) then
    begin
      if (FPosition=aaStart) or (FPosition=aaBoth) then
        if (Pos2>IStartPos) and (Pos2<IStartPos+Length) then
        begin
          result:=0;
          exit;
        end;

      if (FPosition=aaEnd) or (FPosition=aaBoth) then
        if (Pos2<IEndPos) and (Pos2>IEndPos-Length) then
        begin
          result:=1;
          exit;
        end;
    end;
  end;

begin
  result:=-1;

  if Axis.Horizontal then
     Check(y,x)
  else
     Check(x,y);
end;

// Returns True when there is at least on series in the chart,
// that has "both" axis associated (left and right, or top and bottom).
// The OtherAxis parameter returns the "other" axis (right axis if
// series axis is left, left axis if series axis is right, and so on).
Function AnySeriesBothAxis(const Axis:TChartAxis; out OtherAxis:TChartAxis):Boolean;
var t : Integer;
    tmp: TCustomAxisPanel;
begin
  result:=False;

  tmp:=Axis.ParentChart;

  for t:=0 to tmp.SeriesCount-1 do
  if tmp[t].AssociatedToAxis(Axis) then
  begin
    if Axis.Horizontal then
    begin
      if tmp[t].HorizAxis=aBothHorizAxis then
      begin
        if Axis=tmp.TopAxis then OtherAxis:=tmp.BottomAxis
                            else OtherAxis:=tmp.TopAxis;
        result:=True;
      end;
    end
    else
    begin
      if tmp[t].VertAxis=aBothVertAxis then
      begin
        if Axis=tmp.LeftAxis then OtherAxis:=tmp.RightAxis
                             else OtherAxis:=tmp.LeftAxis;
        result:=True;
      end;
    end;
  end;
end;

Procedure TAxisArrowTool.ChartMouseEvent( AEvent: TChartMouseEvent;
                                       Button:TMouseButton;
                                       Shift: TShiftState; X, Y: Integer);

  Procedure DoScroll(const ADelta:TAxisValue);
  var tmp      : Boolean;
      tmpMin   : TAxisValue;
      tmpMax   : TAxisValue;
      tmpAxis2 : TChartAxis;
  begin
    tmp:=True;

    if Assigned(TCustomChart(ParentChart).OnAllowScroll) then
    begin
      tmpMin:=Axis.Minimum+ADelta;
      tmpMax:=Axis.Maximum+ADelta;
      TCustomChart(ParentChart).OnAllowScroll(Axis,tmpMin,tmpMax,tmp);
    end;

    if tmp then
    begin
      Axis.Scroll(ADelta,False);

      if AnySeriesBothAxis(Axis,tmpAxis2) then
         tmpAxis2.Scroll(ADelta,False);

      if Assigned(TCustomChart(Axis.ParentChart).OnScroll) then
         TCustomChart(Axis.ParentChart).OnScroll(Axis.ParentChart);
    end;
  end;

var tmp   : Integer;
    Delta : TAxisValue;
begin
  if Assigned(Axis) and Axis.Visible then
  Case AEvent of
    cmeDown: if ScrollPercent<>0 then
             begin
               tmp:=ClickedArrow(x,y);
               Delta:=(Axis.Maximum-Axis.Minimum)*ScrollPercent/100.0;

               if ScrollInverted then Delta:=-Delta;

               if tmp=0 then DoScroll(Delta)
               else
               if tmp=1 then DoScroll(-Delta);

               if (tmp=0) or (tmp=1) then ParentChart.CancelMouse:=True;

               if Assigned(FOnClick) and (tmp<>-1) then
                  FOnClick(Self,tmp=0);
             end;
    cmeMove: begin
               if ClickedArrow(x,y)<>-1 then
               begin
                 ParentChart.Cursor:=crHandPoint;
                 ParentChart.CancelMouse:=True;
               end;
             end;
  end;
end;

class function TAxisArrowTool.GetEditorClass: String;
begin
  result:='TAxisArrowToolEditor';
end;

procedure TAxisArrowTool.SetPosition(const Value: TAxisArrowToolPosition);
begin
  if FPosition<>Value then
  begin
    FPosition:=Value;
    Repaint;
  end;
end;

procedure TAxisArrowTool.SetSizePercent(const Value: Integer);
begin
  SetIntegerProperty(FSizePercent,Value);
end;

{ TDrawLine }

Function TDrawLine.StartHandle:TRect;
begin
  result:=RectFromCenter(Parent.AxisPoint(StartPos),3);
end;

Function TDrawLine.EndHandle:TRect;
begin
  result:=RectFromCenter(Parent.AxisPoint(EndPos),3);
end;

Procedure TDrawLine.DrawHandles;
begin
  With Parent.ParentChart.Canvas do
  begin
    Brush.Style:=bsSolid;

    if Parent.ParentChart.Color=clBlack then Brush.Color:=clSilver
                                        else Brush.Color:=clBlack;

    Pen.Style:=psClear;
    RectangleWithZ(StartHandle,0);
    RectangleWithZ(EndHandle,0);
  end;
end;

{ TDrawLines }
function TDrawLines.Get(Index: Integer): TDrawLine;
begin
  result:=TDrawLine(inherited Items[Index]);
end;

function TDrawLines.AddLine(const X0,Y0,X1,Y1:Double):TDrawLine;
begin
  result:=Add as TDrawLine;
  result.X0:=X0;
  result.Y0:=Y0;
  result.X1:=X1;
  result.Y1:=Y1;
end;

function TDrawLines.Last: TDrawLine;
begin
  if Count=0 then result:=nil else result:=Get(Count-1);
end;

procedure TDrawLines.Put(Index: Integer; const Value: TDrawLine);
begin
  Items[Index].Assign(Value);
end;

{ TDrawLine }

type
  TOwnedCollectionAccess=class(TOwnedCollection);

Constructor TDrawLine.Create(Collection: TCollection);

  function GetParent: TDrawLineTool;
  begin
    result:=TDrawLineTool(TOwnedCollectionAccess(Collection).GetOwner);
  end;

begin
  inherited;

  // IParent is a local field instead of just using GetParent, due to
  // a strange problem in NextGen compilers (Android and iOS)
  IParent:=GetParent;
end;

Constructor TDrawLine.CreateXY(Collection:TCollection; const X0, Y0, X1, Y1: Double);
begin
  Create(Collection);

  StartPos.X:=X0;
  StartPos.Y:=Y0;
  EndPos.X:=X1;
  EndPos.Y:=Y1;

  if Assigned(IParent) then
  begin
    if Assigned(IParent.FOnNewLine) then // TV52010392
       IParent.FOnNewLine(Self);

    IParent.Repaint;
  end;
end;

Destructor TDrawLine.Destroy;
begin
  if Self=Parent.ISelected then
     Parent.ISelected:=nil;

  FPen.Free;
  inherited;
end;

procedure TDrawLine.Assign(Source: TPersistent);
begin
  if Source is TDrawLine then
  With TDrawLine(Source) do
  Begin
    Self.StartPos :=StartPos;
    Self.EndPos   :=EndPos;
    Self.FStyle   :=FStyle;

    if Assigned(FPen) then
       SetPen(FPen)
    else
    if Assigned(Self.FPen) then
       FreeAndNil(Self.FPen);

  end
  else inherited;
end;

function TDrawLine.Clicked(X,Y:Integer; AHandle:TDrawLineHandle; PixelsTolerance:Integer=0):Boolean;
var tmpStart : TPoint;
    tmpEnd   : TPoint;
    P        : TPoint;
begin
  P:=TeePoint(X,Y);

  tmpStart:=Parent.AxisPoint(StartPos);
  tmpEnd:=Parent.AxisPoint(EndPos);

  Case AHandle of
    chStart: result:=PointInRect(StartHandle,P);
    chEnd  : result:=PointInRect(EndHandle,P);
  else
    Case Style of
        dlLine  : result:=PointInLine(P,tmpStart,tmpEnd,PixelsTolerance);
dlHorizParallel : begin
                    result:=PointInLine(P,tmpStart.X,tmpStart.Y,
                                          tmpEnd.X,tmpStart.Y,PixelsTolerance);
                    if not result then
                       result:=PointInLine( P,tmpStart.X,tmpEnd.Y,
                                              tmpEnd.X,tmpEnd.Y,PixelsTolerance);
                  end;
    dlRectangle,
    dlEllipse   : result:=PointInRect(TeeRect(tmpStart.X,tmpStart.Y,
                                              tmpEnd.X,tmpEnd.Y),P);
    else
    begin
      result:=PointInLine( P,tmpStart.X,tmpStart.Y,
                             tmpStart.X,tmpEnd.Y,PixelsTolerance);
      if not result then
         result:=PointInLine( P,tmpEnd.X,tmpStart.Y,
                                tmpEnd.X,tmpEnd.Y,PixelsTolerance);
    end;
    end;
  end;
end;

function TDrawLine.GetX0: Single;
begin
  result:=StartPos.X;
end;

function TDrawLine.GetX1: Single;
begin
  result:=EndPos.X;
end;

function TDrawLine.GetY0: Single;
begin
  result:=StartPos.Y;
end;

function TDrawLine.GetY1: Single;
begin
  result:=EndPos.Y;
end;

Procedure TDrawLine.SetStyle(Value:TDrawLineStyle);
begin
  if FStyle<>Value then
  begin
    FStyle:=Value;
    Parent.Repaint;
  end;
end;

procedure TDrawLine.SetX0(const Value: Single);
begin
  Parent.SetSingleProperty(StartPos.X,Value);
end;

procedure TDrawLine.SetX1(const Value: Single);
begin
  Parent.SetSingleProperty(EndPos.X,Value);
end;

procedure TDrawLine.SetY0(const Value: Single);
begin
  Parent.SetSingleProperty(StartPos.Y,Value);
end;

procedure TDrawLine.SetY1(const Value: Single);
begin
  Parent.SetSingleProperty(EndPos.Y,Value);
end;

function TDrawLine.IsPenStored: Boolean;
begin
  result:=Assigned(FPen);
end;

function TDrawLine.GetPen:TTeePen;
begin
  if not Assigned(FPen) then
  begin
    // LCL / FPC bug, workaround

    FPen:=TTeePen.Create({$IFDEF LCL}nil{$ELSE}Parent.CanvasChanged{$ENDIF});

    FPen.Assign(Parent.Pen);
  end;

  result:=FPen;
end;

procedure TDrawLine.SetPen(const Value: TTeePen);
begin
  if Assigned(Value) then Pen.Assign(Value)
  else
  begin
    FreeAndNil(FPen);
    Parent.Repaint;
  end;
end;

{ TDrawLineTool }
Constructor TDrawLineTool.Create(AOwner: TComponent);
begin
  inherited;
  FButton:=mbLeft;
  FLines:=TDrawLines.Create(Self,TDrawLine);
  FEnableDraw:=True;
  FEnableSelect:=True;
  FSelectNew:=True;
  ISelected:=nil;
  IHandle:=chNone;
end;

Destructor TDrawLineTool.Destroy;
begin
  ISelected:=nil;
  FLines.Free;
  inherited;
end;

procedure TDrawLineTool.Assign(Source:TPersistent);
begin
  if Source is TDrawLineTool then
  with TDrawLineTool(Source) do
  begin
    Self.FButton:=FButton;
    Self.FClick:=FClick;
    Self.FEnableDraw:=FEnableDraw;
    Self.FEnableSelect:=FEnableSelect;
    Self.FFullRepaint:=FFullRepaint;
    Self.Lines:=Lines;
    Self.FSelectNew:=FSelectNew;
    Self.FStyle:=FStyle;
  end;

  inherited;
end;

Procedure TDrawLineTool.DrawLine( Const StartPos,EndPos:TPoint;
                                  AStyle:TDrawLineStyle);
begin
  With ParentChart.Canvas do
  if ParentChart.View3D then
  begin
    Case AStyle of
          dlLine: begin
                    MoveTo3D(StartPos.X,StartPos.Y,0);
                    LineTo3D(EndPos.X,EndPos.Y,0);
                  end;
 dlHorizParallel: begin
                    HorizLine3D(StartPos.X,EndPos.X,StartPos.Y,0);
                    HorizLine3D(StartPos.X,EndPos.X,EndPos.Y,0);
                  end;
     dlRectangle: RectangleWithZ(TeeRect(StartPos.X,StartPos.Y,EndPos.X,EndPos.Y),0);
       dlEllipse: EllipseWithZ(StartPos.X,StartPos.Y,EndPos.X,EndPos.Y,0);
    else
      begin
        VertLine3D(StartPos.X,StartPos.Y,EndPos.Y,0);
        VertLine3D(EndPos.X,StartPos.Y,EndPos.Y,0);
      end
    end;
  end
  else
  Case AStyle of
          dlLine: Line(StartPos.X,StartPos.Y,EndPos.X,EndPos.Y);
 dlHorizParallel: begin
                    DoHorizLine(StartPos.X,EndPos.X,StartPos.Y);
                    DoHorizLine(StartPos.X,EndPos.X,EndPos.Y);
                  end;
     dlRectangle: Rectangle(StartPos.X,StartPos.Y,EndPos.X,EndPos.Y);
       dlEllipse: Ellipse(StartPos.X,StartPos.Y,EndPos.X,EndPos.Y);
  else
    begin
      DoVertLine(StartPos.X,StartPos.Y,EndPos.Y);
      DoVertLine(EndPos.X,StartPos.Y,EndPos.Y);
    end
  end;
end;

procedure TDrawLineTool.ChartEvent(AEvent: TChartToolEvent);
var t : Integer;
begin
  inherited;

  if AEvent=cteAfterDraw then
  begin
    ParentChart.Canvas.BackMode:=cbmTransparent;
    ClipDrawingRegion;

    if Lines.Count > 0 then
       for t:=0 to Lines.Count-1 do
           RedrawLine(Lines[t]);

    if Assigned(ISelected) and (IHandle=chNone) then
       ISelected.DrawHandles;

    if IDrawing then
       RedrawLine(ISelected);

    ParentChart.Canvas.UnClipRectangle;
  end;
end;

Procedure TDrawLineTool.ClipDrawingRegion;
var R : TRect;
begin
  if Assigned(Series) then
  with Series do
     R:=TeeRect(GetHorizAxis.IStartPos,GetVertAxis.IStartPos,
                GetHorizAxis.IEndPos,GetVertAxis.IEndPos)
  else
     R:=ParentChart.ChartRect;

  with ParentChart do
  if CanClip then
     Canvas.ClipCube(R,0,Width3D);
end;

Function TDrawLineTool.InternalClicked(X,Y:Integer; AHandle:TDrawLineHandle):TDrawLine;
begin
  result:=InternalClicked(X,Y,AHandle,ClickTolerance);
end;

Function TDrawLineTool.InternalClicked(X,Y:Integer; AHandle:TDrawLineHandle; ClickTolerance:Integer):TDrawLine;
var t : Integer;
begin
  result:=nil;

  // convert from 3D to 2D...
  ParentChart.Canvas.Calculate2DPosition(X,Y,0);

  // first, find if clicked the selected line...
  if Assigned(ISelected) and ISelected.Clicked(X,Y,AHandle,ClickTolerance) then
  begin
    result:=ISelected;
    exit;
  end;

  // find which clicked line...
  for t:=0 to Lines.Count-1 do
      if Lines[t].Clicked(X,Y,AHandle,ClickTolerance) then
      begin
        result:=Lines[t];
        break;
      end;
end;

Function TDrawLineTool.Clicked(X,Y:Integer):TDrawLine;
begin
  result:=Clicked(X,Y,ClickTolerance);
end;

Function TDrawLineTool.Clicked(X,Y:Integer; ClickTolerance:Integer):TDrawLine;
begin
  // try first with whole line...
  result:=InternalClicked(X,Y,chNone,ClickTolerance);

  // try then at start and end line points...
  if not Assigned(result) then
  begin
     result:=InternalClicked(X,Y,chStart,ClickTolerance);

    if not Assigned(result) then
       result:=InternalClicked(X,Y,chEnd,ClickTolerance);
  end;
end;

function TDrawLineTool.ShouldForceRepaint:Boolean;
begin
  result:=FullRepaint or (not ParentChart.Canvas.SupportsXORMode);
end;

Procedure TDrawLineTool.ChartMouseEvent( AEvent: TChartMouseEvent;
                                       AButton:TMouseButton;
                                       AShift: TShiftState; X, Y: Integer);

  { returns True if the mouse is over selected line ending points or over
    any non-selected line.
    If True, the Chart cursor is changed.
  }
  Function CheckCursor:Boolean;
  begin
    if Assigned(ISelected) and
       ((InternalClicked(X,Y,chStart)=ISelected) or
        (InternalClicked(X,Y,chEnd)=ISelected)) then { ending points }
    begin
      ParentChart.Cursor:=crCross;
      result:=True;
    end
    else
    if Assigned(Clicked(X,Y)) then { over a line }
    begin
      ParentChart.Cursor:=crHandPoint;  // set cursor
      result:=True;
    end
    else
      result:=False;
  end;

  procedure SetSelection(const ALine:TDrawLine);
  begin
    FromPoint:=AxisPoint(ALine.StartPos);
    ToPoint:=AxisPoint(ALine.EndPos);
    IPoint:=TeePoint(X,Y);
  end;

var tmpLine  : TDrawLine;
    tmpAllow : Boolean;
    tmpXor   : Boolean;
begin
  Case AEvent of
    cmeDown: if AButton=FButton then
             begin
               if FEnableSelect then tmpLine:=Clicked(X,Y)
                                else tmpLine:=nil;

               if Assigned(tmpLine) then { clicked line, do select }
               begin
                 SetSelection(tmpLine);
                 IHandle:=chSeries;

                 if tmpLine<>ISelected then
                 begin
                   tmpAllow:=True;
                   if Assigned(FOnSelecting) then
                      FOnSelecting(Self,tmpLine,tmpAllow);

                   if tmpAllow then
                   begin
                     { change selected line }
                     if Assigned(ISelected) then
                     begin
                       ISelected:=tmpLine;
                       //Repaint;
                     end
                     else
                     begin
                       ISelected:=tmpLine;
                       //ISelected.DrawHandles;
                     end;

                     { call event }
                     if Assigned(FOnSelect) then FOnSelect(Self);
                   end;
                 end
                 else
                 begin
                   { check if clicked on ending points }
                   if Assigned(InternalClicked(X,Y,chStart)) then
                      IHandle:=chStart
                   else
                   if Assigned(InternalClicked(X,Y,chEnd)) then
                      IHandle:=chEnd;
                 end;

                 Repaint;

                 ParentChart.CancelMouse:=True;
               end
               else
               begin
                 { un-select }
                 ISelected:=nil;

                 if EnableDraw then
                 begin
                   IDrawing:=True;
                   FromPoint:=TeePoint(X,Y);
                   ToPoint:=FromPoint;

                   if ShouldForceRepaint then
                      Repaint
                   else
                      RedrawLine(ISelected);

                   ParentChart.CancelMouse:=True;
                 end;
               end;
             end;
    cmeMove: if IDrawing or (IHandle<>chNone) then // drawing or dragging
             begin
               tmpXor:=not ShouldForceRepaint;

               if tmpXor then
                  RedrawLine(ISelected) // hide previous line
               else
                  Repaint;

               if IDrawing then
                  ToPoint:=TeePoint(X,Y)
               else
               begin
                 if IHandle=chStart then FromPoint:=TeePoint(X,Y)
                 else
                 if IHandle=chEnd then ToPoint:=TeePoint(X,Y)
                 else
                 if IHandle=chSeries then
                 begin
                   FromPoint.X:=FromPoint.X+(X-IPoint.X);
                   FromPoint.Y:=FromPoint.Y+(Y-IPoint.Y);
                   ToPoint.X:=ToPoint.X+(X-IPoint.X);
                   ToPoint.Y:=ToPoint.Y+(Y-IPoint.Y);

                   IPoint:=TeePoint(X,Y);
                 end;
               end;

               if Assigned(ISelected) then
               begin
                 ISelected.StartPos:=ScreenPoint(FromPoint);
                 ISelected.EndPos:=ScreenPoint(ToPoint);
               end;

               if tmpXor then
                  RedrawLine(ISelected);  // show line at new position

               ParentChart.CancelMouse:=True;

               IMoved:=True;

               // call event
               if Assigned(FOnDragLine) then
                  FOnDragLine(Self);
             end
             else
             if FEnableSelect then { change the cursor if mouse is over lines }
                ParentChart.CancelMouse:=CheckCursor;

      cmeUp: if AButton=FButton then
             begin
               // if dragging...
               if (IHandle<>chNone) and Assigned(ISelected) then
               begin
                 if (IHandle=chStart) or (IHandle=chSeries) then
                    ISelected.StartPos:=ScreenPoint(FromPoint);

                 if (IHandle=chEnd) or (IHandle=chSeries) then
                    ISelected.EndPos:=ScreenPoint(ToPoint);

                 IHandle:=chNone;

                 // stop dragging, repaint
                 Repaint;

                 // call event
                 if IMoved then // TVC52010342
                 begin
                   IMoved:=False;

                   if Assigned(FOnDraggedLine) then
                      FOnDraggedLine(Self);
                 end;
               end
               else
               if IDrawing then  // if drawing a new line...
               begin
                 if (FromPoint.X<>ToPoint.X) or (FromPoint.Y<>ToPoint.Y) then
                 begin
                   // create the new drawn line...
                   tmpLine:=TeeDrawLineClass.Create(Lines);
                   with tmpLine do
                   begin
                     StartPos:=ScreenPoint(FromPoint);
                     EndPos  :=ScreenPoint(ToPoint);
                     Style:=Self.Style;
                   end;

                   if SelectNewLines then
                   begin
                     SetSelection(tmpLine);
                     ISelected:=tmpLine;
                   end;

                   // repaint chart
                   Repaint;

                   // call event
                   if Assigned(FOnNewLine) then
                      FOnNewLine(Self);
                 end;

                 IDrawing:=False;
               end;
             end;
  end;
end;

class function TDrawLineTool.Description: String;
begin
  result:=TeeMsg_DrawLineTool;
end;

class function TDrawLineTool.LongDescription: String;
begin
  result:=TeeMsg_DrawLineDesc;
end;

procedure TDrawLineTool.RedrawLine(const ALine:TDrawLine);
var tmpPen : TTeePen;
    tmpXor : Boolean;
begin
  // Use the ALine pen, or our default Pen
  if Assigned(ALine) and Assigned(ALine.FPen) then
     tmpPen:=ALine.Pen
  else
     tmpPen:=Pen;

  // draw current selected or dragged line
  with ParentChart.Canvas do
  begin
    Brush.Style:=bsClear;
    BackMode:=cbmTransparent;
  end;

    tmpXor:=not ShouldForceRepaint;

  if tmpXor then
  Begin
     TeePrepareXorPen(ParentChart,tmpPen);
  End
  else
     ParentChart.Canvas.AssignVisiblePenColor(tmpPen, tmpPen.Color);

  {$IFDEF TEEOCX}
  //override XOr, apply setting of Pen
  ParentChart.Canvas.Pen.Mode:= tmpPen.Mode;
  {$ENDIF}

  if Assigned(ALine) then
     DrawLine(AxisPoint(ALine.StartPos),AxisPoint(ALine.EndPos),ALine.Style)
  else
     DrawLine(FromPoint,ToPoint,Self.Style);

  if tmpXor then
     ParentChart.Canvas.Pen.Mode:=pmCopy; // Reset back pen mode
end;

class function TDrawLineTool.GetEditorClass: String;
begin
  result:='TDrawLineEdit';
end;

function TDrawLineTool.AxisPoint(const P: TFloatPoint): TPoint;
begin
  // convert from axis double XY to screen pixels XY
  result.X:=GetHorizAxis.CalcPosValue(P.X);
  result.Y:=GetVertAxis.CalcPosValue(P.Y);
end;

function TDrawLineTool.ScreenPoint(const P: TPoint): TFloatPoint;
begin
  // convert from screen pixels XY position to axis double XY
  result.X:=GetHorizAxis.CalcPosPoint(Round(P.X));
  result.Y:=GetVertAxis.CalcPosPoint(Round(P.Y));
end;

procedure TDrawLineTool.SetEnableSelect(Value: Boolean);
begin
  if FEnableSelect<>Value then
  begin
    FEnableSelect:=Value;

    if (not FEnableSelect) and Assigned(ISelected) then
    begin
      ISelected:=nil;
      Repaint;
    end;
  end;
end;

Procedure TDrawLineTool.SetFullRepaint(Value:Boolean);
begin
  SetBooleanProperty(FFullRepaint,Value);
end;

procedure TDrawLineTool.DeleteSelected;
begin
  if Assigned(ISelected) then
  begin
    IDrawing:=False;
    IHandle:=chNone;
    FreeAndNil(ISelected);
    Repaint;
  end;
end;

procedure TDrawLineTool.SetSelected(const Value: TDrawLine);
begin
  ISelected:=Value;
  Repaint;
end;

procedure TDrawLineTool.SetLines(const Value: TDrawLines);
begin
  FLines.Assign(Value);
end;

type
  TTeePenAccess=class(TTeePen);
  
{ TNearestTool }
Constructor TNearestTool.Create(AOwner: TComponent);
begin
  inherited;

  FLinePen:=CreateChartPen;

  Point:=-1;

  // Note: "False" not supported in FMX (Canvas.SupportsXorMode is False)
  FFullRepaint:=True;

  FHintStyle:=smsLabelValue;
  Brush.Style:=bsClear;

  Pen.Style:=psDot;
  Pen.Color:=clWhite;

  Pen.Width:=3;
  TTeePenAccess(Pen).DefaultWidth:=3;

  FSize:=20;
  FStyle:=hsCircle;
end;

Destructor TNearestTool.Destroy;
begin
  FLinePen.Free;
  inherited;
end;

procedure TNearestTool.Assign(Source:TPersistent);
begin
  if Source is TNearestTool then
  with TNearestTool(Source) do
  begin
    Self.FDistance    :=FDistance;
    Self.FFullRepaint :=FFullRepaint;
    Self.FHint        :=FHint;
    Self.FHintStyle   :=FHintStyle;
    Self.LinePen      :=FLinePen;
    Self.FSize        :=FSize;
    Self.FStyle       :=FStyle;
  end;

  inherited;
end;

function TNearestTool.ZAxisCalc(const Value:Double):Integer;
begin
  result:=Series.ParentChart.Axes.Depth.CalcPosValue(Value);
end;

Function TNearestTool.GetDrawLine:Boolean;
begin
  result:=LinePen.Visible;
end;

procedure TNearestTool.SetDrawLine(const Value: Boolean);
begin
  LinePen.Visible:=Value;
end;

procedure TNearestTool.SetLinePen(const Value: TTeePen);
begin
  FLinePen.Assign(Value);
end;

function TNearestTool.ShouldForceRepaint:Boolean;
begin
  result:=FullRepaint or (not ParentChart.Canvas.SupportsXORMode);
end;

type
  TSeriesAccess=class(TChartSeries);

procedure TNearestTool.PaintHint;
var tmpFull : Boolean;

  procedure PreparePen(const APen:TTeePen);
  begin
    if tmpFull then
       ParentChart.Canvas.AssignVisiblePen(APen)
    else
       TeePrepareXorPen(ParentChart,APen);
  end;

var x : Integer;
    y : Integer;
    R : TRect;
    tmpZ : Integer;
    tmpPen : TTeePen;
begin
  if Assigned(Series) and Series.Active and (Point<>-1) and (Series.Count>Point) then
  begin
    tmpFull:=ShouldForceRepaint;

    PreparePen(Pen);

    TSeriesAccess(Series).CalcSelectionPos(Point,X,Y);

    if Series.HasZValues then
       tmpZ:=ZAxisCalc(Series.GetYValueList('Z').Value[Point])
    else
       tmpZ:=Series.StartZ;

    if Style<>hsNone then
    begin
      R:=RectFromCenter(x,y,FSize);

      ParentChart.Canvas.AssignBrush(Brush,clBlack);

      // This should be done here, after setting the Brush
      ParentChart.Canvas.BackMode:=cbmTransparent;

      Case Style of
        hsCircle: if ParentChart.View3D then
                     ParentChart.Canvas.EllipseWithZ(R,tmpZ)
                  else
                     ParentChart.Canvas.Ellipse(R);

     hsRectangle: begin
                    if ParentChart.View3D then
                       ParentChart.Canvas.RectangleWithZ(R,tmpZ)
                    else
                       ParentChart.Canvas.Rectangle(R);
                  end;

       hsDiamond: ParentChart.Canvas.Diamond(R,tmpZ,tmpZ);

        hsSeries: begin
                    tmpPen:=TTeePen.Create(nil);
                    tmpPen.Assign(Series.Pen);
                    Series.Pen:=Pen;
                    TSeriesAccess(Series).DrawValue(Point);
                    Series.Pen:=tmpPen;
                    tmpPen.Free;
                  end;
      end;

    end;

    if DrawLine then
    begin
      PreparePen(LinePen);

      ParentChart.Canvas.MoveTo(IMouse.X,IMouse.Y);

      if ParentChart.View3D then
         ParentChart.Canvas.LineTo3D(x,y,tmpZ)
      else
         ParentChart.Canvas.LineTo(x,y);
    end;

    if not tmpFull then
       ParentChart.Canvas.Pen.Mode:=pmCopy;
  end;
end;

class Function TNearestTool.GetNearestPoint(Series:TChartSeries; X,Y:Integer; MaxDistance:Integer=0):Integer;
begin
  result:=GetNearestPoint(Series,X,Y,False,MaxDistance);
end;

class Function TNearestTool.GetNearestPoint(Series:TChartSeries; X,Y:Integer; IncludeNulls:Boolean; MaxDistance:Integer=0):Integer;
var t      : Integer;
    Dif    : Integer;
    Dist   : Integer;
    tmpMin : Integer;
    tmpMax : Integer;
    tmpX,
    tmpY   : Integer;
    tmpZList : TChartValueList;
    tmpZ   : Integer;
    tmpHit : Boolean;
begin
  result:=Series.Clicked(X,Y);

  if result=TeeNoPointClicked then
  begin
    Dif:=10000;

    if TeeGetFirstLastSeries(Series,tmpMin,tmpMax) then
    begin
      tmpZList:=Series.GetYValueList('Z');

      for t:=tmpMin to tmpMax do { <-- traverse all points in a Series... }
      if IncludeNulls or (Series.TreatNulls=tnIgnore) or (not Series.IsNull(t)) then
      begin
        { calculate point Selection Position in pixels }

        TSeriesAccess(Series).CalcSelectionPos(t,tmpX,tmpY);

        if Series.HasZValues then
        begin
          tmpZ:=Series.ParentChart.Axes.Depth.CalcPosValue(tmpZList.Value[t]);

          with Series.ParentChart.Canvas.Calculate3DPosition(tmpX,tmpY,tmpZ){$IFDEF FMX}.Round{$ENDIF} do
          begin
            tmpX:=X;
            tmpY:=Y;
          end;

          tmpHit:=True;
        end
        else
          tmpHit:=PointInRect(Series.ParentChart.ChartRect,tmpX,tmpY);

        if tmpHit then
        begin
          // Calculate distance in pixels...
          Dist:=Round(TeeDistance(X-tmpX,Y-tmpY));

          // Consider only points at maximum distance (or any point if MaxDistance=0)
          if (MaxDistance=0) or (Dist<MaxDistance) then
             if Dist<Dif then { store if distance is lower... }
             begin
               Dif:=Dist;
               result:=t;  { <-- set this point to be the nearest... }
             end;
        end;
      end;
    end;
  end;
end;

procedure InternalCancelHint(const Chart:TCustomAxisPanel);
{$IFDEF FMX} // Not needed in Firemonkey
begin
end;
{$ELSE}
var tmp : Boolean;
begin
  tmp:=Chart.Hint<>'';
  Chart.Hint:='';

  if tmp then
  begin
    Application.HintColor:=clInfoBk;
    Application.CancelHint;
  end;
end;
{$ENDIF}

type
  TShapeAccess=class(TCustomTextShape);

procedure InternalShowHint(const tmpSeries:TChartSeries; tmp:Integer;
                           FStyle:TSeriesMarksStyle; const P:TPoint;
                           FOnGetText:TMarksTipGetText;
                           const ASender:TMarksTipTool;
                           BrushColor:TColor;
                           const Font:TFont);

  function CalcScreen(const AChart:TCustomAxisPanel; const P:TPoint):TPoint;
  begin
    if AChart.Parent=nil then
    begin
      result.X:=P.X+AChart.{$IFDEF FMX}Position.X{$ELSE}Left{$ENDIF};
      result.Y:=P.Y+AChart.{$IFDEF FMX}Position.Y{$ELSE}Top{$ENDIF};
    end
    else                          
      result:={$IFNDEF FMX}AChart.ClientToScreen{$ENDIF}(P);
  end;

var tmpStyle  : TSeriesMarksStyle;
    tmpOld    : Boolean;
    tmpText   : String;
    tmpChart2 : TCustomAxisPanel;
    Chart     : TCustomAxisPanel;
    tmpSystem : Boolean;
    tmpCursor : Integer;
    tmpX      : Integer;
    tmpY      : Integer;
begin
  Chart:=tmpSeries.ParentChart;
  tmpStyle:=tmpSeries.Marks.Style;
  tmpOld:=Chart.AutoRepaint;
  Chart.AutoRepaint:=False;

  tmpSeries.Marks.Style:=FStyle;
  try
    tmpText:=tmpSeries.ValueMarkText[tmp];

    if Assigned(FOnGetText) then
       FOnGetText(ASender,tmpText);

    tmpChart2:=nil;

    {$IFDEF TEEOCX}
    if Chart.ParentWindow <> -1 then
      tmpChart2:=Chart;
    {$ELSE}
    if Assigned(Chart.Parent) then
      tmpChart2:=Chart
    else
      if Assigned(Chart.Owner) and (Chart.Owner is TCustomChart) then
        tmpChart2:=TCustomChart(Chart.Owner);
    {$ENDIF}

    tmpSystem:=(not Assigned(ASender)) or ASender.SystemHints;

    if Assigned(tmpChart2) and
       (
         {$IFNDEF FMX}(tmpSystem and (tmpChart2.Hint<>tmpText)) or{$ENDIF}
         ((not tmpSystem) and
          ( (ASender.Format.Text<>tmpText) or (not ASender.Format.Visible))
          )
       )    then
    begin

      {$IFNDEF FMX}  // No support for System Hints in Firemonkey
      if tmpSystem then
      begin
        Application.CancelHint;

        if BrushColor<>clDefault then
           Application.HintColor:=BrushColor;

        Screen.HintFont:=Font;

        tmpChart2.ShowHint:=True;
        tmpChart2.Hint:=tmpText;

        {$IFDEF TEEOCX}
        if tmpChart2.ParentWindow=0 then
           tmpChart2.ParentWindow:=Chart.ParentWindow
        else
        {$ENDIF}
           Application.ActivateHint(CalcScreen(tmpChart2,P));
      end
      else
      {$ENDIF}
      if Assigned(ASender) then
      begin
        ASender.Format.Text:=tmpText;

        {$IFDEF FMX}
        tmpCursor:=16;
        {$ELSE}
        {$IFDEF MSWINDOWS}
        tmpCursor:=GetSystemMetrics(SM_CYCURSOR) div 2;
        {$ELSE}
        tmpCursor:=16; // ??
        {$ENDIF};
        {$ENDIF};

        tmpX:={$IFDEF FMX}Round{$ENDIF}(P.X);
        tmpY:={$IFDEF FMX}Round{$ENDIF}(P.Y)+tmpCursor;

        // Paint hint inside chart bounds when hint overflows chart right edge
        if tmpX+ASender.Format.Width > tmpChart2.Width then
           tmpX:=Max(0,{$IFDEF FMX}Round{$ENDIF}(tmpChart2.Width-ASender.Format.Width-1));

        // Paint hint above point if the hint overflows chart bottom edge
        if tmpY+ASender.Format.Height > tmpChart2.Height then
           tmpY:=Max(0,{$IFDEF FMX}Round{$ENDIF}(tmpY-2*tmpCursor-ASender.Format.Height));

        ASender.Format.MoveTo(tmpX,tmpY);
        ASender.Format.Visible:=True;

        TShapeAccess(ASender.Format).CalcBounds(tmpChart2);

        tmpChart2.AutoRepaint:=True;
        tmpChart2.Invalidate;
      end;
    end;
  finally
    tmpSeries.Marks.Style:=tmpStyle;
    Chart.AutoRepaint:=tmpOld;
  end;
end;

procedure TNearestTool.ChartMouseEvent(AEvent: TChartMouseEvent;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var tmpFull : Boolean;
begin
  inherited;

  if (AEvent=cmeMove) and Assigned(Series) then
  begin
    tmpFull:=ShouldForceRepaint;

    if not tmpFull then
       PaintHint;

    Point:=GetNearestPoint(Series,X,Y,FDistance);
    IMouse:=TeePoint(x,y);

    if not tmpFull then
       PaintHint;

    if Assigned(FOnChange) then
       FOnChange(Self);

    if tmpFull then
       Repaint;

    if FHint then
       if Point=-1 then
          InternalCancelHint(ParentChart)
       else
          InternalShowHint(Series,Point,FHintStyle,TeePoint(X,Y),FOnGetHint,nil,Brush.Color,Series.Marks.Font);
  end;
end;

procedure TNearestTool.ChartEvent(AEvent: TChartToolEvent);
var tmpFull : Boolean;
begin
  inherited;

  if AEvent=cteAfterDraw then
     PaintHint
  else
  if AEvent=cteMouseLeave then
  begin
    // Mouse is outside Chart.
    // Remove previously displayed hint, if exists:
    if Point<>-1 then
    begin
      tmpFull:=ShouldForceRepaint;

      if not tmpFull then
         PaintHint;

      Point:=-1;

      if tmpFull then
         Repaint;
    end;
  end;
end;

class Function TNearestTool.Description:String;
begin
  result:=TeeMsg_NearestTool;
end;

class Function TNearestTool.LongDescription:String;
begin
  result:=TeeMsg_NearestToolDesc;
end;

procedure TNearestTool.SetSize(const Value: Integer);
begin
  SetIntegerProperty(FSize,Value);
end;

class function TNearestTool.GetEditorClass: String;
begin
  result:='TNearestToolEdit';
end;

procedure TNearestTool.SetStyle(const Value: TNearestToolStyle);
begin
  if FStyle<>Value then
  begin
    FStyle:=Value;
    Repaint;
  end;
end;

{ TColorBandTool }

Constructor TColorBandTool.Create(AOwner: TComponent);
begin
  inherited;

  FLineEnd:=NewColorLine;
  FLineStart:=NewColorLine;

  FNoLimitDrag:=True;
  FColor:=clWhite;
  FCursor:=crDefault;
  FDrawBehind:=True;
  FAllowDrag:=True;
end;

Destructor TColorBandTool.Destroy;
begin
  FPicture.Free;
  FreeAndNil(FLineStart);
  FreeAndNil(FLineEnd);
  inherited;
end;

procedure TColorBandTool.Assign(Source:TPersistent);
begin
  if Source is TColorBandTool then
  with TColorBandTool(Source) do
  begin
    Self.FAllowDrag:=FAllowDrag;
    Self.FColor:=FColor;
    Self.FCursor:=FCursor;
    Self.FDrawBehind:=FDrawBehind;
    Self.FDrawBehindAxes:=FDrawBehindAxes;
    Self.FEnd:=FEnd;
    Self.FNoLimitDrag:=FNoLimitDrag;
    Self.FStart:=FStart;
    Self.FTransparency:=FTransparency;
    Self.EndLine.Assign(EndLine);
    Self.Picture:=FPicture;
    Self.StartLine.Assign(StartLine);
  end;

  inherited;
end;

procedure TColorBandTool.InitColorLine(const Value:TColorLineTool);
begin
  Value.OnDragLine:=DragLine;
  Value.InternalOnEndDragLine:=EndDragLine;
  Value.Pen.OnChange:=CanvasChanged;
end;

Function TColorBandTool.NewColorLine:TColorLineTool;
begin
  result:=TColorLineTool.Create(nil);
  result.DragRepaint:=True;
  InitColorLine(result);
end;

procedure TColorBandTool.DragLine(Sender:TColorLineTool);
begin
  if Sender.DragRepaint then
     if Sender=FLineStart then StartValue:=Sender.Value
                          else EndValue:=Sender.Value;

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

procedure TColorBandTool.EndDragLine(Sender:TColorLineTool);
begin
  if not Sender.DragRepaint then
     if Sender=FLineStart then StartValue:=Sender.Value
                          else EndValue:=Sender.Value;

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

Function TColorBandTool.BoundsRect:TRect;
var tmp0 : Double;
    tmp1 : Double;
    tmpDraw : Boolean;
begin
  if Assigned(Axis) then
  begin
    result:=ParentChart.ChartRect;

    tmp0:=FStart;
    tmp1:=FEnd;

    With Axis do
    begin
      if Inverted then
      begin
        if tmp0<tmp1 then SwapDouble(tmp0,tmp1);
        tmpDraw:=(tmp1<=Maximum) and (tmp0>=Minimum);
      end
      else
      begin
        if tmp0>tmp1 then SwapDouble(tmp0,tmp1);
        tmpDraw:=(tmp0<=Maximum) and (tmp1>=Minimum);
      end;

      if tmpDraw then
      begin
        if Horizontal then
        begin
          result.Left:=Math.Max(IStartPos,CalcPosValue(tmp0));
          result.Right:=Math.Min(IEndPos,CalcPosValue(tmp1));

          if Self.Pen.Visible then
             result.Right:=result.Right+1;
        end
        else
        begin
          result.Top:=Math.Max(IStartPos,CalcPosValue(tmp1));
          result.Bottom:=Math.Min(IEndPos,CalcPosValue(tmp0));

          result.Left:=result.Left+1;
          result.Bottom:=result.Bottom+1;

          if not Self.Pen.Visible then
          begin
            result.Bottom:=result.Bottom+1;
            result.Right:=result.Right+1;
          end;
        end;
      end
      else
        result:=TeeZeroRect;
    end;
  end
  else
    result:=TeeZeroRect;
end;

Function TColorBandTool.ZPosition:Integer;
begin
  if DrawBehind then result:=ParentChart.Width3D
                else result:=0;
end;

procedure TColorBandTool.PaintBand;
var R    : TRect;
    tmpZ : Integer;

  // Returns rectangle bounding exact Start and End limits.
  // This function is only used when the band is filled with a Gradient.
  function TotalBounds:TRectF;
  var tmp0,
      tmp1 : Double;
  begin
    tmp0:=FStart;
    tmp1:=FEnd;

    with Axis do
    begin
      if Inverted then
      begin
        if tmp0<tmp1 then SwapDouble(tmp0,tmp1);
      end
      else
      begin
        if tmp0>tmp1 then SwapDouble(tmp0,tmp1);
      end;

      if Horizontal then
      begin
        result.Left:=CalcPosValue(tmp0);
        result.Right:=CalcPosValue(tmp1);
        result.Top:=ParentChart.ChartRect.Top;
        result.Bottom:=ParentChart.ChartRect.Bottom;
      end
      else
      begin
        result.Top:=CalcPosValue(tmp1);
        result.Bottom:=CalcPosValue(tmp0);
        result.Left:=ParentChart.ChartRect.Left;
        result.Right:=ParentChart.ChartRect.Right;
      end;
    end;
  end;

  procedure DrawRectangle;
  begin
    with ParentChart.Canvas do
    if Assigned(FPicture) and FPicture.Valid then
       if ParentChart.View3D and (not ParentChart.View3DOptions.Orthogonal) then
          StretchDraw(R,FPicture.Filtered,tmpZ)
       else
          StretchDraw(CalcRect3D(R,tmpZ),FPicture.Filtered)
    else
    begin
      // Use the real bounding rectangle, not the clipped one ("R"):
      if Gradient.Visible then
         Gradient.CustomTargetRectangle:=TotalBounds;

      if ParentChart.View3D then
         RectangleWithZ(R,tmpZ)
      else
         Rectangle(R);

      if Gradient.Visible then
         Gradient.CustomTargetRectangle:=TeeZeroRectF;
    end;
  end;

var tmpRectBlend : TRect;
    tmpBlend     : TTeeBlend;
begin
  if Assigned(Axis) then
  begin
    R:=BoundsRect;

    if ((R.Right-R.Left)>0) and ((R.Bottom-R.Top)>0) then
    begin
      ParentChart.Canvas.AssignBrush(Brush,Color);
      ParentChart.Canvas.AssignVisiblePen(Pen);

      tmpZ:=ZPosition;

      if Transparency>0 then
      begin
        if ParentChart.Canvas.SupportsAlpha then
           tmpRectBlend:=TeeZeroRect
        else
        if ParentChart.View3D then
           tmpRectBlend:=ParentChart.Canvas.RectFromRectZ(R,tmpZ)
        else
           tmpRectBlend:=R;

        tmpBlend:=ParentChart.Canvas.BeginBlending(tmpRectBlend,Transparency);
      end
      else
        tmpBlend:=nil;

      DrawRectangle;

      if Transparency>0 then
         ParentChart.Canvas.EndBlending(tmpBlend);
    end;

    if FLineEnd.Active then
    begin
      FLineEnd.Value:=EndValue;
      FLineEnd.InternalDrawLine(False);
    end;

    if FLineStart.Active then
    begin
      FLineStart.Value:=StartValue;
      FLineStart.InternalDrawLine(False);
    end;
  end;
end;

procedure TColorBandTool.ChartEvent(AEvent: TChartToolEvent);
begin
  inherited;

  case AEvent of
   cteBeforeDrawAxes: if DrawBehindAxes then PaintBand;
   cteBeforeDrawSeries: if DrawBehind and (not DrawBehindAxes) then PaintBand;
   cteAfterDraw: if (not DrawBehind) and (not DrawBehindAxes) then PaintBand;
  end;
end;

class function TColorBandTool.Description: String;
begin
  result:=TeeMsg_ColorBandTool;
end;

class function TColorBandTool.LongDescription: String;
begin
  result:=TeeMsg_ColorBandDesc;
end;

// Necessary to persist Stard and End ColorLines
procedure TColorBandTool.GetChildren(Proc: TGetChildProc; Root:TComponent);
begin
  inherited;

  if not (csAncestor in ComponentState) then
  begin
    Proc(FLineStart);
    Proc(FLineEnd);
  end;
end;

// Necessary to persist Stard and End ColorLines
function TColorBandTool.GetChildOwner: TComponent;
begin
  result:=Self;
end;

// Necessary to persist Stard and End ColorLines
procedure TColorBandTool.ReadState(Reader: TReader);

  procedure PrepareLine(const Value:TColorLineTool);
  begin
    InitColorLine(Value);
    Value.SetParent(IParent);
    Value.InternalSetAxis(Axis); // <-- Do not set Axis to avoid duplicating at ParentChart.Tools
  end;

begin
  inherited;

  if (ComponentCount>1) and (Components[1] is TColorLineTool) then
  begin
    if ShouldFreeChild(FLineEnd) then
       FreeAndNil(FLineEnd);

    FLineEnd:=TColorLineTool(Components[1]);
  end;

  PrepareLine(FLineEnd);

  if (ComponentCount>0) and (Components[0] is TColorLineTool) then
  begin
    if ShouldFreeChild(FLineStart) then
       FreeAndNil(FLineStart);

    FLineStart:=TColorLineTool(Components[0]);
  end;

  PrepareLine(FLineStart);
end;

class function TColorBandTool.GetEditorClass: String;
begin
  result:='TColorBandToolEditor';
end;

procedure TColorBandTool.DoChanged;
begin
  if Assigned(FOnChanged) then
     FOnChanged(Self);
end;

procedure TColorBandTool.SetEnd(const Value: Double);
begin
  if FEnd<>Value then
  begin
    SetDoubleProperty(FEnd,Value);
    SetLines;
    DoChanged;
  end;
end;

procedure TColorBandTool.SetStart(const Value: Double);
begin
  if FStart<>Value then
  begin
    SetDoubleProperty(FStart,Value);
    SetLines;
    DoChanged;
  end;
end;

function TColorBandTool.GetGradient:TChartGradient;
begin
  result:=Brush.Gradient;
end;

procedure TColorBandTool.SetGradient(const Value: TChartGradient);
begin
  Brush.Gradient.Assign(Value);
end;

procedure TColorBandTool.SetColor(Value: TColor);
begin
  SetColorProperty(FColor,Value);
end;

procedure TColorBandTool.SetTransparency(const Value: TTeeTransparency);
begin
  if FTransparency<>Value then
  begin
    FTransparency:=Value;
    Repaint;
  end;
end;

procedure TColorBandTool.SetDrawBehind(const Value: Boolean);
begin
  SetBooleanProperty(FDrawBehind,Value);
end;

procedure TColorBandTool.SetDrawBehindAxes(const Value: Boolean);
begin
  SetBooleanProperty(FDrawBehindAxes,Value);
end;

Function TColorBandTool.Clicked(X,Y:Integer):Boolean;
var P : TFourPoints;
begin
  ParentChart.Canvas.FourPointsFromRect(BoundsRect,ZPosition,P);
  result:=PointInPolygon(TeePoint(X,Y),P);
end;

procedure TColorBandTool.ChartMouseEvent(AEvent: TChartMouseEvent;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var tmpValue,
    tmpStartValue,
    tmpDiff  : Double;
begin
  if FLineStart.Active then
     FLineStart.ChartMouseEvent(AEvent,Button,Shift,X,Y);

  if FLineEnd.Active and (not ParentChart.CancelMouse) then
     FLineEnd.ChartMouseEvent(AEvent,Button,Shift,X,Y);

  Case AEvent of
    cmeDown: begin
               IDragged:=False;

               if AllowDrag and Clicked(X,Y) then
                  if not ParentChart.CancelMouse then
                  begin
                    IDragging:=True;

                    if Axis.Horizontal then
                       IOld:=Axis.CalcPosPoint(X)-StartValue
                    else
                       IOld:=Axis.CalcPosPoint(Y)-StartValue;

                    ParentChart.CancelMouse:=True;
                  end;
             end;

    cmeMove: begin
               if IDragging and (not ParentChart.CancelMouse) then
               begin
                 tmpDiff:=EndValue-StartValue;

                 if Axis.Horizontal then
                    tmpValue:=Axis.CalcPosPoint(X)-IOld
                 else
                    tmpValue:=Axis.CalcPosPoint(Y)-IOld;

                 if NoLimitDrag then
                    tmpStartValue:=tmpValue
                 else
                    tmpStartValue:=StartLine.LimitValue(tmpValue);

                 tmpValue:=EndLine.LimitValue(tmpStartValue+tmpDiff);

                 if (not NoLimitDrag) and (tmpValue<(tmpStartValue+tmpDiff)) then
                    StartValue:=tmpValue-tmpDiff
                 else
                    StartValue:=tmpStartValue;

                 EndValue:=StartValue+tmpDiff;
               end;

               if (not IDragging) and (AllowDrag or (Cursor<>crDefault)) and (not ParentChart.CancelMouse) then
               begin
                 if Clicked(x,y) then
                 begin
                   {$IFDEF TEESETCURSOR}
                   if (FCursor=crDefault) and AllowDrag then
                      ParentChart.Cursor:=crHandPoint
                   else
                      ParentChart.Cursor:=FCursor;
                   {$ENDIF}

                   // This should be changed with a "ParentChart.CancelCursor" or similar:
                   ParentChart.CancelMouse:=(not ParentChart.Panning.Active) and
                                            (not ParentChart.Zoom.Active);
                 end;
               end;

               if IDragging then
               begin
                 IDragged:=True;

                 ParentChart.CancelMouse:=True;

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

      cmeUp: begin
               IDragging:=False;

               if not IDragged then
                  if Assigned(FOnClick) and Clicked(X,Y) then
                     FOnClick(Self,Button,Shift,X,Y);

               IDragged:=False;
             end;
  end;
end;

function TColorBandTool.GetResizeEnd: Boolean;
begin
  result:=EndLine.AllowDrag;
end;

function TColorBandTool.GetResizeStart: Boolean;
begin
  result:=StartLine.AllowDrag;
end;

procedure TColorBandTool.SetResizeEnd(const Value: Boolean);
begin
  if not (csReading in ComponentState) then
  begin
    if Value then
       EndLine.Active:=True;

    EndLine.AllowDrag:=Value; // 513
  end;
end;

procedure TColorBandTool.SetResizeStart(const Value: Boolean);
begin
  if not (csReading in ComponentState) then
  begin
    if Value then
       StartLine.Active:=Value;

    StartLine.AllowDrag:=Value; // 513
  end;
end;

procedure TColorBandTool.SetAxis(const Value: TChartAxis);
begin
  inherited;

  if not (csReading in ComponentState) then
  begin
    FLineEnd.Axis:=Value;
    FLineEnd.ParentChart:=nil;
    FLineStart.Axis:=Value;
    FLineStart.ParentChart:=nil;
  end;
end;

procedure TColorBandTool.SetLines;
begin
  if Assigned(FLineEnd) then
     FLineEnd.Value:=FEnd;

  if Assigned(FLineStart) then
     FLineStart.Value:=FStart;
end;

procedure TColorBandTool.Loaded;
begin
  inherited;
  SetLines;
end;

Function TColorBandTool.Chart:TCustomAxisPanel;
begin
  result:=IParent;

  if not Assigned(result) then
     result:=ParentChart;
end;

procedure TColorBandTool.SetParent(const Value:TCustomAxisPanel);
begin
  IParent:=Value;

  if Assigned(FLineStart) then
     FLineStart.SetParent(Chart); // <-- Do not use IParent here

  if Assigned(FLineEnd) then
     FLineEnd.SetParent(Chart); // <-- Do not use IParent here
end;

procedure TColorBandTool.SetParentChart(const Value: TCustomAxisPanel);
begin
  inherited;
  SetParent(Value);
end;

function TColorBandTool.GetEndLinePen: TTeePen;
begin
  result:=EndLine.Pen;
end;

function TColorBandTool.GetStartLinePen: TTeePen;
begin
  result:=StartLine.Pen;
end;

procedure TColorBandTool.SetEndLinePen(const Value: TTeePen);
begin
  EndLine.Pen:=Value;
end;

procedure TColorBandTool.SetStartLinePen(const Value: TTeePen);
begin
  StartLine.Pen:=Value;
end;

Procedure TColorBandTool.PictureChanged(Sender:TObject);
begin
  Repaint;
end;

function TColorBandTool.GetPicture: TTeePicture;
begin
  if not Assigned(FPicture) then
  begin
     FPicture:=TTeePicture.Create;
     FPicture.OnChange:=PictureChanged;
  end;

  result:=FPicture;
end;

procedure TColorBandTool.SetPicture(const Value: TTeePicture);
begin
  if Assigned(Value) then Picture.Assign(Value)
                     else FreeAndNil(FPicture);
end;

{ TColorLineTool }
Constructor TColorLineTool.Create(AOwner: TComponent);
begin
  inherited;

  FAllowDrag:=True;
  FDraw3D:=True;

  FClick:=TeeClickTolerance;

  TTeePenAccess(Pen).InitEndStyle(esFlat);
end;

Procedure TColorLineTool.Assign(Source:TPersistent);
begin
  if Source is TColorLineTool then
  with TColorLineTool(Source) do
  begin
    Self.FAllowDrag:=AllowDrag;
    Self.Annotation:=FAnnotation;
    Self.FAnnValue:=FAnnValue;
    Self.FClick:=FClick;
    Self.FDragRepaint:=DragRepaint;
    Self.FDraw3D:=Draw3D;
    Self.FDrawBehind:=DrawBehind;
    Self.FNoLimitDrag:=NoLimitDrag;
    Self.FStyle:=Style;
    Self.FValue:=Value;
  end;

  inherited;
end;

Function TColorLineTool.CalcValue:Double;
begin
  Case Self.FStyle of
    clMaximum: result:=Axis.Maximum;
    clCenter: result:=(Axis.Maximum+Axis.Minimum)*0.5;
    clMinimum: result:=Axis.Minimum;
  else
    result:=FValue;  // clCustom
  end;
end;

procedure TColorLineTool.GetAxisStartEnd(out AStart,AEnd:Integer);
begin
  AStart:=Axis.IStartPos;
  AEnd:=Axis.IEndPos;

  // Note: Do not check Axis.Inverted to invert AStart and AEnd here.
  // These pixel coordinates should be always:  AStart <= AEnd
end;

// Returns the same AValue parameter if it points to
// inside the axes limits.
// If AValue is outside the limits, it will return the closest
// point value of the ultrapassed edge.
Function TColorLineTool.LimitValue(const AValue:Double):Double;
var tmpStart : Integer;
    tmpEnd   : Integer;
    tmpPos   : Integer;
begin
  // do not use Axis Minimum & Maximum, we need the "real" min and max
  GetAxisStartEnd(tmpStart,tmpEnd);

  tmpPos:=Axis.CalcPosValue(AValue);

  if tmpPos<tmpStart then
     result:=Axis.CalcPosPoint(tmpStart)
  else
  if tmpPos>tmpEnd then
     result:=Axis.CalcPosPoint(tmpEnd)
  else
     result:=AValue;
end;

procedure TColorLineTool.SetParent(Value:TCustomAxisPanel);
begin
  IParent:=Value;

  if Assigned(FAnnotation) then
     FAnnotation.InternalSetParent(Chart); // <-- Do not use IParent here
end;

procedure TColorLineTool.SetParentChart(const Value: TCustomAxisPanel);
begin
  inherited;

  if Assigned(FAnnotation) then
     FAnnotation.InternalSetParent(ParentChart);

  Repaint;
end;

Function TColorLineTool.Chart:TCustomAxisPanel;
begin
  result:=IParent;

  if not Assigned(result) then
     result:=ParentChart;
end;

// Full repaint should be done in these cases:
// 1) DragRepaint property is True, or:
// 2) Canvas does not support "xor" mode, or:
// 3) Annotation is visible
function TColorLineTool.ShouldForceRepaint:Boolean;
begin
  result:=FDragRepaint or (not ParentChart.Canvas.SupportsXORMode) or
          IsVisible(FAnnotation);
end;

function TColorLineTool.GetAnnotation:TAnnotationTool;
begin
  if not Assigned(FAnnotation) then
  begin
    FAnnotation:=CreateAnnotation(Self,Chart);

    // Make it visible by default,
    // but remember set Shape.Text to paint something:
    FAnnotation.Shape.Visible:=True;
  end;

  result:=FAnnotation;
end;

procedure TColorLineTool.SetAnnotation(const Value: TAnnotationTool);
begin
  if Assigned(Value) then
     Annotation.Assign(Value)
  else
     FreeAndNil(FAnnotation);
end;

procedure TColorLineTool.SetAnnValue(const Value: Boolean);
begin
  SetBooleanProperty(FAnnValue,Value);

  if not FAnnValue then
    Annotation.Text:='';
end;

Procedure TColorLineTool.DrawColorLine(Back:Boolean);
var tmp       : Integer;
    tmpChart  : TCustomAxisPanel;
    tmpCanvas : TCanvas3D;
    tmpR      : TRect;

  procedure Draw2DLine; //74
  var R    : TRect;
      tmpH,
      tmpW : Integer;
      tmpVPos,
      tmpPos : Integer;
  begin
    if FAnnValue then
       Annotation.Text:=Axis.LabelValue(CalcValue);

    if IsVisible(FAnnotation) and (FAnnotation.Text<>'') then
    begin
      R:=FAnnotation.GetBoundsParent(tmpChart);
      tmpW:={$IFDEF FMX}Round{$ENDIF}(R.Right-R.Left);

      if Axis.Horizontal then
         case FAnnotation.Shape.Alignment of
           taLeftJustify: tmpPos:=tmp;
          taRightJustify: tmpPos:=tmp-tmpW;
         else
            tmpPos:=tmp-(tmpW div 2);
         end
      else
         case FAnnotation.Shape.Alignment of
           taLeftJustify: tmpPos:={$IFDEF FMX}Round{$ENDIF}(tmpR.Left)+4;
          taRightJustify: tmpPos:={$IFDEF FMX}Round{$ENDIF}(tmpR.Right)-4-tmpW;
         else
           tmpPos:={$IFDEF FMX}Round{$ENDIF}(tmpR.Left+((tmpChart.ChartWidth-tmpW) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF}))
         end;

      tmpH:={$IFDEF FMX}Round{$ENDIF}((R.Bottom-R.Top) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF});

      if Axis.Horizontal then
      begin
        tmpVPos:=Round(tmpR.Top+tmpR.Bottom) div 2;

        tmpCanvas.VertLine3D(tmp,tmpR.Top,tmpVPos-tmpH,0);
        tmpCanvas.VertLine3D(tmp,tmpVPos+tmpH,tmpR.Bottom,0);

        FAnnotation.Top:=tmpVPos-tmpH;
        FAnnotation.Left:=tmpPos;
      end
      else
      begin
        tmpCanvas.HorizLine3D(tmpR.Left,tmpPos,tmp,0);
        tmpCanvas.HorizLine3D(tmpPos+tmpW,tmpR.Right,tmp,0);

        FAnnotation.Top:=tmp-tmpH;
        FAnnotation.Left:=tmpPos;
      end;

      FAnnotation.DoDrawText(Chart);
    end
    else
    if Axis.Horizontal then
       tmpCanvas.VertLine3D(tmp,tmpR.Top,tmpR.Bottom,0)
    else
       tmpCanvas.HorizLine3D(tmpR.Left,tmpR.Right,tmp,0);
  end;

var
  tmp3D    : Integer;
  tmpStart : Integer;
  tmpEnd   : Integer;
begin
  { check inside axis limits, only when dragging }
  if AllowDrag and (not NoLimitDrag) then
     FValue:=LimitValue(FValue);

  tmp:=Axis.CalcPosValue(CalcValue);

  // Do not paint outside the axis range:
  if not ShowOutsideAxis then
  begin
    GetAxisStartEnd(tmpStart,tmpEnd);

    if (tmp<tmpStart) or (tmp>tmpEnd) then
       Exit;
  end;

  // Prepare variables
  tmpChart:=Chart;
  tmp3D:=tmpChart.Width3D;
  tmpR:=tmpChart.ChartRect;
  tmpCanvas:=tmpChart.Canvas;

  // Painting behind series
  if Back then
  begin
    if tmpChart.View3D then
    begin
      if Axis.Horizontal then
      begin
        if Draw3D then
           tmpCanvas.ZLine3D(tmp,tmpR.Bottom,0,tmp3D);

        if DrawBehind or Draw3D then
           tmpCanvas.VertLine3D(tmp,tmpR.Top,tmpR.Bottom,tmp3D);
      end
      else
      begin
        if Draw3D then
           tmpCanvas.ZLine3D(tmpR.Left,tmp,0,tmp3D);

        if DrawBehind or Draw3D then
           tmpCanvas.HorizLine3D(tmpR.Left,tmpR.Right,tmp,tmp3D);
      end;
    end
    else
    if DrawBehind then
       Draw2DLine;
  end
  else
  // Painting on top of series
  begin
    if tmpChart.View3D and Draw3D then
       if Axis.Horizontal then
          tmpCanvas.ZLine3D(tmp,tmpR.Top,0,tmp3D)
       else
          tmpCanvas.ZLine3D(tmpR.Right,tmp,0,tmp3D);

    if not DrawBehind then
       Draw2DLine;
  end;
end;

procedure TColorLineTool.InternalDrawLine(Back:Boolean);
begin
  Chart.Canvas.BackMode:=cbmTransparent;

  if ShouldForceRepaint then
     Chart.Canvas.AssignVisiblePen(Pen)
  else
     TeePrepareXorPen(Chart,Pen);

  DrawColorLine(Back);

  if not ShouldForceRepaint then
     Chart.Canvas.Pen.Mode:=pmCopy;
end;

procedure TColorLineTool.ChartEvent(AEvent: TChartToolEvent);
begin
  inherited;

  if Assigned(Axis) and
     ((AEvent=cteBeforeDrawSeries) or (AEvent=cteAfterDraw)) then
       InternalDrawLine(AEvent=cteBeforeDrawSeries);
end;

procedure TColorLineTool.DoEndDragLine;
begin
  if Assigned(InternalOnEndDragLine) then InternalOnEndDragLine(Self);
  if Assigned(FOnEndDragLine) then FOnEndDragLine(Self);
end;

procedure TColorLineTool.ChartMouseEvent(AEvent: TChartMouseEvent;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var tmp       : Integer;
    tmpNew    : Double;
    tmpDoDraw : Boolean;
begin
  tmpDoDraw:=False;

  if AllowDrag and Assigned(Axis) then
  Case AEvent of
      cmeUp: if IDragging then
             begin
               { force repaint }
               if not FDragRepaint then Repaint;

               { call event }
               DoEndDragLine;

               IDragging:=False;
             end;

    cmeMove: if IDragging then
             begin
               if Axis.Horizontal then tmp:=x else tmp:=y;

               { calculate new position }
               tmpNew:=Axis.CalcPosPoint(tmp);

               { check inside axis limits }
               if not NoLimitDrag then  // (already implicit AllowDrag=True)
                  tmpNew:=LimitValue(tmpNew);

               if ShouldForceRepaint then
                  Value:=tmpNew { force repaint whole chart }
               else
               begin

                 tmpDoDraw:=CalcValue<>tmpNew;

                 if tmpDoDraw then
                 begin
                   TeePrepareXorPen(Chart,Self.Pen);

                   { hide previous line }
                   DrawColorLine(True);
                   DrawColorLine(False);

                   { set new value }
                   FStyle:=clCustom;
                   FValue:=tmpNew;
                 end;
               end;

               Chart.CancelMouse:=True;

               { call event, allow event to change Value }
               if Assigned(FOnDragLine) then FOnDragLine(Self);

               if tmpDoDraw then
               begin
                 { draw at new position }
                 DrawColorLine(True);
                 DrawColorLine(False);

                 { reset pen mode }
                 Chart.Canvas.Pen.Mode:=pmCopy;
               end;
             end
             else
             begin
               { is mouse on line? }
               if Clicked(x,y) then
               begin
                 {$IFDEF TEESETCURSOR}
                 { show appropiate cursor }
                 if Axis.Horizontal then
                    Chart.Cursor:=crHSplit
                 else
                    Chart.Cursor:=crVSplit;
                 {$ENDIF}

                 Chart.CancelMouse:=True;
               end
               {$IFDEF TEEOCX}
               else
                  Chart.Cursor:=crDefault;  // DB:Sep-2014 ifdef AX only (TA05013030)
               {$ENDIF}
               ;
             end;

    cmeDown: if Button=mbLeft then
             begin
               { is mouse over line ? }
               IDragging:=Clicked(x,y);
               Chart.CancelMouse:=IDragging;

               if IDragging and Assigned(FOnBeginDragLine) then
                  FOnBeginDragLine(Self);
             end;
  end;
end;

function TColorLineTool.Clicked(x, y: Integer): Boolean;
var tmp : Integer;
begin
  if Axis.Horizontal then tmp:=x else tmp:=y;

  result:=Abs(tmp-Axis.CalcPosValue(CalcValue))<FClick;

  if result then
  with Chart do
     if Axis.Horizontal then
        result:=(y>=ChartRect.Top) and (y<=ChartRect.Bottom)
     else
        result:=(x>=ChartRect.Left) and (x<=ChartRect.Right)
end;

class function TColorLineTool.Description: String;
begin
  result:=TeeMsg_ColorLineTool;
end;

class function TColorLineTool.LongDescription: String;
begin
  result:=TeeMsg_ColorLineDesc;
end;

// Necessary to persist Annotation
function TColorLineTool.GetChildOwner: TComponent;
begin
  result:=Self;
end;

// Necessary to persist Annotation
procedure TColorLineTool.GetChildren(Proc: TGetChildProc; Root: TComponent);
begin
  inherited;

  if Assigned(FAnnotation) then
     Proc(FAnnotation);
end;

// Necessary to persist Annotation
procedure TColorLineTool.ReadState(Reader: TReader);
begin
  inherited;

  if ShouldFreeChild(FAnnotation) then
     FreeAndNil(FAnnotation);

  if (ComponentCount>0) and (Components[0] is TAnnotationTool) then
  begin
    FAnnotation:=TAnnotationTool(Components[0]);
    SetParent(IParent);
  end;
end;

class function TColorLineTool.GetEditorClass: String;
begin
  result:='TColorLineToolEditor';
end;

procedure TColorLineTool.SetValue(const AValue: Double);
begin
  if not (csReading in ComponentState) then
     FStyle:=clCustom;

  if (not IDragging) or ShouldForceRepaint then
     SetDoubleProperty(FValue,AValue)
  else
     FValue:=AValue;
end;

procedure TColorLineTool.SetDragRepaint(const Value: Boolean);
begin
  FDragRepaint:=Value;

  if FDragRepaint then // only when True
     Repaint;
end;

procedure TColorLineTool.SetDraw3D(const Value: Boolean);
begin
  SetBooleanProperty(FDraw3D,Value);
end;

procedure TColorLineTool.SetDrawBehind(const Value: Boolean);
begin
  SetBooleanProperty(FDrawBehind,Value);
end;

procedure TColorLineTool.SetStyle(const Value: TColorLineStyle);
begin
  if FStyle<>Value then
  begin
    FStyle:=Value;
    Repaint;
  end;
end;

type
  TChartPenAccess=class(TTeePen);

{ TRotateTool }

Constructor TRotateTool.Create(AOwner: TComponent);
begin
  inherited;
  FButton:=mbLeft;
  IDragging:=False;
  FInverted:=False;
  FStyle:=rsAll;
  FSpeed:=50;
  FCursor:=crHandPoint;

  Pen.Hide;

  TChartPenAccess(Pen).DefaultVisible:=False;

  Pen.Mode:=pmXor;
  Pen.Color:=clWhite;
end;

Destructor TRotateTool.Destroy;
begin
  FreeAndNil(ITimer);
  inherited;
end;

procedure TRotateTool.Assign(Source:TPersistent);
begin
  if Source is TRotateTool then
  with TRotateTool(Source) do
  begin
    Self.FButton   :=Button;
    Self.FInertia  :=FInertia;
    Self.FInverted :=Inverted;
    Self.FSpeed    :=Speed;
    Self.FStyle    :=Style;
  end;

  inherited;
end;

class Function TRotateTool.RotationChange(FullRotation:Boolean; const AAngle,AChange:Double):Double;
begin
  result:=AAngle;
  if AChange=0 then exit;

  if AChange>0 then
  begin
    result:=result+AChange;

    if result>360 then
       result:=result-360
  end
  else
  begin
    if FullRotation then
       result:=Math.Max(0,{$IFDEF CPUX64}Double{$ENDIF}(AAngle+AChange))
    else
    begin
      result:=AAngle+AChange;

      if result<0 then
         result:=360+result
    end;
  end;
end;

class Function TRotateTool.ElevationChange(FullRotation:Boolean; const AAngle,AChange:Double):Double;
var tmpMinAngle : Integer;
begin
  result:=AAngle;
  if AChange=0 then exit;

  if AChange>0 then
     result:=Math.Min(360,{$IFDEF CPUX64}Double{$ENDIF}(AAngle+AChange))
  else
  begin
    if FullRotation then tmpMinAngle:=0
                    else tmpMinAngle:=TeeMinAngle;

    result:=Math.Max(tmpMinAngle,{$IFDEF CPUX64}Double{$ENDIF}(AAngle+AChange));
  end;
end;

procedure TRotateTool.Rotate(const RotationDegrees,ElevationDegrees:Double);
begin
  if Assigned(ParentChart) then
  with ParentChart.View3DOptions do
  begin
    RotationFloat:=RotationFloat+RotationDegrees;
    ElevationFloat:=ElevationFloat+ElevationDegrees;
  end;
end;

procedure TRotateTool.DoRotation(const IDifX,IDifY:Double);

  Function CorrectAngle(Const AAngle:Double):Double;
  begin
    result:=AAngle;
    if result>360 then result:=result-360 else
    if result<0 then result:=360+result;
  end;

begin
  With ParentChart.View3DOptions do
  begin
    Orthogonal:=False;

    if ParentChart.Canvas.SupportsFullRotation then
    begin
      if (FStyle=rsRotation) or (FStyle=rsAll) then
         RotationFloat:=CorrectAngle(RotationFloat+IDifX);

      if (FStyle=rsElevation) or (FStyle=rsAll) then
         ElevationFloat:=CorrectAngle(ElevationFloat+IDifY);
    end
    else
    begin
      if (FStyle=rsRotation) or (FStyle=rsAll) then
      begin
        // FirstSeriesPie
        RotationFloat:=RotationChange(ParentChart.Canvas.SupportsFullRotation,RotationFloat,IDifX);
      end;

      if (FStyle=rsElevation) or (FStyle=rsAll) then
         ElevationFloat:=ElevationChange(ParentChart.Canvas.SupportsFullRotation,ElevationFloat,IDifY);
    end;
  end;
end;

type
  TCanvas3DAccess=class(TCanvas3D);
  TTeeCanvas3DAccess=class(TTeeCanvas3D);

procedure TRotateTool.ChartMouseEvent(AEvent: TChartMouseEvent;
  AButton: TMouseButton; AShift: TShiftState; X, Y: Integer);

  Procedure MouseMove(X,Y:Integer);

    Procedure DrawCubeOutline;
    var w : Integer;
        R : TRect;
    begin
      R:=ParentChart.ChartRect;

      with ParentChart.Canvas do
      begin
        Brush.Style:=bsClear;
        AssignVisiblePen(Self.Pen);

        w:=ParentChart.Width3D;

        RectangleWithZ(R,0);
        RectangleWithZ(R,w);

        with R do
        begin
          MoveTo3D(Left,Top,0);
          LineTo3D(Left,Top,W);
          MoveTo3D(Right,Top,0);
          LineTo3D(Right,Top,W);
          MoveTo3D(Left,Bottom,0);
          LineTo3D(Left,Bottom,W);
          MoveTo3D(Right,Bottom,0);
          LineTo3D(Right,Bottom,W);
        end;
      end;
    end;

  var tmpSpeed : Double;
  begin
    tmpSpeed:=(1.0+(Speed-50)*0.01);

    IDifX:=tmpSpeed*(90.0*(X-IOldX)/ParentChart.Width);
    if FInverted then IDifX:=-IDifX;

    IDifY:=tmpSpeed*(90.0*(IOldY-Y)/ParentChart.Height);
    if FInverted then IDifY:=-IDifY;

    if Pen.Visible then
    begin
      if IFirstTime then
      begin
        IOldRepaint:=ParentChart.AutoRepaint;
        ParentChart.AutoRepaint:=False;
        IFirstTime:=False;
      end
      else DrawCubeOutline;
    end;

    ParentChart.View3D:=True;

    DoRotation(IDifX,IDifY);

    IOldX:=X;
    IOldY:=Y;

    ParentChart.CancelMouse:=True;

    if Pen.Visible then
    begin
      TCanvas3DAccess(ParentChart.Canvas).FIsOrthogonal:=False;

      if ParentChart.Canvas is TTeeCanvas3D then
      begin
        TTeeCanvas3DAccess(ParentChart.Canvas).CalcTrigValues;
        TTeeCanvas3DAccess(ParentChart.Canvas).CalcPerspective(ParentChart.ChartRect);
      end;

      DrawCubeOutline;
    end;

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

begin
  inherited;

  Case AEvent of
      cmeUp: if IDragging then
             begin
               IDragging:=False;

               if Pen.Visible then
               begin
                 ParentChart.AutoRepaint:=IOldRepaint;
                 Repaint;
               end;

               if Inertia>0 then
               begin
                 if not Assigned(ITimer) then
                 begin
                   ITimer:=TTimer.Create(nil);
                   ITimer.OnTimer:=DoTimer;
                 end;

                 ITimer.Interval:=1;
                 ITimer.Enabled:=True;
               end;

               if FCursor<>crDefault then
                  TeeChangeCursor(nil,IOldCursor);
             end;

    cmeMove: if IDragging then
                MouseMove(X,Y);

    cmeDown: if AButton=Self.Button then
             begin
               if Assigned(ITimer) then
                  ITimer.Enabled:=False;

               IDragging:=True;
               IOldX:=X;
               IOldY:=Y;
               IFirstTime:=True;

               if FCursor<>crDefault then
               begin
                 IOldCursor:={$IFDEF FMX}crDefault{$ELSE}Screen.Cursor{$ENDIF};
                 TeeChangeCursor(nil,FCursor);
               end;

               ParentChart.CancelMouse:=True;
             end;
  end;
end;

class function TRotateTool.Description: String;
begin
  result:=TeeMsg_RotateTool;
end;

class function TRotateTool.LongDescription: String;
begin
  result:=TeeMsg_RotateDesc;
end;

class function TRotateTool.GetEditorClass: String;
begin
  result:='TRotateToolEditor';
end;

procedure TRotateTool.DoTimer(Sender: TObject);
begin
  ITimer.Enabled:=False;

  if not IDragging then
  begin
    DoRotation(IDifX,IDifY);
    ITimer.Enabled:=True;
  end;
end;

procedure TRotateTool.ChartEvent(AEvent: TChartToolEvent);
const
  Level = 0.01;
var tmp : Double;
begin
  inherited;

  if Inertia>0 then
  begin
    if (Abs(IDifX)>Level) or (Abs(IDifY)>Level) then
    begin
      tmp:=(100-Inertia)*0.001;

      if Abs(IDifX)>Level then
         IDifX:=IDifX-(IDifX*tmp)
      else
         IDifX:=0;

      if Abs(IDifY)>Level then
         IDifY:=IDifY-(IDifY*tmp)
      else
         IDifY:=0;
    end
    else
    if Assigned(ITimer) then
       ITimer.Enabled:=False;
  end;
end;

{ TChartImageTool }

Constructor TChartImageTool.Create(AOwner: TComponent);
begin
  inherited;
  FPicture:=TTeePicture.Create;
  FPicture.OnChange:=CanvasChanged;
  Pen.Hide;
end;

Destructor TChartImageTool.Destroy;
begin
  FPicture.Free;
  inherited;
end;

procedure TChartImageTool.Assign(Source:TPersistent);
begin
  if Source is TChartImageTool then
     FPicture.Assign(TChartImageTool(Source).FPicture);

  inherited;
end;

procedure TChartImageTool.ChartEvent(AEvent: TChartToolEvent);
var R   : TRect;
    tmp : TGraphic;
    tmpS: {$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF};
begin
  inherited;

  if (AEvent=cteBeforeDrawSeries) and Assigned(FPicture) and FPicture.Valid then
  begin
    if Assigned(Series) then
    With Series do
    begin
      R.Left:=CalcXPosValue(MinXValue);
      R.Right:=CalcXPosValue(MaxXValue);
      R.Top:=CalcYPosValue(MaxYValue);
      R.Bottom:=CalcYPosValue(MinYValue);
    end
    else
    if ParentChart.CountActiveSeries=0 then
       R:=ParentChart.ChartRect
    else
    begin
      With GetHorizAxis do
      begin
        R.Left:=CalcPosValue(Minimum);
        R.Right:=CalcPosValue(Maximum);
      end;

      With GetVertAxis do
      begin
        R.Top:=CalcYPosValue(Maximum);
        R.Bottom:=CalcYPosValue(Minimum);
      end;
    end;

    With ParentChart,Canvas do
    begin
      if ClipPoints and CanClip then
         ClipCube(ChartRect,0,Width3D);

      tmp:=FPicture.Filtered;

      if View3D and (not View3DOptions.Orthogonal) then
         StretchDraw(R,tmp,Width3D)
      else
         StretchDraw(CalcRect3D(R,Width3D),tmp);

      if Self.Pen.Visible then
      begin
        AssignVisiblePen(Self.Pen);

        tmpS:=Brush.Style;
        Brush.Style:=bsClear;

        if View3D then
           RectangleWithZ(R,Width3D)
        else
           Rectangle(R);

        Brush.Style:=tmpS;
      end;

      UnClipRectangle;
    end;
  end;
end;

class function TChartImageTool.Description: String;
begin
  result:=TeeMsg_ImageTool;
end;

class function TChartImageTool.LongDescription: String;
begin
  result:=TeeMsg_ImageToolDesc;
end;

class function TChartImageTool.GetEditorClass: String;
begin
  result:='TChartImageToolEditor';
end;

procedure TChartImageTool.SetPicture(const Value: TTeePicture);
begin
  FPicture.Assign(Value);
end;

procedure TChartImageTool.SetSeries(const Value: TChartSeries);
begin
  inherited;
  Repaint;
end;

{ TMarksToolTip }
constructor TMarksTipTool.Create(AOwner: TComponent);
begin
  inherited;

  FSystemHints:={$IFDEF FMX}False{$ELSE}True{$ENDIF};
  FStyle:=smsLabelOrValue;

  FHidePause:=2500;

  FFormat:=TTextShape.Create(nil);
  FFormat.Visible:=False;
  FFormat.TextAlignment:=taCenter;
  FFormat.CustomPosition:=True;
end;

Destructor TMarksTipTool.Destroy;
begin
  FFormat.Free;
  ITimer.Free;
  inherited;
end;

procedure TMarksTipTool.Assign(Source:TPersistent);
begin
  if Source is TMarksTipTool then
  begin
    FFormat.Assign(TMarksTipTool(Source).Format);
    FMouseAction:=TMarksTipTool(Source).MouseAction;
    FStyle      :=TMarksTipTool(Source).Style;
  end;

  inherited;
end;

procedure TMarksTipTool.HideHint;
begin
  if Chart<>nil then
  begin
    if Assigned(FOnCancel) then
       FOnCancel(Self);

    if SystemHints then
       InternalCancelHint(Chart)
    else
    if Format.Visible then
    begin
      Format.Visible:=False;
      Chart.Invalidate;
    end;
  end;
end;

procedure TMarksTipTool.TimerCancelHint(Sender:TObject);
begin
  ITimer.Enabled:=False;
  HideHint;
end;

Procedure TMarksTipTool.ChartEvent(AEvent:TChartToolEvent);
var
  tmpCanvas : ICanvasToolTips;

  procedure AddTips(ASeries:TChartSeries);
  var t : Integer;
      tmpIndex  : Integer;
      oldStyle  : TSeriesMarksStyle;
      tmpName   : String;
      tmpText   : String;
      IsCustom  : Boolean;
  begin
    oldStyle:=ASeries.Marks.Style;
    ASeries.Marks.Style:=FStyle;
    try
      tmpName:='Series_'+SeriesNameOrIndex(ASeries);

      IsCustom:=ASeries is TCustomSeries;

      for t:=ASeries.FirstValueIndex to ASeries.LastValueIndex - 1 do
      begin
        tmpText:=ASeries.ValueMarkText[t];

        if Assigned(FOnGetText) then
           FOnGetText(Self,tmpText);

        if IsCustom then
           tmpIndex:=t+1
        else
           tmpIndex:=t;

        tmpCanvas.AddToolTip(tmpName+'_'+TeeStr(tmpIndex),tmpText);

        if IsCustom then
           tmpCanvas.AddToolTip(tmpName+'_'+TeeStr(tmpIndex)+'_SeriesPointer',tmpText);
      end;
    finally
      ASeries.Marks.Style:=oldStyle;
    end;
  end;

var t : Integer;
begin
  inherited;

  if (AEvent=cteMouseLeave) and (HideDelay=0) then
     HideHint
  else
  if AEvent=cteAfterDraw then
  begin
    if Supports(ParentChart.Canvas,ICanvasToolTips) then
    begin
      tmpCanvas:=(ParentChart.Canvas as ICanvasToolTips);

      if Assigned(Series) then
         AddTips(Series)
      else
      for t:=0 to ParentChart.SeriesCount-1 do
         AddTips(ParentChart[t]);
    end
    else
    if not SystemHints then
       if Format.Visible then
          Format.Draw(ParentChart,Format.ShapeBounds);
  end;
end;

procedure TMarksTipTool.ChartMouseEvent(AEvent: TChartMouseEvent;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var tmp       : Integer;
    tmpSeries : TChartSeries;
    t         : Integer;
    tmpCancel : Boolean;
begin
  inherited;

  // The canvas does the tooltips, so we exit here.
  if Supports(ParentChart.Canvas,ICanvasToolTips) then
     Exit;

  if ((MouseAction=mtmMove) and (AEvent=cmeMove)) or
     ((MouseAction=mtmClick) and (AEvent=cmeDown)) then
  begin
    tmp:=TeeNoPointClicked;

    { find which series is under XY }
    if Assigned(Series) then
    begin
      tmpSeries:=Series;
      tmp:=tmpSeries.Clicked(x,y);
    end
    else
      tmpSeries:=ParentChart.SeriesList.Clicked(x,y,tmp);

    { if not ok then cancel hint... }
    if tmp=TeeNoPointClicked then
    begin
      tmpCancel:=True;

      { do not cancel if other MarkTipTools exist... }
      for t:=0 to Chart.Tools.Count-1 do
      if (Chart.Tools[t]<>Self) and
         (Chart.Tools[t] is TMarksTipTool) then
      begin
        tmpCancel:=False;
        break;
      end;

      if tmpCancel then
         HideHint;
    end
    else
    begin
      InternalShowHint(tmpSeries,tmp,FStyle,TeePoint(X,Y),FOnGetText,Self,Brush.Color,Font);

      if (not SystemHints) and (HideDelay>0) then
      begin
        if not Assigned(ITimer) then
        begin
          ITimer:=TTimer.Create(Self);
          ITimer.OnTimer:=TimerCancelHint;
        end;

        ITimer.Interval:=HideDelay;
        ITimer.Enabled:=True;
      end;
    end;
  end;
end;

class function TMarksTipTool.Description: String;
begin
  result:=TeeMsg_MarksTipTool;
end;

class function TMarksTipTool.LongDescription: String;
begin
  result:=TeeMsg_MarksTipDesc;
end;

class function TMarksTipTool.GetEditorClass: String;
begin
  result:='TMarksTipToolEdit';
end;

Function TMarksTipTool.Chart:TCustomChart;
begin
  if Assigned(Series) then result:=TCustomChart(Series.ParentChart)
                      else result:=TCustomChart(ParentChart);
end;

procedure TMarksTipTool.SetActive(Value: Boolean);
begin
  inherited;

  if not Active then
     HideHint;
end;

procedure TMarksTipTool.Loaded;   //#1949
Begin
  inherited;

  {$IFNDEF FMX}
  if SystemHints then
     Application.HintPause:= MouseDelay;
  {$ENDIF}
end;

procedure TMarksTipTool.SetMouseAction(Value: TMarkToolMouseAction);
begin
  FMouseAction:=Value;
  HideHint;
end;

function TMarksTipTool.GetMouseDelay: Integer;
begin
  {$IFNDEF FMX}
  if SystemHints then
     result:=Application.HintPause
  else
  {$ENDIF}
     result:=FMouseDelay;
end;

procedure TMarksTipTool.SetMouseDelay(const Value: Integer);
begin
  {$IFNDEF FMX}
  if (not (csLoading in ComponentState)) and SystemHints then
     Application.HintPause:=Value
  else
  {$ENDIF}
     FMouseDelay:=Value;
end;

function TMarksTipTool.GetFont:TTeeFont;
begin
  result:=FFormat.Font;
end;

function TMarksTipTool.GetHideDelay: Integer;
begin
  {$IFNDEF FMX}
  if SystemHints then
     result:=Application.HintHidePause
  else
  {$ENDIF}
     result:=FHidePause;
end;

procedure TMarksTipTool.SetFont(const Value:TTeeFont);
begin
  FFormat.Font:=Value;
end;

procedure TMarksTipTool.SetFormat(const Value:TTextShape);
begin
  FFormat.Assign(Value);
end;

procedure TMarksTipTool.SetHideDelay(const Value: Integer);
begin
  {$IFNDEF FMX}
  if SystemHints then
     Application.HintHidePause:=Value
  else
  {$ENDIF}
     FHidePause:=Value;
end;

{ TAnnotationCallout }

Constructor TAnnotationCallout.Create(const AOwner: TChartSeries);
begin
  inherited;

  Arrow.Hide;
  TTeePenAccess(Arrow).DefaultVisible:=False;

  Visible:=False;
  DefaultVisible:=False;
end;

procedure TAnnotationCallout.Assign(Source: TPersistent);
begin
  if Source is TAnnotationCallout then
  With TAnnotationCallout(Source) do
  begin
    Self.FX:=XPosition;
    Self.FY:=YPosition;
    Self.FZ:=ZPosition;
  end;

  inherited;
end;

procedure TAnnotationCallout.SetX(const Value: Integer);
begin
  if FX<>Value then
  begin
    FX:=Value;
    Repaint;
  end;
end;

procedure TAnnotationCallout.SetY(const Value: Integer);
begin
  if FY<>Value then
  begin
    FY:=Value;
    Repaint;
  end;
end;

procedure TAnnotationCallout.SetZ(const Value: Integer);
begin
  if FZ<>Value then
  begin
    FZ:=Value;
    Repaint;
  end;
end;

function TAnnotationCallout.CloserPoint(const R: TRect;
  const P: TPoint): TPoint;
var tmpX : TCoordinate;
    tmpY : TCoordinate;
begin
  if P.X>R.Right then tmpX:=R.Right
  else
  if P.X<R.Left then tmpX:=R.Left
  else
     tmpX:=(R.Left+R.Right) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};

  if P.Y>R.Bottom then tmpY:=R.Bottom
  else
  if P.Y<R.Top then tmpY:=R.Top
  else
     tmpY:=(R.Top+R.Bottom) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};

  result:=TeePoint(tmpX,tmpY);
end;

{ TAnnotationTool }
Constructor TAnnotationTool.Create(AOwner: TComponent);
begin
  inherited;

  FShape:=CreateShape;

  FCallout:=TAnnotationCallout.Create(nil);
  FPosition:=ppLeftTop;
  FPositionUnits:=muPixels;

  FSelected:=TTeeSelected.CreateChart(nil);
end;

function TAnnotationTool.CreateShape:TTextShape;
begin
  result:=TTextShape.Create(nil);
end;

Destructor TAnnotationTool.Destroy;
begin
  FSelected.Free;
  FreeAndNil(FCallout);
  FreeAndNil(FShape);
  inherited;
end;

procedure TAnnotationTool.Assign(Source:TPersistent);
begin
  if Source is TAnnotationTool then
  with TAnnotationTool(Source) do
  begin
    Self.FCallout.Assign(FCallout);
    Self.FPosition :=FPosition;
    Self.FPositionUnits:=FPositionUnits;
    Self.FSelected.Assign(FSelected);
    Self.FShape.Assign(FShape);
    Self.FZ:=FZ;
  end;

  inherited;
end;

procedure TAnnotationTool.ChartEvent(AEvent: TChartToolEvent);
begin
  inherited;

  if AEvent=cteMouseLeave then
     FSelected.HoverIndex:=-1
  else
  if AEvent=cteAfterDraw then
  begin
    if Assigned(FOnBeforeDraw) then
       FOnBeforeDraw(Self);

    DoDrawText;

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

Function TAnnotationTool.GetText:String;
begin
  result:=Shape.Text;
end;

procedure TAnnotationTool.SetText(const Value: String);
begin
  Shape.Text:=Value;
end;

function TAnnotationTool.GetXOffset:Integer;
begin // nothing here. See TBannerTool.
  result:=0;
end;

procedure TAnnotationTool.DoDrawText;
begin
  DoDrawText(ParentChart);
end;

procedure TAnnotationTool.DoDrawText(const AParent:TCustomAxisPanel);
var tmpTo     : TPoint;
    tmpFrom   : TPoint;
    tmpR      : TRect;
    tmpX,
    tmpY      : Integer;
    tmpPen    : TTeePen;
begin
  Shape.Text:=GetText;

  tmpR:=GetTextBounds(AParent,tmpX,tmpY);

  TShapeAccess(Shape).IDraw2D:=not FDraw3D;

  if not FDraw3D then
     AParent.Canvas.FrontPlaneBegin;

  // For TBannerTool: GetXOffset
  TShapeAccess(Shape).ITextOffset:=GetXOffset;

  if (Selected.HoverIndex=0) and Selected.Hover.Visible then
  begin
    tmpPen:=TTeePen.Create;
    tmpPen.Assign(Shape.Pen);
    Shape.Pen:=Selected.Hover.Pen;
  end
  else
    tmpPen:=nil;

  Shape.Draw(AParent,tmpR,Shape.Angle,Z);

  if Assigned(tmpPen) then
  begin
    Shape.Pen:=tmpPen;
    tmpPen.Free;
  end;

  if not FDraw3D then
     AParent.Canvas.FrontPlaneEnd;

  with Callout do
  if Visible or Arrow.Visible then
  begin
    tmpTo:=TeePoint(XPosition,YPosition);
    tmpFrom:=CloserPoint(tmpR,tmpTo);

    if Distance<>0 then
       tmpTo:=PointAtDistance(tmpFrom,tmpTo,Distance);

    {$IFDEF LCL}Self.Callout.{$ENDIF}Draw(AParent,clNone,tmpTo,TeeZeroPoint,tmpFrom,ZPosition);
  end;
end;

// Renamed from GetBounds to GetTextBounds
Function TAnnotationTool.GetTextBounds(out x,y:Integer):TRect;
begin
  result:=GetTextBounds(ParentChart,x,y);
end;

Function TAnnotationTool.GetTextBounds(Panel:TCustomAxisPanel; out x,y:Integer):TRect;
begin
  if Assigned(Panel) then
     result:=GetTextBounds(Panel,Panel.ChartBounds,x,y)
  else
     result:=TeeZeroRect;
end;

Function TAnnotationTool.GetTextBounds(const Panel:TCustomAxisPanel;
                            const ABounds:TRect; out x,y:Integer):TRect;

const
  XPadding=10;
  YPadding=10;

var tmpPanelW : Integer;
    tmpPanelH : Integer;
    tmpX   : Integer;
    tmpY   : Integer;
begin
  (*
  tmp:=GetText;
  if tmp='' then
     tmp:=' ';  { at least one space character... }
  *)

  TShapeAccess(Shape).CalcBounds(Panel);

  tmpPanelW:={$IFDEF FMX}Round{$ENDIF}(ABounds.Right-ABounds.Left+1);
  tmpPanelH:={$IFDEF FMX}Round{$ENDIF}(ABounds.Bottom-ABounds.Top+1);

  if Shape.CustomPosition then
  begin
    x:=Shape.Left;
    y:=Shape.Top;

    // Percent on parent chart size:
    if PositionUnits=muPercent then
    begin
      x:=Round(x*tmpPanelW*0.01);
      y:=Round(y*tmpPanelH*0.01);
    end;

    // Offset by parent chart Left Top position:
    Inc(x,{$IFDEF FMX}Round{$ENDIF}(ABounds.Left));
    Inc(y,{$IFDEF FMX}Round{$ENDIF}(ABounds.Top));

    result:=TeeRect(x,y,x+Shape.Width,y+Shape.Height);  //70
  end
  else
  begin
    tmpX:={$IFDEF FMX}Round{$ENDIF}(ABounds.Left+tmpPanelW-Shape.Width); //tmpW-Shape.Margins.HorizSize);
    tmpY:={$IFDEF FMX}Round{$ENDIF}(ABounds.Top+tmpPanelH-Shape.Height); //tmpH-Shape.Margins.VertSize);

    Case Position of
      ppLeftTop     : begin
                        x:={$IFDEF FMX}Round{$ENDIF}(ABounds.Left+XPadding);
                        y:={$IFDEF FMX}Round{$ENDIF}(ABounds.Top+YPadding);
                      end;
      ppLeftBottom  : begin
                        x:={$IFDEF FMX}Round{$ENDIF}(ABounds.Left+XPadding);
                        y:={$IFDEF FMX}Round{$ENDIF}(tmpY-YPadding);
                      end;
      ppRightTop    : begin
                        x:={$IFDEF FMX}Round{$ENDIF}(tmpX-XPadding);
                        y:={$IFDEF FMX}Round{$ENDIF}(ABounds.Top+YPadding);
                      end;
      ppRightBottom : begin
                        x:={$IFDEF FMX}Round{$ENDIF}(tmpX-XPadding);
                        y:={$IFDEF FMX}Round{$ENDIF}(tmpY-YPadding);
                      end;
    else
      // ppCenter:
      begin
        x:={$IFDEF FMX}Round{$ENDIF}(ABounds.Left+ABounds.Right-Shape.Width) div 2;
        y:={$IFDEF FMX}Round{$ENDIF}(ABounds.Top+ABounds.Bottom-Shape.Height) div 2;
      end
    end;

    Shape.MoveTo(x,y);

    result:=Shape.ShapeBounds;
  end;
end;

procedure TAnnotationTool.InternalSetParent(const Value: TCustomAxisPanel);
begin
  if not (csDestroying in ComponentState) then
  begin
    FParent:=Value;

    if Assigned(Shape) then
       Shape.ParentChart:=Value;

    if Assigned(Callout) then
       Callout.ParentChart:=Value;

    if Assigned(ParentChart) then
       Repaint;
  end;
end;

procedure TAnnotationTool.SetName(const Value: TComponentName);
begin
  inherited;

  if (not (csLoading in ComponentState)) and ((Owner = nil) or not (Owner is TComponent) or
     (not (csLoading in TComponent(Owner).ComponentState))) then
        Text:=Name;
end;

Procedure TAnnotationTool.SetActive(Value:Boolean);
begin
  inherited;
  Shape.Visible:=Value; // Alias Active with Shape.Visible
end;

procedure TAnnotationTool.SetParentChart(const Value: TCustomAxisPanel);
begin
  inherited;
  InternalSetParent(Value);
end;

procedure TAnnotationTool.SetSelected(const Value: TTeeSelected);
begin
  FSelected.Assign(Value);
end;

procedure TAnnotationTool.SetShape(const Value: TTextShape);
begin
  FShape.Assign(Value);
end;

class function TAnnotationTool.Description: String;
begin
  result:=TeeMsg_AnnotationTool;
end;

class function TAnnotationTool.LongDescription: String;
begin
  result:=TeeMsg_AnnotationDesc;
end;

class function TAnnotationTool.GetEditorClass: String;
begin
  result:='TAnnotationToolEdit';
end;

procedure TAnnotationTool.SetPosition(const Value: TAnnotationPosition);
begin
  if FPosition<>Value then
  begin
    FPosition:=Value;
    Shape.CustomPosition:=False;
    Repaint;
  end;
end;

Function TAnnotationTool.Clicked(x,y:Integer):Boolean;
var tmp : TRect;
begin
  if ClipText and (not Shape.CustomPosition) then
     tmp:=Shape.ShapeBounds
  else
     tmp:=Bounds;

  if FDraw3D then
     tmp:=ParentChart.Canvas.CalcRect3D(tmp,Z);

  result:=PointInRect(tmp,x,y);
end;

procedure TAnnotationTool.ChartMouseEvent(AEvent: TChartMouseEvent;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var tmp : Boolean;
begin
  if AEvent=cmeDown then
  begin
    if Assigned(FOnClick) and Clicked(x,y) then
       FOnClick(Self,Button,Shift,X,Y);

    if (ssDouble in Shift) and Assigned(FOnDblClick) and Clicked(x,y) then
       FOnDblClick(Self,Button,Shift,X,Y);
  end
  else
  if AEvent=cmeMove then
  begin
    tmp:=Clicked(x,y);

    if tmp and FSelected.Hover.Visible then
       FSelected.HoverIndex:=0
    else
       FSelected.HoverIndex:=-1;

    if tmp and (Shape.Cursor<>crDefault) then
    begin
      if not IKeepCursor then
         ParentChart.Cursor:=Shape.Cursor;

      ParentChart.CancelMouse:=True;
    end;
  end;
end;

procedure TAnnotationTool.SetTextAlign(const Value: TAlignment);
begin
  Shape.TextAlignment:=Value;
end;

procedure TAnnotationTool.Repaint;
begin
  if Assigned(ParentChart) then
     inherited
  else
     Shape.Repaint; // Needed by NumericGauge Markers
end;

procedure TAnnotationTool.SetAutoSize(const Value: Boolean);
begin
  Shape.AutoSize:=Value;
end;

procedure TAnnotationTool.SetCallout(const Value: TAnnotationCallout);
begin
  FCallout.Assign(Value);
end;

procedure TAnnotationTool.SetClipText(const Value: Boolean);
begin
  Shape.ClipText:=Value;
end;

procedure TAnnotationTool.SetPositionUnits(const Value: TTeeUnits);
begin
  if FPositionUnits<>Value then
  begin
    FPositionUnits:=Value;
    Repaint;
  end;
end;

function TAnnotationTool.GetHeight: Integer;
begin
  result:={$IFDEF FMX}Round{$ENDIF}(Shape.Height);
end;

function TAnnotationTool.GetWidth: Integer;
begin
  result:={$IFDEF FMX}Round{$ENDIF}(Shape.Width);
end;

function TAnnotationTool.IsNotAutoSize: Boolean;
begin
  result:=not AutoSize;
end;

procedure TAnnotationTool.SetCursor(const Value:TCursor);
begin
  Shape.Cursor:=Value;
end;

procedure TAnnotationTool.SetDraw3D(const Value:Boolean);
begin
  SetBooleanProperty(FDraw3D,Value);
end;

procedure TAnnotationTool.SetHeight(const Value: Integer);
begin
  if Height<>Value then
  begin
    AutoSize:=False;
    Shape.Height:=Value;
    Repaint;
  end;
end;

procedure TAnnotationTool.SetWidth(const Value: Integer);
begin
  if Width<>Value then
  begin
    AutoSize:=False;
    Shape.Width:=Value;
    Repaint;
  end;
end;

procedure TAnnotationTool.SetZ(const Value: Integer);
begin
  SetIntegerProperty(FZ,Value);
end;

function TAnnotationTool.GetLeft: Integer;
begin
  result:=Shape.Left;
end;

function TAnnotationTool.GetTop: Integer;
begin
  result:=Shape.Top;
end;

procedure TAnnotationTool.SetLeft(const Value: Integer);
var Old : TCoordinate;
begin
  Old:=Shape.Width;
  Shape.Left:=Value;
  Shape.Width:=Old;
end;

procedure TAnnotationTool.SetTop(const Value: Integer);
var Old : TCoordinate;
begin
  Old:=Shape.Height;
  Shape.Top:=Value;
  Shape.Height:=Old;
end;

function TAnnotationTool.GetBounds:TRect;
begin
  result:=GetBoundsParent(ParentChart);
end;

function TAnnotationTool.GetBoundsParent(const AParent:TCustomAxisPanel):TRect;
var x : Integer;
    y : Integer;
begin
  result:=GetTextBounds(AParent,x,y);
end;

function TAnnotationTool.GetAutoSize:Boolean;
begin
  result:=Shape.AutoSize;
end;

function TAnnotationTool.GetClipText:Boolean;
begin
  result:=Shape.ClipText;
end;

function TAnnotationTool.GetCursor:TCursor;
begin
  result:=Shape.Cursor
end;

function TAnnotationTool.GetTextAlign: TAlignment;
begin
  result:=Shape.TextAlignment;
end;

procedure TAnnotationTool.SetBounds(const Value: TRect);
begin
  Shape.ShapeBounds:=Value;
  Repaint;
end;

{ TGridBandBrush }
Constructor TGridBandBrush.Create(const OnChangeEvent:TNotifyEvent);
begin
  inherited;
  BackColor:=clNone;
end;

Procedure TGridBandBrush.Assign(Source:TPersistent);
begin
  if Source is TGridBandBrush then
  with TGridBandBrush(Source) do
  begin
    Self.FTransp:=FTransp;
  end;

  inherited;
end;

procedure TGridBandBrush.SetTransp(const Value: TTeeTransparency);
begin
  if FTransp<>Value then
  begin
    FTransp:=Value;
    Changed;
  end;
end;

{ TGridBandTool }
constructor TGridBandTool.Create(AOwner: TComponent);
begin
  inherited;
  FBand1:=TGridBandBrush.Create(CanvasChanged);
  FBand2:=TGridBandBrush.Create(CanvasChanged);
  FBand2.Color:=clWhite;
end;

type
  TAxisAccess=class(TChartAxis);

procedure TGridBandTool.CleanAxis;
begin
  if Assigned(Axis) then
     TAxisAccess(Axis).OnDrawGrids:=nil;
end;

procedure TGridBandTool.Assign(Source:TPersistent);
begin
  if Source is TGridBandTool then
  begin
    Band1.Assign(TGridBandTool(Source).Band1);
    Band2.Assign(TGridBandTool(Source).Band2);
  end;

  CleanAxis;

  inherited;
end;

class function TGridBandTool.Description: String;
begin
  result:=TeeMsg_GridBandTool;
end;

class function TGridBandTool.LongDescription: String;
begin
  result:=TeeMsg_GridBandDesc;
end;

Destructor TGridBandTool.Destroy;
begin
  CleanAxis;

  FBand2.Free;
  FBand1.Free;
  inherited;
end;

Function TGridBandTool.BandBackColor(const ABand:TGridBandBrush):TColor;
begin
  result:=ABand.BackColor;

  if (result=clNone) and Assigned(ParentChart) then
     result:=TCustomChart(ParentChart).Walls.Back.Color;
end;

Procedure TGridBandTool.DrawGrids(Sender:TChartAxis);
var tmpBand  : TGridBandBrush;
    tmpBlend : TTeeBlend;
    tmpStart : Integer;
    tmpEnd   : Integer;

  Procedure DrawBand(tmpPos1,tmpPos2:Integer);
  var tmpR : TRect;
  begin
    if tmpBand.Style<>bsClear then
    with ParentChart,Canvas do
    begin
      tmpBlend:=BeginBlending(TeeZeroRect,tmpBand.Transparency);
      try

        if tmpPos1>tmpPos2 then
           SwapInteger(tmpPos1,tmpPos2);

        if Axis.Horizontal then
           tmpR:=TeeRect(tmpPos1,tmpStart,tmpPos2,tmpEnd)
        else
           tmpR:=TeeRect(tmpStart,tmpPos1,tmpEnd,tmpPos2+1);

        if Assigned(tmpBlend) then
           if View3D then tmpBlend.SetRectangle(CalcRect3D(tmpR,Width3D))
                     else tmpBlend.SetRectangle(tmpR);

        AssignBrush(tmpBand,tmpBand.Color,BandBackColor(tmpBand));
        Pen.Style:=psClear;

        if View3D then
           RectangleWithZ(tmpR,Width3D)
        else
        begin
          tmpR.Right:=tmpR.Right+1;
          Rectangle(tmpR);
        end;

      finally
        EndBlending(tmpBlend);
      end;
    end;
  end;

  procedure SwitchBands;
  begin
    if tmpBand=Band1 then tmpBand:=Band2
                     else tmpBand:=Band1;
  end;

var t   : Integer;
    tmp : Integer;
    tmpValue : Integer;
    tmpR : TRect;
begin
  if (not Active) or (Length(Sender.Tick)=0) then
     Exit;

  tmp:=High(Sender.Tick);

  if (tmp>0) and ((Band1.Style<>bsClear) or (Band2.Style<>bsClear)) then
  begin
    ParentChart.Canvas.Pen.Style:=psClear;

    tmpBand:=Band1;

    if (StartPos=0) and (EndPos=0) then
    begin
      tmpR:=ParentChart.ChartRect;

      if Sender.Horizontal then
      begin
        tmpStart:={$IFDEF FMX}Round{$ENDIF}(tmpR.Top+1);
        tmpEnd:={$IFDEF FMX}Round{$ENDIF}(tmpR.Bottom);
      end
      else
      begin
        tmpStart:={$IFDEF FMX}Round{$ENDIF}(tmpR.Left+1);
        tmpEnd:={$IFDEF FMX}Round{$ENDIF}(tmpR.Right);
      end;
    end
    else
    begin
      tmpStart:=StartPos;
      tmpEnd:=EndPos;
    end;

    tmpValue := 0;

    with Sender do
    begin
      if tmp>1 then
         tmpValue := ((Tick[tmp - 1] - Tick[tmp - 2]) div 2);

      // first remainder band...
      if Horizontal then
      begin
        if GridCentered then
        begin
          if (Tick[0] - tmpValue > IEndPos) then
          begin
             SwitchBands;
             DrawBand(Tick[0] + tmpValue, IEndPos - 1);
             SwitchBands;
          end
          else
          begin
             DrawBand(Tick[0] + tmpValue, Tick[0] - tmpValue);
             SwitchBands;
             DrawBand(Tick[0] - tmpValue, IEndPos - 1);
          end;
        end
        else
        begin
          if Tick[0]<IEndPos then
          begin
            DrawBand(IEndPos-1,Tick[0]);
            SwitchBands;
          end;
        end;
      end
      else
        if GridCentered then
        begin
          if (Tick[0] - tmpValue > IStartPos + 1) then
          begin
            DrawBand(IStartPos + 1, Tick[0] - tmpValue);
            SwitchBands;
            DrawBand(Tick[0] - tmpValue, Tick[0] + tmpValue);
            SwitchBands;
          end
          else
          begin
            DrawBand(IStartPos + 1, Tick[0] + tmpValue);
            SwitchBands;
          end;
        end
        else
        begin
         if Tick[0]>IStartPos then
          begin
            DrawBand(IStartPos+1,Tick[0]);
            SwitchBands;
          end;
        end;

      if tmp>1 then
      begin
        // all bands...
        if GridCentered then
        begin
          for t := 2 to tmp do
          begin
            if (Tick[t - 1]-tmpValue > IStartPos) then
                DrawBand(Tick[t - 1]-tmpValue, Tick[t]-tmpValue)
              else
                DrawBand(IStartPos+1, Tick[t] - tmpValue);

            SwitchBands;
          end;
        end
        else
          for t:=1 to tmp do
          begin
            DrawBand(Tick[Pred(t)],Tick[t]);
            SwitchBands;
          end;

        // last remainder band...
        if Horizontal then
        begin
           if GridCentered then
           begin
            if (Tick[tmp] + tmpValue > IStartPos) then
            begin
              DrawBand(Tick[tmp] + tmpValue, Tick[tmp] - tmpValue);
              SwitchBands;
              DrawBand(Tick[tmp] + tmpValue, IStartPos + 1);
              SwitchBands;
            end
            else
            begin
              DrawBand(IStartPos + 1, Tick[tmp] - tmpValue);
              SwitchBands;
            end;
          end
          else
            if Tick[tmp]>IStartPos then
               DrawBand(Tick[tmp],IStartPos);
        end
        else
        begin
          if GridCentered then
          begin
            if (Tick[tmp] + tmpValue > IEndPos - 1) then
            begin
              DrawBand(IEndPos - 1, Tick[tmp] - tmpValue);
              SwitchBands;
            end
            else
            begin
              DrawBand(Tick[tmp] - tmpValue, Tick[tmp] + tmpValue);
              SwitchBands;
              DrawBand(IEndPos - 1, Tick[tmp] + tmpValue);
              SwitchBands;
            end;
          end
          else
            if Tick[tmp]<IEndPos then DrawBand(Tick[tmp],IEndPos);
        end;
      end;
    end;
  end;
end;

class function TGridBandTool.GetEditorClass: String;
begin
  result:='TGridBandToolEdit';
end;

procedure TGridBandTool.SetAxis(const Value: TChartAxis);
var Old : TCustomTeePanel;
begin
  CleanAxis;

  // Set Theme to nil to avoid reentrancy (ApplyTheme again) at inherited
  if (Value=nil) or (Value.ParentChart=nil) then
     Old:=nil
  else
  begin
    Old:=Value.ParentChart.Theme;
    Value.ParentChart.Theme:=nil;
  end;

  inherited;

  // Restore Theme
  if (Value<>nil) and (Value.ParentChart<>nil) then
     Value.ParentChart.Theme:=Old;

  if Assigned(Axis) then
     TAxisAccess(Axis).OnDrawGrids:=DrawGrids;
end;

procedure TGridBandTool.SetBand1(const Value: TGridBandBrush);
begin
  FBand1.Assign(Value);
end;

procedure TGridBandTool.SetBand2(const Value: TGridBandBrush);
begin
  FBand2.Assign(Value);
end;

{ TAxisScrollBarTool }

Constructor TAxisScrollBarTool.Create(AOwner:TComponent);
begin
  inherited;

  FAllowResize:=True;
  FMargin:=5;

  // Default precision (scrollbar step)
  Max:=1000;
end;

procedure TAxisScrollBarTool.Assign(Source:TPersistent);
begin
  if Source is TAxisScrollBarTool then
  with TAxisScrollBarTool(Source) do
  begin
    Self.FAxis:=FAxis;
    Self.FMargin:=FMargin;
  end;

  inherited;
end;

procedure TAxisScrollBarTool.WriteAxis(Writer: TWriter);
begin
  // Use Axis.ParentChart instead of Self.ParentChart to avoid potential AV.
  Writer.WriteInteger(Axis.ParentChart.Axes.IndexOf(Axis));
end;

class procedure TAxisScrollBarTool.GetAxisLimits(const Axis:TChartAxis; out ARange,AMin,AMax:Double);
begin
  ARange:=Axis.Maximum-Axis.Minimum;

  if Axis.Horizontal then
  begin
    AMin:=Axis.ParentChart.MinXValue(Axis);
    AMax:=Axis.ParentChart.MaxXValue(Axis);
  end
  else
  begin
    AMin:=Axis.ParentChart.MinYValue(Axis);
    AMax:=Axis.ParentChart.MaxYValue(Axis);
  end;
end;

function TAxisScrollBarTool.CurrentCount:Integer;
var tmp,
    tmpRange,
    tmpMin,
    tmpMax : Double;
begin
  GetAxisLimits(FAxis,tmpRange,tmpMin,tmpMax);

  tmp:=tmpMax-tmpMin;

  if IsZero(tmp) then
     result:=0
  else
     result:=Round(tmpRange*Max/tmp);
end;

procedure TAxisScrollBarTool.DefineProperties(Filer: TFiler);
begin
  inherited;
  Filer.DefineProperty('AxisID',ReadAxis,WriteAxis,Assigned(Axis));
end;

procedure TAxisScrollBarTool.ReadAxis(Reader: TReader);
var tmp : Integer;
begin
  tmp:=Reader.ReadInteger;

  if Assigned(ParentChart) then
     Axis:=ParentChart.Axes[tmp];
end;

procedure TAxisScrollBarTool.InternalSetAxis(const Value: TChartAxis);
begin
  FAxis:=Value;
end;

procedure TAxisScrollBarTool.SetAxis(const Value: TChartAxis);
begin
  if (FAxis<>Value) or (Assigned(FAxis) and (FAxis.ParentChart<>ParentChart)) then
  begin
    if Assigned(FAxis) then
       TAxisAccess(FAxis).IExtraSpace:=0;

    InternalSetAxis(Value);

    if Assigned(FAxis) then ParentChart:=FAxis.ParentChart
                       else ParentChart:=nil;

    PrepareScrollBar;
    PrepareMargin;
    //Repaint;
  end;
end;

procedure TAxisScrollBarTool.InflateThumbLength(IsInc:Boolean; const ADelta:Double);
var tmp,
    tmpMin,
    tmpMax,
    tmpRange : Double;
begin
  GetAxisLimits(Axis,tmpRange,tmpMin,tmpMax);
  tmpRange:=tmpMax-tmpMin;

  tmp:=ADelta*tmpRange/Max;

  tmpMin:=Axis.Minimum;
  tmpMax:=Axis.Maximum;
  tmpRange:=tmpMax-tmpMin;

  if tmpRange<>0 then
  begin
    if not Horizontal then
    begin
      IsInc:=not IsInc;
      tmp:=-tmp;
    end;

    if IsInc then
    begin
      if tmpMax+tmp>tmpMin then
         tmpMax:=tmpMax+tmp;
    end
    else
    begin
      if tmpMin+tmp<tmpMax then
         tmpMin:=tmpMin+tmp;
    end;

    ChangeAxisMinMax(tmpMin,tmpMax);

    Reposition;
  end;
end;

procedure TAxisScrollBarTool.Reposition;
var tmp,
    tmpMin,
    tmpMax,
    tmpRange : Double;
begin
  GetAxisLimits(FAxis,tmpRange,tmpMin,tmpMax);

  if tmpRange<>0 then
  begin
    // Recalc tmpRange with total limits
    tmpRange:=tmpMax-tmpMin;

    if tmpRange=0 then
       Position:=0
    else
    begin
      if Horizontal then
         tmp:=FAxis.Minimum-tmpMin
      else
         tmp:=tmpMax-FAxis.Maximum;

      Position:=Round(tmp*Max/tmpRange);
    end;
  end;
end;

procedure TAxisScrollBarTool.SetMargin(const Value:Integer);
begin
  SetIntegerProperty(FMargin,Value);
  PrepareMargin;
end;

procedure TAxisScrollBarTool.ChangeAxisMinMax(const AMin,AMax:Double);
var tmpAxis : TChartAxis;
begin
  FAxis.SetMinMax(AMin,AMax);

  if AnySeriesBothAxis(FAxis,tmpAxis) then
     tmpAxis.SetMinMax(AMin,AMax);
end;

procedure TAxisScrollBarTool.SetPosition(Value:Integer);
var tmpMin,
    tmpMax,
    tmpRange : Double;
begin
  inherited;

  GetAxisLimits(FAxis,tmpRange,tmpMin,tmpMax);

  if tmpRange<>0 then
  begin
    tmpMin:=tmpMin+Position*(tmpMax-tmpMin)/Max;

    if not FAxis.Horizontal then
       tmpMin:=tmpMax-tmpMin-tmpRange;

    if (not FAxis.Automatic) or
       (FAxis.Minimum<>tmpMin) or (FAxis.Maximum<>tmpMin+tmpRange) then
          ChangeAxisMinMax(tmpMin,tmpMin+tmpRange);
  end;
end;

procedure TAxisScrollBarTool.PrepareMargin;
begin
  if Assigned(Axis) then
     if Visible then
        TAxisAccess(Axis).IExtraSpace:=FSize+FMargin
     else
        TAxisAccess(Axis).IExtraSpace:=0;
end;

procedure TAxisScrollBarTool.SetSize(const Value: Integer);
begin
  inherited;
  PrepareMargin;
end;

function TAxisScrollBarTool.ShouldDraw(var R:TRect):Boolean;
var tmp : TCoordinate;
begin
  result:=Assigned(FAxis) and FAxis.Visible;

  if result then
  begin
    R:=TAxisAccess(FAxis).AxisRect;

    if FAxis.Horizontal then
    begin
      tmp:=FMargin+R.Bottom-R.Top;

      if FAxis.OtherSide then
      begin
        R.Bottom:=R.Top-FSize;
        OffsetRect(R,0,-tmp);
      end
      else
         OffsetRect(R,0,tmp);
    end
    else
    begin
      tmp:=FAxis.MaxLabelsWidth+FMargin+R.Right-R.Left;

      if FAxis.OtherSide then
      begin
        R.Right:=R.Left+FSize;
        OffsetRect(R,tmp,0);
      end
      else
         OffsetRect(R,-tmp,0);
    end;
  end;
end;

procedure TAxisScrollBarTool.ChartEvent(AEvent:TChartToolEvent);
begin
  inherited;

  if AEvent=cteBeforeDraw then
     if Axis<>nil then
        Reposition;
end;

class Function TAxisScrollBarTool.GetEditorClass:String;
begin
  result:='TAxisScrollBarEditor';
end;

Procedure TAxisScrollBarTool.SetActive(Value:Boolean);
begin
  inherited;
  PrepareMargin;
end;

procedure TAxisScrollBarTool.PrepareScrollBar;
begin
  if Assigned(Axis) then
     Horizontal:=Axis.Horizontal;
end;

class Function TAxisScrollBarTool.Description:String;
begin
  result:=TeeMsg_AxisScrollBarTool;
end;

class Function TAxisScrollBarTool.LongDescription:String;
begin
  result:=TeeMsg_AxisScrollBarDesc;
end;

procedure TAxisScrollBarTool.SetParentChart(const Value: TCustomAxisPanel);
begin
  inherited;

  if (not Assigned(FAxis)) and Assigned(ParentChart) then
     Axis:=ParentChart.Axes.Bottom
  else
     PrepareScrollBar;
end;

{ TAxisScrollTool }

procedure TAxisScrollTool.Assign(Source:TPersistent);
begin
  if Source is TAxisScrollTool then
  with TAxisScrollTool(Source) do
  begin
    Self.FLimit:=FLimit;
    Self.FScrollInverted:=FScrollInverted;
  end;

  inherited;
end;

procedure TAxisScrollTool.SetAxis(const Value: TChartAxis);
begin
  if Assigned(Value) then
     inherited
  else
    InternalSetAxis(nil);
end;

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

  Function AxisClicked:TChartAxis;
  begin
    if Assigned(Axis) and Axis.Visible and Axis.Clicked(x,y) then
       result:=Axis
    else
      if not Assigned(Axis) then
         result:=ParentChart.Axes.Clicked(x,y)
      else
         result:=nil;
  end;

var Delta : Integer;

  procedure DoAxisScroll(const AAxis:TChartAxis);
  var tmp    : Boolean;
      tmpMin : TAxisValue;
      tmpMax : TAxisValue;
      ADelta : TAxisValue;
  begin
    if AAxis.IAxisSize<>0 then
    begin
      tmp:=True;
      ADelta:=Delta*(AAxis.Maximum-AAxis.Minimum)/AAxis.IAxisSize;

      //TV52014801, trigger the OnScroll nor the OnAllowScroll events
      if Assigned(TCustomChart(ParentChart).OnAllowScroll) then
      begin
        tmpMin:=AAxis.Minimum+ADelta;
        tmpMax:=AAxis.Maximum+ADelta;
        TCustomChart(ParentChart).OnAllowScroll(AAxis,tmpMin,tmpMax,tmp);
      end;

      if tmp then
      begin
        if ADelta>0 then
           AAxis.Scroll(ADelta,(FLimit=slBoth) or (FLimit=slMaximum))
        else
           AAxis.Scroll(ADelta,(FLimit=slBoth) or (FLimit=slMinimum));

        with TCustomChart(AAxis.ParentChart) do
           if Assigned(OnScroll) then
              OnScroll(AAxis.ParentChart);
      end;
    end;
  end;

  procedure CheckOtherAxes;
  var t  : Integer;
      tt : Integer;
  begin
    with ParentChart do
    for t:=0 to Axes.Count-1 do
    if (Axes[t]<>InAxis) and (Axes[t].Horizontal=InAxis.Horizontal) then
       for tt:=0 to SeriesCount-1 do
           if Series[tt].AssociatedToAxis(InAxis) and
              Series[tt].AssociatedToAxis(Axes[t]) then
           begin
             DoAxisScroll(Axes[t]);
             break;
           end;
  end;

var tmpChart : TCustomChart;
begin
  inherited;

  if Active then
  Case AEvent of
    cmeDown: begin
               InAxis:=AxisClicked;
               OldX:=X;
               OldY:=Y;

               if Assigned(InAxis) then
               begin
                 ParentChart.CancelMouse:=True;

                 tmpChart:=TCustomChart(ParentChart);

                 if Assigned(tmpChart.OnClickAxis) then
                    tmpChart.OnClickAxis(tmpChart,InAxis,Button,Shift,x,y);
               end;
             end;

    cmeMove: begin
               if Assigned(InAxis) then
               begin
                 if InAxis.Horizontal then Delta:=OldX-X
                                      else Delta:=OldY-Y;

                 if InAxis.Inverted then Delta:=-Delta;

                 if InAxis.Horizontal then
                 begin
                   if ScrollInverted then Delta:=-Delta;
                 end
                 else
                   if not ScrollInverted then Delta:=-Delta;

                 DoAxisScroll(InAxis);

                 CheckOtherAxes;

                 OldX:=X;
                 OldY:=Y;
                 ParentChart.CancelMouse:=True;
               end
               else
               if AxisClicked<>nil then
               begin
                 ParentChart.Cursor:=crHandPoint;
                 ParentChart.CancelMouse:=True;
               end;
             end;

    cmeUp: InAxis:=nil;
  end;
end;

class Function TAxisScrollTool.GetEditorClass:String;
begin
  result:='TAxisScrollEditor';
end;

class function TAxisScrollTool.Description: String;
begin
  result:=TeeMsg_AxisScrollTool;
end;

class function TAxisScrollTool.LongDescription: String;
begin
  result:=TeeMsg_AxisScrollDesc;
end;

type
  TShapePosAccess=class(TTeeCustomShapePosition);

{ TRectangleTool }
Constructor TRectangleTool.Create(AOwner:TComponent);
begin
  inherited;

  IKeepCursor:=True;
  
  AutoSize:=False;
  IEdge:=-1;

  TShapePosAccess(Shape).DefaultCustom:=True;
  TShapePosAccess(Shape).FCustomPosition:=True;

  with Shape do
  begin
    Emboss.Size:=0;
    Shadow.Size:=0;
    Transparency:=75;
    ShapeBounds.Left:=10;
    ShapeBounds.Top:=10;
    Width:=50;
    Height:=50;
  end;

  FAllowDrag:=True;
  FAllowResize:=True;

  PositionUnits:=muPixels;
  Cursor:=crHandPoint;
end;

procedure TRectangleTool.Assign(Source:TPersistent);
begin
  if Source is TRectangleTool then
  with TRectangleTool(Source) do
  begin
    Self.FAllowDrag   :=FAllowDrag;
    Self.FAllowResize :=FAllowResize;
  end;

  inherited;
end;

Function TRectangleTool.ClickedEdge(x,y:Integer):Integer;
const
  Tolerance=4;

var R : TRect;
begin
  result:=-1;

  if Clicked(x,y) then
  begin
    R:=Bounds;

    if Abs(x-R.Left)<Tolerance then
       if Abs(y-R.Top)<Tolerance then
          result:=4
       else
       if Abs(y-R.Bottom)<Tolerance then
          result:=5
       else
          result:=0
    else
    if Abs(y-R.Top)<4 then
       if Abs(x-R.Right)<Tolerance then
          result:=6
       else
          result:=1
    else
    if Abs(x-R.Right)<4 then
       if Abs(y-R.Bottom)<Tolerance then
          result:=7
       else
          result:=2
    else
    if Abs(y-R.Bottom)<4 then
       result:=3;
  end;
end;

function TRectangleTool.GetResizing: Boolean;
begin
  result:=IEdge=-1;
end;

class Function TRectangleTool.Description:String;
begin
  result:=TeeMsg_RectangleTool;
end;

class Function TRectangleTool.LongDescription:String;
begin
  result:=TeeMsg_RectangleToolDesc;
end;

Procedure TRectangleTool.ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button:TMouseButton;
                               Shift: TShiftState; X, Y: Integer);

  Function GuessEdgeCursor:Boolean;

    procedure TrySet(const ACursor:TCursor);
    begin
      if AllowResize then
      begin
        ParentChart.Cursor:=ACursor;
        result:=True;
      end;
    end;

  begin
    result:=False;

    case ClickedEdge(x,y) of
      0,2: TrySet(crSizeWE);
      1,3: TrySet(crSizeNS);
      5,6: TrySet(crSizeNESW);
      4,7: TrySet(crSizeNWSE);
    else
      if Clicked(x,y) and (Cursor<>crDefault) then
      begin
        ParentChart.Cursor:=Cursor;
        result:=True;
      end;
    end;
  end;

  procedure StartResizing;
  begin
    if (IEdge=2) or (IEdge=6) or (IEdge=7) then
       P.X:=X-Bounds.Right
    else
       P.X:=X-Bounds.Left;

    if (IEdge=3) or (IEdge=5) or (IEdge=7) then
       P.Y:=Y-Bounds.Bottom
    else
       P.Y:=Y-Bounds.Top;

    ParentChart.CancelMouse:=True;
  end;

  procedure StartDragging;
  begin
    P.X:=X-Bounds.Left;
    P.Y:=Y-Bounds.Top;

    IDrag:=True;
    ParentChart.CancelMouse:=True;
  end;

  procedure ChangeLeftTop(const ALeft,ATop:Integer);
  begin
    if PositionUnits=muPixels then
       Shape.MoveTo(ALeft,ATop)
    else
       Shape.MoveTo(Round(ALeft*100/ParentChart.Width),Round(ATop*100/ParentChart.Height));
  end;

  procedure TryDrag;
  begin
    if (X<>P.X) or (Y<>P.Y) then
    begin
      Shape.CustomPosition:=True;
      ChangeLeftTop({$IFDEF FMX}Round{$ENDIF}(X-P.X),
                    {$IFDEF FMX}Round{$ENDIF}(Y-P.Y));

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

  procedure DoResize;
  var
    tmpR : TRect;

    procedure ChangeLeft;
    begin
      tmpR.Left:=Min(tmpR.Right-3,X-P.X);
    end;

    procedure ChangeTop;
    begin
      tmpR.Top:=Min(tmpR.Bottom-3,Y-P.Y);
    end;

    procedure ChangeRight;
    begin
      tmpR.Right:=Max(tmpR.Left+3,X-P.X);
    end;

    procedure ChangeBottom;
    begin
      tmpR.Bottom:=Max(tmpR.Top+3,Y-P.Y);
    end;

  begin
    tmpR:=Bounds;

    case IEdge of
      0: ChangeLeft;
      1: ChangeTop;
      2: ChangeRight;
      3: ChangeBottom;
      4: begin
           ChangeLeft;
           ChangeTop;
         end;
      5: begin
           ChangeLeft;
           ChangeBottom;
         end;
      6: begin
           ChangeRight;
           ChangeTop;
         end;
      7: begin
           ChangeRight;
           ChangeBottom;
         end;
    end;

    if PositionUnits=muPixels then
       Shape.ShapeBounds:=tmpR
    else
       ChangeLeftTop({$IFDEF FMX}Round{$ENDIF}(tmpR.Left),
                     {$IFDEF FMX}Round{$ENDIF}(tmpR.Top));

    Repaint;

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

  procedure StopDrag;
  begin
    if Assigned(FOnDragged) then
       FOnDragged(Self);

    IDrag:=False;
  end;

  procedure StopResize;
  begin
    if Assigned(FOnResized) then
       FOnResized(Self);

    IEdge:=-1;
  end;

begin
  inherited;

  if Active then
  case AEvent of
    cmeDown: if Button=mbLeft then
             begin
               if AllowResize then
                  IEdge:=ClickedEdge(x,y)
               else
                  IEdge:=-1;

               if IEdge<>-1 then
                  StartResizing
               else
               if AllowDrag and Clicked(x,y) then
                  StartDragging;
             end;

      cmeUp: if IDrag then
                 StopDrag
             else
             if IEdge<>-1 then
                StopResize;

    cmeMove: if IDrag then
                TryDrag
             else
             if IEdge<>-1 then
             begin
               AutoSize:=False;
               DoResize;
             end
             else
             if Assigned(ParentChart) then
                ParentChart.CancelMouse:=GuessEdgeCursor;
  end;
end;

// Returns chart bitmap under rectangle shape
Function TRectangleTool.Bitmap(ChartOnly:Boolean=False):TBitmap;

  Procedure CopyResult(ACanvas:TCanvas);
  begin
    {$IFNDEF FMX}
    result.Canvas.CopyRect( TeeRect(result.Width,result.Height),
                            ACanvas,Bounds);
    {$ENDIF}
  end;

var tmp       : TTeeCanvas3D;
    OldActive : Boolean;
    tmpB      : TBitmap;
begin
  result:=TBitmap.Create{$IFDEF FMX}(Round(Shape.Width),Round(Shape.Height)){$ENDIF};

  {$IFNDEF FMX}
  TeeSetBitmapSize(result,Shape.Width,Shape.Height);
  {$ENDIF}

  if ChartOnly then
  begin
    OldActive:=Active;
    Active:=False;
    try
      tmpB:=ParentChart.TeeCreateBitmap(ParentChart.Color,ParentChart.ChartBounds);
      try
        CopyResult(tmpB.Canvas);
      finally
        tmpB.Free;
      end;
    finally
      Active:=OldActive;
    end;
  end
  else
  if ParentChart.Canvas is TTeeCanvas3D then
  begin
    tmp:=TTeeCanvas3D(ParentChart.Canvas);

    if not Assigned(tmp.Bitmap) then
       ParentChart.Draw;

    CopyResult(tmp.Bitmap.Canvas);
  end;
end;

{ TClipTool }

Procedure TClipSeriesTool.SeriesEvent(AEvent:TChartToolEvent; ASeries:TChartSeries);
var tmpR : TRect;
begin
  if Assigned(ParentChart) and ParentChart.CanClip and (ASeries=Series) then
     if AEvent=cteBeforeDrawSeries then
     begin
       tmpR.Left:=Series.GetHorizAxis.IStartPos;
       tmpR.Right:=Series.GetHorizAxis.IEndPos;
       tmpR.Top:=Series.GetVertAxis.IStartPos;
       tmpR.Bottom:=Series.GetVertAxis.IEndPos;

       ParentChart.Canvas.ClipRectangle(tmpR);
     end
     else
     if AEvent=cteAfterDrawSeries then
        ParentChart.Canvas.UnClipRectangle;
end;

class Function TClipSeriesTool.Description:String;
begin
  result:=TeeMsg_ClipSeries;
end;

class Function TClipSeriesTool.LongDescription:String;
begin
  result:=TeeMsg_ClipSeriesDesc;
end;

{ TFullScreenTool }

Constructor TFullScreenTool.Create(AOwner: TComponent);
begin
  inherited;
  FKey:=TeeKey_Escape;
  IFreeForm:=True;
  Active:=False;
end;

Destructor TFullScreenTool.Destroy;
begin
  if Active then
     RemoveFullScreen;

  inherited;
end;

procedure TFullScreenTool.RemoveFullScreen;
{$IFNDEF TEEOCX}
var tmp : {$IFDEF FMX}TFmxObject{$ELSE}TWinControl{$ENDIF};
{$ENDIF}
begin
  {$IFNDEF TEEOCX}
  tmp:=ParentChart.Parent;

  if Assigned(tmp) and (tmp is TCustomForm) and
     (tmp.Tag=ObjectToTag(Self)) then
  begin
    tmp.Tag:=0;

    if (not Assigned(OldParent)) or
       (not (csDestroying in OldParent.ComponentState)) then
         ParentChart.Parent:=OldParent
    else
        ParentChart.Parent:=nil;

    ParentChart.BoundsRect:=OldRChart;
    ParentChart.Align:=OldAlign;

    if IFreeForm then
       tmp.Free;

    if Assigned(FOnReset) then
       FOnReset(Self);
  end;
  {$ELSE}
  if (Assigned(tmpForm)) and IFreeForm then
  Begin
     tmpChart.Free;
     tmpForm.Free;
     isFullScreenRunning := False;
  end;
  {$ENDIF}
end;

{$IFDEF D6}
{$IFNDEF LCL}
{$DEFINE USEDESKTOPRECT}
{$ENDIF}
{$ENDIF}

class function TFullScreenTool.ScreenDesktopRect:TRect;
begin
  {$IFDEF USEDESKTOPRECT}
    {$IFDEF FMX}
    result.Left:=0;
    result.Top:=0;
    result.BottomRight:=TeeGetScreenSize;
    {$ELSE}
    result:=Screen.DesktopRect;
    {$ENDIF}
  {$ELSE}

  with Screen do
       {$IFDEF LCL}
       result:=Bounds(0,0,Width,Height);
       {$ELSE}
       result:=Bounds(DesktopLeft, DesktopTop, DesktopWidth, DesktopHeight);
       {$ENDIF}

  {$ENDIF}
end;

procedure TFullScreenTool.SetActive(Value: Boolean);

  procedure ActivateFullScreen;
  var
    {$IFNDEF TEEOCX}
    tmpForm : TForm;
    {$ELSE}
    tmpStream : TStream;
    tmpChart2  : TCustomChart;
    i : Integer;
    {$ENDIF}
    {$IFDEF FMX}
    P : TPoint;
    {$ENDIF}
  begin
    OldParent:=ParentChart.Parent;

    tmpForm:=TForm.CreateNew(nil {$IFDEF LCL},0{$ENDIF});

    tmpForm.Tag:=ObjectToTag(Self);

    {$IFNDEF FMX}
    tmpForm.KeyPreview:=True;
    {$ENDIF}

    tmpForm.BorderStyle:={$IFDEF FMX}
                         TFmxFormBorderStyle.{$IFDEF D20}None{$ELSE}bsNone{$ENDIF}
                         {$ELSE}
                         bsNone
                         {$ENDIF};

    {$IFDEF FMX}
    P:=TeeGetScreenSize;
    tmpForm.SetBounds(0,0,Round(P.X),Round(P.Y));
    {$ELSE}
    tmpForm.BoundsRect:=ScreenDesktopRect;
    {$ENDIF}

    {$IFNDEF TEEOCX}
    OldAlign:=ParentChart.Align;
    ParentChart.Align:={$IFDEF FMX}TAlignLayout.{$IFDEF D20}None{$ELSE}alNone{$ENDIF}{$ELSE}alNone{$ENDIF};

    ParentChart.Parent:=tmpForm;

    OldRChart:=ParentChart.BoundsRect;
    ParentChart.BoundsRect:=ScreenDesktopRect;

    {$ELSE}

    if not isFullScreenRunning then
    Begin
      isFullScreenRunning := True;
      tmpChart:=TChart.Create(nil);
      tmpChart2:=TChart.Create(nil);
      tmpStream:=TMemoryStream.Create;
      SaveChartToStream(TCustomChart(ParentChart),tmpStream,True,False);
      tmpStream.Position :=0;

      LoadChartFromStream(TCustomChart(tmpChart2),tmpStream);

      for i:=0 to tmpChart2.Tools.Count-1 do
      Begin
        if tmpChart2.Tools[i] is TFullScreenTool then
          tmpChart2.Tools.Remove(tmpChart2.Tools[i]);
      end;

      tmpStream:=TMemoryStream.Create;
      SaveChartToStream(TCustomChart(tmpChart2),tmpStream,True,False);
      tmpStream.Position :=0;

      LoadChartFromStream(TCustomChart(tmpChart),tmpStream);
      tmpChart.Parent:=tmpForm;

      tmpChart.BoundsRect:=ScreenDesktopRect;
    end;
    {$ENDIF}

    {$IFNDEF FMX}
    tmpForm.OnKeyUp:=FormKeyUp;
    {$ENDIF}

    tmpForm.OnCloseQuery:=FormCloseQuery;

    tmpForm.Show;

    // tmpForm.FormStyle:=fsStayOnTop; (workaround when Active=True at FormCreate)
  end;

begin
  inherited;

  if (not (csDesigning in ComponentState)) and Assigned(ParentChart) then
     if Value then
        ActivateFullScreen
     else
        RemoveFullScreen;
end;

{$IFNDEF FMX}
procedure TFullScreenTool.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if Active and (Key=FKey) then
  begin
    Active:=False;
    Key:=0;
  end;
end;
{$ENDIF}

procedure TFullScreenTool.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var tmp : TeeNativeInt;
begin
  tmp:=(Sender as TForm).Tag;

  if tmp<>0 then
  with TFullScreenTool(tmp) do
  begin
    IFreeForm:=False;
    try
      Active:=False;
    finally
      IFreeForm:=True;
    end;
  end;

  CanClose:=True;
end;

class function TFullScreenTool.Description: String;
begin
  result:=TeeMsg_FullScreen;
end;

class function TFullScreenTool.LongDescription: String;
begin
  result:=TeeMsg_FullScreenDesc;
end;

{ TRepaintMonitor }

Constructor TRepaintMonitor.Create(AOwner:TComponent);
begin
  inherited;

  Shape.Shadow.Visible:=False;
  Shape.Color:=clDkGray;
  Shape.Font.Color:=clWhite;
  Shape.Transparency:=20;

  FInterval:=1000;

  IMemory:=TTeeCPU.TotalMemoryAllocated;
  FMonitorTime:=TeeTickCount;

  ShowChart:=True;
end;

Destructor TRepaintMonitor.Destroy;
begin
  IBitmap.Free;
  FChart.Free;
  inherited;
end;

function TRepaintMonitor.CalcText:Cardinal;
begin
  case FMode of
    rmFrames: result:=ILastMonitor;
      rmMsec: result:=IMsec;
    rmMemory: result:=(TTeeCPU.TotalMemoryAllocated-IMemory) div 1024;
  else
    result:=TTeeCPU.TotalMemoryAllocated div 1024;
  end;

  case FMode of
    rmFrames: FMonitorText:=IntToStr(result)+'fps '+IntToStr(FTotalFrames);
      rmMsec: FMonitorText:=IntToStr(result)+'ms';
  else
    FMonitorText:=IntToStr(result)+'KB';
  end;
end;

Procedure TRepaintMonitor.ChartEvent(AEvent:TChartToolEvent);

  procedure RefreshChart(const Value:Cardinal);
  var tmpSeries : TChartSeries;
  begin
    tmpSeries:=FChart[0];

    if tmpSeries.Count>100 then
    begin
      tmpSeries.Delete(0);
      tmpSeries.NotMandatoryValueList.FillSequence;
    end;

    tmpSeries.Add(Value);
  end;

  procedure RecreateBitmap(const R:TRect);
  begin
    IBitmap.Free;
    FChart.Color:=Shape.Color;
    IBitmap:=FChart.TeeCreateBitmap(Shape.Color,R);
  end;

  function DifferentSize(const R:TRect):Boolean;
  begin
    result:=((R.Right-R.Left)<>IBitmap.Width) or
            ((R.Bottom-R.Top)<>IBitmap.Height);
  end;

var tmp : Single;
    tmpValue : Cardinal;
    tmpH : Integer;
    tmpR : TRect;
begin
  inherited;

  if AEvent=cteAfterDraw then
  begin
    Inc(FTotalFrames);
    Inc(FMonitor);

    tmp:=TeeTickCount;

    IMsec:=Round(tmp-ILastMsec);
    ILastMsec:=tmp;

    if tmp-ILastSecond>1000 then
    begin
      ILastMonitor:=FMonitor;
      FMonitor:=0;
      ILastSecond:=tmp;
    end;

    if Shape.Visible then
       tmpH:=ParentChart.Canvas.FontHeight
    else
       tmpH:=0;

    tmpR:=TeeRect(0,0,Width-8,Height-8-tmpH);

    if tmp-FMonitorTime>FInterval then
    begin
      FMonitorTime:=tmp;

      tmpValue:=CalcText;

      if Assigned(FChart) and FChart.Visible and (FChart.SeriesCount>0) and (tmpValue>0) then
      begin
        RefreshChart(tmpValue);
        RecreateBitmap(tmpR);
      end;
    end
    else
      if Assigned(IBitmap) and DifferentSize(tmpR) then
         RecreateBitmap(tmpR);

    if Assigned(IBitmap) then
       ParentChart.Canvas.Draw(Left+4,Top+tmpH+4,IBitmap);
  end;
end;

function TRepaintMonitor.GetChart:TChart;
begin
  if not Assigned(FChart) then
  begin
    FChart:=TChart.Create(Self);

    FChart.Visible:=False;
    FChart.Legend.Hide;
    FChart.View3D:=False;
    FChart.Walls.Hide;
    FChart.Axes.Hide;
    FChart.BevelOuter:=bvNone;
    FChart.Title.Hide;

    FChart.AddSeries(TFastLineSeries);
  end;

  result:=FChart;
end;

procedure TRepaintMonitor.SetMode(const Value:TRepaintMonitorMode);
begin
  if FMode<>Value then
  begin
    FMode:=Value;

    CalcText;

    if Assigned(FChart) and (FChart.SeriesCount>0) then
       FChart[0].Clear;

    Repaint;
  end;
end;

class Function TRepaintMonitor.GetEditorClass:String;
begin
  result:='TRepaintMonitorEditor';
end;

function TRepaintMonitor.GetText:String;
begin
  if Shape.Visible then
     result:=FMonitorText
  else
     result:='';
end;

class function TRepaintMonitor.Description:String;
begin
  result:=TeeMsg_RepaintMonitor;
end;

class function TRepaintMonitor.LongDescription:String;
begin
  result:=TeeMsg_RepaintMonitorDesc;
end;

function TRepaintMonitor.GetShowChart:Boolean;
begin
  result:=Assigned(FChart) and FChart.Visible;
end;

procedure TRepaintMonitor.SetShowChart(const Value:Boolean);
begin
  if Value or Assigned(FChart) then
     Chart.Visible:=Value;

  if Value then
  begin
    Width:=70;
    Height:=50;
  end
  else
  begin
    AutoSize:=True;
    FreeAndNil(IBitmap);
  end;
end;

{$R TeeLegendScrollBar.res}

type
  TToolAccess=class(TTeeCustomTool);

{ TScrollBarThumb }

Constructor TScrollBarThumb.Create(const AOwner:TTeeCustomTool);
begin
  inherited Create;
  IScroll:=AOwner;

  Brush.Color:=clSilver;
  Brush.BackColor:=clSilver;

  Pen.Hide;
end;

procedure TScrollBarThumb.Assign(Source:TPersistent);
begin
  if Source is TScrollBarThumb then
  with TScrollBarThumb(Source) do
  begin
    Self.FSize:=FSize;
    Self.FStyle:=FStyle;
  end;

  inherited;
end;

function TScrollBarThumb.GetGradient:TTeeGradient;
begin
  result:=Brush.Gradient;
end;

procedure TScrollBarThumb.SetGradient(const Value: TTeeGradient);
begin
  Brush.Gradient:=Value;
end;

function TScrollBarThumb.GetImage:TTeePicture;
begin
  result:=Brush.Image;
end;

type
  TBrushAccess=class(TTeeBrush);

function TScrollBarThumb.HasImage:Boolean;
begin
  result:=TBrushAccess(Brush).HasImage;
end;

procedure TScrollBarThumb.SetImage(const Value: TTeePicture);
begin
  Brush.Image:=Value;
end;

procedure TScrollBarThumb.SetSize(const Value: Integer);
begin
  TToolAccess(IScroll).SetIntegerProperty(FSize,Value);
end;

{ This function loads a bitmap from a resource linked to the executable }
Procedure LoadBitmapFromResourceName(ABitmap:TBitmap; const ResName: string);
begin
  {$IFDEF FMX}
  TeeLoadBitmap(ABitmap,ResName,'');
  {$ELSE}
  ABitmap.LoadFromResourceName(HInstance,ResName);
  {$ENDIF}
end;

procedure TScrollBarThumb.SetStyle(const Value: TScrollThumbStyle);
begin
  if FStyle<>Value then
  begin
    FStyle:=Value;

    case FStyle of
      stsThumb1: LoadBitmapFromResourceName(Image.Bitmap,'TeeThumb1'); // Do not localize
      stsThumb2: LoadBitmapFromResourceName(Image.Bitmap,'TeeThumb2'); // Do not localize
      stsThumb3: LoadBitmapFromResourceName(Image.Bitmap,'TeeThumb3'); // Do not localize
      stsThumb4: LoadBitmapFromResourceName(Image.Bitmap,'TeeThumb4'); // Do not localize
    else
      // stsCustom:
      Image.Graphic:=nil;
    end;

    if Image.Valid then
    begin
      FSize:=Image.Graphic.Width;
      TTeeScrollBar(IScroll).FSize:=Image.Graphic.Height;
    end;

    TToolAccess(IScroll).CanvasChanged(Self);
  end;
end;

{ TTeeScrollBar }

Constructor TTeeScrollBar.Create(AOwner: TComponent);
begin
  inherited;

  FAutoRepeat:=True;
  FirstTime:=True;
  FMinSize:=4;
  FSize:=DefaultScrollBarSize;
  FInitial:=250;

  BackColor:={$IFNDEF FMX}ColorToRGB{$ENDIF}(clTeeScrollBar);
  Brush.Color:=clWhite;
  Pen.Color:={$IFDEF FMX}claDarkGray{$ELSE}ColorToRGB(clWindowFrame){$ENDIF};

  FArrowBrush:=TTeeBrush.Create(CanvasChanged);
  FArrowBrush.BackColor:=clDkGray;

  FArrows:=True;

  FDisabledArrow:=TTeeBrush.Create(CanvasChanged);
  FDisabledArrow.BackColor:=clWhite;

  FThumb:=TScrollBarThumb.Create(Self);
  FBevel:=bvRaised;
end;

Destructor TTeeScrollBar.Destroy;
begin
  FreeAndNil(FThumb);
  FDisabledArrow.Free;
  FArrowBrush.Free;
  FTimer.Free;

  inherited;
end;

function TTeeScrollBar.ClickedThumb(const P:TPoint): Boolean;
begin
  result:=PointInRect(ThumbRectangle,P);
end;

function TTeeScrollBar.ClickedInc(const P:TPoint): Boolean;
begin
  result:=Arrows and PointInRect(IncRectangle,P);
end;

function TTeeScrollBar.ClickedDec(const P:TPoint): Boolean;
begin
  result:=Arrows and PointInRect(DecRectangle,P);
end;

procedure TTeeScrollBar.EnsureVisible(AIndex:Integer);
begin
  if AIndex<Position then
     Position:=AIndex
  else
  if AIndex>=Position+CurrentCount then
     Position:=1+AIndex-CurrentCount;
end;

Function TTeeScrollBar.MainRectangle:TRect;
begin
  if Horizontal then
     result:=TeeRect(R.Left,R.Bottom,R.Right,R.Bottom+FSize)
  else
     result:=TeeRect(R.Right-FSize-1,R.Top,R.Right,R.Bottom);
end;

Function TTeeScrollBar.ScrollRectangle:TRect;
begin
  result:=MainRectangle;

  if Arrows then
  begin
    if Horizontal then
    begin
      result.Left:=result.Left+FSize;
      result.Right:=result.Right-FSize;
    end
    else
    begin
      result.Top:=result.Top+FSize;
      result.Bottom:=result.Bottom-FSize;
    end;
  end;
end;

Function TTeeScrollBar.ThumbRectangle:TRect;
begin
  if Horizontal then
  begin
    result:=TeeRect(ThumbBegin,R.Bottom+1,ThumbEnd,R.Bottom+FSize-1);

    if result.Right-result.Left<2 then
       result.Right:=result.Right+1;
  end
  else
     result:=TeeRect(R.Right-FSize,ThumbBegin,R.Right,ThumbEnd);
end;

Function TTeeScrollBar.DecRectangle:TRect;
begin
  if Horizontal then
     result:=TeeRect(R.Left,R.Bottom,R.Left+FSize,R.Bottom+FSize)
  else
     result:=TeeRect(R.Right-FSize-1,R.Top,R.Right,R.Top+FSize);
end;

Function TTeeScrollBar.IncRectangle:TRect;
begin
  if Horizontal then
     result:=TeeRect(R.Right-FSize,R.Bottom,R.Right,R.Bottom+FSize)
  else
     result:=TeeRect(R.Right-FSize-1,R.Bottom-FSize,R.Right,R.Bottom);
end;

procedure TTeeScrollBar.DecArrow(out P:TTrianglePoints);
var tmpMargin : Integer;
begin
  tmpMargin:=FSize div 6;

  // Top Arrow
  with DecRectangle do
  begin
    if Horizontal then
    begin
      P[0]:=TeePoint(Left+tmpMargin,Top+(FSize div 2));
      P[1]:=TeePoint(Left+FSize-tmpMargin,Top+tmpMargin);
      P[2]:=TeePoint(Left+FSize-tmpMargin,Bottom-tmpMargin);
    end
    else
    begin
      P[0]:=TeePoint(Left+tmpMargin,Top+FSize-2*tmpMargin);
      P[1]:=TeePoint(Left+(FSize div 2),Top+tmpMargin);
      P[2]:=TeePoint(Left+FSize-tmpMargin,Top+FSize-2*tmpMargin);
    end;
  end;
end;

procedure TTeeScrollBar.IncArrow(out P:TTrianglePoints);
var tmpMargin : Integer;
begin
  tmpMargin:=FSize div 6;

  // Bottom Arrow
  if Horizontal then
  begin
    with IncRectangle do
    begin
      P[0]:=TeePoint(Right-tmpMargin,Top+(FSize div 2));
      P[1]:=TeePoint(Right-FSize+tmpMargin,Top+tmpMargin);
      P[2]:=TeePoint(Right-FSize+tmpMargin,Bottom-tmpMargin);
    end;
  end
  else
  begin
    with IncRectangle do
    begin
      P[0]:=TeePoint(Left+tmpMargin,Bottom-FSize+2*tmpMargin);
      P[1]:=TeePoint(Left+(FSize div 2),Bottom-tmpMargin);
      P[2]:=TeePoint(Left+FSize-tmpMargin,Bottom-FSize+2*tmpMargin);
    end;
  end;
end;

procedure TTeeScrollBar.DrawArrow(IsDisabled:Boolean; const P:TTrianglePoints);
begin
  if IsDisabled then
     ParentChart.Canvas.AssignBrush(FDisabledArrow)
  else
     ParentChart.Canvas.AssignBrush(FArrowBrush);

  ParentChart.Canvas.Polygon(P);
end;

procedure TTeeScrollBar.Draw;

  procedure DrawArrows;
  var P: TTrianglePoints;
  begin
    ParentChart.Canvas.Pen.Style:=psClear;

    DecArrow(P);
    DrawArrow((FIn=spArrowDec) or (Position=0),P);
    FDecOutline:=False;

    IncArrow(P);
    DrawArrow((FIn=spArrowInc) or (Position=TotalCount-CurrentCount),P);
    FIncOutline:=False;
  end;

  procedure DrawThumb;
  var Num         : Integer;
      tmpPen      : TCoordinate;
      tmpSize     : TCoordinate;
      tmpItemSize : Double;
      tmpR        : TRect;
      IThumbSize  : Integer;
      tmp         : TGraphic;
      tmpMargins  : Integer;
  begin
    Num:=TotalCount;

    if Num<>0 then
    begin
      if Arrows then
         tmpMargins:=FSize
      else
         tmpMargins:=0;

      if FHorizontal then
         tmpSize:=R.Right-R.Left-2*tmpMargins
      else
         tmpSize:=R.Bottom-R.Top-2*tmpMargins;

      if FThumb.Size=0 then
      begin
        tmpItemSize:=tmpSize/Num;
        IThumbSize:=Math.Max(FMinSize,Round(CurrentCount*tmpItemSize));
      end
      else
      begin
        IThumbSize:=FThumb.Size;
        tmpItemSize:=tmpSize/Math.Max(1,(Num-CurrentCount+1));
      end;

      if FHorizontal then
         ThumbBegin:=R.Left
      else
         ThumbBegin:=R.Top;

      ThumbBegin:=ThumbBegin+tmpMargins+Round(Position*tmpItemSize);

      ThumbEnd:=ThumbBegin+IThumbSize+1;

      // Restrict thumb rectangle to inside scrollbar

      if FHorizontal then
      begin
        if ThumbBegin<R.Left+tmpMargins then
           ThumbBegin:=R.Left+tmpMargins;

        if ThumbEnd>R.Right-tmpMargins then
           ThumbEnd:=R.Right-tmpMargins;
      end
      else
      begin
        if ThumbBegin<R.Top+tmpMargins then
           ThumbBegin:=R.Top+tmpMargins;

        if ThumbEnd>R.Bottom-tmpMargins then
           ThumbEnd:=R.Bottom-tmpMargins;   
      end;

      // Thumb

      tmpR:=ThumbRectangle;

      if FThumb.HasImage then
         tmp:=FThumb.Image.Filtered
      else
         tmp:=nil;

      if Assigned(tmp) and (not tmp.{$IFDEF FMX}IsEmpty{$ELSE}Empty{$ENDIF}) then
      begin
        tmpR.Right:=tmpR.Right-1;
        ParentChart.Canvas.StretchDraw(tmpR,tmp);
        tmpR.Right:=tmpR.Right+1;
      end
      else
      begin
        ParentChart.Canvas.AssignVisiblePen(FThumb.Pen);
        ParentChart.Canvas.AssignBrush(FThumb.Brush);

        if FThumb.Pen.Visible then
        begin
          tmpPen:=-FThumb.Pen.HalfWidth;
          InflateRect(tmpR,tmpPen,tmpPen);
        end;

        if Arrows or (RoundSize=0) then
           ParentChart.Canvas.Rectangle(tmpR)
        else
        if RoundSize<>0 then
           ParentChart.Canvas.RoundRect(tmpR,RoundSize,RoundSize);
      end;

      if FBevel<>bvNone then
      begin
        tmpR.Right:=tmpR.Right-1;
        DrawBevel(ParentChart.Canvas,FBevel,tmpR,1);
      end;
    end;
  end;

var tmpCanvas: TCanvas3D;
begin
  if Assigned(ParentChart) and ShouldDraw(R) then
  begin
    tmpCanvas:=ParentChart.Canvas;

    tmpCanvas.AssignVisiblePen(Pen);
    tmpCanvas.AssignBrush(Brush);

    if RoundSize=0 then
       tmpCanvas.Rectangle(MainRectangle)
    else
       tmpCanvas.RoundRect(MainRectangle,RoundSize,RoundSize);

    if Arrows then
    begin
      tmpCanvas.Rectangle(DecRectangle);
      tmpCanvas.Rectangle(IncRectangle);

      DrawArrows;
    end;

    DrawThumb;
  end;
end;

Procedure TTeeScrollBar.ApplyScroll(Delta:Double; ActivateTimer:Boolean);

  Procedure DoChange(const NewValue:Double);
  begin
    Position:=Round(NewValue);
    DoScroll;

    if ActivateTimer and FirstTime and FAutoRepeat then
    begin
      if not Assigned(FTimer) then
      begin
        FTimer:=TTimer.Create(Self);
        FTimer.OnTimer:=DoTimer;
      end;

      FTimer.Interval:=FInitial;
      FTimer.Enabled:=True;
    end;
  end;

  procedure StopTimer;
  begin
    if Assigned(FTimer) then
       FTimer.Enabled:=False;
  end;

begin
  if Delta<0 then
  begin
    Delta:=Math.Max(-Position,Delta);

    if Position>(Delta+1) then
       DoChange(Position+Delta)
    else
       StopTimer;
  end
  else
  begin
    if (Position+Delta-1)<(TotalCount-CurrentCount) then
       DoChange(Position+Delta)
    else
    begin
      DoChange(TotalCount-CurrentCount); // scroll to bottom end
      StopTimer;
    end;
  end;
end;

function TTeeScrollBar.GetBackColor:TColor;
begin
  result:=Brush.BackColor;
end;

function TTeeScrollBar.GetThumbBrush:TTeeBrush; // obsolete
begin
  result:=FThumb.Brush;
end;

function TTeeScrollBar.GetThumbSize:Integer; // obsolete
begin
  result:=FThumb.Size;
end;

Procedure TTeeScrollBar.ProcessClick(const P:TPoint);
var tmp : TCoordinate;
begin
  OldPoint:=P;

  FIn:=spNone;

  if ClickedDec(P) then
  begin
    FIn:=spArrowDec;
    ApplyScroll(-1,True)
  end
  else
  if ClickedInc(P) then
  begin
    FIn:=spArrowInc;
    ApplyScroll(1,True)
  end
  else
  if ClickedResize(True,P.X,P.Y) then
     FIn:=spResizeInc
  else
  if ClickedResize(False,P.X,P.Y) then
     FIn:=spResizeDec
  else
  if ClickedThumb(P) then
     FIn:=spThumb
  else
  if PointInRect(MainRectangle,P) then
  begin
    if Horizontal then tmp:=P.X
                  else tmp:=P.Y;

    if tmp<ThumbBegin then
       ApplyScroll(-DeltaMain,True)
    else
    if tmp>ThumbEnd then
       ApplyScroll(DeltaMain,True)
  end;
end;

Function TTeeScrollBar.CalcDelta(const A,B:TCoordinate):Double;
var tmpR : TRect;
begin
  if A=B then result:=0
  else
  begin
    tmpR:=ScrollRectangle;

    if Horizontal then
       result:=TotalCount/((tmpR.Right-tmpR.Left)/Abs(A-B))
    else
       result:=TotalCount/((tmpR.Bottom-tmpR.Top)/Abs(A-B));

    if A<B then result:=-result;
  end;
end;

function TTeeScrollBar.ClickedResize(IsInc:Boolean; const X,Y:TCoordinate):Boolean;
var tmpR : TRect;
begin
  result:=False;

  if AllowResize then
  begin
    tmpR:=ThumbRectangle;

    if Horizontal then
    begin
      if (Y>tmpR.Top) and (Y<tmpR.Bottom) then
         if ((not IsInc) and (Abs(X-tmpR.Left)<TeeClickTolerance)) or
            (IsInc and (Abs(X-tmpR.Right)<TeeClickTolerance)) then
              result:=True;
    end
    else
      if (X>tmpR.Left) and (X<tmpR.Right) then
         if ((not IsInc) and (Abs(Y-tmpR.Top)<TeeClickTolerance)) or
            (IsInc and (Abs(Y-tmpR.Bottom)<TeeClickTolerance)) then
              result:=True;
  end;
end;

procedure TTeeScrollBar.InflateThumbLength(IsInc:Boolean; const ADelta:Double);
var tmp : Integer;
begin
  tmp:=ThumbLength+Round(ADelta);

  if tmp<1 then
     tmp:=1;

  ThumbLength:=tmp;
end;

Procedure TTeeScrollBar.MouseMove(X,Y:Integer);

  procedure DrawOutline(IsInc:Boolean);

    procedure PrepareOutline;
    begin
      with ParentChart.Canvas do
      begin
        Brush.Style:=bsClear;
        Pen.Style:=psSolid;
        Pen.Color:=clSilver;
        Pen.Width:=2;
        Pen.Mode:=pmXor;
      end;
    end;

  var P : TTrianglePoints;
  begin
    PrepareOutline;

    if IsInc then
       IncArrow(P)
    else
       DecArrow(P);

    DrawArrow(False,P);

    ParentChart.Canvas.Pen.Mode:=pmCopy;
  end;

  function InternalCalcDelta(out ADelta:Double):Boolean;
  begin
    if Horizontal then ADelta:=CalcDelta(X,OldPoint.X)
                  else ADelta:=CalcDelta(Y,OldPoint.Y);

    result:=Abs(ADelta)>=1;
  end;

  procedure TryResize(IsInc:Boolean);
  var tmp : Double;
  begin
    if InternalCalcDelta(tmp) then
    begin
      InflateThumbLength(IsInc,tmp);

      OldPoint.X:=X;
      OldPoint.Y:=Y;

      Repaint;
    end;
  end;

var tmp : Double;
begin
  if FIn=spResizeInc then
     TryResize(True)
  else
  if FIn=spResizeDec then
     TryResize(False)
  else
  if FIn=spThumb then
  begin
    if InternalCalcDelta(tmp) then
    begin
      ApplyScroll(tmp,False);
      OldPoint.X:=X;
      OldPoint.Y:=Y;
    end;
  end
  else
  if ClickedDec(TeePoint(X,Y)) then
  begin
    if (Position>0) and (not FDecOutline) then
    begin
      DrawOutline(False);
      FDecOutline:=True;
    end;
  end
  else
  begin
    if FDecOutline then
    begin
      DrawOutline(False);
      FDecOutline:=False;
    end;

    if ClickedInc(TeePoint(X,Y)) then
    begin
      if (Position<(TotalCount-CurrentCount)) and (not FIncOutline) then
      begin
        DrawOutline(True);
        FIncOutline:=True;
      end;
    end
    else
    begin
      if FIncOutline then
      begin
        DrawOutline(True);
        FIncOutline:=False;
      end;

      if ClickedResize(True,X,Y) or ClickedResize(False,X,Y) then
      begin
        if Horizontal then
           ParentChart.Cursor:=crHSplit
        else
           ParentChart.Cursor:=crVSplit;

        ParentChart.CancelMouse:=True;
      end;
    end;
  end;
end;

Procedure TTeeScrollBar.MouseUp;
begin
  FIn:=spNone;

  FirstTime:=True;
  if Assigned(FTimer) then
     FTimer.Enabled:=False;

  Repaint;
end;

Procedure TTeeScrollBar.DoScroll;
begin
  if Assigned(FOnScrolled) then FOnScrolled(Self);
end;

procedure TTeeScrollBar.SetThumb(const Value: TScrollBarThumb);
begin
  FThumb.Assign(Value);
end;

procedure TTeeScrollBar.SetThumbBrush(const Value: TTeeBrush);
begin
  FThumb.Brush:=Value; // obsolete
end;

procedure TTeeScrollBar.SetThumbSize(const Value: Integer);
begin
  FThumb.Size:=Value; // obsolete
end;

procedure TTeeScrollBar.SetBevel(const Value: TPanelBevel);
begin
  if FBevel<>Value then
  begin
    FBevel:=Value;
    Repaint;
  end;
end;

procedure TTeeScrollBar.ChartEvent(AEvent:TChartToolEvent);
begin
  if AEvent=cteAfterDraw then
     Draw;
end;

procedure TTeeScrollBar.DoTimer(Sender: TObject);
begin
  ProcessClick(OldPoint);

  if FTimer.Interval>50 then
     FTimer.Interval:=50;
end;

procedure TTeeScrollBar.SetHorizontal(const Value: Boolean);
begin
  SetBooleanProperty(FHorizontal,Value);
end;

procedure TTeeScrollBar.ChartMouseEvent(AEvent: TChartMouseEvent;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  case AEvent of
    cmeDown: ProcessClick(TeePoint(X,Y));
    cmeMove: MouseMove(X,Y);
      cmeUp: MouseUp;
  end;
end;

function TTeeScrollBar.CurrentCount: Integer;
begin
  result:=ThumbLength;
end;

function TTeeScrollBar.GetPosition: Integer;
begin
  result:=FPosition;
end;

procedure TTeeScrollBar.Assign(Source:TPersistent);
begin
  if Source is TTeeScrollBar then
  with TTeeScrollBar(Source) do
  begin
    Self.FAllowResize:=FAllowResize;
    Self.ArrowBrush:=ArrowBrush;
    Self.FArrows:=FArrows;
    Self.AutoRepeat:=AutoRepeat;
    Self.BackColor:=BackColor;
    Self.Bevel:=Bevel;
    Self.DisabledArrow:=DisabledArrow;
    Self.DrawStyle:=DrawStyle;
    Self.Gradient:=Gradient;
    Self.InitialDelay:=InitialDelay;
    Self.MinThumbSize:=MinThumbSize;
    Self.FRoundSize:=FRoundSize;
    Self.Size:=Size;
    Self.Thumb:=Thumb;
  end;

  inherited;
end;

class function TTeeScrollBar.Description: String;
begin
  result:=TeeMsg_TeeScrollBar;
end;

procedure TTeeScrollBar.SetRoundSize(const Value: Integer);
begin
  SetIntegerProperty(FRoundSize,Value);
end;

procedure TTeeScrollBar.SetBackColor(const Value: TColor);
begin
  Brush.BackColor:=Value;
end;

class function TTeeScrollBar.GetEditorClass: String;
begin
  result:='TScrollBarEditor'; // Do not localize
end;

procedure TTeeScrollBar.SetPosition(Value: Integer);
begin
  if FPosition<0 then
     FPosition:=0
  else
  if FPosition>Max then
     FPosition:=Max;

  SetIntegerProperty(FPosition,Value);

  if Assigned(FOnSetPosition) then
     FOnSetPosition(Self,Value);
end;

procedure TTeeScrollBar.SetArrowBrush(const Value: TTeeBrush);
begin
  FArrowBrush.Assign(Value);
end;

procedure TTeeScrollBar.SetArrows(const Value: Boolean);
begin
  SetBooleanProperty(FArrows,Value);
end;

procedure TTeeScrollBar.SetDisabledArrow(const Value: TTeeBrush);
begin
  FDisabledArrow.Assign(Value);
end;

function TTeeScrollBar.DeltaMain: Integer;
begin
  result:=CurrentCount div 4;
end;

function TTeeScrollBar.ShouldDraw(var R: TRect): Boolean;
begin
  result:=TotalCount>0;
end;

function TTeeScrollBar.TotalCount: Integer;
begin
  result:=Max;
end;

procedure TTeeScrollBar.SetSize(const Value: Integer);
begin
  if FSize<>Value then
  begin
    SetIntegerProperty(FSize,Value);
    if Assigned(FOnChangeSize) then
       FOnChangeSize(Self);
  end;
end;

procedure TTeeScrollBar.SetParentChart(const Value: TCustomAxisPanel);
begin
  inherited;

  if Assigned(Thumb) then
  begin
    Thumb.ParentChart:=ParentChart;
    Repaint;
  end;
end;

function TTeeScrollBar.GetGradient:TTeeGradient;
begin
  result:=Brush.Gradient;
end;

procedure TTeeScrollBar.SetGradient(const Value: TTeeGradient);
begin
  Brush.Gradient:=Value;
end;

procedure TTeeScrollBar.SetMinSize(const Value: Integer);
begin
  SetIntegerProperty(FMinSize,Value);
end;

{ TTeeCombo }

Constructor TTeeCombo.Create(AOwner: TComponent);
begin
  inherited;

  Cursor:=crHandPoint;

  FItems:=TStringList.Create;
  FItems.OnChange:=CheckItems;

  FItemIndex:=-1;

  Width:=50;

  IHeight:=16;
  Height:=IHeight;
end;

Destructor TTeeCombo.Destroy;
begin
  FItems.Free;
  inherited;
end;

procedure TTeeCombo.Assign(Source: TPersistent);
begin
  if Source is TTeeCombo then
  begin
    Items:=TTeeCombo(Source).Items;
    FItemIndex:=TTeeCombo(Source).FItemIndex;
  end;

  inherited;
end;

function TTeeCombo.ItemHeight:Integer;
begin
  result:=ParentChart.Canvas.FontHeight+2;
end;

type
  TTeePanelAccess=class(TCustomTeePanel);

function TTeeCombo.ClickedItem(X,Y:Integer):Integer;
begin
  if (X>=Left) and (X<=Left+Shape.Width) and (Y>(Top+IHeight)) then
  begin
    TTeePanelAccess(ParentChart).TryAssignFont(Shape.Font);

    result:=TopIndex+((Y-Top-IHeight) div ItemHeight);

    if result>Count-1 then
       result:=Count-1;
  end
  else
     result:=-1;
end;

procedure TTeeCombo.Collapse;
begin
  FExpanded:=False;
  Height:=IHeight;
  Selected.HoverIndex:=-1;
  IUnderMouse:=-1;
end;

procedure TTeeCombo.RecalcHeight;
var tmpH,
    tmpMax,
    tmp : Integer;
begin
  TTeePanelAccess(ParentChart).TryAssignFont(Shape.Font);
  IHeight:=ItemHeight;

  if Expanded then
  begin
    tmpH:=ItemHeight;

    tmp:=IHeight+tmpH*Count;

    tmpMax:={$IFDEF FMX}Round{$ENDIF}(ParentChart.ChartBounds.Bottom-ParentChart.ChartBounds.Top-Top)-2;

    if tmp>tmpMax then
       tmp:=tmpMax;

    Height:=tmpH*(tmp div tmpH);
  end
  else
    Height:=IHeight;
end;

procedure TTeeCombo.Expand;
begin
  IUnderMouse:=-1;
  FExpanded:=True;

  RecalcHeight;
end;

procedure TTeeCombo.ChartMouseEvent(AEvent: TChartMouseEvent;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var tmpIndex : Integer;
begin
  inherited;

  if AEvent=cmeDown then
  begin
    if Clicked(x,y) and (Count>0) then
    begin
      if Expanded then
      begin
        tmpIndex:=ClickedItem(x,y);

        if tmpIndex>-1 then
           ItemIndex:=tmpIndex;

        Collapse;
      end
      else
        Expand;

      ParentChart.CancelMouse:=True;
    end
    else
    if Expanded then
    begin
      Collapse;
      ParentChart.CancelMouse:=True;
    end;
  end
  else
  if AEvent=cmeMove then
  begin
     if Expanded then
     begin
       tmpIndex:=ClickedItem(X,Y);

       if tmpIndex<>IUnderMouse then
       begin
         IUnderMouse:=tmpIndex;
         Repaint;
       end;
     end
     else
     if AutoHide then
        if Selected.HoverIndex<>ILastSelected then
        begin
          ILastSelected:=Selected.HoverIndex;
          Repaint;
        end;
  end;
end;

class function TTeeCombo.Description: String;
begin
  result:='ComboBox';
end;

class function TTeeCombo.LongDescription: String;
begin
  result:='ComboBox';
end;

class function TTeeCombo.GetEditorClass:String;
begin
  result:='TComboToolEditor';
end;

procedure TTeeCombo.CheckItems(Sender:TObject);
begin
  if FItems.Count=0 then
     FItemIndex:=-1
  else
  if FItems.Count-1<FItemIndex then
     FItemIndex:=FItems.Count-1;

  IWidth:=0;

  Repaint;
end;

procedure TTeeCombo.SetAutoHide(const Value: Boolean);
begin
  SetBooleanProperty(FAutoHide,Value);
end;

procedure TTeeCombo.SetItems(const Value: TStringList);
begin
  FItems.Assign(Value);
  CheckItems(FItems);
end;

procedure TTeeCombo.DoDrawText(const AParent: TCustomAxisPanel);
var
  Canvas : TCanvas3D;

  procedure DrawDropTriangle;
  var X,Y,
      tmpW,
      tmpH : TCoordinate;
  begin
    tmpH:=ItemHeight;

    X:=Shape.ShapeBounds.Right-tmpH+(tmpH{$IFDEF FMX}*0.5{$ELSE} div 2{$ENDIF});
    Y:=Shape.ShapeBounds.Top+(tmpH{$IFDEF FMX}*0.25{$ELSE} div 4{$ENDIF});

    Canvas.Pen.Visible:=True;
    Canvas.Pen.Width:=1;
    Canvas.Brush.Style:=bsSolid;

    if Count>1 then
    begin
      Canvas.Pen.Color:=clBlack;
      Canvas.Brush.Color:=clDkGray;
    end
    else
    begin
      Canvas.Pen.Color:=clSilver;
      Canvas.Brush.Color:=clSilver;
    end;

    tmpW:=tmpH{$IFDEF FMX}*0.25{$ELSE} div 4{$ENDIF};
    tmpH:=tmpH{$IFDEF FMX}*0.5{$ELSE}  div 2{$ENDIF};

    if Expanded then
       Canvas.TriangleWithZ(TeePoint(X-tmpW,Y+tmpH),TeePoint(X+tmpW,Y+tmpH),TeePoint(X,Y),0)
    else
       Canvas.TriangleWithZ(TeePoint(X-tmpW,Y),TeePoint(X+tmpW,Y),TeePoint(X,Y+tmpH),0);
  end;

  procedure DrawSeparator;
  begin
    Canvas.Pen.Visible:=True;
    Canvas.Pen.Width:=1;
    Canvas.Pen.Color:=clSilver;

    Canvas.DoVertLine(Shape.ShapeBounds.Right-Shape.Height,Shape.Top,Shape.Top+Shape.Height);
  end;

  procedure DrawItems;
  var tmpH,
      tmpY,
      tmpFirst,
      tmpMax,
      t : Integer;
  begin
    tmpH:=ItemHeight;

    Canvas.Pen.Visible:=True;
    Canvas.Pen.SmallDots:=True;
    Canvas.DoHorizLine(Left,Left+Shape.Width,Top+tmpH-1);

    if ItemIndex=-1 then
       TopIndex:=0
    else
    begin
      tmpMax:=(Height-IHeight) div tmpH;

      if ItemIndex<TopIndex then
         TopIndex:=ItemIndex
      else
      if ItemIndex>(TopIndex+tmpMax-1) then
      begin
        tmpFirst:=ItemIndex-tmpMax+1;

        if tmpFirst<0 then
           tmpFirst:=0;

        TopIndex:=tmpFirst;
      end;
    end;

    for t:=TopIndex to Count-1 do
    begin
      if t=ItemIndex then
      begin
        Canvas.Font.Style:=[{$IFDEF FMX}TFontStyle.{$ENDIF}fsBold];
        Canvas.Brush.Color:={$IFDEF FMX}claBlue{$ELSE}clBlue{$ENDIF}; // clHighlight?
        Canvas.Font.Color:=clWhite;
        Canvas.BackMode:=cbmOpaque;
      end
      else
      begin
        if t=IUnderMouse then
           Canvas.Font.Style:=[{$IFDEF FMX}TFontStyle.{$ENDIF}fsBold]
        else
           Canvas.Font.Style:=[];

        Canvas.Brush.Color:=clWhite;
        Canvas.Font.Color:=clBlack;
        Canvas.BackMode:=cbmTransparent;
      end;

      tmpY:=Shape.Top+IHeight+tmpH*(t-TopIndex);

      if tmpY+tmpH>Height+Shape.Top then
         break
      else
         Canvas.TextOut(Left+2,tmpY,Items[t]);
    end;
  end;

  procedure RecalcWidth;
  var t : Integer;
  begin
    if Expanded then
    begin
      if IWidth=0 then
         for t:=0 to Count-1 do
             IWidth:=Max(IWidth,ParentChart.Canvas.TextWidth(Items[t]));

      Width:=IWidth+IHeight+2;
    end
    else
    begin
      if Text='' then
         Width:=50*IHeight div 17
      else
         Width:=Canvas.TextWidth(Text+' ')+IHeight+2;
    end;
  end;

var Old : Boolean;
begin
  if (not Visible) or (not Shape.Visible) then
     Exit;

  Canvas:=ParentChart.Canvas;

  if ItemIndex=-1 then
     Text:=''
  else
     Text:=FItems[ItemIndex];

  RecalcHeight;

  RecalcWidth;

  Old:=Shape.Transparent;
  Shape.Transparent:=(not Expanded) and AutoHide and (Selected.HoverIndex=-1);

  inherited;

  Shape.Transparent:=Old;

  if Expanded or (not AutoHide) or (Selected.HoverIndex<>-1) then
  begin
    DrawDropTriangle;
    DrawSeparator;
  end;

  if Expanded then
     DrawItems
end;

function TTeeCombo.Count: Integer;
begin
  result:=FItems.Count;
end;

procedure TTeeCombo.SetItemIndex(const Value: Integer);
begin
  if FItemIndex<>Value then
  begin
    if Value<-1 then
       FItemIndex:=-1
    else
    if (not (csLoading in ComponentState)) and (Value>=Count) then
       FItemIndex:=Count-1
    else
       FItemIndex:=Value;

    Repaint;

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

procedure TTeeCombo.ChartKeyEvent(AEvent: TChartKeyEvent; var Key: Word;
  var Key1: Char; Shift: TShiftState);
var tmp : Integer;
begin
  inherited;

  if AEvent=ckeDown then
  begin
    if Expanded then
    begin
      tmp:=ItemIndex;

      case Key of
        TeeKey_Up    : tmp:=tmp-1;
        TeeKey_Down  : tmp:=tmp+1;
        TeeKey_Home  : tmp:=0;
        TeeKey_End   : tmp:=Count-1;

        TeeKey_Prior : begin
                         tmp:=tmp-10;

                         if tmp<0 then
                            tmp:=0;
                       end;

        TeeKey_Next  : begin
                         tmp:=tmp+10;

                         if tmp>Count-1 then
                            tmp:=Count-1;
                       end;
      else
        begin
          if (Key=TeeKey_Escape) or (Key=TeeKey_Return) then
             Collapse;

          Exit;
        end;
      end;

      if (tmp>-1) and (tmp<Count) then
         ItemIndex:=tmp;
    end
    else
    if Key=TeeKey_Down then
       if Selected.HoverIndex<>-1 then
          Expand;
  end;
end;

// Default Tools:

const T:Array[0..18] of TTeeCustomToolClass=(
                     TCursorTool,     TDragMarksTool,
                     TAxisArrowTool,  TDrawLineTool,
                     TNearestTool,    TColorBandTool,
                     TColorLineTool,  TRotateTool,
                     TChartImageTool, TMarksTipTool,
                     TAnnotationTool,
                     TGridBandTool,   TAxisScrollTool,
                     TRectangleTool,  TClipSeriesTool,
                     TFullScreenTool, TRepaintMonitor,
                     TAxisScrollBarTool, TTeeCombo
                     );

initialization
  RegisterTeeTools(T);
finalization
  UnRegisterTeeTools(T);
end.
