{********************************************}
{ Base types and Procedures                  }
{ Copyright (c) 1995-2020 by Steema Software }
{ All Rights Reserved                        }
{********************************************}
unit TeeProcs;
{$I TeeDefs.inc}

interface

{$IFDEF FMX}
{$IFDEF MSWINDOWS}
{$DEFINE TEEWINDOWS}
{$ENDIF}
{$ELSE}
{$IFNDEF LINUX}
{$DEFINE TEEWINDOWS}
{$ENDIF}
{$ENDIF}

Uses {$IFDEF TEEWINDOWS}
     Windows, // <-- FMX needs this (TeeGotoURL SW_SHOW)
     {$IFNDEF FMX}
     Messages,
     {$ENDIF}
     {$ENDIF}

     {$IFDEF POSIX}  // Mac OSX
     Posix.Stdlib,
     {$ENDIF}

     {$IFDEF IOS}
     Posix.Base,
     {$ENDIF}

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

     TypInfo,

     {$IFDEF TEEVCL}

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

     {$IFNDEF ANDROID}
     FMX.Printer,
     {$ENDIF}

     FMX.Forms, FMX.Platform,

       {$IFDEF D18}
       FMX.StdCtrls,
       {$ENDIF}

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

     FMX.ListBox, FMX.Edit,

     {$IFDEF MACOS}
     Macapi.CoreServices,
     {$ENDIF}

     {$ELSE}
     Printers, Clipbrd, ExtCtrls, Controls, Graphics, Forms, StdCtrls, Dialogs,

     {$IFDEF USEWINDOWS}
     MMSystem,
     {$ENDIF}

     {$ENDIF}

     {$ENDIF}

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

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

     Classes, SysUtils,

     {$IFDEF FMX}
     System.UIConsts, System.UITypes, FMXTee.Canvas
     {$ELSE}

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

     TeCanvas
     {$ENDIF}
     ;

{$IFDEF FMX}
{$DEFINE TEE_METAFILE}
{$ENDIF}

{$IFDEF LCL} // Lazarus
{$DEFINE TEE_METAFILE}

// Moved from TeePenDlg.pas until finished
Const
  TeeFormBorderStyle=bsNone;
{$ENDIF}

{$IFNDEF LCL}
{$DEFINE IGNOREPALETTE}
{$ENDIF}

{$IFDEF TEE_METAFILE}
type
  TMetafile=class(TBitmap)
  private
    FEnhanced: Boolean;
  published
    property Enhanced:Boolean read FEnhanced write FEnhanced;
  end;
{$ENDIF}

Const
   TeeDefVerticalMargin = 4;
   TeeDefHorizMargin    = 3;

   crTeeHand              = TCursor(2020); { Hand cursor }
   TeeMsg_TeeHand         = 'crTeeHand'; { string cursor name (dont translate) }

   TeeNormalPrintDetail   =    0;
   TeeHighPrintDetail     = -100;

   TeeDefault_PrintMargin =   15;

   MaxDefaultColors=19;  // Length of DefaultPalette (+1)

   TeeTabDelimiter        = #9;  { separator used in TeExport.pas }
   TeeTextLineSeparator   = #13#10;

   TeeLineSeparator :Char = {$IFDEF POSIX}#10{$ELSE}#13{$ENDIF}; // To separate lines of text in TeeShape objects

var
  TeeClipWhenPrinting   : Boolean=True;  { Apply clipping when printing }
  TeeClipWhenMetafiling : Boolean=True;  { Apply clipping when creating metafiles }
  TeeEraseBack          : Boolean=False; { erase background before repainting panel }

  { Should Panel background to be printed ? Default: True }
  PrintTeePanel         : Boolean=True;  // Default was False: (v9.10818)

  // Class name for the default Canvas class used when creating new Charts or TeePanel controls.
  // Default value for VCL is TGDIPlusCanvas, default for FMX is empty string to use FMX defaults.
  TeeDefaultCanvas      : String;

type
  TColorEvent = procedure(const aColor: TColor) of object;

type
  TDateTimeStep=(  dtOneMicroSecond,
                   dtOneMillisecond,
                   dtOneSecond,
                   dtFiveSeconds,
                   dtTenSeconds,
                   dtFifteenSeconds,
                   dtThirtySeconds,
                   dtOneMinute,
                   dtFiveMinutes,
                   dtTenMinutes,
                   dtFifteenMinutes,
                   dtThirtyMinutes,
                   dtOneHour,
                   dtTwoHours,
                   dtSixHours,
                   dtTwelveHours,
                   dtOneDay,
                   dtTwoDays,
                   dtThreeDays,
                   dtOneWeek,
                   dtHalfMonth,
                   dtOneMonth,
                   dtTwoMonths,
                   dtThreeMonths,
                   dtFourMonths,
                   dtSixMonths,
                   dtOneYear,
                   dtNone );


const
    DateTimeStep:Array[TDateTimeStep] of Double=
      ( 1.0/(1000000.0*86400.0), 1.0/(1000.0*86400.0), 1.0/86400.0,
        5.0/86400.0,  10.0/86400.0,
        0.25/1440.0,  0.5/1440.0,    1.0/1440.0,
        5.0/1440.0,  10.0/1440.0,    0.25/24.0,
        0.5/24.0 ,    1.0/24.0 ,  2.0/24.0 ,  6.0/24.0 ,
        12.0/24.0,    1, 2, 3, 7, 15, 30, 60, 90, 120, 182, 365, {none:} 1
      );

{$IFDEF FMX}
   bsDialog=1;
   bsSizeable=2;
   clWhite=TAlphaColors.White;
{$ENDIF}

{$IFDEF FMX}
{$DEFINE TEEBEVELS}
{$ENDIF}
{$IFDEF LCL}
{$DEFINE TEEBEVELS}
{$ENDIF}

type
  {$IFDEF FMX}
  TCustomPanel=TControl;
  TBorderStyle=(bsNone, bsSingle);
  TPanelBevel=(bvNone, bvLowered, bvRaised);
  TBevelWidth=0..100;
  {$ENDIF}

  TCustomPanelNoCaption=class(TCustomPanel)
  {$IFDEF FMX}
  private
    FColor  : TColor;
    procedure SetColor(const Value:TColor);
  {$ENDIF}
  public
    Constructor Create(AOwner: TComponent); override;

  {$IFDEF FMX}
    procedure Invalidate; virtual;

    property ClipChildren default True;
    property Color:TColor read FColor write SetColor default $FFF0F0F0;
  published
    {$IFDEF D17}
    property Height;
    property Position;
    property Width;
    {$ENDIF}
  {$ENDIF}
  end;

  TZoomPanning=class(TPersistent)
  private
    FActive     : Boolean;
  public
    X0,Y0,X1,Y1 : Integer;

    Procedure Check;
    Procedure Activate(x,y:Integer);

    property Active:Boolean read FActive write FActive;
  end;

  TMouseWheelStyle=(pmwNormal, pmwInverted, pmwNone);

  TPanMouseWheel=TMouseWheelStyle;

  TTeePanning=class(TZoomPanning)
  private
    FInsideBounds : Boolean;
    FMouseWheel   : TPanMouseWheel;
  public
    Procedure Assign(Source:TPersistent); override;
  published
    property InsideBounds:Boolean read FInsideBounds write FInsideBounds default False;
    property MouseWheel:TPanMouseWheel read FMouseWheel write FMouseWheel default pmwNormal;
  end;

  TCustomTeePanel=class;

  TTeeEvent=class
  public
    Sender : TCustomTeePanel;
  end;

  TTeeEventClass=class of TTeeEvent;

  ITeeEventListener=interface
    procedure TeeEvent(Event:TTeeEvent);
  end;

  {$IFDEF D18}
  TTeeEventListeners=TList<ITeeEventListener>;
  {$ELSE}

  TTeeEventListeners=class(TList)
  private
    Function Get(Index:Integer):ITeeEventListener;
  public
    function Add(Const Item: ITeeEventListener): Integer;
    function Remove(Item: ITeeEventListener): Integer;
    property Items[Index:Integer]:ITeeEventListener read Get; default;
  end;
  {$ENDIF}

  TTeeMouseEventKind=(meDown,meUp,meMove);

  TTeeMouseEvent=class(TTeeEvent)
  public
    Event  : TTeeMouseEventKind;
    Button : TMouseButton;
    Shift  : TShiftState;
    X      : TCoordinate;
    Y      : TCoordinate;
  end;

  TTeeView3DEvent=class(TTeeEvent);

  TTeeDrawEvent=class(TTeeEvent);

  TTeeUnits=(muPercent,muPixels);

  TTeePrintEvent=procedure(Sender:TCustomTeePanel; Canvas:TCanvas; var R:TRect) of object;

  TTextFormat=(ttfNormal, ttfHtml);

  TCustomTeePanel=class(TCustomPanelNoCaption)
  private
    FApplyZOrder      : Boolean;
    FAutoRepaint      : Boolean;  { when False, it does not refresh }

    {$IFDEF TEEBEVELS}
    FBevelInner : TPanelBevel;
    FBevelOuter : TPanelBevel;
    FBevelWidth : TBevelWidth;
    {$ENDIF}

    {$IFDEF FMX}
    FBorderWidth : Integer;
    {$ENDIF}

    FBorder           : TTeeHiddenPen;
    FBorderRound      : Integer;
    FCancelMouse      : Boolean;  { when True, it does not finish mouse events }
    FChartBounds      : TRect;

    FChartWidth       : Integer;
    FChartHeight      : Integer;
    FChartXCenter     : Integer;
    FChartYCenter     : Integer;

    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}
    FDelphiCanvas     : TCanvas;
    FEmboss           : TTeeEmboss;
    FGlobalFont       : TTeeFont;
    FGlobalPicture    : TTeePicture;
    FHeight3D         : Integer;
    FMargins          : TRect;
    FMarginUnits      : TTeeUnits;
    FOnBeforePrint    : TTeePrintEvent;
    FOriginalCursor   : TCursor;
    FPanning          : TTeePanning;
    FPrinting         : Boolean;
    FPrintProportional: Boolean;
    FPrintResolution  : Integer;
    FShadow           : TTeeShadow;
    FView3D           : Boolean;
    FView3DOptions    : TView3DOptions;
    FWidth3D          : Integer;

    FGLComponent      : TComponent; { internal }

    IEventListeners   : TTeeEventListeners;

    {$IFNDEF FMX}
    IRounding         : Boolean;
    {$ENDIF}

    {$IFDEF MONITOR_REPAINTS} // Internal counter of VCL/FMX Paint events.
    IPaintCount : Integer;
    {$ENDIF}

    {$IFDEF TEEEXCEPTIONS}
    IExceptionDisplayed : Boolean;
    {$ENDIF}

    ICachedChartRect  : TRect;
    InitialSize       : TPoint;

    IDefaultCanvas    : String;

    Procedure BroadcastMouseEvent(Kind:TTeeMouseEventKind;
                                  Button: TMouseButton;
                                  Shift: TShiftState;
                                  X, Y: TCoordinate);

    procedure CreateDefaultCanvas;

    function FixBorderWidth(var Rect:TRect):Integer;

    Function GetBorderStyle:TBorderStyle;
    Function GetBufferedDisplay:Boolean;
    function GetDefaultFont:TTeeFont;
    Function GetMargin(Index:Integer):Integer;
    Function GetMonochrome:Boolean;

    {$IFDEF FMX}
    procedure InvalidateEvent(Sender:TObject);
    {$ENDIF}

    procedure InternalDoMouseLeave;

    Procedure NonBufferDraw(const ACanvas:TCanvas; Const R:TRect);

    procedure ReadBorderStyle(Reader: TReader); // obsolete
    procedure ReadDefaultCanvas(Reader: TReader);
    procedure WriteDefaultCanvas(Writer: TWriter);

    {$IFDEF TEEBEVELS}
    procedure SetBevelInner(const Value: TPanelBevel);
    procedure SetBevelOuter(const Value: TPanelBevel);
    procedure SetBevelWidth(const Value: TBevelWidth);
    {$ENDIF}

    {$IFDEF FMX}
    procedure SetBorderWidth(const Value: Integer);
    {$ENDIF}

    Procedure SetBorder(const Value:TTeeHiddenPen);
    Procedure SetBorderRound(Value:Integer);
    Procedure SetBufferedDisplay(Value:Boolean);
    procedure SetControlRounded;
    procedure SetCustomChartRect(Value:Boolean);
    procedure SetDefaultFont(const AFont:TTeeFont);
    Procedure SetEmboss(const Value:TTeeEmboss);
    Procedure SetMargin(Index,Value:Integer);
    Procedure SetMarginUnits(const Value:TTeeUnits);
    Procedure SetMonochrome(Value:Boolean);
    procedure SetPanning(const Value:TTeePanning);
    Procedure SetShadow(const Value:TTeeShadow);
    Procedure SetTheBorderStyle(Value:TBorderStyle);
    procedure SetView3D(Value:Boolean);
    procedure SetView3DOptions(Value:TView3DOptions);
  protected
    DontShowImage    : Boolean; // Normal Draw, but dont paint the final image
    FCustomChartRect : Boolean;
    IsEmbedded       : Boolean;
    InternalCanvas   : TCanvas3D;
    IsPrinterCanvas  : Boolean;
    ICustomPrinterRect : TRect;

    Procedure ApplyMargins;

    procedure AssignTo(Dest: TPersistent); override;

    function BorderSize:Integer;

    Function BroadcastTeeEvent(const Event:TTeeEvent):TTeeEvent;
    Procedure BroadcastTeeEventClass(const Event:TTeeEventClass);

    {$IFNDEF FMX}
    procedure ChangeScale(M, D: Integer{$IFDEF D24}; isDpiChange: Boolean{$ENDIF}); override;
    {$ENDIF}

    {$IFDEF FMX}
    procedure DoMouseLeave; override;
    {$ELSE}

    {$IFDEF USEWINDOWS}
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure CMSysColorChange(var Message: TMessage); message CM_SYSCOLORCHANGE;
    {$ENDIF}

    {$IFNDEF LCL}
    procedure CreateParams(var Params: TCreateParams); override;
    {$ENDIF}

    {$ENDIF}

    Procedure DefineProperties(Filer:TFiler); override; // obsolete
    procedure DblClick; override;

    Function GetBackColor:TColor; virtual;

    {$IFDEF FMX}
    Function GetClientRect:TRect;
    {$ENDIF}

    function GetEditablePanel:TCustomTeePanel; virtual;

    function Filters:TFilterItems;

    Function InternalCalcProportionalMargins(const AWidth,AHeight,
                    APageWidth,APageHeight:Integer; const APageRatio:Double):TRect;
    Procedure InternalDraw(const UserRectangle:TRect); virtual; // abstract;

    function LinesOfText(S:String):Integer;
    property Listeners:TTeeEventListeners read IEventListeners;

    Procedure Loaded; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
                        X, Y: TCoordinate); override;

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

    procedure MouseMove(Shift: TShiftState; X, Y: TCoordinate); override;
    procedure Paint; override;
    function PanelColor(const AColor:TColor):TColor;
    Function PrintMarginsRect(const AWidth,AHeight:Integer):TRect;
    procedure ReadState(Reader: TReader); override;

    procedure AddListener(Sender:ITeeEventListener);
    procedure RemoveListener(Sender:ITeeEventListener);
    procedure Resize; override;
    Procedure SetBooleanProperty(var Variable:Boolean; Value:Boolean);
    Procedure SetColorProperty(var Variable:TColor; Value:TColor);
    Procedure SetDoubleProperty(var Variable:Double; Const Value:Double);
    Procedure SetIntegerProperty(var Variable:Integer; Value:Integer);
    Procedure SetSingleProperty(var Variable:Single; const Value:Single);
    Procedure SetStringProperty(var Variable:String; Const Value:String);

    procedure TryAssignFont(const AFont:TTeeFont);

    {$IFNDEF FMX}
    {$IFDEF USEWINDOWS}
    procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
    {$ENDIF}
    {$IFDEF MSWINDOWS}
    procedure WMEraseBkgnd(var Message: TWmEraseBkgnd); message WM_ERASEBKGND;
    procedure WndProc(var Msg: TMessage); override;
    {$ENDIF}
    {$ENDIF}

    property GLComponent:TComponent read FGLComponent write FGLComponent; { internal }
  public
    // Do not make this a "property" (ie: breaks --> ChartRect.Left:=123)
    ChartRect : TRect; // The rectangle bounded by 4 axes, in pixels

    // Do not make this a "property" (ie: breaks --> PrintMargins.Left:=15)
    PrintMargins : TRect;    { the percent of paper printer margins }

    Theme           : TCustomTeePanel; // Current Theme

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

    Procedure Assign(Source:TPersistent); override;
    Procedure CalcMetaBounds( var R:TRect; Const AChartRect:TRect;
                              out WinWidth,WinHeight,ViewWidth,ViewHeight:Integer);
    Function CalcProportionalMargins:TRect;
    Function CanClip:Boolean;
    Procedure CanvasChanged(Sender:TObject); virtual;
    Function ChartPrintRect:TRect;
    Procedure CopyToClipboardBitmap; overload;
    Procedure CopyToClipboardBitmap(const R:TRect); overload;
    Procedure CopyToClipboardMetafile(Enhanced:Boolean); overload;
    Procedure CopyToClipboardMetafile(Enhanced:Boolean; Const R:TRect); overload;

    Function TeeCreateBitmap(ABackColor:TColor; Const Rect:TRect;
                             {$IFNDEF FMX}
                             APixelFormat:TPixelFormat=pfDevice;
                             {$ENDIF}
                             ADpi:Integer=0):TBitmap; overload;

    Function TeeCreateBitmap:TBitmap; overload;
    Procedure Draw(UserCanvas:TCanvas; Const UserRect:TRect); overload; virtual;
    Procedure Draw; overload;
    Procedure DrawPanelBevels(Rect:TRect); dynamic;
    Procedure DrawToMetaCanvas(const ACanvas:TCanvas; const Rect:TRect);

    Function GetCursorPos:TPoint; {$IFNDEF FMX}{$IFDEF D9}inline;{$ENDIF}{$ENDIF}
    Function GetRectangle:TRect; virtual;
    procedure Invalidate; override;
    Function IsScreenHighColor:Boolean;

    function MultiLineTextSize(S:String; const TextFormat:TTextFormat; out NumLines:Integer):TPoint;
    function MultiLineTextWidth(S:String; out NumLines:Integer;const TextFormat:TTextFormat=ttfNormal):Integer;

    procedure PrepareFont3D(const AFont:TTeeFont);

    Procedure Print;
    Procedure PrintLandscape;
    Procedure PrintOrientation(const AOrientation:TPrinterOrientation);
    Procedure PrintPartial(Const PrinterRect:TRect);
    Procedure PrintPartialCanvas( const PrintCanvas:TCanvas;
                                  Const PrinterRect:TRect);
    Procedure PrintPortrait;
    Procedure PrintRect(Const R:TRect);

    Procedure SaveToBitmapFile(Const FileName:String); overload;
    Procedure SaveToBitmapFile(Const FileName:String; Const R:TRect); overload;
    Procedure SaveToMetafile(Const FileName:String);
    Procedure SaveToMetafileEnh(Const FileName:String);
    Procedure SaveToMetafileRect( const Enhanced:Boolean; Const FileName:String;
                                  Const Rect:TRect );
    Procedure SetBrushCanvas( AColor:TColor; const ABrush:TChartBrush;
                              ABackColor:TColor);
    procedure SetChartRect(const ARect:TRect);
    Procedure SetInternalCanvas(const NewCanvas:TCanvas3D);
    Procedure ReCalcWidthHeight;
    Function  TeeCreateMetafile(const Enhanced:Boolean; Const Rect:TRect):TMetafile;

    { public properties }
    property ApplyZOrder:Boolean read FApplyZOrder write FApplyZOrder;
    property AutoRepaint:Boolean read FAutoRepaint write FAutoRepaint;  { when False, it does not refresh }
    property Border:TTeeHiddenPen read FBorder write SetBorder;
    property BorderRound:Integer read FBorderRound write SetBorderRound default 0;
    property BorderStyle:TBorderStyle read GetBorderStyle write SetTheBorderStyle; // obsolete
    property BufferedDisplay:Boolean read GetBufferedDisplay
                                     write SetBufferedDisplay;
    property CancelMouse:Boolean read FCancelMouse write FCancelMouse; { when True, it does not finish mouse events }
    property Canvas:TCanvas3D read InternalCanvas write SetInternalCanvas;
    property ChartBounds:TRect read FChartBounds;
    property ChartHeight:Integer read FChartHeight;
    property ChartWidth:Integer read FChartWidth;
    property ChartXCenter:Integer read FChartXCenter;
    property ChartYCenter:Integer read FChartYCenter;

    property CustomChartRect:Boolean read FCustomChartRect write SetCustomChartRect;

    property DefaultFont:TTeeFont read GetDefaultFont write SetDefaultFont;

    property DelphiCanvas:TCanvas read FDelphiCanvas;
    property Height3D:Integer read FHeight3D write FHeight3D;

    property OriginalCursor:TCursor read FOriginalCursor write FOriginalCursor;
    property Panning:TTeePanning read FPanning write SetPanning;
    property Printing:Boolean read FPrinting write FPrinting;
    property PrintResolution:Integer read FPrintResolution
                                     write FPrintResolution default TeeNormalPrintDetail;
    property Width3D:Integer read FWidth3D write FWidth3D;

    { to be published properties }
    property MarginLeft:Integer   index 0 read GetMargin write SetMargin default TeeDefHorizMargin;
    property MarginTop:Integer    index 1 read GetMargin write SetMargin default TeeDefVerticalMargin;
    property MarginRight:Integer  index 2 read GetMargin write SetMargin default TeeDefHorizMargin;
    property MarginBottom:Integer index 3 read GetMargin write SetMargin default TeeDefVerticalMargin;
    property MarginUnits:TTeeUnits read FMarginUnits write SetMarginUnits
                                   default muPercent;

    property Monochrome:Boolean read GetMonochrome write SetMonochrome default False;
    property PrintProportional:Boolean read FPrintProportional
                                       write FPrintProportional default True;
    property Emboss:TTeeEmboss read FEmboss write SetEmboss;
    property Shadow:TTeeShadow read FShadow write SetShadow;
    property View3D:Boolean read FView3D write SetView3D default True;
    property View3DOptions:TView3DOptions read FView3DOptions write SetView3DOptions;

    // "Aspect" not to be published. Just an alias to View3DOptions
    property Aspect:TView3DOptions read FView3DOptions write SetView3DOptions;

    { TPanel properties }
    property Align;

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

    property BevelInner {$IFDEF TEEBEVELS}:TPanelBevel read FBevelInner write SetBevelInner default bvNone{$ENDIF};
    property BevelOuter {$IFDEF TEEBEVELS}:TPanelBevel read FBevelOuter write SetBevelOuter default bvRaised{$ENDIF};
    property BevelWidth {$IFDEF TEEBEVELS}:TBevelWidth read FBevelWidth write SetBevelWidth default 1{$ENDIF};

    property BorderWidth {$IFDEF FMX}:Integer read FBorderWidth write SetBorderWidth default 0{$ENDIF};

    {$IFNDEF FMX}
    property Color;
    property DragCursor;
    {$ENDIF}

    property DragMode;
    property Enabled;

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

    property ShowHint;
    property TabOrder;

    {$IFNDEF FMX}
    property TabStop default True;

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

    {$ENDIF}

    property Visible;

    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;

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

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

    property OnBeforePrint:TTeePrintEvent read FOnBeforePrint write FOnBeforePrint;
  published
  end;

  TChartGradient=TTeeGradient;

  TChartGradientClass=class of TChartGradient;

  TPanningMode=(pmNone,pmHorizontal,pmVertical,pmBoth);

  TTeeZoomPen=class(TTeePen) // do not seal (TeeTree inherits from it)
  private
    Function IsColorStored:Boolean;
  protected
    DefaultColor : TColor;
  published
    property Color stored IsColorStored nodefault;
    property Width {$IFNDEF FMX}default 3{$ENDIF}; // 3 pixels better than 1, with high-res screens
  end;

  TTeeZoomBrush=class(TChartBrush)
  published
    property Color default clWhite;
    property Style default bsClear;
  end;

  TTeeZoomDirection=(tzdHorizontal, tzdVertical, tzdBoth);

  TDoubleArray=Array of Double;

  TZoomSnapshot=class
  public
    AxesMinMax   : TDoubleArray;
    CustomCount  : Integer;
    CustomMinMax : Array of TDoubleArray;
  end;

  {$IFDEF D18}
  TZoomSnapshots=TObjectList<TZoomSnapShot>;
  {$ELSE}
  TZoomSnapshots=class(TList)
  private
    function Get(Index:Integer):TZoomSnapShot;
    procedure Put(Index:Integer; Value:TZoomSnapShot);
  public
    procedure Clear; override;

    property Items[Index:Integer]:TZoomSnapShot read Get write Put; default;
  end;
  {$ENDIF}

  TTeeZoom=class(TZoomPanning)
  private
    FAllow         : Boolean;
    FAnimated      : Boolean;
    FAnimatedSteps : Integer;
    FAxesHistory   : TZoomSnapshots;
    FBrush         : TTeeZoomBrush;
    FDirection     : TTeeZoomDirection;
    FFullRepaint   : Boolean;
    FHistory       : Boolean;
    FKeepAspect    : Boolean;
    FKeyShift      : TShiftState;
    FMinimumPixels : Integer;
    FMouseButton   : TMouseButton;
    FMouseWheel    : TMouseWheelStyle;
    FPen           : TTeeZoomPen;
    FUpLeftZooms   : Boolean;

    function GetAxesHistory:TZoomSnapshots;
    Function GetBrush:TTeeZoomBrush;
    Function GetPen:TTeeZoomPen;
    Procedure SetBrush(const Value:TTeeZoomBrush);
    Procedure SetHistory(Value:Boolean);
    Procedure SetPen(const Value:TTeeZoomPen);
  public
    Constructor Create;
    Destructor Destroy; override;

    Procedure Assign(Source:TPersistent); override;
    procedure CorrectAspectRatio(const R:TRect; const X,Y:TCoordinate);

    property AxesHistory:TZoomSnapshots read GetAxesHistory;
  published
    property Allow:Boolean read FAllow write FAllow default True;
    property Animated:Boolean read FAnimated write FAnimated default False;
    property AnimatedSteps:Integer read FAnimatedSteps write FAnimatedSteps default 8;
    property Brush:TTeeZoomBrush read GetBrush write SetBrush;
    property Direction:TTeeZoomDirection read FDirection write FDirection default tzdBoth;
    property FullRepaint:Boolean read FFullRepaint write FFullRepaint default {$IFDEF FMX}True{$ELSE}False{$ENDIF};
    property History:Boolean read FHistory write SetHistory default False;
    property KeepAspectRatio:Boolean read FKeepAspect write FKeepAspect default False;
    property KeyShift:TShiftState read FKeyShift write FKeyShift default [];
    property MinimumPixels:Integer read FMinimumPixels write FMinimumPixels default 16;
    property MouseButton:TMouseButton read FMouseButton write FMouseButton default {$IFDEF FMX}TMouseButton.{$ENDIF}mbLeft;
    property MouseWheel:TMouseWheelStyle read FMouseWheel write FMouseWheel default {$IFDEF FMX}TMouseWheelStyle.{$ENDIF}pmwNone;
    property Pen:TTeeZoomPen read GetPen write SetPen;
    property UpLeftZooms:Boolean read FUpLeftZooms write FUpLeftZooms default False;
  end;

  TCustomTeePanelExtended=class;

  TTeeBackImageMode=(pbmStretch,pbmTile,pbmCenter,pbmCustom);

  TBackImage=class(TTeePicture)
  private
    FBottom : Integer;
    FDrawQuality : TCanvasStretchQuality;
    FInside : Boolean;
    FLeft   : Integer;
    FMode   : TTeeBackImageMode;
    FRight  : Integer;
    FTop    : Integer;

    procedure DoChanged;
    function GetBounds:TRect;
    procedure SetBottom(Value:Integer);
    procedure SetBounds(const Value:TRect);
    procedure SetDrawQuality(const Value:TCanvasStretchQuality);
    procedure SetField(var Variable:Integer; const Value:Integer);
    procedure SetInside(Const Value:Boolean);
    procedure SetLeft(Value:Integer);
    procedure SetMode(Const Value:TTeeBackImageMode);
    procedure SetRight(Value:Integer);
    procedure SetTop(Value:Integer);
  public
    procedure Assign(Source:TPersistent); override;

    procedure Draw(const Panel:TCustomTeePanelExtended; Rect:TRect; Z:Integer);

    property Bounds:TRect read GetBounds write SetBounds;
  published
    property Bottom:Integer read FBottom write SetBottom default 0;
    property DrawQuality:TCanvasStretchQuality read FDrawQuality write SetDrawQuality default sqHigh;
    property Inside:Boolean read FInside write SetInside default False;
    property Left:Integer read FLeft write SetLeft default 0;
    property Mode:TTeeBackImageMode read FMode write SetMode default pbmStretch;
    property Right:Integer read FRight write SetRight default 0;
    property Top:Integer read FTop write SetTop default 0;
  end;

  TCustomTeePanelExtended=class(TCustomTeePanel)
  private
    FAllowPanning      : TPanningMode;
    FGradient          : TChartGradient;
    FScrollMouse       : TMouseButton;
    FZoom              : TTeeZoom;
    FZoomWheel         : TMouseWheelStyle;
    FZoomed            : Boolean;

    { for compatibility with Tee4 }
    Function GetAllowZoom:Boolean;
    Function GetAnimatedZoom:Boolean;
    Function GetAnimatedZoomSteps:Integer;
    Function GetBackImage:TBackImage;
    Function GetBackImageInside:Boolean;
    Function GetBackImageMode:TTeeBackImageMode;
    Function GetBackImageTransp:Boolean;
    Function GetGradient:TChartGradient;

    procedure ReadAnimatedZoomSteps(Reader: TReader);
    procedure ReadAnimatedZoom(Reader: TReader);
    procedure ReadAllowZoom(Reader: TReader);
    procedure ReadPrintMargins(Reader: TReader);
    procedure SavePrintMargins(Writer: TWriter);
    Procedure SetAllowZoom(Value:Boolean);
    Procedure SetAnimatedZoom(Value:Boolean);
    Procedure SetAnimatedZoomSteps(Value:Integer);

    procedure SetBackImage(const Value:TBackImage);
    procedure SetBackImageInside(Const Value:Boolean);
    procedure SetBackImageMode(Const Value:TTeeBackImageMode);
    procedure SetBackImageTransp(Const Value:Boolean);
    Procedure SetGradient(const Value:TChartGradient);
    Procedure SetZoom(const Value:TTeeZoom);
  protected
    FBackImage   : TBackImage;

    FOnAfterDraw : TNotifyEvent;
    FOnScroll    : TNotifyEvent;
    FOnUndoZoom  : TNotifyEvent;
    FOnZoom      : TNotifyEvent;

    Procedure DefineProperties(Filer:TFiler); override;
    procedure DrawBitmap(Rect:TRect; Z:Integer);
    procedure FillPanelRect(Const Rect:TRect); virtual;
    Function GetBackColor:TColor; override;

    {$IFNDEF FMX}
    {$IFDEF MSWINDOWS}
    function GetPalette: HPALETTE; override;    { override the method }
    {$ENDIF}
    {$ENDIF}

    procedure PanelPaint(const UserRect:TRect); virtual;

    {$IFNDEF FMX}
    {$IFDEF MSWINDOWS}
    function GetDeviceContext(var WindowHandle: HWnd): HDC; override;
    {$ENDIF}
    {$ENDIF}

    procedure TryDrawZoom;

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

    Procedure Assign(Source:TPersistent); override;
    Procedure DrawZoomRectangle;

    function HasBackImage:Boolean; // Returns True when has BackImage.Graphic
    procedure UndoZoom; dynamic;

    property Zoomed:Boolean read FZoomed write FZoomed;

    property AllowPanning:TPanningMode read FAllowPanning
                                       write FAllowPanning default pmBoth;

    { for compatibility with Tee4 }
    property AllowZoom:Boolean read GetAllowZoom write SetAllowZoom default True;
    property AnimatedZoom:Boolean read GetAnimatedZoom
                                  write SetAnimatedZoom default False;
    property AnimatedZoomSteps:Integer read GetAnimatedZoomSteps
                                       write SetAnimatedZoomSteps default 8;
    {}

    property BackImage:TBackImage read GetBackImage write SetBackImage;
    property BackImageInside:Boolean read GetBackImageInside
                                     write SetBackImageInside default False;
    property BackImageMode:TTeeBackImageMode read GetBackImageMode
                                             write SetBackImageMode
                                             default pbmStretch;
    property BackImageTransp:Boolean read GetBackImageTransp
                                     write SetBackImageTransp
                                       default {$IFDEF FMX}True{$ELSE}False{$ENDIF};

    property Gradient:TChartGradient read GetGradient write SetGradient;
    property ScrollMouseButton:TMouseButton read FScrollMouse
                                            write FScrollMouse default {$IFDEF FMX}TMouseButton.{$ENDIF}mbRight;
    property Zoom:TTeeZoom read FZoom write SetZoom;
    property ZoomWheel:TMouseWheelStyle read FZoomWheel write FZoomWheel default pmwNone;

    { events }
    property OnAfterDraw:TNotifyEvent read FOnAfterDraw write FOnAfterDraw;
    property OnScroll:TNotifyEvent read FOnScroll write FOnScroll;
    property OnUndoZoom:TNotifyEvent read FOnUndoZoom write FOnUndoZoom;
    property OnZoom:TNotifyEvent read FOnZoom write FOnZoom;
  end;

  TChartBrushClass=class of TChartBrush;

  TTeeCustomShapeBrushPen=class(TPersistent)
  private
    FBrush   : TTeeBrush;
    FParent  : TCustomTeePanel;
    FPen     : TTeePen;
    FVisible : Boolean;

    function IsVisibleStored:Boolean;
    Procedure SetBrush(const Value:TTeeBrush);
    Procedure SetPen(const Value:TTeePen);
    Procedure SetVisible(Value:Boolean);
  protected
    DefaultVisible : Boolean;
    Transform : TTeeTransform;

    Procedure CanvasChanged(Sender:TObject);
    Function GetBrushClass:TChartBrushClass; dynamic;
    Procedure SetParent(Value:TCustomTeePanel); virtual;
  public
    Constructor Create;
    Destructor Destroy; override;

    Procedure Assign(Source:TPersistent); override;
    Procedure Show; {$IFDEF D9}inline;{$ENDIF}
    Procedure Hide; {$IFDEF D9}inline;{$ENDIF}
    Procedure Repaint; virtual;

    property Brush:TTeeBrush read FBrush write SetBrush;
    property Frame:TTeePen read FPen write SetPen; // alias obsolete
    property ParentChart:TCustomTeePanel read FParent write SetParent;
    property Pen:TTeePen read FPen write SetPen;
    property Visible:Boolean read FVisible write SetVisible stored IsVisibleStored;
  end;

  TTeeCustomShape=class;

  TShapeCalloutSide=(csNone,csLeft,csTop,csRight,csBottom);

  TShapeCallout=class(TPersistent)
  private
    FPointPosition : Integer;
    FPosition  : Integer;
    FSide      : TShapeCalloutSide;
    FSize      : Integer;
    FWidth     : Integer;

    IOwner : TTeeCustomShape;

    procedure SetPosition(const Value:Integer);
    procedure SetSide(const Value:TShapeCalloutSide);
    procedure SetSize(const Value:Integer);
    procedure SetWidth(const Value:Integer);
  protected
    function GetPath(const ACanvas:TTeeCanvas; const R:TRect; Corner:Integer=0):TTeeBasePath;
  public
    Constructor Create(const AOwner:TTeeCustomShape);

    procedure Assign(Source:TPersistent); override;
  published
    property PointPosition:Integer read FPointPosition write FPointPosition default 50;
    property Position:Integer read FPosition write SetPosition default 50; // %
    property Side:TShapeCalloutSide read FSide write SetSide default csNone;
    property Size:Integer read FSize write SetSize default 25; // %
    property Width:Integer read FWidth write SetWidth default 20; // %
  end;

  TTeeShapeItem=class(TCollectionItem)
  private
    FShape : TTeeCustomShape;

    function OwnerShape:TTeeCustomShape;
    procedure SetShape(const Value:TTeeCustomShape);
  public
    Constructor Create(Collection: TCollection); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;
  published
    property Shape:TTeeCustomShape read FShape write SetShape;
  end;

  TTeeCustomShapeClass=class of TTeeCustomShape;

  TTeeShapes=class(TOwnedCollection)
  private
    IShapeClass : TTeeCustomShapeClass;

    Function Get(Index:Integer):TTeeShapeItem;
    Procedure Put(Index:Integer; Const Value:TTeeShapeItem);
  protected
  public
    function Add:TTeeShapeItem;

    property Item[Index:Integer]:TTeeShapeItem read Get write Put; default;
  end;

  TTeeShapeLayout=(slTopBottom, slLeftRight);

  TChartObjectShapeStyle=(fosRectangle,fosRoundRectangle,fosEllipse);

  TTeeCustomShape=class(TTeeCustomShapeBrushPen)
  private
    FAngle        : Integer;
    FAutoSize     : Boolean;
    FBevel        : TPanelBevel;
    FBevelWidth   : TBevelWidth;
    FCallout      : TShapeCallout;
    FChildren     : TTeeShapes;
    FChildLayout  : TTeeShapeLayout;
    FColor        : TColor;
    FCorners      : TTeeCorners;
    FFont         : TTeeFont;
    FRoundSize    : Integer;
    FShapeStyle   : TChartObjectShapeStyle;
    FTransparent  : Boolean;
    FVisual       : TVisualBlock;

    procedure DoDrawPoints(const Panel:TCustomTeePanel; const P:TPointArray; const AZ:Integer);
    Procedure InternalDraw(const Panel:TCustomTeePanel; const Points:TPointArray; const Rect:TRect;
                   UseRect:Boolean; Angle:Integer=0; AZ:Integer=0);

    function GetCallout:TShapeCallout;
    function GetCorners:TTeeCorners;
    function GetEmboss:TTeeEmboss;
    Function GetGradient:TChartGradient;
    Function GetHeight:TCoordinate;
    Function GetWidth:TCoordinate;
    Function GetPicture:TBackImage;
    Function GetShadowColor:TColor; // obsolete
    Function GetShadowSize:Integer; // obsolete

    function IsChildrenStored:Boolean;
    function IsPictureStored:Boolean; {$IFDEF D9}inline;{$ENDIF}
    Function IsTranspStored:Boolean; {$IFDEF D9}inline;{$ENDIF}

    procedure ReadShadowColor(Reader: TReader); // obsolete
    procedure ReadShadowSize(Reader: TReader); // obsolete

    Procedure PictureChanged(Sender:TObject);

    procedure SetAlignment(const Value:TAlignment);
    procedure SetAngle(const Value:Integer);
    procedure SetAutoSize(const Value: Boolean);
    Procedure SetBevel(const Value:TPanelBevel);
    procedure SetBevelWidth(const Value: TBevelWidth);
    procedure SetCallout(const Value:TShapeCallout);
    procedure SetChildren(const Value:TTeeShapes);
    Procedure SetColor(const Value:TColor);
    procedure SetCorners(const Value:TTeeCorners);
    Procedure SetEmboss(const Value:TTeeEmboss);
    Procedure SetFont(const Value:TTeeFont);
    procedure SetGradient(const Value:TChartGradient);
    procedure SetHeight(const Value:TCoordinate);
    procedure SetLayout(const Value:TTeeShapeLayout);
    Procedure SetPicture(const Value:TBackImage);
    procedure SetRoundSize(const Value:Integer);
    Procedure SetShadow(const Value:TTeeShadow);
    Procedure SetShadowColor(const Value:TColor); // obsolete
    Procedure SetShadowSize(const Value:Integer); // obsolete
    Procedure SetShapeStyle(const Value:TChartObjectShapeStyle);
    procedure SetTransparency(const Value:TTeeTransparency);
    procedure SetTransparent(const Value:Boolean);
    procedure SetWidth(const Value:TCoordinate);
  protected
    DefaultTransparent : Boolean;

    FAlignment     : TAlignment;
    FEmboss        : TTeeEmboss;
    FPicture       : TBackImage;
    FShadow        : TTeeShadow;
    FTransparency  : TTeeTransparency;
    FRotatedBounds : TFourPoints;

    FDraw3D       : Boolean;

    procedure CalcBounds(const Panel:TCustomTeePanel); virtual;

    function ClickedShape(const P:TPoint; const R:TRect):Boolean;

    Procedure DefineProperties(Filer:TFiler); override;

    Procedure DoDraw(const Panel:TCustomTeePanel;
                     const Points:TPointArray; const Rect : TRect; Angle:Integer=0; AZ:Integer=0); overload; virtual;
    Procedure DoDraw(const Panel:TCustomTeePanel;
                     const Rect:TRect; Angle:Integer=0; AZ:Integer=0); overload; virtual;

    function FirstChildPos:Integer; virtual;
    function GetChildClass:TTeeCustomShapeClass; virtual;
    class function GetEditorClass:String; virtual;

    function HasCorners:Boolean; {$IFDEF D9}inline;{$ENDIF}
    function HasPicture:Boolean; {$IFDEF D9}inline;{$ENDIF}

    procedure InitAngle(const AAngle:Integer); {$IFDEF D9}inline;{$ENDIF}
    procedure InitEmboss(const AEmboss:TTeeEmboss); dynamic;
    procedure InitShadow(const AShadow:TTeeShadow); dynamic;

    Procedure SetParent(Value:TCustomTeePanel); override;

    property Transparency:TTeeTransparency read FTransparency
                                           write SetTransparency default 0;

    procedure InitTransparent(const Value:Boolean);

    property Visual:TVisualBlock read FVisual write FVisual;
  public
      // Do not make this a property (it can't be accessed ie: ShapeBounds.Left:=123)
    ShapeBounds : TRect;

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

    Procedure Assign(Source:TPersistent); override;
    Procedure Draw; overload;
    Procedure Draw(Rect:TRect; Angle:Integer=0; AZ:Integer=0); overload;
    Procedure Draw(const Panel:TCustomTeePanel; Rect:TRect;
                   Angle:Integer=0; AZ:Integer=0); overload;

    Procedure Draw(Rect:TRect; const Points:TPointArray; Angle:Integer=0; AZ:Integer=0); overload;
    Procedure Draw(const Panel:TCustomTeePanel; Rect:TRect; const Points:TPointArray;
                   Angle:Integer=0; AZ:Integer=0); overload;

    procedure MoveTo(const P:TPoint); overload;
    procedure MoveTo(const x,y:TCoordinate); overload;

    // compatibility with TeeChart v5  (obsolete)
    property ShadowColor:TColor read GetShadowColor write SetShadowColor default {$IFDEF FMX}TAlphaColors.Black{$ELSE}clBlack{$ENDIF};
                               // {$IFDEF D11}deprecated 'Please use Shadow.Color property.';{$ENDIF}

    property ShadowSize:Integer read GetShadowSize write SetShadowSize default 3;
                               // {$IFDEF D11}deprecated 'Please use Shadow.Size property.';{$ENDIF}

    // public properties
    property Height:TCoordinate read GetHeight write SetHeight;
    property Width:TCoordinate read GetWidth write SetWidth;

    // to be published
    property Alignment: TAlignment read FAlignment
                                   write SetAlignment default taLeftJustify;
    property Angle:Integer read FAngle write SetAngle default 0;
    property AutoSize:Boolean read FAutoSize write SetAutoSize default True;
    property Bevel:TPanelBevel read FBevel write SetBevel default bvNone;
    property BevelWidth:TBevelWidth read FBevelWidth write SetBevelWidth default 2;
    property Children:TTeeShapes read FChildren write SetChildren stored IsChildrenStored;
    property ChildLayout:TTeeShapeLayout read FChildLayout write SetLayout default slTopBottom;
    property Color:TColor read FColor write SetColor default clWhite;
    property Corners:TTeeCorners read GetCorners write SetCorners stored HasCorners;
    property Emboss:TTeeEmboss read GetEmboss write SetEmboss;
    property Font:TTeeFont read FFont write SetFont;
    property Gradient:TChartGradient read GetGradient write SetGradient stored False;
    property Picture:TBackImage read GetPicture write SetPicture stored IsPictureStored;
    property RoundSize:Integer read FRoundSize write SetRoundSize default 16;
    property Shadow:TTeeShadow read FShadow write SetShadow;

    property ShapeCallout:TShapeCallout read GetCallout write SetCallout;
    property ShapeStyle:TChartObjectShapeStyle read FShapeStyle
                                   write SetShapeStyle default fosRectangle;
    property Transparent:Boolean read FTransparent write SetTransparent stored IsTranspStored;
  end;

  TTeeShape=class(TTeeCustomShape)
  public
    property Transparency;
  published
    property Angle;
    property Bevel;
    property BevelWidth;
    property Children;
    property ChildLayout;
    property Color;
    property Corners;
    property Emboss;
    property Font;
    property Gradient;
    property Picture;
    property RoundSize;
    property Shadow;
    property ShapeCallout;
    property ShapeStyle;
    property Transparent;
    property Visible;
  end;

  TVertTextAlign  =(vtaCenter,vtaTop,vtaBottom);

  TeeString256=Array[0..255] of Char;

  // Used at TeExport, TeeStore and TeeTree export dialogs.
  TTeeExportData=class(TPersistent)
  public
    {$IFDEF D12}
    FileEncoding : TEncoding;
    {$ENDIF}

    Function AsString:String; virtual; // abstract;
    Procedure CopyToClipboard; dynamic;
    Procedure SaveToFile(Const FileName:String); dynamic;
    Procedure SaveToStream(AStream:TStream); dynamic;
  end;

  TMouseLeaveEvent=class(TTeeEvent);

  {$IFNDEF TEELITEEMB}
  TParallelProc = procedure(Index:Integer) of object;

  {$IFDEF MSWINDOWS}
  {$IFDEF CPUX86}
    {$DEFINE TEECPUASM}
  {$ENDIF}
  {$IFDEF CPUX64}
    {$IFNDEF FPC}
    {$DEFINE TEECPUASM}
    {$ENDIF}
  {$ENDIF}
  {$ENDIF}

  TTeeCPU=class
  public
    {$IFDEF TEECPUASM}
    class function HasAVX:Boolean;
    class function HasMMX:Boolean;
    class function HasSSE:Boolean;
    class function HasSSE2:Boolean;
    {$ENDIF}

    class function Identifier:String;
    class function Name:String;
    class function NumberOfProcessors:Integer;
    class function Speed:Integer;

    class procedure ParallelFor(nMin, nMax, nThreads: Integer; aProc: TParallelProc
           {$IFDEF MSWINDOWS}; APriority:TThreadPriority=tpNormal{$ENDIF}); overload;

    class procedure ParallelFor(nMin, nMax: Integer; aProc: TParallelProc); overload;

    class function TotalMemoryAllocated:Cardinal;
  end;
  {$ENDIF}

  TAxisValue = {$IFDEF TEENOASM}{$IFDEF TEEVALUEEXTENDED}Extended{$ELSE}Double{$ENDIF}{$ELSE}Double{$ENDIF};

