{********************************************}
{ TeeChart Pro visual Themes                 }
{ Copyright (c) 2003-2020 by Steema Software }
{ All Rights Reserved                        }
{********************************************}
unit TeeThemes;
{$I TeeDefs.inc}

interface

uses {$IFNDEF LINUX}
     {$IFDEF MSWINDOWS}
     Windows,
     {$ENDIF}
     {$ENDIF}
     Classes, SysUtils,

     {$IFDEF D18}
     System.Generics.Collections,
     {$ENDIF}

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

     {$IFDEF FMX}
     FMX.Types, 
     {$IFDEF D17}
     System.UIConsts,
     {$ENDIF}
     {$ELSE}
     Graphics,

     {$IFNDEF D15}
     FileCtrl,
     {$ENDIF}

     {$ENDIF}

     {$IFDEF FMX}
     FMXTee.Engine, FMXTee.Canvas, FMXTee.Chart, FMXTee.Tools, FMXTee.Procs
     {$ELSE}
     {$IFDEF D16}
     VCL.Themes,
     {$ENDIF}
     TeEngine, Chart, TeCanvas, TeeProcs, TeeTools
     {$ENDIF};

type
  TDefaultTheme=class(TChartTheme)
  protected
    class procedure ChangeSeries(const Series:TChartSeries);
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  TFlatTheme=class(TChartTheme)
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  TLookoutTheme=class(TChartTheme)
  public
    procedure Apply; override;
    function Description:string; override;
    procedure SetDefaultValues; override;
  end;

  TAndrosTheme=class(TChartTheme)
  public
    procedure Apply; override;
    function Description:string; override;
    procedure SetDefaultValues; override;
  end;
  
  TExcelTheme=class(TChartTheme)
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  TClassicTheme=class(TChartTheme)
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  TBusinessTheme=class(TDefaultTheme)
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  TWebTheme=class(TDefaultTheme)
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  TWindowsXPTheme=class(TBusinessTheme)
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  TBlueSkyTheme=class(TChartTheme)
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  TFactsTheme=class(TChartTheme)
  private
    FBands : TGridBandTool;

    function GetBands: TGridBandTool;
  public
    procedure Apply; override;
    function Description:string; override;
    property Bands:TGridBandTool read GetBands;
  end;

  TOperaTheme=class(TChartTheme)
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  TBlackIsBackTheme=class(TChartTheme)
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  TGrayscaleTheme=class(TChartTheme)
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  TSpeedTheme=class(TChartTheme)
  public
    procedure Apply; override;
    function Description:string; override;
  end;

  {$IFDEF D17}
  TSystemTheme=class(TChartTheme)
  public
    class procedure ApplyStyle(const AChart:TCustomChart;
                               AStyle:{$IFDEF FMX}TFmxObject{$ELSE}TCustomStyleServices{$ENDIF}=nil);

    procedure Apply; override;

    function Description:string; override;

    class function StyleExtension:String;
    class function StylesPath:String;
    class function SystemStyles(const APath:String):TStrings;
  end;
  {$ENDIF}

const
  TeeMsg_ThemesPath={$IFDEF FMX}'FMXThemesPath'{$ELSE}'VCLThemesPath'{$ENDIF}; // Do not localize

type
  TThemesList=class(TList{$IFDEF TEELISTMETA}<TChartThemeClass>{$ENDIF}) // QC115377
  private
    function GetTheme(Index:Integer):TChartThemeClass;
  public
    class procedure Apply(const Dest,Origin:TCustomChart); overload;
    class procedure Apply(const Dest:TCustomChart; const FileName:String); overload;
    class procedure ApplyColors(const Chart:TCustomChart; const Scheme:TColorArray; Start:Integer=0);
    class procedure ApplyFont(const Chart:TCustomChart; const AName:String);
    class procedure ApplyFontSizeDelta(const Chart:TCustomChart; const ADelta:Single);
    class procedure ApplyFromResource(const Dest:TCustomChart; const ResourceName:String);
    class function ThemesPath:String;

    property Theme[Index:Integer]:TChartThemeClass read GetTheme; default;
  end;

// Apply a Theme class to Chart. Example: ApplyChartTheme(TWebTheme,Chart1);
procedure ApplyChartTheme(const Theme:TChartThemeClass; const Chart:TCustomChart; PaletteIndex:Integer=-1);

// List of registered Chart Themes:
var
  ChartThemes : TThemesList=nil;

procedure RegisterChartThemes(const Themes:Array of TChartThemeClass);

// Global array of Color Schemes, to use with TThemesList.ApplyColors
var
  TeeColorSchemes : Array of TColorArray;

implementation

uses {$IFDEF D6}
     Types,
     {$ENDIF}

     Math,
     
     {$IFDEF D17}
     System.IOUtils,
     {$ENDIF}

     {$IFDEF FMX}

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

     FMX.Controls, FMX.Objects,

     {$IFDEF D17}
     FMX.Styles,
     {$ENDIF}

     {$ELSE}
     Controls,
     {$ENDIF}

     {$IFDEF LCL}
     GraphType,
     {$ENDIF}

     {$IFDEF D17}
     {$IFDEF MSWINDOWS}
     {$IFNDEF D19}
     Winapi.SHFolder,
     {$ENDIF}
     {$ENDIF}
     {$ENDIF}

     {$IFDEF FMX}
     FMXTee.Constants, FMXTee.ProConstants, FMXTee.Store
     {$ELSE}
     TeeConst, TeeStore, TeeProCo
     {$ENDIF};

{$R TeeThemes.res}

