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