{ /*************************************************************************** pkggraphexplorer.pas -------------------- ***************************************************************************/ *************************************************************************** * * * 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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. * * * *************************************************************************** Author: Mattias Gaertner Abstract: TPkgGraphExplorerDlg is the IDE window showing the whole package graph. } unit PkgGraphExplorer; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Contnrs, AVL_Tree, // LCL LCLType, LCLIntf, Forms, Controls, ComCtrls, StdCtrls, ExtCtrls, Menus, ButtonPanel, // LazUtils LazLoggerBase, LazTracer, LazStringUtils, // LazControls LvlGraphCtrl, // IdeIntf IDECommands, PackageIntf, IDEImagesIntf, // IDE LazarusIDEStrConsts, IDEOptionDefs, Project, DependencyGraphOptions, EnvironmentOpts, MainIntf, PackageDefs, PackageSystem, PackageEditor, CleanPkgDeps; const GroupPrefixProject = '-Project-'; GroupPrefixIDE = '-IDE-'; type TOnOpenProject = function(Sender: TObject; AProject: TProject): TModalResult of object; { TPkgGraphExplorerDlg } TPkgGraphExplorerDlg = class(TForm) ButtonPanel1: TButtonPanel; CleanPkgDepsMenuItem: TMenuItem; GraphOptionsMenuItem: TMenuItem; PkgTreeView: TTreeView; InfoMemo: TMemo; LvlGraphControl1: TLvlGraphControl; Panel1: TPanel; PkgPopupMenu: TPopupMenu; Splitter1: TSplitter; VerticalSplitter: TSplitter; UninstallMenuItem: TMenuItem; procedure CleanPkgDepsMenuItemClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure GraphOptionsMenuItemClick(Sender: TObject); procedure HelpButtonClick(Sender: TObject); procedure LvlGraphControl1DblClick(Sender: TObject); procedure LvlGraphControl1SelectionChanged(Sender: TObject); procedure OKButtonClick(Sender: TObject); procedure PkgGraphExplorerShow(Sender: TObject); procedure PkgPopupMenuPopup(Sender: TObject); procedure InfoMemoKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState); procedure PkgTreeViewDblClick(Sender: TObject); procedure PkgTreeViewExpanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); procedure PkgTreeViewSelectionChanged(Sender: TObject); procedure UninstallMenuItemClick(Sender: TObject); private FPackageGraphOpts: TLvlGraphOptions; FOnOpenProject: TOnOpenProject; FOnUninstallPackage: TOnPkgEvent; ImgIndexProject: integer; ImgIndexPackage: integer; ImgIndexInstallPackage: integer; ImgIndexInstalledPackage: integer; ImgIndexUninstallPackage: integer; ImgIndexCyclePackage: integer; ImgIndexMissingPackage: integer; FOnOpenPackage: TOnPkgEvent; FChangedDuringLock: boolean; FUpdateLock: integer; FUpdatingSelection: boolean; procedure DoLoadedOpts(Sender: TObject); procedure GraphOptsApplyClicked(AnOpts: TLvlGraphOptions; {%H-}AGraph: TLvlGraph ); procedure OpenDependencyOwner(DependencyOwner: TObject); procedure SetupComponents; function GetPackageImageIndex(Pkg: TLazPackage; InstallPkgList: TFPList): integer; function GetSelectedPackage: TLazPackage; function FindPackage(const NodeText: string): TLazPackage; function PackageAsNodeText(Pkg: TLazPackage): string; protected procedure KeyUp(var Key: Word; Shift: TShiftState); override; public constructor Create(TheOwner: TComponent); override; destructor Destroy; override; procedure BeginUpdate; procedure EndUpdate; function IsUpdateLocked: boolean; procedure UpdateAll; procedure UpdateTree; procedure UpdateLvlGraph; procedure UpdateInfo; procedure UpdatePackageName({%H-}Pkg: TLazPackage; const {%H-}OldName: string); procedure UpdatePackageID({%H-}Pkg: TLazPackage); procedure UpdatePackageAdded({%H-}Pkg: TLazPackage); procedure SelectPackage(Pkg: TLazPackage); function FindLvlGraphNodeWithText(const s: string): TLvlGraphNode; procedure ShowPath(PathList: TFPList); public property OnOpenPackage: TOnPkgEvent read FOnOpenPackage write FOnOpenPackage; property OnOpenProject: TOnOpenProject read FOnOpenProject write FOnOpenProject; property OnUninstallPackage: TOnPkgEvent read FOnUninstallPackage write FOnUninstallPackage; end; var PackageGraphExplorer: TPkgGraphExplorerDlg = nil; implementation {$R *.lfm} { TPkgGraphExplorerDlg } procedure TPkgGraphExplorerDlg.PkgGraphExplorerShow(Sender: TObject); begin UpdateAll; end; procedure TPkgGraphExplorerDlg.LvlGraphControl1DblClick(Sender: TObject); var Pkg: TLazPackage; LGNode: TLvlGraphNode; begin LGNode:=LvlGraphControl1.SelectedNode; if LGNode=nil then exit; if LGNode.Caption=GroupPrefixProject then begin if Assigned(OnOpenProject) and (Project1<>nil) then OnOpenProject(Self,Project1); exit; end; if LGNode.Caption=GroupPrefixIDE then begin ExecuteIDECommand(Self,ecEditInstallPkgs); exit; end; Pkg:=GetSelectedPackage; if Pkg=nil then exit; if Assigned(OnOpenPackage) then OnOpenPackage(Self,Pkg); end; procedure TPkgGraphExplorerDlg.CleanPkgDepsMenuItemClick(Sender: TObject); var Owners: TFPList; ListOfNodeInfos: TObjectList; i: Integer; Info: TCPDNodeInfo; Pkg: TLazPackage; Dependency: TPkgDependency; begin Owners:=TFPList.Create; ListOfNodeInfos:=nil; try //if (Project1<>nil) then // Owners.Add(Project1); for i:=0 to PackageGraph.Count-1 do Owners.Add(PackageGraph[i]); if ShowCleanPkgDepDlg(Owners,true,ListOfNodeInfos)<>mrOk then exit; for i:=0 to ListOfNodeInfos.Count-1 do begin Info:=TCPDNodeInfo(ListOfNodeInfos[i]); debugln(['TPkgGraphExplorerDlg.CleanPkgDepsMenuItemClick ',Info.Owner,'->',Info.Dependency]); if Info.Owner=CPDProjectName then begin end else begin Pkg:=PackageGraph.FindPackageWithName(Info.Owner,nil); debugln(['TPkgGraphExplorerDlg.CleanPkgDepsMenuItemClick Pkg=',Pkg<>nil]); if Pkg=nil then continue; Dependency:=Pkg.FindDependencyByName(Info.Dependency); debugln(['TPkgGraphExplorerDlg.CleanPkgDepsMenuItemClick Dep=',Dependency<>nil]); if Dependency=nil then continue; // open package editor if Assigned(OnOpenPackage) then OnOpenPackage(Self,Pkg); // remove dependency PackageGraph.RemoveDependencyFromPackage(Pkg,Dependency,true); end; end; UpdateLvlGraph; finally ListOfNodeInfos.Free; end; end; procedure TPkgGraphExplorerDlg.FormCreate(Sender: TObject); begin ButtonPanel1.OKButton.Caption:= lisClose; GraphOptionsMenuItem.Caption := ShowOptions; end; procedure TPkgGraphExplorerDlg.GraphOptionsMenuItemClick(Sender: TObject); begin if ShowDependencyGraphOptions(FPackageGraphOpts, LvlGraphControl1.Graph, UnitDepOptionsForPackage, @GraphOptsApplyClicked) = mrOK then begin FPackageGraphOpts.WriteToGraph(LvlGraphControl1); UpdateLvlGraph; LvlGraphControl1.AutoLayout; MainIDEInterface.SaveEnvironment; end; end; procedure TPkgGraphExplorerDlg.HelpButtonClick(Sender: TObject); begin OpenUrl('https://wiki.freepascal.org/IDE_Window:_Package_Graph'); end; procedure TPkgGraphExplorerDlg.LvlGraphControl1SelectionChanged(Sender: TObject ); var LGNode: TLvlGraphNode; TVNode: TTreeNode; begin if FUpdatingSelection then exit; LGNode:=LvlGraphControl1.SelectedNode; if LGNode<>nil then begin TVNode:=PkgTreeView.Items.FindNodeWithText(LGNode.Caption); FUpdatingSelection:=true; PkgTreeView.Selected:=TVNode; FUpdatingSelection:=false; end; UpdateInfo; end; procedure TPkgGraphExplorerDlg.OKButtonClick(Sender: TObject); begin Close; end; procedure TPkgGraphExplorerDlg.PkgPopupMenuPopup(Sender: TObject); var Pkg: TLazPackage; begin Pkg:=GetSelectedPackage; //DebugLn(['TPkgGraphExplorerDlg.PkgPopupMenuPopup ',Pkg<>nil,' ',(Pkg<>nil) and (Pkg.AutoInstall<>pitNope)]); UninstallMenuItem.Visible:=(Pkg<>nil) and (Pkg.AutoInstall<>pitNope); if UninstallMenuItem.Visible then UninstallMenuItem.Caption:=Format(lisPckExplUninstallPackage, [Pkg.IDAsString]); CleanPkgDepsMenuItem.Caption:=lisPckShowUnneededDependencies; end; procedure TPkgGraphExplorerDlg.InfoMemoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if (Key = VK_ESCAPE) and (Parent=nil) then Close; end; procedure TPkgGraphExplorerDlg.PkgTreeViewDblClick(Sender: TObject); var TVNode: TTreeNode; Pkg: TLazPackage; begin TVNode:=PkgTreeView.Selected; if TVNode=nil then exit; Pkg:=FindPackage(TVNode.Text); if Pkg=nil then exit; if Assigned(OnOpenPackage) then OnOpenPackage(Self,Pkg); end; procedure TPkgGraphExplorerDlg.PkgTreeViewExpanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); var TV: TTreeView; Pkg: TLazPackage; Dependency: TPkgDependency; i: Integer; CycleNode: TTreeNode; SubNode: TTreeNode; ImgIndex: Integer; NodeText: String; ReqPkg: TLazPackage; InstallPkgList: TFPList; begin TV:=PkgTreeView; Pkg:=FindPackage(Node.Text); //debugln(['TPkgGraphExplorerDlg.PkgTreeViewExpanding ',Node.Text,' Pkg=',Pkg<>nil]); if (Pkg=nil) or (Pkg.FirstRequiredDependency=nil) then begin AllowExpansion:=false; Node.HasChildren:=false; exit; end; InstallPkgList:=nil; try PackageGraph.GetAllRequiredPackages(nil, PackageGraph.FirstInstallDependency,InstallPkgList,[]); i:=0; Dependency:=Pkg.FirstRequiredDependency; while Dependency<>nil do begin ReqPkg:=Dependency.RequiredPackage; if ReqPkg<>nil then NodeText:=PackageAsNodeText(ReqPkg) else NodeText:=Dependency.AsString; if Node.Count=i then SubNode:=TV.Items.AddChild(Node,NodeText) else begin SubNode:=Node[i]; SubNode.Text:=NodeText; end; if ReqPkg<>nil then begin CycleNode:=Node; while (CycleNode<>nil) and (CycleNode.Text<>NodeText) do CycleNode:=CycleNode.Parent; if CycleNode<>nil then ImgIndex:=ImgIndexCyclePackage else ImgIndex:=GetPackageImageIndex(ReqPkg,InstallPkgList); SubNode.HasChildren:=ReqPkg.FirstRequiredDependency<>nil; end else begin ImgIndex:=ImgIndexMissingPackage; SubNode.HasChildren:=false; end; SubNode.ImageIndex:=ImgIndex; SubNode.SelectedIndex:=ImgIndex; inc(i); Dependency:=Dependency.NextRequiresDependency; end; while Node.Count>i do Node[Node.Count-1].Free; finally InstallPkgList.Free; end; end; procedure TPkgGraphExplorerDlg.PkgTreeViewSelectionChanged(Sender: TObject); var TVNode: TTreeNode; LGNode: TLvlGraphNode; begin if FUpdatingSelection then exit; TVNode:=PkgTreeView.Selected; if TVNode=nil then exit; LGNode:=LvlGraphControl1.Graph.GetNode(TVNode.Text,false); FUpdatingSelection:=true; LvlGraphControl1.SelectedNode:=LGNode; FUpdatingSelection:=false; UpdateInfo; end; procedure TPkgGraphExplorerDlg.UninstallMenuItemClick(Sender: TObject); var Pkg: TLazPackage; begin Pkg:=GetSelectedPackage; if Pkg<>nil then begin if Assigned(OnUninstallPackage) then OnUninstallPackage(Self,Pkg); end; end; procedure TPkgGraphExplorerDlg.SetupComponents; begin ImgIndexProject := IDEImages.LoadImage('item_project'); ImgIndexPackage := IDEImages.LoadImage('item_package'); ImgIndexInstalledPackage := IDEImages.LoadImage('pkg_installed'); ImgIndexInstallPackage := IDEImages.LoadImage('pkg_package_autoinstall'); ImgIndexUninstallPackage := IDEImages.LoadImage('pkg_package_uninstall'); ImgIndexCyclePackage := IDEImages.LoadImage('pkg_package_circle'); ImgIndexMissingPackage := IDEImages.LoadImage('pkg_conflict'); PkgTreeView.Images:=IDEImages.Images_16; LvlGraphControl1.Images:=IDEImages.Images_16; LvlGraphControl1.NodeStyle.DefaultImageIndex:=ImgIndexPackage; LvlGraphControl1.Caption:=''; end; function TPkgGraphExplorerDlg.GetPackageImageIndex(Pkg: TLazPackage; InstallPkgList: TFPList): integer; var WillInstall: Boolean; begin WillInstall:=(Pkg.AutoInstall<>pitNope) or ((InstallPkgList<>nil) and (InstallPkgList.IndexOf(Pkg)>=0)); if Pkg.Installed<>pitNope then begin // installed if WillInstall then begin Result:=ImgIndexInstalledPackage; end else begin Result:=ImgIndexUninstallPackage; end; end else begin // not installed if WillInstall then begin Result:=ImgIndexInstallPackage; end else begin Result:=ImgIndexPackage; end; end; end; function TPkgGraphExplorerDlg.GetSelectedPackage: TLazPackage; var Node: TLvlGraphNode; begin Result:=nil; Node:=nil; if assigned(LvlGraphControl1) and assigned(LvlGraphControl1.Graph) Then Node:=LvlGraphControl1.Graph.FirstSelected; if Node=nil then exit; Result:=FindPackage(Node.Caption); end; function TPkgGraphExplorerDlg.FindPackage(const NodeText: string): TLazPackage; var NodePackageID: TLazPackageID; begin Result:=nil; NodePackageID:=TLazPackageID.Create; try if NodePackageID.StringToID(NodeText) then Result:=PackageGraph.FindPackageWithID(NodePackageID); finally NodePackageID.Free; end; end; function TPkgGraphExplorerDlg.PackageAsNodeText(Pkg: TLazPackage): string; begin Result:=Pkg.IDAsString; end; procedure TPkgGraphExplorerDlg.KeyUp(var Key: Word; Shift: TShiftState); begin inherited KeyUp(Key, Shift); ExecuteIDEShortCut(Self,Key,Shift,nil); end; constructor TPkgGraphExplorerDlg.Create(TheOwner: TComponent); begin inherited Create(TheOwner); FUpdateLock:=PackageGraph.UpdateLock; Name:=NonModalIDEWindowNames[nmiwPkgGraphExplorer]; Caption:=lisMenuPackageGraph; SetupComponents; FPackageGraphOpts := TLvlGraphOptions.Create; FPackageGraphOpts.OnLoaded := @DoLoadedOpts; EnvironmentOptions.RegisterSubConfig(FPackageGraphOpts, 'Packages/PackageGraph'); end; destructor TPkgGraphExplorerDlg.Destroy; begin if EnvironmentOptions <> nil then EnvironmentOptions.UnRegisterSubConfig(FPackageGraphOpts); FPackageGraphOpts.Free; inherited Destroy; end; procedure TPkgGraphExplorerDlg.BeginUpdate; begin inc(FUpdateLock); end; procedure TPkgGraphExplorerDlg.EndUpdate; begin if FUpdateLock<=0 then RaiseGDBException('TPkgGraphExplorerDlg.EndUpdate'); dec(FUpdateLock); if FChangedDuringLock then UpdateAll; end; function TPkgGraphExplorerDlg.IsUpdateLocked: boolean; begin Result:=FUpdateLock>0; end; procedure TPkgGraphExplorerDlg.UpdateAll; begin if IsUpdateLocked then begin FChangedDuringLock:=true; exit; end; FChangedDuringLock:=false; if not Visible then exit; UpdateTree; UpdateLvlGraph; UpdateInfo; end; procedure TPkgGraphExplorerDlg.UpdateTree; var Cnt: Integer; i: Integer; CurIndex: Integer; ViewNode: TTreeNode; NextViewNode: TTreeNode; HiddenNode: TAVLTreeNode; CurPkg: TLazPackage; OldExpanded: TTreeNodeExpandedState; fSortedPackages: TAVLTree; InstallPkgList: TFPList; begin InstallPkgList:=nil; fSortedPackages:=TAVLTree.Create(@CompareLazPackageID); try // get list of packages Cnt:=PackageGraph.Count; for i:=0 to Cnt-1 do fSortedPackages.Add(PackageGraph[i]); PackageGraph.GetAllRequiredPackages(nil, PackageGraph.FirstInstallDependency,InstallPkgList,[]); // rebuild the TreeView PkgTreeView.BeginUpdate; // save old expanded state OldExpanded:=TTreeNodeExpandedState.Create(PkgTreeView); // create first level CurIndex:=0; HiddenNode:=fSortedPackages.FindLowest; ViewNode:=PkgTreeView.Items.GetFirstNode; while HiddenNode<>nil do begin CurPkg:=TLazPackage(HiddenNode.Data); if ViewNode=nil then ViewNode:=PkgTreeView.Items.Add(nil,PackageAsNodeText(CurPkg)) else ViewNode.Text:=PackageAsNodeText(CurPkg); ViewNode.HasChildren:=CurPkg.FirstRequiredDependency<>nil; ViewNode.Expanded:=false; ViewNode.ImageIndex:=GetPackageImageIndex(CurPkg,InstallPkgList); ViewNode.SelectedIndex:=ViewNode.ImageIndex; ViewNode:=ViewNode.GetNextSibling; HiddenNode:=fSortedPackages.FindSuccessor(HiddenNode); inc(CurIndex); end; while ViewNode<>nil do begin NextViewNode:=ViewNode.GetNextSibling; ViewNode.Free; ViewNode:=NextViewNode; end; // restore old expanded state OldExpanded.Apply(PkgTreeView); OldExpanded.Free; // completed finally InstallPkgList.Free; fSortedPackages.Free; PkgTreeView.EndUpdate; end; end; procedure TPkgGraphExplorerDlg.UpdateLvlGraph; procedure AddEdges(LGNode: TLvlGraphNode; Dependency: TPkgDependency); begin while Dependency<>nil do begin if Dependency.RequiredPackage<>nil then LvlGraphControl1.Graph.GetEdge(LGNode, LvlGraphControl1.Graph.GetNode(PackageAsNodeText(Dependency.RequiredPackage),true),true); Dependency:=Dependency.NextRequiresDependency; end; end; var AVLNode: TAVLTreeNode; CurPkg: TLazPackage; Cnt: Integer; i: Integer; ViewNode: TLvlGraphNode; OldSelected: String; ProjectNode: TLvlGraphNode; IDENode: TLvlGraphNode; fSortedPackages: TAVLTree; InstallPkgList: TFPList; begin InstallPkgList:=nil; fSortedPackages:=TAVLTree.Create(@CompareLazPackageID); LvlGraphControl1.BeginUpdate; try // get list of packages Cnt:=PackageGraph.Count; for i:=0 to Cnt-1 do fSortedPackages.Add(PackageGraph[i]); PackageGraph.GetAllRequiredPackages(nil, PackageGraph.FirstInstallDependency,InstallPkgList,[]); // save old selection OldSelected:=''; ViewNode:=LvlGraphControl1.Graph.FirstSelected; if ViewNode<>nil then OldSelected:=ViewNode.Caption; LvlGraphControl1.Graph.Clear; // add a node for the project ProjectNode:=nil; if Project1<>nil then begin ProjectNode:=LvlGraphControl1.Graph.GetNode(GroupPrefixProject,true); ProjectNode.ImageIndex:=ImgIndexProject; end; // add a node for the IDE IDENode:=LvlGraphControl1.Graph.GetNode(GroupPrefixIDE,true); IDENode.ImageIndex:=ImgIndexProject; // add nodes for packages AVLNode:=fSortedPackages.FindLowest; while AVLNode<>nil do begin CurPkg:=TLazPackage(AVLNode.Data); ViewNode:=LvlGraphControl1.Graph.GetNode(PackageAsNodeText(CurPkg),true); ViewNode.ImageIndex:=GetPackageImageIndex(CurPkg,InstallPkgList); AVLNode:=fSortedPackages.FindSuccessor(AVLNode); end; // add project dependencies if ProjectNode<>nil then AddEdges(ProjectNode,Project1.FirstRequiredDependency); // add IDE dependencies AddEdges(IDENode,PackageGraph.FirstInstallDependency); // add package dependencies AVLNode:=fSortedPackages.FindLowest; while AVLNode<>nil do begin CurPkg:=TLazPackage(AVLNode.Data); ViewNode:=LvlGraphControl1.Graph.GetNode(PackageAsNodeText(CurPkg),true); AddEdges(ViewNode,CurPkg.FirstRequiredDependency); AVLNode:=fSortedPackages.FindSuccessor(AVLNode); end; LvlGraphControl1.SelectedNode:=LvlGraphControl1.Graph.GetNode(OldSelected,false); finally InstallPkgList.Free; fSortedPackages.Free; LvlGraphControl1.EndUpdate; end; end; procedure TPkgGraphExplorerDlg.UpdateInfo; var Pkg: TLazPackage; InfoStr: String; procedure AddState(const NewState: string); begin if (InfoStr<>'') and (InfoStr[length(InfoStr)]<>' ') then InfoStr:=InfoStr+', '; InfoStr:=InfoStr+NewState; end; begin InfoStr:=''; Pkg:=GetSelectedPackage; if Pkg<>nil then begin // filename and title InfoStr:=Format(lisOIPFilename, [Pkg.Filename]); // state InfoStr:=Format(lisPckExplState, [InfoStr+LineEnding]); if Pkg.Missing then AddState(lisLpkIsMissing); if Pkg.Installed<>pitNope then AddState(lisPckExplInstalled); if (Pkg.AutoInstall<>pitNope) and (Pkg.Installed=pitNope) then AddState(lisPckExplInstallOnNextStart); if (Pkg.AutoInstall=pitNope) and (Pkg.Installed<>pitNope) then AddState(lisPckExplUninstallOnNextStart); InfoStr:=Format(lisOIPDescriptionDescription, [InfoStr+LineEnding, BreakString(Pkg.Description, 60, length(lisOIPDescription))]); end; InfoMemo.Text:=InfoStr; end; procedure TPkgGraphExplorerDlg.UpdatePackageName(Pkg: TLazPackage; const OldName: string); begin UpdateAll; end; procedure TPkgGraphExplorerDlg.UpdatePackageID(Pkg: TLazPackage); begin UpdateAll; end; procedure TPkgGraphExplorerDlg.UpdatePackageAdded(Pkg: TLazPackage); begin UpdateAll; end; procedure TPkgGraphExplorerDlg.SelectPackage(Pkg: TLazPackage); begin if Pkg=nil then exit; LvlGraphControl1.SelectedNode:=FindLvlGraphNodeWithText(PackageAsNodeText(Pkg)); end; procedure TPkgGraphExplorerDlg.OpenDependencyOwner(DependencyOwner: TObject); begin if DependencyOwner is TLazPackage then begin if Assigned(OnOpenPackage) then OnOpenPackage(Self,TLazPackage(DependencyOwner)); end else if DependencyOwner is TProject then begin if Assigned(OnOpenProject) then OnOpenProject(Self,TProject(DependencyOwner)); end; end; procedure TPkgGraphExplorerDlg.DoLoadedOpts(Sender: TObject); begin FPackageGraphOpts.WriteToGraph(LvlGraphControl1); end; procedure TPkgGraphExplorerDlg.GraphOptsApplyClicked(AnOpts: TLvlGraphOptions; AGraph: TLvlGraph); begin AnOpts.WriteToGraph(LvlGraphControl1); UpdateLvlGraph; MainIDEInterface.SaveEnvironment; end; function TPkgGraphExplorerDlg.FindLvlGraphNodeWithText(const s: string): TLvlGraphNode; begin Result:=LvlGraphControl1.Graph.GetNode(s,false); end; procedure TPkgGraphExplorerDlg.ShowPath(PathList: TFPList); // PathList is a list of TLazPackage and TPkgDependency procedure SelectChild(var Node: TTreeNode; const NodeText: string); var i: Integer; CurNode: TTreeNode; begin if Node=nil then begin for i:=0 to PkgTreeView.Items.TopLvlCount-1 do begin CurNode:=PkgTreeView.Items.TopLvlItems[i]; if SysUtils.CompareText(CurNode.Text,NodeText)=0 then begin Node:=CurNode; exit; end; end; end else begin Node.Expanded:=true; for i:=0 to Node.Count-1 do begin if SysUtils.CompareText(Node[i].Text,NodeText)=0 then begin Node:=Node[i]; exit; end; end; end; Node:=nil; end; var AnObject: TObject; i: Integer; CurNode, LastNode: TTreeNode; begin LvlGraphControl1.BeginUpdate; PkgTreeView.BeginUpdate; CurNode:=nil; LastNode:=nil; for i:=0 to PathList.Count-1 do begin AnObject:=TObject(PathList[i]); LastNode:=CurNode; if AnObject is TLazPackage then begin SelectChild(CurNode,TLazPackage(AnObject).IDAsString); end else if AnObject is TPkgDependency then begin SelectChild(CurNode,TPkgDependency(AnObject).AsString); end else break; if CurNode=nil then break; end; if CurNode<>nil then Lastnode:=CurNode; PkgTreeView.Selected:=LastNode; PkgTreeView.EndUpdate; LvlGraphControl1.EndUpdate; end; end.