{$IFDEF FMX}
const
  clWhite=claWhite;
  clBlack=claBlack;
  clGray=claGray;
  clBtnFace=claSilver;
  clSilver=claSilver;
  clYellow=claYellow;
  clBlue=claBlue;
  clNavy=claNavy;
  fsBold=TFontStyle.fsBold;
  clInfoBk=claLightyellow;
{$ENDIF}

{ TDefaultTheme }

function TDefaultTheme.Description: string;
begin
  result:='TeeChart default';
end;

class procedure TDefaultTheme.ChangeSeries(const Series:TChartSeries);
begin
  with Series.Marks do
  begin
    Transparent:=False;
    Gradient.Visible:=False;
    Font.Name:=GetDefaultFontName;
    Font.Size:=GetDefaultFontSize;
    Arrow.Color:=clWhite;
  end;
end;

procedure TDefaultTheme.Apply;
var t : Integer;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEEDEFAULTTHEME');

  for t:=0 to Chart.SeriesCount-1 do
      TDefaultTheme.ChangeSeries(Chart[t]);
end;

function ThemeFontName(const AFontName:String):String;
begin
  {$IFDEF TEEJAPANESE}
  result:=GetDefaultFontName;
  {$ELSE}
  result:=AFontName;
  {$ENDIF}
end;

{ TClassicTheme }

procedure TClassicTheme.Apply;

  procedure DoChangeSeries(Series:TChartSeries);
  const
    ClassicFont = 'Times New Roman';
  begin
    with Series.Marks do
    begin
      Gradient.Visible:=False;
      Transparent:=True;
      Font.Name:=ThemeFontName(ClassicFont);
      Font.Size:=10;
      Arrow.Color:=clBlack;
    end;
  end;

var t : Integer;
begin
  inherited;

  TThemesList.ApplyFromResource(Chart,'TEECLASSICTHEME');

  for t:=0 to Chart.SeriesCount-1 do
      DoChangeSeries(Chart[t]);
end;

function TClassicTheme.Description: string;
begin
  result:='Classic';
end;

{ TBusinessTheme }

procedure TBusinessTheme.Apply;
var t : Integer;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEEBUSINESSTHEME');

  for t:=0 to Chart.SeriesCount-1 do
  begin
    TDefaultTheme.ChangeSeries(Chart[t]);

    with Chart[t].Marks.Gradient do
    begin
      Visible:=True;
      StartColor:=clSilver;
    end;
  end;
end;

function TBusinessTheme.Description: string;
begin
  result:=TeeMsg_WizardTab;  // Business
end;

{ TFlatTheme }

procedure TFlatTheme.Apply;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEEFLATTHEME');
end;

function TFlatTheme.Description:string;
begin
  result:=TeeMsg_Flat;
end;

{ TLookoutTheme }

procedure TLookoutTheme.Apply;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEELOOKOUTTHEME');
  SetDefaultValues;
end;

function TLookoutTheme.Description:string;
begin
  result:=TeeMsg_Lookout;
end;

type
  TCanvasAccess=class(TTeeCanvas);

procedure TLookoutTheme.SetDefaultValues;

  procedure SetSeries(const ASeries:TChartSeries);
  var tmp : TSeriesMarks;
  begin
    tmp:=ASeries.Marks;

    tmp.ArrowLength:=8;
    tmp.RoundSize:=0;
    tmp.Font.Name:='Verdana';
    tmp.Brush.Color:=ASeries.Color;
    tmp.Shadow.Visible:=False;
  end;

var i: Integer;
begin
  inherited;

  Chart.View3D:=False;

  TCanvasAccess(Chart.Canvas).SetAntiAlias(False);

  for i:=0 to Chart.SeriesCount-1 do
      SetSeries(Chart[i]);
end;

{ TAndrosTheme }

procedure TAndrosTheme.Apply;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEEANDROSTHEME');
  SetDefaultValues;
end;

function TAndrosTheme.Description:string;
begin
  result:=TeeMsg_Andros;
end;

procedure TAndrosTheme.SetDefaultValues;
var i: Integer;
    tmp : TSeriesMarks;
begin
  inherited;

  Chart.View3D:=False;

  TCanvasAccess(Chart.Canvas).SetAntiAlias(False);

  for i:=0 to Chart.SeriesCount-1 do
  begin
    tmp:=Chart[i].Marks;

    tmp.Arrow.Visible:=False;
    tmp.Transparent:=True;
    tmp.Font.Color:=clWhite;
    tmp.Font.Name:='Verdana';
  end;
end;

{ TExcelTheme }

function TExcelTheme.Description: string;
begin
  result:='Microsoft® Excel';
end;

procedure TExcelTheme.Apply;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEEEXCELTHEME');
end;

{ TWebTheme }

procedure TWebTheme.Apply;
const
  WebFont='Lucida Console';

var t : Integer;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEEWEBTHEME');

  for t:=0 to Chart.SeriesCount-1 do
  begin
    TDefaultTheme.ChangeSeries(Chart[t]);
    Chart[t].Marks.Font.Name:=ThemeFontName(WebFont);

    with Chart[t].Marks.Gradient do
    begin
      Visible:=True;
      StartColor:=clSilver;
    end;
  end;
end;

function TWebTheme.Description: string;
begin
  result:='Web';
end;

{ TWindowsXPTheme }

procedure TWindowsXPTheme.Apply;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEEWINDOWSTHEME');
end;

function TWindowsXPTheme.Description: string;
begin
  result:='Windows';
end;

{ TBlueSkyTheme }

procedure TBlueSkyTheme.Apply;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEEBLUESKYTHEME');
end;

function TBlueSkyTheme.Description: string;
begin
  result:='Blues';
end;

