{ *************************************************************************** * * * 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 . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * *************************************************************************** Author: Marius Modified by Juha Manninen, Balazs Szekely Abstract: A dialog to quickly find components and to add the found component to the designed form. } unit ComponentList; {$mode objfpc}{$H+} interface uses Classes, SysUtils, LCLType, Forms, Controls, Graphics, StdCtrls, ExtCtrls, ComCtrls, ButtonPanel, Menus, LazarusIDEStrConsts, ComponentReg, PackageDefs, IDEImagesIntf, TreeFilterEdit, FormEditingIntf, IDEOptionDefs; type { TComponentListForm } TComponentListForm = class(TForm) imListPalette: TImageList; imInheritance: TImageList; ListTree: TTreeView; ButtonPanel: TButtonPanel; miCollapse: TMenuItem; miCollapseAll: TMenuItem; miExpand: TMenuItem; miExpandAll: TMenuItem; OKButton: TPanelBitBtn; LabelSearch: TLabel; PageControl: TPageControl; FilterPanel: TPanel; PalletteTree: TTreeView; InheritanceTree: TTreeView; pnPaletteTree: TPanel; Panel6: TPanel; Panel7: TPanel; pmCollapseExpand: TPopupMenu; TabSheetPaletteTree: TTabSheet; TabSheetInheritance: TTabSheet; TabSheetList: TTabSheet; TreeFilterEd: TTreeFilterEdit; procedure FormActivate(Sender: TObject); procedure FormShow(Sender: TObject); procedure miCollapseAllClick(Sender: TObject); procedure miCollapseClick(Sender: TObject); procedure miExpandAllClick(Sender: TObject); procedure miExpandClick(Sender: TObject); procedure OKButtonClick(Sender: TObject); procedure ComponentsDblClick(Sender: TObject); procedure ComponentsChange(Sender: TObject; {%H-}Node: TTreeNode); procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure pmCollapseExpandPopup(Sender: TObject); procedure TreeFilterEdAfterFilter(Sender: TObject); procedure PageControlChange(Sender: TObject); procedure TreeKeyPress(Sender: TObject; var Key: char); procedure FormKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState); private PrevPageIndex: Integer; PrevChangeStamp: Integer; // List for Component inheritence view FClassList: TStringList; FKeepSelected: Boolean; procedure ClearSelection; procedure SelectionWasChanged; procedure ComponentWasAdded({%H-}ALookupRoot, {%H-}AComponent: TComponent; {%H-}ARegisteredComponent: TRegisteredComponent); procedure DoComponentInheritence(Comp: TRegisteredComponent); procedure UpdateComponents; procedure UpdateButtonState; protected procedure UpdateShowing; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function GetSelectedComponent: TRegisteredComponent; end; var ComponentListForm: TComponentListForm; implementation {$R *.lfm} { TComponentListForm } constructor TComponentListForm.Create(AOwner: TComponent); begin inherited Create(AOwner); Name:=NonModalIDEWindowNames[nmiwComponentList]; //Translations LabelSearch.Caption := lisMenuFind; Caption := lisCmpLstComponents; TabSheetList.Caption := lisCmpLstList; TabSheetPaletteTree.Caption := lisCmpLstPalette; TabSheetInheritance.Caption := lisCmpLstInheritance; ButtonPanel.OKButton.Caption := lisUseAndClose; imListPalette.Width := ComponentPaletteImageWidth; imListPalette.Height := ComponentPaletteImageHeight; imInheritance.Width := ComponentPaletteImageWidth; imInheritance.Height := ComponentPaletteImageHeight; ListTree.Images := imListPalette; PalletteTree.Images := imListPalette; InheritanceTree.Images := imInheritance; PrevPageIndex := -1; PageControl.ActivePage := TabSheetList; if Assigned(IDEComponentPalette) then begin UpdateComponents; TreeFilterEd.InvalidateFilter; IDEComponentPalette.AddHandlerSelectionChanged(@SelectionWasChanged); IDEComponentPalette.AddHandlerComponentAdded(@ComponentWasAdded); end; end; destructor TComponentListForm.Destroy; begin if Assigned(IDEComponentPalette) then IDEComponentPalette.RemoveHandlerComponentAdded(@ComponentWasAdded); ComponentListForm := nil; inherited Destroy; end; procedure TComponentListForm.FormShow(Sender: TObject); var ParentParent: TWinControl; // Used for checking if the form is anchored. begin ParentParent := Nil; if Assigned(Parent) then ParentParent := Parent.Parent; //DebugLn(['*** TComponentListForm.FormShow, Parent=', Parent, ', Parent.Parent=', ParentParent]); ButtonPanel.Visible := ParentParent=Nil; if ButtonPanel.Visible then begin PageControl.AnchorSideBottom.Side := asrTop; UpdateButtonState; end else PageControl.AnchorSideBottom.Side := asrBottom; if not Assigned(Parent) then//only in undocked IDE begin if TreeFilterEd.CanFocus then TreeFilterEd.SetFocus; TreeFilterEd.SelectAll; end; end; procedure TComponentListForm.FormActivate(Sender: TObject); begin if Assigned(IDEComponentPalette) and (IDEComponentPalette.ChangeStamp<>PrevChangeStamp) then UpdateComponents; end; procedure TComponentListForm.ClearSelection; begin ListTree.Selected := Nil; PalletteTree.Selected := Nil; InheritanceTree.Selected := Nil; end; procedure SelectTreeComp(aTree: TTreeView); var Node: TTreeNode; begin with IDEComponentPalette do if Assigned(Selected) then Node := aTree.Items.FindNodeWithText(Selected.ComponentClass.ClassName) else Node := Nil; aTree.Selected := Node; end; procedure TComponentListForm.SelectionWasChanged; begin // ToDo: Select the component in active treeview. if ListTree.IsVisible then SelectTreeComp(ListTree) else if PalletteTree.IsVisible then SelectTreeComp(PalletteTree) else if InheritanceTree.IsVisible then SelectTreeComp(InheritanceTree) end; function GetSelectedTreeComp(aTree: TTreeView): TRegisteredComponent; begin if Assigned(aTree.Selected) then Result := TRegisteredComponent(aTree.Selected.Data) else Result := nil; end; function TComponentListForm.GetSelectedComponent: TRegisteredComponent; begin Result := nil; if ListTree.IsVisible then Result := GetSelectedTreeComp(ListTree) else if PalletteTree.IsVisible then Result := GetSelectedTreeComp(PalletteTree) else if InheritanceTree.IsVisible then Result := GetSelectedTreeComp(InheritanceTree) end; procedure TComponentListForm.ComponentWasAdded(ALookupRoot, AComponent: TComponent; ARegisteredComponent: TRegisteredComponent); begin ClearSelection; UpdateButtonState; end; procedure TComponentListForm.UpdateButtonState; begin ButtonPanel.OKButton.Enabled := Assigned(GetSelectedComponent); end; procedure TComponentListForm.UpdateShowing; begin if (ButtonPanel<>nil) and ButtonPanel.Visible then UpdateButtonState; inherited UpdateShowing; end; procedure TComponentListForm.DoComponentInheritence(Comp: TRegisteredComponent); // Walk down to parent, stop on TComponent, // since components are at least TComponent descendants. var PalList: TStringList; AClass: TClass; Node: TTreeNode; ClssName: string; i, Ind: Integer; CurIcon: TCustomBitmap; begin PalList := TStringList.Create; try AClass := Comp.ComponentClass; while (AClass.ClassInfo <> nil) and (AClass.ClassType <> TComponent.ClassType) do begin PalList.AddObject(AClass.ClassName, TObject(AClass)); AClass := AClass.ClassParent; end; // Build the tree for i := PalList.Count - 1 downto 0 do begin AClass := TClass(PalList.Objects[i]); ClssName := PalList[i]; if not FClassList.Find(ClssName, Ind) then begin // Find out parent position if Assigned(AClass.ClassParent) and FClassList.Find(AClass.ClassParent.ClassName, Ind) then Node := TTreeNode(FClassList.Objects[Ind]) else Node := nil; // Add the item if ClssName <> Comp.ComponentClass.ClassName then Node := InheritanceTree.Items.AddChild(Node, ClssName) else begin Node := InheritanceTree.Items.AddChildObject(Node, ClssName, Comp); CurIcon := nil; if (Comp is TPkgComponent) then CurIcon := TPkgComponent(Comp).Icon; if CurIcon <> nil then begin Node.ImageIndex := imInheritance.Add(CurIcon, nil); Node.SelectedIndex := Node.ImageIndex; end; end; FClassList.AddObject(ClssName, Node); end; end; finally PalList.Free; end; end; procedure TComponentListForm.UpdateComponents; // Fill all three tabsheets: Flat list, Palette layout and Component inheritence. var Pg: TBaseComponentPage; Comps: TStringList; Comp: TRegisteredComponent; ParentNode: TTreeNode; AListNode: TTreeNode; APaletteNode: TTreeNode; i, j: Integer; CurIcon: TCustomBitmap; begin if [csDestroying,csLoading]*ComponentState<>[] then exit; Screen.Cursor := crHourGlass; ListTree.BeginUpdate; PalletteTree.BeginUpdate; InheritanceTree.Items.BeginUpdate; FClassList := TStringList.Create; try ListTree.Items.Clear; PalletteTree.Items.Clear; InheritanceTree.Items.Clear; FClassList.Sorted := true; FClassList.CaseSensitive := false; FClassList.Duplicates := dupIgnore; // ParentInheritence := InheritanceTree.Items.Add(nil, 'TComponent'); // FClassList.AddObject('TComponent', ParentInheritence); // Iterate all pages for i := 0 to IDEComponentPalette.Pages.Count-1 do begin Pg := IDEComponentPalette.Pages[i]; Comps := IDEComponentPalette.RefUserCompsForPage(Pg.PageName); // Palette layout Page header ParentNode := PalletteTree.Items.AddChild(nil, Pg.PageName); // Iterate components of one page for j := 0 to Comps.Count-1 do begin Comp := Comps.Objects[j] as TRegisteredComponent; // Flat list item AListNode := ListTree.Items.AddChildObject(Nil, Comps[j], Comp); // Palette layout item APaletteNode := PalletteTree.Items.AddChildObject(ParentNode, Comps[j], Comp); CurIcon := nil; if (Comp is TPkgComponent) then CurIcon := TPkgComponent(Comp).Icon; if CurIcon <> nil then begin AListNode.ImageIndex := imListPalette.Add(CurIcon, nil); AListNode.SelectedIndex := AListNode.ImageIndex; APaletteNode.ImageIndex := AListNode.ImageIndex; APaletteNode.SelectedIndex := AListNode.ImageIndex; end; // Component inheritence item DoComponentInheritence(Comp); end; end; PalletteTree.FullExpand; InheritanceTree.AlphaSort; InheritanceTree.FullExpand; PrevChangeStamp := IDEComponentPalette.ChangeStamp; finally FClassList.Free; InheritanceTree.Items.EndUpdate; PalletteTree.EndUpdate; ListTree.EndUpdate; Screen.Cursor := crDefault; end; end; procedure TComponentListForm.TreeFilterEdAfterFilter(Sender: TObject); begin UpdateButtonState; end; procedure TComponentListForm.ComponentsDblClick(Sender: TObject); // This is used for all 3 treeviews begin OKButtonClick(nil); // Select and close this form end; procedure TComponentListForm.ComponentsChange(Sender: TObject; Node: TTreeNode); // This is used for all 3 treeviews var AComponent: TRegisteredComponent; begin AComponent:=GetSelectedComponent; if AComponent<>nil then IDEComponentPalette.SetSelectedComp(AComponent, ssShift in GetKeyShiftState); UpdateButtonState; end; procedure TComponentListForm.TreeKeyPress(Sender: TObject; var Key: char); // This is used for all 3 treeviews begin if Key = Char(VK_RETURN) then ComponentsDblClick(Sender); end; procedure TComponentListForm.PageControlChange(Sender: TObject); begin Assert(PageControl.PageIndex <> PrevPageIndex, Format( 'TComponentListForm.PageControlChange: PageControl.PageIndex = PrevPageIndex = %d', [PrevPageIndex])); case PageControl.PageIndex of 0: TreeFilterEd.FilteredTreeview := ListTree; 1: TreeFilterEd.FilteredTreeview := PalletteTree; 2: TreeFilterEd.FilteredTreeview := InheritanceTree; end; TreeFilterEd.InvalidateFilter; PrevPageIndex := PageControl.PageIndex; end; procedure TComponentListForm.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin if Parent=nil then begin ClearSelection; if not fKeepSelected then IDEComponentPalette.Selected := Nil; end else begin // Using a dock manager... CloseAction := caNone; //todo: helper function in DockManager or IDEDockMaster for closing forms. // Only close the window if it's floating. // AnchorDocking doesn't seem to initialize 'FloatingDockSiteClass' so we can't just check 'Floating'. // Also, AnchorDocking use nested forms, so the check for HostDockSite.Parent. if Assigned(HostDockSite) and (HostDockSite.DockClientCount <= 1) and (HostDockSite is TCustomForm) and (HostDockSite.Parent = nil) then begin TCustomForm(HostDockSite).Close; end; end; FKeepSelected := False; end; procedure TComponentListForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key=VK_ESCAPE then begin if ListTree.Selected = nil then //close only if no component is selected Close else ListTree.Selected := nil; //unselect if component is selected end; end; procedure TComponentListForm.OKButtonClick(Sender: TObject); // Select component from palette and close this form. User can insert the component. var AComponent: TRegisteredComponent; begin AComponent := GetSelectedComponent; if AComponent<>nil then begin IDEComponentPalette.Selected := AComponent; FKeepSelected := True; Close; if Assigned(IDEComponentPalette.OnClassSelected) then IDEComponentPalette.OnClassSelected(Self); end; end; procedure TComponentListForm.miCollapseAllClick(Sender: TObject); begin TreeFilterEd.FilteredTreeview.FullCollapse; end; procedure TComponentListForm.miCollapseClick(Sender: TObject); var Node: TTreeNode; begin Node := TreeFilterEd.FilteredTreeview.Selected; if Node = nil then Exit; if (Node.Level > 0) and (Node.HasChildren = False) then Node := Node.Parent; Node.Collapse(True); end; procedure TComponentListForm.miExpandAllClick(Sender: TObject); begin TreeFilterEd.FilteredTreeview.FullExpand; end; procedure TComponentListForm.miExpandClick(Sender: TObject); var Node: TTreeNode; begin Node := TreeFilterEd.FilteredTreeview.Selected; if Node = nil then Exit; if (Node.Level > 0) and (Node.HasChildren = False) then Node := Node.Parent; Node.Expand(True); end; procedure TComponentListForm.pmCollapseExpandPopup(Sender: TObject); var Node: TTreeNode; begin Node := TreeFilterEd.FilteredTreeview.Selected; if Node = nil then begin miExpand.Enabled := False; miCollapse.Enabled := False; end else begin miExpand.Enabled := (Node.HasChildren) and (not Node.Expanded); miCollapse.Enabled := (Node.HasChildren) and (Node.Expanded); end; end; end.