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

interface

uses {$IFNDEF LINUX}
     {$IFDEF LCL}
     LCLIntf,
     {$ELSE}
     Windows, Messages,
     {$ENDIF}
     {$ENDIF}

     Graphics, Controls, StdCtrls, ExtCtrls, ComCtrls, Forms, Buttons,

     SysUtils, Classes,
     {$IFDEF D6}
     StrUtils,
     {$ENDIF}
     TeEngine, Chart, TeeGalleryPanel, TeeProcs, TeCanvas, TeeLisB,

     {$IFDEF MSWINDOWS}
     TeeGDIPlus,
     {$ENDIF}

     TeeFunci, TeePenDlg;

type
  TTeeGallery = class(TForm)
    P1: TPanel;
    CB3D: TCheckBox;
    TabPages: TTabControl;
    TabTypes: TPageControl;
    ChartGalleryPanel1: TChartGalleryPanel;
    CBSmooth: TCheckBox;
    CBPalette: TComboFlat;
    PanelButtons: TPanel;
    BOK: TButton;
    BCancel: TButton;
    ESearch: TEdit;
    TopPanel: TPanel;
    CBPage: TComboFlat;
    ChartListBox1: TChartListBox;
    Splitter1: TSplitter;
    ChartSubGallery: TChartGalleryPanel;
    TabFunctions: TTabSheet;
    TabSeries: TTabSheet;
    LBFuncs: TListBox;
    Splitter2: TSplitter;
    ChartFunctions: TChart;
    Panel2: TPanel;
    CBFuncPage: TComboFlat;
    LData: TLabel;
    TBData: TTrackBar;
    LPeriod: TLabel;
    EPeriod: TEdit;
    CBPeriod: TUpDown;
    SpeedButton1: TSpeedButton;
    TabTemplates: TTabSheet;
    Templates: TChartGalleryPanel;
    procedure CB3DClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure TabPagesChange(Sender: TObject);
    procedure TabTypesChange(Sender: TObject);
    procedure ChartGalleryPanel1SelectedChart(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure CBSmoothClick(Sender: TObject);
    procedure CBPaletteChange(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ESearchClick(Sender: TObject);
    procedure ESearchChange(Sender: TObject);
    procedure CBPageChange(Sender: TObject);
    procedure ChartListBox1Click(Sender: TObject);
    procedure LBFuncsClick(Sender: TObject);
    procedure EPeriodChange(Sender: TObject);
    procedure TBDataChange(Sender: TObject);
    procedure CBFuncPageChange(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure ChartSubGallerySelectedChart(Sender: TObject);
    procedure LBFuncsDblClick(Sender: TObject);
    procedure TabTypesGetImageIndex(Sender: TObject; TabIndex: Integer;
      var ImageIndex: Integer);
    procedure BOKClick(Sender: TObject);
  private
    { Private declarations }

    IHideFunctions: Boolean;
    InFunctions   : Boolean;
    OnlyFunctions : Boolean;
    IChart        : TChart;
    OldClass      : TChartSeriesClass;

    IOldSeries    : TChartSeries;
    ITargetChart  : TCustomChart;
    IFunctionChart: TCustomChart;

    FSelectedFunction : TNotifyEvent;

    procedure AddFunctionDemo(const AType:TTeeSeriesType);
    procedure AddFunctions(const APage:String);
    procedure AddSampleData(const AFunction:TTeeFunction; const ASeries:TChartSeries; ANum:Integer);
    Procedure CreateChartList(Const APage:String);
    function CurrentFunction:TTeeFunction;
    Procedure FillGalleryPages(const AItems:TStrings);
    procedure GenerateTemplates;

    function GetSeriesClass(out AClass:TChartSeriesClass;
                            out AFunction:TTeeFunctionClass;
                            out ASubIndex:Integer):Boolean;

    class procedure LoadTemplateChart(const Dest,Source:TCustomChart);
    procedure PrepareGallery(const AGallery:TChartGalleryPanel);
    function SelectedFunction:TTeeSeriesType;
    function SelectedPaletteIndex:Integer;
    procedure SetPalette(Index:Integer);
    procedure ShowSubGallery(Sender:TObject);
    procedure SwitchAlternate;
  public
    { Public declarations }
    IGalleryWidth  : Integer;
    IGalleryHeight : Integer;

    AlternateStyle : Boolean;

    Procedure HideButtons;
    Procedure HideFunctions;

    class function AddFunctionGallery(const AOwner:TComponent; const AChart:TCustomChart;
                  const Selected:TNotifyEvent):TTeeGallery;
  end;

  TTeeTemplateClass=class of TTeeTemplate;
  TTeeTemplate=class
  private
    Templates : TChartGalleryPanel;
  protected
    Owner : TComponent;
    function NewTemplate(const ATitle:String):TGalleryChart;
  public
    procedure Add; virtual;
  end;

var
  TeeTemplateClass:TTeeTemplateClass=TTeeTemplate;

{ Shows the gallery and asks the user a Series type.
  If user double clicks a chart or presses Ok, a new Series
  is created and returned. The new Series is owned by AOwner
  parameter (usually the Form).
}
Function CreateNewSeriesGallery( const AOwner:TComponent;
                                 const OldSeries:TChartSeries;
                                 const tmpChart:TCustomAxisPanel;
                                 ShowFunctions,
                                 CheckSeries:Boolean;
                                 var SubIndex: Integer;
                                 AGalleryWidth: Integer=0;
                                 AGalleryHeight: Integer=0 ):TChartSeries; overload;

// Same as above but much simpler to use:
Function CreateNewSeriesGallery( const AOwner:TComponent;
                                 const AChart:TCustomAxisPanel;
                                 AGalleryWidth: Integer=0;
                                 AGalleryHeight: Integer=0 ):TChartSeries; overload;

{ Shows the gallery and asks the user a Series type. Then
  changes tmpSeries to the new type. }
procedure ChangeSeriesTypeGallery(const AOwner:TComponent; Var tmpSeries:TChartSeries);

{ DEPRECATED.

  Shows the Gallery Dialog and lets the user choose a Series type.
  Returns True if user pressed OK.
  The "tmpClass" parameter returns the choosen type.

Function GetChartGalleryClass( const AOwner:TComponent;
                               const OldSeries:TChartSeries;
                               ShowFunctions:Boolean;
                               Var tmpClass:TChartSeriesClass;
                               Var Show3D:Boolean;
                               Var tmpFunctionClass:TTeeFunctionClass;
                               CheckValidSeries:Boolean;
                               Var SubIndex,
                                   ColorPaletteIndex : Integer;
                               AGalleryWidth: Integer=0;
                               AGalleryHeight: Integer=0 ):Boolean;
}

{ Shows the gallery and asks the user a Series type. Then
  changes all Series in AChart to the new type. }
procedure ChangeAllSeriesGallery( const AOwner:TComponent; const AChart:TCustomChart );

// Shows the gallery and asks the user a Function type, returns the selected
// function class or nil if the user pressed the Cancel button.
function GetChartGalleryFunction(const AOwner:TComponent):TTeeFunctionClass;

const
  TeeMsg_RememberGallerySize='RememberGallerySize'; // Do not localize
  TeeMsg_GalleryCols='GalleryColumns'; // Do not localize
  TeeDefaultGalleryCols=4;

implementation

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

uses
  TeeConst, Series,
  {$IFDEF TEEEXCEPTIONS}
  {$IFNDEF FPC}
  TeeException,
  {$ENDIF}
  {$ENDIF}
  Math, TeeEditCha;

{ TeeGallery }
Procedure TTeeGallery.HideFunctions;
begin
  if Showing then
     TabFunctions.TabVisible:=False;

  ChartGalleryPanel1.FunctionsVisible:=False;
  IHideFunctions:=True;
end;

Procedure TTeeGallery.HideButtons;
begin
  P1.Visible:=False;
end;

Procedure TTeeGallery.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^=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.Selected[t]:=True;
          break;
        end;

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

Procedure TTeeGallery.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^;

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

function TTeeGallery.SelectedPaletteIndex:Integer;
begin
  result:=CBPalette.ItemIndex;
end;

type
  TSeriesAccess=class(TChartSeries);

procedure TTeeGallery.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.Checked);
  end;
end;

procedure TTeeGallery.CBPaletteChange(Sender: TObject);
var t : Integer;
begin
  if CBPalette.ItemIndex>-1 then
  begin
    PrepareGallery(ChartGalleryPanel1);
    PrepareGallery(ChartSubGallery);
    PrepareGallery(Templates);

    ChartFunctions.ColorPaletteIndex:=SelectedPaletteIndex;

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

procedure TTeeGallery.CB3DClick(Sender: TObject);
begin
  ChartGalleryPanel1.View3D:=CB3D.Checked;
  ChartSubGallery.View3D:=CB3D.Checked;
  ChartFunctions.View3D:=CB3D.Checked;
  Templates.View3D:=CB3D.Checked;
end;

procedure TTeeGallery.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:=alLeft;

  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]);

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

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

    ChartSubGallery.Align:=alClient;
    ChartSubGallery.Visible:=True;

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