Function TeeStr(const Num:Integer):String; // same as IntToStr but a bit faster

{ returns the appropiate string date or time format according to "step" }
Function DateTimeDefaultFormat(Const AStep:Double):String;

{ returns the number of days of month-year }
Function DaysInMonth(const Year,Month:Word):Word;

{ Given a "step", return the corresponding set element }
Function FindDateTimeStep(Const StepValue:Double):TDateTimeStep;

{ Returns the next "step" in ascending order. (eg: TwoDays follows OneDay }
Function NextDateTimeStep(Const AStep:Double):Double;

{ Returns Rectangle around P +/-Half Width +/-Half Height }
function RectFromCenter(const P:TPoint; AHalfWidth:Integer; AHalfHeight:Integer=0):TRect; overload;
function RectFromCenter(const X,Y:TCoordinate; AHalfWidth:Integer; AHalfHeight:Integer=0):TRect; overload;

{ Returns True if point T is over line:  P --> Q }
Function PointInLine(Const P:TPoint; const px,py,qx,qy:Integer):Boolean; overload;
Function PointInLine(Const P,FromPoint,ToPoint:TPoint):Boolean; overload;

{ Returns True if point T is over (more or less "Tolerance" pixels)
  line:  P --> Q }
Function PointInLine(const P,FromPoint,ToPoint:TPoint; const TolerancePixels:Integer):Boolean; overload; // obsolete;
Function PointInLine(const P:TPoint; const px,py,qx,qy,TolerancePixels:
                       TCoordinate):Boolean; overload;

Function PointInLineTolerance(Const P:TPoint;
                              Const px,py,qx,qy,TolerancePixels:Integer):Boolean;
                              {$IFDEF D11}deprecated 'Please use PointInLine function.';{$ENDIF}

{ Returns True if point P is inside Poly polygon }
Function PointInPolygon(Const P:TPoint; Const Poly:Array of TPoint):Boolean;

{ Returns True if point P is inside the triangle formed with P0,P1 and P2 }
Function PointInTriangle(Const P,P0,P1,P2:TPoint):Boolean; overload; {$IFDEF D9}inline;{$ENDIF}

{ Returns True if point P is inside the vert triangle of x0y0, midxY1, x1y0 }
Function PointInTriangle(Const P:TPoint; const X0,X1,Y0,Y1:TCoordinate):Boolean; overload;

{ Returns True if point P is inside the horiz triangle of x0y0, x1midY, x0y0 }
Function PointInHorizTriangle(Const P:TPoint; const Y0,Y1,X0,X1:TCoordinate):Boolean;

{ Returns True if point P is inside the ellipse bounded by Rect }
Function PointInEllipse(Const P:TPoint; Const Rect:TRect):Boolean; overload;
Function PointInEllipse(Const P:TPoint; const Left,Top,Right,Bottom:
                       TCoordinate):Boolean; overload;

{ This functions try to solve locale problems with formatting numbers }
Function DelphiToLocalFormat(Const Format:String):String;
Function LocalToDelphiFormat(Const Format:String):String;

{ For all controls in the array, set the Enabled property }
Procedure EnableControls(Enable:Boolean; Const ControlArray:Array of TControl);

{ Round "ADate" to the nearest "AStep" value }
Function TeeRoundDate(Const ADate:TDateTime; AStep:TDateTimeStep):TDateTime;

{ Increment or Decrement "Value", for DateTime }
// Warning: Removed first parameter "IsDateTime". It is assumed True.
Procedure TeeDateTimeIncrement( Increment:Boolean;
                                var Value:TAxisValue;
                                const AnIncrement:TAxisValue;
                                const ADateTime:TDateTimeStep);

{ Generic "QuickSort" sorting algorithm }
type TTeeSortCompare=Function(a,b:Integer):Integer of object;
     TTeeSortSwap=Procedure(a,b:Integer) of object;

Procedure TeeSort( StartIndex,EndIndex:Integer;
                   CompareFunc:TTeeSortCompare;
                   SwapFunc:TTeeSortSwap);

{ Returns a valid component name }
Function TeeGetUniqueName(const AOwner:TComponent; Const AStartName:String):TComponentName;

{ Delimited field routines }
var
  TeeFieldsSeparator:String=';';

{ Returns the "index" item in string, using "TeeFieldsSeparator" character }
Function TeeExtractField(St:String; Index:Integer):String; overload;

{ Returns the "index" item in string, using "Separator" parameter }
Function TeeExtractField(St:String; Index:Integer; const Separator:String):String; overload;

{ Returns the number of fields in string, using "TeeFieldsSeparator" character }
Function TeeNumFields(St:String):Integer; overload;
{ Returns the number of fields in string, using "Separator" parameter }
Function TeeNumFields(const St,Separator:String):Integer; overload;

{ Try to find a resource bitmap and load it }
Procedure TeeGetBitmapEditor(const AObject:TObject; var Bitmap:TBitmap);
Procedure TeeLoadBitmap(const Bitmap:TBitmap; const Name1:String; const Name2:String='');

type TColorArray=Array of TColor;
  PColorArray=^TColorArray;

{ returns one of the sample colors in default ColorPalette constant array }
Function GetDefaultColor(Const Index:Integer):TColor;

var
  TeeColorPalette:TColorArray;

// Resets global ColorPalette to default one.
Procedure SetDefaultColorPalette; overload;

// Set global ColorPalette to Palette param array.
Procedure SetDefaultColorPalette(const Palette:Array of TColor); overload;

const
  TeeBorderStyle=bsSizeable;

  { Keyboard codes }
  TeeKey_Escape = {$IFDEF FMX}vkEscape   {$ELSE}VK_ESCAPE{$ENDIF};
  TeeKey_Up     = {$IFDEF FMX}vkUp       {$ELSE}VK_UP{$ENDIF};
  TeeKey_Down   = {$IFDEF FMX}vkDown     {$ELSE}VK_DOWN{$ENDIF};
  TeeKey_Insert = {$IFDEF FMX}vkInsert   {$ELSE}VK_INSERT{$ENDIF};
  TeeKey_Delete = {$IFDEF FMX}vkDelete   {$ELSE}VK_DELETE{$ENDIF};
  TeeKey_Left   = {$IFDEF FMX}vkLeft     {$ELSE}VK_LEFT{$ENDIF};
  TeeKey_Right  = {$IFDEF FMX}vkRight    {$ELSE}VK_RIGHT{$ENDIF};
  TeeKey_Return = {$IFDEF FMX}vkReturn   {$ELSE}VK_RETURN{$ENDIF};
  TeeKey_Space  = {$IFDEF FMX}vkSpace    {$ELSE}VK_SPACE{$ENDIF};
  TeeKey_Back   = {$IFDEF FMX}vkBack     {$ELSE}VK_BACK{$ENDIF};
  TeeKey_Home   = {$IFDEF FMX}vkHome     {$ELSE}VK_HOME{$ENDIF};
  TeeKey_Prior  = {$IFDEF FMX}vkPrior    {$ELSE}VK_PRIOR{$ENDIF};
  TeeKey_Next   = {$IFDEF FMX}vkNext     {$ELSE}VK_NEXT{$ENDIF};
  TeeKey_End    = {$IFDEF FMX}vkEnd      {$ELSE}VK_END{$ENDIF};

  TeeKey_F1     = {$IFDEF FMX}vkF1 {$ELSE}VK_F1 {$ENDIF};
  TeeKey_F2     = {$IFDEF FMX}vkF2 {$ELSE}VK_F2 {$ENDIF};
  TeeKey_F3     = {$IFDEF FMX}vkF3 {$ELSE}VK_F3 {$ENDIF};
  TeeKey_F4     = {$IFDEF FMX}vkF4 {$ELSE}VK_F4 {$ENDIF};
  TeeKey_F5     = {$IFDEF FMX}vkF5 {$ELSE}VK_F5 {$ENDIF};
  TeeKey_F6     = {$IFDEF FMX}vkF6 {$ELSE}VK_F6 {$ENDIF};
  TeeKey_F7     = {$IFDEF FMX}vkF7 {$ELSE}VK_F7 {$ENDIF};
  TeeKey_F8     = {$IFDEF FMX}vkF8 {$ELSE}VK_F8 {$ENDIF};
  TeeKey_F9     = {$IFDEF FMX}vkF9 {$ELSE}VK_F9 {$ENDIF};
  TeeKey_F10    = {$IFDEF FMX}vkF10{$ELSE}VK_F10{$ENDIF};
  TeeKey_F11    = {$IFDEF FMX}vkF11{$ELSE}VK_F11{$ENDIF};
  TeeKey_F12    = {$IFDEF FMX}vkF12{$ELSE}VK_F12{$ENDIF};
  TeeKey_F13    = {$IFDEF FMX}vkF13{$ELSE}VK_F13{$ENDIF};
  TeeKey_F14    = {$IFDEF FMX}vkF14{$ELSE}VK_F14{$ENDIF};
  TeeKey_F15    = {$IFDEF FMX}vkF15{$ELSE}VK_F15{$ENDIF};
  TeeKey_F16    = {$IFDEF FMX}vkF16{$ELSE}VK_F16{$ENDIF};
  TeeKey_F17    = {$IFDEF FMX}vkF17{$ELSE}VK_F17{$ENDIF};
  TeeKey_F18    = {$IFDEF FMX}vkF18{$ELSE}VK_F18{$ENDIF};
  TeeKey_F19    = {$IFDEF FMX}vkF19{$ELSE}VK_F19{$ENDIF};
  TeeKey_F20    = {$IFDEF FMX}vkF20{$ELSE}VK_F20{$ENDIF};
  TeeKey_F21    = {$IFDEF FMX}vkF21{$ELSE}VK_F21{$ENDIF};
  TeeKey_F22    = {$IFDEF FMX}vkF22{$ELSE}VK_F22{$ENDIF};
  TeeKey_F23    = {$IFDEF FMX}vkF23{$ELSE}VK_F23{$ENDIF};
  TeeKey_F24    = {$IFDEF FMX}vkF24{$ELSE}VK_F24{$ENDIF};

{$IFNDEF D6}
function StrToFloatDef(const S: string; const Default: Extended): Extended;
function TryStrToFloat(const S: String; var Value:Double):Boolean;
{$ENDIF}

{ Returns True if line1 and line2 cross each other.
  xy is returned with crossing point. }
function CrossingLines(const X1,Y1,X2,Y2,X3,Y3,X4,Y4:Double; out x,y:Double):Boolean;

// TRANSLATIONS
type
  TTranslatable={$IFDEF FMX}TFmxObject{$ELSE}TComponent{$ENDIF};

  TTeeTranslateHook=procedure(const AControl:TTranslatable; const ExcludedChilds:Array of TTranslatable);
  TTeeTranslateStringHook=function(const Text:String):String;

var
  TeeTranslateHook:TTeeTranslateHook=nil;
  TeeTranslateStringHook:TTeeTranslateStringHook=nil;

// Main procedure to translate a control (or Form)
Procedure TeeTranslateControl(const AControl:TTranslatable); overload;
Procedure TeeTranslateControl(const AControl:TTranslatable; const ExcludeChilds:Array of TTranslatable); overload;
function TeeTranslateString(const Text:String):String;

// Replaces "Search" char with "Replace" char
// in all occurrences in AString parameter.
// Returns "AString" with replace characters.
Function ReplaceChar(const AString:String; const Search:{$IFDEF NET}String{$ELSE}Char{$ENDIF};
                     const Replace:Char=#0):String;

// Returns "P" calculating 4 rotated corners using Angle parameter
// Note: Due to a C++ Builder v5 bug, this procedure is not a function.
Procedure RectToFourPoints(Const ARect:TRect; const Angle:Double; out P:TFourPoints);

Function TeeAntiAlias(const Panel:TCustomTeePanel; ChartRect:Boolean=False):TBitmap;

Procedure DrawBevel(const Canvas:TTeeCanvas; Bevel:TPanelBevel; var R:TRect;
                    Width:Integer; Round:Integer=0);


procedure TeeCopyClipboard(const Text:String);
function TeeGetScreenSize:TPoint;
procedure TeeChangeCursor(const AOwner:TObject; ACursor:TCursor);
function ScreenRatio(const ACanvas:TCanvas3D):Double;

// Internal use. Reads and saves values from / to TRegistry / Inifile
// Used by TGalleryPanel, TChartEditor, TeeChartReg unit and TeeDesignOptions
function TeeReadBoolOption(const AKey:String; DefaultValue:Boolean):Boolean;
procedure TeeSaveBoolOption(const AKey:String; Value:Boolean);
Function TeeReadIntegerOption(const AKey:String; DefaultValue:Integer):Integer;
procedure TeeSaveIntegerOption(const AKey:String; Value:Integer);
function TeeReadStringOption(const AKey,DefaultValue:String):String;
procedure TeeSaveStringOption(const AKey,Value:String);

procedure TeeFixRegistry(const AOldKey, ANewKey:String);
// Loads / Saves Form position and size from registry:
procedure TeeLoadFormPosition(const AForm:TForm; const AKey:String; Position,Size:Boolean);
procedure TeeSaveFormPosition(const AForm:TForm; const AKey:String; Position,Size:Boolean);

function TeeDefaultXMLEncoding:String;
function TeeXMLHeader(const Encoding:String):String;

// Converts a Stream with a *.tee file in Text format, to XML format
procedure ConvertTextToXML(const Stream:TStream; XMLHeader:Boolean=True);

// Returns a new TStringList containing all file names in Path, matching wildcards parameter:
Function TeeGetFilesFromDir(const PathAndWildcards:String):TStringList;

type
  TeeWindowHandle={$IFDEF FMX}TFmxHandle{$ELSE}Integer{$ENDIF};

Procedure TeeGotoURL(Handle: TeeWindowHandle; Const URL:String);
Function TeeIsURL(St:String):Boolean;

// Returns the index position of "SubStr" text inside "S" string, or -1 if not exists.
function StringContains(const SubStr,S:String):Integer; {$IFDEF D9}inline;{$ENDIF}

// Removes all Controls in TWinControl
procedure RemoveControls(const AControl:{$IFDEF FMX}TFmxObject{$ELSE}TWinControl{$ENDIF});

// Registers form class (VCL and FMX)
procedure RegisterSeriesEditor(const AClass:TPersistentClass);

{$IFDEF LCL}
const
  CM_SHOWINGCHANGED = CM_BASE + 25;
{$ENDIF}

{$IFDEF TEELITE}
const
  TeeRADXE2_SpecialWWW='http://www.steema.com/linkIn/TeeChartForXE2';
  {$IFDEF TEELITEEMB}
  TeeRADXE2_SpecialOffer='Upgrade to Standard or Pro version with 100% source code !';
  {$ELSE}
  TeeRADXE2_SpecialOffer='Upgrade to Pro version with 100% source code !';
  {$ENDIF}
{$ENDIF}

{$IFNDEF D6}
// From Math.pas:
function SameValue(const A, B: Double; Epsilon: Double=0): Boolean;
{$ENDIF}

// Cross-platform cross-VCL-FMX GetTickCount:
function TeeTickCount:Single;

// Sets APanel.Canvas pen mode to "pmNotXor" and checks APen color against APanel back color
procedure TeePrepareXorPen(const APanel:TCustomTeePanelExtended; const APen:TTeePen);

// Remove the extension of a file name:  Foo.txt --> Foo
function RemoveFileExtension(const S:String):String;

type
  {$IFNDEF FMX}
  TDownloadURLCallBack=procedure(const Size:Int64; var CancelDownload:Boolean) of object;
  {$ENDIF}

  TTeeDownloadURLProc=function(const URL:String; const ToStream:TStream
                               {$IFNDEF FMX}
                               ; OnCallback:TDownloadURLCallBack=nil
                               {$ENDIF}
                               ; UseCache:Boolean=False): {$IFDEF FMX}Integer{$ELSE}HResult{$ENDIF}
                               ;

var
  TeeDownloadURLProc:TTeeDownloadURLProc=nil;

implementation

{$IFDEF MSWINDOWS}
{$IFNDEF FMI}
{$DEFINE TEEREGISTRY}
{$ENDIF}
{$ENDIF}

{$IFDEF D21}
{$IFDEF MSWINDOWS} // For XE7 Android, iOS etc, see: QC 126307
{.$DEFINE TEEPARALLEL} // Not yet ready
{$ENDIF}
{$ENDIF}

Uses {$IFDEF USETHEMES}
     Themes, UxTheme,
     {$ENDIF}

     Math,

     {$IFDEF TEEPARALLEL}
     System.Threading,
     {$ENDIF}
     
     {$IFDEF TEEWINDOWS}
     ShellApi,
     {$ENDIF}

     {$IFDEF LINUX}
     IniFiles,
     {$ELSE}
     {$IFDEF TEEREGISTRY}
     {$IFDEF FMX}System.Win.Registry{$ELSE}Registry{$ENDIF},
     {$ENDIF}
     {$ENDIF}

     {$IFNDEF TEELITEEMB}
     SyncObjs,
     {$ENDIF}

     {$IFDEF FMX}
     FMXTee.Constants, FMXTee.Html, FMX.Colors
       {$IFNDEF TEELITEEMB}
       //, FMX.Editors.Exception
       {$ENDIF}

       {$IFDEF D19}
       {$IFNDEF D20}
       , FMX.PixelFormats
       {$ENDIF}
       {$ENDIF}

     {$ELSE}
     TeeConst, TeeFilters, TeeHtml

       {$IFDEF TEEEXCEPTIONS}
       {$IFNDEF FPC}
       , TeeException
       {$ENDIF}
       {$ENDIF}

     {$ENDIF};

{$R TeeResou.res}

{$IFDEF D15}
{$DEFINE TEEFORMAT}
{$ENDIF}

{$IFDEF LCL}
{$DEFINE TEEFORMAT}
{$ENDIF}

{ Same as IntToStr but faster }
Function TeeStr(const Num:Integer):String; {$IFDEF D9}inline;{$ENDIF}
begin
  {$IFDEF D12}
  result:=IntToStr(Num);  // Unicode
  {$ELSE}
  Str(Num,Result);   // ShortString
  {$ENDIF}
end;

// Returns the index position of "SubStr" text inside "S" string, or -1 if not exists.
function StringContains(const SubStr,S:String):Integer; {$IFDEF D9}inline;{$ENDIF}
begin
  result:={$IFDEF D14}Pos{$ELSE}AnsiPos{$ENDIF}(SubStr,S);
end;

// Returns one of the sample colors in default ColorPalette constant array
Function GetDefaultColor(Const Index:Integer):TColor;
Begin
  result:=TeeColorPalette[Low(TeeColorPalette)+(Index mod Succ(High(TeeColorPalette)))];
end;

function DaysInMonth(const Year,Month:Word):Word;
begin
  result:=MonthDays[IsLeapYear(Year),Month]
end;

Function DateTimeDefaultFormat(Const AStep:Double):String; {$IFDEF D9}inline;{$ENDIF}
Begin
  if AStep<=1 then result:={$IFDEF TEEFORMAT}FormatSettings.{$ENDIF}ShortTimeFormat
              else result:={$IFDEF TEEFORMAT}FormatSettings.{$ENDIF}ShortDateFormat;
end;

Function NextDateTimeStep(Const AStep:Double):Double;
var t : TDateTimeStep;
Begin
  for t:=Pred(dtOneYear) downto Low(DateTimeStep) do
  if AStep>=DateTimeStep[t] then
  Begin
    result:=DateTimeStep[Succ(t)];
    exit;
  end;
  result:=DateTimeStep[dtOneYear];
end;

Function FindDateTimeStep(Const StepValue:Double):TDateTimeStep;
var Min : Double;
begin
  Min:=DateTimeStep[Low(DateTimeStep)];
  for result:=Pred(High(DateTimeStep)) downto Low(DateTimeStep) do
      if Abs(DateTimeStep[result]-StepValue)<Min then
         exit;

  result:=dtNone;
end;

{ TCustomPanelNoCaption }

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

  {$IFDEF FMX}
  FColor:=$FFF0F0F0;
  ClipChildren:=True;
  {$ELSE}
  ControlStyle:=ControlStyle-[csSetCaption {$IFDEF D7}, csParentBackground{$ENDIF} ];
  {$ENDIF}

 // ControlState:=ControlState+[csCustomPaint]; <-- This will force WMPaint instead of PaintControls
end;

{$IFDEF FMX}
procedure TCustomPanelNoCaption.Invalidate;
begin
  Repaint;
end;

procedure TCustomPanelNoCaption.SetColor(const Value: TColor);
begin
  if FColor<>Value then
  begin
    FColor:=Value;
    Invalidate;
  end;
end;
{$ENDIF}

{ TCustomTeePanel }

{$IFDEF FMX}
type
  TView3DAccess=class(TView3DOptions);
{$ENDIF}

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

  ICustomPrinterRect :=  TeeRect(0,0,0,0);

  IEventListeners:=TTeeEventListeners.Create;

  if TeeEraseBack then
     TeeEraseBack:=not (csDesigning in ComponentState);

// This is done automatic in ancestor control TCustomPanel class,
// with ThemeServices Enabled:
//  ControlStyle:=ControlStyle-[csOpaque];
//  ControlStyle:=ControlStyle+[csParentBackground];

  {$IFDEF FMX}
  CanFocus:=True;
  {$ELSE}
  TabStop:=True;
  {$ENDIF}

  {$IFDEF FMI}
  Width:=305;
  Height:=200;
  {$ELSE}

  {$IFDEF FMX}
  if Owner is TCommonCustomForm then
  begin
    Width:=Min(400,TCommonCustomForm(Owner).Width);
    Height:=Min(250,TCommonCustomForm(Owner).Height);
  end;

  {$ELSE}
  Width:=400;
  Height:=250;
  {$ENDIF}
  {$ENDIF}

  FApplyZOrder :=True;
  FDelphiCanvas:=inherited Canvas;

  FView3D      :=True;

  // For new charts, set default canvas whatever is at the Registry,
  // or use the class indicated by TeeDefaultCanvas global variable,
  // which is (in VCL) the GDI+ canvas by default.

  // Setting TeeDefaultCanvas:='' will not look at registry, and will not
  // use VCL GDI+

  if //(csDesigning in ComponentState) and
     (TeeDefaultCanvas<>'') and
     (not (csLoading in ComponentState)) then
     IDefaultCanvas:=TeeReadStringOption('DefaultCanvas',TeeDefaultCanvas)
  else
     IDefaultCanvas:=''; // Existing old charts saved in dfm/fmx, no change

  CreateDefaultCanvas;

  FView3DOptions:=TView3DOptions.Create({$IFDEF TEEVCL}Self{$ENDIF});

  {$IFDEF FMX}
  // Use an event because TControl.Invalidate or TControl.Repaint aren't virtual 
  TView3DAccess(FView3DOptions).FOnInvalidate:=InvalidateEvent;
  {$ENDIF}

  FMargins:= TeeRect( TeeDefHorizMargin,TeeDefVerticalMargin,
                      TeeDefHorizMargin,TeeDefVerticalMargin);
  FPrintProportional:=True;
  FPrintResolution:=TeeNormalPrintDetail;

  PrintMargins.Left:=TeeDefault_PrintMargin;
  PrintMargins.Right:=TeeDefault_PrintMargin;
  PrintMargins.Top:=TeeDefault_PrintMargin;
  PrintMargins.Bottom:=TeeDefault_PrintMargin;

  FOriginalCursor:=Cursor;

  FPanning:=TTeePanning.Create;

  FShadow:=TTeeShadow.Create(CanvasChanged);
  FEmboss:=TTeeEmboss.Create(CanvasChanged);
  FBorder:=TTeeHiddenPen.Create(CanvasChanged);

  {$IFDEF FMX}
  FBevelInner:=bvNone;
  FBevelOuter:=bvRaised;
  FBevelWidth:=1;
  {$ENDIF}

  AutoRepaint:=True;

  ICachedChartRect:=TeeZeroRect;
end;

Destructor TCustomTeePanel.Destroy;
Begin
  if not IsEmbedded then
     FreeAndNil(InternalCanvas);

  FGlobalFont.Free;
  FGlobalPicture.Free;

  FBorder.Free;
  FEmboss.Free;
  FShadow.Free;
  FView3DOptions.Free;
  FPanning.Free;
  FreeAndNil(IEventListeners);

  Theme.Free;

  inherited;
end;

{$IFDEF LCL} // Lazarus AutoSize (GetPreferredSize, etc)
{
class function TCustomTeePanel.GetControlClassDefaultSize: TSize;
begin
  Result.CX := 400;
  Result.CY := 250;
end;
}
{$ENDIF}

procedure TCustomTeePanel.CreateDefaultCanvas;
var t   : Integer;
    tmp,
    tmpClass : TCanvas3DClass;
begin
  tmpClass:=TTeeCanvas3D;

  if IDefaultCanvas<>'' then
     for t:=0 to TeeRenderClasses.Count-1 do
     begin
       tmp:={$IFNDEF D18}TCanvas3DClass{$ENDIF}(TeeRenderClasses[t]);

       if tmp.ClassName=IDefaultCanvas then
       begin
         tmpClass:=tmp;
         break;
       end;
     end;

  if (not Assigned(InternalCanvas)) or (tmpClass<>InternalCanvas.ClassType) then
  begin
    //NOT: Just in case, if we're called from inside a package called from a dll from C++
    //NOT: if tmpClass.IsAvailable then
    begin
      InternalCanvas.Free;
      InternalCanvas:=tmpClass.Create;

      {$IFDEF FMX}
      {$IFDEF D22}
      // Problem XE8 design-time, chart disappears randomly
      if csDesigning in ComponentState then
         InternalCanvas.UseBuffer:=False;
      {$ENDIF}
      {$ENDIF}

      {$IFNDEF FMX}
      //if (Parent<>nil) and Parent.Showing then  // <-- To fix London AV design-time
      {$ENDIF}
      InternalCanvas.ReferenceCanvas:=FDelphiCanvas;
    end;
  end;
end;

Procedure TCustomTeePanel.CanvasChanged(Sender:TObject);
Begin
  Invalidate;
end;

{$IFNDEF FMX}
{$IFNDEF LCL}
procedure TCustomTeePanel.CreateParams(var Params: TCreateParams);
begin
  inherited;

  // Needed for OpenGL, to avoid re-creating GL Context all the time,
  // due to Canvas DC Handle changing:
  with Params.WindowClass do
     Style:=Style or CS_OWNDC;

  // Transparent Window when Color=clNone:
  if Color=clNone then
     Params.ExStyle:=Params.ExStyle or WS_EX_TRANSPARENT;

  // Reset View3DOptions
  if Assigned(InternalCanvas) then
     InternalCanvas.View3DOptions:=nil;
end;
{$ENDIF}
{$ENDIF}

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

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

function TCustomTeePanel.GetEditablePanel:TCustomTeePanel;
begin
  result:=Self;
end;

function TCustomTeePanel.Filters:TFilterItems;
begin
  if not Assigned(FGlobalPicture) then
  begin
    FGlobalPicture:=TTeePicture.Create;
    FGlobalPicture.Filters.OnChange:=CanvasChanged;
  end;

  result:=FGlobalPicture.Filters;
end;

Procedure TCustomTeePanel.InternalDraw(Const UserRectangle:TRect); // virtual; abstract;
begin
end;

// Returns the index position of C char inside "S" string, or -1 if not exists.
function StringContainsChar(const C:Char; const S:String):Integer; {$IFDEF D9}inline;{$ENDIF}
begin
  result:={$IFDEF D14}Pos{$ELSE}AnsiPos{$ENDIF}(C,S);
end;

// Optimized version of MultiLineTextSize.
// Return only the number of lines of text in S
function TCustomTeePanel.LinesOfText(S:String):Integer;
const
  tmpSep:String={$IFDEF POSIX}#10{$ELSE}#13{$ENDIF};

var i : Integer;
begin
  i:={$IFDEF D14}Pos{$ELSE}AnsiPos{$ENDIF}(tmpSep,S);

  if i=0 then
     result:=1
  else
  begin
    result:=0;

    while i>0 do
    begin
      Inc(result);
      Delete(s,1,i);

      if Copy(s,1,1)=#10 then
         Delete(s,1,1);

      i:={$IFDEF D14}Pos{$ELSE}AnsiPos{$ENDIF}(tmpSep,S);
    end;

    if s<>'' then
       Inc(result);
  end;
end;

function TCustomTeePanel.MultiLineTextSize(S:String; const TextFormat:TTextFormat; out NumLines:Integer):TPoint;
var tmpFontH : Integer;

  procedure CalcSegment(const S:String);
  var tmp : TSize;
  begin
    if TextFormat=ttfNormal then
    begin
      tmp.Width:=Canvas.TextWidth(S);

      if tmpFontH=0 then
         tmpFontH:=Canvas.FontHeight;

      tmp.Height:=tmpFontH;
    end
    else
      tmp:=HtmlTextExtent(Canvas,S);

    result.X:=Math.Max(result.X,tmp.Width);
    result.Y:=result.Y+tmp.Height;

    Inc(NumLines);
  end;

var i : Integer;
begin
  result.X:=0;
  result.Y:=0;

  NumLines:=0;

  tmpFontH:=0;

  i:=StringContains(TeeLineSeparator,s);

  while i>0 do
  begin
    CalcSegment(Copy(s,1,i-1));
    Delete(s,1,i);

    if Copy(s,1,1)=#10 then
       Delete(s,1,1);

    i:=StringContains(TeeLineSeparator,s);
  end;

  if s<>'' then
     CalcSegment(s);
end;

{ This function returns the longest width in pixels for a given "S" string.
  It also considers multi-line strings, returning in the NumLines
  parameter the count of lines. }
function TCustomTeePanel.MultiLineTextWidth(S:String; out NumLines:Integer; const TextFormat:TTextFormat):Integer;
begin
  result:={$IFDEF FMX}Round{$ENDIF}(MultiLineTextSize(S,TextFormat,NumLines).X);
end;

procedure TCustomTeePanel.PrepareFont3D(const AFont:TTeeFont);
begin
  if View3D then
  begin
    if AFont.DefaultFont then
       Canvas.AssignFont3D(DefaultFont)
    else
       Canvas.AssignFont3D(AFont)
  end
  else
     TryAssignFont(AFont);
end;

procedure TCustomTeePanel.SetView3DOptions(Value:TView3DOptions);
begin
  FView3DOptions.Assign(Value);
end;

procedure TCustomTeePanel.SetView3D(Value:Boolean);
Begin
  if FView3D<>Value then
  begin
    SetBooleanProperty(FView3D,Value);
    BroadcastTeeEventClass(TTeeView3DEvent);
  end;
end;

{$IFDEF FMX}
Function TCustomTeePanel.GetClientRect:TRect;
begin
  result:=TeeRect(Width, Height);
end;
{$ENDIF}

Procedure TCustomTeePanel.Draw;

  {$IFDEF FMX}
  function GetDelphiCanvas:Boolean;
  var tmpCanvas : TCanvas;
  begin
    result:=True;

    tmpCanvas:=inherited Canvas;

    if (not Assigned(FDelphiCanvas)) or (FDelphiCanvas<>tmpCanvas) then
    begin
      FDelphiCanvas:=tmpCanvas;

      if Assigned(FDelphiCanvas) then
         InternalCanvas.ReferenceCanvas:=FDelphiCanvas
      else
         result:=False;
    end;
  end;
  {$ENDIF}

begin
  {$IFDEF FMX}
  if TempCanvas<>nil then
  begin
    Draw(TempCanvas,GetClientRect);

    GetDelphiCanvas;
    Exit;
  end;

  if not GetDelphiCanvas then
     Exit;
  {$ENDIF}

  {$IFNDEF FMX}
  if not Assigned(Parent) then
     Draw(FDelphiCanvas,GetRectangle) // Avoid "Control has no Parent" exception
  else
  {$ENDIF}
     Draw(FDelphiCanvas,GetClientRect);
end;

function TCustomTeePanel.BorderSize:Integer;
begin
  // Calculate amount of pixels for border and bevels...
  result:=BorderWidth;

  if BevelInner<>bvNone then
     Inc(result,BevelWidth);

  if BevelOuter<>bvNone then
     Inc(result,BevelWidth);

  if Border.Visible then
     Inc(result, {$IFDEF FMX}Round{$ENDIF}(Border.Width));
end;

Procedure TCustomTeePanel.ApplyMargins;

  Function GetMargin(Value,Range:Integer):TCoordinate;
  begin
    if MarginUnits=muPercent then
       result:=Value*Range {$IFDEF FMX}*0.01{$ELSE}div 100{$ENDIF}
    else
       result:=Value;
  end;

var tmpW : Integer;
    tmpH : Integer;
    tmpBorder : Integer;
begin
  RectSize(FChartBounds,tmpW,tmpH);

  tmpBorder:=BorderSize;

  // Apply margins
  with FChartBounds do
    ChartRect:=TeeRect( Left  + tmpBorder + GetMargin(MarginLeft,tmpW),
                        Top   + tmpBorder + GetMargin(MarginTop,tmpH),
                        Right - tmpBorder - GetMargin(MarginRight,tmpW),
                        Bottom- tmpBorder - GetMargin(MarginBottom,tmpH) );

  with Shadow do
  if Visible then
  begin
    if HorizSize>0 then
       ChartRect.Right:=ChartRect.Right - HorizSize
    else
       ChartRect.Left:=ChartRect.Left - HorizSize;

    if VertSize>0 then
       ChartRect.Bottom:=ChartRect.Bottom - VertSize
    else
       ChartRect.Top:=ChartRect.Top - VertSize;
  end;

  with Emboss do
  if Visible then
  begin
    if HorizSize>0 then
       ChartRect.Left:=ChartRect.Left + HorizSize
    else
       ChartRect.Right:=ChartRect.Right + HorizSize;

    if VertSize>0 then
       ChartRect.Top:=ChartRect.Top + VertSize
    else
       ChartRect.Bottom:=ChartRect.Bottom + VertSize;
  end;
end;

type
  TTeeCanvas3DAccess=class(TTeeCanvas3D);

Procedure TCustomTeePanel.Draw(UserCanvas:TCanvas; Const UserRect:TRect);

  procedure InnerDraw;
  begin
    if CustomChartRect and IsRectEmpty(ICachedChartRect) then
       ICachedChartRect:=ChartRect;

    // For charts created at runtime ("Loaded" is never called), so do this here:
    if InitialSize.X=0 then
       InitialSize.X:=Width;

    if InitialSize.Y=0 then
       InitialSize.Y:=Height;

    // Initialize Canvas
    FChartBounds:=InternalCanvas.InitWindow(UserCanvas,FView3DOptions, PanelColor(Color),
                                            FView3D, UserRect);

    ApplyMargins;

    if CustomChartRect then
    begin
       ChartRect:=ICachedChartRect;

       //TV52015163, TV52015390
       if FPrinting then
          OffsetRect(ChartRect,FChartBounds.Left,FChartBounds.Top);
    end;

    RecalcWidthHeight;
    InternalDraw(FChartBounds);

    // Global TeePanel filters (for buffered-bitmap Canvas only) :
    if Assigned(FGlobalPicture) then
    with FGlobalPicture do
    if (Filters.Count>0) and
       (InternalCanvas is TTeeCanvas3D) and Assigned(TTeeCanvas3D(InternalCanvas).Bitmap) then
          Filters.ApplyTo(TTeeCanvas3D(InternalCanvas).Bitmap);

    // Paint buffered bitmap, if it exists:
    if not DontShowImage then
       InternalCanvas.ShowImage(UserCanvas,FDelphiCanvas,UserRect);
  end;

begin
  {$IFDEF TEEEXCEPTIONS}
  {$IFNDEF FPC}
  try
  {$ENDIF}
  {$ENDIF}

    InnerDraw;

  {$IFDEF TEEEXCEPTIONS}
  {$IFNDEF FPC}
  except
   on E:Exception do
   begin
     if (csDesigning in ComponentState) and (not IExceptionDisplayed) then
     begin
       TTeeExceptionForm.Send('DRAW',E);
       IExceptionDisplayed:=True;
     end;

     Raise;
   end;
  end;
  {$ENDIF}
  {$ENDIF}
end;

procedure TCustomTeePanel.Paint;

  {$IFDEF MONITOR_REPAINTS}
  procedure PaintMonitor;
  begin
    Inc(IPaintCount);

    if Assigned(DelphiCanvas) then
    begin
      {$IFDEF FMX}
      DelphiCanvas.Fill.Color:=claBlack;
      DelphiCanvas.FillText(RectF(0,0,1000,1000),IntToStr(IPaintCount),False,1,[],
         TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF},
         TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF});
      {$ELSE}
      DelphiCanvas.TextOut(10,10,IntToStr(IPaintCount));
      {$ENDIF}
    end;
  end;
  {$ENDIF}

  {$IFDEF TEEOCX}
  procedure TeeFpuInit;
  asm
    FNINIT
    FWAIT
    FLDCW   Default8087CW
  end;
  {$ENDIF}