procedure ApplyChartTheme(const Theme:TChartThemeClass; const Chart:TCustomChart; PaletteIndex:Integer);
begin
  with Theme.Create(Chart) do
  try
    Apply;
  finally
    Free;
  end;

  if PaletteIndex<>-1 then
     Chart.ColorPaletteIndex:=PaletteIndex;
end;

procedure RegisterChartThemes(const Themes:Array of TChartThemeClass);
var t : Integer;
begin
  for t:=0 to Length(Themes)-1 do
      ChartThemes.Add({$IFNDEF D18}TObject{$ENDIF}(Themes[t]));
end;

function TThemesList.GetTheme(Index:Integer):TChartThemeClass;
begin
  result:=TChartThemeClass(Items[Index]);
end;

{ TFactsTheme }

type
  TSeriesAccess=class(TChartSeries);

procedure TFactsTheme.Apply;
const
  Color1 : TColor =128*256*256 + 128*256 + 128;
  Color2 : TColor =204*256*256 + 204*256 + 204;
  Color3 : TColor = 64*256*256 +  64*256 +  64;

var t         : Integer;
    tmpAxis   : TChartAxis;
    tmpSeries : TSeriesAccess;
    tmpBand   : TGridBandTool;
begin
  inherited;

  TThemesList.ApplyFromResource(Chart,'TEEFACTSTHEME');

  for t:=0 to Chart.SeriesCount-1 do
  begin
    tmpSeries:=TSeriesAccess(Chart[t]);

    with tmpSeries do
    begin
      Pen.Visible:=IMandatoryPen or ((not Chart.View3D) and IMandatoryPen2D);

      with Marks do
      begin
        Frame.Color:=Color1;
        Shadow.HorizSize:=2;
        Shadow.VertSize:=2;
        Shadow.Color:=Color2;
      end;
    end;
  end;

  tmpBand:=Bands;

  if Chart.SeriesCount=0 then
     tmpBand.Axis:=Chart.Axes.Left
  else
     tmpBand.Axis:=Chart[0].GetVertAxis;

  tmpBand.Band1.Color := RGB(233,233,233); // TV52010198,TV52010527
  tmpBand.Band2.Color := clWhite;

  if Chart.SeriesCount=0 then
     tmpAxis:=Chart.Axes.Bottom
  else
     tmpAxis:=Chart[0].GetHorizAxis;

  with tmpAxis do
  begin
    Axis.Color:=Color3;
    Grid.Color:=Color2;
    Grid.Visible:=False;
  end;
end;

function TFactsTheme.Description: string;
begin
  result:=TeeMsg_ThemeFacts;
end;

function TFactsTheme.GetBands: TGridBandTool;
const
  Magic_FactsBand = $998877;

  function FindBands:TGridBandTool;
  var t : Integer;
  begin
    for t:=0 to Chart.Tools.Count-1 do
        if Chart.Tools[t] is TGridBandTool then
           if Chart.Tools[t].Tag=Magic_FactsBand then
           begin
             result:=TGridBandTool(Chart.Tools[t]);
             Exit;
           end;

    result:=nil;
  end;

var tmp : String;
begin
  if not Assigned(FBands) then
     FBands:=FindBands;

  if not Assigned(FBands) then
  begin
    FBands:=TGridBandTool.Create(Chart.Owner);
    tmp:=FBands.ClassName;
    FBands.Name:=TeeGetUniqueName(Chart.Owner,Copy(tmp,2,Length(tmp)));

    FBands.Tag:=Magic_FactsBand;
  end;

  result:=FBands;
end;

{ TOperaTheme }

{$IFDEF TEEOCX}
Type ThemeGradient = class(TCustomTeeGradient);
{$ENDIF}

procedure TOperaTheme.Apply;
const
  OperaFont='Verdana';
var t : Integer;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEEOPERATHEME');

  for t:=0 to Chart.SeriesCount-1 do
      Chart[t].Marks.Font.Name:=ThemeFontName(OperaFont);

{$IFDEF TEEOCX}
  TeCanvas.GetDefaultFontName := OperaFont;
  ThemeGradient(Chart.Gradient).IDefVisible := True;
  ThemeGradient(Chart.Walls.Back.Gradient).IDefVisible := True;  
{$ENDIF}
end;

function TOperaTheme.Description: string;
begin
  result:=TeeMsg_ThemeOpera;
end;

{ TBlackIsBackTheme }

procedure TBlackIsBackTheme.Apply;

  procedure DoChangeSeries(const Series:TChartSeries);
  begin
    with Series.Marks do
    begin
      Transparent:=True;
      Font.Color:=clWhite;
    end;
  end;

var t : Integer;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEEBLACKISBLACKTHEME');

  for t:=0 to Chart.SeriesCount-1 do
      DoChangeSeries(Chart[t]);
end;

function TBlackIsBackTheme.Description: string;
begin
  result:=TeeMsg_ThemeBlack;
end;

{ TGrayscaleTheme }

procedure TGrayscaleTheme.Apply;

  procedure DoChangeSeries(const Series:TChartSeries);
  begin
    with Series.Marks do
    begin
      Transparent:=True;
      Font.Size:=10;
      Font.Color:=clBlack;
      Arrow.Color:=clBlack;
    end;
  end;

var t : Integer;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEEGRAYSCALETHEME');

  for t:=0 to Chart.SeriesCount-1 do
      DoChangeSeries(Chart[t]);
end;

function TGrayscaleTheme.Description: string;
begin
  result:=TeeMsg_ThemeGrayScale;
end;

{ TSpeedTheme }

procedure TSpeedTheme.Apply;
var t: Integer;
begin
  inherited;
  TThemesList.ApplyFromResource(Chart,'TEESPEEDTHEME');
  Chart.Axes.FastCalc:=True;

  for t:=0 to Chart.SeriesCount-1 do
      Chart[t].Marks.Hide;
