{**********************************************}
{   TeeChart PageNum Tool                      }
{   Copyright (c) 2000-2020 by Steema Software }
{**********************************************}
unit TeePageNumTool;
{$I TeeDefs.inc}

// This unit implements a Chart Tool example:

{
   TPageNumTool

      This tool derives from Annotation tool.
      It is used to display the current chart page number.

      The Chart.Pages.MaxPointsPerPage property should be bigger than zero,
      to divide a chart in pages.

      Can be useful both for screen and print / print preview.

      This tool is automatically used by the Chart Editor dialog at
      "Paging" tab, when this unit is used in your application.
}

interface

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

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

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

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

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

     {$ELSE}

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

     Graphics, Controls,
     {$ENDIF}

     {$IFDEF FMX}
     FMXTee.Canvas, FMXTee.Engine, FMXTee.Chart, FMXTee.Tools
     {$ELSE}
     TeCanvas, TeEngine, Chart, TeeTools
     {$ENDIF}
     ;

type
  TPageNumTool=class(TAnnotationTool)
  private
    FButtonPen   : TChartPen;
    FButtons     : Boolean;
    FButtonWidth : Integer;
    FDisabledBrush : TTeeBrush;
    FEnabledBrush  : TTeeBrush;
    FFormat        : String;

    function IsFormatStored: Boolean;
    procedure LeftTriangle(out ATriangle:TTrianglePoints); // Cannot be made a function: BCC32 D2007
    procedure RightTriangle(out ATriangle:TTrianglePoints); // Cannot be made a function: BCC32 D2007
    procedure SetButtons(Value:Boolean);
    procedure SetButtonWidth(Value:Integer);
    procedure SetDisabledButton(const Value: TTeeGradient);
    procedure SetEnabledButton(const Value: TTeeGradient);
    procedure SetFormat(const Value: String);
    procedure SetButtonPen(const Value: TChartPen);
    function GetDisabledButton: TTeeGradient;
    function GetEnabledButton: TTeeGradient;
    procedure SetDisabledBrush(const Value: TTeeBrush);
    procedure SetEnabledBrush(const Value: TTeeBrush);
  protected
    Procedure ChartMouseEvent( AEvent: TChartMouseEvent;
                               Button: TMouseButton;
                               Shift: TShiftState; X, Y: Integer); override;
    Procedure DoDrawText(const AParent:TCustomAxisPanel); override;
    class Function GetEditorClass:String; override;
    Function GetText:String; override;
    Function GetTextBounds(const Panel:TCustomAxisPanel; const ABounds:TRect;
                           out x,y:Integer):TRect; override;
    function GetXOffset:Integer; override;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;
    class Function Description:String; override;
    class Function LongDescription:String; override;
  published
    property ButtonPen:TChartPen read FButtonPen write SetButtonPen;
    property ButtonWidth:Integer read FButtonWidth write SetButtonWidth default 12;
    property DisabledBrush:TTeeBrush read FDisabledBrush write SetDisabledBrush;
    property DisabledButton:TTeeGradient read GetDisabledButton write SetDisabledButton;
    property EnabledBrush:TTeeBrush read FEnabledBrush write SetEnabledBrush;
    property EnabledButton:TTeeGradient read GetEnabledButton write SetEnabledButton;
    property Format:String read FFormat write SetFormat stored IsFormatStored;
    property ShowButtons:Boolean read FButtons write SetButtons default True;
    property TextAlignment default taCenter;
  end;

implementation

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

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

{ TPageNumTool }

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

  FFormat:=TeeMsg_PageOfPages;
  FButtonWidth:=12;
  FButtons:=True;

  FDisabledBrush:=TTeeBrush.Create(CanvasChanged);
  DisabledButton.Visible:=True;
  DisabledButton.StartColor:=clWhite;
  DisabledButton.EndColor:=clSilver;

  FEnabledBrush:=TTeeBrush.Create(CanvasChanged);
  EnabledButton.Visible:=True;
  EnabledButton.StartColor:=clBlack;
  EnabledButton.EndColor:=clSilver;

  FButtonPen:=CreateChartPen;

  TextAlignment:=taCenter;
end;

Function TPageNumTool.GetText:String;
begin
  with TCustomChart(ParentChart).Pages do
       result:= SysUtils.Format(Format,[Current,Count]);
end;

class function TPageNumTool.Description: String;
begin
  result:=TeeMsg_PageNumber;
end;

function TPageNumTool.IsFormatStored: Boolean;
begin
  result:=FFormat<>TeeMsg_PageOfPages;
end;

class function TPageNumTool.LongDescription: String;
begin
  result:=TeeMsg_PageNumberDesc;
end;

procedure TPageNumTool.SetFormat(const Value: String);
begin
  SetStringProperty(FFormat,Value);
end;

function TPageNumTool.GetXOffset:Integer;
begin
  result:=inherited GetXOffset;

  if FButtons then
     if Shape.CustomPosition or
       (Position=ppLeftTop) or (Position=ppLeftBottom) then

        case TextAlignment of
          taRightJustify: Dec(result,ButtonWidth+2+2);
                taCenter: ;
        else
          Inc(result,ButtonWidth+2+2)
        end
     else
        case TextAlignment of
          taLeftJustify: Inc(result,ButtonWidth+2+2);
                taCenter: ;
        else
          Dec(result,ButtonWidth+2+2)
        end
end;

function TPageNumTool.GetTextBounds(const Panel:TCustomAxisPanel; const ABounds:TRect;
                                    out x,y: Integer): TRect;