type
  TGalleryAccess=class(TChartGalleryPanel);

procedure TTeeGallery.FormShow(Sender: TObject);
begin
  if Assigned(IOldSeries) then
     TabTemplates.Visible:=False;

  ChartGalleryPanel1.NumCols:=TeeReadIntegerOption(TeeMsg_GalleryCols,TeeDefaultGalleryCols);

  SwitchAlternate;

  CBSmooth.Checked:=ChartGalleryPanel1.Smooth;

  if TabSeries.TabVisible then
     TabTypes.ActivePageIndex:=0;

  // This is to fix a "list index out of bounds" in XE VCL (see HideFunctions)
  // http://bugs.teechart.net/show_bug.cgi?id=781
  if Showing then
     if IHideFunctions then
     begin
       if TabFunctions.TabVisible then
          TabFunctions.TabVisible:=False;

       if TabTemplates.TabVisible then
          TabTemplates.TabVisible:=False;
     end;

  TabTypesChange(Self);

  {$IFNDEF LINUX}
  if (IGalleryWidth>0) and (IGalleryHeight>0) then
  begin
    Width:=Math.Max(100,IGalleryWidth);
    Height:=Math.Max(100,IGalleryHeight);
  end
  else
  {$ENDIF}
  if TeeReadBoolOption(TeeMsg_RememberGallerySize,True) then
     TPenDialog.LoadSize(Self,'Gallery');

  {$IFDEF TEELITE}
  ESearch.Visible:=False;
  {$ELSE}
  TeeTranslateControl(ESearch);
  {$ENDIF}