end;

function TSpeedTheme.Description: string;
begin
  result:=TeeMsg_ThemeSpeed;
end;

{$IFDEF D17}

{ TSystemTheme }

class procedure TSystemTheme.ApplyStyle(const AChart:TCustomChart;
                       AStyle:{$IFDEF FMX}TFmxObject{$ELSE}TCustomStyleServices{$ENDIF}=nil);
{$IFDEF FMX}
var
  tmpBorder : TFmxObject;
{$ENDIF}

  procedure SetFonts({$IFDEF FMX}const AFont:TTextSettings{$ELSE}
                     const AColor:TColor
                     {$ENDIF});

    procedure SetFont(const ATeeFont:TTeeFont);
    begin
      {$IFDEF FMX}
      ATeeFont.Assign(AFont.Font);
      {$ENDIF}

      ATeeFont.Color:={$IFDEF FMX}AFont.FontColor{$ELSE}AColor{$ENDIF};
    end;

  var
    t: Integer;
  begin
    SetFont(AChart.Title.Font);
    SetFont(AChart.SubTitle.Font);
    SetFont(AChart.Foot.Font);
    SetFont(AChart.SubFoot.Font);

    SetFont(AChart.Legend.Font);
    SetFont(AChart.Legend.Title.Font);

    for t := 0 to AChart.Axes.Count-1 do
    begin
      SetFont(AChart.Axes[t].Texts.Font);
      SetFont(AChart.Axes[t].Title.Font);

      {$IFDEF FMX}
      if tmpBorder is TRectangle then
         AChart.Axes[t].Axis.Color:=TRectangle(tmpBorder).Fill.Color
      else
         AChart.Axes[t].Axis.Color:=TAlphaColorRec.Black;
      {$ENDIF}
    end;

    for t := 0 to AChart.SeriesCount-1 do
        SetFont(AChart[t].Marks.Font);
  end;

  {$IFDEF FMX}
  function FindChild(const AObject:TFmxObject; const AStyleName:String):TFmxObject;
  var t : Integer;
  begin
    for t := 0 to AObject.ChildrenCount-1 do
        if AObject.Children[t] is TControl then
        if SameText(TControl(AObject.Children[t]).StyleName,AStyleName) then
        begin
          result:=AObject.Children[t];
          Exit;
        end;

    result:=nil;
  end;
  {$ENDIF}

  procedure ChangeBack(const AColor:TAlphaColor);
  var t : Integer;
      tmpS : TTeeShadow;
  begin
    AChart.Color:=AColor;
    AChart.Gradient.Visible:=False;

    AChart.Legend.Color:=AColor;

    tmpS:=AChart.Legend.Shadow;
    tmpS.Color:=TTeeCanvas.ColorFrom(tmpS.Color,TTeeCanvas.ColorAlpha(AColor));

    AChart.Walls.Back.Color:=AColor;
    AChart.Walls.Back.Gradient.Visible:=False;

    AChart.Walls.Left.Color:=AColor;
    AChart.Walls.Right.Color:=AColor;
    AChart.Walls.Bottom.Color:=AColor;

    for t := 0 to AChart.SeriesCount-1 do
    begin
      AChart[t].Marks.Color:=AColor;

      tmpS:=AChart[t].Marks.Shadow;
      tmpS.Color:=TTeeCanvas.ColorFrom(tmpS.Color,TTeeCanvas.ColorAlpha(AColor));
    end;
  end;

  {$IFDEF FMX}
  function FindSelectedListBack:TFmxObject;
  begin
    result:=AStyle.FindStyleResource('listboxstyle');

    if Assigned(result) then
    begin
      result:=FindChild(result,'background');

      if Assigned(result) then
      begin
        result:=FindChild(result,'content');

        if Assigned(result) then
           result:=FindChild(result,'selection');
      end;
    end;
  end;
  {$ENDIF}

  procedure ChangeBorder(const AColor:TColor);
  begin
    AChart.BevelInner:=bvNone;
    AChart.BevelOuter:=bvNone;

    AChart.Border.Show;
    AChart.Border.Color:=AColor;
  end;

{$IFDEF FMX}
var
  tmp : TFmxObject;
{$ENDIF}
begin
  if not Assigned(AStyle) then
     AStyle:=TStyleManager.ActiveStyle{$IFDEF D19}{$IFDEF FMX}(nil){$ENDIF}{$ENDIF};

  if Assigned(AStyle) then
  begin
    {$IFDEF FMX}
    tmpBorder:=FindSelectedListBack;

    tmp:=AStyle.FindStyleResource('labelstyle');

    if Assigned(tmp) then
    begin
      tmp:=FindChild(tmp,'text');

      if tmp is TText then
         SetFonts((TText(tmp) as ITextSettings).{$IFDEF D20}ResultingTextSettings{$ELSE}TextSettings{$ENDIF});
    end;

    tmp:=AStyle.FindStyleResource('backgroundstyle');

    if tmp is TRectangle then
       ChangeBack(TRectangle(tmp).Fill.Color)
    else
    begin
      tmp:=AStyle.FindStyleResource('panelstyle');

      if tmp is TRectangle then
         ChangeBack(TRectangle(tmp).Fill.Color);
    end;

    if tmpBorder is TRectangle then
       ChangeBorder(TRectangle(tmpBorder).Fill.Color)
    else
    begin
      AChart.BevelInner:=bvNone;
      AChart.BevelOuter:=bvRaised;

      AChart.Border.Hide;
      AChart.Border.Color:=TAlphaColorRec.Silver;
    end;

    {$ELSE}
    SetFonts(AStyle.GetStyleFontColor(sfButtonTextNormal));
    ChangeBack(AStyle.GetStyleColor(scPanel));
    ChangeBorder(AStyle.GetStyleColor(scButtonNormal));
    {$ENDIF}

  end;
