{
 ***************************************************************************
 *                                                                         *
 *   This source is free software; you can redistribute it and/or modify   *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This code is distributed in the hope that it will be useful, but      *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   General Public License for more details.                              *
 *                                                                         *
 *   A copy of the GNU General Public License is available on the World    *
 *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
 *   obtain it by writing to the Free Software Foundation,                 *
 *   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.        *
 *                                                                         *
 ***************************************************************************

 Author: Balázs Székely
 Abstract:
   The implementation of IDE Coolbar.
 ToDo:
   Extract an interface from here and put it to IdeIntf package.
}

unit IdeCoolbarData;


{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LCLProc, ComCtrls, ToolWin, Controls, fgl,
  IDEImagesIntf, Laz2_XMLCfg, ToolbarConfig;

type

  // Option classes take care of saving / loading environment options data.
  // They don't contain LCL components.

  { TIDEToolBarOptions }

  TIDEToolBarOptions = class(TIDEToolBarOptionsBase)
  private
    FPosIndex: Integer;
    FBreak: Boolean;
  public
    //constructor Create;
    //destructor Destroy; override;
    function Equals(Opts: TIDEToolBarOptions): boolean; overload;
    procedure Assign(Source: TIDEToolBarOptions);
    procedure CopyPosFromBand(Band: TCoolBand);
    procedure Load(XMLConfig: TXMLConfig; SubPath: String);
    procedure Save(XMLConfig: TXMLConfig; SubPath: String);
  published
    property Break: Boolean read FBreak write FBreak;
    property PosIndex: Integer read FPosIndex write FPosIndex;
  end;


  { TIDEToolBarOptionList }

  Ttbo = specialize TFPGObjectList<TIDEToolBarOptions>;
  TIDEToolBarOptionList = class(Ttbo)
    procedure Assign(Source: TIDEToolBarOptionList);
  end;

  { TIDECoolBarOptions }

  TIDECoolBarOptions = class
  private
    FVisible: Boolean;
    FWidth: Integer;
    FGrabStyle: Integer;
    FGrabWidth: Integer;
    FBorderStyle: Integer; //TFormBorderStyle;
    FToolBars: TIDEToolBarOptionList;
    procedure CreateDefaultToolbars;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Clear;
    procedure Assign(Source: TIDECoolBarOptions);
    function EqualToolbars(Opts: TIDECoolBarOptions): boolean;
    procedure Load(XMLConfig: TXMLConfig; Path: String);
    procedure Save(XMLConfig: TXMLConfig; Path: String);
  public
    property Visible: Boolean read FVisible write FVisible;
    property Width: Integer read FWidth write FWidth;
    property GrabStyle: Integer read FGrabStyle write FGrabStyle;
    property GrabWidth: Integer read FGrabWidth write FGrabWidth;
    property BorderStyle: Integer read FBorderStyle write FBorderStyle;
    property ToolBars: TIDEToolBarOptionList read FToolBars;
  end;

  { TDefaultCoolBarOptions }

  TDefaultCoolBarOptions = class(TIDECoolBarOptions)
  public
    constructor Create;
    destructor Destroy; override;
  end;

  // Actual Coolbar and its member Toolbars

  TOnToolBarClick = procedure(Sender: TObject) of object;

  { TIDEToolBar }

  TIDEToolBar = class(TIDEToolbarBase)
   private
     FCurrentOptions: TIDEToolBarOptions;
     FOnToolbarClick: TOnToolBarClick;
     procedure DoToolBarClick(Sender: TObject);
   public
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
     procedure ClearToolbar;
     procedure UseCurrentOptions;
   public
     property CurrentOptions: TIDEToolBarOptions read FCurrentOptions;
     property OnToolBarClick: TOnToolbarClick read FOnToolbarClick write FOnToolbarClick;
   end;

  TIDEToolBarList = specialize TFPGObjectList<TIDEToolBar>;

  { TIDECoolBar }

  TIDECoolBar = class
  private
    FCoolBar: TCoolBar;  // The actual CoolBar, not owned by this class.
    FCoolBarToolBars: TIDEToolBarList;
    FIsVisible: Boolean; //cannot hide/show the coolbar on toolbar_options, instead we use a variable
    FWidth: Integer;     //same as Isvisible
    // Used for assigning and testing the default configuration.
    FDefaultOptions: TDefaultCoolBarOptions;
    procedure SetIsVisible(AValue: Boolean);
  public
    constructor Create(ACoolBar: TCoolBar);
    destructor Destroy; override;
    procedure SetCoolBarDefaults;
    procedure SetToolBarDefaults;
    procedure CopyFromRealCoolbar(RealCoolbar: TCoolBar);
    procedure CopyFromOptions(Options: TIDECoolBarOptions);
    procedure CopyToOptions(Options: TIDECoolBarOptions);
    function Add: TIDEToolBar;
    function FindByToolBar(const aToolBar: TToolBar): Integer;
    procedure Sort;
    function IsDefaultCoolbar: Boolean;
    function IsDefaultToolbar: Boolean;
  public
    property ToolBars: TIDEToolBarList read FCoolBarToolBars;
    property CoolBar: TCoolBar read FCoolBar;
    property IsVisible: Boolean read FIsVisible write SetIsVisible;
    property Width: Integer read FWidth write FWidth;
  end;

var
  IDECoolBar: TIDECoolBar;

implementation

const
  BasePath = 'IDECoolBarOptions/';

{ TIDEToolBarOptions }
{
constructor TIDEToolBarOptions.Create;
begin
  inherited Create;
end;

destructor TIDEToolBarOptions.Destroy;
begin
  inherited Destroy;
end;
}
function TIDEToolBarOptions.Equals(Opts: TIDEToolBarOptions): boolean;
begin
  Result := inherited Equals(Opts)
      and (FPosIndex = Opts.FPosIndex) and (FBreak = Opts.FBreak);
end;

procedure TIDEToolBarOptions.Assign(Source: TIDEToolBarOptions);
begin
  inherited Assign(Source);
  FPosIndex := Source.FPosIndex;
  FBreak := Source.FBreak;
end;

procedure TIDEToolBarOptions.CopyPosFromBand(Band: TCoolBand);
begin
  FPosIndex := Band.Index;
  FBreak := Band.Break;
end;

procedure TIDEToolBarOptions.Load(XMLConfig: TXMLConfig; SubPath: String);
begin
  FBreak := XMLConfig.GetValue(SubPath + 'Break/Value', False);
  LoadButtonNames(XMLConfig, SubPath);
end;

procedure TIDEToolBarOptions.Save(XMLConfig: TXMLConfig; SubPath: String);
begin
  XMLConfig.SetDeleteValue(SubPath + 'Break/Value', FBreak, False);
  SaveButtonNames(XMLConfig, SubPath);
end;

{ TIDEToolBarOptionList }

procedure TIDEToolBarOptionList.Assign(Source: TIDEToolBarOptionList);
var
  tbo: TIDEToolBarOptions;
  i: Integer;
begin
  Clear;
  for i := 0 to Source.Count-1 do
  begin
    tbo := TIDEToolBarOptions.Create;
    tbo.Assign(Source[i]);
    Add(tbo);
  end;
end;

{ TIDECoolBarOptions }
constructor TIDECoolBarOptions.Create;
begin
  inherited Create;
  FToolBars := TIDEToolBarOptionList.Create;
end;

destructor TIDECoolBarOptions.Destroy;
begin
  FToolBars.Free;
  inherited Destroy;
end;

procedure TIDECoolBarOptions.Clear;
begin
  FToolBars.Clear;
end;

procedure TIDECoolBarOptions.Assign(Source: TIDECoolBarOptions);
begin
  FVisible := Source.FVisible;
  FWidth := Source.FWidth;
  FGrabStyle := Source.FGrabStyle;
  FGrabWidth := Source.FGrabWidth;
  FBorderStyle := Source.FBorderStyle;
  FToolBars.Assign(Source.FToolBars);
end;

function TIDECoolBarOptions.EqualToolbars(Opts: TIDECoolBarOptions): boolean;
var
  I: Integer;
begin
  Result := (FToolBars.Count = Opts.FToolBars.Count);
  if not Result then Exit;
  for I := 0 to FToolBars.Count-1 do
    if not FToolBars[I].Equals(Opts.FToolBars[I]) then Exit(False);
end;

procedure TIDECoolBarOptions.CreateDefaultToolbars;
var
  ToolBarOpts: TIDEToolBarOptions;
begin
  //standard toolbar defaults
  ToolBarOpts := TIDEToolBarOptions.Create;
  ToolBarOpts.PosIndex := 0;
  ToolBarOpts.Break := False;
  with ToolBarOpts.ButtonNames do
  begin
    Add('NewForm');
    Add('NewUnit');
    Add(cIDEToolbarDivider);
    Add('Open');
    Add('Save');
    Add('SaveAll');
    Add(cIDEToolbarDivider);
    Add('Toggle between Unit and Form');
    Add(cIDEToolbarDivider);
    Add('Manage desktops');
  end;
  FToolBars.Add(ToolBarOpts);

  //debug toolbar defaults
  ToolBarOpts := TIDEToolBarOptions.Create;
  ToolBarOpts.PosIndex := 1;
  ToolBarOpts.Break := True;
  with ToolBarOpts.ButtonNames do
  begin
    Add('View Units');
    Add('View Forms');
    Add(cIDEToolbarDivider);
    Add('Change build mode');
    Add('Run program');
    Add('Pause program');
    Add('Stop program');
    Add('Step over');
    Add('Step into');
    Add('Step out');
  end;
  FToolBars.Add(ToolBarOpts);
end;

procedure TIDECoolBarOptions.Load(XMLConfig: TXMLConfig; Path: String);
var
  ToolBarOpt: TIDEToolBarOptions;
  ToolBarCount: Integer;
  I: Integer;
begin
  Path := Path + BasePath;
  ToolbarCount := XMLConfig.GetValue(Path + 'Count', 0);
  if ToolBarCount = 0 then  // Old format
    ToolbarCount := XMLConfig.GetValue(Path + 'ToolBarCount/Value', 0);
  FVisible := XMLConfig.GetValue(Path + 'Visible/Value', True);
  FWidth := XMLConfig.GetValue(Path + 'Width/Value', 230);
  FGrabStyle := XMLConfig.GetValue(Path + 'GrabStyle/Value', 1);
  FGrabWidth := XMLConfig.GetValue(Path + 'GrabWidth/Value', 5);
  FBorderStyle := XMLConfig.GetValue(Path + 'BorderStyle/Value', 1);
  if ToolBarCount > 0 then
  begin
    FToolBars.Clear;
    for I := 0 to ToolbarCount-1 do
    begin
      ToolBarOpt := TIDEToolBarOptions.Create;
      FToolBars.Add(ToolBarOpt);
      ToolBarOpt.PosIndex := I;
      ToolBarOpt.Load(XMLConfig, Path + 'ToolBar' + IntToStr(I+1) + '/');
    end;
  end;
  if ToolBarCount = 0 then
    CreateDefaultToolbars;
end;

procedure TIDECoolBarOptions.Save(XMLConfig: TXMLConfig; Path: String);
var
  DefaultOpts: TDefaultCoolBarOptions;
  I: Integer;
begin
  DefaultOpts := TDefaultCoolBarOptions.Create;
  try
    Path := Path + BasePath;
    XMLConfig.DeletePath(Path);
    XMLConfig.SetDeleteValue(Path + 'Visible/Value', FVisible, True);
    XMLConfig.SetDeleteValue(Path + 'Width/Value', FWidth, 0);
    XMLConfig.SetDeleteValue(Path + 'GrabStyle/Value', FGrabStyle, 1);
    XMLConfig.SetDeleteValue(Path + 'GrabWidth/Value', FGrabWidth, 5);
    XMLConfig.SetDeleteValue(Path + 'BorderStyle/Value', FBorderStyle, 1);
    if EqualToolbars(DefaultOpts) then Exit;
    if FToolBars.Count > 0 then
    begin
      XMLConfig.SetDeleteValue(Path + 'Count', FToolBars.Count, 0);
      for I := 0 to FToolBars.Count - 1 do
        FToolBars[I].Save(XMLConfig, Path + 'ToolBar' + IntToStr(I+1) + '/');
    end;
  finally
    DefaultOpts.Free;
  end;
end;

{ TDefaultCoolBarOptions }

constructor TDefaultCoolBarOptions.Create;
begin
  inherited Create;
  //coolbar defaults
  FVisible := True;
  FWidth := 230;
  FGrabStyle := 1;
  FGrabWidth := 5;
  FBorderStyle := 1;
  //toolbar defaults
  CreateDefaultToolbars;
end;

destructor TDefaultCoolBarOptions.Destroy;
begin
  inherited Destroy;
end;

{ TIDEToolBar }

constructor TIDEToolBar.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FToolBar := TToolbar.Create(nil);
  with FToolBar do
  begin
    ButtonHeight := 22;
    ButtonWidth := 22;
    Height := 22;
    Width := 0;
    Flat     := True;
    AutoSize := True;
    Transparent := True;
    EdgeInner := esNone;
    EdgeOuter := esNone;
    Images   := IDEImages.Images_16;
    ShowHint := True;
    OnClick := @DoToolBarClick;
  end;
  FCurrentOptions := TIDEToolBarOptions.Create;
end;

destructor TIDEToolBar.Destroy;
begin
  FCurrentOptions.Free;
  FToolBar.Free;
  inherited Destroy;
end;

procedure TIDEToolBar.ClearToolbar;
var
  i: Integer;
begin
  FToolBar.BeginUpdate;
  try
    for i := FToolBar.ButtonCount - 1 downto 0 do
      FToolBar.Buttons[i].Free
  finally
    FToolBar.EndUpdate;
  end;
end;

procedure TIDEToolBar.UseCurrentOptions;
begin
  ClearToolbar;
  CopyFromOptions(FCurrentOptions);
end;

procedure TIDEToolBar.DoToolBarClick(Sender: TObject);
begin
  if Assigned(FOnToolbarClick) then
    FOnToolbarClick(FToolbar);
end;

{ TIDECoolBar }

procedure TIDECoolBar.SetIsVisible(AValue: Boolean);
begin
  FIsVisible := AValue;
  if Assigned(FCoolBar) then
    FCoolBar.Visible := AValue;
end;

constructor TIDECoolBar.Create(ACoolBar: TCoolBar);
begin
  inherited Create;
  FCoolBar := ACoolBar;
  FCoolBarToolBars := TIDEToolBarList.Create;
  FDefaultOptions := TDefaultCoolBarOptions.Create;
  if Assigned(FCoolBar) then begin
    CopyFromOptions(FDefaultOptions);
    SetCoolBarDefaults;
    SetToolBarDefaults;
  end;
end;

destructor TIDECoolBar.Destroy;
begin
  FreeAndNil(FDefaultOptions);
  FreeAndNil(FCoolBarToolBars);
  inherited Destroy;
end;

procedure TIDECoolBar.SetCoolBarDefaults;
begin
  FCoolBar.Vertical := False;
  FCoolBar.HorizontalSpacing := 1;
  FCoolBar.VerticalSpacing := 3;
  FCoolBar.FixedSize := True;
  FCoolBar.DoubleBuffered := True;
  FCoolBar.EdgeInner := esNone;
  FCoolBar.EdgeOuter := esNone;

  FCoolBar.GrabStyle := TGrabStyle(1);
  FCoolBar.GrabWidth := 5;
  FCoolBar.BandBorderStyle := bsSingle;
end;

procedure TIDECoolBar.SetToolBarDefaults;
begin
  CopyFromOptions(FDefaultOptions);
end;

procedure TIDECoolBar.CopyFromRealCoolbar(RealCoolbar: TCoolBar);
var
  ToolBar: TToolBar;
  I, J: Integer;
begin
  for I := 0 to RealCoolbar.Bands.Count - 1 do
  begin
    if RealCoolbar.Bands[I].Control = nil then
      Continue;
    ToolBar := (RealCoolbar.Bands[I].Control as TToolBar);
    J := FindByToolBar(ToolBar);
    if J <> -1 then
      ToolBars[J].CurrentOptions.CopyPosFromBand(RealCoolbar.Bands[I]);
  end;
  Sort;
end;

procedure TIDECoolBar.CopyFromOptions(Options: TIDECoolBarOptions);
var
  I: Integer;
  IDEToolBar: TIDEToolBar;
begin
  FCoolBarToolBars.Clear;
  for I := 0 to Options.FToolBars.Count - 1 do
  begin
    IDEToolBar := TIDEToolBar.Create(Nil);
    FCoolBarToolBars.Add(IDEToolBar);
    IDEToolBar.CurrentOptions.PosIndex := I;
    IDEToolBar.CurrentOptions.Break := Options.FToolBars[I].Break;
    IDEToolBar.CurrentOptions.ButtonNames.Assign(Options.FToolBars[I].ButtonNames);
  end;
end;

procedure TIDECoolBar.CopyToOptions(Options: TIDECoolBarOptions);
var
  I: Integer;
  Opt: TIDEToolBarOptions;
begin
  Options.FToolBars.Clear;
  for I := 0 to FCoolBarToolBars.Count - 1 do
  begin
    Opt := TIDEToolBarOptions.Create;
    Options.FToolBars.Add(Opt);
    Opt.PosIndex := FCoolBarToolBars[I].CurrentOptions.PosIndex;
    Opt.Break := FCoolBarToolBars[I].CurrentOptions.Break;
    Opt.ButtonNames.Assign(FCoolBarToolBars[I].CurrentOptions.ButtonNames);
  end;
end;

function TIDECoolBar.Add: TIDEToolBar;
begin
  Result := TIDEToolBar.Create(Nil);
  FCoolBarToolBars.Add(Result);
end;

function TIDECoolBar.FindByToolBar(const aToolBar: TToolBar): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to FCoolbarToolBars.Count-1 do
  begin
    if ToolBars[I].ToolBar = aToolBar then
    begin
      Result := I;
      Break;
    end;
  end;
end;

function Compare(const Item1, Item2: TIDEToolBar): Integer;
begin
  Result := Item1.CurrentOptions.PosIndex - Item2.CurrentOptions.PosIndex;
end;

procedure TIDECoolBar.Sort;
begin
  FCoolbarToolBars.Sort(@Compare);
end;

function TIDECoolBar.IsDefaultCoolbar: Boolean;
begin
  Result := (FIsVisible) and (FCoolBar.BandBorderStyle = bsSingle) and
            (FCoolBar.GrabStyle = gsDouble) and (FCoolBar.GrabWidth = 5) and
            (FWidth = 230);
end;

function TIDECoolBar.IsDefaultToolbar: Boolean;
var
  TempOpts: TIDECoolBarOptions;
begin
  TempOpts := TIDECoolBarOptions.Create;
  try
    CopyToOptions(TempOpts);
    Result := TempOpts.EqualToolbars(FDefaultOptions);
  finally
    TempOpts.Free;
  end;
end;

end.
