1 {
2   Copyright (C) 2007 Graeme Geldenhuys (graemeg@gmail.com)
3 
4   This library is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Library General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or (at your
7   option) any later version.
8 
9   This program is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
12   for more details.
13 
14   You should have received a copy of the GNU Library General Public License
15   along with this library; if not, write to the Free Software Foundation,
16   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
17 }
18 
19 unit ToolbarConfig;
20 
21 {$mode objfpc}{$H+}
22 
23 interface
24 
25 uses
26   Classes, SysUtils,
27   // LCL
28   LCLProc, LclIntf, Controls, Forms, Graphics, ExtCtrls, Buttons, StdCtrls,
29   ComCtrls, Menus, ButtonPanel,
30   // LazControls
31   TreeFilterEdit,
32   // LazUtils
33   Laz2_XMLCfg, LazUTF8,
34   // IdeIntf
35   ToolBarIntf, IDEImagesIntf, IDEWindowIntf,
36   // IDE
37   LazarusIDEStrConsts;
38 
39 const
40   IDEToolBarConfigVersion = 1;  // File version in configuration.
41 
42 type
43   { TToolBarConfig }
44 
45   TToolBarConfig = class(TForm)
46     btnAdd: TSpeedButton;
47     btnAddDivider: TSpeedButton;
48     btnCancel: TButton;
49     btnHelp: TBitBtn;
50     btnMoveDown: TSpeedButton;
51     btnMoveUp: TSpeedButton;
52     btnOK: TButton;
53     btnRemove: TSpeedButton;
54     FilterEdit: TTreeFilterEdit;
55     lblMenuTree: TLabel;
56     lblToolbar: TLabel;
57     lvToolbar: TListView;
58     miAll: TMenuItem;
59     miCustom: TMenuItem;
60     miDebug: TMenuItem;
61     miDesign: TMenuItem;
62     miHTML: TMenuItem;
63     pnlButtons: TButtonPanel;
64     Splitter1: TSplitter;
65     TV: TTreeView;
66     procedure btnHelpClick(Sender: TObject);
67     procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
68     procedure FormCreate(Sender: TObject);
69     procedure FormDestroy(Sender: TObject);
70     procedure lvToolbarDblClick(Sender: TObject);
71     procedure lvToolbarEnterExit(Sender: TObject);
72     procedure TVDblClick(Sender: TObject);
73     procedure UpdateButtonsState;
74     procedure btnAddClick(Sender: TObject);
75     procedure btnAddDividerClick(Sender: TObject);
76     procedure btnMoveDownClick(Sender: TObject);
77     procedure btnMoveUpClick(Sender: TObject);
78     procedure btnRemoveClick(Sender: TObject);
79     procedure lvToolbarSelectItem(Sender: TObject; {%H-}Item: TListItem;
80       {%H-}Selected: Boolean);
81     procedure TVSelectionChanged(Sender: TObject);
82   private
83     Image: TBitMap;
84     defImageIndex: integer;
85     procedure AddCommand;
86     procedure AddDivider;
87     procedure AddTailItem;
88     procedure AddToolBarItem(CmdItem: TIDEButtonCommand);
89     procedure InsertItem(Item: TListItem);
90     procedure MoveUpDown(aOffset: integer);
NewLvItemnull91     function NewLvItem(aCaption: string): TListItem;
92     procedure RemoveCommand;
93     procedure SetupCaptions;
94     procedure LoadCategories;
95     procedure SortCategories(ACtgList: TStrings);
96     procedure AddMenuItem(ParentNode: TTreeNode; CmdItem: TIDEButtonCommand);
RootNodeCaptionnull97     function RootNodeCaption(CmdItem: TIDEButtonCommand): string;
98   public
99     procedure LoadSettings(SL: TStringList);
100     procedure SaveSettings(SL: TStringList);
101   end;
102 
103   { TIDEToolBarOptionsBase }
104 
105   TIDEToolBarOptionsBase = class
106   private
107     FButtonNames: TStringList;
108   protected
109     procedure LoadButtonNames(XMLConfig: TXMLConfig; SubPath: String);
110     procedure SaveButtonNames(XMLConfig: TXMLConfig; SubPath: String);
111   public
112     constructor Create;
113     destructor Destroy; override;
114     procedure Clear;
Equalsnull115     function Equals(Opts: TIDEToolBarOptionsBase): boolean; overload;
116     procedure Assign(Source: TIDEToolBarOptionsBase);
117     //procedure Load(XMLConfig: TXMLConfig; Path: String);
118     //procedure Save(XMLConfig: TXMLConfig; Path: String);
119   published
120     property ButtonNames: TStringList read FButtonNames; // write FButtonNames;
121   end;
122 
123   { TIDEToolbarBase }
124 
125   TIDEToolbarBase = class(TComponent)
126   private
127   protected
128     FToolBar: TToolBar;
129     procedure AddButton(ACommand: TIDEButtonCommand);
130     procedure AddDivider;
131     procedure CopyFromOptions(Options: TIDEToolBarOptionsBase);
132     procedure PositionAtEnd(AToolBar: TToolBar; AButton: TToolButton);
133     procedure PostCopyOptions; virtual;
134   public
135     //constructor Create(AOwner: TComponent); override;
136     //destructor Destroy; override;
137     property ToolBar: TToolBar read FToolBar;
138   end;
139 
140 const
141   cIDEToolbarDivider = '---------------';
142   cTailItemCaption = '                                             ';
143 
ShowToolBarConfignull144 function ShowToolBarConfig(aNames: TStringList): TModalResult;
145 
146 
147 implementation
148 
149 {$R *.lfm}
150 
ShowToolBarConfignull151 function ShowToolBarConfig(aNames: TStringList): TModalResult;
152 var
153   Conf: TToolBarConfig;
154 begin
155   Conf := TToolBarConfig.Create(Nil);
156   try
157     if Assigned(aNames) then
158       Conf.LoadSettings(aNames);
159     Result := Conf.ShowModal;
160     if (Result = mrOK) and Assigned(aNames) then
161       Conf.SaveSettings(aNames);
162   finally
163     Conf.Free;
164   end;
165 end;
166 
167 { TToolBarConfig }
168 
169 procedure TToolBarConfig.FormCreate(Sender: TObject);
170 begin
171   inherited;
172   pnlButtons.Color := clBtnFace;
173   // load button images
174   IDEImages.AssignImage(btnAdd, 'arrow__darkgreen_right');
175   IDEImages.AssignImage(btnRemove, 'arrow__darkred_left');
176   IDEImages.AssignImage(btnMoveUp, 'arrow__darkgreen_up');
177   IDEImages.AssignImage(btnMoveDown, 'arrow__darkgreen_down');
178   //IDEImages.AssignImage(btnAddDivider, 'menu_divider16');  // uncomment if 'menu_divider16' exists (currently not)
179 
180   btnAddDivider.Caption := '---';
181   btnAdd.Hint       := lisCoolBarAddSelected;
182   btnRemove.Hint    := lisCoolBarRemoveSelected;
183   btnMoveUp.Hint    := lisCoolBarMoveSelectedUp;
184   btnMoveDown.Hint  := lisCoolBarMoveSelectedDown;
185   btnAddDivider.Hint:= lisCoolBarAddDivider;
186 
187   TV.Images := IDEImages.Images_16;
188   lvToolbar.SmallImages := IDEImages.Images_16;
189   // default image to be used when none is available
190   defImageIndex := IDEImages.LoadImage('execute');
191 
192   Image := TBitmap.Create;
193   SetupCaptions;
194   LoadCategories;
195   IDEDialogLayoutList.ApplyLayout(Self);
196 end;
197 
198 procedure TToolBarConfig.FormClose(Sender: TObject; var CloseAction: TCloseAction);
199 begin
200   IDEDialogLayoutList.SaveLayout(Self);
201 end;
202 
203 procedure TToolBarConfig.FormDestroy(Sender: TObject);
204 begin
205   Image.Free;
206 end;
207 
208 procedure TToolBarConfig.lvToolbarDblClick(Sender: TObject);
209 begin
210   RemoveCommand;
211 end;
212 
213 procedure TToolBarConfig.lvToolbarEnterExit(Sender: TObject);
214 begin
215   UpdateButtonsState;
216 end;
217 
218 procedure TToolBarConfig.TVDblClick(Sender: TObject);
219 begin
220   AddCommand;
221 end;
222 
223 procedure TToolBarConfig.btnHelpClick(Sender: TObject);
224 begin
225   OpenUrl('http://wiki.freepascal.org/IDE_Window:_Toolbar_Config');
226 end;
227 
228 procedure TToolBarConfig.UpdateButtonsState;
229 var
230   I: Integer;
231 begin
232   I := lvToolbar.ItemIndex;
233   btnAdd.Enabled := Assigned(TV.Selected) and Assigned(TV.Selected.Data);
234   btnRemove.Enabled := (I>-1) and (I<lvToolbar.Items.Count-1);
235   btnMoveUp.Enabled := (I>0) and (I<lvToolbar.Items.Count-1);
236   btnMoveDown.Enabled := (I>-1) and (I<lvToolbar.Items.Count-2);
237   btnAddDivider.Enabled := True;
238 end;
239 
240 procedure TToolBarConfig.TVSelectionChanged(Sender: TObject);
241 begin
242   UpdateButtonsState;
243 end;
244 
245 procedure TToolBarConfig.InsertItem(Item: TListItem);
246 begin
247   lvToolbar.ItemIndex := -1;
248   lvToolbar.Selected := nil;
249   if Item.Index < lvToolbar.Items.Count then
250     lvToolbar.ItemIndex := Item.Index+1
251   else
252     lvToolbar.ItemIndex := Item.Index;
253 end;
254 
255 procedure TToolBarConfig.btnAddClick(Sender: TObject);
256 begin
257   AddCommand;
258 end;
259 
NewLvItemnull260 function TToolBarConfig.NewLvItem(aCaption: string): TListItem;
261 var
262   I: Integer;
263 begin
264   I := lvToolbar.ItemIndex;
265   if I = -1 then
266     I := lvToolbar.Items.Count-1;    // Add before the last empty item.
267   Result := lvToolbar.Items.Insert(I);
268   Result.Caption := aCaption;
269 end;
270 
271 procedure TToolBarConfig.AddCommand;
272 var
273   Node: TTreeNode;
274   CmdCaption: string;
275   lvItem: TListItem;
276 begin
277   Node := TV.Selected;
278   if (Node = Nil) or (Node.Data = Nil) then
279     Exit;
280   CmdCaption := TIDEButtonCommand(Node.Data).Caption;
281   DeleteAmpersands(CmdCaption);
282   lvItem := NewLvItem(CmdCaption);
283   lvItem.Data := Node.Data;
284   if Node.ImageIndex > -1 then
285     lvItem.ImageIndex := Node.ImageIndex
286   else
287     lvItem.ImageIndex := defImageIndex;
288   InsertItem(lvItem);                  // Add the newly created item to ListView.
289   // Update selection in TreeView.
290   Node := TV.Selected.GetNext;
291   TV.Selected.Visible := False;
292   if Node <> nil then
293     TV.Selected := Node;
294   UpdateButtonsState;
295 end;
296 
297 procedure TToolBarConfig.RemoveCommand;
298 Var
299   Cmd: TIDEButtonCommand;
300   Node: TTreeNode;
301   I: Integer;
302 begin
303   I := lvToolbar.ItemIndex;
304   if (I<0) or (I>=lvToolbar.Items.Count-1) then Exit;
305   Cmd := TIDEButtonCommand(lvToolbar.Items[I].Data);
306   lvToolbar.Items.Delete(I);
307   {$IF DEFINED(LCLQt) or DEFINED(LCLQt5)}
308   lvToolbar.ItemIndex := -1;     // Try to make LCLQt behave.
309   lvToolbar.ItemIndex := I;
310   {$ENDIF}
311   lvToolbar.Selected := lvToolbar.Items[I];
312   // Show the command as available again in TreeView.
313   if Assigned(Cmd) then
314   begin
315     Node:= TV.Items.FindNodeWithData(Cmd);
316     if Node<>nil then
317       Node.Visible:= True;
318   end;
319   UpdateButtonsState;
320 end;
321 
322 procedure TToolBarConfig.btnAddDividerClick(Sender: TObject);
323 var
324   lvItem: TListItem;
325 begin
326   lvItem := NewLvItem(cIDEToolbarDivider);
327   lvItem.ImageIndex := -1;
328   InsertItem(lvItem);
329   UpdateButtonsState;
330 end;
331 
332 procedure TToolBarConfig.btnRemoveClick(Sender: TObject);
333 begin
334   RemoveCommand;
335 end;
336 
337 procedure TToolBarConfig.lvToolbarSelectItem(Sender: TObject;
338   Item: TListItem; Selected: Boolean);
339 begin
340   UpdateButtonsState;
341 end;
342 
343 procedure TToolBarConfig.MoveUpDown(aOffset: integer);
344 var
345   Index1,Index2: Integer;
346 begin
347   Index1 := lvToolbar.ItemIndex;
348   Index2 := Index1 + aOffset;
349   lvToolbar.Items.Exchange(Index1,Index2);
350   lvToolbar.Items[Index1].Selected := False;
351   lvToolbar.Items[Index2].Selected := False;
352   lvToolbar.ItemIndex:= -1;
353   lvToolbar.Selected := Nil;
354   lvToolbar.ItemIndex:= Index2;
355   lvToolbar.Selected := lvToolbar.Items[Index2];
356 end;
357 
358 procedure TToolBarConfig.btnMoveDownClick(Sender: TObject);
359 begin
360   if (lvToolbar.ItemIndex<0) or (lvToolbar.ItemIndex>=lvToolbar.Items.Count-2) then
361     Exit;
362   MoveUpDown(1);
363 end;
364 
365 procedure TToolBarConfig.btnMoveUpClick(Sender: TObject);
366 begin
367   if (lvToolbar.ItemIndex<1) or (lvToolbar.ItemIndex>=lvToolbar.Items.Count-1) then
368     Exit;
369   MoveUpDown(-1);
370 end;
371 
372 procedure TToolBarConfig.SetupCaptions;
373 begin
374   Caption             := lisToolbarConfiguration;
375   lblMenuTree.Caption := lisCoolbarAvailableCommands;
376   lblToolbar.Caption  := lisCoolbarToolbarCommands;
377 end;
378 
379 procedure TToolBarConfig.LoadCategories;
380 var
381   i, l: integer;
382   xCategory: TIDEToolButtonCategory;
383   xCaption: string;
384   Node: TTreeNode;
385   SortedCtgList: TStringListUTF8Fast;
386 begin
387   TV.Items.BeginUpdate;
388   SortedCtgList := TStringListUTF8Fast.Create;
389   try
390     SortedCtgList.OwnsObjects := False;
391     for i := 0 to IDEToolButtonCategories.Count-1 do
392     begin
393       xCategory := IDEToolButtonCategories[i];
394       SortedCtgList.AddObject(xCategory.Description, xCategory);
395     end;
396     SortCategories(SortedCtgList);
397 
398     TV.Items.Clear;
399     for i := 0 to SortedCtgList.Count-1 do
400     begin
401       xCaption := SortedCtgList[i];
402       xCategory := SortedCtgList.Objects[i] as TIDEToolButtonCategory;
403       DeleteAmpersands(xCaption);
404       Node := TV.Items.AddChild(nil, Format('%s', [xCaption]));
405       for l := 0 to xCategory.ButtonCount-1 do
406         AddMenuItem(Node, xCategory.Buttons[l]);
407     end;
408   finally
409     SortedCtgList.Free;
410     TV.Items.EndUpdate;
411   end;
412 end;
413 
414 procedure TToolBarConfig.SortCategories(ACtgList: TStrings);
415 var
416   NewIndex: Integer;
417 
418   procedure MoveItem(s: String);
419   var
420     OldIndex: Integer;
421   begin
422     OldIndex := ACtgList.IndexOf(s);
423     if (OldIndex<0) or (NewIndex>=ACtgList.Count) then Exit;
424     ACtgList.Move(OldIndex, NewIndex);
425     Inc(NewIndex);
426   end;
427 
428 begin
429   NewIndex := 0;
430   MoveItem(srkmCatFileMenu);
431   MoveItem(srkmCatCmdCmd);
432   MoveItem(srkmCatSelection);
433   MoveItem(srkmCatMacroRecording);
434   MoveItem(srkmCatSearchReplace);
435   MoveItem(srkmCatMarker);
436   MoveItem(srkmCatViewMenu);
437   MoveItem(srkmCatCodeTools);
438   MoveItem(srkmCatEditing);
439   MoveItem(srkmCatProjectMenu);
440   MoveItem(srkmCatRunMenu);
441   MoveItem(srkmCatPackageMenu);
442   MoveItem(srkmCatToolMenu);
443   MoveItem(srkmCatSrcNoteBook);
444   MoveItem(srkmCarHelpMenu);
445 end;
446 
447 procedure TToolBarConfig.AddMenuItem(ParentNode: TTreeNode; CmdItem: TIDEButtonCommand);
448 var
449   Node: TTreeNode;
450 begin
451   if CmdItem.Caption = '-' then Exit;   // divider
452   Node := TV.Items.AddChild(ParentNode, Format('%s', [CmdItem.GetCaptionWithShortCut]));
453   Node.ImageIndex := CmdItem.ImageIndex;
454   Node.SelectedIndex := CmdItem.ImageIndex;
455   Node.Data := CmdItem;
456 end;
457 
TToolBarConfig.RootNodeCaptionnull458 function TToolBarConfig.RootNodeCaption(CmdItem: TIDEButtonCommand): string;
459 begin
460   case CmdItem.Caption of
461     'IDEMainMenu':        Result := lisCoolbarIDEMainMenu;    // mnuMain
462     'SourceTab':          Result := lisCoolbarSourceTab;      // SourceTabMenuRootName
463     'SourceEditor':       Result := lisCoolbarSourceEditor;   // SourceEditorMenuRootName
464     'Messages':           Result := lisCoolbarMessages;       // MessagesMenuRootName
465     'Code Explorer':      Result := lisCoolbarCodeExplorer;   // CodeExplorerMenuRootName
466     'CodeTemplates':      Result := lisCoolbarCodeTemplates;  // CodeTemplatesMenuRootName
467     'Designer':           Result := lisCoolbarDesigner;       // DesignerMenuRootName
468     'PackageEditor':      Result := lisCoolbarPackageEditor;  // PackageEditorMenuRootName
469     'PackageEditorFiles': Result := lisCoolbarPackageEditorFiles // PackageEditorMenuFilesRootName
470     else                  Result := CmdItem.Caption;
471   end;
472 end;
473 
474 procedure TToolBarConfig.AddToolBarItem(CmdItem: TIDEButtonCommand);
475 Var
476   Node: TTreeNode;
477   lvItem: TListItem;
478 begin
479   if CmdItem=Nil then Exit;
480   lvItem := lvToolbar.Items.Add;
481   lvItem.Caption := CmdItem.GetCaptionWithShortCut;
482   lvItem.Data := CmdItem;
483   if CmdItem.ImageIndex > -1 then
484     lvItem.ImageIndex := CmdItem.ImageIndex
485   else
486     lvItem.ImageIndex := defImageIndex;
487   Node := TV.Items.FindNodeWithData(CmdItem);
488   if Node<>nil then
489     Node.Visible := False;
490 end;
491 
492 procedure TToolBarConfig.AddDivider;
493 var
494   lvItem: TListItem;
495 begin
496   lvItem := lvToolbar.Items.Add;
497   lvItem.Caption := cIDEToolbarDivider;
498   lvItem.ImageIndex := -1;
499 end;
500 
501 procedure TToolBarConfig.AddTailItem;
502 // An extra item at the end of list so that new command can be inserted there.
503 // TToolBarConfig.SaveSettings excludes this item from saving.
504 // In lvToolbar this item may only be selected, any actions with it are prohibited.
505 var
506   lvItem: TListItem;
507 begin
508   lvItem := lvToolbar.Items.Add;
509   lvItem.Caption := cTailItemCaption;
510 end;
511 
512 procedure TToolBarConfig.LoadSettings(SL: TStringList);
513 var
514   I: Integer;
515   Value: string;
516   Cmd: TIDEButtonCommand;
517 begin
518   for I := 0 to SL.Count - 1 do
519   begin
520     Value := SL[I];
521     if Value = '' then Continue;
522     if Value = cIDEToolbarDivider then
523       AddDivider                // Add divider.
524     else
525     begin
526       Cmd := IDEToolButtonCategories.FindItemByMenuPathOrName(Value);
527       AddToolBarItem(Cmd);      // Add command.
528       if Value <> SL[I] then
529         DebugLn(['TToolBarConfig.LoadSettings: SL[I]=', SL[I], ', Value=', Value]);
530       SL[I] := Value;
531     end;
532   end;
533   AddTailItem;                  // Add tail item at the end.
534   lvToolbar.ItemIndex:=lvToolbar.Items.Count-1;
535 end;
536 
537 procedure TToolBarConfig.SaveSettings(SL: TStringList);
538 var
539   lvItem: TListItem;
540   Cmd: TIDEButtonCommand;
541   I: Integer;
542 begin
543   SL.Clear;
544   for I := 0 to lvToolbar.Items.Count - 2 do  // excluding tail item
545   begin
546     lvItem := lvToolbar.Items[I];
547     Cmd := TIDEButtonCommand(lvItem.Data);
548     if lvItem.Caption = cIDEToolbarDivider then
549       SL.Add(cIDEToolbarDivider)
550     else begin
551       Assert(Assigned(Cmd), 'TToolBarConfig.SaveSettings: Cmd = Nil.');
552       SL.Add(Cmd.Name);
553     end;
554   end;
555 end;
556 
557 { TIDEToolBarOptionsBase }
558 
559 constructor TIDEToolBarOptionsBase.Create;
560 begin
561   FButtonNames := TStringList.Create;
562 end;
563 
564 destructor TIDEToolBarOptionsBase.Destroy;
565 begin
566   FButtonNames.Free;
567   inherited Destroy;
568 end;
569 
570 procedure TIDEToolBarOptionsBase.Clear;
571 begin
572   FButtonNames.Clear;
573 end;
574 
Equalsnull575 function TIDEToolBarOptionsBase.Equals(Opts: TIDEToolBarOptionsBase): boolean;
576 begin
577   Result := FButtonNames.Equals(Opts.FButtonNames);
578 end;
579 
580 procedure TIDEToolBarOptionsBase.Assign(Source: TIDEToolBarOptionsBase);
581 begin
582   FButtonNames.Assign(Source.FButtonNames);
583 end;
584 
585 procedure TIDEToolBarOptionsBase.LoadButtonNames(XMLConfig: TXMLConfig; SubPath: String);
586 var
587   ButtonCount: Integer;
588   ButtonName: string;
589   I, FileVersion: Integer;
590 begin
591   FileVersion := XMLConfig.GetValue(SubPath + 'Version', 0);
592   ButtonCount := XMLConfig.GetValue(SubPath + 'Count', 0);
593   if (FileVersion < 1) and (ButtonCount = 0) then  // Old format
594     ButtonCount := XMLConfig.GetValue(SubPath + 'ButtonCount/Value', 0);
595   for I := 1 to ButtonCount do
596   begin
597     ButtonName := XMLConfig.GetValue(SubPath + 'Button' + IntToStr(I) + '/Name', '');
598     if (FileVersion < 1) and (ButtonName = '') then  // Old format
599       ButtonName := XMLConfig.GetValue(SubPath + 'Buttons/Name' + IntToStr(I) + '/Value', '');
600     if ButtonName <> '' then
601       ButtonNames.Add(ButtonName);
602   end;
603 end;
604 
605 procedure TIDEToolBarOptionsBase.SaveButtonNames(XMLConfig: TXMLConfig; SubPath: String);
606 var
607   I: Integer;
608 begin
609   XMLConfig.SetValue(SubPath + 'Version', IDEToolBarConfigVersion);
610   XMLConfig.SetDeleteValue(SubPath + 'Count', ButtonNames.Count, 0);
611   for I := 0 to ButtonNames.Count-1 do
612     XMLConfig.SetDeleteValue(SubPath + 'Button' + IntToStr(I+1) + '/Name', ButtonNames[I], '');
613 end;
614 
615 { TIDEToolbarBase }
616 
617 procedure TIDEToolbarBase.AddButton(ACommand: TIDEButtonCommand);
618 var
619   B: TIDEToolButton;
620 begin
621   B := ACommand.ToolButtonClass.Create(FToolBar);
622   B.Hint := ACommand.GetHintOrCaptionWithShortCut;
623   B.Enabled := ACommand.Enabled;
624   // If we have a image, use it. Otherwise supply a default.
625   if ACommand.ImageIndex <> -1 then
626     B.ImageIndex := ACommand.ImageIndex
627   else
628     B.ImageIndex := IDEImages.LoadImage('execute');
629   B.Style := tbsButton;
630   B.Item := ACommand;
631   PositionAtEnd(FToolBar, B);
632   ACommand.ToolButtonAdded(B);
633 end;
634 
635 procedure TIDEToolbarBase.AddDivider;
636 var
637   B: TToolButton;
638 begin
639   B := TToolButton.Create(FToolBar);
640   B.Style := tbsDivider;
641   PositionAtEnd(FToolBar, B);
642 end;
643 
644 procedure TIDEToolbarBase.CopyFromOptions(Options: TIDEToolBarOptionsBase);
645 var
646   Cmd: TIDEButtonCommand;
647   ButtonName: string;
648   i: Integer;
649 begin
650   FToolBar.BeginUpdate;
651   try
652     for i := 0 to Options.ButtonNames.Count-1 do
653     begin
654       ButtonName := Options.ButtonNames[i];
655       if ButtonName = cIDEToolbarDivider then
656         AddDivider
657       else
658       begin
659         Cmd := IDEToolButtonCategories.FindItemByMenuPathOrName(ButtonName);
660         Options.ButtonNames[i] := ButtonName;
661         if Assigned(Cmd) then
662           AddButton(Cmd);
663       end;
664     end;
665     PostCopyOptions;
666   finally
667     FToolBar.EndUpdate;
668   end;
669 end;
670 
671 procedure TIDEToolbarBase.PositionAtEnd(AToolBar: TToolBar; AButton: TToolButton);
672 // position the button next to the last button
673 var
674   SiblingButton: TToolButton;
675 begin
676   if AToolBar.ButtonCount > 0 then
677   begin
678     SiblingButton := AToolBar.Buttons[AToolBar.ButtonCount-1];
679     AButton.SetBounds(SiblingButton.Left + SiblingButton.Width,
680       SiblingButton.Top, AButton.Width, AButton.Height);
681   end;
682   AButton.Parent := AToolBar;
683 end;
684 
685 procedure TIDEToolbarBase.PostCopyOptions;
686 begin
687   // Can be overridden.
688 end;
689 
690 end.
691 
692