end;

procedure TTeeGallery.TabPagesChange(Sender: TObject);
begin
  With ChartGalleryPanel1 do
  begin
    FunctionsVisible:=OnlyFunctions or (TabTypes.ActivePageIndex=1);
    View3D:=CB3D.Checked;

    With TabPages do
    if TabIndex<>-1 then
       CreateGalleryPage(Tabs[TabIndex])
    else
       Charts.FreeAll;
  end;

  CBPaletteChange(Self);
end;

function TTeeTemplate.NewTemplate(const ATitle:String):TGalleryChart;
begin
  result:=TGalleryChart.Create(Owner);
  result.Title.Caption:=ATitle;

  Templates.Charts.Add(result);
  Templates.FinishAdd(result);

  result.Parent:=Templates;
end;

procedure TTeeTemplate.Add;
var c : TGalleryChart;
begin
  c:=NewTemplate(TeeMsg_TemplateStackedBars);
  c.AddSeries(TBarSeries).FillSampleValues(4);
  c.AddSeries(TBarSeries).FillSampleValues(4);
  c.AddSeries(TBarSeries).FillSampleValues(4);
  TBarSeries(c[0]).MultiBar:=mbStacked;

  c:=NewTemplate(TeeMsg_TemplateLineandPoint);
  c.AddSeries(TLineSeries).FillSampleValues(14);
  c.AddSeries(TPointSeries).FillSampleValues(14);

  c:=NewTemplate(TeeMsg_TemplateConcentricDonuts);
  c.AddSeries(TPieSeries).FillSampleValues(4);
  c.AddSeries(TPieSeries).FillSampleValues(4);
  c.AddSeries(TPieSeries).FillSampleValues(4);

  c[0].Marks.Hide;
  c[1].Marks.Hide;
  c[2].Marks.Hide;

  TPieSeries(c[0]).MultiPie:=mpConcentric;
  TPieSeries(c[1]).MultiPie:=mpConcentric;
  TPieSeries(c[2]).MultiPie:=mpConcentric;

  // Pareto
  c:=NewTemplate(TeeMsg_FunctionPareto);
  c.AddSeries(TBarSeries);

  // Set descending values order
  c[0].NotMandatoryValueList.Order:=loNone;
  c[0].MandatoryValueList.Order:=loDescending;

  c[0].FillSampleValues(8);

  // Add Pareto line
  c.AddSeries(TLineSeries);
  TLineSeries(c[1]).Pointer.Show;

  // Set Pareto Function
  c[1].DataSource:=c[0];
  c[1].FunctionType:=TParetoTeeFunction.Create(Owner);

  // Right Axis
  c[1].VertAxis:=aRightAxis;
  c.Axes.Right.AxisValuesFormat:=TeeMsg_DefPercentFormat;