{$IFDEF FMX}
var Old : Boolean;
{$ENDIF}
var DifferentCanvas : Boolean;
    tmp : TCanvas;
    OldCanvas : TCanvas;
begin
  {$IFDEF TEEOCX}
  TeeFPUInit;
  {$ENDIF}

  {$IFDEF FMX}
  inherited;
  {$ENDIF}

  if not FPrinting then
  begin
    {$IFDEF FMX}
    if FInPaintTo and HasEffect then // Double-paint (Effects)
    begin
      FDelphiCanvas:=inherited Canvas;
      InternalCanvas.ReferenceCanvas:=FDelphiCanvas;

      Old:=InternalCanvas.UseBuffer;
      InternalCanvas.UseBuffer:=False;
      Draw;
      InternalCanvas.UseBuffer:=Old;

      FDelphiCanvas:=nil;
      InternalCanvas.ReferenceCanvas:=nil;

      Exit;
    end
    else
    {$ENDIF}
    begin
      tmp:=inherited Canvas; // <-- Necessary check (ie: SetActiveTabWithTransition)

      DifferentCanvas:=(not Assigned(FDelphiCanvas)) or (FDelphiCanvas<>tmp);

      if DifferentCanvas then
      begin
        FDelphiCanvas:=inherited Canvas;

        OldCanvas:=InternalCanvas.ReferenceCanvas;

        {$IFDEF FMX}
        // Safety check (XE9 Aitana)
        if OldCanvas<>nil then
           if OldCanvas.Fill=nil then
              OldCanvas:=nil;
        {$ENDIF}

        InternalCanvas.ReferenceCanvas:=FDelphiCanvas;
      end
      else
        OldCanvas:=nil;

      if not InternalCanvas.ReDrawBitmap then
         Draw;

      // This code below fixes the issue, for example, when coming from
      // a XXX.MakeScreenShot and then
      // moving the mouse over a chart, canvas is lost.
      if DifferentCanvas and Assigned(OldCanvas) then
         InternalCanvas.ReferenceCanvas:=OldCanvas;
    end;
  end;

  {$IFDEF MONITOR_REPAINTS}
  PaintMonitor;
  {$ENDIF}
end;

{$IFDEF TEE_METAFILE}
type
  TMetafileCanvas=class(TCanvas)
  public
    Constructor Create(Meta:TMetafile; Ref:Integer);
  end;

{ TMetafileCanvas }
Constructor TMetafileCanvas.Create(Meta: TMetafile; Ref: Integer);
begin
  inherited Create;
end;
{$ENDIF}

Function TCustomTeePanel.TeeCreateMetafile(const Enhanced:Boolean; Const Rect:TRect ):TMetafile;
{$IFNDEF FMX}
var tmpCanvas : TMetafileCanvas;
{$ENDIF}
begin
  {$IFDEF FMX}
  result:=TMetafile.Create(Round(Max(1,Rect.Right-Rect.Left)),
                           Round(Max(1,Rect.Bottom-Rect.Top)));
  result.Enhanced:=Enhanced;
  DrawToMetaCanvas(result.Canvas,Rect);
  {$ELSE}
  result:=TMetafile.Create;

  { bug in Delphi 3.02 : graphics.pas metafile reduces width/height.
    Fixed in Delphi 4.0x and BCB4.  }
  result.Width :=Max(1,Rect.Right-Rect.Left);
  result.Height:=Max(1,Rect.Bottom-Rect.Top);

  result.Enhanced:=Enhanced;

  tmpCanvas:=TMetafileCanvas.Create(result,0);
  try
    DrawToMetaCanvas(tmpCanvas,Rect);
  finally
    tmpCanvas.Free;
  end;
  {$ENDIF}
end;

Procedure TCustomTeePanel.SetBrushCanvas( AColor:TColor; const ABrush:TChartBrush;
                                          ABackColor:TColor);
begin
  if (ABrush.Style<>bsSolid) and (AColor=ABackColor) then
     if ABackColor={$IFDEF FMX}TAlphaColors.Black{$ELSE}clBlack{$ENDIF} then
        AColor:=clWhite
     else
        AColor:={$IFDEF FMX}TAlphaColors.Black{$ELSE}clBlack{$ENDIF};

  Canvas.AssignBrush(ABrush,AColor,ABackColor);
end;

function IsRemoteSession:Boolean;
begin
  {$IFDEF FMX}
  result:=False;
  {$ELSE}
  {$IFDEF MSWINDOWS}
  result:=GetSystemMetrics($1000)<>0;
  {$ELSE}
  result:=False;
  {$ENDIF}
  {$ENDIF}
end;

function TeeGetScreenSize:TPoint;
{$IFDEF FMX}
{$IFDEF D17}
var ScreenSvc : IFMXScreenService;
{$ENDIF}
{$ENDIF}
begin
  {$IFDEF FMX}
  {$IFDEF D17}
  if TPlatformServices.Current.SupportsPlatformService(IFMXScreenService, IInterface(ScreenSvc)) then
     result:=ScreenSvc.GetScreenSize
  else
     result:=TeeZeroPoint;
  {$ELSE}
  result:=Platform.GetScreenSize;
  {$ENDIF}
  {$ELSE}
  result.X:=Screen.Width;
  result.Y:=Screen.Height;
  {$ENDIF}
end;

procedure TeeChangeCursor(const AOwner:TObject; ACursor:TCursor);
{$IFDEF FMX}
{$IFDEF D17}
var CursorService: IFMXCursorService;
{$ENDIF}
{$ENDIF}
begin
  {$IFDEF FMX}
  {$IFDEF D17}
  if TPlatformServices.Current.SupportsPlatformService(IFMXCursorService, IInterface(CursorService)) then
     CursorService.SetCursor(ACursor);
  {$ELSE}
   Platform.SetCursor(TCommonCustomForm(AOwner), ACursor);
  {$ENDIF}
  {$ELSE}
  Screen.Cursor:=ACursor;
  {$ENDIF}
end;

function ScreenRatio(const ACanvas:TCanvas3D):Double;
{$IFDEF FMX}
var P : TPointF;
{$ELSE}
var tmpH : Integer;
{$ENDIF}
begin
  {$IFDEF FMX}
  P:=TeeGetScreenSize;
  if P.Y=0 then result:=1
           else result:=P.X/P.Y;
  {$ELSE}

  // if not IsRemoteSession then ...  // <-- check for Remote Desktop

  // Note:
  // It seems Windows 7 and up GetDeviceCaps is not returning accurate values that represent
  // physical monitor dimensions, thus the aspect ratio calculation can be
  // misleading.
  // Ref:
  // http://ofekshilon.com/2011/11/13/reading-monitor-physical-dimensions-or-getting-the-edid-the-right-way/

  tmpH:=TeeGetDeviceCaps(ACanvas.Handle,VERTSIZE);

  if tmpH<>0 then
     result:=1.0*TeeGetDeviceCaps(ACanvas.Handle,HORZSIZE)/tmpH
  else
     result:=0;
  {$ENDIF}
end;

Function TCustomTeePanel.IsScreenHighColor:Boolean;
begin
  {$IFDEF FMX}
  result:=True;
  {$ELSE}
  with InternalCanvas do
       result:=SupportsFullRotation or (TeeBitsperPixel(Handle)>=15);
  {$ENDIF}
end;

Function TCustomTeePanel.CanClip:Boolean;
begin
  result:= ((not Canvas.SupportsFullrotation) or (not View3D)) and
          (
            ((not Printing) and (not Canvas.Metafiling)) or
            (Printing and TeeClipWhenPrinting) or
            (Canvas.Metafiling and TeeClipWhenMetafiling)
          )
end;

Procedure TCustomTeePanel.SetStringProperty(var Variable:String; Const Value:String);
Begin
  if Variable<>Value then
  begin
    Variable:=Value; Invalidate;
  end;
end;

Procedure TCustomTeePanel.SetDoubleProperty(var Variable:Double; Const Value:Double);
begin
  if Variable<>Value then
  begin
    Variable:=Value; Invalidate;
  end;
end;

Procedure TCustomTeePanel.SetColorProperty(var Variable:TColor; Value:TColor);
Begin
  if Variable<>Value then
  begin
    Variable:=Value; Invalidate;
  end;
end;

Procedure TCustomTeePanel.SetBooleanProperty(var Variable:Boolean; Value:Boolean);
Begin
  if Variable<>Value then
  begin
    Variable:=Value; Invalidate;
  end;
end;

Procedure TCustomTeePanel.SetIntegerProperty(var Variable:Integer; Value:Integer);
Begin
  if Variable<>Value then
  begin
    Variable:=Value; Invalidate;
  end;
end;

Procedure TCustomTeePanel.SetSingleProperty(var Variable:Single; const Value:Single);
Begin
  if Variable<>Value then
  begin
    Variable:=Value; Invalidate;
  end;
end;

procedure TCustomTeePanel.TryAssignFont(const AFont:TTeeFont);
begin
  if AFont.DefaultFont then
     Canvas.AssignFont(DefaultFont)
  else
     Canvas.AssignFont(AFont);
end;

procedure TCustomTeePanel.ReadBorderStyle(Reader: TReader); // obsolete
begin
  Border.Visible:=Reader.ReadIdent='bsSingle';
end;

procedure TCustomTeePanel.ReadDefaultCanvas(Reader: TReader);
begin
  IDefaultCanvas:=Reader.ReadString;
  {$IFDEF FMX}
  IDefaultCanvas:=''; // Switching canvas is not supported yet in FireMonkey
  {$ENDIF}
end;

procedure TCustomTeePanel.WriteDefaultCanvas(Writer: TWriter);
begin
  Writer.WriteString(IDefaultCanvas);
end;

procedure TCustomTeePanel.SetCustomChartRect(Value:Boolean);
begin
  FCustomChartRect:=Value;
  ICachedChartRect:=TeeZeroRect;
end;

procedure TCustomTeePanel.SetDefaultFont(const AFont:TTeeFont);
begin
  if AFont=nil then
     FreeAndNil(FGlobalFont)
  else
     DefaultFont.Assign(AFont);
end;

procedure TCustomTeePanel.SetChartRect(const ARect:TRect);
begin
  CustomChartRect:=True;
  ChartRect:=ARect;
  Invalidate;
end;

Procedure TCustomTeePanel.DefineProperties(Filer:TFiler);

  function DoWriteDefaultCanvas:Boolean;
  begin
    result:=(Filer.Ancestor=nil) or
            (IDefaultCanvas<>TCustomTeePanel(Filer.Ancestor).IDefaultCanvas);
  end;

begin
  inherited;

  Filer.DefineProperty('BorderStyle',ReadBorderStyle,nil,False); // obsolete
  Filer.DefineProperty('DefaultCanvas',ReadDefaultCanvas,WriteDefaultCanvas,
        {$IFDEF FMX}False{$ELSE}DoWriteDefaultCanvas{$ENDIF});
end;

{$IFNDEF FMX}
type
  TControlAccess=class(TWinControl);
{$ENDIF}

function TCustomTeePanel.PanelColor(const AColor:TColor):TColor;
begin
  result:=AColor;

  {$IFNDEF FMX}

  {$IFDEF LCL}
  if (result=clDefault) or (result=clBtnFace) then
     result:=GetRGBColorResolvingParent;
  {$ELSE}
     result:=TTeeCanvas.SystemColor(result);
  {$ENDIF}

  {$ENDIF}
end;

Function TCustomTeePanel.GetBackColor:TColor;
begin
  result:=PanelColor(Color);
end;

Procedure TCustomTeePanel.Loaded;
begin
  inherited;

  if IDefaultCanvas<>'' then
     CreateDefaultCanvas;

  // Just in case (see TGDIPlusCanvas.IsAvailable) we cannot use this canvas
  // class, fall back to plain GDI canvas:
  if (not Assigned(Canvas)) or (not Canvas.IsAvailable) then
     Canvas:=TTeeCanvas3D.Create;

  FOriginalCursor:=Cursor; { save cursor }

  if BorderRound>0 then
     SetControlRounded;

  InitialSize.X:=Width;
  InitialSize.Y:=Height;
end;

{$IFNDEF FMX}
procedure TCustomTeePanel.ChangeScale(M, D: Integer{$IFDEF D24}; isDpiChange: Boolean{$ENDIF});
begin
  inherited;

  if M <> D then
  begin
    Canvas.FontZoom:=Canvas.FontZoom*M/D;
    FView3DOptions.FontZoom:=Round(Canvas.FontZoom);
  end;
end;
{$ENDIF}

procedure TCustomTeePanel.InternalDoMouseLeave;
begin
  Cursor:=FOriginalCursor;

  if FPanning.InsideBounds then
     FPanning.Active:=False;

  BroadcastTeeEventClass(TMouseLeaveEvent);
end;

{$IFDEF FMX}
procedure TCustomTeePanel.DoMouseLeave;
begin
  InternalDoMouseLeave;
  inherited;
end;
{$ELSE}
{$IFDEF USEWINDOWS}
procedure TCustomTeePanel.CMMouseLeave(var Message: TMessage);
begin
  InternalDoMouseLeave;
  inherited;
