{*********************************************}
{  TeeChart Charting Library for FireMonkey   }
{  Copyright (c) 2012-2020 by Steema Software }
{  All Rights Reserved                        }
{*********************************************}
unit FMXTee.Editor.Stroke;
{$I TeeDefs.inc}

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms,
  {$IFNDEF UPTOXE3}
  FMX.StdCtrls,
  {$ENDIF}
  {$IFDEF D21}
  FMX.SpinBox,
  {$ENDIF}
  System.UIConsts,
  FMX.Dialogs, FMX.Colors, FMXTee.Canvas, FMX.Edit, FMX.ListBox, FMX.TabControl,
  FMX.Layouts, FMXTee.Chart.GalleryPanel, FMXTee.Editor.Brush;

type
  TStrokeEditor = class(TForm)
    ComboColorBox1: TComboColorBox;
    CBVisible: TCheckBox;
    CBPenStyle: TComboBox;
    SolidItem: TListBoxItem;
    DashItem: TListBoxItem;
    DotItem: TListBoxItem;
    DashDotItem: TListBoxItem;
    DashDotDotItem: TListBoxItem;
    Panel1: TPanel;
    Button1: TButton;
    Label1: TLabel;
    SmallDotsItem: TListBoxItem;
    CBEnd: TComboBox;
    ListBoxItem1: TListBoxItem;
    ListBoxItem3: TListBoxItem;
    Label2: TLabel;
    Label3: TLabel;
    CBJoin: TComboBox;
    ListBoxItem4: TListBoxItem;
    ListBoxItem5: TListBoxItem;
    ListBoxItem6: TListBoxItem;
    TabControl1: TTabControl;
    TabFormat: TTabItem;
    TabStyle: TTabItem;
    TabItem3: TTabItem;
    TabFill: TTabItem;
    CBDefColor: TCheckBox;
    Panel2: TPanel;
    Styles: TChartGalleryPanel;
    Panel3: TLayout;
    Label4: TLabel;
    TBTransp: TTrackBar;
    TBWidth: TTrackBar;
    LWidth: TLabel;
    Button2: TButton;
    procedure ComboColorBox1Change(Sender: TObject);
    procedure CBVisibleChange(Sender: TObject);
    procedure CBPenStyleChange(Sender: TObject);
    procedure CBEndChange(Sender: TObject);
    procedure CBJoinChange(Sender: TObject);
    procedure CBDefColorChange(Sender: TObject);
    procedure TBTranspChange(Sender: TObject);
    procedure TabControl1Change(Sender: TObject);
    procedure StylesChangeChart(Sender: TObject);
    procedure TBWidthTracking(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    IBackup : TTeePen;

    ISelecting  : Boolean;
    IFillEditor : TBrushEditor;

    procedure PrepareDefColor;
    procedure RefreshTransparency;
    procedure SelectStyleGallery;
    procedure SetLabelWidth;
    procedure StyleChartDraw(Sender: TObject);
  protected
    Pen : TTeePen;

    IRefreshing : Boolean;

    function GetDefaultColor:TAlphaColor; virtual;
    procedure SetDefaultColor; virtual;
  public
    { Public declarations }
    class function Edit(const AOwner:TComponent; const APen:TTeePen):Boolean;
    procedure RefreshPen(const APen:TTeePen);

    procedure RepaintButtonColor;

    // Helper procs
    class procedure SetRepeatClick(const ASpinBox:TSpinBox; const AValue:Boolean);
    class function InsertForm(const APen:TTeePen; const AParent:TControl):TStrokeEditor;
  end;

function EmbeddForm(const AForm:TCommonCustomForm; const AParent:TFmxObject):TCommonCustomForm;

const
  TeeCursorPrefix='cr';

Function DeleteCursorPrefix(Const S:String):String;
procedure TeeFillCursors(const ACombo:TComboBox; const ACursor:TCursor);

function TeeInputQuery(const ATitle,ACaption:String; var AValue:String):Boolean;
function YesNo(const AMessage:String):Boolean;
function YesNoCancel(const AMessage:String):Integer;

implementation

{$R *.fmx}

uses
  {$IFDEF D24}
  FMX.Platform,
  {$ENDIF}
  FMXTee.Procs, FMXTee.Engine;

function EmbeddForm(const AForm:TCommonCustomForm; const AParent:TFmxObject):TCommonCustomForm;
begin
  result:=AForm;

  while AForm.ChildrenCount>0 do
        AForm.Children[0].Parent:=AParent;
end;

type
  TTempCursors=class
  private
    FCombo : TComboBox;
    procedure ProcGetCursors(const S: string);
  end;

Function DeleteCursorPrefix(const S:String):String;
begin
  result:=S;
  if Copy(result,1,2)=TeeCursorPrefix then Delete(result,1,2);
end;

procedure TTempCursors.ProcGetCursors(const S: string);
var tmp : TListBoxItem;
begin
  tmp:=TListBoxItem.Create(FCombo.Owner);
  tmp.Text:=DeleteCursorPrefix(S);
  FCombo.AddObject(tmp);
end;

procedure SetComboCursor(const ACombo:TComboBox; const ACursor:TCursor);
var tmpSt : String;
begin
  with ACombo do
  if CursorToIdent(ACursor,tmpSt) then
     ItemIndex:=ListBox.Items.IndexOf(DeleteCursorPrefix(tmpSt))
  else
     ItemIndex:=-1;
end;

procedure TeeFillCursors(const ACombo:TComboBox; const ACursor:TCursor);
var tmp : TTempCursors;
begin
  ACombo.BeginUpdate;
  ACombo.Clear;

  tmp:=TTempCursors.Create;
  try
    tmp.FCombo:=ACombo;
    GetCursorValues(tmp.ProcGetCursors);
    tmp.ProcGetCursors(TeeMsg_TeeHand);
  finally
    tmp.Free;
  end;

  ACombo.EndUpdate;

  SetComboCursor(ACombo,ACursor);
end;

function TeeInputQuery(const ATitle,ACaption:String; var AValue:String):Boolean;
{$IFDEF D24}
var DialogSvc: IFMXDialogServiceSync;
    tmp : Array[0..0] of String;
{$ENDIF}
begin
  {$IFDEF D24}
  if TPlatformServices.Current.SupportsPlatformService(IFMXDialogServiceSync, DialogSvc) then
  begin
    tmp[0]:=AValue;

    result:=DialogSvc.InputQuerySync(ATitle,ACaption,tmp);

    if result then
       AValue:=tmp[0];
  end
  else
     result:=False;

  {$ELSE}
  result:=InputQuery(ATitle,ACaption,AValue);
  {$ENDIF}
end;

function YesNoCancel(const AMessage:String):Integer;
{$IFDEF D24}
var DialogSvc: IFMXDialogServiceSync;
{$ENDIF}
begin
  {$IFDEF D24}
  if TPlatformServices.Current.SupportsPlatformService(IFMXDialogServiceSync, DialogSvc) then
     result:=DialogSvc.MessageDialogSync(AMessage,TMsgDlgType.mtConfirmation,mbYesNoCancel,TMsgDlgBtn.mbYes,-1)
  else
     result:=mrCancel;

  {$ELSE}
  result:=MessageDlg(AMessage, TMsgDlgType.mtConfirmation, mbYesNo, 0);
  {$ENDIF}
end;

function YesNo(const AMessage:String):Boolean;
{$IFDEF D24}
var DialogSvc: IFMXDialogServiceSync;
{$ENDIF}
begin
  {$IFDEF D24}
  if TPlatformServices.Current.SupportsPlatformService(IFMXDialogServiceSync, DialogSvc) then
     result:=DialogSvc.MessageDialogSync(AMessage,TMsgDlgType.mtConfirmation,mbYesNo,TMsgDlgBtn.mbYes,-1)=mrYes
  else
     result:=False;

  {$ELSE}
  result:=MessageDlg(AMessage, TMsgDlgType.mtConfirmation, mbYesNo, 0) = mrYes;
  {$ENDIF}
end;

class procedure TStrokeEditor.SetRepeatClick(const ASpinBox:TSpinBox; const AValue:Boolean);

  procedure SetButton(const AName:String);
  var B : TFmxObject;
  begin
    B := ASpinBox.FindStyleResource(AName);
    if Assigned(B) and (B is TCustomButton) then
       TCustomButton(B).RepeatClick:=AValue;
  end;

begin
  SetButton('minusbutton');
  SetButton('plusbutton');
end;

class function TStrokeEditor.InsertForm(const APen:TTeePen; const AParent:TControl):TStrokeEditor;
begin
  result:=TStrokeEditor.Create(AParent.Owner);
  result.RefreshPen(APen);
  result.Panel1.Visible:=False;
  EmbeddForm(result,AParent);
end;

procedure TStrokeEditor.SelectStyleGallery;
begin
  if Styles.Charts.Count>0 then
  begin
    ISelecting:=True;
    Styles.SelectChart(Styles.Charts[CBPenStyle.ItemIndex]);
    ISelecting:=False;
  end;
end;

procedure TStrokeEditor.StylesChangeChart(Sender: TObject);
begin
  if not ISelecting then
  begin
    CBPenStyle.ItemIndex:=Styles.Charts.IndexOf(Styles.SelectedChart);
    CBPenStyleChange(Self);
  end;
end;

procedure TStrokeEditor.StyleChartDraw(Sender: TObject);
var w,y : Single;
begin
  with Sender as TGalleryChart do
  begin
    Canvas.AssignVisiblePen(Walls.Back.Pen);
    y:=(ChartRect.Top+ChartRect.Bottom)*0.5;
    w:=ChartWidth*0.5;
    Canvas.DoHorizLine(w*0.5,w+(w*0.5),y);
  end;
end;

procedure TStrokeEditor.TabControl1Change(Sender: TObject);

  procedure CreateStylesGallery;
  var t : Integer;
      tmp : TGalleryChart;
  begin
    Styles.NumCols:=3;
    Styles.AspectRatio:=0.5;

    for t:=0 to CBPenStyle.Items.Count-1 do
    begin
      tmp:=TGalleryChart.Create(Styles);
      Styles.Charts.Add(tmp);
      tmp.Parent:=Styles;

      //tmp.Hint:=CBStyle.Items[t];
      tmp.ShowHint:=True;

      Styles.FinishAdd(tmp);
      tmp.OnEnter:=nil;

      tmp.Title.Caption:=CBPenStyle.Items[t];

      tmp.View3D:=False;
      tmp.Axes.Hide;
      tmp.Walls.Hide;

      tmp.Walls.Back.Pen.Visible:=True;

      if t=CBPenStyle.Items.Count-1 then
         tmp.Walls.Back.Pen.SmallDots:=True
      else
      begin
        tmp.Walls.Back.Pen.Width:=3;
        tmp.Walls.Back.Pen.Style:=TPenStyle(t);
      end;

      tmp.OnAfterDraw:=StyleChartDraw;
    end;

    Styles.ResizeCharts;

    Styles.SelectedChart:=Styles.Charts[CBPenStyle.ItemIndex];
  end;

begin
  if (TabControl1.ActiveTab=TabStyle) and (Styles.Charts.Count=0) then
     CreateStylesGallery
  else
  if (TabControl1.ActiveTab=TabFill) and (not Assigned(IFillEditor)) then
     IFillEditor:=TBrushEditor.InsertForm(Pen.Fill,TabFill)
  else
  if TabControl1.ActiveTab=TabFormat then
  begin
    TBTransp.Value:=255-TTeeCanvas.ColorAlpha(Pen.Color);
    ComboColorBox1.Color:=Pen.Color;
    PrepareDefColor;
  end;
end;

procedure TStrokeEditor.TBTranspChange(Sender: TObject);
begin
  if Assigned(Pen) then
     Pen.Color:=TTeeCanvas.ColorFrom(Pen.Color,255-Round(TBTransp.Value));
end;

procedure TStrokeEditor.SetLabelWidth;
begin
  LWidth.Text:=FormatFloat('0.##',Pen.Width);
end;

procedure TStrokeEditor.TBWidthTracking(Sender: TObject);
begin
  if Assigned(Pen) then
  begin
    Pen.Width:=TBWidth.Value;
    SetLabelWidth;
  end;
end;

procedure TStrokeEditor.CBVisibleChange(Sender: TObject);
begin
  Pen.Visible:=CBVisible.IsChecked;
end;

procedure TStrokeEditor.Button2Click(Sender: TObject);
begin
  Pen.Assign(IBackup);
end;

procedure TStrokeEditor.SetDefaultColor;
begin
  Pen.Color:=clTeeColor;
end;

procedure TStrokeEditor.CBDefColorChange(Sender: TObject);
begin
  if CBDefColor.IsChecked then
  begin
    if not IRefreshing then
       SetDefaultColor;

    ComboColorBox1.Color:=Pen.Color;
    CBDefColor.Enabled:=False;
  end;
end;

procedure TStrokeEditor.CBEndChange(Sender: TObject);
begin
  if Assigned(Pen) then
     if CBEnd.ItemIndex=0 then
        Pen.EndStyle:=TPenEndStyle.esRound
     else
        Pen.EndStyle:=TPenEndStyle.esFlat;
end;

procedure TStrokeEditor.CBJoinChange(Sender: TObject);
begin
  if Assigned(Pen) then
     Pen.JoinStyle:=TPenJoinStyle(CBJoin.ItemIndex);
end;

procedure TStrokeEditor.CBPenStyleChange(Sender: TObject);
begin
  if CBPenStyle.Selected=SmallDotsItem then
     Pen.SmallDots:=True
  else
  begin
    Pen.Style:=TPenStyle(CBPenStyle.ItemIndex);
    Pen.SmallDots:=False;
  end;

  SelectStyleGallery;
end;

procedure TStrokeEditor.RefreshTransparency;
begin
  TBTransp.Value:=255-TTeeCanvas.ColorAlpha(Pen.Color);
end;

procedure TStrokeEditor.ComboColorBox1Change(Sender: TObject);
begin
  Pen.Color:=ComboColorBox1.Color;
  RefreshTransparency;
  PrepareDefColor;
end;

procedure TStrokeEditor.RefreshPen(const APen:TTeePen);
begin
  IRefreshing:=True;

  Pen:=APen;
  IBackup.Assign(Pen);

  ComboColorBox1.Color:=Pen.Color;

  TBWidth.Value:=Pen.Width;
  SetLabelWidth;

  CBVisible.IsChecked:=Pen.Visible;

  if Pen.SmallDots then
     CBPenStyle.ItemIndex:=SmallDotsItem.Index
  else
     CBPenStyle.ItemIndex:=Ord(Pen.Style);

  if Pen.EndStyle=esRound then
     CBEnd.ItemIndex:=0
  else
     CBEnd.ItemIndex:=1;

  CBJoin.ItemIndex:=Ord(Pen.JoinStyle);

  RefreshTransparency;

  PrepareDefColor;
  IRefreshing:=False;
end;

function TStrokeEditor.GetDefaultColor:TAlphaColor;
begin
  result:=clTeeColor;
end;

procedure TStrokeEditor.PrepareDefColor;
begin
  CBDefColor.IsChecked:=Pen.Color=GetDefaultColor;
  CBDefColor.Enabled:=not CBDefColor.IsChecked;
end;

procedure TStrokeEditor.RepaintButtonColor;
begin
  ComboColorBox1.Repaint;
end;

class function TStrokeEditor.Edit(const AOwner: TComponent; const APen: TTeePen): Boolean;
begin
  with TStrokeEditor.Create(AOwner) do
  try
    RefreshPen(APen);

    {$IFDEF ANDROID}
    ShowModal(procedure(Value:TModalResult)
    begin
      //result:=Value=mrOk;
    end);

    result:=False;
    {$ELSE}
    result:=ShowModal=mrOk;
    {$ENDIF}
  finally
    Free;
  end;
end;

procedure TStrokeEditor.FormCreate(Sender: TObject);
begin
  IBackup:=TTeePen.Create;
end;

procedure TStrokeEditor.FormDestroy(Sender: TObject);
begin
  IBackup.Free;
end;

end.