end;

procedure TTeeGallery.GenerateTemplates;
begin
  with TeeTemplateClass.Create do
  try
    Owner:=Self;
    Templates:=Self.Templates;
    Add;
  finally
    Free;
  end;

  // Align all charts
  Templates.ResizeCharts;
end;

procedure TTeeGallery.AddSampleData(const AFunction:TTeeFunction; const ASeries:TChartSeries; ANum:Integer);
var tmp : Double;
    t   : Integer;
begin
  // Special case for Multiply function.
  // Add very small sample values to avoid multiplication to crash (Floating point Overflow)
  if AFunction is TMultiplyTeeFunction then
  begin
    ASeries.BeginUpdate;
    ASeries.Clear;

    tmp:=Random(1000);

    for t:=0 to ANum-1 do
    begin
      ASeries.Add(tmp*0.001);
      tmp:=tmp+Random(50)-25;
    end;

    ASeries.EndUpdate;
  end
  else
     ASeries.FillSampleValues(ANum);
end;

procedure TTeeGallery.AddFunctions(const APage:String);
var t : Integer;
begin
  LBFuncs.Items.BeginUpdate;
  LBFuncs.Clear;

  with TeeSeriesTypes do
  for t:=0 to Count-1 do
      with Items[t] do
      if NumGallerySeries>0 then
         if Assigned(FunctionClass) and ((APage='') or (GalleryPage^=APage)) then
            LBFuncs.Items.AddObject(Caption,TObject(t));

  LBFuncs.Sorted:=True;
  LBFuncs.Items.EndUpdate;
end;

procedure TTeeGallery.TabTypesChange(Sender: TObject);

  Function TabIndexOf(Const GalleryPage:String):Integer;
  begin
    result:=TabPages.Tabs.IndexOf(GalleryPage);
  end;

  Procedure AddTabPages;
  var t       : Integer;
      tmpPage : String;
  begin
    TabPages.Tabs.Clear;

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

        if TabPages.Tabs.IndexOf(tmpPage)=-1 then
           if not Assigned(FunctionClass) then
              TabPages.Tabs.Add(tmpPage);
      end;
  end;

var i,t : Integer;
begin
  InFunctions:=OnlyFunctions or (TabTypes.ActivePageIndex=1);

  CBSmooth.Visible:=not InFunctions;

  if InFunctions and (LBFuncs.Items.Count=0) then
  begin
    CBFuncPage.Items.Add(TeeMsg_All);

    with TeeSeriesTypes do
    for t:=0 to Count-1 do
        with Items[t] do
        if NumGallerySeries>0 then
           if Assigned(FunctionClass) then
              if CBFuncPage.Items.IndexOf(GalleryPage^)=-1 then
                 CBFuncPage.Items.Add(GalleryPage^);

    CBFuncPage.ItemIndex:=0; // All Functions by default. Was: CBFuncPage.Items.IndexOf(TeeMsg_GalleryStandard);
    CBFuncPageChange(Self);

    LBFuncs.ItemIndex:=LBFuncs.Items.IndexOf(TeeMsg_FunctionAverage);
    LBFuncsClick(Self);

    CB3D.Checked:=False;
  end
  else
  if (not InFunctions) and (TabTypes.ActivePageIndex=0) and (TabPages.Tabs.Count=0) then
  begin
    AddTabPages;

    i:=TabIndexOf(TeeMsg_GalleryStandard);
    if i>0 then
       TabPages.Tabs.Exchange(0,i);

    TabPages.TabIndex:=0;
    TabPagesChange(Self);
  end
  else
  if TabTypes.ActivePage=TabTemplates then
  begin
    if Templates.Charts.Count=0 then
       GenerateTemplates;
  end;
end;

procedure TTeeGallery.ChartGalleryPanel1SelectedChart(Sender: TObject);
begin
  ModalResult:=mrOk;
end;

procedure TTeeGallery.FormCreate(Sender: TObject);
begin
  IGalleryWidth:=0;
  IGalleryHeight:=0;

  CBPalette.Clear;
  TColorPalettes.AddTo(CBPalette.Items);

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

  TeeTranslateControl(Self);
end;

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

procedure TTeeGallery.SetPalette(Index:Integer);
begin
  CBPalette.ItemIndex:=Index;
end;