end;
{$ENDIF}
{$ENDIF}

{$IFNDEF FMX}
{$IFDEF USEWINDOWS}
procedure TCustomTeePanel.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
  inherited;
  Message.Result:=Message.Result or DLGC_WANTARROWS;
end;

procedure TCustomTeePanel.CMSysColorChange(var Message: TMessage);
begin
  inherited;
  Invalidate;
end;
{$ENDIF}

{$IFDEF MSWINDOWS}
procedure TCustomTeePanel.WMEraseBkgnd(var Message: TWmEraseBkgnd);
begin
  if TeeEraseBack then
     inherited
  else
//  Not done here. See CreateParams override.
//  if Color=clNone then
//     SetBkMode(Message.DC, TRANSPARENT);

     Message.Result:=1;
end;
{$ENDIF}
{$ENDIF}

procedure TCustomTeePanel.ReadState(Reader: TReader);
begin
  AutoRepaint:=False;
  try
    inherited;
  finally
    AutoRepaint:=True;
    Invalidate;
  end;
end;

{$IFNDEF FMX}
{$IFDEF MSWINDOWS}
procedure TCustomTeePanel.WndProc(var Msg: TMessage);
begin
  // Speed opt:
  if Msg.Msg = WM_UPDATEUISTATE then
     Msg.Msg:= WM_NULL;

  inherited;
end;
{$ENDIF}
{$ENDIF}

{$IFDEF FMX}
// Called from TView3DOptions.Repaint at FMXTee.Canvas.pas unit
procedure TCustomTeePanel.InvalidateEvent(Sender:TObject);
begin
  Invalidate;
end;
{$ENDIF}

procedure TCustomTeePanel.Invalidate;
begin
  if AutoRepaint then
  begin
    if Assigned(InternalCanvas) then
       InternalCanvas.Invalidate;

    inherited;
  end;
end;

procedure TCustomTeePanel.AssignTo(Dest: TPersistent);
var tmp : TBitmap;
begin
  if (Dest is TGraphic) or (Dest is TPicture) then
  begin
    tmp:=TeeCreateBitmap;
    try
      Dest.Assign(tmp);
    finally
      tmp.Free;
    end;
  end
  else inherited;
end;

procedure TCustomTeePanel.AddListener(Sender:ITeeEventListener);
begin
  if Assigned(IEventListeners) and (IEventListeners.IndexOf({$IFNDEF D18}Pointer{$ENDIF}(Sender))=-1) then
     IEventListeners.Add(Sender);
end;

procedure TCustomTeePanel.RemoveListener(Sender:ITeeEventListener);
begin
  if Assigned(IEventListeners) then IEventListeners.Remove(Sender);
end;

procedure TCustomTeePanel.Resize;
begin
  if not (csLoading in ComponentState) then
  begin
    if BorderRound>0 then
       SetControlRounded;

    {$IFDEF FMX}
    Invalidate; // <-- for BufferedDisplay canvas
    {$ENDIF}

    {$IFDEF LCL}
    Invalidate; // <-- 1.0.10 Needs this
    {$ENDIF}

    if Assigned(View3DOptions) and (View3DOptions.ZoomText=ztAuto) then
    begin
      if (InitialSize.X>0) and (InitialSize.Y>0) then
        View3DOptions.FontZoom:=100+Max( Round(100.0*(Width-InitialSize.X)/InitialSize.X),
                                         Round(100.0*(Height-InitialSize.Y)/InitialSize.Y));
    end;
  end;

  inherited;
end;

Procedure TCustomTeePanel.BroadcastTeeEventClass(const Event:TTeeEventClass);
begin
  if Listeners.Count>0 then
     BroadcastTeeEvent(Event.Create){$IFNDEF AUTOREFCOUNT}.Free{$ENDIF};
end;

Function TCustomTeePanel.BroadcastTeeEvent(const Event:TTeeEvent):TTeeEvent;

   function DoBroadCast(AIndex:Integer):Boolean;
   var tmp : ITeeEventListener;
   begin
     tmp:=Listeners[AIndex];
     tmp.TeeEvent(Event);
     result:=(Event is TTeeMouseEvent) and CancelMouse;
    end;

var t : Integer;
begin
  result:=Event;

  if not (csDestroying in ComponentState) then
  begin
    Event.Sender:=Self;

    t:=0;
    while t<Listeners.Count do
          if DoBroadCast(t) then
             break
          else
             Inc(t);
  end;
end;

procedure TCustomTeePanel.BroadcastMouseEvent(Kind:TTeeMouseEventKind;
                                           Button: TMouseButton;
                                           Shift: TShiftState;
                                           X, Y: TCoordinate);
var tmp : TTeeMouseEvent;
begin
  if Listeners.Count>0 then
  begin
    tmp:=TTeeMouseEvent.Create;
    try
      tmp.Event:=Kind;
      tmp.Button:=Button;
      tmp.Shift:=Shift;
      tmp.X:=X;
      tmp.Y:=Y;

      BroadcastTeeEvent(tmp);
    finally
      tmp.Free;
    end;
  end;
end;

procedure TCustomTeePanel.MouseDown(Button: TMouseButton; Shift: TShiftState;
                         X, Y: TCoordinate);
begin
  CancelMouse:=False;

  {$IFDEF FMX}
  {$IFNDEF D20}
  if Scene<>nil then // Avoid AV in FMX.Controls.TControl.MouseDown
  {$ENDIF}
  {$ENDIF}
     inherited;

  if not CancelMouse then
     BroadcastMouseEvent(meDown,Button,Shift,X,Y);

  if {$IFDEF FMX}CanFocus and (not IsFocused) and (Root<>nil)
     {$ELSE}CanFocus and TabStop and (not Focused) and HasParent and (not Parent.Focused){$ENDIF} then
     SetFocus;
end;

procedure TCustomTeePanel.MouseUp(Button: TMouseButton; Shift: TShiftState;
                         X, Y: TCoordinate);
begin
  CancelMouse:=False; // <-- breaks TTree editor

  inherited;

  BroadcastMouseEvent(meUp,Button,Shift,X,Y);
end;

procedure TCustomTeePanel.MouseMove(Shift: TShiftState;
                          X, Y: TCoordinate);
{$IFDEF FMX}
var tmpCanvas : TCanvas;
    DifferentCanvas : Boolean;
{$ENDIF}
begin
  CancelMouse:=False;

  inherited;

  {$IFDEF FMX}
  // Protection against FMX Canvas destruction (RS-61098)

  tmpCanvas:=inherited Canvas; // <-- Necessary check (ie: SetActiveTabWithTransition)

  DifferentCanvas:=(not Assigned(FDelphiCanvas)) or (Canvas.ReferenceCanvas<>tmpCanvas);

  if DifferentCanvas then
  begin
    FDelphiCanvas:=tmpCanvas;
    Canvas.ReferenceCanvas:=FDelphiCanvas;
  end;
  {$ENDIF}

  if (Listeners.Count>0) and (not (csDesigning in ComponentState)) then
     BroadcastMouseEvent(meMove,{$IFDEF FMX}TMouseButton.{$ENDIF}mbLeft,Shift,X,Y);
end;

procedure TCustomTeePanel.DblClick;
begin
  CancelMouse:=False;

  inherited;

  if CancelMouse then
     Abort;
end;

type
  TRectArray=array[0..3] of TCoordinate;

Function TCustomTeePanel.GetMargin(Index:Integer):Integer;
begin
  result:={$IFDEF FMX}Round{$ENDIF}(TRectArray(FMargins)[Index]);
end;

Procedure TCustomTeePanel.SetMargin(Index,Value:Integer);
begin
  {$IFDEF FMX}SetSingleProperty{$ELSE}SetIntegerProperty{$ENDIF}(TRectArray(FMargins)[Index],Value);
end;

Function TCustomTeePanel.GetBufferedDisplay:Boolean;
begin
  result:=InternalCanvas.UseBuffer;
end;

function TCustomTeePanel.GetDefaultFont:TTeeFont;
begin
  if Theme=nil then
  begin
    if FGlobalFont=nil then
       FGlobalFont:=TTeeFont.Create(CanvasChanged);

    result:=FGlobalFont;
  end
  else
    result:=Theme.DefaultFont;
end;

{$IFDEF TEEBEVELS}
procedure TCustomTeePanel.SetBevelInner(const Value: TPanelBevel);
begin
  if FBevelInner<>Value then
  begin
    FBevelInner:=Value;
    Invalidate;
  end;
end;

procedure TCustomTeePanel.SetBevelOuter(const Value: TPanelBevel);
begin
  if FBevelOuter<>Value then
  begin
    FBevelOuter:=Value;
    Invalidate;
  end;
end;

procedure TCustomTeePanel.SetBevelWidth(const Value: TBevelWidth);
begin
  if FBevelWidth<>Value then
  begin
    FBevelWidth:=Value;
    Invalidate;
  end;
end;
{$ENDIF}

{$IFDEF FMX}
procedure TCustomTeePanel.SetBorderWidth(const Value: Integer);
begin
  if FBorderWidth<>Value then
  begin
    FBorderWidth:=Value;
    Invalidate;
  end;
end;
{$ENDIF}

Procedure TCustomTeePanel.SetBorder(const Value:TTeeHiddenPen);
begin
  Border.Assign(Value);
end;

procedure TCustomTeePanel.SetControlRounded;
{$IFDEF FMX}
begin
  Invalidate;
  UpdateEffects;
end;
{$ELSE}
{$IFDEF USEWINDOWS}
var Region : HRGN;
begin
  if not IRounding then  // re-entrance protection (from Resize method)
  if Assigned(Parent) then // <-- needs Parent to obtain Handle below
  begin
    IRounding:=True;
    try
      if BorderRound>0 then
         Region:=CreateRoundRectRgn(0,0,Width,Height,BorderRound,BorderRound)
      else
         Region:=0;

      SetWindowRgn(Handle,Region,True);
    finally
      IRounding:=False;
    end;
  end;
end;
{$ELSE}
begin
  // Linux
end;
{$ENDIF}
{$ENDIF}

Procedure TCustomTeePanel.SetBorderRound(Value:Integer);
begin
  if FBorderRound<>Value then
  begin
    FBorderRound:=Value;
    if not (csLoading in ComponentState) then
       SetControlRounded;
  end;
end;

Function TCustomTeePanel.GetBorderStyle:TBorderStyle;
begin
  if FBorder.Visible then result:=bsSingle
                     else result:=bsNone; 
end;

// SetBorderStyle conflicts with LCL. Renamed to SetTheBorderStyle
Procedure TCustomTeePanel.SetTheBorderStyle(Value:TBorderStyle);
begin
  FBorder.Visible:=Value=bsSingle;
end;

Procedure TCustomTeePanel.SetBufferedDisplay(Value:Boolean);
begin
  InternalCanvas.UseBuffer:=Value;
end;

Procedure TCustomTeePanel.SetInternalCanvas(const NewCanvas:TCanvas3D);
var Old : Boolean;
begin
  if Assigned(NewCanvas) then
  begin
    NewCanvas.ReferenceCanvas:=FDelphiCanvas;

    if Assigned(InternalCanvas) then
    begin
      Old:=AutoRepaint;
      AutoRepaint:=False;
      NewCanvas.Assign(InternalCanvas);
      AutoRepaint:=Old;
      InternalCanvas.Free;
    end;

    InternalCanvas:=NewCanvas;
    IDefaultCanvas:=NewCanvas.ClassName;

    if AutoRepaint then
       Invalidate;
  end;
end;

procedure TCustomTeePanel.RecalcWidthHeight;
var P : TPoint;
Begin
  With ChartRect do
  begin
    if Left<FChartBounds.Left then Left:=FChartBounds.Left;
    if Top<FChartBounds.Top then Top:=FChartBounds.Top;
    if Right<Left then Right:=Left+1 else
    if Right=Left then Right:=Left+Width;
    if Bottom<Top then Bottom:=Top+1 else
    if Bottom=Top then Bottom:=Top+Height;
  end;

  RectSize(ChartRect, FChartWidth, FChartHeight);

  P:=RectCenter(ChartRect);
  FChartXCenter:={$IFDEF FMX}Round{$ENDIF}(P.X);
  FChartYCenter:={$IFDEF FMX}Round{$ENDIF}(P.Y);
end;

Function TCustomTeePanel.GetCursorPos:TPoint;
{$IFDEF FMX}
{$IFDEF D17}
var MouseService : IFMXMouseService;
{$ENDIF}
{$ENDIF}
Begin
  {$IFDEF FMX}
  {$IFDEF D17}
  if TPlatformServices.Current.SupportsPlatformService(IFMXMouseService, IInterface(MouseService)) then
     result:=ScreenToLocal(MouseService.GetMousePos)
  else
     result:=TeeZeroPoint;
  {$ELSE}
  result:=Platform.GetMousePos;
  {$ENDIF}
  {$ELSE}
  // Mouse.CursorPos problem in D7 when switching Windows themes (XP).
  result:=ScreenToClient(Mouse.CursorPos);
  {$ENDIF}
end;

Function TCustomTeePanel.GetRectangle:TRect;
begin
  if Assigned(Parent) then result:=GetClientRect
                      else result:=TeeRect(Width,Height);
end;

Procedure TCustomTeePanel.DrawToMetaCanvas(const ACanvas:TCanvas; Const Rect:TRect);
begin  { assume the acanvas is in MM_ANISOTROPIC mode }
  InternalCanvas.Metafiling:=True;
  try
    NonBufferDraw(ACanvas,Rect);
  finally
    InternalCanvas.Metafiling:=False;
  end;
end;

Function TCustomTeePanel.GetMonochrome:Boolean;
Begin
  result:=InternalCanvas.Monochrome;
end;

Procedure TCustomTeePanel.SetMarginUnits(const Value:TTeeUnits);
begin
  if FMarginUnits<>Value then
  begin
    FMarginUnits:=Value;
    Invalidate;
  end;
end;

Procedure TCustomTeePanel.SetMonochrome(Value:Boolean);
Begin
  InternalCanvas.Monochrome:=Value;
end;

procedure TCustomTeePanel.SetPanning(const Value:TTeePanning);
begin
  FPanning.Assign(Value);
end;

Procedure TCustomTeePanel.Assign(Source:TPersistent);
begin
  if Source is TCustomTeePanel then
  With TCustomTeePanel(Source) do
  begin
    Self.BevelInner         := BevelInner;
    Self.BevelOuter         := BevelOuter;
    Self.BevelWidth         := BevelWidth;
    Self.Border             := Border;
    Self.BorderRound        := BorderRound;
    Self.BufferedDisplay    := BufferedDisplay;
    Self.Color              := Color;
    Self.Emboss             := Emboss;
    Self.FMargins           := FMargins;
    Self.FMarginUnits       := FMarginUnits;
    Self.Monochrome         := Monochrome;
    Self.Panning            := Panning;
    Self.PrintMargins       := PrintMargins;
    Self.FPrintProportional := FPrintProportional;
    Self.FPrintResolution   := FPrintResolution;
    Self.Shadow             := Shadow;
    Self.FView3D            := FView3D;
    Self.View3DOptions      := FView3DOptions;
  end
  else inherited;
end;

Procedure TCustomTeePanel.SaveToMetafile(Const FileName:String);
begin
  SaveToMetaFileRect(False,FileName,GetRectangle);
end;

Procedure TCustomTeePanel.SaveToMetafileEnh(Const FileName:String);
begin
  SaveToMetaFileRect(True,FileName,GetRectangle);
end;

{ Enhanced:Boolean }
Procedure TCustomTeePanel.SaveToMetafileRect( const Enhanced:Boolean;
                                              const FileName:String;
                                              const Rect:TRect);
var tmpStream : TFileStream;
Begin
  With TeeCreateMetafile(Enhanced,Rect) do
  try
    tmpStream:=TFileStream.Create(FileName,fmCreate);
    try
      SaveToStream(tmpStream);
    finally
      tmpStream.Free;
    end;
  finally
    Free;
  end;
End;

type
  TCanvas3DAccess=class(TCanvas3D);

Procedure TCustomTeePanel.NonBufferDraw(const ACanvas:TCanvas; Const R:TRect);
var Old     : Boolean;
    OldAuto : Boolean;
    tmpCanvas : TCanvas;
    Old3DOptions : Boolean;
begin
  tmpCanvas:=InternalCanvas.ReferenceCanvas;

  // Remember to set F3DOptions to nil, this will fix repeated calls here
  // from non-Windows apps like Intraweb
  Old3DOptions:=(InternalCanvas is TCanvas3D) and
     (not Assigned(TCanvas3DAccess(InternalCanvas).F3DOptions));

  Old:=BufferedDisplay;
  try
    if InternalCanvas is TTeeCanvas3D then
       TTeeCanvas3DAccess(InternalCanvas).IKeepBitmap:=True;

    BufferedDisplay:=False;

    OldAuto:=AutoRepaint;

    AutoRepaint:=False;
    try
      Draw(ACanvas,R);
    finally
      AutoRepaint:=OldAuto;
    end;

  finally
    // Set F3DOptions to nil *before* setting BufferedDisplay, to avoid
    // "Control has no parent Window" exception in Intraweb apps
    if Old3DOptions then
       TCanvas3DAccess(InternalCanvas).F3DOptions:=nil;

    BufferedDisplay:=Old;

    InternalCanvas.ReferenceCanvas:=tmpCanvas;

    if InternalCanvas is TTeeCanvas3D then
    begin
      TTeeCanvas3DAccess(InternalCanvas).IKeepBitmap:=False;

      if not Old then
         Invalidate;
    end;
  end;
end;

Function TCustomTeePanel.TeeCreateBitmap:TBitmap;
begin
  result:=TeeCreateBitmap(Color,GetRectangle);
end;

Function TCustomTeePanel.TeeCreateBitmap(ABackColor:TColor; Const Rect:TRect;
                                         {$IFNDEF FMX}
                                         APixelFormat:Graphics.TPixelFormat=pfDevice;
                                         {$ENDIF}
                                         ADpi:Integer=0):TBitmap;
begin
  {$IFDEF FMX}
  // MakeSnapShot alternative
  
  result:=TBitmap.Create(Round(Rect.Right-Rect.Left),
                         Round(Rect.Bottom-Rect.Top));

  if not Canvas.RenderToBitmap(result,Rect) then
  begin
    if result.Canvas.BeginScene then
    try
      if ABackColor<>TAlphaColors.White then
      begin
        if ABackColor=TAlphaColors.Null then
           ABackColor:=Color;

        result.Canvas.Fill.Color:=ABackColor;
        result.Canvas.FillRect(Rect,0,0,AllCorners,1);
      end;

      NonBufferDraw(result.Canvas,Rect);
    finally
      result.Canvas.EndScene;
    end;
  end;
  {$ELSE}

  result:=TBitmap.Create;

  if (Canvas is TCanvas3D) and (ABackColor=Color) and
     TCanvas3D(Canvas).RenderToBitmap(result,Rect) then
       result.PixelFormat:=APixelFormat
  else
  with result do
  begin
    // OpenGL issue. Nonbuffer (Bitmap canvas) cannot be "pfDevice"
    if Self.Canvas.SupportsFullRotation and (APixelFormat=pfDevice) then
       APixelFormat:=TeePixelFormat;

    {$IFDEF IGNOREPALETTE}
    IgnorePalette:=APixelFormat=TeePixelFormat;
    {$ENDIF}

    PixelFormat:=APixelFormat;

    TeeSetBitmapSize(result,Rect.Right-Rect.Left,Rect.Bottom-Rect.Top);

    if ABackColor<>clWhite then
    begin
      if ABackColor=clDefault then
         ABackColor:=Color;

      Canvas.Brush.Color:=ABackColor;
      Canvas.FillRect(Rect);
    end;

    NonBufferDraw(Canvas,Rect);
  end;

  if (ABackColor=clNone) or (Cardinal(ABackColor)=$FF000000) then
  begin
    result.Transparent:=True;

    // This is to avoid using automatic pixel 0,0.
    // The problem is, if a Chart has BevelOuter<>bvNone, pixel 0,0 will not be
    // the same as ABackColor
    result.TransparentMode:=tmFixed;
    result.TransparentColor:=ABackColor;
  end;
  {$ENDIF}

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

Procedure TCustomTeePanel.SaveToBitmapFile(Const FileName:String);
Begin
  SaveToBitmapFile(FileName,GetRectangle);
End;

Procedure TCustomTeePanel.SaveToBitmapFile(Const FileName:String; Const R:TRect);
begin
  with TeeCreateBitmap(clWhite,R) do
  try
    SaveToFile(FileName);
  finally
    Free;
  end;
end;

Procedure TCustomTeePanel.PrintPortrait;
Begin
  PrintOrientation({$IFDEF FMX}TPrinterOrientation.{$ENDIF}poPortrait);
end;

Procedure TCustomTeePanel.PrintLandscape;
Begin
  PrintOrientation({$IFDEF FMX}TPrinterOrientation.{$ENDIF}poLandscape);
end;

Procedure TCustomTeePanel.PrintOrientation(const AOrientation:TPrinterOrientation);
{$IFNDEF ANDROID}
var OldOrientation : TPrinterOrientation;
{$ENDIF}
Begin
  {$IFNDEF ANDROID}
  OldOrientation:=Printer.Orientation;
  Printer.Orientation:=AOrientation;
  try
    Print;
  finally
    Printer.Orientation:=OldOrientation;
  end;
  {$ENDIF}
end;

Procedure TCustomTeePanel.CopyToClipboardBitmap(Const R:TRect);
{$IFDEF FMX}
{$IFDEF D23} // XE9 now supports TBitmap
var ClipService : IFMXClipboardService;
{$ENDIF}
{$ENDIF}
var tmpBitmap : TBitmap;
begin
  // Create a new bitmap
  tmpBitmap:=TeeCreateBitmap(Color,R);
  try
    {$IFDEF FMX}
     {$IFDEF D23} // XE9 now supports TBitmap
     if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, IInterface(ClipService)) then
        ClipService.SetClipboard(tmpBitmap);
     {$ENDIF}
    {$ELSE}
    ClipBoard.Assign(tmpBitmap);
    {$ENDIF}
  finally
    tmpBitmap.Free;
  end;
end;

Procedure TCustomTeePanel.CopyToClipboardBitmap;
begin
  CopyToClipboardBitmap(GetRectangle);
end;

Procedure TCustomTeePanel.CopyToClipboardMetafile( Enhanced:Boolean;
                                                   Const R:TRect);
var tmpMeta : TMetaFile;
begin
  tmpMeta:=TeeCreateMetafile(Enhanced,R);
  try
    {$IFDEF FMX}
    //Platform.SetClipboard(tmpMeta);
    {$ELSE}
    ClipBoard.Assign(tmpMeta);
    {$ENDIF}
  finally
    tmpMeta.Free;
  end;
end;

Procedure TCustomTeePanel.CopyToClipboardMetafile(Enhanced:Boolean);
begin
  CopyToClipboardMetafile(Enhanced,GetRectangle);
end;

Procedure TCustomTeePanel.CalcMetaBounds( var R:TRect;
                                          Const AChartRect:TRect;
                                          out WinWidth,WinHeight,
                                          ViewWidth,ViewHeight:Integer);
var tmpRectWidth  : Integer;
    tmpRectHeight : Integer;
begin  { apply PrintResolution to the desired rectangle coordinates }
  RectSize(R,ViewWidth,ViewHeight);
  RectSize(AChartRect,tmpRectWidth,tmpRectHeight);
  WinWidth :=tmpRectWidth -Round(tmpRectWidth * PrintResolution * 0.01);
  WinHeight:=tmpRectHeight-Round(tmpRectHeight * PrintResolution * 0.01);

  {$IFNDEF FMX}
  With R do
  begin
    Left  :={$IFNDEF FMX}Round{$ENDIF}(Left  *WinWidth/ViewWidth);
    Right :={$IFNDEF FMX}Round{$ENDIF}(Right *WinWidth/ViewWidth);
    Top   :={$IFNDEF FMX}Round{$ENDIF}(Top   *WinHeight/ViewHeight);
    Bottom:={$IFNDEF FMX}Round{$ENDIF}(Bottom*WinHeight/ViewHeight);
  end;
  {$ENDIF}
end;

Procedure TCustomTeePanel.PrintPartialCanvas( const PrintCanvas:TCanvas;
                                              Const PrinterRect:TRect);
var ViewWidth   : Integer;
    ViewHeight  : Integer;
    WinWidth    : Integer;
    WinHeight   : Integer;
    tmpR        : TRect;

    {$IFNDEF FMX}
    OldMapMode  : Integer;
    {$ENDIF}

  Procedure SetAnisotropic; { change canvas/windows metafile mode }
  {$IFDEF FMX}
  begin
  end;
  {$ELSE}
  {$IFDEF USEWINDOWS}
  var DC : HDC;
  begin
    DC:=PrintCanvas.Handle;
    OldMapMode:=GetMapMode(DC);

    SetMapMode(DC, MM_ANISOTROPIC);

    SetWindowOrgEx(DC, 0, 0, nil);
    SetWindowExtEx(DC, WinWidth, WinHeight, nil);

    SetViewportExtEx(DC, ViewWidth,ViewHeight, nil);
    SetViewportOrgEx(DC, 0, 0, nil);
  end;
  {$ELSE}
  begin
  end;
  {$ENDIF}
  {$ENDIF}

begin
  { check if margins inverted }
  tmpR:=OrientRectangle(PrinterRect);

  ICustomPrinterRect := PrinterRect;

  { apply PrintResolution to dimensions }
  CalcMetaBounds(tmpR,GetRectangle,WinWidth,WinHeight,ViewWidth,ViewHeight);

  // FMX TODO: Change Scale.X and Y

  SetAnisotropic;

  FPrinting:=True;
  IsPrinterCanvas:=True;
  try
    if CanClip then ClipCanvas(PrintCanvas,tmpR);

    if Assigned(FOnBeforePrint) then
       FOnBeforePrint(Self,PrintCanvas,tmpR);

    DrawToMetaCanvas(PrintCanvas,tmpR);
    UnClipCanvas(PrintCanvas);
  finally
    IsPrinterCanvas:=False;
    FPrinting:=False;
  end;

  {$IFNDEF FMX}
  {$IFDEF MSWINDOWS}
  SetMapMode(PrintCanvas.Handle,OldMapMode);
  {$ENDIF}
  {$ENDIF}
end;

Procedure TCustomTeePanel.PrintPartial(Const PrinterRect:TRect);
Begin
  {$IFNDEF ANDROID}
  PrintPartialCanvas(Printer.Canvas,PrinterRect);
  {$ENDIF}
End;

Procedure TCustomTeePanel.PrintRect(Const R:TRect);
{$IFNDEF ANDROID}
var Old : Boolean;
{$ENDIF}
Begin
  {$IFNDEF ANDROID}
  Old:=AutoRepaint;
  AutoRepaint:=False;

  try
    if Name<>'' then
       Printer.Title:=Name;

    Printer.BeginDoc;
    try
      PrintPartial(R);

      Printer.EndDoc;
    except
      on Exception do
      begin
        Printer.Abort;

        if Printer.Printing then
           Printer.EndDoc;

        Raise;
      end;
    end;

  finally
    AutoRepaint:=Old;
    Invalidate;
  end;
  {$ENDIF}
end;

Function TCustomTeePanel.InternalCalcProportionalMargins(const AWidth,AHeight,
                       APageWidth,APageHeight:Integer; const APageRatio:Double):TRect;

  Function CalcMargin(Size1,Size2:Integer; Const ARatio:Double):Integer;
  var tmpDefaultMargin : Single;
  begin
    if Size2=0 then
       result:=0
    else
    begin
      tmpDefaultMargin:=(2.0*TeeDefault_PrintMargin)*Size1*0.01;

      result:=Round(100.0*(Size2-((Size1-tmpDefaultMargin)*ARatio*APageRatio))/Size2) div 2;
    end;
  end;

var tmp : Integer;
begin
  if AWidth > AHeight then
  begin
    tmp:=CalcMargin(APageWidth,APageHeight,AHeight/AWidth);
    Result:=TeeRect(TeeDefault_PrintMargin,tmp,TeeDefault_PrintMargin,tmp);
  end
  else
  begin
    tmp:=CalcMargin(APageHeight,APageWidth,AWidth/AHeight);
    Result:=TeeRect(tmp,TeeDefault_PrintMargin,tmp,TeeDefault_PrintMargin);
  end;
end;

Function TCustomTeePanel.CalcProportionalMargins:TRect;
var
  tmpPrinterW : Integer;
  tmpPrinterH : Integer;
  tmpPrinterOk : Boolean;

  procedure UseScreenSize;
  var P : TPoint;
  begin
    tmpPrinterOk:=False;

    P:=TeeGetScreenSize;

    tmpPrinterW:={$IFDEF FMX}Round{$ENDIF}(P.X);
    tmpPrinterH:={$IFDEF FMX}Round{$ENDIF}(P.Y);
  end;

  function CalcRatio:Single;
  begin
    if tmpPrinterOk then
    begin
      {$IFDEF FMX}

      {$IFDEF ANDROID}
      result:=1
      {$ELSE}
      result:=Printer.PageWidth/Printer.PageHeight
      {$ENDIF}

      {$ELSE}

      {$IFDEF LCL}
      with Printer.PaperSize.PaperRect.WorkRect do
           result:= (Right-Left)/(Bottom-Top)
      {$ELSE}
      result:= TeeGetDeviceCaps(Printer.Handle,LOGPIXELSX)/
               TeeGetDeviceCaps(Printer.Handle,LOGPIXELSY)
      {$ENDIF}

      {$ENDIF}
    end
    else
      result:=1;
  end;

var tmpWidth    : Integer;
    tmpHeight   : Integer;
begin
  RectSize(GetRectangle, tmpWidth, tmpHeight);

  tmpPrinterOk:=True;

  {$IFDEF ANDROID}
  tmpPrinterW:=tmpWidth; // ??
  tmpPrinterH:=tmpHeight; // ??
  {$ELSE}

  try
    tmpPrinterW:=Printer.PageWidth;
    tmpPrinterH:=Printer.PageHeight;
  except
    on EPrinter do
       UseScreenSize;
  end;
  {$ENDIF}

  if (tmpPrinterW<=0) or (tmpPrinterH<=0) then
     UseScreenSize;

  result:=InternalCalcProportionalMargins(tmpWidth,tmpHeight,tmpPrinterW,tmpPrinterH,CalcRatio);
end;

Function TCustomTeePanel.PrintMarginsRect(const AWidth,AHeight:Integer):TRect;
{$IFNDEF ANDROID}
var tmp : Single;
{$ENDIF}
begin
  {$IFDEF ANDROID}
  result:=PrintMargins; // ?
  {$ELSE}
  with result do
  Begin
    tmp:=AWidth*0.01;
    Left  :={$IFNDEF FMX}Round{$ENDIF}(PrintMargins.Left*tmp);
    Right :=AWidth-{$IFNDEF FMX}Round{$ENDIF}(PrintMargins.Right*tmp);

    tmp:=AHeight*0.01;
    Top   :={$IFNDEF FMX}Round{$ENDIF}(PrintMargins.Top*tmp);
    Bottom:=AHeight-{$IFNDEF FMX}Round{$ENDIF}(PrintMargins.Bottom*tmp);
  end;
  {$ENDIF}
end;

Function TCustomTeePanel.ChartPrintRect:TRect;
Begin
  if FPrintProportional then
     PrintMargins:=CalcProportionalMargins;

  {$IFDEF ANDROID}
  result:=PrintMargins; // ?
  {$ELSE}
  result:=PrintMarginsRect(Printer.PageWidth,Printer.PageHeight);
  {$ENDIF}
end;

Procedure TCustomTeePanel.Print;
Begin
  PrintRect(ChartPrintRect);
end;

Procedure DrawBevel(const Canvas:TTeeCanvas; Bevel:TPanelBevel; var R:TRect;
                    Width:Integer; Round:Integer=0);
Const Colors:Array[Boolean] of TColor=
   (clWhite,{$IFDEF FMX}$FFA0A0A0{$ELSE}$A0A0A0 {clBtnHighlight,clBtnShadow} {$ENDIF});
