{
 ***************************************************************************
 *                                                                         *
 *   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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, 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, fgl,
  // LCL
  ComCtrls, Controls, Graphics, Dialogs, ToolWin,
  // IdeIntf
  IDEImagesIntf,
  // IdeConfig
  CoolBarOptions,
  // IDE
  LazarusIDEStrConsts, ToolbarConfig;

type

  // 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 DisableToolbarButtons(IDEToolbar: TIDEToolBar);
    procedure SetIsVisible(AValue: Boolean);
    procedure ToolBarClick(Sender: TObject);
  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 AddBand(ToolBar: TToolBar; aBreak: Boolean): TCoolBand;
    procedure AddExtra;
    procedure Delete;
    function FindByToolBar(const aToolBar: TToolBar): Integer;
    procedure Config;
    procedure Sort;
    procedure PopulateToolBar;
    function GetSelectedBand: Integer;
    function IsDefaultCoolbar: Boolean;
    function IsDefaultToolbar: Boolean;
    procedure SelectBand(const ID: integer);
    procedure SelectBandAtXY(X, Y: integer);
  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

{ 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 }

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.SetIsVisible(AValue: Boolean);
begin
  FIsVisible := AValue;
  if Assigned(FCoolBar) then
    FCoolBar.Visible := AValue;
end;

procedure TIDECoolBar.ToolBarClick(Sender: TObject);
var
  CoolBand: TCoolBand;
begin
  CoolBand := FCoolbar.Bands.FindBand(Sender as TToolBar);
  if CoolBand <> nil then
    SelectBand(CoolBand.Index);
end;

procedure TIDECoolBar.SetCoolBarDefaults;
begin
  FCoolBar.Vertical := False;
  FCoolBar.HorizontalSpacing := 1;
  FCoolBar.VerticalSpacing := 3;
  FCoolBar.FixedSize := 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;
  Band: TCoolBand;
  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
    begin
      Band := RealCoolbar.Bands[I];
      ToolBars[J].CurrentOptions.CopyPosFromBandValues(Band.Index, Band.Break);
    end;
  end;
  Sort;
end;

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

procedure TIDECoolBar.CopyToOptions(Options: TIDECoolBarOptions);
var
  I: Integer;
  Opt: TIDEToolBarOptions;
begin
  Options.ToolBars.Clear;
  for I := 0 to FCoolBarToolBars.Count - 1 do
  begin
    Opt := TIDEToolBarOptions.Create;
    Options.ToolBars.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.AddBand(ToolBar: TToolBar; aBreak: Boolean): TCoolBand;
begin
  Result := FCoolBar.Bands.Add;
  Result.Break := aBreak;
  Result.Control := Toolbar;
  //Result.MinWidth := 25;
  //Result.MinHeight := 22;
  Result.FixedSize := True;
end;

procedure TIDECoolBar.AddExtra;
var
  IDEToolbar: TIDEToolBar;
begin
  IDEToolbar := Add;
  IDEToolbar.CurrentOptions.Break := False;
  IDEToolbar.OnToolBarClick := @ToolBarClick;
  IDEToolbar.ToolBar.DisabledImages := IDEToolbar.ToolBar.Images;
  SelectBand(AddBand(IDEToolbar.ToolBar, True).Index);
end;

procedure TIDECoolBar.Delete;
var
  I: integer;
  ToDelete: integer;
begin
  if FCoolbar.Bands.Count = 1 then
  begin
    MessageDlg(lisCoolbarDeleteWarning, mtInformation, [mbOk], 0);
    Exit;
  end;
  ToDelete := GetSelectedBand;
  if ToDelete > -1 then
  begin
    if MessageDlg(lisCoolbarDeleteToolBar, mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    begin
      if ToDelete < FCoolBar.Bands.Count-1 then
        SelectBand(ToDelete + 1)
      else if ToDelete > 0 then
        SelectBand(ToDelete - 1);
      I := FindByToolBar((FCoolBar.Bands.Items[ToDelete].Control as TToolBar));
      Assert(I = ToDelete, 'TIDECoolBar.Delete: index mismatch.');
      ToolBars.Delete(ToDelete);
    end;
  end;
end;

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

procedure TIDECoolBar.Config;
var
  ToConfig: Integer;
  ToolBar: TToolBar;
  IDEToolbar: TIDEToolBar;
begin
  ToConfig := GetSelectedBand;
  if ToConfig = -1 then
  begin
    MessageDlg(lisCoolbarSelectToolBar, mtInformation, [mbOk], 0);
    Exit;
  end;
  ToolBar := FCoolbar.Bands.Items[ToConfig].Control as TToolBar;
  Assert(Assigned(ToolBar), 'TIDECoolBar.Config: ToolBar=Nil.');
  Assert(ToConfig=FindByToolBar(ToolBar), 'TIDECoolBar.Config: Indices differ!');
  IDEToolbar := ToolBars[ToConfig];
  if ShowToolBarConfig(IDEToolbar.CurrentOptions.ButtonNames) = mrOK then
    DisableToolbarButtons(IDEToolbar);
  FCoolbar.AutosizeBands;
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;

procedure TIDECoolBar.DisableToolbarButtons(IDEToolbar: TIDEToolBar);
var
  I: Integer;
begin
  IDEToolbar.UseCurrentOptions;
  for I := 0 to Pred(IDEToolbar.ToolBar.ButtonCount) do
    IDEToolbar.ToolBar.Buttons[I].Enabled := False;
end;

procedure TIDECoolBar.PopulateToolBar;
var
  I: Integer;
  IDEToolbar: TIDEToolBar;
begin
  FCoolBar.Bands.Clear;
  for I := 0 to ToolBars.Count - 1 do
  begin
    IDEToolbar := ToolBars[I];
    IDEToolbar.OnToolBarClick := @ToolBarClick;
    IDEToolbar.ToolBar.DisabledImages := IDEToolbar.ToolBar.Images;
    AddBand(IDEToolbar.ToolBar, IDEToolbar.CurrentOptions.Break);
    DisableToolbarButtons(IDEToolbar);
  end;
  if FCoolBar.Bands.Count > 0 then
    SelectBand(0);
  FCoolbar.AutosizeBands;
end;

function TIDECoolBar.GetSelectedBand: Integer;
var
  I: Integer;
begin
  for I := 0 to FCoolBar.Bands.Count - 1 do
    if FCoolBar.Bands.Items[I].Color = clHighlight then
      Exit(I);
  Result := -1;
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;

procedure TIDECoolBar.SelectBand(const ID: integer);
var
  I: integer;
  Band: TCoolBand;
begin
  FCoolbar.Color := clDefault;
  for I := 0 to FCoolBar.Bands.Count - 1 do
  begin
    Band := FCoolBar.Bands.Items[I];
    if I <> ID then
    begin
      Band.Color := clDefault;
      Band.Control.Color := clDefault;
    end
    else
    begin
      Band.Color := clHighlight;
      Band.Control.Color := clHighLight;
    end;
  end;
end;

procedure TIDECoolBar.SelectBandAtXY(X, Y: integer);
var
  ABand: integer;
  AGrabber: boolean;
begin
  FCoolBar.MouseToBandPos(X, Y, ABand, AGrabber);
  if ABand < 0 then
    Exit;
  if FCoolBar.Bands.Items[ABand].Color <> clHighlight then
    SelectBand(ABand);
end;

end.