{ Helper functions }
Function GetChartGalleryClass( const AOwner:TComponent;
                               const OldSeries:TChartSeries;
                               ShowFunctions:Boolean;
                               Var tmpClass:TChartSeriesClass;
                               Var Show3D:Boolean;
                               Var tmpFunctionClass:TTeeFunctionClass;
                               CheckValidSeries:Boolean;
                               Var SubIndex,
                                   ColorPaletteIndex : Integer;
                               AGalleryWidth: Integer=0;
                               AGalleryHeight: Integer=0;
                               const Target:TCustomAxisPanel=nil ):Boolean;

  procedure DoShowGallery(IsAlternate:Boolean);
  var tmpGally : TTeeGallery;
  begin
    tmpGally:=TTeeGallery.Create(AOwner);

    with tmpGally do
    try
      AlternateStyle:=IsAlternate;

      IGalleryWidth:=AGalleryWidth;
      IGalleryHeight:=AGalleryHeight;
      PositionToCenter(tmpGally);

      CB3D.Checked:=Show3D;

      IOldSeries:=OldSeries;
      ChartGalleryPanel1.SelectedSeries:=IOldSeries;

      if not ShowFunctions then
         HideFunctions;

      if not Assigned(OldSeries) then
         if Target is TCustomChart then
            ITargetChart:=TCustomChart(Target);

      ChartGalleryPanel1.CheckSeries:=CheckValidSeries;

      SetPalette(ColorPaletteIndex);

      if ShowModal=mrOk then
      begin
        ColorPaletteIndex:=SelectedPaletteIndex;

        result:=GetSeriesClass(tmpClass,tmpFunctionClass,SubIndex);

        if result then
           Show3D:=CB3D.Checked and ChartGalleryPanel1.SelectedChart.View3D;
      end;

    finally
      Free;
    end;
  end;

  // Deprecated. See TChartGalleryPanel.DefaultMode property
  // procedure AlternateGallery;

begin
  result:=False;
  DoShowGallery(TChartGalleryPanel.DefaultMode<>0);
end;

Function CreateNewSeriesGallery( const AOwner:TComponent;
                                 const AChart:TCustomAxisPanel;
                                 AGalleryWidth: Integer=0;
                                 AGalleryHeight: Integer=0 ):TChartSeries;
var tmp : Integer;
begin
  result:=CreateNewSeriesGallery( AOwner, nil, AChart, True, False, tmp, AGalleryWidth, AGalleryHeight );
end;

type
  TCustomAxisPanelAccess=class(TCustomAxisPanel);

Function CreateNewSeriesGallery( const AOwner:TComponent;
                                 const OldSeries:TChartSeries;
                                 const tmpChart:TCustomAxisPanel;
                                 ShowFunctions,
                                 CheckSeries:Boolean;
                                 Var SubIndex : Integer;
                                 AGalleryWidth: Integer=0;
                                 AGalleryHeight: Integer=0  ):TChartSeries;

  function InternalShowGallery:TChartSeries;
  var Show3D   : Boolean;
      tmpClass : TChartSeriesClass;
      tmpFunctionClass : TTeeFunctionClass;
      tmpPalette : Integer;
      tmpOwner : TComponent;
  begin
    result:=nil;
    Show3D:=tmpChart.View3D;

    tmpPalette:=(tmpChart as TCustomChart).ColorPaletteIndex;

    //If we show the gallery from the chart editor we use its gallery size
    if (AOwner is TChartEditForm) and
       (TChartEditForm(AOwner).GalleryWidth>0) and
       (TChartEditForm(AOwner).GalleryHeight>0) then
    begin
      AGalleryWidth:=TChartEditForm(AOwner).GalleryWidth;
      AGalleryHeight:=TChartEditForm(AOwner).GalleryHeight;
    end;

    if GetChartGalleryClass(AOwner,OldSeries,ShowFunctions,tmpClass,Show3D,
                            tmpFunctionClass,CheckSeries,SubIndex,
                            tmpPalette, AGalleryWidth, AGalleryHeight,
                            tmpChart) then
    begin
      tmpChart.View3D:=Show3D;
      (tmpChart as TCustomChart).ColorPaletteIndex:=tmpPalette;

      if (not Assigned(OldSeries)) or
         (ShowFunctions or ((tmpClass<>OldSeries.ClassType) or (SubIndex<>-1))) then
      begin
        if Assigned(OldSeries) then tmpOwner:=OldSeries.Owner
                               else tmpOwner:=TCustomAxisPanelAccess(tmpChart).GetDesignOwner;

        result:=CreateNewSeries(tmpOwner,tmpChart,tmpClass,tmpFunctionClass);

        if SubIndex<>-1 then
           TSeriesAccess(result).SetSubGallery(result,SubIndex);
      end;
    end;
  end;