begin
  if Bevel<>bvNone then
  begin
    {$IFDEF FMX}
    Width:=System.Round(Min(Width, Min((R.Bottom-R.Top) *0.5,
                          (R.Right-R.Left) *0.5)));
    {$ELSE}
    Width:=Min(Width, Min((R.Bottom-R.Top) div 2,
                          (R.Right-R.Left) div 2));
    {$ENDIF}

    if Round>0 then
    begin
      Canvas.Pen.Color:=Colors[Bevel=bvLowered];
      InflateRect(R,-1,-1);
      Canvas.RoundRect(R,Round,Round);
      InflateRect(R,-Width,-Width);
    end
    else
      Canvas.Frame3D(R,Colors[Bevel=bvLowered],Colors[Bevel=bvRaised],Width);
  end;
end;

Procedure TCustomTeePanel.DrawPanelBevels(Rect:TRect);
var tmp : Integer;
    tmpPaint : Boolean;
begin
  tmpPaint:=( (not Printing) or PrintTeePanel ) and
            (
              (BevelOuter<>bvNone) or (BevelInner<>bvNone) or (BorderWidth>0)
            );

  if tmpPaint then
  begin
    with Canvas.Pen do
    begin
      {$IFDEF FMX}
      Visible:=True;

      if Assigned(Canvas.ReferenceCanvas) then
         Canvas.ReferenceCanvas.Stroke.Kind:=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};
      {$ENDIF}

      SmallDots:=False;
      Style:=psSolid;
      Width:=1;
      Mode:=pmCopy;
    end;

    {$IFNDEF FMX}
    Canvas.AssignVisiblePen(Canvas.Pen); // Force reset
    {$ENDIF}

    Canvas.FrontPlaneBegin;

    if Border.Visible then
    begin
      tmp:=FixBorderWidth(Rect);

      if not Border.SmallDots then Inc(tmp);

      InflateRect(Rect,-tmp,-tmp);
    end;

    DrawBevel(Canvas,BevelOuter,Rect,BevelWidth,BorderRound);

    if BorderWidth>0 then
       Canvas.Frame3D(Rect, Color, Color, BorderWidth);

    DrawBevel(Canvas,BevelInner,Rect,BevelWidth,BorderRound);

    Canvas.FrontPlaneEnd;
  end;
end;

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

{$IFNDEF D18}

{ TZoomSnapshots }

procedure TZoomSnapshots.Put(Index:Integer; Value:TZoomSnapShot);
begin
  inherited Items[Index]:=Value;
end;

function TZoomSnapshots.Get(Index:Integer):TZoomSnapShot;
begin
  result:=TZoomSnapShot(inherited Items[Index]);
end;

Procedure TZoomSnapshots.Clear;
var t : Integer;
begin
  for t:=0 to Count-1 do
      Items[t].Free;

  inherited;
end;
{$ENDIF}

{ TTeeZoom }
Constructor TTeeZoom.Create;
begin
  inherited;

  FAnimatedSteps:=8;
  FAllow:=True;
  FDirection:=tzdBoth;
  FMinimumPixels:=16;
  FMouseButton:={$IFDEF FMX}TMouseButton.{$ENDIF}mbLeft;

  FMouseWheel:=pmwNone;
  
  {$IFDEF FMX}
  FFullRepaint:=True;
  {$ENDIF}
end;

Destructor TTeeZoom.Destroy;
Begin
  FAxesHistory.Free;
  FPen.Free;
  FBrush.Free;
  inherited;
end;

Procedure TTeeZoom.Assign(Source:TPersistent);
begin
  if Source is TTeeZoom then
  With TTeeZoom(Source) do
  begin
    Self.FAllow        := FAllow;
    Self.FAnimated     := FAnimated;
    Self.FAnimatedSteps:= FAnimatedSteps;
    Self.Brush         := FBrush;
    Self.FDirection    := FDirection;
    Self.FFullRepaint  := FFullRepaint;
    Self.FHistory      := FHistory;
    Self.FKeepAspect   := FKeepAspect;
    Self.FKeyShift     := FKeyShift;
    Self.FMouseButton  := FMouseButton;
    Self.Pen           := FPen;
  end;
end;

procedure TTeeZoom.SetBrush(const Value:TTeeZoomBrush);
begin
  if Assigned(Value) then Brush.Assign(Value)
                     else FreeAndNil(FBrush);
end;

procedure TTeeZoom.SetPen(const Value:TTeeZoomPen);
begin
  if Assigned(Value) then Pen.Assign(Value)
                     else FreeAndNil(FPen);
end;

procedure TTeeZoom.CorrectAspectRatio(const R:TRect; const X,Y:TCoordinate);
var tmp,
    tmpRatio : Single;
begin
  with R do
  if Bottom=Top then
     tmp:=1
  else
     tmp:=(Right-Left)/(Bottom-Top);

  if y=Y0 then
     tmpRatio:=1
  else
     tmpRatio:=Abs(x-X0)/Abs(y-Y0);

  if tmpRatio<tmp then
  begin
    X1:=X0+Round((y-Y0)*tmp);
    Y1:=Y0+Round((X1-X0)/tmp);
  end
  else
  begin
    Y1:=Y0+Round((x-X0)/tmp);
    X1:=X0+Round((Y1-Y0)*tmp);
  end;
end;

function TTeeZoom.GetAxesHistory:TZoomSnapshots;
begin
  if not Assigned(FAxesHistory) then
     FAxesHistory:=TZoomSnapshots.Create;

  result:=FAxesHistory;
end;

procedure TTeeZoom.SetHistory(Value:Boolean);
begin
  FHistory:=Value;

  if (not FHistory) and Assigned(FAxesHistory) then
     FAxesHistory.Clear;
end;

//type
//  TGradientAccess=class(TTeeGradient);

Function TTeeZoom.GetBrush:TTeeZoomBrush;
begin
  if not Assigned(FBrush) then
  begin
    FBrush:=TTeeZoomBrush.Create(nil);
    FBrush.InitColor(clWhite);
    FBrush.Style:=bsClear;

    (* No way to avoid streaming defaults to DFM/FMX due to MidColor Transparency
    with TGradientAccess(FBrush.Gradient) do
    begin
      InitDirection(gdFromTopLeft);

      StartColor:={$IFDEF FMX}TAlphaColors.Silver{$ELSE}clSilver{$ENDIF};
      IDefStart:=StartColor;

      EndColor:=StartColor;
      IDefEnd:=EndColor;

      UseMiddleColor;
      MidColor:=clWhite;
      IDefMid:=MidColor;

      Colors[1].Transparency:=100;
    end;
    *)
  end;

  result:=FBrush;
end;

Function TTeeZoom.GetPen:TTeeZoomPen;
begin
  if not Assigned(FPen) then
  begin
    FPen:=TTeeZoomPen.Create(nil);

    // Problem: NotXor mode and clWhite sometimes fails with GDI+
    FPen.Color:={$IFDEF FMX}TAlphaColors.Silver{$ELSE}clSilver{$ENDIF};
    FPen.DefaultColor:={$IFDEF FMX}TAlphaColors.Silver{$ELSE}clSilver{$ENDIF};

    FPen.Width:=3;
    FPen.DefaultWidth:=3;

    FPen.InitEndStyle(esFlat);
  end;

  result:=FPen;
end;

{ TBackImage }

procedure TBackImage.Assign(Source:TPersistent);
begin
  if Source is TBackImage then
  with TBackImage(Source) do
  begin
    Self.FBottom:=FBottom;
    Self.FDrawQuality:=FDrawQuality;
    Self.FInside:=FInside;
    Self.FLeft:=FLeft;
    Self.FMode:=FMode;
    Self.FRight:=FRight;
    Self.FTop:=FTop;
  end;

  inherited;
end;

procedure TBackImage.DoChanged;
begin
  {$IFDEF FMX}
  {$IFDEF D17}
  if Assigned(OnChange) then
     OnChange(Self);
  {$ELSE}
  BitmapChanged;
  {$ENDIF}
  {$ELSE}
  Changed(Self);
  {$ENDIF}
end;

procedure TBackImage.SetInside(Const Value:Boolean);
begin
  if Inside<>Value then
  begin
    FInside:=Value;
    DoChanged;
  end;
end;

procedure TBackImage.SetMode(Const Value:TTeeBackImageMode);
begin
  if Mode<>Value then
  begin
    FMode:=Value;
    DoChanged;
  end;
end;

procedure TBackImage.SetField(var Variable:Integer; const Value:Integer);
begin
  if Variable<>Value then
  begin
    Variable:=Value;
    DoChanged;
  end;
end;

procedure TBackImage.SetLeft(Value:Integer);
begin
  SetField(FLeft,Value);
end;

function TBackImage.GetBounds:TRect;
var tmpRight,
    tmpBottom : Integer;
begin
  tmpRight:=FRight;
  if tmpRight=0 then tmpRight:=FLeft+Width;

  tmpBottom:=FBottom;
  if tmpBottom=0 then tmpBottom:=FTop+Height;

  result:=TeeRect(FLeft,FTop,tmpRight,tmpBottom);
end;

procedure TBackImage.SetDrawQuality(const Value:TCanvasStretchQuality);
begin
  if FDrawQuality<>Value then
  begin
    FDrawQuality:=Value;
    DoChanged;
  end;
end;

procedure TBackImage.SetBounds(const Value:TRect);
begin
  FLeft:={$IFDEF FMX}Round{$ENDIF}(Value.Left);
  FTop:={$IFDEF FMX}Round{$ENDIF}(Value.Top);
  FRight:={$IFDEF FMX}Round{$ENDIF}(Value.Right);
  FBottom:={$IFDEF FMX}Round{$ENDIF}(Value.Bottom);

  DoChanged;
end;

procedure TBackImage.SetBottom(Value:Integer);
begin
  SetField(FBottom,Value);
end;

procedure TBackImage.SetTop(Value:Integer);
begin
  SetField(FTop,Value);
end;

procedure TBackImage.SetRight(Value:Integer);
begin
  SetField(FRight,Value);
end;

procedure TBackImage.Draw(const Panel:TCustomTeePanelExtended; Rect:TRect; Z:Integer);
var RectH : Integer;
    RectW : Integer;
    tmp   : TGraphic;

    Procedure TileBitmap;
    var tmpWidth  : Integer;
        tmpHeight : Integer;
        tmpX      : Integer;
        tmpY      : Integer;
        tmpIs3D   : Boolean;
    begin
      tmpWidth :=Width;
      tmpHeight:=Height;

      { keep "painting" the wall with tiled small images... }
      if (tmpWidth>0) and (tmpHeight>0) then
      begin
        tmpY:=0;

        tmpIs3D:=(Z>0) and (not Panel.View3DOptions.Orthogonal);

        while tmpY<RectH do
        begin
          tmpX:=0;

          while tmpX<RectW do
          begin
            if tmpIs3D then
               Panel.Canvas.StretchDraw(TeeRect(Rect.Left+tmpX,Rect.Top+tmpY,
                                          Rect.Left+tmpX+tmpWidth,
                                          Rect.Top+tmpY+tmpHeight),
                                          tmp,Z)
            else
               Panel.Canvas.Draw( {$IFDEF FMX}Round{$ENDIF}(Rect.Left+tmpX),
                                  {$IFDEF FMX}Round{$ENDIF}(Rect.Top+tmpY),
                                  tmp);

            Inc(tmpX,tmpWidth);
          end;

          Inc(tmpY,tmpHeight);
        end;
      end;
    end;

    Procedure Calc3DRect;
    begin
      Rect:=Panel.Canvas.CalcRect3D(Rect,Z);

      if not Panel.View3D then
         Rect.Top:=Rect.Top+1;
    end;

var OldQuality : TCanvasStretchQuality;
    ShouldClip : Boolean;
begin
  tmp:=Filtered;

  OldQuality:=Panel.Canvas.StretchDrawQuality;
  Panel.Canvas.StretchDrawQuality:=FDrawQuality;

  if Mode=pbmStretch then
  begin
    if Z<>0 then
       if Panel.Canvas.View3DOptions.Orthogonal then
       begin
         Calc3DRect;
         Panel.Canvas.StretchDraw(Rect,tmp);
       end
       else
         Panel.Canvas.StretchDraw(Rect,tmp,Z)
    else
       Panel.Canvas.StretchDraw(Rect,tmp);
  end
  else
  begin
    ShouldClip:=Panel.CanClip;

    if ShouldClip then
       if Z=0 then Panel.Canvas.ClipRectangle(Rect)
              else Panel.Canvas.ClipCube(Rect,0,Panel.Width3D);

    if (Z<>0) and Panel.View3DOptions.Orthogonal then
       Calc3DRect;

    RectSize(Rect,RectW,RectH);

    if Mode=pbmTile then
       TileBitmap
    else { draw centered }
    begin
      Panel.FillPanelRect(Rect);

      if Mode=pbmCustom then
      begin
        Rect.Left:=Rect.Left+Left;
        Rect.Top:=Rect.Top+Top;

        if Right=0 then
           Rect.Right:=Rect.Left+Width
        else
           Rect.Right:=Rect.Left+Right;

        if Bottom=0 then
           Rect.Bottom:=Rect.Top+Height
        else
           Rect.Bottom:=Rect.Top+Bottom;
      end
      else
        InflateRect(Rect,-(RectW-Width) div 2,
                         -(RectH-Height) div 2);

      if (Z<>0) and (not Panel.View3DOptions.Orthogonal) then
         Panel.Canvas.StretchDraw(Rect,tmp,Z)
      else
      if (Right=0) and (Bottom=0) then
         Panel.Canvas.Draw({$IFDEF FMX}Round{$ENDIF}(Rect.Left),
                           {$IFDEF FMX}Round{$ENDIF}(Rect.Top),tmp)
      else
         Panel.Canvas.StretchDraw(Rect,tmp);
    end;

    if ShouldClip then
       Panel.Canvas.UnClipRectangle;
  end;

  Panel.Canvas.StretchDrawQuality:=OldQuality;
end;

{ TCustomTeePanelExtended }

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

  FAllowPanning:=pmBoth;
  FScrollMouse:={$IFDEF FMX}TMouseButton.{$ENDIF}mbRight;
  FZoom:=TTeeZoom.Create;
  FZoomWheel:=pmwNone;
end;

Destructor TCustomTeePanelExtended.Destroy;
Begin
  FZoom.Free;
  FGradient.Free;
  FBackImage.Free;
  inherited;
end;

Procedure TCustomTeePanelExtended.SetAnimatedZoom(Value:Boolean);
Begin
  FZoom.Animated:=Value;
end;

Procedure TCustomTeePanelExtended.SetAnimatedZoomSteps(Value:Integer);
Begin
  FZoom.AnimatedSteps:=Value;
end;

Function TCustomTeePanelExtended.GetBackImage:TBackImage;
begin
  if not Assigned(FBackImage) then
  begin
    FBackImage:=TBackImage.Create;
    FBackImage.OnChange:=CanvasChanged;
  end;

  result:=FBackImage;
end;

procedure TCustomTeePanelExtended.SetBackImage(const Value:TBackImage);
begin
  if Assigned(Value) then
     BackImage.Assign(Value)
  else
  begin
    FreeAndNil(FBackImage);
    Invalidate;
  end;
end;

procedure TCustomTeePanelExtended.SetBackImageInside(Const Value:Boolean);
begin
  BackImage.Inside:=Value;
end;

procedure TCustomTeePanelExtended.SetBackImageMode(Const Value:TTeeBackImageMode);
Begin
  BackImage.Mode:=Value;
End;

function TCustomTeePanelExtended.HasBackImage:Boolean;
begin
  result:=Assigned(FBackImage) and FBackImage.Valid;
end;

procedure TCustomTeePanelExtended.SetBackImageTransp(Const Value:Boolean);
begin
  {$IFNDEF FMX}
  if HasBackImage then
     FBackImage.Graphic.Transparent:=Value;
  {$ENDIF}
end;

procedure TCustomTeePanelExtended.UndoZoom;
begin
  if Assigned(FOnUndoZoom) then FOnUndoZoom(Self);
  Invalidate;
  FZoomed:=False;
end;

Function TCustomTeePanelExtended.GetGradient:TChartGradient;
begin
  if not Assigned(FGradient) then
     FGradient:=TChartGradient.Create(CanvasChanged);
     
  result:=FGradient;
end;

procedure TCustomTeePanelExtended.SetGradient(const Value:TChartGradient);
begin
  if Assigned(Value) then Gradient.Assign(Value)
                     else FreeAndNil(FGradient);
end;

Procedure TCustomTeePanelExtended.Assign(Source:TPersistent);
begin
  if Source is TCustomTeePanelExtended then
  With TCustomTeePanelExtended(Source) do
  begin
    Self.BackImage    := FBackImage;
    Self.Gradient     := FGradient;
    Self.FAllowPanning:= FAllowPanning;
    Self.FScrollMouse := FScrollMouse;
    Self.Zoom         := Zoom;
  end;

  inherited;
end;

{$IFNDEF FMX}
{$IFDEF MSWINDOWS}
function TCustomTeePanelExtended.GetPalette: HPALETTE;
begin
  Result:=0; { default result is no palette }

  if HasBackImage and
     (FBackImage.Graphic is TBitmap) then { only bitmaps have palettes }
         Result := TBitmap(FBackImage.Graphic).Palette; { use it if available }
end;
{$ENDIF}
{$ENDIF}

procedure TCustomTeePanelExtended.DrawBitmap(Rect:TRect; Z:Integer);
begin
  if HasBackImage then
     FBackImage.Draw(Self,Rect,Z);
end;

{$IFNDEF FMX}
{$IFDEF MSWINDOWS}

// For OpenGL Design-time, to avoid the DCX_CACHE that TWinControl does:
function TCustomTeePanelExtended.GetDeviceContext(var WindowHandle: HWnd): HDC;
begin
  {$IFNDEF TEEOCX}
  // To avoid the "Control has no Parent" VCL exception, lets check Parent is
  // assigned, otherwise use Owner (if available):
  if Assigned(Parent) then
  begin
  {$ENDIF}
    result:=inherited GetDeviceContext(WindowHandle); // B888 design-time fix
  {$IFNDEF TEEOCX}
  end
  else
  if Assigned(Owner) and (Owner is TWinControl) then
     result:=GetDC(TWinControl(Owner).Handle)
  else
     result:=GetDC(WindowHandle);
  {$ENDIF}
end;

{$ENDIF}
{$ENDIF}

// Returns True if there is at least one gradient color that has
// some transparency:
function AnyTransparency(const AGradient:TTeeGradient):Boolean;
var t : Integer;
begin
  result:=False;

  for t:=0 to AGradient.Colors.Count-1 do
    if AGradient.Colors[t].Transparency>0 then
    begin
      result:=True;
      break;
    end;
end;

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

procedure TCustomTeePanelExtended.PanelPaint(const UserRect:TRect);
var Rect : TRect;

  procedure DrawShadowEmboss(const AShadow:TTeeShadow; IsEmboss:Boolean);
  begin
    if AShadow.ShouldDraw then
    begin
      if IsEmboss then
      begin
        if AShadow.HorizSize>0 then
           Rect.Left:=Rect.Left + (AShadow.HorizSize+1)
        else
           Rect.Right:=Rect.Right - (AShadow.HorizSize+1);

        if AShadow.VertSize>0 then
           Rect.Top:=Rect.Top + (AShadow.VertSize+1)
        else
           Rect.Bottom:=Rect.Bottom - (AShadow.VertSize+1);
      end
      else
      begin
        if AShadow.HorizSize>0 then
           Rect.Right:=Rect.Right - (AShadow.HorizSize+1)
        else
           Rect.Left:=Rect.Left - (AShadow.HorizSize+1);

        if AShadow.VertSize>0 then
           Rect.Bottom:=Rect.Bottom - (AShadow.VertSize+1)
        else
           Rect.Top:=Rect.Top - (AShadow.VertSize+1);
      end;

      Canvas.Pen.Style:=psClear;
      AShadow.Draw(Canvas,Rect,0,BorderRound);
    end;
  end;

var tmpColor : TColor;
begin
  Rect:=UserRect;

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

  Canvas.FrontPlaneBegin;

  // PrintTeePanel is a "trick" to paint Chart background also when printing.
  // When AColor = clNone, it means background should NOT be painted at all.

  if (not Printing) or PrintTeePanel then
  begin
    if Color<>clNone then
    begin
      {$IFNDEF FMX}
      if Assigned(Parent) then
         tmpColor:=TControlAccess(Parent).Color
      else
      {$ENDIF}
         tmpColor:=Color;

      with Canvas do
      begin
        Brush.Color:=Self.PanelColor(tmpColor);
        Brush.Style:=bsSolid;
        Pen.Style:=psClear;
        Gradient.Visible:=False;
        EraseBackground(Rect);
      end;

      DrawShadowEmboss(FShadow,False);
      DrawShadowEmboss(FEmboss,True);
    end;

    FillPanelRect(Rect);

    if HasBackImage and (not BackImage.Inside) then
    begin
      DrawBitmap(Rect,0);

      // Redraw gradient over bitmap, if the gradient has transparency:
      if Gradient.Visible and AnyTransparency(Gradient) then
         FillPanelRect(Rect);
    end;

    DrawPanelBevels(Rect);
  end;

  Canvas.FrontPlaneEnd;

  TCanvas3DAccess(Canvas).EndEntity;
end;

function TCustomTeePanel.FixBorderWidth(var Rect:TRect):Integer;
var tmpHoriz : Integer;
begin
  if Border.SmallDots then
     result:=1
  else
  begin
    // Fix big pen width
    result:={$IFDEF FMX}Round{$ENDIF}(Border.Width-1);

    if result>0 then
    begin
      tmpHoriz:=result div 2;
      if result mod 2=1 then
         Inc(tmpHoriz);

      Rect.Left:=Rect.Left+tmpHoriz;
      Rect.Top:=Rect.Top+tmpHoriz;

      result:=result div 2;

      Rect.Right:=Rect.Right-result;
      Rect.Bottom:=Rect.Bottom-result;
    end;
  end;
end;

procedure TCustomTeePanelExtended.FillPanelRect(const Rect:TRect);

  procedure PaintBack(ARect:TRect; AColor:TColor);
  begin
    // PrintTeePanel is a "trick" to paint Chart background also when printing.
    // When AColor = clNone, it means background should NOT be painted at all.

    with Canvas do
    begin
      if AColor=clNone then
         Brush.Style:=bsClear
      else
      begin
        Brush.Color:=AColor;
        Brush.Style:=bsSolid;
        Brush.Image:=nil;
      end;

      if Border.Visible then
         FixBorderWidth(ARect)
      else
      begin
        // Fix 739:
        ARect.Right:=ARect.Right+1;
        ARect.Bottom:=ARect.Bottom+1;
      end;

      if BorderRound>0 then
         Canvas.RoundRect(ARect,BorderRound,BorderRound)
      else
         Canvas.Rectangle(ARect);
    end;
  end;

var tmpColor : TColor;
begin
  if (not Printing) or PrintTeePanel then
  begin
    {$IFNDEF FMX}
    if ParentColor and Assigned(Parent) then
       tmpColor:=TControlAccess(Parent).Color
    else
    {$ENDIF}
       tmpColor:=Color;

    if tmpColor<>clNone then
       tmpColor:=PanelColor(tmpColor);

    {$IFDEF USETHEMES}
    if tmpColor=clNone then
       ThemeServices.DrawParentBackground(Handle,Canvas.Handle,nil,False);
    {$ENDIF}

    if Border.Visible or (tmpColor<>clNone) then
    begin
      Canvas.FrontPlaneBegin;
      Canvas.AssignVisiblePen(Border);

      if Assigned(FGradient) and FGradient.Visible then
         Canvas.Gradient.Assign(FGradient)
      else
         Canvas.Gradient.Visible:=False;

      PaintBack(Rect,tmpColor);
      Canvas.FrontPlaneEnd;
    end;
  end;
end;

Function TCustomTeePanelExtended.GetBackColor:TColor;
begin
  // Use panel self back color or gradient color
  if Gradient.Visible then
     result:=Gradient.StartColor
  else
     result:=inherited GetBackColor;
end;

procedure TCustomTeePanelExtended.TryDrawZoom;
begin
  if Zoom.FullRepaint or (not InternalCanvas.SupportsXORMode) then
     Invalidate
  else
     DrawZoomRectangle;
end;

// Draw the rectangle while mouse/touch dragging:
procedure TCustomTeePanelExtended.DrawZoomRectangle;
var tmp : TColor;
begin
  if Assigned(Zoom.FBrush) then
  begin
     tmp:=GetBackColor;

     if not Zoom.FullRepaint then
        tmp:=(clWhite-tmp) xor Zoom.Brush.Color;

     SetBrushCanvas(Zoom.Brush.Color,Zoom.Brush,tmp);
  end
  else
     InternalCanvas.Brush.Style:=bsClear;

  if Zoom.FullRepaint or (not InternalCanvas.SupportsXORMode) then
  begin
    Zoom.Pen.Mode:=pmCopy;
    InternalCanvas.AssignVisiblePen(Zoom.Pen);
  end
  else
    TeePrepareXorPen(Self,Zoom.Pen);

  with Zoom do
       InternalCanvas.Rectangle(X0,Y0,X1,Y1);

  if not Zoom.FullRepaint then
     InternalCanvas.Pen.Mode:=pmCopy;
end;

function TCustomTeePanelExtended.GetAllowZoom: Boolean;
begin
  result:=Zoom.Allow;
end;

function TCustomTeePanelExtended.GetAnimatedZoom: Boolean;
begin
  result:=Zoom.Animated;
end;

function TCustomTeePanelExtended.GetAnimatedZoomSteps: Integer;
begin
  result:=Zoom.AnimatedSteps;
end;

Function TCustomTeePanelExtended.GetBackImageTransp:Boolean;
begin
  {$IFDEF FMX}
  result:=True;
  {$ELSE}
  result:=HasBackImage and FBackImage.Graphic.Transparent;
  {$ENDIF}
end;

Function TCustomTeePanelExtended.GetBackImageInside:Boolean;
begin
  result:=HasBackImage and BackImage.Inside;
end;

Function TCustomTeePanelExtended.GetBackImageMode:TTeeBackImageMode;
begin
  result:=BackImage.Mode;
end;

procedure TCustomTeePanelExtended.SetAllowZoom(Value: Boolean);
begin
  Zoom.Allow:=Value;
end;

procedure TCustomTeePanelExtended.SetZoom(const Value: TTeeZoom);
begin
  FZoom.Assign(Value);
end;

procedure TCustomTeePanelExtended.ReadAllowZoom(Reader: TReader);
begin
  Zoom.Allow:=Reader.ReadBoolean;
end;

procedure TCustomTeePanelExtended.ReadAnimatedZoom(Reader: TReader);
begin
  Zoom.Animated:=Reader.ReadBoolean;
end;

procedure TCustomTeePanelExtended.ReadAnimatedZoomSteps(Reader: TReader);
begin
  Zoom.AnimatedSteps:=Reader.ReadInteger;
end;

procedure TCustomTeePanelExtended.ReadPrintMargins(Reader: TReader);
begin
  Reader.ReadListBegin;
  with PrintMargins do
  begin
    Left  :=Reader.ReadInteger;
    Top   :=Reader.ReadInteger;
    Right :=Reader.ReadInteger;
    Bottom:=Reader.ReadInteger;
  end;
  Reader.ReadListEnd;
end;

procedure TCustomTeePanelExtended.SavePrintMargins(Writer: TWriter);
begin
  Writer.WriteListBegin;

  with PrintMargins{$IFDEF FMX}.Round{$ENDIF} do
  begin
    Writer.WriteInteger(Left);
    Writer.WriteInteger(Top);
    Writer.WriteInteger(Right);
    Writer.WriteInteger(Bottom);
  end;

  Writer.WriteListEnd;
end;

procedure TCustomTeePanelExtended.DefineProperties(Filer:TFiler);

  Function NotDefaultPrintMargins:Boolean;
  begin
    with PrintMargins{$IFDEF FMX}.Round{$ENDIF} do
         result:= (Left<>TeeDefault_PrintMargin) or
                  (Top<>TeeDefault_PrintMargin) or
                  (Right<>TeeDefault_PrintMargin) or
                  (Bottom<>TeeDefault_PrintMargin);
  end;

begin
  inherited;
  Filer.DefineProperty('AllowZoom',ReadAllowZoom,nil,False);
  Filer.DefineProperty('AnimatedZoom',ReadAnimatedZoom,nil,False);
  Filer.DefineProperty('AnimatedZoomSteps',ReadAnimatedZoomSteps,nil,False);
  Filer.DefineProperty('PrintMargins',ReadPrintMargins,SavePrintMargins,NotDefaultPrintMargins);
end;

{ TTeeCustomShapeBrushPen }
Constructor TTeeCustomShapeBrushPen.Create;
Begin
  inherited Create;
  FBrush:=GetBrushClass.Create(CanvasChanged);
  FPen:=TTeePen.Create(CanvasChanged);

  FVisible:=True;
  DefaultVisible:=True;
end;

Destructor TTeeCustomShapeBrushPen.Destroy;
begin
  FBrush.Free;
  FPen.Free;
  Transform.Free;
  inherited;
end;

Procedure TTeeCustomShapeBrushPen.Assign(Source:TPersistent);
Begin
  if Source is TTeeCustomShapeBrushPen then
  With TTeeCustomShapeBrushPen(Source) do
  begin
    Self.Brush   :=FBrush;
    Self.Pen     :=FPen;
    Self.FVisible:=FVisible;
  end
  else inherited;
end;

function TTeeCustomShapeBrushPen.GetBrushClass: TChartBrushClass;
begin
  result:=TChartBrush;
end;

function TTeeCustomShapeBrushPen.IsVisibleStored:Boolean;
begin
  result:=FVisible<>DefaultVisible;
end;

Procedure TTeeCustomShapeBrushPen.SetBrush(const Value:TChartBrush);
begin
  FBrush.Assign(Value);
end;

Procedure TTeeCustomShapeBrushPen.SetPen(const Value:TTeePen);
Begin
  FPen.Assign(Value);
end;

Procedure TTeeCustomShapeBrushPen.Show;
begin
  Visible:=True;
end;

Procedure TTeeCustomShapeBrushPen.Hide;
begin
  Visible:=False;
end;

Procedure TTeeCustomShapeBrushPen.Repaint;
begin
  if Assigned(ParentChart) then
     ParentChart.Invalidate;
end;

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

Procedure TTeeCustomShapeBrushPen.SetParent(Value:TCustomTeePanel);
begin
  FParent:=Value;
end;

procedure TTeeCustomShapeBrushPen.SetVisible(Value:Boolean);
begin
  if Assigned(ParentChart) then ParentChart.SetBooleanProperty(FVisible,Value)
                           else FVisible:=Value;
end;

{ TShapeCallout }

Constructor TShapeCallout.Create(const AOwner:TTeeCustomShape);
begin
  inherited Create;

  IOwner:=AOwner;

  FPointPosition:=50;
  FPosition:=50; // %
  FSize:=25; // %
  FWidth:=20; // %
end;

procedure TShapeCallout.SetPosition(const Value:Integer);
begin
  if FPosition<>Value then
  begin
    FPosition:=Value;
    IOwner.Repaint;
  end;
end;

procedure TShapeCallout.SetSide(const Value:TShapeCalloutSide);
begin
  if FSide<>Value then
  begin
    FSide:=Value;
    IOwner.Repaint;
  end;
end;

procedure TShapeCallout.SetSize(const Value:Integer);
begin
  if FSize<>Value then
  begin
    FSize:=Value;
    IOwner.Repaint;
  end;
end;

procedure TShapeCallout.SetWidth(const Value:Integer);
begin
  if FWidth<>Value then
  begin
    FWidth:=Value;
    IOwner.Repaint;
  end;
end;

procedure TShapeCallout.Assign(Source:TPersistent);
begin
  if Source is TShapeCallout then
  begin
    FPointPosition:=TShapeCallout(Source).FPointPosition;
    FPosition:=TShapeCallout(Source).FPosition;
    FSide:=TShapeCallout(Source).FSide;
    FSize:=TShapeCallout(Source).FSize;
    FWidth:=TShapeCallout(Source).FWidth;
  end
  else
    inherited;
end;

