lazarus/ide/toolbarconfig.pas
2023-05-15 02:15:40 +03:00

606 lines
17 KiB
ObjectPascal

{
Copyright (C) 2007 Graeme Geldenhuys (graemeg@gmail.com)
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program 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 Library General Public License
for more details.
You should have received a copy of the GNU Library General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
}
unit ToolbarConfig;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
// LCL
LCLProc, LclIntf, Controls, Forms, Graphics, ExtCtrls, Buttons, StdCtrls,
ComCtrls, Menus, ButtonPanel,
// LazControls
TreeFilterEdit,
// LazUtils
LazUTF8,
// IdeIntf
ToolBarIntf, IDEImagesIntf, IDEWindowIntf,
// IdeConfig
ToolBarOptionsBase,
// IDE
LazarusIDEStrConsts;
type
{ TToolBarConfig }
TToolBarConfig = class(TForm)
btnAdd: TSpeedButton;
btnAddDivider: TSpeedButton;
btnCancel: TButton;
btnHelp: TBitBtn;
btnMoveDown: TSpeedButton;
btnMoveUp: TSpeedButton;
btnOK: TButton;
btnRemove: TSpeedButton;
FilterEdit: TTreeFilterEdit;
lblMenuTree: TLabel;
lblToolbar: TLabel;
lvToolbar: TListView;
miAll: TMenuItem;
miCustom: TMenuItem;
miDebug: TMenuItem;
miDesign: TMenuItem;
miHTML: TMenuItem;
pnlButtons: TButtonPanel;
Splitter1: TSplitter;
TV: TTreeView;
procedure btnHelpClick(Sender: TObject);
function FilterEditFilterNode({%H-}ItemNode: TTreeNode; out Done: Boolean): Boolean;
procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure lvToolbarDblClick(Sender: TObject);
procedure lvToolbarEnterExit(Sender: TObject);
procedure TVDblClick(Sender: TObject);
procedure UpdateButtonsState;
procedure btnAddClick(Sender: TObject);
procedure btnAddDividerClick(Sender: TObject);
procedure btnMoveDownClick(Sender: TObject);
procedure btnMoveUpClick(Sender: TObject);
procedure btnRemoveClick(Sender: TObject);
procedure lvToolbarSelectItem(Sender: TObject; {%H-}Item: TListItem;
{%H-}Selected: Boolean);
procedure TVSelectionChanged(Sender: TObject);
private
defImageIndex: integer;
procedure AddCommand;
procedure AddDivider;
procedure AddTailItem;
procedure AddToolBarItem(CmdItem: TIDEButtonCommand);
procedure InsertItem(Item: TListItem);
procedure MoveUpDown(aOffset: integer);
function NewLvItem(aCaption: string): TListItem;
procedure RemoveCommand;
procedure LoadCategories;
procedure SortCategories(ACtgList: TStrings);
procedure AddMenuItem(ParentNode: TTreeNode; CmdItem: TIDEButtonCommand);
function RootNodeCaption(CmdItem: TIDEButtonCommand): string;
public
procedure LoadSettings(SL: TStringList);
procedure SaveSettings(SL: TStringList);
end;
{ TIDEToolbarBase }
TIDEToolbarBase = class(TComponent)
private
protected
FToolBar: TToolBar;
procedure AddButton(ACommand: TIDEButtonCommand);
procedure AddDivider;
procedure CopyFromOptions(Options: TIDEToolBarOptionsBase);
procedure PositionAtEnd(AToolBar: TToolBar; AButton: TToolButton);
procedure PostCopyOptions; virtual;
public
//constructor Create(AOwner: TComponent); override;
//destructor Destroy; override;
property ToolBar: TToolBar read FToolBar;
end;
function ShowToolBarConfig(aNames: TStringList): TModalResult;
implementation
{$R *.lfm}
function ShowToolBarConfig(aNames: TStringList): TModalResult;
var
Conf: TToolBarConfig;
begin
Conf := TToolBarConfig.Create(Nil);
try
if Assigned(aNames) then
Conf.LoadSettings(aNames);
Result := Conf.ShowModal;
if (Result = mrOK) and Assigned(aNames) then
Conf.SaveSettings(aNames);
finally
Conf.Free;
end;
end;
{ TToolBarConfig }
procedure TToolBarConfig.FormCreate(Sender: TObject);
begin
inherited;
pnlButtons.Color := clBtnFace;
// load button images
IDEImages.AssignImage(btnAdd, 'arrow__darkgreen_right');
IDEImages.AssignImage(btnRemove, 'arrow__darkred_left');
IDEImages.AssignImage(btnMoveUp, 'arrow__darkgreen_up');
IDEImages.AssignImage(btnMoveDown, 'arrow__darkgreen_down');
//IDEImages.AssignImage(btnAddDivider, 'menu_divider16'); // uncomment if 'menu_divider16' exists (currently not)
btnAddDivider.Caption := '---';
btnAdd.Hint := lisCoolBarAddSelected;
btnRemove.Hint := lisCoolBarRemoveSelected;
btnMoveUp.Hint := lisCoolBarMoveSelectedUp;
btnMoveDown.Hint := lisCoolBarMoveSelectedDown;
btnAddDivider.Hint:= lisCoolBarAddDivider;
TV.Images := IDEImages.Images_16;
lvToolbar.SmallImages := IDEImages.Images_16;
// default image to be used when none is available
defImageIndex := IDEImages.LoadImage('execute');
Caption := lisToolbarConfiguration;
lblMenuTree.Caption := lisCoolbarAvailableCommands;
lblToolbar.Caption := lisCoolbarToolbarCommands;
LoadCategories;
IDEDialogLayoutList.ApplyLayout(Self);
end;
procedure TToolBarConfig.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
IDEDialogLayoutList.SaveLayout(Self);
end;
procedure TToolBarConfig.FormDestroy(Sender: TObject);
begin
;
end;
procedure TToolBarConfig.lvToolbarDblClick(Sender: TObject);
begin
RemoveCommand;
end;
procedure TToolBarConfig.lvToolbarEnterExit(Sender: TObject);
begin
UpdateButtonsState;
end;
procedure TToolBarConfig.TVDblClick(Sender: TObject);
begin
AddCommand;
end;
procedure TToolBarConfig.btnHelpClick(Sender: TObject);
begin
OpenUrl('http://wiki.freepascal.org/IDE_Window:_Toolbar_Config');
end;
function TToolBarConfig.FilterEditFilterNode(ItemNode: TTreeNode; out Done: Boolean): Boolean;
begin
Result := lvToolbar.FindCaption(0, ItemNode.Text, True, True, False) = Nil;
Done := not Result;
end;
procedure TToolBarConfig.UpdateButtonsState;
var
I: Integer;
begin
I := lvToolbar.ItemIndex;
btnAdd.Enabled := Assigned(TV.Selected) and Assigned(TV.Selected.Data);
btnRemove.Enabled := (I>-1) and (I<lvToolbar.Items.Count-1);
btnMoveUp.Enabled := (I>0) and (I<lvToolbar.Items.Count-1);
btnMoveDown.Enabled := (I>-1) and (I<lvToolbar.Items.Count-2);
btnAddDivider.Enabled := True;
end;
procedure TToolBarConfig.TVSelectionChanged(Sender: TObject);
begin
UpdateButtonsState;
end;
procedure TToolBarConfig.InsertItem(Item: TListItem);
begin
lvToolbar.ItemIndex := -1;
lvToolbar.Selected := nil;
if Item.Index < lvToolbar.Items.Count then
lvToolbar.ItemIndex := Item.Index+1
else
lvToolbar.ItemIndex := Item.Index;
end;
procedure TToolBarConfig.btnAddClick(Sender: TObject);
begin
AddCommand;
end;
function TToolBarConfig.NewLvItem(aCaption: string): TListItem;
var
I: Integer;
begin
I := lvToolbar.ItemIndex;
if I = -1 then
I := lvToolbar.Items.Count-1; // Add before the last empty item.
Result := lvToolbar.Items.Insert(I);
Result.Caption := aCaption;
end;
procedure TToolBarConfig.AddCommand;
var
Node: TTreeNode;
CmdCaption: string;
lvItem: TListItem;
begin
Node := TV.Selected;
if (Node = Nil) or (Node.Data = Nil) then
Exit;
CmdCaption := TIDEButtonCommand(Node.Data).GetCaptionWithShortCut;
lvItem := NewLvItem(CmdCaption);
lvItem.Data := Node.Data;
if Node.ImageIndex > -1 then
lvItem.ImageIndex := Node.ImageIndex
else
lvItem.ImageIndex := defImageIndex;
InsertItem(lvItem); // Add the newly created item to ListView.
// Update selection in TreeView.
Node := TV.Selected.GetNext;
TV.Selected.Visible := False;
if Node <> nil then
TV.Selected := Node;
UpdateButtonsState;
end;
procedure TToolBarConfig.RemoveCommand;
Var
Cmd: TIDEButtonCommand;
Node: TTreeNode;
I: Integer;
begin
I := lvToolbar.ItemIndex;
if (I<0) or (I>=lvToolbar.Items.Count-1) then Exit;
Cmd := TIDEButtonCommand(lvToolbar.Items[I].Data);
lvToolbar.Items.Delete(I);
{$IF DEFINED(LCLQt) or DEFINED(LCLQt5) or DEFINED(LCLQt6)}
lvToolbar.ItemIndex := -1; // Try to make LCLQt behave.
lvToolbar.ItemIndex := I;
{$ENDIF}
lvToolbar.Selected := lvToolbar.Items[I];
// Show the command as available again in TreeView.
if Assigned(Cmd) then
begin
Node:= TV.Items.FindNodeWithData(Cmd);
if Node<>nil then
Node.Visible:= True;
end;
UpdateButtonsState;
end;
procedure TToolBarConfig.btnAddDividerClick(Sender: TObject);
var
lvItem: TListItem;
begin
lvItem := NewLvItem(cIDEToolbarDivider);
lvItem.ImageIndex := -1;
InsertItem(lvItem);
UpdateButtonsState;
end;
procedure TToolBarConfig.btnRemoveClick(Sender: TObject);
begin
RemoveCommand;
end;
procedure TToolBarConfig.lvToolbarSelectItem(Sender: TObject;
Item: TListItem; Selected: Boolean);
begin
UpdateButtonsState;
end;
procedure TToolBarConfig.MoveUpDown(aOffset: integer);
var
Index1,Index2: Integer;
begin
Index1 := lvToolbar.ItemIndex;
Index2 := Index1 + aOffset;
lvToolbar.Items.Exchange(Index1,Index2);
lvToolbar.Items[Index1].Selected := False;
lvToolbar.Items[Index2].Selected := False;
lvToolbar.ItemIndex:= -1;
lvToolbar.Selected := Nil;
lvToolbar.ItemIndex:= Index2;
lvToolbar.Selected := lvToolbar.Items[Index2];
end;
procedure TToolBarConfig.btnMoveDownClick(Sender: TObject);
begin
if (lvToolbar.ItemIndex<0) or (lvToolbar.ItemIndex>=lvToolbar.Items.Count-2) then
Exit;
MoveUpDown(1);
end;
procedure TToolBarConfig.btnMoveUpClick(Sender: TObject);
begin
if (lvToolbar.ItemIndex<1) or (lvToolbar.ItemIndex>=lvToolbar.Items.Count-1) then
Exit;
MoveUpDown(-1);
end;
procedure TToolBarConfig.LoadCategories;
var
i, l: integer;
xCategory: TIDEToolButtonCategory;
xCaption: string;
Node: TTreeNode;
SortedCtgList: TStringListUTF8Fast;
begin
TV.Items.BeginUpdate;
SortedCtgList := TStringListUTF8Fast.Create;
try
SortedCtgList.OwnsObjects := False;
for i := 0 to IDEToolButtonCategories.Count-1 do
begin
xCategory := IDEToolButtonCategories[i];
SortedCtgList.AddObject(xCategory.Description, xCategory);
end;
SortCategories(SortedCtgList);
TV.Items.Clear;
for i := 0 to SortedCtgList.Count-1 do
begin
xCaption := SortedCtgList[i];
xCategory := SortedCtgList.Objects[i] as TIDEToolButtonCategory;
DeleteAmpersands(xCaption);
Node := TV.Items.AddChild(nil, Format('%s', [xCaption]));
for l := 0 to xCategory.ButtonCount-1 do
AddMenuItem(Node, xCategory.Buttons[l]);
end;
finally
SortedCtgList.Free;
TV.Items.EndUpdate;
end;
end;
procedure TToolBarConfig.SortCategories(ACtgList: TStrings);
var
NewIndex: Integer;
procedure MoveItem(s: String);
var
OldIndex: Integer;
begin
OldIndex := ACtgList.IndexOf(s);
if (OldIndex<0) or (NewIndex>=ACtgList.Count) then Exit;
ACtgList.Move(OldIndex, NewIndex);
Inc(NewIndex);
end;
begin
NewIndex := 0;
MoveItem(srkmCatFileMenu);
MoveItem(srkmCatCmdCmd);
MoveItem(srkmCatSelection);
MoveItem(srkmCatMacroRecording);
MoveItem(srkmCatSearchReplace);
MoveItem(srkmCatMarker);
MoveItem(srkmCatViewMenu);
MoveItem(srkmCatCodeTools);
MoveItem(srkmCatEditing);
MoveItem(srkmCatProjectMenu);
MoveItem(srkmCatRunMenu);
MoveItem(srkmCatPackageMenu);
MoveItem(srkmCatToolMenu);
MoveItem(srkmCatSrcNoteBook);
MoveItem(srkmCarHelpMenu);
end;
procedure TToolBarConfig.AddMenuItem(ParentNode: TTreeNode; CmdItem: TIDEButtonCommand);
var
Node: TTreeNode;
begin
if CmdItem.Caption = '-' then Exit; // divider
Node := TV.Items.AddChild(ParentNode, CmdItem.GetCaptionWithShortCut);
Node.ImageIndex := CmdItem.ImageIndex;
Node.SelectedIndex := CmdItem.ImageIndex;
Node.Data := CmdItem;
end;
function TToolBarConfig.RootNodeCaption(CmdItem: TIDEButtonCommand): string;
begin
case CmdItem.Caption of
'IDEMainMenu': Result := lisCoolbarIDEMainMenu; // mnuMain
'SourceTab': Result := lisCoolbarSourceTab; // SourceTabMenuRootName
'SourceEditor': Result := lisCoolbarSourceEditor; // SourceEditorMenuRootName
'Messages': Result := lisCoolbarMessages; // MessagesMenuRootName
'Code Explorer': Result := lisCoolbarCodeExplorer; // CodeExplorerMenuRootName
'CodeTemplates': Result := lisCoolbarCodeTemplates; // CodeTemplatesMenuRootName
'Designer': Result := lisCoolbarDesigner; // DesignerMenuRootName
'PackageEditor': Result := lisCoolbarPackageEditor; // PackageEditorMenuRootName
'PackageEditorFiles': Result := lisCoolbarPackageEditorFiles // PackageEditorMenuFilesRootName
else Result := CmdItem.Caption;
end;
end;
procedure TToolBarConfig.AddToolBarItem(CmdItem: TIDEButtonCommand);
var
Node: TTreeNode;
lvItem: TListItem;
begin
if CmdItem=Nil then Exit;
lvItem := lvToolbar.Items.Add;
lvItem.Caption := CmdItem.GetCaptionWithShortCut;
lvItem.Data := CmdItem;
if CmdItem.ImageIndex > -1 then
lvItem.ImageIndex := CmdItem.ImageIndex
else
lvItem.ImageIndex := defImageIndex;
Node := TV.Items.FindNodeWithData(CmdItem);
if Node<>nil then
Node.Visible := False;
end;
procedure TToolBarConfig.AddDivider;
var
lvItem: TListItem;
begin
lvItem := lvToolbar.Items.Add;
lvItem.Caption := cIDEToolbarDivider;
lvItem.ImageIndex := -1;
end;
procedure TToolBarConfig.AddTailItem;
// An extra item at the end of list so that new command can be inserted there.
// TToolBarConfig.SaveSettings excludes this item from saving.
// In lvToolbar this item may only be selected, any actions with it are prohibited.
var
lvItem: TListItem;
begin
lvItem := lvToolbar.Items.Add;
lvItem.Caption := cTailItemCaption;
end;
procedure TToolBarConfig.LoadSettings(SL: TStringList);
var
I: Integer;
Value: string;
Cmd: TIDEButtonCommand;
begin
for I := 0 to SL.Count - 1 do
begin
Value := SL[I];
if Value = '' then Continue;
if Value = cIDEToolbarDivider then
AddDivider // Add divider.
else
begin
Cmd := IDEToolButtonCategories.FindItemByMenuPathOrName(Value);
AddToolBarItem(Cmd); // Add command.
//if Value <> SL[I] then
// DebugLn(['TToolBarConfig.LoadSettings: SL[I]=', SL[I], ', Value=', Value]);
SL[I] := Value;
end;
end;
AddTailItem; // Add tail item at the end.
lvToolbar.ItemIndex:=lvToolbar.Items.Count-1;
end;
procedure TToolBarConfig.SaveSettings(SL: TStringList);
var
lvItem: TListItem;
Cmd: TIDEButtonCommand;
I: Integer;
begin
SL.Clear;
for I := 0 to lvToolbar.Items.Count - 2 do // excluding tail item
begin
lvItem := lvToolbar.Items[I];
Cmd := TIDEButtonCommand(lvItem.Data);
if lvItem.Caption = cIDEToolbarDivider then
SL.Add(cIDEToolbarDivider)
else begin
Assert(Assigned(Cmd), 'TToolBarConfig.SaveSettings: Cmd = Nil.');
SL.Add(Cmd.Name);
end;
end;
end;
{ TIDEToolbarBase }
procedure TIDEToolbarBase.AddButton(ACommand: TIDEButtonCommand);
var
B: TIDEToolButton;
begin
B := ACommand.ToolButtonClass.Create(FToolBar);
B.Hint := ACommand.GetHintOrCaptionWithShortCut;
B.Enabled := ACommand.Enabled;
// If we have a image, use it. Otherwise supply a default.
if ACommand.ImageIndex <> -1 then
B.ImageIndex := ACommand.ImageIndex
else
B.ImageIndex := IDEImages.LoadImage('execute');
B.Style := tbsButton;
B.Item := ACommand;
PositionAtEnd(FToolBar, B);
ACommand.ToolButtonAdded(B);
end;
procedure TIDEToolbarBase.AddDivider;
var
B: TToolButton;
begin
B := TToolButton.Create(FToolBar);
B.Style := tbsDivider;
PositionAtEnd(FToolBar, B);
end;
procedure TIDEToolbarBase.CopyFromOptions(Options: TIDEToolBarOptionsBase);
var
Cmd: TIDEButtonCommand;
ButtonName: string;
i: Integer;
begin
FToolBar.BeginUpdate;
try
for i := 0 to Options.ButtonNames.Count-1 do
begin
ButtonName := Options.ButtonNames[i];
if ButtonName = cIDEToolbarDivider then
AddDivider
else
begin
Cmd := IDEToolButtonCategories.FindItemByMenuPathOrName(ButtonName);
Options.ButtonNames[i] := ButtonName;
if Assigned(Cmd) then
AddButton(Cmd);
end;
end;
PostCopyOptions;
finally
FToolBar.EndUpdate;
end;
end;
procedure TIDEToolbarBase.PositionAtEnd(AToolBar: TToolBar; AButton: TToolButton);
// position the button next to the last button
var
SiblingButton: TToolButton;
begin
if AToolBar.ButtonCount > 0 then
begin
SiblingButton := AToolBar.Buttons[AToolBar.ButtonCount-1];
AButton.SetBounds(SiblingButton.Left + SiblingButton.Width,
SiblingButton.Top, AButton.Width, AButton.Height);
end;
AButton.Parent := AToolBar;
end;
procedure TIDEToolbarBase.PostCopyOptions;
begin
// Can be overridden.
end;
end.