begin
  try
    result:=InternalShowGallery;
  except
    on E:Exception do
    begin
      {$IFDEF TEEEXCEPTIONS}
      if csDesigning in tmpChart.ComponentState then
         TTeeExceptionForm.Send('GALL',E);
      {$ENDIF}
      raise;
    end;
  end;
end;

procedure ChangeSeriesTypeGallery(const AOwner:TComponent; Var tmpSeries:TChartSeries);
var NewSeries : TChartSeries;
    SubIndex  : Integer;
begin
  NewSeries:=CreateNewSeriesGallery(AOwner,tmpSeries,tmpSeries.ParentChart,False,True,SubIndex);

  if Assigned(NewSeries) then
  begin
    AssignSeries(tmpSeries,NewSeries);
    tmpSeries:=NewSeries;

    if SubIndex<>-1 then
       TSeriesAccess(tmpSeries).SetSubGallery(tmpSeries,SubIndex);
  end;
end;

procedure ChangeAllSeriesGallery(const AOwner:TComponent; const AChart:TCustomChart );
var NewSeries : TChartSeries;
    tmpSeries : TChartSeries;
    SubIndex  : Integer;
begin
  if AChart.SeriesCount>0 then
  begin
    tmpSeries:=AChart.Series[0];
    NewSeries:=CreateNewSeriesGallery(AOwner,tmpSeries,AChart,False,True,SubIndex);

    if Assigned(NewSeries) then
    begin
      AssignSeries(tmpSeries,NewSeries);
      ChangeAllSeriesType(AChart,TChartSeriesClass(NewSeries.ClassType));
    end;
  end;
end;

function GetChartGalleryFunction(const AOwner:TComponent):TTeeFunctionClass;
var tmpGally : TTeeGallery;
    tmpClass : TChartSeriesClass;
    SubIndex : Integer;
begin
  result:=nil;

  tmpGally:=TTeeGallery.Create(AOwner);

  with tmpGally do
  try
    PositionToCenter(tmpGally);
    TabSeries.TabVisible:=False;
    OnlyFunctions:=True;

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

Function TeeAddGallery(const AOwner:TComponent; const Chart:TCustomChart; const Series:TChartSeries):TChartSeries;
var SubIndex : Integer;
begin
  if Assigned(Chart) then
     result:=CreateNewSeriesGallery(AOwner,Series,Chart,True,False,SubIndex)
  else
     result:=nil;
end;

Function TeeChangeGallery(const AOwner:TComponent; var Series: TChartSeries):TChartSeriesClass;
begin
  ChangeSeriesTypeGallery(AOwner,Series);
  result:=TChartSeriesClass(Series.ClassType);
end;

procedure TTeeGallery.CBSmoothClick(Sender: TObject);
begin
  ChartGalleryPanel1.Smooth:=CBSmooth.Checked;
  ChartGalleryPanel1.SaveSmooth(ChartGalleryPanel1.Smooth);
end;

procedure TTeeGallery.FormDestroy(Sender: TObject);
begin
  IChart.Free;

  if TeeReadBoolOption(TeeMsg_RememberGallerySize,True) and
     ((IGalleryWidth=0) or (IGalleryHeight=0)) then
       TPenDialog.SaveSize(Self,'Gallery');
end;

procedure TTeeGallery.ESearchClick(Sender: TObject);
begin
  if ESearch.Font.Color=clSilver then
  begin
    ESearch.Text:='';
    ESearch.Font.Color:=clWindowText;
  end;
end;

procedure TTeeGallery.ESearchChange(Sender: TObject);
var t   : Integer;
    tmp : String;
begin
  if Trim(ESearch.Text)='' then
  begin
    if TabSeries.Caption=TeeMsg_Search then
    begin
      TabTypes.ActivePageIndex:=0;
      TabTypesChange(Self);
      ESearch.SetFocus;
    end;
  end
  else
  begin
    if TabSeries.Caption<>TeeMsg_Search then
    begin
      TabPages.Tabs.Clear;
      TabSeries.Caption:=TeeMsg_Search;
    end;

    // Filter gallery
    with ChartGalleryPanel1 do
    begin
      Charts.FreeAll;

      tmp:=AnsiUpperCase(Trim(ESearch.Text));

      with TeeSeriesTypes do
      for t:=0 to Count-1 do
      begin
        with Items[t] do
        if NumGallerySeries>0 then
        begin
          if (InFunctions and Assigned(FunctionClass)) or
             ((not InFunctions) and (not Assigned(FunctionClass))) then
             {$IFDEF D6}
             if AnsiContainsStr(AnsiUpperCase(
                     {$IFNDEF TEESAFECLR}Description^{$ELSE}String(Items[t].Description){$ENDIF}
                     ),tmp) then
             {$ELSE}
             if Pos(tmp,UpperCase({$IFNDEF TEESAFECLR}Description^{$ELSE}String(Items[t].Description){$ENDIF}))>0 then
             {$ENDIF}
                CreateChart(Items[t]).ParentColor:=True;
        end;

        if Charts.Count>=NumRows*NumCols then
           break;  // Pending: scrollbar
      end;

      SelectedChart:=nil;
    end;

    CBPaletteChange(Self);

    //ESearch.SetFocus;
    //ESearch.SelLength:=0;
  end;