function TShapeCallout.GetPath(const ACanvas:TTeeCanvas; const R:TRect; Corner:Integer=0):TTeeBasePath;
var
  P: TTeeBasePath;

  procedure DrawHoriz(Sign:Integer; const Pos:TCoordinate);
  var tmp,
      tmpMid,
      //tmpMid2,
      tmpWide,
      tmp2 : TCoordinate;
  begin
    if Corner>0 then
       if Sign=-1 then
          P.MoveTo(R.Left+Corner,Pos)
       else
          P.MoveTo(R.Right-Corner,Pos);

    tmp:=(R.Right-R.Left);

    if Sign=1 then
       tmpMid:=R.Left+{$IFNDEF FMX}Round{$ENDIF}(Position*tmp*0.01)
    else
       tmpMid:=R.Right-{$IFNDEF FMX}Round{$ENDIF}(Position*tmp*0.01);

    tmpWide:={$IFNDEF FMX}Round{$ENDIF}(Width*tmp*0.005);

    if tmpMid-tmpWide<R.Left+Corner then
       tmpMid:=R.Left+Corner+tmpWide;

    if tmpMid+tmpWide>R.Right then
       tmpMid:=R.Right-tmpWide;

    if Sign=1 then
       tmp:=tmpMid+tmpWide
    else
       tmp:=tmpMid-tmpWide;

    P.LineTo(tmp,Pos);

    tmp2:={$IFNDEF FMX}Round{$ENDIF}((R.Bottom-R.Top)*Size*0.01);

    (*
    if Sign=1 then
       tmpMid2:=R.Right-{$IFNDEF FMX}Round{$ENDIF}(Position*tmp*0.01)
    else
       tmpMid2:=R.Left+{$IFNDEF FMX}Round{$ENDIF}(Position*tmp*0.01);
    *)

    //if RoundSize=0 then
       P.LineTo(tmpMid,Pos+Sign*tmp2)
    //else
    //   P.ArcTo(tmp-tmpWide,Pos,tmpWide,(100-IOwner.RoundSize)*0.01*tmp2,0,-180)
    ;

    if Sign=1 then
    begin
      P.LineTo(tmpMid-tmpWide,Pos);
      P.LineTo(R.Left+Corner,Pos);

      if Corner>0 then
         P.ArcTo(R.Left, R.Bottom-Corner, Corner, Corner, 90, 90);
    end
    else
    begin
      P.LineTo(tmpMid+tmpWide,Pos);
      P.LineTo(R.Right-Corner,Pos);

      if Corner>0 then
         P.ArcTo(R.Right-Corner, R.Top, Corner, Corner, 270, 90);
    end;
  end;

  procedure DrawVert(Sign:Integer; const Pos:TCoordinate);
  var tmp,
      tmpMid,
      tmpWide,
      tmp2 : TCoordinate;
  begin
    if Corner>0 then
       if Sign=1 then
          P.MoveTo(Pos,R.Top+Corner)
       else
          P.MoveTo(Pos,R.Bottom-Corner);

    tmp:=(R.Bottom-R.Top);

    if Sign=1 then
       tmpMid:=R.Bottom-{$IFNDEF FMX}Round{$ENDIF}(Position*tmp*0.01)
    else
       tmpMid:=R.Top+{$IFNDEF FMX}Round{$ENDIF}(Position*tmp*0.01);

    tmpWide:={$IFNDEF FMX}Round{$ENDIF}(Width*tmp*0.005);

    if tmpMid-tmpWide<R.Top then
       tmpMid:=R.Top+tmpWide;

    if tmpMid+tmpWide>R.Bottom then
       tmpMid:=R.Bottom-tmpWide;

    if Sign=1 then
       tmp:=tmpMid-tmpWide
    else
       tmp:=tmpMid+tmpWide;

    P.LineTo(Pos,tmp);

    tmp2:={$IFNDEF FMX}Round{$ENDIF}((R.Right-R.Left)*Size*0.01);

    P.LineTo(Pos+Sign*tmp2,tmpMid);

    if Sign=1 then
    begin
      P.LineTo(Pos,tmpMid+tmpWide);
      P.LineTo(Pos,R.Bottom-Corner);

      if Corner>0 then
         P.ArcTo(R.Right-Corner, R.Bottom-Corner, Corner, Corner, 0, 90);
    end
    else
    begin
      P.LineTo(Pos,tmpMid-tmpWide);
      P.LineTo(Pos,R.Top+Corner);

      if Corner>0 then
         P.ArcTo(R.Left, R.Top, Corner, Corner, 180, 90);
    end;
  end;

begin
  P:=ACanvas.CreatePath;

  if Corner=0 then
     P.MoveTo(R.TopLeft)
  else
     P.ArcTo(R.Left, R.Top, Corner, Corner, 180, 90);

  if Side=csTop then
     DrawHoriz(-1,R.Top)
  else
  if Corner=0 then
     P.LineTo(R.Right,R.Top)
  else
     P.ArcTo(R.Right-Corner, R.Top, Corner, Corner, 270, 90);

  if Side=csRight then
     DrawVert(1,R.Right)
  else
  if Corner=0 then
     P.LineTo(R.BottomRight)
  else
     P.ArcTo(R.Right-Corner, R.Bottom-Corner, Corner, Corner, 0, 90);

  if Side=csBottom then
     DrawHoriz(1,R.Bottom)
  else
  if Corner=0 then
     P.LineTo(R.Left,R.Bottom)
  else
     P.ArcTo(R.Left, R.Bottom-Corner, Corner, Corner, 90, 90);

  if Side=csLeft then
     DrawVert(-1,R.Left)
  else
     P.Close;

  result:=P;
end;

{ TTeeShapeItem }

Constructor TTeeShapeItem.Create(Collection: TCollection);
var tmp : TCustomTeePanel;
begin
  inherited;

  if OwnerShape<>nil then
     tmp:=OwnerShape.ParentChart
  else
     tmp:=nil;

  FShape:=TTeeShapes(Collection).IShapeClass.Create(tmp);
  FShape.Transparent:=True;
  FShape.DefaultTransparent:=True;
end;

Destructor TTeeShapeItem.Destroy;
begin
  if OwnerShape<>nil then
     OwnerShape.Repaint;
     
  FShape.Free;
  inherited;
end;

type
  TOwnedCollectionAccess=class(TOwnedCollection);

function TTeeShapeItem.OwnerShape:TTeeCustomShape;
begin
  result:=TTeeCustomShape(TOwnedCollectionAccess(Collection).GetOwner);
end;

procedure TTeeShapeItem.SetShape(const Value:TTeeCustomShape);
begin
  FShape.Assign(Value);
end;

procedure TTeeShapeItem.Assign(Source:TPersistent);
begin
  if Source is TTeeShapeItem then
     FShape.Assign(TTeeShapeItem(Source).Shape)
  else
     inherited;
end;

{ TTeeShapes }

Function TTeeShapes.Get(Index:Integer):TTeeShapeItem;
begin
  result:=TTeeShapeItem(GetItem(Index));
end;

Procedure TTeeShapes.Put(Index:Integer; Const Value:TTeeShapeItem);
begin
  SetItem(Index,Value);
end;

function TTeeShapes.Add:TTeeShapeItem;
begin
  result:=(inherited Add) as TTeeShapeItem;
end;

{ TTeeCustomShape }

Constructor TTeeCustomShape.Create(const AOwner: TCustomTeePanel);
Begin
  ParentChart:=AOwner;
  inherited Create;

  FAutoSize:=True;
  
  FShadow:=TTeeShadow.Create(CanvasChanged);
  InitShadow(FShadow);

  FFont:=TTeeFont.Create(CanvasChanged);

  FBevelWidth:=2;
  FColor:=clWhite;

  FRoundSize:=16;

  FChildren:=TTeeShapes.Create(Self,TTeeShapeItem);
  FChildren.IShapeClass:=GetChildClass;
end;

Destructor TTeeCustomShape.Destroy;
begin
  FChildren.Free;
  FCallout.Free;
  FCorners.Free;
  FEmboss.Free;
  FShadow.Free;
  FFont.Free;
  FPicture.Free;

  inherited;
end;

Procedure TTeeCustomShape.Assign(Source:TPersistent);
Begin
  if Source is TTeeCustomShape then
  With TTeeCustomShape(Source) do
  begin
    Self.FAlignment   :=FAlignment;
    Self.FAngle       :=FAngle;
    Self.FAutoSize    :=FAutoSize;
    Self.FBevel       :=FBevel;
    Self.FBevelWidth  :=FBevelWidth;
    Self.FChildren.Assign(FChildren);
    Self.FChildLayout :=FChildLayout;
    Self.Color        :=Color;
    Self.Corners      :=FCorners;
    Self.Font         :=FFont;
    Self.FShapeStyle  :=FShapeStyle;
    Self.Picture      :=FPicture;
    Self.FRoundSize   :=FRoundSize;
    Self.Emboss       :=FEmboss;
    Self.Shadow       :=FShadow;
    Self.ShapeCallout :=FCallout;
    Self.FTransparent :=FTransparent;
    Self.FTransparency:=FTransparency;
  end;

  inherited;
end;

Procedure TTeeCustomShape.Draw;
begin
  Draw(ShapeBounds,FAngle);
end;

procedure TTeeCustomShape.MoveTo(const P:TPoint);
begin
  MoveTo(P.X,P.Y);
end;

procedure TTeeCustomShape.MoveTo(const X,Y:TCoordinate);
{$IFNDEF FMX}
var tmp : Integer;
{$ENDIF}
begin
  {$IFDEF FMX}
  ShapeBounds.SetLocation(X,Y);
  {$ELSE}
  with ShapeBounds do
  begin
    tmp:=(Right-Left);
    Left:=x;
    Right:=Left+tmp;

    tmp:=(Bottom-Top);
    Top:=y;
    Bottom:=Top+tmp;
  end;
  {$ENDIF}

  Repaint;
end;

Procedure RectToFourPoints(Const ARect:TRect; const Angle:Double; out P:TFourPoints);
var tmpSin    : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCos    : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCenter : TPoint;

  Procedure RotatePoint(out P:TPoint; const AX,AY:TCoordinate);
  begin
    P.X:=tmpCenter.x+{$IFNDEF FMX}Round{$ENDIF}(  AX*tmpCos + AY*tmpSin );
    P.Y:=tmpCenter.y+{$IFNDEF FMX}Round{$ENDIF}( -AX*tmpSin + AY*tmpCos );
  end;

var tmpRect : TRect;
begin
  tmpCenter:=RectCenter(ARect);
  SinCos(Angle*TeePiStep,tmpSin,tmpCos);

  tmpRect:=ARect;

  With tmpRect do
  begin
    Left:=Left-tmpCenter.X;
    Right:=Right-tmpCenter.X;
    Top:=Top-tmpCenter.Y;
    Bottom:=Bottom-tmpCenter.Y;

    RotatePoint(P[0],Left,Top);
    RotatePoint(P[1],Right,Top);
    RotatePoint(P[2],Right,Bottom);
    RotatePoint(P[3],Left,Bottom);
  end;
end;

Procedure TTeeCustomShape.Draw(Rect:TRect; Angle:Integer=0; AZ:Integer=0);
begin
  Draw(ParentChart,Rect,Angle,AZ);
end;

procedure TTeeCustomShape.DoDrawPoints(const Panel:TCustomTeePanel;
                                       const P:TPointArray; const AZ:Integer);
begin
  if Panel.Canvas.SupportsFullRotation or FDraw3D then
     Panel.Canvas.PolygonWithZ(P,AZ)
  else
     Panel.Canvas.Polygon(P);
end;

Procedure TTeeCustomShape.DoDraw(const Panel:TCustomTeePanel;
                                 const Points:TPointArray; const Rect : TRect;
                                 Angle:Integer=0; AZ:Integer=0);

  function Rotated(const P:TPointArray):TPointArray;
  begin
    result:=P; // <-- MM todo, handle rotation
  end;

begin
  if not Transparent then
     if Angle=0 then
        DoDrawPoints(Panel,Points,AZ)
     else
        DoDrawPoints(Panel,Rotated(Points),AZ);
end;

Procedure TTeeCustomShape.DoDraw(const Panel:TCustomTeePanel;
                                 const Rect:TRect; Angle:Integer=0; AZ:Integer=0);

  function PointsFrom(const P:TFourPoints):TPointArray;
  begin
    SetLength(result,4);
    result[0]:=P[0];
    result[1]:=P[1];
    result[2]:=P[2];
    result[3]:=P[3];
  end;

var P : TFourPoints;
    tmp : TTeeBasePath;
    tmpRound : Integer;
begin
  if not Transparent then
  begin
    if Angle<>0 then
    begin
      RectToFourPoints(Rect,Angle,P);
      DoDrawPoints(Panel,PointsFrom(P),AZ);
    end
    else
    with Panel.Canvas do
    if SupportsFullRotation or FDraw3D then
    begin
      case FShapeStyle of
        fosRectangle: RectangleWithZ(Rect,AZ);
        fosEllipse  : EllipseWithZ(Rect,AZ);
      else
        RoundRect(Rect,RoundSize,RoundSize,AZ,AZ);
      end
    end
    else
    begin
      if Assigned(FCallout) and (FCallout.Side<>csNone) then
      begin
        if FShapeStyle=fosRoundRectangle then
           tmpRound:=RoundSize
        else
           tmpRound:=0;

        tmp:=FCallout.GetPath(Panel.Canvas,Rect,tmpRound);
        try
          tmp.Draw(Panel.Canvas);
        finally
          tmp.Free;
        end;
      end
      else
        case FShapeStyle of
          fosRectangle: Rectangle(Rect);
          fosEllipse  : Ellipse(Rect);
        else
          if HasCorners then
             RoundRect(Rect,RoundSize,RoundSize,Corners)
          else
             RoundRect(Rect,RoundSize,RoundSize);
        end;
    end;
  end;
end;

function TTeeCustomShape.ClickedShape(const P:TPoint; const R:TRect):Boolean;
var tmpP : TFourPoints;
begin
  if FAngle=0 then
     result:=PointInRect(R,P)
  else
  begin
    RectToFourPoints(R,FAngle mod 360,tmpP);

    result:=PointInPolygon(P,tmpP);
  end;
end;

type
  TTeeBrushAccess=class(TTeeBrush);

Procedure TTeeCustomShape.Draw(Rect:TRect; const Points:TPointArray; Angle:Integer=0; AZ:Integer=0);
begin
  Draw(ParentChart,Rect,Points,Angle,AZ);
end;

Procedure TTeeCustomShape.Draw(const Panel:TCustomTeePanel; Rect:TRect; const Points:TPointArray;
                   Angle:Integer=0; AZ:Integer=0);
Begin
  InternalDraw(Panel,Points,Rect,False,Angle,AZ);
end;

Procedure TTeeCustomShape.Draw(const Panel:TCustomTeePanel;
                               Rect:TRect; Angle:Integer=0; AZ:Integer=0);
Begin
  InternalDraw(Panel,nil,Rect,True,Angle,AZ);
end;

Procedure TTeeCustomShape.InternalDraw(const Panel:TCustomTeePanel;
                   const Points:TPointArray; const Rect:TRect;
                   UseRect:Boolean; Angle:Integer=0; AZ:Integer=0);

  procedure DrawPicture;

    function GradientTransparency:Boolean;
    begin
      result:=TTeeBrushAccess(Brush).HasGradient and AnyTransparency(Brush.Gradient);
    end;

  begin
    if ShapeStyle=fosEllipse then
       Panel.Canvas.ClipEllipse(Rect)
    else
    if ShapeStyle=fosRoundRectangle then
       Panel.Canvas.ClipRectangle(Rect,RoundSize,RoundSize);

    if not UseRect then
      Panel.Canvas.ClipPolygon(Points, Length(Points));

    if Panel.Canvas.SupportsFullRotation then
       FPicture.Draw(TCustomTeePanelExtended(Panel),Rect,AZ)
    else
       FPicture.Draw(TCustomTeePanelExtended(Panel),Rect,0);

    if (ShapeStyle=fosEllipse) or (ShapeStyle=fosRoundRectangle) then
       Panel.Canvas.UnClipRectangle;

    // Semi-transparent gradients can be painted over images:
    if GradientTransparency then
       Panel.Canvas.AssignBrush(Brush,Color)
    else
       Panel.Canvas.Brush.Style:=bsClear;
  end;

  procedure DrawChildren;
  var t,
      tmpPos   : Integer;
      tmp      : TTeeCustomShape;
      tmpX     : Integer;
  begin
    tmpPos:=FirstChildPos;

    for t:=0 to Children.Count-1 do
    begin
      tmp:=Children[t].Shape;

      if tmp.Visible then
      begin
        if ChildLayout=slTopBottom then
        begin
          case tmp.FAlignment of
            taLeftJustify: tmpX:={$IFDEF FMX}Round{$ENDIF}(Rect.Left);
            taCenter: tmpX:={$IFDEF FMX}Round{$ENDIF}((Rect.Left+Rect.Right-tmp.Width) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF});
          else
            tmpX:={$IFDEF FMX}Round{$ENDIF}(Rect.Right-tmp.Width);
          end;

          tmp.MoveTo(tmpX,tmpPos);
        end
        else
           tmp.MoveTo(tmpPos,{$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Top));

        tmp.Draw(Panel,tmp.ShapeBounds,Angle,AZ);

        if ChildLayout=slTopBottom then
           Inc(tmpPos,{$IFDEF FMX}Round{$ENDIF}(tmp.Height))
        else
           Inc(tmpPos,{$IFDEF FMX}Round{$ENDIF}(tmp.Width));
      end;
    end;
  end;

  function CreateBlend:TTeeBlend;
  var P        : TFourPoints;
      tmpR     : TRect;
  begin
    if Panel.Canvas.SupportsAlpha then
       result:=Panel.Canvas.BeginBlending(TeeZeroRect,Transparency)
    else
    begin
      if Angle<>0 then
      begin
        RectToFourPoints(Rect,Angle,P);
        tmpR:=RectFromPolygon(P,4);
      end
      else tmpR:=Rect;

      result:=Panel.Canvas.BeginBlending(tmpR,Transparency);
    end;
  end;

  Procedure DrawShadowEmboss(R:TRect; const AShadow:TTeeShadow; AFactor:Integer);
  var tmp : Integer;
  begin
    if Assigned(AShadow) and AShadow.ShouldDraw and (FBrush.Style<>bsClear) then
    begin
      if Pen.Visible and (Pen.Width>1) then
      begin
        tmp:={$IFDEF FMX}Round{$ENDIF}(AFactor*(Pen.Width {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF}));
        InflateRect(R,tmp,tmp);
      end;

      AShadow.ZPosition:=AZ+2; // OpenGL

      if FShapeStyle=fosEllipse then
         AShadow.DrawEllipse(Panel.Canvas,R)
      else
      begin
        if FShapeStyle=fosRectangle then tmp:=0
                                    else tmp:=RoundSize;

        if Panel.Canvas.SupportsFullRotation then
           AShadow.Draw(Panel.Canvas,R,tmp,Angle)
        else
        begin
          Panel.Canvas.FrontPlaneBegin;
          AShadow.Draw(Panel.Canvas,R,tmp,Angle);
          Panel.Canvas.FrontPlaneEnd;
        end;
      end;
    end;
  end;

var tmpBlend : TTeeBlend;
    tmpTransp: Byte;
    tmpR     : TRect;
begin
  tmpTransp:=Transparency;

  if tmpTransp>0 then
     tmpBlend:=CreateBlend
  else
     tmpBlend:=nil;

  if not Transparent then
  begin
    DrawShadowEmboss(Rect,FShadow,1);      //MM todo polygon
    DrawShadowEmboss(Rect,FEmboss,-1);

    // Note: GDI+ uses FBrush.FImage instead of FPicture (much faster and precise)

    if Assigned(FPicture) and FPicture.Valid then
       DrawPicture
    else
       Panel.Canvas.AssignBrush(Brush,Color);

    Panel.Canvas.AssignVisiblePen(Pen);
  end
  else
    Panel.Canvas.Brush.Style:=bsClear;

  if UseRect then
     DoDraw(Panel,Rect,Angle,AZ)
  else
     DoDraw(Panel,Points,Rect,Angle,AZ);

  if not Transparent then
  begin
    if Angle=0 then
    begin
      tmpR:=Rect;
      DrawBevel(Panel.Canvas,FBevel,tmpR,BevelWidth);
    end;
  end;

  // Draw Children, Vertical Layout:
  if Children.Count>0 then
     DrawChildren;

  if tmpTransp>0 then
  begin
    if Assigned(tmpBlend) and (not Transparent) and (Picture.Filters.Count>0) and
       (not Assigned(Picture.Graphic)) then
          Picture.Filters.ApplyTo(tmpBlend.Bitmap);

    Panel.Canvas.EndBlending(tmpBlend);
  end;
end;

function TTeeCustomShape.IsChildrenStored:Boolean;
begin
  result:=FChildren.Count>0;
end;

function TTeeCustomShape.HasCorners:Boolean;
begin
  result:=Assigned(FCorners);
end;

function TTeeCustomShape.HasPicture:Boolean;
begin
  result:=Assigned(FPicture);
end;

function TTeeCustomShape.IsPictureStored:Boolean;
begin
  result:=HasPicture;
end;

Function TTeeCustomShape.IsTranspStored:Boolean;
begin
  result:=FTransparent<>DefaultTransparent;
end;

procedure TTeeCustomShape.ReadShadowColor(Reader: TReader); // obsolete
begin
  if Reader.NextValue=vaIdent then
     Shadow.Color:=StringToColor(Reader.ReadIdent)
  else
     Shadow.Color:=Reader.ReadInteger;
end;

procedure TTeeCustomShape.ReadShadowSize(Reader: TReader); // obsolete
begin
  Shadow.Size:=Reader.ReadInteger;
end;

procedure TTeeCustomShape.CalcBounds(const Panel:TCustomTeePanel);
var t : Integer;
    tmp : TTeeCustomShape;
begin
  if AutoSize then
  begin
    ShapeBounds.Right:=ShapeBounds.Left;
    ShapeBounds.Bottom:=ShapeBounds.Top;
  end;

  for t:=0 to Children.Count-1 do
  begin
    tmp:=Children[t].Shape;

    if tmp.Visible then
    begin
      tmp.CalcBounds(Panel);

      if ChildLayout=slTopBottom then
      begin
        // Vertical layout:
        ShapeBounds.Right:=ShapeBounds.Left+Max(ShapeBounds.Right-ShapeBounds.Left,tmp.Width);
        ShapeBounds.Bottom:=ShapeBounds.Bottom+tmp.Height;
      end
      else
      begin
        ShapeBounds.Right:=ShapeBounds.Right+tmp.Width;
        ShapeBounds.Bottom:=ShapeBounds.Top+Max(ShapeBounds.Bottom-ShapeBounds.Top,tmp.Height);
      end;
    end;
  end;
end;

Procedure TTeeCustomShape.DefineProperties(Filer:TFiler);
begin
  inherited;
  
  Filer.DefineProperty('ShadowColor',ReadShadowColor,nil,False);
  Filer.DefineProperty('ShadowSize',ReadShadowSize,nil,False);
end;

function TTeeCustomShape.FirstChildPos:Integer;
begin
  if ChildLayout=slTopBottom then
     result:={$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Top)
  else
     result:={$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Left);
end;

function TTeeCustomShape.GetChildClass:TTeeCustomShapeClass;
begin
  result:=TTeeShape;
end;

class function TTeeCustomShape.GetEditorClass:String;
begin
  result:='TFormTeeShape';
end;

Function TTeeCustomShape.GetHeight:TCoordinate;
begin
  result:=ShapeBounds.Bottom-ShapeBounds.Top;
end;

Function TTeeCustomShape.GetWidth:TCoordinate;
begin
  result:=ShapeBounds.Right-ShapeBounds.Left;
end;

Procedure TTeeCustomShape.SetParent(Value:TCustomTeePanel);
var t : Integer;
begin
  inherited;

  if Assigned(FChildren) then
  for t:=0 to Children.Count-1 do
      Children[t].Shape.ParentChart:=ParentChart;
end;

procedure TTeeCustomShape.InitShadow(const AShadow:TTeeShadow);
begin
  TShadowAccess(AShadow).InitValues(AShadow.Visible,
             {$IFDEF FMX}TAlphaColors.Black{$ELSE}clBlack{$ENDIF},3);
end;

procedure TTeeCustomShape.InitAngle(const AAngle:Integer);
begin
  FAngle:=AAngle;
end;

procedure TTeeCustomShape.InitEmboss(const AEmboss:TTeeEmboss);
begin
  TEmbossAccess(AEmboss).InitValues(AEmboss.Visible,
             {$IFDEF FMX}TAlphaColors.Black{$ELSE}clBlack{$ENDIF},3);
end;

procedure TTeeCustomShape.InitTransparent(const Value:Boolean);
begin
  FTransparent:=Value;
  DefaultTransparent:=Value;
end;

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

Function TTeeCustomShape.GetPicture:TBackImage;
begin
  if not Assigned(FPicture) then
  begin
    FPicture:=TBackImage.Create;
    FPicture.OnChange:=PictureChanged;
  end;

  result:=FPicture;
end;

Function TTeeCustomShape.GetShadowColor:TColor; // obsolete
begin
  result:=Shadow.Color;
end;

Function TTeeCustomShape.GetShadowSize:Integer; // obsolete
begin
  result:=Shadow.Size;
end;

procedure TTeeCustomShape.SetAlignment(const Value:TAlignment);
begin
  if FAlignment<>Value then
  begin
    FAlignment:=Value;
    Repaint;
  end;
end;

procedure TTeeCustomShape.SetAngle(const Value:Integer);
begin
  if FAngle<>Value then
  begin
    FAngle:=Value;
    Repaint;
  end;
end;

procedure TTeeCustomShape.SetAutoSize(const Value: Boolean);
begin
  if FAutoSize<>Value then
  begin
    FAutoSize:=Value;
    Repaint;
  end;
end;

procedure TTeeCustomShape.SetBevel(const Value: TPanelBevel);
begin
  if FBevel<>Value then
  begin
    FBevel:=Value;
    Repaint;
  end;
end;

procedure TTeeCustomShape.SetBevelWidth(const Value: TBevelWidth);
begin
  if FBevelWidth<>Value then
  begin
    FBevelWidth:=Value;
    Repaint;
  end;
end;

procedure TTeeCustomShape.SetCallout(const Value:TShapeCallout);
begin
  if Assigned(Value) then
     ShapeCallout.Assign(Value)
  else
     FreeAndNil(FCallout);
end;

procedure TTeeCustomShape.SetChildren(const Value:TTeeShapes);
begin
  FChildren.Assign(Value);
end;

Procedure TTeeCustomShape.SetColor(const Value:TColor);
begin
  if Assigned(ParentChart) then ParentChart.SetColorProperty(FColor,Value)
                           else FColor:=Value;
end;

Procedure TTeeCustomShape.SetFont(const Value:TTeeFont);
begin
  FFont.Assign(Value);
end;

function TTeeCustomShape.GetCallout:TShapeCallout;
begin
  if not Assigned(FCallout) then
     FCallout:=TShapeCallout.Create(Self);

  result:=FCallout;
end;

function TTeeCustomShape.GetCorners:TTeeCorners;
begin
  if not Assigned(FCorners) then
     FCorners:=TTeeCorners.Create(CanvasChanged);

  result:=FCorners;
end;

function TTeeCustomShape.GetEmboss:TTeeEmboss;
begin
  if not Assigned(FEmboss) then
  begin
    FEmboss:=TTeeEmboss.Create(CanvasChanged);
    InitEmboss(FEmboss);
  end;

  result:=FEmboss;
end;

Function TTeeCustomShape.GetGradient:TChartGradient;
begin
  result:=Brush.Gradient;
end;

procedure TTeeCustomShape.SetGradient(const Value:TChartGradient);
begin
  Brush.Gradient:=Value;
end;

procedure TTeeCustomShape.SetHeight(const Value:TCoordinate);
begin
  ShapeBounds.Bottom:=ShapeBounds.Top+Value;
end;

procedure TTeeCustomShape.SetWidth(const Value:TCoordinate);
begin
  ShapeBounds.Right:=ShapeBounds.Left+Value;
end;

procedure TTeeCustomShape.SetRoundSize(const Value:Integer);
begin
  if FRoundSize<>Value then
  begin
    FRoundSize:=Value;

    if FRoundSize>0 then
       FShapeStyle:=fosRoundRectangle;

    Repaint;
  end;
end;

procedure TTeeCustomShape.SetLayout(const Value:TTeeShapeLayout);
begin
  if FChildLayout<>Value then
  begin
    FChildLayout:=Value;
    Repaint;
  end;
end;

Procedure TTeeCustomShape.SetPicture(const Value:TBackImage);
begin
  if Assigned(Value) then Picture.Assign(Value)
                     else FreeAndNil(FPicture);
  Repaint;
end;

procedure TTeeCustomShape.SetShadow(const Value:TTeeShadow);
begin
  if Assigned(Value) then Shadow.Assign(Value)
                     else FreeAndNil(FShadow);
end;

procedure TTeeCustomShape.SetCorners(const Value:TTeeCorners);
begin
  if Assigned(Value) then Corners.Assign(Value)
                     else FreeAndNil(FCorners);
end;

procedure TTeeCustomShape.SetEmboss(const Value:TTeeEmboss);
begin
  if Assigned(Value) then Emboss.Assign(Value)
                     else FreeAndNil(FEmboss);
end;

Procedure TTeeCustomShape.SetShadowColor(const Value:TColor); // obsolete
begin
  Shadow.Color:=Value;
end;

Procedure TTeeCustomShape.SetShadowSize(const Value:Integer); // obsolete
begin
  Shadow.Size:=Value;
end;

Procedure TTeeCustomShape.SetShapeStyle(const Value:TChartObjectShapeStyle);
Begin
  if FShapeStyle<>Value then
  begin
    FShapeStyle:=Value;
    Repaint;
  end;
end;

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

procedure TTeeCustomShape.SetTransparent(const Value:Boolean);
begin
  if FTransparent<>Value then
  begin
    FTransparent:=Value;
    Repaint;
  end;
end;

function TeeTickCount:Single;
{$IFDEF FMX}
{$IFDEF D17}
var TimerService: IFMXTimerService;
{$ENDIF}
{$ENDIF}
begin
  {$IFDEF FMX}
  {$IFDEF D17}
  if TPlatformServices.Current.SupportsPlatformService(IFMXTimerService, IInterface(TimerService)) then
     result:=TimerService.GetTick*1000
  else
     result:=0; // ?
  {$ELSE}
  result:=Platform.GetTick*1000;
  {$ENDIF}
  {$ELSE}
  {$IFDEF USEWINDOWS}
  result:=TimeGetTime;
  {$ELSE}
  result:=GetTickCount64;
  {$ENDIF}
  {$ENDIF}
end;

procedure TeePrepareXorPen(const APanel:TCustomTeePanelExtended; const APen:TTeePen);
begin
  APanel.Canvas.AssignVisiblePenColor(APen,(clWhite-APanel.GetBackColor) xor APen.Color);
  {$IFDEF TEEOCX}
  if APanel.Printing then
    APanel.Canvas.Pen.Mode:= pmBlack //#1967 prints out of bounds
  else
  {$ENDIF}
  APanel.Canvas.Pen.Mode:=pmNotXor;
end;

{ Zoom & Scroll (Panning) }
Procedure TZoomPanning.Check;
begin
  if X0>X1 Then SwapInteger(X0,X1);
  if Y0>Y1 Then SwapInteger(Y0,Y1);
end;

Procedure TZoomPanning.Activate(x,y:Integer);
Begin
  X0:=x;
  Y0:=y;
  X1:=x;
  Y1:=y;
  Active:=True;
End;

{ TTeePanning }

Procedure TTeePanning.Assign(Source:TPersistent);
begin
  if Source is TTeePanning then
  begin
    FInsideBounds:=TTeePanning(Source).FInsideBounds;
    FMouseWheel:=TTeePanning(Source).FMouseWheel;
  end
  else
    inherited;
end;

{$IFNDEF D18}
{ TTeeMouseEventListeners }

// Adds Item to the list. If duplicated, do not add again.
function TTeeEventListeners.Add(Const Item: ITeeEventListener): Integer;
begin
  result:=IndexOf(Pointer(Item));  // prevent duplicating

  if result=-1 then
  begin
    result:=inherited Add(nil);
    inherited Items[result]:=Pointer(Item);
  end;
end;

function TTeeEventListeners.Get(Index: Integer): ITeeEventListener;
begin
  result:=ITeeEventListener({$IFOPT R-}List{$ELSE}inherited Items{$ENDIF}[Index]);
end;

function TTeeEventListeners.Remove(Item: ITeeEventListener): Integer;
begin
  result:=IndexOf(Pointer(Item));

  if result>-1 then
  begin
    inherited Items[result]:=nil;

    Delete(result);
  end;
