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

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Generics.Collections, 
  System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs,
  {$IFNDEF UPTOXE3}
  FMX.StdCtrls,
  {$ENDIF}
  {$IFDEF D21}
  FMX.ComboTrackBar,
  {$ENDIF}
  FMXTee.Chart.GalleryPanel, FMXTee.Engine, FMX.ListBox, FMX.TabControl,
  FMX.Layouts, FMX.Edit, FMXTee.Editor.Functions.Gallery, FMXTee.Chart.ListBox,
  FMXTee.Chart;

type
  TChartGallery = class(TForm)
    Panel1: TPanel;
    BOK: TButton;
    BCancel: TButton;
    CB3D: TCheckBox;
    CBPalette: TComboBox;
    TabTypes: TTabControl;
    TabSeries: TTabItem;
    TabFunctions: TTabItem;
    TabPages: TTabControl;
    PanelButtons: TPanel;
    CBCount: TComboTrackBar;
    TopPanel: TLayout;
    CBPage: TComboBox;
    SpeedButton1: TSpeedButton;
    ChartListBox1: TChartListBox;
    Splitter1: TSplitter;
    ChartSubGallery: TChartGalleryPanel;
    procedure CB3DChange(Sender: TObject);
    procedure BOKClick(Sender: TObject);
    procedure BCancelClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure CBPaletteChange(Sender: TObject);
    procedure TabTypesChange(Sender: TObject);
    procedure CBCountChangeTracking(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure TabPagesChange(Sender: TObject);
    procedure ChartListBox1Change(Sender: TObject);
    procedure CBPageChange(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure ChartSubGallerySelectedChart(Sender: TObject);
  private
    { Private declarations }
    FEnableCount,
    OnlyFunctions : Boolean;
    FOnChangeChart : TNotifyEvent;
    OldClass : TChartSeriesClass;

    IChanging : Boolean;
    IChart : TChart;
    IFuncGallery : TFunctionsGallery;

    Procedure AddTabPages;
    procedure ChangedChart(Sender:TObject);
    Procedure CreateChartList(Const APage:String);
    Procedure FillGalleryPages(const AItems:TStrings);
    function GetSeriesClass(out AClass:TChartSeriesClass;
                                    out AFunction:TTeeFunctionClass;
                                    out ASubIndex:Integer):Boolean;
    procedure PrepareGallery(const AGallery:TChartGalleryPanel);
    function SelectedPaletteIndex:Integer;
    procedure ShowSubGallery(Sender:TObject);
    procedure SwitchAlternate;
  protected
    AlternateStyle : Boolean;
  public
    { Public declarations }

    function Gallery:TChartGalleryPanel;

    class function ChangeSeries(const AOwner:TComponent; const ASeries:TChartSeries):TChartSeriesClass;

    class function ChooseFunction(const AOwner:TComponent):TTeeFunctionClass;

    class function ChooseSeries(const AOwner:TComponent;
                                out SubIndex:Integer;
                                DefaultPalette:Integer=-1):TChartSeriesClass; overload;

    class function ChooseSeries(const AOwner:TComponent;
                                out FunctionType:TTeeFunctionClass;
                                out SubIndex:Integer;
                                DefaultPalette:Integer=-1):TChartSeriesClass; overload;

    class function ChooseSeries(const AOwner:TComponent;
                                out FunctionType:TTeeFunctionClass;
                                out SeriesCount:Integer;
                                out SubIndex:Integer;
                                DefaultPalette:Integer=-1;
                                AEnableCount:Boolean=True):TChartSeriesClass; overload;

    property OnChangeChart:TNotifyEvent read FOnChangeChart write FOnChangeChart;
  end;

implementation

{$R *.fmx}

uses
  FMXTee.Constants, FMXTee.Editor.Stroke;

procedure TChartGallery.ChangedChart(Sender:TObject);
begin
  if Assigned(FOnChangeChart) then
     FOnChangeChart(Sender)
end;

{
function TChartGallery.GalleryGetResult(var FunctionType:TTeeFunctionClass):TChartSeriesClass;
var tmp : TChartGalleryPanel;
begin
  tmp:=Gallery;

  FunctionType:=nil;

  if Assigned(tmp) then
     tmp.GetSeriesClass(result)
  else
  begin
    result:=nil;

    if Assigned(IFuncGallery) then
       FunctionType:=IFuncGallery.SelectedFunctionClass;
  end;
end;
}

function TChartGallery.GetSeriesClass(out AClass:TChartSeriesClass;
                                    out AFunction:TTeeFunctionClass;
                                    out ASubIndex:Integer):Boolean;
begin
  if TabTypes.ActiveTab=TabFunctions then
  begin
    AClass:=TChartSeriesClass(IFuncGallery.ChartFunctions[IFuncGallery.ChartFunctions.SeriesCount-1].ClassType);
    AFunction:=IFuncGallery.SelectedFunctionClass;
    ASubIndex:=0;
    result:=True;
  end
  else
  begin
    if AlternateStyle then
    begin
      if Assigned(ChartListBox1.SelectedSeries) then
      begin
        AClass:=TChartSeriesClass(ChartListBox1.SelectedSeries.ClassType);
        ASubIndex:=ChartSubGallery.Charts.IndexOf(ChartSubGallery.SelectedChart);
        result:=True;
      end
      else
        result:=False;

      AFunction:=nil;
    end
    else
      result:=Gallery.GetSeriesClass(AClass,AFunction,ASubIndex);
  end;
end;

procedure TChartGallery.BOKClick(Sender: TObject);
begin
  ModalResult:=mrOk;
end;

procedure TChartGallery.BCancelClick(Sender: TObject);
begin
  ModalResult:=mrCancel;
end;

procedure TChartGallery.CB3DChange(Sender: TObject);

  procedure DoTabs(const AControl:TTabControl);
  var t: Integer;
  begin
    for t := 0 to AControl.TabCount-1 do
        TChartGalleryPanel(AControl.Tabs[t].TagObject).View3D:=CB3D.IsChecked;
  end;

begin
  if Assigned(TabPages) then 
     DoTabs(TabPages);

  if Assigned(IFuncGallery) then
     IFuncGallery.ChartFunctions.View3D:=CB3D.IsChecked;

  ChartSubGallery.View3D:=CB3D.IsChecked;
end;

function TChartGallery.Gallery:TChartGalleryPanel;
begin
  if TabTypes.TabIndex=0 then
     result:=TChartGalleryPanel(TabPages.Tabs[TabPages.TabIndex].TagObject)
  else
     result:=nil;
end;

class function TChartGallery.ChangeSeries(const AOwner: TComponent;
                                   const ASeries: TChartSeries): TChartSeriesClass;
var sub : Integer;
    tmpIndex : Integer;
    tmpFunc : TTeeFunctionClass;
begin
  with TChartGallery.Create(AOwner) do
  try
    if Assigned(ASeries) then
    begin
      tmpIndex:=(ASeries.ParentChart as TCustomChart).ColorPaletteIndex;

      if tmpIndex<>-1 then
         CBPalette.ItemIndex:=tmpIndex;

      CB3D.IsChecked:=ASeries.ParentChart.View3D;

      Gallery.SelectedSeries:=ASeries;
    end;

    if ShowModal=mrOk then
       GetSeriesClass(result,tmpFunc,sub)
    else
       result:=nil;
  finally
    Free;
  end;
end;

type
  TSeriesAccess=class(TChartSeries);

procedure TChartGallery.PrepareGallery(const AGallery:TChartGalleryPanel);
var t : Integer;
begin
  with AGallery do
  for t:=0 to Charts.Count-1 do
  begin
    Charts[t].ColorPaletteIndex:=SelectedPaletteIndex;
    TSeriesAccess(Charts[t].Series[0]).GalleryChanged3D(CB3D.IsChecked);
  end;
end;

type
  TGalleryAccess=class(TChartGalleryPanel);

procedure TChartGallery.ChartListBox1Change(Sender: TObject);

  Procedure CreateGallery(AClass:TChartSeriesClass);
  begin
    With TGalleryAccess(ChartSubGallery) do
    begin
      //NumRows:=3;
      DisplaySub:=False;
      tmpG:=ChartSubGallery;
      tmpType:=TTeeSeriesType.Create;

      try
        tmpType.SeriesClass:=AClass;
        tmpType.NumGallerySeries:=1;
        tmpType.FunctionClass:=nil;

        FreeAllCharts;

        SelectedChart:=nil;
        tmpSeries:=tmpType.SeriesClass.Create(nil);

        try
          TSeriesAccess(tmpSeries).CreateSubGallery(TGalleryAccess(ChartSubGallery).CreateSubChart);

          // Increase number of rows if necessary:
          //if Charts.Count>(NumRows*NumCols) then
          //   NumRows:=1+(Charts.Count div NumCols);

          {$IFNDEF CLR}TGalleryAccess{$ENDIF}(ChartSubGallery).ShowSelectedChart;
        finally
          tmpSeries.Free;
        end;
      finally
        tmpType.Free;
      end;
    end;
  end;

var AClass : TChartSeriesClass;
begin
  if Assigned(ChartListBox1.SelectedSeries) then
  begin
    AClass:=TChartSeriesClass(ChartListBox1.SelectedSeries.ClassType);

    if OldClass<>AClass then
    begin
      CreateGallery(AClass);
      PrepareGallery(ChartSubGallery);

      OldClass:=AClass;

      with ChartListBox1 do
      if CanFocus then
         SetFocus;
    end;
  end;
end;

procedure TChartGallery.ChartSubGallerySelectedChart(Sender: TObject);
begin
  ModalResult:=mrOk;
end;

class function TChartGallery.ChooseFunction(const AOwner: TComponent): TTeeFunctionClass;
var tmpGally : TChartGallery;
    tmpClass : TChartSeriesClass;
    SubIndex : Integer;
begin
  result:=nil;

  tmpGally:=TChartGallery.Create(AOwner);

  with tmpGally do
  try
    TabSeries.Free;

    OnlyFunctions:=True;

    TabTypesChange(tmpGally);

    if ShowModal=mrOk then
       GetSeriesClass(tmpClass,result,SubIndex);
  finally
    Free;
  end;
end;

class function TChartGallery.ChooseSeries(const AOwner:TComponent;
                              out SubIndex:Integer;
                              DefaultPalette:Integer=-1):TChartSeriesClass;
var tmp : Integer;
    tmpFunc : TTeeFunctionClass;
begin
  result:=ChooseSeries(AOwner,tmpFunc,tmp,SubIndex,DefaultPalette,False);
end;

class function TChartGallery.ChooseSeries(const AOwner:TComponent;
                              out FunctionType:TTeeFunctionClass;
                              out SubIndex:Integer;
                              DefaultPalette:Integer=-1):TChartSeriesClass;
var tmp : Integer;
begin
  result:=ChooseSeries(AOwner,FunctionType,tmp,SubIndex,DefaultPalette,False);
end;

class function TChartGallery.ChooseSeries(const AOwner: TComponent;
                              out FunctionType:TTeeFunctionClass;
                              out SeriesCount:Integer;
                              out SubIndex:Integer;
                              DefaultPalette:Integer=-1;
                              AEnableCount:Boolean=True): TChartSeriesClass;
begin
  FunctionType:=nil;

  with TChartGallery.Create(AOwner) do
  try
    if DefaultPalette<>-1 then
       CBPalette.ItemIndex:=DefaultPalette;

    FEnableCount:=AEnableCount;
    SeriesCount:=1;

    if ShowModal=mrOk then
    begin
      GetSeriesClass(result,FunctionType,SubIndex);
      SeriesCount:=Round(CBCount.Value);
    end
    else
       result:=nil;
  finally
    Free;
  end;
end;

procedure TChartGallery.CBCountChangeTracking(Sender: TObject);
begin
  CBCount.Value:=Round(CBCount.Value);
  CBCount.Text:=IntToStr(Round(CBCount.Value));
end;

procedure TChartGallery.CBPageChange(Sender: TObject);
begin
  if CBPage.ItemIndex=0 then
     CreateChartList('')
  else
     CreateChartList(CBPage.Selected.Text);
end;

procedure TChartGallery.CBPaletteChange(Sender: TObject);

  procedure DoChart(const AChart:TCustomChart);
  var t : Integer;
  begin
    AChart.ColorPaletteIndex:=SelectedPaletteIndex;

    for t:= 0 to AChart.SeriesCount-1 do
        TSeriesAccess(AChart[t]).GalleryChanged3D(CB3D.IsChecked);
  end;

  procedure DoGallery(const AGallery:TChartGalleryPanel);
  var t : Integer;
  begin
    if Assigned(AGallery) then
    for t:=0 to AGallery.Charts.Count-1 do
        DoChart(AGallery.Charts[t]);
  end;

  procedure DoTabs(const AControl:TTabControl);
  var t : Integer;
  begin
    for t := 0 to AControl.TabCount-1 do
        DoGallery(TChartGalleryPanel(AControl.Tabs[t].TagObject));
  end;

begin
  if CBPalette.ItemIndex>-1 then
  begin
    DoTabs(TabPages);

    if Assigned(IFuncGallery) then
       DoChart(IFuncGallery.ChartFunctions);

    DoGallery(ChartSubGallery);
  end;
end;

function TChartGallery.SelectedPaletteIndex:Integer;
begin
  result:=CBPalette.Selected.Tag;
end;

procedure TChartGallery.SpeedButton1Click(Sender: TObject);
begin
  SpeedButton1.IsPressed:=not SpeedButton1.IsPressed;
  AlternateStyle:=False;
  SwitchAlternate;
end;

procedure TChartGallery.TabPagesChange(Sender: TObject);
var tmp : TChartGalleryPanel;
begin
  if (not IChanging) and Assigned(TabPages.ActiveTab) then
  begin
    tmp:=TChartGalleryPanel(TabPages.ActiveTab.TagObject);

    if tmp.Charts.Count=0 then
       tmp.CreateGalleryPage(TabPages.ActiveTab.Text);
  end;

  CBPaletteChange(Self);
end;

procedure TChartGallery.TabTypesChange(Sender: TObject);
begin
  if TabTypes.TabIndex=0 then
  begin
    if TabPages.TabCount=0 then
    begin
      AddTabPages;

      if TabPages.TabCount>0 then
      begin
        TabPages.TabIndex:=0;
        TabPagesChange(Self); // <-- needed for XE9
      end;
    end;
  end
  else
  if not Assigned(IFuncGallery) then
  begin
    IFuncGallery:=TFunctionsGallery.Create(Self);
    IFuncGallery.PanelButtons.Visible:=False;
    EmbeddForm(IFuncGallery,TabFunctions);

    CB3D.IsChecked:=False;
  end;
end;

Procedure TChartGallery.AddTabPages;

  Function TabIndexOf(Const GalleryPage:String):Integer;
  var t : Integer;
      tmp : TTabControl;
  begin
    tmp:=TabPages;

    for t:=0 to tmp.TabCount-1 do
        if tmp.Tabs[t].Text=GalleryPage then
           Exit(t);

    result:=-1;
  end;

  procedure AddTab(AParent:TTabControl; const ACaption:String);
  var tmp : TTabItem;
      tmpGallery : TChartGalleryPanel;
  begin
    tmp:=TTabItem.Create(Self);
    tmp.Text:=ACaption;

    AParent.AddObject(tmp);

    tmpGallery:=TChartGalleryPanel.Create(Self);
    tmpGallery.Align:=TAlignLayout.{$IFDEF D20}Client{$ELSE}alClient{$ENDIF};
    tmpGallery.OnChangeChart:=ChangedChart;
    tmpGallery.OnSelectedChart:=BOKClick;

    tmpGallery.DisplaySub:=True;
    tmpGallery.OnShowSubGallery:=ShowSubGallery;

    tmp.AddObject(tmpGallery);

    tmp.TagObject:=tmpGallery;

    with tmpGallery do
    begin
      FunctionsVisible:=OnlyFunctions or (TabTypes.ActiveTab=TabFunctions);
      View3D:=CB3D.IsChecked;
    end;
  end;

var t       : Integer;
    tmpPage : String;
begin
  IChanging:=True;

  with TeeSeriesTypes do
  for t:=0 to Count-1 do
  with Items[t] do
    if NumGallerySeries>0 then
    begin
      tmpPage:=GalleryPage^;

      if TabIndexOf(tmpPage)=-1 then

        {
        if OnlyFunctions then
        begin
          if Assigned(FunctionClass) then
             AddTab(TabFuncPages, tmpPage);
        end
        else
        }
        Case TabTypes.TabIndex of
         0: if not Assigned(FunctionClass) then AddTab(TabPages, tmpPage);
         //1: if Assigned(FunctionClass) then AddTab(TabFuncPages, tmpPage);
        end;
    end;

  IChanging:=False;
end;

procedure TChartGallery.FormCreate(Sender: TObject);

  procedure ColorPalettesAddTo(ACombo:TComboBox);
  var t : Integer;
      tmp : TListBoxItem;
  begin
    for t:=0 to ColorPalettes.Count-1 do
    begin
      tmp:=TListBoxItem.Create(Self);
      tmp.Tag:=t;
      tmp.Text:=ColorPalettes[t];
      ACombo.AddObject(tmp);
    end;
  end;

begin
  {$IFDEF D19}
  Position:=TFormPosition.{$IFDEF D20}MainFormCenter{$ELSE}poMainFormCenter{$ENDIF};
  {$ENDIF};

  // Fix for XE7:
  CBCount.Value:=1;
  CBCount.Min:=1;

  CBPalette.Clear;
  ColorPalettesAddTo(CBPalette);
  CBPalette.ItemIndex:=13;
//  CBPalette.Sorted:=True;

  if TabTypes.ActiveTab=TabSeries then
     TabTypesChange(Self)
  else
     TabTypes.ActiveTab:=TabSeries;

  TopPanel.Visible:=False;
  ChartListBox1.Visible:=False;

  ChartListBox1.ShowSeriesColor:=False;
  ChartListBox1.ShowActiveCheck:=False;

  Splitter1.Visible:=False;
end;

procedure TChartGallery.ShowSubGallery(Sender:TObject);
begin
  AlternateStyle:=True;
  SwitchAlternate;
end;

procedure TChartGallery.FormResize(Sender: TObject);
begin
  CBCount.Visible:=(Width>644) and FEnableCount;
end;

Procedure TChartGallery.CreateChartList(Const APage:String);
var t   : Integer;
    Old : String;
begin
  if Assigned(ChartListBox1.SelectedSeries) then
     Old:=ChartListBox1.SelectedSeries.ClassName
  else
     Old:='';

  ChartListBox1.Items.BeginUpdate;
  try
    IChart.FreeAllSeries;

    // Add all series classes that belong to APage group:

    with TeeSeriesTypes do
    for t:=0 to Count-1 do
        with Items[t] do
        if FunctionClass=nil then
        if (APage='') or (GalleryPage{$IFNDEF CLR}^{$ENDIF}=APage) then
           if SubIndex=0 then
              IChart.AddSeries(SeriesClass).Title:=TeeSeriesTypes.Find(SeriesClass).Caption;

    // Try to select again the old selected series class:

    if Old<>'' then
    for t:=0 to ChartListBox1.Items.Count-1 do
        if ChartListBox1.Series[t].ClassName=Old then
        begin
          ChartListBox1.ItemByIndex(t).IsSelected:=True;
          break;
        end;

    ChartListBox1Change(Self);
  finally
    ChartListBox1.Items.EndUpdate;
  end;
end;

procedure TChartGallery.SwitchAlternate;

  function FindSeriesList(ASeries:TChartSeries):TChartSeries;
  var t : Integer;
  begin
    result:=nil;

    for t:=0 to ChartListBox1.Items.Count-1 do
    if ChartListBox1.Items.Objects[t].ClassType=ASeries.ClassType then
    begin
      result:=TChartSeries(ChartListBox1.Items.Objects[t]);
      break;
    end;
  end;

begin
  TopPanel.Visible:=AlternateStyle;
  ChartListBox1.Visible:=AlternateStyle;
  Splitter1.Visible:=AlternateStyle;

  if Splitter1.Visible then
     Splitter1.Align:=TAlignLayout.{$IFDEF D20}Left{$ELSE}alLeft{$ENDIF};

  TabTypes.Visible:=not AlternateStyle;
  //ESearch.Visible:=not AlternateStyle;

  if AlternateStyle then
  begin
    if not Assigned(IChart) then
    begin
      IChart:=TChart.Create(Self);
      ChartListBox1.Chart:=IChart;
      ChartListBox1.Sorted:=True;

      FillGalleryPages(CBPage.Items);

      CBPage.Items.Insert(0,TeeMsg_All);
    end;

    if TabPages.TabIndex=-1 then
       CBPage.ItemIndex:=CBPage.Items.IndexOf(TeeMsg_GalleryStandard)
    else
       CBPage.ItemIndex:=CBPage.Items.IndexOf(TabPages.Tabs[TabPages.TabIndex].Text);

    // Do this here, to not re-translate ChartListBox1...
    CBPageChange(Self);

    if Assigned(Gallery.SelectedChart) then
    begin
      ChartListBox1.SelectedSeries:=FindSeriesList(Gallery.SelectedChart[0]);
      ChartListBox1Change(Self);
    end;

    ChartSubGallery.Align:=TAlignLayout.{$IFDEF D20}Client{$ELSE}alClient{$ENDIF};
    ChartSubGallery.Visible:=True;

    ChartSubGallery.ResizeCharts;
  end
  else
  begin
    ChartSubGallery.Visible:=False;
  end;
end;

Procedure TChartGallery.FillGalleryPages(const AItems:TStrings);
var t : Integer;
    s : String;
begin
  With TeeSeriesTypes do
  for t:=0 to Count-1 do
  begin
    s:=Items[t].GalleryPage{$IFNDEF CLR}^{$ENDIF};

    if AItems.IndexOf(s)=-1 then
       if Items[t].FunctionClass=nil then
          AItems.Add(s);
  end;
end;

end.