end;

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

procedure TTeeGallery.ChartListBox1Click(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);

          TGalleryAccess(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;

function TTeeGallery.CurrentFunction:TTeeFunction;
var t : Integer;
begin
  result:=nil;

  for t := 0 to ChartFunctions.SeriesCount-1 do
  begin
    result:=ChartFunctions[t].FunctionType;

    if Assigned(result) then
       break;
  end;
end;

function TTeeGallery.SelectedFunction:TTeeSeriesType;
begin
  result:=TeeSeriesTypes[Integer(LBFuncs.Items.Objects[LBFuncs.ItemIndex])];
end;

procedure TTeeGallery.LBFuncsClick(Sender: TObject);
begin
  if LBFuncs.ItemIndex<>-1 then
  begin
    ChartFunctions.Title.Caption:=LBFuncs.Items[LBFuncs.ItemIndex];
    AddFunctionDemo(SelectedFunction);

    CBPeriod.Position:=Round(CurrentFunction.Period);
  end;
end;

type
  TFunctionAccess=class(TTeeFunction);

procedure TTeeGallery.AddFunctionDemo(const AType:TTeeSeriesType);
var f : TTeeFunction;
    data : Integer;
    SourceSeries : TChartSeriesClass;
    tmpHasSource : Boolean;
    tmpAxes : TChartAxes;
    tmpDest : TFastLineSeries;
begin
  ChartFunctions.FreeAllSeries;

  f:=AType.FunctionClass.Create(Self);

  data:=TBData.Position;

  SourceSeries:=TFunctionAccess(f).GallerySource;

  tmpHasSource:=Assigned(SourceSeries);

  if not tmpHasSource then
     SourceSeries:=TFastLineSeries;

  AddSampleData(f,ChartFunctions.AddSeries(SourceSeries),Data);

  if ChartFunctions[0] is TFastLineSeries then
     TFastLineSeries(ChartFunctions[0]).DrawStyle:=flAll;

  ChartFunctions[0].Title:=TeeMsg_Data;

  ChartFunctions.Legend.Show;
  ChartFunctions.Axes.Left.EndPosition:=100;
  ChartFunctions.Axes.Right.StartPosition:=0;

  // Reset chart axes properties that might have changed by previous functions:
  tmpAxes:=ChartFunctions.Axes;

  tmpAxes.Automatic:=True;

  tmpAxes.Right.AxisValuesFormat:=TeeMsg_DefValueFormat;
  tmpAxes.Left.Title.Caption:='';
  tmpAxes.Right.Title.Caption:='';
  tmpAxes.Bottom.Title.Caption:='';
  tmpAxes.Left.Title.Angle:=90;

  // Function Series destination:
  tmpDest:=TFastLineSeries(ChartFunctions.AddSeries(TFastLineSeries));

  tmpDest.Pen.Width:=2;
  tmpDest.DrawStyle:=flAll;

  TFunctionAccess(f).PrepareForGallery(ChartFunctions);

  if not Assigned(f.ParentSeries) then
  begin
    ChartFunctions.AddSeries(TFastLineSeries).Pen.Width:=2;
    //tmp:=ChartFunctions.SeriesCount-1;
    ChartFunctions[1].FunctionType:=f;
    ChartFunctions[1].DataSource:=ChartFunctions[0];
  end;

  f.ParentSeries.GetVertAxis.Grid.Hide;

  CBPeriod.Enabled:=TFunctionAccess(f).CanUsePeriod;

  if CBPeriod.Visible then
     CBPeriod.Position:=Round(f.Period);


  f.ParentSeries.Title:=AType.Caption;
end;

procedure TTeeGallery.EPeriodChange(Sender: TObject);
var tmp : TTeeFunction;
begin
  tmp:=CurrentFunction;

  if Assigned(tmp) then
     tmp.Period:=CBPeriod.Position;
end;

procedure TTeeGallery.TBDataChange(Sender: TObject);
var t : Integer;
begin
  if CurrentFunction is TCustomTeeFunction then
  begin
    (CurrentFunction as TCustomTeeFunction).NumPoints:=TBData.Position;
    CurrentFunction.ReCalculate;
  end
  else
  for t:=0 to ChartFunctions.SeriesCount-2 do
      AddSampleData(CurrentFunction,ChartFunctions[t],TBData.Position);
end;

procedure TTeeGallery.CBFuncPageChange(Sender: TObject);
begin
  if CBFuncPage.ItemIndex=0 then
     AddFunctions('')
  else
     AddFunctions(CBFuncPage.CurrentItem);

  if LBFuncs.Items.Count>0 then
  begin
    LBFuncs.ItemIndex:=0;
    LBFuncsClick(Self);
  end;
end;

class procedure TTeeGallery.LoadTemplateChart(const Dest,Source:TCustomChart);
var tmp : String;
begin
  Source.AllowZoom:=Dest.AllowZoom;
  Source.AllowPanning:=Dest.AllowPanning;
  Source.Chart3DPercent:=Dest.Chart3DPercent;
  Source.ClipPoints:=Dest.ClipPoints;
  Source.Legend.Visible:=Dest.Legend.Visible;
  Source.View3D:=Dest.View3D;
  Source.View3DOptions:=Dest.View3DOptions;

  tmp:=Source.Title.Caption;
  Source.Title:=Dest.Title;
  Dest.Title.Caption:=tmp;

  CloneChart(Dest,Source,Dest.Owner,False);
end;

function TTeeGallery.GetSeriesClass(out AClass:TChartSeriesClass;
                                    out AFunction:TTeeFunctionClass;
                                    out ASubIndex:Integer):Boolean;
begin
  if TabTypes.ActivePage=TabTemplates then
  begin
    if Assigned(ITargetChart) and (Templates.SelectedChart<>nil) then
       LoadTemplateChart(ITargetChart,Templates.SelectedChart);

    result:=False;
  end
  else
  if TabTypes.ActivePage=TabFunctions then
  begin
    AClass:=TChartSeriesClass(ChartFunctions[ChartFunctions.SeriesCount-1].ClassType);
    AFunction:=TTeeFunctionClass(CurrentFunction.ClassType);
    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:=ChartGalleryPanel1.GetSeriesClass(AClass,AFunction,ASubIndex);
  end;
end;

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

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

procedure TTeeGallery.LBFuncsDblClick(Sender: TObject);
begin
  ModalResult:=mrOk;
end;

procedure TTeeGallery.TabTypesGetImageIndex(Sender: TObject;
  TabIndex: Integer; var ImageIndex: Integer);
begin
  // Empty event. D7 VCL TPageControl bug workaround
end;

class function TTeeGallery.AddFunctionGallery(const AOwner:TComponent; const AChart:TCustomChart;
                  const Selected:TNotifyEvent):TTeeGallery;
begin
  result:=TTeeGallery.Create(AOwner);

  result.BCancel.Hide;
  result.BOK.Caption:=TeeMsg_Apply;

  result.IFunctionChart:=AChart;
  result.FSelectedFunction:=Selected;

  result.TabSeries.TabVisible:=False;
  result.TabTemplates.TabVisible:=False;
  result.TabTypes.ActivePage:=result.TabFunctions;
  result.TabTypesChange(result);
end;

// When showing the Functions Gallery standalone, clicking the "Apply" button
// adds all demo series and functions to "IFunctionChart" chart.
// See AddFunctionGallery method.

procedure TTeeGallery.BOKClick(Sender: TObject);
var t : Integer;
begin
  if Assigned(IFunctionChart) then
  begin
    // Add current selected function demo series to Chart:

    for t:=0 to ChartFunctions.SeriesCount-1 do
    if ChartFunctions[t].Visible then
       CloneChartSeries(ChartFunctions[t],IFunctionChart.Owner,IFunctionChart);

    if Assigned(FSelectedFunction) then
       FSelectedFunction(Self);
  end;
end;

initialization
  TeeAddGalleryProc:=TeeAddGallery;
  TeeChangeGalleryProc:=TeeChangeGallery;
finalization
  TeeChangeGalleryProc:=nil;
  TeeAddGalleryProc:=nil;
end.

