1 {
2  /***************************************************************************
3                           componentpalette.pas
4                           --------------------
5 
6 
7  ***************************************************************************/
8 
9  ***************************************************************************
10  *                                                                         *
11  *   This source is free software; you can redistribute it and/or modify   *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  *   This code is distributed in the hope that it will be useful, but      *
17  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
19  *   General Public License for more details.                              *
20  *                                                                         *
21  *   A copy of the GNU General Public License is available on the World    *
22  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
23  *   obtain it by writing to the Free Software Foundation,                 *
24  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
25  *                                                                         *
26  ***************************************************************************
27 
28   Author: Mattias Gaertner, Juha Manninen
29 
30   Abstract:
31    The implementation of the component palette.
32    Supports reordering of pages and components by user settings in environment options.
33 }
34 unit ComponentPalette;
35 
36 {$mode objfpc}{$H+}
37 
38 interface
39 
40 uses
41   Classes, SysUtils, Laz_AVL_Tree,
42   // LCL
43   LCLProc, Controls, Forms, Graphics, ComCtrls, Buttons, Menus, ExtCtrls,
44   // LazUtils
45   LazFileUtils, LazFileCache, AvgLvlTree,
46   // IdeIntf
47   FormEditingIntf, LazIDEIntf, IDEImagesIntf, PropEdits, ComponentReg,
48   // IDE
49   ComponentPalette_Options,
50   MainBase, LazarusIDEStrConsts, DesignerProcs, PackageDefs, EnvironmentOpts,
51   ImgList;
52 
53 const
54   CompPalSelectionToolBtnPrefix = 'PaletteSelectBtn';
55   CompPaletteCompBtnPrefix = 'PaletteBtn';
56 type
57   { TComponentPage }
58 
59   TComponentPage = class(TBaseComponentPage)
60   private
61     fPageComponent: TCustomPage;
62     fSelectButton: TComponent;
63     fBtnIndex: integer;
64     fRegComps: TRegisteredCompList;   // Reference to components.
65     fGuiCreated: Boolean;
66     procedure ReAlignButtons;
67     procedure RemoveSheet;
68     procedure InsertVisiblePage(aCompNames: TRegisteredCompList);
69     procedure CreateSelectionButton(aButtonUniqueName: string; aScrollBox: TScrollBox);
70     procedure CreateOrDelButton(aComp: TPkgComponent; aButtonUniqueName: string;
71       aScrollBox: TScrollBox);
72     procedure CreateButtons;
73   protected
74   public
75     constructor Create(const ThePageName: string);
76     destructor Destroy; override;
GetScrollBoxnull77     function GetScrollBox: TScrollBox;
78   public
79     property PageComponent: TCustomPage read fPageComponent write fPageComponent;
80     property SelectButton: TComponent read fSelectButton write fSelectButton;
81   end;
82 
83   { TComponentPalette }
84 
85   TComponentPalette = class(TBaseComponentPalette)
86     PalettePopupMenu: TPopupMenu;
87     PopupMenu: TPopupMenu;
88     OpenPackageMenuItem: TMenuItem;
89     OpenUnitMenuItem: TMenuItem;
90     procedure ActivePageChanged(Sender: TObject);
91     procedure OnScrollBoxResize(Sender: TObject);
92     procedure OpenPackageClicked(Sender: TObject);
93     procedure OpenUnitClicked(Sender: TObject);
94     procedure ComponentListClicked(Sender: TObject);
95     procedure OptionsClicked(Sender: TObject);
96     procedure PalettePopupMenuPopup(Sender: TObject);
97     procedure PopupMenuPopup(Sender: TObject);
98   private
99     // Generics TFPGMap<> is bocus, being actually a sorted list, slow adding items.
100     // Instead use a tree container as Map<TComponentClass,TSpeedButton>.
101     fComponentButtons: TPointerToPointerTree;
102     // Visual container for tabs
103     FPageControl: TPageControl;
104     FOnOpenPackage: TNotifyEvent;
105     FOnOpenUnit: TNotifyEvent;
106     FOnChangeActivePage: TNotifyEvent;
107     fSelectButtonIcon: TCustomBitmap;
108     fUpdatingPageControl: boolean;
109     fUpdateLock: integer;
110     // Used by UpdateNoteBookButtons
111     fOldActivePage: TTabSheet;
112     fVisiblePageIndex: integer;
113     procedure ClearButtons;
FindCompByButtonnull114     function FindCompByButton(Button: TSpeedButton): TRegisteredComponent;
FindPkgCompByButtonnull115     function FindPkgCompByButton(Button: TComponent): TPkgComponent;
IndexOfPageComponentnull116     function IndexOfPageComponent(AComponent: TComponent): integer;
117     procedure ReAlignButtons(aSheet: TCustomPage);
118     procedure UpdateNoteBookButtons(ForceUpdateAll: Boolean);
119     procedure RemoveUnneededPage(aSheet: TCustomPage);
120     procedure SetPageControl(const AValue: TPageControl);
121     procedure SelectionToolClick(Sender: TObject);
122     procedure ComponentBtnMouseDown(Sender: TObject; Button: TMouseButton;
123       Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
124     procedure ComponentBtnMouseUp(Sender: TObject; {%H-}Button: TMouseButton;
125       {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
126     procedure ComponentBtnDblClick(Sender: TObject);
127     procedure OnPageMouseWheel(Sender: TObject; {%H-}Shift: TShiftState;
128       WheelDelta: Integer; {%H-}MousePos: TPoint; var Handled: Boolean);
129     procedure CreatePopupMenu;
130     procedure UnselectAllButtons;
131     procedure SelectionWasChanged;
132     procedure GetUnregisteredIcon(var ImageList: TCustomImageList; var ImageIndex: TImageIndex);
GetSelectButtonIconnull133     function GetSelectButtonIcon: TCustomBitmap;
SelectAButtonnull134     function SelectAButton(Button: TSpeedButton): boolean;
135     procedure ComponentWasAdded({%H-}ALookupRoot, {%H-}AComponent: TComponent;
136                                 {%H-}ARegisteredComponent: TRegisteredComponent);
137   protected
138     procedure DoChange; override;
139   public
140     constructor Create;
141     destructor Destroy; override;
142     procedure OnGetNonVisualCompIcon(Sender: TObject;
143       AComponent: TComponent; var ImageList: TCustomImageList; var ImageIndex: TImageIndex);
144     procedure BeginUpdate; override;
145     procedure EndUpdate; override;
IsUpdateLockednull146     function IsUpdateLocked: boolean;
147     procedure Update(ForceUpdateAll: Boolean); override;
148   public
149     property PageControl: TPageControl read FPageControl write SetPageControl;
150     property OnOpenPackage: TNotifyEvent read FOnOpenPackage write FOnOpenPackage;
151     property OnOpenUnit: TNotifyEvent read FOnOpenUnit write FOnOpenUnit;
152     property OnChangeActivePage: TNotifyEvent read FOnChangeActivePage write FOnChangeActivePage;
153   end;
154 
CompareControlsWithTagnull155 function CompareControlsWithTag(Control1, Control2: Pointer): integer;
156 
157 implementation
158 
159 {$R ../images/components_images.res}
160 {$DEFINE USE_PageIndex}
161 
162 const
163   OVERVIEW_PANEL_WIDTH = 20;
164 
CompareControlsWithTagnull165 function CompareControlsWithTag(Control1, Control2: Pointer): integer;
166 var
167   Ctrl1: TControl absolute Control1;
168   Ctrl2: TControl absolute Control2;
169 begin
170   if Ctrl1.Tag>Ctrl2.Tag then
171     Result:=1
172   else if Ctrl1.Tag<Ctrl2.Tag then
173     Result:=-1
174   else
175     Result:=0;
176 end;
177 
178 { TComponentPage }
179 
180 constructor TComponentPage.Create(const ThePageName: string);
181 begin
182   inherited Create(ThePageName);
183 end;
184 
185 destructor TComponentPage.Destroy;
186 begin
187   FreeAndNil(fSelectButton);
188   FreeAndNil(fPageComponent);
189   inherited Destroy;
190 end;
191 
GetScrollBoxnull192 function TComponentPage.GetScrollBox: TScrollBox;
193 begin
194   if Assigned(PageComponent) and (PageComponent.ComponentCount > 0)
195   and (PageComponent.Components[0] is TScrollBox) then
196     Result := TScrollBox(PageComponent.Components[0])
197   else
198     Result := Nil;
199 end;
200 
IsSelectionToolBtnnull201 function IsSelectionToolBtn(aControl: TControl): boolean;
202 begin
203   Result:=(aControl is TSpeedButton)
204     and (LeftStr(aControl.Name,length(CompPalSelectionToolBtnPrefix))=CompPalSelectionToolBtnPrefix);
205 end;
206 
207 procedure TComponentPage.ReAlignButtons;
208 var
209   Pal: TComponentPalette;
210   CurButton: TSpeedButton;
211   ButtonTree: TAVLTree;
212   Node: TAVLTreeNode;
213   ScrollBox: TScrollBox;
214   buttonx, MaxBtnPerRow, i, ComponentPaletteBtnWidthScaled,
215     ComponentPaletteBtnHeightScaled: integer;
216 begin
217   if (PageComponent=Nil) or (PageComponent.ComponentCount=0)
218   or not (PageComponent.Components[0] is TScrollBox) then
219     exit;
220   if not fGuiCreated then begin
221     {$IFDEF VerboseComponentPalette}
222     DebugLn(['TComponentPage.ReAlignButtons, ', PageName, ', calling CreateButtons']);
223     {$ENDIF}
224     CreateButtons;         // Delayed creation of buttons at startup.
225   end;
226   Pal := TComponentPalette(Palette);
227   if Pal.PageControl<>nil then
228     Pal.PageControl.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TComponentPage.ReAlignButtons'){$ENDIF};
229   ComponentPaletteBtnWidthScaled := Pal.PageControl.Scale96ToForm(ComponentPaletteBtnWidth);
230   ComponentPaletteBtnHeightScaled := Pal.PageControl.Scale96ToForm(ComponentPaletteBtnHeight);
231   ButtonTree:=nil;
232   try
233     ScrollBox:=TScrollBox(PageComponent.Components[0]);
234     ButtonTree:=TAVLTree.Create(@CompareControlsWithTag);
235     for i:=0 to ScrollBox.ControlCount-1 do begin
236       CurButton:=TSpeedbutton(ScrollBox.Controls[i]);
237       if IsSelectionToolBtn(CurButton) then continue;
238       if (CurButton is TSpeedButton) and CurButton.Visible then
239         ButtonTree.Add(CurButton);
240     end;
241     if ButtonTree.Count=0 then exit;
242 
243     ButtonX:= ((ComponentPaletteBtnWidthScaled*3) div 2) + 2;
244 
245     {$IFDEF VerboseComponentPalette}
246     if PageComponent.Caption = CompPalVerbPgName then
247       DebugLn(['TComponentPage.ReAlignButtons',
248         ' ButtonTree.Count=',ButtonTree.Count,
249         ' ScrollBox.ControlCount=',ScrollBox.ControlCount,
250         ' ScrollBox.Bounds=',dbgs(ScrollBox.BoundsRect),
251         ' VertScrollBar.Size=',ScrollBox.VertScrollBar.Size,
252         ' ClientSizeWithoutBar=',ScrollBox.VertScrollBar.ClientSizeWithoutBar,
253         ' IsScrollBarVisible=',ScrollBox.VertScrollBar.IsScrollBarVisible,
254         ' HorzScrollBar.Size=',ScrollBox.HorzScrollBar.Size,
255         ' Page=',ScrollBox.HorzScrollBar.Page,
256         ' Range=',ScrollBox.HorzScrollBar.Range,
257         ' IsScrollBarVisible=',ScrollBox.HorzScrollBar.IsScrollBarVisible
258         ]);
259     {$ENDIF}
260 
261     MaxBtnPerRow:=((ScrollBox.VertScrollBar.ClientSizeWithoutBar - ButtonX) div ComponentPaletteBtnWidthScaled);
262 
263     // If we need to wrap, make sure we have space for the scrollbar
264     if MaxBtnPerRow < ButtonTree.Count then
265       MaxBtnPerRow:=((ScrollBox.VertScrollBar.ClientSizeWithBar - ButtonX) div ComponentPaletteBtnWidthScaled);
266     //debugln(['TComponentPage.ReAlignButtons MaxBtnPerRow=',MaxBtnPerRow,' ButtonTree.Count=',ButtonTree.Count,' ',ButtonX + MaxBtnPerRow * ComponentPaletteBtnWidthScaled]);
267     if MaxBtnPerRow<1 then MaxBtnPerRow:=1;
268 
269     i:=0;
270     Node:=ButtonTree.FindLowest;
271     while Node<>nil do begin
272       CurButton:=TSpeedbutton(Node.Data);
273       CurButton.SetBounds(ButtonX + (i mod MaxBtnPerRow) * ComponentPaletteBtnWidthScaled,
274                           (i div MaxBtnPerRow) * ComponentPaletteBtnHeightScaled,
275                           CurButton.Width, CurButton.Height);
276       {$IFDEF VerboseComponentPalette}
277       if PageComponent.Caption = CompPalVerbPgName then
278         DebugLn(['TComponentPage.ReAlignButtons ',CurButton.Name,' ',dbgs(CurButton.BoundsRect)]);
279       {$ENDIF}
280       inc(i);
281       Node:=ButtonTree.FindSuccessor(Node);
282     end;
283     PageComponent.Invalidate;
284   finally
285     if Pal.PageControl<>nil then
286       Pal.PageControl.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TComponentPage.ReAlignButtons'){$ENDIF};
287     FreeAndNil(ButtonTree);
288   end;
289 end;
290 
291 procedure TComponentPage.RemoveSheet;
292 var
293   Btn: TSpeedButton;
294 begin
295   Btn:=TSpeedButton(SelectButton);
296   if Btn<>nil then begin
297     SelectButton:=nil;
298     Application.ReleaseComponent(Btn);
299     Btn.Visible:=false;
300   end;
301   PageComponent:=nil;
302 end;
303 
304 procedure TComponentPage.InsertVisiblePage(aCompNames: TRegisteredCompList);
305 var
306   Pal: TComponentPalette;
307   TabIndex: Integer;
308   PanelRight: TPanel;
309   BtnRight: TSpeedButton;
310   TabControl: TCustomTabControl;
311 begin
312   if not Visible then begin
313     {$IFDEF VerboseComponentPalette}
314     DebugLn(['TComponentPalette.InsertVisiblePage: Not inserting Page=', PageName]);
315     {$ENDIF}
316     exit;
317   end;
318   fRegComps := aCompNames;
319   Pal := TComponentPalette(Palette);
320   TabControl := TCustomTabControl(Pal.FPageControl);
321   if PageComponent=nil then
322   begin
323     // insert a new PageControl page
324     {$IFDEF VerboseComponentPalette}
325     DebugLn(['TComponentPalette.InsertVisiblePage: Inserting Page=', PageName,
326              ', at index=', Pal.fVisiblePageIndex]);
327     {$ENDIF}
328     {$IFDEF USE_PageIndex}
329     TabIndex:= TabControl.Pages.Add(PageName);
330     PageComponent := Pal.FPageControl.Page[TabIndex];
331     PageComponent.PageIndex := Pal.fVisiblePageIndex;
332     {$ELSE}
333     TabControl.Pages.Insert(Pal.fVisiblePageIndex, PageName);
334     PageComponent := Pal.FPageControl.Page[Pal.fVisiblePageIndex];
335     {$ENDIF}
336     with TScrollBox.Create(PageComponent) do begin
337       Align := alClient;
338       BorderStyle := bsNone;
339       BorderWidth := 0;
340       HorzScrollBar.Visible := false;
341       {$IFDEF LCLCarbon}
342       // carbon has not implemented turning scrollbars on and off
343       VertScrollBar.Visible := false;
344       AutoScroll:=false;
345       {$ENDIF}
346       VertScrollBar.Increment := PageComponent.Scale96ToForm(ComponentPaletteBtnHeight);
347       VertScrollBar.Tracking := True;
348       Parent := PageComponent;
349     end;
350     PanelRight := TPanel.Create(PageComponent);
351     with PanelRight do
352     begin
353       Align := alRight;
354       Caption := '';
355       BevelOuter := bvNone;
356       Visible := True; // EnvironmentOptions.IDESpeedButtonsVisible;
357       Parent := PageComponent;
358       Width := Scale96ToForm(OVERVIEW_PANEL_WIDTH);
359       OnMouseWheel := @Pal.OnPageMouseWheel;
360     end;
361     BtnRight:=TSpeedButton.Create(PageComponent);
362     with BtnRight do
363     begin
364       IDEImages.AssignImage(BtnRight, 'SelCompPage');
365       Flat := True;
366       Hint := lisClickToSelectPalettePage;
367       ShowHint := True;
368       OnMouseDown := @MainIDE.SelComponentPageButtonMouseDown;
369       OnClick := @MainIDE.SelComponentPageButtonClick;
370       OnMouseWheel := @Pal.OnPageMouseWheel;
371       Parent := PanelRight;
372       SetBounds(Scale96ToForm(2), Scale96ToForm(1), Scale96ToForm(16), Scale96ToForm(16));
373     end;
374   end
375   else begin
376     // move to the right position
377     {$IFDEF USE_PageIndex}
378       {$IFDEF VerboseComponentPalette}
379       DebugLn(['TComponentPalette.InsertVisiblePage: Page=', PageName,
380                ' setting PageIndex from ', PageComponent.PageIndex , ' to ', Pal.fVisiblePageIndex]);
381       {$ENDIF}
382     PageComponent.PageIndex := Pal.fVisiblePageIndex;
383     {$ELSE}
384     TabIndex := PageComponent.PageIndex;
385     {$IFDEF VerboseComponentPalette}
386     DebugLn(['TComponentPalette.InsertVisiblePage: Start moving Page=', PageName,
387              ' from ', TabIndex, ' to ', Pal.fVisiblePageIndex]);
388     {$ENDIF}
389     if (TabIndex<>Pal.fVisiblePageIndex)
390     and (Pal.fVisiblePageIndex < TabControl.Pages.Count) then
391     begin
392       {$IFDEF VerboseComponentPalette}
393       if {PageName = CompPalVerbPgName} true then
394         DebugLn(['TComponentPalette.InsertVisiblePage: Moving Page=', PageName,
395                  ' from ', TabIndex, ' to ', Pal.fVisiblePageIndex]);
396       {$ENDIF}
397       TabControl.Pages.Move(TabIndex, Pal.fVisiblePageIndex);
398     end;
399     {$ENDIF}
400   end;
401   inc(Pal.fVisiblePageIndex);
402 end;
403 
404 procedure TComponentPage.CreateSelectionButton(aButtonUniqueName: string; aScrollBox: TScrollBox);
405 var
406   Pal: TComponentPalette;
407   Btn: TSpeedButton;
408 begin
409   if Assigned(SelectButton) then Exit;
410   Pal := TComponentPalette(Palette);
411   Btn := TSpeedButton.Create(nil);
412   SelectButton:=Btn;
413   IDEImages.AssignImage(Btn, 'tmouse');
414   with Btn do begin
415     Name := CompPalSelectionToolBtnPrefix + aButtonUniqueName;
416     OnClick := @Pal.SelectionToolClick;
417     OnMouseWheel := @Pal.OnPageMouseWheel;
418     Flat := True;
419     GroupIndex:= 1;
420     Down := True;
421     Hint := lisSelectionTool;
422     ShowHint := EnvironmentOptions.ShowHintsForComponentPalette;
423     SetBounds(0,0,aScrollBox.Scale96ToForm(ComponentPaletteBtnWidth),aScrollBox.Scale96ToForm(ComponentPaletteBtnHeight));
424     Parent := aScrollBox;
425   end;
426 end;
427 
428 procedure TComponentPage.CreateOrDelButton(aComp: TPkgComponent; aButtonUniqueName: string;
429   aScrollBox: TScrollBox);
430 var
431   Pal: TComponentPalette;
432   CompCls: TComponentClass;
433   Btn: TSpeedButton;
434 begin
435   Pal := TComponentPalette(Palette);
436   CompCls := aComp.ComponentClass;
437   Btn := TSpeedButton(Pal.fComponentButtons[CompCls]);
438   if aComp.Visible then
439   begin
440     inc(fBtnIndex);
441     if Btn=nil then
442     begin
443       Btn := TSpeedButton.Create(nil);
444       Pal.fComponentButtons[CompCls] := Btn;
445       Btn.Name := CompPaletteCompBtnPrefix + aButtonUniqueName + CompCls.ClassName;
446       // Left and Top will be set in ReAlignButtons.
447       Btn.SetBounds(Btn.Left, Btn.Top,
448                     aScrollBox.Scale96ToForm(ComponentPaletteBtnWidth),
449                     aScrollBox.Scale96ToForm(ComponentPaletteBtnHeight));
450       Btn.Images := aComp.Images;
451       Btn.ImageIndex := aComp.ImageIndex;
452       Btn.GroupIndex := 1;
453       Btn.Flat := true;
454       Btn.OnMouseDown := @Pal.ComponentBtnMouseDown;
455       Btn.OnMouseUp := @Pal.ComponentBtnMouseUp;
456       Btn.OnDblClick := @Pal.ComponentBtnDblClick;
457       Btn.OnMouseWheel := @Pal.OnPageMouseWheel;
458       Btn.ShowHint := EnvironmentOptions.ShowHintsForComponentPalette;
459       Btn.Hint := CompCls.ClassName + sLineBreak +
460         '(' + aComp.ComponentClass.UnitName+', '+aComp.PkgFile.LazPackage.Name + ')';
461       Btn.PopupMenu:=Pal.PopupMenu;
462       {$IFDEF VerboseComponentPalette}
463       if aComp.RealPage.PageName = CompPalVerbPgName then
464         DebugLn(['TComponentPalette.CreateOrDelButton Created Button: ',CompCls.ClassName,' ',Btn.Name]);
465       {$ENDIF}
466     end else
467     begin
468       {$IFDEF VerboseComponentPalette}
469       if aComp.RealPage.PageName = CompPalVerbPgName then
470         DebugLn(['TComponentPalette.CreateOrDelButton Keep Button: ',CompCls.ClassName,' ',Btn.Name,' ',DbgSName(Btn.Parent)]);
471       {$ENDIF}
472     end;
473     Btn.Parent := aScrollBox;
474     Btn.Tag:=fBtnIndex;
475   end
476   else if Btn<>nil then
477   begin
478     {$IFDEF VerboseComponentPalette}
479     if aComp.RealPage.PageName = CompPalVerbPgName then
480       DebugLn(['TComponentPalette.CreateOrDelButton Destroy Button: ',CompCls.ClassName,' ',Btn.Name]);
481     {$ENDIF}
482     Application.ReleaseComponent(Btn);
483     Pal.fComponentButtons.Remove(CompCls);
484     Btn.Visible:=false;
485   end;
486 end;
487 
488 procedure TComponentPage.CreateButtons;
489 // Create speedbuttons for every visible component
490 var
491   ScrollBox: TScrollBox;
492   Comp: TPkgComponent;
493   i: Integer;
494 begin
495   if not Visible then Exit;
496   ScrollBox := GetScrollBox;
497   Assert(Assigned(ScrollBox), 'CreateButtons: ScrollBox not assigned.');
498   ScrollBox.OnResize := @TComponentPalette(Palette).OnScrollBoxResize;
499   ScrollBox.OnMouseWheel := @TComponentPalette(Palette).OnPageMouseWheel;
500   {$IFDEF VerboseComponentPalette}
501   if PageName = CompPalVerbPgName then
502     DebugLn(['TComponentPalette.CreateButtons PAGE="',PageName,'", PageIndex=',PageComponent.PageIndex]);
503   {$ENDIF}
504   // create selection button
505   CreateSelectionButton(IntToStr(FIndex), ScrollBox);
506   // create component buttons and delete unneeded ones
507   fBtnIndex := 0;
508   Assert(Assigned(fRegComps), 'TComponentPage.CreateButtons: fCompNames is not assigned.');
509   for i := 0 to fRegComps.Count-1 do begin
510     Comp := fRegComps[i] as TPkgComponent;
511     if Assigned(Comp) then
512       CreateOrDelButton(Comp, Format('%d_%d_',[FIndex,i]), ScrollBox);
513   end;
514   fGuiCreated := True;
515 end;
516 
517 { TComponentPalette }
518 
519 procedure TComponentPalette.ActivePageChanged(Sender: TObject);
520 begin
521   if (FPageControl=nil) or fUpdatingPageControl then exit;
522   if (Selected<>nil)
523   and ((Selected.RealPage as TComponentPage).PageComponent=FPageControl.ActivePage) then exit;
524   {$IFDEF VerboseComponentPalette}
525   DebugLn('TComponentPalette.ActivePageChanged: Calling ReAlignButtons, setting Selected:=nil.');
526   {$ENDIF}
527   ReAlignButtons(FPageControl.ActivePage);
528   Selected:=nil;
529 
530   if MainIDE.IDEStarted and Assigned(FOnChangeActivePage) then
531     FOnChangeActivePage(Sender);
532 end;
533 
534 procedure TComponentPalette.OnScrollBoxResize(Sender: TObject);
535 begin
536   if MainIDE.IDEStarted and (TControl(Sender).Parent is TCustomPage) then
537   begin
538     {$IFDEF VerboseComponentPalette}
539     DebugLn(['TComponentPalette.OnScrollBoxResize Calling ReAlignButtons, IDEStarted=', MainIDE.IDEStarted]);
540     {$ENDIF}
541     ReAlignButtons(TCustomPage(TControl(Sender).Parent));
542   end;
543 end;
544 
545 procedure TComponentPalette.OpenPackageClicked(Sender: TObject);
546 var
547   PkgComponent: TPkgComponent;
548 begin
549   PkgComponent:=FindPkgCompByButton(PopupMenu.PopupComponent);
550   if (PkgComponent=nil) or (PkgComponent.PkgFile=nil)
551   or (PkgComponent.PkgFile.LazPackage=nil) then exit;
552   if Assigned(OnOpenPackage) then
553     OnOpenPackage(PkgComponent.PkgFile.LazPackage);
554 end;
555 
556 procedure TComponentPalette.OpenUnitClicked(Sender: TObject);
557 var
558   PkgComponent: TPkgComponent;
559 begin
560   PkgComponent:=FindPkgCompByButton(PopupMenu.PopupComponent);
561   if (PkgComponent=nil) or (PkgComponent.PkgFile=nil)
562   or (PkgComponent.PkgFile.LazPackage=nil) then exit;
563   if Assigned(OnOpenUnit) then
564     OnOpenUnit(PkgComponent);
565 end;
566 
567 procedure TComponentPalette.ComponentListClicked(Sender: TObject);
568 begin
569   MainIDE.DoShowComponentList;
570 end;
571 
572 procedure TComponentPalette.OptionsClicked(Sender: TObject);
573 begin
574   MainIDE.DoOpenIDEOptions(TCompPaletteOptionsFrame, '', [], []);
575 end;
576 
577 procedure TComponentPalette.PalettePopupMenuPopup(Sender: TObject);
578 begin
579   ;
580 end;
581 
582 procedure TComponentPalette.PopupMenuPopup(Sender: TObject);
583 var
584   PkgComponent: TPkgComponent;
585   APackage: TLazPackage;
586   UnitFilename: String;
587   ShownFilename: String;
588 begin
589   PkgComponent:=FindPkgCompByButton(PopupMenu.PopupComponent);
590   APackage:=nil;
591   if (PkgComponent<>nil) and (PkgComponent.PkgFile<>nil) then
592     APackage:=PkgComponent.PkgFile.LazPackage;
593   if APackage=nil then begin
594     OpenPackageMenuItem.Visible:=false;
595     OpenUnitMenuItem.Visible:=false;
596   end else begin
597     OpenPackageMenuItem.Caption:=Format(lisCPOpenPackage, [APackage.IDAsString]);
598     OpenPackageMenuItem.Visible:=true;
599     ShownFilename:=PkgComponent.PkgFile.Filename;
600     UnitFilename:=PkgComponent.PkgFile.GetFullFilename;
601     if not FileExistsCached(UnitFilename) then begin
602       UnitFilename:=LazarusIDE.FindSourceFile(ExtractFilename(UnitFilename),
603                                               APackage.Directory,[]);
604       if FileExistsUTF8(UnitFilename) then
605         UnitFilename:=ShownFilename;
606     end;
607     OpenUnitMenuItem.Caption:=Format(lisCPOpenUnit, [ShownFilename]);
608     OpenUnitMenuItem.Visible:=true;
609     OpenUnitMenuItem.Enabled:=FileExistsCached(UnitFilename);
610   end;
611 end;
612 
613 procedure TComponentPalette.SetPageControl(const AValue: TPageControl);
614 var
615   miCompList,
616   miOptions: TMenuItem;
617 begin
618   if FPageControl=AValue then exit;
619   ClearButtons;
620   FPageControl:=AValue;
621   if FPageControl<>nil then begin
622     FPageControl.OnChange:=@ActivePageChanged;
623     if PalettePopupMenu=nil then begin
624       PalettePopupMenu:=TPopupMenu.Create(nil);
625       PalettePopupMenu.OnPopup:=@PalettePopupMenuPopup;
626       PalettePopupMenu.Name:='PalettePopupMenu';
627       // Component List
628       PalettePopupMenu.Images := IDEImages.Images_16;
629       miCompList:=TMenuItem.Create(PalettePopupMenu);
630       with miCompList do begin
631         Name:='ComponentListMenuItem';
632         Caption:=lisCompPalComponentList;
633         OnClick:=@ComponentListClicked;
634         ImageIndex := IDEImages.LoadImage('menu_view_components');
635       end;
636       PalettePopupMenu.Items.Add(miCompList);
637       miOptions:=TMenuItem.Create(PalettePopupMenu);
638       with miOptions do begin
639         Name:='OptionsMenuItem';
640         Caption:=lisMenuGeneralOptions;
641         OnClick:=@OptionsClicked;
642         ImageIndex := IDEImages.LoadImage('menu_environment_options');
643       end;
644       PalettePopupMenu.Items.Add(miOptions);
645     end;
646     FPageControl.PopupMenu:=PalettePopupMenu;
647   end;
648   {$IFDEF VerboseComponentPalette}
649   DebugLn(['TComponentPalette.SetPageControl, calling UpdateNoteBookButtons, ', AValue]);
650   {$ENDIF}
651 end;
652 
653 procedure TComponentPalette.SelectionToolClick(Sender: TObject);
654 begin
655   SelectAButton(TSpeedButton(Sender));
656 end;
657 
658 procedure TComponentPalette.ComponentBtnMouseDown(Sender: TObject; Button: TMouseButton;
659   Shift: TShiftState; X, Y: Integer);
660 begin
661   if Button=mbLeft then
662   begin
663     if ssShift in Shift then
664       SelectionMode := csmMulty
665     else
666       SelectionMode := csmSingle;
667     SelectAButton(TSpeedButton(Sender));
668     if Assigned(OnClassSelected) then
669       OnClassSelected(Self);
670   end;
671 end;
672 
673 procedure TComponentPalette.ComponentBtnMouseUp(Sender: TObject; Button: TMouseButton;
674   Shift: TShiftState; X, Y: Integer);
675 begin
676    { If the visual state is down, but internal "no selection" then
677     just do visual unselection of all buttons
678     This trick is for double-click handling (to unselect the button visually ). }
679   if ((Sender as TCustomSpeedButton).Down) and (Selected = Nil) then
680     UnselectAllButtons;
681 end;
682 
683 procedure TComponentPalette.ComponentBtnDblClick(Sender: TObject);
684 var
685   TypeClass: TComponentClass;
686   ParentComp: TComponent;
687   X, Y: integer;
688   AComponent: TComponent;
689   DisableAutoSize: Boolean;
690 begin
691   //debugln('TComponentPalette.ComponentBtnDblClick ',TComponent(Sender).Name);
692   if SelectAButton(TSpeedButton(Sender)) and (Selected<>nil) then begin
693     if FormEditingHook<>nil then begin
694       TypeClass:=Selected.ComponentClass;
695       if assigned(Selected.OnGetCreationClass) then
696         Selected.OnGetCreationClass(Self,TypeClass);
697       if TypeClass=nil then exit;
698       ParentComp:=FormEditingHook.GetDefaultComponentParent(TypeClass);
699       if ParentComp=nil then exit;
700       if not FormEditingHook.GetDefaultComponentPosition(TypeClass,ParentComp,X,Y)
701       then exit;
702       //debugln('TComponentPalette.ComponentBtnDblClick ',dbgsName(Sender),' ',dbgs(X),',',dbgs(Y));
703       DisableAutoSize:=true;
704       AComponent:=FormEditingHook.CreateComponent(ParentComp,TypeClass,'',X,Y,0,0,
705         DisableAutoSize);
706       if AComponent<>nil then begin
707         if DisableAutoSize and (AComponent is TControl) then
708           TControl(AComponent).EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TComponentPalette.ComponentBtnDblClick'){$ENDIF};
709         GlobalDesignHook.PersistentAdded(AComponent,true);
710       end;
711     end;
712   end;
713   Selected:=nil;
714   if Assigned(OnClassSelected) then
715     OnClassSelected(Self);
716 end;
717 
718 // unselect all other buttons on all other PageControl pages
719 procedure TComponentPalette.UnselectAllButtons;
720 var
721   i: Integer;
722   CurPage: TBaseComponentPage;
723   SelectButtonOnPage: TSpeedButton;
724 begin
725   for i:=0 to Pages.Count-1 do begin
726     CurPage:=Pages[i];
727     if (Selected=nil) or (Selected.RealPage<>CurPage) then begin
728       SelectButtonOnPage:=TSpeedButton(TComponentPage(CurPage).SelectButton);
729       if SelectButtonOnPage<>nil then
730         SelectButtonOnPage.Down:=true;
731     end;
732   end;
733 end;
734 
735 procedure TComponentPalette.SelectionWasChanged;
736 var
737   Sheet: TTabSheet;
738   Btn: TSpeedButton;
739 begin
740   if FPageControl=nil then exit;
741   UnselectAllButtons;
742   if Selected=nil then exit;
743   Assert(Assigned(Selected.RealPage), 'TComponentPalette.SelectionWasChanged: Selected.RealPage = Nil.');
744   Sheet:=(Selected.RealPage as TComponentPage).PageComponent as TTabSheet;
745   {$IFDEF VerboseComponentPalette}
746   DebugLn(['TComponentPalette.SelectionWasChanged: Setting FPageControl.ActivePage index ',Sheet.PageIndex]);
747   {$ENDIF}
748   // Switch to the new page
749   FPageControl.ActivePage:=Sheet;
750   // Build the GUI layout for this page if not done yet.
751   Btn:=TSpeedButton(fComponentButtons[Selected.ComponentClass]);
752   if Btn=nil then
753     ReAlignButtons(FPageControl.ActivePage);
754   // Select button
755   Btn:=TSpeedButton(fComponentButtons[Selected.ComponentClass]);  //find again!
756   if Btn<>nil then
757     Btn.Down:=true;
758 end;
759 
760 procedure TComponentPalette.CreatePopupMenu;
761 var
762   MenuItem: TMenuItem;
763 begin
764   if PopupMenu<>nil then exit;
765   PopupMenu:=TPopupMenu.Create(nil);
766   PopupMenu.OnPopup:=@PopupMenuPopup;
767   PopupMenu.Name:='ComponentPopupMenu';
768   PopupMenu.Images:=IDEImages.Images_16;
769 
770   OpenPackageMenuItem:=TMenuItem.Create(PopupMenu);
771   with OpenPackageMenuItem do begin
772     Name:='OpenPackageMenuItem';
773     Caption:=lisCompPalOpenPackage;
774     OnClick:=@OpenPackageClicked;
775   end;
776   PopupMenu.Items.Add(OpenPackageMenuItem);
777 
778   OpenUnitMenuItem:=TMenuItem.Create(PopupMenu);
779   with OpenUnitMenuItem do begin
780     Name:='OpenUnitMenuItem';
781     Caption:=lisCompPalOpenUnit;
782     OnClick:=@OpenUnitClicked;
783   end;
784   PopupMenu.Items.Add(OpenUnitMenuItem);
785 
786   PopupMenu.Items.AddSeparator;
787 
788   MenuItem:=TMenuItem.Create(PopupMenu);
789   with MenuItem do begin
790     Name:='ComponentListMenuItem';
791     Caption:=lisCompPalComponentList;
792     OnClick:=@ComponentListClicked;
793   end;
794   PopupMenu.Items.Add(MenuItem);
795 
796   MenuItem:=TMenuItem.Create(PopupMenu);
797   with MenuItem do begin
798     Name:='OptionsMenuItem';
799     Caption:=lisMenuGeneralOptions;
800     OnClick:=@OptionsClicked;
801     ImageIndex := IDEImages.LoadImage('menu_environment_options');
802   end;
803   PopupMenu.Items.Add(MenuItem);
804 end;
805 
806 procedure TComponentPalette.OnPageMouseWheel(Sender: TObject; Shift: TShiftState;
807       WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
808 begin
809   if (WheelDelta > 0) then
810   begin
811     if (PageControl.ActivePageIndex > 0) then
812     begin
813       PageControl.ActivePageIndex := PageControl.ActivePageIndex - 1;
814       PageControl.OnChange(PageControl);
815     end;
816   end else begin
817     if (PageControl.ActivePageIndex < PageControl.PageCount-1) then
818     begin
819       PageControl.ActivePageIndex := PageControl.ActivePageIndex + 1;
820       PageControl.OnChange(PageControl);
821     end;
822   end;
823   Handled := True;
824 end;
825 
826 constructor TComponentPalette.Create;
827 begin
828   inherited Create(EnvironmentOptions.Desktop.ComponentPaletteOptions);
829   fComponentButtons:=TPointerToPointerTree.Create;
830   AddHandlerComponentAdded(@ComponentWasAdded);
831   AddHandlerSelectionChanged(@SelectionWasChanged);
832   ComponentPageClass := TComponentPage;   // Used by CreatePagesFromUserOrder
833 end;
834 
835 destructor TComponentPalette.Destroy;
836 var
837   AVLNode: TAVLTreeNode;
838   P2PItem: PPointerToPointerItem;
839 begin
840   PageControl:=nil;
841   // Free SpeedButtons stored in the tree map.
842   AVLNode:=fComponentButtons.Tree.FindLowest;
843   while AVLNode<>nil do begin
844     P2PItem:=PPointerToPointerItem(AVLNode.Data);
845     TSpeedButton(P2PItem^.Value).Free;
846     AVLNode:=fComponentButtons.Tree.FindSuccessor(AVLNode);
847   end;
848   FreeAndNil(fComponentButtons);
849   FreeAndNil(fSelectButtonIcon);
850   FreeAndNil(PopupMenu);
851   FreeAndNil(PalettePopupMenu);
852   inherited Destroy;
853 end;
854 
855 procedure TComponentPalette.BeginUpdate;
856 begin
857   inc(FUpdateLock);
858 end;
859 
860 procedure TComponentPalette.EndUpdate;
861 begin
862   if FUpdateLock<=0 then
863     raise Exception.Create('TBaseComponentPalette.EndUpdate: FUpdateLock<=0');
864   dec(FUpdateLock);
865   if (FUpdateLock=0) and FChanged then
866     Update(False);
867 end;
868 
TComponentPalette.IsUpdateLockednull869 function TComponentPalette.IsUpdateLocked: boolean;
870 begin
871   Result:=FUpdateLock>0;
872 end;
873 
874 procedure TComponentPalette.Update(ForceUpdateAll: Boolean);
875 begin
876   if not (ForceUpdateAll or FChanged) then Exit;
877   inherited Update(ForceUpdateAll);
878   {$IFDEF VerboseComponentPalette}
879   DebugLn(['TComponentPalette.Update, calling UpdateNoteBookButtons, fUpdatingPageControl=',
880            fUpdatingPageControl, ', ForceUpdateAll=', ForceUpdateAll, ', FChanged=', FChanged]);
881   {$ENDIF}
882   UpdateNoteBookButtons(ForceUpdateAll);
883   FChanged:=False;
884 end;
885 
886 procedure TComponentPalette.ClearButtons;
887 begin
888   if FPageControl<>nil then
889     FPageControl.DisableAlign;
890   Selected:=nil;
891   if PopupMenu<>nil then begin
892     PopupMenu.Free;
893     PopupMenu:=nil;
894     OpenPackageMenuItem:=nil;
895   end;
896   if FPageControl<>nil then
897     FPageControl.EnableAlign;
898 end;
899 
900 procedure TComponentPalette.GetUnregisteredIcon(
901   var ImageList: TCustomImageList; var ImageIndex: TImageIndex);
902 var
903   IL: TLCLGlyphs;
904 begin
905   IL := IDEImages.Images_24;
906   ImageList := IL;
907   ImageIndex := IL.GetImageIndex('unregisteredcomponent');
908   if ImageIndex<0 then
909     ImageIndex := IL.GetImageIndex('default');
910 end;
911 
GetSelectButtonIconnull912 function TComponentPalette.GetSelectButtonIcon: TCustomBitmap;
913 begin
914   if fSelectButtonIcon=nil then
915     fSelectButtonIcon := CreateBitmapFromResourceName(hInstance, 'tmouse');
916   Result:=fSelectButtonIcon;
917 end;
918 
SelectAButtonnull919 function TComponentPalette.SelectAButton(Button: TSpeedButton): boolean;
920 var
921   NewComponent: TRegisteredComponent;
922 begin
923   NewComponent := FindCompByButton(Button);
924   Selected := NewComponent;
925   Result := (Selected = NewComponent);
926 end;
927 
928 procedure TComponentPalette.ComponentWasAdded(ALookupRoot, AComponent: TComponent;
929   ARegisteredComponent: TRegisteredComponent);
930 begin
931   if not (ssShift in GetKeyShiftState) and (SelectionMode = csmSingle) then
932     Selected := nil;
933 end;
934 
935 procedure TComponentPalette.DoChange;
936 begin
937   if FUpdateLock>0 then
938     FChanged:=true
939   else
940     Update(False);
941 end;
942 
943 procedure TComponentPalette.ReAlignButtons(aSheet: TCustomPage);
944 var
945   PageInd: Integer;
946 begin
947   if (aSheet=Nil) or not aSheet.Visible then
948     exit;
949   {$IFDEF VerboseComponentPalette}
950   DebugLn(['TComponentPalette.ReAlignButtons Visible="',aSheet.Caption,'", ClientWidth=',aSheet.ClientWidth]);
951   {$ENDIF}
952   PageInd:=IndexOfPageComponent(aSheet);
953   if PageInd>=0 then
954     TComponentPage(Pages[PageInd]).ReAlignButtons;
955 end;
956 
957 procedure TComponentPalette.RemoveUnneededPage(aSheet: TCustomPage);
958 var
959   PageInd: Integer;
960 begin
961   PageInd:=IndexOfPageComponent(aSheet);
962   if (PageInd>=0) and Pages[PageInd].Visible then
963     Exit;
964   // page is not needed anymore => delete
965   if PageInd>=0 then
966     TComponentPage(Pages[PageInd]).RemoveSheet;
967   if aSheet=fOldActivePage then
968     fOldActivePage:=nil;
969   aSheet.Visible:=false;
970   {$IFDEF VerboseComponentPalette}
971   if aSheet.Caption = CompPalVerbPgName then
972     DebugLn(['TComponentPalette.RemoveUnneededPage: Removing Page=', aSheet.Caption, ', index=', PageInd]);
973   {$ENDIF}
974   Application.ReleaseComponent(aSheet);
975 end;
976 
977 procedure TComponentPalette.UpdateNoteBookButtons(ForceUpdateAll: Boolean);
978 var
979   i: Integer;
980   Pg: TComponentPage;
981 begin
982   if fUpdatingPageControl then exit;
983   Assert(not IsUpdateLocked, 'TComponentPalette.UpdateNoteBookButtons: IsUpdateLocked');
984   if FPageControl=Nil then exit;
985   // lock
986   fUpdatingPageControl:=true;
987   FPageControl.DisableAlign;
988   try
989     fOldActivePage:=FPageControl.ActivePage;
990     CreatePopupMenu;
991     {$IFDEF VerboseComponentPalette}
992     DebugLn(['TComponentPalette.UpdateNoteBookButtons: FPageCount before=', FPageControl.PageCount]);
993     {$ENDIF}
994     // remove every page in the PageControl without a visible page
995     for i:=FPageControl.PageCount-1 downto 0 do
996       RemoveUnneededPage(FPageControl.Pages[i]);
997 
998     {$IFDEF VerboseComponentPalette}
999     DebugLn(['TComponentPalette.UpdateNoteBookButtons: FPageCount after=', FPageControl.PageCount,
1000     ' PageCount=', Pages.count]);
1001     {$ENDIF}
1002 
1003     // Mark GUIs as not created. They will be created later when page gets selected.
1004     for i := 0 to Pages.Count-1 do
1005       TComponentPage(Pages[i]).fGuiCreated := False;
1006 
1007     // insert a PageControl page for every visible palette page
1008     fVisiblePageIndex := 0;
1009     for i := 0 to Pages.Count-1 do
1010     begin
1011       // Pages and UserOrder.ComponentPages are now synchronized, same index applies.
1012       Assert(Pages[i].PageName=UserOrder.ComponentPages[i],
1013              'UpdateNoteBookButtons: Page names do not match.');
1014       Pg := TComponentPage(Pages[i]);
1015       {$IF DEFINED(LCLQt) OR DEFINED(LCLQt5)}   // Qt has some problems in moving existing tabs!
1016       if Assigned(Pg.PageComponent) then begin
1017         Pg.PageComponent.Free;
1018         Pg.RemoveSheet;
1019       end;
1020       {$ENDIF}
1021       Pg.InsertVisiblePage(UserOrder.ComponentPages.Objects[i] as TRegisteredCompList);
1022       {$IFDEF VerboseComponentPalette}
1023       DebugLn(['TComponentPalette.UpdateNoteBookButtons: PageIndex=', i, ' PageName=',Pages[i].PageName]);
1024       {$ENDIF}
1025     end;
1026 
1027     // OldActivePage can be invalid if a user defined page is just deleted.
1028     if Assigned(fOldActivePage) and (FPageControl.IndexOf(fOldActivePage) = -1) then
1029       fOldActivePage := Nil;
1030     for i := Pages.Count-1 downto 0 do
1031     begin
1032       Pg := TComponentPage(Pages[i]);
1033       // During IDE start create GUI only for the active page.
1034       if ((fOldActivePage=Nil) and (i=0))  // First page is activated by default.
1035       or (Pg.PageComponent=fOldActivePage) // Previous active page will be restored.
1036       or (ForceUpdateAll) then             // Forced after changing configuration.
1037         Pg.ReAlignButtons;
1038     end;
1039     // restore active page
1040     if Assigned(fOldActivePage) then
1041       FPageControl.ActivePage:=fOldActivePage
1042     else if FPageControl.PageCount>0 then
1043       FPageControl.PageIndex:=0;
1044   finally
1045     // unlock
1046     fUpdatingPageControl:=false;
1047     FPageControl.EnableAlign;
1048   end;
1049 end;
1050 
1051 procedure TComponentPalette.OnGetNonVisualCompIcon(Sender: TObject;
1052   AComponent: TComponent; var ImageList: TCustomImageList;
1053   var ImageIndex: TImageIndex);
1054 var
1055   ARegComp: TRegisteredComponent;
1056 begin
1057   if AComponent<>nil then
1058     ARegComp:=FindRegComponent(AComponent.ClassType)
1059   else
1060     ARegComp:=nil;
1061   if ARegComp<>nil then
1062   begin
1063     ImageList := TPkgComponent(ARegComp).Images;
1064     ImageIndex := TPkgComponent(ARegComp).ImageIndex;
1065   end else
1066   begin
1067     GetUnregisteredIcon(ImageList, ImageIndex);
1068   end;
1069 end;
1070 
IndexOfPageComponentnull1071 function TComponentPalette.IndexOfPageComponent(AComponent: TComponent): integer;
1072 begin
1073   if AComponent<>nil then begin
1074     Result:=Pages.Count-1;
1075     while (Result>=0) and ((Pages[Result] as TComponentPage).PageComponent<>AComponent) do
1076       dec(Result);
1077   end else
1078     Result:=-1;
1079 end;
1080 
FindCompByButtonnull1081 function TComponentPalette.FindCompByButton(Button: TSpeedButton): TRegisteredComponent;
1082 var
1083   CompClass: TComponentClass;
1084 begin
1085   CompClass := TComponentClass(fComponentButtons.FindByValue(Button));
1086   if Assigned(CompClass) then
1087     Result := FindRegComponent(CompClass)
1088   else
1089     Result := nil;
1090 end;
1091 
TComponentPalette.FindPkgCompByButtonnull1092 function TComponentPalette.FindPkgCompByButton(Button: TComponent): TPkgComponent;
1093 begin
1094   Result := FindCompByButton(Button as TSpeedButton) as TPkgComponent;
1095 end;
1096 
1097 end.
1098 
1099