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     A dialog showing the unused units of the current unit
25     (at cursor in source editor).
26     With the ability to remove them automatically.
27 }
28 unit UnusedUnitsDlg;
29 
30 {$mode objfpc}{$H+}
31 
32 interface
33 
34 uses
35   Classes, SysUtils,
36   // LCL
37   Forms, Controls, ComCtrls, StdCtrls, ExtCtrls, Buttons, Dialogs,
38   // LazUtils
39   LazUTF8, LazLoggerBase,
40   // Codetools
41   CodeCache, CodeToolManager,
42   // IdeIntf
43   SrcEditorIntf, LazIDEIntf, IDEImagesIntf, IDEDialogs,
44   // IDE
45   LazarusIDEStrConsts;
46 
47 type
48 
49   { TUnusedUnitsDialog }
50 
51   TUnusedUnitsDialog = class(TForm)
52     CancelBitBtn: TBitBtn;
53     ShowInitializationCheckBox: TCheckBox;
54     RemoveAllBitBtn: TBitBtn;
55     RemoveSelectedBitBtn: TBitBtn;
56     Panel1: TPanel;
57     UnitsTreeView: TTreeView;
58     procedure CancelBitBtnClick(Sender: TObject);
59     procedure FormCreate(Sender: TObject);
60     procedure RemoveAllBitBtnClick(Sender: TObject);
61     procedure RemoveSelectedBitBtnClick(Sender: TObject);
62     procedure ShowInitializationCheckBoxClick(Sender: TObject);
63     procedure UnitsTreeViewSelectionChanged(Sender: TObject);
64   private
65     FCode: TCodeBuffer;
66     FUnits: TStrings;
67     ImgIDInterface: LongInt;
68     ImgIDImplementation: LongInt;
69     ImgIDInitialization: LongInt;
70     ImgIDNone: LongInt;
71     procedure SetCode(AValue: TCodeBuffer);
72     procedure SetUnits(const AValue: TStrings);
73     procedure RebuildUnitsTreeView;
74     procedure UpdateButtons;
75   public
GetSelectedUnitsnull76     function GetSelectedUnits: TStrings;
GetAllUnitsnull77     function GetAllUnits: TStrings;
78     property Units: TStrings read FUnits write SetUnits;
79     property Code: TCodeBuffer read FCode write SetCode;
80   end;
81 
82 
ShowUnusedUnitsDialognull83 function ShowUnusedUnitsDialog: TModalResult;
ShowUnusedUnitsDialognull84 function ShowUnusedUnitsDialog(Code: TCodeBuffer): TModalResult;
85 
86 implementation
87 
88 {$R *.lfm}
89 
ShowUnusedUnitsDialognull90 function ShowUnusedUnitsDialog: TModalResult;
91 var
92   SrcEdit: TSourceEditorInterface;
93   Code: TCodeBuffer;
94 begin
95   // get cursor position
96   Result:=mrAbort;
97   SrcEdit:=SourceEditorManagerIntf.ActiveEditor;
98   if SrcEdit=nil then exit;
99   Code:=TCodeBuffer(SrcEdit.CodeToolsBuffer);
100   if Code=nil then exit;
101   Result:=ShowUnusedUnitsDialog(Code);
102 end;
103 
ShowUnusedUnitsDialognull104 function ShowUnusedUnitsDialog(Code: TCodeBuffer): TModalResult;
105 var
106   UnusedUnitsDialog: TUnusedUnitsDialog;
107   xUnits: TStringListUTF8Fast;
108   RemoveUnits: TStrings;
109   i: Integer;
110   DlgResult: TModalResult;
111   SrcEdit: TSourceEditorInterface;
112 begin
113   Result:=mrOk;
114   if Code=nil then exit;
115   if not LazarusIDE.BeginCodeTools then exit;
116 
117   UnusedUnitsDialog:=nil;
118   RemoveUnits:=nil;
119   xUnits:=TStringListUTF8Fast.Create;
120   try
121     if not CodeToolBoss.FindUnusedUnits(Code,xUnits) then begin
122       DebugLn(['ShowUnusedUnitsDialog CodeToolBoss.FindUnusedUnits failed']);
123       LazarusIDE.DoJumpToCodeToolBossError;
124       exit(mrCancel);
125     end;
126     xUnits.Sort;
127 
128     UnusedUnitsDialog:=TUnusedUnitsDialog.Create(nil);
129     UnusedUnitsDialog.Units:=xUnits;
130     UnusedUnitsDialog.Code:=Code;
131     DlgResult:=UnusedUnitsDialog.ShowModal;
132     if DlgResult=mrOk then
133       RemoveUnits:=UnusedUnitsDialog.GetSelectedUnits
134     else if DlgResult=mrAll then
135       RemoveUnits:=UnusedUnitsDialog.GetAllUnits
136     else
137       RemoveUnits:=nil;
138     if (RemoveUnits<>nil) and (RemoveUnits.Count>0) then begin
139       LazarusIDE.DoOpenEditorFile(Code.Filename,-1,-1,[]);
140       SrcEdit:=SourceEditorManagerIntf.SourceEditorIntfWithFilename(Code.Filename);
141       if SrcEdit=nil then begin
142         IDEMessageDialog(lisCCOErrorCaption,
143           Format(lisUnableToOpen, [Code.Filename]),
144           mtError,[mbCancel]);
145         exit(mrCancel);
146       end;
147 
148       SrcEdit.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('ShowUnusedUnitsDialog'){$ENDIF};
149       try
150         for i:=0 to RemoveUnits.Count-1 do begin
151           if not CodeToolBoss.RemoveUnitFromAllUsesSections(Code,RemoveUnits[i])
152           then begin
153             LazarusIDE.DoJumpToCodeToolBossError;
154             exit(mrCancel);
155           end;
156         end;
157       finally
158         SrcEdit.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('ShowUnusedUnitsDialog'){$ENDIF};
159       end;
160     end;
161   finally
162     CodeToolBoss.SourceCache.ClearAllSourceLogEntries;
163     RemoveUnits.Free;
164     UnusedUnitsDialog.Free;
165     xUnits.Free;
166   end;
167 end;
168 
169 { TUnusedUnitsDialog }
170 
171 procedure TUnusedUnitsDialog.FormCreate(Sender: TObject);
172 begin
173   ShowInitializationCheckBox.Caption:=lisShowUnitsWithInitialization;
174   ShowInitializationCheckBox.Hint:=lisShowUnitsWithInitializationHint;
175   RemoveSelectedBitBtn.Caption:=lisRemoveSelectedUnits;
176   RemoveAllBitBtn.Caption:=lisRemoveAllUnits;
177   CancelBitBtn.Caption:=lisCancel;
178 
179   UnitsTreeView.StateImages := IDEImages.Images_16;
180   ImgIDInterface := IDEImages.LoadImage('ce_interface');
181   ImgIDImplementation := IDEImages.LoadImage('ce_implementation');
182   ImgIDInitialization := IDEImages.LoadImage('ce_initialization');
183   ImgIDNone := IDEImages.LoadImage('ce_default');
184 end;
185 
186 procedure TUnusedUnitsDialog.RemoveAllBitBtnClick(Sender: TObject);
187 begin
188   ModalResult:=mrAll;
189 end;
190 
191 procedure TUnusedUnitsDialog.CancelBitBtnClick(Sender: TObject);
192 begin
193   ModalResult:=mrCancel;
194 end;
195 
196 procedure TUnusedUnitsDialog.RemoveSelectedBitBtnClick(Sender: TObject);
197 begin
198   ModalResult:=mrOk;
199 end;
200 
201 procedure TUnusedUnitsDialog.ShowInitializationCheckBoxClick(Sender: TObject);
202 begin
203   RebuildUnitsTreeView;
204 end;
205 
206 procedure TUnusedUnitsDialog.UnitsTreeViewSelectionChanged(Sender: TObject);
207 begin
208   UpdateButtons;
209 end;
210 
211 procedure TUnusedUnitsDialog.SetUnits(const AValue: TStrings);
212 begin
213   if FUnits=AValue then exit;
214   FUnits:=AValue;
215   RebuildUnitsTreeView;
216 end;
217 
218 procedure TUnusedUnitsDialog.SetCode(AValue: TCodeBuffer);
219 begin
220   if FCode=AValue then Exit;
221   FCode:=AValue;
222   if FCode<>nil then
223     Caption:=Format(lisUnusedUnitsOf, [ExtractFilename(Code.Filename)]);
224 end;
225 
226 procedure TUnusedUnitsDialog.RebuildUnitsTreeView;
227 var
228   i: Integer;
229   AUnitname: string;
230   Flags: string;
231   UseInterface: Boolean;
232   InImplUsesSection: Boolean;
233   UseCode, HideCode: Boolean;
234   IntfTreeNode: TTreeNode;
235   ImplTreeNode: TTreeNode;
236   ParentNode: TTreeNode;
237   TVNode: TTreeNode;
238 begin
239   UnitsTreeView.BeginUpdate;
240   UnitsTreeView.Items.Clear;
241   IntfTreeNode:=UnitsTreeView.Items.Add(nil,'Interface');
242   IntfTreeNode.StateIndex:=ImgIDInterface;
243   ImplTreeNode:=UnitsTreeView.Items.Add(nil,'Implementation');
244   ImplTreeNode.StateIndex:=ImgIDImplementation;
245   if Units<>nil then
246   begin
247     for i:=0 to Units.Count-1 do
248     begin
249       AUnitname:=Units.Names[i];
250       Flags:=Units.ValueFromIndex[i];
251       InImplUsesSection:=System.Pos(',implementation',Flags)>0;
252       UseInterface:=System.Pos(',used',Flags)>0;
253       UseCode:=System.Pos(',code',Flags)>0;
254       HideCode:=not ShowInitializationCheckBox.Checked;
255       if not (UseInterface or (HideCode and UseCode)) then
256       begin
257         if InImplUsesSection then
258           ParentNode:=ImplTreeNode
259         else
260           ParentNode:=IntfTreeNode;
261         TVNode:=UnitsTreeView.Items.AddChild(ParentNode,AUnitname);
262         if UseCode then
263           TVNode.StateIndex:=ImgIDInitialization
264         else
265           TVNode.StateIndex:=ImgIDInterface;
266       end;
267     end;
268   end;
269   IntfTreeNode.Expanded:=true;
270   ImplTreeNode.Expanded:=true;
271   UnitsTreeView.EndUpdate;
272   UpdateButtons;
273 end;
274 
275 procedure TUnusedUnitsDialog.UpdateButtons;
276 var
277   RemoveUnits: TStrings;
278 begin
279   RemoveUnits:=GetSelectedUnits;
280   RemoveSelectedBitBtn.Enabled:=RemoveUnits.Count>0;
281   RemoveAllBitBtn.Enabled:=Units.Count>0;
282   RemoveUnits.Free;
283 end;
284 
TUnusedUnitsDialog.GetSelectedUnitsnull285 function TUnusedUnitsDialog.GetSelectedUnits: TStrings;
286 var
287   TVNode: TTreeNode;
288 begin
289   Result:=TStringList.Create;
290   TVNode:=UnitsTreeView.Items.GetFirstNode;
291   while TVNode<>nil do begin
292     if TVNode.MultiSelected and (TVNode.Level=1) then
293       Result.Add(TVNode.Text);
294     TVNode:=TVNode.GetNext;
295   end;
296 end;
297 
GetAllUnitsnull298 function TUnusedUnitsDialog.GetAllUnits: TStrings;
299 var
300   TVNode: TTreeNode;
301 begin
302   Result:=TStringList.Create;
303   TVNode:=UnitsTreeView.Items.GetFirstNode;
304   while TVNode<>nil do begin
305     if (TVNode.Level=1) then
306       Result.Add(TVNode.Text);
307     TVNode:=TVNode.GetNext;
308   end;
309 end;
310 
311 end.
312 
313