begin
  result:=inherited GetTextBounds(Panel,ABounds,x,y);

  if AutoSize and FButtons then
     if Shape.CustomPosition or
       (Position=ppLeftTop) or (Position=ppLeftBottom) then
        result.Right:=result.Right+ (2*ButtonWidth+2*4)
     else
        result.Left:=result.Left- (2*ButtonWidth+2*4);

  x:=GetXOffset;

  Shape.ShapeBounds:=result;
end;

procedure TPageNumTool.DoDrawText(const AParent:TCustomAxisPanel);

  procedure DrawPolygon(const P:TTrianglePoints; IsEnabled:Boolean);
  begin
    if IsEnabled then
       AParent.Canvas.AssignBrush(EnabledBrush)
    else
       AParent.Canvas.AssignBrush(DisabledBrush);

    AParent.Canvas.Polygon(P);
  end;

var tmp : TTrianglePoints;
begin
  inherited;

  if FButtons then
  begin
    AParent.Canvas.AssignVisiblePen(ButtonPen);

    LeftTriangle(tmp);
    DrawPolygon(tmp,AParent.Pages.Current>1);

    RightTriangle(tmp);
    DrawPolygon(tmp,AParent.Pages.Current<AParent.Pages.Count);
  end;
end;

procedure TPageNumTool.LeftTriangle(out ATriangle:TTrianglePoints);
var tmpX,
    tmpY : TCoordinate;
begin
  with Shape.ShapeBounds do
  begin
    tmpY:=(Top+Bottom) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};
    tmpX:=Left+3;
  end;

  ATriangle[0]:=TeePoint(tmpX,tmpY);
  ATriangle[1]:=TeePoint(tmpX+ButtonWidth,Shape.ShapeBounds.Top+4);
  ATriangle[2]:=TeePoint(tmpX+ButtonWidth,Shape.ShapeBounds.Bottom-4);
end;

procedure TPageNumTool.RightTriangle(out ATriangle:TTrianglePoints);
var tmpX,
    tmpY : TCoordinate;
begin
  with Shape.ShapeBounds do
  begin
    tmpY:=(Top+Bottom) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};
    tmpX:=Right-3-1;
  end;

  ATriangle[0]:=TeePoint(tmpX,tmpY);
  ATriangle[1]:=TeePoint(tmpX-ButtonWidth,Shape.ShapeBounds.Top+4);
  ATriangle[2]:=TeePoint(tmpX-ButtonWidth,Shape.ShapeBounds.Bottom-4);
end;

procedure TPageNumTool.ChartMouseEvent(AEvent: TChartMouseEvent;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var P : TPoint;
    PLeft,
    PRight : TTrianglePoints;
begin
  if FButtons then
  begin
    P.X:=X;
    P.Y:=Y;

    if (AEvent=cmeDown) and (Button={$IFDEF FMX}TMouseButton.{$ENDIF}mbLeft) then
    begin
      LeftTriangle(PLeft);

      if PointInPolygon(P,PLeft) then
      begin
        ParentChart.Pages.PreviousPage;
        Exit;
      end
      else
      begin
        RightTriangle(PRight);

        if PointInPolygon(P,PRight) then
        begin
          ParentChart.Pages.NextPage;
          Exit;
        end;
      end;
    end
    else
    if AEvent=cmeMove then
    begin
      LeftTriangle(PLeft);
      RightTriangle(PRight);

      if ((ParentChart.Pages.Current>1) and PointInPolygon(P,PLeft)) or
         ((ParentChart.Pages.Current<ParentChart.Pages.Count) and PointInPolygon(P,PRight)) then
      begin
        ParentChart.Cursor:=crHandPoint;
        ParentChart.CancelMouse:=True;
        Exit;
      end;
    end;
  end;

  inherited;
end;

procedure TPageNumTool.SetButtons(Value: Boolean);
begin
  SetBooleanProperty(FButtons,Value);
end;

procedure TPageNumTool.SetButtonWidth(Value:Integer);
begin
  SetIntegerProperty(FButtonWidth,Value);
end;

class function TPageNumTool.GetEditorClass: String;
begin
  result:='TPageNumToolEdit';
end;

procedure TPageNumTool.SetDisabledBrush(const Value: TTeeBrush);
begin
  FDisabledBrush.Assign(Value);
end;

procedure TPageNumTool.SetEnabledBrush(const Value: TTeeBrush);
begin
  FEnabledBrush.Assign(Value);
end;

procedure TPageNumTool.SetDisabledButton(const Value: TTeeGradient);
begin
  DisabledButton.Assign(Value);
end;

procedure TPageNumTool.SetEnabledButton(const Value: TTeeGradient);
begin
  EnabledButton.Assign(Value);
end;

Destructor TPageNumTool.Destroy;
begin
  FButtonPen.Free;
  FEnabledBrush.Free;
  FDisabledBrush.Free;

  inherited;
end;

procedure TPageNumTool.SetButtonPen(const Value: TChartPen);
begin
  FButtonPen.Assign(Value);
end;

procedure TPageNumTool.Assign(Source: TPersistent);
begin
  if Source is TPageNumTool then
  with TPageNumTool(Source) do
  begin
    Self.FButtonWidth:=FButtonWidth;
    Self.ButtonPen:=ButtonPen;
    Self.EnabledButton:=EnabledButton;
    Self.DisabledButton:=DisabledButton;
    Self.Format:=Format;
    Self.FButtons:=FButtons;
  end;

  inherited;
end;

function TPageNumTool.GetDisabledButton: TTeeGradient;
begin
  result:=FDisabledBrush.Gradient;
end;

function TPageNumTool.GetEnabledButton: TTeeGradient;
begin
  result:=FEnabledBrush.Gradient;
end;

initialization
  RegisterTeeTools([TPageNumTool]);
  TeePageNumToolClass:=TPageNumTool;
finalization
  TeePageNumToolClass:=nil;
  UnRegisterTeeTools([TPageNumTool]);
end.