end;

{$IFNDEF FMX}
function IsValidStyleFile(const AFile:String):Boolean;
var tmp : TFileStream;
begin
  tmp:=TFileStream.Create(AFile,fmOpenRead);
  try
    result:=StyleServices.IsValidStyle(tmp);
  finally
    tmp.Free;
  end;
end;
{$ENDIF}

class function TSystemTheme.SystemStyles(const APath: String): TStrings;

  procedure FillItems(const ADir,APrefix:String);
  var S : TStringDynArray;
      tmpS,
      tmp  : String;
  begin
    if TDirectory.Exists(ADir) then
    begin
      S:=TDirectory.GetFiles(ADir,'*'+StyleExtension);
      try
        for tmp in S do
        {$IFDEF FMX}
        {$IFDEF D20}
        if TStyleStreaming.CanLoadFromFile(tmp) then
        {$ELSE}
        // Alternative for XE5 and below: TStyleManager.LoadFromStream <> nil
        {$ENDIF}
        {$ELSE}
        if IsValidStyleFile(tmp) then
        {$ENDIF}
        begin
          tmpS:=ADir+'\'+System.IOUtils.TPath.GetFileNameWithoutExtension(tmp);

          if SameText(Copy(tmpS,1,Length(APrefix)),APrefix) then
             Delete(tmpS,1,Length(APrefix));

          result.Add(tmpS);
        end;
      finally
        S:=nil;
      end;

      S:=TDirectory.GetDirectories(ADir);
      try
        for tmp in S do
            FillItems(tmp,APrefix);
      finally
        S:=nil;
      end;
    end;
  end;

