{********************************************}
{    TeeChart. TBrushDialog                  }
{ Copyright (c) 1996-2020 by Steema Software }
{********************************************}
unit TeeBrushDlg;
{$I TeeDefs.inc}

interface

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

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

     Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls,

     {$IFDEF LCL}
     Buttons,
     {$ENDIF}

     TeCanvas, TeePenDlg, TeeEdiGrad, TeeProcs, TeeGalleryPanel;

type
  TBrushDialog = class(TForm)
    PanelOk: TPanel;
    ImageMetal: TImage;
    ImageWood: TImage;
    ImageStone: TImage;
    ImageClouds: TImage;
    ImageGrass: TImage;
    ImageFire: TImage;
    ImageSnow: TImage;
    BColor: TButtonColor;
    CBClear: TCheckBox;
    PageControl1: TPageControl;
    TabSolid: TTabSheet;
    TabGradient: TTabSheet;
    TabHatch: TTabSheet;
    TabTexture: TTabSheet;
    TabCustom: TTabSheet;
    LImage: TLabel;
    Button1: TButton;
    Image1: TImage;
    TextureGallery: TChartGalleryPanel;
    ImageCarbon: TImage;
    HatchGallery: TChartGalleryPanel;
    Panel1: TPanel;
    BColor2: TButtonColor;
    BBackColor: TButtonColor;
    Button4: TButton;
    CBTransp: TCheckBox;
    Panel2: TPanel;
    Button2: TButton;
    Button3: TButton;
    ImageCarbon2: TImage;
    ImagePaper: TImage;
    Label1: TLabel;
    TBTransp: TTrackBar;
    LTransp: TLabel;
    CBDefColor: TCheckBox;
    ImageMe: TImage;
    procedure FormShow(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure CBTranspClick(Sender: TObject);
    procedure BBackColorClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure BSwapClick(Sender: TObject);
    procedure BColorClick(Sender: TObject);
    procedure PageControl1Change(Sender: TObject);
    procedure HatchGalleryChangeChart(Sender: TObject);
    procedure TextureGalleryChangeChart(Sender: TObject);
    procedure PageControl1GetImageIndex(Sender: TObject; TabIndex: Integer;
      var ImageIndex: Integer);
    procedure TextureGallerySelectedChart(Sender: TObject);
    procedure CBClearClick(Sender: TObject);
    procedure TBTranspChange(Sender: TObject);
    procedure CBDefColorClick(Sender: TObject);
  private
    { Private declarations }
    FLinkedColor: TColorEvent;
    BackupBrush : TTeeBrush;
    FGradientEditor : TTeeGradientEditor;

    Procedure CheckImageButton;
    function GetInternalTexture(Index:Integer):TImage;
    procedure PrepareDefColor;
    procedure RedrawShape;
    procedure RefreshHatchGallery;
    procedure SetLTransp;
  public
    { Public declarations }
    TheBrush : TChartBrush;

    class function AddDialog(const ABrush:TChartBrush; const AParent:TWinControl):TBrushDialog;
    class function ImageGallery(const AOwner:TComponent; const AImage:TTeePicture):Boolean;
    procedure RefreshBrush(const ABrush:TChartBrush);

    property LinkedColor: TColorEvent read FLinkedColor write FLinkedColor;
  end;

Function EditChartBrush(const AOwner:TComponent;
                        const ChartBrush:TChartBrush):Boolean;

Function EditChartBrushLinkedColor(const AOwner:TComponent;
                          const ChartBrush:TChartBrush;
                          aLinkedColor:TColorEvent):Boolean;

Function EditTeeFont(const AOwner:TComponent; const AFont:TFont):Boolean;

Function TeeGetPictureFileName(const AOwner:TComponent):String;

procedure TeeLoadClearImage(const AOwner:TComponent; const AImage:TPicture);

Procedure GetTeeBrush(Index:Integer; const ABitmap:TBitmap);

implementation

{$IFNDEF LCL}
{$R *.DFM}
{$ELSE}
{$R *.lfm}
{$ENDIF}

{$R TeeBrushes.res}

Uses
  ExtDlgs, TeEngine, TeeConst;

Function EditTeeFont(const AOwner:TComponent; const AFont:TFont):Boolean;
Begin
  result:=False;

  With TFontDialog.Create(AOwner) do
  try
    Font.Assign(AFont);

    if Execute then
    begin
      AFont.Assign(Font);
      result:=True;
    end;
  finally
    Free;
  end;
end;

Function EditChartBrush(const AOwner:TComponent;
                        const ChartBrush:TChartBrush):Boolean;
var tmp : TBrushDialog;
Begin
  tmp:=TeeCreateForm(TBrushDialog,AOwner) as TBrushDialog;

  with tmp do
  try
    TheBrush:=ChartBrush;
    TeeTranslateControl(tmp);
    result:=ShowModal=mrOk;
  finally
    Free;
  end;
end;

Function EditChartBrushLinkedColor(const AOwner:TComponent;
                          const ChartBrush:TChartBrush;
                          aLinkedColor:TColorEvent):Boolean;
var tmp : TBrushDialog;
Begin
  tmp:=TeeCreateForm(TBrushDialog,AOwner) as TBrushDialog;

  with tmp do
  try
    LinkedColor:=aLinkedColor;
    TheBrush:=ChartBrush;
    TeeTranslateControl(tmp);
    result:=ShowModal=mrOk;
  finally
    Free;
  end;
end;

Function TeeGetPictureFileName(const AOwner:TComponent):String;
begin
  result:='';

  With TOpenPictureDialog.Create(AOwner) do
  try
    if Execute then
       result:=FileName;
  finally
    Free;
  end;
end;

procedure TeeLoadClearImage(const AOwner:TComponent; const AImage:TPicture);
var tmp : String;
begin
  if Assigned(AImage.Graphic) then AImage.Assign(nil)
  else
  begin
    tmp:=TeeGetPictureFileName(AOwner);

    if tmp<>'' then
       AImage.LoadFromFile(tmp);
  end;
end;

{ TBrushDialog }
procedure TBrushDialog.RedrawShape;
begin
  BColor.Enabled:=TheBrush.Style<>bsClear;
  BBackColor.Enabled:=BColor.Enabled;

  CBTransp.Checked:=TheBrush.BackColor=clNone;
  CBTransp.Enabled:=not CBTransp.Checked;

  if Assigned(FLinkedColor) then  //force repaint where link enabled (eg. TAreaSeries)
     FLinkedColor(TheBrush.BackColor);
end;

procedure TBrushDialog.FormShow(Sender: TObject);
begin
  if not Assigned(BackupBrush) then
     BackupBrush:=TTeeBrush.Create(nil);

  if Assigned(TheBrush) then
  begin
    BackupBrush.Assign(TheBrush);
    RefreshBrush(TheBrush);
  end;
end;

class function TBrushDialog.AddDialog(const ABrush:TChartBrush; const AParent:TWinControl):TBrushDialog;
begin
  result:=TBrushDialog.Create(AParent.Owner);
  result.Align:=alClient;
  result.TheBrush:=ABrush;
  result.PanelOk.Visible:=False;

  AddFormTo(result,AParent);
  TeeTranslateControl(result);
end;

type
  TBrushAccess=class(TTeeBrush);

procedure TBrushDialog.RefreshBrush(const ABrush:TChartBrush);

  function SamePicture(const A,B:{$IFDEF LCL}TCustomBitmap{$ELSE}TBitmap{$ENDIF}):Boolean;
  var x,y : Integer;
  begin
    result:=(A.Width=B.Width) and (A.Height=B.Height);

    if result then
       for y:=0 to A.Height-1 do
           for x:=0 to A.Width-1 do
               if A.Canvas.Pixels[x,y]<>B.Canvas.Pixels[x,y] then
               begin
                 result:=False;
                 Exit;
               end;
  end;

  function IsHatch:Boolean;
  var t : Integer;
      tmp : TBitmap;
  begin
    result:=(TheBrush.Style<>bsSolid) and (TheBrush.Style<>bsClear);

    if (not result) and Assigned(TheBrush.Bitmap) then
    begin
      tmp:=TBitmap.Create;

      for t:=0 to 11 do
      begin
        GetTeeBrush(t,tmp);

        if SamePicture(TheBrush.Bitmap,tmp) then
        begin
          result:=True;
          break;
        end;
      end;

      tmp.Free;
    end;
  end;

begin
  TheBrush:=ABrush;

  PageControl1.Enabled:=Assigned(TheBrush);

  if Assigned(TheBrush) then
  begin
    if TBrushAccess(TheBrush).HasImage and Assigned(TheBrush.Image.Graphic) then
    begin
      if IsHatch then
         PageControl1.ActivePage:=TabHatch
      else
         PageControl1.ActivePage:=TabCustom
    end
    else
    if TheBrush.Gradient.Visible then
       PageControl1.ActivePage:=TabGradient
    else
       PageControl1.ActivePage:=TabSolid;

    PageControl1Change(Self);
    
    BColor.LinkProperty(TheBrush,'Color'); // Do not localize

    PrepareDefColor;

    BColor2.LinkProperty(TheBrush,'Color'); // Do not localize
    BBackColor.LinkProperty(TheBrush,'BackColor'); // Do not localize

    if TheBrush.Color=clTeeColor then
       TBTransp.Position:=0
    else
       TBTransp.Position:=TTeeCanvas.ColorAlpha(TheBrush.Color);

    SetLTransp;

    CBClear.Checked:=TheBrush.Style=bsClear;

    CheckImageButton;
    RedrawShape;
  end;
end;

procedure TBrushDialog.Button3Click(Sender: TObject);
begin
  if Assigned(TheBrush) then
     TheBrush.Assign(BackupBrush);

  ModalResult:=mrCancel;
end;

procedure TBrushDialog.Button1Click(Sender: TObject);
var tmp       : String;
    tmpBitmap : TBitmap;
begin
  if Button1.Tag=1 then
  begin
    TheBrush.Image.Assign(nil);
    TheBrush.Bitmap.Free;
    TheBrush.Bitmap:=nil;

    LImage.Caption:='';
  end
  else
  begin
    tmp:=TeeGetPictureFileName(Self);

    if tmp<>'' then
    begin
      TheBrush.Image.LoadFromFile(tmp);

      if not (TheBrush.Image.Graphic is TBitmap) then
      begin
        tmpBitmap:=TBitmap.Create;
        TeeSetBitmapSize(tmpBitmap,TheBrush.Image.Width,TheBrush.Image.Height);

        tmpBitmap.Canvas.Draw(0,0,TheBrush.Image.Graphic);

        TheBrush.Image.Bitmap:=tmpBitmap;
      end;
    end;
  end;

  CheckImageButton;
end;

Procedure TBrushDialog.CheckImageButton;

  procedure SetImage(Source:TPersistent);
  begin
    Button1.Tag:=1;
    Button1.Caption:=TeeMsg_ClearImage;
    Image1.Picture.Assign(Source);

    if Source is TPicture then
    with (Source as TPicture).Graphic do
       LImage.Caption:=IntToStr(Width)+'x'+IntToStr(Height)
    else
    if Source is TBitmap then
    with (Source as TBitmap) do
       LImage.Caption:=IntToStr(Width)+'x'+IntToStr(Height)
  end;

begin
  if Assigned(TheBrush.Image.Graphic) then
     SetImage(TheBrush.Image)
  else
  if Assigned(TheBrush.Bitmap) then
     SetImage(TheBrush.Bitmap)
  else
  begin
    Button1.Tag:=0;
    Button1.Caption:=TeeMsg_BrowseImage;
    Image1.Picture:=nil;
    LImage.Caption:='';
  end;
end;

procedure TBrushDialog.FormCreate(Sender: TObject);
begin
  {$IFDEF D7}
  PageControl1.DoubleBuffered:=True;
  {$ENDIF}

  PageControl1.ActivePage:=TabSolid;
end;

procedure TBrushDialog.CBTranspClick(Sender: TObject);
begin
  if CBTransp.Checked then
  begin
    TheBrush.BackColor:=clNone;
    BBackColor.Invalidate;
  end;

  CBTransp.Enabled:=not CBTransp.Checked;
  RefreshHatchGallery;
end;

procedure TBrushDialog.RefreshHatchGallery;
var t : Integer;
    tmp : TTeeBrush;
begin
  for t:=0 to HatchGallery.Charts.Count-1 do
  begin
    tmp:=HatchGallery.Charts[t].Walls.Back.Brush;
    tmp.Color:=TheBrush.Color;
    tmp.BackColor:=TheBrush.BackColor;
  end;
end;

procedure TBrushDialog.BBackColorClick(Sender: TObject);
begin
  CBTransp.Checked:=False;
  CBTransp.Enabled:=True;

  RefreshHatchGallery;

  if Assigned(FLinkedColor) then
     FLinkedColor(TheBrush.BackColor);
end;

procedure TBrushDialog.BColorClick(Sender: TObject);
begin
  PrepareDefColor;
  TheBrush.Style:=bsSolid;
  RedrawShape;
  RefreshHatchGallery;
end;

const
  Tee_InternalTextures=11;

  BrushNames:Array[0..18+Tee_InternalTextures-1] of String=(
    'Horizontal',
    'Vertical',
    'Diagonal',
    'Back Diagonal',
    'Cross',
    'Diagonal Cross',
    'Fill 80%',
    'Fill 60%',
    'Fill 40%',
    'Fill 20%',
    'Fill 10%',
    'Zig zag',
    'Vertical small',
    'Horiz. small',
    'Diag. small',
    'Back Diag. small',
    'Cross small',
    'Diag. Cross small',
    'Metal',
    'Wood',
    'Stone',
    'Clouds',
    'Grass',
    'Fire',
    'Snow',
    'Carbon',
    'Carbon 2',
    'Paper',
    'Me'
  );

function TBrushDialog.GetInternalTexture(Index:Integer):TImage;
begin
  case Index of
    0: result:=ImageMetal;
    1: result:=ImageWood;
    2: result:=ImageStone;
    3: result:=ImageClouds;
    4: result:=ImageGrass;
    5: result:=ImageFire;
    6: result:=ImageSnow;
    7: result:=ImageCarbon;
    8: result:=ImageCarbon2;
    9: result:=ImagePaper;
  else
    result:=ImageMe;
  end;
end;

Procedure GetTeeBrush(Index:Integer; const ABitmap:TBitmap);

  function BrushFileName:String;
  begin
    result:='TeeBrush'+TeeStr(1+Index); // Do not localize
  end;

begin
  with ABitmap do
  begin
    LoadFromResourceName(HInstance,BrushFileName);
    TransparentMode:=tmFixed;
    TransparentColor:=clWhite;
  end;
end;

procedure TBrushDialog.FormDestroy(Sender: TObject);
begin
  BackupBrush.Free;
end;

procedure TBrushDialog.BSwapClick(Sender: TObject);
var tmp : TColor;
begin
  with TheBrush do
  begin
    tmp:=Color;
    Color:=BackColor;
    BackColor:=tmp;

    if Assigned(LinkedColor) then
       LinkedColor(BackColor);
  end;

  BColor.Invalidate;
  BBackColor.Invalidate;

  RefreshHatchGallery;
end;

procedure TBrushDialog.PageControl1Change(Sender: TObject);

  procedure CreateDefaultTexture(Index:Integer; const ATitle:String);
  var tmp : TGalleryChart;
  begin
    tmp:=TGalleryChart.Create(nil);
    tmp.View3D:=False;
    tmp.Walls.Back.Transparent:=False;
    tmp.Axes.Hide;

    tmp.Walls.Back.Brush.Image.Graphic:=GetInternalTexture(Index).Picture.Graphic;

    tmp.Hint:=ATitle;
    tmp.Title.Caption:=tmp.Hint;

    TextureGallery.Charts.Add(tmp);
    tmp.Parent:=TextureGallery;
    TextureGallery.FinishAdd(tmp);
  end;

var tmp : TGalleryChart;
    t : Integer;
begin
  if PageControl1.ActivePage=TabSolid then
  else
  if (PageControl1.ActivePage=TabHatch) and (HatchGallery.Charts.Count=0) then
  begin
    for t:=0 to 15 do
    begin
      tmp:=TGalleryChart.Create(nil);
      tmp.View3D:=False;
      tmp.Walls.Back.Transparent:=False;
      tmp.Axes.Hide;

      tmp.Walls.Back.Brush.Color:=TheBrush.Color;
      tmp.Walls.Back.Brush.BackColor:=TheBrush.BackColor;

      if t<5 then
         tmp.Walls.Back.Brush.Style:=TBrushStyle(t+2)
      else
         GetTeeBrush(t-5,tmp.Walls.Back.Brush.Image.Bitmap);

      tmp.Hint:=BrushNames[t];
      tmp.Title.Caption:=tmp.Hint;

      HatchGallery.Charts.Add(tmp);
      tmp.Parent:=HatchGallery;
      HatchGallery.FinishAdd(tmp);
    end;

    HatchGallery.ResizeCharts;
  end
  else
  if (PageControl1.ActivePage=TabTexture) and (TextureGallery.Charts.Count=0) then
  begin
    for t:=0 to Tee_InternalTextures-1 do
        CreateDefaultTexture(t,BrushNames[19+t-1]);

    // GetFilesFromDir(GalleryPath+'\Images')

    TextureGallery.ResizeCharts;
  end
  else
  if PageControl1.ActivePage=TabGradient then
     if Assigned(FGradientEditor) then
        FGradientEditor.RefreshGradient(TheBrush.Gradient)
     else
        FGradientEditor:=TTeeGradientEditor.InsertForm(TheBrush.Gradient,TabGradient);
end;

procedure TBrushDialog.HatchGalleryChangeChart(Sender: TObject);
var tmpBitmap : TBitmap;
    tmpIndex : Integer;
begin
  tmpIndex:=HatchGallery.Charts.IndexOf(HatchGallery.SelectedChart);

  if tmpIndex>=5 then
  begin
    tmpBitmap:=TBitmap.Create;
    try
      GetTeeBrush(tmpIndex-5,tmpBitmap);
      TheBrush.Image.Graphic:=tmpBitmap;
    finally
      tmpBitmap.Free;
    end;
  end
  else
  begin
    TheBrush.Style:=TBrushStyle(tmpIndex+2);
    TheBrush.Image:=nil;
  end;

  CheckImageButton;
  RedrawShape;
end;

procedure TBrushDialog.TextureGalleryChangeChart(Sender: TObject);
begin
  if Assigned(TheBrush) then
  begin
    TheBrush.Image.Graphic:=TextureGallery.SelectedChart.Walls.Back.Brush.Image.Graphic;

    CheckImageButton;
    RedrawShape;
  end;
end;

class function TBrushDialog.ImageGallery(const AOwner: TComponent;
  const AImage: TTeePicture): Boolean;
begin
  with TBrushDialog.Create(AOwner) do
  try
    TabSolid.TabVisible:=False;
    TabGradient.TabVisible:=False;
    TabHatch.TabVisible:=False;
    TabCustom.TabVisible:=False;

    PageControl1Change(nil);

    result:=ShowModal=mrOk;

    if result then
       AImage.Graphic:=TextureGallery.SelectedChart.Walls.Back.Brush.Image.Graphic;
  finally
    Free;
  end;
end;

procedure TBrushDialog.PageControl1GetImageIndex(Sender: TObject;
  TabIndex: Integer; var ImageIndex: Integer);
begin
  // Dummy
end;

procedure TBrushDialog.TextureGallerySelectedChart(Sender: TObject);
begin
  if PanelOk.Visible then
     ModalResult:=mrOk;
end;

procedure TBrushDialog.CBClearClick(Sender: TObject);
begin
  if CBClear.Checked then
     TheBrush.Style:=bsClear
  else
     TheBrush.Style:=bsSolid;

  PrepareDefColor;
end;

procedure TBrushDialog.SetLTransp;
const HundredInv255=100.0/255.0;
begin
  LTransp.Caption:=TeeStr(Round(TBTransp.Position*HundredInv255))+'%';
end;

procedure TBrushDialog.TBTranspChange(Sender: TObject);
begin
  BColor.SymbolColor:=TTeeCanvas.ColorFrom(TheBrush.Color,TBTransp.Position);
  SetLTransp;
  PrepareDefColor;
end;

procedure TBrushDialog.PrepareDefColor;
begin
  CBDefColor.Checked:=TheBrush.Color=clTeeColor;
  CBDefColor.Enabled:=not CBDefColor.Checked;

  BColor.Enabled:=TheBrush.Style<>bsClear;
end;

procedure TBrushDialog.CBDefColorClick(Sender: TObject);
begin
  if CBDefColor.Checked then
  begin
    TheBrush.Color:=clTeeColor;
    BColor.Invalidate;
    CBDefColor.Enabled:=False;
  end;
end;

end.
