{**********************************************}
{   THighLowLineSeries                         }
{                                              }
{   Copyright (c) 2006-2020 by Steema Software }
{**********************************************}
unit TeeHighLowLine;
{$I TeeDefs.inc}

interface

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

  {$IFDEF FMX}
  FMX.Types,
  {$ELSE}
  Graphics, Controls, Forms, Dialogs, StdCtrls,
  {$ENDIF}

  {$IFDEF D6}
  Types,
  {$ENDIF}

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

type
  THighLowLineSeries=class(TBaseHighLowSeries)
  private
    FHighPointer : TSeriesPointer;
    FLowPointer  : TSeriesPointer;

    procedure SetHighPointer(const Value: TSeriesPointer);
    procedure SetLowPointer(const Value: TSeriesPointer);
    procedure ChangePen(Sender: TObject);
  protected
    Procedure AssignFormatting(Source:TChartSeries); override;
    Procedure CalcHorizMargins(Var LeftMargin,RightMargin:Integer); override;
    Procedure CalcVerticalMargins(Var TopMargin,BottomMargin:Integer); override;
    Procedure DrawValue(ValueIndex:Integer); override;
    class Function GetEditorClass:String; override;
    Procedure PrepareForGallery; override;
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
    Procedure SetSeriesColor(AColor:TColor); override;
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    function Clicked(x, y: Integer): Integer; override;
  published
    property HighPointer:TSeriesPointer read FHighPointer write SetHighPointer;
    property LowPointer:TSeriesPointer read FLowPointer write SetLowPointer;
  end;

implementation

uses
  Math,
  {$IFDEF FMX}
  FMXTee.Constants, FMXTee.ProConstants, FMXTee.Chart
  {$ELSE}
  TeeConst, TeeProCo, Chart
  {$ENDIF};

{ THighLowLineSeries }

Constructor THighLowLineSeries.Create(AOwner: TComponent);
begin
  inherited;
  FHighPointer:=TSeriesPointer.Create(Self);
  FLowPointer:=TSeriesPointer.Create(Self);

  Pen.OnChange:=ChangePen;
end;

Destructor THighLowLineSeries.Destroy;
begin
  FreeAndNil(FLowPointer);
  FreeAndNil(FHighPointer);
  inherited;
end;

procedure THighLowLineSeries.ChangePen(Sender: TObject);
begin
  Color:=TTeePen(Sender).Color;
end;

Procedure THighLowLineSeries.SetSeriesColor(AColor:TColor);
begin
  Pen.Color:=AColor;
  inherited;
end;

procedure THighLowLineSeries.AssignFormatting(Source: TChartSeries);
begin
  if Source is THighLowLineSeries then
  with THighLowLineSeries(Source) do
  begin
    Self.HighPointer:=FHighPointer;
    Self.LowPointer:=FLowPointer;
  end;

  inherited;
end;

function THighLowLineSeries.Clicked(x, y: Integer): Integer;

  function ClickedPointer(const P:TSeriesPointer; tmpX,tmpY:Integer):Boolean;
  begin
    result:=(P.Visible) and (P.Style<>psNothing) and
            (Abs(tmpX-X)<P.HorizSize) and
            (Abs(tmpY-Y)<P.VertSize);
  end;

var t  : Integer;
    xx : Integer;
    y0 : Integer;
    y1 : Integer;
begin
  result:=inherited Clicked(x,y);

  if (result=TeeNoPointClicked) and
     (FirstValueIndex>-1) and (LastValueIndex>-1) then
  begin
    for t:=FirstValueIndex to LastValueIndex do
    begin
      if t>=Count then exit;  // prevent re-entrancy if series is cleared.

      if not IsNull(t) then
      begin
        // Calculate coordinates
        xx:=CalcXPos(t);
        y0:=CalcYPos(t);
        y1:=CalcYPosValue(LowValues.Value[t]);

        if ClickedPointer(HighPointer,xx,y0) or
           ClickedPointer(LowPointer,xx,y0) or
           PointInLine(TeePoint(x,y),xx,y0,xx,y1) then
        begin
          result:=t;
          break;
        end;
      end;
    end;
  end;
end;

procedure THighLowLineSeries.DrawValue(ValueIndex: Integer);
var x,
    y0,
    y1 : Integer;
    tmp : TCanvas3D;
begin
  if not IsNull(ValueIndex) then
  begin
    // Calculate coordinates
    x:=CalcXPos(ValueIndex);
    y0:=CalcYPos(ValueIndex);
    y1:=CalcYPosValue(LowValues.Value[ValueIndex]);

    tmp:=ParentChart.Canvas;

    // Draw line
    if not PrepareHoverPen(ValueIndex) then
       tmp.AssignVisiblePenColor(Pen,ValueColor[ValueIndex]);

    if ParentChart.View3D then
       tmp.VertLine3D(x,y0,y1,StartZ)
    else
       tmp.DoVertLine(x,y0,y1);

    // Draw High
    if HighPointer.Visible then
    begin
      HighPointer.PrepareCanvas(tmp,Color);
      HighPointer.Draw(x,y0);
    end;

    // Draw Low
    if LowPointer.Visible then
    begin
      LowPointer.PrepareCanvas(tmp,Color);
      LowPointer.Draw(x,y1);
    end;
  end;
end;

Procedure THighLowLineSeries.CalcHorizMargins(Var LeftMargin,RightMargin:Integer);
var tmp : Integer;
begin
  inherited;

  if Pen.Visible then
     tmp:={$IFDEF FMX}Round{$ENDIF}(Pen.Width)
  else
     tmp:=0;

  if HighPointer.Visible and HighPointer.InflateMargins then
     tmp:=Max(tmp,HighPointer.HorizSize);

  if LowPointer.Visible and LowPointer.InflateMargins then
     tmp:=Max(tmp,LowPointer.HorizSize);

  LeftMargin:=Max(LeftMargin,tmp);
  RightMargin:=Max(RightMargin,tmp);
end;

Procedure THighLowLineSeries.CalcVerticalMargins(Var TopMargin,BottomMargin:Integer);
begin
  inherited;

  if HighPointer.Visible and HighPointer.InflateMargins then
     TopMargin:=Max(TopMargin,HighPointer.VertSize);

  if LowPointer.Visible and LowPointer.InflateMargins then
     BottomMargin:=Max(BottomMargin,LowPointer.VertSize);
end;

procedure THighLowLineSeries.SetHighPointer(const Value: TSeriesPointer);
begin
  FHighPointer.Assign(Value);
end;

procedure THighLowLineSeries.SetLowPointer(const Value: TSeriesPointer);
begin
  FLowPointer.Assign(Value);
end;

class function THighLowLineSeries.GetEditorClass: String;
begin
  result:='THighLowLineEditor'; // Do not localize
end;

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

  if Assigned(FHighPointer) then
     FHighPointer.ParentChart:=Value;

  if Assigned(FLowPointer) then
     FLowPointer.ParentChart:=Value;
end;

procedure THighLowLineSeries.PrepareForGallery;
begin
  inherited;
  HighPointer.Size:=2;
  LowPointer.Size:=2;
end;

initialization
  RegisterTeeSeries(THighLowLineSeries, @TeeMsg_GalleryHighLowLine,
                                    @TeeMsg_GalleryStats,1);
finalization
  UnRegisterTeeSeries( [THighLowLineSeries]);
end.
