1 {
2 ***************************************************************************
3 *                                                                         *
4 *   This source is free software; you can redistribute it and/or modify   *
5 *   it under the terms of the GNU General Public License as published by  *
6 *   the Free Software Foundation; either version 2 of the License, or     *
7 *   (at your option) any later version.                                   *
8 *                                                                         *
9 *   This code is distributed in the hope that it will be useful, but      *
10 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
11 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
12 *   General Public License for more details.                              *
13 *                                                                         *
14 *   A copy of the GNU General Public License is available on the World    *
15 *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
16 *   obtain it by writing to the Free Software Foundation,                 *
17 *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
18 *                                                                         *
19 ***************************************************************************
20 
21   Author: Mattias Gaertner
22 
23   Abstract:
24     IDE Window showing dependencies of units and packages.
25 
26   ToDo:
27     - show unit selected in TV on units graph
28 }
29 unit UnitDependencies;
30 
31 {$mode objfpc}{$H+}
32 
33 {$I ide.inc}
34 
35 interface
36 
37 uses
38   // RTL + FCL
39   Classes, SysUtils, types, math, Laz_AVL_Tree,
40   // LCL
41   Forms, Controls, ExtCtrls, ComCtrls, StdCtrls, Buttons, Dialogs, Menus, Clipbrd,
42   // CodeTools
43   CodeToolManager, DefineTemplates, CTUnitGraph, CTUnitGroupGraph,
44   FileProcs, CodeCache, AvgLvlTree,
45   // LazUtils
46   LazLoggerBase, LazFileUtils, LazFileCache, LazStringUtils, LazUTF8, LvlGraphCtrl,
47   // IDE interface
48   LazIDEIntf, ProjectIntf, IDEWindowIntf, PackageIntf, SrcEditorIntf, IDEImagesIntf,
49   IDEMsgIntf, IDEExternToolIntf, IDECommands, IDEDialogs,
50   // IDE
51   IDEOptionDefs, LazarusIDEStrConsts, UnusedUnitsDlg;
52 
53 const
54   GroupPrefixProject = '-Project-';
55   GroupPrefixFPCSrc = 'FPC:';
56   GroupNone = '-None-';
57 type
58 
59   { TUDSCCNode }
60 
61   TUDSCCNode = class
62   public
63     UDItem: TObject; // a TUDUnit or TUDUses
64     InIntfCycle: boolean;
65     InImplCycle: boolean;
66     TarjanIndex: integer;
67     TarjanLowLink: integer;
68     TarjanVisiting: boolean; // currently on stack
AsStringnull69     function AsString: string;
70     constructor Create(Item: TObject);
71   end;
72 
73   { TUDUnit }
74 
75   TUDUnit = class(TUGGroupUnit)
76   public
77     SCCNode: TUDSCCNode;
GetSCCNodenull78     function GetSCCNode: TUDSCCNode;
HasImplementationUsesnull79     function HasImplementationUses: boolean;
80     destructor Destroy; override;
81   end;
82 
83   { TUDUses }
84 
85   TUDUses = class(TUGUses)
86   public
87     SCCNode: TUDSCCNode;
GetSCCNodenull88     function GetSCCNode: TUDSCCNode;
89     destructor Destroy; override;
90   end;
91 
92   TUDNodeType = (
93     udnNone,
94     udnGroup,
95     udnDirectory,
96     udnInterface,
97     udnImplementation,
98     udnUsedByInterface,
99     udnUsedByImplementation,
100     udnUnit
101     );
102   TUDNodeTypes = set of TUDNodeType;
103 
104   { TUDBaseNode }
105 
106   TUDBaseNode = class
107   public
108     TVNode: TTreeNode;
109     NodeText: string;
110     Typ: TUDNodeType;
111     Identifier: string; // GroupName, Directory, Filename
112     Group: string;
113     HasChildren: boolean;
114     IntfCycle: boolean;
115     ImplCycle: boolean;
116     HasImplementationUses: boolean;
117   end;
118 
119   { TUDNode }
120 
121   TUDNode = class(TUDBaseNode)
122   public
123     Parent: TUDNode;
124     ChildNodes: TAVLTree; // tree of TUDNode sorted for Typ and NodeText
125     constructor Create;
126     destructor Destroy; override;
127     procedure Clear;
GetNodenull128     function GetNode(aTyp: TUDNodeType; const ANodeText: string;
129       CreateIfNotExists: boolean = false): TUDNode;
FindFirstnull130     function FindFirst(aTyp: TUDNodeType): TUDNode;
FindUnitnull131     function FindUnit(const aUnitName: string): TUDNode;
Countnull132     function Count: integer;
133   end;
134 
135   TUDWFlag = (
136     udwParsing,
137     udwNeedUpdateGroupsLvlGraph, // rebuild GroupsLvlGraph
138     udwNeedUpdateUnitsLvlGraph, // rebuild UnitsLvlGraph
139     udwNeedUpdateAllUnitsTreeView, // rebuild AllUnitsTreeView
140     udwNeedUpdateAllUnitsTVSearch, // update search in AllUnitsTreeView
141     udwNeedUpdateSelUnitsTreeView, // rebuild SelUnitsTreeView
142     udwNeedUpdateSelUnitsTVSearch // update search in SelUnitsTreeView
143     );
144   TUDWFlags = set of TUDWFlag;
145 
146   { TUnitDependenciesWindow }
147 
148   TUnitDependenciesWindow = class(TForm)
149     AllUnitsFilterEdit: TEdit;
150     AllUnitsSearchEdit: TEdit;
151     AllUnitsSearchNextSpeedButton: TSpeedButton;
152     AllUnitsSearchPrevSpeedButton: TSpeedButton;
153     AllUnitsGroupBox: TGroupBox;
154     AllUnitsShowDirsSpeedButton: TSpeedButton;
155     AllUnitsShowGroupNodesSpeedButton: TSpeedButton;
156     AllUnitsTreeView: TTreeView; // Node.Data is TUDNode
157     MainPageControl: TPageControl;
158     UnitsTVOpenFileMenuItem: TMenuItem;
159     RefreshButton: TButton;
160     StatsLabel: TLabel;
161     StatusPanel: TPanel;
162     Timer1: TTimer;
163     UnitsTVUnusedUnitsMenuItem: TMenuItem;
164     UnitsTVCopyFilenameMenuItem: TMenuItem;
165     UnitsTVCollapseAllMenuItem: TMenuItem;
166     UnitsTVExpandAllMenuItem: TMenuItem;
167     ProgressBar1: TProgressBar;
168     GroupsTabSheet: TTabSheet;
169     GroupsSplitter: TSplitter;
170     SearchPkgsCheckBox: TCheckBox;
171     SearchSrcEditCheckBox: TCheckBox;
172     SelectedUnitsGroupBox: TGroupBox;
173     SelUnitsSearchEdit: TEdit;
174     SelUnitsSearchNextSpeedButton: TSpeedButton;
175     SelUnitsSearchPrevSpeedButton: TSpeedButton;
176     SelUnitsTreeView: TTreeView;
177     SearchCustomFilesBrowseButton: TButton;
178     SearchCustomFilesCheckBox: TCheckBox;
179     ScopePanel: TPanel;
180     SearchCustomFilesComboBox: TComboBox;
181     UnitsSplitter: TSplitter;
182     UnitsTabSheet: TTabSheet;
183     UnitsTVPopupMenu: TPopupMenu;
184     procedure AllUnitsFilterEditChange(Sender: TObject);
185     procedure AllUnitsSearchEditChange(Sender: TObject);
186     procedure AllUnitsSearchNextSpeedButtonClick(Sender: TObject);
187     procedure AllUnitsSearchPrevSpeedButtonClick(Sender: TObject);
188     procedure AllUnitsShowDirsSpeedButtonClick(Sender: TObject);
189     procedure AllUnitsShowGroupNodesSpeedButtonClick(Sender: TObject);
190     procedure FormShow(Sender: TObject);
191     procedure RefreshButtonClick(Sender: TObject);
192     procedure SelUnitsTreeViewExpanding(Sender: TObject; Node: TTreeNode;
193       var AllowExpansion: Boolean);
194     procedure Timer1Timer(Sender: TObject);
195     procedure UnitsLvlGraphMouseDown(Sender: TObject; Button: TMouseButton;
196       Shift: TShiftState; X, Y: Integer);
197     procedure UnitsLvlGraphSelectionChanged(Sender: TObject);
198     procedure UnitsTreeViewShowHint(Sender: TObject; HintInfo: PHintInfo);
199     procedure UnitsTreeViewMouseDown(Sender: TObject; Button: TMouseButton;
200       Shift: TShiftState; X, Y: Integer);
201     procedure AllUnitsTreeViewSelectionChanged(Sender: TObject);
202     procedure FormCreate(Sender: TObject);
203     procedure FormDestroy(Sender: TObject);
204     procedure GroupsLvlGraphSelectionChanged(Sender: TObject);
205     procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean);
206     procedure SearchPkgsCheckBoxChange(Sender: TObject);
207     procedure SearchSrcEditCheckBoxChange(Sender: TObject);
208     procedure SelUnitsSearchEditChange(Sender: TObject);
209     procedure SelUnitsSearchNextSpeedButtonClick(Sender: TObject);
210     procedure SelUnitsSearchPrevSpeedButtonClick(Sender: TObject);
211     procedure SearchCustomFilesBrowseButtonClick(Sender: TObject);
212     procedure SearchCustomFilesCheckBoxChange(Sender: TObject);
213     procedure SearchCustomFilesComboBoxChange(Sender: TObject);
214     procedure UnitsTVCollapseAllMenuItemClick(Sender: TObject);
215     procedure UnitsTVCopyFilenameMenuItemClick(Sender: TObject);
216     procedure UnitsTVExpandAllMenuItemClick(Sender: TObject);
217     procedure UnitsTVOpenFileMenuItemClick(Sender: TObject);
218     procedure UnitsTVPopupMenuPopup(Sender: TObject);
219     procedure UnitsTVUnusedUnitsMenuItemClick(Sender: TObject);
220   private
221     FCurrentUnit: TUGUnit;
222     FIdleConnected: boolean;
223     FPendingUnitDependencyRoute: TStrings;
224     FUsesGraph: TUsesGraph;
225     FGroups: TUGGroups; // referenced by Nodes.Data of GroupsLvlGraph
226     FNewUsesGraph: TUsesGraph; // on idle the units are scanned and this graph
227       // is filled up, when parsing is complete it becomes the new UsesGraph
228     FNewGroups: TUGGroups;
229     FAllUnitsRootUDNode: TUDNode;
230     FSelUnitsRootUDNode: TUDNode;
231     FFlags: TUDWFlags;
232     fImgIndexProject: integer;
233     fImgIndexUnit: integer;
234     fImgIndexPackage: integer;
235     fImgIndexDirectory: integer;
236     fImgIndexOverlayImplUses: integer;
237     fImgIndexOverlayIntfCycle: integer;
238     fImgIndexOverlayImplCycle: integer;
239     fAllUnitsTVSearchStartNode: TTreeNode;
240     fSelUnitsTVSearchStartNode: TTreeNode;
CreateAllUnitsTreenull241     function CreateAllUnitsTree: TUDNode;
CreateSelUnitsTreenull242     function CreateSelUnitsTree: TUDNode;
243     procedure ExpandPendingUnitDependencyRoute(RootNode: TUDNode);
244     procedure ConvertUnitNameRouteToPath(Route: TStrings); // inserts missing links
245     procedure AddUsesSubNodes(UDNode: TUDNode);
246     procedure CreateTVNodes(TV: TTreeView;
247       ParentTVNode: TTreeNode; ParentUDNode: TUDNode; Expand: boolean);
248     procedure FreeUsesGraph;
GetPopupTV_UDNodenull249     function GetPopupTV_UDNode(out UDNode: TUDNode): boolean;
250     procedure SelectNextSearchTV(TV: TTreeView; StartTVNode: TTreeNode;
251       SearchNext, SkipStart: boolean);
FindNextTVNodenull252     function FindNextTVNode(StartNode: TTreeNode;
253       LowerSearch: string; SearchNext, SkipStart: boolean): TTreeNode;
FindUnitTVNodeWithFilenamenull254     function FindUnitTVNodeWithFilename(TV: TTreeView; aFilename: string): TTreeNode;
FindUnitTVNodeWithUnitNamenull255     function FindUnitTVNodeWithUnitName(TV: TTreeView; aUnitName: string): TTreeNode;
256     procedure SetCurrentUnit(AValue: TUGUnit);
257     procedure SetIdleConnected(AValue: boolean);
258     procedure CreateGroups;
CreateProjectGroupnull259     function CreateProjectGroup(AProject: TLazProject): TUGGroup;
CreatePackageGroupnull260     function CreatePackageGroup(APackage: TIDEPackage): TUGGroup;
261     procedure CreateFPCSrcGroups;
262     procedure GuessGroupOfUnits;
263     procedure MarkCycles(WithImplementationUses: boolean);
264     procedure SetPendingUnitDependencyRoute(AValue: TStrings);
265     procedure StartParsing;
266     procedure ScopeChanged;
267     procedure AddStartAndTargetUnits;
268     procedure AddAdditionalFilesAsStartUnits;
269     procedure SetupGroupsTabSheet;
270     procedure SetupUnitsTabSheet;
271     procedure UpdateUnitsButtons;
272     procedure UpdateAll;
273     procedure UpdateGroupsLvlGraph;
274     procedure UpdateUnitsLvlGraph;
275     procedure UpdateAllUnitsTreeView;
276     procedure UpdateSelUnitsTreeView;
277     procedure UpdateAllUnitsTreeViewSearch;
278     procedure UpdateSelUnitsTreeViewSearch;
GetImgIndexnull279     function GetImgIndex(Node: TUDNode): integer;
NodeTextToUnitnull280     function NodeTextToUnit(NodeText: string): TUGUnit;
UGUnitToNodeTextnull281     function UGUnitToNodeText(UGUnit: TUGUnit): string;
GetFPCSrcDirnull282     function GetFPCSrcDir: string;
IsFPCSrcGroupnull283     function IsFPCSrcGroup(Group: TUGGroup): boolean;
IsProjectGroupnull284     function IsProjectGroup(Group: TUGGroup): boolean;
IsProjectGroupnull285     function IsProjectGroup(GroupName: string): boolean;
GetFilenamenull286     function GetFilename(UDNode: TUDNode): string;
GetAllUnitsFilternull287     function GetAllUnitsFilter(Lower: boolean): string;
GetAllUnitsSearchnull288     function GetAllUnitsSearch(Lower: boolean): string;
GetSelUnitsSearchnull289     function GetSelUnitsSearch(Lower: boolean): string;
ResStrFilternull290     function ResStrFilter: string;
ResStrSearchnull291     function ResStrSearch: string;
NodeTextFitsFilternull292     function NodeTextFitsFilter(const NodeText, LowerFilter: string): boolean;
293     procedure CreateUsesGraph(out TheUsesGraph: TUsesGraph; out TheGroups: TUGGroups);
294   public
295     GroupsLvlGraph: TLvlGraphControl; // Nodes.Data are TUGGroup of Groups
296     UnitsLvlGraph: TLvlGraphControl; // Nodes.Data are Units in Groups
297   public
298     property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
299     property UsesGraph: TUsesGraph read FUsesGraph;
300     property Groups: TUGGroups read FGroups;
301     property CurrentUnit: TUGUnit read FCurrentUnit write SetCurrentUnit;
302     property PendingUnitDependencyRoute: TStrings read FPendingUnitDependencyRoute
303       write SetPendingUnitDependencyRoute; // list of unit names, missing links are automatically found
304   end;
305 
306 type
307 
308   { TQuickFixCircularUnitReference }
309 
310   TQuickFixCircularUnitReference = class(TMsgQuickFix)
311   public
IsApplicablenull312     function IsApplicable(Msg: TMessageLine; out Unitname1, Unitname2: string): boolean;
313     procedure CreateMenuItems(Fixes: TMsgQuickFixes); override;
314     procedure QuickFix({%H-}Fixes: TMsgQuickFixes; Msg: TMessageLine); override;
315   end;
316 
317 var
318   UnitDependenciesWindow: TUnitDependenciesWindow;
319 
320 procedure ShowUnitDependenciesClicked(Sender: TObject);
321 procedure ShowUnitDependencies(State: TIWGetFormState = iwgfShowOnTop);
322 procedure InitUnitDependenciesQuickFixItems;
323 
CompareUDBaseNodesnull324 function CompareUDBaseNodes(UDNode1, UDNode2: Pointer): integer;
325 
326 implementation
327 
328 {$R *.lfm}
329 
330 procedure ShowUnitDependenciesClicked(Sender: TObject);
331 begin
332   ShowUnitDependencies;
333 end;
334 
335 procedure ShowUnitDependencies(State: TIWGetFormState);
336 begin
337   if UnitDependenciesWindow = Nil then
338     IDEWindowCreators.CreateForm(UnitDependenciesWindow,TUnitDependenciesWindow,
339        State=iwgfDisabled,LazarusIDE.OwningComponent)
340   else if State=iwgfDisabled then
341     UnitDependenciesWindow.DisableAlign;
342   if State>=iwgfShow then
343     IDEWindowCreators.ShowForm(UnitDependenciesWindow,State=iwgfShowOnTop);
344 end;
345 
346 procedure InitUnitDependenciesQuickFixItems;
347 begin
348   RegisterIDEMsgQuickFix(TQuickFixCircularUnitReference.Create);
349 end;
350 
CompareUDBaseNodesnull351 function CompareUDBaseNodes(UDNode1, UDNode2: Pointer): integer;
352 var
353   Node1: TUDBaseNode absolute UDNode1;
354   Node2: TUDBaseNode absolute UDNode2;
355 begin
356   Result:=ord(Node1.Typ)-ord(Node2.Typ);
357   if Result<>0 then exit;
358   case Node1.Typ of
359   udnDirectory: Result:=CompareFilenames(Node1.NodeText,Node2.NodeText);
360   else Result:=SysUtils.CompareText(Node1.NodeText,Node2.NodeText);
361   end;
362 end;
363 
364 { TUDSCCNode }
365 
AsStringnull366 function TUDSCCNode.AsString: string;
367 begin
368   if UDItem is TUDUnit then
369     Result:='Unit="'+ExtractFileNameOnly(TUDUnit(UDItem).Filename)+'"'
370   else
371     Result:='Uses="'+ExtractFileNameOnly(TUDUses(UDItem).Owner.Filename)+'"->"'+ExtractFileNameOnly(TUDUses(UDItem).UsesUnit.Filename)+'"';
372   Result+=',Index='+dbgs(TarjanIndex)+',LowLink='+dbgs(TarjanLowLink)+',Visiting='+dbgs(TarjanVisiting);
373 end;
374 
375 constructor TUDSCCNode.Create(Item: TObject);
376 begin
377   UDItem:=Item;
378   TarjanIndex:=-1;
379 end;
380 
381 { TUDUses }
382 
TUDUses.GetSCCNodenull383 function TUDUses.GetSCCNode: TUDSCCNode;
384 begin
385   if SCCNode=nil then
386     SCCNode:=TUDSCCNode.Create(Self);
387   Result:=SCCNode;
388 end;
389 
390 destructor TUDUses.Destroy;
391 begin
392   FreeAndNil(SCCNode);
393   inherited Destroy;
394 end;
395 
396 { TUDUnit }
397 
TUDUnit.GetSCCNodenull398 function TUDUnit.GetSCCNode: TUDSCCNode;
399 begin
400   if SCCNode=nil then
401     SCCNode:=TUDSCCNode.Create(Self);
402   Result:=SCCNode;
403 end;
404 
TUDUnit.HasImplementationUsesnull405 function TUDUnit.HasImplementationUses: boolean;
406 var
407   i: Integer;
408 begin
409   Result:=false;
410   if UsesUnits=nil then exit;
411   for i:=0 to UsesUnits.Count-1 do
412     if TUDUses(UsesUnits[i]).InImplementation then
413       exit(true);
414 end;
415 
416 destructor TUDUnit.Destroy;
417 begin
418   FreeAndNil(SCCNode);
419   inherited Destroy;
420 end;
421 
422 { TQuickFixCircularUnitReference }
423 
TQuickFixCircularUnitReference.IsApplicablenull424 function TQuickFixCircularUnitReference.IsApplicable(Msg: TMessageLine; out
425   Unitname1, Unitname2: string): boolean;
426 begin
427   Result:=IDEFPCParser.MsgLineIsId(Msg,10020,Unitname1,Unitname2);
428 end;
429 
430 procedure TQuickFixCircularUnitReference.CreateMenuItems(Fixes: TMsgQuickFixes);
431 var
432   Msg: TMessageLine;
433   Unitname1: string;
434   Unitname2: string;
435   i: Integer;
436 begin
437   for i:=0 to Fixes.LineCount-1 do begin
438     Msg:=Fixes.Lines[i];
439     if not IsApplicable(Msg,Unitname1,Unitname2) then continue;
440     Fixes.AddMenuItem(Self,Msg,'Show unit dependencies');
441     exit;
442   end;
443 end;
444 
445 procedure TQuickFixCircularUnitReference.QuickFix(Fixes: TMsgQuickFixes;
446   Msg: TMessageLine);
447 var
448   UnitName1: String;
449   UnitName2: String;
450   Path: TStringList;
451 begin
452   if not IsApplicable(Msg,UnitName1,UnitName2) then exit;
453   ShowUnitDependencies;
454   Path:=TStringList.Create;
455   try
456     Path.Add(UnitName1);
457     Path.Add(UnitName2);
458     Path.Add(UnitName1);
459     UnitDependenciesWindow.PendingUnitDependencyRoute:=Path;
460   finally
461     Path.Free;
462   end;
463 end;
464 
465 { TUDNode }
466 
467 constructor TUDNode.Create;
468 begin
469   ChildNodes:=TAVLTree.Create(@CompareUDBaseNodes);
470 end;
471 
472 destructor TUDNode.Destroy;
473 begin
474   Clear;
475   FreeAndNil(ChildNodes);
476   inherited Destroy;
477 end;
478 
479 procedure TUDNode.Clear;
480 begin
481   ChildNodes.FreeAndClear;
482 end;
483 
GetNodenull484 function TUDNode.GetNode(aTyp: TUDNodeType; const ANodeText: string;
485   CreateIfNotExists: boolean): TUDNode;
486 var
487   Node: TUDBaseNode;
488   AVLNode: TAVLTreeNode;
489 begin
490   Node:=TUDBaseNode.Create;
491   Node.Typ:=aTyp;
492   Node.NodeText:=ANodeText;
493   AVLNode:=ChildNodes.Find(Node);
494   Node.Free;
495   if AVLNode<>nil then begin
496     Result:=TUDNode(AVLNode.Data);
497   end else if CreateIfNotExists then begin
498     Result:=TUDNode.Create;
499     Result.Typ:=aTyp;
500     Result.NodeText:=ANodeText;
501     ChildNodes.Add(Result);
502     Result.Parent:=Self;
503   end else
504     Result:=nil;
505 end;
506 
TUDNode.FindFirstnull507 function TUDNode.FindFirst(aTyp: TUDNodeType): TUDNode;
508 var
509   AVLNode: TAVLTreeNode;
510 begin
511   AVLNode:=ChildNodes.FindLowest;
512   while AVLNode<>nil do begin
513     Result:=TUDNode(AVLNode.Data);
514     if Result.Typ=aTyp then exit;
515     AVLNode:=ChildNodes.FindSuccessor(AVLNode);
516   end;
517   Result:=nil;
518 end;
519 
FindUnitnull520 function TUDNode.FindUnit(const aUnitName: string): TUDNode;
521 var
522   AVLNode: TAVLTreeNode;
523 begin
524   AVLNode:=ChildNodes.FindLowest;
525   while AVLNode<>nil do begin
526     Result:=TUDNode(AVLNode.Data);
527     if (Result.Typ=udnUnit)
528     and (CompareText(ExtractFileNameOnly(Result.Identifier),aUnitName)=0) then
529       exit;
530     AVLNode:=ChildNodes.FindSuccessor(AVLNode);
531   end;
532   Result:=nil;
533 end;
534 
TUDNode.Countnull535 function TUDNode.Count: integer;
536 begin
537   Result:=ChildNodes.Count;
538 end;
539 
540 { TUnitDependenciesWindow }
541 
542 procedure TUnitDependenciesWindow.FormCreate(Sender: TObject);
543 begin
544   Name := NonModalIDEWindowNames[nmiwUnitDependenciesName];
545 
546   FPendingUnitDependencyRoute:=TStringList.Create;
547   CreateUsesGraph(FUsesGraph,FGroups);
548 
549   fImgIndexProject   := IDEImages.LoadImage('item_project');
550   fImgIndexUnit      := IDEImages.LoadImage('item_unit');
551   fImgIndexPackage   := IDEImages.LoadImage('pkg_required');
552   fImgIndexDirectory := IDEImages.LoadImage('pkg_files');
553   fImgIndexOverlayImplUses := IDEImages.LoadImage('pkg_core_overlay');
554   fImgIndexOverlayIntfCycle := IDEImages.LoadImage('ce_cycleinterface');
555   fImgIndexOverlayImplCycle := IDEImages.LoadImage('ce_cycleimplementation');
556   AllUnitsTreeView.Images:=IDEImages.Images_16;
557   SelUnitsTreeView.Images:=IDEImages.Images_16;
558 
559   Caption:=lisMenuViewUnitDependencies;
560   RefreshButton.Caption:=dlgUnitDepRefresh;
561 
562   MainPageControl.ActivePage:=UnitsTabSheet;
563 
564   SetupUnitsTabSheet;
565   SetupGroupsTabSheet;
566 
567   StartParsing;
568 end;
569 
570 procedure TUnitDependenciesWindow.AllUnitsSearchEditChange(Sender: TObject);
571 begin
572   Include(FFlags,udwNeedUpdateAllUnitsTVSearch);
573   IdleConnected:=true;
574 end;
575 
576 procedure TUnitDependenciesWindow.AllUnitsSearchNextSpeedButtonClick(Sender: TObject);
577 begin
578   SelectNextSearchTV(AllUnitsTreeView,AllUnitsTreeView.Selected,true,true);
579   fAllUnitsTVSearchStartNode:=AllUnitsTreeView.Selected;
580 end;
581 
582 procedure TUnitDependenciesWindow.AllUnitsSearchPrevSpeedButtonClick(Sender: TObject);
583 begin
584   SelectNextSearchTV(AllUnitsTreeView,AllUnitsTreeView.Selected,false,true);
585   fAllUnitsTVSearchStartNode:=AllUnitsTreeView.Selected;
586 end;
587 
588 procedure TUnitDependenciesWindow.AllUnitsShowDirsSpeedButtonClick(Sender: TObject);
589 begin
590   Include(FFlags,udwNeedUpdateAllUnitsTreeView);
591   IdleConnected:=true;
592 end;
593 
594 procedure TUnitDependenciesWindow.AllUnitsShowGroupNodesSpeedButtonClick(Sender: TObject);
595 begin
596   Include(FFlags,udwNeedUpdateAllUnitsTreeView);
597   IdleConnected:=true;
598 end;
599 
600 procedure TUnitDependenciesWindow.FormShow(Sender: TObject);
601 begin
602   AllUnitsFilterEdit.TextHint:=ResStrFilter;
603   AllUnitsSearchEdit.TextHint:=ResStrSearch;
604   SelUnitsSearchEdit.TextHint:=ResStrSearch;
605 end;
606 
607 procedure TUnitDependenciesWindow.RefreshButtonClick(Sender: TObject);
608 begin
609   if udwParsing in FFlags then exit;
610   StartParsing;
611 end;
612 
613 procedure TUnitDependenciesWindow.SelUnitsTreeViewExpanding(Sender: TObject;
614   Node: TTreeNode; var AllowExpansion: Boolean);
615 var
616   UDNode: TUDNode;
617 begin
618   if Node.Count>0 then exit;
619   if not (TObject(Node.Data) is TUDNode) then exit;
620   UDNode:=TUDNode(Node.Data);
621   if UDNode.Typ=udnUnit then begin
622     AddUsesSubNodes(UDNode);
623     CreateTVNodes(SelUnitsTreeView,Node,UDNode,false);
624     AllowExpansion:=true;
625   end;
626 end;
627 
628 procedure TUnitDependenciesWindow.Timer1Timer(Sender: TObject);
629 var
630   Cnt: Integer;
631 begin
632   if (FNewUsesGraph=nil) then exit;
633   Cnt:=0;
634   if FNewUsesGraph.FilesTree<>nil then
635     Cnt:=FNewUsesGraph.FilesTree.Count;
636   StatsLabel.Caption:=Format(lisUDScanningUnits, [IntToStr(Cnt)]);
637 end;
638 
639 procedure TUnitDependenciesWindow.UnitsLvlGraphMouseDown(Sender: TObject;
640   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
641 var
642   GraphNode: TLvlGraphNode;
643   UGUnit: TUGUnit;
644 begin
645   GraphNode:=UnitsLvlGraph.GetNodeAt(X,Y);
646   if (Button=mbLeft) and (ssDouble in Shift) then begin
647     if (GraphNode<>nil) and (GraphNode.Data<>nil) then begin
648       UGUnit:=TUGUnit(GraphNode.Data);
649       LazarusIDE.DoOpenEditorFile(UGUnit.Filename,-1,-1,[ofAddToRecent]);
650     end;
651   end;
652 end;
653 
654 procedure TUnitDependenciesWindow.UnitsLvlGraphSelectionChanged(Sender: TObject);
655 var
656   GraphNode: TLvlGraphNode;
657   UGUnit: TUGUnit;
658 begin
659   GraphNode:=UnitsLvlGraph.Graph.FirstSelected;
660   while GraphNode<>nil do begin
661     UGUnit:=TUGUnit(GraphNode.Data);
662     if UGUnit<>nil then begin
663 
664     end;
665     GraphNode:=GraphNode.NextSelected;
666   end;
667 end;
668 
669 procedure TUnitDependenciesWindow.UnitsTreeViewShowHint(Sender: TObject;
670   HintInfo: PHintInfo);
671 
672   procedure CountUses(List: TFPList; out IntfCnt, ImplCnt: integer);
673   var
674     i: Integer;
675   begin
676     IntfCnt:=0;
677     ImplCnt:=0;
678     if List=nil then exit;
679     for i:=0 to List.Count-1 do
680       if TUDUses(List[i]).InImplementation then
681         inc(ImplCnt)
682       else
683         inc(IntfCnt);
684   end;
685 
686 var
687   TV: TTreeView;
688   TVNode: TTreeNode;
689   p: types.TPoint;
690   UDNode: TUDNode;
691   Filename: String;
692   s: String;
693   UGUnit: TUGUnit;
694   UsedByIntf: Integer;
695   UsedByImpl: Integer;
696   UsesIntf: integer;
697   UsesImpl: integer;
698 begin
699   TV:=Sender as TTreeView;
700   p:=HintInfo^.CursorPos;
701   TVNode:=TV.GetNodeAt(p.X,p.Y);
702   if (TVNode=nil) or not (TObject(TVNode.Data) is TUDNode) then exit;
703   UDNode:=TUDNode(TVNode.Data);
704   Filename:=GetFilename(UDNode);
705   if Filename='' then exit;
706   s:=Format(lisUDFile, [Filename]);
707   if UDNode.Typ=udnUnit then begin
708     UGUnit:=UsesGraph.GetUnit(Filename,false);
709     if UGUnit<>nil then begin
710       CountUses(UGUnit.UsesUnits,UsesIntf,UsesImpl);
711       CountUses(UGUnit.UsedByUnits,UsedByIntf,UsedByImpl);
712       if UsesIntf>0 then
713         s+=LineEnding+Format(lisUDInterfaceUses, [IntToStr(UsesIntf)]);
714       if UsesImpl>0 then
715         s+=LineEnding+Format(lisUDImplementationUses, [IntToStr(UsesImpl)]);
716       if UsedByIntf>0 then
717         s+=LineEnding+Format(lisUDUsedByInterfaces, [IntToStr(UsedByIntf)]);
718       if UsedByImpl>0 then
719         s+=LineEnding+Format(lisUDUsedByImplementations, [IntToStr(UsedByImpl)]
720           );
721     end;
722   end;
723   HintInfo^.HintStr:=s;
724 end;
725 
726 procedure TUnitDependenciesWindow.UnitsTreeViewMouseDown(Sender: TObject;
727   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
728 var
729   TVNode: TTreeNode;
730   UDNode: TUDNode;
731   UGGroup: TUGGroup;
732   TV: TTreeView;
733 begin
734   TV:=Sender as TTreeView;
735   TVNode:=TV.GetNodeAt(X,Y);
736   if TVNode=nil then exit;
737   UDNode:=nil;
738   if TObject(TVNode.Data) is TUDNode then
739     UDNode:=TUDNode(TVNode.Data);
740   if (Button=mbLeft) and (ssDouble in Shift) and (UDNode<>nil) then begin
741     if UDNode.Typ=udnUnit then
742       // open unit in source editor
743       LazarusIDE.DoOpenEditorFile(UDNode.Identifier,-1,-1,[ofAddToRecent])
744     else if UDNode.Typ=udnGroup then begin
745       UGGroup:=Groups.GetGroup(UDNode.Group,false);
746       if UGGroup=nil then exit;
747       if IsProjectGroup(UGGroup) then begin
748         // open project inspector
749         ExecuteIDECommand(Self,ecProjectInspector);
750       end else begin
751         // open package editor
752         PackageEditingInterface.DoOpenPackageWithName(UGGroup.Name,[pofAddToRecent],false);
753       end;
754     end;
755   end;
756 end;
757 
758 procedure TUnitDependenciesWindow.AllUnitsTreeViewSelectionChanged(
759   Sender: TObject);
760 begin
761   Include(FFlags,udwNeedUpdateSelUnitsTreeView);
762   IdleConnected:=true;
763 end;
764 
765 procedure TUnitDependenciesWindow.AllUnitsFilterEditChange(Sender: TObject);
766 begin
767   Include(FFlags,udwNeedUpdateAllUnitsTreeView);
768   IdleConnected:=true;
769 end;
770 
771 
772 procedure TUnitDependenciesWindow.FormDestroy(Sender: TObject);
773 begin
774   IdleConnected:=false;
775 
776   FreeUsesGraph;
777   FreeAndNil(FNewGroups);
778   FreeAndNil(FNewUsesGraph);
779   FreeAndNil(FPendingUnitDependencyRoute);
780 end;
781 
782 procedure TUnitDependenciesWindow.GroupsLvlGraphSelectionChanged(Sender: TObject
783   );
784 begin
785   UpdateUnitsLvlGraph;
786 end;
787 
788 procedure TUnitDependenciesWindow.OnIdle(Sender: TObject; var Done: Boolean);
789 var
790   Completed: boolean;
791 begin
792   if udwParsing in FFlags then begin
793     fNewUsesGraph.Parse(true,Completed,200);
794     if Completed then begin
795       Exclude(FFlags,udwParsing);
796       // free old uses graph
797       FreeUsesGraph;
798       // switch to new UsesGraph
799       FUsesGraph:=FNewUsesGraph;
800       FNewUsesGraph:=nil;
801       FGroups:=FNewGroups;
802       FNewGroups:=nil;
803       // create Groups
804       CreateGroups;
805       // mark cycles
806       MarkCycles(false);
807       MarkCycles(true);
808       // hide progress bar and update stats
809       ProgressBar1.Visible:=false;
810       ProgressBar1.Style:=pbstNormal;
811       RefreshButton.Enabled:=true;
812       Timer1.Enabled:=false;
813       StatsLabel.Caption:=Format(lisUDUnits2, [IntToStr(
814         FUsesGraph.FilesTree.Count)]);
815       // update controls
816       UpdateAll;
817     end;
818   end else if udwNeedUpdateGroupsLvlGraph in FFlags then
819     UpdateGroupsLvlGraph
820   else if udwNeedUpdateUnitsLvlGraph in FFlags then
821     UpdateUnitsLvlGraph
822   else if udwNeedUpdateAllUnitsTreeView in FFlags then
823     UpdateAllUnitsTreeView
824   else if udwNeedUpdateAllUnitsTVSearch in FFlags then
825     UpdateAllUnitsTreeViewSearch
826   else if udwNeedUpdateSelUnitsTreeView in FFlags then
827     UpdateSelUnitsTreeView
828   else if udwNeedUpdateSelUnitsTVSearch in FFlags then
829     UpdateSelUnitsTreeViewSearch
830   else
831     IdleConnected:=false;
832   Done:=not IdleConnected;
833 end;
834 
835 procedure TUnitDependenciesWindow.SearchPkgsCheckBoxChange(Sender: TObject);
836 begin
837   ScopeChanged;
838 end;
839 
840 procedure TUnitDependenciesWindow.SearchSrcEditCheckBoxChange(Sender: TObject);
841 begin
842   ScopeChanged;
843 end;
844 
845 procedure TUnitDependenciesWindow.SelUnitsSearchEditChange(Sender: TObject);
846 begin
847   Include(FFlags,udwNeedUpdateSelUnitsTVSearch);
848   IdleConnected:=true;
849 end;
850 
851 procedure TUnitDependenciesWindow.SelUnitsSearchNextSpeedButtonClick(Sender: TObject);
852 begin
853   SelectNextSearchTV(SelUnitsTreeView,SelUnitsTreeView.Selected,true,true);
854   fSelUnitsTVSearchStartNode:=SelUnitsTreeView.Selected;
855 end;
856 
857 procedure TUnitDependenciesWindow.SelUnitsSearchPrevSpeedButtonClick(Sender: TObject);
858 begin
859   SelectNextSearchTV(SelUnitsTreeView,SelUnitsTreeView.Selected,false,true);
860   fSelUnitsTVSearchStartNode:=SelUnitsTreeView.Selected;
861 end;
862 
863 procedure TUnitDependenciesWindow.SearchCustomFilesBrowseButtonClick(Sender: TObject);
864 var
865   Dlg: TSelectDirectoryDialog;
866   s: TCaption;
867   aFilename: String;
868   p: Integer;
869 begin
870   Dlg:=TSelectDirectoryDialog.Create(nil);
871   try
872     InitIDEFileDialog(Dlg);
873     Dlg.Options:=Dlg.Options+[ofPathMustExist];
874     if not Dlg.Execute then exit;
875     aFilename:=TrimFilename(Dlg.FileName);
876     s:=SearchCustomFilesComboBox.Text;
877     p:=1;
878     if FindNextDelimitedItem(s,';',p,aFilename)<>'' then exit;
879     if s<>'' then s+=';';
880     s+=aFilename;
881     SearchCustomFilesComboBox.Text:=s;
882     ScopeChanged;
883   finally
884     Dlg.Free;
885   end;
886 end;
887 
888 procedure TUnitDependenciesWindow.SearchCustomFilesCheckBoxChange(Sender: TObject);
889 begin
890   UpdateUnitsButtons;
891   ScopeChanged;
892 end;
893 
894 procedure TUnitDependenciesWindow.SearchCustomFilesComboBoxChange(Sender: TObject);
895 begin
896   ScopeChanged;
897 end;
898 
899 procedure TUnitDependenciesWindow.UnitsTVCollapseAllMenuItemClick(Sender: TObject);
900 var
901   TV: TTreeView;
902   i: Integer;
903 begin
904   TV:=TTreeView(UnitsTVPopupMenu.PopupComponent);
905   if not (TV is TTreeView) then exit;
906   TV.BeginUpdate;
907   for i:=0 to TV.Items.TopLvlCount-1 do
908     TV.Items.TopLvlItems[i].Collapse(true);
909   TV.EndUpdate;
910 end;
911 
912 procedure TUnitDependenciesWindow.UnitsTVCopyFilenameMenuItemClick(Sender: TObject);
913 var
914   UDNode: TUDNode;
915 begin
916   if not GetPopupTV_UDNode(UDNode) then exit;
917   Clipboard.AsText:=GetFilename(UDNode);
918 end;
919 
920 procedure TUnitDependenciesWindow.UnitsTVExpandAllMenuItemClick(Sender: TObject);
921 var
922   TV: TTreeView;
923   i: Integer;
924 begin
925   TV:=TTreeView(UnitsTVPopupMenu.PopupComponent);
926   if not (TV is TTreeView) then exit;
927   TV.BeginUpdate;
928   for i:=0 to TV.Items.TopLvlCount-1 do
929     TV.Items.TopLvlItems[i].Expand(true);
930   TV.EndUpdate;
931 end;
932 
933 procedure TUnitDependenciesWindow.UnitsTVOpenFileMenuItemClick(Sender: TObject);
934 var
935   UDNode: TUDNode;
936 begin
937   if not GetPopupTV_UDNode(UDNode) then exit;
938   LazarusIDE.DoOpenEditorFile(GetFilename(UDNode),-1,-1,OpnFlagsPlainFile);
939 end;
940 
941 procedure TUnitDependenciesWindow.UnitsTVPopupMenuPopup(Sender: TObject);
942 var
943   TV: TTreeView;
944   TVNode: TTreeNode;
945   UDNode: TUDNode;
946   aFilename: String;
947   ShortFilename: String;
948 begin
949   TV:=UnitsTVPopupMenu.PopupComponent as TTreeView;
950   UnitsTVExpandAllMenuItem.Visible:=TV=AllUnitsTreeView;
951   TVNode:=TV.Selected;
952   if (TVNode<>nil) and (TObject(TVNode.Data) is TUDNode) then begin
953     UDNode:=TUDNode(TVNode.Data);
954     UnitsTVUnusedUnitsMenuItem.Enabled:=UDNode.Typ=udnUnit;
955     aFilename:=GetFilename(UDNode);
956     if aFilename<>'' then begin
957       ShortFilename:=aFilename;
958       if length(ShortFilename)>50 then
959         ShortFilename:='...'+ExtractFilename(ShortFilename);
960       UnitsTVCopyFilenameMenuItem.Enabled:=true;
961       UnitsTVCopyFilenameMenuItem.Caption:=Format(lisCopyFilename, [
962         ShortFilename]);
963       UnitsTVOpenFileMenuItem.Visible:=true;
964       UnitsTVOpenFileMenuItem.Caption:=Format(lisOpenLfm, [ShortFilename]);
965     end else begin
966       UnitsTVCopyFilenameMenuItem.Enabled:=false;
967       UnitsTVCopyFilenameMenuItem.Caption:=uemCopyFilename;
968       UnitsTVOpenFileMenuItem.Visible:=false;
969     end;
970   end else
971     UnitsTVUnusedUnitsMenuItem.Enabled:=false;
972 end;
973 
974 procedure TUnitDependenciesWindow.UnitsTVUnusedUnitsMenuItemClick(Sender: TObject);
975 var
976   TV: TTreeView;
977   TVNode: TTreeNode;
978   UDNode: TUDNode;
979   Filename: String;
980   Code: TCodeBuffer;
981 begin
982   TV:=TTreeView(UnitsTVPopupMenu.PopupComponent);
983   if not (TV is TTreeView) then exit;
984   TVNode:=TV.Selected;
985   if (TVNode=nil) or not (TObject(TVNode.Data) is TUDNode) then exit;
986   UDNode:=TUDNode(TVNode.Data);
987   if UDNode.Typ<>udnUnit then exit;
988   Filename:=GetFilename(UDNode);
989   Code:=CodeToolBoss.LoadFile(Filename,true,false);
990   ShowUnusedUnitsDialog(Code);
991 end;
992 
993 procedure TUnitDependenciesWindow.SetIdleConnected(AValue: boolean);
994 begin
995   if FIdleConnected=AValue then Exit;
996   FIdleConnected:=AValue;
997   if IdleConnected then
998     Application.AddOnIdleHandler(@OnIdle)
999   else
1000     Application.RemoveOnIdleHandler(@OnIdle);
1001 end;
1002 
1003 procedure TUnitDependenciesWindow.CreateGroups;
1004 var
1005   i: Integer;
1006 begin
1007   if FGroups=nil then
1008     RaiseCatchableException('');
1009   CreateProjectGroup(LazarusIDE.ActiveProject);
1010   for i:=0 to PackageEditingInterface.GetPackageCount-1 do
1011     CreatePackageGroup(PackageEditingInterface.GetPackages(i));
1012   CreateFPCSrcGroups;
1013   GuessGroupOfUnits;
1014 end;
1015 
TUnitDependenciesWindow.CreateProjectGroupnull1016 function TUnitDependenciesWindow.CreateProjectGroup(AProject: TLazProject): TUGGroup;
1017 var
1018   i: Integer;
1019   Filename: String;
1020   CurUnit: TUGUnit;
1021   ProjFile: TLazProjectFile;
1022 begin
1023   if AProject=nil then exit(nil);
1024   Result:=Groups.GetGroup(GroupPrefixProject,true);
1025   Result.BaseDir:=ExtractFilePath(AProject.ProjectInfoFile);
1026   if not FilenameIsAbsolute(Result.BaseDir) then
1027     Result.BaseDir:='';
1028   //debugln(['TUnitDependenciesDialog.CreateProjectGroup ',Result.Name,' FileCount=',AProject.FileCount]);
1029   for i:=0 to AProject.FileCount-1 do begin
1030     ProjFile:=AProject.Files[i];
1031     if not ProjFile.IsPartOfProject then continue;
1032     Filename:=AProject.Files[i].Filename;
1033     CurUnit:=UsesGraph.GetUnit(Filename,false);
1034     if CurUnit=nil then continue;
1035     if not (CurUnit is TUDUnit) then begin
1036       debugln(['TUnitDependenciesDialog.CreateProjectGroup WARNING: ',CurUnit.Filename,' ',CurUnit.Classname,' should be TUGGroupUnit']);
1037       continue;
1038     end;
1039     if TUDUnit(CurUnit).Group<>nil then continue;
1040     Result.AddUnit(TUDUnit(CurUnit));
1041   end;
1042 end;
1043 
TUnitDependenciesWindow.CreatePackageGroupnull1044 function TUnitDependenciesWindow.CreatePackageGroup(APackage: TIDEPackage): TUGGroup;
1045 var
1046   i: Integer;
1047   Filename: String;
1048   CurUnit: TUGUnit;
1049 begin
1050   if APackage=nil then exit(nil);
1051   Result:=Groups.GetGroup(APackage.Name,true);
1052   Result.BaseDir:=APackage.DirectoryExpanded;
1053   if not FilenameIsAbsolute(Result.BaseDir) then
1054     Result.BaseDir:='';
1055   //debugln(['TUnitDependenciesDialog.CreatePackageGroup ',Result.Name]);
1056   for i:=0 to APackage.FileCount-1 do begin
1057     Filename:=APackage.Files[i].GetFullFilename;
1058     CurUnit:=UsesGraph.GetUnit(Filename,false);
1059     if CurUnit is TUDUnit then begin
1060       if TUDUnit(CurUnit).Group<>nil then continue;
1061       Result.AddUnit(TUDUnit(CurUnit));
1062     end;
1063   end;
1064 end;
1065 
1066 procedure TUnitDependenciesWindow.CreateFPCSrcGroups;
1067 
ExtractFilePathStartnull1068   function ExtractFilePathStart(Filename: string; DirCount: integer): string;
1069   var
1070     p: Integer;
1071   begin
1072     p:=1;
1073     while p<=length(Filename) do begin
1074       if Filename[p]=PathDelim then begin
1075         DirCount-=1;
1076         if DirCount=0 then begin
1077           Result:=LeftStr(Filename,p-1);
1078           exit;
1079         end;
1080       end;
1081       inc(p);
1082     end;
1083     Result:=Filename;
1084   end;
1085 
1086 var
1087   FPCSrcDir: String;
1088   Node: TAVLTreeNode;
1089   CurUnit: TUDUnit;
1090   Directory: String;
1091   Grp: TUGGroup;
1092   BaseDir: String;
1093 begin
1094   FPCSrcDir:=AppendPathDelim(GetFPCSrcDir);
1095 
1096   // for each unit in the fpc source directory:
1097   // if in rtl/ put into group GroupPrefixFPCSrc+RTL
1098   // if in packages/<name>, put in group GroupPrefixFPCSrc+<name>
1099   Node:=UsesGraph.FilesTree.FindLowest;
1100   while Node<>nil do begin
1101     CurUnit:=TUDUnit(Node.Data);
1102     Node:=UsesGraph.FilesTree.FindSuccessor(Node);
1103     if TUDUnit(CurUnit).Group<>nil then continue;
1104     if CompareFilenames(FPCSrcDir,LeftStr(CurUnit.Filename,length(FPCSrcDir)))<>0
1105     then
1106       continue;
1107     // a unit in the FPC sources
1108     BaseDir:=ExtractFilePath(CurUnit.Filename);
1109     Directory:=copy(BaseDir,length(FPCSrcDir)+1,length(BaseDir));
1110     Directory:=ExtractFilePathStart(Directory,2);
1111     if LeftStr(Directory,length('rtl'))='rtl' then
1112       Directory:='RTL'
1113     else if LeftStr(Directory,length('packages'))='packages' then
1114       System.Delete(Directory,1,length('packages'+PathDelim));
1115     Grp:=Groups.GetGroup(GroupPrefixFPCSrc+Directory,true);
1116     if Grp.BaseDir='' then
1117       Grp.BaseDir:=BaseDir;
1118     //debugln(['TUnitDependenciesDialog.CreateFPCSrcGroups ',Grp.Name]);
1119     Grp.AddUnit(TUDUnit(CurUnit));
1120   end;
1121 end;
1122 
1123 procedure TUnitDependenciesWindow.GuessGroupOfUnits;
1124 var
1125   Node: TAVLTreeNode;
1126   CurUnit: TUDUnit;
1127   Filename: String;
1128   Owners: TFPList;
1129   i: Integer;
1130   Group: TUGGroup;
1131   CurDirectory: String;
1132   LastDirectory: Char;
1133 begin
1134   Owners:=nil;
1135   LastDirectory:='.';
1136   Node:=UsesGraph.FilesTree.FindLowest;
1137   while Node<>nil do begin
1138     CurUnit:=TUDUnit(Node.Data);
1139     if CurUnit.Group=nil then begin
1140       Filename:=CurUnit.Filename;
1141       //debugln(['TUnitDependenciesDialog.GuessGroupOfUnits no group for ',Filename]);
1142       CurDirectory:=ExtractFilePath(Filename);
1143       if CompareFilenames(CurDirectory,LastDirectory)<>0 then begin
1144         FreeAndNil(Owners);
1145         Owners:=PackageEditingInterface.GetPossibleOwnersOfUnit(Filename,[piosfIncludeSourceDirectories]);
1146       end;
1147       Group:=nil;
1148       if (Owners<>nil) then begin
1149         for i:=0 to Owners.Count-1 do begin
1150           if TObject(Owners[i]) is TLazProject then begin
1151             Group:=Groups.GetGroup(GroupPrefixProject,true);
1152             //debugln(['TUnitDependenciesDialog.GuessGroupOfUnits ',Group.Name]);
1153             break;
1154           end else if TObject(Owners[i]) is TIDEPackage then begin
1155             Group:=Groups.GetGroup(TIDEPackage(Owners[i]).Name,true);
1156             //debugln(['TUnitDependenciesDialog.GuessGroupOfUnits ',Group.Name]);
1157             break;
1158           end;
1159         end;
1160       end;
1161       if Group=nil then begin
1162         Group:=Groups.GetGroup(GroupNone,true);
1163         //debugln(['TUnitDependenciesDialog.GuessGroupOfUnits ',Group.Name]);
1164       end;
1165       Group.AddUnit(TUDUnit(CurUnit));
1166     end;
1167     Node:=UsesGraph.FilesTree.FindSuccessor(Node);
1168   end;
1169   FreeAndNil(Owners);
1170 end;
1171 
1172 procedure TUnitDependenciesWindow.MarkCycles(WithImplementationUses: boolean);
1173 { Using Tarjan's strongly connected components (SCC) algorithm
1174 }
1175 var
1176   TarjanIndex: integer;
1177   Stack: TFPList; // stack of TUDSCCNode
1178 
GetNodenull1179   function GetNode(UDItem: TObject): TUDSCCNode;
1180   begin
1181     if UDItem is TUDUnit then
1182       Result:=TUDUnit(UDItem).GetSCCNode
1183     else
1184       Result:=TUDUses(UDItem).GetSCCNode;
1185   end;
1186 
1187   procedure ClearNode(Node: TUDSCCNode);
1188   begin
1189     Node.TarjanIndex:=-1;
1190     Node.TarjanLowLink:=-1;
1191     Node.TarjanVisiting:=false;
1192     if WithImplementationUses then
1193       Node.InImplCycle:=false
1194     else
1195       Node.InIntfCycle:=false;
1196   end;
1197 
1198   procedure SearchNode(Node: TUDSCCNode); forward;
1199 
1200   procedure SearchEdge(FromNode, ToNode: TUDSCCNode);
1201   begin
1202     if ToNode.TarjanIndex<0 then begin
1203       // not yet visited
1204       SearchNode(ToNode);
1205       FromNode.TarjanLowLink:=Min(FromNode.TarjanLowLink,ToNode.TarjanLowLink);
1206     end else if ToNode.TarjanVisiting then begin
1207       // currently visiting => ToNode is in current SCC
1208       FromNode.TarjanLowLink:=Min(FromNode.TarjanLowLink,ToNode.TarjanIndex);
1209     end;
1210   end;
1211 
1212   procedure SearchNode(Node: TUDSCCNode);
1213   var
1214     UDUnit: TUDUnit;
1215     UDUses: TUDUses;
1216     i: Integer;
1217     CycleNode: TUDSCCNode;
1218     MoreThanOneNode: Boolean; // true = there is a cycle with more than one node
1219   begin
1220     //debugln(['SearchNode ',Node.AsString]);
1221     // Set the depth index for Node to the smallest unused index
1222     Node.TarjanIndex := TarjanIndex;
1223     Node.TarjanLowLink := TarjanIndex;
1224     inc(TarjanIndex);
1225     Stack.Add(Node);
1226     Node.TarjanVisiting:=true;
1227 
1228     // search all edges
1229     if Node.UDItem is TUDUnit then begin
1230       UDUnit:=TUDUnit(Node.UDItem);
1231       if UDUnit.UsesUnits<>nil then
1232         for i:=0 to UDUnit.UsesUnits.Count-1 do begin
1233           UDUses:=TUDUses(UDUnit.UsesUnits[i]);
1234           if (not WithImplementationUses) and UDUses.InImplementation then
1235             continue;
1236           SearchEdge(Node,GetNode(UDUses));
1237         end;
1238     end else begin
1239       UDUses:=TUDUses(Node.UDItem);
1240       SearchEdge(Node,GetNode(UDUses.UsesUnit));
1241     end;
1242 
1243     if Node.TarjanIndex=Node.TarjanLowLink then begin
1244       // this is a root node of a SCC
1245       MoreThanOneNode:=TUDSCCNode(Stack[Stack.Count-1])<>Node;
1246       repeat
1247         CycleNode:=TUDSCCNode(Stack[Stack.Count-1]);
1248         Stack.Delete(Stack.Count-1);
1249         CycleNode.TarjanVisiting:=false;
1250         if MoreThanOneNode then begin
1251           if WithImplementationUses then
1252             CycleNode.InImplCycle:=true
1253           else
1254             CycleNode.InIntfCycle:=true;
1255           //debugln(['SearchNode WithImpl=',WithImplementationUses,' Cycle=',CycleNode.AsString]);
1256         end;
1257       until CycleNode=Node;
1258     end;
1259   end;
1260 
1261 var
1262   AVLNode: TAVLTreeNode;
1263   UDUnit: TUDUnit;
1264   Node: TUDSCCNode;
1265   i: Integer;
1266 begin
1267   // init
1268   TarjanIndex:=0;
1269   for AVLNode in FUsesGraph.FilesTree do begin
1270     UDUnit:=TUDUnit(AVLNode.Data);
1271     Node:=GetNode(UDUnit);
1272     ClearNode(Node);
1273     if UDUnit.UsesUnits<>nil then
1274       for i:=0 to UDUnit.UsesUnits.Count-1 do
1275         ClearNode(GetNode(TObject(UDUnit.UsesUnits[i])));
1276   end;
1277   Stack:=TFPList.Create;
1278   try
1279     // depth first search through the forest
1280     for AVLNode in FUsesGraph.FilesTree do begin
1281       UDUnit:=TUDUnit(AVLNode.Data);
1282       //debugln(['TUnitDependenciesWindow.MarkCycles ',dbgsname(UDUnit)]);
1283       Node:=GetNode(UDUnit);
1284       if Node.TarjanIndex<0 then
1285         SearchNode(Node);
1286     end;
1287   finally
1288     Stack.Free;
1289   end;
1290 end;
1291 
1292 procedure TUnitDependenciesWindow.SetPendingUnitDependencyRoute(AValue: TStrings);
1293 begin
1294   if FPendingUnitDependencyRoute.Equals(AValue) then Exit;
1295   FPendingUnitDependencyRoute.Assign(AValue);
1296   FFlags:=FFlags+[udwNeedUpdateAllUnitsTreeView,udwNeedUpdateSelUnitsTreeView];
1297   IdleConnected:=true;
1298 end;
1299 
1300 procedure TUnitDependenciesWindow.StartParsing;
1301 begin
1302   if (FNewUsesGraph<>nil) or (udwParsing in FFlags) then
1303     RaiseCatchableException('');
1304   Include(FFlags,udwParsing);
1305 
1306   ProgressBar1.Visible:=true;
1307   ProgressBar1.Style:=pbstMarquee;
1308   StatsLabel.Caption:=lisUDScanning;
1309   Timer1.Enabled:=true;
1310   RefreshButton.Enabled:=false;
1311 
1312   CreateUsesGraph(FNewUsesGraph,FNewGroups);
1313 
1314   LazarusIDE.BeginCodeTools;
1315   AddStartAndTargetUnits;
1316 
1317   IdleConnected:=true;
1318 end;
1319 
1320 procedure TUnitDependenciesWindow.ScopeChanged;
1321 begin
1322   FreeAndNil(FNewGroups);
1323   FreeAndNil(FNewUsesGraph);
1324   Exclude(FFlags,udwParsing);
1325   StartParsing;
1326 end;
1327 
1328 procedure TUnitDependenciesWindow.SetCurrentUnit(AValue: TUGUnit);
1329 begin
1330   if FCurrentUnit=AValue then Exit;
1331   FCurrentUnit:=AValue;
1332 end;
1333 
CreateAllUnitsTreenull1334 function TUnitDependenciesWindow.CreateAllUnitsTree: TUDNode;
1335 var
1336   Node: TUDNode;
1337   ParentNode: TUDNode;
1338   GroupName: String;
1339   ShowDirectories: Boolean;
1340   ShowGroups: Boolean;
1341   NodeText: String;
1342   RootNode: TUDNode;
1343   Filter: String;
1344   UGUnit: TUDUnit;
1345   AVLNode: TAVLTreeNode;
1346   Group: TUGGroup;
1347   GroupNode: TUDNode;
1348   Filename: String;
1349   p: Integer;
1350   Dir: String;
1351   DirNode: TUDNode;
1352   BaseDir: String;
1353   CurDir: String;
1354 begin
1355   Filter:=GetAllUnitsFilter(true);
1356   ShowGroups:=AllUnitsShowGroupNodesSpeedButton.Down;
1357   ShowDirectories:=AllUnitsShowDirsSpeedButton.Down;
1358   RootNode:=TUDNode.Create;
1359   for AVLNode in UsesGraph.FilesTree do begin
1360     UGUnit:=TUDUnit(AVLNode.Data);
1361     Filename:=UGUnit.Filename;
1362     NodeText:=ExtractFileName(Filename);
1363     if (Filter<>'') and (Pos(Filter, UTF8LowerCase(NodeText))<1) then
1364       continue;
1365     Group:=UGUnit.Group;
1366     BaseDir:='';
1367     if Group=nil then begin
1368       GroupName:=GroupNone
1369     end else begin
1370       GroupName:=Group.Name;
1371       if FilenameIsAbsolute(Group.BaseDir) then
1372         BaseDir:=ChompPathDelim(Group.BaseDir);
1373     end;
1374     ParentNode:=RootNode;
1375     if ShowGroups then begin
1376       // create group nodes
1377       GroupNode:=ParentNode.GetNode(udnGroup,GroupName,true);
1378       if GroupNode.Identifier='' then begin
1379         GroupNode.Identifier:=GroupName;
1380         GroupNode.Group:=GroupName;
1381       end;
1382       ParentNode:=GroupNode;
1383       if FilenameIsAbsolute(BaseDir) and FilenameIsAbsolute(Filename) then
1384         Filename:=CreateRelativePath(Filename,BaseDir);
1385     end;
1386     if ShowDirectories then begin
1387       // create directory nodes
1388       CurDir:=BaseDir;
1389       p:=1;
1390       repeat
1391         Dir:=FindNextDirectoryInFilename(Filename,p);
1392         if p>length(Filename) then break;
1393         if Dir<>'' then begin
1394           DirNode:=ParentNode.GetNode(udnDirectory,Dir,true);
1395           CurDir+=PathDelim+Dir;
1396           if DirNode.Identifier='' then begin
1397             DirNode.Identifier:=CurDir;
1398           end;
1399           ParentNode:=DirNode;
1400         end;
1401       until false;
1402     end;
1403     Node:=ParentNode.GetNode(udnUnit, NodeText, true);
1404     Node.Identifier:=UGUnit.Filename;
1405     Node.Group:=GroupName;
1406     Node.IntfCycle:=UGUnit.GetSCCNode.InIntfCycle;
1407     Node.ImplCycle:=UGUnit.GetSCCNode.InImplCycle;
1408     Node.HasImplementationUses:=UGUnit.HasImplementationUses;
1409   end;
1410   Result:=RootNode;
1411 end;
1412 
CreateSelUnitsTreenull1413 function TUnitDependenciesWindow.CreateSelUnitsTree: TUDNode;
1414 var
1415   RootNode: TUDNode;
1416   SelTVNode: TTreeNode;
1417   SelUDNode: TUDNode;
1418   UDNode: TUDNode;
1419 begin
1420   RootNode:=TUDNode.Create;
1421   SelTVNode:=AllUnitsTreeView.GetFirstMultiSelected;
1422   if SelTVNode=nil then
1423     SelTVNode:=AllUnitsTreeView.Selected;
1424   //debugln(['TUnitDependenciesWindow.CreateSelUnitsTree SelTVNode=',SelTVNode<>nil]);
1425   while SelTVNode<>nil do begin
1426     if TObject(SelTVNode.Data) is TUDNode then begin
1427       SelUDNode:=TUDNode(SelTVNode.Data);
1428       if SelUDNode.Typ=udnUnit then begin
1429         UDNode:=RootNode.GetNode(udnUnit,SelUDNode.NodeText,true);
1430         UDNode.Identifier:=SelUDNode.Identifier;
1431         UDNode.Group:=SelUDNode.Group;
1432         AddUsesSubNodes(UDNode);
1433       end;
1434     end;
1435     SelTVNode:=SelTVNode.GetNextMultiSelected;
1436   end;
1437 
1438   ExpandPendingUnitDependencyRoute(RootNode);
1439 
1440   Result:=RootNode;
1441 end;
1442 
1443 procedure TUnitDependenciesWindow.ExpandPendingUnitDependencyRoute(RootNode: TUDNode);
1444 var
1445   i: Integer;
1446   CurUnitName: String;
1447   UDNode: TUDNode;
1448   IntfUDNode: TUDNode;
1449   ParentUDNode: TUDNode;
1450 begin
1451   if PendingUnitDependencyRoute.Count=0 then exit;
1452   ConvertUnitNameRouteToPath(PendingUnitDependencyRoute);
1453   try
1454     ParentUDNode:=RootNode;
1455     for i:=0 to PendingUnitDependencyRoute.Count-1 do begin
1456       CurUnitName:=PendingUnitDependencyRoute[i];
1457       UDNode:=ParentUDNode.FindUnit(CurUnitName);
1458       //debugln(['TUnitDependenciesWindow.ExpandPendingUnitDependencyPath CurUnitName="',CurUnitName,'" UDNode=',DbgSName(UDNode)]);
1459       if UDNode=nil then exit;
1460       if i=PendingUnitDependencyRoute.Count-1 then exit;
1461       IntfUDNode:=UDNode.FindFirst(udnInterface);
1462       if IntfUDNode=nil then begin
1463         if UDNode.Count>0 then
1464           exit; // already expanded -> has no interface
1465         // expand
1466         AddUsesSubNodes(UDNode);
1467         IntfUDNode:=UDNode.FindFirst(udnInterface);
1468         if IntfUDNode=nil then exit;
1469       end;
1470       ParentUDNode:=IntfUDNode;
1471     end;
1472   finally
1473     // apply only once => clear pending
1474     PendingUnitDependencyRoute.Clear;
1475   end;
1476 end;
1477 
1478 procedure TUnitDependenciesWindow.ConvertUnitNameRouteToPath(Route: TStrings);
1479 var
1480   UGUnitList: TFPList;
1481   UGUnit: TUGUnit;
1482   i: Integer;
1483 begin
1484   if Route.Count<=1 then exit;
1485   UGUnitList:=TFPList.Create;
1486   try
1487     // convert unit names to TUGUnit
1488     for i:=0 to Route.Count-1 do begin
1489       UGUnit:=FUsesGraph.FindUnit(Route[i]);
1490       if UGUnit=nil then continue;
1491       UGUnitList.Add(UGUnit);
1492     end;
1493     // insert missing links
1494     FUsesGraph.InsertMissingLinks(UGUnitList);
1495     // convert TUGUnit to unit names
1496     Route.Clear;
1497     for i:=0 to UGUnitList.Count-1 do
1498       Route.Add(ExtractFileNameOnly(TUGUnit(UGUnitList[i]).Filename));
1499   finally
1500     UGUnitList.Free;
1501   end;
1502 end;
1503 
1504 procedure TUnitDependenciesWindow.AddUsesSubNodes(UDNode: TUDNode);
1505 
1506   procedure AddUses(ParentUDNode: TUDNode; UsesList: TFPList;
1507     NodeTyp: TUDNodeType);
1508   var
1509     i: Integer;
1510     UGUses: TUDUses;
1511     NodeText: String;
1512     SectionUDNode: TUDNode;
1513     InImplementation: Boolean;
1514     UsedBy: Boolean;
1515     OtherUnit: TUDUnit;
1516     Filename: String;
1517     UDNode: TUDNode;
1518     GroupName: String;
1519     Cnt: Integer;
1520     HasIntfCycle: Boolean;
1521     HasImplCycle: Boolean;
1522   begin
1523     if ParentUDNode=nil then exit;
1524     if UsesList=nil then exit;
1525     if not (NodeTyp in [udnInterface,udnImplementation,udnUsedByInterface,udnUsedByImplementation])
1526     then exit;
1527     InImplementation:=(NodeTyp in [udnImplementation,udnUsedByImplementation]);
1528     UsedBy:=(NodeTyp in [udnUsedByInterface,udnUsedByImplementation]);
1529 
1530     // count the number of uses
1531     Cnt:=0;
1532     HasIntfCycle:=false;
1533     HasImplCycle:=false;
1534     for i:=0 to UsesList.Count-1 do begin
1535       UGUses:=TUDUses(UsesList[i]);
1536       if UGUses.InImplementation<>InImplementation then continue;
1537       HasIntfCycle:=HasIntfCycle or UGUses.GetSCCNode.InIntfCycle;
1538       HasImplCycle:=HasImplCycle or UGUses.GetSCCNode.InImplCycle;
1539       inc(Cnt);
1540     end;
1541     if Cnt=0 then exit;
1542 
1543     // create a section node
1544     NodeText:=IntToStr(Cnt);
1545     case NodeTyp of
1546     udnInterface: NodeText:=Format(lisUDInterfaceUses2, [NodeText]);
1547     udnImplementation: NodeText:=Format(lisUDImplementationUses2, [NodeText]);
1548     udnUsedByInterface: NodeText:=Format(lisUDUsedByInterfaces2, [NodeText]);
1549     udnUsedByImplementation: NodeText:=Format(lisUDUsedByImplementations2, [
1550       NodeText]);
1551     else exit;
1552     end;
1553     SectionUDNode:=ParentUDNode.GetNode(NodeTyp,NodeText,true);
1554     SectionUDNode.IntfCycle:=HasIntfCycle;
1555     SectionUDNode.ImplCycle:=HasImplCycle;
1556 
1557     // create unit nodes
1558     for i:=0 to UsesList.Count-1 do begin
1559       UGUses:=TUDUses(UsesList[i]);
1560       if UGUses.InImplementation<>InImplementation then continue;
1561       if UsedBy then
1562         OtherUnit:=TUDUnit(UGUses.Owner)
1563       else
1564         OtherUnit:=TUDUnit(UGUses.UsesUnit);
1565       Filename:=OtherUnit.Filename;
1566       NodeText:=ExtractFileName(Filename);
1567       UDNode:=SectionUDNode.GetNode(udnUnit,NodeText,true);
1568       UDNode.Identifier:=Filename;
1569       if OtherUnit.Group<>nil then
1570         GroupName:=OtherUnit.Group.Name
1571       else
1572         GroupName:=GroupNone;
1573       UDNode.Group:=GroupName;
1574       UDNode.HasChildren:=
1575          ((OtherUnit.UsedByUnits<>nil) and (OtherUnit.UsedByUnits.Count>0))
1576          or ((OtherUnit.UsesUnits<>nil) and (OtherUnit.UsesUnits.Count>0));
1577       UDNode.IntfCycle:=UGUses.GetSCCNode.InIntfCycle;
1578       UDNode.ImplCycle:=UGUses.GetSCCNode.InImplCycle;
1579     end;
1580   end;
1581 
1582 var
1583   Filename: String;
1584   UGUnit: TUDUnit;
1585 begin
1586   // add connected units
1587   Filename:=UDNode.Identifier;
1588   UGUnit:=TUDUnit(UsesGraph.GetUnit(Filename,false));
1589   if UGUnit<>nil then begin
1590     AddUses(UDNode,UGUnit.UsesUnits,udnInterface);
1591     AddUses(UDNode,UGUnit.UsesUnits,udnImplementation);
1592     AddUses(UDNode,UGUnit.UsedByUnits,udnUsedByInterface);
1593     AddUses(UDNode,UGUnit.UsedByUnits,udnUsedByImplementation);
1594   end;
1595 end;
1596 
1597 procedure TUnitDependenciesWindow.SelectNextSearchTV(TV: TTreeView;
1598   StartTVNode: TTreeNode; SearchNext, SkipStart: boolean);
1599 var
1600   TVNode: TTreeNode;
1601   NextTVNode: TTreeNode;
1602   PrevTVNode: TTreeNode;
1603   LowerSearch: String;
1604 begin
1605   //debugln(['TUnitDependenciesWindow.SelectNextSearchTV START ',DbgSName(TV),' ',StartTVNode<>nil,' SearchNext=',SearchNext,' SkipStart=',SkipStart]);
1606   TV.BeginUpdate;
1607   try
1608     TVNode:=StartTVNode;
1609     if TVNode=nil then begin
1610       if SearchNext then
1611         TVNode:=TV.Items.GetFirstNode
1612       else
1613         TVNode:=TV.Items.GetLastNode;
1614       SkipStart:=false;
1615     end;
1616     if TV=AllUnitsTreeView then
1617       LowerSearch:=GetAllUnitsSearch(true)
1618     else
1619       LowerSearch:=GetSelUnitsSearch(true);
1620     //if TVNode<>nil then debugln(['TUnitDependenciesWindow.SelectNextSearchTV searching "',LowerSearch,'" TVNode=',TVNode.Text,' SearchNext=',SearchNext,' SkipStart=',SkipStart]);
1621     TVNode:=FindNextTVNode(TVNode,LowerSearch,SearchNext,SkipStart);
1622     //if TVNode<>nil then debugln(['TUnitDependenciesWindow.SelectNextSearchTV found TVNode=',TVNode.Text]);
1623     NextTVNode:=nil;
1624     PrevTVNode:=nil;
1625     if TVNode<>nil then begin
1626       TV.Items.ClearMultiSelection(True);
1627       TV.Selected:=TVNode;
1628       TV.MakeSelectionVisible;
1629       NextTVNode:=FindNextTVNode(TVNode,LowerSearch,true,true);
1630       PrevTVNode:=FindNextTVNode(TVNode,LowerSearch,false,true);
1631     end;
1632     if TV=AllUnitsTreeView then begin
1633       AllUnitsSearchNextSpeedButton.Enabled:=NextTVNode<>nil;
1634       AllUnitsSearchPrevSpeedButton.Enabled:=PrevTVNode<>nil;
1635     end else begin
1636       SelUnitsSearchNextSpeedButton.Enabled:=NextTVNode<>nil;
1637       SelUnitsSearchPrevSpeedButton.Enabled:=PrevTVNode<>nil;
1638     end;
1639   finally
1640     TV.EndUpdate;
1641   end;
1642   //debugln(['TUnitDependenciesWindow.SelectNextSearchTV END']);
1643 end;
1644 
1645 procedure TUnitDependenciesWindow.AddStartAndTargetUnits;
1646 var
1647   aProject: TLazProject;
1648   i: Integer;
1649   SrcEdit: TSourceEditorInterface;
1650   AFilename: String;
1651   Pkg: TIDEPackage;
1652   j: Integer;
1653   PkgFile: TLazPackageFile;
1654 begin
1655   FNewUsesGraph.TargetAll:=true;
1656 
1657   // project lpr
1658   aProject:=LazarusIDE.ActiveProject;
1659   if (aProject<>nil) and (aProject.MainFile<>nil) then
1660     FNewUsesGraph.AddStartUnit(aProject.MainFile.Filename);
1661 
1662   // add all open packages
1663   if SearchPkgsCheckBox.Checked then begin
1664     for i:=0 to PackageEditingInterface.GetPackageCount-1 do begin
1665       Pkg:=PackageEditingInterface.GetPackages(i);
1666       if not FilenameIsAbsolute(Pkg.Filename) then continue;
1667       for j:=0 to Pkg.FileCount-1 do begin
1668         PkgFile:=Pkg.Files[j];
1669         if PkgFile.Removed then continue;
1670         if not (PkgFile.FileType in PkgFileRealUnitTypes) then continue;
1671         if not PkgFile.InUses then continue;
1672         aFilename:=PkgFile.GetFullFilename;
1673         if FilenameIsAbsolute(AFilename)
1674         and FilenameIsPascalUnit(AFilename) then
1675           FNewUsesGraph.AddStartUnit(AFilename);
1676       end;
1677     end;
1678   end;
1679 
1680   // add all source editor files
1681   if SearchSrcEditCheckBox.Checked then begin
1682     for i:=0 to SourceEditorManagerIntf.SourceEditorCount-1 do begin
1683       SrcEdit:=SourceEditorManagerIntf.SourceEditors[i];
1684       AFilename:=SrcEdit.FileName;
1685       if FilenameIsPascalUnit(AFilename) then
1686         FNewUsesGraph.AddStartUnit(AFilename);
1687     end;
1688   end;
1689 
1690   // additional units and directories
1691   if SearchCustomFilesCheckBox.Checked then
1692     AddAdditionalFilesAsStartUnits;
1693 end;
1694 
1695 procedure TUnitDependenciesWindow.AddAdditionalFilesAsStartUnits;
1696 var
1697   List: TCaption;
1698   aFilename: String;
1699   Files: TStrings;
1700   i: Integer;
1701   p: Integer;
1702 begin
1703   List:=SearchCustomFilesComboBox.Text;
1704   p:=1;
1705   while p<=length(List) do begin
1706     aFilename:=TrimAndExpandFilename(GetNextDelimitedItem(List,';',p));
1707     if (AFilename='') then continue;
1708     if not FileExistsCached(aFilename) then continue;
1709     if DirPathExistsCached(aFilename) then begin
1710       aFilename:=AppendPathDelim(aFilename);
1711       // add all units in directory
1712       Files:=nil;
1713       try
1714         CodeToolBoss.DirectoryCachePool.GetListing(aFilename,Files,false);
1715         if Files<>nil then begin
1716           for i:=0 to Files.Count-1 do begin
1717             if FilenameIsPascalUnit(Files[i]) then
1718               fNewUsesGraph.AddStartUnit(aFilename+Files[i]);
1719           end;
1720         end;
1721       finally
1722         Files.Free;
1723       end;
1724     end else begin
1725       // add a single file
1726       fNewUsesGraph.AddStartUnit(aFilename);
1727     end;
1728   end;
1729 end;
1730 
1731 procedure TUnitDependenciesWindow.SetupGroupsTabSheet;
1732 begin
1733   GroupsTabSheet.Caption:=lisUDProjectsAndPackages;
1734 
1735   GroupsLvlGraph:=TLvlGraphControl.Create(Self);
1736   with GroupsLvlGraph do
1737   begin
1738     Name:='GroupsLvlGraph';
1739     Caption:='';
1740     Align:=alTop;
1741     Height:=200;
1742     NodeStyle.GapBottom:=5;
1743     Parent:=GroupsTabSheet;
1744     OnSelectionChanged:=@GroupsLvlGraphSelectionChanged;
1745   end;
1746 
1747   GroupsSplitter.Top:=GroupsLvlGraph.Height;
1748 
1749   UnitsLvlGraph:=TLvlGraphControl.Create(Self);
1750   with UnitsLvlGraph do
1751   begin
1752     Name:='UnitsLvlGraph';
1753     Caption:='';
1754     Align:=alClient;
1755     NodeStyle.GapBottom:=5;
1756     Parent:=GroupsTabSheet;
1757     OnSelectionChanged:=@UnitsLvlGraphSelectionChanged;
1758     OnMouseDown:=@UnitsLvlGraphMouseDown;
1759   end;
1760 end;
1761 
1762 procedure TUnitDependenciesWindow.SetupUnitsTabSheet;
1763 begin
1764   UnitsTabSheet.Caption:=lisUDUnits;
1765 
1766   // start searching
1767   SearchCustomFilesCheckBox.Caption:=lisUDAdditionalDirectories;
1768   SearchCustomFilesCheckBox.Hint:=
1769     lisUDByDefaultOnlyTheProjectUnitsAndTheSourceEditorUnit;
1770   SearchCustomFilesComboBox.Text:='';
1771   SearchCustomFilesBrowseButton.Caption:=lisPathEditBrowse;
1772 
1773   SearchPkgsCheckBox.Caption:=lisUDAllPackageUnits;
1774   SearchSrcEditCheckBox.Caption:=lisUDAllSourceEditorUnits;
1775 
1776   // view all units
1777   AllUnitsGroupBox.Caption:=lisUDAllUnits;
1778 
1779   AllUnitsShowDirsSpeedButton.Hint:=lisUDShowNodesForDirectories;
1780   IDEImages.AssignImage(AllUnitsShowDirsSpeedButton, 'pkg_hierarchical');
1781   AllUnitsShowDirsSpeedButton.Down:=true;
1782   AllUnitsShowGroupNodesSpeedButton.Hint:=lisUDShowNodesForProjectAndPackages;
1783   IDEImages.AssignImage(AllUnitsShowGroupNodesSpeedButton, 'pkg_hierarchical');
1784   AllUnitsShowGroupNodesSpeedButton.Down:=true;
1785 
1786   AllUnitsSearchNextSpeedButton.Hint:=lisUDSearchNextOccurrenceOfThisPhrase;
1787   IDEImages.AssignImage(AllUnitsSearchNextSpeedButton, 'arrow_down');
1788   AllUnitsSearchPrevSpeedButton.Hint:=lisUDSearchPreviousOccurrenceOfThisPhrase;
1789   IDEImages.AssignImage(AllUnitsSearchPrevSpeedButton, 'arrow_up');
1790 
1791   // selected units
1792   SelectedUnitsGroupBox.Caption:=lisUDSelectedUnits;
1793   SelUnitsSearchNextSpeedButton.Hint:=lisUDSearchNextUnitOfThisPhrase;
1794   IDEImages.AssignImage(SelUnitsSearchNextSpeedButton, 'arrow_down');
1795   SelUnitsSearchPrevSpeedButton.Hint:=lisUDSearchPreviousUnitOfThisPhrase;
1796   IDEImages.AssignImage(SelUnitsSearchPrevSpeedButton, 'arrow_up');
1797 
1798   // popup menu
1799   UnitsTVCopyFilenameMenuItem.Caption:=uemCopyFilename;
1800   UnitsTVUnusedUnitsMenuItem.Caption:=lisShowUnusedUnits;
1801   UnitsTVExpandAllMenuItem.Caption:=lisUDExpandAllNodes;
1802   UnitsTVCollapseAllMenuItem.Caption:=lisUDCollapseAllNodes;
1803 
1804   UpdateUnitsButtons;
1805 end;
1806 
1807 procedure TUnitDependenciesWindow.UpdateUnitsButtons;
1808 begin
1809   SearchCustomFilesComboBox.Enabled:=SearchCustomFilesCheckBox.Checked;
1810   SearchCustomFilesBrowseButton.Enabled:=SearchCustomFilesCheckBox.Checked;
1811 end;
1812 
1813 procedure TUnitDependenciesWindow.UpdateAll;
1814 begin
1815   UpdateGroupsLvlGraph;
1816   UpdateUnitsLvlGraph;
1817   UpdateAllUnitsTreeView;
1818 end;
1819 
1820 procedure TUnitDependenciesWindow.UpdateGroupsLvlGraph;
1821 var
1822   AVLNode: TAVLTreeNode;
1823   Group: TUGGroup;
1824   Graph: TLvlGraph;
1825   PkgList: TFPList;
1826   i: Integer;
1827   RequiredPkg: TIDEPackage;
1828   GroupObj: TObject;
1829   GraphGroup: TLvlGraphNode;
1830   UnitNode: TAVLTreeNode;
1831   GrpUnit: TUDUnit;
1832   UsedUnit: TUDUnit;
1833 begin
1834   Exclude(FFlags,udwNeedUpdateGroupsLvlGraph);
1835   GroupsLvlGraph.BeginUpdate;
1836   Graph:=GroupsLvlGraph.Graph;
1837   Graph.Clear;
1838   AVLNode:=Groups.Groups.FindLowest;
1839   while AVLNode<>nil do begin
1840     Group:=TUGGroup(AVLNode.Data);
1841     AVLNode:=Groups.Groups.FindSuccessor(AVLNode);
1842     GraphGroup:=Graph.GetNode(Group.Name,true);
1843     GraphGroup.Data:=Group;
1844     GroupObj:=nil;
1845     if IsProjectGroup(Group) then begin
1846       // project
1847       GroupObj:=LazarusIDE.ActiveProject;
1848       GraphGroup.Selected:=true;
1849     end else begin
1850       // package
1851       GroupObj:=PackageEditingInterface.FindPackageWithName(Group.Name);
1852     end;
1853     if GroupObj<>nil then begin
1854       // add lpk dependencies
1855       PkgList:=nil;
1856       try
1857         PackageEditingInterface.GetRequiredPackages(GroupObj,PkgList,[pirNotRecursive]);
1858         if (PkgList<>nil) then begin
1859           // add for each dependency an edge in the Graph
1860           for i:=0 to PkgList.Count-1 do begin
1861             RequiredPkg:=TIDEPackage(PkgList[i]);
1862             Graph.GetEdge(GraphGroup,Graph.GetNode(RequiredPkg.Name,true),true);
1863           end;
1864         end;
1865       finally
1866         PkgList.Free;
1867       end;
1868     end else if IsFPCSrcGroup(Group) then begin
1869       // add FPC source dependencies
1870       UnitNode:=Group.Units.FindLowest;
1871       while UnitNode<>nil do begin
1872         GrpUnit:=TUDUnit(UnitNode.Data);
1873         UnitNode:=Group.Units.FindSuccessor(UnitNode);
1874         if GrpUnit.UsesUnits=nil then continue;
1875         for i:=0 to GrpUnit.UsesUnits.Count-1 do begin
1876           UsedUnit:=TUDUnit(TUDUses(GrpUnit.UsesUnits[i]).UsesUnit);
1877           if (UsedUnit.Group=nil) or (UsedUnit.Group=Group) then continue;
1878           Graph.GetEdge(GraphGroup,Graph.GetNode(UsedUnit.Group.Name,true),true);
1879         end;
1880       end;
1881     end;
1882   end;
1883   GroupsLvlGraph.EndUpdate;
1884 end;
1885 
1886 procedure TUnitDependenciesWindow.UpdateUnitsLvlGraph;
1887 
UnitToCaptionnull1888   function UnitToCaption(AnUnit: TUGUnit): string;
1889   begin
1890     Result:=ExtractFileNameOnly(AnUnit.Filename);
1891   end;
1892 
1893 var
1894   GraphGroup: TLvlGraphNode;
1895   NewUnits: TFilenameToPointerTree;
1896   UnitGroup: TUGGroup;
1897   AVLNode: TAVLTreeNode;
1898   GroupUnit: TUDUnit;
1899   i: Integer;
1900   HasChanged: Boolean;
1901   Graph: TLvlGraph;
1902   CurUses: TUDUses;
1903   SourceGraphNode: TLvlGraphNode;
1904   TargetGraphNode: TLvlGraphNode;
1905   NewGroups: TStringToPointerTree;
1906   UsedUnit: TUDUnit;
1907 begin
1908   Exclude(FFlags,udwNeedUpdateUnitsLvlGraph);
1909   NewGroups:=TStringToPointerTree.Create(false);
1910   NewUnits:=TFilenameToPointerTree.Create(false);
1911   try
1912     // fetch new list of units
1913     GraphGroup:=GroupsLvlGraph.Graph.FirstSelected;
1914     while GraphGroup<>nil do begin
1915       UnitGroup:=TUGGroup(GraphGroup.Data);
1916       if UnitGroup<>nil then begin
1917         NewGroups[UnitGroup.Name]:=UnitGroup;
1918         AVLNode:=UnitGroup.Units.FindLowest;
1919         while AVLNode<>nil do begin
1920           GroupUnit:=TUDUnit(AVLNode.Data);
1921           NewUnits[GroupUnit.Filename]:=GroupUnit;
1922           AVLNode:=UnitGroup.Units.FindSuccessor(AVLNode);
1923         end;
1924       end;
1925       GraphGroup:=GraphGroup.NextSelected;
1926     end;
1927 
1928     // check if something changed
1929     Graph:=UnitsLvlGraph.Graph;
1930     HasChanged:=false;
1931     i:=0;
1932     AVLNode:=NewUnits.Tree.FindLowest;
1933     while AVLNode<>nil do begin
1934       GroupUnit:=TUDUnit(NewUnits.GetNodeData(AVLNode)^.Value);
1935       if (Graph.NodeCount<=i) or (Graph.Nodes[i].Data<>Pointer(GroupUnit)) then
1936       begin
1937         HasChanged:=true;
1938         break;
1939       end;
1940       i+=1;
1941       AVLNode:=NewUnits.Tree.FindSuccessor(AVLNode);
1942     end;
1943     if i<Graph.NodeCount then HasChanged:=true;
1944     if not HasChanged then exit;
1945 
1946     // units changed -> update level graph of units
1947     UnitsLvlGraph.BeginUpdate;
1948     Graph.Clear;
1949     AVLNode:=NewUnits.Tree.FindLowest;
1950     while AVLNode<>nil do begin
1951       GroupUnit:=TUDUnit(NewUnits.GetNodeData(AVLNode)^.Value);
1952       SourceGraphNode:=Graph.GetNode(UnitToCaption(GroupUnit),true);
1953       SourceGraphNode.Data:=GroupUnit;
1954       if GroupUnit.UsesUnits<>nil then begin
1955         for i:=0 to GroupUnit.UsesUnits.Count-1 do begin
1956           CurUses:=TUDUses(GroupUnit.UsesUnits[i]);
1957           UsedUnit:=TUDUnit(CurUses.UsesUnit);
1958           if UsedUnit.Group=nil then continue;
1959           if not NewGroups.Contains(UsedUnit.Group.Name) then continue;
1960           TargetGraphNode:=Graph.GetNode(UnitToCaption(UsedUnit),true);
1961           TargetGraphNode.Data:=UsedUnit;
1962           Graph.GetEdge(SourceGraphNode,TargetGraphNode,true);
1963         end;
1964       end;
1965       AVLNode:=NewUnits.Tree.FindSuccessor(AVLNode);
1966     end;
1967 
1968     UnitsLvlGraph.EndUpdate;
1969   finally
1970     NewGroups.Free;
1971     NewUnits.Free;
1972   end;
1973 end;
1974 
1975 procedure TUnitDependenciesWindow.CreateTVNodes(TV: TTreeView;
1976   ParentTVNode: TTreeNode; ParentUDNode: TUDNode; Expand: boolean);
1977 var
1978   AVLNode: TAVLTreeNode;
1979   UDNode: TUDNode;
1980   TVNode: TTreeNode;
1981 begin
1982   if ParentUDNode=nil then exit;
1983   AVLNode:=ParentUDNode.ChildNodes.FindLowest;
1984   while AVLNode<>nil do begin
1985     UDNode:=TUDNode(AVLNode.Data);
1986     TVNode:=TV.Items.AddChild(ParentTVNode,UDNode.NodeText);
1987     UDNode.TVNode:=TVNode;
1988     TVNode.Data:=UDNode;
1989     TVNode.ImageIndex:=GetImgIndex(UDNode);
1990     TVNode.SelectedIndex:=TVNode.ImageIndex;
1991     TVNode.HasChildren:=UDNode.HasChildren;
1992     if UDNode.IntfCycle then
1993       TVNode.OverlayIndex:=fImgIndexOverlayIntfCycle
1994     else if UDNode.ImplCycle then
1995       TVNode.OverlayIndex:=fImgIndexOverlayImplCycle
1996     else if UDNode.HasImplementationUses then
1997       TVNode.OverlayIndex:=fImgIndexOverlayImplUses;
1998     //if TVNode.OverlayIndex>=0 then
1999     //  debugln(['TUnitDependenciesWindow.CreateTVNodes ',TVNode.Text,' Overlay=',TVNode.OverlayIndex,' ',TV.Images.Count]);
2000     CreateTVNodes(TV,TVNode,UDNode,Expand);
2001     TVNode.Expanded:=Expand and (TVNode.Count>0);
2002     AVLNode:=ParentUDNode.ChildNodes.FindSuccessor(AVLNode);
2003   end;
2004 end;
2005 
2006 procedure TUnitDependenciesWindow.FreeUsesGraph;
2007 begin
2008   FreeAndNil(FAllUnitsRootUDNode);
2009   FreeAndNil(FSelUnitsRootUDNode);
2010   GroupsLvlGraph.Clear;
2011   UnitsLvlGraph.Clear;
2012   FreeAndNil(FGroups);
2013   FreeAndNil(FUsesGraph);
2014 end;
2015 
GetPopupTV_UDNodenull2016 function TUnitDependenciesWindow.GetPopupTV_UDNode(out UDNode: TUDNode
2017   ): boolean;
2018 var
2019   TV: TTreeView;
2020   TVNode: TTreeNode;
2021 begin
2022   Result:=false;
2023   UDNode:=nil;
2024   TV:=TTreeView(UnitsTVPopupMenu.PopupComponent);
2025   if not (TV is TTreeView) then exit;
2026   TVNode:=TV.Selected;
2027   if (TVNode=nil) or not (TObject(TVNode.Data) is TUDNode) then exit;
2028   UDNode:=TUDNode(TVNode.Data);
2029   Result:=true;
2030 end;
2031 
2032 procedure TUnitDependenciesWindow.UpdateAllUnitsTreeView;
2033 var
2034   TV: TTreeView;
2035   OldExpanded: TTreeNodeExpandedState;
2036   SrcEdit: TSourceEditorInterface;
2037   SelPath: String;
2038 begin
2039   Exclude(FFlags,udwNeedUpdateAllUnitsTreeView);
2040   TV:=AllUnitsTreeView;
2041   TV.BeginUpdate;
2042   // save old expanded state
2043   if (TV.Items.Count>1) and (GetAllUnitsFilter(false)='') then
2044     OldExpanded:=TTreeNodeExpandedState.Create(TV)
2045   else
2046     OldExpanded:=nil;
2047   SelPath:='';
2048   if TV.Selected<>nil then
2049     SelPath:=TV.Selected.GetTextPath;
2050   // clear
2051   FreeAndNil(FAllUnitsRootUDNode);
2052   fAllUnitsTVSearchStartNode:=nil;
2053   TV.Items.Clear;
2054   // create nodes
2055   FAllUnitsRootUDNode:=CreateAllUnitsTree;
2056   CreateTVNodes(TV,nil,FAllUnitsRootUDNode,true);
2057   // restore old expanded state
2058   if OldExpanded<>nil then begin
2059     OldExpanded.Apply(TV);
2060     OldExpanded.Free;
2061   end;
2062   // update search
2063   UpdateAllUnitsTreeViewSearch;
2064   // select an unit
2065   if PendingUnitDependencyRoute.Count>0 then begin
2066     TV.Selected:=FindUnitTVNodeWithUnitName(TV,PendingUnitDependencyRoute[0]);
2067   end;
2068   if (TV.Selected=nil) and (SelPath<>'') then begin
2069     TV.Selected:=TV.Items.FindNodeWithTextPath(SelPath);
2070   end;
2071   if (TV.Selected=nil) then begin
2072     SrcEdit:=SourceEditorManagerIntf.ActiveEditor;
2073     if SrcEdit<>nil then
2074       TV.Selected:=FindUnitTVNodeWithFilename(TV,SrcEdit.FileName);
2075   end;
2076   if (TV.Selected=nil) and (LazarusIDE.ActiveProject<>nil)
2077   and (LazarusIDE.ActiveProject.MainFile<>nil) then
2078     TV.Selected:=FindUnitTVNodeWithFilename(TV,LazarusIDE.ActiveProject.MainFile.Filename);
2079 
2080   TV.EndUpdate;
2081 end;
2082 
2083 procedure TUnitDependenciesWindow.UpdateSelUnitsTreeView;
2084 var
2085   TV: TTreeView;
2086 begin
2087   //debugln(['TUnitDependenciesWindow.UpdateSelUnitsTreeView START']);
2088   Exclude(FFlags,udwNeedUpdateSelUnitsTreeView);
2089   TV:=SelUnitsTreeView;
2090   TV.BeginUpdate;
2091   // clear
2092   FreeAndNil(FSelUnitsRootUDNode);
2093   fSelUnitsTVSearchStartNode:=nil;
2094   TV.Items.Clear;
2095   // create nodes
2096   FSelUnitsRootUDNode:=CreateSelUnitsTree;
2097   CreateTVNodes(TV,nil,FSelUnitsRootUDNode,true);
2098   // update search
2099   UpdateSelUnitsTreeViewSearch;
2100   TV.EndUpdate;
2101 end;
2102 
2103 procedure TUnitDependenciesWindow.UpdateAllUnitsTreeViewSearch;
2104 begin
2105   Exclude(FFlags,udwNeedUpdateAllUnitsTVSearch);
2106   SelectNextSearchTV(AllUnitsTreeView,fAllUnitsTVSearchStartNode,true,false);
2107   AllUnitsTreeView.Invalidate;
2108 end;
2109 
2110 procedure TUnitDependenciesWindow.UpdateSelUnitsTreeViewSearch;
2111 begin
2112   Exclude(FFlags,udwNeedUpdateSelUnitsTVSearch);
2113   SelectNextSearchTV(SelUnitsTreeView,fSelUnitsTVSearchStartNode,true,false);
2114   SelUnitsTreeView.Invalidate;
2115 end;
2116 
TUnitDependenciesWindow.FindNextTVNodenull2117 function TUnitDependenciesWindow.FindNextTVNode(StartNode: TTreeNode;
2118   LowerSearch: string; SearchNext, SkipStart: boolean): TTreeNode;
2119 begin
2120   Result:=StartNode;
2121   while Result<>nil do begin
2122     if ((Result<>StartNode) or (not SkipStart))
2123     and NodeTextFitsFilter(Result.Text,LowerSearch) then
2124       exit;
2125     if SearchNext then
2126       Result:=Result.GetNext
2127     else
2128       Result:=Result.GetPrev;
2129   end;
2130 end;
2131 
FindUnitTVNodeWithFilenamenull2132 function TUnitDependenciesWindow.FindUnitTVNodeWithFilename(TV: TTreeView;
2133   aFilename: string): TTreeNode;
2134 var
2135   i: Integer;
2136   UDNode: TUDNode;
2137 begin
2138   for i:=0 to TV.Items.Count-1 do begin
2139     Result:=TV.Items[i];
2140     if TObject(Result.Data) is TUDNode then begin
2141       UDNode:=TUDNode(Result.Data);
2142       if (UDNode.Typ in [udnDirectory,udnUnit])
2143       and (CompareFilenames(UDNode.Identifier,aFilename)=0) then
2144         exit;
2145     end;
2146   end;
2147   Result:=nil;
2148 end;
2149 
FindUnitTVNodeWithUnitNamenull2150 function TUnitDependenciesWindow.FindUnitTVNodeWithUnitName(TV: TTreeView;
2151   aUnitName: string): TTreeNode;
2152 var
2153   i: Integer;
2154   UDNode: TUDNode;
2155 begin
2156   for i:=0 to TV.Items.Count-1 do begin
2157     Result:=TV.Items[i];
2158     if TObject(Result.Data) is TUDNode then begin
2159       UDNode:=TUDNode(Result.Data);
2160       if (UDNode.Typ in [udnUnit])
2161       and (CompareText(ExtractFileNameOnly(UDNode.Identifier),aUnitName)=0) then
2162         exit;
2163     end;
2164   end;
2165   Result:=nil;
2166 end;
2167 
TUnitDependenciesWindow.GetImgIndexnull2168 function TUnitDependenciesWindow.GetImgIndex(Node: TUDNode): integer;
2169 begin
2170   case Node.Typ of
2171   //udnNone: ;
2172   udnGroup:
2173     if IsProjectGroup(Node.Group) then
2174       Result:=fImgIndexProject
2175     else
2176       Result:=fImgIndexPackage;
2177   udnDirectory: Result:=fImgIndexDirectory;
2178   //udnInterface: ;
2179   //udnImplementation: ;
2180   //udnUsedByInterface: ;
2181   //udnUsedByImplementation: ;
2182   udnUnit: Result:=fImgIndexUnit;
2183   else
2184     Result:=fImgIndexDirectory;
2185   end;
2186 end;
2187 
TUnitDependenciesWindow.NodeTextToUnitnull2188 function TUnitDependenciesWindow.NodeTextToUnit(NodeText: string): TUGUnit;
2189 var
2190   AVLNode: TAVLTreeNode;
2191 begin
2192   AVLNode:=UsesGraph.FilesTree.FindLowest;
2193   while AVLNode<>nil do begin
2194     Result:=TUGUnit(AVLNode.Data);
2195     if NodeText=UGUnitToNodeText(Result) then exit;
2196     AVLNode:=UsesGraph.FilesTree.FindSuccessor(AVLNode);
2197   end;
2198   Result:=nil;
2199 end;
2200 
TUnitDependenciesWindow.UGUnitToNodeTextnull2201 function TUnitDependenciesWindow.UGUnitToNodeText(UGUnit: TUGUnit): string;
2202 begin
2203   Result:=ExtractFileName(UGUnit.Filename);
2204 end;
2205 
TUnitDependenciesWindow.GetFPCSrcDirnull2206 function TUnitDependenciesWindow.GetFPCSrcDir: string;
2207 var
2208   UnitSet: TFPCUnitSetCache;
2209 begin
2210   UnitSet:=CodeToolBoss.GetUnitSetForDirectory('');
2211   Result:=UnitSet.FPCSourceDirectory;
2212 end;
2213 
TUnitDependenciesWindow.IsFPCSrcGroupnull2214 function TUnitDependenciesWindow.IsFPCSrcGroup(Group: TUGGroup): boolean;
2215 begin
2216   Result:=(Group<>nil) and (LeftStr(Group.Name,length(GroupPrefixFPCSrc))=GroupPrefixFPCSrc);
2217 end;
2218 
IsProjectGroupnull2219 function TUnitDependenciesWindow.IsProjectGroup(Group: TUGGroup): boolean;
2220 begin
2221   Result:=(Group<>nil) and IsProjectGroup(Group.Name);
2222 end;
2223 
IsProjectGroupnull2224 function TUnitDependenciesWindow.IsProjectGroup(GroupName: string): boolean;
2225 begin
2226   Result:=(GroupName=GroupPrefixProject);
2227 end;
2228 
TUnitDependenciesWindow.GetFilenamenull2229 function TUnitDependenciesWindow.GetFilename(UDNode: TUDNode): string;
2230 var
2231   Pkg: TIDEPackage;
2232 begin
2233   Result:='';
2234   if UDNode.Typ in [udnUnit,udnDirectory] then
2235     Result:=UDNode.Identifier
2236   else if UDNode.Typ=udnGroup then begin
2237     if IsProjectGroup(UDNode.Group) then begin
2238       if (LazarusIDE.ActiveProject<>nil) then
2239         Result:=LazarusIDE.ActiveProject.ProjectInfoFile;
2240     end else begin
2241       Pkg:=PackageEditingInterface.FindPackageWithName(UDNode.Group);
2242       if Pkg<>nil then
2243         Result:=Pkg.Filename;
2244     end;
2245   end;
2246 end;
2247 
TUnitDependenciesWindow.GetAllUnitsFilternull2248 function TUnitDependenciesWindow.GetAllUnitsFilter(Lower: boolean): string;
2249 begin
2250   Result:=AllUnitsFilterEdit.Text;
2251   if Lower then
2252     Result:=UTF8LowerCase(Result);
2253 end;
2254 
TUnitDependenciesWindow.GetAllUnitsSearchnull2255 function TUnitDependenciesWindow.GetAllUnitsSearch(Lower: boolean): string;
2256 begin
2257   Result:=AllUnitsSearchEdit.Text;
2258   if Lower then
2259     Result:=UTF8LowerCase(Result);
2260 end;
2261 
GetSelUnitsSearchnull2262 function TUnitDependenciesWindow.GetSelUnitsSearch(Lower: boolean): string;
2263 begin
2264   Result:=SelUnitsSearchEdit.Text;
2265   if Lower then
2266     Result:=UTF8LowerCase(Result);
2267 end;
2268 
ResStrFilternull2269 function TUnitDependenciesWindow.ResStrFilter: string;
2270 begin
2271   Result:=lisUDFilter;
2272 end;
2273 
TUnitDependenciesWindow.ResStrSearchnull2274 function TUnitDependenciesWindow.ResStrSearch: string;
2275 begin
2276   Result:=lisUDSearch;
2277 end;
2278 
NodeTextFitsFilternull2279 function TUnitDependenciesWindow.NodeTextFitsFilter(const NodeText,
2280   LowerFilter: string): boolean;
2281 begin
2282   Result:=Pos(LowerFilter,UTF8LowerCase(NodeText))>0;
2283 end;
2284 
2285 procedure TUnitDependenciesWindow.CreateUsesGraph(out TheUsesGraph: TUsesGraph;
2286   out TheGroups: TUGGroups);
2287 begin
2288   TheUsesGraph:=CodeToolBoss.CreateUsesGraph;
2289   TheGroups:=TUGGroups.Create(TheUsesGraph);
2290   if not TUDUnit.InheritsFrom(TheUsesGraph.UnitClass) then
2291     RaiseCatchableException('');
2292   TheUsesGraph.UnitClass:=TUDUnit;
2293   if not TUDUses.InheritsFrom(TheUsesGraph.UsesClass) then
2294     RaiseCatchableException('');
2295   TheUsesGraph.UsesClass:=TUDUses;
2296 end;
2297 
2298 end.
2299 
2300