end;
{$ENDIF}

procedure TeeCopyClipboard(const Text:String);
{$IFDEF FMX}
{$IFDEF D17}
var ClipService : IFMXClipboardService;
{$ENDIF}
{$ENDIF}
begin
  {$IFDEF FMX}
  {$IFDEF D17}
  if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, IInterface(ClipService)) then
     ClipService.SetClipboard(Text);
  {$ELSE}
  Platform.SetClipboard(Text);
  {$ENDIF}
  {$ELSE}
  Clipboard.AsText:=Text;
  {$ENDIF}
end;

{ TTeeExportData }
Procedure TTeeExportData.CopyToClipboard;
begin
  TeeCopyClipboard(AsString);
end;

Function TTeeExportData.AsString:String; // virtual; abstract;
begin
  result:='';
end;

Procedure TTeeExportData.SaveToStream(AStream:TStream);
var {$IFDEF D12}
    ByteOrderMark : TBytes;
    {$ENDIF}
    tmpSt : String;
begin

  {$IFDEF D12}
  ByteOrderMark := TEncoding.Unicode.GetPreamble();
  AStream.Write(Pointer(ByteOrderMark)^,Length(ByteOrderMark));
  {$ENDIF}

  tmpSt:=AsString;
  AStream.Write(Pointer(tmpSt)^,Length(tmpSt)*SizeOf(Char));
end;

Procedure TTeeExportData.SaveToFile(Const FileName:String);
var tmp : TFileStream;
begin
  tmp:=TFileStream.Create(FileName,fmCreate);
  try
    SaveToStream(tmp);
  finally
    tmp.Free;
  end;
end;

{ Returns Rectangle around P +/-Half Width +/-Half Height }
function RectFromCenter(const P:TPoint; AHalfWidth:Integer; AHalfHeight:Integer=0):TRect;
begin
  if AHalfHeight=0 then AHalfHeight:=AHalfWidth;

  result:=TeeRect(P.X-AHalfWidth,P.Y-AHalfHeight,P.X+AHalfWidth,P.Y+AHalfHeight);
end;

function RectFromCenter(const X,Y:TCoordinate; AHalfWidth:Integer; AHalfHeight:Integer=0):TRect;
begin
  if AHalfHeight=0 then AHalfHeight:=AHalfWidth;

  result:=TeeRect(X-AHalfWidth,Y-AHalfHeight,X+AHalfWidth,Y+AHalfHeight);
end;

// Returns True if point P is over (more or less "Tolerance" pixels)
// line:  FromPoint --> ToPoint
function PointInLine(const P,FromPoint,ToPoint:TPoint; const TolerancePixels:Integer):Boolean;
begin
  result:=PointInLine(P,FromPoint.X,FromPoint.Y,ToPoint.X,ToPoint.Y,TolerancePixels);
end;

Function PointInLine(const P:TPoint; const px,py,qx,qy,TolerancePixels:TCoordinate):Boolean; overload;

  function Distance:Single;
  var dx : Single;
      dy : Single;
  begin
    if ((qX=pX) and (qY=pY)) Then
    begin
      dx:=P.X-pX;
      dy:=P.Y-pY;
      result:=Sqrt(dx*dx+dy*dy);
    end
    else
    begin
      dx:=qX-pX;
      dy:=qY-pY;

      result:=((P.X-pX)*dx+(P.Y-pY)*dy)/(dx*dx+dy*dy);

      if result<0 then
      begin
        dx:=P.X-pX;
        dy:=P.Y-pY;
      end
      else
      if result>1 then
      begin
        dx:=P.X-qX;
        dy:=P.Y-qY;
      end
      else
      begin
        dx:=P.X-(pX+result*dx);
        dy:=P.Y-(pY+result*dy);
      end;

      result:=Sqrt(dx*dx+dy*dy);
    end;
  end;

begin
  if (((P.X=pX) and (P.Y=pY)) or ((P.X=qX) and (P.Y=qY))) then
     result:=True
  else
     result:=Abs(Distance)<=(TolerancePixels+1);
end;

Function PointInLineTolerance(Const P:TPoint; const px,py,qx,qy,TolerancePixels:Integer):Boolean; // deprecated / obsolete
begin
  result:=PointInLine(P,px,py,qx,qy,TolerancePixels);
end;

Function PointInLine(Const P:TPoint; const px,py,qx,qy:Integer):Boolean; {$IFDEF D9}inline;{$ENDIF}
Begin
  result:=PointInLine(P,px,py,qx,qy,0);
end;

Function PointInLine(Const P,FromPoint,ToPoint:TPoint):Boolean; overload; {$IFDEF D9}inline;{$ENDIF}
begin
  result:=PointInLine(P,FromPoint.X,FromPoint.Y,ToPoint.X,ToPoint.Y,0);
end;

Function PointInPolygon(Const P:TPoint; Const Poly:Array of TPoint):Boolean;
{ this is slow...
var Region:HRGN;
begin
  Region:=CreatePolygonRgn(Poly,1+High(Poly),0);
  result:=(Region>0) and PtInRegion(Region,p.x,p.y);
  DeleteObject(Region);
end;
}
var i : Integer;
    Px,Py : TCoordinate;
    Pi,Pj : TPoint;
begin
  result:=False;

  if Length(Poly)>1 then
  begin
    Px:=P.X;
    Py:=P.Y;

    Pj:=Poly[High(Poly)];

    for i:=0 to High(Poly) do
    begin
      Pi:=Poly[i];

      if (((( Pi.Y <= PY ) and ( PY < Pj.Y ) ) or
           (( Pj.Y <= PY ) and ( PY < Pi.Y ) )) and
            ( PX < (1.0* (( Pj.X - Pi.X ) * ( PY - Pi.Y )))
            / (1.0*( Pj.Y - Pi.Y )) + Pi.X )) then
               result:=not result;

      Pj:=Pi;
    end;
  end;
end;

Function PointInTriangle(Const P,P0,P1,P2:TPoint):Boolean; overload; {$IFDEF D9}inline;{$ENDIF}
begin
  result:=PointInPolygon(P,[P0,P1,P2]);
end;

Function PointInTriangle(Const P:TPoint;
               const X0,X1,Y0,Y1:TCoordinate):Boolean; overload; {$IFDEF D9}inline;{$ENDIF}
begin
  result:=PointInPolygon(P,[ TeePoint(X0,Y0),
                             TeePoint((X0+X1) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF},Y1),
                             TeePoint(X1,Y0) ]);
end;

Function PointInHorizTriangle(Const P:TPoint;
               const Y0,Y1,X0,X1:TCoordinate):Boolean; {$IFDEF D9}inline;{$ENDIF}
begin
  result:=PointInPolygon(P,[ TeePoint(X0,Y0),
                             TeePoint(X1,(Y0+Y1) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF}),
                             TeePoint(X0,Y1) ]);
end;

Function PointInEllipse(Const P:TPoint; const Left,Top,Right,Bottom:
                         TCoordinate):Boolean;
var tmpWidth,
    tmpHeight,
    tmpXCenter,
    tmpYCenter : TCoordinate;
begin
  {$IFDEF FMX}
  tmpXCenter:=(Left+Right)*0.5;
  tmpYCenter:=(Top+Bottom)*0.5;
  {$ELSE}
  tmpXCenter:=(Left+Right) div 2;
  tmpYCenter:=(Top+Bottom) div 2;
  {$ENDIF}

  tmpWidth :=Sqr(tmpXCenter-Left);
  tmpHeight:=Sqr(tmpYCenter-Top);

  result:=(tmpWidth<>0) and (tmpHeight<>0) and
          ( (Sqr(1.0*P.X-tmpXCenter) / tmpWidth ) +
            (Sqr(1.0*P.Y-tmpYCenter) / tmpHeight) <= 1 );
end;

Function PointInEllipse(Const P:TPoint; Const Rect:TRect):Boolean; {$IFDEF D9}inline;{$ENDIF}
begin
  with Rect do
       result:=PointInEllipse(P,Left,Top,Right,Bottom);
end;

Function DelphiToLocalFormat(Const Format:String):String;
var t : Integer;
begin
  result:=Format;

  if {$IFDEF TEEFORMAT}FormatSettings.{$ENDIF}ThousandSeparator<>{$IFDEF TEEFORMAT}FormatSettings.{$ENDIF}DecimalSeparator then
  {$IFDEF NEXTGEN}
  for t:=0 to Length(result)-1 do
  {$ELSE}
  for t:=1 to Length(result) do
  {$ENDIF}
      if result[t]=',' then result[t]:={$IFDEF TEEFORMAT}FormatSettings.{$ENDIF}ThousandSeparator else
      if result[t]='.' then result[t]:={$IFDEF TEEFORMAT}FormatSettings.{$ENDIF}DecimalSeparator;
end;

Function LocalToDelphiFormat(Const Format:String):String;
var t : Integer;
begin
  result:=Format;

  if {$IFDEF TEEFORMAT}FormatSettings.{$ENDIF}ThousandSeparator<>{$IFDEF TEEFORMAT}FormatSettings.{$ENDIF}DecimalSeparator then
  {$IFDEF NEXTGEN}
  for t:=0 to Length(result)-1 do
  {$ELSE}
  for t:=1 to Length(result) do
  {$ENDIF}
      if result[t]={$IFDEF TEEFORMAT}FormatSettings.{$ENDIF}ThousandSeparator then result[t]:=',' else
      if result[t]={$IFDEF TEEFORMAT}FormatSettings.{$ENDIF}DecimalSeparator then result[t]:='.';
end;

Procedure EnableControls(Enable:Boolean; Const ControlArray:Array of TControl);
var t : Integer;
begin
  for t:=Low(ControlArray) to High(ControlArray) do
  if Assigned(ControlArray[t]) then
     ControlArray[t].Enabled:=Enable;
end;

Function TeeRoundDate(Const ADate:TDateTime; AStep:TDateTimeStep):TDateTime;
var Year  : Word;
    Month : Word;
    Day   : Word;
begin
  if ADate=0 then // only suppress rounding for the Delphi 'null' date 30-12-1899
     result:=ADate
  else
  begin
    if AStep<dtHalfMonth then
    begin
      Case AStep of
        dtOneDay: result:=round(ADate);
        dtTwoDays: result:=round(ADate/2.0)*2;
        dtThreeDays: result:=round(ADate/3.0)*3;
        dtOneWeek: result:=2.0+round((ADate-2.0)/7.0)*7;
        else result:=ADate;
      end;
    end
    else
    begin
      DecodeDate(ADate,Year,Month,Day);
      Case AStep of
         dtHalfMonth   : if Day>=15 then Day:=15
                                    else Day:=1;
         dtOneMonth,
         dtTwoMonths,
         dtThreeMonths,
         dtFourMonths,
         dtSixMonths   : Day:=1;
         dtOneYear     : begin
                           Day:=1;
                           Month:=1;
                         end;
      end;
      result:=EncodeDate(Year,Month,Day);
    end;
  end;
end;

Procedure TeeDateTimeIncrement( Increment:Boolean;
                                var Value:TAxisValue;
                                const AnIncrement:TAxisValue;
                                const ADateTime:TDateTimeStep);
var Year  : Word;
    Month : Word;
    Day   : Word;

 Procedure DecMonths(HowMany:Word);
 begin
   Day:=1;

   if Month>HowMany then Dec(Month,HowMany)
   else
   begin
     Dec(Year);
     Month:=12-(HowMany-Month);
   end;
 end;

 Procedure IncMonths(HowMany:Word);
 begin
   Day:=1;
   Inc(Month,HowMany);
   if Month>12 then
   begin
     Inc(Year);
     Month:=Month-12;
   end;
 end;

 Procedure IncDecMonths(HowMany:Word);
 begin
   if Increment then IncMonths(HowMany)
                else DecMonths(HowMany);
 end;

var MinNewValue : Double;
begin
  DecodeDate(Value,year,month,day);

  // This loop keeps adding the desired value until the
  // AnIncrement value is reached. This avoids having too many
  // date labels below a chart
  if Increment then MinNewValue:=Value+AnIncrement
               else MinNewValue:=Value-AnIncrement;

  if SameValue(Value,MinNewValue) then
     Exit;

  repeat
    Case ADateTime of
      dtHalfMonth   : begin
                        if Day>15 then Day:=15
                        else
                        if Day>1 then Day:=1
                        else
                        begin
                          IncDecMonths(1);
                          Day:=15;
                        end;
                      end;

      dtOneMonth    : IncDecMonths(1);
      dtTwoMonths   : IncDecMonths(2);
      dtThreeMonths : IncDecMonths(3);
      dtFourMonths  : IncDecMonths(4);
      dtSixMonths   : IncDecMonths(6);
      dtOneYear     : if Increment then Inc(year) else Dec(year);
    else
    begin
      if Increment then Value:=Value+AnIncrement
                   else Value:=Value-AnIncrement;
      Exit;
    end;
    end;

    Value:=EncodeDate(year,month,day);

  until (ADateTime<dtOneYear)
        or (Increment and (Value>=MinNewValue))
        or (not Increment and (Value<=MinNewValue));
end;

Procedure TeeSort( StartIndex,EndIndex:Integer; CompareFunc:TTeeSortCompare;
                   SwapFunc:TTeeSortSwap);

  procedure PrivateSort(const l,r:Integer);
  var i : Integer;
      j : Integer;
      x : Integer;
  begin
    i:=l;
    j:=r;
    x:=(i+j) shr 1;

    while i<j do
    begin
      while CompareFunc(i,x)<0 do inc(i);
      while CompareFunc(x,j)<0 do dec(j);

      if i<j then
      begin
        SwapFunc(i,j);
        if i=x then x:=j else if j=x then x:=i;
      end;

      if i<=j then
      begin
        inc(i);
        dec(j)
      end;
    end;

    if l<j then PrivateSort(l,j);
    if i<r then PrivateSort(i,r);
  end;

begin
  PrivateSort(StartIndex,EndIndex);
end;

{ Helper functions }
Function TeeGetUniqueName(const AOwner:TComponent; Const AStartName:String):TComponentName;

  Function FindNameLoop:String;
  var tmp : Integer;
  begin
    if Assigned(AOwner) then
    begin
      tmp:={$IFDEF TEEOCX}0{$ELSE}1{$ENDIF};

      while Assigned(AOwner.FindComponent(AStartName+TeeStr(tmp))) do
           Inc(tmp);

      result:=AStartName+TeeStr(tmp);
    end
    else result:=AStartName+'1';
  end;

{$IFNDEF D6}
{$IFNDEF FMX}
{$IFNDEF LCL}
{$DEFINE USE_DESIGNER}
{$ENDIF}
{$ENDIF}
{$ENDIF}

{$IFDEF USE_DESIGNER}
{$IFNDEF TEEOCX}
var tmpForm : {$IFDEF FMX}TCommonCustomForm{$ELSE}TCustomForm{$ENDIF};
    Designer : IDesigner;
{$ENDIF}
begin
  result:='';

  if Assigned(AOwner) then
  begin
    {$IFNDEF TEEOCX}
    if AOwner is {$IFDEF FMX}TCommonCustomForm{$ELSE}TCustomForm{$ENDIF} then
    begin
      tmpForm:={$IFDEF FMX}TCommonCustomForm{$ELSE}TCustomForm{$ENDIF}(AOwner);

      if Assigned(tmpForm.Designer) then
      begin
        Designer:=nil;
        tmpForm.Designer.QueryInterface(IDesigner,Designer);

        if Assigned(Designer) then
           result:=Designer.UniqueName('T'+AStartName);
      end;
    end;
    {$ENDIF}

    if result='' then
       result:=FindNameLoop;
  end;

{$ELSE}
begin
  result:=FindNameLoop;
{$ENDIF}
end;

{ returns number of sections in St string separated by ";" }
Function TeeNumFields(St:String):Integer;
var i : Integer;
begin
  if St='' then result:=0
  else
  begin
    result:=1;

    i:=StringContains(TeeFieldsSeparator,St);

    While i>0 do
    begin
      Delete(St,1,i);

      if St='' then
         break
      else
      begin
        Inc(result);
        i:=StringContains(TeeFieldsSeparator,St);
      end;
    end;
  end;
end;

Function TeeNumFields(const St,Separator:String):Integer;
var Old : String;
begin
  Old:=TeeFieldsSeparator;
  TeeFieldsSeparator:=Separator;
  try
    result:=TeeNumFields(St);
  finally
    TeeFieldsSeparator:=Old;
  end;
end;

{ returns the index-th section in St string separated by TeeFieldsSeparator (ie: ";") }
Function TeeExtractField(St:String; Index:Integer):String;
var i : Integer;
begin
  result:='';

  if (St<>'') and (Index>0) then
  begin
    i:=StringContains(TeeFieldsSeparator,St);

    While i>0 do
    begin
      Dec(Index);

      if Index=0 then
      begin
        result:=Copy(St,1,i-1);
        exit;
      end;

      Delete(St,1,i);

      i:=StringContains(TeeFieldsSeparator,St);
    end;

    if Index<2 then
       result:=St
    else
       result:='';
  end;
end;

Function TeeExtractField(St:String; Index:Integer; const Separator:String):String;
var Old : String;
begin
  Old:=TeeFieldsSeparator;
  TeeFieldsSeparator:=Separator;
  try
    result:=TeeExtractField(St,Index);
  finally
    TeeFieldsSeparator:=Old;
  end;
end;

type PTeeBitmap=^TTeeBitmap;
     TTeeBitmap=record
       Name1  : String;
       Name2  : String;
       Bitmap : TBitmap;
     end;

{$IFDEF FMX}
procedure ReadDIBFromStream(ABitmap:TBitmap; Stream: TStream);

  function BytesPerScanline(PixelsPerScanline, BitsPerPixel, Alignment: Integer{Longint}): Integer; //Longint;
  begin
    Dec(Alignment);
    Result := ((PixelsPerScanline * BitsPerPixel) + Alignment) and not Alignment;
    Result := Result div 8;
  end;

var
  ColorTable: Pointer;

  {$IFNDEF D18}
  {$IFNDEF IOS}
  {$IFDEF MACOS}
  {$DEFINE TEEINVERTRGB}
  {$ENDIF}
  {$ENDIF}
  {$ENDIF}

  function ColorFrom(const Index:Integer):TAlphaColor;
  begin
    TAlphaColorRec(result).A:=$FF;

    {$IFDEF TEEINVERTRGB}
    TAlphaColorRec(result).R:=PByteArray(ColorTable)^[0+Index];
    TAlphaColorRec(result).G:=PByteArray(ColorTable)^[1+Index];
    TAlphaColorRec(result).B:=PByteArray(ColorTable)^[2+Index];
    {$ELSE}
    TAlphaColorRec(result).B:=PByteArray(ColorTable)^[0+Index];
    TAlphaColorRec(result).G:=PByteArray(ColorTable)^[1+Index];
    TAlphaColorRec(result).R:=PByteArray(ColorTable)^[2+Index];
    {$ENDIF}

    // white=transparent
    if result=claWhite then
       result:=PremultiplyAlpha(MakeColor(claWhite,0));
  end;

type
  TBITMAPINFOHEADER = record
    biSize: DWORD;
    biWidth: Integer; //Longint;
    biHeight: Integer; //Longint;
    biPlanes: Word;
    biBitCount: Word;
    biCompression: DWORD;
    biSizeImage: DWORD;
    biXPelsPerMeter: Integer; //Longint;
    biYPelsPerMeter: Integer; //Longint;
    biClrUsed: DWORD;
    biClrImportant: DWORD;
  end;

  TRGBQUAD = record
    rgbBlue: Byte;
    rgbGreen: Byte;
    rgbRed: Byte;
    rgbAlpha: Byte;
  end;

  PBitmapInfo = ^TBitmapInfo;
  TBITMAPINFO = record
    bmiHeader: TBitmapInfoHeader;
    bmiColors: array[0..0] of TRGBQuad;
  end;

var
  BitsMem: Pointer;
  p2 : Integer;

  {$IFDEF D17}
  tmpData : TBitmapData;
  {$ENDIF}

  procedure Read16BitPixel(xx,yy:Integer);
  begin
    p2:=Byte(BitsMem^);
    Inc(NativeInt(BitsMem));

    {$IFDEF D17}
    tmpData.SetPixel(2*xx,yy,ColorFrom(4*(p2 div 16)));
    tmpData.SetPixel(2*xx+1,yy,ColorFrom(4*(p2 mod 16)));
    {$ELSE}
    ABitmap.Pixels[2*xx,yy]:=ColorFrom(4*(p2 div 16));
    ABitmap.Pixels[2*xx+1,yy]:=ColorFrom(4*(p2 mod 16));
    {$ENDIF}
  end;

var
  OldBitsMem : Pointer;
  BitmapInfo: PBitmapInfo;
  bps,HeaderSize: Integer;
  xx,yy: Integer;
  ColorsUsed: Integer;
  ImageSize: Int64;
  BitCount:Word;
  c:TAlphaColorRec;
begin
  ImageSize:=Stream.Size;

  Stream.Read(HeaderSize, sizeof(HeaderSize));
  GetMem(BitmapInfo, HeaderSize + 12 + 256 * sizeof(TRGBQuad));

  with BitmapInfo^ do
  try
    Stream.Read(Pointer({$IFDEF D16}NativeInt{$ELSE}Longint{$ENDIF}(BitmapInfo) + sizeof(HeaderSize))^,
                HeaderSize - SizeOf(HeaderSize));
    Dec(ImageSize, HeaderSize);

    with bmiHeader do
    begin
      ColorTable := Pointer({$IFDEF D16}NativeInt{$ELSE}Longint{$ENDIF}(BitmapInfo) + HeaderSize);

      if biClrUsed = 0 then
          case biBitCount of
            1, 4, 8: biClrUsed := 1 shl biBitCount;
          end;

      Stream.ReadBuffer(ColorTable^, biClrUsed * sizeof(TRGBQuad));
      Dec(ImageSize, biClrUsed * Sizeof(TRGBQuad));

      ColorsUsed:=biClrUsed;
      BitCount:=biBitCount;

      bps:=BytesPerScanLine(biWidth, biBitCount, 32);
      
      if biSizeImage = 0 then
         biSizeImage := bps * Abs(biHeight);

      if biSizeImage < ImageSize then
         ImageSize := biSizeImage;
    end;

    GetMem(BitsMem,ImageSize);
    OldBitsMem:=BitsMem;

    Stream.ReadBuffer(BitsMem^, ImageSize);

    ABitmap.SetSize(bmiHeader.biWidth,bmiHeader.biHeight);
  finally
    FreeMem(BitmapInfo);
  end;

  {$IFDEF D17}
  if ABitmap.Map(TMapAccess.{$IFDEF D20}Write{$ELSE}maWrite{$ENDIF},tmpData) then
  {$ENDIF}
  begin
    if ColorsUsed=256 then
    begin
      for yy := ABitmap.Height-1 downto 0 do
        for xx := 0 to ABitmap.Width-1 do
        begin
          p2:=Byte(BitsMem^);
          Inc(NativeInt(BitsMem));

          {$IFDEF D17}
          tmpData.SetPixel(xx,yy,ColorFrom(4*p2));
          {$ELSE}
          ABitmap.Pixels[xx,yy]:=ColorFrom(4*p2);
          {$ENDIF}
        end
    end
    else
    if ColorsUsed=16 then
    begin
      for yy := ABitmap.Height-1 downto 0 do
      begin
        for xx := 0 to (ABitmap.Width div 2)-1 do
            Read16BitPixel(xx,yy);

        if (ABitmap.Width mod 2)=1 then
            Read16BitPixel(ABitmap.Width div 2,yy);

        while (NativeInt(BitsMem)-NativeInt(OldBitsMem)) mod bps>0 do
              Inc(NativeInt(BitsMem));
      end;
    end
    else
    begin
      c.A:=$FF;

      for yy := ABitmap.Height-1 downto 0 do
      begin
        for xx := 0 to ABitmap.Width-1 do
        begin
          {$IFDEF D19}
          if tmpData.PixelFormat=TPixelFormat.{$IFDEF D20}BGRA{$ELSE}pfA8R8G8B8{$ENDIF} then
          begin
            c.B:=Byte(BitsMem^);
            Inc(NativeInt(BitsMem));
            c.G:=Byte(BitsMem^);
            Inc(NativeInt(BitsMem));
            c.R:=Byte(BitsMem^);
            Inc(NativeInt(BitsMem));
          end
          else
          {$ENDIF}
          begin
            c.R:=Byte(BitsMem^);
            Inc(NativeInt(BitsMem));
            c.G:=Byte(BitsMem^);
            Inc(NativeInt(BitsMem));
            c.B:=Byte(BitsMem^);
            Inc(NativeInt(BitsMem));
          end;

          if BitCount=32 then
          begin
            c.A:=Byte(BitsMem^);
            Inc(NativeInt(BitsMem));
          end;

          {$IFDEF D17}
          tmpData.SetPixel(xx,yy,c.Color);
          {$ELSE}
          ABitmap.Pixels[xx,yy]:=c.Color;
          {$ENDIF}
        end;

        Inc(NativeInt(BitsMem), bps-ABitmap.Width*3);
      end;
    end;

    {$IFDEF D17}
    ABitmap.Unmap(tmpData);
    {$ENDIF}
  end;

  if Assigned(OldBitsMem) then
     FreeMem(OldBitsMem,ImageSize);
end;
{$ENDIF}

{ Load the Bitmap resource with Name1 or Name2 name }
function LoadTeeBitmap(AInstance: THandle; Data: Pointer): Boolean;

  { Try to find a resource bitmap and load it }
  Function TryFindResource(Const ResName:String):Boolean;
  {$IFDEF FMX}
  const
    RT_BITMAP= PChar(2);
  var S : TStream;
      {$IFDEF FPC}
      AnsiResName : AnsiString;
      {$ENDIF}
  begin
    {$IFDEF FPC}
    AnsiResName:=ResName;
    {$ENDIF}

    if FindResource(AInstance, {$IFDEF FPC}PAnsiChar{$ELSE}PWideChar{$ENDIF}({$IFDEF FPC}AnsiResName{$ELSE}ResName{$ENDIF}), RT_BITMAP)<>0 then
    begin
      S := TResourceStream.Create(AInstance, {$IFDEF FPC}AnsiResName{$ELSE}ResName{$ENDIF}, RT_BITMAP);
      try
        ReadDIBFromStream(PTeeBitmap(Data)^.Bitmap, S);
        result:=True;
      finally
        S.Free;
      end;
    end
    else
      result:=False;
  end;
  {$ELSE}
  var tmpSt : TeeString256;
  begin
    StrPCopy(tmpSt,ResName);

    if FindResource(AInstance, tmpSt, RT_BITMAP)<>0 then
    begin
      PTeeBitmap(Data)^.Bitmap.LoadFromResourceName(AInstance,ResName);
      result:=True;
    end
    else result:=False;
  end;
  {$ENDIF}

begin
  result:=True;
  try
    With PTeeBitmap(Data)^ do
    if ((Name1<>'') and TryFindResource(Name1)) or
       ((Name2<>'') and TryFindResource(Name2)) then
         result:=False;
  except
    on Exception do ;
  end;
end;

Procedure TeeLoadBitmap(const Bitmap:TBitmap; const Name1:String; const Name2:String='');
var tmpBitmap : TTeeBitmap;
begin
  tmpBitmap.Bitmap:=Bitmap;
  tmpBitmap.Name1:=Name1;
  tmpBitmap.Name2:=Name2;

  {$IFDEF LCL}
  LoadTeeBitmap(HInstance,@tmpBitmap);
  {$ELSE}

  {$IFDEF MSWINDOWS}
  EnumModules(LoadTeeBitmap,@tmpBitmap);
  {$ELSE}
  LoadTeeBitmap(HInstance,@tmpBitmap);
  {$ENDIF}

  {$ENDIF}
end;

{ Returns a bitmap from the linked resources that has the same name
  than our class name or parent class name }
Procedure TeeGetBitmapEditor(const AObject:TObject; var Bitmap:TBitmap); {$IFDEF D9}inline;{$ENDIF}
begin
  TeeLoadBitmap(Bitmap,AObject.ClassName,AObject.ClassParent.ClassName);
end;

{$IFNDEF D6}
function TryStrToInt(const S: string; var Value: Integer): Boolean;
var
  E: Integer;
begin
  Val(S, Value, E);
  Result := E = 0;
end;

function TryStrToFloat(const S: String; var Value:Double):Boolean;
var tmp : Extended;
begin
  result:=TextToFloat(PChar(S), tmp, fvExtended);

  if result then
     Value:=tmp;
end;

function StrToFloatDef(const S: string; const Default: Extended): Extended;
begin
  if not TextToFloat(PChar(S), Result, fvExtended) then
    Result := Default;
end;
{$ENDIF}

// Returns True when lines X1Y1->X2Y2 and X3Y3->X4Y4 cross.
// Returns crossing position at x and y var parameters.
function CrossingLines(const X1,Y1,X2,Y2,X3,Y3,X4,Y4:Double; out x,y:Double):Boolean;
var slope1 : Double;
    slope2 : Double;
    intercept1 : Double;
    intercept2 : Double;
    Dif,
    tmp1, tmp2, tmp3, tmp4,
    A1, B1, C1, A2, B2, C2 : Double;
begin
  B1:=X2-X1;
  B2:=X4-X3;

  if (B1=0) or (B2=0) then
  begin
    result:=False;
    exit;
  end;

  A1:=Y2-Y1;
  A2:=Y4-Y3;

  slope1 := A1 / B1;
  slope2 := A2 / B2;
  Intercept1 := Y1 - (slope1 * X1);
  Intercept2 := Y3 - (slope2 * X3);

  Dif:=slope2-slope1;
  if Abs(Dif)>1E-15 then
     X:= (Intercept1-Intercept2) / Dif
  else
     X:=X2;

  Y:= (slope1 * X) + Intercept1;

  C1:=(X1*Y2)-(X2*Y1);
  C2:=(X3*Y4)-(X4*Y3);

  tmp1:=A1*X3-B1*Y3-C1;
  tmp2:=A1*X4-B1*Y4-C1;
  tmp3:=A2*X1-B2*Y1-C2;
  tmp4:=A2*X2-B2*Y2-C2;

  result:=((((tmp1>0) xor (tmp2>0)) and
          ((tmp3>0) xor (tmp4>0))) or //TF02014228
          (((tmp1<>0) xor (tmp2<>0)) and
          ((tmp3<>0) xor (tmp4<>0)))); // TV52010079,TV52010196
end;

// TRANSLATIONS
Procedure TeeTranslateControl(const AControl:TTranslatable; const ExcludeChilds:Array of TTranslatable);
begin
  // if current language is English, do not translate...
  if Assigned(TeeTranslateHook) then
     TeeTranslateHook(AControl,ExcludeChilds);
end;

Procedure TeeTranslateControl(const AControl:TTranslatable);
begin
  TeeTranslateControl(AControl,[]);
end;

function TeeTranslateString(const Text:String):String;
begin
  if Assigned(TeeTranslateStringHook) then
     result:=TeeTranslateStringHook(Text)
  else
     result:=Text;
end;