begin
  result:=TStringList.Create;
  FillItems(APath,APath+'\');
end;

class function TSystemTheme.StylesPath:String;

  {$IFNDEF D19}
  // From XE6 IOUtils.pas:
  function GetSharedDocumentsPath: string;
  {$IFDEF MSWINDOWS}
  const
    CSIDL_COMMON_DOCUMENTS = $002e;

  var
    LStr: array[0 .. MAX_PATH] of Char;
  begin
    SetLastError(ERROR_SUCCESS);

    if SHGetFolderPath(0, CSIDL_COMMON_DOCUMENTS, 0, 0, @LStr) = S_OK then
      Result := LStr;
  {$ELSE}
  begin
    result:='';
  {$ENDIF}
  end;
  {$ENDIF}

  function RADPath:String;
  begin
    result:={$IFDEF D19}System.IOUtils.TPath.{$ENDIF}GetSharedDocumentsPath;

    {$IFDEF D21}
    result:=result+'\Embarcadero\Studio\15.0';
    {$ELSE}
    {$IFDEF D20}
    result:=result+'\Embarcadero\Studio\14.0';
    {$ELSE}
    result:=result+'\RAD Studio';

    {$IFDEF D19}
    result:=result+'\12.0';
    {$ELSE}
    {$IFDEF D18}
    result:=result+'\11.0';
    {$ELSE}
    {$IFDEF D17}
    result:=result+'\10.0';
    {$ELSE}
    {$IFDEF D16}
    result:=result+'\9.0';
    {$ENDIF}
    {$ENDIF}
    {$ENDIF}
    {$ENDIF}
    {$ENDIF}
    {$ENDIF}
  end;

begin
  {$IFDEF FMX}
  if FileExists('FMX.Platform.Win.Style') then
     result:=GetCurrentDir
  else
  {$ENDIF}
     result:=RadPath+'\Styles';
end;

procedure TSystemTheme.Apply;
begin
  ApplyStyle(Chart);
end;

class function TSystemTheme.StyleExtension:String;
begin
  result:={$IFDEF FMX}'.style'{$ELSE}'.vsf'{$ENDIF};
end;

function TSystemTheme.Description:string;
begin
  result:='System';
end;
{$ENDIF}

{ TThemesList }

type
  TShapeAccess=class(TTeeCustomShape);
  TPanelAccess=class(TCustomTeePanel);
  TCustomAxisPanelAccess=class(TCustomAxisPanel);

class procedure TThemesList.Apply(const Dest,Origin:TCustomChart);

  procedure ApplyFormat(const Dest,Origin:TTeeCustomShape; ChangeVisible:Boolean=True);
  begin
    with Dest do
    begin
      Bevel:=Origin.Bevel;
      BevelWidth:=Origin.BevelWidth;
      Brush:=Origin.Brush;
      Color:=Origin.Color;
      Emboss:=Origin.Emboss;

      Font:=Origin.Font;

      {$IFDEF FMX}
      if TTeeCanvas3D.ColorAlpha(Font.Color)=0 then
         Font.Color:=TTeeCanvas3D.ColorFrom(Font.Color,$FF);
      {$ENDIF}

      Font.Name:=ThemeFontName(Font.Name);

      Shadow:=Origin.Shadow;

      if Assigned(TShapeAccess(Origin).FPicture) then
         Picture:=Origin.Picture
      else
         Picture:=nil;

      RoundSize:=Origin.RoundSize;
      Transparent:=Origin.Transparent;
      ShapeStyle:=Origin.ShapeStyle;
      Pen:=Origin.Pen;

      if ChangeVisible then
         Visible:=Origin.Visible;
    end;

    TShapeAccess(Dest).Transparency:=TShapeAccess(Origin).Transparency;
  end;

  procedure ApplyTitle(const Dest,Origin:TChartTitle);
  begin
    ApplyFormat(Dest,Origin);

    with Dest do
    begin
      AdjustFrame:=Origin.AdjustFrame;
      Alignment:=Origin.Alignment;
      VertMargin:=Origin.VertMargin;
    end;
  end;

  procedure ApplyWall(const Dest,Origin:TCustomChartWall);
  begin
    ApplyFormat(Dest,Origin);

    with Dest do
    begin
      AutoHide:=Origin.AutoHide;
      Dark3D:=Origin.Dark3D;
      EndPosition:=Origin.EndPosition;
      Size:=Origin.Size;
      StartPosition:=Origin.StartPosition;
    end;
  end;

  procedure ApplyAxis(const Dest,Origin:TChartAxis);

    procedure ApplyTexts(const Dest,Origin:TAxisLabels);
    begin
      Dest.Align:=Origin.Align;
      Dest.Alternate:=Origin.Alternate;
      Dest.Angle:=Origin.Angle;
      Dest.Font:=Origin.Font;
      Dest.OnAxis:=Origin.OnAxis;
      Dest.Behind:=Origin.Behind;
    end;

  begin
    with Dest do
    begin
      Ticks:=Origin.Ticks;
      TickInnerLength:=Origin.TickInnerLength;
      TickLength:=Origin.TickLength;
      TicksInner:=Origin.TicksInner;

      Axis:=Origin.Axis;
      Grid:=Origin.Grid;
      GridCentered:=Origin.GridCentered;

      ApplyTexts(Dest.Texts,Origin.Texts);

      MinorGrid:=Origin.MinorGrid;
      MinorTickCount:=Origin.MinorTickCount;
      MinorTickLength:=Origin.MinorTickLength;
      MinorTicks:=Origin.MinorTicks;
      ZPosition:=Origin.ZPosition;

      ApplyFormat(Title,Origin.Title);
      Title.Angle:=Origin.Title.Angle;

      // Problem:
      // Theme is made with VCL, so colors have Alpha=0,
      // which means in FMX totally transparent color.
      {$IFDEF FMX}
      if TTeeCanvas3D.ColorAlpha(Grid.Color)=0 then
         Grid.Color:=TTeeCanvas3D.ColorFrom(Grid.Color,$9F);
      {$ENDIF}
    end;
  end;

var t,
    L  : Integer;
begin
  with Dest do
  begin
    ColorPaletteIndex:=Origin.ColorPaletteIndex;

    L:=Length(Origin.ColorPalette);
    SetLength(ColorPalette,L);
    for t:=0 to L-1 do
        ColorPalette[t]:=Origin.ColorPalette[t];

    BackImage:=Origin.BackImage;
    Gradient:=Origin.Gradient;
    Border:=Origin.Border;
    BevelInner:=Origin.BevelInner;
    BevelOuter:=Origin.BevelOuter;
    BevelWidth:=Origin.BevelWidth;
    Color:=Origin.Color;

    ApplyFormat(Legend,Origin.Legend,False);
    Legend.Symbol:=Origin.Legend.Symbol;
    ApplyFormat(Legend.Title,Origin.Legend.Title);
    Legend.Title.TextAlignment:=Origin.Legend.Title.TextAlignment;

    ApplyTitle(Title,Origin.Title);
    ApplyTitle(Foot,Origin.Foot);

    with Walls do
    begin
      ApplyWall(Left,Origin.Walls.Left);
      ApplyWall(Right,Origin.Walls.Right);
      ApplyWall(Back,Origin.Walls.Back);
      ApplyWall(Bottom,Origin.Walls.Bottom);
    end;

    Axes.Behind:=Origin.Axes.Behind;

    for t:=0 to Axes.Count-1 do
    if t<Origin.Axes.Count then
       ApplyAxis(Axes[t],Origin.Axes[t])
    else
       ApplyAxis(Axes[t],Origin.Axes[0]);

    View3DOptions:=Origin.View3DOptions;

    // Themes should not change View3D.
    // View3D should be respected:
    // (* View3D:=Origin.View3D; *)

    View3DWalls:=Origin.View3DWalls;
  end;

  with TPanelAccess(Dest) do
  begin
    Theme.Free;
    Theme:=TCustomChart.Create(nil);

    CloneChart(TCustomChart(Theme),Origin,nil,True);
  end;

  for t:=0 to Dest.SeriesCount-1 do
      TCustomAxisPanelAccess(Dest).ApplyThemeSeries(TCustomAxisPanel(Dest).Series[t]);
end;

class procedure TThemesList.Apply(const Dest:TCustomChart; const FileName:String);
var Origin : TCustomChart;
begin
  BeginGlobalLoading;
  try
    Origin:=TChart.Create(nil);
    try
      LoadChartFromFile(Origin,FileName);
      Apply(Dest,Origin);
    finally
      Origin.Free;
    end;
  finally
    EndGlobalLoading;
  end;
end;

// Changes all fonts in Chart globally
class procedure TThemesList.ApplyFont(const Chart:TCustomChart; const AName:String);

  procedure SetTitle(const Title:TChartTitle);
  begin
    Title.Font.Name:=AName;
  end;

  procedure SetAxis(const Axis:TChartAxis);
  var t : Integer;
  begin
    with Axis do
    begin
      Texts.Font.Name:=AName;
      Title.Font.Name:=AName;

      for t:=0 to SubAxes.Count-1 do
          SetAxis(SubAxes[t]);
    end;
  end;

var t : Integer;
begin
  Chart.Legend.Font.Name:=AName;

  SetTitle(Chart.Title);
  SetTitle(Chart.Foot);
  SetTitle(Chart.SubTitle);
  SetTitle(Chart.SubFoot);

  for t:=0 to Chart.Axes.Count-1 do
      SetAxis(Chart.Axes[t]);

  for t:=0 to Chart.SeriesCount-1 do
  with Chart[t] do
  begin
    Marks.Font.Name:=AName;
  end;

  for t:=0 to Chart.Tools.Count-1 do
  if Chart.Tools[t] is TAnnotationTool then
     TAnnotationTool(Chart.Tools[t]).Shape.Font.Name:=AName;
end;

// Changes all Chart font sizes globally
class procedure TThemesList.ApplyFontSizeDelta(const Chart:TCustomChart; const ADelta:Single);

  procedure ApplyTo(const AFont:TTeeFont);
  begin
    with AFont do
         SizeFloat:=Max(0.1,SizeFloat+ADelta);
  end;

  procedure SetTitle(const Title:TChartTitle);
  begin
    ApplyTo(Title.Font);
  end;

  procedure SetAxis(const Axis:TChartAxis);
  var t : Integer;
  begin
    ApplyTo(Axis.Texts.Font);
    ApplyTo(Axis.Title.Font);

    for t:=0 to Axis.SubAxes.Count-1 do
        SetAxis(Axis.SubAxes[t]);
  end;

var t : Integer;
begin
  ApplyTo(Chart.Legend.Font);

  SetTitle(Chart.Title);
  SetTitle(Chart.Foot);
  SetTitle(Chart.SubTitle);
  SetTitle(Chart.SubFoot);

  for t:=0 to Chart.Axes.Count-1 do
      SetAxis(Chart.Axes[t]);

  for t:=0 to Chart.SeriesCount-1 do
      ApplyTo(Chart[t].Marks.Font);

  for t:=0 to Chart.Tools.Count-1 do
  if Chart.Tools[t] is TAnnotationTool then
     ApplyTo(TAnnotationTool(Chart.Tools[t]).Shape.Font);
end;

// Changes all colors globally in Chart, using the color array
class procedure TThemesList.ApplyColors(const Chart:TCustomChart; const Scheme:TColorArray; Start:Integer=0);

  procedure IncPos;
  begin
    Inc(Start);

    if Start>=Length(Scheme) then
       Start:=0;
  end;

  procedure SetPensColor(const AColor:TColor);

    procedure SetTitle(const Title:TChartTitle);
    begin
      Title.Pen.Color:=AColor;
    end;

    procedure SetWall(const Wall:TChartWall);
    begin
      Wall.Pen.Color:=AColor;
    end;

    procedure SetAxis(const Axis:TChartAxis);
    var t : Integer;
    begin
      with Axis do
      begin
        Axis.Color:=AColor;
        Grid.Color:=AColor;
        MinorGrid.Color:=AColor;
        Ticks.Color:=AColor;
        MinorTicks.Color:=AColor;
        TicksInner.Color:=AColor;

        Title.Pen.Color:=AColor;

        for t:=0 to SubAxes.Count-1 do
            SetAxis(SubAxes[t]);
      end;
    end;

  var t : Integer;
  begin
    Chart.Border.Color:=AColor;
    Chart.Legend.Pen.Color:=AColor;

    SetTitle(Chart.Title);
    SetTitle(Chart.Foot);
    SetTitle(Chart.SubTitle);
    SetTitle(Chart.SubFoot);

    SetWall(Chart.Walls.Left);
    SetWall(Chart.Walls.Right);
    SetWall(Chart.Walls.Back);
    SetWall(Chart.Walls.Bottom);

    for t:=0 to Chart.Axes.Count-1 do
        SetAxis(Chart.Axes[t]);

    for t:=0 to Chart.SeriesCount-1 do
    with Chart[t] do
    begin
      Pen.Color:=AColor;
      Marks.Pen.Color:=AColor;
    end;

    for t:=0 to Chart.Tools.Count-1 do
        Chart.Tools[t].Pen.Color:=AColor;
  end;

  procedure SetFontsColor(const AColor:TColor);

    procedure SetTitle(const Title:TChartTitle);
    begin
      Title.Font.Color:=AColor;
    end;

    procedure SetAxis(const Axis:TChartAxis);
    var t : Integer;
    begin
      with Axis do
      begin
        Texts.Font.Color:=AColor;
        Title.Font.Color:=AColor;

        for t:=0 to SubAxes.Count-1 do
            SetAxis(SubAxes[t]);
      end;
    end;

  var t : Integer;
  begin
    Chart.Legend.Font.Color:=AColor;

    SetTitle(Chart.Title);
    SetTitle(Chart.Foot);
    SetTitle(Chart.SubTitle);
    SetTitle(Chart.SubFoot);

    for t:=0 to Chart.Axes.Count-1 do
        SetAxis(Chart.Axes[t]);

    for t:=0 to Chart.SeriesCount-1 do
    with Chart[t] do
    begin
      Marks.Font.Color:=AColor;
    end;

    for t:=0 to Chart.Tools.Count-1 do
    if Chart.Tools[t] is TAnnotationTool then
       TAnnotationTool(Chart.Tools[t]).Shape.Font.Color:=AColor;
  end;

  procedure SetBrushColor(const AColor:TColor);

    procedure SetTitle(const Title:TChartTitle);
    begin
      Title.Color:=AColor;
      Title.Gradient.Visible:=False;
    end;

    procedure SetAxis(const Axis:TChartAxis);
    var t : Integer;
    begin
      with Axis do
      begin
        Shape.Color:=AColor;
        Shape.Gradient.Visible:=False;

        for t:=0 to SubAxes.Count-1 do
            SetAxis(SubAxes[t]);
      end;
    end;

  var t : Integer;
  begin
    Chart.Color:=AColor;
    Chart.Gradient.Visible:=False;

    Chart.Legend.Color:=AColor;
    Chart.Legend.Gradient.Visible:=False;

    SetTitle(Chart.Title);
    SetTitle(Chart.Foot);
    SetTitle(Chart.SubTitle);
    SetTitle(Chart.SubFoot);

    for t:=0 to Chart.Axes.Count-1 do
        SetAxis(Chart.Axes[t]);

    for t:=0 to Chart.SeriesCount-1 do
    with Chart[t] do
    begin
      Marks.Color:=AColor;
      Marks.Gradient.Visible:=False;
    end;

    for t:=0 to Chart.Tools.Count-1 do
    if Chart.Tools[t] is TAnnotationTool then
       with TAnnotationTool(Chart.Tools[t]).Shape do
       begin
         Color:=AColor;
         Gradient.Visible:=False;
       end;
  end;

  procedure SetWall(const Wall:TChartWall);
  begin
    Wall.Color:=Scheme[Start];

    Wall.Gradient.Visible:=False;
  end;

begin
  if Start>=Length(Scheme) then
     Start:=0;

  if Length(Scheme)>Start then
  begin
    SetBrushColor(Scheme[Start]);

    IncPos;
    SetPensColor(Scheme[Start]);

    IncPos;
    SetFontsColor(Scheme[Start]);

    IncPos;
    SetWall(Chart.Walls.Back);

    IncPos;
    SetWall(Chart.Walls.Left);
    SetWall(Chart.Walls.Right);
    SetWall(Chart.Walls.Bottom);
  end;
end;

class function TThemesList.ThemesPath:String;
begin
  result:=TeeReadStringOption(TeeMsg_ThemesPath,'');
  if not {$IFDEF D15}SysUtils.{$ENDIF}DirectoryExists(result) then
     result:='';
end;

class procedure TThemesList.ApplyFromResource(const Dest:TCustomChart; const ResourceName:String);
var tmp : TResourceStream;
    tmpChart : TCustomChart;
begin
  tmp:=TeeResourceStream(ResourceName);

  {$IFDEF FMXLINUX}
  if tmp<>nil then // temporary
  {$ENDIF}
  try
    BeginGlobalLoading;
    try
      tmpChart:=TChart.Create(nil);
      try
        LoadChartFromStream(tmpChart,tmp);
        TThemesList.Apply(Dest,tmpChart);
      finally
        tmpChart.Free;
      end;
    finally
      EndGlobalLoading;
    end;
  finally
    tmp.Free;
  end;
end;

procedure InitColorSchemes;

  procedure AddScheme(const Colors:Array of TColor; Inverted:Boolean=False);

    function InvertColor(const Color:TColor):TColor;
    {$IFDEF FMX}
    begin
      result:=Color; // FMX: RGB is already inverted

      // FMX opacity is inverted from VCL:
      if TRGBA(Color).Alpha=0 then
         TRGBA(result).Alpha:=255;
    end;
    {$ELSE}
    var r,g,b : Byte;
    begin
      r:=Byte(Color);
      g:=(Color AND $0000FF00) SHR 8;
      b:=(Color AND $00FF0000) SHR 16;

      result:=(r SHL 16) OR (g SHL 8) OR b;
    end;
    {$ENDIF}

  var t,
      L : Integer;
  begin
    L:=Length(TeeColorSchemes);
    SetLength(TeeColorSchemes,L+1);

    SetLength(TeeColorSchemes[L],Length(Colors));

    for t:=Low(Colors) to High(Colors) do
        if Inverted then
           TeeColorSchemes[L,t]:=InvertColor(Colors[t])
        else
           TeeColorSchemes[L,t]:=Colors[t];
  end;

begin
  AddScheme([ RGB(239,229,201),RGB(116,101,87),RGB(86,2,2),RGB(36,1,0),RGB(12,0,0) ]);

  AddScheme([ RGB(0,0,0),RGB(38,50,72),RGB(126,138,162),RGB(255,255,255),RGB(255,152,0) ]);

  AddScheme([ RGB(61,36,116),RGB(104,48,168),RGB(149,88,164),RGB(217,152,204),RGB(243,214,201) ]);

  AddScheme([ RGB(204,59,55),RGB(57,136,153),RGB(255,252,232),RGB(255,133,127),RGB(204,193,163) ]);

  AddScheme([ clWhite, $1D2F3E, $152737, $0F9FB4, $0FB493, $C9E1C1 ],True);

  AddScheme([ $EFEFEF, $CCCCCC, $F79B2E, $E1358D, $F87AB8 ],True);

  AddScheme([ clWhite, $DDDAD6, $F9F1B5, $A6D8DE, $AE9C96 ],True);

  AddScheme([ $EDDAB6, $E8D1A4, $DDA185, $AB3334, $331919 ],True);

  AddScheme([ $9BCE7D, $72AC93, $699E87, $BD0102, $98002F ],True);

  AddScheme([ clWhite, $393939, $FF5A09, $F3843E, $FF9900, $6E6E6E ],True);
end;

initialization
  ChartThemes:=TThemesList.Create;

  // DO NOT REORDER / INSERT / REMOVE themes here !  Append is ok.
  // (Registry setting is numeric index in the array).

  RegisterChartThemes([ TDefaultTheme,
                        TExcelTheme,
                        TClassicTheme,
                        TBusinessTheme,
                        TWebTheme,
                        TWindowsXPTheme,
                        TBlueSkyTheme,
                        TFactsTheme,
                        TFlatTheme, // was: TRandomTheme
                        TOperaTheme,
                        TBlackIsBackTheme,
                        TGrayscaleTheme,
                        TSpeedTheme
                        {$IFDEF D17},
                        TSystemTheme
                        {$ENDIF},
                        TLookoutTheme,
                        TAndrosTheme]);

  InitColorSchemes;
finalization
  ChartThemes.Free;
end.