Function ReplaceChar(const AString:String; const Search:{$IFDEF NET}String{$ELSE}Char{$ENDIF};
                     const Replace:Char=#0):String;
var i,
    Offset  : Integer;
begin
  result:=AString;

  {$IFDEF NEXTGEN}
  if Low(String)=0 then
     Offset:=1
  else
     Offset:=0;
  {$ELSE}
  Offset:=0;
  {$ENDIF}

  Repeat
    i:=Pos(Search,result);

    if i>0 then
       if Replace=#0 then Delete(result,i,1)
                     else result[i-Offset]:=Replace;
  Until i=0;

  // NextGen:
  {
  tmp:=TStringBuilder.Create;

  for i:=Low(AString) to High(AString) do
      if AString.Chars[i]=Search then
      begin
        if Replace<>#0 then
           tmp.Append(Replace);
      end
      else
        tmp.Append(AString.Chars[i]);

  result:=tmp.ToString;
  }
end;

// Deprecated. Use a GDI+ canvas (VCL), or FireMonkey Form.Quality property.
Function TeeAntiAlias(const Panel:TCustomTeePanel; ChartRect:Boolean=False):TBitmap;
begin
  (*
  if not Panel.Canvas.SupportAntiAlias then
     Panel.Canvas:=... <-- use a temporary GDI+ canvas (FireMonkey???)
  *)

  result:=Panel.TeeCreateBitmap(Panel.Color,Panel.GetRectangle,TeePixelFormat);
end;

Const DefaultPalette:Array[0..MaxDefaultColors] of TColor=
        ( {$IFDEF FMX}TAlphaColors.Red{$ELSE}clRed{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Green{$ELSE}clGreen{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Yellow{$ELSE}clYellow{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Blue{$ELSE}clBlue{$ENDIF},
          clWhite,
          {$IFDEF FMX}TAlphaColors.Gray{$ELSE}clGray{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Fuchsia{$ELSE}clFuchsia{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Teal{$ELSE}clTeal{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Navy{$ELSE}clNavy{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Maroon{$ELSE}clMaroon{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Lime{$ELSE}clLime{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Olive{$ELSE}clOlive{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Purple{$ELSE}clPurple{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Silver{$ELSE}clSilver{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Aqua{$ELSE}clAqua{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Black{$ELSE}clBlack{$ENDIF},
          {$IFDEF FMX}TAlphaColors.MoneyGreen{$ELSE}clMoneyGreen{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Skyblue{$ELSE}clSkyBlue{$ENDIF},
          {$IFDEF FMX}TAlphaColors.Cream{$ELSE}clCream{$ENDIF},
          {$IFDEF FMX}TAlphaColors.MedGray{$ELSE}clMedGray{$ENDIF}
          );

Procedure SetDefaultColorPalette; overload;
begin
  SetDefaultColorPalette(DefaultPalette);
end;

Procedure SetDefaultColorPalette(const Palette:Array of TColor); overload;
var t, L : Integer;
begin
  L:=Length(Palette);

  if L=0 then
     SetDefaultColorPalette
  else
  begin
    SetLength(TeeColorPalette,L);

    for t:=0 to L-1 do
        TeeColorPalette[t]:=Palette[t];
  end;
end;

procedure TeeFixRegistry(const AOldKey, ANewKey:String);
begin
  {$IFDEF TEEREGISTRY}
  with TRegistry.Create do
  try
    if KeyExists(AOldKey) then
       if KeyExists(ANewKey) then
          DeleteKey(AOldKey)
       else
          MoveKey(AOldKey, ANewKey, True);
  finally
    Free;
  end;
  {$ENDIF}
end;

procedure TeeLoadFormPosition(const AForm:TForm; const AKey:String; Position,Size:Boolean);
{$IFDEF TEEREGISTRY}
var tmpSize : TPoint;
{$ENDIF}
begin
  {$IFDEF TEEREGISTRY}
  with TRegistry.Create(KEY_READ) do
  try
    if OpenKeyReadOnly(AKey) then
    begin
      if Position then
        if ValueExists('Left') then
        begin
          AForm.Position:={$IFDEF FMX}TFormPosition.{$IFDEF D20}Designed{$ELSE}poDesigned{$ENDIF}{$ELSE}poDesigned{$ENDIF};

          AForm.Left:=Math.Max(0,ReadInteger('Left'));
          AForm.Top:=Math.Max(0,ReadInteger('Top'));
        end;

      if Size then
      begin
        if ValueExists('Maximized') then
           if ReadBool('Maximized') then
              AForm.WindowState:={$IFDEF FMX}TWindowState.{$ENDIF}wsMaximized
           else
              AForm.WindowState:={$IFDEF FMX}TWindowState.{$ENDIF}wsNormal;

        if ValueExists('Width') then
           AForm.Width:=Math.Max(100,ReadInteger('Width'));

        if ValueExists('Height') then
           AForm.Height:=Math.Max(100,ReadInteger('Height'));
      end;

      tmpSize:=TeeGetScreenSize;

      if AForm.Left>tmpSize.X then
         AForm.Left:={$IFDEF FMX}Round{$ENDIF}(tmpSize.X)-AForm.Width;

      if AForm.Top>tmpSize.Y then
         AForm.Top:={$IFDEF FMX}Round{$ENDIF}(tmpSize.Y)-AForm.Height;
    end;
  finally
    Free;
  end;
  {$ENDIF}
end;

procedure TeeSaveFormPosition(const AForm:TForm; const AKey:String; Position,Size:Boolean);
begin
  {$IFDEF TEEREGISTRY}
  with TRegistry.Create do
  try
    if OpenKey(AKey,True) then
    begin
      if Position then
      begin
        if AForm.WindowState<>{$IFDEF FMX}TWindowState.{$ENDIF}wsMaximized then
        begin
          WriteInteger('Left',AForm.Left);
          WriteInteger('Top',AForm.Top);
        end;
      end;

      if Size then
      begin
        if AForm.WindowState<>{$IFDEF FMX}TWindowState.{$ENDIF}wsMaximized then
        begin
          WriteInteger('Width',AForm.Width);
          WriteInteger('Height',AForm.Height);
        end;

        WriteBool('Maximized',AForm.WindowState={$IFDEF FMX}TWindowState.{$ENDIF}wsMaximized);
      end;
    end;
  finally
    Free;
  end;
  {$ENDIF}
end;

function TeeReadBoolOption(const AKey:String; DefaultValue:Boolean):Boolean;
begin
  {$IFDEF LINUX}
  with TIniFile.Create(ExpandFileName('~/.teechart')) do
  try
    result:=ReadBool('Editor', AKey, DefaultValue);
  finally
    Free;
  end;
  {$ELSE}

  result:=DefaultValue;

  {$IFDEF TEEREGISTRY}
  with TRegistry.Create(KEY_READ) do
  try
    RootKey:=HKEY_CURRENT_USER;

    if OpenKeyReadOnly(TeeMsg_EditorKey) and ValueExists(AKey) then
       result:=ReadBool(AKey);
  finally
    Free;
  end;
  {$ENDIF}
  {$ENDIF}
end;

procedure TeeSaveBoolOption(const AKey:String; Value:Boolean);
begin
  {$IFDEF LINUX}
  with TIniFile.Create(ExpandFileName('~/.teechart')) do
  try
    WriteBool('Editor',AKey,Value);
  finally
    Free;
  end;
  {$ELSE}
  {$IFDEF TEEREGISTRY}
  with TRegistry.Create do
  try
    RootKey:=HKEY_CURRENT_USER;

    if OpenKey(TeeMsg_EditorKey,True) then
       WriteBool(AKey,Value);
  finally
    Free;
  end;
  {$ENDIF}
  {$ENDIF}
end;

Function TeeReadIntegerOption(const AKey:String; DefaultValue:Integer):Integer;
begin
  {$IFDEF LINUX}
  with TIniFile.Create(ExpandFileName('~/.teechart')) do
  try
    result:=ReadInteger('Editor', AKey, DefaultValue);
  finally
    Free;
  end;
  {$ELSE}

  result:=DefaultValue;

  {$IFDEF TEEREGISTRY}
  With TRegistry.Create(KEY_READ) do
  try
    RootKey:=HKEY_CURRENT_USER;

    if OpenKeyReadOnly(TeeMsg_EditorKey) and ValueExists(AKey) then
       result:=ReadInteger(AKey);
  finally
    Free;
  end;
  {$ENDIF}
  {$ENDIF}
end;

procedure TeeSaveIntegerOption(const AKey:String; Value:Integer);
begin
  {$IFDEF LINUX}
  with TIniFile.Create(ExpandFileName('~/.teechart')) do
  try
    WriteInteger('Editor',AKey,Value);
  finally
    Free;
  end;
  {$ELSE}
  {$IFDEF TEEREGISTRY}
  with TRegistry.Create do
  try
    RootKey:=HKEY_CURRENT_USER;

    if OpenKey(TeeMsg_EditorKey,True) then
       WriteInteger(AKey,Value);
  finally
    Free;
  end;
  {$ENDIF}
  {$ENDIF}
end;

function TeeReadStringOption(const AKey,DefaultValue:String):String;
begin
  result:=DefaultValue;

  {$IFDEF LINUX}
  with TIniFile.Create(ExpandFileName('~/.teechart')) do
  try
    result:=ReadString('Editor', AKey, DefaultValue);
  finally
    Free;
  end;
  {$ELSE}
  {$IFDEF TEEREGISTRY}
  with TRegistry.Create(KEY_READ) do
  try
    RootKey:=HKEY_CURRENT_USER;

    if OpenKeyReadOnly(TeeMsg_EditorKey) and ValueExists(AKey) then
       result:=ReadString(AKey);
  finally
    Free;
  end;
  {$ENDIF}
  {$ENDIF}
end;

procedure TeeSaveStringOption(const AKey,Value:String);
begin
  {$IFDEF LINUX}
  with TIniFile.Create(ExpandFileName('~/.teechart')) do
  try
    WriteString('Editor',AKey,Value);
  finally
    Free;
  end;
  {$ELSE}
  {$IFDEF TEEREGISTRY}
  with TRegistry.Create do
  try
    RootKey:=HKEY_CURRENT_USER;

    if OpenKey(TeeMsg_EditorKey,True) then
       WriteString(AKey,Value);
  finally
    Free;
  end;
  {$ENDIF}
  {$ENDIF}
end;

function TeeDefaultXMLEncoding:String;
begin
  {$IFNDEF FMX}
  {$IFNDEF LINUX}
  if SysLocale.PriLangID=LANG_JAPANESE then
     result:='shift_jis'
  else
  {$ENDIF}
  {$ENDIF}

  {$IFDEF D9}
  if SizeOf(Char)=2 then
     result:='UTF-16'
  else
  {$ENDIF}
     result:='ISO-8859-1';
end;

function TeeXMLHeader(const Encoding:String):String;
begin
  result:='<?xml version="1.0"';

  if Encoding<>'' then
     result:=result+' encoding="'+Encoding+'"';

  result:=result+'?>';
end;

type
  TStringsStack=class(TStringList)
  public
    function Pop:String;
    function Push(const Value:String):Integer;
  end;

function TStringsStack.Pop:String;
begin
  if Count=0 then result:=''
  else
  begin
    result:=Strings[Count-1];
    Delete(Count-1);
  end;
end;

function TStringsStack.Push(const Value:String):Integer;
begin
  Append(Value);
  result:=Count-1;
end;

procedure ConvertTextToXML(const Stream:TStream; XMLHeader:Boolean=True);
const
  ObjectConst='OBJECT';
  TabStep=2;

var n : Integer;
    Dest : TStrings;
    Tags : TStringsStack;
    Objects : TStringsStack;

  function Spaces(Count:Integer):String;
  {$IFNDEF D18}
  var t : Integer;
  {$ENDIF}
  begin
    {$IFDEF D18}
    result:=StringOfChar(' ',Count);
    {$ELSE}
    SetLength(result,Count);
    for t:=1 to Count do
        result[t]:=' ';
    {$ENDIF}
  end;

  procedure BeginObject(const s:String; Source:TStrings; var t:Integer);
  var i : Integer;
      tmp2,
      tmp : String;
  begin
    i:=Pos(':',s);

    if i>0 then
    begin
      tmp:=Trim(Copy(s,i+1,Length(s)));
      tmp2:=tmp+' Name="'+Trim(Copy(s,Length(ObjectConst)+1,i-1-Length(ObjectConst)))+'"';
    end
    else
    begin
      tmp:=Trim(Copy(s,Length(ObjectConst)+1,Length(s)));
      tmp2:=tmp;
    end;

    if UpperCase(Trim(Source[t+1]))='END' then
    begin
      Dest.Add(Spaces(n)+'<'+tmp2+'/>');
      Inc(t);
    end
    else
    begin
      Dest.Add(Spaces(n)+'<'+tmp2+'>');
      Objects.Push(tmp);
      Inc(n,TabStep);
    end;
  end;

  procedure AddTag(const Tag:String);
  begin
    if n>=TabStep then
       Dec(n,TabStep)
    else
       n:=0;

    Dest.Add(Spaces(n)+'</'+Tag+'>');
  end;

  procedure AddTags(TagStart:Integer); overload;
  begin
    while Tags.Count>TagStart do
          AddTag(Tags.Pop);
  end;

  procedure AddTags; overload;
  begin
    AddTags(0);
  end;

  procedure AddStrings(Source:TStrings; var t : Integer);
  var s,
      tmp : String;
  begin
    Inc(n,TabStep);

    repeat
      Inc(t);

      s:=Trim(Source[t]);
      tmp:=Copy(s,Length(s),1);
      s:=Copy(s,1,Length(s)-1);
      s:='String value="'+s+'"';
      Dest.Add(Spaces(n)+'<'+s+'/>');

    until (tmp=')') or (tmp='}');

    Inc(t);
  end;

  procedure AddItems(Source:TStrings; var t : Integer);
  var tmp,
       s : String;
      i : Integer;
      Items : TStringsStack;
  begin
    Items:=TStringsStack.Create;
    try
      Inc(n,TabStep);

      repeat
        Inc(t);

        s:=Trim(Source[t]);

        i:=Pos('=',s);

        tmp:=UpperCase(s);

        if i=0 then
        begin
          if (tmp='END') or (tmp='END>') then
          begin
            Dec(n,TabStep);
            Dest.Add(Spaces(n)+'</'+Items.Pop+'>');
          end
          else
          begin
            Dest.Add(Spaces(n)+'<'+s+'>');
            Items.Push(s);
            Inc(n,TabStep);
          end;
        end
        else
           Dest.Add(Spaces(n)+'<'+Trim(Copy(s,1,i-1))+' value="'+Trim(Copy(s,i+1,Length(s)))+'"/>');

      until tmp='END>';

      Inc(t);
    finally
      Items.Free;
    end;
  end;

var i,
    TagIndex,
    tmpEq,
    t    : Integer;
    ss,
    sss,
    tmpValue,
    tmp,
    tmpTag,
    s : String;

    Source:TStrings;
begin
  Dest:=TStringList.Create;
  Tags:=TStringsStack.Create;
  Objects:=TStringsStack.Create;
  Source:=TStringList.Create;

  try
    if XMLHeader then
       Dest.Add(TeeXMLHeader(TeeDefaultXMLEncoding));

    Source.LoadFromStream(Stream);

    n:=0;

    t:=0;
    while t<Source.Count do
    begin
      s:=Trim(Source[t]);

      if s='' then
         Continue;

      if UpperCase(Copy(s,1,Length(ObjectConst)))=ObjectConst then
         BeginObject(s,Source,t)
      else
      begin
        if Pos('.',s)=0 then
           AddTags
        else
        begin

          TagIndex:=-1;

          ss:=s;
          repeat
            tmpEq:=Pos('=',s);
            i:=Pos('.',ss);

            if (tmpEq>0) and (i>tmpEq) then
               i:=0;

            if i>0 then
            begin
              sss:=Copy(ss,1,i-1);

              Inc(TagIndex);

              if (Tags.Count<=TagIndex) or (Tags[TagIndex]<>sss) then
              begin
                AddTags(TagIndex);
                Tags.Add(sss);
                Dest.Add(Spaces(n)+'<'+sss+'>');

                Inc(n,TabStep);
              end;

              Delete(ss,1,i);
              Delete(s,1,i);

            end;

          until i=0;

          if Tags.Count>TagIndex+1 then
             AddTags(TagIndex+1);
        end;

        i:=Pos('=',s);

        if i>0 then
        begin
          tmp:=Trim(Copy(s,i+1,Length(s)));

          if (tmp='{') or (tmp='(') then
          begin
            tmpTag:=Trim(Copy(s,1,i-1));

            Dest.Add(Spaces(n)+'<'+tmpTag+'>');
            Tags.Push(tmpTag);
            AddStrings(Source,t);
            AddTag(Tags.Pop);

            Continue;
          end
          else
          begin
            tmpValue:=Copy(s,i+2,Length(s));

            if Trim(tmpValue)='<' then
            begin
              tmpTag:=Trim(Copy(s,1,i-1));

              Dest.Add(Spaces(n)+'<'+tmpTag+'>');
              Tags.Push(tmpTag);
              AddItems(Source,t);
              AddTag(Tags.Pop);

              Continue;
            end
            else
            begin
              if Trim(tmpValue)='<>' then
                 tmpValue:='';

              s:=Copy(s,1,i-1)+'value="'+tmpValue+'"';

              Dest.Add(Spaces(n)+'<'+s+'/>');
            end;
          end;
        end
        else
        if UpperCase(s)='END' then
        begin
          AddTags;
          Dec(n,TabStep);
          Dest.Add(Spaces(n)+'</'+Objects.Pop+'>');
        end
        else
          Assert(False,'Wrong DFM format: '+s);
      end;

      Inc(t);
    end;

    Stream.Position:=0;
    Dest.SaveToStream(Stream);

  finally
    Source.Free;
    Objects.Free;
    Tags.Free;
    Dest.Free;
  end;
end;

{$IFNDEF D6}
// From Math.pas
const
  FuzzFactor = 1000;
  DoubleResolution   = 1E-15 * FuzzFactor;

function SameValue(const A, B: Double; Epsilon: Double=0): Boolean;
begin
  if Epsilon = 0 then
    Epsilon := Max(Min(Abs(A), Abs(B)) * DoubleResolution, DoubleResolution);
  if A > B then
    Result := (A - B) <= Epsilon
  else
    Result := (B - A) <= Epsilon;
end;
{$ENDIF}

// Returns a new TStringList containing all file names in Path, matching wildcards parameter:
Function TeeGetFilesFromDir(const PathAndWildcards:String):TStringList;
var f : TSearchRec;
begin
  result:=TStringList.Create;

  if FindFirst(PathAndWildcards,faAnyFile,f)=0 then
  begin
    Repeat
      if (f.Attr and faDirectory)<>faDirectory then
         result.Add(f.Name);

    Until FindNext(f)<>0;

    FindClose(f);
  end;
end;


// Returns True when "St" is suspicious to contain a web address...
Function TeeIsURL(St:String):Boolean;
begin
  St:=UpperCase(Trim(St));
  result:=(Pos('HTTP://',St)>0) or (Pos('FTP://',St)>0);
end;

Procedure TeeGotoURL(Handle: TeeWindowHandle; Const URL:String);

  {$IFDEF TEEWINDOWS}
  procedure WindowsExecute;
  {$IFNDEF LINUX}
  Var St : TeeString256;
  {$ENDIF}
  begin
    {$IFNDEF LINUX}
    ShellExecute(Handle,'open', // Do not localize
                 StrPCopy(St,URL),nil,nil,SW_SHOW);  { <-- do not translate }
    {$ENDIF}
  end;
  {$ENDIF}

{$IFDEF IOS}
//var app : UIApplication;
//    tmpUrl : NSUrl;
{$ENDIF}

{$IFDEF ANDROID}
//var tmpIntent : TJIntent;
{$ENDIF}
begin
  {$IFDEF TEEWINDOWS}
  WindowsExecute;
  {$ELSE}
  {$IFDEF MACOS}
   {$IFDEF POSIX}

    {$IFDEF IOS}
    (*
    tmpUrl:=TNSUrl.Wrap(TNSUrl.OCClass.URLWithString(NSStr(URL)));
    app:=TUIApplication.Wrap(TUIApplication.OCClass.sharedApplication);
    app.openURL(tmpUrl);
    *)

    {$ELSE}
     _system(PAnsiChar(AnsiString('open ' + URL)));
    //NSWorkspace.openURL:[NSURL URLWithString:@"http://www.url.com/"]];
    {$ENDIF}

   {$ENDIF}
  {$ELSE}
  {$IFDEF ANDROID}
  (*
    tmpIntent:=TJIntent.Create;
    tmpIntent.setAction(TJIntent.JavaClass.ACTION_VIEW);
    tmpIntent.setData(TJnet_Uri.JavaClass.parse(StringToJString(URL)));
    MainActivity.startActivity(tmpIntent);
  *)
  {$ENDIF}
  {$ENDIF}
  {$ENDIF}
end;

procedure RemoveControls(const AControl:{$IFDEF FMX}TFmxObject{$ELSE}TWinControl{$ENDIF});
begin
  with AControl do
       {$IFDEF FMX}
         {$IFDEF D18}
         DeleteChildren;
         {$ELSE}
         while ChildrenCount>0 do
               Children[0].Free;
         {$ENDIF}
       {$ELSE}
       while ControlCount>0 do
             Controls[0].Free;
       {$ENDIF}
end;

// Remove the extension of a file name:  Foo.txt --> Foo
function RemoveFileExtension(const S:String):String;
var ext: String;
begin
  result:=s;

  ext:=ExtractFileExt(S);

  if ext<>'' then
     Delete(result,Length(result)-Length(ext)+1,Length(ext));
end;

{$IFDEF TEECPUASM}

// http://stackoverflow.com/questions/9341119/how-to-probe-a-computer-if-it-supports-sse2-in-delphi-32?rq=1
function IsProcessorFeaturePresent(ProcessorFeature: DWORD): BOOL; stdcall;
  external kernel32 name 'IsProcessorFeaturePresent';

function HasSSE2: boolean;
const
  PF_XMMI64_INSTRUCTIONS_AVAILABLE = 10;
begin
  result := IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
end;

// http://stackoverflow.com/questions/9942118/how-to-probe-the-availability-of-intel-advanced-vector-extensions
function isAvxSupported: Boolean;
asm
{$IFDEF CPUX86}
    push ebx
{$ENDIF}
{$IFDEF CPUX64}
    mov r10, rbx
{$ENDIF}
    xor eax, eax
    cpuid
    cmp eax, 1
    jb @not_supported
    mov eax, 1
    cpuid
    and ecx, 018000000h
    cmp ecx, 018000000h
    jne @not_supported
    xor ecx, ecx
    db 0Fh, 01h, 0D0h //XGETBV
    and eax, 110b
    cmp eax, 110b
    jne @not_supported
    mov eax, 1
    jmp @done
@not_supported:
    xor eax, eax
@done:
{$IFDEF CPUX86}
    pop ebx
{$ENDIF}
{$IFDEF CPUX64}
    mov rbx, r10
{$ENDIF}
end;

// ($800000,  $400000, $2000000, $4000000, $80000000, $40000000);
// {ciMMX  ,  ciEMMX,  ciSSE   , ciSSE2  , ci3DNow ,  ci3DNowExt}

function SupportsSSE: LongBool;
const
  CPUID_INTEL_SSE = $02000000;
asm
{$IFDEF CPUX86}
    push ebx
{$ENDIF}
{$IFDEF CPUX64}
    mov r10, rbx
{$ENDIF}
  mov eax, 1
  cpuid
  mov eax, FALSE
  test edx, CPUID_INTEL_SSE
  jz @END
  mov eax, TRUE
@END:
{$IFDEF CPUX86}
    pop ebx
{$ENDIF}
{$IFDEF CPUX64}
    mov rbx, r10
{$ENDIF}
end;

function SupportsSSE2: LongBool;
const
  CPUID_INTEL_SSE2 = $04000000;
asm
{$IFDEF CPUX86}
    push ebx
{$ENDIF}
{$IFDEF CPUX64}
    mov r10, rbx
{$ENDIF}
  mov eax, 1
  cpuid
  mov eax, FALSE
  test edx, CPUID_INTEL_SSE2
  jz @END
  mov eax, TRUE
@END:
{$IFDEF CPUX86}
    pop ebx
{$ENDIF}
{$IFDEF CPUX64}
    mov rbx, r10
{$ENDIF}
end;

function SupportsMMX: Boolean;
asm
  mov eax,1
  cpuid
  and edx,$800000
  cmp edx,0
  jnz @no
  mov result,1
  jmp @bye
 @no:
  mov result,0
 @bye:
end;

{ TTeeCPU }

{$IFDEF D6}
class function TTeeCPU.HasAVX:Boolean;
begin
  result:=isAvxSupported;
end;

class function TTeeCPU.HasSSE:Boolean;
begin
  result:=SupportsSSE;
end;

class function TTeeCPU.HasSSE2:Boolean;
begin
  result:=SupportsSSE2;
end;

class function TTeeCPU.HasMMX:Boolean;
begin
  result:=SupportsMMX;
end;
{$ENDIF}

{$ENDIF}

{$IFNDEF TEELITEEMB}

// FPC: NativeUInt = {$IFDEF CPU64}QWORD{$ELSE}LongWord{$ENDIF}

class function TTeeCPU.NumberOfProcessors:Integer;
{$IFDEF MSWINDOWS}
var t : Integer;
    PAffinity,
    SAffinity: {$IFDEF D16}NativeUInt{$ELSE}{$IFDEF LCL}NativeUInt{$ELSE}DWORD{$ENDIF}{$ENDIF};
    {$IFNDEF D16}
    tmpInfo : _SYSTEM_INFO;
    {$ENDIF}
begin
  {$IFDEF FPC}
  PAffinity:=0;
  SAffinity:=0;
  {$ENDIF}

  if GetProcessAffinityMask(GetCurrentProcess, PAffinity, SAffinity) then
  begin
    result:=0;

    for t:=0 to 31 do
    if (PAffinity and (1 shl t))<>0 then
       Inc(Result);
  end
  else
    {$IFDEF D16}
    result:=CPUCount;
    {$ELSE}
    begin
      GetSystemInfo(tmpInfo);
      result:=tmpInfo.dwNumberOfProcessors;
    end;
    {$ENDIF}
{$ELSE}
begin
  {$IFDEF D16}
  result:=CPUCount;
  {$ELSE}
  result:=1;  // ??
  {$ENDIF}
{$ENDIF}
end;

const
  CPU_RegKey='\Hardware\Description\System\CentralProcessor\0';

class function TTeeCPU.Identifier:String;
begin
  {$IFDEF TEEREGISTRY}
  with TRegistry.Create(KEY_READ) do
  try
    RootKey := HKEY_LOCAL_MACHINE;

    if OpenKey(CPU_RegKey, False) then
       result:=ReadString('Identifier')
    else
       result:='';
  finally
    Free;
  end;
  {$ELSE}
  result:='';
  {$ENDIF}
end;

class function TTeeCPU.Name:String;
begin
  {$IFDEF TEEREGISTRY}
  with TRegistry.Create(KEY_READ) do
  try
    RootKey := HKEY_LOCAL_MACHINE;

    if OpenKey(CPU_RegKey, False) then
       result:=ReadString('ProcessorNameString')
    else
       result:='';
  finally
    Free;
  end;
  {$ELSE}
  result:='';
  {$ENDIF}
end;

class function TTeeCPU.Speed:Integer;
begin
  {$IFDEF TEEREGISTRY}
  with TRegistry.Create(KEY_READ) do
  try
    RootKey := HKEY_LOCAL_MACHINE;

    if OpenKey(CPU_RegKey, False) then
       result:=ReadInteger('~MHz')
    else
       result:=0;
  finally
    Free;
  end;
  {$ELSE}
  result:=0;
  {$ENDIF}
end;

class function TTeeCPU.TotalMemoryAllocated:Cardinal;

  {$IFDEF D16}
  {$IFDEF MSWINDOWS}
  function MemoryUsed: Cardinal;
  var State: TMemoryManagerState;
      Small: TSmallBlockTypeState;
  begin
    GetMemoryManagerState(State);

    result:=State.TotalAllocatedMediumBlockSize + State.TotalAllocatedLargeBlockSize;

    for Small in State.SmallBlockTypeStates do
        Inc(result, Small.UseableBlockSize * Small.AllocatedBlockCount);
  end;
  {$ENDIF}
  {$ENDIF}

begin
  result:={$IFDEF D16}
            {$IFDEF MSWINDOWS}
            MemoryUsed;
            {$ELSE}
            0  // Posix, MACos, iOS, Android
            {$ENDIF}
          {$ELSE}
            {$IFDEF FASTMM}FastGetHeapStatus{$ELSE}GetHeapStatus{$ENDIF}.TotalAllocated;
          {$ENDIF}
end;

{$IFNDEF TEEPARALLEL}

// Code adapted from:
// http://stackoverflow.com/users/205496/linas

type
  TParallelGroup=class;

  TParallel=class(TThread)
  private
    FProc     : TParallelProc;
    Current,
    FThreadID : Integer;
    Owner     : TParallelGroup;
  protected
    procedure Execute; override;
  public
    Constructor Create;
    Destructor Destroy; override;
  end;

  TParallelGroup=class
  private
    Current : Integer;
    MaxPos  : Integer;
    threads : Array of TParallel;

    LiveCount : Integer;
  public
    Constructor Create(const AMin,AMax:Integer);
    Destructor Destroy; override;

    procedure Run(Count:Integer; const aProc: TParallelProc
                {$IFDEF MSWINDOWS}; const APriority:TThreadPriority{$ENDIF});
  end;

{ TParallel }

Constructor TParallel.Create;
begin
  inherited Create(True);
  FreeOnTerminate:=True;
end;

Destructor TParallel.Destroy;
begin
  {$IFDEF MSWINDOWS}
  InterlockedDecrement(Owner.LiveCount);
  {$ELSE}
  Owner.LiveCount:=Owner.LiveCount-1; // Pending: TCriticalSection
  {$ENDIF}

  Owner.threads[FThreadID]:=nil;
  inherited;
end;

procedure TParallel.Execute;
begin
  FProc(Current);

  repeat
    {$IFDEF MSWINDOWS}
    Current:=InterlockedIncrement(Owner.Current);
    {$ELSE}
    Current:=Owner.Current+1;  // Pending: TCriticalSection
    {$ENDIF}

    if Current<=Owner.MaxPos then
    begin
      FProc(Current);

      {$IFDEF D6}
      if FatalException<>nil then
         break;
      {$ENDIF}
    end;

  until Current>Owner.MaxPos;
end;

Constructor TParallelGroup.Create(const AMin,AMax:Integer);
begin
  inherited Create;
  Current:=AMin;
  MaxPos:=AMax;
end;

Destructor TParallelGroup.Destroy;
begin
  threads:=nil;
  inherited;
end;

procedure TParallelGroup.Run(Count:Integer; const aProc: TParallelProc
                          {$IFDEF MSWINDOWS}; const APriority:TThreadPriority{$ENDIF});
var t : Integer;
begin
  SetLength(threads,Count);

  for t:=0 to Count-1 do
  begin
    threads[t]:=TParallel.Create;
    threads[t].Owner:=Self;

    {$IFDEF MSWINDOWS}
    threads[t].Priority:=APriority;
    {$ENDIF}

    threads[t].FThreadID:=t;
    threads[t].Current:=t;
    threads[t].FProc:=aProc;
  end;

  LiveCount:=Count;

  Current:=Count-1;

  for t:=0 to Count-1 do
      threads[t].{$IFDEF D14}Start{$ELSE}Resume{$ENDIF};

  repeat
  until LiveCount=0;
end;
{$ENDIF}

class procedure TTeeCPU.ParallelFor(nMin, nMax, nThreads: Integer; aProc: TParallelProc
                  {$IFDEF MSWINDOWS}; APriority:TThreadPriority=tpNormal{$ENDIF});
{$IFDEF TEEPARALLEL}
begin
  TParallel.For(1,nMin,nMax+1,aProc);
end;
{$ELSE}
var t : Integer;
begin
  if nMin>nMax then
     Exit;

  if nThreads=1 then
     for t:=nMin to nMax do  // Single CPU
         aProc(t)
  else
  with TParallelGroup.Create(nMin,nMax) do
  try
    Run(nThreads,aProc{$IFDEF MSWINDOWS},APriority{$ENDIF});
  finally
    Free;
  end;
end;
{$ENDIF}

class procedure TTeeCPU.ParallelFor(nMin, nMax: Integer; aProc: TParallelProc);
var tmp : Integer;
begin
  tmp:=TTeeCPU.NumberOfProcessors;
  ParallelFor(nMin, nMax, tmp, aProc);
end;
{$ENDIF} // TEELITEEMB

// Register AClass so the Chart editor can find it.
procedure RegisterSeriesEditor(const AClass:TPersistentClass);
begin
  TeeActivateGroup;

  {$IFDEF FMX}
  RegisterFmxClasses([AClass]);
  {$ELSE}
  RegisterClass(AClass);
  {$ENDIF}
end;

initialization
  TeeDefaultCanvas:={$IFDEF FMX}''{$ELSE}'TGDIPlusCanvas'{$ENDIF};

  SetDefaultColorPalette;

  {$IFNDEF FMX}
  {$IFDEF MSWINDOWS}
  Screen.Cursors[crTeeHand]:=LoadCursor(HInstance,'TEE_CURSOR_HAND');
  {$ENDIF}
  {$ENDIF}

finalization
  TeeColorPalette:=nil;

  {$IFNDEF FMX}
  {$IFDEF MSWINDOWS}
  Screen.Cursors[crTeeHand]:=0;
  {$ENDIF}
  {$ENDIF}
end.
